diff --git a/code/ATMOSPHERICS/components/unary_devices/cryo.dm b/code/ATMOSPHERICS/components/unary_devices/cryo.dm
index f49c772816f..2f79446bac7 100644
--- a/code/ATMOSPHERICS/components/unary_devices/cryo.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/cryo.dm
@@ -8,7 +8,7 @@
var/on = 0
var/temperature_archived
- var/obj/item/weapon/reagent_containers/beaker = null
+ var/obj/item/weapon/reagent_containers/glass/beaker = null
var/next_trans = 0
var/current_heat_capacity = 50
state_open = 0
@@ -43,7 +43,7 @@
T.contents += contents
if(beaker)
- beaker.loc = get_step(loc, SOUTH) //Beaker is carefully fed from the wreckage of the cryotube
+ beaker.loc = get_step(loc, SOUTH) // Beaker is carefully ejected from the wreckage of the cryotube.
beaker = null
return ..()
@@ -159,8 +159,8 @@
data["isBeakerLoaded"] = beaker ? 1 : 0
var beakerContents[0]
- if(beaker && beaker:reagents && beaker:reagents.reagent_list.len)
- for(var/datum/reagent/R in beaker:reagents.reagent_list)
+ if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
data["beakerContents"] = beakerContents
return data
@@ -187,8 +187,7 @@
if(href_list["ejectBeaker"])
if(beaker)
- var/obj/item/weapon/reagent_containers/glass/B = beaker
- B.loc = get_step(loc, SOUTH)
+ beaker.loc = get_step(loc, SOUTH)
beaker = null
add_fingerprint(usr)
diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm
new file mode 100644
index 00000000000..3e81a67b1b2
--- /dev/null
+++ b/code/__DEFINES/reagents.dm
@@ -0,0 +1,3 @@
+#define SOLID 1
+#define LIQUID 2
+#define GAS 3
\ No newline at end of file
diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm
deleted file mode 100644
index ad500369e71..00000000000
--- a/code/modules/reagents/Chemistry-Machinery.dm
+++ /dev/null
@@ -1,1547 +0,0 @@
-#define SOLID 1
-#define LIQUID 2
-#define GAS 3
-
-/obj/machinery/chem_dispenser
- name = "chem dispenser"
- desc = "Creates and dispenses chemicals."
- density = 1
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "dispenser"
- use_power = 1
- idle_power_usage = 40
- var/energy = 100
- var/max_energy = 100
- var/amount = 30
- var/beaker = null
- var/recharged = 0
- var/recharge_delay = 5 //Time it game ticks between recharges
- var/image/icon_beaker = null //cached overlay
- var/list/dispensable_reagents = list("hydrogen","lithium","carbon","nitrogen","oxygen","fluorine",
- "sodium","aluminium","silicon","phosphorus","sulfur","chlorine","potassium","iron",
- "copper","mercury","radium","water","ethanol","sugar","sacid","welding_fuel","silver","iodine","bromine","stable_plasma")
-
-/obj/machinery/chem_dispenser/proc/recharge()
- if(stat & (BROKEN|NOPOWER)) return
- var/addenergy = 1
- var/oldenergy = energy
- energy = min(energy + addenergy, max_energy)
- if(energy != oldenergy)
- use_power(1500) // This thing uses up alot of power (this is still low as shit for creating reagents from thin air)
-
-/obj/machinery/chem_dispenser/power_change()
- if(powered())
- stat &= ~NOPOWER
- else
- spawn(rand(0, 15))
- stat |= NOPOWER
-
-/obj/machinery/chem_dispenser/process()
-
- if(recharged < 0)
- recharge()
- recharged = recharge_delay
- else
- recharged -= 1
-
-/obj/machinery/chem_dispenser/New()
- ..()
- recharge()
- dispensable_reagents = sortList(dispensable_reagents)
-
-/obj/machinery/chem_dispenser/ex_act(severity, target)
- if(severity < 3)
- ..()
-
-/obj/machinery/chem_dispenser/blob_act()
- if(prob(50))
- qdel(src)
-
-/obj/machinery/chem_dispenser/interact(mob/user)
- if(stat & BROKEN)
- return
- ui_interact(user)
-
-/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
- ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
- if (!ui)
- ui = new(user, src, ui_key, "chem_dispenser.tmpl", name, 500, 650)
- ui.open()
-
-/obj/machinery/chem_dispenser/get_ui_data()
- var/data = list()
- data["amount"] = amount
- data["energy"] = energy
- data["maxEnergy"] = max_energy
- data["isBeakerLoaded"] = beaker ? 1 : 0
-
- var beakerContents[0]
- var beakerCurrentVolume = 0
- if(beaker && beaker:reagents && beaker:reagents.reagent_list.len)
- for(var/datum/reagent/R in beaker:reagents.reagent_list)
- beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
- beakerCurrentVolume += R.volume
- data["beakerContents"] = beakerContents
-
- if (beaker)
- data["beakerCurrentVolume"] = beakerCurrentVolume
- data["beakerMaxVolume"] = beaker:volume
- else
- data["beakerCurrentVolume"] = null
- data["beakerMaxVolume"] = null
-
- var chemicals[0]
- for(var/re in dispensable_reagents)
- var/datum/reagent/temp = chemical_reagents_list[re]
- if(temp)
- chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("dispense" = temp.id)))) // list in a list because Byond merges the first list...
- data["chemicals"] = chemicals
- return data
-
-/obj/machinery/chem_dispenser/Topic(href, href_list)
- if(..())
- return
-
- if(href_list["amount"])
- amount = round(text2num(href_list["amount"]), 5) // round to nearest 5
- if (amount < 0) // Since the user can actually type the commands himself, some sanity checking
- amount = 0
- if (amount > 100)
- amount = 100
-
- if(href_list["dispense"])
- if (dispensable_reagents.Find(href_list["dispense"]) && beaker != null)
- var/obj/item/weapon/reagent_containers/glass/B = src.beaker
- var/datum/reagents/R = B.reagents
- var/space = R.maximum_volume - R.total_volume
-
- R.add_reagent(href_list["dispense"], min(amount, energy * 10, space))
- energy = max(energy - min(amount, energy * 10, space) / 10, 0)
-
- if(href_list["ejectBeaker"])
- if(beaker)
- var/obj/item/weapon/reagent_containers/glass/B = beaker
- B.loc = loc
- beaker = null
- overlays.Cut()
-
- add_fingerprint(usr)
-
-/obj/machinery/chem_dispenser/attackby(obj/item/weapon/reagent_containers/glass/B, mob/user, params)
- if(isrobot(user))
- return
-
- if(!istype(B, /obj/item/weapon/reagent_containers/glass))
- return
-
- if(src.beaker)
- user << "A beaker is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.beaker = B
- B.loc = src
- user << "You add the beaker to the machine."
-
- if(!icon_beaker)
- icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
- icon_beaker.pixel_x = rand(-10,5)
- overlays += icon_beaker
-
-/obj/machinery/chem_dispenser/attack_hand(mob/user)
- if (!user) return
- interact(user)
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/obj/machinery/chem_dispenser/constructable
- name = "portable chem dispenser"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "minidispenser"
- energy = 5
- max_energy = 5
- amount = 5
- recharge_delay = 30
- dispensable_reagents = list()
- var/list/special_reagents = list(list("hydrogen", "oxygen", "silicon", "phosphorus", "sulfur", "carbon", "nitrogen", "water"),
- list("lithium", "sugar", "sacid", "copper", "mercury", "sodium","iodine","bromine"),
- list("ethanol", "chlorine", "potassium", "aluminium", "radium", "fluorine", "iron", "welding_fuel","silver","stable_plasma"),
- list("oil", "ash", "acetone", "saltpetre", "ammonia", "diethylamine"))
-
-/obj/machinery/chem_dispenser/constructable/New()
- ..()
- component_parts = list()
- component_parts += new /obj/item/weapon/circuitboard/chem_dispenser(null)
- component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
- component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
- component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
- component_parts += new /obj/item/weapon/stock_parts/capacitor(null)
- component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
- component_parts += new /obj/item/weapon/stock_parts/cell/high(null)
- RefreshParts()
-
-/obj/machinery/chem_dispenser/constructable/RefreshParts()
- var/time = 0
- var/temp_energy = 0
- var/i
- for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
- temp_energy += M.rating
- temp_energy--
- max_energy = temp_energy * 5 //max energy = (bin1.rating + bin2.rating - 1) * 5, 5 on lowest 25 on highest
- for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
- time += C.rating
- for(var/obj/item/weapon/stock_parts/cell/P in component_parts)
- time += round(P.maxcharge, 10000) / 10000
- recharge_delay /= time/2 //delay between recharges, double the usual time on lowest 50% less than usual on highest
- for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
- for(i=1, i<=M.rating, i++)
- dispensable_reagents |= special_reagents[i]
- dispensable_reagents = sortList(dispensable_reagents)
-
-/obj/machinery/chem_dispenser/constructable/attackby(var/obj/item/I, var/mob/user, params)
- ..()
- if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I))
- return
-
- if(exchange_parts(user, I))
- return
-
- if(panel_open)
- if(istype(I, /obj/item/weapon/crowbar))
- if(beaker)
- var/obj/item/weapon/reagent_containers/glass/B = beaker
- B.loc = loc
- beaker = null
- default_deconstruction_crowbar(I)
- return 1
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/obj/machinery/chem_master
- name = "ChemMaster 3000"
- desc = "Used to bottle chemicals to create pills."
- density = 1
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "mixer0"
- use_power = 1
- idle_power_usage = 20
- var/obj/item/weapon/reagent_containers/glass/beaker = null
- var/obj/item/weapon/storage/pill_bottle/loaded_pill_bottle = null
- var/mode = 0
- var/condi = 0
- var/useramount = 30 // Last used amount
-
-
-/obj/machinery/chem_master/New()
- create_reagents(100)
- overlays += "waitlight"
-
-/obj/machinery/chem_master/ex_act(severity, target)
- if(severity < 3)
- ..()
-
-/obj/machinery/chem_master/blob_act()
- if (prob(50))
- qdel(src)
-
-/obj/machinery/chem_master/power_change()
- if(powered())
- stat &= ~NOPOWER
- else
- spawn(rand(0, 15))
- stat |= NOPOWER
-
-
-/obj/machinery/chem_master/attackby(obj/item/B, mob/user, params)
- if(default_unfasten_wrench(user, B))
- return
-
- if(istype(B, /obj/item/weapon/reagent_containers/glass))
- if(isrobot(user))
- return
- if(src.beaker)
- user << "A beaker is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.beaker = B
- B.loc = src
- user << "You add the beaker to the machine."
- src.updateUsrDialog()
- icon_state = "mixer1"
-
- else if(!condi && istype(B, /obj/item/weapon/storage/pill_bottle))
- if(src.loaded_pill_bottle)
- user << "A pill bottle is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.loaded_pill_bottle = B
- B.loc = src
- user << "You add the pill bottle into the dispenser slot."
- src.updateUsrDialog()
-
- return
-
-/obj/machinery/chem_master/Topic(href, href_list)
- if(..())
- return
-
- usr.set_machine(src)
-
- if(href_list["ejectp"])
- if(loaded_pill_bottle)
- loaded_pill_bottle.loc = src.loc
- loaded_pill_bottle = null
-
- else if(href_list["close"])
- usr << browse(null, "window=chem_master")
- usr.unset_machine()
- return
-
- else if(href_list["toggle"])
- mode = !mode
-
- else if(href_list["createbottle"])
- var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
- if(!name)
- return
- var/obj/item/weapon/reagent_containers/P
- if(condi)
- P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc)
- else
- P = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
- P.name = trim("[name] bottle")
- reagents.trans_to(P, P.volume)
-
- if(beaker)
-
- if(href_list["analyze"])
- if(locate(href_list["reagent"]))
- var/datum/reagent/R = locate(href_list["reagent"])
- if(R)
- var/dat = ""
- dat += "
[condi ? "Condiment" : "Chemical"] information:
"
- dat += "Name: [initial(R.name)]
"
- dat += "State: "
- if(initial(R.reagent_state) == 1)
- dat += "Solid"
- else if(initial(R.reagent_state) == 2)
- dat += "Liquid"
- else if(initial(R.reagent_state) == 3)
- dat += "Gas"
- else
- dat += "Unknown"
- dat += "
"
- dat += "Color: [initial(R.color)]
"
- dat += "Description: [initial(R.description)]
"
- var/const/P = 3 //The number of seconds between life ticks
- var/T = initial(R.metabolization_rate) * (60 / P)
- dat += "Metabolization Rate: [T]u/minute
"
- dat += "Overdose Threshold: [initial(R.overdose_threshold) ? "[initial(R.overdose_threshold)]u" : "none"]
"
- dat += "Addiction Threshold: [initial(R.addiction_threshold) ? "[initial(R.addiction_threshold)]u" : "none"]
"
- dat += "
Back"
- var/datum/browser/popup = new(usr, "chem_master", name)
- popup.set_content(dat)
- popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
- popup.open(1)
- return
-
- else if(href_list["main"]) // Used to exit the analyze screen.
- attack_hand(usr)
- return
-
- else if(href_list["add"])
- if(href_list["amount"])
- var/id = href_list["add"]
- var/amount = text2num(href_list["amount"])
- if (amount > 0)
- beaker.reagents.trans_id_to(src, id, amount)
-
- else if(href_list["addcustom"])
- var/id = href_list["addcustom"]
- var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
- if(!amt_temp)
- return
- useramount = amt_temp
- src.Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
-
- else if(href_list["remove"])
- if(href_list["amount"])
- var/id = href_list["remove"]
- var/amount = text2num(href_list["amount"])
- if (amount > 0)
- if(mode)
- reagents.trans_id_to(beaker, id, amount)
- else
- reagents.remove_reagent(id, amount)
-
- else if(href_list["removecustom"])
- var/id = href_list["removecustom"]
- var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
- if(!amt_temp)
- return
- useramount = amt_temp
- src.Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
-
- else if(href_list["eject"])
- if(beaker)
- beaker.loc = src.loc
- beaker = null
- reagents.clear_reagents()
- icon_state = "mixer0"
-
- else if(href_list["createpill"]) //Also used for condiment packs.
- if(reagents.total_volume == 0) return
- if(!condi)
- var/amount = 1
- var/vol_each = min(reagents.total_volume, 50)
- if(text2num(href_list["many"]))
- amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many pills?", amount) as num|null), 0), 10)
- if(!amount)
- return
- vol_each = min(reagents.total_volume / amount, 50)
- var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
- if(!name || !reagents.total_volume)
- return
- var/obj/item/weapon/reagent_containers/pill/P
-
- for(var/i = 0; i < amount; i++)
- if(loaded_pill_bottle && loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots)
- P = new/obj/item/weapon/reagent_containers/pill(loaded_pill_bottle)
- else
- P = new/obj/item/weapon/reagent_containers/pill(src.loc)
- P.name = trim("[name] pill")
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
- reagents.trans_to(P,vol_each)
- else
- var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
- if(!name || !reagents.total_volume)
- return
- var/obj/item/weapon/reagent_containers/food/condiment/pack/P = new/obj/item/weapon/reagent_containers/food/condiment/pack(src.loc)
-
- P.originalname = name
- P.name = trim("[name] pack")
- P.desc = "A small condiment pack. The label says it contains [name]."
- reagents.trans_to(P,10)
-
- else if(href_list["createpatch"])
- if(reagents.total_volume == 0) return
- var/amount = 1
- var/vol_each = min(reagents.total_volume, 50)
- if(text2num(href_list["many"]))
- amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many patches?", amount) as num|null), 0), 10)
- if(!amount)
- return
- vol_each = min(reagents.total_volume / amount, 50)
- var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
- if(!name || !reagents.total_volume)
- return
- var/obj/item/weapon/reagent_containers/pill/P
-
- for(var/i = 0; i < amount; i++)
- P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc)
- P.name = trim("[name] patch")
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
- reagents.trans_to(P,vol_each)
-
- src.updateUsrDialog()
- return
-
-/obj/machinery/chem_master/attack_ai(mob/user)
- return src.attack_hand(user)
-
-/obj/machinery/chem_master/attack_paw(mob/user)
- return src.attack_hand(user)
-
-/obj/machinery/chem_master/attack_hand(mob/user)
- if(stat & BROKEN)
- return
-
- user.set_machine(src)
- var/dat = ""
- if(beaker)
- dat += "Beaker \[[beaker.reagents.total_volume]/[beaker.volume]\] Eject and Clear Buffer
"
- else
- dat = "Please insert beaker.
"
-
- dat += "
Add to buffer:"
- if(beaker)
- if(beaker.reagents.total_volume)
- for(var/datum/reagent/G in beaker.reagents.reagent_list)
- dat += "- [G.name], [G.volume] Units - "
- dat += "Analyze "
- dat += "1 "
- dat += "5 "
- dat += "10 "
- dat += "All "
- dat += "Custom"
- else
- dat += "
- Beaker is empty."
- else
- dat += "
- No beaker."
-
- dat += "
Transfer to [(!mode ? "disposal" : "beaker")]:"
- if(reagents.total_volume)
- for(var/datum/reagent/N in reagents.reagent_list)
- dat += "- [N.name], [N.volume] Units - "
- dat += "Analyze "
- dat += "1 "
- dat += "5 "
- dat += "10 "
- dat += "All "
- dat += "Custom"
- else
- dat += "
- Buffer is empty."
- dat += "
"
-
- if(!condi)
- if(src.loaded_pill_bottle)
- dat += "Pill Bottle \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.storage_slots]\] Eject"
- else
- dat += "No pill bottle inserted."
- else
- dat += "
"
-
- dat += ""
- if(!condi)
- if(beaker && reagents.total_volume)
- dat += "- Create pill (50 units max)"
- dat += "
- Create multiple pills
"
- dat += " - Create patch (50 units max)"
- dat += "
- Create multiple patches
"
- else
- dat += " - Create pill (50 units max)"
- dat += "
- Create multiple pills
"
- dat += " - Create patch (50 units max)"
- dat += "
- Create multiple patches
"
- else
- if(beaker && reagents.total_volume)
- dat += " - Create pack (10 units max)
"
- else
- dat += " - Create pack (10 units max)
"
- dat += " - Create bottle ([condi ? "50" : "30"] units max)"
- dat += "
"
- dat += "
Close"
- var/datum/browser/popup = new(user, "chem_master", name, 470, 500)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open(1)
- return
-
-/obj/machinery/chem_master/proc/isgoodnumber(num)
- if(isnum(num))
- if(num > 200)
- num = 200
- else if(num < 0)
- num = 0
- else
- num = round(num)
- return num
- else
- return 0
-
-
-/obj/machinery/chem_master/condimaster
- name = "CondiMaster 3000"
- desc = "Used to create condiments and other cooking supplies."
- condi = 1
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/obj/machinery/chem_master/constructable
- name = "ChemMaster 2999"
- desc = "Used to seperate chemicals and distribute them in a variety of forms."
-
-/obj/machinery/chem_master/constructable/New()
- ..()
- component_parts = list()
- component_parts += new /obj/item/weapon/circuitboard/chem_master(null)
- component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
- component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
- component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)
- component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)
-
-/obj/machinery/chem_master/constructable/attackby(obj/item/B, mob/user, params)
-
- if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", B))
- if(beaker)
- beaker.loc = src.loc
- beaker = null
- reagents.clear_reagents()
- if(loaded_pill_bottle)
- loaded_pill_bottle.loc = src.loc
- loaded_pill_bottle = null
- return
-
- if(exchange_parts(user, B))
- return
-
- if(panel_open)
- if(istype(B, /obj/item/weapon/crowbar))
- default_deconstruction_crowbar(B)
- return 1
- else
- user << "You can't use the [src.name] while it's panel is opened!"
- return 1
-
- if(istype(B, /obj/item/weapon/reagent_containers/glass))
- if(src.beaker)
- user << "A beaker is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.beaker = B
- B.loc = src
- user << "You add the beaker to the machine."
- src.updateUsrDialog()
- icon_state = "mixer1"
-
- else if(!condi && istype(B, /obj/item/weapon/storage/pill_bottle))
- if(src.loaded_pill_bottle)
- user << "A pill bottle is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.loaded_pill_bottle = B
- B.loc = src
- user << "You add the pill bottle into the dispenser slot."
- src.updateUsrDialog()
-
- return
-
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-
-/obj/machinery/computer/pandemic
- name = "PanD.E.M.I.C 2200"
- desc = "Used to work with viruses."
- density = 1
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "mixer0"
- circuit = /obj/item/weapon/circuitboard/pandemic
- use_power = 1
- idle_power_usage = 20
- var/temp_html = ""
- var/wait = null
- var/obj/item/weapon/reagent_containers/glass/beaker = null
-
-/obj/machinery/computer/pandemic/New()
- ..()
- update_icon()
-
-/obj/machinery/computer/pandemic/set_broken()
- icon_state = (src.beaker?"mixer1_b":"mixer0_b")
- overlays.Cut()
- stat |= BROKEN
-
-/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index)
- if(beaker && beaker.reagents)
- if(beaker.reagents.reagent_list.len)
- var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
- if(BL)
- if(BL.data && BL.data["viruses"])
- var/list/viruses = BL.data["viruses"]
- return viruses[index]
- return null
-
-/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index)
- if(beaker && beaker.reagents)
- if(beaker.reagents.reagent_list.len)
- var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
- if(BL)
- if(BL.data && BL.data["resistances"])
- var/list/resistances = BL.data["resistances"]
- return resistances[index]
- return null
-
-/obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(index)
- var/datum/disease/D = GetVirusByIndex(index)
- if(D)
- return D.GetDiseaseID()
- return null
-
-/obj/machinery/computer/pandemic/proc/replicator_cooldown(waittime)
- wait = 1
- update_icon()
- spawn(waittime)
- src.wait = null
- update_icon()
- playsound(src.loc, 'sound/machines/ping.ogg', 30, 1)
-
-/obj/machinery/computer/pandemic/update_icon()
- if(stat & BROKEN)
- icon_state = (src.beaker?"mixer1_b":"mixer0_b")
- return
-
- icon_state = "mixer[(beaker)?"1":"0"][(powered()) ? "" : "_nopower"]"
-
- if(wait)
- overlays.Cut()
- else
- overlays += "waitlight"
-
-/obj/machinery/computer/pandemic/Topic(href, href_list)
- if(..())
- return
-
- usr.set_machine(src)
- if(!beaker) return
-
- if (href_list["create_vaccine"])
- if(!src.wait)
- var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
- if(B)
- B.pixel_x = rand(-3, 3)
- B.pixel_y = rand(-3, 3)
- var/path = GetResistancesByIndex(text2num(href_list["create_vaccine"]))
- var/vaccine_type = path
- var/vaccine_name = "Unknown"
-
- if(!ispath(vaccine_type))
- if(archive_diseases[path])
- var/datum/disease/D = archive_diseases[path]
- if(D)
- vaccine_name = D.name
- vaccine_type = path
- else if(vaccine_type)
- var/datum/disease/D = new vaccine_type(0, null)
- if(D)
- vaccine_name = D.name
-
- if(vaccine_type)
-
- B.name = "[vaccine_name] vaccine bottle"
- B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
- replicator_cooldown(200)
- else
- src.temp_html = "The replicator is not ready yet."
- src.updateUsrDialog()
- return
- else if (href_list["create_virus_culture"])
- if(!wait)
- var/type = GetVirusTypeByIndex(text2num(href_list["create_virus_culture"]))//the path is received as string - converting
- var/datum/disease/D = null
- if(!ispath(type))
- D = GetVirusByIndex(text2num(href_list["create_virus_culture"]))
- var/datum/disease/advance/A = archive_diseases[D.GetDiseaseID()]
- if(A)
- D = new A.type(0, A)
- else if(type)
- if(type in diseases) // Make sure this is a disease
- D = new type(0, null)
- if(!D)
- return
- var/name = stripped_input(usr,"Name:","Name the culture",D.name,MAX_NAME_LEN)
- if(name == null || wait)
- return
- var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
- B.icon_state = "bottle3"
- B.pixel_x = rand(-3, 3)
- B.pixel_y = rand(-3, 3)
- replicator_cooldown(50)
- var/list/data = list("viruses"=list(D))
- B.name = "[name] culture bottle"
- B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium."
- B.reagents.add_reagent("blood",20,data)
- src.updateUsrDialog()
- else
- src.temp_html = "The replicator is not ready yet."
- src.updateUsrDialog()
- return
- else if (href_list["empty_beaker"])
- beaker.reagents.clear_reagents()
- src.updateUsrDialog()
- return
- else if (href_list["eject"])
- beaker:loc = src.loc
- beaker = null
- icon_state = "mixer0"
- src.updateUsrDialog()
- return
- else if(href_list["clear"])
- src.temp_html = ""
- src.updateUsrDialog()
- return
- else if(href_list["name_disease"])
- var/new_name = stripped_input(usr, "Name the Disease", "New Name", "", MAX_NAME_LEN)
- if(!new_name)
- return
- if(..())
- return
- var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"]))
- if(archive_diseases[id])
- var/datum/disease/advance/A = archive_diseases[id]
- A.AssignName(new_name)
- for(var/datum/disease/advance/AD in SSdisease.processing)
- AD.Refresh()
- src.updateUsrDialog()
-
-
- else
- usr << browse(null, "window=pandemic")
- src.updateUsrDialog()
- return
-
- src.add_fingerprint(usr)
- return
-
-/obj/machinery/computer/pandemic/attack_hand(mob/user)
- if(..())
- return
- user.set_machine(src)
- var/dat = ""
- if(src.temp_html)
- dat = "[src.temp_html]
Main Menu"
- else if(!beaker)
- dat += "Please insert beaker.
"
- dat += "Close"
- else
- var/datum/reagents/R = beaker.reagents
- var/datum/reagent/blood/Blood = null
- for(var/datum/reagent/blood/B in R.reagent_list)
- if(B)
- Blood = B
- break
- if(!R.total_volume||!R.reagent_list.len)
- dat += "The beaker is empty
"
- else if(!Blood)
- dat += "No blood sample found in beaker."
- else if(!Blood.data)
- dat += "No blood data found in beaker."
- else
- dat += "Blood sample data:
"
- dat += "Blood DNA: [(Blood.data["blood_DNA"]||"none")]
"
- dat += "Blood Type: [(Blood.data["blood_type"]||"none")]
"
-
-
- if(Blood.data["viruses"])
- var/list/vir = Blood.data["viruses"]
- if(vir.len)
- var/i = 0
- for(var/datum/disease/D in Blood.data["viruses"])
- i++
- if(!(D.visibility_flags & HIDDEN_PANDEMIC))
-
- if(istype(D, /datum/disease/advance))
-
- var/datum/disease/advance/A = D
- D = archive_diseases[A.GetDiseaseID()]
- if(D && D.name == "Unknown")
- dat += "Name Disease
"
-
- if(!D)
- CRASH("We weren't able to get the advance disease from the archive.")
-
- dat += "Disease Agent: [D?"[D.agent] - Create virus culture bottle":"none"]
"
- dat += "Common name: [(D.name||"none")]
"
- dat += "Description: [(D.desc||"none")]
"
- dat += "Spread: [(D.spread_text||"none")]
"
- dat += "Possible cure: [(D.cure_text||"none")]
"
-
- if(istype(D, /datum/disease/advance))
- var/datum/disease/advance/A = D
- dat += "Symptoms: "
- var/english_symptoms = list()
- for(var/datum/symptom/S in A.symptoms)
- english_symptoms += S.name
- dat += english_list(english_symptoms)
-
- else
- dat += "No detectable virus in the sample."
- else
- dat += "No detectable virus in the sample."
-
- dat += "
Contains antibodies to: "
- if(Blood.data["resistances"])
- var/list/res = Blood.data["resistances"]
- if(res.len)
- dat += ""
- var/i = 0
- for(var/type in Blood.data["resistances"])
- i++
- var/disease_name = "Unknown"
-
- if(!ispath(type))
- var/datum/disease/advance/A = archive_diseases[type]
- if(A)
- disease_name = A.name
- else
- var/datum/disease/D = new type(0, null)
- disease_name = D.name
-
- dat += "- [disease_name] - Create vaccine bottle
"
- dat += "
"
- else
- dat += "nothing
"
- else
- dat += "nothing
"
- dat += "
Eject beaker[((R.total_volume&&R.reagent_list.len) ? "-- Empty beaker":"")]
"
- dat += "Close"
-
- user << browse("[src.name]
[dat]", "window=pandemic;size=575x400")
- onclose(user, "pandemic")
- return
-
-
-/obj/machinery/computer/pandemic/attackby(obj/I, mob/user, params)
- if(istype(I, /obj/item/weapon/reagent_containers/glass))
- if(stat & (NOPOWER|BROKEN)) return
- if(src.beaker)
- user << "A beaker is already loaded into the machine!"
- return
- if(!user.drop_item())
- return
-
- src.beaker = I
- I.loc = src
- user << "You add the beaker to the machine."
- src.updateUsrDialog()
- icon_state = "mixer1"
-
- else if(istype(I, /obj/item/weapon/screwdriver))
- if(src.beaker)
- beaker.loc = get_turf(src)
- ..()
- return
- else
- ..()
- return
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-/obj/machinery/reagentgrinder
-
- name = "All-In-One Grinder"
- desc = "Used to grind things up into raw materials."
- icon = 'icons/obj/kitchen.dmi'
- icon_state = "juicer1"
- layer = 2.9
- anchored = 1
- use_power = 1
- idle_power_usage = 5
- active_power_usage = 100
- pass_flags = PASSTABLE
- var/operating = 0
- var/obj/item/weapon/reagent_containers/beaker = null
- var/limit = 10
- var/list/blend_items = list (
-
- //Sheets
- /obj/item/stack/sheet/mineral/plasma = list("plasma" = 20),
- /obj/item/stack/sheet/metal = list("iron" = 20),
- /obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma" = 20),
- /obj/item/stack/sheet/mineral/wood = list("carbon" = 20),
- /obj/item/stack/sheet/glass = list("silicon" = 20),
- /obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20),
- /obj/item/stack/sheet/mineral/uranium = list("uranium" = 20),
- /obj/item/stack/sheet/mineral/bananium = list("banana" = 20),
- /obj/item/stack/sheet/mineral/silver = list("silver" = 20),
- /obj/item/stack/sheet/mineral/gold = list("gold" = 20),
- /obj/item/weapon/grown/nettle/basic = list("sacid" = 0),
- /obj/item/weapon/grown/nettle/death = list("facid" = 0),
- /obj/item/weapon/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),
-
- //Crayons (for overriding colours)
- /obj/item/toy/crayon/red = list("redcrayonpowder" = 10),
- /obj/item/toy/crayon/orange = list("orangecrayonpowder" = 10),
- /obj/item/toy/crayon/yellow = list("yellowcrayonpowder" = 10),
- /obj/item/toy/crayon/green = list("greencrayonpowder" = 10),
- /obj/item/toy/crayon/blue = list("bluecrayonpowder" = 10),
- /obj/item/toy/crayon/purple = list("purplecrayonpowder" = 10),
- /obj/item/toy/crayon/mime = list("invisiblecrayonpowder" = 50),
-
- //Blender Stuff
- /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("cornoil" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/wheat = list("flour" = -5),
- /obj/item/weapon/reagent_containers/food/snacks/grown/oat = list("flour" = -5),
- /obj/item/weapon/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/egg = list("eggyolk" = -5),
-
- //Grinder stuff, but only if dry
- /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/arabica = list("coffeepowder" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tea/aspera = list("teapowder" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
-
-
-
- //All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.!
- /obj/item/weapon/reagent_containers/pill = list(),
- /obj/item/weapon/reagent_containers/food = list()
- )
-
- var/list/juice_items = list (
-
- //Juicer Stuff
- /obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/banana = list("banana" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/potato = list("potato" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0),
- )
-
- var/list/dried_items = list(
-
- //Grinder stuff, but only if dry
- /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/arabica = list("coffeepowder" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tea/aspera = list("teapowder" = 0),
- /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
- )
-
- var/list/holdingitems = list()
-
-/obj/machinery/reagentgrinder/New()
- ..()
- beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
- return
-
-/obj/machinery/reagentgrinder/update_icon()
- icon_state = "juicer"+num2text(!isnull(beaker))
- return
-
-
-/obj/machinery/reagentgrinder/attackby(obj/item/O, mob/user, params)
-
- if(default_unfasten_wrench(user, O))
- return
-
- if (istype(O,/obj/item/weapon/reagent_containers/glass) || \
- istype(O,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \
- istype(O,/obj/item/weapon/reagent_containers/food/drinks/shaker))
-
- if (beaker)
- return 1
- else
- if(!user.drop_item())
- return 1
- src.beaker = O
- O.loc = src
- update_icon()
- src.updateUsrDialog()
- return 0
-
- if(is_type_in_list(O, dried_items))
- if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O
- if(!G.dry)
- user << "You must dry that first!"
- return 1
-
- if(holdingitems && holdingitems.len >= limit)
- usr << "The machine cannot hold anymore items."
- return 1
-
- //Fill machine with a bag!
- if(istype(O, /obj/item/weapon/storage/bag))
- var/obj/item/weapon/storage/bag/B = O
-
- for (var/obj/item/weapon/reagent_containers/food/snacks/grown/G in B.contents)
- B.remove_from_storage(G, src)
- holdingitems += G
- if(holdingitems && holdingitems.len >= limit) //Sanity checking so the blender doesn't overfill
- user << "You fill the All-In-One grinder to the brim."
- break
-
- if(!O.contents.len)
- user << "You empty the plant bag into the All-In-One grinder."
-
- src.updateUsrDialog()
- return 0
-
- if (!is_type_in_list(O, blend_items) && !is_type_in_list(O, juice_items))
- user << "Cannot refine into a reagent!"
- return 1
-
- user.unEquip(O)
- O.loc = src
- holdingitems += O
- src.updateUsrDialog()
- return 0
-
-/obj/machinery/reagentgrinder/attack_paw(mob/user)
- return src.attack_hand(user)
-
-/obj/machinery/reagentgrinder/attack_ai(mob/user)
- return 0
-
-/obj/machinery/reagentgrinder/attack_hand(mob/user)
- user.set_machine(src)
- interact(user)
-
-/obj/machinery/reagentgrinder/interact(mob/user) // The microwave Menu
- var/is_chamber_empty = 0
- var/is_beaker_ready = 0
- var/processing_chamber = ""
- var/beaker_contents = ""
- var/dat = ""
-
- if(!operating)
- for (var/obj/item/O in holdingitems)
- processing_chamber += "\A [O.name]
"
-
- if (!processing_chamber)
- is_chamber_empty = 1
- processing_chamber = "Nothing."
- if (!beaker)
- beaker_contents = "No beaker attached.
"
- else
- is_beaker_ready = 1
- beaker_contents = "The beaker contains:
"
- var/anything = 0
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- anything = 1
- beaker_contents += "[R.volume] - [R.name]
"
- if(!anything)
- beaker_contents += "Nothing
"
-
-
- dat = {"
- Processing chamber contains:
- [processing_chamber]
- [beaker_contents]
- "}
- if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
- dat += "Grind the reagents
"
- dat += "Juice the reagents
"
- if(holdingitems && holdingitems.len > 0)
- dat += "Eject the reagents
"
- if (beaker)
- dat += "Detach the beaker
"
- else
- dat += "Please wait..."
-
- var/datum/browser/popup = new(user, "reagentgrinder", "All-In-One Grinder")
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open(1)
- return
-
-/obj/machinery/reagentgrinder/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- if(operating)
- updateUsrDialog()
- return
- switch(href_list["action"])
- if ("grind")
- grind()
- if("juice")
- juice()
- if("eject")
- eject()
- if ("detach")
- detach()
-
-/obj/machinery/reagentgrinder/proc/detach()
-
- if (usr.stat != 0)
- return
- if (!beaker)
- return
- beaker.loc = src.loc
- beaker = null
- update_icon()
- updateUsrDialog()
-
-/obj/machinery/reagentgrinder/proc/eject()
-
- if (usr.stat != 0)
- return
- if (holdingitems && holdingitems.len == 0)
- return
-
- for(var/obj/item/O in holdingitems)
- O.loc = src.loc
- holdingitems -= O
- holdingitems = list()
- updateUsrDialog()
-
-/obj/machinery/reagentgrinder/proc/is_allowed(obj/item/weapon/reagent_containers/O)
- for (var/i in blend_items)
- if(istype(O, i))
- return 1
- return 0
-
-/obj/machinery/reagentgrinder/proc/get_allowed_by_id(obj/item/O)
- for (var/i in blend_items)
- if (istype(O, i))
- return blend_items[i]
-
-/obj/machinery/reagentgrinder/proc/get_allowed_snack_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
- for(var/i in blend_items)
- if(istype(O, i))
- return blend_items[i]
-
-/obj/machinery/reagentgrinder/proc/get_allowed_juice_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
- for(var/i in juice_items)
- if(istype(O, i))
- return juice_items[i]
-
-/obj/machinery/reagentgrinder/proc/get_grownweapon_amount(obj/item/weapon/grown/O)
- if (!istype(O))
- return 5
- else if (O.potency == -1)
- return 5
- else
- return round(O.potency)
-
-/obj/machinery/reagentgrinder/proc/get_juice_amount(obj/item/weapon/reagent_containers/food/snacks/grown/O)
- if (!istype(O))
- return 5
- else if (O.potency == -1)
- return 5
- else
- return round(5*sqrt(O.potency))
-
-/obj/machinery/reagentgrinder/proc/remove_object(obj/item/O)
- holdingitems -= O
- qdel(O)
-
-/obj/machinery/reagentgrinder/proc/juice()
- power_change()
- if(stat & (NOPOWER|BROKEN))
- return
- if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
- return
- playsound(src.loc, 'sound/machines/juicer.ogg', 20, 1)
- var/offset = prob(50) ? -2 : 2
- animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
- operating = 1
- updateUsrDialog()
- spawn(50)
- pixel_x = initial(pixel_x) //return to its spot after shaking
- operating = 0
- updateUsrDialog()
-
- //Snacks
- for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
-
- var/allowed = get_allowed_juice_by_id(O)
- if(isnull(allowed))
- break
-
- for (var/r_id in allowed)
-
- var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
- var/amount = get_juice_amount(O)
-
- beaker.reagents.add_reagent(r_id, min(amount, space))
-
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
-
- remove_object(O)
-
-/obj/machinery/reagentgrinder/proc/grind()
-
- power_change()
- if(stat & (NOPOWER|BROKEN))
- return
- if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
- return
- playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
- var/offset = prob(50) ? -2 : 2
- animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
- operating = 1
- updateUsrDialog()
- spawn(60)
- pixel_x = initial(pixel_x) //return to its spot after shaking
- operating = 0
- updateUsrDialog()
-
- //Snacks and Plants
- for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
-
- var/allowed = get_allowed_snack_by_id(O)
- if(isnull(allowed))
- break
-
- for (var/r_id in allowed)
-
- var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
- var/amount = allowed[r_id]
- if(amount <= 0)
- if(amount == 0)
- if (O.reagents != null && O.reagents.has_reagent("nutriment"))
- beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("nutriment"), space))
- O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
- else
- if (O.reagents != null && O.reagents.has_reagent("nutriment"))
- beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("nutriment")*abs(amount)), space))
- O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
-
- else
- O.reagents.trans_id_to(beaker, r_id, min(amount, space))
-
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
-
- if(O.reagents.reagent_list.len == 0)
- remove_object(O)
-
- //Sheets
- for (var/obj/item/stack/sheet/O in holdingitems)
- var/allowed = get_allowed_by_id(O)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
- for(var/i = 1; i <= round(O.amount, 1); i++)
- for (var/r_id in allowed)
- var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
- var/amount = allowed[r_id]
- beaker.reagents.add_reagent(r_id,min(amount, space))
- if (space < amount)
- break
- if (i == round(O.amount, 1))
- remove_object(O)
- break
- //Plants
- for (var/obj/item/weapon/grown/O in holdingitems)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
- var/allowed = get_allowed_by_id(O)
- for (var/r_id in allowed)
- var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
- var/amount = allowed[r_id]
- if (amount == 0)
- if (O.reagents != null && O.reagents.has_reagent(r_id))
- beaker.reagents.add_reagent(r_id,min(O.reagents.get_reagent_amount(r_id), space))
- else
- beaker.reagents.add_reagent(r_id,min(amount, space))
-
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
- remove_object(O)
-
-
- //Crayons
- //With some input from aranclanos, now 30% less shoddily copypasta
- for (var/obj/item/toy/crayon/O in holdingitems)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
- var/allowed = get_allowed_by_id(O)
- for (var/r_id in allowed)
- var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
- var/amount = allowed[r_id]
- beaker.reagents.add_reagent(r_id,min(amount, space))
- if (space < amount)
- break
- remove_object(O)
-
- //Everything else - Transfers reagents from it into beaker
- for (var/obj/item/weapon/reagent_containers/O in holdingitems)
- if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
- break
- var/amount = O.reagents.total_volume
- O.reagents.trans_to(beaker, amount)
- if(!O.reagents.total_volume)
- remove_object(O)
-////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////
-/obj/machinery/chem_heater
- name = "chemical heater"
- density = 1
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "mixer0b"
- use_power = 1
- idle_power_usage = 40
- var/obj/item/weapon/reagent_containers/beaker = null
- var/desired_temp = 300
- var/heater_coefficient = 0.10
- var/on = FALSE
-
-/obj/machinery/chem_heater/New()
- ..()
- component_parts = list()
- component_parts += new /obj/item/weapon/circuitboard/chem_heater(null)
- component_parts += new /obj/item/weapon/stock_parts/micro_laser(null)
- component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
- RefreshParts()
-
-/obj/machinery/chem_heater/RefreshParts()
- heater_coefficient = 0.10
- for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
- heater_coefficient *= M.rating
-
-/obj/machinery/chem_heater/process()
- ..()
- if(stat & NOPOWER)
- return
- if(on)
- if(beaker)
- if(beaker.reagents.chem_temp > desired_temp)
- beaker.reagents.chem_temp += min(-1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient)
- if(beaker.reagents.chem_temp < desired_temp)
- beaker.reagents.chem_temp += max(1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient)
- beaker.reagents.chem_temp = round(beaker.reagents.chem_temp) //stops stuff like 456.12312312302
-
- beaker.reagents.handle_reactions()
-
-/obj/machinery/chem_heater/proc/eject_beaker()
- if(beaker)
- beaker.loc = get_turf(src)
- beaker.reagents.handle_reactions()
- beaker = null
- icon_state = "mixer0b"
-
-/obj/machinery/chem_heater/power_change()
- if(powered())
- stat &= ~NOPOWER
- else
- spawn(rand(0, 15))
- stat |= NOPOWER
-
-/obj/machinery/chem_heater/attackby(obj/item/I, mob/user, params)
- if(isrobot(user))
- return
-
- if(istype(I, /obj/item/weapon/reagent_containers/glass))
- if(beaker)
- user << "A beaker is already loaded into the machine!"
- return
-
- if(user.drop_item())
- beaker = I
- I.loc = src
- user << "You add the beaker to the machine."
- icon_state = "mixer1b"
-
- if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I))
- return
-
- if(exchange_parts(user, I))
- return
-
- if(panel_open)
- if(istype(I, /obj/item/weapon/crowbar))
- eject_beaker()
- default_deconstruction_crowbar(I)
- return 1
-
-/obj/machinery/chem_heater/attack_hand(mob/user)
- if (!user)
- return
- interact(user)
-
-/obj/machinery/chem_heater/Topic(href, href_list)
- if(..())
- return
-
- if(href_list["toggle_on"])
- on = !on
-
- if(href_list["adjust_temperature"])
- var/val = href_list["adjust_temperature"]
- if(isnum(val))
- desired_temp = Clamp(desired_temp+val, 0, 1000)
- else if(val == "input")
- desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000)
- else
- return
-
- if(href_list["eject_beaker"])
- eject_beaker()
-
- add_fingerprint(usr)
-
-/obj/machinery/chem_heater/interact(mob/user)
- if(stat & BROKEN)
- return
- ui_interact(user)
-
-/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
- ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
- if (!ui)
- ui = new(user, src, ui_key, "chem_heater.tmpl", name, 350, 400)
- ui.open()
-
-/obj/machinery/chem_heater/get_ui_data()
- var/data = list()
- data["targetTemp"] = desired_temp
- data["isActive"] = on
- data["isBeakerLoaded"] = beaker ? 1 : 0
-
- data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null
- data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
- data["beakerMaxVolume"] = beaker ? beaker.volume : null
-
- //copy-pasted from chem dispenser
- var beakerContents[0]
- if(beaker)
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
- data["beakerContents"] = beakerContents
- return data
-
-///////////////////////////////////////////////////////////////////////////
-
-/obj/machinery/chem_dispenser/drinks
- name = "soda dispenser"
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "soda_dispenser"
- energy = 100
- max_energy = 100
- amount = 30
- recharge_delay = 5
- dispensable_reagents = list("water","ice","coffee","cream","tea","icetea","cola","spacemountainwind","dr_gibb","space_up","tonic","sodawater","lemon_lime","sugar","orangejuice","limejuice","tomatojuice")
-
-/obj/machinery/chem_dispenser/drinks/attackby(obj/item/O, mob/user)
-
- if(default_unfasten_wrench(user, O))
- return
-
- if (istype(O,/obj/item/weapon/reagent_containers/glass) || \
- istype(O,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \
- istype(O,/obj/item/weapon/reagent_containers/food/drinks/shaker))
-
- if (beaker)
- return 1
- else
- if(!user.drop_item())
- return 1
- src.beaker = O
- O.loc = src
- update_icon()
- src.updateUsrDialog()
- return 0
-
-
-
-/obj/machinery/chem_dispenser/drinks/beer
- name = "booze dispenser"
- anchored = 1
- icon = 'icons/obj/chemical.dmi'
- icon_state = "booze_dispenser"
- dispensable_reagents = list("lemon_lime","sugar","orangejuice","limejuice","sodawater","tonic","beer","kahlua","whiskey","wine","vodka","gin","rum","tequila","vermouth","cognac","ale")
diff --git a/code/modules/reagents/Chemistry-Colours.dm b/code/modules/reagents/chemistry/colors.dm
similarity index 91%
rename from code/modules/reagents/Chemistry-Colours.dm
rename to code/modules/reagents/chemistry/colors.dm
index 7251c9c25e1..05ee15c2936 100644
--- a/code/modules/reagents/Chemistry-Colours.dm
+++ b/code/modules/reagents/chemistry/colors.dm
@@ -1,24 +1,21 @@
-
-/proc/mix_color_from_reagents(list/reagent_list)
- if(!istype(reagent_list))
- return
-
- var/color
- var/vol_counter = 0
- var/vol_temp
-
- for(var/datum/reagent/R in reagent_list)
- vol_temp = R.volume
- vol_counter += vol_temp
-
- if(!color)
- color = R.color
-
- else if (length(color) >= length(R.color))
- color = BlendRGB(color, R.color, vol_temp/vol_counter)
- else
- color = BlendRGB(R.color, color, vol_temp/vol_counter)
-
- return color
-
-
+/proc/mix_color_from_reagents(list/reagent_list)
+ if(!istype(reagent_list))
+ return
+
+ var/color
+ var/vol_counter = 0
+ var/vol_temp
+
+ for(var/datum/reagent/R in reagent_list)
+ vol_temp = R.volume
+ vol_counter += vol_temp
+
+ if(!color)
+ color = R.color
+
+ else if (length(color) >= length(R.color))
+ color = BlendRGB(color, R.color, vol_temp/vol_counter)
+ else
+ color = BlendRGB(R.color, color, vol_temp/vol_counter)
+
+ return color
\ No newline at end of file
diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/chemistry/holder.dm
similarity index 96%
rename from code/modules/reagents/Chemistry-Holder.dm
rename to code/modules/reagents/chemistry/holder.dm
index 495f438bd7a..e26987c1e31 100644
--- a/code/modules/reagents/Chemistry-Holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -1,603 +1,613 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
-
-var/const/TOUCH = 1 //splashing
-var/const/INGEST = 2 //ingestion
-var/const/VAPOR = 3 //foam, spray, blob attack
-var/const/PATCH = 4 //patches
-var/const/INJECT = 5 //injection
-
-///////////////////////////////////////////////////////////////////////////////////
-
-/datum/reagents
- var/list/datum/reagent/reagent_list = new/list()
- var/total_volume = 0
- var/maximum_volume = 100
- var/atom/my_atom = null
- var/chem_temp = 150
- var/last_tick = 1
- var/addiction_tick = 1
- var/list/datum/reagent/addiction_list = new/list()
-
-/datum/reagents/New(maximum=100)
- maximum_volume = maximum
- SSobj.processing |= src
- //I dislike having these here but map-objects are initialised before world/New() is called. >_>
- if(!chemical_reagents_list)
- //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
- var/paths = subtypesof(/datum/reagent)
- chemical_reagents_list = list()
- for(var/path in paths)
- var/datum/reagent/D = new path()
- chemical_reagents_list[D.id] = D
- if(!chemical_reactions_list)
- //Chemical Reactions - Initialises all /datum/chemical_reaction into a list
- // It is filtered into multiple lists within a list.
- // For example:
- // chemical_reaction_list["plasma"] is a list of all reactions relating to plasma
-
- var/paths = subtypesof(/datum/chemical_reaction)
- chemical_reactions_list = list()
-
- for(var/path in paths)
-
- var/datum/chemical_reaction/D = new path()
- var/list/reaction_ids = list()
-
- if(D.required_reagents && D.required_reagents.len)
- for(var/reaction in D.required_reagents)
- reaction_ids += reaction
-
- // Create filters based on each reagent id in the required reagents list
- for(var/id in reaction_ids)
- if(!chemical_reactions_list[id])
- chemical_reactions_list[id] = list()
- chemical_reactions_list[id] += D
- break // Don't bother adding ourselves to other reagent ids, it is redundant.
-
-/datum/reagents/Destroy()
- . = ..()
- SSobj.processing.Remove(src)
- for(var/datum/reagent/R in reagent_list)
- qdel(R)
- reagent_list.Cut()
- reagent_list = null
- if(my_atom && my_atom.reagents == src)
- my_atom.reagents = null
-
-/datum/reagents/proc/remove_any(amount=1)
- var/total_transfered = 0
- var/current_list_element = 1
-
- current_list_element = rand(1,reagent_list.len)
-
- while(total_transfered != amount)
- if(total_transfered >= amount) break
- if(total_volume <= 0 || !reagent_list.len) break
-
- if(current_list_element > reagent_list.len) current_list_element = 1
- var/datum/reagent/current_reagent = reagent_list[current_list_element]
-
- src.remove_reagent(current_reagent.id, 1)
-
- current_list_element++
- total_transfered++
- src.update_total()
-
- handle_reactions()
- return total_transfered
-
-/datum/reagents/proc/get_master_reagent_name()
- var/the_name = null
- var/the_volume = 0
- for(var/datum/reagent/A in reagent_list)
- if(A.volume > the_volume)
- the_volume = A.volume
- the_name = A.name
-
- return the_name
-
-/datum/reagents/proc/get_master_reagent_id()
- var/the_id = null
- var/the_volume = 0
- for(var/datum/reagent/A in reagent_list)
- if(A.volume > the_volume)
- the_volume = A.volume
- the_id = A.id
-
- return the_id
-
-/datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred.
- if (!target )
- return
- var/datum/reagents/R
- if(istype(target,/datum/reagents/))
- R = target
- else
- if (!target.reagents || src.total_volume<=0)
- return
- R = target.reagents
- amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
- var/part = amount / src.total_volume
- var/trans_data = null
- for (var/datum/reagent/current_reagent in src.reagent_list)
- if (current_reagent.id == "blood" && ishuman(target))
- var/mob/living/carbon/human/H = target
- H.inject_blood(my_atom, amount)
- continue
- var/current_reagent_transfer = current_reagent.volume * part
- if(preserve_data)
- trans_data = copy_data(current_reagent)
- R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, src.chem_temp, no_react = 1) //we only handle reaction after every reagent has been transfered.
- src.remove_reagent(current_reagent.id, current_reagent_transfer)
-
- src.update_total()
- R.update_total()
- R.handle_reactions()
- src.handle_reactions()
- return amount
-
-/datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1)
- if(!target)
- return
- if(!target.reagents || src.total_volume<=0)
- return
- var/datum/reagents/R = target.reagents
- amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
- var/part = amount / src.total_volume
- var/trans_data = null
- for (var/datum/reagent/current_reagent in src.reagent_list)
- var/current_reagent_transfer = current_reagent.volume * part
- if(preserve_data)
- trans_data = current_reagent.data
- R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data)
-
- src.update_total()
- R.update_total()
- R.handle_reactions()
- src.handle_reactions()
- return amount
-
-/datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N
- if (!target)
- return
- if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent))
- return
-
- var/datum/reagents/R = target.reagents
- if(src.get_reagent_amount(reagent) R.maximum_volume) return 0
-
- current_list_element = rand(1,reagent_list.len) //Eh, bandaid fix.
-
- while(total_transfered != amount)
- if(total_transfered >= amount) break //Better safe than sorry.
- if(total_volume <= 0 || !reagent_list.len) break
- if(R.total_volume >= R.maximum_volume) break
-
- if(current_list_element > reagent_list.len) current_list_element = 1
- var/datum/reagent/current_reagent = reagent_list[current_list_element]
- if(preserve_data)
- trans_data = current_reagent.data
- R.add_reagent(current_reagent.id, (1 * multiplier), trans_data)
- src.remove_reagent(current_reagent.id, 1)
-
- current_list_element++
- total_transfered++
- src.update_total()
- R.update_total()
- R.handle_reactions()
- handle_reactions()
-
- return total_transfered
-*/
-
-/datum/reagents/proc/metabolize(mob/M, can_overdose = 0)
- if(M)
- chem_temp = M.bodytemperature
- handle_reactions()
-
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if(!R.holder)
- continue
- if(!M)
- M = R.holder.my_atom
- if(M && R)
- if(M.reagent_check(R) != 1)
- if(can_overdose)
- if(R.overdose_threshold)
- if(R.volume >= R.overdose_threshold && !R.overdosed)
- R.overdosed = 1
- R.overdose_start(M)
- if(R.addiction_threshold)
- if(R.volume >= R.addiction_threshold && !is_type_in_list(R, addiction_list))
- var/datum/reagent/new_reagent = new R.type()
- addiction_list.Add(new_reagent)
- if(R.overdosed)
- R.overdose_process(M)
- if(is_type_in_list(R,addiction_list))
- for(var/datum/reagent/addicted_reagent in addiction_list)
- if(istype(R, addicted_reagent))
- addicted_reagent.addiction_stage = -15 // you're satisfied for a good while.
- R.on_mob_life(M)
-
- if(can_overdose)
- if(addiction_tick == 6)
- addiction_tick = 1
- for(var/A in addiction_list)
- var/datum/reagent/R = A
- if(M && R)
- if(R.addiction_stage <= 0)
- R.addiction_stage++
- if(R.addiction_stage > 0 && R.addiction_stage <= 10)
- R.addiction_act_stage1(M)
- R.addiction_stage++
- if(R.addiction_stage > 10 && R.addiction_stage <= 20)
- R.addiction_act_stage2(M)
- R.addiction_stage++
- if(R.addiction_stage > 20 && R.addiction_stage <= 30)
- R.addiction_act_stage3(M)
- R.addiction_stage++
- if(R.addiction_stage > 30 && R.addiction_stage <= 40)
- R.addiction_act_stage4(M)
- R.addiction_stage++
- if(R.addiction_stage > 40)
- M << "You feel like you've gotten over your need for [R.name]."
- addiction_list.Remove(R)
- addiction_tick++
- update_total()
-
-/datum/reagents/process()
- for(var/datum/reagent/R in reagent_list)
- R.on_tick()
- return
-
-/datum/reagents/proc/conditional_update_move(atom/A, Running = 0)
- for(var/datum/reagent/R in reagent_list)
- R.on_move (A, Running)
- update_total()
-
-/datum/reagents/proc/conditional_update(atom/A)
- for(var/datum/reagent/R in reagent_list)
- R.on_update (A)
- update_total()
-
-/datum/reagents/proc/handle_reactions()
- if(my_atom.flags & NOREACT) return //Yup, no reactions here. No siree.
-
- var/reaction_occured = 0
- do
- reaction_occured = 0
- for(var/datum/reagent/R in reagent_list) // Usually a small list
- for(var/reaction in chemical_reactions_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id
-
- if(!reaction)
- continue
-
- var/datum/chemical_reaction/C = reaction
- var/total_required_reagents = C.required_reagents.len
- var/total_matching_reagents = 0
- var/total_required_catalysts = C.required_catalysts.len
- var/total_matching_catalysts= 0
- var/matching_container = 0
- var/matching_other = 0
- var/list/multipliers = new/list()
- var/required_temp = C.required_temp
-
- for(var/B in C.required_reagents)
- if(!has_reagent(B, C.required_reagents[B])) break
- total_matching_reagents++
- multipliers += round(get_reagent_amount(B) / C.required_reagents[B])
- for(var/B in C.required_catalysts)
- if(!has_reagent(B, C.required_catalysts[B])) break
- total_matching_catalysts++
-
- if(!C.required_container)
- matching_container = 1
-
- else
- if(my_atom.type == C.required_container)
- matching_container = 1
- if (isliving(my_atom)) //Makes it so certain chemical reactions don't occur in mobs
- if (C.mob_react)
- return
- if(!C.required_other)
- matching_other = 1
-
- else if(istype(my_atom, /obj/item/slime_extract))
- var/obj/item/slime_extract/M = my_atom
-
- if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this
- matching_other = 1
-
- if(required_temp == 0)
- required_temp = chem_temp
-
-
- if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other && chem_temp >= required_temp)
- var/multiplier = min(multipliers)
- for(var/B in C.required_reagents)
- remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1)
-
- var/created_volume = C.result_amount*multiplier
- if(C.result)
- feedback_add_details("chemical_reaction","[C.result]|[C.result_amount*multiplier]")
- multiplier = max(multiplier, 1) //this shouldnt happen ...
- add_reagent(C.result, C.result_amount*multiplier, null, chem_temp)
-
- var/list/seen = viewers(4, get_turf(my_atom))
-
- if(!istype(my_atom, /mob)) // No bubbling mobs
- playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 80, 1)
- for(var/mob/M in seen)
- M << "\icon[my_atom] [C.mix_message]"
-
- if(istype(my_atom, /obj/item/slime_extract))
- var/obj/item/slime_extract/ME2 = my_atom
- ME2.Uses--
- if(ME2.Uses <= 0) // give the notification that the slime core is dead
- for(var/mob/M in seen)
- M << "\icon[my_atom] \The [my_atom]'s power is consumed in the reaction."
- ME2.name = "used slime extract"
- ME2.desc = "This extract has been used up."
-
- C.on_reaction(src, created_volume)
- reaction_occured = 1
- break
-
- while(reaction_occured)
- update_total()
- return 0
-
-/datum/reagents/proc/isolate_reagent(reagent)
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if (R.id != reagent)
- del_reagent(R.id)
- update_total()
-
-/datum/reagents/proc/del_reagent(reagent)
- for(var/datum/reagent/R in reagent_list)
- if (R.id == reagent)
- if(istype(my_atom, /mob/living))
- var/mob/living/M = my_atom
- R.on_mob_delete(M)
- qdel(R)
- reagent_list -= R
- update_total()
- my_atom.on_reagent_change()
- check_ignoreslow(my_atom)
- check_gofast(my_atom)
- check_goreallyfast(my_atom)
- return 1
-
-/datum/reagents/proc/check_ignoreslow(mob/M)
- if(istype(M, /mob))
- if(M.reagents.has_reagent("morphine")||M.reagents.has_reagent("ephedrine"))
- return 1
- else
- M.status_flags &= ~IGNORESLOWDOWN
-
-/datum/reagents/proc/check_gofast(mob/M)
- if(istype(M, /mob))
- if(M.reagents.has_reagent("unholywater")||M.reagents.has_reagent("nuka_cola")||M.reagents.has_reagent("stimulants"))
- return 1
- else
- M.status_flags &= ~GOTTAGOFAST
-
-/datum/reagents/proc/check_goreallyfast(mob/M)
- if(istype(M, /mob))
- if(M.reagents.has_reagent("methamphetamine"))
- return 1
- else
- M.status_flags &= ~GOTTAGOREALLYFAST
-
-/datum/reagents/proc/update_total()
- total_volume = 0
- for(var/datum/reagent/R in reagent_list)
- if(R.volume < 0.1)
- del_reagent(R.id)
- else
- total_volume += R.volume
-
- return 0
-
-/datum/reagents/proc/clear_reagents()
- for(var/datum/reagent/R in reagent_list)
- del_reagent(R.id)
- return 0
-
-/datum/reagents/proc/reaction(atom/A, method=TOUCH, volume_modifier=1,show_message=1)
- if(isliving(A))
- var/mob/living/L = A
- var/touch_protection = 0
- if(method == VAPOR)
- touch_protection = L.get_permeability_protection()
- for(var/datum/reagent/R in reagent_list)
- R.reaction_mob(L, method, R.volume*volume_modifier, show_message, touch_protection)
- else if(isturf(A))
- for(var/datum/reagent/R in reagent_list)
- R.reaction_turf(A, R.volume*volume_modifier, show_message)
- else if(isobj(A))
- for(var/datum/reagent/R in reagent_list)
- R.reaction_obj(A, R.volume*volume_modifier, show_message)
-
-/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0)
- if(!isnum(amount) || !amount)
- return 1
- update_total()
- if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen.
- chem_temp = round(((amount * reagtemp) + (total_volume * chem_temp)) / (total_volume + amount)) //equalize with new chems
-
- for(var/A in reagent_list)
-
- var/datum/reagent/R = A
- if (R.id == reagent)
- R.volume += amount
- update_total()
- my_atom.on_reagent_change()
- R.on_merge(data)
- if(!no_react)
- handle_reactions()
- return 0
-
- var/datum/reagent/D = chemical_reagents_list[reagent]
- if(D)
-
- var/datum/reagent/R = new D.type(data)
- reagent_list += R
- R.holder = src
- R.volume = amount
- if(data)
- R.data = data
- R.on_new(data)
-
- update_total()
- my_atom.on_reagent_change()
- if(!no_react)
- handle_reactions()
- return 0
- else
- WARNING("[my_atom] attempted to add a reagent called ' [reagent] ' which doesn't exist. ([usr])")
-
- if(!no_react)
- handle_reactions()
-
- return 1
-
-/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15)
- for(var/r_id in list_reagents)
- var/amt = list_reagents[r_id]
- add_reagent(r_id, amt, data)
-
-/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to
-
- if(!isnum(amount)) return 1
-
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if (R.id == reagent)
- R.volume -= amount
- update_total()
- if(!safety)//So it does not handle reactions when it need not to
- handle_reactions()
- my_atom.on_reagent_change()
- return 0
-
- return 1
-
-/datum/reagents/proc/has_reagent(reagent, amount = -1)
-
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if (R.id == reagent)
- if(!amount) return R
- else
- if(R.volume >= amount) return R
- else return 0
-
- return 0
-
-/datum/reagents/proc/get_reagent_amount(reagent)
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if (R.id == reagent)
- return R.volume
-
- return 0
-
-/datum/reagents/proc/get_reagents()
- var/res = ""
- for(var/datum/reagent/A in reagent_list)
- if (res != "") res += ","
- res += A.name
-
- return res
-
-/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included.
- if(!isnum(amount)) return 1
-
- var/has_removed_reagent = 0
-
- for(var/datum/reagent/R in reagent_list)
- var/matches = 0
- // Switch between how we check the reagent type
- if(strict)
- if(R.type == reagent_type)
- matches = 1
- else
- if(istype(R, reagent_type))
- matches = 1
- // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type.
- if(matches)
- // Have our other proc handle removement
- has_removed_reagent = remove_reagent(R.id, amount, safety)
-
- return has_removed_reagent
-
- //two helper functions to preserve data across reactions (needed for xenoarch)
-/datum/reagents/proc/get_data(reagent_id)
- for(var/datum/reagent/D in reagent_list)
- if(D.id == reagent_id)
- //world << "proffering a data-carrying reagent ([reagent_id])"
- return D.data
-
-/datum/reagents/proc/set_data(reagent_id, new_data)
- for(var/datum/reagent/D in reagent_list)
- if(D.id == reagent_id)
- //world << "reagent data set ([reagent_id])"
- D.data = new_data
-
-/datum/reagents/proc/copy_data(datum/reagent/current_reagent)
- if (!current_reagent || !current_reagent.data) return null
- if (!istype(current_reagent.data, /list)) return current_reagent.data
-
- var/list/trans_data = current_reagent.data.Copy()
-
- // We do this so that introducing a virus to a blood sample
- // doesn't automagically infect all other blood samples from
- // the same donor.
- //
- // Technically we should probably copy all data lists, but
- // that could possibly eat up a lot of memory needlessly
- // if most data lists are read-only.
- if (trans_data["viruses"])
- var/list/v = trans_data["viruses"]
- trans_data["viruses"] = v.Copy()
-
- return trans_data
-
-
-///////////////////////////////////////////////////////////////////////////////////
-
-
-// Convenience proc to create a reagents holder for an atom
-// Max vol is maximum volume of holder
-/atom/proc/create_reagents(max_vol)
- if(reagents)
- qdel(reagents)
- reagents = new/datum/reagents(max_vol)
- reagents.my_atom = src
+//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
+
+var/const/TOUCH = 1 //splashing
+var/const/INGEST = 2 //ingestion
+var/const/VAPOR = 3 //foam, spray, blob attack
+var/const/PATCH = 4 //patches
+var/const/INJECT = 5 //injection
+
+///////////////////////////////////////////////////////////////////////////////////
+
+/datum/reagents
+ var/list/datum/reagent/reagent_list = new/list()
+ var/total_volume = 0
+ var/maximum_volume = 100
+ var/atom/my_atom = null
+ var/chem_temp = 150
+ var/last_tick = 1
+ var/addiction_tick = 1
+ var/list/datum/reagent/addiction_list = new/list()
+
+/datum/reagents/New(maximum=100)
+ maximum_volume = maximum
+ SSobj.processing |= src
+ //I dislike having these here but map-objects are initialised before world/New() is called. >_>
+ if(!chemical_reagents_list)
+ //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
+ var/paths = subtypesof(/datum/reagent)
+ chemical_reagents_list = list()
+ for(var/path in paths)
+ var/datum/reagent/D = new path()
+ chemical_reagents_list[D.id] = D
+ if(!chemical_reactions_list)
+ //Chemical Reactions - Initialises all /datum/chemical_reaction into a list
+ // It is filtered into multiple lists within a list.
+ // For example:
+ // chemical_reaction_list["plasma"] is a list of all reactions relating to plasma
+
+ var/paths = subtypesof(/datum/chemical_reaction)
+ chemical_reactions_list = list()
+
+ for(var/path in paths)
+
+ var/datum/chemical_reaction/D = new path()
+ var/list/reaction_ids = list()
+
+ if(D.required_reagents && D.required_reagents.len)
+ for(var/reaction in D.required_reagents)
+ reaction_ids += reaction
+
+ // Create filters based on each reagent id in the required reagents list
+ for(var/id in reaction_ids)
+ if(!chemical_reactions_list[id])
+ chemical_reactions_list[id] = list()
+ chemical_reactions_list[id] += D
+ break // Don't bother adding ourselves to other reagent ids, it is redundant.
+
+/datum/reagents/Destroy()
+ . = ..()
+ SSobj.processing.Remove(src)
+ for(var/datum/reagent/R in reagent_list)
+ qdel(R)
+ reagent_list.Cut()
+ reagent_list = null
+ if(my_atom && my_atom.reagents == src)
+ my_atom.reagents = null
+
+/datum/reagents/proc/remove_any(amount = 1)
+ var/total_transfered = 0
+ var/current_list_element = 1
+
+ current_list_element = rand(1,reagent_list.len)
+
+ while(total_transfered != amount)
+ if(total_transfered >= amount) break
+ if(total_volume <= 0 || !reagent_list.len) break
+
+ if(current_list_element > reagent_list.len) current_list_element = 1
+ var/datum/reagent/current_reagent = reagent_list[current_list_element]
+
+ src.remove_reagent(current_reagent.id, 1)
+
+ current_list_element++
+ total_transfered++
+ src.update_total()
+
+ handle_reactions()
+ return total_transfered
+
+/datum/reagents/proc/remove_all(amount = 1)
+ var/part = amount / src.total_volume
+ for (var/datum/reagent/current_reagent in src.reagent_list)
+ var/current_reagent_transfer = current_reagent.volume * part
+ src.remove_reagent(current_reagent.id, current_reagent_transfer)
+
+ src.update_total()
+ src.handle_reactions()
+ return amount
+
+/datum/reagents/proc/get_master_reagent_name()
+ var/the_name = null
+ var/the_volume = 0
+ for(var/datum/reagent/A in reagent_list)
+ if(A.volume > the_volume)
+ the_volume = A.volume
+ the_name = A.name
+
+ return the_name
+
+/datum/reagents/proc/get_master_reagent_id()
+ var/the_id = null
+ var/the_volume = 0
+ for(var/datum/reagent/A in reagent_list)
+ if(A.volume > the_volume)
+ the_volume = A.volume
+ the_id = A.id
+
+ return the_id
+
+/datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred.
+ if (!target )
+ return
+ var/datum/reagents/R
+ if(istype(target,/datum/reagents/))
+ R = target
+ else
+ if (!target.reagents || src.total_volume<=0)
+ return
+ R = target.reagents
+ amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
+ var/part = amount / src.total_volume
+ var/trans_data = null
+ for (var/datum/reagent/current_reagent in src.reagent_list)
+ if (current_reagent.id == "blood" && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.inject_blood(my_atom, amount)
+ continue
+ var/current_reagent_transfer = current_reagent.volume * part
+ if(preserve_data)
+ trans_data = copy_data(current_reagent)
+ R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, src.chem_temp, no_react = 1) //we only handle reaction after every reagent has been transfered.
+ src.remove_reagent(current_reagent.id, current_reagent_transfer)
+
+ src.update_total()
+ R.update_total()
+ R.handle_reactions()
+ src.handle_reactions()
+ return amount
+
+/datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1)
+ if(!target)
+ return
+ if(!target.reagents || src.total_volume<=0)
+ return
+ var/datum/reagents/R = target.reagents
+ amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
+ var/part = amount / src.total_volume
+ var/trans_data = null
+ for (var/datum/reagent/current_reagent in src.reagent_list)
+ var/current_reagent_transfer = current_reagent.volume * part
+ if(preserve_data)
+ trans_data = current_reagent.data
+ R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data)
+
+ src.update_total()
+ R.update_total()
+ R.handle_reactions()
+ src.handle_reactions()
+ return amount
+
+/datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N
+ if (!target)
+ return
+ if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent))
+ return
+
+ var/datum/reagents/R = target.reagents
+ if(src.get_reagent_amount(reagent) R.maximum_volume) return 0
+
+ current_list_element = rand(1,reagent_list.len) //Eh, bandaid fix.
+
+ while(total_transfered != amount)
+ if(total_transfered >= amount) break //Better safe than sorry.
+ if(total_volume <= 0 || !reagent_list.len) break
+ if(R.total_volume >= R.maximum_volume) break
+
+ if(current_list_element > reagent_list.len) current_list_element = 1
+ var/datum/reagent/current_reagent = reagent_list[current_list_element]
+ if(preserve_data)
+ trans_data = current_reagent.data
+ R.add_reagent(current_reagent.id, (1 * multiplier), trans_data)
+ src.remove_reagent(current_reagent.id, 1)
+
+ current_list_element++
+ total_transfered++
+ src.update_total()
+ R.update_total()
+ R.handle_reactions()
+ handle_reactions()
+
+ return total_transfered
+*/
+
+/datum/reagents/proc/metabolize(mob/M, can_overdose = 0)
+ if(M)
+ chem_temp = M.bodytemperature
+ handle_reactions()
+
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if(!R.holder)
+ continue
+ if(!M)
+ M = R.holder.my_atom
+ if(M && R)
+ if(M.reagent_check(R) != 1)
+ if(can_overdose)
+ if(R.overdose_threshold)
+ if(R.volume >= R.overdose_threshold && !R.overdosed)
+ R.overdosed = 1
+ R.overdose_start(M)
+ if(R.addiction_threshold)
+ if(R.volume >= R.addiction_threshold && !is_type_in_list(R, addiction_list))
+ var/datum/reagent/new_reagent = new R.type()
+ addiction_list.Add(new_reagent)
+ if(R.overdosed)
+ R.overdose_process(M)
+ if(is_type_in_list(R,addiction_list))
+ for(var/datum/reagent/addicted_reagent in addiction_list)
+ if(istype(R, addicted_reagent))
+ addicted_reagent.addiction_stage = -15 // you're satisfied for a good while.
+ R.on_mob_life(M)
+
+ if(can_overdose)
+ if(addiction_tick == 6)
+ addiction_tick = 1
+ for(var/A in addiction_list)
+ var/datum/reagent/R = A
+ if(M && R)
+ if(R.addiction_stage <= 0)
+ R.addiction_stage++
+ if(R.addiction_stage > 0 && R.addiction_stage <= 10)
+ R.addiction_act_stage1(M)
+ R.addiction_stage++
+ if(R.addiction_stage > 10 && R.addiction_stage <= 20)
+ R.addiction_act_stage2(M)
+ R.addiction_stage++
+ if(R.addiction_stage > 20 && R.addiction_stage <= 30)
+ R.addiction_act_stage3(M)
+ R.addiction_stage++
+ if(R.addiction_stage > 30 && R.addiction_stage <= 40)
+ R.addiction_act_stage4(M)
+ R.addiction_stage++
+ if(R.addiction_stage > 40)
+ M << "You feel like you've gotten over your need for [R.name]."
+ addiction_list.Remove(R)
+ addiction_tick++
+ update_total()
+
+/datum/reagents/process()
+ for(var/datum/reagent/R in reagent_list)
+ R.on_tick()
+ return
+
+/datum/reagents/proc/conditional_update_move(atom/A, Running = 0)
+ for(var/datum/reagent/R in reagent_list)
+ R.on_move (A, Running)
+ update_total()
+
+/datum/reagents/proc/conditional_update(atom/A)
+ for(var/datum/reagent/R in reagent_list)
+ R.on_update (A)
+ update_total()
+
+/datum/reagents/proc/handle_reactions()
+ if(my_atom.flags & NOREACT) return //Yup, no reactions here. No siree.
+
+ var/reaction_occured = 0
+ do
+ reaction_occured = 0
+ for(var/datum/reagent/R in reagent_list) // Usually a small list
+ for(var/reaction in chemical_reactions_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id
+
+ if(!reaction)
+ continue
+
+ var/datum/chemical_reaction/C = reaction
+ var/total_required_reagents = C.required_reagents.len
+ var/total_matching_reagents = 0
+ var/total_required_catalysts = C.required_catalysts.len
+ var/total_matching_catalysts= 0
+ var/matching_container = 0
+ var/matching_other = 0
+ var/list/multipliers = new/list()
+ var/required_temp = C.required_temp
+
+ for(var/B in C.required_reagents)
+ if(!has_reagent(B, C.required_reagents[B])) break
+ total_matching_reagents++
+ multipliers += round(get_reagent_amount(B) / C.required_reagents[B])
+ for(var/B in C.required_catalysts)
+ if(!has_reagent(B, C.required_catalysts[B])) break
+ total_matching_catalysts++
+
+ if(!C.required_container)
+ matching_container = 1
+
+ else
+ if(my_atom.type == C.required_container)
+ matching_container = 1
+ if (isliving(my_atom)) //Makes it so certain chemical reactions don't occur in mobs
+ if (C.mob_react)
+ return
+ if(!C.required_other)
+ matching_other = 1
+
+ else if(istype(my_atom, /obj/item/slime_extract))
+ var/obj/item/slime_extract/M = my_atom
+
+ if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this
+ matching_other = 1
+
+ if(required_temp == 0)
+ required_temp = chem_temp
+
+
+ if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other && chem_temp >= required_temp)
+ var/multiplier = min(multipliers)
+ for(var/B in C.required_reagents)
+ remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1)
+
+ var/created_volume = C.result_amount*multiplier
+ if(C.result)
+ feedback_add_details("chemical_reaction","[C.result]|[C.result_amount*multiplier]")
+ multiplier = max(multiplier, 1) //this shouldnt happen ...
+ add_reagent(C.result, C.result_amount*multiplier, null, chem_temp)
+
+ var/list/seen = viewers(4, get_turf(my_atom))
+
+ if(!istype(my_atom, /mob)) // No bubbling mobs
+ playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 80, 1)
+ for(var/mob/M in seen)
+ M << "\icon[my_atom] [C.mix_message]"
+
+ if(istype(my_atom, /obj/item/slime_extract))
+ var/obj/item/slime_extract/ME2 = my_atom
+ ME2.Uses--
+ if(ME2.Uses <= 0) // give the notification that the slime core is dead
+ for(var/mob/M in seen)
+ M << "\icon[my_atom] \The [my_atom]'s power is consumed in the reaction."
+ ME2.name = "used slime extract"
+ ME2.desc = "This extract has been used up."
+
+ C.on_reaction(src, created_volume)
+ reaction_occured = 1
+ break
+
+ while(reaction_occured)
+ update_total()
+ return 0
+
+/datum/reagents/proc/isolate_reagent(reagent)
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if (R.id != reagent)
+ del_reagent(R.id)
+ update_total()
+
+/datum/reagents/proc/del_reagent(reagent)
+ for(var/datum/reagent/R in reagent_list)
+ if (R.id == reagent)
+ if(istype(my_atom, /mob/living))
+ var/mob/living/M = my_atom
+ R.on_mob_delete(M)
+ qdel(R)
+ reagent_list -= R
+ update_total()
+ my_atom.on_reagent_change()
+ check_ignoreslow(my_atom)
+ check_gofast(my_atom)
+ check_goreallyfast(my_atom)
+ return 1
+
+/datum/reagents/proc/check_ignoreslow(mob/M)
+ if(istype(M, /mob))
+ if(M.reagents.has_reagent("morphine")||M.reagents.has_reagent("ephedrine"))
+ return 1
+ else
+ M.status_flags &= ~IGNORESLOWDOWN
+
+/datum/reagents/proc/check_gofast(mob/M)
+ if(istype(M, /mob))
+ if(M.reagents.has_reagent("unholywater")||M.reagents.has_reagent("nuka_cola")||M.reagents.has_reagent("stimulants"))
+ return 1
+ else
+ M.status_flags &= ~GOTTAGOFAST
+
+/datum/reagents/proc/check_goreallyfast(mob/M)
+ if(istype(M, /mob))
+ if(M.reagents.has_reagent("methamphetamine"))
+ return 1
+ else
+ M.status_flags &= ~GOTTAGOREALLYFAST
+
+/datum/reagents/proc/update_total()
+ total_volume = 0
+ for(var/datum/reagent/R in reagent_list)
+ if(R.volume < 0.1)
+ del_reagent(R.id)
+ else
+ total_volume += R.volume
+
+ return 0
+
+/datum/reagents/proc/clear_reagents()
+ for(var/datum/reagent/R in reagent_list)
+ del_reagent(R.id)
+ return 0
+
+/datum/reagents/proc/reaction(atom/A, method=TOUCH, volume_modifier=1,show_message=1)
+ if(isliving(A))
+ var/mob/living/L = A
+ var/touch_protection = 0
+ if(method == VAPOR)
+ touch_protection = L.get_permeability_protection()
+ for(var/datum/reagent/R in reagent_list)
+ R.reaction_mob(L, method, R.volume*volume_modifier, show_message, touch_protection)
+ else if(isturf(A))
+ for(var/datum/reagent/R in reagent_list)
+ R.reaction_turf(A, R.volume*volume_modifier, show_message)
+ else if(isobj(A))
+ for(var/datum/reagent/R in reagent_list)
+ R.reaction_obj(A, R.volume*volume_modifier, show_message)
+
+/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0)
+ if(!isnum(amount) || !amount)
+ return 1
+ update_total()
+ if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen.
+ chem_temp = round(((amount * reagtemp) + (total_volume * chem_temp)) / (total_volume + amount)) //equalize with new chems
+
+ for(var/A in reagent_list)
+
+ var/datum/reagent/R = A
+ if (R.id == reagent)
+ R.volume += amount
+ update_total()
+ my_atom.on_reagent_change()
+ R.on_merge(data)
+ if(!no_react)
+ handle_reactions()
+ return 0
+
+ var/datum/reagent/D = chemical_reagents_list[reagent]
+ if(D)
+
+ var/datum/reagent/R = new D.type(data)
+ reagent_list += R
+ R.holder = src
+ R.volume = amount
+ if(data)
+ R.data = data
+ R.on_new(data)
+
+ update_total()
+ my_atom.on_reagent_change()
+ if(!no_react)
+ handle_reactions()
+ return 0
+ else
+ WARNING("[my_atom] attempted to add a reagent called ' [reagent] ' which doesn't exist. ([usr])")
+
+ if(!no_react)
+ handle_reactions()
+
+ return 1
+
+/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15)
+ for(var/r_id in list_reagents)
+ var/amt = list_reagents[r_id]
+ add_reagent(r_id, amt, data)
+
+/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to
+
+ if(!isnum(amount)) return 1
+
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if (R.id == reagent)
+ R.volume -= amount
+ update_total()
+ if(!safety)//So it does not handle reactions when it need not to
+ handle_reactions()
+ my_atom.on_reagent_change()
+ return 0
+
+ return 1
+
+/datum/reagents/proc/has_reagent(reagent, amount = -1)
+
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if (R.id == reagent)
+ if(!amount) return R
+ else
+ if(R.volume >= amount) return R
+ else return 0
+
+ return 0
+
+/datum/reagents/proc/get_reagent_amount(reagent)
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if (R.id == reagent)
+ return R.volume
+
+ return 0
+
+/datum/reagents/proc/get_reagents()
+ var/res = ""
+ for(var/datum/reagent/A in reagent_list)
+ if (res != "") res += ","
+ res += A.name
+
+ return res
+
+/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included.
+ if(!isnum(amount)) return 1
+
+ var/has_removed_reagent = 0
+
+ for(var/datum/reagent/R in reagent_list)
+ var/matches = 0
+ // Switch between how we check the reagent type
+ if(strict)
+ if(R.type == reagent_type)
+ matches = 1
+ else
+ if(istype(R, reagent_type))
+ matches = 1
+ // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type.
+ if(matches)
+ // Have our other proc handle removement
+ has_removed_reagent = remove_reagent(R.id, amount, safety)
+
+ return has_removed_reagent
+
+ //two helper functions to preserve data across reactions (needed for xenoarch)
+/datum/reagents/proc/get_data(reagent_id)
+ for(var/datum/reagent/D in reagent_list)
+ if(D.id == reagent_id)
+ //world << "proffering a data-carrying reagent ([reagent_id])"
+ return D.data
+
+/datum/reagents/proc/set_data(reagent_id, new_data)
+ for(var/datum/reagent/D in reagent_list)
+ if(D.id == reagent_id)
+ //world << "reagent data set ([reagent_id])"
+ D.data = new_data
+
+/datum/reagents/proc/copy_data(datum/reagent/current_reagent)
+ if (!current_reagent || !current_reagent.data) return null
+ if (!istype(current_reagent.data, /list)) return current_reagent.data
+
+ var/list/trans_data = current_reagent.data.Copy()
+
+ // We do this so that introducing a virus to a blood sample
+ // doesn't automagically infect all other blood samples from
+ // the same donor.
+ //
+ // Technically we should probably copy all data lists, but
+ // that could possibly eat up a lot of memory needlessly
+ // if most data lists are read-only.
+ if (trans_data["viruses"])
+ var/list/v = trans_data["viruses"]
+ trans_data["viruses"] = v.Copy()
+
+ return trans_data
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+
+// Convenience proc to create a reagents holder for an atom
+// Max vol is maximum volume of holder
+/atom/proc/create_reagents(max_vol)
+ if(reagents)
+ qdel(reagents)
+ reagents = new/datum/reagents(max_vol)
+ reagents.my_atom = src
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
new file mode 100644
index 00000000000..9ab66de80ae
--- /dev/null
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -0,0 +1,356 @@
+/obj/machinery/chem_dispenser
+ name = "chem dispenser"
+ desc = "Creates and dispenses chemicals."
+ density = 1
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "dispenser"
+ use_power = 1
+ idle_power_usage = 40
+ var/energy = 100
+ var/max_energy = 100
+ var/amount = 30
+ var/recharged = 0
+ var/recharge_delay = 5
+ var/image/icon_beaker = null
+ var/obj/item/weapon/reagent_containers/beaker = null
+ var/list/dispensable_reagents = list(
+ "hydrogen",
+ "lithium",
+ "carbon",
+ "nitrogen",
+ "oxygen",
+ "fluorine",
+ "sodium",
+ "aluminium",
+ "silicon",
+ "phosphorus",
+ "sulfur",
+ "chlorine",
+ "potassium",
+ "iron",
+ "copper",
+ "mercury",
+ "radium",
+ "water",
+ "ethanol",
+ "sugar",
+ "sacid",
+ "welding_fuel",
+ "silver",
+ "iodine",
+ "bromine",
+ "stable_plasma"
+ )
+
+/obj/machinery/chem_dispenser/New()
+ ..()
+ recharge()
+ dispensable_reagents = sortList(dispensable_reagents)
+
+/obj/machinery/chem_dispenser/power_change()
+ if(powered())
+ stat &= ~NOPOWER
+ else
+ spawn(rand(0, 15))
+ stat |= NOPOWER
+
+/obj/machinery/chem_dispenser/process()
+
+ if(recharged < 0)
+ recharge()
+ recharged = recharge_delay
+ else
+ recharged -= 1
+
+/obj/machinery/chem_dispenser/proc/recharge()
+ if(stat & (BROKEN|NOPOWER)) return
+ var/addenergy = 1
+ var/oldenergy = energy
+ energy = min(energy + addenergy, max_energy)
+ if(energy != oldenergy)
+ use_power(2500)
+
+/obj/machinery/chem_dispenser/ex_act(severity, target)
+ if(severity < 3)
+ ..()
+
+/obj/machinery/chem_dispenser/blob_act()
+ if(prob(50))
+ qdel(src)
+
+/obj/machinery/chem_dispenser/interact(mob/user)
+ if(stat & BROKEN)
+ return
+ ui_interact(user)
+
+/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
+ ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "chem_dispenser.tmpl", name, 500, 650)
+ ui.open()
+
+/obj/machinery/chem_dispenser/get_ui_data()
+ var/data = list()
+ data["amount"] = amount
+ data["energy"] = energy
+ data["maxEnergy"] = max_energy
+ data["isBeakerLoaded"] = beaker ? 1 : 0
+
+ var beakerContents[0]
+ var beakerCurrentVolume = 0
+ if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
+ beakerCurrentVolume += R.volume
+ data["beakerContents"] = beakerContents
+
+ if (beaker)
+ data["beakerCurrentVolume"] = beakerCurrentVolume
+ data["beakerMaxVolume"] = beaker.volume
+ data["beakerTransferAmounts"] = beaker.possible_transfer_amounts
+ else
+ data["beakerCurrentVolume"] = null
+ data["beakerMaxVolume"] = null
+ data["beakerTransferAmounts"] = null
+
+ var chemicals[0]
+ for(var/re in dispensable_reagents)
+ var/datum/reagent/temp = chemical_reagents_list[re]
+ if(temp)
+ chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("dispense" = temp.id)))) // list in a list because Byond merges the first list...
+ data["chemicals"] = chemicals
+ return data
+
+/obj/machinery/chem_dispenser/Topic(href, href_list)
+ if(..())
+ return
+
+ if(href_list["amount"])
+ amount = round(text2num(href_list["amount"]), 5) // round to nearest 5
+ if (amount < 0) // Since the user can actually type the commands himself, some sanity checking
+ amount = 0
+ if (amount > 100)
+ amount = 100
+
+ if(href_list["dispense"])
+ if(beaker && dispensable_reagents.Find(href_list["dispense"]))
+ var/datum/reagents/R = beaker.reagents
+ var/space = R.maximum_volume - R.total_volume
+
+ R.add_reagent(href_list["dispense"], min(amount, energy * 10, space))
+ energy = max(energy - min(amount, energy * 10, space) / 10, 0)
+
+ if(href_list["remove"])
+ if(beaker)
+ var/amount = text2num(href_list["remove"])
+ if(isnum(amount) && (amount > 0) && (amount in beaker.possible_transfer_amounts))
+ beaker.reagents.remove_all(amount)
+
+ if(href_list["ejectBeaker"])
+ if(beaker)
+ beaker.loc = loc
+ beaker = null
+ overlays.Cut()
+
+ add_fingerprint(usr)
+
+/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
+ if(default_unfasten_wrench(user, I))
+ return
+
+ if(isrobot(user))
+ return
+
+ var/obj/item/weapon/reagent_containers/B = I // Get a beaker from it?
+ if(!istype(B))
+ return // Not a beaker?
+
+ if(beaker)
+ user << "A beaker is already loaded into the machine!"
+ return
+
+ if(!user.drop_item()) // Can't let go?
+ return
+
+ beaker = B
+ beaker.loc = src
+ user << "You add the beaker to the machine."
+
+ if(!icon_beaker)
+ icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
+ icon_beaker.pixel_x = rand(-10,5)
+ overlays += icon_beaker
+
+/obj/machinery/chem_dispenser/attack_hand(mob/user)
+ if (!user)
+ return
+ interact(user)
+
+
+/obj/machinery/chem_dispenser/constructable
+ name = "portable chem dispenser"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "minidispenser"
+ energy = 10
+ max_energy = 10
+ amount = 5
+ recharge_delay = 30
+ dispensable_reagents = list()
+ var/list/dispensable_reagent_tiers = list(
+ list(
+ "hydrogen",
+ "oxygen",
+ "silicon",
+ "phosphorus",
+ "sulfur",
+ "carbon",
+ "nitrogen",
+ "water"
+ ),
+ list(
+ "lithium",
+ "sugar",
+ "sacid",
+ "copper",
+ "mercury",
+ "sodium",
+ "iodine",
+ "bromine"
+ ),
+ list(
+ "ethanol",
+ "chlorine",
+ "potassium",
+ "aluminium",
+ "radium",
+ "fluorine",
+ "iron",
+ "welding_fuel",
+ "silver",
+ "stable_plasma"
+ ),
+ list(
+ "oil",
+ "ash",
+ "acetone",
+ "saltpetre",
+ "ammonia",
+ "diethylamine"
+ )
+ )
+
+/obj/machinery/chem_dispenser/constructable/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/chem_dispenser(null)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(null)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
+ component_parts += new /obj/item/weapon/stock_parts/cell/high(null)
+ RefreshParts()
+
+/obj/machinery/chem_dispenser/constructable/RefreshParts()
+ var/time = 0
+ var/temp_energy = 0
+ var/i
+ for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
+ temp_energy += M.rating
+ temp_energy--
+ max_energy = temp_energy * 5 //max energy = (bin1.rating + bin2.rating - 1) * 5, 5 on lowest 25 on highest
+ for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
+ time += C.rating
+ for(var/obj/item/weapon/stock_parts/cell/P in component_parts)
+ time += round(P.maxcharge, 10000) / 10000
+ recharge_delay /= time/2 //delay between recharges, double the usual time on lowest 50% less than usual on highest
+ for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
+ for(i=1, i<=M.rating, i++)
+ dispensable_reagents |= dispensable_reagent_tiers[i]
+ dispensable_reagents = sortList(dispensable_reagents)
+
+/obj/machinery/chem_dispenser/constructable/attackby(var/obj/item/I, var/mob/user, params)
+ ..()
+ if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I))
+ return
+
+ if(exchange_parts(user, I))
+ return
+
+ if(panel_open)
+ if(istype(I, /obj/item/weapon/crowbar))
+ if(beaker)
+ beaker.loc = loc
+ beaker = null
+ default_deconstruction_crowbar(I)
+ return 1
+
+/obj/machinery/chem_dispenser/drinks
+ name = "soda dispenser"
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "soda_dispenser"
+ amount = 10
+ dispensable_reagents = list(
+ "water",
+ "ice",
+ "coffee",
+ "cream",
+ "tea",
+ "icetea",
+ "cola",
+ "spacemountainwind",
+ "dr_gibb",
+ "space_up",
+ "tonic",
+ "sodawater",
+ "lemon_lime",
+ "sugar",
+ "orangejuice",
+ "limejuice",
+ "tomatojuice"
+ )
+
+/obj/machinery/chem_dispenser/drinks/attackby(obj/item/I, mob/user)
+ if(default_unfasten_wrench(user, I))
+ return
+
+ if (istype(I, /obj/item/weapon/reagent_containers/glass) || \
+ istype(I, /obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \
+ istype(I, /obj/item/weapon/reagent_containers/food/drinks/shaker))
+
+ if (beaker)
+ return 1
+ else
+ if(!user.drop_item())
+ return 1
+ src.beaker = I
+ beaker.loc = src
+ update_icon()
+ return
+
+/obj/machinery/chem_dispenser/drinks/beer
+ name = "booze dispenser"
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "booze_dispenser"
+ dispensable_reagents = list(
+ "lemon_lime",
+ "sugar",
+ "orangejuice",
+ "limejuice",
+ "sodawater",
+ "tonic",
+ "beer",
+ "kahlua",
+ "whiskey",
+ "wine",
+ "vodka",
+ "gin",
+ "rum",
+ "tequila",
+ "vermouth",
+ "cognac",
+ "ale"
+ )
diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm
new file mode 100644
index 00000000000..317bb67a55f
--- /dev/null
+++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm
@@ -0,0 +1,134 @@
+/obj/machinery/chem_heater
+ name = "chemical heater"
+ density = 1
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "mixer0b"
+ use_power = 1
+ idle_power_usage = 40
+ var/obj/item/weapon/reagent_containers/beaker = null
+ var/desired_temp = 300
+ var/heater_coefficient = 0.10
+ var/on = FALSE
+
+/obj/machinery/chem_heater/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/chem_heater(null)
+ component_parts += new /obj/item/weapon/stock_parts/micro_laser(null)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
+ RefreshParts()
+
+/obj/machinery/chem_heater/RefreshParts()
+ heater_coefficient = 0.10
+ for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
+ heater_coefficient *= M.rating
+
+/obj/machinery/chem_heater/process()
+ ..()
+ if(stat & NOPOWER)
+ return
+ if(on)
+ if(beaker)
+ if(beaker.reagents.chem_temp > desired_temp)
+ beaker.reagents.chem_temp += min(-1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient)
+ if(beaker.reagents.chem_temp < desired_temp)
+ beaker.reagents.chem_temp += max(1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient)
+ beaker.reagents.chem_temp = round(beaker.reagents.chem_temp) //stops stuff like 456.12312312302
+
+ beaker.reagents.handle_reactions()
+
+/obj/machinery/chem_heater/power_change()
+ if(powered())
+ stat &= ~NOPOWER
+ else
+ spawn(rand(0, 15))
+ stat |= NOPOWER
+
+/obj/machinery/chem_heater/attackby(obj/item/I, mob/user, params)
+ if(isrobot(user))
+ return
+
+ if(istype(I, /obj/item/weapon/reagent_containers/glass))
+ if(beaker)
+ user << "A beaker is already loaded into the machine!"
+ return
+
+ if(user.drop_item())
+ beaker = I
+ I.loc = src
+ user << "You add the beaker to the machine."
+ icon_state = "mixer1b"
+
+ if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I))
+ return
+
+ if(exchange_parts(user, I))
+ return
+
+ if(panel_open)
+ if(istype(I, /obj/item/weapon/crowbar))
+ eject_beaker()
+ default_deconstruction_crowbar(I)
+ return 1
+
+/obj/machinery/chem_heater/attack_hand(mob/user)
+ if (!user)
+ return
+ interact(user)
+
+/obj/machinery/chem_heater/Topic(href, href_list)
+ if(..())
+ return
+
+ if(href_list["toggle_on"])
+ on = !on
+
+ if(href_list["adjust_temperature"])
+ var/val = href_list["adjust_temperature"]
+ if(isnum(val))
+ desired_temp = Clamp(desired_temp+val, 0, 1000)
+ else if(val == "input")
+ desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000)
+ else
+ return
+
+ if(href_list["eject_beaker"])
+ eject_beaker()
+
+ add_fingerprint(usr)
+
+/obj/machinery/chem_heater/interact(mob/user)
+ if(stat & BROKEN)
+ return
+ ui_interact(user)
+
+/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
+ ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "chem_heater.tmpl", name, 350, 400)
+ ui.open()
+
+/obj/machinery/chem_heater/get_ui_data()
+ var/data = list()
+ data["targetTemp"] = desired_temp
+ data["isActive"] = on
+ data["isBeakerLoaded"] = beaker ? 1 : 0
+
+ data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null
+ data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
+ data["beakerMaxVolume"] = beaker ? beaker.volume : null
+
+ var beakerContents[0]
+ if(beaker)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
+ data["beakerContents"] = beakerContents
+ return data
+
+/obj/machinery/chem_heater/proc/eject_beaker()
+ if(beaker)
+ beaker.loc = get_turf(src)
+ beaker.reagents.handle_reactions()
+ beaker = null
+ icon_state = "mixer0b"
\ No newline at end of file
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
new file mode 100644
index 00000000000..1030f9dc2a6
--- /dev/null
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -0,0 +1,391 @@
+/obj/machinery/chem_master
+ name = "ChemMaster 3000"
+ desc = "Used to bottle chemicals to create pills."
+ density = 1
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "mixer0"
+ use_power = 1
+ idle_power_usage = 20
+ var/obj/item/weapon/reagent_containers/glass/beaker = null
+ var/obj/item/weapon/storage/pill_bottle/bottle = null
+ var/mode = 0
+ var/condi = 0
+ var/useramount = 30 // Last used amount
+
+/obj/machinery/chem_master/New()
+ create_reagents(100)
+ overlays += "waitlight"
+
+/obj/machinery/chem_master/ex_act(severity, target)
+ if(severity < 3)
+ ..()
+
+/obj/machinery/chem_master/blob_act()
+ if (prob(50))
+ qdel(src)
+
+/obj/machinery/chem_master/power_change()
+ if(powered())
+ stat &= ~NOPOWER
+ else
+ spawn(rand(0, 15))
+ stat |= NOPOWER
+
+/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params)
+ if(default_unfasten_wrench(user, I))
+ return
+
+ if(istype(I, /obj/item/weapon/reagent_containers/glass))
+ if(isrobot(user))
+ return
+ if(beaker)
+ user << "A beaker is already loaded into the machine!"
+ return
+ if(!user.drop_item())
+ return
+
+ beaker = I
+ beaker.loc = src
+ user << "You add the beaker to the machine."
+ src.updateUsrDialog()
+ icon_state = "mixer1"
+
+ else if(!condi && istype(I, /obj/item/weapon/storage/pill_bottle))
+ if(bottle)
+ user << "A pill bottle is already loaded into the machine!"
+ return
+ if(!user.drop_item())
+ return
+
+ bottle = I
+ bottle.loc = src
+ user << "You add the pill bottle into the dispenser slot."
+ src.updateUsrDialog()
+
+ return
+
+/obj/machinery/chem_master/Topic(href, href_list)
+ if(..())
+ return
+
+ usr.set_machine(src)
+
+ if(href_list["ejectp"])
+ if(bottle)
+ bottle.loc = src.loc
+ bottle = null
+
+ else if(href_list["close"])
+ usr << browse(null, "window=chem_master")
+ usr.unset_machine()
+ return
+
+ else if(href_list["toggle"])
+ mode = !mode
+
+ else if(href_list["createbottle"])
+ var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
+ if(!name)
+ return
+ var/obj/item/weapon/reagent_containers/P
+ if(condi)
+ P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc)
+ else
+ P = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
+ P.pixel_x = rand(-7, 7) //random position
+ P.pixel_y = rand(-7, 7)
+ P.name = trim("[name] bottle")
+ reagents.trans_to(P, P.volume)
+
+ if(beaker)
+
+ if(href_list["analyze"])
+ if(locate(href_list["reagent"]))
+ var/datum/reagent/R = locate(href_list["reagent"])
+ if(R)
+ var/dat = ""
+ dat += "[condi ? "Condiment" : "Chemical"] information:
"
+ dat += "Name: [initial(R.name)]
"
+ dat += "State: "
+ if(initial(R.reagent_state) == 1)
+ dat += "Solid"
+ else if(initial(R.reagent_state) == 2)
+ dat += "Liquid"
+ else if(initial(R.reagent_state) == 3)
+ dat += "Gas"
+ else
+ dat += "Unknown"
+ dat += "
"
+ dat += "Color: [initial(R.color)]
"
+ dat += "Description: [initial(R.description)]
"
+ var/const/P = 3 //The number of seconds between life ticks
+ var/T = initial(R.metabolization_rate) * (60 / P)
+ dat += "Metabolization Rate: [T]u/minute
"
+ dat += "Overdose Threshold: [initial(R.overdose_threshold) ? "[initial(R.overdose_threshold)]u" : "none"]
"
+ dat += "Addiction Threshold: [initial(R.addiction_threshold) ? "[initial(R.addiction_threshold)]u" : "none"]
"
+ dat += "
Back"
+ var/datum/browser/popup = new(usr, "chem_master", name)
+ popup.set_content(dat)
+ popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
+ popup.open(1)
+ return
+
+ else if(href_list["main"]) // Used to exit the analyze screen.
+ attack_hand(usr)
+ return
+
+ else if(href_list["add"])
+ if(href_list["amount"])
+ var/id = href_list["add"]
+ var/amount = text2num(href_list["amount"])
+ if (amount > 0)
+ beaker.reagents.trans_id_to(src, id, amount)
+
+ else if(href_list["addcustom"])
+ var/id = href_list["addcustom"]
+ var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
+ if(!amt_temp)
+ return
+ useramount = amt_temp
+ src.Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
+
+ else if(href_list["remove"])
+ if(href_list["amount"])
+ var/id = href_list["remove"]
+ var/amount = text2num(href_list["amount"])
+ if (amount > 0)
+ if(mode)
+ reagents.trans_id_to(beaker, id, amount)
+ else
+ reagents.remove_reagent(id, amount)
+
+ else if(href_list["removecustom"])
+ var/id = href_list["removecustom"]
+ var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
+ if(!amt_temp)
+ return
+ useramount = amt_temp
+ src.Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
+
+ else if(href_list["eject"])
+ if(beaker)
+ beaker.loc = src.loc
+ beaker = null
+ reagents.clear_reagents()
+ icon_state = "mixer0"
+
+ else if(href_list["createpill"]) //Also used for condiment packs.
+ if(reagents.total_volume == 0) return
+ if(!condi)
+ var/amount = 1
+ var/vol_each = min(reagents.total_volume, 50)
+ if(text2num(href_list["many"]))
+ amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many pills?", amount) as num|null), 0), 10)
+ if(!amount)
+ return
+ vol_each = min(reagents.total_volume / amount, 50)
+ var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
+ if(!name || !reagents.total_volume)
+ return
+ var/obj/item/weapon/reagent_containers/pill/P
+
+ for(var/i = 0; i < amount; i++)
+ if(bottle && bottle.contents.len < bottle.storage_slots)
+ P = new/obj/item/weapon/reagent_containers/pill(bottle)
+ else
+ P = new/obj/item/weapon/reagent_containers/pill(src.loc)
+ P.name = trim("[name] pill")
+ P.pixel_x = rand(-7, 7) //random position
+ P.pixel_y = rand(-7, 7)
+ reagents.trans_to(P,vol_each)
+ else
+ var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
+ if(!name || !reagents.total_volume)
+ return
+ var/obj/item/weapon/reagent_containers/food/condiment/pack/P = new/obj/item/weapon/reagent_containers/food/condiment/pack(src.loc)
+
+ P.originalname = name
+ P.name = trim("[name] pack")
+ P.desc = "A small condiment pack. The label says it contains [name]."
+ reagents.trans_to(P,10)
+
+ else if(href_list["createpatch"])
+ if(reagents.total_volume == 0) return
+ var/amount = 1
+ var/vol_each = min(reagents.total_volume, 50)
+ if(text2num(href_list["many"]))
+ amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many patches?", amount) as num|null), 0), 10)
+ if(!amount)
+ return
+ vol_each = min(reagents.total_volume / amount, 50)
+ var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
+ if(!name || !reagents.total_volume)
+ return
+ var/obj/item/weapon/reagent_containers/pill/P
+
+ for(var/i = 0; i < amount; i++)
+ P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc)
+ P.name = trim("[name] patch")
+ P.pixel_x = rand(-7, 7) //random position
+ P.pixel_y = rand(-7, 7)
+ reagents.trans_to(P,vol_each)
+
+ src.updateUsrDialog()
+ return
+
+/obj/machinery/chem_master/attack_hand(mob/user)
+ if(stat & BROKEN)
+ return
+
+ user.set_machine(src)
+ var/dat = ""
+ if(beaker)
+ dat += "Beaker \[[beaker.reagents.total_volume]/[beaker.volume]\] Eject and Clear Buffer
"
+ else
+ dat = "Please insert beaker.
"
+
+ dat += "
Add to buffer:"
+ if(beaker)
+ if(beaker.reagents.total_volume)
+ for(var/datum/reagent/G in beaker.reagents.reagent_list)
+ dat += "- [G.name], [G.volume] Units - "
+ dat += "Analyze "
+ dat += "1 "
+ dat += "5 "
+ dat += "10 "
+ dat += "All "
+ dat += "Custom"
+ else
+ dat += "
- Beaker is empty."
+ else
+ dat += "
- No beaker."
+
+ dat += "
Transfer to [(!mode ? "disposal" : "beaker")]:"
+ if(reagents.total_volume)
+ for(var/datum/reagent/N in reagents.reagent_list)
+ dat += "- [N.name], [N.volume] Units - "
+ dat += "Analyze "
+ dat += "1 "
+ dat += "5 "
+ dat += "10 "
+ dat += "All "
+ dat += "Custom"
+ else
+ dat += "
- Buffer is empty."
+ dat += "
"
+
+ if(!condi)
+ if(bottle)
+ dat += "Pill Bottle \[[bottle.contents.len]/[bottle.storage_slots]\] Eject"
+ else
+ dat += "No pill bottle inserted."
+ else
+ dat += "
"
+
+ dat += ""
+ if(!condi)
+ if(beaker && reagents.total_volume)
+ dat += "- Create pill (50 units max)"
+ dat += "
- Create multiple pills
"
+ dat += " - Create patch (50 units max)"
+ dat += "
- Create multiple patches
"
+ else
+ dat += " - Create pill (50 units max)"
+ dat += "
- Create multiple pills
"
+ dat += " - Create patch (50 units max)"
+ dat += "
- Create multiple patches
"
+ else
+ if(beaker && reagents.total_volume)
+ dat += " - Create pack (10 units max)
"
+ else
+ dat += " - Create pack (10 units max)
"
+ dat += " - Create bottle ([condi ? "50" : "30"] units max)"
+ dat += "
"
+ dat += "
Close"
+ var/datum/browser/popup = new(user, "chem_master", name, 470, 500)
+ popup.set_content(dat)
+ popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
+ popup.open(1)
+ return
+
+/obj/machinery/chem_master/proc/isgoodnumber(num)
+ if(isnum(num))
+ if(num > 200)
+ num = 200
+ else if(num < 0)
+ num = 0
+ else
+ num = round(num)
+ return num
+ else
+ return 0
+
+
+/obj/machinery/chem_master/condimaster
+ name = "CondiMaster 3000"
+ desc = "Used to create condiments and other cooking supplies."
+ condi = 1
+
+/obj/machinery/chem_master/constructable
+ name = "ChemMaster 2999"
+ desc = "Used to seperate chemicals and distribute them in a variety of forms."
+
+/obj/machinery/chem_master/constructable/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/chem_master(null)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
+ component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)
+ component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)
+
+/obj/machinery/chem_master/constructable/attackby(obj/item/B, mob/user, params)
+
+ if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", B))
+ if(beaker)
+ beaker.loc = src.loc
+ beaker = null
+ reagents.clear_reagents()
+ if(bottle)
+ bottle.loc = src.loc
+ bottle = null
+ return
+
+ if(exchange_parts(user, B))
+ return
+
+ if(panel_open)
+ if(istype(B, /obj/item/weapon/crowbar))
+ default_deconstruction_crowbar(B)
+ return 1
+ else
+ user << "You can't use the [src.name] while it's panel is opened!"
+ return 1
+
+ if(istype(B, /obj/item/weapon/reagent_containers/glass))
+ if(beaker)
+ user << "A beaker is already loaded into the machine!"
+ return
+ if(!user.drop_item())
+ return
+
+ beaker = B
+ beaker.loc = src
+ user << "You add the beaker to the machine."
+ src.updateUsrDialog()
+ icon_state = "mixer1"
+
+ else if(!condi && istype(B, /obj/item/weapon/storage/pill_bottle))
+ if(bottle)
+ user << "A pill bottle is already loaded into the machine!"
+ return
+ if(!user.drop_item())
+ return
+
+ src.bottle = B
+ B.loc = src
+ user << "You add the pill bottle into the dispenser slot."
+ src.updateUsrDialog()
+
+ return
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
new file mode 100644
index 00000000000..bb068c421c1
--- /dev/null
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -0,0 +1,295 @@
+/obj/machinery/computer/pandemic
+ name = "PanD.E.M.I.C 2200"
+ desc = "Used to work with viruses."
+ density = 1
+ anchored = 1
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "mixer0"
+ circuit = /obj/item/weapon/circuitboard/pandemic
+ use_power = 1
+ idle_power_usage = 20
+ var/temp_html = ""
+ var/wait = null
+ var/obj/item/weapon/reagent_containers/glass/beaker = null
+
+/obj/machinery/computer/pandemic/New()
+ ..()
+ update_icon()
+
+/obj/machinery/computer/pandemic/set_broken()
+ icon_state = (beaker ? "mixer1_b" : "mixer0_b")
+ overlays.Cut()
+ stat |= BROKEN
+
+/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index)
+ if(beaker && beaker.reagents)
+ if(beaker.reagents.reagent_list.len)
+ var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
+ if(BL)
+ if(BL.data && BL.data["viruses"])
+ var/list/viruses = BL.data["viruses"]
+ return viruses[index]
+ return null
+
+/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index)
+ if(beaker && beaker.reagents)
+ if(beaker.reagents.reagent_list.len)
+ var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
+ if(BL)
+ if(BL.data && BL.data["resistances"])
+ var/list/resistances = BL.data["resistances"]
+ return resistances[index]
+ return null
+
+/obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(index)
+ var/datum/disease/D = GetVirusByIndex(index)
+ if(D)
+ return D.GetDiseaseID()
+ return null
+
+/obj/machinery/computer/pandemic/proc/replicator_cooldown(waittime)
+ wait = 1
+ update_icon()
+ spawn(waittime)
+ src.wait = null
+ update_icon()
+ playsound(src.loc, 'sound/machines/ping.ogg', 30, 1)
+
+/obj/machinery/computer/pandemic/update_icon()
+ if(stat & BROKEN)
+ icon_state = (beaker ? "mixer1_b" : "mixer0_b")
+ return
+
+ icon_state = "mixer[(beaker)?"1":"0"][(powered()) ? "" : "_nopower"]"
+
+ if(wait)
+ overlays.Cut()
+ else
+ overlays += "waitlight"
+
+/obj/machinery/computer/pandemic/Topic(href, href_list)
+ if(..())
+ return
+
+ usr.set_machine(src)
+ if(!beaker) return
+
+ if (href_list["create_vaccine"])
+ if(!src.wait)
+ var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
+ if(B)
+ B.pixel_x = rand(-3, 3)
+ B.pixel_y = rand(-3, 3)
+ var/path = GetResistancesByIndex(text2num(href_list["create_vaccine"]))
+ var/vaccine_type = path
+ var/vaccine_name = "Unknown"
+
+ if(!ispath(vaccine_type))
+ if(archive_diseases[path])
+ var/datum/disease/D = archive_diseases[path]
+ if(D)
+ vaccine_name = D.name
+ vaccine_type = path
+ else if(vaccine_type)
+ var/datum/disease/D = new vaccine_type(0, null)
+ if(D)
+ vaccine_name = D.name
+
+ if(vaccine_type)
+
+ B.name = "[vaccine_name] vaccine bottle"
+ B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
+ replicator_cooldown(200)
+ else
+ src.temp_html = "The replicator is not ready yet."
+ src.updateUsrDialog()
+ return
+ else if (href_list["create_virus_culture"])
+ if(!wait)
+ var/type = GetVirusTypeByIndex(text2num(href_list["create_virus_culture"]))//the path is received as string - converting
+ var/datum/disease/D = null
+ if(!ispath(type))
+ D = GetVirusByIndex(text2num(href_list["create_virus_culture"]))
+ var/datum/disease/advance/A = archive_diseases[D.GetDiseaseID()]
+ if(A)
+ D = new A.type(0, A)
+ else if(type)
+ if(type in diseases) // Make sure this is a disease
+ D = new type(0, null)
+ if(!D)
+ return
+ var/name = stripped_input(usr,"Name:","Name the culture",D.name,MAX_NAME_LEN)
+ if(name == null || wait)
+ return
+ var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
+ B.icon_state = "bottle3"
+ B.pixel_x = rand(-3, 3)
+ B.pixel_y = rand(-3, 3)
+ replicator_cooldown(50)
+ var/list/data = list("viruses"=list(D))
+ B.name = "[name] culture bottle"
+ B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium."
+ B.reagents.add_reagent("blood",20,data)
+ src.updateUsrDialog()
+ else
+ src.temp_html = "The replicator is not ready yet."
+ src.updateUsrDialog()
+ return
+ else if (href_list["empty_beaker"])
+ beaker.reagents.clear_reagents()
+ src.updateUsrDialog()
+ return
+ else if (href_list["eject"])
+ beaker:loc = src.loc
+ beaker = null
+ icon_state = "mixer0"
+ src.updateUsrDialog()
+ return
+ else if(href_list["clear"])
+ src.temp_html = ""
+ src.updateUsrDialog()
+ return
+ else if(href_list["name_disease"])
+ var/new_name = stripped_input(usr, "Name the Disease", "New Name", "", MAX_NAME_LEN)
+ if(!new_name)
+ return
+ if(..())
+ return
+ var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"]))
+ if(archive_diseases[id])
+ var/datum/disease/advance/A = archive_diseases[id]
+ A.AssignName(new_name)
+ for(var/datum/disease/advance/AD in SSdisease.processing)
+ AD.Refresh()
+ src.updateUsrDialog()
+
+
+ else
+ usr << browse(null, "window=pandemic")
+ src.updateUsrDialog()
+ return
+
+ src.add_fingerprint(usr)
+ return
+
+/obj/machinery/computer/pandemic/attack_hand(mob/user)
+ if(..())
+ return
+ user.set_machine(src)
+ var/dat = ""
+ if(src.temp_html)
+ dat = "[src.temp_html]
Main Menu"
+ else if(!beaker)
+ dat += "Please insert beaker.
"
+ dat += "Close"
+ else
+ var/datum/reagents/R = beaker.reagents
+ var/datum/reagent/blood/Blood = null
+ for(var/datum/reagent/blood/B in R.reagent_list)
+ if(B)
+ Blood = B
+ break
+ if(!R.total_volume||!R.reagent_list.len)
+ dat += "The beaker is empty
"
+ else if(!Blood)
+ dat += "No blood sample found in beaker."
+ else if(!Blood.data)
+ dat += "No blood data found in beaker."
+ else
+ dat += "Blood sample data:
"
+ dat += "Blood DNA: [(Blood.data["blood_DNA"]||"none")]
"
+ dat += "Blood Type: [(Blood.data["blood_type"]||"none")]
"
+
+
+ if(Blood.data["viruses"])
+ var/list/vir = Blood.data["viruses"]
+ if(vir.len)
+ var/i = 0
+ for(var/datum/disease/D in Blood.data["viruses"])
+ i++
+ if(!(D.visibility_flags & HIDDEN_PANDEMIC))
+
+ if(istype(D, /datum/disease/advance))
+
+ var/datum/disease/advance/A = D
+ D = archive_diseases[A.GetDiseaseID()]
+ if(D && D.name == "Unknown")
+ dat += "Name Disease
"
+
+ if(!D)
+ CRASH("We weren't able to get the advance disease from the archive.")
+
+ dat += "Disease Agent: [D?"[D.agent] - Create virus culture bottle":"none"]
"
+ dat += "Common name: [(D.name||"none")]
"
+ dat += "Description: [(D.desc||"none")]
"
+ dat += "Spread: [(D.spread_text||"none")]
"
+ dat += "Possible cure: [(D.cure_text||"none")]
"
+
+ if(istype(D, /datum/disease/advance))
+ var/datum/disease/advance/A = D
+ dat += "Symptoms: "
+ var/english_symptoms = list()
+ for(var/datum/symptom/S in A.symptoms)
+ english_symptoms += S.name
+ dat += english_list(english_symptoms)
+
+ else
+ dat += "No detectable virus in the sample."
+ else
+ dat += "No detectable virus in the sample."
+
+ dat += "
Contains antibodies to: "
+ if(Blood.data["resistances"])
+ var/list/res = Blood.data["resistances"]
+ if(res.len)
+ dat += ""
+ var/i = 0
+ for(var/type in Blood.data["resistances"])
+ i++
+ var/disease_name = "Unknown"
+
+ if(!ispath(type))
+ var/datum/disease/advance/A = archive_diseases[type]
+ if(A)
+ disease_name = A.name
+ else
+ var/datum/disease/D = new type(0, null)
+ disease_name = D.name
+
+ dat += "- [disease_name] - Create vaccine bottle
"
+ dat += "
"
+ else
+ dat += "nothing
"
+ else
+ dat += "nothing
"
+ dat += "
Eject beaker[((R.total_volume&&R.reagent_list.len) ? "-- Empty beaker":"")]
"
+ dat += "Close"
+
+ user << browse("[src.name]
[dat]", "window=pandemic;size=575x400")
+ onclose(user, "pandemic")
+ return
+
+
+/obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/weapon/reagent_containers/glass))
+ if(stat & (NOPOWER|BROKEN)) return
+ if(beaker)
+ user << "A beaker is already loaded into the machine!"
+ return
+ if(!user.drop_item())
+ return
+
+ beaker = I
+ beaker.loc = src
+ user << "You add the beaker to the machine."
+ src.updateUsrDialog()
+ icon_state = "mixer1"
+
+ else if(istype(I, /obj/item/weapon/screwdriver))
+ if(src.beaker)
+ beaker.loc = get_turf(src)
+ ..()
+ return
+ else
+ ..()
+ return
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
new file mode 100644
index 00000000000..f27773397d9
--- /dev/null
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -0,0 +1,441 @@
+/obj/machinery/reagentgrinder
+ name = "All-In-One Grinder"
+ desc = "Used to grind things up into raw materials."
+ icon = 'icons/obj/kitchen.dmi'
+ icon_state = "juicer1"
+ layer = 2.9
+ anchored = 1
+ use_power = 1
+ idle_power_usage = 5
+ active_power_usage = 100
+ pass_flags = PASSTABLE
+ var/operating = 0
+ var/obj/item/weapon/reagent_containers/beaker = null
+ var/limit = 10
+ var/list/blend_items = list (
+
+ //Sheets
+ /obj/item/stack/sheet/mineral/plasma = list("plasma" = 20),
+ /obj/item/stack/sheet/metal = list("iron" = 20),
+ /obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma" = 20),
+ /obj/item/stack/sheet/mineral/wood = list("carbon" = 20),
+ /obj/item/stack/sheet/glass = list("silicon" = 20),
+ /obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20),
+ /obj/item/stack/sheet/mineral/uranium = list("uranium" = 20),
+ /obj/item/stack/sheet/mineral/bananium = list("banana" = 20),
+ /obj/item/stack/sheet/mineral/silver = list("silver" = 20),
+ /obj/item/stack/sheet/mineral/gold = list("gold" = 20),
+ /obj/item/weapon/grown/nettle/basic = list("sacid" = 0),
+ /obj/item/weapon/grown/nettle/death = list("facid" = 0),
+ /obj/item/weapon/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),
+
+ //Crayons (for overriding colours)
+ /obj/item/toy/crayon/red = list("redcrayonpowder" = 10),
+ /obj/item/toy/crayon/orange = list("orangecrayonpowder" = 10),
+ /obj/item/toy/crayon/yellow = list("yellowcrayonpowder" = 10),
+ /obj/item/toy/crayon/green = list("greencrayonpowder" = 10),
+ /obj/item/toy/crayon/blue = list("bluecrayonpowder" = 10),
+ /obj/item/toy/crayon/purple = list("purplecrayonpowder" = 10),
+ /obj/item/toy/crayon/mime = list("invisiblecrayonpowder" = 50),
+
+ //Blender Stuff
+ /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("cornoil" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/wheat = list("flour" = -5),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/oat = list("flour" = -5),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/egg = list("eggyolk" = -5),
+
+ //Grinder stuff, but only if dry
+ /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/arabica = list("coffeepowder" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tea/aspera = list("teapowder" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
+
+
+
+ //All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.!
+ /obj/item/weapon/reagent_containers/pill = list(),
+ /obj/item/weapon/reagent_containers/food = list()
+ )
+
+ var/list/juice_items = list (
+
+ //Juicer Stuff
+ /obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana = list("banana" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/potato = list("potato" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0),
+ )
+
+ var/list/dried_items = list(
+
+ //Grinder stuff, but only if dry
+ /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/arabica = list("coffeepowder" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tea/aspera = list("teapowder" = 0),
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
+ )
+
+ var/list/holdingitems = list()
+
+/obj/machinery/reagentgrinder/New()
+ ..()
+ beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
+ return
+
+/obj/machinery/reagentgrinder/update_icon()
+ icon_state = "juicer"+num2text(!isnull(beaker))
+ return
+
+
+/obj/machinery/reagentgrinder/attackby(obj/item/I, mob/user, params)
+ if(default_unfasten_wrench(user, I))
+ return
+
+ if (istype(I, /obj/item/weapon/reagent_containers/glass) || \
+ istype(I, /obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \
+ istype(I, /obj/item/weapon/reagent_containers/food/drinks/shaker))
+
+ if (beaker)
+ return 1
+ else
+ if(!user.drop_item())
+ return 1
+ beaker = I
+ beaker.loc = src
+ update_icon()
+ src.updateUsrDialog()
+ return 0
+
+ if(is_type_in_list(I, dried_items))
+ if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/grown))
+ var/obj/item/weapon/reagent_containers/food/snacks/grown/G = I
+ if(!G.dry)
+ user << "You must dry that first!"
+ return 1
+
+ if(holdingitems && holdingitems.len >= limit)
+ usr << "The machine cannot hold anymore items."
+ return 1
+
+ //Fill machine with a bag!
+ if(istype(I, /obj/item/weapon/storage/bag))
+ var/obj/item/weapon/storage/bag/B = I
+ for (var/obj/item/weapon/reagent_containers/food/snacks/grown/G in B.contents)
+ B.remove_from_storage(G, src)
+ holdingitems += G
+ if(holdingitems && holdingitems.len >= limit) //Sanity checking so the blender doesn't overfill
+ user << "You fill the All-In-One grinder to the brim."
+ break
+
+ if(!I.contents.len)
+ user << "You empty the plant bag into the All-In-One grinder."
+
+ src.updateUsrDialog()
+ return 0
+
+ if (!is_type_in_list(I, blend_items) && !is_type_in_list(I, juice_items))
+ user << "Cannot refine into a reagent!"
+ return 1
+
+ user.unEquip(I)
+ I.loc = src
+ holdingitems += I
+ src.updateUsrDialog()
+ return 0
+
+/obj/machinery/reagentgrinder/attack_paw(mob/user)
+ return src.attack_hand(user)
+
+/obj/machinery/reagentgrinder/attack_ai(mob/user)
+ return 0
+
+/obj/machinery/reagentgrinder/attack_hand(mob/user)
+ user.set_machine(src)
+ interact(user)
+
+/obj/machinery/reagentgrinder/interact(mob/user) // The microwave Menu
+ var/is_chamber_empty = 0
+ var/is_beaker_ready = 0
+ var/processing_chamber = ""
+ var/beaker_contents = ""
+ var/dat = ""
+
+ if(!operating)
+ for (var/obj/item/O in holdingitems)
+ processing_chamber += "\A [O.name]
"
+
+ if (!processing_chamber)
+ is_chamber_empty = 1
+ processing_chamber = "Nothing."
+ if (!beaker)
+ beaker_contents = "No beaker attached.
"
+ else
+ is_beaker_ready = 1
+ beaker_contents = "The beaker contains:
"
+ var/anything = 0
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ anything = 1
+ beaker_contents += "[R.volume] - [R.name]
"
+ if(!anything)
+ beaker_contents += "Nothing
"
+
+
+ dat = {"
+ Processing chamber contains:
+ [processing_chamber]
+ [beaker_contents]
+ "}
+ if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
+ dat += "Grind the reagents
"
+ dat += "Juice the reagents
"
+ if(holdingitems && holdingitems.len > 0)
+ dat += "Eject the reagents
"
+ if (beaker)
+ dat += "Detach the beaker
"
+ else
+ dat += "Please wait..."
+
+ var/datum/browser/popup = new(user, "reagentgrinder", "All-In-One Grinder")
+ popup.set_content(dat)
+ popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
+ popup.open(1)
+ return
+
+/obj/machinery/reagentgrinder/Topic(href, href_list)
+ if(..())
+ return
+ usr.set_machine(src)
+ if(operating)
+ updateUsrDialog()
+ return
+ switch(href_list["action"])
+ if ("grind")
+ grind()
+ if("juice")
+ juice()
+ if("eject")
+ eject()
+ if ("detach")
+ detach()
+
+/obj/machinery/reagentgrinder/proc/detach()
+
+ if (usr.stat != 0)
+ return
+ if (!beaker)
+ return
+ beaker.loc = src.loc
+ beaker = null
+ update_icon()
+ updateUsrDialog()
+
+/obj/machinery/reagentgrinder/proc/eject()
+
+ if (usr.stat != 0)
+ return
+ if (holdingitems && holdingitems.len == 0)
+ return
+
+ for(var/obj/item/O in holdingitems)
+ O.loc = src.loc
+ holdingitems -= O
+ holdingitems = list()
+ updateUsrDialog()
+
+/obj/machinery/reagentgrinder/proc/is_allowed(obj/item/weapon/reagent_containers/O)
+ for (var/i in blend_items)
+ if(istype(O, i))
+ return 1
+ return 0
+
+/obj/machinery/reagentgrinder/proc/get_allowed_by_id(obj/item/O)
+ for (var/i in blend_items)
+ if (istype(O, i))
+ return blend_items[i]
+
+/obj/machinery/reagentgrinder/proc/get_allowed_snack_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
+ for(var/i in blend_items)
+ if(istype(O, i))
+ return blend_items[i]
+
+/obj/machinery/reagentgrinder/proc/get_allowed_juice_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
+ for(var/i in juice_items)
+ if(istype(O, i))
+ return juice_items[i]
+
+/obj/machinery/reagentgrinder/proc/get_grownweapon_amount(obj/item/weapon/grown/O)
+ if (!istype(O))
+ return 5
+ else if (O.potency == -1)
+ return 5
+ else
+ return round(O.potency)
+
+/obj/machinery/reagentgrinder/proc/get_juice_amount(obj/item/weapon/reagent_containers/food/snacks/grown/O)
+ if (!istype(O))
+ return 5
+ else if (O.potency == -1)
+ return 5
+ else
+ return round(5*sqrt(O.potency))
+
+/obj/machinery/reagentgrinder/proc/remove_object(obj/item/O)
+ holdingitems -= O
+ qdel(O)
+
+/obj/machinery/reagentgrinder/proc/juice()
+ power_change()
+ if(stat & (NOPOWER|BROKEN))
+ return
+ if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
+ return
+ playsound(src.loc, 'sound/machines/juicer.ogg', 20, 1)
+ var/offset = prob(50) ? -2 : 2
+ animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
+ operating = 1
+ updateUsrDialog()
+ spawn(50)
+ pixel_x = initial(pixel_x) //return to its spot after shaking
+ operating = 0
+ updateUsrDialog()
+
+ //Snacks
+ for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+
+ var/allowed = get_allowed_juice_by_id(O)
+ if(isnull(allowed))
+ break
+
+ for (var/r_id in allowed)
+
+ var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
+ var/amount = get_juice_amount(O)
+
+ beaker.reagents.add_reagent(r_id, min(amount, space))
+
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+
+ remove_object(O)
+
+/obj/machinery/reagentgrinder/proc/grind()
+
+ power_change()
+ if(stat & (NOPOWER|BROKEN))
+ return
+ if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
+ return
+ playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
+ var/offset = prob(50) ? -2 : 2
+ animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
+ operating = 1
+ updateUsrDialog()
+ spawn(60)
+ pixel_x = initial(pixel_x) //return to its spot after shaking
+ operating = 0
+ updateUsrDialog()
+
+ //Snacks and Plants
+ for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+
+ var/allowed = get_allowed_snack_by_id(O)
+ if(isnull(allowed))
+ break
+
+ for (var/r_id in allowed)
+
+ var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
+ var/amount = allowed[r_id]
+ if(amount <= 0)
+ if(amount == 0)
+ if (O.reagents != null && O.reagents.has_reagent("nutriment"))
+ beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("nutriment"), space))
+ O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
+ else
+ if (O.reagents != null && O.reagents.has_reagent("nutriment"))
+ beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("nutriment")*abs(amount)), space))
+ O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
+
+ else
+ O.reagents.trans_id_to(beaker, r_id, min(amount, space))
+
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+
+ if(O.reagents.reagent_list.len == 0)
+ remove_object(O)
+
+ //Sheets
+ for (var/obj/item/stack/sheet/O in holdingitems)
+ var/allowed = get_allowed_by_id(O)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+ for(var/i = 1; i <= round(O.amount, 1); i++)
+ for (var/r_id in allowed)
+ var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
+ var/amount = allowed[r_id]
+ beaker.reagents.add_reagent(r_id,min(amount, space))
+ if (space < amount)
+ break
+ if (i == round(O.amount, 1))
+ remove_object(O)
+ break
+ //Plants
+ for (var/obj/item/weapon/grown/O in holdingitems)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+ var/allowed = get_allowed_by_id(O)
+ for (var/r_id in allowed)
+ var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
+ var/amount = allowed[r_id]
+ if (amount == 0)
+ if (O.reagents != null && O.reagents.has_reagent(r_id))
+ beaker.reagents.add_reagent(r_id,min(O.reagents.get_reagent_amount(r_id), space))
+ else
+ beaker.reagents.add_reagent(r_id,min(amount, space))
+
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+ remove_object(O)
+
+
+ //Crayons
+ //With some input from aranclanos, now 30% less shoddily copypasta
+ for (var/obj/item/toy/crayon/O in holdingitems)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+ var/allowed = get_allowed_by_id(O)
+ for (var/r_id in allowed)
+ var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
+ var/amount = allowed[r_id]
+ beaker.reagents.add_reagent(r_id,min(amount, space))
+ if (space < amount)
+ break
+ remove_object(O)
+
+ //Everything else - Transfers reagents from it into beaker
+ for (var/obj/item/weapon/reagent_containers/O in holdingitems)
+ if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
+ break
+ var/amount = O.reagents.total_volume
+ O.reagents.trans_to(beaker, amount)
+ if(!O.reagents.total_volume)
+ remove_object(O)
\ No newline at end of file
diff --git a/code/modules/reagents/Chemistry-Readme.dm b/code/modules/reagents/chemistry/readme.dm
similarity index 97%
rename from code/modules/reagents/Chemistry-Readme.dm
rename to code/modules/reagents/chemistry/readme.dm
index b754f17596f..b2759dbe7af 100644
--- a/code/modules/reagents/Chemistry-Readme.dm
+++ b/code/modules/reagents/chemistry/readme.dm
@@ -1,267 +1,270 @@
-/*
-NOTE: IF YOU UPDATE THE REAGENT-SYSTEM, ALSO UPDATE THIS README.
-
-Structure: /////////////////// //////////////////////////
- // Mob or object // -------> // Reagents var (datum) // Is a reference to the datum that holds the reagents.
- /////////////////// //////////////////////////
- | |
- The object that holds everything. V
- reagent_list var (list) A List of datums, each datum is a reagent.
-
- | | |
- V V V
-
- reagents (datums) Reagents. I.e. Water , cryoxadone or mercury.
-
-
-Random important notes:
-
- An objects on_reagent_change will be called every time the objects reagents change.
- Useful if you want to update the objects icon etc.
-
-About the Holder:
-
- The holder (reagents datum) is the datum that holds a list of all reagents
- currently in the object.It also has all the procs needed to manipulate reagents
-
- remove_any(var/amount)
- This proc removes reagents from the holder until the passed amount
- is matched. It'll try to remove some of ALL reagents contained.
-
- trans_to(var/obj/target, var/amount)
- This proc equally transfers the contents of the holder to another
- objects holder. You need to pass it the object (not the holder) you want
- to transfer to and the amount you want to transfer. Its return value is the
- actual amount transfered (if one of the objects is full/empty)
-
- trans_id_to(var/obj/target, var/reagent, var/amount)
- Same as above but only for a specific reagent in the reagent list.
- If the specified amount is greater than what is available, it will use
- the amount of the reagent that is available. If no reagent exists, returns null.
-
- metabolize(var/mob/M)
- This proc is called by the mobs life proc. It simply calls on_mob_life for
- all contained reagents. You shouldnt have to use this one directly.
-
- handle_reactions()
- This proc check all recipes and, on a match, uses them.
- It will also call the recipe's on_reaction proc (for explosions or w/e).
- Currently, this proc is automatically called by trans_to.
-
- isolate_reagent(var/reagent)
- Pass it a reagent id and it will remove all reagents but that one.
- It's that simple.
-
- del_reagent(var/reagent)
- Completely remove the reagent with the matching id.
-
- reaction_fire(exposed_temp)
- Simply calls the reaction_fire procs of all contained reagents.
-
- update_total()
- This one simply updates the total volume of the holder.
- (the volume of all reagents added together)
-
- clear_reagents()
- This proc removes ALL reagents from the holder.
-
- reaction(var/atom/A, var/method=TOUCH, var/volume_modifier=0)
- This proc calls the appropriate reaction procs of the reagents.
- I.e. if A is an object, it will call the reagents reaction_obj
- proc. The method var is used for reaction on mobs. It simply tells
- us if the mob TOUCHed the reagent, if it INGESTed the reagent, if the reagent
- was VAPORIZEd on them, if the reagent was INJECTed, or transfered via a PATCH to them.
- Since the volume can be checked in a reagents proc, you might want to
- use the volume_modifier var to modifiy the passed value without actually
- changing the volume of the reagents.
- If you're not sure if you need to use this the answer is very most likely 'No'.
- You'll want to use this proc whenever an atom first comes in
- contact with the reagents of a holder. (in the 'splash' part of a beaker i.e.)
- More on the reaction in the reagent part of this readme.
-
- add_reagent(var/reagent, var/amount, var/data)
- Attempts to add X of the matching reagent to the holder.
- You wont use this much. Mostly in new procs for pre-filled
- objects.
-
- remove_reagent(var/reagent, var/amount)
- The exact opposite of the add_reagent proc.
-
- has_reagent(var/reagent, var/amount)
- Returns 1 if the holder contains this reagent.
- Or 0 if not.
- If you pass it an amount it will additionally check
- if the amount is matched. This is optional.
-
- get_reagent_amount(var/reagent)
- Returns the amount of the matching reagent inside the
- holder. Returns 0 if the reagent is missing.
-
- Important variables:
-
- total_volume
- This variable contains the total volume of all reagents in this holder.
-
- reagent_list
- This is a list of all contained reagents. More specifically, references
- to the reagent datums.
-
- maximum_volume
- This is the maximum volume of the holder.
-
- my_atom
- This is the atom the holder is 'in'. Useful if you need to find the location.
- (i.e. for explosions)
-
-
-About Reagents:
-
- Reagents are all the things you can mix and fille in bottles etc. This can be anything from
- rejuvs over water to ... iron. Each reagent also has a few procs - i'll explain those below.
-
- reaction_mob(var/mob/M, var/method=TOUCH)
- This is called by the holder's reation proc.
- This version is only called when the reagent
- reacts with a mob. The method var can be either
- TOUCH or INGEST. You'll want to put stuff like
- acid-facemelting in here.
-
- reaction_obj(var/obj/O)
- This is called by the holder's reation proc.
- This version is called when the reagents reacts
- with an object. You'll want to put stuff like
- object melting in here ... or something. i dunno.
-
- reaction_turf(var/turf/T)
- This is called by the holder's reation proc.
- This version is called when the reagents reacts
- with a turf. You'll want to put stuff like extra
- slippery floors for lube or something in here.
-
- on_mob_life(var/mob/M)
- This proc is called everytime the mobs life proc executes.
- This is the place where you put damage for toxins ,
- drowsyness for sleep toxins etc etc.
- You'll want to call the parents proc by using ..() .
- If you dont, the chemical will stay in the mob forever -
- unless you write your own piece of code to slowly remove it.
- (Should be pretty easy, 1 line of code)
-
- Important variables:
-
- holder
- This variable contains a reference to the holder the chemical is 'in'
-
- volume
- This is the volume of the reagent.
-
- id
- The id of the reagent
-
- name
- The name of the reagent.
-
- data
- This var can be used for whatever the fuck you want. I used it for the sleep
- toxins to make them work slowly instead of instantly. You could also use this
- for DNA in a blood reagent or ... well whatever you want.
-
- color
- This is a hexadecimal color that represents the reagent outside of containers,
- you define it as "#RRGGBB", or, red green blue. You can also define it using the
- rgb() proc, which returns a hexadecimal value too. The color is black by default.
-
- A good website for color calculations: http://www.psyclops.com/tools/rgb/
-
-
-
-
-About Recipes:
-
- Recipes are simple datums that contain a list of required reagents and a result.
- They also have a proc that is called when the recipe is matched.
-
- on_reaction(var/datum/reagents/holder, var/created_volume)
- This proc is called when the recipe is matched.
- You'll want to add explosions etc here.
- To find the location you'll have to do something
- like get_turf(holder.my_atom)
-
- name & id
- Should be pretty obvious.
-
- result
- This var contains the id of the resulting reagent.
-
- required_reagents
- This is a list of ids of the required reagents.
- Each id also needs an associated value that gives us the minimum required amount
- of that reagent. The handle_reaction proc can detect mutiples of the same recipes
- so for most cases you want to set the required amount to 1.
-
- required_catalysts
- This is a list of the ids of the required catalysts.
- Functionally similar to required_reagents, it is a list of reagents that are required
- for the reaction. However, unlike required_reagents, catalysts are NOT consumed.
- They mearly have to be present in the container.
-
- result_amount
- This is the amount of the resulting reagent this recipe will produce.
- I recommend you set this to the total volume of all required reagent.
-
- required_container
- The container the recipe has to take place in in order to happen. Leave this blank/null
- if you want the reaction to happen anywhere.
-
- required_other
- Basically like a reagent's data variable. You can set extra requirements for a
- reaction with this.
-
- required_temp
- This is the required temperature.
-
-
-About the Tools:
-
- By default, all atom have a reagents var - but its empty. if you want to use an object for the chem.
- system you'll need to add something like this in its new proc:
-
- var/datum/reagents/R = new/datum/reagents(100) <<<<< create a new datum , 100 is the maximum_volume of the new holder datum.
- reagents = R <<<<< assign the new datum to the objects reagents var
- R.my_atom = src <<<<< set the holders my_atom to src so that we know where we are.
-
- This can also be done by calling a convenience proc:
- atom/proc/create_reagents(var/max_volume)
-
- Other important stuff:
-
- amount_per_transfer_from_this var
- This var is mostly used by beakers and bottles.
- It simply tells us how much to transfer when
- 'pouring' our reagents into something else.
-
- atom/proc/is_open_container()
- Checks atom/var/flags & OPENCONTAINER.
- If this returns 1 , you can use syringes, beakers etc
- to manipulate the contents of this object.
- If it's 0, you'll need to write your own custom reagent
- transfer code since you will not be able to use the standard
- tools to manipulate it.
-
-*/
-
-
-
-
-/* GOON CHEMS README:
-
- Credit goes to Cogwerks, and all the other goonstation coders
- for the original idea and implementation of this over at goonstation.
-
- THE REQUESTED DON'T PORT LIST: IF YOU PORT THESE THE GOONS WILL MURDER US IN OUR SLEEP SO PLEASE DON'T KTHX - Iamgoofball
- Any of the Secret Chems
- Goon in-joke chems (Eg. Cat Drugs, Hairgrownium)
- Liquid Electricity
- Rajajajah
-
+/*
+NOTE: IF YOU UPDATE THE REAGENT-SYSTEM, ALSO UPDATE THIS README.
+
+Structure: /////////////////// //////////////////////////
+ // Mob or object // -------> // Reagents var (datum) // Is a reference to the datum that holds the reagents.
+ /////////////////// //////////////////////////
+ | |
+ The object that holds everything. V
+ reagent_list var (list) A List of datums, each datum is a reagent.
+
+ | | |
+ V V V
+
+ reagents (datums) Reagents. I.e. Water , cryoxadone or mercury.
+
+
+Random important notes:
+
+ An objects on_reagent_change will be called every time the objects reagents change.
+ Useful if you want to update the objects icon etc.
+
+About the Holder:
+
+ The holder (reagents datum) is the datum that holds a list of all reagents
+ currently in the object.It also has all the procs needed to manipulate reagents
+
+ remove_any(var/amount)
+ This proc removes reagents from the holder until the passed amount
+ is matched. It'll try to remove some of ALL reagents contained.
+
+ remove_all(var/amount)
+ This proc removes reagents from the holder equally.
+
+ trans_to(var/obj/target, var/amount)
+ This proc equally transfers the contents of the holder to another
+ objects holder. You need to pass it the object (not the holder) you want
+ to transfer to and the amount you want to transfer. Its return value is the
+ actual amount transfered (if one of the objects is full/empty)
+
+ trans_id_to(var/obj/target, var/reagent, var/amount)
+ Same as above but only for a specific reagent in the reagent list.
+ If the specified amount is greater than what is available, it will use
+ the amount of the reagent that is available. If no reagent exists, returns null.
+
+ metabolize(var/mob/M)
+ This proc is called by the mobs life proc. It simply calls on_mob_life for
+ all contained reagents. You shouldnt have to use this one directly.
+
+ handle_reactions()
+ This proc check all recipes and, on a match, uses them.
+ It will also call the recipe's on_reaction proc (for explosions or w/e).
+ Currently, this proc is automatically called by trans_to.
+
+ isolate_reagent(var/reagent)
+ Pass it a reagent id and it will remove all reagents but that one.
+ It's that simple.
+
+ del_reagent(var/reagent)
+ Completely remove the reagent with the matching id.
+
+ reaction_fire(exposed_temp)
+ Simply calls the reaction_fire procs of all contained reagents.
+
+ update_total()
+ This one simply updates the total volume of the holder.
+ (the volume of all reagents added together)
+
+ clear_reagents()
+ This proc removes ALL reagents from the holder.
+
+ reaction(var/atom/A, var/method=TOUCH, var/volume_modifier=0)
+ This proc calls the appropriate reaction procs of the reagents.
+ I.e. if A is an object, it will call the reagents reaction_obj
+ proc. The method var is used for reaction on mobs. It simply tells
+ us if the mob TOUCHed the reagent, if it INGESTed the reagent, if the reagent
+ was VAPORIZEd on them, if the reagent was INJECTed, or transfered via a PATCH to them.
+ Since the volume can be checked in a reagents proc, you might want to
+ use the volume_modifier var to modifiy the passed value without actually
+ changing the volume of the reagents.
+ If you're not sure if you need to use this the answer is very most likely 'No'.
+ You'll want to use this proc whenever an atom first comes in
+ contact with the reagents of a holder. (in the 'splash' part of a beaker i.e.)
+ More on the reaction in the reagent part of this readme.
+
+ add_reagent(var/reagent, var/amount, var/data)
+ Attempts to add X of the matching reagent to the holder.
+ You wont use this much. Mostly in new procs for pre-filled
+ objects.
+
+ remove_reagent(var/reagent, var/amount)
+ The exact opposite of the add_reagent proc.
+
+ has_reagent(var/reagent, var/amount)
+ Returns 1 if the holder contains this reagent.
+ Or 0 if not.
+ If you pass it an amount it will additionally check
+ if the amount is matched. This is optional.
+
+ get_reagent_amount(var/reagent)
+ Returns the amount of the matching reagent inside the
+ holder. Returns 0 if the reagent is missing.
+
+ Important variables:
+
+ total_volume
+ This variable contains the total volume of all reagents in this holder.
+
+ reagent_list
+ This is a list of all contained reagents. More specifically, references
+ to the reagent datums.
+
+ maximum_volume
+ This is the maximum volume of the holder.
+
+ my_atom
+ This is the atom the holder is 'in'. Useful if you need to find the location.
+ (i.e. for explosions)
+
+
+About Reagents:
+
+ Reagents are all the things you can mix and fille in bottles etc. This can be anything from
+ rejuvs over water to ... iron. Each reagent also has a few procs - i'll explain those below.
+
+ reaction_mob(var/mob/M, var/method=TOUCH)
+ This is called by the holder's reation proc.
+ This version is only called when the reagent
+ reacts with a mob. The method var can be either
+ TOUCH or INGEST. You'll want to put stuff like
+ acid-facemelting in here.
+
+ reaction_obj(var/obj/O)
+ This is called by the holder's reation proc.
+ This version is called when the reagents reacts
+ with an object. You'll want to put stuff like
+ object melting in here ... or something. i dunno.
+
+ reaction_turf(var/turf/T)
+ This is called by the holder's reation proc.
+ This version is called when the reagents reacts
+ with a turf. You'll want to put stuff like extra
+ slippery floors for lube or something in here.
+
+ on_mob_life(var/mob/M)
+ This proc is called everytime the mobs life proc executes.
+ This is the place where you put damage for toxins ,
+ drowsyness for sleep toxins etc etc.
+ You'll want to call the parents proc by using ..() .
+ If you dont, the chemical will stay in the mob forever -
+ unless you write your own piece of code to slowly remove it.
+ (Should be pretty easy, 1 line of code)
+
+ Important variables:
+
+ holder
+ This variable contains a reference to the holder the chemical is 'in'
+
+ volume
+ This is the volume of the reagent.
+
+ id
+ The id of the reagent
+
+ name
+ The name of the reagent.
+
+ data
+ This var can be used for whatever the fuck you want. I used it for the sleep
+ toxins to make them work slowly instead of instantly. You could also use this
+ for DNA in a blood reagent or ... well whatever you want.
+
+ color
+ This is a hexadecimal color that represents the reagent outside of containers,
+ you define it as "#RRGGBB", or, red green blue. You can also define it using the
+ rgb() proc, which returns a hexadecimal value too. The color is black by default.
+
+ A good website for color calculations: http://www.psyclops.com/tools/rgb/
+
+
+
+
+About Recipes:
+
+ Recipes are simple datums that contain a list of required reagents and a result.
+ They also have a proc that is called when the recipe is matched.
+
+ on_reaction(var/datum/reagents/holder, var/created_volume)
+ This proc is called when the recipe is matched.
+ You'll want to add explosions etc here.
+ To find the location you'll have to do something
+ like get_turf(holder.my_atom)
+
+ name & id
+ Should be pretty obvious.
+
+ result
+ This var contains the id of the resulting reagent.
+
+ required_reagents
+ This is a list of ids of the required reagents.
+ Each id also needs an associated value that gives us the minimum required amount
+ of that reagent. The handle_reaction proc can detect mutiples of the same recipes
+ so for most cases you want to set the required amount to 1.
+
+ required_catalysts
+ This is a list of the ids of the required catalysts.
+ Functionally similar to required_reagents, it is a list of reagents that are required
+ for the reaction. However, unlike required_reagents, catalysts are NOT consumed.
+ They mearly have to be present in the container.
+
+ result_amount
+ This is the amount of the resulting reagent this recipe will produce.
+ I recommend you set this to the total volume of all required reagent.
+
+ required_container
+ The container the recipe has to take place in in order to happen. Leave this blank/null
+ if you want the reaction to happen anywhere.
+
+ required_other
+ Basically like a reagent's data variable. You can set extra requirements for a
+ reaction with this.
+
+ required_temp
+ This is the required temperature.
+
+
+About the Tools:
+
+ By default, all atom have a reagents var - but its empty. if you want to use an object for the chem.
+ system you'll need to add something like this in its new proc:
+
+ var/datum/reagents/R = new/datum/reagents(100) <<<<< create a new datum , 100 is the maximum_volume of the new holder datum.
+ reagents = R <<<<< assign the new datum to the objects reagents var
+ R.my_atom = src <<<<< set the holders my_atom to src so that we know where we are.
+
+ This can also be done by calling a convenience proc:
+ atom/proc/create_reagents(var/max_volume)
+
+ Other important stuff:
+
+ amount_per_transfer_from_this var
+ This var is mostly used by beakers and bottles.
+ It simply tells us how much to transfer when
+ 'pouring' our reagents into something else.
+
+ atom/proc/is_open_container()
+ Checks atom/var/flags & OPENCONTAINER.
+ If this returns 1 , you can use syringes, beakers etc
+ to manipulate the contents of this object.
+ If it's 0, you'll need to write your own custom reagent
+ transfer code since you will not be able to use the standard
+ tools to manipulate it.
+
+*/
+
+
+
+
+/* GOON CHEMS README:
+
+ Credit goes to Cogwerks, and all the other goonstation coders
+ for the original idea and implementation of this over at goonstation.
+
+ THE REQUESTED DON'T PORT LIST: IF YOU PORT THESE THE GOONS WILL MURDER US IN OUR SLEEP SO PLEASE DON'T KTHX - Iamgoofball
+ Any of the Secret Chems
+ Goon in-joke chems (Eg. Cat Drugs, Hairgrownium)
+ Liquid Electricity
+ Rajajajah
+
*/
\ No newline at end of file
diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/chemistry/reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents.dm
rename to code/modules/reagents/chemistry/reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Drink-Reagents/Alcohols.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Drink-Reagents/Alcohols.dm
rename to code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Blob-Reagents.dm b/code/modules/reagents/chemistry/reagents/blob_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Blob-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/blob_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Drink-Reagents/Drinks.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
similarity index 99%
rename from code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Drink-Reagents/Drinks.dm
rename to code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 9997396152b..ae26a8a1802 100644
--- a/code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Drink-Reagents/Drinks.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -267,7 +267,7 @@
return
/datum/reagent/consumable/spacemountainwind
- name = "Space Mountain Wind"
+ name = "SM Wind"
id = "spacemountainwind"
description = "Blows right through you like a space wind."
color = "#102000" // rgb: 16, 32, 0
diff --git a/code/modules/reagents/Chemistry-Reagents/Drug-Reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Drug-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/drug_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Food-Reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Consumable-Reagents/Food-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/food_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/medicine_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Other-Reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Other-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/other_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Pyrotechnic-Reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Pyrotechnic-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Reagents/Toxin-Reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Reagents/Toxin-Reagents.dm
rename to code/modules/reagents/chemistry/reagents/toxin_reagents.dm
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/chemistry/recipes.dm
similarity index 95%
rename from code/modules/reagents/Chemistry-Recipes.dm
rename to code/modules/reagents/chemistry/recipes.dm
index 84822634782..366ca9e7403 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/chemistry/recipes.dm
@@ -1,91 +1,90 @@
-///////////////////////////////////////////////////////////////////////////////////
-/datum/chemical_reaction
- var/name = null
- var/id = null
- var/result = null
- var/list/required_reagents = new/list()
- var/list/required_catalysts = new/list()
-
- // Both of these variables are mostly going to be used with slime cores - but if you want to, you can use them for other things
- var/atom/required_container = null // the container required for the reaction to happen
- var/required_other = 0 // an integer required for the reaction to happen
-
- var/result_amount = 0
- var/secondary = 0 // set to nonzero if secondary reaction
- var/mob_react = 0 //Determines if a chemical reaction can occur inside a mob
-
- var/required_temp = 0
- var/mix_message = "The solution begins to bubble."
-
-/datum/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume)
- return
- //I recommend you set the result amount to the total volume of all components.
-
-var/list/chemical_mob_spawn_meancritters = list() // list of possible hostile mobs
-var/list/chemical_mob_spawn_nicecritters = list() // and possible friendly mobs
-/datum/chemical_reaction/proc/chemical_mob_spawn(datum/reagents/holder, amount_to_spawn, reaction_name, mob_faction = "chemicalsummon")
- if(holder && holder.my_atom)
- if (chemical_mob_spawn_meancritters.len <= 0 || chemical_mob_spawn_nicecritters.len <= 0)
- for (var/T in typesof(/mob/living/simple_animal))
- var/mob/living/simple_animal/SA = T
- switch(initial(SA.gold_core_spawnable))
- if(1)
- chemical_mob_spawn_meancritters += T
- if(2)
- chemical_mob_spawn_nicecritters += T
- var/atom/A = holder.my_atom
- var/turf/T = get_turf(A)
- var/area/my_area = get_area(T)
- var/message = "A [reaction_name] reaction has occured in [my_area.name]. (JMP)"
- message += " (VV)"
-
- var/mob/M = get(A, /mob)
- if(M)
- message += " - Carried By: [key_name_admin(M)](?) (FLW)"
- else
- message += " - Last Fingerprint: [(A.fingerprintslast ? A.fingerprintslast : "N/A")]"
-
- message_admins(message, 0, 1)
-
- playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1)
-
- for(var/mob/living/carbon/C in viewers(get_turf(holder.my_atom), null))
- C.flash_eyes()
- for(var/i = 1, i <= amount_to_spawn, i++)
- var/chosen
- if (reaction_name == "Friendly Gold Slime")
- chosen = pick(chemical_mob_spawn_nicecritters)
- else
- chosen = pick(chemical_mob_spawn_meancritters)
- var/mob/living/simple_animal/C = new chosen
- C.faction |= mob_faction
- C.loc = get_turf(holder.my_atom)
- if(prob(50))
- for(var/j = 1, j <= rand(1, 3), j++)
- step(C, pick(NORTH,SOUTH,EAST,WEST))
-
-/datum/chemical_reaction/proc/goonchem_vortex(turf/simulated/T, setting_type, range)
- for(var/atom/movable/X in orange(range, T))
- if(istype(X, /obj/effect))
- continue
- if(!X.anchored)
- var/distance = get_dist(X, T)
- var/moving_power = max(range - distance, 1)
- spawn(0) //so everything moves at the same time.
- if(moving_power > 2) //if the vortex is powerful and we're close, we get thrown
- if(setting_type)
- var/atom/throw_target = get_edge_target_turf(X, get_dir(X, get_step_away(X, T)))
- X.throw_at(throw_target, moving_power, 1)
- else
- X.throw_at(T, moving_power, 1)
- else
- if(setting_type)
- for(var/i = 0, i < moving_power, i++)
- sleep(2)
- if(!step_away(X, T))
- break
- else
- for(var/i = 0, i < moving_power, i++)
- sleep(2)
- if(!step_towards(X, T))
- break
+/datum/chemical_reaction
+ var/name = null
+ var/id = null
+ var/result = null
+ var/list/required_reagents = new/list()
+ var/list/required_catalysts = new/list()
+
+ // Both of these variables are mostly going to be used with slime cores - but if you want to, you can use them for other things
+ var/atom/required_container = null // the container required for the reaction to happen
+ var/required_other = 0 // an integer required for the reaction to happen
+
+ var/result_amount = 0
+ var/secondary = 0 // set to nonzero if secondary reaction
+ var/mob_react = 0 //Determines if a chemical reaction can occur inside a mob
+
+ var/required_temp = 0
+ var/mix_message = "The solution begins to bubble."
+
+/datum/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume)
+ return
+ //I recommend you set the result amount to the total volume of all components.
+
+var/list/chemical_mob_spawn_meancritters = list() // list of possible hostile mobs
+var/list/chemical_mob_spawn_nicecritters = list() // and possible friendly mobs
+/datum/chemical_reaction/proc/chemical_mob_spawn(datum/reagents/holder, amount_to_spawn, reaction_name, mob_faction = "chemicalsummon")
+ if(holder && holder.my_atom)
+ if (chemical_mob_spawn_meancritters.len <= 0 || chemical_mob_spawn_nicecritters.len <= 0)
+ for (var/T in typesof(/mob/living/simple_animal))
+ var/mob/living/simple_animal/SA = T
+ switch(initial(SA.gold_core_spawnable))
+ if(1)
+ chemical_mob_spawn_meancritters += T
+ if(2)
+ chemical_mob_spawn_nicecritters += T
+ var/atom/A = holder.my_atom
+ var/turf/T = get_turf(A)
+ var/area/my_area = get_area(T)
+ var/message = "A [reaction_name] reaction has occured in [my_area.name]. (JMP)"
+ message += " (VV)"
+
+ var/mob/M = get(A, /mob)
+ if(M)
+ message += " - Carried By: [key_name_admin(M)](?) (FLW)"
+ else
+ message += " - Last Fingerprint: [(A.fingerprintslast ? A.fingerprintslast : "N/A")]"
+
+ message_admins(message, 0, 1)
+
+ playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1)
+
+ for(var/mob/living/carbon/C in viewers(get_turf(holder.my_atom), null))
+ C.flash_eyes()
+ for(var/i = 1, i <= amount_to_spawn, i++)
+ var/chosen
+ if (reaction_name == "Friendly Gold Slime")
+ chosen = pick(chemical_mob_spawn_nicecritters)
+ else
+ chosen = pick(chemical_mob_spawn_meancritters)
+ var/mob/living/simple_animal/C = new chosen
+ C.faction |= mob_faction
+ C.loc = get_turf(holder.my_atom)
+ if(prob(50))
+ for(var/j = 1, j <= rand(1, 3), j++)
+ step(C, pick(NORTH,SOUTH,EAST,WEST))
+
+/datum/chemical_reaction/proc/goonchem_vortex(turf/simulated/T, setting_type, range)
+ for(var/atom/movable/X in orange(range, T))
+ if(istype(X, /obj/effect))
+ continue
+ if(!X.anchored)
+ var/distance = get_dist(X, T)
+ var/moving_power = max(range - distance, 1)
+ spawn(0) //so everything moves at the same time.
+ if(moving_power > 2) //if the vortex is powerful and we're close, we get thrown
+ if(setting_type)
+ var/atom/throw_target = get_edge_target_turf(X, get_dir(X, get_step_away(X, T)))
+ X.throw_at(throw_target, moving_power, 1)
+ else
+ X.throw_at(T, moving_power, 1)
+ else
+ if(setting_type)
+ for(var/i = 0, i < moving_power, i++)
+ sleep(2)
+ if(!step_away(X, T))
+ break
+ else
+ for(var/i = 0, i < moving_power, i++)
+ sleep(2)
+ if(!step_towards(X, T))
+ break
diff --git a/code/modules/reagents/Chemistry-Recipes/Drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Drugs.dm
rename to code/modules/reagents/chemistry/recipes/drugs.dm
diff --git a/code/modules/reagents/Chemistry-Recipes/Medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Medicine.dm
rename to code/modules/reagents/chemistry/recipes/medicine.dm
diff --git a/code/modules/reagents/Chemistry-Recipes/Others.dm b/code/modules/reagents/chemistry/recipes/others.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Others.dm
rename to code/modules/reagents/chemistry/recipes/others.dm
diff --git a/code/modules/reagents/Chemistry-Recipes/Pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Pyrotechnics.dm
rename to code/modules/reagents/chemistry/recipes/pyrotechnics.dm
diff --git a/code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm
rename to code/modules/reagents/chemistry/recipes/slime_extracts.dm
diff --git a/code/modules/reagents/Chemistry-Recipes/Toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm
similarity index 100%
rename from code/modules/reagents/Chemistry-Recipes/Toxins.dm
rename to code/modules/reagents/chemistry/recipes/toxins.dm
diff --git a/nano/interfaces/chem_dispenser.tmpl b/nano/interfaces/chem_dispenser.tmpl
index 21550c9241c..14cc5035095 100644
--- a/nano/interfaces/chem_dispenser.tmpl
+++ b/nano/interfaces/chem_dispenser.tmpl
@@ -14,11 +14,9 @@
Amount:
- {{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}}
- {{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}}
- {{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}}
- {{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}}
- {{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}}
+ {{for data.beakerTransferAmounts :amount:i}}
+ {{:helper.link(amount, 'plus', {'amount': amount}, (data.amount == amount) ? 'selected' : null)}}
+ {{/for}}
@@ -34,7 +32,16 @@
Beaker
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
+ {{:helper.link('Eject', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
+ {{for data.beakerTransferAmounts :amount:i}}
+ {{:helper.link(amount, 'minus', {'remove': amount})}}
+ {{/for}}
+
+
Contents:
@@ -43,7 +50,7 @@
{{if data.isBeakerLoaded}}
Volume: {{:data.beakerCurrentVolume}}/{{:data.beakerMaxVolume}} Units
{{for data.beakerContents}}
- {{:value.volume}} units of {{:value.name}}
+ {{:value.volume}} units of {{:value.name}}
{{empty}}
Beaker Empty
{{/for}}
diff --git a/nano/interfaces/chem_heater.tmpl b/nano/interfaces/chem_heater.tmpl
index 48b4916bbb9..7ed92dee487 100644
--- a/nano/interfaces/chem_heater.tmpl
+++ b/nano/interfaces/chem_heater.tmpl
@@ -19,7 +19,11 @@
Beaker
- {{:helper.link('Eject Beaker', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
+ {{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
Contents:
@@ -28,7 +32,7 @@
{{if data.isBeakerLoaded}}
Temperature: {{:data.currentTemp}}K
{{for data.beakerContents}}
- {{:value.volume}} units of {{:value.name}}
+ {{:value.volume}} units of {{:value.name}}
{{empty}}
Beaker Empty
{{/for}}
diff --git a/nano/interfaces/cryo.tmpl b/nano/interfaces/cryo.tmpl
index 9f6c5bf661e..a93242e441a 100644
--- a/nano/interfaces/cryo.tmpl
+++ b/nano/interfaces/cryo.tmpl
@@ -128,7 +128,11 @@
Beaker
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
+ {{:helper.link('Eject', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
+
+
Contents:
@@ -136,7 +140,7 @@
{{if data.isBeakerLoaded}}
{{for data.beakerContents}}
- {{:value.volume}} units of {{:value.name}}
+ {{:value.volume}} units of {{:value.name}}
{{empty}}
Beaker Empty
{{/for}}
diff --git a/tgstation.dme b/tgstation.dme
index 7086095021c..783cea66dd8 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -37,6 +37,7 @@
#include "code\__DEFINES\pipe_construction.dm"
#include "code\__DEFINES\preferences.dm"
#include "code\__DEFINES\qdel.dm"
+#include "code\__DEFINES\reagents.dm"
#include "code\__DEFINES\role_preferences.dm"
#include "code\__DEFINES\say.dm"
#include "code\__DEFINES\sight.dm"
@@ -1478,29 +1479,33 @@
#include "code\modules\projectiles\projectile\magic.dm"
#include "code\modules\projectiles\projectile\reusable.dm"
#include "code\modules\projectiles\projectile\special.dm"
-#include "code\modules\reagents\Chemistry-Colours.dm"
-#include "code\modules\reagents\Chemistry-Holder.dm"
-#include "code\modules\reagents\Chemistry-Machinery.dm"
-#include "code\modules\reagents\Chemistry-Readme.dm"
-#include "code\modules\reagents\Chemistry-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Recipes.dm"
#include "code\modules\reagents\reagent_containers.dm"
#include "code\modules\reagents\reagent_dispenser.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Blob-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Drug-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Medicine-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Other-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Pyrotechnic-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Toxin-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Consumable-Reagents\Food-Reagents.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Consumable-Reagents\Drink-Reagents\Alcohols.dm"
-#include "code\modules\reagents\Chemistry-Reagents\Consumable-Reagents\Drink-Reagents\Drinks.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Drugs.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Medicine.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Others.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Pyrotechnics.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Slime_extracts.dm"
-#include "code\modules\reagents\Chemistry-Recipes\Toxins.dm"
+#include "code\modules\reagents\chemistry\colors.dm"
+#include "code\modules\reagents\chemistry\holder.dm"
+#include "code\modules\reagents\chemistry\readme.dm"
+#include "code\modules\reagents\chemistry\reagents.dm"
+#include "code\modules\reagents\chemistry\recipes.dm"
+#include "code\modules\reagents\chemistry\machinery\chem_dispenser.dm"
+#include "code\modules\reagents\chemistry\machinery\chem_heater.dm"
+#include "code\modules\reagents\chemistry\machinery\chem_master.dm"
+#include "code\modules\reagents\chemistry\machinery\pandemic.dm"
+#include "code\modules\reagents\chemistry\machinery\reagentgrinder.dm"
+#include "code\modules\reagents\chemistry\reagents\alcohol_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\blob_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\drink_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\drug_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\food_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\medicine_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\other_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\pyrotechnic_reagents.dm"
+#include "code\modules\reagents\chemistry\reagents\toxin_reagents.dm"
+#include "code\modules\reagents\chemistry\recipes\drugs.dm"
+#include "code\modules\reagents\chemistry\recipes\medicine.dm"
+#include "code\modules\reagents\chemistry\recipes\others.dm"
+#include "code\modules\reagents\chemistry\recipes\pyrotechnics.dm"
+#include "code\modules\reagents\chemistry\recipes\slime_extracts.dm"
+#include "code\modules\reagents\chemistry\recipes\toxins.dm"
#include "code\modules\reagents\reagent_containers\blood_pack.dm"
#include "code\modules\reagents\reagent_containers\borghydro.dm"
#include "code\modules\reagents\reagent_containers\bottle.dm"