diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 2a7405fa0b0..eb198e14ccc 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -180,6 +180,31 @@
///from base of [/datum/reagent/proc/expose_atom]: (/turf, reac_volume)
#define COMSIG_REAGENT_EXPOSE_TURF "reagent_expose_turf"
+///from base of [/datum/reagents/proc/add_reagent]: (/datum/reagent, amount, reagtemp, data, no_react)
+#define COMSIG_REAGENTS_NEW_REAGENT "reagents_new_reagent"
+///from base of [/datum/reagents/proc/add_reagent]: (/datum/reagent, amount, reagtemp, data, no_react)
+#define COMSIG_REAGENTS_ADD_REAGENT "reagents_add_reagent"
+///from base of [/datum/reagents/proc/del_reagent]: (/datum/reagent)
+#define COMSIG_REAGENTS_DEL_REAGENT "reagents_del_reagent"
+///from base of [/datum/reagents/proc/clear_reagents]: ()
+#define COMSIG_REAGENTS_REM_REAGENT "reagents_rem_reagent"
+///from base of [/datum/reagents/proc/set_temperature]: (new_temp, old_temp)
+#define COMSIG_REAGENTS_CLEAR_REAGENTS "reagents_clear_reagents"
+///from base of [/datum/reagents/proc/remove_reagent]: (/datum/reagent, amount)
+#define COMSIG_REAGENTS_TEMP_CHANGE "reagents_temp_change"
+///from base of [/datum/reagents/proc/handle_reactions]: (num_reactions)
+#define COMSIG_REAGENTS_REACTED "reagents_reacted"
+///from base of [/atom/proc/expose_reagents]: (/atom, /list, methods, volume_modifier, show_message)
+#define COMSIG_REAGENTS_EXPOSE_ATOM "reagents_expose_atom"
+///from base of [/obj/proc/expose_reagents]: (/obj, /list, methods, volume_modifier, show_message)
+#define COMSIG_REAGENTS_EXPOSE_OBJ "reagents_expose_obj"
+///from base of [/mob/living/proc/expose_reagents]: (/mob/living, /list, methods, volume_modifier, show_message, touch_protection)
+#define COMSIG_REAGENTS_EXPOSE_MOB "reagents_expose_mob"
+///from base of [/turf/proc/expose_reagents]: (/turf, /list, methods, volume_modifier, show_message)
+#define COMSIG_REAGENTS_EXPOSE_TURF "reagents_expose_turf"
+///from base of [/datum/component/personal_crafting/proc/del_reqs]: ()
+#define COMSIG_REAGENTS_CRAFTING_PING "reagents_crafting_ping"
+
///Called right before the atom changes the value of light_range to a different one, from base atom/set_light_range(): (new_range)
#define COMSIG_ATOM_SET_LIGHT_RANGE "atom_set_light_range"
///Called right before the atom changes the value of light_power to a different one, from base atom/set_light_power(): (new_power)
diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm
index 23de112bf4a..c149c503a69 100644
--- a/code/__DEFINES/reagents.dm
+++ b/code/__DEFINES/reagents.dm
@@ -28,13 +28,6 @@
/// Used for direct injection of reagents.
#define INJECT (1<<4)
-
-//defines passed through to the on_reagent_change proc
-#define DEL_REAGENT 1 // reagent deleted (fully cleared)
-#define ADD_REAGENT 2 // reagent added
-#define REM_REAGENT 3 // reagent removed (may still exist)
-#define CLEAR_REAGENTS 4 // all reagents were cleared
-
#define MIMEDRINK_SILENCE_DURATION 30 //ends up being 60 seconds given 1 tick every 2 seconds
///Health threshold for synthflesh and rezadone to unhusk someone
#define UNHUSK_DAMAGE_THRESHOLD 50
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index d1fb0b72b11..f5069cf1237 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -252,7 +252,7 @@
RGNT.volume += RG.volume
RGNT.data += RG.data
qdel(RG)
- RC.on_reagent_change()
+ SEND_SIGNAL(RC.reagents, COMSIG_REAGENTS_CRAFTING_PING) // - [] TODO: Make this entire thing less spaghetti
else
surroundings -= RC
else if(ispath(A, /obj/item/stack))
diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm
index 3345eb0e76d..88d7c760a7d 100644
--- a/code/datums/components/plumbing/reaction_chamber.dm
+++ b/code/datums/components/plumbing/reaction_chamber.dm
@@ -35,4 +35,4 @@
RC.emptying = TRUE //If we move this up, it'll instantly get turned off since any reaction always sets the reagent_total to zero. Other option is make the reaction update
//everything for every chemical removed, wich isn't a good option either.
- RC.on_reagent_change() //We need to check it now, because some reactions leave nothing left.
+ RC.on_reagent_change(reagents) //We need to check it now, because some reactions leave nothing left.
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 51741c9cb9a..dd28dd55f43 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -47,6 +47,3 @@
. = list()
if(("name" in vars) && !isatom(src))
. += "[vars["name"]]
"
-
-/datum/proc/on_reagent_change(changetype)
- return
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index b18ae480a0a..d5a4206974a 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -506,6 +506,7 @@
if(. & COMPONENT_NO_EXPOSE_REAGENTS)
return
+ SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_ATOM, src, reagents, methods, volume_modifier, show_message)
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_atom(src, reagents[R])
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index c35e0ad7bc9..8c09dc27e01 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -365,6 +365,7 @@
if(. & COMPONENT_NO_EXPOSE_REAGENTS)
return
+ SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_OBJ, src, reagents, methods, volume_modifier, show_message)
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_obj(src, reagents[R])
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 24a99742210..67ac9215446 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -592,6 +592,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
if(. & COMPONENT_NO_EXPOSE_REAGENTS)
return
+ SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_TURF, src, reagents, methods, volume_modifier, show_message)
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_turf(src, reagents[R])
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index eb2459a878b..0d45eb253c9 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -238,18 +238,16 @@
desc = "Either Nanotrasen's water supply is contaminated, or this machine actually vends lemon, chocolate, and cherry snow cones."
list_reagents = list(/datum/reagent/consumable/ice = 25, /datum/reagent/liquidgibs = 5)
-/obj/item/reagent_containers/food/drinks/mug/ // parent type is literally just so empty mug sprites are a thing
+/obj/item/reagent_containers/food/drinks/mug // parent type is literally just so empty mug sprites are a thing
name = "mug"
desc = "A drink served in a classy mug."
icon_state = "tea"
inhand_icon_state = "coffee"
spillable = TRUE
-/obj/item/reagent_containers/food/drinks/mug/on_reagent_change(changetype)
- if(reagents.total_volume)
- icon_state = "tea"
- else
- icon_state = "tea_empty"
+/obj/item/reagent_containers/food/drinks/mug/update_icon_state()
+ . = ..()
+ icon_state = reagents.total_volume ? "tea" : "tea_empty"
/obj/item/reagent_containers/food/drinks/mug/tea
name = "Duke Purple tea"
@@ -450,11 +448,9 @@
spillable = TRUE
isGlass = FALSE
-/obj/item/reagent_containers/food/drinks/sillycup/on_reagent_change(changetype)
- if(reagents.total_volume)
- icon_state = "water_cup"
- else
- icon_state = "water_cup_e"
+/obj/item/reagent_containers/food/drinks/sillycup/update_icon_state()
+ . = ..()
+ icon_state = reagents.total_volume ? "water_cup" : "water_cup_e"
/obj/item/reagent_containers/food/drinks/sillycup/smallcarton
name = "small carton"
@@ -462,6 +458,66 @@
icon_state = "juicebox"
volume = 15 //I figure if you have to craft these it should at least be slightly better than something you can get for free from a watercooler
+/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/on_reagent_change(datum/reagents/holder, ...)
+ . = ..()
+ if(!length(reagents.reagent_list))
+ name = "small carton"
+ desc = "A small carton, intended for holding drinks."
+ foodtype = NONE
+ return
+
+ switch(reagents.get_master_reagent_id()) // - [] TODO: Unshitcode this. Right now I'm just passing through and this really needs it's own dedicated PR.
+ if(/datum/reagent/consumable/orangejuice)
+ name = "orange juice box" // I know this shouldn't be here, but I really don't have the time to fix this right now. Blame the last guy
+ desc = "A great source of vitamins. Stay healthy!" // Ditto
+ foodtype = FRUIT | BREAKFAST // Ditto
+ if(/datum/reagent/consumable/milk)
+ name = "carton of milk"
+ desc = "An excellent source of calcium for growing space explorers."
+ foodtype = DAIRY | BREAKFAST
+ if(/datum/reagent/consumable/applejuice)
+ name = "apple juice box"
+ desc = "Sweet apple juice. Don't be late for school!"
+ foodtype = FRUIT
+ if(/datum/reagent/consumable/grapejuice)
+ name = "grape juice box"
+ desc = "Tasty grape juice in a fun little container. Non-alcoholic!"
+ foodtype = FRUIT
+ if(/datum/reagent/consumable/pineapplejuice)
+ name = "pineapple juice box"
+ desc = "Why would you even want this?"
+ foodtype = FRUIT | PINEAPPLE
+ if(/datum/reagent/consumable/milk/chocolate_milk)
+ name = "carton of chocolate milk"
+ desc = "Milk for cool kids!"
+ foodtype = SUGAR
+ if(/datum/reagent/consumable/ethanol/eggnog)
+ name = "carton of eggnog"
+ desc = "For enjoying the most wonderful time of the year."
+ foodtype = MEAT
+
+/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/update_icon_state()
+ . = ..()
+ if(!length(reagents.reagent_list))
+ icon_state = "juicebox"
+ return
+
+ switch(reagents.get_master_reagent_id()) // Thanks to update_name not existing we need to do this whole switch twice
+ if(/datum/reagent/consumable/orangejuice)
+ icon_state = "orangebox"
+ if(/datum/reagent/consumable/milk)
+ icon_state = "milkbox"
+ if(/datum/reagent/consumable/applejuice)
+ icon_state = "juicebox"
+ if(/datum/reagent/consumable/grapejuice)
+ icon_state = "grapebox"
+ if(/datum/reagent/consumable/pineapplejuice)
+ icon_state = "pineapplebox"
+ if(/datum/reagent/consumable/milk/chocolate_milk)
+ icon_state = "chocolatebox"
+ if(/datum/reagent/consumable/ethanol/eggnog)
+ icon_state = "nog2"
+
/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/smash(atom/target, mob/thrower, ranged = FALSE)
if(bartender_check(target) && ranged)
return
@@ -479,50 +535,6 @@
qdel(src)
target.Bumped(B)
-/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/on_reagent_change(changetype)
- if (reagents.reagent_list.len)
- switch(reagents.get_master_reagent_id())
- if(/datum/reagent/consumable/orangejuice)
- icon_state = "orangebox"
- name = "orange juice box"
- desc = "A great source of vitamins. Stay healthy!"
- foodtype = FRUIT | BREAKFAST
- if(/datum/reagent/consumable/milk)
- icon_state = "milkbox"
- name = "carton of milk"
- desc = "An excellent source of calcium for growing space explorers."
- foodtype = DAIRY | BREAKFAST
- if(/datum/reagent/consumable/applejuice)
- icon_state = "juicebox"
- name = "apple juice box"
- desc = "Sweet apple juice. Don't be late for school!"
- foodtype = FRUIT
- if(/datum/reagent/consumable/grapejuice)
- icon_state = "grapebox"
- name = "grape juice box"
- desc = "Tasty grape juice in a fun little container. Non-alcoholic!"
- foodtype = FRUIT
- if(/datum/reagent/consumable/pineapplejuice)
- icon_state = "pineapplebox"
- name = "pineapple juice box"
- desc = "Why would you even want this?"
- foodtype = FRUIT | PINEAPPLE
- if(/datum/reagent/consumable/milk/chocolate_milk)
- icon_state = "chocolatebox"
- name = "carton of chocolate milk"
- desc = "Milk for cool kids!"
- foodtype = SUGAR
- if(/datum/reagent/consumable/ethanol/eggnog)
- icon_state = "nog2"
- name = "carton of eggnog"
- desc = "For enjoying the most wonderful time of the year."
- foodtype = MEAT
- else
- icon_state = "juicebox"
- name = "small carton"
- desc = "A small carton, intended for holding drinks."
-
-
/obj/item/reagent_containers/food/drinks/colocup
name = "colo cup"
desc = "A cheap, mass produced style of cup, typically used at parties. They never seem to come out red, for some reason..."
diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
index 6c8002861cc..5ecf173cf3c 100644
--- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
@@ -15,23 +15,38 @@
pickup_sound = 'sound/items/handling/drinkglass_pickup.ogg'
custom_price = PAYCHECK_PRISONER
-/obj/item/reagent_containers/food/drinks/drinkingglass/on_reagent_change(changetype)
- cut_overlays()
- if(reagents.reagent_list.len)
- var/datum/reagent/R = reagents.get_master_reagent()
- if(!renamedByPlayer)
- name = R.glass_name
- desc = R.glass_desc
- if(R.glass_icon_state)
- icon_state = R.glass_icon_state
- else
- var/mutable_appearance/reagent_overlay = mutable_appearance(icon, "glassoverlay")
- icon_state = "glass_empty"
- reagent_overlay.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(reagent_overlay)
- else
- icon_state = "glass_empty"
+/obj/item/reagent_containers/food/drinks/drinkingglass/on_reagent_change(datum/reagents/holder, ...)
+ . = ..()
+ if(!length(reagents.reagent_list))
renamedByPlayer = FALSE //so new drinks can rename the glass
+ return
+
+ if(renamedByPlayer)
+ return
+
+ var/datum/reagent/largest_reagent = reagents.get_master_reagent()
+ name = largest_reagent.glass_name || initial(name)
+ desc = largest_reagent.glass_desc || initial(desc)
+
+/obj/item/reagent_containers/food/drinks/drinkingglass/update_icon_state()
+ . = ..()
+ if(!length(reagents.reagent_list))
+ icon_state = "glass_empty"
+ return
+
+ var/datum/reagent/largest_reagent = reagents.get_master_reagent()
+ if(largest_reagent.glass_icon_state)
+ icon_state = largest_reagent.glass_icon_state
+ return NONE
+
+/obj/item/reagent_containers/food/drinks/drinkingglass/update_overlays()
+ . = ..()
+ if(icon_state != initial(icon_state))
+ return
+
+ var/mutable_appearance/reagent_overlay = mutable_appearance(icon, "glassoverlay")
+ reagent_overlay.color = mix_color_from_reagents(reagents.reagent_list)
+ . += reagent_overlay
//Shot glasses!//
// This lets us add shots in here instead of lumping them in with drinks because >logic //
@@ -51,34 +66,37 @@
custom_materials = list(/datum/material/glass=100)
custom_price = PAYCHECK_ASSISTANT * 0.4
-/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change(changetype)
- cut_overlays()
+/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change(datum/reagents/holder, ...)
+ . = ..()
+ if(!length(reagents.reagent_list))
+ name = "shot glass"
+ desc = "A shot glass - the universal symbol for bad decisions."
+ return
- gulp_size = max(round(reagents.total_volume / 15), 15)
+ name = "filled shot glass"
+ desc = "The challenge is not taking as many as you can, but guessing what it is before you pass out."
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/largest_reagent = reagents.get_master_reagent()
- name = "filled shot glass"
- desc = "The challenge is not taking as many as you can, but guessing what it is before you pass out."
-
- if(largest_reagent.shot_glass_icon_state)
- icon_state = largest_reagent.shot_glass_icon_state
- else
- icon_state = "shotglassclear"
- var/mutable_appearance/shot_overlay = mutable_appearance(icon, "shotglassoverlay")
- shot_overlay.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(shot_overlay)
-
-
- else
+/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/update_icon_state()
+ . = ..()
+ if(!length(reagents.reagent_list))
icon_state = "shotglass"
name = "shot glass"
desc = "A shot glass - the universal symbol for bad decisions."
return
-/obj/item/reagent_containers/food/drinks/drinkingglass/filled/Initialize()
+ var/datum/reagent/largest_reagent = reagents.get_master_reagent()
+ name = "filled shot glass"
+ desc = "The challenge is not taking as many as you can, but guessing what it is before you pass out."
+ icon_state = largest_reagent.shot_glass_icon_state || "shotglassclear"
+
+/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/update_overlays()
. = ..()
- on_reagent_change(ADD_REAGENT)
+ if(icon_state != "shotglassclear")
+ return
+
+ var/mutable_appearance/shot_overlay = mutable_appearance(icon, "shotglassoverlay")
+ shot_overlay.color = mix_color_from_reagents(reagents.reagent_list)
+ . += shot_overlay
/obj/item/reagent_containers/food/drinks/drinkingglass/filled/soda
name = "Soda Water"
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index 1381eaa7c01..35f4b688ea9 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -237,6 +237,11 @@
/// Can't use initial(name) for this. This stores the name set by condimasters.
var/originalname = "condiment"
+/obj/item/reagent_containers/food/condiment/pack/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_add, TRUE)
+ RegisterSignal(reagents, COMSIG_REAGENTS_DEL_REAGENT, .proc/on_reagent_del, TRUE)
+
/obj/item/reagent_containers/food/condiment/pack/update_icon()
return
@@ -264,19 +269,23 @@
return
. = ..()
-/obj/item/reagent_containers/food/condiment/pack/on_reagent_change(changetype)
- if(reagents.reagent_list.len > 0)
- var/main_reagent = reagents.get_master_reagent_id()
- if(main_reagent in possible_states)
- var/list/temp_list = possible_states[main_reagent]
- icon_state = temp_list[1]
- desc = temp_list[3]
- else
- icon_state = "condi_mixed"
- desc = "A small condiment pack. The label says it contains [originalname]"
+/// Handles reagents getting added to the condiment pack.
+/obj/item/reagent_containers/food/condiment/pack/proc/on_reagent_add(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ var/main_reagent = reagents.get_master_reagent_id()
+ if(main_reagent in possible_states)
+ var/list/temp_list = possible_states[main_reagent]
+ icon_state = temp_list[1]
+ desc = temp_list[3]
else
- icon_state = "condi_empty"
- desc = "A small condiment pack. It is empty."
+ icon_state = "condi_mixed"
+ desc = "A small condiment pack. The label says it contains [originalname]"
+
+/// Handles reagents getting removed from the condiment pack.
+/obj/item/reagent_containers/food/condiment/pack/proc/on_reagent_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ icon_state = "condi_empty"
+ desc = "A small condiment pack. It is empty."
//Ketchup
/obj/item/reagent_containers/food/condiment/pack/ketchup
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index 49d036831b0..b580fada7a3 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -21,7 +21,6 @@
/obj/machinery/biogenerator/Initialize()
. = ..()
stored_research = new /datum/techweb/specialized/autounlocking/biogenerator
- create_reagents(1000)
/obj/machinery/biogenerator/Destroy()
QDEL_NULL(beaker)
@@ -62,9 +61,6 @@
if(in_range(user, src) || isobserver(user))
. += "The status display reads: Productivity at [productivity*100]%.
Matter consumption reduced by [(efficiency*25)-25]%.
Machine can hold up to [max_items] pieces of produce."
-/obj/machinery/biogenerator/on_reagent_change(changetype) //When the reagents change, change the icon as well.
- update_icon()
-
/obj/machinery/biogenerator/update_icon_state()
if(panel_open)
icon_state = "biogen-empty-o"
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index 189ef7a2e71..1c94513d525 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -31,36 +31,58 @@
create_reagents(volume, INJECTABLE|DRAWABLE)
-/obj/item/seeds/replicapod/on_reagent_change(changetype)
- if(changetype == ADD_REAGENT)
- var/datum/reagent/blood/B = reagents.has_reagent(/datum/reagent/blood)
- if(B)
- if(B.data["mind"] && B.data["cloneable"])
- mind = B.data["mind"]
- ckey = B.data["ckey"]
- realName = B.data["real_name"]
- blood_gender = B.data["gender"]
- blood_type = B.data["blood_type"]
- features = B.data["features"]
- factions = B.data["factions"]
- quirks = B.data["quirks"]
- sampleDNA = B.data["blood_DNA"]
- contains_sample = TRUE
- visible_message("The [src] is injected with a fresh blood sample.")
- log_cloning("[key_name(mind)]'s cloning record was added to [src] at [AREACOORD(src)].")
- else
- visible_message("The [src] rejects the sample!")
+/obj/item/seeds/replicapod/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT), .proc/on_reagent_add)
+ RegisterSignal(reagents, COMSIG_REAGENTS_DEL_REAGENT, .proc/on_reagent_del)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
- if(!reagents.has_reagent(/datum/reagent/blood))
- mind = null
- ckey = null
- realName = null
- blood_gender = null
- blood_type = null
- features = null
- factions = null
- sampleDNA = null
- contains_sample = FALSE
+/// Handles the seeds' reagents datum getting deleted.
+/obj/item/seeds/replicapod/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
+/// Handles reagents getting added to this seed.
+/obj/item/seeds/replicapod/proc/on_reagent_add(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ var/datum/reagent/blood/B = reagents.has_reagent(/datum/reagent/blood)
+ if(!B)
+ return
+
+ if(B.data["mind"] && B.data["cloneable"])
+ mind = B.data["mind"]
+ ckey = B.data["ckey"]
+ realName = B.data["real_name"]
+ blood_gender = B.data["gender"]
+ blood_type = B.data["blood_type"]
+ features = B.data["features"]
+ factions = B.data["factions"]
+ quirks = B.data["quirks"]
+ sampleDNA = B.data["blood_DNA"]
+ contains_sample = TRUE
+ visible_message("The [src] is injected with a fresh blood sample.")
+ log_cloning("[key_name(mind)]'s cloning record was added to [src] at [AREACOORD(src)].")
+ else
+ visible_message("The [src] rejects the sample!")
+ return NONE
+
+/// Handles reagents being deleted from these seeds.
+/obj/item/seeds/replicapod/proc/on_reagent_del(changetype)
+ SIGNAL_HANDLER
+ if(reagents.has_reagent(/datum/reagent/blood))
+ return
+
+ mind = null
+ ckey = null
+ realName = null
+ blood_gender = null
+ blood_type = null
+ features = null
+ factions = null
+ sampleDNA = null
+ contains_sample = FALSE
+ return NONE
/obj/item/seeds/replicapod/get_analyzer_text()
var/text = ..()
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index 5d892fd00c7..5c411cb3df9 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -57,6 +57,17 @@
SSmobs.cubemonkeys -= src
return ..()
+/mob/living/carbon/monkey/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles removing signal hooks incase someone is crazy enough to reset the reagents datum.
+/mob/living/carbon/monkey/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
/mob/living/carbon/monkey/create_internal_organs()
internal_organs += new /obj/item/organ/appendix
internal_organs += new /obj/item/organ/lungs
@@ -69,8 +80,13 @@
internal_organs += new /obj/item/organ/stomach
..()
-/mob/living/carbon/monkey/on_reagent_change()
- . = ..()
+/**
+ * Snowflake handling for morphine and nuka cola speed mods.
+ *
+ * Should be moved to the reagents at some future point. As it is I'm in a hurry.
+ */
+/mob/living/carbon/monkey/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
var/amount
if(reagents.has_reagent(/datum/reagent/medicine/morphine))
amount = -1
@@ -78,6 +94,7 @@
amount = -1
if(amount)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_reagent_speedmod, TRUE, amount)
+ return NONE
/mob/living/carbon/monkey/updatehealth()
. = ..()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 25bac22a63e..4624c76a75f 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -437,6 +437,7 @@
taste(source)
var/touch_protection = (methods & VAPOR) ? get_permeability_protection() : 0
+ SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_MOB, src, reagents, methods, volume_modifier, show_message, touch_protection)
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_mob(src, methods, reagents[R], show_message, touch_protection)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index ec3f6eb2b9a..25e05c3ccb5 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -67,7 +67,6 @@
new /obj/item/stock_parts/cell/potato(Tsec)
var/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/S = new(Tsec)
S.reagents.add_reagent(/datum/reagent/consumable/ethanol/whiskey, 15)
- S.on_reagent_change(ADD_REAGENT)
..()
/mob/living/simple_animal/bot/secbot/pingsky
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 7c23346ee0e..c1507dc4e70 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -113,6 +113,17 @@
Friends = null
return ..()
+/mob/living/simple_animal/slime/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles removing signal hooks incase someone is crazy enough to reset the reagents datum.
+/mob/living/simple_animal/slime/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
/mob/living/simple_animal/slime/proc/set_colour(new_colour)
colour = new_colour
update_name()
@@ -142,8 +153,13 @@
icon_state = icon_dead
..()
-/mob/living/simple_animal/slime/on_reagent_change()
- . = ..()
+/**
+ * Snowflake handling of reagent movespeed modifiers
+ *
+ * Should be moved to the reagents at some point in the future. As it is I'm in a hurry.
+ */
+/mob/living/simple_animal/slime/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
remove_movespeed_modifier(/datum/movespeed_modifier/slime_reagentmod)
var/amount = 0
if(reagents.has_reagent(/datum/reagent/medicine/morphine)) // morphine slows slimes down
@@ -152,6 +168,7 @@
amount = 5
if(amount)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/slime_reagentmod, multiplicative_slowdown = amount)
+ return NONE
/mob/living/simple_animal/slime/updatehealth()
. = ..()
diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm
index ee6275916bf..794987e5e04 100644
--- a/code/modules/plumbing/plumbers/reaction_chamber.dm
+++ b/code/modules/plumbing/plumbers/reaction_chamber.dm
@@ -17,10 +17,25 @@
. = ..()
AddComponent(/datum/component/plumbing/reaction_chamber, bolt)
-/obj/machinery/plumbing/reaction_chamber/on_reagent_change()
- if(reagents.total_volume == 0 && emptying) //we were emptying, but now we aren't
+/obj/machinery/plumbing/reaction_chamber/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles properly detaching signal hooks.
+/obj/machinery/plumbing/reaction_chamber/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_PARENT_QDELETING))
+ return NONE
+
+
+/// Handles stopping the emptying process when the chamber empties.
+/obj/machinery/plumbing/reaction_chamber/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
+ if(holder.total_volume == 0 && emptying) //we were emptying, but now we aren't
emptying = FALSE
- reagents.flags |= NO_REACT
+ holder.flags |= NO_REACT
+ return NONE
/obj/machinery/plumbing/reaction_chamber/power_change()
. = ..()
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index fa54e9115fe..261aad8e259 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -38,6 +38,17 @@
desc += " This one has a rating of [DisplayEnergy(maxcharge)], and you should not swallow it."
update_icon()
+/obj/item/stock_parts/cell/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles properly detaching signal hooks.
+/obj/item/stock_parts/cell/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
/obj/item/stock_parts/cell/update_overlays()
. = ..()
if(grown_battery)
@@ -86,9 +97,10 @@
user.visible_message("[user] is licking the electrodes of [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return (FIRELOSS)
-/obj/item/stock_parts/cell/on_reagent_change(changetype)
- rigged = (corrupted || reagents.has_reagent(/datum/reagent/toxin/plasma, 5)) //has_reagent returns the reagent datum
- return ..()
+/obj/item/stock_parts/cell/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
+ rigged = (corrupted || holder.has_reagent(/datum/reagent/toxin/plasma, 5)) ? TRUE : FALSE //has_reagent returns the reagent datum
+ return NONE
/obj/item/stock_parts/cell/proc/explode()
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index e264eca6a2d..f1b5be2c2d4 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -925,11 +925,32 @@
if(status == LIGHT_BURNED || status == LIGHT_OK)
shatter()
-// attack bulb/tube with object
-// if a syringe, can inject plasma to make it explode
-/obj/item/light/on_reagent_change(changetype)
- rigged = (reagents.has_reagent(/datum/reagent/toxin/plasma, LIGHT_REAGENT_CAPACITY)) //has_reagent returns the reagent datum
- return ..()
+/obj/item/light/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/**
+ * Handles rigging the cell if it contains enough plasma.
+ */
+/obj/item/light/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
+ rigged = (reagents.has_reagent(/datum/reagent/toxin/plasma, LIGHT_REAGENT_CAPACITY)) ? TRUE : FALSE //has_reagent returns the reagent datum, we don't want to hold a reference to prevent hard dels
+ return NONE
+
+/**
+ * Handles the reagent holder datum being deleted for some reason. Probably someone making pizza lights.
+ */
+/obj/item/light/proc/on_reagents_del(datum/reagents/holder)
+ SIGNAL_HANDLER
+ UnregisterSignal(holder, list(
+ COMSIG_PARENT_QDELETING,
+ COMSIG_REAGENTS_NEW_REAGENT,
+ COMSIG_REAGENTS_ADD_REAGENT,
+ COMSIG_REAGENTS_REM_REAGENT,
+ COMSIG_REAGENTS_DEL_REAGENT,
+ ))
+ return NONE
#undef LIGHT_REAGENT_CAPACITY
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index 2e7f3ef203d..feb49c20db2 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -90,38 +90,130 @@
my_atom.reagents = null
my_atom = null
+
/**
- * Used in attack logs for reagents in pills and such
+ * Adds a reagent to this holder
*
* Arguments:
- * * external_list - list of reagent types = amounts
+ * * reagent - The reagent id to add
+ * * amount - Amount to add
+ * * list/data - Any reagent data for this reagent, used for transferring data with reagents
+ * * reagtemp - Temperature of this reagent, will be equalized
+ * * no_react - prevents reactions being triggered by this addition
*/
-/datum/reagents/proc/log_list(external_list)
- if((external_list && !length(external_list)) || !length(reagent_list))
- return "no reagents"
+/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0)
+ if(!isnum(amount) || !amount)
+ return FALSE
- var/list/data = list()
- if(external_list)
- for(var/r in external_list)
- data += "[r] ([round(external_list[r], 0.1)]u)"
- else
- for(var/r in reagent_list) //no reagents will be left behind
- var/datum/reagent/R = r
- data += "[R.type] ([round(R.volume, 0.1)]u)"
- //Using types because SOME chemicals (I'm looking at you, chlorhydrate-beer) have the same names as other chemicals.
- return english_list(data)
+ if(amount <= 0)
+ return FALSE
+
+ var/datum/reagent/glob_reagent = GLOB.chemical_reagents_list[reagent]
+ if(!glob_reagent)
+ WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])")
+ return FALSE
+
+ update_total()
+ var/cached_total = total_volume
+ if(cached_total + amount > maximum_volume)
+ amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. shouldn't happen. Will happen.
+ if(amount <= 0)
+ return FALSE
+
+ var/cached_temp = chem_temp
+ var/list/cached_reagents = reagent_list
+
+ //Equalize temperature - Not using specific_heat() because the new chemical isn't in yet.
+ var/old_heat_capacity = 0
+ if(reagtemp != cached_temp)
+ for(var/r in cached_reagents)
+ var/datum/reagent/iter_reagent = r
+ old_heat_capacity += iter_reagent.specific_heat * iter_reagent.volume
+
+ //add the reagent to the existing if it exists
+ for(var/r in cached_reagents)
+ var/datum/reagent/iter_reagent = r
+ if (iter_reagent.type == reagent)
+ iter_reagent.volume += amount
+ update_total()
+
+ iter_reagent.on_merge(data, amount)
+ if(reagtemp != cached_temp)
+ set_temperature(((old_heat_capacity * cached_temp) + (iter_reagent.specific_heat * amount * reagtemp)) / heat_capacity())
+
+ SEND_SIGNAL(src, COMSIG_REAGENTS_ADD_REAGENT, iter_reagent, amount, reagtemp, data, no_react)
+ if(!no_react)
+ handle_reactions()
+ return TRUE
+
+ //otherwise make a new one
+ var/datum/reagent/new_reagent = new reagent(data)
+ cached_reagents += new_reagent
+ new_reagent.holder = src
+ new_reagent.volume = amount
+ if(data)
+ new_reagent.data = data
+ new_reagent.on_new(data)
+
+ if(isliving(my_atom))
+ new_reagent.on_mob_add(my_atom) //Must occur before it could posibly run on_mob_delete
+
+ update_total()
+ if(reagtemp != cached_temp)
+ set_temperature(((old_heat_capacity * cached_temp) + (new_reagent.specific_heat * amount * reagtemp)) / heat_capacity())
+
+ SEND_SIGNAL(src, COMSIG_REAGENTS_NEW_REAGENT, new_reagent, amount, reagtemp, data, no_react)
+ if(!no_react)
+ handle_reactions()
+ return TRUE
+
+/// Like add_reagent but you can enter a list. Format it like this: list(/datum/reagent/toxin = 10, "beer" = 15)
+/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null)
+ for(var/r_id in list_reagents)
+ var/amt = list_reagents[r_id]
+ add_reagent(r_id, amt, data)
+
+
+/// Remove a specific reagent
+/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to
+ if(isnull(amount))
+ amount = 0
+ CRASH("null amount passed to reagent code")
+
+ if(!isnum(amount))
+ return FALSE
+
+ if(amount < 0)
+ return FALSE
+
+ var/list/cached_reagents = reagent_list
+ for(var/A in cached_reagents)
+ var/datum/reagent/R = A
+ if (R.type == reagent)
+ //clamp the removal amount to be between current reagent amount
+ //and zero, to prevent removing more than the holder has stored
+ amount = clamp(amount, 0, R.volume)
+ R.volume -= amount
+ update_total()
+ SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(R) ? reagent : R, amount)
+ if(!safety)//So it does not handle reactions when it need not to
+ handle_reactions()
+
+ return TRUE
+
+ return FALSE
/// Remove an amount of reagents without caring about what they are
/datum/reagents/proc/remove_any(amount = 1)
var/list/cached_reagents = reagent_list
- var/total_transfered = 0
+ var/total_removed = 0
var/current_list_element = 1
var/initial_list_length = cached_reagents.len //stored here because removing can cause some reagents to be deleted, ergo length change.
current_list_element = rand(1, cached_reagents.len)
- while(total_transfered != amount)
- if(total_transfered >= amount)
+ while(total_removed != amount)
+ if(total_removed >= amount)
break
if(total_volume <= 0 || !cached_reagents.len)
break
@@ -130,16 +222,16 @@
current_list_element = 1
var/datum/reagent/R = cached_reagents[current_list_element]
- var/remove_amt = min(amount-total_transfered,round(amount/rand(2,initial_list_length),round(amount/10,0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers.
+ var/remove_amt = min(amount-total_removed,round(amount/rand(2,initial_list_length),round(amount/10,0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers.
//min ensures we don't go over amount.
remove_reagent(R.type, remove_amt)
current_list_element++
- total_transfered += remove_amt
+ total_removed += remove_amt
update_total()
handle_reactions()
- return total_transfered //this should be amount unless the loop is prematurely broken, in which case it'll be lower. It shouldn't ever go OVER amount.
+ return total_removed //this should be amount unless the loop is prematurely broken, in which case it'll be lower. It shouldn't ever go OVER amount.
/// Removes all reagents from this holder
/datum/reagents/proc/remove_all(amount = 1)
@@ -154,44 +246,90 @@
handle_reactions()
return amount
-/// Get the name of the reagent there is the most of in this holder
-/datum/reagents/proc/get_master_reagent_name()
+/// Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included.
+/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1)
+ if(!isnum(amount))
+ return 1
var/list/cached_reagents = reagent_list
- var/name
- var/max_volume = 0
+ var/has_removed_reagent = 0
+
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
- if(R.volume > max_volume)
- max_volume = R.volume
- name = R.name
+ 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.type, amount, safety)
- return name
+ return has_removed_reagent
-/// Get the id of the reagent there is the most of in this holder
-/datum/reagents/proc/get_master_reagent_id()
+/// Fuck this one reagent
+/datum/reagents/proc/del_reagent(reagent)
+ var/list/cached_reagents = reagent_list
+ for(var/_reagent in cached_reagents)
+ var/datum/reagent/R = _reagent
+ if(R.type == reagent)
+ if(isliving(my_atom))
+ if(R.metabolizing)
+ R.metabolizing = FALSE
+ R.on_mob_end_metabolize(my_atom)
+ R.on_mob_delete(my_atom)
+
+ //Clear from relevant lists
+ LAZYREMOVE(addiction_list, R)
+ reagent_list -= R
+ qdel(R)
+ update_total()
+ SEND_SIGNAL(src, COMSIG_REAGENTS_DEL_REAGENT, reagent)
+ return TRUE
+
+/// Remove every reagent except this one
+/datum/reagents/proc/isolate_reagent(reagent)
+ var/list/cached_reagents = reagent_list
+ for(var/_reagent in cached_reagents)
+ var/datum/reagent/R = _reagent
+ if(R.type != reagent)
+ del_reagent(R.type)
+ update_total()
+
+/// Removes all reagents
+/datum/reagents/proc/clear_reagents()
var/list/cached_reagents = reagent_list
- var/max_type
- var/max_volume = 0
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
- if(R.volume > max_volume)
- max_volume = R.volume
- max_type = R.type
+ del_reagent(R.type)
+ SEND_SIGNAL(src, COMSIG_REAGENTS_CLEAR_REAGENTS)
- return max_type
-/// Get a reference to the reagent there is the most of in this holder
-/datum/reagents/proc/get_master_reagent()
+/**
+ * Check if this holder contains this reagent.
+ * Reagent takes a PATH to a reagent.
+ * Amount checks for having a specific amount of that chemical.
+ * Needs matabolizing takes into consideration if the chemical is matabolizing when it's checked.
+ */
+/datum/reagents/proc/has_reagent(reagent, amount = -1, needs_metabolizing = FALSE)
var/list/cached_reagents = reagent_list
- var/datum/reagent/master
- var/max_volume = 0
- for(var/reagent in cached_reagents)
- var/datum/reagent/R = reagent
- if(R.volume > max_volume)
- max_volume = R.volume
- master = R
+ for(var/_reagent in cached_reagents)
+ var/datum/reagent/R = _reagent
+ if (R.type == reagent)
+ if(!amount)
+ if(needs_metabolizing && !R.metabolizing)
+ return FALSE
+ return R
+ else
+ if(round(R.volume, CHEMICAL_QUANTISATION_LEVEL) >= amount)
+ if(needs_metabolizing && !R.metabolizing)
+ return FALSE
+ return R
+ return FALSE
- return master
/**
* Transfer some stuff from this holder to a target object
@@ -292,6 +430,35 @@
src.handle_reactions()
return amount
+/// Transfer a specific reagent id to the target object
+/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
+ var/list/cached_reagents = reagent_list
+ if (!target)
+ return
+ if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent))
+ return
+ if(amount < 0)
+ return
+
+ var/datum/reagents/R = target.reagents
+ if(src.get_reagent_amount(reagent) max_volume)
+ max_volume = R.volume
+ name = R.name
- var/datum/reagents/R = target.reagents
- if(src.get_reagent_amount(reagent) max_volume)
+ max_volume = R.volume
+ max_type = R.type
+
+ return max_type
+
+/// Get a reference to the reagent there is the most of in this holder
+/datum/reagents/proc/get_master_reagent()
+ var/list/cached_reagents = reagent_list
+ var/datum/reagent/master
+ var/max_volume = 0
+ for(var/reagent in cached_reagents)
+ var/datum/reagent/R = reagent
+ if(R.volume > max_volume)
+ max_volume = R.volume
+ master = R
+
+ return master
/**
* Triggers metabolizing the reagents in this holder
@@ -448,13 +626,6 @@
C.update_stamina()
update_total()
-/// Removes addiction to a specific reagent on [/datum/reagents/var/my_atom]
-/datum/reagents/proc/remove_addiction(datum/reagent/R)
- to_chat(my_atom, "You feel like you've gotten over your need for [R.name].")
- SEND_SIGNAL(my_atom, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
- LAZYREMOVE(addiction_list, R)
- qdel(R)
-
/// Signals that metabolization has stopped, triggering the end of trait-based effects
/datum/reagents/proc/end_metabolization(mob/living/carbon/C, keep_liverless = TRUE)
var/list/cached_reagents = reagent_list
@@ -470,6 +641,14 @@
R.metabolizing = FALSE
R.on_mob_end_metabolize(C)
+/// Removes addiction to a specific reagent on [/datum/reagents/var/my_atom]
+/datum/reagents/proc/remove_addiction(datum/reagent/R)
+ to_chat(my_atom, "You feel like you've gotten over your need for [R.name].")
+ SEND_SIGNAL(my_atom, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
+ LAZYREMOVE(addiction_list, R)
+ qdel(R)
+
+
/**
* Calls [/datum/reagent/proc/on_move] on every reagent in this holder
*
@@ -481,7 +660,7 @@
var/list/cached_reagents = reagent_list
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
- R.on_move (A, Running)
+ R.on_move(A, Running)
update_total()
/**
@@ -494,22 +673,23 @@
var/list/cached_reagents = reagent_list
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
- R.on_update (A)
+ R.on_update(A)
update_total()
/// Handle any reactions possible in this holder
/datum/reagents/proc/handle_reactions()
if(flags & NO_REACT)
- return //Yup, no reactions here. No siree.
+ return 0 //Yup, no reactions here. No siree.
var/list/cached_reagents = reagent_list
var/list/cached_reactions = GLOB.chemical_reactions_list
var/datum/cached_my_atom = my_atom
- var/reaction_occurred = 0
+ . = 0
+ var/reaction_occurred
do
var/list/possible_reactions = list()
- reaction_occurred = 0
+ reaction_occurred = FALSE
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
for(var/reaction in cached_reactions[R.type]) // Was a big list but now it should be smaller since we filtered it with our reagent id
@@ -610,40 +790,14 @@
ME2.desc = "This extract has been used up."
selected_reaction.on_reaction(src, multiplier)
- reaction_occurred = 1
+ reaction_occurred = TRUE
+ .++
while(reaction_occurred)
update_total()
+ if(.)
+ SEND_SIGNAL(src, COMSIG_REAGENTS_REACTED, .)
-/// Remove every reagent except this one
-/datum/reagents/proc/isolate_reagent(reagent)
- var/list/cached_reagents = reagent_list
- for(var/_reagent in cached_reagents)
- var/datum/reagent/R = _reagent
- if(R.type != reagent)
- del_reagent(R.type)
- update_total()
-
-/// Fuck this one reagent
-/datum/reagents/proc/del_reagent(reagent)
- var/list/cached_reagents = reagent_list
- for(var/_reagent in cached_reagents)
- var/datum/reagent/R = _reagent
- if(R.type == reagent)
- if(isliving(my_atom))
- if(R.metabolizing)
- R.metabolizing = FALSE
- R.on_mob_end_metabolize(my_atom)
- R.on_mob_delete(my_atom)
-
- //Clear from relevant lists
- LAZYREMOVE(addiction_list, R)
- reagent_list -= R
- qdel(R)
- update_total()
- if(my_atom)
- my_atom.on_reagent_change(DEL_REAGENT)
- return 1
/// Updates [/datum/reagents/var/total_volume]
/datum/reagents/proc/update_total()
@@ -656,16 +810,6 @@
else
total_volume += R.volume
-
-/// Removes all reagents
-/datum/reagents/proc/clear_reagents()
- var/list/cached_reagents = reagent_list
- for(var/reagent in cached_reagents)
- var/datum/reagent/R = reagent
- del_reagent(R.type)
- if(my_atom)
- my_atom.on_reagent_change(CLEAR_REAGENTS)
-
/**
* Applies the relevant expose_ proc for every reagent in this holder
* * [/datum/reagent/proc/expose_mob]
@@ -713,155 +857,6 @@
return TRUE
return FALSE
-/// Returns the average specific heat for all reagents currently in this holder.
-/datum/reagents/proc/specific_heat()
- . = 0
- var/cached_amount = total_volume //cache amount
- var/list/cached_reagents = reagent_list //cache reagents
- for(var/I in cached_reagents)
- var/datum/reagent/R = I
- . += R.specific_heat * (R.volume / cached_amount)
-
-/datum/reagents/proc/adjust_thermal_energy(J, min_temp = 2.7, max_temp = 1000)
- var/S = specific_heat()
- chem_temp = clamp(chem_temp + (J / (S * total_volume)), 2.7, 1000)
-
-/**
- * Adds a reagent to this holder
- *
- * Arguments:
- * * reagent - The reagent id to add
- * * amount - Amount to add
- * * list/data - Any reagent data for this reagent, used for transferring data with reagents
- * * reagtemp - Temperature of this reagent, will be equalized
- * * no_react - prevents reactions being triggered by this addition
- */
-/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0)
- if(!isnum(amount) || !amount)
- return FALSE
-
- if(amount <= 0)
- return FALSE
-
- var/datum/reagent/D = GLOB.chemical_reagents_list[reagent]
- if(!D)
- WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])")
- return FALSE
-
- update_total()
- var/cached_total = total_volume
- if(cached_total + amount > maximum_volume)
- amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. shouldn't happen. Will happen.
- if(amount <= 0)
- return FALSE
- var/new_total = cached_total + amount
- var/cached_temp = chem_temp
- var/list/cached_reagents = reagent_list
-
- //Equalize temperature - Not using specific_heat() because the new chemical isn't in yet.
- var/specific_heat = 0
- var/thermal_energy = 0
- for(var/i in cached_reagents)
- var/datum/reagent/R = i
- specific_heat += R.specific_heat * (R.volume / new_total)
- thermal_energy += R.specific_heat * R.volume * cached_temp
- specific_heat += D.specific_heat * (amount / new_total)
- thermal_energy += D.specific_heat * amount * reagtemp
- chem_temp = thermal_energy / (specific_heat * new_total)
- ////
-
- //add the reagent to the existing if it exists
- for(var/A in cached_reagents)
- var/datum/reagent/R = A
- if (R.type == reagent)
- R.volume += amount
- update_total()
- if(my_atom)
- my_atom.on_reagent_change(ADD_REAGENT)
- R.on_merge(data, amount)
- if(!no_react)
- handle_reactions()
- return TRUE
-
- //otherwise make a new one
- var/datum/reagent/R = new D.type(data)
- cached_reagents += R
- R.holder = src
- R.volume = amount
- if(data)
- R.data = data
- R.on_new(data)
-
- if(isliving(my_atom))
- R.on_mob_add(my_atom) //Must occur before it could posibly run on_mob_delete
-
- update_total()
- if(my_atom)
- my_atom.on_reagent_change(ADD_REAGENT)
- if(!no_react)
- handle_reactions()
- return TRUE
-
-/// Like add_reagent but you can enter a list. Format it like this: list(/datum/reagent/toxin = 10, "beer" = 15)
-/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null)
- for(var/r_id in list_reagents)
- var/amt = list_reagents[r_id]
- add_reagent(r_id, amt, data)
-
-/// Remove a specific reagent
-/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to
-
- if(isnull(amount))
- amount = 0
- CRASH("null amount passed to reagent code")
-
- if(!isnum(amount))
- return FALSE
-
- if(amount < 0)
- return FALSE
-
- var/list/cached_reagents = reagent_list
-
- for(var/A in cached_reagents)
- var/datum/reagent/R = A
- if (R.type == reagent)
- //clamp the removal amount to be between current reagent amount
- //and zero, to prevent removing more than the holder has stored
- amount = clamp(amount, 0, R.volume)
- R.volume -= amount
- update_total()
- if(!safety)//So it does not handle reactions when it need not to
- handle_reactions()
- if(my_atom)
- my_atom.on_reagent_change(REM_REAGENT)
- return TRUE
-
- return FALSE
-
-
-/*
-Check if this holder contains this reagent.
-Reagent takes a PATH to a reagent.
-Amount checks for having a specific amount of that chemical.
-Needs matabolizing takes into consideration if the chemical is matabolizing when it's checked.
-*/
-/datum/reagents/proc/has_reagent(reagent, amount = -1, needs_metabolizing = FALSE)
- var/list/cached_reagents = reagent_list
- for(var/_reagent in cached_reagents)
- var/datum/reagent/R = _reagent
- if (R.type == reagent)
- if(!amount)
- if(needs_metabolizing && !R.metabolizing)
- return FALSE
- return R
- else
- if(round(R.volume, CHEMICAL_QUANTISATION_LEVEL) >= amount)
- if(needs_metabolizing && !R.metabolizing)
- return FALSE
- return R
- return FALSE
-
/// Get the amount of this reagent
/datum/reagents/proc/get_reagent_amount(reagent)
var/list/cached_reagents = reagent_list
@@ -871,8 +866,8 @@ Needs matabolizing takes into consideration if the chemical is matabolizing when
return round(R.volume, CHEMICAL_QUANTISATION_LEVEL)
return 0
-/// Get a comma separated string of every reagent name in this holder
-/datum/reagents/proc/get_reagents()
+/// Get a comma separated string of every reagent name in this holder. UNUSED
+/datum/reagents/proc/get_reagent_names()
var/list/names = list()
var/list/cached_reagents = reagent_list
for(var/reagent in cached_reagents)
@@ -881,30 +876,6 @@ Needs matabolizing takes into consideration if the chemical is matabolizing when
return jointext(names, ",")
-/// Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included.
-/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1)
- if(!isnum(amount))
- return 1
- var/list/cached_reagents = reagent_list
- var/has_removed_reagent = 0
-
- for(var/reagent in cached_reagents)
- var/datum/reagent/R = reagent
- 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.type, amount, safety)
-
- return has_removed_reagent
-
/// helper function to preserve data across reactions (needed for xenoarch)
/datum/reagents/proc/get_data(reagent_id)
var/list/cached_reagents = reagent_list
@@ -988,6 +959,26 @@ Needs matabolizing takes into consideration if the chemical is matabolizing when
return english_list(out, "something indescribable")
+
+/// Returns the total heat capacity for all of the reagents currently in this holder.
+/datum/reagents/proc/heat_capacity()
+ . = 0
+ var/list/cached_reagents = reagent_list //cache reagents
+ for(var/I in cached_reagents)
+ var/datum/reagent/R = I
+ . += R.specific_heat * R.volume
+
+/** Adjusts the thermal energy of the reagents in this holder by an amount.
+ *
+ * Arguments:
+ * - delta_energy: The amount to change the thermal energy by.
+ * - min_temp: The minimum temperature that can be reached.
+ * - max_temp: The maximum temperature that can be reached.
+ */
+/datum/reagents/proc/adjust_thermal_energy(delta_energy, min_temp = 2.7, max_temp = 1000)
+ var/heat_capacity = heat_capacity()
+ set_temperature(clamp(chem_temp + (delta_energy / heat_capacity), min_temp, max_temp))
+
/// Applies heat to this holder
/datum/reagents/proc/expose_temperature(temperature, coeff=0.02)
if(istype(my_atom,/obj/item/reagent_containers))
@@ -999,12 +990,46 @@ Needs matabolizing takes into consideration if the chemical is matabolizing when
chem_temp = min(chem_temp + max(temp_delta, 1), temperature)
else
chem_temp = max(chem_temp + min(temp_delta, -1), temperature)
- chem_temp = round(chem_temp)
- for(var/i in reagent_list)
- var/datum/reagent/R = i
- R.on_temp_change()
+ set_temperature(round(chem_temp))
handle_reactions()
+/** Sets the temperature of this reagent container to a new value.
+ *
+ * Handles setter signals.
+ *
+ * Arguments:
+ * - _temperature: The new temperature value.
+ */
+/datum/reagents/proc/set_temperature(_temperature)
+ if(_temperature == chem_temp)
+ return
+
+ . = chem_temp
+ chem_temp = _temperature
+ SEND_SIGNAL(src, COMSIG_REAGENTS_TEMP_CHANGE, _temperature, .)
+
+
+/**
+ * Used in attack logs for reagents in pills and such
+ *
+ * Arguments:
+ * * external_list - list of reagent types = amounts
+ */
+/datum/reagents/proc/log_list(external_list)
+ if((external_list && !length(external_list)) || !length(reagent_list))
+ return "no reagents"
+
+ var/list/data = list()
+ if(external_list)
+ for(var/r in external_list)
+ data += "[r] ([round(external_list[r], 0.1)]u)"
+ else
+ for(var/r in reagent_list) //no reagents will be left behind
+ var/datum/reagent/R = r
+ data += "[R.type] ([round(R.volume, 0.1)]u)"
+ //Using types because SOME chemicals (I'm looking at you, chlorhydrate-beer) have the same names as other chemicals.
+ return english_list(data)
+
///////////////////////////////////////////////////////////////////////////////////
@@ -1024,7 +1049,7 @@ Needs matabolizing takes into consideration if the chemical is matabolizing when
/proc/get_random_reagent_id() // Returns a random reagent ID minus blacklisted reagents
var/static/list/random_reagents = list()
if(!random_reagents.len)
- for(var/thing in subtypesof(/datum/reagent))
+ for(var/thing in subtypesof(/datum/reagent))
var/datum/reagent/R = thing
if(initial(R.can_synth))
random_reagents += R
diff --git a/code/modules/reagents/chemistry/readme.md b/code/modules/reagents/chemistry/readme.md
index 3ead7a5d185..9acfb65fc76 100644
--- a/code/modules/reagents/chemistry/readme.md
+++ b/code/modules/reagents/chemistry/readme.md
@@ -14,9 +14,6 @@ Structure: /////////////////// //////////////////////////
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
```
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index cbf8e1a2d11..5f27bcbe39a 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -165,9 +165,6 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/datum/reagent/proc/on_update(atom/A)
return
-///called on expose_temperature
-/datum/reagent/proc/on_temp_change()
- return
/// Called when the reagent container is hit by an explosion
/datum/reagent/proc/on_ex_act(severity)
return
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 5f47738756c..1af7487fb76 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -2028,8 +2028,6 @@ All effects don't start immediately, but rather get worse over time; the rate is
flavor += ", with a hint of "
flavor += english_list(secondary_tastes)
taste_description = flavor
- if(holder.my_atom)
- holder.my_atom.on_reagent_change()
/datum/reagent/consumable/ethanol/champagne //How the hell did we not have champagne already!?
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 91ca8b0821a..3ab31b8fe19 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -73,19 +73,31 @@
material = /datum/material/plasma
penetrates_skin = NONE
+/datum/reagent/toxin/plasma/on_new(data)
+ . = ..()
+ RegisterSignal(holder, COMSIG_REAGENTS_TEMP_CHANGE, .proc/on_temp_change)
+
+/datum/reagent/toxin/plasma/Destroy()
+ UnregisterSignal(holder, COMSIG_REAGENTS_TEMP_CHANGE)
+ return ..()
+
/datum/reagent/toxin/plasma/on_mob_life(mob/living/carbon/C)
if(holder.has_reagent(/datum/reagent/medicine/epinephrine))
holder.remove_reagent(/datum/reagent/medicine/epinephrine, 2*REM)
C.adjustPlasma(20)
return ..()
-/datum/reagent/toxin/plasma/on_temp_change()
+/// Handles plasma boiling.
+/datum/reagent/toxin/plasma/proc/on_temp_change(datum/reagents/_holder, old_temp)
+ SIGNAL_HANDLER
if(holder.chem_temp < LIQUID_PLASMA_BP)
return
- if(holder.my_atom)
- var/atom/A = holder.my_atom
- A.atmos_spawn_air("plasma=[volume];TEMP=[holder.chem_temp]")
- holder.del_reagent(type)
+ if(!holder.my_atom)
+ return
+
+ var/atom/A = holder.my_atom
+ A.atmos_spawn_air("plasma=[volume];TEMP=[holder.chem_temp]")
+ holder.del_reagent(type)
/datum/reagent/toxin/plasma/expose_turf(turf/open/exposed_turf, reac_volume)
if(!istype(exposed_turf))
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 295a1da9dcf..9e3f0968a0a 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -27,6 +27,19 @@
add_initial_reagents()
+/obj/item/reagent_containers/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/obj/item/reagent_containers/Destroy()
+ return ..()
+
+/obj/item/reagent_containers/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
/obj/item/reagent_containers/proc/add_initial_reagents()
if(list_reagents)
reagents.add_reagent_list(list_reagents)
@@ -135,8 +148,11 @@
/obj/item/reagent_containers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
reagents.expose_temperature(exposed_temperature)
-/obj/item/reagent_containers/on_reagent_change(changetype)
+/// Updates the icon of the container when the reagents change. Eats signal args
+/obj/item/reagent_containers/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
update_icon()
+ return NONE
/obj/item/reagent_containers/update_overlays()
. = ..()
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 0ff2618f661..a3b3954941e 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -15,22 +15,21 @@
reagents.add_reagent(unique_blood ? unique_blood : /datum/reagent/blood, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
update_icon()
-/obj/item/reagent_containers/blood/on_reagent_change(changetype)
- if(reagents)
- var/datum/reagent/blood/B = reagents.has_reagent(/datum/reagent/blood)
- if(B && B.data && B.data["blood_type"])
- blood_type = B.data["blood_type"]
- else
- blood_type = null
+/// Handles updating the container when the reagents change.
+/obj/item/reagent_containers/blood/on_reagent_change(datum/reagents/holder, ...)
+ var/datum/reagent/blood/B = holder.has_reagent(/datum/reagent/blood)
+ if(B && B.data && B.data["blood_type"])
+ blood_type = B.data["blood_type"]
+ else
+ blood_type = null
update_pack_name()
- update_icon()
+ return ..()
/obj/item/reagent_containers/blood/proc/update_pack_name()
- if(!labelled)
- if(blood_type)
- name = "blood pack - [blood_type]"
- else
- name = "blood pack"
+ if(labelled)
+ return
+
+ name = "blood_pack[blood_type ? " - [blood_type]" : ""]"
/obj/item/reagent_containers/blood/random
icon_state = "random_bloodpack"
diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm
index 3ca6fef15ba..77bb6f94e1f 100644
--- a/code/modules/reagents/reagent_containers/borghydro.dm
+++ b/code/modules/reagents/reagent_containers/borghydro.dm
@@ -54,7 +54,6 @@ Borg Hypospray
regenerate_reagents()
charge_timer = 0
- //update_icon()
return 1
// Use this to add more chemicals for the borghypo to produce.
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 469ed5b2c02..b3e8eca4502 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -144,18 +144,23 @@
reagents.expose(usr.loc)
src.reagents.clear_reagents()
-/obj/item/reagent_containers/spray/on_reagent_change(changetype)
- var/total_reagent_weight
- var/amount_of_reagents
- for (var/datum/reagent/R in reagents.reagent_list)
- total_reagent_weight = total_reagent_weight + R.reagent_weight
- amount_of_reagents++
+/// Handles updating the spreay distance when the reagents change.
+/obj/item/reagent_containers/spray/on_reagent_change(datum/reagents/holder, ...)
+ . = ..()
+ var/total_reagent_weight = 0
+ var/number_of_reagents = 0
+ var/amount_of_reagents = holder.total_volume
+ var/list/cached_reagents = holder.reagent_list
+ for(var/datum/reagent/reagent in cached_reagents)
+ total_reagent_weight += reagent.reagent_weight * reagent.volume
+ number_of_reagents++
- if(total_reagent_weight && amount_of_reagents) //don't bother if the container is empty - DIV/0
+ if(total_reagent_weight && number_of_reagents && amount_of_reagents) //don't bother if the container is empty - DIV/0
var/average_reagent_weight = total_reagent_weight / amount_of_reagents
- spray_range = clamp(round((initial(spray_range) / average_reagent_weight) - ((amount_of_reagents - 1) * 1)), 3, 5) //spray distance between 3 and 5 tiles rounded down; extra reagents lose a tile
+ spray_range = clamp(round((initial(spray_range) / average_reagent_weight) - ((number_of_reagents - 1) * 1)), 3, 5) //spray distance between 3 and 5 tiles rounded down; extra reagents lose a tile
else
spray_range = initial(spray_range)
+
if(stream_mode == 0)
current_range = spray_range
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 52dbebce403..4499a93d95d 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -28,9 +28,6 @@
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
-/obj/item/reagent_containers/syringe/on_reagent_change(changetype)
- update_icon()
-
/obj/item/reagent_containers/syringe/pickup(mob/user)
..()
update_icon()
diff --git a/code/modules/research/xenobiology/vatgrowing/vatgrower.dm b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm
index 325ae26ab87..c0accc7467c 100644
--- a/code/modules/research/xenobiology/vatgrowing/vatgrower.dm
+++ b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm
@@ -12,6 +12,17 @@
. = ..()
AddComponent(/datum/component/plumbing/simple_demand, bolt)
+/obj/machinery/plumbing/growing_vat/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles properly detaching signal hooks.
+/obj/machinery/plumbing/growing_vat/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
///When we process, we make use of our reagents to try and feed the samples we have.
/obj/machinery/plumbing/growing_vat/process()
if(!is_operational)
@@ -64,10 +75,11 @@
. = ..()
QDEL_NULL(biological_sample)
-///Call update icon when reagents change to update the reagent content icons
-/obj/machinery/plumbing/growing_vat/on_reagent_change(changetype)
+/// Call update icon when reagents change to update the reagent content icons. Eats signal args.
+/obj/machinery/plumbing/growing_vat/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
update_icon()
- return ..()
+ return NONE
///Adds overlays to show the reagent contents
/obj/machinery/plumbing/growing_vat/update_overlays()
diff --git a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm
index 49fea9fdfb3..6d713cc80ad 100644
--- a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm
@@ -284,11 +284,22 @@
create_reagents(max_volume, NO_REACT)
known_reagents = list(/datum/reagent/medicine/epinephrine="Epinephrine",/datum/reagent/medicine/c2/multiver="Multiver")
-/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/detach()
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/Destroy()
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/create_reagents(max_vol, flags)
+ . = ..()
+ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
+ RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)
+
+/// Handles detaching signal hooks incase someone is crazy enough to make this edible.
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/on_reagents_del(datum/reagents/reagents)
+ SIGNAL_HANDLER
+ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_PARENT_QDELETING))
+ return NONE
+
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/detach()
STOP_PROCESSING(SSobj, src)
return ..()
@@ -477,9 +488,11 @@
send_byjax(chassis.occupants,"msyringegun.browser","reagents",get_current_reagents())
send_byjax(chassis.occupants,"msyringegun.browser","reagents_form",get_reagents_form())
-/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/on_reagent_change(changetype)
- . = ..()
+/// Updates the equipment info list when the reagents change. Eats signal args.
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/on_reagent_change(datum/reagents/holder, ...)
+ SIGNAL_HANDLER
update_equip_info()
+ return NONE
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/process(delta_time)