diff --git a/citadel.dme b/citadel.dme
index 7c7f9707d0ac..ad778783a38c 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -1621,7 +1621,6 @@
#include "code\game\objects\items\signs.dm"
#include "code\game\objects\items\spritechanger.dm"
#include "code\game\objects\items\throwing.dm"
-#include "code\game\objects\items\toys.dm"
#include "code\game\objects\items\trash.dm"
#include "code\game\objects\items\upgradekit.dm"
#include "code\game\objects\items\circuitboards\broken.dm"
@@ -1801,6 +1800,8 @@
#include "code\game\objects\items\tools\weldingtool.dm"
#include "code\game\objects\items\tools\wirecutters.dm"
#include "code\game\objects\items\tools\wrench.dm"
+#include "code\game\objects\items\toys\balloon.dm"
+#include "code\game\objects\items\toys\toys.dm"
#include "code\game\objects\items\weapons\AI_modules.dm"
#include "code\game\objects\items\weapons\autopsy.dm"
#include "code\game\objects\items\weapons\barrier_tape.dm"
@@ -1932,7 +1933,7 @@
#include "code\game\objects\structures\loot_piles.dm"
#include "code\game\objects\structures\low_wall.dm"
#include "code\game\objects\structures\map_blocker.dm"
-#include "code\game\objects\structures\medical_stand_vr.dm"
+#include "code\game\objects\structures\medical_stand.dm"
#include "code\game\objects\structures\mineral_bath.dm"
#include "code\game\objects\structures\mirror.dm"
#include "code\game\objects\structures\mop_bucket.dm"
@@ -3718,7 +3719,11 @@
#include "code\modules\mob\living\bot\mulebot.dm"
#include "code\modules\mob\living\bot\secbot.dm"
#include "code\modules\mob\living\bot\SLed209bot.dm"
+#include "code\modules\mob\living\carbon\blood_fragment.dm"
+#include "code\modules\mob\living\carbon\blood_holder.dm"
+#include "code\modules\mob\living\carbon\blood_mixture.dm"
#include "code\modules\mob\living\carbon\breathe.dm"
+#include "code\modules\mob\living\carbon\carbon-blood.dm"
#include "code\modules\mob\living\carbon\carbon-defense.dm"
#include "code\modules\mob\living\carbon\carbon-hands.dm"
#include "code\modules\mob\living\carbon\carbon.dm"
@@ -4668,17 +4673,18 @@
#include "code\modules\random_map\noise\tundra.dm"
#include "code\modules\reagents\exposedprocs.dm"
#include "code\modules\reagents\reagent_containers.dm"
-#include "code\modules\reagents\chemistry\_readme.dm"
#include "code\modules\reagents\chemistry\chemical_reaction-legacy.dm"
#include "code\modules\reagents\chemistry\chemical_reaction.dm"
-#include "code\modules\reagents\chemistry\colors.dm"
#include "code\modules\reagents\chemistry\helpers.dm"
#include "code\modules\reagents\chemistry\machinery.dm"
-#include "code\modules\reagents\chemistry\metabolism.dm"
#include "code\modules\reagents\chemistry\reagent.dm"
+#include "code\modules\reagents\chemistry\reagent_holder-color.dm"
+#include "code\modules\reagents\chemistry\reagent_holder-helpers.dm"
#include "code\modules\reagents\chemistry\reagent_holder-legacy.dm"
+#include "code\modules\reagents\chemistry\reagent_holder-metabolism.dm"
#include "code\modules\reagents\chemistry\reagent_holder-reactions.dm"
#include "code\modules\reagents\chemistry\reagent_holder.dm"
+#include "code\modules\reagents\chemistry\reagent_metabolism.dm"
#include "code\modules\reagents\chemistry\wiki_generation.dm"
#include "code\modules\reagents\chemistry\reactions\automata.dm"
#include "code\modules\reagents\chemistry\reactions\carpet.dm"
@@ -4701,13 +4707,31 @@
#include "code\modules\reagents\chemistry\reagents\Chemistry-Reagents-Other.dm"
#include "code\modules\reagents\chemistry\reagents\Chemistry-Reagents-Toxins.dm"
#include "code\modules\reagents\chemistry\reagents\Chemistry-Topical.dm"
+#include "code\modules\reagents\chemistry\reagents\core\acid.dm"
+#include "code\modules\reagents\chemistry\reagents\core\blood.dm"
#include "code\modules\reagents\chemistry\reagents\core\elements.dm"
+#include "code\modules\reagents\chemistry\reagents\core\ethanol.dm"
+#include "code\modules\reagents\chemistry\reagents\core\toxin.dm"
+#include "code\modules\reagents\chemistry\reagents\core\water.dm"
+#include "code\modules\reagents\chemistry\reagents\nutrition\nutriment.dm"
+#include "code\modules\reagents\chemistry\reagents\nutrition\nutriment\coating.dm"
+#include "code\modules\reagents\chemistry\reagents\other\adminordrazine.dm"
+#include "code\modules\reagents\chemistry\reagents\other\carpet.dm"
+#include "code\modules\reagents\chemistry\reagents\other\chalk_dust.dm"
#include "code\modules\reagents\chemistry\reagents\other\cleaner.dm"
+#include "code\modules\reagents\chemistry\reagents\other\crayon_dust.dm"
+#include "code\modules\reagents\chemistry\reagents\other\holywater.dm"
+#include "code\modules\reagents\chemistry\reagents\other\luminol.dm"
+#include "code\modules\reagents\chemistry\reagents\other\marker_ink.dm"
+#include "code\modules\reagents\chemistry\reagents\other\paint.dm"
#include "code\modules\reagents\chemistry\reagents\pyrotechnics\thermite.dm"
#include "code\modules\reagents\distilling\Distilling-Recipes.dm"
#include "code\modules\reagents\distilling\distilling.dm"
#include "code\modules\reagents\items\hypospray.dm"
#include "code\modules\reagents\items\hypovial.dm"
+#include "code\modules\reagents\items\spray.dm"
+#include "code\modules\reagents\items\spray\squirt.dm"
+#include "code\modules\reagents\items\spray\waterflower.dm"
#include "code\modules\reagents\machinery\chem_master.dm"
#include "code\modules\reagents\machinery\reagent_dispenser.dm"
#include "code\modules\reagents\machinery\dispenser\cartridge.dm"
@@ -4737,8 +4761,6 @@
#include "code\modules\reagents\reagent_containers\patch.dm"
#include "code\modules\reagents\reagent_containers\pill.dm"
#include "code\modules\reagents\reagent_containers\pill_vr.dm"
-#include "code\modules\reagents\reagent_containers\spray.dm"
-#include "code\modules\reagents\reagent_containers\spray_vr.dm"
#include "code\modules\reagents\reagent_containers\syringes.dm"
#include "code\modules\reagents\reagent_containers\syringes_vr.dm"
#include "code\modules\reagents\reagent_containers\unidentified_hypospray.dm"
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index 53c7f16bb372..a2d26e2c8e64 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -146,8 +146,10 @@
if(istype(I, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RC = I
if(RC.is_open_container())
- for(var/datum/reagent/A in RC.reagents.reagent_list)
- .["other"][A.type] += A.volume
+ // todo: this shouldn't be by type.
+ for(var/id in RC.reagents.reagent_volumes)
+ var/datum/reagent/A = SSchemistry.fetch_reagent(id)
+ .["other"][A.type] += RC.reagents.reagent_volumes[id]
.["other"][I.type] += 1
/datum/component/personal_crafting/proc/check_tools(atom/a, datum/crafting_recipe/R, list/contents)
@@ -235,34 +237,17 @@
surroundings = get_environment(a, R.blacklist)
surroundings -= Deletion
if(ispath(A, /datum/reagent))
- var/datum/reagent/RG = new A
- var/datum/reagent/RGNT
+ var/datum/reagent/wanted_reagent = SSchemistry.fetch_reagent(A)
while(amt > 0)
var/obj/item/reagent_containers/RC = locate() in surroundings
- RG = RC.reagents.get_reagent(RG.id)
- if(RG)
- if(!locate(RG.type) in Deletion)
- Deletion += new RG.type()
- if(RG.volume > amt)
- RG.volume -= amt
- data = RG.data
- RC.reagents.conditional_update(RC)
- RG = locate(RG.type) in Deletion
- RG.volume = amt
- RG.data += data
+ surroundings -= RC
+ if(RC.reagents?.reagent_volumes?[wanted_reagent.id])
+ var/removing_volume = RC.reagents.reagent_volumes[wanted_reagent.id]
+ removing_volume = min(removing_volume, amt)
+ RC.reagents.remove_reagent(wanted_reagent.id, removing_volume)
+ amt -= removing_volume
+ if(amt <= 0)
continue main_loop
- else
- surroundings -= RC
- amt -= RG.volume
- RC.reagents.reagent_list -= RG
- RC.reagents.conditional_update(RC)
- RGNT = locate(RG.type) in Deletion
- RGNT.volume += RG.volume
- RGNT.data += RG.data
- qdel(RG)
- RC.on_reagent_change()
- else
- surroundings -= RC
else if(ispath(A, /obj/item/stack))
var/obj/item/stack/S
var/obj/item/stack/SD
@@ -296,14 +281,7 @@
for(var/M in R.parts)
partlist[M] = R.parts[M]
for(var/A in R.parts)
- if(istype(A, /datum/reagent))
- var/datum/reagent/RG = locate(A) in Deletion
- if(RG.volume > partlist[A])
- RG.volume = partlist[A]
- . += RG
- Deletion -= RG
- continue
- else if(istype(A, /obj/item/stack))
+ if(istype(A, /obj/item/stack))
var/obj/item/stack/ST = locate(A) in Deletion
if(ST.amount > partlist[A])
ST.amount = partlist[A]
diff --git a/code/datums/material_container.dm b/code/datums/material_container.dm
index f372fb790ebf..d2cfc8935f90 100644
--- a/code/datums/material_container.dm
+++ b/code/datums/material_container.dm
@@ -214,14 +214,14 @@
/**
* basically, divides by given resources and takes lowest.
*/
-/datum/material_container/proc/has_multiple(list/wanted, multiplier = 1)
+/datum/material_container/proc/has_multiple(list/wanted)
if(isnull(stored))
return 0
. = INFINITY
for(var/key in wanted)
if(!wanted[key])
continue
- . = min(., stored[key] / (wanted[key] * multiplier))
+ . = min(., stored[key] / wanted[key])
if(!.)
return
diff --git a/code/datums/recipe/recipe.dm b/code/datums/recipe/recipe.dm
index 2bb732f9cc96..ee2cd529332d 100644
--- a/code/datums/recipe/recipe.dm
+++ b/code/datums/recipe/recipe.dm
@@ -92,7 +92,7 @@
else
return -1
- if ((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len)
+ if ((reagents?(reagents.len):(0)) < avail_reagents.reagent_volumes.len)
return 0
return .
@@ -268,26 +268,20 @@
if (RECIPE_REAGENT_MAX)
//We want the highest of each.
//Iterate through everything in buffer. If the target has less than the buffer, then top it up
- for (var/datum/reagent/R in temp.reagents.reagent_list)
- var/rvol = tempholder.reagents.get_reagent_amount(R.id)
- if (rvol < R.volume)
- //Transfer the difference
- temp.reagents.trans_id_to(tempholder, R.id, R.volume-rvol)
+ for(var/id in temp.reagents.reagent_volumes)
+ var/volume = temp.reagents.reagent_volumes[id]
+ if(tempholder.reagents.reagent_volumes[id] < volume)
+ temp.reagents.trans_id_to(tempholder, id, volume - tempholder.reagents.reagent_volumes[id])
if (RECIPE_REAGENT_MIN)
//Min is slightly more complex. We want the result to have the lowest from each side
//But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used
- for (var/datum/reagent/R in temp.reagents.reagent_list)
- var/rvol = tempholder.reagents.get_reagent_amount(R.id)
- if (rvol == 0) //If the target has zero of this reagent
- temp.reagents.trans_id_to(tempholder, R.id, R.volume)
- //Then transfer all of ours
-
- else if (rvol > R.volume)
- //if the target has more than ours
- //Remove the difference
- tempholder.reagents.remove_reagent(R.id, rvol-R.volume)
-
+ for(var/id in temp.reagents.reagent_volumes)
+ var/volume = temp.reagents.reagent_volumes[id]
+ if(!tempholder.reagents.reagent_volumes[id])
+ temp.reagents.trans_id_to(tempholder, id, volume)
+ else if(tempholder.reagents.reagent_volumes[id] > volume)
+ temp.reagents.remove_reagent(id, tempholder.reagents.reagent_volumes[id] - volume)
if (results.len > 1)
//If we're here, then holder is a buffer containing the total reagents for all the results.
diff --git a/code/game/atoms/_atom.dm b/code/game/atoms/_atom.dm
index e4e9097fd3ed..db4b0a483b88 100644
--- a/code/game/atoms/_atom.dm
+++ b/code/game/atoms/_atom.dm
@@ -95,6 +95,8 @@
//? Chemistry
// todo: properly finalize the semantics of this variable and what it's for.
+ // todo: should this variable even exist? most atoms don't need this, and we can easily have an APi
+ // to fetch a relevant holder upon being inspected by an analyzer.
var/datum/reagent_holder/reagents = null
//? Detective Work
@@ -297,7 +299,6 @@
found += A.search_contents_for(path,filter_path)
return found
-
// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set.
// see code/modules/mob/mob_movement.dm for more.
/atom/proc/relaymove()
@@ -671,12 +672,13 @@
/atom/proc/CheckParts(list/parts_list)
for(var/A in parts_list)
- if(istype(A, /datum/reagent))
- if(!reagents)
- reagents = new()
- reagents.reagent_list.Add(A)
- reagents.conditional_update()
- else if(ismovable(A))
+ // todo: i don't know why we do this in crafting but crafting needs fucking refactored lmao
+ // if(istype(A, /datum/reagent))
+ // if(!reagents)
+ // reagents = new()
+ // reagents.reagent_list.Add(A)
+ // reagents.conditional_update()
+ if(ismovable(A))
var/atom/movable/M = A
M.forceMove(src)
diff --git a/code/game/atoms/atom-examine.dm b/code/game/atoms/atom-examine.dm
index 3d734f0a984c..003abd8438e9 100644
--- a/code/game/atoms/atom-examine.dm
+++ b/code/game/atoms/atom-examine.dm
@@ -73,19 +73,19 @@
if(reagents)
if(reagents.reagents_holder_flags & TRANSPARENT)
. += "It contains:"
- if(length(reagents.reagent_list))
+ if(reagents.total_volume)
var/has_alcohol = FALSE
if(user.can_see_reagents()) //Show each individual reagent
- for(var/datum/reagent/current_reagent as anything in reagents.reagent_list)
+ for(var/datum/reagent/current_reagent as anything in reagents.get_reagent_datums())
if(!has_alcohol && istype(current_reagent,/datum/reagent/ethanol))
has_alcohol = TRUE
- . += "• [round(current_reagent.volume, 0.01)] units of [current_reagent.name]"
+ . += "• [round(reagents.reagent_volumes[current_reagent.id], 0.01)] units of [current_reagent.name]"
else //Otherwise, just show the total volume
var/total_volume = 0
- for(var/datum/reagent/current_reagent as anything in reagents.reagent_list)
+ for(var/datum/reagent/current_reagent as anything in reagents.get_reagent_datums())
if(!has_alcohol && istype(current_reagent,/datum/reagent/ethanol))
has_alcohol = TRUE
- total_volume += current_reagent.volume
+ total_volume += reagents.reagent_volumes[current_reagent.id]
. += "[total_volume] units of various reagents"
if(has_alcohol)
. += "It smells of alcohol."
diff --git a/code/game/atoms/movable/pulling.dm b/code/game/atoms/movable/pulling.dm
index f02c792439fc..f219e6ad36a7 100644
--- a/code/game/atoms/movable/pulling.dm
+++ b/code/game/atoms/movable/pulling.dm
@@ -119,11 +119,11 @@
if(H.species.species_flags & NO_BLOOD)
bloodtrail = 0
else
- var/blood_volume = H.vessel.get_reagent_amount("blood")
+ var/blood_volume = H.blood_holder.get_total_volume()
if(blood_volume < H.species?.blood_volume * H.species?.blood_level_fatal)
bloodtrail = 0 //Most of it's gone already, just leave it be
else
- H.vessel.remove_reagent("blood", 1)
+ H.blood_holder?.draw(1)
if(bloodtrail)
var/turf/location = M.loc
if(istype(location, /turf/simulated))
@@ -144,11 +144,11 @@
if(H.species.species_flags & NO_BLOOD)
bloodtrail = 0
else
- var/blood_volume = H.vessel.get_reagent_amount("blood")
+ var/blood_volume = H.blood_holder.get_total_volume()
if(blood_volume < H.species?.blood_volume * H.species?.blood_level_fatal)
bloodtrail = 0 //Most of it's gone already, just leave it be
else
- H.vessel.remove_reagent("blood", 1)
+ H.blood_holder?.draw(1)
if(bloodtrail)
if(istype(location, /turf/simulated))
location.add_blood(M)
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 1956e08c89e1..17c126b3f381 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -389,9 +389,7 @@
data["beakerVolume"] = 0
if(connected.beaker)
data["beakerLabel"] = connected.beaker.label_text ? connected.beaker.label_text : null
- if (connected.beaker.reagents && connected.beaker.reagents.reagent_list.len)
- for(var/datum/reagent/R in connected.beaker.reagents.reagent_list)
- data["beakerVolume"] += R.volume
+ data["beakerVolume"] = connected.beaker.reagents?.total_volume || 0
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
diff --git a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm
index 9ecf521ff101..17a0475630c2 100644
--- a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm
+++ b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm
@@ -50,10 +50,10 @@
description = "Reduces stun times, but causing toxicity due to high concentration."
reagent_state = REAGENT_LIQUID
color = "#C8A5DC"
- metabolism = REM * 2
+ metabolism_rate = REM * 2
overdose = 5 //This is intentionally low, as we want the ling to take some tox damage, to discourage spamming the ability.
-/datum/reagent/epinephrine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/epinephrine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.add_chemical_effect(CE_SPEEDBOOST, 3)
diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm
index aeeff476bc82..708034f934b3 100644
--- a/code/game/gamemodes/cult/construct_spells.dm
+++ b/code/game/gamemodes/cult/construct_spells.dm
@@ -473,8 +473,8 @@
var/mob/living/carbon/human/H = owner
if(!H.should_have_organ(O_HEART))
return 1
- if(H.vessel.remove_reagent("blood", amount))
- return 1
+ if(H.erase_checked_blood(amount))
+ return TRUE
return 0
/obj/item/spell/construct/afterattack(atom/target, mob/user, clickchain_flags, list/params)
diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm
index abfa87f6cf79..3c051228fcff 100644
--- a/code/game/gamemodes/cult/narsie.dm
+++ b/code/game/gamemodes/cult/narsie.dm
@@ -189,8 +189,6 @@ var/global/list/narsie_list = list()
if (dist <= consume_range && !istype(A, /turf/space))
var/turf/T = A
- if(T.holy)
- T.holy = 0 //Nar-Sie doesn't give a shit about sacred grounds.
T.cultify()
/obj/singularity/narsie/proc/old_narsie(const/atom/A)
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 14d54e5a7a68..c49c3e0a93a6 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -253,11 +253,11 @@
beaker.reagents,
min(
remaining_beaker_volume_for_dialysis * (3 / 4),
- length(occupant.reagents.reagent_list) * 3,
+ length(occupant.reagents.reagent_volumes) * 3,
),
dialysis_reagent_filter_flags,
)
- occupant.vessel.trans_to_holder(beaker.reagents, filtered_volume * (1 / 3))
+ occupant.take_blood_legacy(beaker, filtered_volume * (1 / 3))
else
toggle_filter()
@@ -268,7 +268,7 @@
beaker.reagents,
min(
beaker.reagents.maximum_volume - beaker.reagents.total_volume,
- length(occupant.ingested.reagent_list) * 3,
+ length(occupant.ingested.reagent_volumes) * 3,
),
dialysis_reagent_filter_flags,
)
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index ae3898fc44ea..70158b200c54 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -292,8 +292,8 @@
occupantData["hasBorer"] = H.has_brain_worms()
var/bloodData[0]
- if(H.vessel)
- var/blood_volume = round(H.vessel.get_reagent_amount("blood"))
+ if(H.blood_holder)
+ var/blood_volume = round(H.blood_holder.get_total_volume())
var/blood_max = H.species.blood_volume
bloodData["volume"] = blood_volume
bloodData["percent"] = round(((blood_volume / blood_max)*100))
@@ -301,18 +301,22 @@
occupantData["blood"] = bloodData
var/reagentData[0]
- if(H.reagents.reagent_list.len >= 1)
- for(var/datum/reagent/R in H.reagents.reagent_list)
- reagentData[++reagentData.len] = list("name" = R.name, "amount" = R.volume)
+ if(length(H.reagents.reagent_volumes))
+ for(var/id in H.reagents?.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = H.reagents.reagent_volumes[id]
+ reagentData[++reagentData.len] = list("name" = R.name, "amount" = volume)
else
reagentData = null
occupantData["reagents"] = reagentData
var/ingestedData[0]
- if(H.ingested.reagent_list.len >= 1)
- for(var/datum/reagent/R in H.ingested.reagent_list)
- ingestedData[++ingestedData.len] = list("name" = R.name, "amount" = R.volume)
+ if(length(H.ingested.reagent_volumes))
+ for(var/id in H.ingested?.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = H.ingested.reagent_volumes[id]
+ ingestedData[++ingestedData.len] = list("name" = R.name, "amount" = volume)
else
ingestedData = null
@@ -479,8 +483,8 @@
if(occupant.has_brain_worms())
dat += "Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
"
- if(occupant.vessel)
- var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
+ if(occupant.blood_holder)
+ var/blood_volume = round(occupant.blood_holder.get_total_volume())
var/blood_max = occupant.species.blood_volume
var/blood_percent = blood_volume / blood_max
blood_percent *= 100
@@ -488,13 +492,15 @@
extra_font = " 448 ? "blue" : "red"]>"
dat += "[extra_font]\tBlood Level %: [blood_percent] ([blood_volume] units)
"
- if(occupant.reagents)
- for(var/datum/reagent/R in occupant.reagents.reagent_list)
- dat += "Reagent: [R.name], Amount: [R.volume]
"
+ for(var/id in occupant.reagents?.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = occupant.reagents.reagent_volumes[id]
+ dat += "Reagent: [R.name], Amount: [volume]
"
- if(occupant.ingested)
- for(var/datum/reagent/R in occupant.ingested.reagent_list)
- dat += "Stomach: [R.name], Amount: [R.volume]
"
+ for(var/id in occupant.ingested?.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = occupant.ingested.reagent_volumes[id]
+ dat += "Stomach: [R.name], Amount: [volume]
"
dat += "
"
dat += ""
diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm
index b9a8ce78f739..5cb108fc872d 100644
--- a/code/game/machinery/bioprinter.dm
+++ b/code/game/machinery/bioprinter.dm
@@ -21,8 +21,8 @@
var/base_print_delay = 100
///Is the bioprinter printing
var/printing = FALSE
- ///Blood sample for DNA hashing.
- var/loaded_dna
+ /// Blood sample for DNA hashing.
+ var/datum/blood_mixture/loaded_blood_mixture
///May cause rejection, or the printing of some alien limb instead!
var/malfunctioning = FALSE
///Can it print more 'complex' organs?
@@ -206,13 +206,7 @@
/// Checks for reagents, then reports how much biomass it has in it.
/obj/machinery/organ_printer/proc/get_biomass_volume()
- var/biomass_count = 0
- if(container && container.reagents)
- for(var/datum/reagent/R in container.reagents.reagent_list)
- if(R.id == "biomass")
- biomass_count += R.volume
-
- return biomass_count
+ return container.reagents?.reagent_volumes?[/datum/reagent/nutriment/biomass::id]
/obj/machinery/organ_printer/proc/can_print(choice, biomass_needed = 0)
var/biomass = get_biomass_volume()
@@ -220,7 +214,8 @@
visible_message(SPAN_INFO("\The [src] displays a warning: 'Not enough biomass. [biomass] stored and [biomass_needed] needed.'"))
return FALSE
- if(!loaded_dna || !loaded_dna["donor"])
+ var/datum/blood_fragment/using_fragment = (length(loaded_blood_mixture?.fragments) && loaded_blood_mixture.fragments[1]) || null
+ if(!using_fragment)
visible_message(SPAN_INFO("\The [src] displays a warning: 'No DNA saved. Insert a blood sample.'"))
return FALSE
return TRUE
@@ -229,7 +224,8 @@
var/new_organ = choice
var/obj/item/organ/O = new new_organ(get_turf(src))
O.status |= ORGAN_CUT_AWAY
- var/mob/living/carbon/human/C = loaded_dna["donor"]
+ var/datum/blood_fragment/using_fragment = loaded_blood_mixture.fragments[1]
+ var/mob/living/carbon/human/C = using_fragment.legacy_donor
O.set_dna(C.dna)
O.species = C.species
@@ -299,10 +295,9 @@
// DNA sample from syringe.
if(istype(W,/obj/item/reagent_containers/syringe)) //TODO: Make this actually empty the syringe
var/obj/item/reagent_containers/syringe/S = W
- var/datum/reagent/blood/injected = locate() in S.reagents.reagent_list //Grab some blood
- if(injected && injected.data)
- loaded_dna = injected.data
- S.reagents.remove_reagent("blood", injected.volume)
+ var/datum/blood_mixture/mixture = S.reagents?.reagent_datas[/datum/reagent/blood::id]
+ if((loaded_blood_mixture = mixture))
+ S.reagents.del_reagent(/datum/reagent/blood)
to_chat(user, SPAN_INFO("You scan the blood sample into the bioprinter."))
return
else if(istype(W,/obj/item/reagent_containers/glass))
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 4e203298c624..98647ace0ea1 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -344,34 +344,17 @@
// Returns the total amount of biomass reagent in all of the pod's stored containers
/obj/machinery/clonepod/proc/get_biomass()
var/biomass_count = 0
- if(LAZYLEN(containers))
- for(var/obj/item/reagent_containers/glass/G in containers)
- for(var/datum/reagent/R in G.reagents.reagent_list)
- if(R.id == "biomass")
- biomass_count += R.volume
-
+ for(var/obj/item/reagent_containers/container in containers)
+ biomass_count += container.reagents?.reagent_volumes?[/datum/reagent/nutriment/biomass::id]
return biomass_count
// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though.
-/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone
- var/to_remove = 0 // Tracks how much biomass has been found so far
- if(LAZYLEN(containers))
- for(var/obj/item/reagent_containers/glass/G in containers)
- if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers
- for(var/datum/reagent/R in G.reagents.reagent_list)
- if(R.id == "biomass") // Finds Biomass
- var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container
- if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need
- R.remove_self(need_remove)
- to_remove = amount
- else //Otherwise, take everything and move on
- to_remove += R.volume
- R.remove_self(R.volume)
- else
- continue
- else
- return 1
- return 0
+/obj/machinery/clonepod/proc/remove_biomass(amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone
+ for(var/obj/item/reagent_containers/glass/beaker in containers)
+ amount -= beaker.reagents.remove_reagent(/datum/reagent/nutriment/biomass, amount)
+ if(!amount)
+ return TRUE
+ return !amount
// Empties all of the beakers from the cloning pod, used to refill it
/obj/machinery/clonepod/verb/empty_beakers()
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 853f74f96f03..bbc6c28f274d 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -110,10 +110,10 @@
occupantData["btCelsius"] = occupant.bodytemperature - T0C
occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32
- if(ishuman(occupant) && !(NO_BLOOD in occupant.species.species_flags) && occupant.vessel)
+ if(ishuman(occupant) && !(NO_BLOOD in occupant.species.species_flags) && occupant.blood_holder)
occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
occupantData["hasBlood"] = 1
- var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
+ var/blood_volume = round(occupant.blood_holder.get_total_volume())
occupantData["bloodLevel"] = blood_volume
occupantData["bloodMax"] = occupant.species.blood_volume
occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here
diff --git a/code/game/machinery/computer/arcade/_arcade.dm b/code/game/machinery/computer/arcade/_arcade.dm
index 93529c4d883e..7ff9acca744a 100644
--- a/code/game/machinery/computer/arcade/_arcade.dm
+++ b/code/game/machinery/computer/arcade/_arcade.dm
@@ -19,7 +19,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/toy/prize/mauler = 1,
/obj/item/toy/prize/odysseus = 1,
/obj/item/toy/prize/phazon = 1,
- /obj/item/toy/waterflower = 1,
+ /obj/item/reagent_containers/spray/waterflower = 1,
/obj/random/action_figure = 1,
/obj/random/plushie = 1,
/obj/item/toy/cultsword = 1,
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 6f4ef4ab70b5..d6ba7bcfeb0c 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -139,9 +139,8 @@
data["beakerVolume"] = 0
if(beaker)
data["beakerLabel"] = beaker.label_text ? beaker.label_text : null
- if(beaker.reagents && beaker.reagents.reagent_list.len)
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- data["beakerVolume"] += R.volume
+ if(beaker.reagents)
+ data["beakerVolume"] = beaker.reagents?.total_volume
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index d652621f749a..a8fdadedd55a 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -113,7 +113,7 @@
if(91 to INFINITY)
filling_overlay.icon_state = "reagent100"
- filling_overlay.color = mix_color_from_reagents(target_reagents.reagent_list)
+ filling_overlay.color = target_reagents.get_color()
. += filling_overlay
/obj/machinery/iv_drip/OnMouseDropLegacy(mob/living/target)
@@ -218,8 +218,8 @@
visible_message(SPAN_HEAR("[src] beeps loudly."))
playsound(loc, 'sound/machines/twobeep_high.ogg', 50, TRUE)
var/atom/movable/target = reagent_container
- attached_victim.take_blood(target, amount)
- update_appearance()
+ if(attached_victim.take_blood_legacy(target, amount) > 0)
+ update_appearance()
/// Called when an IV is attached.
/obj/machinery/iv_drip/proc/attach_iv(mob/living/target, mob/user)
@@ -303,7 +303,7 @@
. += "[src] is [injection_mode ? "injecting" : "taking blood"]."
if(reagent_container)
- if(reagent_container.reagents && reagent_container.reagents.reagent_list.len)
+ if(reagent_container.reagents?.total_volume)
. += SPAN_NOTICE("Attached is \a [reagent_container] with [reagent_container.reagents.total_volume] units of liquid.")
else
. += SPAN_NOTICE("Attached is an empty [reagent_container.name].")
diff --git a/code/game/machinery/lathes/lathe.dm b/code/game/machinery/lathes/lathe.dm
index c5e622c1fe6d..472cb48dfa32 100644
--- a/code/game/machinery/lathes/lathe.dm
+++ b/code/game/machinery/lathes/lathe.dm
@@ -288,11 +288,11 @@
for(var/key in instance.material_costs)
var/id = material_parts[key]
materials[id] += instance.material_costs[key]
- . = stored_materials.has_multiple(materials, efficiency_multiplier)
+ . = stored_materials.has_multiple(materials) / efficiency_multiplier
if(!.)
return
if(length(instance.reagents))
- . = min(., stored_reagents?.has_multiple(instance.reagents, efficiency_multiplier))
+ . = min(., stored_reagents?.has_multiple(instance.reagents) / efficiency_multiplier)
if(!.)
return
// ingredients? return 1 at most.
diff --git a/code/game/machinery/vending/plants.dm b/code/game/machinery/vending/plants.dm
index 07116a335ee8..49c0675e2f69 100644
--- a/code/game/machinery/vending/plants.dm
+++ b/code/game/machinery/vending/plants.dm
@@ -80,7 +80,7 @@
/obj/item/seeds/shandseed = 2,
)
premium = list(
- /obj/item/toy/waterflower = 1,
+ /obj/item/reagent_containers/spray/waterflower = 1,
/obj/item/toy/gnome = 1,
)
diff --git a/code/game/objects/effects/chem/coating.dm b/code/game/objects/effects/chem/coating.dm
index 246d22ffd5ae..c980a78a9a48 100644
--- a/code/game/objects/effects/chem/coating.dm
+++ b/code/game/objects/effects/chem/coating.dm
@@ -19,18 +19,11 @@
continue
if(istype(O, /obj/effect/debris/cleanable/chemcoating))
var/obj/effect/debris/cleanable/chemcoating/C = O
- if(C.reagents && C.reagents.reagent_list.len)
+ if(C.reagents?.total_volume)
C.reagents.trans_to_obj(src,C.reagents.total_volume)
qdel(O)
-/obj/effect/debris/cleanable/chemcoating/Bumped(A as mob|obj)
- if(reagents)
- reagents.touch(A)
- return ..()
-
-/obj/effect/debris/cleanable/chemcoating/Crossed(AM as mob|obj)
- . = ..()
- Bumped(AM)
+// todo: apply to whole body or just feet depending on if they're laying down, when crossed by mob or obj
/obj/effect/debris/cleanable/chemcoating/update_icon()
..()
diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm
index 0508452dba0d..490d1d577c2b 100644
--- a/code/game/objects/effects/chem/foam.dm
+++ b/code/game/objects/effects/chem/foam.dm
@@ -69,8 +69,8 @@
if(!metal)
F.create_reagents(10)
if(reagents)
- for(var/datum/reagent/R in reagents.reagent_list)
- F.reagents.add_reagent(R.id, 1, null, TRUE)
+ for(var/id in reagents.reagent_volumes)
+ F.reagents.add_reagent(id, 1, null, TRUE)
/obj/effect/foam/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) // foam disolves when heated, except metal foams
if(!metal && prob(max(0, exposed_temperature - 475)))
diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm
index 52ed441d1982..11dbc8d26c55 100644
--- a/code/game/objects/items/contraband.dm
+++ b/code/game/objects/items/contraband.dm
@@ -40,7 +40,7 @@
reagents.add_reagent(reagent, picked_reagents[reagent])
var/list/names = new
- for(var/datum/reagent/R in reagents.reagent_list)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
names += R.name
desc = "Contains [english_list(names)]."
diff --git a/code/game/objects/items/defib/shockpaddles.dm b/code/game/objects/items/defib/shockpaddles.dm
index 6194487dc220..f6b9d4ea2c26 100644
--- a/code/game/objects/items/defib/shockpaddles.dm
+++ b/code/game/objects/items/defib/shockpaddles.dm
@@ -134,7 +134,7 @@
if(!heart)
return TRUE
- var/blood_volume = H.vessel.get_reagent_amount("blood")
+ var/blood_volume = H.blood_holder.get_total_volume()
if(!heart || heart.is_broken())
blood_volume *= 0.3
else if(heart.is_bruised())
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index f75e992a18f8..a9bc680d5a5e 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -1414,10 +1414,10 @@ GLOBAL_LIST_EMPTY(PDAs)
if(!isobj(target))
return
if(!isnull(target.reagents))
- if(target.reagents.reagent_list.len > 0)
- var/reagents_length = target.reagents.reagent_list.len
+ if(target.reagents.total_volume)
+ var/reagents_length = length(target.reagents.reagent_volumes)
to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.")
- for (var/re in target.reagents.reagent_list)
+ for (var/re in target.reagents.get_reagent_datums())
to_chat(user," [re]")
else
to_chat(user,"No active chemical agents found in [target].")
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 5e4f88d287b6..0d6aefd11a30 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -175,11 +175,18 @@
if(L.getBrainLoss() > 15)
to_chat(user, SPAN_NOTICE("There's visible lag between left and right pupils' reactions."))
- var/list/pinpoint = list("oxycodone"=1,"tramadol"=5)
- var/list/dilating = list("space_drugs"=5,"mindbreaker"=1)
- if(L.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint))
+ // todo: reagent effects.
+ var/static/list/reagents_that_cause_constriction = list(
+ /datum/reagent/tramadol,
+ /datum/reagent/oxycodone,
+ )
+ var/static/list/reagents_that_cause_dilation = list(
+ /datum/reagent/space_drugs,
+ /datum/reagent/mindbreaker,
+ )
+ if(L.reagents.has_any(reagents_that_cause_constriction) || H.ingested.has_any(reagents_that_cause_constriction))
to_chat(user, SPAN_NOTICE("\The [L]'s pupils are already pinpoint and cannot narrow any more."))
- else if(L.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating))
+ else if(L.reagents.has_any(reagents_that_cause_dilation) || H.ingested.has_any(reagents_that_cause_dilation))
to_chat(user, SPAN_NOTICE("\The [L]'s pupils narrow slightly, but are still very dilated."))
else
to_chat(user, SPAN_NOTICE("\The [L]'s pupils narrow."))
diff --git a/code/game/objects/items/scanners/health.dm b/code/game/objects/items/scanners/health.dm
index a5a907d2b98b..f4a5cd42bc24 100644
--- a/code/game/objects/items/scanners/health.dm
+++ b/code/game/objects/items/scanners/health.dm
@@ -122,8 +122,8 @@
var/unknown = 0
var/reagentdata[0]
var/unknownreagents[0]
- for(var/A in C.reagents.reagent_list)
- var/datum/reagent/R = A
+ for(var/A in C.reagents.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(A)
if(R.scannable)
reagentdata["[R.id]"] = SPAN_NOTICE("\n[round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name]")
else
@@ -144,8 +144,8 @@
var/unknown = 0
var/stomachreagentdata[0]
var/stomachunknownreagents[0]
- for(var/B in C.ingested.reagent_list)
- var/datum/reagent/T = B
+ for(var/B in C.ingested.reagent_volumes)
+ var/datum/reagent/T = SSchemistry.fetch_reagent(B)
if(T.scannable)
stomachreagentdata["[T.id]"] = SPAN_NOTICE("\n[round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]")
if (advscan == 0 || showadvscan == 0)
@@ -168,8 +168,8 @@
var/unknown = 0
var/touchreagentdata[0]
var/touchunknownreagents[0]
- for(var/B in C.touching.reagent_list)
- var/datum/reagent/T = B
+ for(var/B in C.touching.reagent_volumes)
+ var/datum/reagent/T = SSchemistry.fetch_reagent(B)
if(T.scannable)
touchreagentdata["[T.id]"] = SPAN_NOTICE("\n[round(C.touching.get_reagent_amount(T.id), 1)]u [T.name]")
if (advscan == 0 || showadvscan == 0)
@@ -258,7 +258,7 @@
// Blood level
if(M:vessel)
- var/blood_volume = H.vessel.get_reagent_amount("blood")
+ var/blood_volume = H.blood_holder.get_total_volume()
var/blood_percent = round((blood_volume / H.species.blood_volume)*100)
var/blood_type = H.dna.b_type
if(blood_volume <= H.species.blood_volume*H.species.blood_level_danger)
diff --git a/code/game/objects/items/scanners/plant.dm b/code/game/objects/items/scanners/plant.dm
index b517b638c1cc..73acc8b91f3c 100644
--- a/code/game/objects/items/scanners/plant.dm
+++ b/code/game/objects/items/scanners/plant.dm
@@ -90,12 +90,12 @@
user.visible_message("[user] runs the scanner over \the [target].")
last_reagents = list()
- if(grown_reagents && grown_reagents.reagent_list && grown_reagents.reagent_list.len)
- for(var/datum/reagent/R in grown_reagents.reagent_list)
- last_reagents.Add(list(list(
- "name" = R.name,
- "volume" = grown_reagents.get_reagent_amount(R.id),
- )))
+ for(var/reagent_id in grown_reagents?.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(reagent_id)
+ last_reagents.Add(list(list(
+ "name" = R.name,
+ "volume" = grown_reagents.get_reagent_amount(R.id),
+ )))
ui_interact(user)
diff --git a/code/game/objects/items/scanners/reagent.dm b/code/game/objects/items/scanners/reagent.dm
index b76223f485bf..c32663a78554 100644
--- a/code/game/objects/items/scanners/reagent.dm
+++ b/code/game/objects/items/scanners/reagent.dm
@@ -27,10 +27,11 @@
return
var/dat = ""
- if(target.reagents.reagent_list.len > 0)
+ if(target.reagents.reagent_volumes.len > 0)
var/one_percent = target.reagents.total_volume / 100
- for (var/datum/reagent/R in target.reagents.reagent_list)
- dat += "\n \t " + SPAN_NOTICE("[R][details ? ": [R.volume / one_percent]%" : ""]")
+ for (var/id in target.reagents.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ dat += "\n \t " + SPAN_NOTICE("[R][details ? ": [target.reagents.reagent_volumes[id] / one_percent]%" : ""]")
if(dat)
to_chat(user, SPAN_NOTICE("Chemicals found: [dat]"))
else
diff --git a/code/game/objects/items/scanners/spectrometer.dm b/code/game/objects/items/scanners/spectrometer.dm
index d5619348f47a..8668ba41ada6 100644
--- a/code/game/objects/items/scanners/spectrometer.dm
+++ b/code/game/objects/items/scanners/spectrometer.dm
@@ -39,13 +39,13 @@
return
if(reagents.total_volume)
var/list/blood_traces = list()
- for(var/datum/reagent/R in reagents.reagent_list)
- if(R.id != "blood")
- reagents.clear_reagents()
- to_chat(user, "The sample was contaminated! Please insert another sample")
- return
+ for(var/id in reagents.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ if(R.type != /datum/reagent/blood)
+ continue
else
- blood_traces = params2list(R.data["trace_chem"])
+ var/datum/blood_mixture/mixture = reagents.reagent_datas?[id]
+ blood_traces = params2list(mixture.legacy_trace_chem)
break
var/dat = "Trace Chemicals Found: "
for(var/R in blood_traces)
diff --git a/code/game/objects/items/toys/balloon.dm b/code/game/objects/items/toys/balloon.dm
new file mode 100644
index 000000000000..680d76fb2b0d
--- /dev/null
+++ b/code/game/objects/items/toys/balloon.dm
@@ -0,0 +1,60 @@
+/obj/item/toy/balloon
+ name = "water balloon"
+ desc = "A translucent balloon. There's nothing in it."
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "waterballoon-e"
+ damage_force = 0
+
+/obj/item/toy/balloon/Initialize(mapload)
+ . = ..()
+ create_reagents(10)
+
+/obj/item/toy/balloon/afterattack(atom/target, mob/user, clickchain_flags, list/params)
+ if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return
+ if (istype(target, /obj/structure/reagent_dispensers/watertank) && get_dist(src,target) <= 1)
+ target.reagents.trans_to_obj(src, 10)
+ to_chat(user, "You fill the balloon with the contents of [target].")
+ src.desc = "A translucent balloon with some form of liquid sloshing around in it."
+ src.update_icon()
+ return
+
+/obj/item/toy/balloon/attackby(obj/O as obj, mob/user as mob)
+ if(istype(O, /obj/item/reagent_containers/glass))
+ if(O.reagents)
+ if(O.reagents.total_volume < 1)
+ to_chat(user, "The [O] is empty.")
+ else if(O.reagents.total_volume >= 1)
+ if(O.reagents.has_reagent("pacid", 1))
+ to_chat(user, "The acid chews through the balloon!")
+ O.reagents.splash(user, reagents.total_volume)
+ qdel(src)
+ else
+ src.desc = "A translucent balloon with some form of liquid sloshing around in it."
+ to_chat(user, "You fill the balloon with the contents of [O].")
+ O.reagents.trans_to_obj(src, 10)
+ src.update_icon()
+ return
+
+/obj/item/toy/balloon/throw_impact(atom/A, datum/thrownthing/TT)
+ . = ..()
+ if(!(TT.throw_flags & THROW_AT_IS_GENTLE))
+ if(src.reagents.total_volume >= 1)
+ src.visible_message("\The [src] bursts!","You hear a pop and a splash.")
+ reagents.auto_spill(A, 1, TRUE, FALSE)
+ src.icon_state = "burst"
+ QDEL_IN(src, 5)
+
+/obj/item/toy/balloon/throw_land(atom/A, datum/thrownthing/TT)
+ . = ..()
+ if(!(TT.throw_flags & THROW_AT_IS_GENTLE))
+ if(src.reagents.total_volume >= 1)
+ src.visible_message("\The [src] bursts!","You hear a pop and a splash.")
+ reagents.auto_spill(A, 1, TRUE, FALSE)
+ src.icon_state = "burst"
+ QDEL_IN(src, 5)
+
+/obj/item/toy/balloon/update_icon()
+ if(src.reagents.total_volume >= 1)
+ icon_state = "waterballoon"
+ else
+ icon_state = "waterballoon-e"
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys/toys.dm
similarity index 93%
rename from code/game/objects/items/toys.dm
rename to code/game/objects/items/toys/toys.dm
index 069da682a19a..02faa2c0f852 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys/toys.dm
@@ -27,64 +27,9 @@
throw_range = 20
damage_force = 0
-
/*
* Balloons
*/
-/obj/item/toy/balloon
- name = "water balloon"
- desc = "A translucent balloon. There's nothing in it."
- icon = 'icons/obj/toy.dmi'
- icon_state = "waterballoon-e"
- damage_force = 0
-
-/obj/item/toy/balloon/Initialize(mapload)
- . = ..()
- create_reagents(10)
-
-/obj/item/toy/balloon/afterattack(atom/target, mob/user, clickchain_flags, list/params)
- if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return
- if (istype(target, /obj/structure/reagent_dispensers/watertank) && get_dist(src,target) <= 1)
- target.reagents.trans_to_obj(src, 10)
- to_chat(user, "You fill the balloon with the contents of [target].")
- src.desc = "A translucent balloon with some form of liquid sloshing around in it."
- src.update_icon()
- return
-
-/obj/item/toy/balloon/attackby(obj/O as obj, mob/user as mob)
- if(istype(O, /obj/item/reagent_containers/glass))
- if(O.reagents)
- if(O.reagents.total_volume < 1)
- to_chat(user, "The [O] is empty.")
- else if(O.reagents.total_volume >= 1)
- if(O.reagents.has_reagent("pacid", 1))
- to_chat(user, "The acid chews through the balloon!")
- O.reagents.splash(user, reagents.total_volume)
- qdel(src)
- else
- src.desc = "A translucent balloon with some form of liquid sloshing around in it."
- to_chat(user, "You fill the balloon with the contents of [O].")
- O.reagents.trans_to_obj(src, 10)
- src.update_icon()
- return
-
-/obj/item/toy/balloon/throw_impact(atom/hit_atom)
- if(src.reagents.total_volume >= 1)
- src.visible_message("\The [src] bursts!","You hear a pop and a splash.")
- src.reagents.touch_turf(get_turf(hit_atom))
- for(var/atom/A in get_turf(hit_atom))
- src.reagents.touch(A)
- src.icon_state = "burst"
- spawn(5)
- if(src)
- qdel(src)
- return
-
-/obj/item/toy/balloon/update_icon()
- if(src.reagents.total_volume >= 1)
- icon_state = "waterballoon"
- else
- icon_state = "waterballoon-e"
/obj/item/toy/syndicateballoon
name = "criminal balloon"
@@ -401,70 +346,6 @@
playsound(src, 'sound/effects/snap.ogg', 50, 1)
qdel(src)
-/*
- * Water flower
- */
-/obj/item/toy/waterflower
- name = "water flower"
- desc = "A seemingly innocent sunflower...with a twist."
- icon = 'icons/obj/device.dmi'
- icon_state = "sunflower"
- item_state = "sunflower"
- var/empty = 0
- slot_flags = SLOT_HOLSTER
- damage_force = 0
-
-/obj/item/toy/waterflower/Initialize(mapload)
- . = ..()
- var/datum/reagent_holder/R = create_reagents(10)
- R.add_reagent("water", 10)
-
-/obj/item/toy/waterflower/afterattack(atom/target, mob/user, clickchain_flags, list/params)
-
- if (istype(target, /obj/item/storage/backpack ))
- return
-
- else if (locate (/obj/structure/table, src.loc))
- return
-
- else if (istype(target, /obj/structure/reagent_dispensers/watertank) && get_dist(src,target) <= 1)
- target.reagents.trans_to(src, 10)
- to_chat(user, "You refill your flower!")
- return
-
- else if (src.reagents.total_volume < 1)
- src.empty = 1
- to_chat(user, "Your flower has run dry!")
- return
-
- else
- src.empty = 0
-
-
- var/obj/effect/decal/D = new/obj/effect/decal/(get_turf(src))
- D.name = "water"
- D.icon = 'icons/obj/medical/chemical.dmi'
- D.icon_state = "chempuff"
- D.create_reagents(5)
- src.reagents.trans_to_obj(D, 1)
- playsound(src.loc, 'sound/effects/spray3.ogg', 50, 1, -6)
-
- spawn(0)
- for(var/i=0, i<1, i++)
- step_towards(D,target)
- D.reagents.touch_turf(get_turf(D))
- for(var/atom/T in get_turf(D))
- D.reagents.touch(T)
- if(ismob(T))
- to_chat(T, "\The [user] has sprayed you with water!")
- sleep(4)
- qdel(D)
-
- return
-
-/obj/item/toy/waterflower/examine(mob/user, dist)
- . = ..()
- . += "[src] has [src.reagents.total_volume] units of water left!"
/*
* Bosun's whistle
diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm
index c53397dcc2dd..aaaec4bc5b57 100644
--- a/code/game/objects/random/mapping.dm
+++ b/code/game/objects/random/mapping.dm
@@ -419,7 +419,7 @@
/obj/item/pda/clown,
/obj/item/clothing/mask/gas/clown_hat,
/obj/item/bikehorn,
- /obj/item/toy/waterflower,
+ /obj/item/reagent_containers/spray/waterflower,
/obj/item/pen/crayon/rainbow,
/obj/structure/closet/crate
),
diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm
index e7868cde8b70..f5003588af29 100644
--- a/code/game/objects/random/misc.dm
+++ b/code/game/objects/random/misc.dm
@@ -540,7 +540,7 @@
/obj/item/toy/balloon,
/obj/item/toy/crossbow,
/obj/item/toy/blink,
- /obj/item/toy/waterflower,
+ /obj/item/reagent_containers/spray/waterflower,
/obj/item/toy/eight_ball,
/obj/item/toy/eight_ball/conch,
/obj/item/toy/prize/ripley,
diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand.dm
similarity index 97%
rename from code/game/objects/structures/medical_stand_vr.dm
rename to code/game/objects/structures/medical_stand.dm
index 05f1c7681468..34149bf080e3 100644
--- a/code/game/objects/structures/medical_stand_vr.dm
+++ b/code/game/objects/structures/medical_stand.dm
@@ -423,15 +423,10 @@
return
// If the human is losing too much blood, beep.
- if(H.vessel.get_reagent_amount("blood") < H.species.blood_volume*H.species.blood_level_safe)
+ if(H.blood_holder.get_total_volume() < H.species.blood_volume*H.species.blood_level_safe)
visible_message("\The [src] beeps loudly.")
- var/datum/reagent/B = H.take_blood(beaker,amount)
- if (B)
- beaker.reagents.reagent_list |= B
- beaker.reagents.update_total()
- beaker.on_reagent_change()
- beaker.reagents.reconsider_reactions()
+ if(H.take_blood_legacy(beaker, amount) > 0)
update_icon()
if ((!valve_opened || tank.distribute_pressure == 0) && !breather && !attached)
diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm
index 999bdbbaa7e9..807fe344005a 100644
--- a/code/game/turfs/simulated.dm
+++ b/code/game/turfs/simulated.dm
@@ -34,6 +34,7 @@
return ..()
// This is not great.
+// todo: this is shit, rework wet floors to be component, element, or just a goddamn cached datum at this point
/turf/simulated/proc/wet_floor(var/wet_val = 1)
if(wet > 2) //Can't mop up ice
return
@@ -43,9 +44,9 @@
cut_overlay(wet_overlay)
wet_overlay = image('icons/effects/water.dmi', icon_state = "wet_floor")
add_overlay(wet_overlay)
- sleep(800)
+ sleep(1 MINUTES + 20 SECONDS)
if(wet == 2)
- sleep(3200)
+ sleep(5 MINUTES + 20 SECONDS)
wet = 0
if(wet_overlay)
cut_overlay(wet_overlay)
@@ -167,7 +168,7 @@
B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
B.virus2 = virus_copylist(M.virus2)
return 1 //we bloodied the floor
- blood_splatter(src,M.get_blood(M.vessel),1)
+ blood_splatter_legacy(src, M.get_blood_mixture(), TRUE)
return 1 //we bloodied the floor
return 0
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 62daecd72cc6..080c1d36241f 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -1045,42 +1045,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
v = Failsafe
if("CFG")
v = config
-/*
- //Subsystem switches
- if("SSgarbage")
- v = SSgarbage
- if("SSmachines")
- v = SSmachines
- if("SSobj")
- v = SSobj
- if("SSresearch")
- v = SSresearch
- if("SSprojectiles")
- v = SSprojectiles
- if("SSprocess_5fps")
- v = SSprocess_5fps
- if("SSticker")
- v = SSticker
- if("SStimer")
- v = SStimer
- if("SSradiation")
- v = SSradiation
- if("SSnpcpool")
- v = SSnpcpool
- if("SSmobs")
- v = SSmobs
- if("SSquirks")
- v = SSquirks
- if("SSwet_floors")
- v = SSwet_floors
- if("SSshuttle")
- v = SSshuttle
- if("SSmapping")
- v = SSmapping
- if("SSevents")
- v = SSevents
- //End
-*/
else
return null
else if(object == GLOB) // Shitty ass hack kill me.
diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm
index fdf66d521e5a..3e97be729505 100644
--- a/code/modules/detectivework/tools/rag.dm
+++ b/code/modules/detectivework/tools/rag.dm
@@ -190,8 +190,8 @@
fuel += reagents.get_reagent_amount("fuel")
else
- for(var/datum/reagent/ethanol/R in reagents.reagent_list)
- fuel += reagents.get_reagent_amount(R.id)
+ for(var/datum/reagent/ethanol/R in reagents.get_reagent_datums())
+ fuel += reagents.reagent_volumes[R.id]
return (fuel >= 2 && fuel >= reagents.total_volume*0.8)
@@ -250,7 +250,7 @@
return
reagents.remove_reagent("fuel", reagents.maximum_volume/25)
- for(var/datum/reagent/ethanol/R in reagents.reagent_list)
+ for(var/datum/reagent/ethanol/R in reagents.get_reagent_datums())
if(istype(R, /datum/reagent/ethanol))
reagents.remove_reagent(R.id, reagents.maximum_volume/25)
update_name()
diff --git a/code/modules/food/drinkingglass/glass1.dm b/code/modules/food/drinkingglass/glass1.dm
index 9a8a1fac1857..8c3c57d7f8dc 100644
--- a/code/modules/food/drinkingglass/glass1.dm
+++ b/code/modules/food/drinkingglass/glass1.dm
@@ -18,8 +18,8 @@
/*else if(reagents.reagent_list.len == 1)
for(var/datum/reagent/R in reagents.reagent_list)
switch(R.id)*/
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(R.glass_icon_state)
icon_state = R.glass_icon_state
@@ -62,8 +62,8 @@
materials_base = list(MAT_GLASS = 60)
/obj/item/reagent_containers/food/drinks/cup/on_reagent_change()
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(R.cup_icon_state)
icon_state = R.cup_icon_state
@@ -132,7 +132,7 @@
filling.color += reagents.get_color()
add_overlay(filling)
- name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
+ name = "shot glass of " + reagents.get_majority_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
else
name = "shot glass"
diff --git a/code/modules/food/drinkingglass/glass2.dm b/code/modules/food/drinkingglass/glass2.dm
index 65a6a64e32f8..97edc8f12b29 100644
--- a/code/modules/food/drinkingglass/glass2.dm
+++ b/code/modules/food/drinkingglass/glass2.dm
@@ -45,8 +45,8 @@
. += "It is fizzing slightly."
/obj/item/reagent_containers/food/drinks/glass2/proc/has_ice()
- if(reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if(reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(!((R.id == "ice") || ("ice" in R.glass_special))) // if it's not a cup of ice, and it's not already supposed to have ice in, see if the bartender's put ice in it
if(reagents.has_reagent("ice", reagents.total_volume / 10)) // 10% ice by volume
return 1
@@ -54,13 +54,13 @@
return 0
/obj/item/reagent_containers/food/drinks/glass2/proc/has_fizz()
- if(reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if(reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(!("fizz" in R.glass_special))
var/totalfizzy = 0
- for(var/datum/reagent/re in reagents.reagent_list)
+ for(var/datum/reagent/re in reagents.get_reagent_datums())
if("fizz" in re.glass_special)
- totalfizzy += re.volume
+ totalfizzy += reagents.reagent_volumes[re.id]
if(totalfizzy >= reagents.total_volume / 5) // 20% fizzy by volume
return 1
return 0
@@ -84,8 +84,8 @@
/obj/item/reagent_containers/food/drinks/glass2/update_icon()
underlays.Cut()
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
name = "[base_name] of [R.glass_name ? R.glass_name : "something"]"
desc = R.glass_desc ? R.glass_desc : initial(desc)
diff --git a/code/modules/food/drinkingglass/metaglass.dm b/code/modules/food/drinkingglass/metaglass.dm
index 2ce50b7d500f..3da94ee4f1c5 100644
--- a/code/modules/food/drinkingglass/metaglass.dm
+++ b/code/modules/food/drinkingglass/metaglass.dm
@@ -10,8 +10,8 @@
icon = 'icons/obj/drinks.dmi'
/obj/item/reagent_containers/food/drinks/metaglass/on_reagent_change()
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(R.glass_icon_state)
icon_state = R.glass_icon_state
@@ -49,10 +49,6 @@
Drinks Data
*/
-/datum/reagent
- var/glass_icon_state = null
- var/glass_center_of_mass = null
-
/datum/reagent/adminordrazine
glass_icon_state = "golden_cup"
diff --git a/code/modules/food/drinks/cup.dm b/code/modules/food/drinks/cup.dm
index 5e4f9e26681c..d2d89b515c4e 100644
--- a/code/modules/food/drinks/cup.dm
+++ b/code/modules/food/drinks/cup.dm
@@ -8,8 +8,8 @@
/obj/item/reagent_containers/food/drinks/cup/on_reagent_change()
..()
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(R.cup_icon_state)
icon_state = R.cup_icon_state
@@ -58,8 +58,8 @@
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(changetype)
- if (reagents.reagent_list.len)
- switch(reagents.get_master_reagent_id())
+ if (reagents.total_volume)
+ switch(reagents.get_majority_reagent_id())
if("orangejuice")
icon_state = "orangebox"
name = "orange juice box"
diff --git a/code/modules/food/drinks/jar.dm b/code/modules/food/drinks/jar.dm
index db60cc816231..cb4c7d1d9cc1 100644
--- a/code/modules/food/drinks/jar.dm
+++ b/code/modules/food/drinks/jar.dm
@@ -8,8 +8,8 @@
integrity_flags = INTEGRITY_ACIDPROOF
/obj/item/reagent_containers/food/drinks/jar/on_reagent_change()
- if (reagents.reagent_list.len > 0)
- switch(reagents.get_master_reagent_id())
+ if (reagents.total_volume > 0)
+ switch(reagents.get_majority_reagent_id())
if("slime")
icon_state = "jar_slime"
name = "slime jam"
diff --git a/code/modules/food/food.dm b/code/modules/food/food.dm
index 64318cdd0de6..a7e966b53add 100644
--- a/code/modules/food/food.dm
+++ b/code/modules/food/food.dm
@@ -37,8 +37,9 @@
src.pixel_y = rand(-6.0, 6)
. = ..()
// prefill depending on if we were cooked or an actual spawn.
+ var/static/datum/nutriment_data/static_nutrient_data_initializer = new /datum/nutriment_data/static_spawn_initializer
for(var/key in cooked? inherent_reagents : inherent_reagents | prefill_reagents)
- reagents.add_reagent(key, inherent_reagents[key])
+ reagents.add_reagent(key, inherent_reagents[key], static_nutrient_data_initializer)
/obj/item/reagent_containers/food/afterattack(atom/target, mob/user, clickchain_flags, list/params)
if(center_of_mass.len && (clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) && istype(target, /obj/structure/table))
diff --git a/code/modules/food/food/condiment.dm b/code/modules/food/food/condiment.dm
index e23b5299148f..e434f36269c2 100644
--- a/code/modules/food/food/condiment.dm
+++ b/code/modules/food/food/condiment.dm
@@ -57,8 +57,8 @@
to_chat(user, "You swallow some of contents of \the [src].")
/obj/item/reagent_containers/food/condiment/on_reagent_change()
- if(reagents.reagent_list.len > 0)
- switch(reagents.get_master_reagent_id())
+ if(reagents.total_volume)
+ switch(reagents.get_majority_reagent_id())
if("ketchup")
name = "Ketchup"
desc = "You feel more American already."
@@ -117,10 +117,10 @@
center_of_mass = list("x"=16, "y"=6) // END CITADEL CHANGE - AURORA KITCHEN PORT
else
name = "Misc Condiment Bottle"
- if (reagents.reagent_list.len==1)
- desc = "Looks like it is [reagents.get_master_reagent_name()], but you are not sure."
+ if (length(reagents.reagent_volumes) > 1)
+ desc = "Looks like it is [reagents.get_majority_reagent_name()], but you are not sure."
else
- desc = "A mixture of various condiments. [reagents.get_master_reagent_name()] is one of them."
+ desc = "A mixture of various condiments. [reagents.get_majority_reagent_name()] is one of them."
icon_state = "mixedcondiments"
center_of_mass = list("x"=16, "y"=6)
else
diff --git a/code/modules/food/food/drinks.dm b/code/modules/food/food/drinks.dm
index 22144611a5b0..a1a40f592066 100644
--- a/code/modules/food/food/drinks.dm
+++ b/code/modules/food/food/drinks.dm
@@ -17,8 +17,8 @@
var/custom_open_sound
/obj/item/reagent_containers/food/drinks/on_reagent_change()
- if (reagents.reagent_list.len > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ if (reagents.total_volume)
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
if(R.price_tag)
price_tag = R.price_tag
else
@@ -94,7 +94,7 @@
else
. += "\The [src] is full!"
if(reagents)
- var/datum/reagent/ethanol/R = locate() in reagents.reagent_list
+ var/datum/reagent/ethanol/R = locate() in reagents.get_reagent_datums()
if(istype(R))
. += "It contains alcohol."
diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm
index 4992efb9a1ea..ea4af7a28325 100644
--- a/code/modules/food/food/snacks.dm
+++ b/code/modules/food/food/snacks.dm
@@ -16,6 +16,7 @@
var/survivalfood = FALSE
var/nutriment_amt = 0
var/list/nutriment_desc = list("food" = 1)
+ #warn ohhh no
var/datum/reagent/nutriment/coating/coating = null
var/sealed = FALSE
var/custom_open_sound
@@ -3985,9 +3986,7 @@ END CITADEL CHANGE */
//Code for dipping food in batter
/obj/item/reagent_containers/food/snacks/afterattack(atom/target, mob/user, clickchain_flags, list/params)
if(target.is_open_container() && target.reagents && !(istype(target, /obj/item/reagent_containers/food)))
- for (var/r in target.reagents.reagent_list)
-
- var/datum/reagent/R = r
+ for(var/datum/reagent/R as anything in target.reagents.get_reagent_datums())
if (istype(R, /datum/reagent/nutriment/coating))
if (apply_coating(R, user))
return 1
@@ -4002,12 +4001,11 @@ END CITADEL CHANGE */
//Calculate the reagents of the coating needed
var/req = 0
- for (var/r in reagents.reagent_list)
- var/datum/reagent/R = r
+ for(var/datum/reagent/R as anything in reagents.get_reagent_datums())
if (istype(R, /datum/reagent/nutriment))
- req += R.volume * 0.2
+ req += reagents.reagent_volumes[R.id] * 0.2
else
- req += R.volume * 0.1
+ req += reagents.reagent_volumes[R.id] * 0.1
req += w_class*0.5
@@ -4030,7 +4028,7 @@ END CITADEL CHANGE */
C.holder.trans_to_holder(reagents, req)
//We're done with C now, repurpose the var to hold a reference to our local instance of it
- C = reagents.get_reagent(id)
+ C = SSchemistry.fetch_reagent(id)
if (!C)
return
diff --git a/code/modules/food/machinery/appliance/_appliance.dm b/code/modules/food/machinery/appliance/_appliance.dm
index 7f6356ef76ce..c723c8f36aac 100644
--- a/code/modules/food/machinery/appliance/_appliance.dm
+++ b/code/modules/food/machinery/appliance/_appliance.dm
@@ -297,26 +297,25 @@
for (var/obj/item/J in CI.container)
oilwork(J, CI)
- for (var/r in CI.container.reagents.reagent_list)
- var/datum/reagent/R = r
- if (istype(R, /datum/reagent/nutriment))
- CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form
-
- //Nonfat reagents will soak oil
- if (!istype(R, /datum/reagent/nutriment/triglyceride))
- CI.max_oil += R.volume * 0.25
+ for(var/id in CI.container.reagents.reagent_volumes)
+ var/datum/reagent/the_reagent = SSchemistry.fetch_reagent(id)
+ var/the_volume = CI.container.reagents.reagent_volumes[id]
+ if(istype(the_reagent, /datum/reagent/nutriment))
+ CI.max_cookwork += the_volume * 2
+ if(!istype(the_reagent, /datum/reagent/nutriment/triglyceride))
+ CI.max_oil += the_volume * 0.25
else
- CI.max_cookwork += R.volume
- CI.max_oil += R.volume * 0.10
+ CI.max_cookwork += the_volume
+ CI.max_oil += the_volume * 0.1
//Rescaling cooking work to avoid insanely long times for large things
var/buffer = CI.max_cookwork
CI.max_cookwork = 0
var/multiplier = 1
- var/step = 4
- while (buffer > step)
- buffer -= step
- CI.max_cookwork += step*multiplier
+ var/step_amount = 4
+ while (buffer > step_amount)
+ buffer -= step_amount
+ CI.max_cookwork += step_amount * multiplier
multiplier *= 0.95
CI.max_cookwork += buffer*multiplier
@@ -326,19 +325,16 @@
var/obj/item/reagent_containers/food/snacks/S = I
var/work = 0
if (istype(S))
- if (S.reagents)
- for (var/r in S.reagents.reagent_list)
- var/datum/reagent/R = r
- if (istype(R, /datum/reagent/nutriment))
- work += R.volume *3//Core nutrients contribute much more than peripheral chemicals
-
- //Nonfat reagents will soak oil
- if (!istype(R, /datum/reagent/nutriment/triglyceride))
- CI.max_oil += R.volume * 0.35
- else
- work += R.volume
- CI.max_oil += R.volume * 0.15
-
+ for(var/id in S.reagents.reagent_volumes)
+ var/datum/reagent/the_reagent = SSchemistry.fetch_reagent(id)
+ var/the_volume = S.reagents.reagent_volumes[id]
+ if(istype(the_reagent, /datum/reagent/nutriment))
+ work += the_volume
+ if(!istype(the_reagent, /datum/reagent/nutriment/triglyceride))
+ CI.max_oil += the_volume * 0.35
+ else
+ work += the_volume
+ CI.max_oil += the_volume * 0.15
else if(istype(I, /obj/item/holder))
var/obj/item/holder/H = I
diff --git a/code/modules/food/machinery/appliance/container.dm b/code/modules/food/machinery/appliance/container.dm
index 4005e7741f02..e7990da77b00 100644
--- a/code/modules/food/machinery/appliance/container.dm
+++ b/code/modules/food/machinery/appliance/container.dm
@@ -110,7 +110,7 @@
.+=O.name//Just append the name of the first object
return
else if (reagents && reagents.total_volume > 0)
- var/datum/reagent/R = reagents.get_master_reagent()
+ var/datum/reagent/R = reagents.get_majority_reagent_datum()
.+=R.name//Append name of most voluminous reagent
return
else
diff --git a/code/modules/food/machinery/appliance/fryer.dm b/code/modules/food/machinery/appliance/fryer.dm
index 03ee80157ae5..55a37838226a 100644
--- a/code/modules/food/machinery/appliance/fryer.dm
+++ b/code/modules/food/machinery/appliance/fryer.dm
@@ -46,26 +46,17 @@
/obj/machinery/appliance/cooker/fryer/heat_up()
if (..())
- //Set temperature of oil reagent
- var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
- if (OL && istype(OL))
- OL.data["temperature"] = temperature
+ oil.temperature = temperature
/obj/machinery/appliance/cooker/fryer/equalize_temperature()
if (..())
//Set temperature of oil reagent
- var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
- if (OL && istype(OL))
- OL.data["temperature"] = temperature
-
+ oil.temperature = temperature
/obj/machinery/appliance/cooker/fryer/update_cooking_power()
..()//In addition to parent temperature calculation
//Fryer efficiency also drops when oil levels arent optimal
- var/oil_level = 0
- var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
- if (OL && istype(OL))
- oil_level = OL.volume
+ var/oil_level = oil.reagent_volumes[/datum/reagent/nutriment/triglyceride/oil::id] || 0
var/oil_efficiency = 0
if (oil_level)
@@ -104,19 +95,21 @@
var/total_oil = 0
var/total_our_oil = 0
var/total_removed = 0
- var/datum/reagent/our_oil = oil.get_master_reagent()
+ var/datum/reagent/our_oil = oil.get_majority_reagent_datum()
for (var/obj/item/I in CI.container)
- if (I.reagents && I.reagents.total_volume)
- for (var/datum/reagent/R in I.reagents.reagent_list)
- if (istype(R, /datum/reagent/nutriment/triglyceride/oil))
- total_oil += R.volume
- if (R.id != our_oil.id)
- total_removed += R.volume
- I.reagents.remove_reagent(R.id, R.volume)
- else
- total_our_oil += R.volume
-
+ if(!I.reagents?.total_volume)
+ continue
+ for(var/datum/reagent/reagent as anything in I.reagents.get_reagent_datums())
+ if(!istype(reagent, /datum/reagent/nutriment/triglyceride/oil))
+ continue
+ var/the_volume = I.reagents.reagent_volumes[reagent.id]
+ total_oil += the_volume
+ if(reagent.id != our_oil.id)
+ total_removed += the_volume
+ I.reagents.remove_reagent(reagent.id, the_volume)
+ else
+ total_our_oil += the_volume
if (total_removed > 0 || total_oil != CI.max_oil)
total_oil = min(total_oil, CI.max_oil)
@@ -131,13 +124,10 @@
//If we have more than the maximum allowed then we delete some.
//This could only happen if one of the objects spawns with the same type of oil as ours
var/portion = 1 - (total_oil / total_our_oil) //find the percentage to remove
- for (var/obj/item/I in CI.container)
- if (I.reagents && I.reagents.total_volume)
- for (var/datum/reagent/R in I.reagents.reagent_list)
- if (R.id == our_oil.id)
- I.reagents.remove_reagent(R.id, R.volume*portion)
-
-
+ for (var/obj/item/I as anything in CI.container)
+ if(!I.reagents?.total_volume)
+ continue
+ I.reagents.remove_reagent(our_oil.id, I.reagents.reagent_volumes[our_oil.id] * portion)
/obj/machinery/appliance/cooker/fryer/cook_mob(var/mob/living/victim, var/mob/user)
@@ -160,8 +150,8 @@
var/damage = rand(7,13)
//Though this damage seems reduced, some hot oil is transferred to the victim and will burn them for a while after
- var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
- damage *= OL.heatdamage(victim)
+ var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_majority_reagent_datum()
+ damage *= OL.heatdamage(victim, oil)
var/obj/item/organ/external/E
var/nopain
diff --git a/code/modules/food/machinery/microwave.dm b/code/modules/food/machinery/microwave.dm
index 8318bb6802ca..27ba7308e3de 100644
--- a/code/modules/food/machinery/microwave.dm
+++ b/code/modules/food/machinery/microwave.dm
@@ -130,8 +130,8 @@
)
if (!O.reagents)
return 1
- for (var/datum/reagent/R in O.reagents.reagent_list)
- if (!(R.id in acceptable_reagents))
+ for (var/id in O.reagents.reagent_volumes)
+ if (!(id in acceptable_reagents))
to_chat(user, "Your [O] contains components unsuitable for cookery.")
return 1
return
@@ -211,15 +211,15 @@
else
dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
"}
- for (var/datum/reagent/R in reagents.reagent_list)
+ for (var/datum/reagent/R in reagents.get_reagent_datums())
var/display_name = R.name
if (R.id == "capsaicin")
display_name = "Hotsauce"
if (R.id == "frostoil")
display_name = "Coldsauce"
- dat += {"[display_name]: [R.volume] unit\s
"}
+ dat += {"[display_name]: [reagents.reagent_volumes[R.id]] unit\s
"}
- if (items_counts.len==0 && reagents.reagent_list.len==0)
+ if (items_counts.len==0 && !reagents.total_volume==0)
dat = {"The microwave is empty
"}
else
dat = {"Ingredients:
[dat]"}
@@ -405,7 +405,7 @@
for (var/obj/O in contents-ffuu)
amount++
if (O.reagents)
- var/id = O.reagents.get_master_reagent_id()
+ var/id = O.reagents.get_majority_reagent_id()
if (id)
amount+=O.reagents.get_reagent_amount(id)
qdel(O)
diff --git a/code/modules/hardsuits/modules/utility.dm b/code/modules/hardsuits/modules/utility.dm
index f5fb2a8f031e..1da36cd43028 100644
--- a/code/modules/hardsuits/modules/utility.dm
+++ b/code/modules/hardsuits/modules/utility.dm
@@ -180,12 +180,12 @@
// Magical chemical filtration system, do not question it.
var/total_transferred = 0
- for(var/datum/reagent/R in input_item.reagents.reagent_list)
+ for(var/datum/reagent/R in input_item.reagents.get_reagent_datums())
for(var/chargetype in charges)
var/datum/rig_charge/charge = charges[chargetype]
if(charge.display_name == R.id)
- var/chems_to_transfer = R.volume
+ var/chems_to_transfer = input_item.reagents.reagent_volumes[R.id]
if((charge.charges + chems_to_transfer) > max_reagent_volume)
chems_to_transfer = max_reagent_volume - charge.charges
@@ -193,7 +193,6 @@
charge.charges += chems_to_transfer
input_item.reagents.remove_reagent(R.id, chems_to_transfer)
total_transferred += chems_to_transfer
-
break
if(total_transferred)
diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm
index 707cf82533f7..9fd56cde328c 100644
--- a/code/modules/hydroponics/trays/tray.dm
+++ b/code/modules/hydroponics/trays/tray.dm
@@ -241,7 +241,7 @@
reagents.trans_to_obj(temp_chem_holder, min(reagents.total_volume,rand(1,3)))
- for(var/datum/reagent/R in temp_chem_holder.reagents.reagent_list)
+ for(var/datum/reagent/R in temp_chem_holder.reagents.get_reagent_datums())
var/reagent_total = temp_chem_holder.reagents.get_reagent_amount(R.id)
diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm
index a862baf5599f..196423ba1cd5 100644
--- a/code/modules/integrated_electronics/passive/power.dm
+++ b/code/modules/integrated_electronics/passive/power.dm
@@ -143,12 +143,6 @@
for(var/I in fuel)
if(DYNAMIC_CELL_UNITS_TO_W(assembly.battery.maxcharge - assembly.battery.charge, 1) > fuel[I])
var/power = 1
- if(I == "blood")
- var/list/data = reagents.get_data(I)
- if(data && istype(data["donor"], /mob/living/carbon/human))
- var/mob/living/carbon/human/H = data["donor"]
- if(H.mind && H.mind.ckey)
- power = 10
if(reagents.remove_reagent(I, 1))
assembly.give_power(fuel[I]*power)
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index 2dd9e32b12b2..7cef7f79cc35 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -321,13 +321,13 @@
set_pin_data(IC_OUTPUT, 7, H.getToxLoss())
set_pin_data(IC_OUTPUT, 8, H.getOxyLoss())
set_pin_data(IC_OUTPUT, 9, H.getCloneLoss())
- set_pin_data(IC_OUTPUT, 10, round((H.vessel.get_reagent_amount("blood") / H.species.blood_volume)*100))
+ set_pin_data(IC_OUTPUT, 10, round((H.blood_holder.get_total_volume() / H.species.blood_volume)*100))
set_pin_data(IC_OUTPUT, 11, H.traumatic_shock)
set_pin_data(IC_OUTPUT, 12, H.radiation)
set_pin_data(IC_OUTPUT, 13, H.nutrition)
var/cont[0]
var/amt[0]
- for(var/datum/reagent/RE in H.reagents.reagent_list)
+ for(var/datum/reagent/RE in H.reagents.get_reagent_datums())
if(RE.scannable || advscan >= 3)
cont += RE.id
amt += round(H.reagents.get_reagent_amount(RE.id), 1)
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index fc491f64e633..711a0df99242 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -196,13 +196,7 @@
busy = TRUE
if(do_atom(src, L, extra_checks=CALLBACK(L, TYPE_PROC_REF(/mob/living, can_inject),null,0,BP_TORSO,bypass),uninterruptible = bypass))
var/mob/living/carbon/LB = L
- var/datum/reagent/B
- B = LB.take_blood(src, tramount)
- if(B)
- reagents.reagent_list += B
- reagents.update_total()
- AM.on_reagent_change()
- reagents.reconsider_reactions()
+ if(LB.take_blood_legacy(src, tramount) > 0)
L.visible_message("[acting_object] takes a blood sample from [L]!", \
"[acting_object] takes a blood sample from you!")
else
@@ -569,7 +563,7 @@
var/obj/item/I = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
if(istype(I) && (I.reagents?.total_volume) && check_target(I))
var/list/reagent_names_list = list()
- for(var/datum/reagent/R in reagents?.reagent_list)
+ for(var/datum/reagent/R in reagents?.get_reagent_datums())
reagent_names_list.Add(R.name)
var/atom/AM = get_object()
AM.investigate_log("ground reagents: [jointext(reagent_names_list, ", ")] with [src].", INVESTIGATE_CIRCUIT)
@@ -595,7 +589,7 @@
/obj/item/integrated_circuit/reagent/storage/scan/do_work()
var/cont[0]
- for(var/datum/reagent/RE in reagents.reagent_list)
+ for(var/datum/reagent/RE in reagents.get_reagent_datums())
cont += RE.id
set_pin_data(IC_OUTPUT, 3, cont)
push_data()
@@ -648,7 +642,7 @@
return
if(!target.reagents.available_volume())
return
- for(var/datum/reagent/G in source.reagents.reagent_list)
+ for(var/datum/reagent/G in source.reagents.get_reagent_datums())
if (!direc)
if(G.id in demand)
source.reagents.trans_id_to(target, G.id, transfer_amount)
diff --git a/code/modules/loot/packs/misc.dm b/code/modules/loot/packs/misc.dm
index c7dad6fb77aa..767d4e016aa4 100644
--- a/code/modules/loot/packs/misc.dm
+++ b/code/modules/loot/packs/misc.dm
@@ -19,7 +19,7 @@
/obj/item/clothing/mask/gas/clown_hat,
/obj/item/bikehorn,
/obj/item/pen/crayon/rainbow,
- /obj/item/toy/waterflower,
+ /obj/item/reagent_containers/spray/waterflower,
)
/datum/prototype/struct/loot_pack/misc/clown/draw(amount)
diff --git a/code/modules/mob/living/bot/medibot.dm b/code/modules/mob/living/bot/medibot.dm
index 895abfebedcc..31c006470e3c 100644
--- a/code/modules/mob/living/bot/medibot.dm
+++ b/code/modules/mob/living/bot/medibot.dm
@@ -527,8 +527,8 @@
// If they're injured, we're using a beaker, and they don't have on of the chems in the beaker.
if(reagent_glass && use_beaker && ((victim.getBruteLoss() >= heal_threshold) || (victim.getToxLoss() >= heal_threshold) || (victim.getToxLoss() >= heal_threshold) || (victim.getOxyLoss() >= (heal_threshold + 15))))
- for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
- if(!victim.reagents.has_reagent(R))
+ for(var/datum/reagent/R in reagent_glass.reagents.get_reagent_datums())
+ if(!victim.reagents.has_reagent(R.id))
return 1
continue
diff --git a/code/modules/mob/living/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm
index 4b683e5b746a..52c6ad05efa1 100644
--- a/code/modules/mob/living/bot/mulebot.dm
+++ b/code/modules/mob/living/bot/mulebot.dm
@@ -235,8 +235,8 @@
/mob/living/bot/mulebot/Bump(var/mob/living/M)
if(!safety && istype(M))
visible_message("[src] knocks over [M]!")
- M.afflict_stun(20 * 8)
- M.afflict_paralyze(20 * 5)
+ M.afflict_paralyze(1 SECONDS)
+ M.afflict_knockdown(2 SECONDS)
..()
/mob/living/bot/mulebot/proc/runOver(var/mob/living/M)
@@ -252,7 +252,11 @@
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_L_ARM)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_ARM)
- blood_splatter(src, M, 1)
+ var/datum/blood_mixture/to_use
+ if(iscarbon(M))
+ var/mob/living/carbon/carbon = M
+ to_use = carbon.get_blood_mixture()
+ blood_splatter_legacy(get_turf(M), to_use, TRUE)
/mob/living/bot/mulebot/relaymove(var/mob/user, var/direction)
if(load == user)
diff --git a/code/modules/mob/living/carbon/blood_fragment.dm b/code/modules/mob/living/carbon/blood_fragment.dm
new file mode 100644
index 000000000000..1ddfc939e9e2
--- /dev/null
+++ b/code/modules/mob/living/carbon/blood_fragment.dm
@@ -0,0 +1,87 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station developers. *//
+
+/**
+ * Reagent blood data
+ */
+/datum/blood_fragment
+ /// the blood's color
+ var/color
+
+ //! LEGACY FIELDS
+ var/legacy_species
+ var/legacy_blood_dna
+ var/legacy_blood_type
+ var/legacy_donor
+ var/legacy_name
+ //! END
+
+/datum/blood_fragment/clone(include_contents)
+ var/datum/blood_fragment/copy = new /datum/blood_fragment
+ copy.color = color
+ if(legacy_blood_dna)
+ copy.legacy_blood_dna = legacy_blood_dna
+ if(legacy_blood_type)
+ copy.legacy_blood_type = legacy_blood_type
+ if(legacy_donor)
+ copy.legacy_donor = legacy_donor
+ if(legacy_name)
+ copy.legacy_name = legacy_name
+ if(legacy_species)
+ copy.legacy_species = legacy_species
+ return copy
+
+/**
+ * Checks if other is equivalent to self.
+ *
+ * * We intentionally do not check color. It's too expensive to, given this is used for
+ * deduping.
+ * * We intentionally do not check name. It's too expensive to, given this is used for
+ * deduping.
+ * * This implies that color / name should implicitly be the same if this proc returns TRUE
+ * for two given blood fragments.
+ */
+/datum/blood_fragment/proc/equivalent(datum/blood_fragment/other)
+ if(other.legacy_species != src.legacy_species)
+ return FALSE
+ if(other.legacy_blood_dna != src.legacy_blood_dna)
+ return FALSE
+ if(other.legacy_blood_type != src.legacy_blood_type)
+ return FALSE
+ return TRUE
+
+/**
+ * Checks if other is compatible with self.
+ *
+ * * This is **not** a symmetric relation. Some bloods are universally compatible with others,
+ * and accept everything.
+ * * This means that if host is not compatible with donor, but donor is compatible with host,
+ * the host attacks the donoar, even if the donor wouldn't if it was reversed.
+ */
+/datum/blood_fragment/proc/compatible_with_self(datum/blood_fragment/other)
+ return legacy_blood_compatible_with_self(legacy_blood_type, other.legacy_blood_type, legacy_species, other.legacy_species)
+
+/proc/legacy_blood_compatible_with_self(our_blood_type, their_blood_type, our_species_name, their_species_name)
+ // todo: remove species check, we have dumb xeno-compatibility-like canon anyways
+ if(our_species_name && their_species_name && (their_species_name != our_species_name))
+ return FALSE
+
+ var/our_antigen = copytext(our_blood_type, 1, length(our_blood_type))
+ var/their_antigen = copytext(their_blood_type, 1, length(their_blood_type))
+
+ var/static/antigen_incompatible_matrix = list(
+ "AB" = list(),
+ "A" = list("AB" = TRUE, "B" = TRUE),
+ "B" = list("AB" = TRUE, "A" = TRUE),
+ "O" = list("AB" = TRUE, "A" = TRUE, "B" = TRUE),
+ )
+ if(antigen_incompatible_matrix[our_antigen]?[their_antigen])
+ return FALSE
+
+ var/our_rh = our_blood_type[length(our_blood_type)] == "+"
+ var/their_rh = their_blood_type[length(their_blood_type)] == "+"
+
+ if(their_rh && !our_rh)
+ return FALSE
+
+ return TRUE
diff --git a/code/modules/mob/living/carbon/blood_holder.dm b/code/modules/mob/living/carbon/blood_holder.dm
new file mode 100644
index 000000000000..1a93d927aa59
--- /dev/null
+++ b/code/modules/mob/living/carbon/blood_holder.dm
@@ -0,0 +1,167 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station developers. *//
+
+/**
+ * Blood holder.
+ *
+ * This is basically just a blood mixture datum but permanent. Caching is more pronounced,
+ * and things can be stored that otherwise wouldn't be because we're considered cheap to store.
+ *
+ * * We own all fragments in us. We always copy when giving references, and always copy
+ * when accepting references.
+ */
+/datum/blood_holder
+ /// Our host blood.
+ var/datum/blood_fragment/host_blood
+ /// Amount of host blood we have
+ var/host_blood_volume = 0
+ /// Our fragments that aren't ourselves, associated to ratio.
+ var/list/datum/blood_fragment/guest_bloods
+ /// Total amount of guest blood volume
+ var/guest_blood_volume = 0
+
+/datum/blood_holder/proc/get_total_volume()
+ return host_blood_volume + guest_blood_volume
+
+/datum/blood_holder/proc/set_host_fragment_to(datum/blood_fragment/fragment)
+ host_blood = fragment.clone()
+
+/datum/blood_holder/proc/set_host_volume(amount)
+ host_blood_volume = amount
+
+/**
+ * @return amount change
+ */
+/datum/blood_holder/proc/adjust_host_volume(amount)
+ . = host_blood_volume
+ host_blood_volume = max(host_blood_volume + amount, 0)
+ . = host_blood_volume - .
+
+/**
+ * @return amount injected
+ */
+/datum/blood_holder/proc/inject_mixture(datum/blood_mixture/mixture, amount)
+ if(amount < 0)
+ return 0
+ if(isnull(amount))
+ amount = mixture.ctx_return_amount
+ if(!amount)
+ return 0
+ #warn impl; check for host blood
+
+ // todo: auto-trim system
+
+ var/list/datum/blood_fragment/new_fragments = list()
+
+ first_pass:
+ for(var/datum/blood_fragment/potential_injecting as anything in mixture.fragments)
+ if(host_blood.equivalent(potential_injecting))
+ var/computed_amount = (1 - mixture.fragments[potential_injecting]) * amount
+ amount -= computed_amount
+ . += adjust_host_volume(computed_amount)
+ continue
+ for(var/datum/blood_fragment/potential_pair as anything in guest_bloods)
+ if(potential_pair.equivalent(potential_injecting))
+ #warn impl; scaling will be a pain here
+ continue first_pass
+ new_fragments += potential_injecting
+
+ var/old_volume = guest_blood_volume
+
+/**
+ * @return amount injected
+ */
+/datum/blood_holder/proc/inject_fragment(datum/blood_fragment/fragment, amount)
+ if(amount < 0)
+ return 0
+ if(host_blood.equivalent(fragment))
+ return adjust_host_volume(amount)
+
+ // todo: auto-trim system
+
+ var/datum/blood_fragment/existing
+ for(var/datum/blood_fragment/checking as anything in guest_bloods)
+ if(checking.equivalent(fragment))
+ existing = checking
+ break
+
+ var/old_volume = guest_blood_volume
+ guest_blood_volume += amount
+ var/scaler = old_volume / guest_blood_volume
+
+ for(var/datum/blood_fragment/scaling as anything in guest_bloods)
+ guest_bloods[scaling] *= scaler
+
+ if(!existing)
+ guest_bloods[fragment] = 1 - scaler
+ else
+ guest_bloods[existing] += 1 - scaler
+
+ return amount
+
+/**
+ * Erases blood uniformly.
+ *
+ * * Fails if we don't have enough.
+ *
+ * @return amount erased
+ */
+/datum/blood_holder/proc/erase_checked_amount(amount)
+ var/total = host_blood_volume + guest_blood_volume
+ if(amount < total)
+ return 0
+ var/multiplier = 1 - (amount / total)
+ host_blood_volume *= multiplier
+ guest_blood_volume *= multiplier
+ return amount
+
+/**
+ * Erases blood uniformly.
+ *
+ * @return amount erased
+ */
+/datum/blood_holder/proc/erase_amount(amount)
+ var/total = host_blood_volume + guest_blood_volume
+ var/multiplier = max(0, 1 - (amount / total))
+ . = min(amount, total)
+ host_blood_volume *= multiplier
+ guest_blood_volume *= multiplier
+
+/**
+ * Takes a blood mixture from us.
+ *
+ * * Expensive.
+ * * Fails if we don't have enough.
+ * * Does not put viruses/antibodies/etc into it; that's the responsibiliy of the host, for now.
+ * * `ctx_return_amount` will be set on the returned mixture.
+ *
+ * @return mixture taken or null
+ */
+/datum/blood_holder/proc/checked_draw(amount) as /datum/blood_mixture
+ if(get_total_volume() < amount)
+ return null
+ var/datum/blood_mixture/taken = draw(amount)
+ // assert that it is enough
+ taken.ctx_return_amount = amount
+ return taken
+
+/**
+ * Takes a blood mixture from us.
+ *
+ * * Expensive.
+ * * Does not put viruses/antibodies/etc into it; that's the responsibiliy of the host, for now.
+ * * `ctx_return_amount` will be set on the returned mixture.
+ *
+ * @params
+ * * amount - amount to take
+ * * infinite - don't actually take any, and allow drawing any amount
+ *
+ * @return mixture taken
+ */
+/datum/blood_holder/proc/draw(amount, infinite) as /datum/blood_mixture
+ var/datum/blood_mixture/creating = new
+ creating.fragments = list()
+ #warn impl fragments self/others, amounts
+ return creating
+
+#warn how to handle color? compute_color_from_data, don't forget!
diff --git a/code/modules/mob/living/carbon/blood_mixture.dm b/code/modules/mob/living/carbon/blood_mixture.dm
new file mode 100644
index 000000000000..88631177342b
--- /dev/null
+++ b/code/modules/mob/living/carbon/blood_mixture.dm
@@ -0,0 +1,69 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station developers. *//
+
+/**
+ * Reagent blood data
+ */
+/datum/blood_mixture
+ var/list/legacy_antibodies
+ var/list/legacy_virus2
+ #warn uhh
+ var/legacy_trace_chem
+ #warn hook
+ var/legacy_is_synthetic = FALSE
+
+ /// Fragments, associated to **ratio of total**.
+ var/list/datum/blood_fragment/fragments
+
+ /// The total amount of all of our fragments
+ /// * Only useful in a return-value context. This is to avoid needing to recalcualte this.
+ /// * This can, in-fact, be '0'.
+ /// * In a reagent / storage context, the reagent's volume will always supercede this.
+ /// * This is not copied during a clone, as it's purely return-value context.
+ var/tmp/ctx_return_amount = 0
+
+/datum/blood_mixture/clone(include_contents)
+ var/datum/blood_mixture/copy = new /datum/blood_mixture
+ if(!isnull(legacy_trace_chem))
+ copy.legacy_trace_chem = legacy_trace_chem
+ if(!isnull(legacy_antibodies))
+ copy.legacy_antibodies = legacy_antibodies
+ if(!isnull(legacy_is_synthetic))
+ copy.legacy_is_synthetic = legacy_is_synthetic
+ if(!isnull(legacy_virus2))
+ copy.legacy_virus2 = legacy_virus2
+ if(length(fragments))
+ copy.fragments = list()
+ for(var/datum/blood_fragment/data as anything in fragments)
+ copy.fragments[data.clone()] = fragments[data]
+ return copy
+
+/**
+ * Get overall color
+ */
+/datum/blood_mixture/proc/get_color()
+ #warn impl
+
+/**
+ * Get overall name
+ */
+/datum/blood_mixture/proc/get_name()
+ #warn impl
+
+/**
+ * Get atom blood DNA variable
+ *
+ * * this is shit, ugh
+ */
+/datum/blood_mixture/proc/legacy_get_forensic_blood_dna()
+ if(!length(fragments))
+ return null
+ var/highest_so_far
+ var/datum/blood_fragment/highest_to_use
+ for(var/datum/blood_fragment/fragment as anything in fragments)
+ if(fragments[fragment] > highest_so_far)
+ highest_so_far = fragments[fragment]
+ highest_to_use = fragment
+ return list(
+ (highest_to_use.legacy_blood_dna) = (highest_to_use.legacy_blood_type),
+ )
diff --git a/code/modules/mob/living/carbon/carbon-blood.dm b/code/modules/mob/living/carbon/carbon-blood.dm
new file mode 100644
index 000000000000..fb020faf19ea
--- /dev/null
+++ b/code/modules/mob/living/carbon/carbon-blood.dm
@@ -0,0 +1,176 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station Developers *//
+
+/**
+ * Creates our blood.
+ */
+/mob/living/carbon/proc/create_blood()
+ if(!blood_holder)
+ blood_holder = new
+ reset_blood_to_species()
+
+/**
+ * Hard resets our data to our 'natural' blood.
+ *
+ * @params
+ * * do_not_regenerate - if set, do not reset to species.blood_volume, instead use current
+ */
+/mob/living/carbon/proc/reset_blood_to_species(do_not_regenerate)
+ if(!blood_holder)
+ return
+
+ if(!do_not_regenerate)
+ blood_holder.set_host_volume(species.blood_volume)
+ #warn erase all others, combine volumes
+
+ blood_holder.set_host_fragment_to(create_natural_blood_fragment())
+ #warn erase all others, combine volumes
+
+/**
+ * Imprint ourselves on an outgoing blood mixture.
+ *
+ * * Accepts null, for ease of use.
+ *
+ * @return the imprinted mixture
+ */
+/mob/living/carbon/proc/imprint_blood_mixture(datum/blood_mixture/mixture)
+ if(!mixture)
+ return null
+ mixture.legacy_trace_chem = list2params(bloodstr.reagent_volumes)
+ mixture.legacy_virus2 = virus_copylist(virus2)
+ mixture.legacy_antibodies = antibodies?.Copy()
+ return mixture
+
+/**
+ * Gets our blood mixture.
+ *
+ * todo: get_blood
+ *
+ * @return /datum/blood_mixture or null
+ */
+/mob/living/carbon/proc/get_blood_mixture() as /datum/blood_mixture
+ var/datum/blood_mixture/mixture = blood_holder?.draw(0)
+ if(!mixture)
+ return
+ imprint_blood_mixture(mixture)
+ return mixture
+
+/**
+ * Regenerates our blood by a certain amount / volume.
+ *
+ * @return amount regenerated
+ */
+/mob/living/carbon/proc/regen_blood(amount)
+ return blood_holder.adjust_host_volume(amount)
+
+/**
+ * [take_checked_blood_mixture] but fast,
+ *
+ * * This returns just a number, not the mixture.
+ *
+ * @return amount erased
+ */
+/mob/living/carbon/proc/erase_checked_blood(amount) as num
+ return blood_holder.erase_checked_amount(amount)
+
+/**
+ * [take_mixture] but fast,
+ *
+ * * This returns just a number, not the mixture.
+ *
+ * @return amount erased
+ */
+/mob/living/carbon/proc/erase_blood(amount) as num
+ return blood_holder.erase_amount(amount)
+
+
+/**
+ * Takes a blood mixture from ourselves.
+ *
+ * * Expensive
+ * * Fails if we don't have enough.
+ *
+ * todo: take_checked_blood
+ *
+ * @params
+ * * amount - amount to take
+ * * infinite - allow taking any amount, don't actually remove any
+ *
+ * @return mixture or null
+ */
+/mob/living/carbon/proc/take_checked_blood_mixture(amount) as /datum/blood_mixture
+ return imprint_blood_mixture(blood_holder?.checked_draw(amount))
+
+/**
+ * Takes a blood mixture from ourselves.
+ *
+ * * Expensive
+ *
+ * todo: take_blood
+ *
+ * @params
+ * * amount - amount to take
+ * * infinite - allow taking any amount, don't actually remove any
+ *
+ * @return mixture or null
+ */
+/mob/living/carbon/proc/take_blood_mixture(amount, infinite) as /datum/blood_mixture
+ return imprint_blood_mixture(blood_holder?.draw(amount, infinite))
+
+/**
+ * Puts a blood mixture into ourselves.
+ *
+ * todo: give_blood
+ *
+ * @params
+ * * mixture - mixture descriptor
+ * * amount - the amount. defaults to the mixture's `ctx_return_amount`.
+ *
+ * @return amount given
+ */
+/mob/living/carbon/proc/give_blood_mixture(datum/blood_mixture/mixture, amount)
+ if(!blood_holder)
+ return 0
+ if(isnull(amount))
+ amount = mixture.ctx_return_amount
+
+ // give them the blood
+ blood_holder.inject_mixture(mixture, amount)
+ // give them the sniffles
+ var/list/i_hate_old_virology = virus_copylist(mixture.legacy_virus2)
+ for(var/id in i_hate_old_virology)
+ var/datum/disease2/the_coof = i_hate_old_virology[id]
+ infect_virus2(src, the_coof, TRUE)
+ // give them the anti-sniffles but only sometimes
+ // yes this does mean IV drips are very good for this. too bad!
+ // i don't care to deal with this dumb shit.
+ if(length(mixture.legacy_antibodies) && prob(5))
+ antibodies |= mixture.legacy_antibodies
+ // this is very questionable but this does mean that you can cross-contaminate chemicals via blood
+ // this dumb part is the chemicals aren't actually in the extracted reagent holder,
+ // so this only .. does anything to humans.
+ // oh well. here, let's fix this with this one simple trick:
+ // todo: taking blood should generally draw out a bit of the reagents in someone's bloodstream,
+ // without using the trace chem system!
+ var/list/decoded_trace_chem = params2list(mixture.legacy_trace_chem)
+ for(var/id in decoded_trace_chem)
+ var/volume_str = decoded_trace_chem[id]
+ // the math here is weird; this means that this scales to blood volume and not bloodstream volume of
+ // where it came from. oh well, we'll fix it later. it's called legacy_trace_chem for a reason.
+ bloodstr.add_reagent(id, (text2num(volume_str) / species.blood_volume) * amount)
+
+ return amount
+
+/**
+ * Makes a blood fragment of our natural blood
+ */
+/mob/living/carbon/proc/create_natural_blood_fragment()
+ var/datum/blood_fragment/creating = new
+ creating.legacy_blood_dna = dna.unique_enzymes
+ creating.legacy_blood_type = dna.b_type
+ creating.legacy_donor = src
+ creating.legacy_species = isSynthetic() ? "synthetic" : species.name
+ #warn how to deal with name / color?
+ creating.legacy_name = species.get_blood_name(src)
+ creating.color = species.get_blood_colour(src)
+ return creating
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 4e26578962f9..001233b556a2 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1,3 +1,9 @@
+/mob/living/carbon
+ //* Organs, Reagents, Biologies *//
+
+ /// Our blood holder.
+ var/datum/blood_holder/blood_holder
+
/mob/living/carbon/Initialize(mapload)
. = ..()
//setup reagent holders
@@ -9,6 +15,7 @@
default_language = RSlanguages.legacy_resolve_language_name(species_language)
/mob/living/carbon/Destroy()
+ QDEL_NULL(blood_holder)
qdel(ingested)
qdel(touching)
// We don't qdel(bloodstr) because it's the same as qdel(reagents)
diff --git a/code/modules/mob/living/carbon/human/blood.dm b/code/modules/mob/living/carbon/human/blood.dm
index f220de966e8b..4eb7de0a3dff 100644
--- a/code/modules/mob/living/carbon/human/blood.dm
+++ b/code/modules/mob/living/carbon/human/blood.dm
@@ -11,48 +11,14 @@ var/const/BLOOD_VOLUME_SURVIVE = 40
var/const/CE_STABLE_THRESHOLD = 0.5
*/
-/mob/living/carbon/human/var/datum/reagent_holder/vessel // Container for blood and BLOOD ONLY. Do not transfer other chems here.
/mob/living/carbon/human/var/var/pale = 0 // Should affect how mob sprite is drawn, but currently doesn't.
-//Initializes blood vessels
-/mob/living/carbon/human/proc/make_blood()
-
- if(vessel)
- return
-
- if(species.species_flags & NO_BLOOD)
- return
-
- vessel = new/datum/reagent_holder(species.blood_volume)
- vessel.my_atom = src
-
- if(!should_have_organ(O_HEART)) //We want the var for safety but we can do without the actual blood.
- return
-
- vessel.add_reagent("blood",species.blood_volume)
-
-//Resets blood data
-/mob/living/carbon/human/proc/fixblood()
- for(var/datum/reagent/blood/B in vessel.reagent_list)
- if(B.id == "blood")
- B.data = list( "donor"=src,"viruses"=null,"species"=species.name,"blood_DNA"=dna.unique_enzymes,"blood_colour"= species.get_blood_colour(src),"blood_type"=dna.b_type, \
- "resistances"=null,"trace_chem"=null, "virus2" = null, "antibodies" = list(), "blood_name" = species.get_blood_name(src))
-
- if(isSynthetic())
- B.data["species"] = "synthetic"
-
-
- B.color = B.data["blood_colour"]
- B.name = B.data["blood_name"]
-
-/mob/living/carbon/human/proc/fixblood_if_broken()
+/mob/living/carbon/human/proc/reset_blood_to_species_if_needed(do_not_regenerate)
if(species.species_flags & NO_BLOOD)
return
if(!should_have_organ(O_HEART))
return
- if(!vessel.has_reagent("blood"))
- vessel.add_reagent("blood", 0.1)
- fixblood()
+ reset_blood_to_species(do_not_regenerate)
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood()
@@ -67,22 +33,15 @@ var/const/CE_STABLE_THRESHOLD = 0.5
if(stat != DEAD && bodytemperature >= 170) //Dead or cryosleep people do not pump the blood.
- var/blood_volume_raw = vessel.get_reagent_amount("blood")
+ var/blood_volume_raw = blood_holder.get_total_volume()
var/blood_volume = round((blood_volume_raw/species.blood_volume)*100) // Percentage.
+ #warn assimilate other bloods; no hemolytic reaction system yet
+
//Blood regeneration if there is some space
if(blood_volume_raw < species.blood_volume)
- var/datum/reagent/blood/B = locate() in vessel.reagent_list //Grab some blood
- if(B) // Make sure there's some blood at all
- if(B.data["donor"] != src) //If it's not theirs, then we look for theirs
- for(var/datum/reagent/blood/D in vessel.reagent_list)
- if(D.data["donor"] == src)
- B = D
- break
-
- B.volume += 0.1 // regenerate blood VERY slowly
- if(CE_BLOODRESTORE in chem_effects)
- B.volume += chem_effects[CE_BLOODRESTORE]
+ var/regenerating_volume = 0.1 + max(0, chem_effects[CE_BLOODRESTORE])
+ blood_holder.adjust_host_volume(regenerating_volume)
// Damaged heart virtually reduces the blood volume, as the blood isn't
// being pumped properly anymore.
@@ -210,209 +169,119 @@ var/const/CE_STABLE_THRESHOLD = 0.5
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/human/proc/drip(var/amt)
- if(remove_blood(amt))
- blood_splatter(src,src)
-
-/mob/living/carbon/human/proc/remove_blood(var/amt)
- if(!should_have_organ(O_HEART)) //TODO: Make drips come from the reagents instead.
- return 0
-
- if(!amt)
- return 0
-
- if(amt >= vessel.get_reagent_amount("blood"))
- amt = vessel.get_reagent_amount("blood") - 1 // Bit of a safety net; it's impossible to add blood if there's not blood already in the vessel.
-
- return vessel.remove_reagent("blood",amt * (src.mob_size/MOB_MEDIUM))
+ if(erase_blood(amt))
+ blood_splatter_legacy(get_turf(src), get_blood_mixture())
/****************************************************
BLOOD TRANSFERS
****************************************************/
-//Gets blood from mob to the container, preserving all data in it.
-/mob/living/carbon/proc/take_blood(obj/item/reagent_containers/container, var/amount)
-
- var/datum/reagent/B = get_blood(container.reagents)
- if(!B)
- B = new /datum/reagent/blood
- B.holder = container.reagents
- B.volume += amount
- B.initialize_data()
- // todo: burn this file with fire
- container.reagents.reagent_list |= B
- container.reagents.update_total()
-
- //set reagent data
- B.data["donor"] = src
- if (!B.data["virus2"])
- B.data["virus2"] = list()
- B.data["virus2"] |= virus_copylist(src.virus2)
- B.data["antibodies"] = src.antibodies
- B.data["blood_DNA"] = copytext(src.dna.unique_enzymes,1,0)
- B.data["blood_type"] = copytext(src.dna.b_type,1,0)
-
- // Putting this here due to return shenanigans.
- if(istype(src,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = src
- B.data["blood_colour"] = H.species.get_blood_colour(H)
- B.color = B.data["blood_colour"]
-
- var/list/temp_chem = list()
- for(var/datum/reagent/R in src.reagents.reagent_list)
- temp_chem += R.id
- temp_chem[R.id] = R.volume
- B.data["trace_chem"] = list2params(temp_chem)
- return B
+/**
+ * puts blood into container
+ *
+ * @return amount transferred
+ */
+/mob/living/carbon/proc/take_blood_legacy(obj/item/reagent_containers/container, amount)
+ ASSERT(container.reagents)
-//For humans, blood does not appear from blue, it comes from vessels.
-/mob/living/carbon/human/take_blood(obj/item/reagent_containers/container, var/amount)
+ var/wanted = max(0, container.reagents.maximum_volume - container.reagents.total_volume)
+ if(!wanted)
+ return 0
- if(!should_have_organ(O_HEART))
- return null
+ var/datum/blood_mixture/taken_mixture = take_blood_mixture(wanted)
+ var/amount_given = container.reagents.add_reagent(/datum/reagent/blood, taken_mixture.ctx_return_amount, taken_mixture)
- if(vessel.get_reagent_amount("blood") < amount)
- return null
+ if(amount_given < taken_mixture.ctx_return_amount)
+ // give back the remainder if we couldn't fill
+ give_blood_mixture(taken_mixture, taken_mixture.ctx_return_amount - amount_given)
- if(amount >= vessel.get_reagent_amount("blood"))
- amount = vessel.get_reagent_amount("blood") - 1 // Bit of a safety net; it's impossible to add blood if there's not blood already in the vessel.
+ return amount_given
- . = ..()
- vessel.remove_reagent("blood",amount) // Removes blood if human
+//For humans, blood does not appear from blue, it comes from vessels.
+/mob/living/carbon/human/take_blood_legacy(obj/item/reagent_containers/container, amount)
+ if(!should_have_organ(O_HEART))
+ return 0
+ if(blood_holder.get_total_volume() < amount)
+ return 0
+ return ..()
//Transfers blood from container ot vessels
-/mob/living/carbon/proc/inject_blood(var/datum/reagent/blood/injected, var/amount)
- if (!injected || !istype(injected))
- return
- var/list/sniffles = virus_copylist(injected.data["virus2"])
- for(var/ID in sniffles)
- var/datum/disease2/disease/sniffle = sniffles[ID]
- infect_virus2(src,sniffle,1)
- if (injected.data["antibodies"] && prob(5))
- antibodies |= injected.data["antibodies"]
- var/list/chems = list()
- chems = params2list(injected.data["trace_chem"])
- for(var/C in chems)
- src.reagents.add_reagent(C, (text2num(chems[C]) / species.blood_volume) * amount)//adds trace chemicals to owner's blood
- reagents.update_total()
+/mob/living/carbon/proc/inject_blood_legacy(datum/blood_mixture/mixture, amount)
+ return give_blood_mixture(mixture, amount)
//Transfers blood from reagents to vessel, respecting blood types compatability.
-/mob/living/carbon/human/inject_blood(var/datum/reagent/blood/injected, var/amount)
-
+/mob/living/carbon/human/inject_blood_legacy(datum/blood_mixture/mixture, amount)
+ if(!blood_holder)
+ return 0
if(!should_have_organ(O_HEART))
- reagents.add_reagent("blood", amount, injected.data)
- reagents.update_total()
+ return ..()
+
+ // todo: ??? why is this here ???
+ reset_blood_to_species_if_needed()
+
+ // at some point we'll simulate immune system but for now this is just
+ // raw conversion to toxin
+ //
+ // too bad!
+
+ mixture = mixture.clone()
+ var/total_ratio_removed = 0
+ for(var/datum/blood_fragment/fragment as anything in mixture.fragments)
+ if(blood_holder.host_blood.compatible_with_self(fragment))
+ continue
+ // rejected
+ var/fragment_ratio = mixture.fragments[fragment]
+ bloodstr.add_reagent(/datum/reagent/toxin, amount * 0.5 * fragment_ratio)
+ total_ratio_removed += fragment_ratio
+ mixture.fragments -= fragment
+ if(total_ratio_removed)
+ amount *= (1 - total_ratio_removed)
+ var/expand_ratio = 1 / total_ratio_removed
+ for(var/datum/blood_fragment/expanding_to_fill as anything in mixture.fragments)
+ mixture.fragments[expanding_to_fill] *= expand_ratio
+
+ return ..()
+
+/proc/blood_splatter_legacy(turf/target, datum/blood_mixture/mixture, large)
+ if(!istype(target))
return
- fixblood_if_broken()
+ var/splatter_type = /obj/effect/debris/cleanable/blood/splatter
+ var/obj/effect/debris/cleanable/blood/existing_splatter
- var/datum/reagent/blood/our = get_blood(vessel)
+ // todo: change how this works, we should stop making effects like this! only one should ever
+ // exist.
- if (!injected || !our)
- return
- if(blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]) )
- reagents.add_reagent("toxin",amount * 0.5)
- reagents.update_total()
- else
- vessel.add_reagent("blood", amount, injected.data)
- vessel.update_total()
- ..()
-
-//Gets human's own blood.
-/mob/living/carbon/proc/get_blood(datum/reagent_holder/container)
- var/datum/reagent/blood/res = locate() in container.reagent_list //Grab some blood
- if(res) // Make sure there's some blood at all
- if(res.data["donor"] != src) //If it's not theirs, then we look for theirs
- for(var/datum/reagent/blood/D in container.reagent_list)
- if(D.data["donor"] == src)
- return D
- return res
-
-/proc/blood_incompatible(donor, receiver, donor_species, receiver_species)
- if(!donor || !receiver)
- return 0
-
- if(donor_species && receiver_species)
- if(donor_species != receiver_species)
- return 1
-
- var/donor_antigen = copytext(donor,1,length(donor))
- var/receiver_antigen = copytext(receiver,1,length(receiver))
- var/donor_rh = (findtext(donor,"+")>0)
- var/receiver_rh = (findtext(receiver,"+")>0)
-
- if(donor_rh && !receiver_rh) return 1
- switch(receiver_antigen)
- if("A")
- if(donor_antigen != "A" && donor_antigen != "O") return 1
- if("B")
- if(donor_antigen != "B" && donor_antigen != "O") return 1
- if("O")
- if(donor_antigen != "O") return 1
- //AB is a universal receiver.
- return 0
-
-/proc/blood_splatter(target, datum/reagent/blood/source, large)
-
- // We're not going to splatter at all because we're in something and that's silly.
- if(istype(source,/atom/movable))
- var/atom/movable/A = source
- if(!isturf(A.loc))
- return
-
- var/obj/effect/debris/cleanable/blood/B
- var/decal_type = /obj/effect/debris/cleanable/blood/splatter
- var/turf/T = get_turf(target)
- var/synth = 0
-
- if(istype(source,/mob/living/carbon/human))
- var/mob/living/carbon/human/M = source
- if(M.isSynthetic()) synth = 1
- source = M.get_blood(M.vessel)
-
- // Are we dripping or splattering?
var/list/drips = list()
- // Only a certain number of drips (or one large splatter) can be on a given turf.
- for(var/obj/effect/debris/cleanable/blood/drip/drop in T)
- drips |= drop.drips
+ for(var/obj/effect/debris/cleanable/blood/drip/drop in target)
+ drips += drop.icon_state
qdel(drop)
- if(!large && drips.len < 3)
- decal_type = /obj/effect/debris/cleanable/blood/drip
-
- // Find a blood decal or create a new one.
- B = locate(decal_type) in T
- if(!B)
- B = new decal_type(T)
-
- var/obj/effect/debris/cleanable/blood/drip/drop = B
- if(istype(drop) && drips && drips.len && !large)
- drop.add_overlay(drips)
- drop.drips |= drips
-
- // If there's no data to copy, call it quits here.
- if(!istype(source))
- return B
-
- // Update appearance.
- if(source.data["blood_colour"])
- B.basecolor = source.data["blood_colour"]
- B.synthblood = synth
- B.update_icon()
-
- if(source.data["blood_name"])
- B.name = source.data["blood_name"]
-
- // Update blood information.
- if(source.data["blood_DNA"])
- B.blood_DNA = list()
- if(source.data["blood_type"])
- B.blood_DNA[source.data["blood_DNA"]] = source.data["blood_type"]
- else
- B.blood_DNA[source.data["blood_DNA"]] = "O+"
-
- // Update virus information.
- if(source.data["virus2"])
- B.virus2 = virus_copylist(source.data["virus2"])
-
- B.fluorescent = 0
- B.invisibility = 0
- return B
+
+ if(!large && length(drips) < 3)
+ splatter_type = /obj/effect/debris/cleanable/blood/drip
+
+ existing_splatter = locate(splatter_type) in target
+ if(!existing_splatter)
+ existing_splatter = new splatter_type(target)
+
+ // todo: this is fucking stupid
+
+ var/obj/effect/debris/cleanable/blood/drip/is_it_drips = existing_splatter
+ if(istype(is_it_drips) && length(drips) && !large)
+ is_it_drips.add_overlay(drips)
+ is_it_drips.drips |= drips
+
+ // is there data to copy?
+ if(!mixture)
+ return existing_splatter
+
+ existing_splatter.name = mixture.get_name()
+ existing_splatter.basecolor = mixture.get_color()
+ existing_splatter.synthblood = mixture.legacy_is_synthetic
+ existing_splatter.update_icon()
+
+ existing_splatter.blood_DNA = mixture.legacy_get_forensic_blood_dna()
+ if(mixture.legacy_virus2)
+ existing_splatter.virus2 = virus_copylist(mixture.legacy_virus2)
+
+ return existing_splatter
diff --git a/code/modules/mob/living/carbon/human/health.dm b/code/modules/mob/living/carbon/human/health.dm
index 91ac2b4d3fe2..75dee9715c00 100644
--- a/code/modules/mob/living/carbon/human/health.dm
+++ b/code/modules/mob/living/carbon/human/health.dm
@@ -12,7 +12,7 @@
return
// blood
restore_blood()
- fixblood()
+ reset_blood_to_species()
// todo: this obviously doesn't respect reset_to_slot.
if(fix_missing || reset_to_slot)
diff --git a/code/modules/mob/living/carbon/human/human-damage-legacy.dm b/code/modules/mob/living/carbon/human/human-damage-legacy.dm
index 0f699869fc39..b4e8719a53e8 100644
--- a/code/modules/mob/living/carbon/human/human-damage-legacy.dm
+++ b/code/modules/mob/living/carbon/human/human-damage-legacy.dm
@@ -337,10 +337,7 @@
This function restores the subjects blood to max.
*/
/mob/living/carbon/human/proc/restore_blood()
- if(!should_have_organ(O_HEART))
- return
- if(vessel.total_volume < species.blood_volume)
- vessel.add_reagent("blood", species.blood_volume - vessel.total_volume)
+ blood_holder.set_host_volume(species.blood_volume)
/*
This function restores all organs.
diff --git a/code/modules/mob/living/carbon/human/human-defense-legacy.dm b/code/modules/mob/living/carbon/human/human-defense-legacy.dm
index 4d0e86fa83fb..b9bce27dc723 100644
--- a/code/modules/mob/living/carbon/human/human-defense-legacy.dm
+++ b/code/modules/mob/living/carbon/human/human-defense-legacy.dm
@@ -315,7 +315,7 @@
put_in_active_hand(O)
visible_message("[src] catches [O]!")
throw_mode_off()
- return
+ return COMPONENT_THROW_HIT_NEVERMIND
var/dtype = DAMAGE_TYPE_BRUTE
if(isitem(AM))
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index f529701c0e7d..d03e74b44115 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -153,7 +153,7 @@
//Handles chem traces
/mob/living/carbon/human/proc/handle_trace_chems()
//New are added for reagents to random organs.
- for(var/datum/reagent/A in reagents.reagent_list)
+ for(var/datum/reagent/A in reagents.get_reagent_datums())
var/obj/item/organ/O = pick(organs)
O.trace_chemicals[A.name] = 100
@@ -161,4 +161,4 @@
var/list/all_bits = internal_organs|organs
for(var/obj/item/organ/O in all_bits)
O.set_dna(dna)
- fixblood() // make sure we have the right DNA since blood is an ""organ"" (scientists say it is!!)
+ reset_blood_to_species() // make sure we have the right DNA since blood is an ""organ"" (scientists say it is!!)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index cfd9aec30caa..82cc52fc04b1 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1795,7 +1795,7 @@
if(Pump)
temp += Pump.standard_pulse_level - PULSE_NORM
- if(round(vessel.get_reagent_amount("blood")) <= species.blood_volume*species.blood_level_danger) //how much blood do we have
+ if(round(blood_holder.get_total_volume()) <= species.blood_volume*species.blood_level_danger) //how much blood do we have
temp = temp + 3 //not enough :(
if(status_flags & STATUS_FAKEDEATH)
@@ -1807,7 +1807,7 @@
temp = max(0, temp + modifier_shift) // No negative pulses.
if(Pump)
- for(var/datum/reagent/R in reagents.reagent_list)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
if(R.id in bradycardics)
if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level)
temp--
@@ -1817,11 +1817,11 @@
if(R.id in heartstopper) //To avoid using fakedeath
temp = PULSE_NONE
if(R.id in cheartstopper) //Conditional heart-stoppage
- if(R.volume >= R.overdose)
+ if(reagents.get_reagent_amount(R.id) >= R.overdose)
temp = PULSE_NONE
return temp * brain_modifier
//handles different chems' influence on pulse
- for(var/datum/reagent/R in reagents.reagent_list)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
if(R.id in bradycardics)
if(temp <= PULSE_THREADY && temp >= PULSE_NORM)
temp--
@@ -1831,7 +1831,7 @@
if(R.id in heartstopper) //To avoid using fakedeath
temp = PULSE_NONE
if(R.id in cheartstopper) //Conditional heart-stoppage
- if(R.volume >= R.overdose)
+ if(reagents.get_reagent_amount(R.id) >= R.overdose)
temp = PULSE_NONE
return max(0, round(temp * brain_modifier))
diff --git a/code/modules/mob/living/carbon/taste.dm b/code/modules/mob/living/carbon/taste.dm
index 0a047fe30bf2..d3ead87eb550 100644
--- a/code/modules/mob/living/carbon/taste.dm
+++ b/code/modules/mob/living/carbon/taste.dm
@@ -24,11 +24,13 @@ calculate text size per text.
var/list/out = list()
var/list/tastes = list() //descriptor = strength
if(minimum_percent <= 100)
- for(var/datum/reagent/R in reagent_list)
+ for(var/id in reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
if(!R.taste_mult)
continue
+ #warn check this shit
if(R.id == "nutriment") //this is ugly but apparently only nutriment (not subtypes) has taste data TODO figure out why
- var/list/taste_data = R.get_data()
+ var/list/taste_data = reagent_datas?[id]
for(var/taste in taste_data)
if(taste in tastes)
tastes[taste] += taste_data[taste]
diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
index 07f1d3ef29eb..d5282bff2cca 100644
--- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
+++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
@@ -103,9 +103,9 @@
user.visible_message("[user] sniffs at \the [target.name].", "You sniff \the [target.name]...")
if(!isnull(target.reagents))
var/dat = ""
- if(target.reagents.reagent_list.len > 0)
- for (var/datum/reagent/R in target.reagents.reagent_list)
- dat += "\n \t [R]"
+ for(var/id in target.reagents.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ dat += "\n \t [R]"
if(dat)
to_chat(user, "Your BOOP module indicates: [dat]")
else
diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm
index 6c03d9fd4db1..40a5a99c3870 100644
--- a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm
+++ b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm
@@ -282,9 +282,10 @@
dat += "Significant brain damage detected.
"
if(patient.getCloneLoss())
dat += "Patient may be improperly cloned.
"
- if(patient.reagents.reagent_list.len)
- for(var/datum/reagent/R in patient.reagents.reagent_list)
- dat += "[R.name]:
[round(R.volume, 0.1)] units
"
+ for(var/id in patient.reagents.reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = patient.reagents.reagent_volumes[id]
+ dat += "[R.name]:
[round(volume, 0.1)] units
"
dat += ""
var/datum/browser/popup = new(user, "sleeper_b", "[name] Console", 400, 500, src)
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm
index 1306b7868f26..6a54ac7e842d 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm
@@ -174,7 +174,7 @@
if(!docile && ishuman(host) && chemicals < max_chemicals)
var/mob/living/carbon/human/H = host
- H.vessel.remove_reagent("blood", 1)
+ H.take_blood_mixture(1)
if(!H.reagents.has_reagent("inaprovaline"))
H.reagents.add_reagent("inaprovaline", 1)
chemicals += 2
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm
index 2f8bf86e6d79..ccea54ce2103 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm
@@ -111,7 +111,11 @@
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_LEG)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_L_ARM)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_ARM)
- blood_splatter(src, M, 1)
+ var/datum/blood_mixture/using_blood_mixture
+ if(iscarbon(M))
+ var/mob/living/carbon/carbon_victim = M
+ using_blood_mixture = carbon_victim.get_blood_mixture()
+ blood_splatter_legacy(get_turf(src), using_blood_mixture, TRUE)
/mob/living/simple_mob/animal/horing/handle_special()
if(ai_holder)
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/xenomorph/xenomorph_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/animal/xenomorph/xenomorph_abilities.dm
index c0a083a3e38f..1da1e4fb8531 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/xenomorph/xenomorph_abilities.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/xenomorph/xenomorph_abilities.dm
@@ -42,11 +42,11 @@
/mob/living/simple_mob/animal/space/xenomorph/breaker/Bump(atom/movable/AM)
if(charging)
- visible_message("[src] runs [AM]!")
+ visible_message("[src] rams [AM]!")
if(istype(AM, /mob/living))
var/mob/living/M = AM
- M.afflict_stun(20 * 5)
- M.afflict_paralyze(20 * 3)
+ M.afflict_paralyze(1 SECONDS)
+ M.afflict_knockdown(2 SECONDS)
var/throwdir = pick(turn(dir, 45), turn(dir, -45))
M.throw_at_old(get_step(src.loc, throwdir), 1, 1, src)
runOver(M) // Actually should not use this, placeholder
@@ -66,7 +66,12 @@
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_LEG)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_L_ARM)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_ARM)
- blood_splatter(src, M, 1)
+
+ var/datum/blood_mixture/to_use
+ if(iscarbon(M))
+ var/mob/living/carbon/carbon = M
+ to_use = carbon.get_blood_mixture()
+ blood_splatter_legacy(get_turf(M), to_use, TRUE)
/mob/living/simple_mob/animal/space/xenomorph/breaker/apply_melee_effects(atom/A)
if(isliving(A))
@@ -152,7 +157,11 @@
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_LEG)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_L_ARM)
M.apply_damage(0.5 * damage, DAMAGE_TYPE_BRUTE, BP_R_ARM)
- blood_splatter(src, M, 1)
+ var/datum/blood_mixture/using_blood_mixture
+ if(iscarbon(M))
+ var/mob/living/carbon/carbon_victim = M
+ using_blood_mixture = carbon_victim.get_blood_mixture()
+ blood_splatter_legacy(get_turf(src), using_blood_mixture, TRUE)
/mob/living/simple_mob/animal/space/xenomorph/monarch/apply_melee_effects(atom/A)
if(isliving(A))
diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm
index fee458b99ca3..e1c506702eaf 100644
--- a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm
@@ -139,7 +139,7 @@
/mob/living/simple_mob/animal/gutshank/proc/blood_drink(var/mob/living/carbon/human/M)
if(istype(M))
to_chat(M, "The [src] pierces your flesh! You feel a sickening suction!")
- M.vessel.remove_reagent("blood",rand(10,20))
+ M.take_blood_mixture(rand(10, 20))
/mob/living/simple_mob/animal/gutshank/death()
STOP_PROCESSING(SSobj, src)
@@ -265,7 +265,7 @@
/mob/living/simple_mob/animal/shank/proc/blood_drink(var/mob/living/carbon/human/M)
if(istype(M))
to_chat(M, "The [src] pierces your flesh! You feel a sickening suction!")
- M.vessel.remove_reagent("blood",rand(20,25))
+ M.take_blood_mixture(rand(20, 25))
/mob/living/simple_mob/animal/shank/update_icon()
if(rideable)
diff --git a/code/modules/organs/external/external.dm b/code/modules/organs/external/external.dm
index e6507bcfb7a3..7db8976e1820 100644
--- a/code/modules/organs/external/external.dm
+++ b/code/modules/organs/external/external.dm
@@ -801,7 +801,7 @@ Note that amputating the affected organ does in fact remove the infection from t
if(!(W.can_autoheal() || (bicardose && inaprovaline) || myeldose)) //bicaridine and inaprovaline stop internal wounds from growing bigger with time, unless it is so small that it is already healing
W.open_wound(0.1)
- owner.vessel.remove_reagent("blood", W.damage/40) //line should possibly be moved to handle_blood, so all the bleeding stuff is in one place.
+ owner.erase_blood(W.damage / 40)
if(prob(1))
owner.custom_pain("You feel a stabbing pain in your [name]!", 50)
diff --git a/code/modules/organs/external/wound.dm b/code/modules/organs/external/wound.dm
index a56c1f153e98..5de7560bec10 100644
--- a/code/modules/organs/external/wound.dm
+++ b/code/modules/organs/external/wound.dm
@@ -18,7 +18,7 @@
if((damage > 5 || damage + burn_dam >= 15) && type == WOUND_TYPE_BURN && (robotic < ORGAN_ROBOT) && !(species.species_flags & NO_BLOOD))
var/fluid_loss = 0.4 * (damage/(owner.getMaxHealth() - config_legacy.health_threshold_dead)) * owner.species.blood_volume*(1 - owner.species.blood_level_fatal)
- owner.remove_blood(fluid_loss)
+ owner.erase_blood(fluid_loss)
// first check whether we can widen an existing wound
if(length(wounds) > 0 && prob(max(50+(wound_tally-1)*10,90)))
diff --git a/code/modules/organs/internal/misc.dm b/code/modules/organs/internal/misc.dm
index f06ac698a3bd..12e784a029ea 100644
--- a/code/modules/organs/internal/misc.dm
+++ b/code/modules/organs/internal/misc.dm
@@ -16,19 +16,19 @@
owner.reagents.add_reagent(chem, 5)
// They're also super gross and ooze ichor.
- if(prob(5))
- var/mob/living/carbon/human/H = owner
- if(!istype(H))
- return
-
- var/datum/reagent/blood/B = locate(/datum/reagent/blood) in H.vessel.reagent_list
- blood_splatter(H,B,1)
- var/obj/effect/debris/cleanable/blood/splatter/goo = locate() in get_turf(owner)
- if(goo)
- goo.name = "husk ichor"
- goo.desc = "It's thick and stinks of decay."
- goo.basecolor = "#412464"
- goo.update_icon()
+ // if(prob(5))
+ // var/mob/living/carbon/human/H = owner
+ // if(!istype(H))
+ // return
+
+ // var/datum/reagent/blood/B = locate(/datum/reagent/blood) in H.vessel.reagent_list
+ // blood_splatter(H,B,1)
+ // var/obj/effect/debris/cleanable/blood/splatter/goo = locate() in get_turf(owner)
+ // if(goo)
+ // goo.name = "husk ichor"
+ // goo.desc = "It's thick and stinks of decay."
+ // goo.basecolor = "#412464"
+ // goo.update_icon()
/obj/item/organ/internal/borer/removed(var/mob/living/user)
diff --git a/code/modules/organs/internal/species/unathi.dm b/code/modules/organs/internal/species/unathi.dm
index 72167c29ace5..0c61ed1b9456 100644
--- a/code/modules/organs/internal/species/unathi.dm
+++ b/code/modules/organs/internal/species/unathi.dm
@@ -17,15 +17,13 @@
..()
if(!owner) return
- var/datum/reagent/coffee = locate(/datum/reagent/drink/coffee) in owner.reagents.reagent_list
- if(coffee)
+ if(owner.reagents.has_reagent(/datum/reagent/drink/coffee::id))
if(is_bruised())
owner.adjustToxLoss(0.1 * (delta_time * 5))
else if(is_broken())
owner.adjustToxLoss(0.3 * (delta_time * 5))
- var/datum/reagent/sugar = locate(/datum/reagent/sugar) in owner.reagents.reagent_list
- if(sugar)
+ if(owner.reagents.has_reagent(/datum/reagent/sugar::id))
if(is_bruised())
owner.adjustToxLoss(0.1 * (delta_time * 5))
else if(is_broken())
diff --git a/code/modules/organs/internal/subtypes/kidneys.dm b/code/modules/organs/internal/subtypes/kidneys.dm
index e8ccb9d844c0..f997311b2d31 100644
--- a/code/modules/organs/internal/subtypes/kidneys.dm
+++ b/code/modules/organs/internal/subtypes/kidneys.dm
@@ -12,7 +12,7 @@
// Coffee is really bad for you with busted kidneys.
// This should probably be expanded in some way, but fucked if I know
// what else kidneys can process in our reagent list.
- var/datum/reagent/coffee = locate(/datum/reagent/drink/coffee) in owner.reagents.reagent_list
+ var/datum/reagent/coffee = locate(/datum/reagent/drink/coffee) in owner.reagents.get_reagent_datums()
if(coffee)
if(is_bruised())
owner.adjustToxLoss(0.1 * (dt * 5))
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 3ca01afc5760..17798a8498d8 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -383,19 +383,20 @@
reconsider_processing()
/obj/item/organ/proc/replaced(var/mob/living/carbon/human/target,var/obj/item/organ/external/affected)
+ if(!istype(target))
+ return
- if(!istype(target)) return
-
- var/datum/reagent/blood/transplant_blood = locate(/datum/reagent/blood) in reagents.reagent_list
+ var/datum/blood_mixture/mixture_data = reagents.get_reagent_data(/datum/reagent/blood)
transplant_data = list()
- if(!transplant_blood)
+ if(!mixture_data || length(mixture_data.fragments) < 1)
transplant_data["species"] = target?.species.name
transplant_data["blood_type"] = target?.dna.b_type
transplant_data["blood_DNA"] = target?.dna.unique_enzymes
else
- transplant_data["species"] = transplant_blood?.data["species"]
- transplant_data["blood_type"] = transplant_blood?.data["blood_type"]
- transplant_data["blood_DNA"] = transplant_blood?.data["blood_DNA"]
+ var/datum/blood_fragment/use_fragment = mixture_data.fragments[1]
+ transplant_data["species"] = use_fragment.legacy_species
+ transplant_data["blood_type"] = use_fragment.legacy_blood_type
+ transplant_data["blood_DNA"] = use_fragment.legacy_blood_dna
owner = target
loc = owner
@@ -630,7 +631,7 @@
// immunosuppressant that changes transplant data to make it match.
if(dna && can_reject)
if(!rejecting)
- if(blood_incompatible(dna.b_type, owner.dna.b_type, species.name, owner.species.name)) // Process species by name.
+ if(!legacy_blood_compatible_with_self(owner.dna.b_type, dna.b_type, owner.species.name, species.name))
rejecting = 1
else
rejecting++ //Rejection severity increases over time.
diff --git a/code/modules/power/engines/rust/fuel_assembly/fuel_compressor.dm b/code/modules/power/engines/rust/fuel_assembly/fuel_compressor.dm
index 496e5bf66775..8ca1f64ee8fb 100644
--- a/code/modules/power/engines/rust/fuel_assembly/fuel_compressor.dm
+++ b/code/modules/power/engines/rust/fuel_assembly/fuel_compressor.dm
@@ -14,16 +14,16 @@
/obj/machinery/fusion_fuel_compressor/proc/do_special_fuel_compression(var/obj/item/thing, var/mob/user)
if(istype(thing) && thing.reagents && thing.reagents.total_volume && thing.is_open_container())
- if(thing.reagents.reagent_list.len > 1)
+ if(length(thing.reagents.reagent_volumes) > 1)
to_chat(user, "The contents of \the [thing] are impure and cannot be used as fuel.")
return 1
if(thing.reagents.total_volume < 50)
to_chat(user, "You need at least fifty units of material to form a fuel rod.")
return 1
- var/datum/reagent/R = thing.reagents.reagent_list[1]
+ var/datum/reagent/R = SSchemistry.fetch_reagent(thing.reagents.reagent_volumes[1])
visible_message("\The [src] compresses the contents of \the [thing] into a new fuel assembly.")
var/obj/item/fuel_assembly/F = new(get_turf(src), R.id, R.color)
- thing.reagents.remove_reagent(R.id, R.volume)
+ thing.reagents.remove_reagent(R.id, thing.reagents.get_reagent_amount(R.id))
user.put_in_hands(F)
else if(istype(thing, /obj/machinery/power/supermatter))
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 1fbe3f438125..b7d9b3d9da29 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -85,7 +85,7 @@
end_nutrition = H.nutrition
if(start_nutrition - max(0, end_nutrition) < rechargeamt / 10)
- H.remove_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition)))
+ H.erase_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition)))
power_supply.give(rechargeamt) //... to recharge 1/5th the battery
update_icon()
diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm
index 23c87c79e17a..f4bc84fccacd 100644
--- a/code/modules/projectiles/guns/projectile/dartgun.dm
+++ b/code/modules/projectiles/guns/projectile/dartgun.dm
@@ -58,18 +58,6 @@
if(istype(dart))
fill_dart(dart)
-/obj/item/gun/ballistic/dartgun/examine(mob/user, dist)
- //update_icon()
- //if (!..(user, 2))
- // return
- . = ..()
- if (beakers.len)
- to_chat(user, "[src] contains:")
- for(var/obj/item/reagent_containers/glass/beaker/B in beakers)
- if(B.reagents && B.reagents.reagent_list.len)
- for(var/datum/reagent/R in B.reagents.reagent_list)
- . += "[R.volume] units of [R.name]"
-
/obj/item/gun/ballistic/dartgun/attackby(obj/item/I as obj, mob/user as mob)
if(istype(I, /obj/item/reagent_containers/glass))
if(!istype(I, container_type))
@@ -105,9 +93,9 @@
var/i = 1
for(var/obj/item/reagent_containers/glass/beaker/B in beakers)
dat += "Beaker [i] contains: "
- if(B.reagents && B.reagents.reagent_list.len)
- for(var/datum/reagent/R in B.reagents.reagent_list)
- dat += "
[R.volume] units of [R.name], "
+ if(B.reagents?.total_volume)
+ for(var/datum/reagent/R in B.reagents.get_reagent_datums())
+ dat += "
[B.reagents.reagent_volumes[R.id]] units of [R.name], "
if (check_beaker_mixing(B))
dat += "Mixing "
else
diff --git a/code/modules/reagents/chemistry/_readme.dm b/code/modules/reagents/chemistry/_readme.dm
deleted file mode 100644
index 3833cd11ba3c..000000000000
--- a/code/modules/reagents/chemistry/_readme.dm
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
-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 , antitoxins 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
-
- Vars:
- list/datum/reagent/reagent_list
- List of reagent datums.
-
- total_volume
- Total volume of all reagents.
-
- maximum_volume
- Maximum volume.
-
- atom/my_atom
- Reference to the object that contains this.
-
- Procs:
-
- available_volume()
- Returns the remaining free volume in the holder.
-
- get_master_reagent()
- Returns the reference to the reagent with the largest volume
-
- get_master_reagent_name()
- Ditto, but returns the name.
-
- get_master_reagent_id()
- Ditto, but returns ID.
-
- update_total()
- Updates total volume, called automatically.
-
- handle_reactions()
- Checks reagents and triggers any reactions that happen. Usually called automatically.
-
- add_reagent(id, amount, data = null, safety = 0)
- Adds [amount] units of [id] reagent. [data] will be passed to reagent's mix_data() or initialize_data(). If [safety] is 0, handle_reactions() will be called. Returns 1 if successful, 0 otherwise.
-
- remove_reagent(id, amount, safety = 0)
- Ditto, but removes reagent. Returns 1 if successful, 0 otherwise.
-
- del_reagent(id)
- Removes all of the reagent.
-
- has_reagent(id, amount = 0)
- Checks if holder has at least [amount] of [id] reagent. Returns 1 if the reagent is found and volume is above [amount]. Returns 0 otherwise.
-
- clear_reagents()
- Removes all reagents.
-
- get_reagent_amount(id)
- Returns reagent volume. Returns 0 if reagent is not found.
-
- get_data(id)
- Returns get_data() of the reagent.
-
- get_reagents()
- Returns a string containing all reagent ids and volumes, e.g. "carbon(4),nittrogen(5)".
-
- remove_any(amount = 1)
- Removes up to [amount] of reagents from [src]. Returns actual amount removed.
-
- trans_to_holder(datum/reagent_holder/target, amount = 1, multiplier = 1, copy = 0)
- Transfers [amount] reagents from [src] to [target], multiplying them by [multiplier]. Returns actual amount removed from [src] (not amount transferred to [target]). If [copy] is 1, copies reagents instead.
-
- touch(var/atom/target)
- When applying reagents to an atom externally, touch() is called to trigger any on-touch effects of the reagent.
- This does not handle transferring reagents to things.
- For example, splashing someone with water will get them wet and extinguish them if they are on fire,
- even if they are wearing an impermeable suit that prevents the reagents from contacting the skin.
- Basically just defers to touch_mob(target), touch_turf(target), or touch_obj(target), depending on target's type.
- Not recommended to use this directly, since trans_to() calls it before attempting to transfer.
-
- touch_mob(mob/target)
- Calls each reagent's touch_mob(target).
-
- touch_turf(var/turf/target)
- Calls each reagent's touch_turf(target).
-
- touch_obj(var/obj/target)
- Calls each reagent's touch_obj(target).
-
- trans_to(var/atom/target, var/amount = 1, var/multiplier = 1, var/copy = 0)
- The general proc for applying reagents to things externally (as opposed to directly injected into the contents).
- It first calls touch, then the appropriate trans_to_*() or splash_mob().
- If for some reason you want touch effects to be bypassed (e.g. injecting stuff directly into a reagent container or person), call the appropriate trans_to_*() proc.
-
- Calls touch() before checking the type of [target], calling splash_mob(target, amount), trans_to_turf(target, amount, multiplier, copy), or trans_to_obj(target, amount, multiplier, copy).
-
- trans_id_to(atom/target, id, amount = 1)
- Transfers [amount] of [id] to [target]. Returns amount transferred.
-
- splash_mob(var/mob/target, var/amount = 1, var/clothes = 1)
- Checks mob's clothing if [clothes] is 1 and transfers [amount] reagents to mob's skin.
- Don't call this directly. Call apply_to() instead.
-
- trans_to_mob(mob/target, amount = 1, type = CHEM_INJECT, multiplier = 1, copy = 0)
- Transfers [amount] reagents to the mob's appropriate holder, depending on [type]. Ignores protection.
-
- trans_to_turf(turf/target, amount = 1, multiplier = 1, copy = 0)
- Turfs don't currently have any reagents. Puts [amount] reagents into a temporary holder, calls touch_turf(target) from it, and deletes it.
-
- trans_to_obj(var/turf/target, var/amount = 1, var/multiplier = 1, var/copy = 0)
- If target has reagents, transfers [amount] to it. Otherwise, same as trans_to_turf().
-
- atom/proc/create_reagents(var/max_vol)
- Creates a new reagent datum.
-
-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.
-
- Vars:
-
- name
- Name that shows up in-game.
-
- id
- ID that is used for internal tracking. MUST BE UNIQUE.
-
- description
- Description that shows up in-game.
-
- datum/reagent_holder/holder
- Reference to holder.
-
- reagent_state
- Could be GAS, LIQUID, or SOLID. Affects nothing. Reserved for future use.
-
- list/data
- Use varies by reagent. Custom variable. For example, blood stores blood group and viruses.
-
- volume
- Current volume.
-
- metabolism
- How quickly reagent is processed in mob's bloodstream; by default aslo affects ingest and touch metabolism.
-
- ingest_met
- How quickly reagent is processed when ingested; [metabolism] is used if zero.
-
- touch_met
- Ditto when touching.
-
- dose
- How much of the reagent has been processed, limited by [max_dose]. Used for reagents with varying effects (e.g. ethanol or rezadone) and overdosing.
-
- max_dose
- Maximum amount of reagent that has ever been in a mob. Exists so dose won't grow infinitely when small amounts of reagent are added over time.
-
- overdose
- If [dose] is bigger than [overdose], overdose() proc is called every tick.
-
- scannable
- If set to 1, will show up on health analyzers by name.
-
- affects_dead
- If set to 1, will affect dead players. Used by Adminordrazine.
-
- glass_icon_state
- Used by drinks. icon_state of the glass when this reagent is the master reagent.
-
- glass_name
- Ditto for glass name.
-
- glass_desc
- Ditto for glass desciption.
-
- glass_center_of_mass
- Used for glass placement on tables.
-
- color
- "#RRGGBB" or "#RRGGBBAA" where A is alpha channel.
-
- color_weight
- How much reagent affects color of holder. Used by paint.
-
- Procs:
-
- remove_self(var/amount)
- Removes [amount] of itself.
-
- touch_mob(var/mob/M)
- Called when reagent is in another holder and not splashing the mob. Can be used with noncarbons.
-
- touch_obj(obj/O)
- How reagent reacts with objects.
-
- touch_turf(turf/T)
- How reagent reacts with turfs.
-
- on_mob_life(var/mob/living/carbon/M, var/alien, var/location)
- Makes necessary checks and calls one of affect procs.
-
- affect_blood(mob/living/carbon/M, alien, removed)
- How reagent affects mob when injected. [removed] is the amount of reagent that has been removed this tick. [alien] is the mob's reagent flag.
-
- affect_ingest(mob/living/carbon/M, alien, removed)
- Ditto, ingested. Defaults to affect_blood with halved dose.
-
- affect_touch(mob/living/carbon/M, alien, removed)
- Ditto, touching.
-
- overdose(mob/living/carbon/M, alien)
- Called when dose is above overdose. Defaults to M.adjustToxLoss(REM).
-
- initialize_data(newdata)
- Called when reagent is created. Defaults to setting [data] to [newdata].
-
- mix_data(var/newdata, var/newamount)
- Called when [newamount] of reagent with [newdata] data is added to the current reagent. Used by paint.
-
- get_data()
- Returns data. Can be overriden.
-
-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.
-
- Vars:
-
- name
- Name of the reaction, currently unused.
-
- id
- ID of the reaction, must be unique.
-
- result
- ID of the resulting reagent. Can be null.
-
- list/required_reagents
- Reagents that are required for the reaction and are used up during it.
-
- list/catalysts
- Ditto, but not used up.
-
- list/inhibitors
- Opposite, prevent the reaction from happening.
-
- result_amount
- Amount of resulting reagent.
-
- mix_message
- Message that is shown to mobs when reaction happens.
-
- Procs:
-
- can_happen(var/datum/reagent_holder/holder)
- Customizable. If it returns 0, reaction will not happen. Defaults to always returning 1. Used by slime core reactions.
-
- on_reaction(datum/reagent_holder/holder, created_volume)
- Called when reaction happens. Used by explosives.
-
- send_data(var/datum/reagent_holder/T)
- Sets resulting reagent's data. Used by blood paint.
-
-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:
-
- 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/atom_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.
-
-*/
diff --git a/code/modules/reagents/chemistry/chemical_reaction.dm b/code/modules/reagents/chemistry/chemical_reaction.dm
index e4af0cf7e949..022767ebc011 100644
--- a/code/modules/reagents/chemistry/chemical_reaction.dm
+++ b/code/modules/reagents/chemistry/chemical_reaction.dm
@@ -203,6 +203,7 @@
//obtains any special data that will be provided to the reaction products
//this is called just before reactants are removed.
// todo: rework data system
+#warn refactor
/datum/chemical_reaction/proc/send_data(datum/reagent_holder/holder, reaction_limit)
return null
diff --git a/code/modules/reagents/chemistry/colors.dm b/code/modules/reagents/chemistry/colors.dm
deleted file mode 100644
index 1f239859b486..000000000000
--- a/code/modules/reagents/chemistry/colors.dm
+++ /dev/null
@@ -1,46 +0,0 @@
-/proc/mix_color_from_reagents(list/reagent_list)
- if(!istype(reagent_list))
- return
-
- var/mixcolor
- var/vol_counter = 0
- var/vol_temp
-
- for(var/datum/reagent/R in reagent_list)
- vol_temp = R.volume
- vol_counter += vol_temp
-
- if(!mixcolor)
- mixcolor = R.color
-
- else if (length(mixcolor) >= length(R.color))
- mixcolor = BlendRGB(mixcolor, R.color, vol_temp/vol_counter)
- else
- mixcolor = BlendRGB(R.color, mixcolor, vol_temp/vol_counter)
-
- return mixcolor
-
-/datum/reagent_holder/proc/get_color()
- // todo: cache this shit
- if(!reagent_list || !reagent_list.len)
- return "#ffffffff"
- if(reagent_list.len == 1) // It's pretty common and saves a lot of work
- var/datum/reagent/R = reagent_list[1]
- return R.color
-
- var/list/colors = list(0, 0, 0, 0)
- var/tot_w = 0
- for(var/datum/reagent/R in reagent_list)
- var/hex = uppertext(R.color)
- if(length(hex) == 7)
- hex += "FF"
- if(length(hex) != 9) // PANIC PANIC PANIC
- warning("Reagent [R.id] has an incorrect color set ([R.color])")
- hex = "#FFFFFFFF"
- colors[1] += hex2num(copytext(hex, 2, 4)) * R.volume * R.color_weight
- colors[2] += hex2num(copytext(hex, 4, 6)) * R.volume * R.color_weight
- colors[3] += hex2num(copytext(hex, 6, 8)) * R.volume * R.color_weight
- colors[4] += hex2num(copytext(hex, 8, 10)) * R.volume * R.color_weight
- tot_w += R.volume * R.color_weight
-
- return rgb(colors[1] / tot_w, colors[2] / tot_w, colors[3] / tot_w, colors[4] / tot_w)
diff --git a/code/modules/reagents/chemistry/reagent.dm b/code/modules/reagents/chemistry/reagent.dm
index 2b8dc3122323..99cae6702cb6 100644
--- a/code/modules/reagents/chemistry/reagent.dm
+++ b/code/modules/reagents/chemistry/reagent.dm
@@ -17,20 +17,27 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/// reagent flags - see [code/__DEFINES/reagents/flags.dm]
var/reagent_flags = NONE
- //* Filtering *//
- /// reagent filter flags - dynamic flags used for simulations of filtration/identification/detection
- ///
- /// * used for a lot of things
- /// * REAGENT_FILTER_GENERIC is a default because this allows us to have a single 'flags' on filter,
- /// instead of a 'include flags' and 'exclude flags'.
- var/reagent_filter_flags = REAGENT_FILTER_GENERIC
+ //* Color *//
+ /// our reagent color
+ var/color = "#000000"
+ /// multiplier to effective volume when calculating color
+ var/color_weight = 1
+
+ //* Data *//
+ /// Supports data system.
+ var/holds_data = FALSE
- //* Identity
+ //* Economy *//
+ /// Raw intrinsic worth of this reagent
+ var/worth = 0
+ /// economic category of the reagent
+ var/economic_category_reagent = ECONOMIC_CATEGORY_REAGENT_DEFAULT
+
+ //* Identity *//
/// our name - visible from guidebooks and to admins
var/name = "Reagent"
/// our description - visible from guidebooks and to admins
var/description = "A non-descript chemical of some kind."
- /// player-facing name - visible via scan tools
/// defaults to [name]
/// overrides name in guidebook
var/display_name
@@ -39,6 +46,14 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/// overrides desc in guidebook
var/display_description
+ //* Filtering *//
+ /// reagent filter flags - dynamic flags used for simulations of filtration/identification/detection
+ ///
+ /// * used for a lot of things
+ /// * REAGENT_FILTER_GENERIC is a default because this allows us to have a single 'flags' on filter,
+ /// instead of a 'include flags' and 'exclude flags'.
+ var/reagent_filter_flags = REAGENT_FILTER_GENERIC
+
//* Guidebook
/// guidebook flags
var/reagent_guidebook_flags = NONE
@@ -46,14 +61,13 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
var/reagent_guidebook_category = "Unsorted"
//? legacy / unsorted
+ var/glass_icon_state = null
+ var/glass_center_of_mass = null
var/taste_description = "bitterness"
/// How this taste compares to others. Higher values means it is more noticable
var/taste_mult = 1
- var/datum/reagent_holder/holder = null
var/reagent_state = REAGENT_SOLID
- var/list/data = null
- var/volume = 0
- var/metabolism = REM // This would be 0.2 normally
+ var/metabolism_rate = REM // This would be 0.2 normally
/// Used for vampric-Digestion
var/blood_content = 0
/// Organs that will slow the processing of this chemical.
@@ -62,8 +76,6 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
var/mrate_static = FALSE
var/ingest_met = 0
var/touch_met = 0
- var/dose = 0
- var/max_dose = 0
///Amount at which overdose starts
var/overdose = 0
///Modifier to overdose damage
@@ -81,20 +93,11 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
var/cup_desc = null
var/cup_center_of_mass = null
- var/color = "#000000"
- var/color_weight = 1
-
var/glass_icon = DRINK_ICON_DEFAULT
var/glass_name = "something"
var/glass_desc = "It's a glass of... what, exactly?"
var/list/glass_special = null // null equivalent to list()
- //? Economy
- /// Raw intrinsic worth of this reagent
- var/worth = 0
- /// economic category of the reagent
- var/economic_category_reagent = ECONOMIC_CATEGORY_REAGENT_DEFAULT
-
//? wiki markup generation additional
/// override "name"
var/wiki_name
@@ -105,21 +108,17 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/// forced sort ordering in its category - falls back to name otherwise.
var/wiki_sort = 0
-/datum/reagent/proc/remove_self(var/amount) // Shortcut
- if(holder)
- holder.remove_reagent(id, amount)
-
/// This doesn't apply to skin contact - this is for, e.g. extinguishers and sprays. The difference is that reagent is not directly on the mob's skin - it might just be on their clothing.
/datum/reagent/proc/touch_mob(mob/M, amount)
- return
+ SHOULD_NOT_OVERRIDE(TRUE)
/// Acid melting, cleaner cleaning, etc
/datum/reagent/proc/touch_obj(obj/O, amount)
- return
+ SHOULD_NOT_OVERRIDE(TRUE)
/// Cleaner cleaning, lube lubbing, etc, all go here
/datum/reagent/proc/touch_turf(turf/T, amount)
- return
+ SHOULD_NOT_OVERRIDE(TRUE)
/// Currently, on_mob_life is called on carbons. Any interaction with non-carbon mobs (lube) will need to be done in touch_mob.
/datum/reagent/proc/on_mob_life(var/mob/living/carbon/M, var/alien, var/datum/reagent_holder/metabolism/location, speed_mult = 1, force_allow_dead)
@@ -133,7 +132,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
return
var/datum/reagent_holder/metabolism/active_metab = location
- var/removed = metabolism
+ var/removed = metabolism_rate
var/mechanical_circulation = HAS_TRAIT(M, TRAIT_MECHANICAL_CIRCULATION)
var/ingest_rem_mult = 1
@@ -222,37 +221,41 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
removed = ingest_met * ingest_rem_mult
if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH))
removed = touch_met
+ var/volume = location.reagent_volumes[id]
+ var/datum/reagent_metabolism/metabolism = location.reagent_metabolisms[id]
removed = min(removed, volume)
- max_dose = max(volume, max_dose)
- dose = min(dose + removed, max_dose)
- if(removed >= (metabolism * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
+ metabolism.peak_dose = max(volume, metabolism.peak_dose)
+ metabolism.total_processed_dose = min(metabolism.peak_dose, metabolism.total_processed_dose + removed)
+ metabolism.cycles_so_far++
+ metabolism.legacy_volume_remaining = volume
+ metabolism.legacy_data = location.reagent_datas?[id]
+ metabolism.legacy_current_holder = active_metab
+ if(removed >= (metabolism_rate * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
switch(active_metab.metabolism_class)
if(CHEM_INJECT)
- affect_blood(M, alien, removed)
+ legacy_affect_blood(M, alien, removed, metabolism)
if(CHEM_INGEST)
- affect_ingest(M, alien, removed * ingest_abs_mult)
+ legacy_affect_ingest(M, alien, removed * ingest_abs_mult, metabolism)
if(CHEM_TOUCH)
- affect_touch(M, alien, removed)
+ legacy_affect_touch(M, alien, removed, metabolism)
if(overdose && (volume > overdose) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch))
- overdose(M, alien, removed)
- remove_self(removed)
+ metabolism.cycles_overdosing++
+ legacy_affect_overdose(M, alien, removed, metabolism)
+ else
+ metabolism.cycles_overdosing = 0
+ metabolism.legacy_current_holder = null
+ active_metab.remove_reagent(id, removed)
+
+/datum/reagent/proc/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
return
-// todo: on_mob_life with method of CHEM_INJECT, or tick_mob_blood
-/datum/reagent/proc/affect_blood(mob/living/carbon/M, alien, removed)
- return
-
-// todo: on_mob_life with method of CHEM_INGEST, or tick_mob_ingest
-/datum/reagent/proc/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/proc/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.bloodstr.add_reagent(id, removed)
return
-// todo: on_mob_life with method of CHEM_TOUCH, or tick_mob_touch
-/datum/reagent/proc/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/proc/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
return
-// todo: fourth apply method of CHEM_VAPOR implementation?
-
/datum/reagent/proc/handle_vampire(var/mob/living/carbon/M, var/alien, var/removed, var/is_vampire)
if(blood_content > 0 && is_vampire)
#define blud_warn_timer 3000
@@ -264,7 +267,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
return
M.nutrition += removed * blood_content //We should always be able to process real blood.
-/datum/reagent/proc/overdose(var/mob/living/carbon/M, var/alien, var/removed) // Overdose effect.
+/datum/reagent/proc/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(ishuman(M))
@@ -272,40 +275,60 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
overdose_mod *= H.species.chemOD_mod
M.adjustToxLoss(removed * overdose_mod)
-/datum/reagent/proc/initialize_data(newdata) // Called when the reagent is created.
- if(!isnull(newdata))
- data = newdata
- return
-
-/datum/reagent/proc/get_data() // Just in case you have a reagent that handles data differently.
- if(data && istype(data, /list))
- return data.Copy()
- else if(data)
- return data
- return null
-
-/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
- holder = null
- . = ..()
-
-/* DEPRECATED - TODO: REMOVE EVERYWHERE */
+//* Color *//
-/datum/reagent/proc/reaction_turf(var/turf/target, amt)
- touch_turf(target, amt)
+/**
+ * Only called if holds_data is TRUE.
+ */
+/datum/reagent/proc/compute_color_with_data(data)
+ return color
-/datum/reagent/proc/reaction_obj(var/obj/target, amt)
- touch_obj(target, amt)
+//* Data *//
-/datum/reagent/proc/reaction_mob(var/mob/target, amt)
- touch_mob(target, amt)
+/**
+ * Get data to feed in as the `data_initializer` during a reagents transfer.
+ *
+ * * `data` is not considered mutable. You may not edit it in this proc.
+ * * Do not modify the returned value. It is not considered mutable.
+ *
+ * @return an initializer. This initializer is not mutable.
+ */
+/datum/reagent/proc/make_copy_data_initializer(data)
+ return null
-/datum/reagent/proc/on_move(mob/M)
- return
+/**
+ * Preprocess data fed in during add_reagent
+ *
+ * * `data_initializer` is not considered mutable. You may not edit it in this proc.
+ * * Do not modify the returned value. It is not considered mutable.
+ *
+ * @return data to put into mix_data. This data is immutable.
+ */
+/datum/reagent/proc/preprocess_data(data_initializer)
+ return null
-/datum/reagent/proc/on_update(atom/A)
- return
+/**
+ * Mixes data
+ *
+ * * in add_reagent, this is called with the preprocessed new data
+ * * in transfer procs, this is called with the old data
+ * * this is not called if there's no reagents of ourselves in the new container.
+ * * `old_data` is considered mutable. `new_data` is not. This is becuase `old_data` belongs to the holder
+ * calling us, but `new_data` can potentially be a shared struct.
+ *
+ * @params
+ * * old_data - existing data; null if not there
+ * * old_volume - existing volume; 0 if not there
+ * * new_data - adding data; this is from the returns of `preprocess_data()`
+ * * new_volume - adding volume
+ * * holder - (optional) the holder we're mixing in, if any
+ *
+ * @return overall data to assign to reagent
+ */
+/datum/reagent/proc/mix_data(old_data, old_volume, new_data, new_volume, datum/reagent_holder/holder)
+ return null
-//* Guidebook
+//* Guidebook *//
/**
* Guidebook Data for TGUIGuidebookReagent
@@ -322,63 +345,119 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
"alcoholStrength" = null,
)
-//* Holder - Application
+//* Holder - Application *//
+
+/**
+ * Called when we're sprayed / splashed onto an obj
+ *
+ * @params
+ * * target - turf
+ * * remaining - how much is being applied / is remaining / is in the container right now
+ * * allocated - how much is supposed to be hitting the obj (useful for sprays)
+ * this will never be over remaining.
+ * * data - our reagent data, if any
+ * * spread_between - (optional) unlike turfs, there's potentially a lot of objects.
+ * this hints at how many objs are being hit. this is optional.
+ *
+ * @return amount used, if any
+ */
+/datum/reagent/proc/on_touch_obj(obj/target, remaining, allocated, data, spread_between)
+ return 0
/**
- * called when we first get applied to a mob
+ * Called when we're sprayed / splashed onto a turf
*
* @params
- * * target - target mob
- * * holder - the holder on the target mob
- * * method - an enum of how we're applied from [code/__DEFINES/chemistry.dm]
- * * amount - how much is being applied
- * * data - data. not necessarily a list, but casted as one. this is before mix_data is called.
+ * * target - turf
+ * * remaining - how much is being applied / is remaining / is in the container right now
+ * * allocated - how much is supposed to be hitting the turf (useful for sprays)
+ * this will never be over remaining.
+ * * data - our reagent data, if any
*
- * @return amount to inject into the mob side holder. defaults to amount. this can be overriden by the mob / transfer procs.
+ * @return amount used, if any
*/
-// todo: implement this proc, replace reaction mob and similar with it.
-// /datum/reagent/proc/apply_to_mob(mob/target, datum/reagent_holder/holder, amount, list/data)
-// return amount
+/datum/reagent/proc/on_touch_turf(turf/target, remaining, allocated, data)
+ return 0
/**
- * called when we first get sprayed/splashed on a non-mob
+ * This should only be called when the reagent reaches the mob's skin / whatever, not before.
*
- * not called if we're transferred into a holder on the obj
+ * * Do not manually implement splashing if a limb is specified. The caller does this already.
*
* @params
- * * target - the target.
- * * amount - how much is being applied
- * * data - data. not necessarily a list, but casted as one. this is before mix_data is caled.
+ * * target - the mob
+ * * remaining - how much is left in the thing being splashed.
+ * * allocated - how much is supposed to be hitting the target limb (useful for sprays).
+ * this will never be over remaining.
+ * it might be useful to subtract from this in overrides before invoking ..() sometimes.
+ * * data - our reagent data, if any
+ * * zone - (optional) the body zone targeted
+ * * limb - (optional) the external organ splashed onto.
+ *
+ * @return amount used, if any
*/
-// todo: implement this proc, replace touch_obj/reaction_obj and similar with it.
-// /datum/reagent/proc/apply_to_obj(obj/target, amount, list/data)
+/datum/reagent/proc/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ . = 0
+ if(istype(src, /mob/living/carbon))
+ var/mob/living/carbon/casted = src
+ . += on_touch_carbon(target, remaining, allocated, data, zone, casted.get_bodypart_for_zone(zone))
+ else if(istype(src, /mob/living/simple_mob))
+ . += on_touch_simple(target, remaining, allocated, data, zone)
+ else if(istype(src, /mob/living/silicon))
+ . += on_touch_silicon(target, remaining, allocated, data, zone)
+ return 0
/**
- * called when we first get sprayed/splashed on a turf
+ * Called by on_touch_mob().
*
- * not called if we're transferred into a holder on the turf, somehow
+ * * Do not manually implement splashing if a limb is specified. The caller does this already.
*
* @params
- * * target - the target.
- * * amount - how much is being applied
- * * data - data. not necessarily a list, but casted as one. this is before mix_data is caled.
+ * * target - the mob
+ * * remaining - how much is left in the thing being splashed.
+ * * allocated - how much is supposed to be hitting the target limb (useful for sprays).
+ * this will never be over remaining.
+ * * data - our reagent data, if any
+ * * zone - (optional) target body zone
+ * * limb - (optional) the external organ splashed onto.
+ *
+ * @return amount used, if any
*/
-// todo: implement this proc, replace touch_turf/reaction_turf and similar with it.
-// /datum/reagent/proc/apply_to_turf(turf/target, amount, list/data)
+/datum/reagent/proc/on_touch_carbon(mob/living/carbon/target, remaining, allocated, data, zone, obj/item/organ/external/limb)
+ return 0
-//* Holder - Mixing
+/**
+ * Called by on_touch_mob().
+ *
+ * * Do not manually implement splashing if a limb is specified. The caller does this already.
+ *
+ * @params
+ * * target - the mob
+ * * remaining - how much is left in the thing being splashed.
+ * * allocated - how much is supposed to be hitting the target limb (useful for sprays).
+ * this will never be over remaining.
+ * * data - our reagent data, if any
+ * * zone - (optional) target body zone
+ *
+ * @return amount used, if any
+ */
+/datum/reagent/proc/on_touch_silicon(mob/living/silicon/target, remaining, allocated, data, zone)
+ return 0
/**
- * called when a new reagent is being mixed with this one to mix our data lists.
+ * Called by on_touch_mob().
*
- * this may not be called if the data is the exact same!
+ * * Do not manually implement splashing if a limb is specified. The caller does this already.
*
* @params
- * * holder - (optional) the holder we're mixing in, if any.
- * * current_data - our current data. not necessarily a list, only typecasted to one.
- * * current_amount - our current amount
- * * new_data - new inbound data. not necessarily a list, only typedcasted to one.
- * * new_amount - the amount that's coming in, not what we will be at after mixing.
+ * * target - the mob
+ * * remaining - how much is left in the thing being splashed.
+ * * allocated - how much is supposed to be hitting the target limb (useful for sprays).
+ * this will never be over remaining.
+ * * data - our reagent data, if any
+ * * zone - (optional) target body zone
+ *
+ * @return amount used, if any
*/
-/datum/reagent/proc/mix_data(datum/reagent_holder/holder, list/current_data, current_amount, list/new_data, new_amount)
- return
+/datum/reagent/proc/on_touch_simple(mob/living/simple_mob/target, remaining, allocated, data, zone)
+ return 0
diff --git a/code/modules/reagents/chemistry/reagent_holder-color.dm b/code/modules/reagents/chemistry/reagent_holder-color.dm
new file mode 100644
index 000000000000..60a3d624e7aa
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagent_holder-color.dm
@@ -0,0 +1,31 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station Developers *//
+
+/datum/reagent_holder/proc/get_color()
+ switch(length(reagent_volumes))
+ if(0)
+ return "#ffffff"
+ if(1)
+ var/datum/reagent/solo_reagent = SSchemistry.reagent_lookup[reagent_volumes[1]]
+ return solo_reagent.holds_data ? solo_reagent.compute_color_with_data(reagent_datas?[solo_reagent.id]) : solo_reagent.color
+ var/total_r = 0
+ var/total_g = 0
+ var/total_b = 0
+ var/total_a = 0
+ var/total_weight = 0
+ for(var/id in reagent_volumes)
+ var/volume = reagent_volumes[id]
+ var/datum/reagent/resolved_reagent = SSchemistry.reagent_lookup[id]
+ var/effective_color = resolved_reagent.holds_data ? resolved_reagent.compute_color_with_data(reagent_datas?[resolved_reagent.id]) : resolved_reagent.color
+ switch(effective_color)
+ if(7)
+ if(9)
+ total_a += hex2num(copytext(resolved_reagent.color, 8, 10)) * volume * resolved_reagent.color_weight
+ else
+ // todo: this should be checked in reagent init. just runtime at this point.
+ stack_trace("reagent id [id] has an incorrect color set: [resolved_reagent.color]")
+ resolved_reagent.color = "#ffffff"
+ total_r += hex2num(copytext(resolved_reagent.color, 2, 4)) * volume * resolved_reagent.color_weight
+ total_g += hex2num(copytext(resolved_reagent.color, 4, 6)) * volume * resolved_reagent.color_weight
+ total_b += hex2num(copytext(resolved_reagent.color, 6, 8)) * volume * resolved_reagent.color_weight
+ return rgb(total_r / total_weight, total_g / total_weight, total_b / total_weight, total_a / total_weight)
diff --git a/code/modules/reagents/chemistry/reagent_holder-helpers.dm b/code/modules/reagents/chemistry/reagent_holder-helpers.dm
new file mode 100644
index 000000000000..5b415eee59ba
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagent_holder-helpers.dm
@@ -0,0 +1,24 @@
+//* Majority Reagent *//
+
+/**
+ * Returns reagent datum of highest single reagent in volume, or null if we are empty.
+ */
+/datum/reagent_holder/proc/get_majority_reagent_datum() as /datum/reagent
+ var/highest_so_far = 0
+ var/id_so_far
+ for(var/id in reagent_volumes)
+ if(reagent_volumes[id] > highest_so_far)
+ id_so_far = id
+ return SSchemistry.fetch_reagent(id_so_far)
+
+/**
+ * Returns reagent name of highest single reagent in volume, or null if we are empty.
+ */
+/datum/reagent_holder/proc/get_majority_reagent_name()
+ return get_majority_reagent_datum()?.name
+
+/**
+ * Returns reagent ID of highest single reagent in volume, or null if we are empty.
+ */
+/datum/reagent_holder/proc/get_majority_reagent_id()
+ return get_majority_reagent_datum()?.id
diff --git a/code/modules/reagents/chemistry/reagent_holder-legacy.dm b/code/modules/reagents/chemistry/reagent_holder-legacy.dm
index 4ed9a85f5508..01a08651889b 100644
--- a/code/modules/reagents/chemistry/reagent_holder-legacy.dm
+++ b/code/modules/reagents/chemistry/reagent_holder-legacy.dm
@@ -1,13 +1,10 @@
/**
- * todo: this should just be reagent_volumes[id] but the current system is pants on head stupid
+ * todo: this should just be reagent_volumes[id], this is not done for PR atomicity reasons
*
* @return null if not found, otherwise amount as number
*/
/datum/reagent_holder/proc/legacy_direct_access_reagent_amount(id)
- for(var/datum/reagent/reagent in reagent_list)
- if(reagent.id == id)
- return reagent.volume
- return null
+ return reagent_volumes[id]
/**
* todo: what do we do with this?
diff --git a/code/modules/reagents/chemistry/metabolism.dm b/code/modules/reagents/chemistry/reagent_holder-metabolism.dm
similarity index 75%
rename from code/modules/reagents/chemistry/metabolism.dm
rename to code/modules/reagents/chemistry/reagent_holder-metabolism.dm
index 469d40a3a8c8..479f51e4bf45 100644
--- a/code/modules/reagents/chemistry/metabolism.dm
+++ b/code/modules/reagents/chemistry/reagent_holder-metabolism.dm
@@ -1,4 +1,7 @@
/datum/reagent_holder/metabolism
+ /// reagent id to /datum/reagent_metabolism
+ var/list/reagent_metabolisms
+
var/metabolism_class //CHEM_TOUCH, CHEM_INGEST, or CHEM_INJECT
var/metabolism_speed = 1 // Multiplicative, 1 is full speed, 0.5 is half, etc.
var/mob/living/carbon/parent
@@ -22,6 +25,16 @@
current.on_mob_life(parent, metabolism_type, src, speed_mult, force_allow_dead)
update_total()
+/datum/reagent_holder/metabolism/add_reagent(id, amount, data_initializer, skip_reactions)
+ . = ..()
+ LAZYINITLIST(reagent_metabolisms)
+ #warn impl
+
+/datum/reagent_holder/metabolism/remove_reagent(id, amount, skip_reactions)
+ . = ..()
+ LAZYINITLIST(reagent_metabolisms)
+ #warn impl
+
// "Specialized" metabolism datums
/datum/reagent_holder/metabolism/bloodstream
metabolism_class = CHEM_INJECT
diff --git a/code/modules/reagents/chemistry/reagent_holder-reactions.dm b/code/modules/reagents/chemistry/reagent_holder-reactions.dm
index a62162d0bb60..05a6e5e0c015 100644
--- a/code/modules/reagents/chemistry/reagent_holder-reactions.dm
+++ b/code/modules/reagents/chemistry/reagent_holder-reactions.dm
@@ -12,11 +12,7 @@
/datum/reagent_holder/proc/reconsider_reactions()
SHOULD_NOT_SLEEP(TRUE)
- var/list/reagent_ids = list()
- for(var/datum/reagent/reagent in reagent_list)
- reagent_ids[reagent.id] = TRUE
-
- var/list/datum/chemical_reaction/reactions = SSchemistry.immutable_relevant_reactions_for_reagent_ids(reagent_ids)
+ var/list/datum/chemical_reaction/reactions = SSchemistry.immutable_relevant_reactions_for_reagent_ids(reagent_volumes)
check_reactions(reactions)
//* Internal API *//
diff --git a/code/modules/reagents/chemistry/reagent_holder.dm b/code/modules/reagents/chemistry/reagent_holder.dm
index 45505ac908c1..7d42be9c18c6 100644
--- a/code/modules/reagents/chemistry/reagent_holder.dm
+++ b/code/modules/reagents/chemistry/reagent_holder.dm
@@ -4,6 +4,11 @@
/// reagent holder flags - see [code/__DEFINES/reagents/flags.dm]
var/reagent_holder_flags = NONE
+ //* Container *//
+
+ /// Our maximum volume
+ var/maximum_volume = 100
+
//* Reactions *//
/// active reactions
@@ -15,14 +20,22 @@
//* Reagents *//
+ /// Our reagent volumes
+ ///
+ /// * Lazy list.
+ var/list/reagent_volumes
+ /// Our reagent datas
+ ///
+ /// * Lazy list.
+ var/list/reagent_datas
/// Our temperature
var/temperature = T20C
+ /// Our current volume
+ ///
+ /// * Must be eagerly updated. Many internal procs depend on this for speed.
+ var/total_volume = 0
///? legacy / unsorted
- // todo: 3 lists, for volume, data, flags; data should always be a list.
- var/list/datum/reagent/reagent_list = list()
- var/total_volume = 0
- var/maximum_volume = 100
var/atom/my_atom = null
// todo: remove / refactor this var into reagent_holder_flags with proper defines, this was never ported properly.
@@ -35,68 +48,36 @@
reagents_holder_flags = new_flags
/datum/reagent_holder/Destroy()
+ // stop all reactions
if(reagent_holder_flags & REAGENT_HOLDER_FLAG_CURRENTLY_REACTING)
stop_reacting()
-
- for(var/datum/reagent/R in reagent_list)
- qdel(R)
- reagent_list = null
- if(my_atom && my_atom.reagents == src)
- my_atom.reagents = null
+ // unreference volumes and datas
+ reagent_volumes = reagent_datas = null
+ // unreference our atom
+ if(my_atom)
+ if(my_atom.reagents == src)
+ my_atom.reagents = null
+ my_atom = null
return ..()
// Used in attack logs for reagents in pills and such
/datum/reagent_holder/proc/log_list()
- if(!length(reagent_list))
+ if(!length(reagent_volumes))
return "no reagents"
var/list/data = list()
- for(var/r in reagent_list) //no reagents will be left behind
- var/datum/reagent/R = r
- data += "[R.type] [R.volume]u)"
+ for(var/id in reagent_volumes)
+ data += "[id] ([reagent_volumes[id]]u)"
//Using IDs because SOME chemicals (I'm looking at you, chlorhydrate-beer) have the same names as other chemicals.
return english_list(data)
/* Internal procs */
-/datum/reagent_holder/proc/get_master_reagent() // Returns reference to the reagent with the biggest volume.
- var/the_reagent = null
- var/the_volume = 0
-
- for(var/datum/reagent/A in reagent_list)
- if(A.volume > the_volume)
- the_volume = A.volume
- the_reagent = A
-
- return the_reagent
-
-/datum/reagent_holder/proc/get_master_reagent_name() // Returns the name of the reagent with the biggest volume.
- 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/reagent_holder/proc/get_master_reagent_id() // Returns the id of the reagent with the biggest volume.
- 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/reagent_holder/proc/update_total() // Updates volume.
- total_volume = 0
- for(var/datum/reagent/R in reagent_list)
- if(R.volume < MINIMUM_CHEMICAL_VOLUME)
- del_reagent_impl(R)
- else
- total_volume += R.volume
+ var/new_volume = 0
+ for(var/id in reagent_volumes)
+ new_volume += reagent_volumes[id]
+ total_volume = new_volume
/datum/reagent_holder/proc/holder_full()
if(total_volume >= maximum_volume)
@@ -121,55 +102,55 @@
var/datum/reagent/accessing = id
id = initial(accessing.id)
- if(!isnum(amount) || amount <= 0)
+ amount = min(amount, maximum_volume - total_volume)
+ if(amount <= 0)
+ return 0
+
+ // this reagent may need to be loaded from persistence
+ var/datum/reagent/reagent = SSchemistry.fetch_reagent(id)
+
+ if(!reagent)
return 0
- // todo: rewrite this entire proc; especially data.
-
- update_total()
- amount = min(amount, available_volume())
-
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- if(current.id == "blood")
- if(data_initializer && !isnull(data_initializer["species"]) && !isnull(current.data["species"]) && data_initializer["species"] != current.data["species"]) // Species bloodtypes are already incompatible, this just stops it from mixing into the one already in a container.
- continue
-
- current.volume += amount
- if(!isnull(data_initializer)) // For all we know, it could be zero or empty string and meaningful
- current.mix_data(src, current.data, current.volume, data_initializer, amount)
- update_total()
- if(!skip_reactions)
- try_reactions_for_reagent_change(id)
- if(my_atom)
- my_atom.on_reagent_change()
- return amount
- var/datum/reagent/D = SSchemistry.reagent_lookup[id]
- if(D)
- var/datum/reagent/R = new D.type()
- reagent_list += R
- R.holder = src
- R.volume = amount
- R.initialize_data(data_initializer)
- update_total()
- if(!skip_reactions)
- // todo: use the relevant reactions on add, instead of all relevant reactions, for speed
- try_reactions_for_reagent_change(id)
- if(my_atom)
- my_atom.on_reagent_change()
- return amount
+ if(reagent_volumes)
+ reagent_volumes[id] += amount
else
- stack_trace("[my_atom] attempted to add a reagent called '[id]' which doesn't exist. ([usr])")
- return 0
+ reagent_volumes = list((id) = amount)
+
+ if(reagent.holds_data)
+ if(reagent_datas)
+ reagent_datas[id] = reagent.mix_data(
+ reagent_datas[id],
+ reagent_volumes[id] - amount,
+ reagent.preprocess_data(data_initializer),
+ amount,
+ src,
+ )
+ else
+ reagent_datas = list((id) = reagent.mix_data(null, 0, reagent.preprocess_data(data_initializer), amount, src))
+
+ total_volume += amount
+
+ if(!skip_reactions)
+ try_reactions_for_reagent_change(id)
+
+ //! LEGACY
+ if(my_atom)
+ my_atom.on_reagent_change()
+ //! END
+
+ return amount
/datum/reagent_holder/proc/isolate_reagent(reagent)
+ if(ispath(reagent))
+ var/datum/reagent/path = reagent
+ reagent = initial(path.id)
var/list/changed_ids = list()
- for(var/A in reagent_list)
- var/datum/reagent/R = A
- if(R.id != reagent)
- changed_ids[R.id] = TRUE
- del_reagent(R.id)
- update_total()
+ for(var/id in reagent_volumes)
+ if(id == reagent)
+ continue
+ changed_ids += id
+ del_reagent(id, TRUE)
try_reactions_for_reagents_changed(changed_ids)
/**
@@ -186,150 +167,103 @@
if(ispath(id))
var/datum/reagent/path = id
id = initial(path.id)
- if(!isnum(amount))
+ if(amount <= 0)
return 0
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- amount = min(amount, current.volume)
- current.volume -= amount
- update_total()
- if(!skip_reactions)
- // todo: use the relevant reactions on remove, instead of all relevant reactions, for speed
- try_reactions_for_reagent_change(id)
- if(my_atom)
- my_atom.on_reagent_change()
- return amount
- return 0
-
-/datum/reagent_holder/proc/del_reagent(id, skip_reactions)
- for(var/datum/reagent/current in reagent_list)
- if (current.id == id)
- del_reagent_impl(current)
- update_total()
- if(!skip_reactions)
- // todo: use the relevant reactions on remove, instead of all relevant reactions, for speed
- try_reactions_for_reagent_change(current.id)
- if(my_atom)
- my_atom.on_reagent_change()
- return 0
-
-// todo: burn this shit with fire
-/datum/reagent_holder/proc/del_reagent_impl(datum/reagent/reagent)
- if(!(reagent in reagent_list))
+ var/current = reagent_volumes?[id]
+ if(!current)
return
- reagent_list -= reagent
- qdel(reagent)
-
-/datum/reagent_holder/proc/clear_reagents(skip_reactions)
- for(var/datum/reagent/current in reagent_list)
- //* telling del_reagent skip reactions is very very important *//
- // without it, if you have potassium, water, and something halting the explosion, //
- // you can have an explosion by clearing the beaker if it goes in the wrong order //
- // that and it's faster this way. do not touch this call! //
- del_reagent(current.id, TRUE)
+ if(amount >= current)
+ reagent_volumes -= id
+ if(reagent_datas)
+ reagent_datas -= id
+ total_volume -= current
+ . = current
+ else
+ reagent_volumes[id] -= amount
+ total_volume -= amount
+ . = amount
if(!skip_reactions)
- reconsider_reactions()
+ try_reactions_for_reagent_change(id)
+ //! LEGACY
+ if(my_atom)
+ my_atom.on_reagent_change()
+ //! END
-/datum/reagent_holder/proc/has_reagent(id, amount = 0)
+/**
+ * Completely remove a reagent.
+ *
+ * @params
+ * * id - id or typepath.
+ * * skip_reactions - do not reconsider relevant reactions.
+ *
+ * @return amount removed
+ */
+/datum/reagent_holder/proc/del_reagent(id, skip_reactions)
if(ispath(id))
var/datum/reagent/path = id
id = initial(path.id)
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- if(current.volume >= amount)
- return 1
- else
- return 0
- return 0
+ var/current = reagent_volumes?[id]
+ if(!current)
+ return 0
+ reagent_volumes -= id
+ total_volume -= current
+ if(!skip_reactions)
+ try_reactions_for_reagent_change(id)
+ //! LEGACY
+ if(my_atom)
+ my_atom.on_reagent_change()
+ //! END
+ return current
-/datum/reagent_holder/proc/has_any_reagent(list/check_reagents)
- for(var/datum/reagent/current in reagent_list)
- if(current.id in check_reagents)
- if(current.volume >= check_reagents[current.id])
- return 1
- else
- return 0
- return 0
+/**
+ * Completely remove all reagents.
+ */
+/datum/reagent_holder/proc/clear_reagents()
+ reagent_volumes = null
+ reagent_datas = null
-/datum/reagent_holder/proc/has_all_reagents(list/check_reagents, multiplier = 1)
- //this only works if check_reagents has no duplicate entries... hopefully okay since it expects an associative list
- var/missing = check_reagents.len
- for(var/datum/reagent/current in reagent_list)
- if(current.id in check_reagents)
- if(current.volume >= check_reagents[current.id] * multiplier)
- missing--
- return !missing
-
-/datum/reagent_holder/proc/get_reagent(id)
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- return current
-
-/datum/reagent_holder/proc/get_reagent_amount(id)
+/datum/reagent_holder/proc/has_reagent(id, amount = 0.00001)
if(ispath(id))
var/datum/reagent/path = id
id = initial(path.id)
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- return current.volume
- return 0
-
-/datum/reagent_holder/proc/get_data(id)
- for(var/datum/reagent/current in reagent_list)
- if(current.id == id)
- return current.get_data()
- return 0
+ return !isnull(reagent_volumes?[id])
/datum/reagent_holder/proc/get_reagents()
. = list()
- for(var/datum/reagent/current in reagent_list)
- . += "[current.id] ([current.volume])"
+ for(var/id in reagent_volumes)
+ var/volume = reagent_volumes[id]
+ . += "[id] ([volume])"
return english_list(., "EMPTY", "", ", ", ", ")
/* Holder-to-holder and similar procs */
-/datum/reagent_holder/proc/remove_any(amount = 1) // Removes up to [amount] of reagents from [src]. Returns actual amount removed.
- amount = min(amount, total_volume)
-
- if(!amount)
+/**
+ * Removes a given amount from the holder, equally from all reagents.
+ *
+ * @params
+ * * amount - amount to remove
+ *
+ * @return amount removed
+ */
+/datum/reagent_holder/proc/remove_any(amount)
+ if(amount <= 0 || !total_volume)
+ return 0
+ if(amount >= total_volume)
+ . = total_volume
+ clear_reagents()
return
-
- var/part = amount / total_volume
-
- for(var/datum/reagent/current in reagent_list)
- var/amount_to_remove = current.volume * part
- remove_reagent(current.id, amount_to_remove, 1)
-
- update_total()
- // todo: do we really need to update everything?
+ var/remaining_ratio = 1 - (amount / total_volume)
+ for(var/id in reagent_volumes)
+ reagent_volumes[id] *= remaining_ratio
+ // todo: don't update everything, just update relevant?
reconsider_reactions()
return amount
+// todo: annihilate this proc
// Transfers [amount] reagents from [src] to [target], multiplying them by [multiplier].
// Returns actual amount removed from [src] (not amount transferred to [target]).
/datum/reagent_holder/proc/trans_to_holder(datum/reagent_holder/target, amount = 1, multiplier = 1, copy = 0)
- if(!target || !istype(target))
- return
-
- amount = max(0, min(amount, total_volume, target.available_volume() / multiplier))
-
- if(!amount)
- return
-
- var/part = amount / total_volume
-
- for(var/datum/reagent/current in reagent_list)
- var/amount_to_transfer = current.volume * part
- target.add_reagent(current.id, amount_to_transfer * multiplier, current.get_data(), TRUE)
- if(!copy)
- remove_reagent(current.id, amount_to_transfer, 1)
-
- // todo: do we really need to update everything?
- if(!copy)
- reconsider_reactions()
- target.reconsider_reactions()
-
- return amount
+ return transfer_to_holder(target, null, amount, copy, multiplier)
/* Holder-to-atom and similar procs */
@@ -377,46 +311,6 @@
return F.trans_to(target, amount) // Let this proc check the atom's type
-// When applying reagents to an atom externally, touch() is called to trigger any on-touch effects of the reagent.
-// This does not handle transferring reagents to things.
-// For example, splashing someone with water will get them wet and extinguish them if they are on fire,
-// even if they are wearing an impermeable suit that prevents the reagents from contacting the skin.
-/datum/reagent_holder/proc/touch(atom/target, amount)
- if(ismob(target))
- touch_mob(target, amount)
- if(isturf(target))
- touch_turf(target, amount)
- if(isobj(target))
- touch_obj(target, amount)
- return
-
-/datum/reagent_holder/proc/touch_mob(mob/target)
- if(!target || !istype(target))
- return
-
- for(var/datum/reagent/current in reagent_list)
- current.touch_mob(target, current.volume)
-
- update_total()
-
-/datum/reagent_holder/proc/touch_turf(turf/target, amount)
- if(!target || !istype(target))
- return
-
- for(var/datum/reagent/current in reagent_list)
- current.touch_turf(target, amount)
-
- update_total()
-
-/datum/reagent_holder/proc/touch_obj(obj/target, amount)
- if(!target || !istype(target))
- return
-
- for(var/datum/reagent/current in reagent_list)
- current.touch_obj(target, amount)
-
- update_total()
-
// Attempts to place a reagent on the mob's skin.
// Reagents are not guaranteed to transfer to the target.
// Do not call this directly, call trans_to() instead.
@@ -494,7 +388,8 @@
for (var/turf/T in turfs)
var/datum/reagent_holder/TR = new /datum/reagent_holder(turfportion)
R.trans_to_holder(TR, turfportion, 1, 0)
- TR.splash_turf(T)
+ #warn uh
+ // TR.splash_turf(T)
qdel(R)
//Spreads the contents of this reagent holder all over the target turf, dividing among things in it.
@@ -527,17 +422,42 @@
if (total_volume <= 0)
qdel(src)
-/datum/reagent_holder/proc/conditional_update_move(atom/A, Running = 0)
- var/list/cached_reagents = reagent_list
- for(var/datum/reagent/R in cached_reagents)
- R.on_move (A, Running)
- update_total()
+//* Application *//
-/datum/reagent_holder/proc/conditional_update(atom/A)
- var/list/cached_reagents = reagent_list
- for(var/datum/reagent/R in cached_reagents)
- R.on_update (A)
- update_total()
+/**
+ * Spill us onto an entity.
+ *
+ * * 'auto' designates the fact that we're default handling. Use with caution; this reserves
+ * the right to do pretty much anything, really.
+ * * Returned amount is amount removed. This doesn't actually return the amount 'consumed'.
+ * * The ratio controls actual amount consumed for splash; if something isn't consumed, it's still deleted.
+ *
+ * The reality of this proc is that this is not enough (intentionally) to prevent reagent duping of any kind.
+ *
+ * If we actually enforced hard unit conservation, a lot of reagents would just not do much on splash
+ * because it wouldn't apply enough to be a big deal, and we can't have it do more with less because
+ * that would make the reagent too powerful.
+ *
+ * @params
+ * * target - what to splash on
+ * * ratio - % to apply
+ * * splash - splash around. if it's an object, we can hit stuff around it; if it's a turf, we can hit
+ * anything on the turf.
+ * * keep_remaining - return unused reagents to holder.
+ *
+ * @return amount splashed
+ */
+/datum/reagent_holder/proc/auto_spill(atom/target, ratio, splash, keep_remaining)
+ . = ratio * total_volume
+ if(splash)
+
+ if(isturf(target))
+ else if(ismob(target))
+ else if(isobj(target))
+
+
+
+#warn deal with these
//* Filtering *//
@@ -553,10 +473,11 @@
if(amount <= 0)
return
var/list/filtering_ids = list()
- for(var/datum/reagent/reagent in reagent_list)
+ for(var/id in reagent_volumes)
+ var/datum/reagent/reagent = SSchemistry.fetch_reagent(id)
if(!(reagent.reagent_filter_flags & flags))
continue
- filtering_ids += reagent.id
+ filtering_ids += id
return transfer_to_holder(transfer_to, filtering_ids, amount)
/**
@@ -570,18 +491,43 @@
if(amount <= 0)
return
var/total_filterable = 0
- var/list/datum/reagent/filtering = list()
- for(var/datum/reagent/reagent in reagent_list)
+ var/list/filtering_ids = list()
+ for(var/id in reagent_volumes)
+ var/datum/reagent/reagent = SSchemistry.fetch_reagent(id)
if(!(reagent.reagent_filter_flags & flags))
continue
- total_filterable += reagent.volume
- filtering += reagent
+ total_filterable += reagent_volumes[id]
+ filtering_ids += id
var/ratio = amount / total_filterable
- for(var/datum/reagent/to_filter in filtering)
- remove_reagent(to_filter.id, to_filter.volume * ratio, TRUE)
+ for(var/id in filtering_ids)
+ remove_reagent(id, reagent_volumes[id] * ratio, TRUE)
reconsider_reactions()
return min(amount, total_filterable)
+//* Getters *//
+
+/**
+ * Gets the amount of a reagent ID or path
+ */
+/datum/reagent_holder/proc/get_reagent_amount(datum/reagent/reagentlike)
+ return reagent_volumes ? reagent_volumes[ispath(reagentlike) ? initial(reagentlike.id) : (istype(reagentlike) ? reagentlike.id : reagentlike)] : 0
+
+/**
+ * Gets the data of a reagent ID or path
+ */
+/datum/reagent_holder/proc/get_reagent_data(datum/reagent/reagentlike)
+ return reagent_datas ? reagent_datas[ispath(reagentlike) ? initial(reagentlike.id) : (istype(reagentlike) ? reagentlike.id : reagentlike)] : null
+
+/**
+ * Gets the global singletons of reagents in us.
+ *
+ * todo: how do we handle this cleanly? this shouldn't be the usual case. rename to fetch_reagent_datums()?
+ */
+/datum/reagent_holder/proc/get_reagent_datums() as /list
+ . = list()
+ for(var/id in reagent_volumes)
+ . += SSchemistry.fetch_reagent(id)
+
//* Queries *//
/**
@@ -591,18 +537,33 @@
return maximum_volume - total_volume
/**
- * returns lowest multiple of what we have compared to reagents list.
+ * Returns if we have any of the given reagent IDs or paths.
*
- * both typepaths and ids are acceptable.
+ * @params
+ * * reagent_ids - ids or paths
+ * * minimum - minimum to be considered to be there. Do not set this to 0 or this proc will always succeed.
*/
-/datum/reagent_holder/proc/has_multiple(list/reagents, multiplier = 1)
+/datum/reagent_holder/proc/has_any(list/reagent_ids, minimum = 0.00001)
+ for(var/datum/reagent/id as anything in reagent_ids)
+ if(ispath(id))
+ id = initial(id.id)
+ if(reagent_volumes[id] >= minimum)
+ return TRUE
+ return FALSE
+
+/**
+ * Returns lowest multiple of what we have compared to reagents list.
+ *
+ * * Reagent instances are not allowed in reagent ids list.
+ *
+ * @params
+ * * reagent_ids - ids or paths
+ */
+/datum/reagent_holder/proc/has_multiple(list/reagent_ids)
. = INFINITY
// *sigh*
- var/list/legacy_translating = list()
- for(var/datum/reagent/R in reagent_list)
- legacy_translating[R.id] = R.volume
- for(var/datum/reagent/reagent as anything in reagents)
- . = min(., legacy_translating[ispath(reagent)? initial(reagent.id) : reagent] / reagents[reagent])
+ for(var/datum/reagent/reagent as anything in reagent_ids)
+ . = min(., reagent_volumes[ispath(reagent)? initial(reagent.id) : reagent] / reagent_ids[reagent])
if(!.)
return
@@ -628,6 +589,7 @@
* If 'no_check_reactions' is set to TRUE, we skip that.
*/
/datum/reagent_holder/proc/set_no_react(new_value, no_check_reactions)
+ // todo: this shouldn't be on atom AAAAA
if(!my_atom)
return
if(!!new_value == !!(my_atom?.atom_flags & NOREACT))
@@ -647,59 +609,59 @@
//* Transfers *//
/**
+ * Transfers to a holder.
+ *
+ * * It is **undefined behavior** to have duplicate IDs in the list of reagent IDs to filter by.
+ * * Reagent instances are not allowed in reagent filter list.
+ *
* @params
* * target - target holder
- * * reagents - list of paths or ids to filter by
+ * * reagents - list of reagent ids or paths to filter by;
* * amount - limit of how much
* * copy - do not remove the reagent from source
* * multiplier - magically multiply the transferred reagent volumes by this much; does not affect return value.
- * * defer_reactions - should we + the recipient handle reactions?
+ * * defer_reactions - should we + the recipient skip handling reactions immediately?
*
- * @return reagents transferred
+ * @return total volume transferred
*/
/datum/reagent_holder/proc/transfer_to_holder(datum/reagent_holder/target, list/reagents, amount = INFINITY, copy, multiplier = 1, defer_reactions)
- . = 0
- // todo: rework this proc
if(!total_volume)
- return
- if(!reagents)
- var/ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_volume)
- . = total_volume * ratio
- if(!copy)
- for(var/datum/reagent/R as anything in reagent_list)
- var/transferred = R.volume * ratio
- target.add_reagent(R.id, transferred * multiplier, R.get_data(), TRUE)
- remove_reagent(R.id, transferred, TRUE)
- else
- for(var/datum/reagent/R as anything in reagent_list)
- var/transferred = R.volume * ratio
- target.add_reagent(R.id, transferred * multiplier, R.get_data(), TRUE)
- else
+ return 0
+
+ var/list/ids_to_transfer
+ var/ratio
+
+ if(reagents)
var/total_transferable = 0
- var/list/reagents_transferring = list()
- // preprocess to IDs
- for(var/i in 1 to length(reagents))
- var/datum/reagent/resolved = SSchemistry.fetch_reagent(reagents[i])
- reagents[i] = resolved.id
- // filter & gather
- for(var/datum/reagent/R as anything in reagent_list)
- if(!(R.id in reagents))
+ ids_to_transfer = list()
+ for(var/datum/reagent/potential as anything in reagents)
+ if(ispath(potential))
+ potential = initial(potential.id)
+ var/volume = reagent_volumes[potential]
+ if(!volume)
continue
- total_transferable += R.volume
- reagents_transferring += R
+ total_transferable += volume
+ ids_to_transfer += potential
if(!total_transferable)
return 0
var/ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_transferable)
. = total_transferable * ratio
- if(!copy)
- for(var/datum/reagent/R as anything in reagents_transferring)
- var/transferred = R.volume * ratio
- target.add_reagent(R.id, transferred * multiplier, R.get_data(), TRUE)
- remove_reagent(R.id, transferred, TRUE)
- else
- for(var/datum/reagent/R as anything in reagents_transferring)
- var/transferred = R.volume * ratio
- target.add_reagent(R.id, transferred * multiplier, R.get_data(), TRUE)
+ else
+ ids_to_transfer = reagent_volumes
+ ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_volume)
+ . = total_volume * ratio
+
+ if(!copy)
+ for(var/id in ids_to_transfer)
+ var/datum/reagent/resolved = SSchemistry.fetch_reagent(id)
+ var/transferred = reagent_volumes[id] * ratio
+ target.add_reagent(id, transferred, resolved.make_copy_data_initializer(reagent_datas[id]), TRUE)
+ remove_reagent(id, transferred, TRUE)
+ else
+ for(var/id in ids_to_transfer)
+ var/datum/reagent/resolved = SSchemistry.fetch_reagent(id)
+ var/transferred = reagent_volumes[id] * ratio
+ target.add_reagent(id, transferred, resolved.make_copy_data_initializer(reagent_datas[id]), TRUE)
if(!defer_reactions)
if(!copy)
@@ -713,10 +675,12 @@
*/
/datum/reagent_holder/proc/tgui_reagent_contents()
var/list/built = list()
- for(var/datum/reagent/R as anything in reagent_list)
+ for(var/id in reagent_volumes)
+ var/datum/reagent/R = SSchemistry.fetch_reagent(id)
+ var/volume = reagent_volumes[id]
built[++built.len] = list(
"name" = R.name,
- "amount" = R.volume,
+ "amount" = volume,
"id" = R.id,
)
return built
diff --git a/code/modules/reagents/chemistry/reagent_metabolism.dm b/code/modules/reagents/chemistry/reagent_metabolism.dm
new file mode 100644
index 000000000000..e831992e4293
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagent_metabolism.dm
@@ -0,0 +1,31 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station Developers *//
+
+/**
+ * Holds metabolism state on reagents.
+ */
+/datum/reagent_metabolism
+ /// arbitrary blackboard for metabolism
+ ///
+ /// * This is separate from data because a lot of things require this and data is far more expensive.
+ var/list/blackboard = list()
+
+ /// maximum amount ever put in
+ var/peak_dose = 0
+ /// maximum amount processed so far
+ var/total_processed_dose = 0
+
+ /// cycles we've been in a mob
+ var/cycles_so_far = 0
+ /// cycles we've been overdosing
+ ///
+ /// * This is reset if we stop overdosing, even for a single tick.
+ var/cycles_overdosing = 0
+
+ //! LEGACY LOOKUP VARIABLES !//
+ /// set to volume remaining
+ var/legacy_volume_remaining
+ /// set to data
+ var/legacy_data
+ /// the reagent holder we're in
+ var/datum/reagent_holder/legacy_current_holder
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Core.dm
index 3030bec93dd0..0c63c409a386 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Core.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Core.dm
@@ -1,162 +1,9 @@
-/datum/reagent/blood
- data = new/list("donor" = null, "viruses" = null, "species" = SPECIES_HUMAN, "blood_DNA" = null, "blood_type" = null, "blood_colour" = "#A10808", "resistances" = null, "trace_chem" = null, "antibodies" = list())
- name = "Blood"
- id = "blood"
- taste_description = "iron"
- taste_mult = 1.3
- reagent_state = REAGENT_LIQUID
- metabolism = REM * 5
- mrate_static = TRUE
- affects_dead = 1 //so you can pump blood into someone before defibbing them
- color = "#C80000"
- var/volume_mod = 1 // So if you add different subtypes of blood, you can affect how much vessel blood each unit of reagent adds
- blood_content = 4 //How effective this is for vampires.
-
- glass_name = "tomato juice"
- glass_desc = "Are you sure this is tomato juice?"
-
-/datum/reagent/blood/initialize_data(newdata)
- ..()
- if(data && data["blood_colour"])
- color = data["blood_colour"]
- return
-
-/datum/reagent/blood/get_data() // Just in case you have a reagent that handles data differently.
- var/t = data.Copy()
- if(t["virus2"])
- var/list/v = t["virus2"]
- t["virus2"] = v.Copy()
- return t
-
-/datum/reagent/blood/touch_turf(turf/simulated/T)
- if(!istype(T) || volume < 3)
- return
- if(!data["donor"] || istype(data["donor"], /mob/living/carbon/human))
- blood_splatter(T, src, 1)
- else if(istype(data["donor"], /mob/living/carbon/alien))
- var/obj/effect/debris/cleanable/blood/B = blood_splatter(T, src, 1)
- if(B)
- B.blood_DNA["UNKNOWN DNA STRUCTURE"] = "X*"
-
-/datum/reagent/blood/affect_ingest(mob/living/carbon/M, alien, removed)
-
- var/effective_dose = dose
- if(issmall(M))
- effective_dose *= 2
-
- var/nutritionvalue = 10 //for reference, normal nutrition has a value of about 30.
- var/is_vampire = M.species.is_vampire
- switch(alien) //unique interactions sorted from the species who benefit the least to the species who benefit the most.
- if(IS_SKRELL) //arguing that blood is "meat" and is still toxic for the vegan skrell at least
- if(effective_dose > 5)
- if(!is_vampire) //a vetalan skrell sounds funny as hell
- M.adjustToxLoss(removed)
- if(effective_dose > 15)
- if(!is_vampire)
- M.adjustToxLoss(removed)
- if(IS_SLIME)
- nutritionvalue = 20
- if(data["species"] == M.species.name) //just 'inject' the blood if it happens to be promethean "blood".
- M.inject_blood(src, volume * volume_mod)
- remove_self(volume)
- return
- if(IS_TESHARI) //birb.
- nutritionvalue = 30
- if(IS_UNATHI) //carnivorous lizord...
- nutritionvalue = 45
- if(IS_ALRAUNE) //lorewise, alraune are meant to enjoy blood.
- nutritionvalue = 60
- if(IS_CHIMERA) //obligate carnivores.
- nutritionvalue = 80
-
- if(is_vampire)
- handle_vampire(M, alien, removed, is_vampire)
- M.heal_organ_damage(0.7 * removed * volume_mod, 0) // Heals vampires more.
- M.adjust_hydration(7 * removed) // Hydrates vetalan better.
- M.add_chemical_effect(CE_BLOODRESTORE, 8 * removed) // Same rating as taking iron
- else
- M.adjust_nutrition(nutritionvalue * removed * volume_mod)
- M.heal_organ_damage(0.2 * removed * volume_mod, 0) // Heal brute slightly like normal nutrition. More 'effective' blood means more usable material.
- M.adjust_hydration(2 * removed) // Still has some water in the form of plasma. Hydrates less than a normal drink.
- M.add_chemical_effect(CE_BLOODRESTORE, 4 * removed) //same rating as eating nutriment
-
- if(data && data["virus2"])
- var/list/vlist = data["virus2"]
- if(vlist.len)
- for(var/ID in vlist)
- var/datum/disease2/disease/V = vlist[ID]
- if(V.spreadtype == "Contact")
- infect_virus2(M, V.getcopy())
-
-/datum/reagent/blood/affect_touch(mob/living/carbon/M, alien, removed)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.isSynthetic())
- return
- if(alien == IS_SLIME)
- affect_ingest(M, alien, removed)
- return
- if(data && data["virus2"])
- var/list/vlist = data["virus2"]
- if(vlist.len)
- for(var/ID in vlist)
- var/datum/disease2/disease/V = vlist[ID]
- if(V.spreadtype == "Contact")
- infect_virus2(M, V.getcopy())
- if(data && data["antibodies"])
- M.antibodies |= data["antibodies"]
-
-/datum/reagent/blood/affect_blood(mob/living/carbon/M, alien, removed)
- if(alien == IS_SLIME) //They don't have blood, so it seems weird that they would instantly 'process' the chemical like another species does.
- affect_ingest(M, alien, removed)
- return
-
- if(M.isSynthetic())
- return
-
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
-
- var/datum/reagent/blood/recipient = H.get_blood(H.vessel)
-
- if(recipient && blood_incompatible(data["blood_type"], recipient.data["blood_type"], data["species"], recipient.data["species"]))
- H.inject_blood(src, removed * volume_mod)
-
- if(!H.isSynthetic() && data["species"] == "synthetic") // Remember not to inject oil into your veins, it's bad for you.
- H.reagents.add_reagent("toxin", removed * 1.5)
-
- return
-
- M.inject_blood(src, volume * volume_mod)
- remove_self(volume)
-
-/datum/reagent/blood/synthblood
- name = "Synthetic blood"
- id = "synthblood"
- color = "#999966"
- volume_mod = 2
-
-/datum/reagent/blood/synthblood/initialize_data(newdata)
- ..()
- if(data && !data["blood_type"])
- data["blood_type"] = "O-"
- return
-
-/datum/reagent/blood/bludbloodlight
- name = "Synthetic blood"
- id = "bludbloodlight"
- color = "#999966"
- volume_mod = 2
-
-/datum/reagent/blood/bludbloodlight/initialize_data(newdata)
- ..()
- if(data && !data["blood_type"])
- data["blood_type"] = "AB+"
- return
-
-// pure concentrated antibodies
+/**
+ * * Data: list of raw antibodies as per definition in virus system
+ * * Data: basically, i don't know what it is and i don't care, and neither does the reagent
+ */
/datum/reagent/antibodies
- data = list("antibodies"=list())
+ holds_data = TRUE
name = "Antibodies"
taste_description = "slime"
id = "antibodies"
@@ -164,86 +11,26 @@
color = "#0050F0"
mrate_static = TRUE
-/datum/reagent/antibodies/affect_blood(mob/living/carbon/M, alien, removed)
- if(src.data)
- M.antibodies |= src.data["antibodies"]
- ..()
-
-/// How much heat is removed when applied to a hot turf, in J/unit (19000 makes 120 u of water roughly equivalent to 4L)
-#define WATER_LATENT_HEAT 19000
-/datum/reagent/water
- name = "Water"
- id = "water"
- taste_description = "water"
- description = "A ubiquitous chemical substance that is composed of hydrogen and oxygen."
- reagent_state = REAGENT_LIQUID
- color = "#0064C877"
- metabolism = REM * 10
-
- glass_name = "water"
- glass_desc = "The father of all refreshments."
-
- cup_name = "water"
- cup_desc = "The father of all refreshments."
-
-/datum/reagent/water/touch_turf(turf/simulated/T)
- if(!istype(T))
- return
+/datum/reagent/antibodies/make_copy_data_initializer(data)
+ return data
- var/datum/gas_mixture/environment = T.return_air()
- var/min_temperature = T0C + 100 // 100C, the boiling point of water
+/datum/reagent/antibodies/preprocess_data(data_initializer)
+ return data_initializer
- var/hotspot = (locate(/atom/movable/fire) in T)
- if(hotspot && !istype(T, /turf/space))
- var/datum/gas_mixture/lowertemp = T.remove_cell_volume()
- lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), 0)
- lowertemp.react()
- T.assume_air(lowertemp)
- qdel(hotspot)
-
- if (environment && environment.temperature > min_temperature) // Abstracted as steam or something
- var/removed_heat = between(0, volume * WATER_LATENT_HEAT, -environment.get_thermal_energy_change(min_temperature))
- environment.adjust_thermal_energy(-removed_heat)
- if (prob(5))
- T.visible_message("The water sizzles as it lands on \the [T]!")
-
- else if(volume >= 10)
- T.wet_floor(1)
-
-/datum/reagent/water/touch_obj(obj/O, amount)
- if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube))
- var/obj/item/reagent_containers/food/snacks/monkeycube/cube = O
- if(!cube.wrapped)
- cube.Expand()
+/datum/reagent/antibodies/mix_data(list/old_data, old_volume, list/new_data, new_volume, datum/reagent_holder/holder)
+ if(old_data)
+ if(new_data)
+ return old_data | new_data
+ return old_data
+ else if(new_data)
+ // new data is not mutable, so copy it for an owned reference
+ return new_data.Copy()
else
- O.water_act(amount / 5)
- var/effective = amount || 10
- O.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (effective / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (effective / 10)))
-
-/datum/reagent/water/touch_mob(mob/living/L, amount)
- if(istype(L))
- // First, kill slimes.
- if(istype(L, /mob/living/simple_mob/slime))
- var/mob/living/simple_mob/slime/S = L
- var/amt = 15 * amount * (1-S.water_resist)
- if(amt>0)
- S.adjustToxLoss(amt)
- S.visible_message("[S]'s flesh sizzles where the water touches it!", "Your flesh burns in the water!")
-
- // Then extinguish people on fire.
- var/needed = L.fire_stacks * 5
- if(amount > needed)
- L.ExtinguishMob()
- L.adjust_fire_stacks(-(amount / 5))
- remove_self(needed)
- var/effective = amount || 10
- L.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (effective / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (effective / 10)))
+ return list()
-/datum/reagent/water/affect_ingest(mob/living/carbon/M, alien, removed)
- //if(alien == IS_SLIME)
- // M.adjustToxLoss(6 * removed)
- //else
- M.adjust_hydration(removed * 10)
+/datum/reagent/antibodies/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(metabolism.legacy_data)
+ M.antibodies |= metabolism.legacy_data
..()
/datum/reagent/fuel
@@ -257,15 +44,19 @@
glass_name = "welder fuel"
glass_desc = "Unless you are an industrial tool, this is probably not safe for consumption."
-/datum/reagent/fuel/touch_turf(turf/T, amount)
- new /obj/effect/debris/cleanable/liquid_fuel(T, amount, FALSE)
- remove_self(amount)
- return
+/datum/reagent/fuel/on_touch_turf(turf/target, remaining, allocated, data)
+ // todo: pseudofludi system
+ new /obj/effect/debris/cleanable/liquid_fuel(target, allocated, FALSE)
+ return allocated
-/datum/reagent/fuel/affect_blood(mob/living/carbon/M, alien, removed)
- if(issmall(M)) removed *= 2
+/datum/reagent/fuel/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(issmall(M))
+ removed *= 2
M.adjustToxLoss(4 * removed)
-/datum/reagent/fuel/touch_mob(mob/living/L, amount)
- if(istype(L))
- L.adjust_fire_stacks(amount / 10) // Splashing people with welding fuel to make them easy to ignite!
+/datum/reagent/fuel/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ if(isliving(target))
+ // todo: rework and actually use some
+ var/mob/living/living_target = target
+ living_target.adjust_fire_stacks(allocated / 10)
+ return ..()
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Dispenser.dm
index f65c970acf9b..d0b2022f262a 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Dispenser.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Dispenser.dm
@@ -1,250 +1,3 @@
-#define ETHANOL_MET_DIVISOR 20
-
-/datum/reagent/ethanol
- name = "Ethanol" //Parent class for all alcoholic reagents.
- id = "ethanol"
- description = "A well-known alcohol with a variety of applications."
- taste_description = "pure alcohol"
- reagent_state = REAGENT_LIQUID
- color = "#404030"
-
- metabolism = REM/ETHANOL_MET_DIVISOR
-
- ingest_met = REM * 5
-
- var/nutriment_factor = 0
- var/hydration_factor = 0
- var/proof = 200
- var/toxicity = 1
-
- var/druggy = 0
- var/adj_temp = 0
- var/targ_temp = 310
- var/halluci = 0
-
- data=0
-
- glass_name = "ethanol"
- glass_desc = "A well-known alcohol with a variety of applications."
-
-/datum/reagent/ethanol/touch_mob(mob/living/L, amount)
- if(istype(L))
- L.adjust_fire_stacks(amount / 15)
-
-#define ABV (proof/200)
-
-/datum/reagent/ethanol/affect_blood(mob/living/carbon/M, alien, removed) //This used to do just toxin. That's boring. Let's make this FUN.
- var/strength_mod = 1 //Alcohol is 3x stronger when injected into the veins.
- if(alien == IS_SKRELL)
- strength_mod *= 5
- if(alien == IS_TAJARA)
- strength_mod *= 1.25
- if(alien == IS_UNATHI)
- strength_mod *= 0.75
- if(alien == IS_DIONA)
- strength_mod = 0
- if(alien == IS_SLIME)
- strength_mod *= 2
- if(alien == IS_ALRAUNE)
- if(prob(5))
- to_chat(M, "You feel your leaves start to wilt.")
- strength_mod *=5 //cit change - alcohol ain't good for plants
-
- var/effective_dose = volume * strength_mod * ABV * min(1,dose*(ETHANOL_MET_DIVISOR/10)) // give it 50 ticks to ramp up
- M.add_chemical_effect(CE_ALCOHOL, 1)
- if(HAS_TRAIT(M, TRAIT_ALCOHOL_INTOLERANT))
- if(proof > 0)
- var/intolerant_dose = strength_mod*removed*ABV*10
- if(prob((intolerant_dose)))
- M.add_chemical_effect(CE_ALCOHOL_TOXIC, 1)
- M.adjustToxLoss(intolerant_dose)
- return 0
- #define DOSE_LEVEL 6
- var/effect_level=round(effective_dose/DOSE_LEVEL)
- if(effect_level != data)
- var/lowering=(data>effect_level)
- data=effect_level
- if(lowering)
- switch(effect_level)
- if(0)
- to_chat(M,SPAN_NOTICE("You no longer feel under the influence."))
- if(1)
- to_chat(M,SPAN_DANGER("You are no longer slurring your words as much."))
- if(2)
- to_chat(M,SPAN_DANGER("You're not seeing double anymore."))
- if(3)
- to_chat(M,SPAN_DANGER("You can walk straight again."))
- if(4)
- to_chat(M,SPAN_DANGER("You no longer feel like you're going to puke."))
- if(5)
- to_chat(M,SPAN_DANGER("You don't feel like you're going to pass out anymore."))
- if(6)
- to_chat(M,SPAN_DANGER("You feel like you're out of the danger zone."))
- else
- var/hydration_str=""
- if(M.hydration<250)
- hydration_str=" You're feeling a little dehydrated, too."
- switch(effect_level)
- if(1)
- to_chat(M,SPAN_DANGER("You're starting to feel a little tipsy.[hydration_str]"))
- M.dizziness=max(M.dizziness,150)
- if(2)
- to_chat(M,SPAN_DANGER("You're getting drunk.[hydration_str] Use the Feign Impairment verb if you want slurring."))
- if(3)
- to_chat(M,SPAN_DANGER("You're seeing double![hydration_str]"))
- M.eye_blurry=max(M.eye_blurry,30)
- if(4)
- to_chat(M,SPAN_DANGER("You can barely walk straight![hydration_str]"))
- if(5)
- to_chat(M,SPAN_USERDANGER("You feel like you might puke...[hydration_str]"))
- if(6)
- to_chat(M,SPAN_USERDANGER("Your eyelids feel heavy![hydration_str]"))
- if(7)
- to_chat(M,SPAN_USERDANGER("You are getting dangerously drunk![hydration_str]"))
- var/hydration_removal=(clamp((M.hydration-150)/300,0,1)*effect_level) + max(0,(M.hydration-450)/300)
- if(hydration_removal>0)
- M.adjust_hydration(-hydration_removal)
- volume-=removed*hydration_removal*3
- if(effect_level>=4 && prob(effect_level-2))
- M.Confuse(60)
- if(effect_level>=5 && prob(effect_level-4) && !M.lastpuke)
- M.vomit(1,0)
- if(M.nutrition>=100)
- volume-=DOSE_LEVEL/4
- if(effect_level>=6 && prob(effect_level-5))
- M.drowsyness=max(M.drowsyness,60)
- if(effect_level>=7)
- M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity*strength_mod)
- if(volume>DOSE_LEVEL*7)
- volume-=REM // liver working overtime, or whatever (mostly to prevent people from always just dying from this)
- #undef DOSE_LEVEL
- return
-
-/datum/reagent/ethanol/affect_ingest(mob/living/carbon/M, alien, removed)
- if(issmall(M)) removed *= 2
- M.adjust_nutrition(nutriment_factor * removed)
- M.adjust_hydration(hydration_factor * removed)
- M.bloodstr.add_reagent("ethanol", removed * ABV)
- if(druggy != 0)
- M.druggy = max(M.druggy, druggy)
-
- if(adj_temp > 0 && M.bodytemperature < targ_temp) // 310 is the normal bodytemp. 310.055
- M.bodytemperature = min(targ_temp, M.bodytemperature + (adj_temp * TEMPERATURE_DAMAGE_COEFFICIENT))
- if(adj_temp < 0 && M.bodytemperature > targ_temp)
- M.bodytemperature = min(targ_temp, M.bodytemperature - (adj_temp * TEMPERATURE_DAMAGE_COEFFICIENT))
-
- if(halluci)
- M.setHallucination(max(M.hallucination, halluci))
- return
-
-/datum/reagent/ethanol/touch_obj(obj/O)
- if(istype(O, /obj/item/paper))
- var/obj/item/paper/paperaffected = O
- paperaffected.clearpaper()
- to_chat(usr, "The solution dissolves the ink on the paper.")
- return
- if(istype(O, /obj/item/book))
- if(volume < 5)
- return
- if(istype(O, /obj/item/book/tome))
- to_chat(usr, "The solution does nothing. Whatever this is, it isn't normal ink.")
- return
- var/obj/item/book/affectedbook = O
- affectedbook.dat = null
- to_chat(usr, "The solution dissolves the ink on the book.")
- return
-
-#undef ABV
-
-/datum/reagent/acid
- name = "Sulphuric acid"
- id = "sacid"
- description = "A very corrosive mineral acid with the molecular formula H2SO4."
- taste_description = "acid"
- reagent_state = REAGENT_LIQUID
- color = "#DB5008"
- metabolism = REM * 2
- touch_met = 50 // It's acid!
- var/power = 5
- var/meltdose = 10 // How much is needed to melt
-
-/datum/reagent/acid/affect_blood(mob/living/carbon/M, alien, removed)
- if(issmall(M)) removed *= 2
- M.take_random_targeted_damage(brute = 0, brute = removed * power * 2)
-
-/datum/reagent/acid/affect_touch(mob/living/carbon/M, alien, removed) // This is the most interesting
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.head)
- if(H.head.integrity_flags & INTEGRITY_ACIDPROOF)
- to_chat(H, "Your [H.head] protects you from the acid.")
- remove_self(volume)
- return
- else if(removed > meltdose)
- to_chat(H, "Your [H.head] melts away!")
- qdel(H.head)
- H.update_inv_head(1)
- H.update_hair(1)
- removed -= meltdose
- if(removed <= 0)
- return
-
- if(H.wear_mask)
- if(H.wear_mask.integrity_flags & INTEGRITY_ACIDPROOF)
- to_chat(H, "Your [H.wear_mask] protects you from the acid.")
- remove_self(volume)
- return
- else if(removed > meltdose)
- to_chat(H, "Your [H.wear_mask] melts away!")
- qdel(H.wear_mask)
- H.update_inv_wear_mask(1)
- H.update_hair(1)
- removed -= meltdose
- if(removed <= 0)
- return
-
- if(H.glasses)
- if(H.glasses.integrity_flags & INTEGRITY_ACIDPROOF)
- to_chat(H, "Your [H.glasses] partially protect you from the acid!")
- removed /= 2
- else if(removed > meltdose)
- to_chat(H, "Your [H.glasses] melt away!")
- qdel(H.glasses)
- H.update_inv_glasses(1)
- removed -= meltdose / 2
- if(removed <= 0)
- return
-
- if(volume < meltdose) // Not enough to melt anything
- M.take_random_targeted_damage(brute = 0, brute = removed * power * 0.2) //burn damage, since it causes chemical burns. Acid doesn't make bones shatter, like brute trauma would.
- return
- if(!M.unacidable && removed > 0)
- if(istype(M, /mob/living/carbon/human) && volume >= meltdose)
- var/mob/living/carbon/human/H = M
- var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD)
- if(affecting)
- affecting.inflict_bodypart_damage(
- burn = removed * power * 0.1,
- )
- if(prob(100 * removed / meltdose)) // Applies disfigurement
- if (affecting.organ_can_feel_pain())
- H.emote("scream")
- else
- M.take_random_targeted_damage(brute = 0, brute = removed * power * 0.1) // Balance. The damage is instant, so it's weaker. 10 units -> 5 damage, double for pacid. 120 units beaker could deal 60, but a) it's burn, which is not as dangerous, b) it's a one-use weapon, c) missing with it will splash it over the ground and d) clothes give some protection, so not everything will hit
-
-/datum/reagent/acid/touch_obj(obj/O)
- if(O.integrity_flags & INTEGRITY_INDESTRUCTIBLE)
- return
- // todo: newacid
- if((istype(O, /obj/item) || istype(O, /obj/effect/plant)) && (volume > meltdose))
- var/obj/effect/debris/cleanable/molten_item/I = new/obj/effect/debris/cleanable/molten_item(O.loc)
- I.desc = "Looks like this was \an [O] some time ago."
- for(var/mob/M in viewers(5, O))
- to_chat(M, "\The [O] melts.")
- qdel(O)
- remove_self(meltdose) // 10 units of acid will not melt EVERYTHING on the tile
-
-
/datum/reagent/sugar
name = "Sugar"
id = "sugar"
@@ -258,16 +11,16 @@
glass_desc = "The organic compound commonly known as table sugar and sometimes called saccharose. This white, odorless, crystalline powder has a pleasing, sweet taste."
glass_icon = DRINK_ICON_NOISY
-/datum/reagent/sugar/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/sugar/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.nutrition += removed * 3
- var/effective_dose = dose
+ var/effective_dose = metabolism.total_processed_dose
if(issmall(M))
effective_dose *= 2
if(alien == IS_UNATHI)
if(effective_dose < 2)
- if(effective_dose == metabolism * 2 || prob(5))
+ if(effective_dose == metabolism_rate * 2 || prob(5))
M.emote("yawn")
else if(effective_dose < 5)
M.eye_blurry = max(M.eye_blurry, 10)
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Food-Drinks.dm
index 56c37118c323..5b5cd9b342a5 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Food-Drinks.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Food-Drinks.dm
@@ -1,64 +1,5 @@
/* Food */
-/datum/reagent/nutriment
- name = "Nutriment"
- id = "nutriment"
- description = "All the vitamins, minerals, and carbohydrates the body needs in pure form."
- taste_mult = 4
- reagent_state = REAGENT_SOLID
- metabolism = REM * 4
- ingest_met = REM * 4
- var/nutriment_factor = 30 // Per unit
- var/hydration_factor = 0 //Per unit
- var/injectable = 0
- color = "#664330"
-
-// todo: review data procs
-
-/datum/reagent/nutriment/mix_data(datum/reagent_holder/holder, list/current_data, current_amount, list/new_data, new_amount)
-
- if(!islist(new_data) || !length(new_data))
- return
-
- LAZYINITLIST(data)
-
- //add the new taste data
- for(var/taste in new_data)
- if(taste in data)
- data[taste] += new_data[taste]
- else
- data[taste] = new_data[taste]
-
- //cull all tastes below 10% of total
- var/totalFlavor = 0
- for(var/taste in data)
- totalFlavor += data[taste]
- if(totalFlavor) //Let's not divide by zero for things w/o taste
- for(var/taste in data)
- if(data[taste]/totalFlavor < 0.1)
- data -= taste
-
-/datum/reagent/nutriment/affect_blood(mob/living/carbon/M, alien, removed)
- if(!injectable && alien != IS_SLIME && alien != IS_CHIMERA)
- M.adjustToxLoss(0.1 * removed)
- return
- affect_ingest(M, alien, removed)
-
-/datum/reagent/nutriment/affect_ingest(mob/living/carbon/M, alien, removed)
- switch(alien)
- if(IS_DIONA)
- return
- if(IS_UNATHI)
- removed *= 0.5
- if(IS_CHIMERA)
- removed *= 0.25
- if(issmall(M))
- removed *= 2 // Small bodymass, more effect from lower volume.
- M.heal_organ_damage(0.5 * removed, 0)
- if(!M.species.is_vampire) // If this is set to 0, they don't get nutrition from food.
- M.nutrition += nutriment_factor * removed // For hunger and fatness
- M.adjust_hydration(hydration_factor * removed)
- M.add_chemical_effect(CE_BLOODRESTORE, 4 * removed)
/datum/reagent/nutriment/glucose
name = "Glucose"
@@ -74,7 +15,7 @@
taste_description = "some sort of meat"
color = "#440000"
-/datum/reagent/nutriment/protein/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/nutriment/protein/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
switch(alien)
if(IS_SKRELL)
M.adjustToxLoss(0.5 * removed)
@@ -87,7 +28,7 @@
else
..()
-/datum/reagent/nutriment/protein/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nutriment/protein/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien && alien == IS_SKRELL)
M.adjustToxLoss(2 * removed)
return
@@ -113,16 +54,14 @@
nutriment_factor = 10
color = "#FFFF00"
-/datum/reagent/nutriment/honey/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/nutriment/honey/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
- var/effective_dose = dose
- if(issmall(M))
- effective_dose *= 2
+ var/effective_dose = metabolism.total_processed_dose
if(alien == IS_UNATHI)
if(effective_dose < 2)
- if(effective_dose == metabolism * 2 || prob(5))
+ if(effective_dose == metabolism_rate * 2 || prob(5))
M.emote("yawn")
else if(effective_dose < 5)
M.eye_blurry = max(M.eye_blurry, 10)
@@ -274,20 +213,12 @@
nutriment_factor = 15
color = "#4F3500"
-/datum/reagent/nutriment/peanutoil/touch_turf(turf/simulated/T)
- if(!istype(T))
- return
-
- var/hotspot = (locate(/atom/movable/fire) in T)
- if(hotspot && !istype(T, /turf/space))
- var/datum/gas_mixture/lowertemp = T.remove_cell_volume()
- lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), 0)
- lowertemp.react()
- T.assume_air(lowertemp)
- qdel(hotspot)
-
- if(volume >= 5)
- T.wet_floor()
+// todo: reagent effects
+/datum/reagent/nutriment/peanutoil/apply_to_turf(turf/target, remaining, allocated, data)
+ if(allocate >= 5)
+ target.wet_floor()
+ return 5
+ return 0
/datum/reagent/nutriment/peanutbutter
name = "Peanut Butter"
@@ -319,20 +250,19 @@
glass_name = "durian paste"
glass_desc = "Durian paste. It smells horrific."
-/datum/reagent/nutriment/durian/touch_mob(mob/M, amount)
- if(iscarbon(M) && !M.isSynthetic())
+/datum/reagent/nutriment/durian/on_touch_carbon(mob/living/carbon/target, remaining, allocated, data, zone, obj/item/organ/external/limb)
+ if(!target.isSynthetic())
var/message = pick("Oh god, it smells disgusting here.", "What is that stench?", "That's an awful odor.")
- to_chat(M,"[message]")
- if(prob(clamp(amount, 5, 90)))
- var/mob/living/L = M
- L.vomit()
+ to_chat(target, "[message]")
+ if(prob(clamp(allocated, 5, 90)))
+ target.vomit()
return ..()
-/datum/reagent/nutriment/durian/touch_turf(turf/T, amount)
- if(istype(T))
- var/obj/effect/debris/cleanable/chemcoating/C = new /obj/effect/debris/cleanable/chemcoating(T)
- C.reagents.add_reagent(id, amount)
- return ..()
+/datum/reagent/nutriment/durian/on_touch_turf(turf/target, remaining, allocated, data)
+ var/obj/effect/debris/cleanable/chemcoating/C = new /obj/effect/debris/cleanable/chemcoating(target)
+ var/amount_used = C.reagents.add_reagent(id, allocated)
+ allocated -= amount_used
+ return ..() + amount_used
/datum/reagent/nutriment/virus_food
name = "Virus Food"
@@ -387,7 +317,7 @@
color = "#BBEDA4"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/lipozine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/lipozine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.nutrition = max(M.nutrition - 10 * removed, 0)
M.overeatduration = 0
if(M.nutrition < 0)
@@ -405,15 +335,15 @@
overdose = REAGENTS_OVERDOSE
ingest_met = REM
-/datum/reagent/sodiumchloride/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/sodiumchloride/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
M.adjustFireLoss(removed)
-/datum/reagent/sodiumchloride/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/sodiumchloride/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/pass_mod = rand(3,5)
var/passthrough = (removed - (removed/pass_mod)) //Some may be nullified during consumption, between one third and one fifth.
- affect_blood(M, alien, passthrough)
+ legacy_affect_blood(M, alien, passthrough, metabolism)
/datum/reagent/blackpepper
name = "Black Pepper"
@@ -444,13 +374,13 @@
ingest_met = REM
color = "#B31008"
-/datum/reagent/frostoil/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/frostoil/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, 215)
if(prob(1))
M.emote("shiver")
- holder.remove_reagent("capsaicin", 5)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/capsaicin, 5)
/datum/reagent/frostoil/cryotoxin //A longer lasting version of frost oil.
name = "Cryotoxin"
@@ -458,7 +388,7 @@
description = "Lowers the body's internal temperature."
reagent_state = REAGENT_LIQUID
color = "#B31008"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
/datum/reagent/capsaicin
name = "Capsaicin Oil"
@@ -470,12 +400,12 @@
ingest_met = REM
color = "#B31008"
-/datum/reagent/capsaicin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/capsaicin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.adjustToxLoss(0.5 * removed)
-/datum/reagent/capsaicin/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/capsaicin/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(alien == IS_NARAMADI)
@@ -490,13 +420,13 @@
if(!H.can_feel_pain())
return
- if(dose < 5 && (dose == metabolism || prob(5)))
+ if(metabolism.total_processed_dose < 5 && (metabolism.cycles_so_far == 1 || prob(5)))
to_chat(M, "Your insides feel uncomfortably hot!")
- if(dose >= 5)
+ if(metabolism.total_processed_dose >= 5)
M.apply_effect(2, AGONY, 0)
if(prob(5))
M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]", "You feel like your insides are burning!")
- holder.remove_reagent("frostoil", 5)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/frostoil, 5)
/datum/reagent/hexaisin
name = "Hexaisin"
@@ -508,7 +438,7 @@
ingest_met = REM
color = "#B31008"
-/datum/reagent/hexaisin/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/hexaisin/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_UNATHI)
return
if(alien == IS_NARAMADI)
@@ -517,13 +447,13 @@
var/mob/living/carbon/human/H = M
if(!H.can_feel_pain())
return
- if(dose == metabolism)
+ if(metabolism.cycles_so_far == 1)
to_chat(M, "You feel like your insides are burning!")
else
M.apply_effect(3, AGONY, 0)
if(prob(5))
M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]", "You feel like your insides are burning!")
- holder.remove_reagent("frostoil", 5)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/frostoil, 5)
/datum/reagent/condensedcapsaicin
name = "Condensed Capsaicin"
@@ -536,12 +466,12 @@
ingest_met = REM
color = "#B31008"
-/datum/reagent/condensedcapsaicin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/condensedcapsaicin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.adjustToxLoss(0.5 * removed)
-/datum/reagent/condensedcapsaicin/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/condensedcapsaicin/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/eyes_covered = 0
var/mouth_covered = 0
@@ -662,7 +592,7 @@
to_chat(M, "The exposed flesh on your feet burns!")
M.apply_effect(effective_strength / 2, AGONY, 0)
-/datum/reagent/condensedcapsaicin/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/condensedcapsaicin/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_NARAMADI) //Moghes species with exception of Zaddat (for obvious reasons) are immune to taste and ingested effects of Capsaisin and Condensed variants.
return
if(alien == IS_UNATHI) //If you want to know why, look at Hexaisin. They are still affected by pepperspray, but not drinking it.
@@ -671,13 +601,13 @@
var/mob/living/carbon/human/H = M
if(!H.can_feel_pain())
return
- if(dose == metabolism)
+ if(metabolism.cycles_so_far == 1)
to_chat(M, "You feel like your insides are burning!")
else
M.apply_effect(4, AGONY, 0)
if(prob(5))
M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]", "You feel like your insides are burning!")
- holder.remove_reagent("frostoil", 5)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/frostoil, 5)
/* Drinks */
@@ -696,14 +626,14 @@
var/adj_temp = 0
var/water_based = TRUE
-/datum/reagent/drink/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/strength_mod = 1
if(alien == IS_SLIME && water_based)
strength_mod = 3
M.adjustToxLoss(removed * strength_mod) // Probably not a good idea; not very deadly though
return
-/datum/reagent/drink/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjust_nutrition(nutrition * removed)
M.adjust_hydration(hydration * removed)
M.dizziness = max(0, M.dizziness + adj_dizzy)
@@ -718,7 +648,7 @@
if(is_vampire)
handle_vampire(M, alien, removed, is_vampire)
-/datum/reagent/drink/overdose(mob/living/carbon/M, alien) //Add special interactions here in the future if desired.
+/datum/reagent/drink/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
// Juices
@@ -763,7 +693,7 @@
glass_name = "Carrot Juice"
glass_desc = "It is just like a carrot but without crunching."
-/datum/reagent/drink/juice/carrot/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/juice/carrot/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.reagents.add_reagent("imidazoline", removed * 0.2)
@@ -779,16 +709,14 @@
glass_name = "Grape Juice"
glass_desc = "It's grrrrrape!"
-/datum/reagent/drink/juice/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/juice/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
- var/effective_dose = dose/2
- if(issmall(M))
- effective_dose *= 2
+ var/effective_dose = metabolism.total_processed_dose
if(alien == IS_UNATHI)
if(effective_dose < 2)
- if(effective_dose == metabolism * 2 || prob(5))
+ if(effective_dose == metabolism_rate * 2 || prob(5))
M.emote("yawn")
else if(effective_dose < 5)
M.eye_blurry = max(M.eye_blurry, 10)
@@ -833,7 +761,7 @@
glass_name = "Lime Juice"
glass_desc = "A glass of sweet-sour lime juice"
-/datum/reagent/drink/juice/lime/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/juice/lime/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -849,7 +777,7 @@
glass_name = "orange juice"
glass_desc = "Vitamins! Yay!"
-/datum/reagent/drink/juice/orange/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/juice/orange/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -888,7 +816,7 @@
glass_name = "Tomato Juice"
glass_desc = "Are you sure this is tomato juice?"
-/datum/reagent/drink/juice/tomato/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/juice/tomato/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -946,19 +874,19 @@
glass_name = "Chocolate Milk"
glass_desc = "Deliciously fattening!"
-/datum/reagent/drink/milk/chocolate/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/milk/chocolate/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_ALRAUNE) //cit change: choccy is full of natural easily digestible plant fats
M.nutrition += removed * 5
-/datum/reagent/drink/milk/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/milk/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
if(alien == IS_ALRAUNE) //cit change: milk good for plant.
M.nutrition += removed * 3
M.heal_organ_damage(0.5 * removed, 0)
- holder.remove_reagent("capsaicin", 10 * removed)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/capsaicin, 10 * removed)
if(contains_lactose == TRUE && alien == IS_NARAMADI) //Species-wide lactose intolerance, also funny that cheeses can't drink milk.
if(prob(5))
to_chat(M, SPAN_WARNING("You feel nauseous!"))
@@ -1031,7 +959,7 @@
cup_name = "Cup of Tea"
cup_desc = "Tasty black tea, it has antioxidants, it's good for you!"
-/datum/reagent/drink/tea/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/tea/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -1053,7 +981,7 @@
cup_name = "Cup of Iced Tea"
cup_desc = "No relation to a certain rap artist/ actor."
-/datum/reagent/drink/tea/icetea/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/tea/icetea/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -1061,7 +989,7 @@
if(M.bodytemperature < T0C)
M.bodytemperature += 0.5
-/datum/reagent/drink/tea/icetea/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/tea/icetea/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -1153,7 +1081,7 @@
cup_name = "Cup of Milk Tea"
cup_desc = "Sweet iced tea cut with milk."
-/datum/reagent/drink/tea/icetea/milktea/affect_ingest(mob/living/carbon/M, alien, removed) //Milk tea and its variants inherit the properties of both iced tea and milk.
+/datum/reagent/drink/tea/icetea/milktea/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) //Milk tea and its variants inherit the properties of both iced tea and milk.
..()
if(alien == IS_DIONA)
return
@@ -1269,17 +1197,17 @@
glass_desc = "Don't drop it, or you'll send scalding liquid and glass shards everywhere."
-/datum/reagent/drink/coffee/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
..()
if(adj_temp > 0)
- holder.remove_reagent("frostoil", 10 * removed)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/frostoil, 10 * removed)
-/datum/reagent/drink/coffee/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
-/datum/reagent/drink/coffee/overdose(mob/living/carbon/M, alien)
+/datum/reagent/drink/coffee/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.make_jittery(5)
@@ -1295,7 +1223,7 @@
glass_desc = "A drink to perk you up and refresh you!"
glass_special = list(DRINK_ICE)
-/datum/reagent/drink/coffee/icecoffee/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/icecoffee/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -1303,7 +1231,7 @@
if(M.bodytemperature < T0C)
M.bodytemperature += 0.5
-/datum/reagent/drink/coffee/icecoffee/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/icecoffee/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -1328,7 +1256,7 @@
cup_name = "Cup of Soy Latte"
cup_desc = "A nice and refreshing beverage while you are reading."
-/datum/reagent/drink/coffee/soy_latte/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/soy_latte/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.heal_organ_damage(0.5 * removed, 0)
@@ -1348,7 +1276,7 @@
cup_name = "Cup of Cafe Latte"
cup_desc = "A nice and refreshing beverage while you are reading."
-/datum/reagent/drink/coffee/cafe_latte/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/coffee/cafe_latte/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.heal_organ_damage(0.5 * removed, 0)
@@ -1369,7 +1297,7 @@
cup_name = "Cup of Hot Chocolate"
cup_desc = "Made with love! And cocoa beans."
-/datum/reagent/drink/hot_coco/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/hot_coco/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_ALRAUNE) //cit change: choccy is full of natural easily digestible plant fats
M.nutrition += removed * 5
@@ -1532,16 +1460,14 @@
glass_name = "Milkshake"
glass_desc = "Glorious brainfreezing mixture."
-/datum/reagent/drink/milkshake/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/milkshake/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
- var/effective_dose = dose/2
- if(issmall(M))
- effective_dose *= 2
+ var/effective_dose = metabolism.total_processed_dose / 2
if(alien == IS_UNATHI)
if(effective_dose < 2)
- if(effective_dose == metabolism * 2 || prob(5))
+ if(effective_dose == metabolism_rate * 2 || prob(5))
M.emote("yawn")
else if(effective_dose < 5)
M.eye_blurry = max(M.eye_blurry, 10)
@@ -1574,7 +1500,7 @@
glass_name = "Chocolate Milkshake"
glass_desc = "A refreshing chocolate milkshake, just like mom used to make."
-/datum/reagent/drink/milkshake/chocoshake/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/milkshake/chocoshake/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE) //cit change: it wouldn't affect plants that much.
M.nutrition += removed * 5
@@ -1605,7 +1531,7 @@
glass_name = "Coffee Milkshake"
glass_desc = "An energizing coffee milkshake, perfect for hot days at work.."
-/datum/reagent/drink/milkshake/coffeeshake/overdose(mob/living/carbon/M, alien)
+/datum/reagent/drink/milkshake/coffeeshake/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.make_jittery(5)
/datum/reagent/drink/milkshake/peanutshake
@@ -1631,7 +1557,7 @@
glass_name = "Rewriter"
glass_desc = "The secret of the sanctuary of the Libarian..."
-/datum/reagent/drink/rewriter/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/rewriter/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.make_jittery(5)
@@ -1649,7 +1575,7 @@
glass_desc = "Don't cry, Don't raise your eye, It's only nuclear wasteland"
glass_special = list(DRINK_FIZZ)
-/datum/reagent/drink/soda/nuka_cola/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/soda/nuka_cola/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.add_chemical_effect(CE_SPEEDBOOST, 1)
M.make_jittery(20)
@@ -1922,7 +1848,7 @@
glass_name = "The Doctor's Delight"
glass_desc = "A healthy mixture of juices, guaranteed to keep you healthy until the next toolboxing takes place."
-/datum/reagent/drink/doctor_delight/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/doctor_delight/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -1972,7 +1898,7 @@
glass_name = "Hell Ramen"
glass_desc = "A glass of extremely spicy noodles. Wait, why did you put this into a glass?"
-/datum/reagent/drink/hell_ramen/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/hell_ramen/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -2003,7 +1929,7 @@
glass_desc = "Generally, you're supposed to put something else in there too..."
glass_icon = DRINK_ICON_NOISY
-/datum/reagent/drink/ice/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/ice/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -2011,7 +1937,7 @@
if(M.bodytemperature < T0C)
M.bodytemperature += rand(1,3)
-/datum/reagent/drink/ice/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/ice/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
if(M.bodytemperature > T0C)
@@ -2124,13 +2050,13 @@
glass_icon = DRINK_ICON_NOISY
glass_special = list(DRINK_FIZZ)
-/datum/reagent/drink/nuclearwaste/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/nuclearwaste/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
M.bloodstr.add_reagent("radium", 0.3)
-/datum/reagent/drink/nuclearwaste/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/nuclearwaste/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -2150,19 +2076,19 @@
glass_icon = DRINK_ICON_NOISY
glass_special = list(DRINK_FIZZ)
-/datum/reagent/drink/sodaoil/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/sodaoil/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(M.bloodstr) // If, for some reason, they are injected, dilute them as well.
- for(var/datum/reagent/R in M.ingested.reagent_list)
+ for(var/datum/reagent/R in M.ingested.get_reagent_datums())
if(istype(R, /datum/reagent/drink))
var/datum/reagent/drink/D = R
if(D.water_based)
M.adjustToxLoss(removed * -3)
-/datum/reagent/drink/sodaoil/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/sodaoil/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(M.ingested) // Find how many drinks are causing tox, and negate them.
- for(var/datum/reagent/R in M.ingested.reagent_list)
+ for(var/datum/reagent/R in M.ingested.get_reagent_datums())
if(istype(R, /datum/reagent/drink))
var/datum/reagent/drink/D = R
if(D.water_based)
@@ -2373,7 +2299,7 @@
glass_name = "Beer"
glass_desc = "A freezing pint of beer"
-/datum/reagent/ethanol/beer/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/beer/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(.)
M.jitteriness = max(M.jitteriness - 3, 0)
@@ -2418,7 +2344,7 @@
glass_name = "Rum"
glass_desc = "Now you want to Pray for a pirate suit, don't you?"
-/datum/reagent/ethanol/deadrum/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/deadrum/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(.)
M.dizziness += 5
@@ -2455,7 +2381,7 @@
id = "coffee_alcohol"
overdose = 45
-/datum/reagent/ethanol/coffee/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/coffee/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
. = ..() // the rest is coffee stuff, ugh, go make reagent traits etc
@@ -2465,7 +2391,7 @@
if(M.bodytemperature > 310)
M.bodytemperature = max(310, M.bodytemperature - (5 * TEMPERATURE_DAMAGE_COEFFICIENT))
-/datum/reagent/ethanol/coffee/overdose(mob/living/carbon/M, alien)
+/datum/reagent/ethanol/coffee/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.make_jittery(5)
@@ -2549,7 +2475,7 @@
glass_name = "Thirteen Loko"
glass_desc = "This is a glass of Thirteen Loko, it appears to be of the highest quality. The drink, not the glass."
-/datum/reagent/ethanol/thirteenloko/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/thirteenloko/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(alien == IS_DIONA)
return
@@ -2585,7 +2511,7 @@
glass_name = "Vodka"
glass_desc = "The glass contain wodka. Xynta."
-/datum/reagent/ethanol/vodka/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/vodka/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
M.cure_radiation(RAD_MOB_CURE_STRENGTH_VODKA(removed))
@@ -2982,7 +2908,7 @@
glass_name = "Beepsky Smash"
glass_desc = "Heavy, hot and strong. Just like the Iron fist of the LAW."
-/datum/reagent/ethanol/beepsky_smash/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/beepsky_smash/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
M.afflict_stun(20 * 2)
@@ -3292,7 +3218,7 @@
glass_icon = DRINK_ICON_NOISY
glass_special = list("neuroright")
-/datum/reagent/ethanol/neurotoxin/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/neurotoxin/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
M.afflict_paralyze(20 * 3)
@@ -3319,7 +3245,7 @@
glass_name = "???"
glass_desc = "A black ichor with an oily purple sheer on top. Are you sure you should drink this?"
-/datum/reagent/ethanol/pwine/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/pwine/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(. > 30)
M.adjustToxLoss(2 * removed)
@@ -3532,7 +3458,7 @@
glass_name = "Redeemer's Brew"
glass_desc = "This barely qualifies as a drink, and may cause euphoria and numbness. Imbiber beware!"
-/datum/reagent/ethanol/unathiliquor/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/unathiliquor/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -3552,7 +3478,7 @@
var/mob/living/carbon/human/H = M
if(!H.can_feel_pain())
return
- if(dose == metabolism)
+ if(metabolism.cycles_so_far == 1)
to_chat(M, "You feel like your insides are burning!")
else
M.apply_effect(4, AGONY, 0)
@@ -3739,7 +3665,7 @@
glass_name = "Soemmer Fire"
glass_desc = "A painfully hot mixed drink, for when you absolutely need to hurt right now."
-/datum/reagent/ethanol/soemmerfire/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/soemmerfire/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -3789,7 +3715,7 @@
glass_name = "Vox's Delight"
glass_desc = "Not recommended if you enjoy having organs."
-/datum/reagent/ethanol/voxdelight/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/voxdelight/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -3854,7 +3780,7 @@
glass_name = "Named Bullet"
glass_desc = "A thick slime jelly shot. You can feel your death approaching."
-/datum/reagent/ethanol/slimeshot/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/slimeshot/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
@@ -3998,7 +3924,7 @@
glass_desc = "The glass is barely able to contain the wodka. Xynta."
glass_special = list(DRINK_FIZZ)
-/datum/reagent/ethanol/godka/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/godka/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
M.cure_radiation(RAD_MOB_CURE_STRENGTH_GODKA(removed * .))
if(. && ishuman(M))
@@ -4342,7 +4268,7 @@
glass_desc = "A drink usually enjoyed by only the highest castes of Apinae society. Incredibly sweet, it is said to have enormous health benefits."
//This functions the same as Doctor's Delight, except it gets you drunk too.
-/datum/reagent/ethanol/royaljelly/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/royaljelly/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(alien == IS_DIONA)
return
@@ -4536,81 +4462,6 @@
///////////////////////////////////////////////
//// End of list for drinks for bartenders ////
///////////////////////////////////////////////
-/*
- Coatings are used in cooking. Dipping food items in a reagent container with a coating in it
- allows it to be covered in that, which will add a masked overlay to the sprite.
- Coatings have both a raw and a cooked image. Raw coating is generally unhealthy
- Generally coatings are intended for deep frying foods
-*/
-/datum/reagent/nutriment/coating
- name = "coating"
- id = "coating"
- nutriment_factor = 6 //Less dense than the food itself, but coatings still add extra calories
- var/messaged = 0
- var/icon_raw
- var/icon_cooked
- var/coated_adj = "coated"
- var/cooked_name = "coating"
-
-/datum/reagent/nutriment/coating/affect_ingest(mob/living/carbon/M, alien, removed)
-
- //We'll assume that the batter isnt going to be regurgitated and eaten by someone else. Only show this once
- if (data["cooked"] != 1)
- if (!messaged)
- to_chat(M, "Ugh, this raw [name] tastes disgusting.")
- nutriment_factor *= 0.5
- messaged = 1
-
- //Raw coatings will sometimes cause vomiting
- if (prob(1))
- M.vomit()
- ..()
-
-/datum/reagent/nutriment/coating/initialize_data(newdata) // Called when the reagent is created.
- ..()
- if (!data)
- data = list()
- else
- if (isnull(data["cooked"]))
- data["cooked"] = 0
- return
- data["cooked"] = 0
- if (holder && holder.my_atom && istype(holder.my_atom,/obj/item/reagent_containers/food/snacks))
- data["cooked"] = 1
- name = cooked_name
-
- //Batter which is part of objects at compiletime spawns in a cooked state
-
-
-//Handles setting the temperature when oils are mixed
-// todo: review data procs
-/datum/reagent/nutriment/coating/mix_data(datum/reagent_holder/holder, list/current_data, current_amount, list/new_data, new_amount)
- LAZYINITLIST(data)
- data["cooked"] = new_data["cooked"]
-
-/datum/reagent/nutriment/coating/batter
- name = "batter mix"
- cooked_name = "batter"
- id = "batter"
- color = "#f5f4e9"
- reagent_state = REAGENT_LIQUID
- icon_raw = "batter_raw"
- icon_cooked = "batter_cooked"
- coated_adj = "battered"
-
-/datum/reagent/nutriment/coating/beerbatter
- name = "beer batter mix"
- cooked_name = "beer batter"
- id = "beerbatter"
- color = "#f5f4e9"
- reagent_state = REAGENT_LIQUID
- icon_raw = "batter_raw"
- icon_cooked = "batter_cooked"
- coated_adj = "beer-battered"
-
-/datum/reagent/nutriment/coating/beerbatter/affect_ingest(mob/living/carbon/M, alien, removed)
- ..()
- M.add_chemical_effect(CE_ALCOHOL, 0.02) //Very slightly alcoholic
//=========================
//Fats
@@ -4635,40 +4486,23 @@
touch_met = 1.5
var/lastburnmessage = 0
-/datum/reagent/nutriment/triglyceride/oil/touch_turf(turf/simulated/T)
- if(!istype(T))
- return
-
- if(volume >= 3)
- T.wet_floor(2)
-
-// todo: review data procs
-/datum/reagent/nutriment/triglyceride/oil/initialize_data(newdata) // Called when the reagent is created.
- ..()
- if (!data)
- data = list("temperature" = T20C)
-
-//Handles setting the temperature when oils are mixed
-/datum/reagent/nutriment/triglyceride/oil/mix_data(datum/reagent_holder/holder, list/current_data, current_amount, list/new_data, new_amount)
- LAZYINITLIST(data)
- if (current_amount <= 0 || !data["temperature"] || !volume)
- //If we get here, then this reagent has just been created, just copy the temperature exactly
- data["temperature"] = new_data["temperature"]
- else
- //Our temperature is set to the mean of the two mixtures, taking volume into account
- var/total = (data["temperature"] * current_amount) + (new_data["temperature"] * new_amount)
- data["temperature"] = total / volume
+/datum/reagent/nutriment/triglyceride/oil/on_touch_turf(turf/target, remaining, allocated, data)
+ if(allocated >= 3)
+ if(istype(target, /turf/simulated))
+ var/turf/simulated/simulated_target = target
+ simulated_target.wet_floor(2)
return ..()
//Calculates a scaling factor for scalding damage, based on the temperature of the oil and creature's heat resistance
-/datum/reagent/nutriment/triglyceride/oil/proc/heatdamage(mob/living/carbon/M)
+/datum/reagent/nutriment/triglyceride/oil/proc/heatdamage(mob/living/carbon/M, datum/reagent_holder/holder)
+ var/temperature = holder.temperature
var/threshold = 360//Human heatdamage threshold
var/datum/species/S = M.species
if (S && istype(S))
threshold = S.heat_level_1
//If temperature is too low to burn, return a factor of 0. no damage
- if (data["temperature"] < threshold)
+ if (temperature < threshold)
return 0
//Step = degrees above heat level 1 for 1.0 multiplier
@@ -4676,15 +4510,16 @@
if (S && istype(S))
step = (S.heat_level_2 - S.heat_level_1)*1.5
- . = data["temperature"] - threshold
+ . = temperature - threshold
. /= step
. = min(., 2.5)//Cap multiplier at 2.5
-/datum/reagent/nutriment/triglyceride/oil/affect_touch(mob/living/carbon/M, alien, removed)
- var/dfactor = heatdamage(M)
+/datum/reagent/nutriment/triglyceride/oil/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ var/temperature = metabolism.legacy_current_holder.temperature
+ var/dfactor = heatdamage(M, metabolism.legacy_current_holder)
if (dfactor)
M.take_random_targeted_damage(brute = 0, brute = removed * 1.5 * dfactor)
- data["temperature"] -= (6 * removed) / (1 + volume*0.1)//Cools off as it burns you
+ temperature -= (6 * removed) / (1 + volume*0.1)//Cools off as it burns you
if (lastburnmessage+100 < world.time )
to_chat(M, SPAN_DANGER("Searing hot oil burns you, wash it off quick!"))
lastburnmessage = world.time
@@ -4738,7 +4573,7 @@
color = "#EDB91F"
taste_description = "cheese"
-/datum/reagent/nutriment/protein/cheese/affect_ingest(mob/living/carbon/M, alien, removed) //Cheese is a kind of milk.
+/datum/reagent/nutriment/protein/cheese/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) //Cheese is a kind of milk.
if(alien == IS_NARAMADI)
if(prob(5))
to_chat(M, SPAN_WARNING("You feel nauseous!"))
@@ -4820,19 +4655,19 @@
taste_description = "the gross yet satisfying combination of chewing on a raw steak while downing a shot of whiskey"
proof = WHISKEY/2
color = "#d3785d"
- metabolism = REM * 2.5 // about right for mixing nutriment and ethanol.
+ metabolism_rate = REM * 2.5 // about right for mixing nutriment and ethanol.
var/alt_nutriment_factor = 5 //half as much as protein since it's half protein.
//using a new variable instead of nutriment_factor so we can call ..() without that adding nutrition for us without taking factors for protein into account
glass_name = "Monster Tamer"
glass_desc = "This looks like an alcoholic slurry of meat. Gross."
-/datum/reagent/ethanol/monstertamer/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/monstertamer/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
monster_tamer(M,alien, removed, alt_nutriment_factor)
-/datum/reagent/ethanol/monstertamer/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/monstertamer/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SKRELL)
M.adjustToxLoss(removed) //Equivalent to half as much protein, since it's half protein.
@@ -4846,19 +4681,19 @@
description = "A questionably-delicious blend of a carnivore's favorite food and, it turns out, certain oligosaccharides common to certain plants."
taste_description = "the gross yet satisfying combination of chewing on a raw steak while gulping a bunch of root beer"
color = "#d3785d"
- metabolism = REM * 2.5 // not actually right for the mix, but required to make it mechanically equivalent for feralness purposes
+ metabolism_rate = REM * 2.5 // not actually right for the mix, but required to make it mechanically equivalent for feralness purposes
var/alt_nutriment_factor = 5 //half as much as protein since it's half protein.
//using a new variable instead of nutriment_factor so we can call ..() without that adding nutrition for us without taking factors for protein into account
glass_name = "Dry Monster Tamer"
glass_desc = "This looks like a fizzy slurry of meat. Gross."
-/datum/reagent/drink/drymonstertamer/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/drymonstertamer/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
monster_tamer(M,alien, removed, alt_nutriment_factor)
-/datum/reagent/drink/drymonstertamer/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/drymonstertamer/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SKRELL)
M.adjustToxLoss(removed) //Equivalent to half as much protein, since it's half protein.
@@ -4880,7 +4715,7 @@
glass_name = "Galactic Panic Attack"
glass_desc = "Looking into this is like staring at the stars."
-/datum/reagent/ethanol/galacticpanic/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/galacticpanic/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
M.afflict_stun(20 * 2)
@@ -4961,7 +4796,7 @@
glass_name = "Dumb Shroom Juice"
glass_desc = "Touch fuzzy, get dizzy."
-/datum/reagent/drink/shroomjuice/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/drink/shroomjuice/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(alien == IS_DIONA)
@@ -4976,7 +4811,7 @@
M.druggy = max(M.druggy, 30)
- var/effective_dose = dose
+ var/effective_dose = metabolism.total_processed_dose
if(issmall(M)) effective_dose *= 2
if(effective_dose < 1 * threshold)
M.apply_effect(3, STUTTER)
@@ -5129,13 +4964,13 @@
glass_name = "A Desire to Die"
glass_desc = "Deathbell and nuclear waste. The bane of your liver."
-/datum/reagent/ethanol/desiretodie/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/desiretodie/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(alien == IS_DIONA)
return
M.bloodstr.add_reagent("radium", 0.3)
-/datum/reagent/ethanol/desiretodie/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethanol/desiretodie/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
. = ..()
if(alien == IS_DIONA)
return
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Medicine.dm
index cb52e4e473da..7c2d4174742e 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Medicine.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Medicine.dm
@@ -8,10 +8,10 @@
reagent_state = REAGENT_LIQUID
color = "#00BFFF"
overdose = REAGENTS_OVERDOSE * 2
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
scannable = 1
-/datum/reagent/inaprovaline/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/inaprovaline/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.add_chemical_effect(CE_STABLE, 15)//Reduces bleeding rate, and allowes the patient to breath even when in shock
M.ceiling_chemical_effect(CE_PAINKILLER, 10)
@@ -27,14 +27,14 @@
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/bicaridine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/bicaridine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
if(alien != IS_DIONA)
M.heal_organ_damage(4 * removed * chem_effective, 0) //The first Parameter of the function is brute, the second burn damage
-/datum/reagent/bicaridine/overdose(mob/living/carbon/M, alien, removed)
+/datum/reagent/bicaridine/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
var/wound_heal = 1.5 * removed//Overdose enhances the healing effects
M.eye_blurry = min(M.eye_blurry + wound_heal, 250)
@@ -62,7 +62,7 @@
touch_met = REM * 0.75
can_overdose_touch = TRUE
-/datum/reagent/bicaridine/topical/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/bicaridine/topical/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -70,7 +70,7 @@
..(M, alien, removed * chem_effective)//heals the patient like Bicardine would
M.adjustToxLoss(2 * removed)//deals toxic damage like the other topical gels
-/datum/reagent/bicaridine/topical/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/bicaridine/topical/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -88,7 +88,7 @@
overdose = REAGENTS_OVERDOSE * 0.5
scannable = 1
-/datum/reagent/vermicetol/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/vermicetol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -103,14 +103,14 @@
reagent_state = REAGENT_SOLID
color = "#eae6e3"
overdose = REAGENTS_OVERDOSE * 0.8
- metabolism = REM * 0.4
+ metabolism_rate = REM * 0.4
scannable = 1
-/datum/reagent/calciumcarbonate/affect_blood(mob/living/carbon/M, alien, removed) // Why would you inject this.
+/datum/reagent/calciumcarbonate/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) // Why would you inject this.
if(alien != IS_DIONA)
M.adjustToxLoss(3 * removed)
-/datum/reagent/calciumcarbonate/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/calciumcarbonate/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.add_chemical_effect(CE_ANTACID, 3)//Antipuke effect
@@ -124,7 +124,7 @@
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/kelotane/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/kelotane/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.5
@@ -142,7 +142,7 @@
overdose = REAGENTS_OVERDOSE * 0.5
scannable = 1
-/datum/reagent/dermaline/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/dermaline/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -161,7 +161,7 @@
touch_met = REM * 0.75
can_overdose_touch = TRUE
-/datum/reagent/dermaline/topical/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/dermaline/topical/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -169,7 +169,7 @@
..(M, alien, removed * chem_effective)
M.adjustToxLoss(2 * removed)
-/datum/reagent/dermaline/topical/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/dermaline/topical/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -185,11 +185,11 @@
color = "#00A000"
scannable = 1
-/datum/reagent/dylovene/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/dylovene/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.66
- if(dose >= 15)
+ if(metabolism.total_processed_dose >= 15)
M.druggy = max(M.druggy, 5)
if(alien != IS_DIONA)
M.drowsyness = max(0, M.drowsyness - 6 * removed * chem_effective)//reduces drowsyness to zero
@@ -206,7 +206,7 @@
color = "#225722"
scannable = 1
-/datum/reagent/carthatoline/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/carthatoline/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(M.getToxLoss() && prob(10))//if the patient has toxin damage 10% chance to cause vomiting
@@ -234,13 +234,14 @@
color = "#0080FF"
overdose = REAGENTS_OVERDOSE
scannable = 1
- metabolism = REM * 0.25
-/datum/reagent/dexalin/affect_blood(mob/living/carbon/M, alien, removed)
+ metabolism_rate = REM * 0.25
+
+/datum/reagent/dexalin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_VOX)
M.adjustToxLoss(removed * 24) //Vox breath phoron, oxygen is rather deadly to them
if(alien == IS_ALRAUNE)
M.adjustToxLoss(removed * 10) //cit change: oxygen is waste for plants
- else if(alien == IS_SLIME && dose >= 15)
+ else if(alien == IS_SLIME && metabolism.total_processed_dose >= 15)
M.ceiling_chemical_effect(CE_PAINKILLER, 15)
if(prob(15))
to_chat(M, "You have a moment of clarity as you collapse.")
@@ -249,8 +250,8 @@
else if(alien != IS_DIONA)
M.adjustOxyLoss(-60 * removed) //Heals alot of oxyloss damage/but
//keep in mind that Dexaline has a metabolism rate of 0.25*REM meaning only 0.25 units are removed every tick(if your metabolism takes usuall 1u per tick)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/lexorin, 8 * removed)
- holder.remove_reagent("lexorin", 8 * removed)
/datum/reagent/dexalinp
name = "Dexalin Plus"
id = "dexalinp"
@@ -261,12 +262,12 @@
overdose = REAGENTS_OVERDOSE * 0.5
scannable = 1
-/datum/reagent/dexalinp/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/dexalinp/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_VOX)
M.adjustToxLoss(removed * 9)//Again, vox dont like O2
if(alien == IS_ALRAUNE)
M.adjustToxLoss(removed * 5) //cit change: oxygen is waste for plants
- else if(alien == IS_SLIME && dose >= 10)
+ else if(alien == IS_SLIME && metabolism.total_processed_dose >= 10)
M.ceiling_chemical_effect(CE_PAINKILLER, 25)
if(prob(25))
to_chat(M, "You have a moment of clarity, as you feel your tubes lose pressure rapidly.")
@@ -275,7 +276,7 @@
else if(alien != IS_DIONA)
M.adjustOxyLoss(-150 * removed)//Heals more oxyloss than Dex and has no metabolism reduction
- holder.remove_reagent("lexorin", 3 * removed)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/lexorin, 3 * removed)
/datum/reagent/tricordrazine
name = "Tricordrazine"
@@ -286,18 +287,20 @@
color = "#8040FF"
scannable = 1
-/datum/reagent/tricordrazine/affect_blood(mob/living/carbon/M, alien, removed)
- if(alien != IS_DIONA)//Heals everyone besides diona on all 4 base damage types.
- var/chem_effective = 1
- if(alien == IS_SLIME)
- chem_effective = 0.5
- M.adjustOxyLoss(-3 * removed * chem_effective)
- M.heal_organ_damage(1.5 * removed, 1.5 * removed * chem_effective)
- M.adjustToxLoss(-1.5 * removed * chem_effective)
+/datum/reagent/tricordrazine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(alien == IS_DIONA)
+ return
+ var/chem_effective = 1
+ if(alien == IS_SLIME)
+ chem_effective = 0.5
+ M.adjustOxyLoss(-3 * removed * chem_effective)
+ M.heal_organ_damage(1.5 * removed, 1.5 * removed * chem_effective)
+ M.adjustToxLoss(-1.5 * removed * chem_effective)
-/datum/reagent/tricordrazine/affect_touch(mob/living/carbon/M, alien, removed)
- if(alien != IS_DIONA)
- affect_blood(M, alien, removed * 0.4)
+/datum/reagent/tricordrazine/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(alien == IS_DIONA)
+ return
+ legacy_affect_blood(M, alien, removed * 0.4, metabolism)
/datum/reagent/earthsblood
name = "Earthsblood"
@@ -309,7 +312,7 @@
overdose = REAGENTS_OVERDOSE * 0.50
-/datum/reagent/earthsblood/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/earthsblood/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_ALRAUNE)
chem_effective = 1.1 //Plant to Plant Restoration
@@ -335,7 +338,7 @@
scannable = 1
can_overdose_touch = TRUE
-/datum/reagent/tricorlidaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/tricorlidaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
var/chem_effective = 1
if(alien == IS_SLIME)
@@ -344,7 +347,7 @@
M.heal_organ_damage(2 * removed, 2 * removed * chem_effective)//alittle more potent on brute and burns than Tricordrazine
M.adjustToxLoss(-0.5 * removed * chem_effective)//Same as Oxyloss
-/datum/reagent/tricorlidaze/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/tricorlidaze/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.adjustToxLoss(3 * removed)
@@ -368,11 +371,11 @@
taste_description = "overripe bananas"
reagent_state = REAGENT_LIQUID
color = "#8080FF"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
mrate_static = TRUE
scannable = 1
-/datum/reagent/cryoxadone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/cryoxadone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(M.bodytemperature < 170)
var/chem_effective = 1
if(alien == IS_SLIME)
@@ -393,11 +396,11 @@
taste_description = "rotten bananas"
reagent_state = REAGENT_LIQUID
color = "#80BFFF"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
mrate_static = TRUE
scannable = 1
-/datum/reagent/clonexadone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/clonexadone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(M.bodytemperature < 170)
var/chem_effective = 1
if(alien == IS_SLIME)
@@ -419,7 +422,7 @@
taste_description = "meat"
reagent_state = REAGENT_LIQUID
color = "#94B21C"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
mrate_static = TRUE
scannable = 1
@@ -431,7 +434,7 @@
. = ..(M, alien, location)
-/datum/reagent/necroxadone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/necroxadone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(M.bodytemperature < 170 || (M.stat == DEAD && M.has_modifier_of_type(/datum/modifier/bloodpump_corpse)))
var/chem_effective = 1
if(alien == IS_SLIME)
@@ -456,16 +459,16 @@
color = "#C8A5DC"
overdose = 60
scannable = 1
- metabolism = 0.02
+ metabolism_rate = 0.02
mrate_static = TRUE
-/datum/reagent/paracetamol/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/paracetamol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
M.ceiling_chemical_effect(CE_PAINKILLER, 25 * chem_effective)//kinda weak painkilling, for non life threatening injuries
-/datum/reagent/paracetamol/overdose(mob/living/carbon/M, alien)
+/datum/reagent/paracetamol/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
M.add_chemical_effect(CE_SLOWDOWN, 1)
@@ -480,17 +483,17 @@
color = "#CB68FC"
overdose = 30
scannable = 1
- metabolism = 0.02
+ metabolism_rate = 0.02
mrate_static = TRUE
-/datum/reagent/tramadol/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/tramadol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.8
M.add_chemical_effect(CE_SLOWDOWN, 1)
M.ceiling_chemical_effect(CE_PAINKILLER, 80 * chem_effective)//more potent painkilling, for close to fatal injuries
-/datum/reagent/tramadol/overdose(mob/living/carbon/M, alien)
+/datum/reagent/tramadol/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.setHallucination(max(M.hallucination, 2))
@@ -503,10 +506,10 @@
color = "#800080"
overdose = 20
scannable = 1
- metabolism = 0.02
+ metabolism_rate = 0.02
mrate_static = TRUE
-/datum/reagent/oxycodone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/oxycodone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_SLIME)
chem_effective = 0.75
@@ -515,7 +518,7 @@
M.add_chemical_effect(CE_SLOWDOWN, 1)
M.eye_blurry = min(M.eye_blurry + 10, 250 * chem_effective)
-/datum/reagent/oxycodone/overdose(mob/living/carbon/M, alien)
+/datum/reagent/oxycodone/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.druggy = max(M.druggy, 10)
M.setHallucination(max(M.hallucination, 3))
@@ -527,18 +530,18 @@
taste_description = "sourness"
reagent_state = REAGENT_LIQUID
color = "#800080"
- metabolism = 0.1 //Lasts up to 200 seconds if you give 20u which is OD.
+ metabolism_rate = 0.1 //Lasts up to 200 seconds if you give 20u which is OD.
mrate_static = TRUE
overdose = 20 //High OD. This is to make numbing bites have somewhat of a downside if you get bit too much. Have to go to medical for dialysis.
scannable = 0 //Let's not have medical mechs able to make an extremely strong organic painkiller
-/datum/reagent/numbing_enzyme/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/numbing_enzyme/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.ceiling_chemical_effect(CE_PAINKILLER, 200)//Similar to Oxycodone
if(prob(0.01)) //1 in 10000 chance per tick. Extremely rare.
to_chat(M,"Your body feels numb as a light, tingly sensation spreads throughout it, like some odd warmth.")
//Not noted here, but a movement debuff of 1.5 is handed out in human_movement.dm when numbing_enzyme is in a person's bloodstream!
-/datum/reagent/numbing_enzyme/overdose(mob/living/carbon/M, alien)
+/datum/reagent/numbing_enzyme/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
//..() //Add this if you want it to do toxin damage. Personally, let's allow them to have the horrid effects below without toxin damage.
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -572,16 +575,16 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#99CCFF"
- metabolism = REM * 0.05
+ metabolism_rate = REM * 0.05
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/synaptizine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/synaptizine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/chem_effective = 1
if(alien == IS_DIONA)
return
if(alien == IS_SLIME)
- if(dose >= 5) //Not effective in small doses, though it causes toxloss at higher ones, it will make the regeneration for brute and burn more 'efficient' at the cost of more nutrition.
+ if(metabolism.total_processed_dose >= 5) //Not effective in small doses, though it causes toxloss at higher ones, it will make the regeneration for brute and burn more 'efficient' at the cost of more nutrition.
M.nutrition -= removed * 2
M.adjustBruteLoss(-2 * removed)
M.adjustFireLoss(-1 * removed)
@@ -590,7 +593,7 @@
M.adjust_unconscious(20 * -1)
M.adjust_stunned(20 * -1)
M.adjust_paralyzed(20 * -1)
- holder.remove_reagent("mindbreaker", 5)
+ metabolism.legacy_current_holder.remove_reagent(/datum/reagent/mindbreaker, 5)
M.adjustHallucination(-10) //Primary use
M.adjustToxLoss(5 * removed * chem_effective) // It used to be incredibly deadly due to an oversight. Not anymore!
M.ceiling_chemical_effect(CE_PAINKILLER, 20 * chem_effective)
@@ -600,18 +603,16 @@
id = "hyperzine"
description = "Hyperzine is a highly effective, long lasting, muscle stimulant."
taste_description = "bitterness"
- metabolism = REM * 0.25 // see "long lasting"
+ metabolism_rate = REM * 0.25 // see "long lasting"
reagent_state = REAGENT_LIQUID
color = "#FF3300"
overdose = REAGENTS_OVERDOSE * 0.5
-/datum/reagent/hyperzine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/hyperzine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_TAJARA)
removed *= 1.25
if(alien == IS_SLIME)
M.make_jittery(4) //Hyperactive fluid pumping results in unstable 'skeleton', resulting in vibration.
- if(dose >= 5)
- M.nutrition = (M.nutrition - (removed * 2)) //Sadly this movement starts burning food in higher doses.
..()
if(prob(5))
M.emote(pick("twitch", "blink_r", "shiver"))
@@ -624,11 +625,11 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#FFFF66"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/alkysine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/alkysine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
var/chem_effective = 1
@@ -636,7 +637,7 @@
chem_effective = 0.25
if(M.brainloss >= 10)
M.afflict_paralyze(20 * 5)
- if(dose >= 10 && M.is_unconscious())
+ if(metabolism.total_processed_dose >= 10 && M.is_unconscious())
M.adjust_unconscious(20 * 1) //Messing with the core with a simple chemical probably isn't the best idea.
M.adjustBrainLoss(-8 * removed * chem_effective) //the Brain damage heal
M.ceiling_chemical_effect(CE_PAINKILLER, 10 * chem_effective)
@@ -651,7 +652,7 @@
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/imidazoline/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/imidazoline/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.eye_blurry = max(M.eye_blurry - 5, 0)
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -674,7 +675,7 @@
overdose = 10
scannable = 1
-/datum/reagent/peridaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/peridaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(ishuman(M))
var/mob/living/carbon/human/H = M
for(var/obj/item/organ/I in H.internal_organs)
@@ -701,7 +702,7 @@
overdose = 10
scannable = 1
-/datum/reagent/nanoperidaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanoperidaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(ishuman(M))
var/mob/living/carbon/human/H = M
for(var/obj/item/organ/I in H.internal_organs)
@@ -722,11 +723,11 @@
description = "An experimental drug used to heal bone fractures."
reagent_state = REAGENT_LIQUID
color = "#C9BCE3"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
overdose = REAGENTS_OVERDOSE * 0.5
scannable = 1
-/datum/reagent/osteodaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/osteodaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.heal_organ_damage(3 * removed, 0) //Gives the bones a chance to set properly even without other meds
@@ -744,12 +745,12 @@
description = "Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets."
reagent_state = REAGENT_LIQUID
color = "#4246C7"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
overdose = REAGENTS_OVERDOSE * 0.5
scannable = 1
var/repair_strength = 3
-/datum/reagent/myelamine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/myelamine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.eye_blurry += min(M.eye_blurry + (repair_strength * removed), 250)
@@ -776,11 +777,11 @@
taste_description = "metallic"
reagent_state = REAGENT_LIQUID
color = "#4444FF"
- metabolism = REM * 1.5
+ metabolism_rate = REM * 1.5
overdose = 10
scannable = 1
-/datum/reagent/respirodaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/respirodaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/repair_strength = 1
if(alien == IS_SLIME)
repair_strength = 0.6
@@ -799,14 +800,8 @@
H.losebreath = clamp(H.losebreath + 3, 0, 20)
else
H.losebreath = max(H.losebreath - 4, 0)
- if(M.ingested)
- for(var/datum/reagent/asbestos/R in M.ingested.reagent_list)
- R.remove_self(removed * 4)
- if(M.bloodstr)
- for(var/datum/reagent/asbestos/R in M.bloodstr.reagent_list)
- R.remove_self(removed * 4)
-
-
+ M.ingested?.remove_reagent(/datum/reagent/asbestos)
+ M.bloodstr?.remove_reagent(/datum/reagent/asbestos)
/datum/reagent/gastirodaxon
name = "Gastirodaxon"
@@ -815,11 +810,11 @@
taste_description = "chalk"
reagent_state = REAGENT_LIQUID
color = "#8B4513"
- metabolism = REM * 1.5
+ metabolism_rate = REM * 1.5
overdose = 10
scannable = 1
-/datum/reagent/gastirodaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/gastirodaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/repair_strength = 1
if(alien == IS_SLIME)
repair_strength = 0.6
@@ -846,11 +841,11 @@
taste_description = "glue"
reagent_state = REAGENT_LIQUID
color = "#D2691E"
- metabolism = REM * 1.5
+ metabolism_rate = REM * 1.5
overdose = 10
scannable = 1
-/datum/reagent/hepanephrodaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/hepanephrodaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/repair_strength = 1
if(alien == IS_SLIME)
repair_strength = 0.4
@@ -879,11 +874,11 @@
taste_description = "rust"
reagent_state = REAGENT_LIQUID
color = "#FF4444"
- metabolism = REM * 1.5
+ metabolism_rate = REM * 1.5
overdose = 10
scannable = 1
-/datum/reagent/cordradaxon/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/cordradaxon/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/repair_strength = 1
if(alien == IS_SLIME)
repair_strength = 0.6
@@ -910,7 +905,7 @@
overdose = 20
scannable = 1
-/datum/reagent/immunosuprizine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/immunosuprizine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/strength_mod = 1
if(alien == IS_DIONA) // It's a tree.
@@ -941,7 +936,7 @@
if(I.robotic >= ORGAN_ROBOT)
organtotal -= I
- if(dose >= 15)
+ if(metabolism.total_processed_dose >= 15)
for(var/obj/item/organ/I in organtotal)
if(I.transplant_data && prob(round(15 * strength_mod))) // Reset the rejection process, toggle it to not reject.
I.rejecting = 0
@@ -963,11 +958,11 @@
taste_description = "mordant"
reagent_state = REAGENT_SOLID
color = "#84B2B0"
- metabolism = REM * 0.75
+ metabolism_rate = REM * 0.75
overdose = 20
scannable = 1
-/datum/reagent/skrellimmuno/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/skrellimmuno/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/strength_mod = 0.5
if(alien == IS_SKRELL)
@@ -986,7 +981,7 @@
if(I.robotic >= ORGAN_ROBOT)
organtotal -= I
- if(dose >= 15)
+ if(metabolism.total_processed_dose >= 15)
for(var/obj/item/organ/I in organtotal)
if(I.transplant_data && prob(round(15 * strength_mod)))
I.rejecting = 0
@@ -1010,7 +1005,7 @@
color = "#004000"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/ryetalyn/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ryetalyn/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/needs_update = M.mutations.len > 0
M.mutations = list()
@@ -1057,19 +1052,17 @@
color = "#605048"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/ethylredoxrazine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ethylredoxrazine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.dizziness = 0
M.drowsyness = 0
M.stuttering = 0
M.SetConfused(0)
- if(M.ingested)
- for(var/datum/reagent/ethanol/R in M.ingested.reagent_list)
- R.remove_self(removed * 30)
- if(M.bloodstr)
- for(var/datum/reagent/ethanol/R in M.bloodstr.reagent_list)
- R.remove_self(removed * 20)
+ for(var/datum/reagent/ethanol/ethanol_filter in M.ingested.get_reagent_datums())
+ M.ingested.remove_reagent(ethanol_filter.id, removed * 30)
+ for(var/datum/reagent/ethanol/ethanol_filter in M.bloodstr.get_reagent_datums())
+ M.ingested.remove_reagent(ethanol_filter.id, removed * 20)
/datum/reagent/hyronalin
name = "Hyronalin"
@@ -1078,11 +1071,11 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#408000"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/hyronalin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/hyronalin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.cure_radiation(RAD_MOB_CURE_STRENGTH_HYRONALIN(removed))
@@ -1094,11 +1087,11 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#008000"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/arithrazine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/arithrazine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.cure_radiation(RAD_MOB_CURE_STRENGTH_ARITHRAZINE(removed))
@@ -1113,27 +1106,26 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#C1C1C1"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
mrate_static = TRUE
overdose = REAGENTS_OVERDOSE
scannable = 1
- data = 0
-/datum/reagent/spaceacillin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/spaceacillin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "You regain focus...")
else
var/delay = (5 MINUTES)
- if(world.time > data + delay)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + delay)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "Your senses feel unfocused, and divided.")
- M.add_chemical_effect(CE_ANTIBIOTIC, dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM)
+ M.add_chemical_effect(CE_ANTIBIOTIC, metabolism.total_processed_dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM)
-/datum/reagent/spaceacillin/affect_touch(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.8) // Not 100% as effective as injections, though still useful.
+/datum/reagent/spaceacillin/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.8, metabolism)
/datum/reagent/corophizine
name = "Corophizine"
@@ -1145,26 +1137,26 @@
mrate_static = TRUE
overdose = 10
scannable = 1
- data = 0
-/datum/reagent/corophizine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/corophizine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.add_chemical_effect(CE_ANTIBIOTIC, ANTIBIO_SUPER)
var/mob/living/carbon/human/H = M
if(ishuman(M) && alien == IS_SLIME) //Everything about them is treated like a targetted organism. Widespread bodily function begins to fail.
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "Your body ceases its revolt.")
else
var/delay = (3 MINUTES)
- if(world.time > data + delay)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + delay)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "It feels like your body is revolting!")
M.Confuse(7)
M.adjustFireLoss(removed * 2)
M.adjustToxLoss(removed * 2)
+ var/dose = metabolism.total_processed_dose
if(dose >= 5 && M.toxloss >= 10) //It all starts going wrong.
M.adjustBruteLoss(removed * 3)
M.eye_blurry = min(20, max(0, M.eye_blurry + 10))
@@ -1209,46 +1201,46 @@
taste_description = "oil"
reagent_state = REAGENT_SOLID
color = "#C1C1C8"
- metabolism = REM * 0.4
+ metabolism_rate = REM * 0.4
mrate_static = TRUE
overdose = REAGENTS_OVERDOSE
scannable = 1
- data = 0
can_overdose_touch = TRUE
-/datum/reagent/spacomycaze/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/spacomycaze/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.ceiling_chemical_effect(CE_PAINKILLER, 10)
M.adjustToxLoss(3 * removed)
-/datum/reagent/spacomycaze/affect_ingest(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.8)
+/datum/reagent/spacomycaze/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.8, metabolism)
-/datum/reagent/spacomycaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/spacomycaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "The itching fades...")
else
var/delay = (2 MINUTES)
- if(world.time > data + delay)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + delay)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "Your skin itches.")
- M.add_chemical_effect(CE_ANTIBIOTIC, dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM)
+ M.add_chemical_effect(CE_ANTIBIOTIC, metabolism.total_processed_dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM)
M.ceiling_chemical_effect(CE_PAINKILLER, 20) // 5 less than paracetamol.
-/datum/reagent/spacomycaze/touch_obj(obj/O)
- if(istype(O, /obj/item/stack/medical/crude_pack) && round(volume) >= 1)
- var/obj/item/stack/medical/crude_pack/C = O
+/datum/reagent/spacomycaze/on_touch_obj(obj/target, remaining, allocated, data, spread_between)
+ if(istype(target, /obj/item/stack/medical/crude_pack) && round(volume) >= 1)
+ var/obj/item/stack/medical/crude_pack/C = target
var/packname = C.name
- var/to_produce = min(C.amount, round(volume))
+ var/to_produce = min(C.amount, floor(allocated))
var/obj/item/stack/medical/M = C.upgrade_stack(to_produce)
if(M && M.amount)
- holder.my_atom.visible_message("\The [packname] bubbles.")
- remove_self(to_produce)
+ target.visible_message("\The [packname] bubbles.")
+ . += to_produce
+ return . + ..()
/datum/reagent/sterilizine
name = "Sterilizine"
@@ -1259,13 +1251,13 @@
color = "#C8A5DC"
touch_met = 5
-/datum/reagent/sterilizine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/sterilizine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_SLIME)
M.adjustFireLoss(removed)
M.adjustToxLoss(2 * removed)
return
-/datum/reagent/sterilizine/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/sterilizine/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.germ_level -= min(removed*20, M.germ_level)
for(var/obj/item/I in M.contents)
I.was_bloodied = null
@@ -1305,7 +1297,7 @@
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/leporazine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/leporazine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(M.bodytemperature > 310)
@@ -1323,7 +1315,7 @@
overdose = REAGENTS_OVERDOSE
scannable = 1
-/datum/reagent/rezadone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/rezadone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
var/mob/living/carbon/human/H = M
@@ -1368,20 +1360,19 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#BF80BF"
- metabolism = 0.01
+ metabolism_rate = 0.01
ingest_met = 0.25
mrate_static = TRUE
- data = 0
-/datum/reagent/methylphenidate/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/methylphenidate/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "You lose focus...")
else
- if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + ANTIDEPRESSANT_MESSAGE_DELAY)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "Your mind feels focused and undivided.")
/datum/reagent/citalopram
@@ -1391,20 +1382,19 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#FF80FF"
- metabolism = 0.01
+ metabolism_rate = 0.01
ingest_met = 0.25
mrate_static = TRUE
- data = 0
-/datum/reagent/citalopram/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/citalopram/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "Your mind feels a little less stable...")
else
- if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + ANTIDEPRESSANT_MESSAGE_DELAY)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "Your mind feels stable... a little stable.")
/datum/reagent/paroxetine
@@ -1414,20 +1404,19 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#FF80BF"
- metabolism = 0.01
+ metabolism_rate = 0.01
ingest_met = 0.25
mrate_static = TRUE
- data = 0
-/datum/reagent/paroxetine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/paroxetine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "Your mind feels much less stable...")
else
- if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + ANTIDEPRESSANT_MESSAGE_DELAY)
+ metabolism.blackboard["last-message"] = world.time
if(prob(1))
to_chat(M, "Your mind breaks apart...")
M.adjustHallucination(200)
@@ -1442,7 +1431,7 @@
reagent_state = REAGENT_SOLID
color = "#d5e2e5"
-/datum/reagent/adranol/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/adranol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(M.confused)
@@ -1459,20 +1448,19 @@
taste_description = "mint"
reagent_state = REAGENT_LIQUID
color = "#e6efe3"
- metabolism = 0.01
+ metabolism_rate = 0.01
ingest_met = 0.25
mrate_static = TRUE
- data = 0
-/datum/reagent/qerr_quem/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/qerr_quem/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- if(volume <= 0.1 && data != -1)
- data = -1
+ if(metabolism.legacy_volume_remaining <= 0.1 && metabolism.blackboard["last-message"] != -1)
+ metabolism.blackboard["last-message"] = -1
to_chat(M, "You feel antsy, your concentration wavers...")
else
- if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY)
- data = world.time
+ if(world.time > metabolism.blackboard["last-message"] + ANTIDEPRESSANT_MESSAGE_DELAY)
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "You feel invigorated and calm.")
// This exists to cut the number of chemicals a merc borg has to juggle on their hypo.
@@ -1483,17 +1471,17 @@
taste_description = "metal"
reagent_state = REAGENT_SOLID
color = "#555555"
- metabolism = REM * 4 // Nanomachines gotta go fast.
+ metabolism_rate = REM * 4 // Nanomachines gotta go fast.
scannable = TRUE
affects_robots = TRUE
-/datum/reagent/nanite/healing/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/healing/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.heal_organ_damage(2 * removed, 2 * removed)
M.adjustOxyLoss(-4 * removed)
M.adjustToxLoss(-2 * removed)
M.adjustCloneLoss(-2 * removed)
-/datum/reagent/nanite/healing/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/healing/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.heal_organ_damage(2 * removed, 2 * removed)
M.adjustOxyLoss(-4 * removed)
M.adjustToxLoss(-2 * removed)
@@ -1508,7 +1496,7 @@
color = "#0E900E"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/ickypak/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ickypak/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.make_dizzy(1)
M.adjustHalLoss(2)
@@ -1531,7 +1519,7 @@
color = "#EF77E5"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/unsorbitol/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/unsorbitol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.make_dizzy(1)
M.adjustHalLoss(1)
if(!M.confused) M.confused = 1
@@ -1563,7 +1551,7 @@
color = "#333333"
scannable = 1
-/datum/reagent/nif_repair_nanites/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nif_repair_nanites/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.nif)
@@ -1627,19 +1615,19 @@
taste_description = "sour metal"
taste_mult = 2
reagent_state = REAGENT_LIQUID
- metabolism = REM * 0.016
+ metabolism_rate = REM * 0.016
mrate_static = TRUE
color = "#52ca22"
scannable = 1
overdose = 16
-/datum/reagent/neuratrextate/affect_ingest(mob/living/carbon/M)
+/datum/reagent/neuratrextate/legacy_affect_ingest(mob/living/carbon/M, datum/reagent_metabolism/metabolism)
remove_self(30)
to_chat(M, "It feels like there's a pile of knives in your stomach!")
M.druggy += 10
M.vomit()
-/datum/reagent/neuratrextate/overdose(mob/living/carbon/M)
+/datum/reagent/neuratrextate/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.druggy += 30
M.adjustHallucination(20)
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Modifiers.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Modifiers.dm
index 5ed243757767..ae7162010236 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Modifiers.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Modifiers.dm
@@ -9,15 +9,15 @@
taste_description = "metal"
reagent_state = REAGENT_LIQUID
color = "#ff5555"
- metabolism = REM
+ metabolism_rate = REM
var/modifier_to_add = /datum/modifier/berserk
var/modifier_duration = 2 SECONDS // How long, per unit dose, will this last?
-/datum/reagent/modapplying/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/modapplying/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- M.add_modifier(modifier_to_add, dose * modifier_duration)
+ M.add_modifier(modifier_to_add, metabolism.total_processed_dose * modifier_duration)
/datum/reagent/modapplying/cryofluid
name = "cryogenic slurry"
@@ -25,20 +25,20 @@
description = "An incredibly strange liquid that rapidly absorbs thermal energy from materials it contacts."
taste_description = "siberian hellscape"
color = "#4CDBDB"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
modifier_to_add = /datum/modifier/cryogelled
modifier_duration = 3 SECONDS
-/datum/reagent/modapplying/cryofluid/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/modapplying/cryofluid/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..(M, alien, removed)
M.bodytemperature -= removed * 20
-/datum/reagent/modapplying/cryofluid/affect_ingest(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 2.5)
+/datum/reagent/modapplying/cryofluid/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 2.5, metabolism)
-/datum/reagent/modapplying/cryofluid/affect_touch(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.6)
+/datum/reagent/modapplying/cryofluid/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.6, metabolism)
/datum/reagent/modapplying/cryofluid/touch_mob(mob/M, amount)
if(isliving(M))
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm
index aa3fb658bdb7..aebe8a15a147 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm
@@ -1,254 +1,5 @@
-/* Paint and crayons */
-
-/datum/reagent/crayon_dust
- name = "Crayon dust"
- id = "crayon_dust"
- description = "Intensely coloured powder obtained by grinding crayons."
- taste_description = "powdered wax"
- reagent_state = REAGENT_LIQUID
- color = "#888888"
- overdose = 5
-
-/datum/reagent/crayon_dust/red
- name = "Red crayon dust"
- id = "crayon_dust_red"
- color = "#FE191A"
-
-/datum/reagent/crayon_dust/orange
- name = "Orange crayon dust"
- id = "crayon_dust_orange"
- color = "#FFBE4F"
-
-/datum/reagent/crayon_dust/yellow
- name = "Yellow crayon dust"
- id = "crayon_dust_yellow"
- color = "#FDFE7D"
-
-/datum/reagent/crayon_dust/green
- name = "Green crayon dust"
- id = "crayon_dust_green"
- color = "#18A31A"
-
-/datum/reagent/crayon_dust/blue
- name = "Blue crayon dust"
- id = "crayon_dust_blue"
- color = "#247CFF"
-
-/datum/reagent/crayon_dust/purple
- name = "Purple crayon dust"
- id = "crayon_dust_purple"
- color = "#CC0099"
-
-/datum/reagent/crayon_dust/grey //Mime
- name = "Grey crayon dust"
- id = "crayon_dust_grey"
- color = "#808080"
-
-/datum/reagent/crayon_dust/brown //Rainbow
- name = "Brown crayon dust"
- id = "crayon_dust_brown"
- color = "#846F35"
-
-/datum/reagent/marker_ink
- name = "Marker ink"
- id = "marker_ink"
- description = "Intensely coloured ink used in markers."
- taste_description = "extremely bitter"
- reagent_state = REAGENT_LIQUID
- color = "#888888"
- overdose = 5
-
-/datum/reagent/marker_ink/black
- name = "Black marker ink"
- id = "marker_ink_black"
- color = "#000000"
-
-/datum/reagent/marker_ink/red
- name = "Red marker ink"
- id = "marker_ink_red"
- color = "#FE191A"
-
-/datum/reagent/marker_ink/orange
- name = "Orange marker ink"
- id = "marker_ink_orange"
- color = "#FFBE4F"
-
-/datum/reagent/marker_ink/yellow
- name = "Yellow marker ink"
- id = "marker_ink_yellow"
- color = "#FDFE7D"
-
-/datum/reagent/marker_ink/green
- name = "Green marker ink"
- id = "marker_ink_green"
- color = "#18A31A"
-
-/datum/reagent/marker_ink/blue
- name = "Blue marker ink"
- id = "marker_ink_blue"
- color = "#247CFF"
-
-/datum/reagent/marker_ink/purple
- name = "Purple marker ink"
- id = "marker_ink_purple"
- color = "#CC0099"
-
-/datum/reagent/marker_ink/grey //Mime
- name = "Grey marker ink"
- id = "marker_ink_grey"
- color = "#808080"
-
-/datum/reagent/marker_ink/brown //Rainbow
- name = "Brown marker ink"
- id = "marker_ink_brown"
- color = "#846F35"
-
-/datum/reagent/chalk_dust
- name = "chalk dust"
- id = "chalk_dust"
- description = "Dusty powder obtained by grinding chalk."
- taste_description = "powdered chalk"
- reagent_state = REAGENT_LIQUID
- color = "#FFFFFF"
- overdose = 5
-
-/datum/reagent/chalk_dust/red
- name = "red chalk dust"
- id = "chalk_dust_red"
- color = "#aa0000"
-
-/datum/reagent/chalk_dust/black
- name = "black chalk dust"
- id = "chalk_dust_black"
- color = "#180000"
-
-/datum/reagent/chalk_dust/blue
- name = "blue chalk dust"
- id = "chalk_dust_blue"
- color = "#000370"
-
-/datum/reagent/paint
- name = "Paint"
- id = "paint"
- description = "This paint will stick to almost any object."
- taste_description = "chalk"
- reagent_state = REAGENT_LIQUID
- color = "#808080"
- overdose = REAGENTS_OVERDOSE * 0.5
- color_weight = 20
-
-/datum/reagent/paint/touch_turf(turf/T)
- if(istype(T) && !istype(T, /turf/space))
- T.color = color
-
-/datum/reagent/paint/touch_obj(obj/O)
- if(istype(O))
- O.color = color
-
-/datum/reagent/paint/touch_mob(mob/M)
- if(istype(M) && !istype(M, /mob/observer)) //painting ghosts: not allowed
- M.color = color //maybe someday change this to paint only clothes and exposed body parts for human mobs.
-
-/datum/reagent/paint/get_data()
- return color
-
-/datum/reagent/paint/initialize_data(newdata)
- color = newdata
- return
-
-/datum/reagent/paint/mix_data(newdata, newamount)
- var/list/colors = list(0, 0, 0, 0)
- var/tot_w = 0
-
- var/hex1 = uppertext(color)
- var/hex2 = uppertext(newdata)
- if(length(hex1) == 7)
- hex1 += "FF"
- if(length(hex2) == 7)
- hex2 += "FF"
- if(length(hex1) != 9 || length(hex2) != 9)
- return
- colors[1] += hex2num(copytext(hex1, 2, 4)) * volume
- colors[2] += hex2num(copytext(hex1, 4, 6)) * volume
- colors[3] += hex2num(copytext(hex1, 6, 8)) * volume
- colors[4] += hex2num(copytext(hex1, 8, 10)) * volume
- tot_w += volume
- colors[1] += hex2num(copytext(hex2, 2, 4)) * newamount
- colors[2] += hex2num(copytext(hex2, 4, 6)) * newamount
- colors[3] += hex2num(copytext(hex2, 6, 8)) * newamount
- colors[4] += hex2num(copytext(hex2, 8, 10)) * newamount
- tot_w += newamount
-
- color = rgb(colors[1] / tot_w, colors[2] / tot_w, colors[3] / tot_w, colors[4] / tot_w)
- return
-
/* Things that didn't fit anywhere else */
-/datum/reagent/adminordrazine //An OP chemical for admins
- name = "Adminordrazine"
- id = "adminordrazine"
- description = "It's magic. We don't have to explain it."
- taste_description = "bwoink"
- reagent_state = REAGENT_LIQUID
- color = "#C8A5DC"
- affects_dead = 1 //This can even heal dead people.
- metabolism = 0.1
- mrate_static = TRUE //Just in case
-
- glass_name = "liquid gold"
- glass_desc = "It's magic. We don't have to explain it."
-
-/datum/reagent/adminordrazine/affect_touch(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed)
-
-/datum/reagent/adminordrazine/affect_blood(mob/living/carbon/M, alien, removed)
- M.setCloneLoss(0)
- M.setOxyLoss(0)
- M.radiation = 0
- M.heal_organ_damage(20,20)
- M.adjustToxLoss(-20)
- M.setHallucination(0)
- M.setHalLoss(0)
- M.setBrainLoss(0)
- M.disabilities = 0
- M.sdisabilities = 0
- M.eye_blurry = 0
- M.remove_status_effect(/datum/status_effect/sight/blindness)
- M.set_paralyzed(0)
- M.set_stunned(0)
- M.set_unconscious(0)
- M.silent = 0
- M.dizziness = 0
- M.drowsyness = 0
- M.stuttering = 0
- M.SetConfused(0)
- M.set_sleeping(0)
- M.jitteriness = 0
- M.radiation = 0
- M.ExtinguishMob()
- M.fire_stacks = 0
- if(M.bodytemperature > 310)
- M.bodytemperature = max(310, M.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
- else if(M.bodytemperature < 311)
- M.bodytemperature = min(310, M.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/wound_heal = 5
- for(var/obj/item/organ/external/O in H.bad_external_organs)
- if(O.status & ORGAN_BROKEN)
- O.mend_fracture() //Only works if the bone won't rebreak, as usual
- for(var/datum/wound/W as anything in O.wounds)
- if(W.bleeding())
- W.damage = max(W.damage - wound_heal, 0)
- if(W.damage <= 0)
- O.cure_exact_wound(W)
- continue
- if(W.internal)
- W.damage = max(W.damage - wound_heal, 0)
- if(W.damage <= 0)
- O.cure_exact_wound(W)
- continue
-
/datum/reagent/gold
name = "Gold"
id = "gold"
@@ -281,10 +32,10 @@
reagent_state = REAGENT_SOLID
color = "#777777"
-/datum/reagent/uranium/affect_touch(mob/living/carbon/M, alien, removed)
- affect_ingest(M, alien, removed)
+/datum/reagent/uranium/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_ingest(M, alien, removed, metabolism)
-/datum/reagent/uranium/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/uranium/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.apply_effect(5 * removed, IRRADIATE, 0)
/datum/reagent/uranium/touch_turf(turf/T)
@@ -304,34 +55,13 @@
color = "#C8A5DC"
mrate_static = TRUE
-/datum/reagent/adrenaline/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/adrenaline/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.set_unconscious(0)
M.set_paralyzed(0)
M.adjustToxLoss(rand(3))
-/datum/reagent/water/holywater
- name = "Holy Water"
- id = "holywater"
- description = "An ashen-obsidian-water mix, this solution will alter certain sections of the brain's rationality."
- taste_description = "water"
- color = "#E0E8EF"
- mrate_static = TRUE
-
- glass_name = "holy water"
- glass_desc = "An ashen-obsidian-water mix, this solution will alter certain sections of the brain's rationality."
-
-/datum/reagent/water/holywater/affect_ingest(mob/living/carbon/M, alien, removed)
- ..()
- if(ishuman(M)) // Any location
- if(M.mind && cult.is_antagonist(M.mind) && prob(10))
- cult.remove_antagonist(M.mind)
-
-/datum/reagent/water/holywater/touch_turf(turf/T)
- if(volume >= 5)
- T.holy = 1
- return
/datum/reagent/ammonia
name = "Ammonia"
@@ -342,7 +72,7 @@
reagent_state = REAGENT_GAS
color = "#404030"
-/datum/reagent/ammonia/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/ammonia/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE)
M.nutrition += removed * 2 //cit change: fertilizer is waste for plants
return
@@ -355,7 +85,7 @@
reagent_state = REAGENT_LIQUID
color = "#604030"
-/datum/reagent/diethylamine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/diethylamine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE)
M.nutrition += removed * 5 //cit change: fertilizer is waste for plants
return
@@ -431,22 +161,6 @@
color = "#C8A5DC"
affects_robots = TRUE
-/datum/reagent/coolant/affect_blood(mob/living/carbon/M, alien, removed)
- if(M.isSynthetic() && ishuman(M))
- var/mob/living/carbon/human/H = M
-
- var/datum/reagent/blood/coolant = H.get_blood(H.vessel)
-
- if(coolant)
- H.vessel.add_reagent("blood", removed, coolant.data)
-
- else
- H.vessel.add_reagent("blood", removed)
- H.fixblood()
-
- else
- ..()
-
/datum/reagent/ultraglue
name = "Ultra Glue"
id = "glue"
@@ -462,20 +176,6 @@
reagent_state = REAGENT_LIQUID
color = "#B97A57"
-/datum/reagent/luminol
- name = "Luminol"
- id = "luminol"
- description = "A compound that interacts with blood on the molecular level."
- taste_description = "metal"
- reagent_state = REAGENT_LIQUID
- color = "#F2F3F4"
-
-/datum/reagent/luminol/touch_obj(obj/O)
- O.reveal_blood()
-
-/datum/reagent/luminol/touch_mob(mob/living/L)
- L.reveal_blood()
-
/datum/reagent/nutriment/biomass
name = "Biomass"
id = "biomass"
@@ -492,10 +192,10 @@
taste_description = "metal"
reagent_state = REAGENT_SOLID
color = "#333333"
- metabolism = REM * 3 // Broken nanomachines go a bit slower.
+ metabolism_rate = REM * 3 // Broken nanomachines go a bit slower.
scannable = 1
-/datum/reagent/defective_nanites/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/defective_nanites/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.take_random_targeted_damage(brute = 2 * removed, brute = 2 * removed)
M.adjustOxyLoss(4 * removed)
M.adjustToxLoss(2 * removed)
@@ -533,72 +233,6 @@
color = "#464650"
taste_description = "salt"
-/*Liquid Carpets*/
-
-/datum/reagent/carpet
- name = "Liquid Carpet"
- id = "liquidcarpet"
- description = "Liquified carpet fibers, ready for dyeing."
- reagent_state = REAGENT_LIQUID
- color = "#b51d05"
- taste_description = "carpet"
-
-/datum/reagent/carpet/black
- name = "Liquid Black Carpet"
- id = "liquidcarpetb"
- description = "Black Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#000000"
- taste_description = "rare and ashy carpet"
-
-/datum/reagent/carpet/blue
- name = "Liquid Blue Carpet"
- id = "liquidcarpetblu"
- description = "Blue Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#3f4aee"
- taste_description = "commanding carpet"
-
-/datum/reagent/carpet/turquoise
- name = "Liquid Turquoise Carpet"
- id = "liquidcarpettur"
- description = "Turquoise Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#0592b5"
- taste_description = "water-logged carpet"
-
-/datum/reagent/carpet/sblue
- name = "Liquid Silver Blue Carpet"
- id = "liquidcarpetsblu"
- description = "Silver Blue Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#0011ff"
- taste_description = "sterile and medicinal carpet"
-
-/datum/reagent/carpet/clown
- name = "Liquid Clown Carpet"
- id = "liquidcarpetc"
- description = "Clown Carpet Fibers.... No clowns were harmed in the making of this."
- reagent_state = REAGENT_LIQUID
- color = "#e925be"
- taste_description = "clown shoes and banana peels"
-
-/datum/reagent/carpet/purple
- name = "Liquid Purple Carpet"
- id = "liquidcarpetp"
- description = "Purple Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#a614d3"
- taste_description = "bleeding edge carpet research"
-
-/datum/reagent/carpet/orange
- name = "Liquid Orange Carpet"
- id = "liquidcarpeto"
- description = "Orange Carpet Fibers, ready for reinforcement."
- reagent_state = REAGENT_LIQUID
- color = "#f16e16"
- taste_description = "extremely overengineered carpet"
-
//Ashlander Alchemy!
/datum/reagent/alchemybase
name = "Alchemical Base"
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Toxins.dm
index 4491514ddbbf..2ef2b758aa47 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Toxins.dm
@@ -1,32 +1,5 @@
/* Toxins, poisons, venoms */
-/datum/reagent/toxin
- name = "toxin"
- id = "toxin"
- description = "A toxic chemical."
- taste_description = "bitterness"
- taste_mult = 1.2
- reagent_state = REAGENT_LIQUID
- color = "#CF3600"
- metabolism = REM * 0.25 // 0.05 by default. Hopefully enough to get some help, or die horribly, whatever floats your boat
- filtered_organs = list(O_LIVER, O_KIDNEYS)
- var/strength = 4 // How much damage it deals per unit
- var/skin_danger = 0.2 // The multiplier for how effective the toxin is when making skin contact.
-
-/datum/reagent/toxin/affect_blood(mob/living/carbon/M, alien, removed)
- if(strength && alien != IS_DIONA)
- if(issmall(M)) removed *= 2 // Small bodymass, more effect from lower volume.
- if(alien == IS_SLIME)
- removed *= 0.25 // Results in half the standard tox as normal. Prometheans are 'Small' for flaps.
- if(dose >= 10)
- M.nutrition += strength * removed //Body has to deal with the massive influx of toxins, rather than try using them to repair.
- else
- M.heal_organ_damage((10/strength) * removed, (10/strength) * removed) //Doses of toxins below 10 units, and 10 strength, are capable of providing useful compounds for repair.
- M.adjustToxLoss(strength * removed)
-
-/datum/reagent/toxin/affect_touch(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.2)
-
/datum/reagent/toxin/plasticide
name = "Plasticide"
id = "plasticide"
@@ -64,7 +37,7 @@
strength = 8
skin_danger = 0.4
-/datum/reagent/toxin/neurotoxic_protein/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/neurotoxic_protein/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien != IS_DIONA)
if(CHECK_MOBILITY(M, MOBILITY_CAN_MOVE) && istype(M.loc, /turf/space))
@@ -87,7 +60,7 @@
if(istype(L))
L.adjust_fire_stacks(amount / fire_mult)
-/datum/reagent/toxin/hydrophoron/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/hydrophoron/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.take_random_targeted_damage(brute = 0, brute = removed * 0.1) //being splashed directly with hydrophoron causes minor chemical burns
if(prob(10 * fire_mult))
M.pl_effects()
@@ -101,7 +74,7 @@
spawn (0) target_tile.hotspot_expose(700, 400)
remove_self(volume)
-/datum/reagent/toxin/hydrophoron/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/hydrophoron/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
M.adjust_fire_stacks(removed * 10)
@@ -132,7 +105,7 @@
if(istype(L))
L.adjust_fire_stacks(amount / 5)
-/datum/reagent/toxin/phoron/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/phoron/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.adjust_fire_stacks(removed / 5)
if(alien == IS_VOX || alien == IS_XENOHYBRID)
@@ -141,7 +114,7 @@
if(prob(50))
M.pl_effects()
-/datum/reagent/toxin/phoron/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/phoron/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_VOX)
M.adjustOxyLoss(-100 * removed) //5 oxyloss healed per tick.
return //You're wasting plasma (a semi-limited chemical) to save someone, so it might as well be somewhat strong.
@@ -154,7 +127,7 @@
/datum/reagent/toxin/phoron/touch_turf(turf/simulated/T, amount)
if(!istype(T))
return
- T.assume_gas(GAS_ID_VOLATILE_FUEL, amount, T20C)
+ T.assume_gas(GAS_ID_PHORON, amount, T20C)
remove_self(amount)
/datum/reagent/toxin/cyanide //Fast and Lethal
@@ -166,9 +139,9 @@
reagent_state = REAGENT_LIQUID
color = "#CF3600"
strength = 20
- metabolism = REM * 2
+ metabolism_rate = REM * 2
-/datum/reagent/toxin/cyanide/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/cyanide/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.adjustOxyLoss(20 * removed)
M.afflict_sleeping(20 * 1)
@@ -181,7 +154,7 @@
reagent_state = REAGENT_SOLID
strength = 5
-/datum/reagent/toxin/mold/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/mold/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.adjustToxLoss(strength * removed)
if(prob(5))
@@ -205,14 +178,13 @@
strength = 5
filtered_organs = list(O_SPLEEN)
-/datum/reagent/toxin/expired_medicine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/expired_medicine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(prob(5))
M.vomit()
-/datum/reagent/toxin/expired_medicine/affect_ingest(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.66)
-
+/datum/reagent/toxin/expired_medicine/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.66, metabolism)
/datum/reagent/toxin/stimm //Homemade Hyperzine
name = "Stimm"
@@ -221,11 +193,11 @@
taste_description = "sweetness"
taste_mult = 1.8
color = "#d0583a"
- metabolism = REM * 3
+ metabolism_rate = REM * 3
overdose = 10
strength = 3
-/datum/reagent/toxin/stimm/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/stimm/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_TAJARA)
removed *= 1.25
..()
@@ -247,12 +219,12 @@
overdose = REAGENTS_OVERDOSE
filtered_organs = list(O_SPLEEN, O_KIDNEYS)
-/datum/reagent/toxin/potassium_chloride/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/potassium_chloride/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_SLIME)
M.adjustFireLoss(removed * 2)
-/datum/reagent/toxin/potassium_chloride/overdose(mob/living/carbon/M, alien)
+/datum/reagent/toxin/potassium_chloride/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -273,7 +245,7 @@
overdose = 20
filtered_organs = list(O_SPLEEN, O_KIDNEYS)
-/datum/reagent/toxin/potassium_chlorophoride/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/potassium_chlorophoride/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -292,25 +264,26 @@
taste_description = "numbness"
reagent_state = REAGENT_SOLID
color = "#669900"
- metabolism = REM
+ metabolism_rate = REM
strength = 3
mrate_static = TRUE
-/datum/reagent/toxin/zombiepowder/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/zombiepowder/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
M.status_flags |= STATUS_FAKEDEATH
+ addtimer(CALLBACK(M, __really_shitty_zombie_powder_shim), 0.5 SECONDS)
M.adjustOxyLoss(3 * removed)
- M.afflict_paralyze(20 * 10)
+ M.afflict_paralyze(20 SECONDS)
M.silent = max(M.silent, 10)
M.tod = stationtime2text()
-/datum/reagent/toxin/zombiepowder/Destroy()
- if(holder && holder.my_atom && ismob(holder.my_atom))
- var/mob/M = holder.my_atom
- M.status_flags &= ~STATUS_FAKEDEATH
- return ..()
+/mob/living/carbon/proc/__really_shitty_zombie_powder_shim()
+ if(bloodstr.has_reagent(/datum/reagent/toxin/zombiepowder))
+ return
+ status_flags &= ~STATUS_FAKEDEATH
+ update_mobility()
/datum/reagent/toxin/lichpowder
name = "Lich Powder"
@@ -318,15 +291,16 @@
description = "A stablized nerve agent that puts the subject into a strange state of un-death."
reagent_state = REAGENT_SOLID
color = "#666666"
- metabolism = REM * 0.75
+ metabolism_rate = REM * 0.75
strength = 2
mrate_static = TRUE
-/datum/reagent/toxin/lichpowder/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/lichpowder/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
if(alien == IS_DIONA)
return
M.status_flags |= STATUS_FAKEDEATH
+ addtimer(CALLBACK(M, __really_shitty_lich_powder_shim), 0.5 SECONDS)
M.adjustOxyLoss(1 * removed)
M.silent = max(M.silent, 10)
M.tod = stationtime2text()
@@ -335,11 +309,11 @@
M.visible_message("[M] wheezes.", "You wheeze sharply... it's cold.")
M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, T0C - 10)
-/datum/reagent/toxin/lichpowder/Destroy()
- if(holder && holder.my_atom && ismob(holder.my_atom))
- var/mob/M = holder.my_atom
- M.status_flags &= ~STATUS_FAKEDEATH
- return ..()
+/mob/living/carbon/proc/__really_shitty_lich_powder_shim()
+ if(bloodstr.has_reagent(/datum/reagent/toxin/lichpowder))
+ return
+ status_flags &= ~STATUS_FAKEDEATH
+ update_mobility()
/datum/reagent/toxin/fertilizer //Reagents used for plant fertilizers.
name = "fertilizer"
@@ -351,7 +325,7 @@
strength = 0.5 // It's not THAT poisonous.
color = "#664330"
-/datum/reagent/toxin/fertilizer/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/fertilizer/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE) //cit change: fertilizer is full of natural easily digestible plant fats
M.nutrition += removed * 5
return
@@ -392,11 +366,11 @@
var/obj/structure/alien/weeds/alien_weeds = O
alien_weeds.damage_integrity(15, 35)
-/datum/reagent/toxin/plantbgone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/plantbgone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE)
M.adjustToxLoss(50 * removed)
-/datum/reagent/toxin/plantbgone/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/plantbgone/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE)
M.adjustToxLoss(50 * removed)
@@ -415,11 +389,11 @@
else if(istype(A, /mob/living/simple_mob/animal/giant_spider))
A.adjustToxLoss(5 * removed)
-/datum/reagent/toxin/pestbgone/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/pestbgone/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_APIDAEN)
M.adjustToxLoss(50 * removed)
-/datum/reagent/toxin/pestbgone/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/pestbgone/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_APIDAEN)
M.adjustToxLoss(50 * removed)
@@ -433,7 +407,7 @@
strength = 2
overdose = 20
-/datum/reagent/toxin/sifslurry/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/toxin/sifslurry/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA) // Symbiotic bacteria.
M.nutrition += strength * removed
return
@@ -441,7 +415,7 @@
M.add_modifier(/datum/modifier/slow_pulse, 30 SECONDS)
..()
-/datum/reagent/toxin/sifslurry/overdose(mob/living/carbon/M, alien, removed) // Overdose effect.
+/datum/reagent/toxin/sifslurry/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(ishuman(M))
@@ -450,8 +424,8 @@
M.afflict_radiation(RAD_MOB_AFFLICT_STRENGTH_SIFSLURRY_OD(removed))
M.apply_effect(5 * removed, DROWSY, 0, 0)
-/datum/reagent/toxin/sifslurry/affect_ingest(mob/living/carbon/M, alien, removed)
- affect_blood(M, alien, removed * 0.7)
+/datum/reagent/toxin/sifslurry/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.7, metabolism)
/datum/reagent/acid/polyacid
name = "Polytrinic acid"
@@ -473,16 +447,13 @@
power = 2
meltdose = 30
-//Solid Chlorine is alkaline, but gaseous Chlorine is acidic.
-/datum/reagent/acid/chlorine_gas
+/datum/reagent/toxin/chlorine_gas
name = "Chlorine gas"
id = "chlorinegas"
description = "A pungent yellow-green acidic gas."
taste_description = "bleach"
reagent_state = REAGENT_GAS
color = "#c5f72d"
- power = 5
- meltdose = 10
/datum/reagent/thermite/venom
name = "Pyrotoxin"
@@ -493,7 +464,7 @@
color = "#673910"
touch_met = 50
-/datum/reagent/thermite/venom/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/thermite/venom/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustFireLoss(3 * removed)
if(M.fire_stacks <= 1.5)
M.adjust_fire_stacks(0.15)
@@ -514,7 +485,7 @@
color = "#B31008"
filtered_organs = list(O_SPLEEN)
-/datum/reagent/condensedcapsaicin/venom/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/condensedcapsaicin/venom/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(prob(50))
@@ -537,7 +508,7 @@
color = "#C8A5DC"
overdose = REAGENTS_OVERDOSE
-/datum/reagent/lexorin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/lexorin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(alien == IS_SLIME)
@@ -565,15 +536,15 @@
reagent_state = REAGENT_LIQUID
color = "#13BC5E"
-/datum/reagent/mutagen/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/mutagen/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(prob(33))
- affect_blood(M, alien, removed)
+ legacy_affect_blood(M, alien, removed, metabolism)
-/datum/reagent/mutagen/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/mutagen/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(prob(67))
- affect_blood(M, alien, removed)
+ legacy_affect_blood(M, alien, removed, metabolism)
-/datum/reagent/mutagen/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/mutagen/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(M.isSynthetic())
return
@@ -659,7 +630,7 @@
reagent_state = REAGENT_LIQUID
color = "#801E28"
-/datum/reagent/slimejelly/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/slimejelly/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(alien == IS_SLIME) //Partially made of the stuff. Why would it hurt them?
@@ -682,7 +653,7 @@
reagent_state = REAGENT_LIQUID
color = "#13BC5E"
-/datum/reagent/advmutationtoxin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/advmutationtoxin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.species.get_species_id() != SPECIES_ID_PROMETHEAN)
@@ -713,11 +684,11 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#009CA8"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
ingest_met = REM * 1.5
overdose = REAGENTS_OVERDOSE
-/datum/reagent/soporific/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/soporific/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -728,9 +699,7 @@
if(alien == IS_SLIME)
threshold = 6 //Evens to 3 due to the fact they are considered 'small' for flaps.
- var/effective_dose = dose
- if(issmall(M))
- effective_dose *= 2
+ var/effective_dose = metabolism.total_processed_dose
if(effective_dose < 1 * threshold)
if(effective_dose == metabolism * 2 || prob(5))
@@ -761,12 +730,12 @@
taste_description = "bitterness"
reagent_state = REAGENT_SOLID
color = "#000067"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
ingest_met = REM * 1.5
overdose = REAGENTS_OVERDOSE * 0.5
overdose_mod = 5 //For that good, lethal feeling
-/datum/reagent/chloralhydrate/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/chloralhydrate/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -777,7 +746,7 @@
if(alien == IS_SLIME)
threshold = 6 //Evens to 3 due to the fact they are considered 'small' for flaps.
- var/effective_dose = dose
+ var/effective_dose = metabolism.total_processed_dose
if(issmall(M))
effective_dose *= 2
@@ -800,7 +769,7 @@
if(effective_dose > 1 * threshold)
M.adjustToxLoss(removed)
-/datum/reagent/chloralhydrate/overdose(mob/living/carbon/M, alien, removed)
+/datum/reagent/chloralhydrate/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
..()
M.SetLosebreath(10)
M.adjustOxyLoss(removed * overdose_mod)
@@ -826,10 +795,10 @@
taste_mult = 0.4
reagent_state = REAGENT_LIQUID
color = "#60A584"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
overdose = REAGENTS_OVERDOSE
-/datum/reagent/space_drugs/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/space_drugs/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -853,10 +822,10 @@
taste_description = "bitterness"
reagent_state = REAGENT_LIQUID
color = "#202040"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
overdose = REAGENTS_OVERDOSE
-/datum/reagent/serotrotium/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/serotrotium/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(prob(7))
@@ -870,7 +839,7 @@
taste_description = "chalky bitterness"
filtered_organs = list(O_SPLEEN)
-/datum/reagent/serotrotium/venom/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/serotrotium/venom/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
if(prob(30))
@@ -886,10 +855,10 @@
taste_description = "sourness"
reagent_state = REAGENT_LIQUID
color = "#000055"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
overdose = REAGENTS_OVERDOSE
-/datum/reagent/cryptobiolin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/cryptobiolin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
var/drug_strength = 4
@@ -913,7 +882,7 @@
overdose = REAGENTS_OVERDOSE
filtered_organs = list(O_SPLEEN)
-/datum/reagent/impedrezene/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/impedrezene/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
M.jitteriness = max(M.jitteriness - 5, 0)
@@ -931,10 +900,10 @@
taste_description = "sourness"
reagent_state = REAGENT_LIQUID
color = "#B31008"
- metabolism = REM * 0.25
+ metabolism_rate = REM * 0.25
overdose = REAGENTS_OVERDOSE
-/datum/reagent/mindbreaker/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/mindbreaker/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -955,9 +924,9 @@
taste_description = "mushroom"
color = "#E700E7"
overdose = REAGENTS_OVERDOSE
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
-/datum/reagent/psilocybin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/psilocybin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -970,8 +939,8 @@
M.druggy = max(M.druggy, 30)
- var/effective_dose = dose
- if(issmall(M)) effective_dose *= 2
+ var/effective_dose = metabolism.total_processed_dose
+
if(effective_dose < 1 * threshold)
M.apply_effect(3, STUTTER)
M.make_dizzy(5)
@@ -1008,10 +977,10 @@
taste_mult = 1.6
reagent_state = REAGENT_LIQUID
color = "#db2ed8"
- metabolism = REM * 0.5
+ metabolism_rate = REM * 0.5
overdose = REAGENTS_OVERDOSE
-/datum/reagent/talum_quem/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/talum_quem/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
@@ -1037,7 +1006,7 @@
reagent_state = REAGENT_LIQUID
color = "#13BC5E"
-/datum/reagent/slimetoxin/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/slimetoxin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(M.isSynthetic())
return
@@ -1067,7 +1036,7 @@
reagent_state = REAGENT_LIQUID
color = "#FF69B4"
-/datum/reagent/aslimetoxin/affect_blood(mob/living/carbon/M, alien, removed) // TODO: check if there's similar code anywhere else
+/datum/reagent/aslimetoxin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) // TODO: check if there's similar code anywhere else
if(M.isSynthetic())
return
@@ -1103,15 +1072,15 @@
reagent_state = REAGENT_SOLID
color = "#555555"
mrate_static = TRUE
- metabolism = 0.01 //Fast no more they stick in you for a long time
+ metabolism_rate = 0.01 //Fast no more they stick in you for a long time
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
affects_robots = TRUE
-/datum/reagent/nanite/shredding/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/shredding/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustBruteLoss(40 * removed) //Bicaridine can outheal it (unless Metabolism is less then 75%).
M.adjustOxyLoss(40 * removed)
-/datum/reagent/nanite/shredding/affect_ingest(mob/living/carbon/M, alien, removed) //Now they can be used in chemsmoke (Nanite Kill Clouds)
+/datum/reagent/nanite/shredding/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) //Now they can be used in chemsmoke (Nanite Kill Clouds)
M.adjustBruteLoss(40 * removed)
M.adjustOxyLoss(40 * removed)
@@ -1124,11 +1093,11 @@
reagent_state = REAGENT_SOLID
mrate_static = TRUE
color = "#555555"
- metabolism = 0.01 //No gimmick you just glow for longer now!
+ metabolism_rate = 0.01 //No gimmick you just glow for longer now!
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
affects_robots = TRUE
-/datum/reagent/nanite/irradiated/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/irradiated/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
// todo: this should be more brutal on people around the person without being too brutal on the person
// new radiation just kind of scales pretty badly
/// rads to everyone around you
@@ -1136,7 +1105,7 @@
/// radiate the person a bit just in case they're armored
M.rad_act(RAD_INTENSITY_CHEM_IRRADIATED_NANITES_SELF)
-/datum/reagent/nanite/irradiated/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/irradiated/affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
radiation_pulse(M, RAD_INTENSITY_CHEM_IRRADIATED_NANITES)
M.rad_act(RAD_INTENSITY_CHEM_IRRADIATED_NANITES_SELF)
@@ -1149,31 +1118,22 @@
reagent_state = REAGENT_SOLID
mrate_static = TRUE
color = "#555555"
- metabolism = 0.01
+ metabolism_rate = 0.01
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
filtered_organs = list(O_SPLEEN)
affects_robots = TRUE
-/datum/reagent/nanite/neurophage/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/neurophage/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustBrainLoss(20 * removed) // Alkysine can outheal it, however since mrate is so slow you need 5 times the Alky to fully heal through it
M.adjustBruteLoss(20 * removed)
- if(M.ingested)
- for(var/datum/reagent/alkysine/R in M.ingested.reagent_list) //Alkysine takes a while to metabolize this means the brain shield Alkysine gives you wears off quicker
- R.remove_self(removed * 10)
- if(M.bloodstr)
- for(var/datum/reagent/alkysine/R in M.bloodstr.reagent_list) //This is about triple alkysine's regular metabolization rate.
- R.remove_self(removed * 10)
-
+ M.ingested?.remove_reagent(/datum/reagent/alkysine, removed * 10)
+ M.bloodstr?.remove_reagent(/datum/reagent/alkysine, removed * 10)
-/datum/reagent/nanite/neurophage/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/nanite/neurophage/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustBrainLoss(20 * removed)
M.adjustBruteLoss(20 * removed)
- if(M.ingested)
- for(var/datum/reagent/alkysine/R in M.ingested.reagent_list)
- R.remove_self(removed * 10)
- if(M.bloodstr)
- for(var/datum/reagent/alkysine/R in M.bloodstr.reagent_list)
- R.remove_self(removed * 10)
+ M.ingested?.remove_reagent(/datum/reagent/alkysine, removed * 10)
+ M.bloodstr?.remove_reagent(/datum/reagent/alkysine, removed * 10)
/datum/reagent/nanite/heartkill
name = "Heartkill Nanites"
@@ -1184,21 +1144,20 @@
reagent_state = REAGENT_SOLID
mrate_static = TRUE
color = "#555555"
- metabolism = 0.01
+ metabolism_rate = 0.01
-/datum/reagent/nanite/heartkill/affect_ingest(mob/living/carbon/M, alien, removed) //Damage handled using the heart_attack() proc in heart.dm
+/datum/reagent/nanite/heartkill/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) //Damage handled using the heart_attack() proc in heart.dm
var/mob/living/carbon/human/H = M
if(istype(H))
if(prob(5))
H.heart_attack()
-/datum/reagent/nanite/heartkill/affect_blood(mob/living/carbon/M, alien, removed)//Damage handled using the heart_attack() proc in heart.dm
+/datum/reagent/nanite/heartkill/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)//Damage handled using the heart_attack() proc in heart.dm
var/mob/living/carbon/human/H = M
if(istype(H))
if(prob(5))
H.heart_attack()
-
//Special toxins for solargrubs
//Moved from Chemistry-Reagents-Vore_vr.dm
/datum/reagent/grubshock
@@ -1207,30 +1166,25 @@
description = "A liquid that quickly dissapates to deliver a painful shock. It can also disable nanomachines in the body."
reagent_state = REAGENT_LIQUID
color = "#E4EC2F"
- metabolism = 2.50
+ metabolism_rate = 2.50
affects_robots = TRUE
-/datum/reagent/grubshock/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/grubshock/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.electrocute_act(10 * removed, src, 1.0, BP_TORSO, 0)
- if(M.ingested)
- for(var/datum/reagent/nanite/R in M.ingested.reagent_list)
- R.remove_self(removed * 30)
- if(M.bloodstr)
- for(var/datum/reagent/nanite/R in M.bloodstr.reagent_list)
- R.remove_self(removed * 20)
-
+ for(var/datum/reagent/nanite/path_check in M.ingested.get_reagent_datums())
+ M.ingested.remove_reagent(path_check.id, removed * 30)
+ for(var/datum/reagent/nanite/path_check in M.bloodstr.get_reagent_datums())
+ M.bloodstr.remove_reagent(path_check.id, removed * 30)
-/datum/reagent/grubshock/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/grubshock/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.electrocute_act(10 * removed, src, 1.0, BP_TORSO, 0)
- if(M.ingested)
- for(var/datum/reagent/nanite/R in M.ingested.reagent_list)
- R.remove_self(removed * 30)
- if(M.bloodstr)
- for(var/datum/reagent/nanite/R in M.bloodstr.reagent_list)
- R.remove_self(removed * 20)
+ for(var/datum/reagent/nanite/path_check in M.ingested.get_reagent_datums())
+ M.ingested.remove_reagent(path_check.id, removed * 30)
+ for(var/datum/reagent/nanite/path_check in M.bloodstr.get_reagent_datums())
+ M.bloodstr.remove_reagent(path_check.id, removed * 30)
-/datum/reagent/grubshock/affect_touch(mob/living/carbon/M, alien, removed)
- M.electrocute_act(10 * volume, src, 1.0, BP_TORSO, 0)
+/datum/reagent/grubshock/affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ M.electrocute_act(10 * removed, src, 1.0, BP_TORSO, 0)
//This chem is for removing nanites without grubmeat
/datum/reagent/lessershock
@@ -1239,30 +1193,26 @@
description = "A liquidified Lithium-Iron-Phosphate battery. Can be used to deliver shocks to the body in order to counter hostile nanomachines."
reagent_state = REAGENT_SOLID
color = "#E4EC2F"
- metabolism = 2.50
+ metabolism_rate = 2.50
affects_robots = TRUE
-/datum/reagent/lessershock/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/lessershock/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.electrocute_act(5 * removed, src, 1.0, BP_TORSO, 0)
- if(M.ingested)
- for(var/datum/reagent/nanite/R in M.ingested.reagent_list)
- R.remove_self(removed)
- if(M.bloodstr)
- for(var/datum/reagent/nanite/R in M.bloodstr.reagent_list)
- R.remove_self(removed)
-
-/datum/reagent/lessershock/affect_ingest(mob/living/carbon/M, alien, removed)
+ for(var/datum/reagent/nanite/path_check in M.ingested.get_reagent_datums())
+ M.ingested.remove_reagent(path_check.id, removed)
+ for(var/datum/reagent/nanite/path_check in M.bloodstr.get_reagent_datums())
+ M.bloodstr.remove_reagent(path_check.id, removed)
+
+/datum/reagent/lessershock/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.electrocute_act(5 * removed, src, 1.0, BP_TORSO, 0)
- if(M.ingested)
- for(var/datum/reagent/nanite/R in M.ingested.reagent_list)
- R.remove_self(removed)
- if(M.bloodstr)
- for(var/datum/reagent/nanite/R in M.bloodstr.reagent_list)
- R.remove_self(removed)
+ for(var/datum/reagent/nanite/path_check in M.ingested.get_reagent_datums())
+ M.ingested.remove_reagent(path_check.id, removed)
+ for(var/datum/reagent/nanite/path_check in M.bloodstr.get_reagent_datums())
+ M.bloodstr.remove_reagent(path_check.id, removed)
-/datum/reagent/lessershock/affect_touch(mob/living/carbon/M, alien, removed)
- M.electrocute_act(5 * volume, src, 1.0, BP_TORSO, 0)
+/datum/reagent/lessershock/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ M.electrocute_act(5 * removed, src, 1.0, BP_TORSO, 0)
/datum/reagent/asbestos //asbestos removal handled in under datum/reagent/respirodaxon
name = "Asbestos"
@@ -1271,15 +1221,15 @@
taste_description = "sharp dust"
reagent_state = REAGENT_SOLID
color = "#d8d6d6"
- metabolism = 0.01 // Does not leave your system easily
+ metabolism_rate = 0.01 // Does not leave your system easily
mrate_static = TRUE
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
overdose = 10
-/datum/reagent/asbestos/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/asbestos/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.ingested.add_reagent(/datum/reagent/asbestos, 0.02) //No idea what to do with injected asbestos I will simply say it goes to your lungs faster.
-/datum/reagent/asbestos/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/asbestos/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
var/mob/living/carbon/human/H = M
if(prob(2))
M.emote("cough")
@@ -1315,14 +1265,14 @@
taste_mult = 0 //It would be a rather bad if you could taste this poison
reagent_state = REAGENT_SOLID
color = "#A6FAFF"
- metabolism = 0.01 //This is around 100 radiation a tick for a total of 5k radiaion a unit. Use stasis!!!
+ metabolism_rate = 0.01 //This is around 100 radiation a tick for a total of 5k radiaion a unit. Use stasis!!!
mrate_static = TRUE
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
-/datum/reagent/polonium/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/polonium/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.afflict_radiation(RAD_MOB_AFFLICT_STRENGTH_POL210(removed))
-/datum/reagent/polonium/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/polonium/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.afflict_radiation(RAD_MOB_AFFLICT_STRENGTH_POL210(removed))
/datum/reagent/polonium/touch_turf(turf/T)
@@ -1340,10 +1290,10 @@
taste_description = "burning alcohol"
color = "#404030"
taste_mult = 0.1
- metabolism = 0.01
+ metabolism_rate = 0.01
mrate_static = TRUE
reagent_filter_flags = REAGENT_FILTER_NO_COMMON_BIOANALYSIS
-/datum/reagent/superhol/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/superhol/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.bloodstr.add_reagent(/datum/reagent/ethanol, removed * 200) //Two Ethanol a tick enough so the drunkeness hits slow enough to be semi plausible
M.add_chemical_effect(CE_ALCOHOL_TOXIC, 5)
diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Topical.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Topical.dm
index 76fe7a487c03..4912da0706a4 100644
--- a/code/modules/reagents/chemistry/reagents/Chemistry-Topical.dm
+++ b/code/modules/reagents/chemistry/reagents/Chemistry-Topical.dm
@@ -8,7 +8,7 @@
taste_description = "Sourness"
taste_mult = 2
- metabolism = REM / 2//The skin is not directly connected to any filter organs so reagents are removed much slower
+ metabolism_rate = REM / 2//The skin is not directly connected to any filter organs so reagents are removed much slower
overdose = REAGENTS_OVERDOSE //Usually we want topicals to overdose just as other chems do.
overdose_mod = 0.5 // deals little damage on overdose, because the chem should only be applied via touch.
@@ -19,19 +19,21 @@
var/toxicity = 1//factor of toxin damage dealt by improper application
-/datum/reagent/topical/affect_blood(mob/living/carbon/M, alien, removed)
- if(alien != IS_DIONA)
- M.adjustToxLoss(toxicity * removed)//if injected cause toxin damage
-
-/datum/reagent/topical/affect_ingest(mob/living/carbon/M, alien, removed)
- if(alien != IS_DIONA)
- if(prob(10) && toxicity)//If ingested, either throw up
- M.vomit(1)
- else//or half the reagent passes through into blood(rest is filtered off) and alittle bit affects the inner skin
- affect_blood(M, alien, removed/2)
- affect_touch(M, alien, removed/10)
-
-/datum/reagent/topical/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(alien == IS_DIONA)
+ return
+ M.adjustToxLoss(toxicity * removed)//if injected cause toxin damage
+
+/datum/reagent/topical/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(alien == IS_DIONA)
+ return
+ if(prob(10) && toxicity)//If ingested, either throw up
+ M.vomit(1)
+ else//or half the reagent passes through into blood(rest is filtered off) and alittle bit affects the inner skin
+ legacy_affect_blood(M, alien, removed/2, metabolism)
+ legacy_affect_touch(M, alien, removed/10, metabolism)
+
+/datum/reagent/topical/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.ceiling_chemical_effect(CE_PAINKILLER, 1)//just so there is something here...
/datum/reagent/topical/bicarilaze
@@ -42,7 +44,7 @@
toxicity = 3
-/datum/reagent/topical/bicarilaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/bicarilaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.heal_organ_damage(6*removed,0)//Heal brute damage
@@ -54,7 +56,7 @@
toxicity = 2
-/datum/reagent/topical/kelotalaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/kelotalaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.heal_organ_damage(0,6*removed)//Heal burns
@@ -65,7 +67,7 @@
toxicity = 0
-/datum/reagent/topical/tricoralaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/tricoralaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.heal_organ_damage(3*removed,3*removed)//Heal both damage
@@ -76,7 +78,7 @@
toxicity = 0
-/datum/reagent/topical/inaprovalaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/inaprovalaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.add_chemical_effect(CE_STABLE, 20)//Reduces bleeding rate, and allowes the patient to breath even when in shock
M.ceiling_chemical_effect(CE_PAINKILLER, 40)
@@ -96,14 +98,14 @@
name = "Neurolaze"
id = "neurolaze"
description = "Superficial painkiller, do not inject or ingest"
- metabolism = REM * 2 //Nervocells absorb this chem super fast so much faster metabolism...
+ metabolism_rate = REM * 2 //Nervocells absorb this chem super fast so much faster metabolism...
overdose = REAGENTS_OVERDOSE * 0.5
color = "#000000"
toxicity = 5
-/datum/reagent/topical/neurolaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/neurolaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.ceiling_chemical_effect(CE_PAINKILLER, 100)//Half oxycodone
M.make_jittery(50*removed)//Your nerves are itching
@@ -112,20 +114,20 @@
if(prob(5))// Speed boost and emotes
M.emote(pick("twitch", "blink_r", "shiver"))
if(world.time > (data + (60*10)))
- data = world.time
+ metabolism.blackboard["last-message"] = world.time
to_chat(M, "You feel like all your nerves are itching.")
-/datum/reagent/topical/neurolaze/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/neurolaze/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.apply_damage(5 * removed, DAMAGE_TYPE_HALLOSS)//holodeck boxing glove damage
M.make_jittery(200)
-/datum/reagent/topical/neurolaze/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/neurolaze/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.vomit()
holder.remove_reagent("neurolaze", 10 * removed)//purges itself...
-/datum/reagent/topical/neurolaze/overdose(mob/living/carbon/M, alien)
+/datum/reagent/topical/neurolaze/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.adjustBrainLoss(0.1)//deals braindamage on overdose
@@ -136,7 +138,7 @@
toxicity = 3
-/datum/reagent/topical/sterilaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/sterilaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(ishuman(M))
var/mob/living/carbon/human/H = M
for(var/obj/item/organ/external/O in H.bad_external_organs)
@@ -150,7 +152,7 @@
toxicity = 1
-/datum/reagent/topical/cleansalaze/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/cleansalaze/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.cure_radiation(RAD_MOB_CURE_STRENGTH_CLEANSALAZE(removed))
@@ -161,12 +163,11 @@
toxicity = 0
-/datum/reagent/topical/lotion/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/topical/lotion/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if (alien != IS_DIONA)
M.ceiling_chemical_effect(CE_PAINKILLER, 5)//Not really usefull but I guess a lotion would help alittle with pain
- if(world.time > (data + (5*60*10)))
- data = world.time
- to_chat(M, "Your skin feels refreshed and sooth.")
+ if(prob(1))
+ to_chat(M, "Your skin feels refreshed and soothed.")
//Remove before merge
/obj/item/storage/box/touch_bottles
diff --git a/code/modules/reagents/chemistry/reagents/core/acid.dm b/code/modules/reagents/chemistry/reagents/core/acid.dm
new file mode 100644
index 000000000000..c16eaffa43cf
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/core/acid.dm
@@ -0,0 +1,88 @@
+/datum/reagent/acid
+ name = "Sulphuric acid"
+ id = "sacid"
+ description = "A very corrosive mineral acid with the molecular formula H2SO4."
+ taste_description = "acid"
+ reagent_state = REAGENT_LIQUID
+ color = "#DB5008"
+ metabolism_rate = REM * 2
+ touch_met = 50 // It's acid!
+ var/power = 5
+ var/meltdose = 10 // How much is needed to melt
+
+/datum/reagent/acid/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(issmall(M)) removed *= 2
+ M.take_random_targeted_damage(brute = 0, brute = removed * power * 2)
+
+/datum/reagent/acid/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) // This is the most interesting
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.head)
+ if(H.head.integrity_flags & INTEGRITY_ACIDPROOF)
+ to_chat(H, "Your [H.head] protects you from the acid.")
+ remove_self(volume)
+ return
+ else if(removed > meltdose)
+ to_chat(H, "Your [H.head] melts away!")
+ qdel(H.head)
+ H.update_inv_head(1)
+ H.update_hair(1)
+ removed -= meltdose
+ if(removed <= 0)
+ return
+
+ if(H.wear_mask)
+ if(H.wear_mask.integrity_flags & INTEGRITY_ACIDPROOF)
+ to_chat(H, "Your [H.wear_mask] protects you from the acid.")
+ remove_self(volume)
+ return
+ else if(removed > meltdose)
+ to_chat(H, "Your [H.wear_mask] melts away!")
+ qdel(H.wear_mask)
+ H.update_inv_wear_mask(1)
+ H.update_hair(1)
+ removed -= meltdose
+ if(removed <= 0)
+ return
+
+ if(H.glasses)
+ if(H.glasses.integrity_flags & INTEGRITY_ACIDPROOF)
+ to_chat(H, "Your [H.glasses] partially protect you from the acid!")
+ removed /= 2
+ else if(removed > meltdose)
+ to_chat(H, "Your [H.glasses] melt away!")
+ qdel(H.glasses)
+ H.update_inv_glasses(1)
+ removed -= meltdose / 2
+ if(removed <= 0)
+ return
+
+ if(volume < meltdose) // Not enough to melt anything
+ M.take_random_targeted_damage(brute = 0, brute = removed * power * 0.2) //burn damage, since it causes chemical burns. Acid doesn't make bones shatter, like brute trauma would.
+ return
+ if(!M.unacidable && removed > 0)
+ if(istype(M, /mob/living/carbon/human) && volume >= meltdose)
+ var/mob/living/carbon/human/H = M
+ var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD)
+ if(affecting)
+ affecting.inflict_bodypart_damage(
+ burn = removed * power * 0.1,
+ )
+ if(prob(100 * removed / meltdose)) // Applies disfigurement
+ if (affecting.organ_can_feel_pain())
+ H.emote("scream")
+ else
+ M.take_random_targeted_damage(brute = 0, brute = removed * power * 0.1) // Balance. The damage is instant, so it's weaker. 10 units -> 5 damage, double for pacid. 120 units beaker could deal 60, but a) it's burn, which is not as dangerous, b) it's a one-use weapon, c) missing with it will splash it over the ground and d) clothes give some protection, so not everything will hit
+
+/datum/reagent/acid/touch_obj(obj/O)
+ if(O.integrity_flags & INTEGRITY_INDESTRUCTIBLE)
+ return
+ // todo: newacid
+ if((istype(O, /obj/item) || istype(O, /obj/effect/plant)) && (volume > meltdose))
+ var/obj/effect/debris/cleanable/molten_item/I = new/obj/effect/debris/cleanable/molten_item(O.loc)
+ I.desc = "Looks like this was \an [O] some time ago."
+ for(var/mob/M in viewers(5, O))
+ to_chat(M, "\The [O] melts.")
+ qdel(O)
+ remove_self(meltdose) // 10 units of acid will not melt EVERYTHING on the tile
+
diff --git a/code/modules/reagents/chemistry/reagents/core/blood.dm b/code/modules/reagents/chemistry/reagents/core/blood.dm
new file mode 100644
index 000000000000..18dbccf5a496
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/core/blood.dm
@@ -0,0 +1,167 @@
+// todo: stuff in this file might need to be moved somewhere else, this being in reagents/core is weird.
+
+/**
+ * Blood.
+ *
+ * I'm not sure what you expected this to say.
+ *
+ * * `data_initializer`: `/datum/blood_mixture` instance
+ * * `data`: /datum/blood_mixture instance
+ */
+/datum/reagent/blood
+ name = "Blood"
+ id = "blood"
+ taste_description = "iron"
+ taste_mult = 1.3
+ reagent_state = REAGENT_LIQUID
+ metabolism_rate = REM * 5
+ mrate_static = TRUE
+ affects_dead = 1 //so you can pump blood into someone before defibbing them
+ color = "#A80000"
+ holds_data = TRUE
+ blood_content = 4 //How effective this is for vampires.
+
+ glass_name = "tomato juice"
+ glass_desc = "Are you sure this is tomato juice?"
+ var/volume_mod = 1 // So if you add different subtypes of blood, you can affect how much vessel blood each unit of reagent adds
+
+/datum/reagent/blood/make_copy_data_initializer(datum/blood_mixture/data)
+ return data
+
+/datum/reagent/blood/mix_data(datum/blood_mixture/old_data, old_volume, datum/blood_mixture/new_data, new_volume, datum/reagent_holder/holder)
+ . = ..()
+
+ #warn impl ; hard limit of 10 blood instances. also, dedupe.
+
+/datum/reagent/blood/on_touch_turf(turf/target, remaining, allocated, data)
+ . = ..()
+
+/datum/reagent/blood/
+
+/datum/reagent/blood/touch_turf(turf/simulated/T)
+ if(!istype(T) || volume < 3)
+ return
+ if(!data["donor"] || istype(data["donor"], /mob/living/carbon/human))
+ blood_splatter(T, src, 1)
+ else if(istype(data["donor"], /mob/living/carbon/alien))
+ var/obj/effect/debris/cleanable/blood/B = blood_splatter(T, src, 1)
+ if(B)
+ B.blood_DNA["UNKNOWN DNA STRUCTURE"] = "X*"
+
+/datum/reagent/blood/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+
+ var/effective_dose = dose
+ if(issmall(M))
+ effective_dose *= 2
+
+ var/nutritionvalue = 10 //for reference, normal nutrition has a value of about 30.
+ var/is_vampire = M.species.is_vampire
+ switch(alien) //unique interactions sorted from the species who benefit the least to the species who benefit the most.
+ if(IS_SKRELL) //arguing that blood is "meat" and is still toxic for the vegan skrell at least
+ if(effective_dose > 5)
+ if(!is_vampire) //a vetalan skrell sounds funny as hell
+ M.adjustToxLoss(removed)
+ if(effective_dose > 15)
+ if(!is_vampire)
+ M.adjustToxLoss(removed)
+ if(IS_SLIME)
+ nutritionvalue = 20
+ if(data["species"] == M.species.name) //just 'inject' the blood if it happens to be promethean "blood".
+ M.inject_blood(src, volume * volume_mod)
+ remove_self(volume)
+ return
+ if(IS_TESHARI) //birb.
+ nutritionvalue = 30
+ if(IS_UNATHI) //carnivorous lizord...
+ nutritionvalue = 45
+ if(IS_ALRAUNE) //lorewise, alraune are meant to enjoy blood.
+ nutritionvalue = 60
+ if(IS_CHIMERA) //obligate carnivores.
+ nutritionvalue = 80
+
+ if(is_vampire)
+ handle_vampire(M, alien, removed, is_vampire)
+ M.heal_organ_damage(0.7 * removed * volume_mod, 0) // Heals vampires more.
+ M.adjust_hydration(7 * removed) // Hydrates vetalan better.
+ M.add_chemical_effect(CE_BLOODRESTORE, 8 * removed) // Same rating as taking iron
+ else
+ M.adjust_nutrition(nutritionvalue * removed * volume_mod)
+ M.adjust_hydration(2 * removed) // Still has some water in the form of plasma. Hydrates less than a normal drink.
+ M.add_chemical_effect(CE_BLOODRESTORE, 4 * removed) //same rating as eating nutriment
+ if(effective_dose >= 20 && prob(10))
+ M.vomit(FALSE, FALSE) //Drinking blood makes you vomit, due to the high iron content and unpleasant consistency
+
+ if(data && data["virus2"])
+ var/list/vlist = data["virus2"]
+ if(vlist.len)
+ for(var/ID in vlist)
+ var/datum/disease2/disease/V = vlist[ID]
+ if(V.spreadtype == "Contact")
+ infect_virus2(M, V.getcopy())
+
+/datum/reagent/blood/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.isSynthetic())
+ return
+ if(alien == IS_SLIME)
+ legacy_affect_ingest(M, alien, removed, metabolism)
+ return
+ if(data && data["virus2"])
+ var/list/vlist = data["virus2"]
+ if(vlist.len)
+ for(var/ID in vlist)
+ var/datum/disease2/disease/V = vlist[ID]
+ if(V.spreadtype == "Contact")
+ infect_virus2(M, V.getcopy())
+ if(data && data["antibodies"])
+ M.antibodies |= data["antibodies"]
+
+/datum/reagent/blood/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(alien == IS_SLIME) //They don't have blood, so it seems weird that they would instantly 'process' the chemical like another species does.
+ legacy_affect_ingest(M, alien, removed, metabolism)
+ return
+
+ if(M.isSynthetic())
+ return
+
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+
+ var/datum/reagent/blood/recipient = H.get_blood(H.vessel)
+
+ if(recipient && blood_incompatible(data["blood_type"], recipient.data["blood_type"], data["species"], recipient.data["species"]))
+ H.inject_blood(src, removed * volume_mod)
+
+ if(!H.isSynthetic() && data["species"] == "synthetic") // Remember not to inject oil into your veins, it's bad for you.
+ H.reagents.add_reagent("toxin", removed * 1.5)
+
+ return
+
+ M.inject_blood(src, volume * volume_mod)
+ remove_self(volume)
+
+// todo: this should be the same as /datum/reagent/blood!
+/datum/reagent/blood/synthblood
+ name = "Synthetic blood"
+ id = "synthblood"
+ color = "#999966"
+ volume_mod = 2
+
+/datum/reagent/blood/synthblood/initialize_data(newdata)
+ ..()
+ if(data && !data["blood_type"])
+ data["blood_type"] = "O-"
+
+// todo: this should be the same as /datum/reagent/blood!
+/datum/reagent/blood/bludbloodlight
+ name = "Synthetic blood"
+ id = "bludbloodlight"
+ color = "#999966"
+ volume_mod = 2
+
+// todo: this should be the same as /datum/reagent/blood!
+/datum/reagent/blood/bludbloodlight/initialize_data(newdata)
+ ..()
+ if(data && !data["blood_type"])
+ data["blood_type"] = "AB+"
diff --git a/code/modules/reagents/chemistry/reagents/core/elements.dm b/code/modules/reagents/chemistry/reagents/core/elements.dm
index afcd425c111c..b20dfad06d44 100644
--- a/code/modules/reagents/chemistry/reagents/core/elements.dm
+++ b/code/modules/reagents/chemistry/reagents/core/elements.dm
@@ -26,12 +26,12 @@
color = "#1C1300"
ingest_met = REM * 5
-/datum/reagent/carbon/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/carbon/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_DIONA)
return
- if(M.ingested && M.ingested.reagent_list.len > 1) // Need to have at least 2 reagents - cabon and something to remove
- var/effect = 1 / (M.ingested.reagent_list.len - 1)
- for(var/datum/reagent/R in M.ingested.reagent_list)
+ if(length(M.ingested.reagent_volumes) > 1)
+ var/effect = 1 / (length(M.ingested.reagent_volumes) - 1)
+ for(var/datum/reagent/R in M.ingested.get_reagent_datums())
if(R == src)
continue
M.ingested.remove_reagent(R.id, removed * effect)
@@ -53,10 +53,10 @@
reagent_state = REAGENT_GAS
color = "#d1db77"
-/datum/reagent/chlorine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/chlorine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.take_random_targeted_damage(brute = 1*REM, brute = 0)
-/datum/reagent/chlorine/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/chlorine/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.take_random_targeted_damage(brute = 1*REM, brute = 0)
/datum/reagent/copper
@@ -74,10 +74,10 @@
reagent_state = REAGENT_GAS
color = "#808080"
-/datum/reagent/fluorine/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/fluorine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustToxLoss(removed)
-/datum/reagent/fluorine/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/fluorine/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustToxLoss(removed)
/datum/reagent/hydrogen
@@ -96,7 +96,7 @@
reagent_state = REAGENT_SOLID
color = "#353535"
-/datum/reagent/iron/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/iron/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
M.add_chemical_effect(CE_BLOODRESTORE, 8 * removed)
@@ -108,7 +108,7 @@
reagent_state = REAGENT_SOLID
color = "#808080"
-/datum/reagent/lithium/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/lithium/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
if(CHECK_MOBILITY(M, MOBILITY_CAN_MOVE) && istype(M.loc, /turf/space))
step(M, pick(GLOB.cardinal))
@@ -123,7 +123,7 @@
reagent_state = REAGENT_LIQUID
color = "#484848"
-/datum/reagent/mercury/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/mercury/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien != IS_DIONA)
if(CHECK_MOBILITY(M, MOBILITY_CAN_MOVE) && istype(M.loc, /turf/space))
step(M, pick(GLOB.cardinal))
@@ -147,7 +147,7 @@
reagent_state = REAGENT_GAS
color = "#808080"
-/datum/reagent/oxygen/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/oxygen/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_VOX)
M.adjustToxLoss(removed * 3)
@@ -159,7 +159,7 @@
reagent_state = REAGENT_SOLID
color = "#832828"
-/datum/reagent/phosphorus/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/phosphorus/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_ALRAUNE)
M.nutrition += removed * 2 //cit change - phosphorus is good for plants
@@ -179,7 +179,7 @@
reagent_state = REAGENT_SOLID
color = "#C7C7C7"
-/datum/reagent/radium/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/radium/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(issmall(M))
removed *= 2
M.afflict_radiation(RAD_MOB_AFFLICT_STRENGTH_RADIUM(removed))
diff --git a/code/modules/reagents/chemistry/reagents/core/ethanol.dm b/code/modules/reagents/chemistry/reagents/core/ethanol.dm
new file mode 100644
index 000000000000..b292ce39c89b
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/core/ethanol.dm
@@ -0,0 +1,155 @@
+#define ETHANOL_MET_DIVISOR 20
+
+/datum/reagent/ethanol
+ name = "Ethanol" //Parent class for all alcoholic reagents.
+ id = "ethanol"
+ description = "A well-known alcohol with a variety of applications."
+ taste_description = "pure alcohol"
+ reagent_state = REAGENT_LIQUID
+ color = "#404030"
+
+ metabolism_rate = REM/ETHANOL_MET_DIVISOR
+
+ ingest_met = REM * 5
+
+ var/nutriment_factor = 0
+ var/hydration_factor = 0
+ var/proof = 200
+ var/toxicity = 1
+
+ var/druggy = 0
+ var/adj_temp = 0
+ var/targ_temp = 310
+ var/halluci = 0
+
+ glass_name = "ethanol"
+ glass_desc = "A well-known alcohol with a variety of applications."
+
+/datum/reagent/ethanol/touch_mob(mob/living/L, amount)
+ if(istype(L))
+ L.adjust_fire_stacks(amount / 15)
+
+#define ABV (proof/200)
+
+/datum/reagent/ethanol/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) //This used to do just toxin. That's boring. Let's make this FUN.
+ var/strength_mod = 1 //Alcohol is 3x stronger when injected into the veins.
+ if(alien == IS_SKRELL)
+ strength_mod *= 5
+ if(alien == IS_TAJARA)
+ strength_mod *= 1.25
+ if(alien == IS_UNATHI)
+ strength_mod *= 0.75
+ if(alien == IS_DIONA)
+ strength_mod = 0
+ if(alien == IS_SLIME)
+ strength_mod *= 2
+ if(alien == IS_ALRAUNE)
+ if(prob(5))
+ to_chat(M, "You feel your leaves start to wilt.")
+ strength_mod *=5 //cit change - alcohol ain't good for plants
+
+ var/effective_dose = volume * strength_mod * ABV * min(1,dose*(ETHANOL_MET_DIVISOR/10)) // give it 50 ticks to ramp up
+ M.add_chemical_effect(CE_ALCOHOL, 1)
+ if(HAS_TRAIT(M, TRAIT_ALCOHOL_INTOLERANT))
+ if(proof > 0)
+ var/intolerant_dose = strength_mod*removed*ABV*10
+ if(prob((intolerant_dose)))
+ M.add_chemical_effect(CE_ALCOHOL_TOXIC, 1)
+ M.adjustToxLoss(intolerant_dose)
+ return 0
+ #define DOSE_LEVEL 6
+ var/effect_level=round(effective_dose/DOSE_LEVEL)
+ if(effect_level != metabolism.blackboard["last-effect-level"])
+ var/lowering=(metabolism.blackboard["last-effect-level"]>effect_level)
+ metabolism.blackboard["last-effect-level"]=effect_level
+ if(lowering)
+ switch(effect_level)
+ if(0)
+ to_chat(M,SPAN_NOTICE("You no longer feel under the influence."))
+ if(1)
+ to_chat(M,SPAN_DANGER("You are no longer slurring your words as much."))
+ if(2)
+ to_chat(M,SPAN_DANGER("You're not seeing double anymore."))
+ if(3)
+ to_chat(M,SPAN_DANGER("You can walk straight again."))
+ if(4)
+ to_chat(M,SPAN_DANGER("You no longer feel like you're going to puke."))
+ if(5)
+ to_chat(M,SPAN_DANGER("You don't feel like you're going to pass out anymore."))
+ if(6)
+ to_chat(M,SPAN_DANGER("You feel like you're out of the danger zone."))
+ else
+ var/hydration_str=""
+ if(M.hydration<250)
+ hydration_str=" You're feeling a little dehydrated, too."
+ switch(effect_level)
+ if(1)
+ to_chat(M,SPAN_DANGER("You're starting to feel a little tipsy.[hydration_str]"))
+ M.dizziness=max(M.dizziness,150)
+ if(2)
+ to_chat(M,SPAN_DANGER("You're getting drunk.[hydration_str] Use the Feign Impairment verb if you want slurring."))
+ if(3)
+ to_chat(M,SPAN_DANGER("You're seeing double![hydration_str]"))
+ M.eye_blurry=max(M.eye_blurry,30)
+ if(4)
+ to_chat(M,SPAN_DANGER("You can barely walk straight![hydration_str]"))
+ if(5)
+ to_chat(M,SPAN_USERDANGER("You feel like you might puke...[hydration_str]"))
+ if(6)
+ to_chat(M,SPAN_USERDANGER("Your eyelids feel heavy![hydration_str]"))
+ if(7)
+ to_chat(M,SPAN_USERDANGER("You are getting dangerously drunk![hydration_str]"))
+ var/hydration_removal=(clamp((M.hydration-150)/300,0,1)*effect_level) + max(0,(M.hydration-450)/300)
+ if(hydration_removal>0)
+ M.adjust_hydration(-hydration_removal)
+ volume-=removed*hydration_removal*3
+ if(effect_level>=4 && prob(effect_level-2))
+ M.Confuse(60)
+ if(effect_level>=5 && prob(effect_level-4) && !M.lastpuke)
+ M.vomit(1,0)
+ if(M.nutrition>=100)
+ volume-=DOSE_LEVEL/4
+ if(effect_level>=6 && prob(effect_level-5))
+ M.drowsyness=max(M.drowsyness,60)
+ if(effect_level>=7)
+ M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity*strength_mod)
+ if(volume>DOSE_LEVEL*7)
+ volume-=REM // liver working overtime, or whatever (mostly to prevent people from always just dying from this)
+ #undef DOSE_LEVEL
+ return
+
+/datum/reagent/ethanol/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(issmall(M)) removed *= 2
+ M.adjust_nutrition(nutriment_factor * removed)
+ M.adjust_hydration(hydration_factor * removed)
+ M.bloodstr.add_reagent("ethanol", removed * ABV)
+ if(druggy != 0)
+ M.druggy = max(M.druggy, druggy)
+
+ if(adj_temp > 0 && M.bodytemperature < targ_temp) // 310 is the normal bodytemp. 310.055
+ M.bodytemperature = min(targ_temp, M.bodytemperature + (adj_temp * TEMPERATURE_DAMAGE_COEFFICIENT))
+ if(adj_temp < 0 && M.bodytemperature > targ_temp)
+ M.bodytemperature = min(targ_temp, M.bodytemperature - (adj_temp * TEMPERATURE_DAMAGE_COEFFICIENT))
+
+ if(halluci)
+ M.setHallucination(max(M.hallucination, halluci))
+ return
+
+/datum/reagent/ethanol/touch_obj(obj/O)
+ if(istype(O, /obj/item/paper))
+ var/obj/item/paper/paperaffected = O
+ paperaffected.clearpaper()
+ to_chat(usr, "The solution dissolves the ink on the paper.")
+ return
+ if(istype(O, /obj/item/book))
+ if(volume < 5)
+ return
+ if(istype(O, /obj/item/book/tome))
+ to_chat(usr, "The solution does nothing. Whatever this is, it isn't normal ink.")
+ return
+ var/obj/item/book/affectedbook = O
+ affectedbook.dat = null
+ to_chat(usr, "The solution dissolves the ink on the book.")
+ return
+
+#undef ABV
diff --git a/code/modules/reagents/chemistry/reagents/core/toxin.dm b/code/modules/reagents/chemistry/reagents/core/toxin.dm
new file mode 100644
index 000000000000..f1d6d8994e53
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/core/toxin.dm
@@ -0,0 +1,27 @@
+// todo: replace with reagent effects
+/datum/reagent/toxin
+ name = "toxin"
+ id = "toxin"
+ description = "A toxic chemical."
+ taste_description = "bitterness"
+ taste_mult = 1.2
+ reagent_state = REAGENT_LIQUID
+ color = "#CF3600"
+ metabolism_rate = REM * 0.25 // 0.05 by default. Hopefully enough to get some help, or die horribly, whatever floats your boat
+ filtered_organs = list(O_LIVER, O_KIDNEYS)
+ var/strength = 4 // How much damage it deals per unit
+ var/skin_danger = 0.2 // The multiplier for how effective the toxin is when making skin contact.
+
+/datum/reagent/toxin/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(strength && alien != IS_DIONA)
+ if(issmall(M)) removed *= 2 // Small bodymass, more effect from lower volume.
+ if(alien == IS_SLIME)
+ removed *= 0.25 // Results in half the standard tox as normal. Prometheans are 'Small' for flaps.
+ if(metabolism.total_processed_dose >= 10)
+ M.nutrition += strength * removed //Body has to deal with the massive influx of toxins, rather than try using them to repair.
+ else
+ M.heal_organ_damage((10/strength) * removed, (10/strength) * removed) //Doses of toxins below 10 units, and 10 strength, are capable of providing useful compounds for repair.
+ M.adjustToxLoss(strength * removed)
+
+/datum/reagent/toxin/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed * 0.2, metabolism)
diff --git a/code/modules/reagents/chemistry/reagents/core/water.dm b/code/modules/reagents/chemistry/reagents/core/water.dm
new file mode 100644
index 000000000000..bc41adf61509
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/core/water.dm
@@ -0,0 +1,79 @@
+/// How much heat is removed when applied to a hot turf, in J/unit (19000 makes 120 u of water roughly equivalent to 4L)
+#define WATER_LATENT_HEAT 19000
+/datum/reagent/water
+ name = "Water"
+ id = "water"
+ taste_description = "water"
+ description = "A ubiquitous chemical substance that is composed of hydrogen and oxygen."
+ reagent_state = REAGENT_LIQUID
+ color = "#0064C877"
+ metabolism_rate = REM * 10
+
+ glass_name = "water"
+ glass_desc = "The father of all refreshments."
+
+ cup_name = "water"
+ cup_desc = "The father of all refreshments."
+
+/datum/reagent/water/on_touch_turf(turf/target, remaining, allocated, data)
+ . = ..()
+
+ var/datum/gas_mixture/environment = target.return_air()
+ var/min_temperature = T0C + 100 // 100C, the boiling point of water
+
+ var/hotspot = (locate(/atom/movable/fire) in target)
+ if(hotspot && !istype(target, /turf/space))
+ var/datum/gas_mixture/lowertemp = target.remove_cell_volume()
+ lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), 0)
+ lowertemp.react()
+ target.assume_air(lowertemp)
+ qdel(hotspot)
+
+ if (environment && environment.temperature > min_temperature) // Abstracted as steam or something
+ var/removed_heat = between(0, allocated * WATER_LATENT_HEAT, -environment.get_thermal_energy_change(min_temperature))
+ environment.adjust_thermal_energy(-removed_heat)
+ if (prob(5))
+ target.visible_message("The water sizzles as it lands on \the [target]!")
+ else if(allocated >= 10)
+ if(istype(target, /turf/simulated))
+ var/turf/simulated/simulated_target = target
+ simulated_target.wet_floor(1)
+
+/datum/reagent/water/on_touch_obj(obj/target, remaining, allocated, data, spread_between)
+ if(istype(target, /obj/item/reagent_containers/food/snacks/monkeycube))
+ var/obj/item/reagent_containers/food/snacks/monkeycube/cube = target
+ if(!cube.wrapped)
+ cube.Expand()
+ else
+ target.water_act(allocated / 5)
+ var/effective = allocated || 10
+ target.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (effective / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (effective / 10)))
+ return ..()
+
+/datum/reagent/water/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ if(isliving(target))
+ var/mob/living/living_target = target
+ // First, kill slimes.
+ if(istype(living_target, /mob/living/simple_mob/slime))
+ var/mob/living/simple_mob/slime/S = living_target
+ var/amt = 15 * allocated * (1-S.water_resist)
+ if(amt>0)
+ S.adjustToxLoss(amt)
+ S.visible_message("[S]'s flesh sizzles where the water touches it!", "Your flesh burns in the water!")
+
+ // Then extinguish people on fire.
+ var/needed = living_target.fire_stacks * 5
+ if(allocated > needed)
+ living_target.ExtinguishMob()
+ living_target.adjust_fire_stacks(-(allocated / 5))
+ allocated -= needed
+ . += needed
+
+ var/effective = allocated || 10
+ target.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (effective / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (effective / 10)))
+ return . + ..()
+
+/datum/reagent/water/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ //else
+ M.adjust_hydration(removed * 10)
+ ..()
diff --git a/code/modules/reagents/chemistry/reagents/nutrition/nutriment.dm b/code/modules/reagents/chemistry/reagents/nutrition/nutriment.dm
new file mode 100644
index 000000000000..510cfeb4df5d
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/nutrition/nutriment.dm
@@ -0,0 +1,93 @@
+/**
+ * datastruct for nutriment data.
+ */
+/datum/nutriment_data
+ /// were we cooked?
+ var/cooked = FALSE
+ /// taste list as "description" = amount
+ ///
+ /// * always sorted from largest to least
+ var/list/taste
+ /// total taste amount used for blending
+ var/taste_total = 0
+
+/datum/nutriment_data/proc/set_taste(description, amount)
+ #warn impl
+
+/datum/nutriment_data/proc/add_taste(description, amount, skip_culling)
+ #warn impl ; cull if needed
+
+ if(!skip_culling)
+ cull_taste()
+
+/datum/nutriment_data/proc/remove_taste(description, amount)
+ #warn impl
+
+/datum/nutriment_data/proc/cull_taste()
+ // this should be a define (10 being max) but idrc lol
+ taste?.len = min(length(taste), 10)
+
+/datum/nutriment_data/proc/merge_from(datum/nutriment_data/source)
+ for(var/description in source.taste)
+ var/power = source.taste[description]
+ add_taste(description, power, TRUE)
+ cull_taste()
+ cooked ||= source.cooked
+
+/datum/nutriment_data/clone(include_contents)
+ var/datum/nutriment_data/copying = new
+ copying.cooked = cooked
+ copying.taste = taste?.Copy()
+ copying.taste_total = taste_total
+ return copying
+
+/datum/nutriment_data/static_spawn_initializer
+ cooked = TRUE
+
+/datum/reagent/nutriment
+ name = "Nutriment"
+ id = "nutriment"
+ holds_data = TRUE
+ description = "All the vitamins, minerals, and carbohydrates the body needs in pure form."
+ taste_mult = 4
+ reagent_state = REAGENT_SOLID
+ metabolism_rate = REM * 4
+ ingest_met = REM * 4
+ var/nutriment_factor = 30 // Per unit
+ var/hydration_factor = 0 //Per unit
+ var/injectable = 0
+ color = "#664330"
+
+/datum/reagent/nutriment/make_copy_data_initializer(datum/nutriment_data/data)
+ return data
+
+/datum/reagent/nutriment/preprocess_data(datum/nutriment_data/data_initializer)
+ return data_initializer
+
+/datum/reagent/nutriment/mix_data(datum/nutriment_data/old_data, old_volume, datum/nutriment_data/new_data, new_volume, datum/reagent_holder/holder)
+ if(!istype(new_data))
+ return old_data
+ old_data.merge_from(new_data)
+ return old_data
+
+/datum/reagent/nutriment/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ if(!injectable && alien != IS_SLIME && alien != IS_CHIMERA)
+ M.adjustToxLoss(0.1 * removed)
+ return
+ legacy_affect_ingest(M, alien, removed, metabolism)
+
+/datum/reagent/nutriment/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ switch(alien)
+ if(IS_DIONA)
+ return
+ if(IS_UNATHI)
+ removed *= 0.5
+ if(IS_CHIMERA)
+ removed *= 0.25
+ if(issmall(M))
+ removed *= 2 // Small bodymass, more effect from lower volume.
+ M.heal_organ_damage(0.5 * removed, 0)
+ if(!M.species.is_vampire) // If this is set to 0, they don't get nutrition from food.
+ M.nutrition += nutriment_factor * removed // For hunger and fatness
+ M.adjust_hydration(hydration_factor * removed)
+ M.add_chemical_effect(CE_BLOODRESTORE, 4 * removed)
diff --git a/code/modules/reagents/chemistry/reagents/nutrition/nutriment/coating.dm b/code/modules/reagents/chemistry/reagents/nutrition/nutriment/coating.dm
new file mode 100644
index 000000000000..27a11cde3426
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/nutrition/nutriment/coating.dm
@@ -0,0 +1,57 @@
+/*
+ Coatings are used in cooking. Dipping food items in a reagent container with a coating in it
+ allows it to be covered in that, which will add a masked overlay to the sprite.
+ Coatings have both a raw and a cooked image. Raw coating is generally unhealthy
+ Generally coatings are intended for deep frying foods
+*/
+
+/datum/reagent/nutriment/coating
+ name = "coating"
+ id = "coating"
+ nutriment_factor = 6 //Less dense than the food itself, but coatings still add extra calories
+ var/messaged = 0
+ var/icon_raw
+ var/icon_cooked
+ var/coated_adj = "coated"
+ var/cooked_name = "coating"
+
+/datum/reagent/nutriment/coating/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ var/datum/nutriment_data/data = M.ingested.get_reagent_data(src)
+ //We'll assume that the batter isnt going to be regurgitated and eaten by someone else. Only show this once
+ if(!data.cooked)
+ if (!messaged)
+ to_chat(M, "Ugh, this raw [name] tastes disgusting.")
+ nutriment_factor *= 0.5
+ messaged = 1
+
+ //Raw coatings will sometimes cause vomiting
+ if (prob(1))
+ M.vomit()
+ ..()
+
+// todo: cooked_name isn't implemented right now!
+#warn cooked name?
+
+/datum/reagent/nutriment/coating/batter
+ name = "batter mix"
+ cooked_name = "batter"
+ id = "batter"
+ color = "#f5f4e9"
+ reagent_state = REAGENT_LIQUID
+ icon_raw = "batter_raw"
+ icon_cooked = "batter_cooked"
+ coated_adj = "battered"
+
+/datum/reagent/nutriment/coating/beerbatter
+ name = "beer batter mix"
+ cooked_name = "beer batter"
+ id = "beerbatter"
+ color = "#f5f4e9"
+ reagent_state = REAGENT_LIQUID
+ icon_raw = "batter_raw"
+ icon_cooked = "batter_cooked"
+ coated_adj = "beer-battered"
+
+/datum/reagent/nutriment/coating/beerbatter/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ ..()
+ M.add_chemical_effect(CE_ALCOHOL, 0.02) //Very slightly alcoholic
diff --git a/code/modules/reagents/chemistry/reagents/other/adminordrazine.dm b/code/modules/reagents/chemistry/reagents/other/adminordrazine.dm
new file mode 100644
index 000000000000..771128523c1e
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/adminordrazine.dm
@@ -0,0 +1,64 @@
+/datum/reagent/adminordrazine //An OP chemical for admins
+ name = "Adminordrazine"
+ id = "adminordrazine"
+ description = "It's magic. We don't have to explain it."
+ taste_description = "bwoink"
+ reagent_state = REAGENT_LIQUID
+ color = "#C8A5DC"
+ affects_dead = 1 //This can even heal dead people.
+ metabolism_rate = 0.1
+ mrate_static = TRUE //Just in case
+
+ glass_name = "liquid gold"
+ glass_desc = "It's magic. We don't have to explain it."
+
+/datum/reagent/adminordrazine/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ legacy_affect_blood(M, alien, removed, metabolism)
+
+/datum/reagent/adminordrazine/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ M.setCloneLoss(0)
+ M.setOxyLoss(0)
+ M.radiation = 0
+ M.heal_organ_damage(20,20)
+ M.adjustToxLoss(-20)
+ M.setHallucination(0)
+ M.setHalLoss(0)
+ M.setBrainLoss(0)
+ M.disabilities = 0
+ M.sdisabilities = 0
+ M.eye_blurry = 0
+ M.remove_status_effect(/datum/status_effect/sight/blindness)
+ M.set_paralyzed(0)
+ M.set_stunned(0)
+ M.set_unconscious(0)
+ M.silent = 0
+ M.dizziness = 0
+ M.drowsyness = 0
+ M.stuttering = 0
+ M.SetConfused(0)
+ M.set_sleeping(0)
+ M.jitteriness = 0
+ M.radiation = 0
+ M.ExtinguishMob()
+ M.fire_stacks = 0
+ if(M.bodytemperature > 310)
+ M.bodytemperature = max(310, M.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
+ else if(M.bodytemperature < 311)
+ M.bodytemperature = min(310, M.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ var/wound_heal = 5
+ for(var/obj/item/organ/external/O in H.bad_external_organs)
+ if(O.status & ORGAN_BROKEN)
+ O.mend_fracture() //Only works if the bone won't rebreak, as usual
+ for(var/datum/wound/W as anything in O.wounds)
+ if(W.bleeding())
+ W.damage = max(W.damage - wound_heal, 0)
+ if(W.damage <= 0)
+ O.cure_exact_wound(W)
+ continue
+ if(W.internal)
+ W.damage = max(W.damage - wound_heal, 0)
+ if(W.damage <= 0)
+ O.cure_exact_wound(W)
+ continue
diff --git a/code/modules/reagents/chemistry/reagents/other/carpet.dm b/code/modules/reagents/chemistry/reagents/other/carpet.dm
new file mode 100644
index 000000000000..5344a51cbd1d
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/carpet.dm
@@ -0,0 +1,63 @@
+/datum/reagent/carpet
+ name = "Liquid Carpet"
+ id = "liquidcarpet"
+ description = "Liquified carpet fibers, ready for dyeing."
+ reagent_state = REAGENT_LIQUID
+ color = "#b51d05"
+ taste_description = "carpet"
+
+/datum/reagent/carpet/black
+ name = "Liquid Black Carpet"
+ id = "liquidcarpetb"
+ description = "Black Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#000000"
+ taste_description = "rare and ashy carpet"
+
+/datum/reagent/carpet/blue
+ name = "Liquid Blue Carpet"
+ id = "liquidcarpetblu"
+ description = "Blue Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#3f4aee"
+ taste_description = "commanding carpet"
+
+/datum/reagent/carpet/turquoise
+ name = "Liquid Turquoise Carpet"
+ id = "liquidcarpettur"
+ description = "Turquoise Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#0592b5"
+ taste_description = "water-logged carpet"
+
+/datum/reagent/carpet/sblue
+ name = "Liquid Silver Blue Carpet"
+ id = "liquidcarpetsblu"
+ description = "Silver Blue Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#0011ff"
+ taste_description = "sterile and medicinal carpet"
+
+/datum/reagent/carpet/clown
+ name = "Liquid Clown Carpet"
+ id = "liquidcarpetc"
+ description = "Clown Carpet Fibers.... No clowns were harmed in the making of this."
+ reagent_state = REAGENT_LIQUID
+ color = "#e925be"
+ taste_description = "clown shoes and banana peels"
+
+/datum/reagent/carpet/purple
+ name = "Liquid Purple Carpet"
+ id = "liquidcarpetp"
+ description = "Purple Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#a614d3"
+ taste_description = "bleeding edge carpet research"
+
+/datum/reagent/carpet/orange
+ name = "Liquid Orange Carpet"
+ id = "liquidcarpeto"
+ description = "Orange Carpet Fibers, ready for reinforcement."
+ reagent_state = REAGENT_LIQUID
+ color = "#f16e16"
+ taste_description = "extremely overengineered carpet"
diff --git a/code/modules/reagents/chemistry/reagents/other/chalk_dust.dm b/code/modules/reagents/chemistry/reagents/other/chalk_dust.dm
new file mode 100644
index 000000000000..78938ad06854
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/chalk_dust.dm
@@ -0,0 +1,23 @@
+/datum/reagent/chalk_dust
+ name = "chalk dust"
+ id = "chalk_dust"
+ description = "Dusty powder obtained by grinding chalk."
+ taste_description = "powdered chalk"
+ reagent_state = REAGENT_LIQUID
+ color = "#FFFFFF"
+ overdose = 5
+
+/datum/reagent/chalk_dust/red
+ name = "red chalk dust"
+ id = "chalk_dust_red"
+ color = "#aa0000"
+
+/datum/reagent/chalk_dust/black
+ name = "black chalk dust"
+ id = "chalk_dust_black"
+ color = "#180000"
+
+/datum/reagent/chalk_dust/blue
+ name = "blue chalk dust"
+ id = "chalk_dust_blue"
+ color = "#000370"
diff --git a/code/modules/reagents/chemistry/reagents/other/cleaner.dm b/code/modules/reagents/chemistry/reagents/other/cleaner.dm
index 37009458c0fc..47abb0d0f07a 100644
--- a/code/modules/reagents/chemistry/reagents/other/cleaner.dm
+++ b/code/modules/reagents/chemistry/reagents/other/cleaner.dm
@@ -24,7 +24,7 @@
for(var/mob/living/simple_mob/slime/M in T)
M.adjustToxLoss(rand(5, 10))
-/datum/reagent/space_cleaner/affect_touch(mob/living/carbon/M, alien, removed)
+/datum/reagent/space_cleaner/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
for(var/obj/item/held as anything in M.get_held_items())
held.clean_blood()
if(M.wear_mask)
@@ -51,7 +51,7 @@
return
M.clean_blood()
-/datum/reagent/space_cleaner/affect_ingest(mob/living/carbon/M, alien, removed)
+/datum/reagent/space_cleaner/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
if(alien == IS_SLIME)
M.adjustToxLoss(6 * removed)
else
diff --git a/code/modules/reagents/chemistry/reagents/other/crayon_dust.dm b/code/modules/reagents/chemistry/reagents/other/crayon_dust.dm
new file mode 100644
index 000000000000..d25059e2f6ba
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/crayon_dust.dm
@@ -0,0 +1,48 @@
+/datum/reagent/crayon_dust
+ name = "Crayon dust"
+ id = "crayon_dust"
+ description = "Intensely coloured powder obtained by grinding crayons."
+ taste_description = "powdered wax"
+ reagent_state = REAGENT_LIQUID
+ color = "#888888"
+ overdose = 5
+
+/datum/reagent/crayon_dust/red
+ name = "Red crayon dust"
+ id = "crayon_dust_red"
+ color = "#FE191A"
+
+/datum/reagent/crayon_dust/orange
+ name = "Orange crayon dust"
+ id = "crayon_dust_orange"
+ color = "#FFBE4F"
+
+/datum/reagent/crayon_dust/yellow
+ name = "Yellow crayon dust"
+ id = "crayon_dust_yellow"
+ color = "#FDFE7D"
+
+/datum/reagent/crayon_dust/green
+ name = "Green crayon dust"
+ id = "crayon_dust_green"
+ color = "#18A31A"
+
+/datum/reagent/crayon_dust/blue
+ name = "Blue crayon dust"
+ id = "crayon_dust_blue"
+ color = "#247CFF"
+
+/datum/reagent/crayon_dust/purple
+ name = "Purple crayon dust"
+ id = "crayon_dust_purple"
+ color = "#CC0099"
+
+/datum/reagent/crayon_dust/grey //Mime
+ name = "Grey crayon dust"
+ id = "crayon_dust_grey"
+ color = "#808080"
+
+/datum/reagent/crayon_dust/brown //Rainbow
+ name = "Brown crayon dust"
+ id = "crayon_dust_brown"
+ color = "#846F35"
diff --git a/code/modules/reagents/chemistry/reagents/other/holywater.dm b/code/modules/reagents/chemistry/reagents/other/holywater.dm
new file mode 100644
index 000000000000..97737d4e7b79
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/holywater.dm
@@ -0,0 +1,16 @@
+/datum/reagent/water/holywater
+ name = "Holy Water"
+ id = "holywater"
+ description = "An ashen-obsidian-water mix, this solution will alter certain sections of the brain's rationality."
+ taste_description = "water"
+ color = "#E0E8EF"
+ mrate_static = TRUE
+
+ glass_name = "holy water"
+ glass_desc = "An ashen-obsidian-water mix, this solution will alter certain sections of the brain's rationality."
+
+/datum/reagent/water/holywater/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
+ ..()
+ if(ishuman(M)) // Any location
+ if(M.mind && cult.is_antagonist(M.mind) && prob(10))
+ cult.remove_antagonist(M.mind)
diff --git a/code/modules/reagents/chemistry/reagents/other/luminol.dm b/code/modules/reagents/chemistry/reagents/other/luminol.dm
new file mode 100644
index 000000000000..f2b39d9e74d2
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/luminol.dm
@@ -0,0 +1,15 @@
+/datum/reagent/luminol
+ name = "Luminol"
+ id = "luminol"
+ description = "A compound that interacts with blood on the molecular level."
+ taste_description = "metal"
+ reagent_state = REAGENT_LIQUID
+ color = "#F2F3F4"
+
+/datum/reagent/luminol/on_touch_obj(obj/target, remaining, allocated, data, spread_between)
+ target.reveal_blood()
+ return ..()
+
+/datum/reagent/luminol/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ target.reveal_blood()
+ return ..()
diff --git a/code/modules/reagents/chemistry/reagents/other/marker_ink.dm b/code/modules/reagents/chemistry/reagents/other/marker_ink.dm
new file mode 100644
index 000000000000..2e9a0deacd08
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/marker_ink.dm
@@ -0,0 +1,53 @@
+/datum/reagent/marker_ink
+ name = "Marker ink"
+ id = "marker_ink"
+ description = "Intensely coloured ink used in markers."
+ taste_description = "extremely bitter"
+ reagent_state = REAGENT_LIQUID
+ color = "#888888"
+ overdose = 5
+
+/datum/reagent/marker_ink/black
+ name = "Black marker ink"
+ id = "marker_ink_black"
+ color = "#000000"
+
+/datum/reagent/marker_ink/red
+ name = "Red marker ink"
+ id = "marker_ink_red"
+ color = "#FE191A"
+
+/datum/reagent/marker_ink/orange
+ name = "Orange marker ink"
+ id = "marker_ink_orange"
+ color = "#FFBE4F"
+
+/datum/reagent/marker_ink/yellow
+ name = "Yellow marker ink"
+ id = "marker_ink_yellow"
+ color = "#FDFE7D"
+
+/datum/reagent/marker_ink/green
+ name = "Green marker ink"
+ id = "marker_ink_green"
+ color = "#18A31A"
+
+/datum/reagent/marker_ink/blue
+ name = "Blue marker ink"
+ id = "marker_ink_blue"
+ color = "#247CFF"
+
+/datum/reagent/marker_ink/purple
+ name = "Purple marker ink"
+ id = "marker_ink_purple"
+ color = "#CC0099"
+
+/datum/reagent/marker_ink/grey //Mime
+ name = "Grey marker ink"
+ id = "marker_ink_grey"
+ color = "#808080"
+
+/datum/reagent/marker_ink/brown //Rainbow
+ name = "Brown marker ink"
+ id = "marker_ink_brown"
+ color = "#846F35"
diff --git a/code/modules/reagents/chemistry/reagents/other/paint.dm b/code/modules/reagents/chemistry/reagents/other/paint.dm
new file mode 100644
index 000000000000..9afb31806b07
--- /dev/null
+++ b/code/modules/reagents/chemistry/reagents/other/paint.dm
@@ -0,0 +1,54 @@
+/**
+ * data: #ffffffff 7-9 character rgb/rgba color string
+ */
+/datum/reagent/paint
+ name = "Paint"
+ id = "paint"
+ holds_data = TRUE
+ description = "This paint will stick to almost any object."
+ taste_description = "chalk"
+ reagent_state = REAGENT_LIQUID
+ color = "#808080"
+ overdose = REAGENTS_OVERDOSE * 0.5
+ color_weight = 20
+
+// todo: rework
+/datum/reagent/paint/on_touch_turf(turf/target, remaining, allocated, data)
+ target.color = compute_color_with_data(data)
+ . = allocated
+ allocated -= allocated
+ . += ..()
+
+// todo: rework
+/datum/reagent/paint/on_touch_obj(obj/target, remaining, allocated, data, spread_between)
+ target.color = compute_color_with_data(data)
+ . = allocated
+ allocated -= allocated
+ . += ..()
+
+// todo: rework
+/datum/reagent/paint/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ target.color = compute_color_with_data(data)
+ . = allocated
+ allocated -= allocated
+ . += ..()
+
+/datum/reagent/paint/compute_color_with_data(data)
+ return data || "#ffffff"
+
+/datum/reagent/paint/make_copy_data_initializer(data)
+ return data
+
+/datum/reagent/paint/preprocess_data(data_initializer)
+ return data_initializer
+
+/datum/reagent/paint/mix_data(old_data, old_volume, new_data, new_volume, datum/reagent_holder/holder)
+ var/total_volume = old_volume + new_volume
+ var/old_hex = uppertext(old_data || "#ffffff")
+ var/new_hex = uppertext(new_data || "#ffffff")
+ return rgb(
+ ((hex2num(copytext(old_hex, 2, 4)) * old_volume) + (hex2num(copytext(new_hex, 2, 4)))) / total_volume,
+ ((hex2num(copytext(old_hex, 4, 6)) * old_volume) + (hex2num(copytext(new_hex, 4, 6)))) / total_volume,
+ ((hex2num(copytext(old_hex, 6, 8)) * old_volume) + (hex2num(copytext(new_hex, 6, 8)))) / total_volume,
+ ((hex2num(length(old_hex) > 7 ? copytext(old_hex, 8, 10) : 255) * old_volume) + (hex2num(length(new_hex) > 7 ? copytext(new_hex, 8, 10) : 255))) / total_volume,
+ )
diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnics/thermite.dm b/code/modules/reagents/chemistry/reagents/pyrotechnics/thermite.dm
index a7b6b7ef82dd..a28b950de217 100644
--- a/code/modules/reagents/chemistry/reagents/pyrotechnics/thermite.dm
+++ b/code/modules/reagents/chemistry/reagents/pyrotechnics/thermite.dm
@@ -7,18 +7,22 @@
color = "#673910"
touch_met = 50
-/datum/reagent/thermite/touch_turf(turf/T)
- if(volume >= 5)
- if(istype(T, /turf/simulated/wall))
- var/turf/simulated/wall/W = T
- W.thermite = 1
- W.add_overlay(image('icons/effects/effects.dmi', icon_state = "#673910"))
- remove_self(5)
- return
+/datum/reagent/thermite/on_touch_turf(turf/target, remaining, allocated, data)
+ // todo: refactor
+ if(allocated >= 5 && istype(target, /turf/simulated/wall))
+ var/turf/simulated/wall/wall = target
+ wall.thermite = TRUE
+ wall.add_overlay(image('icons/effects/effects.dmi', icon_state = "#673910"))
+ allocated -= 5
+ . += 5
+ return . + ..()
-/datum/reagent/thermite/touch_mob(mob/living/L, amount)
- if(istype(L))
- L.adjust_fire_stacks(amount / 5)
+/datum/reagent/thermite/on_touch_mob(mob/target, remaining, allocated, data, zone)
+ if(isliving(target))
+ // todo: rework and actually use some
+ var/mob/living/living_target = target
+ living_target.adjust_fire_stacks(allocated / 5)
+ return ..()
-/datum/reagent/thermite/affect_blood(mob/living/carbon/M, alien, removed)
+/datum/reagent/thermite/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism)
M.adjustFireLoss(3 * removed)
diff --git a/code/modules/reagents/exposedprocs.dm b/code/modules/reagents/exposedprocs.dm
index 28fbe79acca1..fac249035fe3 100644
--- a/code/modules/reagents/exposedprocs.dm
+++ b/code/modules/reagents/exposedprocs.dm
@@ -12,9 +12,8 @@
if(!src)
return
reagents.trans_to_obj(D, amount_per_transfer_from_this)
- D.color = mix_color_from_reagents(D.reagents.reagent_list)
+ D.color = D.reagents.get_color()
D.set_up(my_target, spray_size, delay)
- return
/// Chem thrower style spray. If spray_size = null a random value from 6-8 will be chosen.
/obj/item/proc/spray_at_wide(atom/A as mob|obj, var/amount_per_transfer_from_this = 10, var/spray_size = null, var/delay = 2)
@@ -34,10 +33,9 @@
return
playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6)
reagents.trans_to_obj(D, amount_per_transfer_from_this)
- D.color = mix_color_from_reagents(D.reagents.reagent_list)
+ D.color = D.reagents.get_color()
spray_size ? null : (spray_size = rand(6, 8))
D.set_up(my_target, spray_size, delay)
- return
/// Extinguisher spray, intended for use with afterattack. See '/obj/effect/water/proc/set_up' for more details.
/obj/item/proc/extinguish_spray(atom/A as mob|obj|turf, var/mob/living/user, var/amount_per_transfer_from_this = 10, var/spray_size = 3, var/delay = 10, var/spray_particles = 3)
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/items/spray.dm
similarity index 83%
rename from code/modules/reagents/reagent_containers/spray.dm
rename to code/modules/reagents/items/spray.dm
index 914e7182d4a0..a3cd14b1fa32 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/items/spray.dm
@@ -65,7 +65,7 @@
if(!src)
return
reagents.trans_to_obj(D, amount_per_transfer_from_this)
- D.color = mix_color_from_reagents(D.reagents.reagent_list)
+ D.color = D.reagents.get_color()
D.set_up(my_target, spray_size, 10)
return
@@ -222,40 +222,15 @@
. = ..()
reagents.add_reagent("pestbgone", volume)
-/obj/item/reagent_containers/spray/squirt
- name = "HydroBlaster 4000"
- desc = "A popular toy produced by Donk Co, the HydroBlaster 4000 is the latest in a long line of recreational pressurized water delivery systems."
- icon = 'icons/obj/toy.dmi'
- icon_state = "squirtgun"
- item_state = "squirtgun"
- w_class = WEIGHT_CLASS_NORMAL
- volume = 100
- var/pumped = TRUE
- materials_base = list(MAT_PLASTIC = 1500)
-
-/obj/item/reagent_containers/spray/squirt/Initialize(mapload)
- . = ..()
- reagents.add_reagent("water", volume)
-
-/obj/item/reagent_containers/spray/squirt/examine(mob/user, dist)
- . = ..()
- . += "The tank is [pumped ? "depressurized" : "pressurized"]."
-
-/obj/item/reagent_containers/spray/squirt/attack_self(mob/user, datum/event_args/actor/actor)
- pumped = !pumped
- to_chat(usr, "You pump the handle [pumped ? "to depressurize" : "to pressurize"] the tank.")
+/obj/item/reagent_containers/spray/windowsealant
+ name = "Krak-b-gone"
+ desc = "A spray bottle of silicate sealant for rapid window repair."
+ icon = 'icons/obj/items_vr.dmi'
+ icon_state = "windowsealant"
+ item_state = "spraycan"
+ possible_transfer_amounts = null
+ volume = 80
-/obj/item/reagent_containers/spray/squirt/Spray_at(atom/A as mob|obj)
- if(pumped)
- to_chat(usr, "The tank has no pressure!")
- return
+/obj/item/reagent_containers/spray/windowsealant/Initialize(mapload)
. = ..()
-
-/obj/item/reagent_containers/spray/squirt/nt
- name = "HydroBlaster 4001"
- desc = "A popular toy produced by Donk Co, the HydroBlaster 4001 is modeled in Nanotrasen corporate colors. This is largely considered a sarcastic gesture."
- icon = 'icons/obj/toy.dmi'
- icon_state = "squirtgun_nt"
- item_state = "squirtgun_nt"
- w_class = WEIGHT_CLASS_NORMAL
- volume = 101
+ reagents.add_reagent("silicate", 80)
diff --git a/code/modules/reagents/items/spray/squirt.dm b/code/modules/reagents/items/spray/squirt.dm
new file mode 100644
index 000000000000..554aca748d69
--- /dev/null
+++ b/code/modules/reagents/items/spray/squirt.dm
@@ -0,0 +1,37 @@
+/obj/item/reagent_containers/spray/squirt
+ name = "HydroBlaster 4000"
+ desc = "A popular toy produced by Donk Co, the HydroBlaster 4000 is the latest in a long line of recreational pressurized water delivery systems."
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "squirtgun"
+ item_state = "squirtgun"
+ w_class = WEIGHT_CLASS_NORMAL
+ volume = 100
+ var/pumped = TRUE
+ materials_base = list(MAT_PLASTIC = 1500)
+
+/obj/item/reagent_containers/spray/squirt/Initialize(mapload)
+ . = ..()
+ reagents.add_reagent("water", volume)
+
+/obj/item/reagent_containers/spray/squirt/examine(mob/user, dist)
+ . = ..()
+ . += "The tank is [pumped ? "depressurized" : "pressurized"]."
+
+/obj/item/reagent_containers/spray/squirt/attack_self(mob/user, datum/event_args/actor/actor)
+ pumped = !pumped
+ to_chat(usr, "You pump the handle [pumped ? "to depressurize" : "to pressurize"] the tank.")
+
+/obj/item/reagent_containers/spray/squirt/Spray_at(atom/A as mob|obj)
+ if(pumped)
+ to_chat(usr, "The tank has no pressure!")
+ return
+ . = ..()
+
+/obj/item/reagent_containers/spray/squirt/nt
+ name = "HydroBlaster 4001"
+ desc = "A popular toy produced by Donk Co, the HydroBlaster 4001 is modeled in Nanotrasen corporate colors. This is largely considered a sarcastic gesture."
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "squirtgun_nt"
+ item_state = "squirtgun_nt"
+ w_class = WEIGHT_CLASS_NORMAL
+ volume = 101
diff --git a/code/modules/reagents/items/spray/waterflower.dm b/code/modules/reagents/items/spray/waterflower.dm
new file mode 100644
index 000000000000..808a7b87258d
--- /dev/null
+++ b/code/modules/reagents/items/spray/waterflower.dm
@@ -0,0 +1,15 @@
+
+/obj/item/reagent_containers/spray/waterflower
+ name = "water flower"
+ desc = "A seemingly innocent sunflower...with a twist."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "sunflower"
+ item_state = "sunflower"
+ var/empty = 0
+ slot_flags = SLOT_HOLSTER
+ damage_force = 0
+
+/obj/item/reagent_containers/spray/waterflower/Initialize(mapload)
+ . = ..()
+ var/datum/reagent_holder/R = create_reagents(10)
+ R.add_reagent("water", 10)
diff --git a/code/modules/reagents/machinery/chem_master.dm b/code/modules/reagents/machinery/chem_master.dm
index 050d55ef6f81..32d211057ca8 100644
--- a/code/modules/reagents/machinery/chem_master.dm
+++ b/code/modules/reagents/machinery/chem_master.dm
@@ -243,10 +243,10 @@
var/beaker_contents[0]
if(beaker)
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ for(var/datum/reagent/R in beaker.reagents.get_reagent_datums())
beaker_contents.Add(list(list( //! list in a list because Byond merges the first list...
"name" = R.name,
- "volume" = round(R.volume, 0.01),
+ "volume" = round(beaker.reagents.reagent_volumes[R.id], 0.01),
"description" = R.description,
"id" = R.id,
)))
@@ -254,10 +254,10 @@
var/buffer_contents[0]
if(reagents.total_volume)
- for(var/datum/reagent/R in reagents.reagent_list)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
buffer_contents.Add(list(list( //! ^
"name" = R.name,
- "volume" = round(R.volume, 0.01),
+ "volume" = round(reagents.reagent_volumes[R.id], 0.01),
"description" = R.description,
"id" = R.id,
)))
@@ -382,7 +382,7 @@
if (style && style["name"] && !style["generate_name"])
name_default = style["name"]
else
- name_default = reagents.get_master_reagent_name()
+ name_default = reagents.get_majority_reagent_name()
if (name_has_units)
name_default += " ([vol_each]u)"
name = tgui_input_text(usr,
@@ -494,7 +494,7 @@
state = "Liquid"
else if(initial(analyzed_reagent.reagent_state) == REAGENT_GAS)
state = "Gas"
- var/metabolization_rate = initial(analyzed_reagent.metabolism)// * (60 / SSMOBS_DT)
+ var/metabolization_rate = initial(analyzed_reagent.metabolism_rate)// * (60 / SSMOBS_DT)
analyze_vars = list(
"name" = initial(analyzed_reagent.name),
"state" = state,
@@ -773,8 +773,8 @@
*/
/obj/machinery/chem_master/proc/guess_condi_style(datum/reagent_holder/reagents)
var/list/styles = get_condi_styles()
- if (reagents.reagent_list.len > 0)
- var/main_reagent = reagents.get_master_reagent_id()
+ if (reagents.total_volume)
+ var/main_reagent = reagents.get_majority_reagent_id()
if (main_reagent)
var/list/path = splittext("[main_reagent]", "/")
main_reagent = path[path.len]
diff --git a/code/modules/reagents/machinery/reagent_dispenser.dm b/code/modules/reagents/machinery/reagent_dispenser.dm
index f78c043b66d7..cb7cab583bea 100644
--- a/code/modules/reagents/machinery/reagent_dispenser.dm
+++ b/code/modules/reagents/machinery/reagent_dispenser.dm
@@ -32,10 +32,11 @@
/obj/structure/reagent_dispensers/examine(mob/user, dist)
. = ..()
if(get_dist(user, src) <= 2)
+ // todo: generic reagent examine
. += "It contains:"
- if(reagents && reagents.reagent_list.len)
- for(var/datum/reagent/R in reagents.reagent_list)
- . += "[R.volume] units of [R.name]"
+ if(reagents.total_volume)
+ for(var/datum/reagent/R in reagents?.get_reagent_datums())
+ . += "[reagents.reagent_volumes[R.id]] units of [R.name]"
else
. += "Nothing."
diff --git a/code/modules/reagents/machinery/reagent_dispenser/oil.dm b/code/modules/reagents/machinery/reagent_dispenser/oil.dm
index e28b3074f4e1..01014b915cb2 100644
--- a/code/modules/reagents/machinery/reagent_dispenser/oil.dm
+++ b/code/modules/reagents/machinery/reagent_dispenser/oil.dm
@@ -20,16 +20,3 @@
/datum/reagent/nutriment/triglyceride/oil/tallow = 5000,
)
starting_capacity = 5000
-
-/obj/structure/reagent_dispensers/cookingoil/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args)
- . = ..()
- if(proj.get_structure_damage())
- explode()
-
-/obj/structure/reagent_dispensers/cookingoil/legacy_ex_act()
- explode()
-
-/obj/structure/reagent_dispensers/cookingoil/proc/explode()
- reagents.splash_area(get_turf(src), 3)
- visible_message(SPAN_DANGER("The [src] bursts open, spreading oil all over the area."))
- qdel(src)
diff --git a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm
index 95e3b73397d5..6e019b73f0b1 100644
--- a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm
+++ b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm
@@ -45,9 +45,7 @@
if(do_after(user, 20) && bottle)
to_chat(user, "You unfasten the jug.")
var/obj/item/reagent_containers/glass/cooler_bottle/G = new /obj/item/reagent_containers/glass/cooler_bottle( src.loc )
- for(var/datum/reagent/R in reagents.reagent_list)
- var/total_reagent = reagents.get_reagent_amount(R.id)
- G.reagents.add_reagent(R.id, total_reagent)
+ reagents.transfer_to_holder(G.reagents)
reagents.clear_reagents()
bottle = 0
update_icon()
@@ -94,9 +92,7 @@
bottle = 1
update_icon()
to_chat(user, "You screw the bottle onto the water-cooler!")
- for(var/datum/reagent/R in G.reagents.reagent_list)
- var/total_reagent = G.reagents.get_reagent_amount(R.id)
- reagents.add_reagent(R.id, total_reagent)
+ G.reagents.transfer_to_holder(reagents)
qdel(G)
else
to_chat(user, "You need to wrench down the cooler first.")
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 92bade4d10e5..92cfd12cbc38 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -36,7 +36,7 @@
if(blood_type != null)
label_text = "[blood_type]"
update_iv_label()
- reagents.add_reagent("blood", 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
+ reagents.add_reagent("blood", 200, list("donor"=null,"blood_DNA"=null,"blood_type"=blood_type,"trace_chem"=null))
update_icon()
/obj/item/reagent_containers/blood/on_reagent_change()
diff --git a/code/modules/reagents/reagent_containers/blood_pack_vr.dm b/code/modules/reagents/reagent_containers/blood_pack_vr.dm
index 7a7c947e2186..7dc1b88aa12f 100644
--- a/code/modules/reagents/reagent_containers/blood_pack_vr.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack_vr.dm
@@ -8,8 +8,8 @@
if(user.a_intent == INTENT_HARM)
if(reagents.total_volume && volume)
var/remove_volume = volume* 0.1 //10% of what the bloodpack can hold.
- var/reagent_to_remove = reagents.get_master_reagent_id()
- switch(reagents.get_master_reagent_id())
+ var/reagent_to_remove = reagents.get_majority_reagent_id()
+ switch(reagents.get_majority_reagent_id())
if("blood")
user.show_message("You sink your fangs into \the [src] and suck the blood out of it!")
user.visible_message("[user] sinks their fangs into \the [src] and drains it!")
diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm
index 9d911d7b0e38..09cb9d1497de 100644
--- a/code/modules/reagents/reagent_containers/dropper.dm
+++ b/code/modules/reagents/reagent_containers/dropper.dm
@@ -17,7 +17,7 @@
/obj/item/reagent_containers/dropper/examine(mob/user, dist)
. = ..()
- if(reagents && reagents.reagent_list.len)
+ if(length(reagents?.reagent_volumes))
. += "It contains [reagents.total_volume] units of liquid."
else
. += "It is empty."
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 6088bce70717..0c28d99e2caf 100644
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -128,7 +128,7 @@
return
if(W && W.get_weight_class() <= get_weight_class() && (atom_flags & OPENCONTAINER))
to_chat(user, "You dip \the [W] into \the [src].")
- reagents.touch_obj(W, reagents.total_volume)
+ reagents.auto_spill(W, 1, FALSE, TRUE)
/obj/item/reagent_containers/glass/proc/update_name_label()
if(label_text == "")
@@ -375,9 +375,9 @@
/obj/item/reagent_containers/glass/bucket/sandstone/examine(mob/user, dist)
. = ..()
- if(reagents && reagents.reagent_list.len)
- for(var/datum/reagent/R in reagents.reagent_list)
- . += "[icon2html(thing = src, target = world)] The [src.name] currently contains [R.volume] units of [R.name]!"
+ if(reagents?.total_volume)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
+ . += "[icon2html(thing = src, target = world)] The [src.name] currently contains [reagents.reagent_volumes[R.id]] units of [R.name]!"
else
. += "It is empty."
@@ -462,9 +462,9 @@
/obj/item/reagent_containers/glass/stone/examine(mob/user, dist)
. = ..()
- if(reagents && reagents.reagent_list.len)
- for(var/datum/reagent/R in reagents.reagent_list)
- . += "[icon2html(thing = src, target = world)] The [src.name] currently contains [R.volume] units of [R.name]!"
+ if(reagents?.total_volume)
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
+ . += "[icon2html(thing = src, target = world)] The [src.name] currently contains [reagents.reagent_volumes[R.id]] units of [R.name]!"
else
. += "It is empty."
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 70066556c1fc..1edfecf79d6b 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -1,6 +1,7 @@
////////////////////////////////////////////////////////////////////////////////
/// HYPOSPRAY
////////////////////////////////////////////////////////////////////////////////
+// todo: remove these, move behavior to /obj/item/autoinjector for autoinjectors
/obj/item/reagent_containers/hypospray
name = "hypospray"
@@ -176,7 +177,7 @@
/obj/item/reagent_containers/hypospray/autoinjector/examine(mob/user, dist)
. = ..()
- if(reagents && reagents.reagent_list.len)
+ if(reagents?.total_volume)
. += "It is currently loaded."
else
. += "It is spent."
@@ -439,7 +440,7 @@
/obj/item/reagent_containers/hypospray/glukoz/examine(mob/user, dist)
. = ..()
- if(reagents && reagents.reagent_list.len)
+ if(reagents?.total_volume)
. += "It is currently loaded."
else
. += "It is spent."
diff --git a/code/modules/reagents/reagent_containers/organic.dm b/code/modules/reagents/reagent_containers/organic.dm
index c6e8ade3ade2..cb7a545979e7 100644
--- a/code/modules/reagents/reagent_containers/organic.dm
+++ b/code/modules/reagents/reagent_containers/organic.dm
@@ -135,7 +135,7 @@
return
if(W && W.get_weight_class() <= get_weight_class() && (atom_flags & OPENCONTAINER))
to_chat(user, "You dip \the [W] into \the [src].")
- reagents.touch_obj(W, reagents.total_volume)
+ reagents.auto_spill(W, 1, FALSE, TRUE)
/obj/item/reagent_containers/organic/proc/update_name_label()
if(label_text == "")
diff --git a/code/modules/reagents/reagent_containers/spray_vr.dm b/code/modules/reagents/reagent_containers/spray_vr.dm
deleted file mode 100644
index 73915d211ebd..000000000000
--- a/code/modules/reagents/reagent_containers/spray_vr.dm
+++ /dev/null
@@ -1,12 +0,0 @@
-/obj/item/reagent_containers/spray/windowsealant
- name = "Krak-b-gone"
- desc = "A spray bottle of silicate sealant for rapid window repair."
- icon = 'icons/obj/items_vr.dmi'
- icon_state = "windowsealant"
- item_state = "spraycan"
- possible_transfer_amounts = null
- volume = 80
-
-/obj/item/reagent_containers/spray/windowsealant/Initialize(mapload)
- . = ..()
- reagents.add_reagent("silicate", 80)
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index fc1117b70ece..b36534e9887d 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -99,7 +99,7 @@
to_chat(user, "You are already drawing blood from [T.name].")
return
- var/datum/reagent/B
+ var/amount_drawn = 0
drawing = 1
if(istype(T, /mob/living/carbon/human))
var/mob/living/carbon/human/H = T
@@ -110,20 +110,15 @@
if(!do_mob(user, target, time))
drawing = 0
return
- B = T.take_blood(src, amount)
+ amount_drawn = T.take_blood_legacy(src, amount)
drawing = 0
else
if(!do_mob(user, target, time))
drawing = 0
return
- B = T.take_blood(src,amount)
+ amount_drawn = T.take_blood_legacy(src,amount)
drawing = 0
- if (B && !(B in reagents.reagent_list))
- reagents.reagent_list += B
- reagents.update_total()
- on_reagent_change()
- reagents.reconsider_reactions()
to_chat(user, "You take a blood sample from [target].")
for(var/mob/O in viewers(4, user))
O.show_message("[user] takes a blood sample from [target].", 1)
diff --git a/code/modules/research/machinery/rdconsole_tgui.dm b/code/modules/research/machinery/rdconsole_tgui.dm
index ef76dd32287f..9707ad37077f 100644
--- a/code/modules/research/machinery/rdconsole_tgui.dm
+++ b/code/modules/research/machinery/rdconsole_tgui.dm
@@ -85,11 +85,11 @@
data["info"]["linked_lathe"]["mats"] = materials
var/list/reagents = list()
- for(var/datum/reagent/R in linked_lathe.reagents.reagent_list)
+ for(var/datum/reagent/R in linked_lathe.reagents.get_reagent_datums())
reagents.Add(list(list(
"name" = R.name,
"id" = R.id,
- "volume" = R.volume,
+ "volume" = linked_lathe.reagents.reagent_volumes[R.id],
)))
data["info"]["linked_lathe"]["reagents"] = reagents
@@ -133,11 +133,11 @@
data["info"]["linked_imprinter"]["mats"] = materials
var/list/reagents = list()
- for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list)
+ for(var/datum/reagent/R in linked_imprinter.reagents.get_reagent_datums())
reagents.Add(list(list(
"name" = R.name,
"id" = R.id,
- "volume" = R.volume,
+ "volume" = linked_imprinter.reagents.reagent_volumes[R.id],
)))
data["info"]["linked_imprinter"]["reagents"] = reagents
diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm
index c94cfb4b646b..0b6326b0dbad 100644
--- a/code/modules/species/species.dm
+++ b/code/modules/species/species.dm
@@ -752,14 +752,9 @@ GLOBAL_LIST_INIT(species_oxygen_tank_by_gas, list(
* this is a destructive proc and will erase incompatible blood.
*/
/datum/species/proc/create_blood(mob/living/carbon/human/H)
- H.make_blood()
- if(H.vessel.total_volume < blood_volume)
- H.vessel.maximum_volume = blood_volume
- H.vessel.add_reagent("blood", blood_volume - H.vessel.total_volume)
- else if(H.vessel.total_volume > blood_volume)
- H.vessel.remove_reagent("blood", H.vessel.total_volume - blood_volume)
- H.vessel.maximum_volume = blood_volume
- H.fixblood()
+ if(species_flags & NO_BLOOD)
+ else
+ H.create_blood()
/datum/species/proc/hug(var/mob/living/carbon/human/H, var/mob/living/target)
diff --git a/code/modules/species/station/station_special_abilities.dm b/code/modules/species/station/station_special_abilities.dm
index b7debb074b5d..5dfb9a3d7d3d 100644
--- a/code/modules/species/station/station_special_abilities.dm
+++ b/code/modules/species/station/station_special_abilities.dm
@@ -75,8 +75,12 @@
revive_ready = REVIVING_READY //reset their cooldown
/mob/living/carbon/human/proc/hasnutriment()
- return (nutrition+ bloodstr.get_reagent("protein") * 10 + bloodstr.get_reagent("nutriment") * 5 + ingested.get_reagent("protein") * 5 + ingested.get_reagent("nutriment") * 2.5) > 425
-
+ . = nutrition
+ . += bloodstr.reagent_volumes[/datum/reagent/nutriment::id] * 5
+ . += bloodstr.reagent_volumes[/datum/reagent/nutriment/protein::id] * 10
+ . += ingested.reagent_volumes[/datum/reagent/nutriment::id] * 2.5
+ . += ingested.reagent_volumes[/datum/reagent/nutriment/protein::id] * 5
+ return . > 425
/mob/living/carbon/human/proc/hatch()
set name = "Hatch"
diff --git a/code/modules/species/station/xenomorph_hybrids/xeno_hybrids.dm b/code/modules/species/station/xenomorph_hybrids/xeno_hybrids.dm
index 4f807228f546..c4f452a936b6 100644
--- a/code/modules/species/station/xenomorph_hybrids/xeno_hybrids.dm
+++ b/code/modules/species/station/xenomorph_hybrids/xeno_hybrids.dm
@@ -149,6 +149,6 @@
if(H.nutrition < 100 || heal_amount <= 0.6)
return
- if(H.vessel.get_reagent_amount("blood") <= blood_level_safe && H.try_take_nutrition(heal_amount * 4))
- H.vessel.add_reagent("blood", heal_amount)//instead of IB healing, they regenerate blood a lot faster
+ if(H.blood_holder.get_total_volume() <= blood_level_safe && H.try_take_nutrition(heal_amount * 4))
+ H.regen_blood(heal_amount)
diff --git a/code/modules/vehicles/sealed/mecha/equipment/tools/sleeper.dm b/code/modules/vehicles/sealed/mecha/equipment/tools/sleeper.dm
index 1074fe36cf70..080453f54600 100644
--- a/code/modules/vehicles/sealed/mecha/equipment/tools/sleeper.dm
+++ b/code/modules/vehicles/sealed/mecha/equipment/tools/sleeper.dm
@@ -166,17 +166,19 @@
/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_occupant_reagents()
if(occupant_legacy.reagents)
- for(var/datum/reagent/R in occupant_legacy.reagents.reagent_list)
- if(R.volume > 0)
- . += "[R]: [round(R.volume,0.01)]
"
+ for(var/datum/reagent/R in occupant_legacy.reagents.get_reagent_datums())
+ var/volume = occupant_legacy.reagents.reagent_volumes[R.id]
+ if(volume > 0)
+ . += "[R]: [round(volume,0.01)]
"
return . || "None"
/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_available_reagents()
var/output
var/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/SG = locate(/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun) in chassis
- if(SG && SG.reagents && islist(SG.reagents.reagent_list))
- for(var/datum/reagent/R in SG.reagents.reagent_list)
- if(R.volume > 0)
+ if(length(SG.reagents.reagent_volumes))
+ for(var/datum/reagent/R in SG.reagents.get_reagent_datums())
+ var/volume = occupant_legacy.reagents.reagent_volumes[R.id]
+ if(volume > 0)
output += "Inject [R.name]
"
return output
@@ -184,7 +186,7 @@
/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/inject_reagent(var/datum/reagent/R,var/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/SG)
if(!R || !occupant_legacy || !SG || !(SG in chassis.equipment))
return 0
- var/to_inject = min(R.volume, inject_amount)
+ var/to_inject = min(SG.reagents.reagent_volumes[R.id], inject_amount)
if(to_inject && occupant_legacy.reagents.get_reagent_amount(R.id) + to_inject > inject_amount*4)
occupant_message("Sleeper safeties prohibit you from injecting more than [inject_amount*4] units of [R.name].")
else
@@ -194,7 +196,6 @@
SG.reagents.remove_reagent(R.id,to_inject)
occupant_legacy.reagents.add_reagent(R.id,to_inject)
update_equip_info()
- return
/obj/item/mecha_parts/mecha_equipment/tool/sleeper/update_equip_info()
if(..())
@@ -202,7 +203,6 @@
send_byjax(chassis.occupant_legacy,"msleeper.browser","reagents",get_occupant_reagents())
send_byjax(chassis.occupant_legacy,"msleeper.browser","injectwith",get_available_reagents())
return 1
- return
/obj/item/mecha_parts/mecha_equipment/tool/sleeper/verb/eject()
set name = "Sleeper Eject"
diff --git a/code/modules/vehicles/sealed/mecha/equipment/tools/syringe_gun.dm b/code/modules/vehicles/sealed/mecha/equipment/tools/syringe_gun.dm
index b4783e26d63b..549d9170d684 100644
--- a/code/modules/vehicles/sealed/mecha/equipment/tools/syringe_gun.dm
+++ b/code/modules/vehicles/sealed/mecha/equipment/tools/syringe_gun.dm
@@ -192,9 +192,8 @@
/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/get_current_reagents()
var/output
- for(var/datum/reagent/R in reagents.reagent_list)
- if(R.volume > 0)
- output += "[R]: [round(R.volume,0.001)] - Purge Reagent
"
+ for(var/datum/reagent/R in reagents.get_reagent_datums())
+ output += "[R]: [round(reagents.reagent_volumes[R.id],0.001)] - Purge Reagent
"
if(output)
output += "Total: [round(reagents.total_volume,0.001)]/[reagents.maximum_volume] - Purge All"
return output || "None"
@@ -229,7 +228,7 @@
occupant_message("No reagent info gained from [A].")
return 0
occupant_message("Analyzing reagents...")
- for(var/datum/reagent/R in A.reagents.reagent_list)
+ for(var/datum/reagent/R in A.reagents.get_reagent_datums())
if(R.id in known_reagents)
occupant_message("Reagent \"[R.name]\" already present in database, skipping.")
else if(R.reagent_state == 2 && add_known_reagent(R.id,R.name))
diff --git a/code/modules/virus2/centrifuge.dm b/code/modules/virus2/centrifuge.dm
index ffe41f72a5b9..0c71193850c1 100644
--- a/code/modules/virus2/centrifuge.dm
+++ b/code/modules/virus2/centrifuge.dm
@@ -136,7 +136,8 @@
. = TRUE
/obj/machinery/computer/centrifuge/proc/cure()
- if(!sample) return
+ if(!sample)
+ return
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if(!B) return
@@ -150,7 +151,8 @@
ping("\The [src] pings, \"Antibody isolated.\"")
/obj/machinery/computer/centrifuge/proc/isolate()
- if(!sample) return
+ if(!sample)
+ return
var/obj/item/virusdish/dish = new/obj/item/virusdish(loc)
dish.virus2 = virus2
virus2 = null
diff --git a/code/modules/virus2/curer.dm b/code/modules/virus2/curer.dm
index 4735ccdf43ce..55bfac0a6a9c 100644
--- a/code/modules/virus2/curer.dm
+++ b/code/modules/virus2/curer.dm
@@ -22,7 +22,7 @@
return
var/obj/item/reagent_containers/glass/beaker/product = new(src.loc)
- var/list/data = list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"virus2"=list(),"antibodies"=list())
+ var/list/data = list("donor"=null,"blood_DNA"=null,"blood_type"=null, "trace_chem"=null,"virus2"=list(),"antibodies"=list())
data["virus2"] |= I:virus2
product.reagents.add_reagent("blood",30,data)
diff --git a/code/modules/virus2/dishincubator.dm b/code/modules/virus2/dishincubator.dm
index eb32bee10fb0..91505b21df23 100644
--- a/code/modules/virus2/dishincubator.dm
+++ b/code/modules/virus2/dishincubator.dm
@@ -186,15 +186,11 @@
if (!dish)
return 1
- var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
- if (!B)
- return 1
-
- if (!B.data["virus2"])
- B.data["virus2"] = list()
-
- var/list/virus = list("[dish.virus2.uniqueID]" = dish.virus2.getcopy())
- B.data["virus2"] += virus
+ var/datum/blood_mixture/mixture = beaker.reagents.reagent_datas?[/datum/reagent/blood::id]
+ if(!mixture)
+ return TRUE
+ LAZYINITLIST(mixture.legacy_virus2)
+ mixture.legacy_virus2["[dish.virus2.uniqueID]"] = dish.virus2.getcopy()
ping("\The [src] pings, \"Injection complete.\"")
return 1
diff --git a/code/modules/virus2/isolator.dm b/code/modules/virus2/isolator.dm
index 80093b43806d..72433b4d9235 100644
--- a/code/modules/virus2/isolator.dm
+++ b/code/modules/virus2/isolator.dm
@@ -61,18 +61,19 @@
var/list/pathogen_pool = list()
if(sample)
- for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
- var/list/virus = B.data["virus2"]
+ var/datum/blood_mixture/mixture = sample.reagents.get_reagent_data(/datum/reagent/blood)
+ for(var/datum/blood_fragment/blood_data as anything in mixture.fragments)
+ var/list/virus = blood_data.legacy_virus2
for (var/ID in virus)
var/datum/disease2/disease/V = virus[ID]
var/datum/data/record/R = null
if (ID in virusDB)
R = virusDB[ID]
- var/mob/living/carbon/human/D = B.data["donor"]
+ var/mob/living/carbon/human/D = blood_data.legacy_donor
pathogen_pool.Add(list(list(\
- "name" = "[D.get_true_species_name()] [B.name]", \
- "dna" = B.data["blood_DNA"], \
+ "name" = "[D.get_true_species_name()] [B]", \
+ "dna" = blood_data.legacy_blood_dna, \
"unique_id" = V.uniqueID, \
"reference" = "\ref[V]", \
"is_in_database" = !!R, \
@@ -158,11 +159,12 @@
P.info += "
"
- for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
- var/mob/living/carbon/human/D = B.data["donor"]
- P.info += "[D.get_true_species_name()] [B.name]:
[B.data["blood_DNA"]]
"
+ var/datum/blood_mixture/mixture = sample.reagents.get_reagent_data(/datum/reagent/blood)
+ for(var/datum/blood_fragment/blood_data as anything in mixture.fragments)
+ var/mob/living/carbon/human/D = blood_data.legacy_donor
+ P.info += "[D.get_true_species_name()] [B.name]:
[blood_data.legacy_blood_dna]
"
- var/list/virus = B.data["virus2"]
+ var/list/virus = mixture.legacy_virus2
P.info += "Pathogens:
"
if (virus.len > 0)
for (var/ID in virus)
diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm
index d7a14a5e4bfd..fda8e62b9ed8 100644
--- a/code/modules/vore/eating/living_vr.dm
+++ b/code/modules/vore/eating/living_vr.dm
@@ -341,8 +341,8 @@
if(ishuman(src))
var/mob/living/carbon/human/H = src
- if(H.touching.reagent_list.len) // Just the first one otherwise I'll go insane.
- var/datum/reagent/R = H.touching.reagent_list[1]
+ if(H.touching.total_volume) // Just the first one otherwise I'll go insane.
+ var/datum/reagent/R = H.touching.get_majority_reagent_datum()
taste_message += " You also get the flavor of [R.taste_description] from something on them"
return taste_message
@@ -655,7 +655,7 @@
return
if(istype(I,/obj/item/reagent_containers/hypospray/autoinjector))
var/obj/item/reagent_containers/hypospray/autoinjector/A = I
- if(A.reagents && A.reagents.reagent_list.len)
+ if(A.reagents?.total_volume)
if(istype(src,/mob/living/carbon/human)) //in case other mobs besides humans have trashcan trait
to_chat(src, "[A] gets injected into you as you try to consume it!")
A.do_injection(src,src) //a rather strange way of injecting yourself, don't you think?
diff --git a/code/modules/xenoarcheaology/effects/heal.dm b/code/modules/xenoarcheaology/effects/heal.dm
index 4cd6acd9c215..3bc9eba681bd 100644
--- a/code/modules/xenoarcheaology/effects/heal.dm
+++ b/code/modules/xenoarcheaology/effects/heal.dm
@@ -16,13 +16,13 @@
if(affecting && istype(affecting))
affecting.heal_damage(25 * weakness, 25 * weakness)
//H:heal_organ_damage(25, 25)
- H.vessel.add_reagent("blood",5)
+ H.blood_holder.adjust_host_volume(5)
H.nutrition += 50 * weakness
H.adjustBrainLoss(-25 * weakness)
H.cure_radiation(RAD_MOB_CURE_ANOMALY_BURST * weakness)
H.bodytemperature = initial(H.bodytemperature)
spawn(1)
- H.fixblood()
+ H.reset_blood_to_species()
//
C.adjustOxyLoss(-25 * weakness)
C.adjustToxLoss(-25 * weakness)
diff --git a/code/modules/xenoarcheaology/finds/special.dm b/code/modules/xenoarcheaology/finds/special.dm
index 98b8a1f7ceda..327c097c72a3 100644
--- a/code/modules/xenoarcheaology/finds/special.dm
+++ b/code/modules/xenoarcheaology/finds/special.dm
@@ -128,7 +128,7 @@
B.target_turf = pick(range(1, src))
B.blood_DNA = list()
B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
- M.vessel.remove_reagent("blood",rand(25,50))
+ M.erase_blood(rand(25, 50))
//animated blood 2 SPOOKY
/obj/effect/debris/cleanable/blood/splatter/animated
diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm
index d994f8a2ccf6..0b4a607016f1 100644
--- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm
+++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm
@@ -101,7 +101,7 @@
fresh_coolant = 0
coolant_purity = 0
var/num_reagent_types = 0
- for (var/datum/reagent/current_reagent in reagents.reagent_list)
+ for (var/datum/reagent/current_reagent in reagents.get_reagent_datums())
if (!current_reagent)
continue
var/cur_purity = coolant_reagents_purity[current_reagent.id]
@@ -109,8 +109,8 @@
cur_purity = 0.1
else if(cur_purity > 1)
cur_purity = 1
- total_purity += cur_purity * current_reagent.volume
- fresh_coolant += current_reagent.volume
+ total_purity += cur_purity * reagents.reagent_volumes[current_reagent.id]
+ fresh_coolant += reagents.reagent_volumes[current_reagent.id]
num_reagent_types += 1
if(total_purity && fresh_coolant)
coolant_purity = total_purity / fresh_coolant
diff --git a/maps/away_missions/140x140/zoo.dmm b/maps/away_missions/140x140/zoo.dmm
index d29742391bfd..c8a1ecf61e9c 100644
--- a/maps/away_missions/140x140/zoo.dmm
+++ b/maps/away_missions/140x140/zoo.dmm
@@ -3771,7 +3771,7 @@
/area/awaymission/zoo)
"kv" = (
/obj/structure/table/rack,
-/obj/item/toy/waterflower,
+/obj/item/reagent_containers/spray/waterflower,
/turf/simulated/floor/holofloor/carpet,
/area/awaymission/zoo)
"kw" = (