diff --git a/citadel.dme b/citadel.dme index 61be27550de..35b411a3758 100644 --- a/citadel.dme +++ b/citadel.dme @@ -423,6 +423,7 @@ #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\visual_effects.dm" #include "code\__HELPERS\animations\attack.dm" +#include "code\__HELPERS\color\decode.dm" #include "code\__HELPERS\datastructs\bodytypes.dm" #include "code\__HELPERS\datastructs\filters.dm" #include "code\__HELPERS\datastructs\ingredients.dm" @@ -1662,7 +1663,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" @@ -1844,6 +1844,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" @@ -1975,7 +1977,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" @@ -3768,6 +3770,7 @@ #include "code\modules\mob\living\bot\secbot.dm" #include "code\modules\mob\living\bot\SLed209bot.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" @@ -4751,17 +4754,19 @@ #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-application.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" @@ -4784,13 +4789,35 @@ #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\blood\blood.dm" +#include "code\modules\reagents\chemistry\reagents\blood\blood_fragment.dm" +#include "code\modules\reagents\chemistry\reagents\blood\blood_holder.dm" +#include "code\modules\reagents\chemistry\reagents\blood\blood_mixture.dm" +#include "code\modules\reagents\chemistry\reagents\core\acid.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\firefighting_foam.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" @@ -4820,8 +4847,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/__DEFINES/_lists.dm b/code/__DEFINES/_lists.dm index 0496f1c3ef8..5369a21c3af 100644 --- a/code/__DEFINES/_lists.dm +++ b/code/__DEFINES/_lists.dm @@ -55,12 +55,12 @@ * Binary search sorted insert * Sorts low to high. * - * INPUT: Object to be inserted - * LIST: List to insert object into - * TYPECONT: The typepath of the contents of the list - * COMPARE: The object to compare against, usualy the same as INPUT - * COMPARISON: The variable on the objects to compare - * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE. + * * INPUT: Object to be inserted + * * LIST: List to insert object into + * * TYPECONT: The typepath of the contents of the list + * * COMPARE: The object to compare against, usualy the same as INPUT + * * COMPARISON: The variable on the objects to compare + * * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE. */ #define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ do {\ diff --git a/code/__DEFINES/reagents/reagent_holder.dm b/code/__DEFINES/reagents/reagent_holder.dm index 6cc21e3cd0b..67c3bbe1f39 100644 --- a/code/__DEFINES/reagents/reagent_holder.dm +++ b/code/__DEFINES/reagents/reagent_holder.dm @@ -1,6 +1,11 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station Developers *// +//* Constants *// + +#define REAGENT_HOLDER_VOLUME_PRECISION 0.00001 +#define REAGENT_HOLDER_VOLUME_QUANTIZE(VAL) round(VAL, REAGENT_HOLDER_VOLUME_PRECISION) + //* flags for /datum/reagent_holder/var/reagent_holder_flags *// /// currently reacting diff --git a/code/__HELPERS/color/decode.dm b/code/__HELPERS/color/decode.dm new file mode 100644 index 00000000000..0653f985bb0 --- /dev/null +++ b/code/__HELPERS/color/decode.dm @@ -0,0 +1,30 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * Decodes a #rrggbb(aa) string. + * + * * returned values are from 0 to 255. + * + * @params + * * rgba_string - #rrggbb(aa) string + * * default_alpha - what alpha is if 'aa' isn't there + * + * @return list(r, g, b, a), or null on parse error. + */ +/proc/decode_rgba(rgba_string, default_alpha = 255) + var/offset = 0 + if(rgba_string[1] == "#") + offset = 1 + switch(length(rgba_string) - offset) + if(6, 8) + else + CRASH("parse error on rgba string '[rgba_string]") + // todo: benchmark this is this faster than copytext() + return list( + hex2num(copytext(rgba_string, 1 + offset, 3 + offset)), + hex2num(copytext(rgba_string, 3 + offset, 5 + offset)), + hex2num(copytext(rgba_string, 5 + offset, 7 + offset)), + // it's over 7 if alpha exists, even with a # + length(rgba_string) > 7 ? hex2num(copytext(rgba_string, 7 + offset, 9 + offset)) : default_alpha, + ) diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index 53c7f16bb37..a2d26e2c8e6 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 f372fb790eb..d2cfc8935f9 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 2bb732f9cc9..f99bea23044 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 0801c02d462..6c0cf109761 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 @@ -309,7 +311,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() @@ -683,12 +684,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-defense.dm b/code/game/atoms/atom-defense.dm index 3070e2c72b1..dfc481c1b71 100644 --- a/code/game/atoms/atom-defense.dm +++ b/code/game/atoms/atom-defense.dm @@ -549,7 +549,7 @@ /atom/proc/atom_shieldcall_handle_touch(datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific, fake_attack, shieldcall_flags) SHOULD_NOT_SLEEP(TRUE) // cannot parry yourself - if(e_args.performer == src) + if(e_args?.performer == src) return shieldcall_flags // send query signal SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_TOUCH) diff --git a/code/game/atoms/atom-examine.dm b/code/game/atoms/atom-examine.dm index 55c17dbd88d..0b0f24d6a08 100644 --- a/code/game/atoms/atom-examine.dm +++ b/code/game/atoms/atom-examine.dm @@ -76,19 +76,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 f02c792439f..f219e6ad36a 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 fabdabb10eb..14e2a73daf0 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 9ecf521ff10..17a0475630c 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 4be195060f0..b8674daa4a5 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 abfa87f6cf7..3c051228fcf 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/OpTable.dm b/code/game/machinery/OpTable.dm index 83993209301..2b0e98397fd 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -66,7 +66,7 @@ user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") else visible_message(SPAN_NOTICE("\The [C] has been laid on \the [src] by [user].")) - C.resting = 1 + C.set_intentionally_resting(TRUE) C.forceMove(loc) // now that we hold parts, this must be commented out to prevent dumping our parts onto our loc. not sure what this was intended to do when it was written. /*for(var/obj/O in src) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 1fb7a028bb9..cecdda1b607 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 580aa6c68a2..ef7ff6616ec 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -270,7 +270,7 @@ update_icon() // Update health display for consoles with light and such. var/mob/living/carbon/human/H = scanner.occupant occupantData["name"] = H.name - occupantData["stat"] = H.stat + occupantData["stbat"] = H.stat occupantData["health"] = H.health occupantData["maxHealth"] = H.getMaxHealth() @@ -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 && H.species.blood_volume) + 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 b9a8ce78f73..0e49e92e8f0 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 = loaded_blood_mixture.unsafe_get_fragment_ref(1) + 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.unsafe_get_fragment_ref(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 4e203298c62..98647ace0ea 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 2e1f80158f1..176a655ec25 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -113,10 +113,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 bea3f96a3f8..11af8bb0056 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 34318abdebc..fc963c5e33e 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 d652621f749..a8fdadedd55 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 c5e622c1fe6..472cb48dfa3 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 07116a335ee..49c0675e2f6 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/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index ef8cd510edd..e53ef77d95e 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -99,16 +99,10 @@ if(!location) return - if(chemholder.reagents.reagent_list.len) //reagent application - only run if there are extra reagents in the smoke - for(var/turf/T in wallList) - chemholder.reagents.touch_turf(T) - for(var/turf/T in targetTurfs) - chemholder.reagents.touch_turf(T) - for(var/atom/A in T.contents) - if(istype(A, /obj/effect/particle_effect/smoke/chem) || istype(A, /mob)) - continue - else if(isobj(A) && (A.atom_flags & ATOM_ABSTRACT)) - chemholder.reagents.touch_obj(A) + var/list/reapplication_exclusion_injection = list() + if(length(chemholder.reagents.reagent_volumes)) + for(var/turf/T in wallList + targetTurfs) + chemholder.reagents.perform_uniform_contact(T, 1, null, reapplication_exclusion_injection) var/color = chemholder.reagents.get_color() //build smoke icon var/icon/I @@ -157,8 +151,7 @@ else smoke = new /obj/effect/particle_effect/smoke/chem(location) - if(chemholder.reagents.reagent_list.len) - chemholder.reagents.trans_to_obj(smoke, chemholder.reagents.total_volume / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents + chemholder.reagents.transfer_to_holder(smoke.reagents, null, chemholder.reagents.total_volume / dist, TRUE) smoke.icon = I smoke.plane = ABOVE_PLANE smoke.setDir(pick(GLOB.cardinal)) diff --git a/code/game/objects/effects/chem/coating.dm b/code/game/objects/effects/chem/coating.dm index 246d22ffd5a..c980a78a9a4 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 0508452dba0..7d4a9d3e396 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -2,6 +2,9 @@ // Similar to smoke, but spreads out more // metal foams leave behind a foamed metal wall +/** + * * reagents are handled in a special manner as it's a shared ref. + */ /obj/effect/foam name = "foam" icon = 'icons/effects/effects.dmi' @@ -18,6 +21,8 @@ var/dries = 1 var/slips = 0 + var/datum/reagent_holder/carried_reagents_shared_ref + /obj/effect/foam/Initialize(mapload, ismetal = FALSE) . = ..() metal = ismetal @@ -27,6 +32,11 @@ addtimer(CALLBACK(src, PROC_REF(pre_harden)), 12 SECONDS) addtimer(CALLBACK(src, PROC_REF(harden)), 15 SECONDS) +/obj/effect/foam/Destroy() + if(carried_reagents_shared_ref) + carried_reagents_shared_ref = null + return ..() + /obj/effect/foam/proc/post_spread() process() checkReagents() @@ -42,11 +52,9 @@ QDEL_IN(src, 5) /obj/effect/foam/proc/checkReagents() // transfer any reagents to the floor - if(!metal && reagents) + if(!metal && carried_reagents_shared_ref) var/turf/T = get_turf(src) - reagents.touch_turf(T) - for(var/obj/O in T) - reagents.touch_obj(O) + carried_reagents_shared_ref.perform_uniform_contact(T, 1) /obj/effect/foam/process() if(--amount < 0) @@ -66,11 +74,7 @@ F = new(T, metal) F.amount = amount - 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) + F.carried_reagents_shared_ref = carried_reagents_shared_ref /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))) @@ -92,8 +96,8 @@ /datum/effect_system/foam_spread /// The size of the foam spread. var/amount = 5 - /// The IDs of reagents present when the foam was mixed. - var/list/carried_reagents + /// reagent holder for carried reagents + var/datum/reagent_holder/carried_reagents /// 0 = foam, 1 = metalfoam, 2 = ironfoam. var/metal = 0 @@ -104,14 +108,16 @@ else location = get_turf(loca) - carried_reagents = list() metal = metalfoam // bit of a hack here. Foam carries along any reagent also present in the glass it is mixed with (defaults to water if none is present). Rather than actually transfer the reagents, this makes a list of the reagent ids and spawns 1 unit of that reagent when the foam disolves. - if(carry && !metal) - for(var/datum/reagent/R in carry.reagent_list) - carried_reagents += R.id + if(!metal) + if(carry) + carried_reagents = carry.clone() + else + carried_reagents = new + carried_reagents.add_reagent(/datum/reagent/water::id, 10) /datum/effect_system/foam_spread/start() spawn(0) @@ -124,13 +130,8 @@ F.amount = amount if(!metal) // don't carry other chemicals if a metal foam - F.create_reagents(10) - - if(carried_reagents) - for(var/id in carried_reagents) - F.reagents.add_reagent(id, 1, null, TRUE) - else - F.reagents.add_reagent("water", 1, null, TRUE) + // directly reference the reagents + F.carried_reagents_shared_ref = carried_reagents // wall formed by metal foams, dense and opaque, but easy to break diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index b5f941c096c..30b31ab161c 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -4,46 +4,35 @@ icon_state = "extinguish" mouse_opacity = MOUSE_OPACITY_TRANSPARENT pass_flags = ATOM_PASS_TABLE | ATOM_PASS_GRILLE | ATOM_PASS_BLOB - var/list/touched + + var/list/reagents_reapplication_injection /obj/effect/water/Initialize(mapload) . = ..() + reagents_reapplication_injection = list() QDEL_IN(src, 15 SECONDS) +/obj/effect/water/Destroy() + reagents_reapplication_injection = null + return ..() + /obj/effect/water/proc/set_color() // Call it after you move reagents to it icon += reagents.get_color() /obj/effect/water/proc/set_up(var/turf/target, var/step_count = 5, var/delay = 5) if(!target) return - touched = list() QDEL_IN(src, step_count * delay + 10) for(var/i = 1 to step_count) if(!loc) return step_towards(src, target) - var/turf/T = get_turf(src) - if(T && reagents) - if(!(T in touched)) - touched |= T - reagents.touch_turf(T) - for(var/atom/movable/AM in T) - if(!isobj(AM) && !ismob(AM)) - continue - if(AM in touched) - continue - touched |= AM - reagents.touch(AM, reagents.total_volume) + reagents?.perform_uniform_contact(get_turf(src), 1, null, reagents_reapplication_injection) sleep(delay) /obj/effect/water/Crossed(atom/movable/AM, oldloc) - . = ..() - if(!isobj(AM) && !ismob(AM)) - return - if(AM in touched) - return - touched |= AM - reagents?.touch(AM, reagents.total_volume) + ..() + reagents?.perform_uniform_contact(AM, 1, null, reagents_reapplication_injection) //Used by spraybottles. /obj/effect/water/chempuff diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index 52b8c27982d..eb2af8e3a75 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 ef096a4caff..45217057a61 100644 --- a/code/game/objects/items/defib/shockpaddles.dm +++ b/code/game/objects/items/defib/shockpaddles.dm @@ -135,7 +135,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 f75e992a18f..a9bc680d5a5 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 0ae10d896e4..11b479a84c2 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 a5a907d2b98..e8305361ac7 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) @@ -257,8 +257,8 @@ dat += ib_dat // Blood level - if(M:vessel) - var/blood_volume = H.vessel.get_reagent_amount("blood") + if(H.blood_holder) + 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 b517b638c1c..73acc8b91f3 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 b76223f485b..c32663a7855 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 d5619348f47..8668ba41ada 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 00000000000..9b405f7b561 --- /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.perform_entity_splash(A, 1) + 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.perform_entity_splash(A, 1) + 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 915f314f32c..02faa2c0f85 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys/toys.dm @@ -27,62 +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" - QDEL_IN(src, 5) - -/obj/item/toy/balloon/update_icon_state() - if(reagents.total_volume) - icon_state = "waterballoon" - else - icon_state = "waterballoon-e" - return ..() /obj/item/toy/syndicateballoon name = "criminal balloon" @@ -399,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/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index e64357af070..6454de2a994 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -170,9 +170,8 @@ steam.attach(src) steam.start() - for(var/atom/A in view(affected_area, src.loc)) - if( A == src ) continue - src.reagents.touch(A) + for(var/turf/spraying_turf in view(affected_area, get_turf(src))) + reagents.perform_uniform_contact(spraying_turf, 1) if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand var/mob/living/carbon/C = loc diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index 78c908ab1fe..12ea456f4b5 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 a800c3c4b89..97dc86d8cd5 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 05f1c768146..34149bf080e 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 999bdbbaa7e..807fe344005 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 62daecd72cc..080c1d36241 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 fdf66d521e5..3e97be72950 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 9a8a1fac185..8c3c57d7f8d 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 65a6a64e32f..97edc8f12b2 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 2ce50b7d500..3da94ee4f1c 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/bottle/alcohol.dm b/code/modules/food/drinks/bottle/alcohol.dm index 3063a90eae0..c75de0fa844 100644 --- a/code/modules/food/drinks/bottle/alcohol.dm +++ b/code/modules/food/drinks/bottle/alcohol.dm @@ -6,144 +6,144 @@ desc = "One sip of this and you just know you're gonna have a good time." icon_state = "absinthebottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/ethanol/absinthe + start_with_single_reagent = /datum/reagent/ethanol/absinthe /obj/item/reagent_containers/food/drinks/bottle/bitters name = "Rattison's Bitters" desc = "The best thing to bitters you're going to get in this sector." icon_state = "bittersbottle" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/bitters + start_with_single_reagent = /datum/reagent/ethanol/bitters /obj/item/reagent_containers/food/drinks/bottle/bluecuracao name = "Miss Blue Curacao" desc = "A fruity, exceptionally azure drink. Does not allow the imbiber to use the fifth magic." icon_state = "alco-blue" //Placeholder. center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/ethanol/bluecuracao + start_with_single_reagent = /datum/reagent/ethanol/bluecuracao /obj/item/reagent_containers/food/drinks/bottle/cognac name = "Chateau De Baton Premium Cognac" desc = "A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing." icon_state = "cognacbottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/ethanol/cognac + start_with_single_reagent = /datum/reagent/ethanol/cognac /obj/item/reagent_containers/food/drinks/bottle/gin name = "Griffeater Gin" desc = "A bottle of high quality gin, produced in Alpha Centauri." icon_state = "ginbottle" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/gin + start_with_single_reagent = /datum/reagent/ethanol/gin /obj/item/reagent_containers/food/drinks/bottle/goldschlager name = "College Girl Goldschlager" desc = "Because they are the only ones who will drink 100 proof cinnamon schnapps." icon_state = "goldschlagerbottle" center_of_mass = list("x"=15, "y"=3) - start_reagent = /datum/reagent/ethanol/goldschlager + start_with_single_reagent = /datum/reagent/ethanol/goldschlager /obj/item/reagent_containers/food/drinks/bottle/kahlua name = "Robert Robust's Coffee Liqueur" desc = "A widely known, Mexican coffee-flavoured liqueur. In production since 1936." icon_state = "kahluabottle" center_of_mass = list("x"=17, "y"=3) - start_reagent = /datum/reagent/ethanol/coffee/kahlua + start_with_single_reagent = /datum/reagent/ethanol/coffee/kahlua /obj/item/reagent_containers/food/drinks/bottle/melonliquor name = "Emeraldine Melon Liquor" desc = "A bottle of 46 proof Emeraldine Melon Liquor. Sweet and light." icon_state = "alco-green" //Placeholder. center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/ethanol/melonliquor + start_with_single_reagent = /datum/reagent/ethanol/melonliquor /obj/item/reagent_containers/food/drinks/bottle/messa_mead name = "Messa's Mead" desc = "A sweet liquor from Adhomai. Orginally considered a drink of the Tajaran nobility improved technology has made this honey and root based liqour available all across Adhomai and the even the stars beyond." icon_state = "messamead" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/messa_mead + start_with_single_reagent = /datum/reagent/ethanol/messa_mead /obj/item/reagent_containers/food/drinks/bottle/patron name = "Wrapp Artiste Patron" desc = "Silver laced tequila, served in space night clubs across the galaxy." icon_state = "patronbottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/ethanol/patron + start_with_single_reagent = /datum/reagent/ethanol/patron /obj/item/reagent_containers/food/drinks/bottle/pwine name = "Warlock's Velvet" desc = "What a delightful packaging for a surely high quality wine! The vintage must be amazing!" icon_state = "pwinebottle" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/pwine + start_with_single_reagent = /datum/reagent/ethanol/pwine /obj/item/reagent_containers/food/drinks/bottle/rum name = "Captain Pete's Cuban Spiced Rum" desc = "This isn't just rum, oh no. It's practically Cuba in a bottle." icon_state = "rumbottle" center_of_mass = list("x"=16, "y"=8) - start_reagent = /datum/reagent/ethanol/rum + start_with_single_reagent = /datum/reagent/ethanol/rum /obj/item/reagent_containers/food/drinks/bottle/specialwhiskey name = "Special Blend Whiskey" desc = "Just when you thought regular station whiskey was good... This silky, amber goodness has to come along and ruin everything." icon_state = "whiskeybottle2" center_of_mass = list("x"=16, "y"=3) - start_reagent = /datum/reagent/ethanol/specialwhiskey + start_with_single_reagent = /datum/reagent/ethanol/specialwhiskey /obj/item/reagent_containers/food/drinks/bottle/tequila name = "Caccavo Guaranteed Quality Tequila" desc = "Made from premium petroleum distillates, pure thalidomide and other fine quality ingredients!" icon_state = "tequilabottle" center_of_mass = list("x"=16, "y"=3) - start_reagent = /datum/reagent/ethanol/tequila + start_with_single_reagent = /datum/reagent/ethanol/tequila /obj/item/reagent_containers/food/drinks/bottle/vermouth name = "Goldeneye Vermouth" desc = "Sweet, sweet dryness~" icon_state = "vermouthbottle" center_of_mass = list("x"=17, "y"=3) - start_reagent = /datum/reagent/ethanol/vermouth + start_with_single_reagent = /datum/reagent/ethanol/vermouth /obj/item/reagent_containers/food/drinks/bottle/victory_gin name = "Victory Gin" desc = "An oily Tajara liquor similar to gin. Bottled at one of the countless distilleries on Adhomai." icon_state = "victorygin" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/victory_gin + start_with_single_reagent = /datum/reagent/ethanol/victory_gin /obj/item/reagent_containers/food/drinks/bottle/vodka name = "Tunguska Triple Distilled" desc = "Aah, vodka. Prime choice of drink and fuel by Russians worldwide." icon_state = "vodkabottle" center_of_mass = list("x"=17, "y"=3) - start_reagent = /datum/reagent/ethanol/vodka + start_with_single_reagent = /datum/reagent/ethanol/vodka /obj/item/reagent_containers/food/drinks/bottle/whiskey name = "Uncle Git's Special Reserve" desc = "A premium single-malt whiskey, gently matured inside the tunnels of a nuclear shelter." icon_state = "whiskeybottle" center_of_mass = list("x"=16, "y"=3) - start_reagent = /datum/reagent/ethanol/whiskey + start_with_single_reagent = /datum/reagent/ethanol/whiskey /obj/item/reagent_containers/food/drinks/bottle/whiterum name = "Captain Pete's Cuban White Rum" desc = "A milky, white alternative to Cuban Spiced! Tastes like coconut." icon_state = "whiterumbottle" center_of_mass = list("x"=16, "y"=8) - start_reagent = /datum/reagent/ethanol/whiterum + start_with_single_reagent = /datum/reagent/ethanol/whiterum /obj/item/reagent_containers/food/drinks/bottle/whitewine name = "Crab Cove Sauvignon Blanc" desc = "White wine that's oddly better than the company's red variant." icon_state = "whitewinebottle" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/whitewine + start_with_single_reagent = /datum/reagent/ethanol/whitewine /obj/item/reagent_containers/food/drinks/bottle/wine name = "Crab Cove Merlot" desc = "Cheap red cooking wine pretending to be drinkable." icon_state = "winebottle" center_of_mass = list("x"=16, "y"=4) - start_reagent = /datum/reagent/ethanol/wine + start_with_single_reagent = /datum/reagent/ethanol/wine diff --git a/code/modules/food/drinks/bottle/dairy.dm b/code/modules/food/drinks/bottle/dairy.dm index 301c9fb2838..b2299408aa6 100644 --- a/code/modules/food/drinks/bottle/dairy.dm +++ b/code/modules/food/drinks/bottle/dairy.dm @@ -8,7 +8,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=8) isGlass = 0 - start_reagent = /datum/reagent/drink/milk/cream + start_with_single_reagent = /datum/reagent/drink/milk/cream /obj/item/reagent_containers/food/drinks/bottle/milk name = "Large Milk Carton" @@ -17,4 +17,4 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=9) isGlass = 0 - start_reagent = /datum/reagent/drink/milk + start_with_single_reagent = /datum/reagent/drink/milk diff --git a/code/modules/food/drinks/bottle/juice.dm b/code/modules/food/drinks/bottle/juice.dm index b11d8926adb..e4f9670d3d7 100644 --- a/code/modules/food/drinks/bottle/juice.dm +++ b/code/modules/food/drinks/bottle/juice.dm @@ -8,7 +8,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=7) isGlass = 0 - start_reagent = /datum/reagent/drink/juice/apple + start_with_single_reagent = /datum/reagent/drink/juice/apple /obj/item/reagent_containers/food/drinks/bottle/coconutmilk name = "Coconut Milk" @@ -17,7 +17,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=8) isGlass = 0 - start_reagent = /datum/reagent/drink/milk/coconutmilk + start_with_single_reagent = /datum/reagent/drink/milk/coconutmilk /obj/item/reagent_containers/food/drinks/bottle/lemonjuice name = "Lemon Juice" @@ -26,7 +26,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=8) isGlass = 0 - start_reagent = /datum/reagent/drink/juice/lemon + start_with_single_reagent = /datum/reagent/drink/juice/lemon /obj/item/reagent_containers/food/drinks/bottle/limejuice name = "Lime Juice" @@ -35,7 +35,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=8) isGlass = 0 - start_reagent = /datum/reagent/drink/juice/lime + start_with_single_reagent = /datum/reagent/drink/juice/lime /obj/item/reagent_containers/food/drinks/bottle/orangejuice name = "Orange Juice" @@ -44,7 +44,7 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=7) isGlass = 0 - start_reagent = /datum/reagent/drink/juice/orange + start_with_single_reagent = /datum/reagent/drink/juice/orange /obj/item/reagent_containers/food/drinks/bottle/tomatojuice name = "Tomato Juice" @@ -53,4 +53,4 @@ item_state = "carton" center_of_mass = list("x"=16, "y"=8) isGlass = 0 - start_reagent = /datum/reagent/drink/juice/tomato + start_with_single_reagent = /datum/reagent/drink/juice/tomato diff --git a/code/modules/food/drinks/bottle/misc.dm b/code/modules/food/drinks/bottle/misc.dm index 3c4d06fc470..7d4baec3e86 100644 --- a/code/modules/food/drinks/bottle/misc.dm +++ b/code/modules/food/drinks/bottle/misc.dm @@ -6,18 +6,18 @@ desc = "A bottle filled with nothing" icon_state = "bottleofnothing" center_of_mass = list("x"=17, "y"=5) - start_reagent = /datum/reagent/drink/nothing + start_with_single_reagent = /datum/reagent/drink/nothing /obj/item/reagent_containers/food/drinks/bottle/grenadine name = "Briar Rose Grenadine Syrup" desc = "Sweet and tangy, a bar syrup used to add color or flavor to drinks." icon_state = "grenadinebottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/drink/grenadine + start_with_single_reagent = /datum/reagent/drink/grenadine /obj/item/reagent_containers/food/drinks/bottle/holywater name = "Flask of Holy Water" desc = "A flask of the chaplain's holy water." icon_state = "holyflask" center_of_mass = list("x"=17, "y"=10) - start_reagent = /datum/reagent/water/holywater + start_with_single_reagent = /datum/reagent/water/holywater diff --git a/code/modules/food/drinks/bottle/soda.dm b/code/modules/food/drinks/bottle/soda.dm index b727501ff97..ef218affca8 100644 --- a/code/modules/food/drinks/bottle/soda.dm +++ b/code/modules/food/drinks/bottle/soda.dm @@ -6,18 +6,18 @@ desc = "Cola. in space" icon_state = "colabottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/drink/soda + start_with_single_reagent = /datum/reagent/drink/soda /obj/item/reagent_containers/food/drinks/bottle/space_mountain_wind name = "\improper Space Mountain Wind" desc = "Blows right through you like a space wind." icon_state = "space_mountain_wind_bottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/drink/soda/spacemountainwind + start_with_single_reagent = /datum/reagent/drink/soda/spacemountainwind /obj/item/reagent_containers/food/drinks/bottle/space_up name = "\improper Space-Up" desc = "Tastes like a hull breach in your mouth." icon_state = "space-up_bottle" center_of_mass = list("x"=16, "y"=6) - start_reagent = /datum/reagent/drink/soda/space_up + start_with_single_reagent = /datum/reagent/drink/soda/space_up diff --git a/code/modules/food/drinks/carton.dm b/code/modules/food/drinks/carton.dm index 1f298f68fb5..efa63b5c5c8 100644 --- a/code/modules/food/drinks/carton.dm +++ b/code/modules/food/drinks/carton.dm @@ -9,7 +9,7 @@ drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' volume = 50 - start_reagent = /datum/reagent/drink/milk + start_with_single_reagent = /datum/reagent/drink/milk /obj/item/reagent_containers/food/drinks/soymilk name = "soymilk carton" @@ -20,7 +20,7 @@ drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' volume = 50 - start_reagent = /datum/reagent/drink/milk/soymilk + start_with_single_reagent = /datum/reagent/drink/milk/soymilk /obj/item/reagent_containers/food/drinks/smallmilk name = "small milk carton" @@ -31,7 +31,7 @@ drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' volume = 30 - start_reagent = /datum/reagent/drink/milk + start_with_single_reagent = /datum/reagent/drink/milk /obj/item/reagent_containers/food/drinks/smallchocmilk name = "small chocolate milk carton" @@ -42,7 +42,7 @@ drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' volume = 30 - start_reagent = /datum/reagent/drink/milk/chocolate + start_with_single_reagent = /datum/reagent/drink/milk/chocolate //* Vetalan @@ -54,7 +54,7 @@ center_of_mass = list("x"=16, "y"=9) drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - start_reagent = /datum/reagent/blood + start_with_single_reagent = /datum/reagent/blood /obj/item/reagent_containers/food/drinks/bludboxmaxlight name = "Bludbox MAX Light carton" @@ -64,7 +64,8 @@ center_of_mass = list("x"=16, "y"=9) drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - start_reagent = /datum/reagent/blood/synthblood + start_with_single_reagent = /datum/reagent/blood + start_with_single_data_initializer = new /datum/blood_mixture/preset/single/synthblood /obj/item/reagent_containers/food/drinks/bludbox name = "Bludbox carton" @@ -74,7 +75,8 @@ center_of_mass = list("x"=16, "y"=9) drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - start_reagent = /datum/reagent/drink/blud + start_with_single_reagent = /datum/reagent/drink/blud + start_with_single_data_initializer = new /datum/blood_mixture/preset/single/synthblood /obj/item/reagent_containers/food/drinks/bludboxlight name = "Bludbox Light carton" @@ -84,5 +86,5 @@ center_of_mass = list("x"=16, "y"=9) drop_sound = 'sound/items/drop/cardboardbox.ogg' pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - start_reagent = /datum/reagent/drink/blud/bludlight - + start_with_single_reagent = /datum/reagent/drink/blud/bludlight + start_with_single_data_initializer = new /datum/blood_mixture/preset/single/synthblood diff --git a/code/modules/food/drinks/cup.dm b/code/modules/food/drinks/cup.dm index 5e4f9e26681..d2d89b515c4 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 db60cc81623..cb4c7d1d9cc 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/drinks/misc.dm b/code/modules/food/drinks/misc.dm index fecb7daea8f..63b7c68932d 100644 --- a/code/modules/food/drinks/misc.dm +++ b/code/modules/food/drinks/misc.dm @@ -5,7 +5,7 @@ drop_sound = 'sound/items/drop/cardboardbox.ogg' volume = 30 center_of_mass = list("x"=15, "y"=13) - start_reagent = /datum/reagent/drink/glue + start_with_single_reagent = /datum/reagent/drink/glue /obj/item/reagent_containers/food/drinks/glue/on_reagent_change() ..() diff --git a/code/modules/food/food.dm b/code/modules/food/food.dm index 64318cdd0de..a7e966b53ad 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 e23b5299148..e434f36269c 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 22144611a5b..a1a40f59206 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 4992efb9a1e..58b5f777cf1 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -3985,29 +3985,26 @@ 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)) + if (apply_coating(R, user, target.reagents)) return 1 return ..() //This proc handles drawing coatings out of a container when this food is dipped into it -/obj/item/reagent_containers/food/snacks/proc/apply_coating(var/datum/reagent/nutriment/coating/C, var/mob/user) +/obj/item/reagent_containers/food/snacks/proc/apply_coating(datum/reagent/nutriment/coating/C, mob/user, datum/reagent_holder/coating_holder) if (coating) to_chat(user, "The [src] is already coated in [coating.name]!") return 0 //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 @@ -4015,24 +4012,17 @@ END CITADEL CHANGE */ //the food has no reagents left, its probably getting deleted soon return 0 - if (C.volume < req) + if (coating_holder.reagent_volumes?[C.id] < req) to_chat(user, SPAN_WARNING( "There's not enough [C.name] to coat the [src]!")) return 0 - var/id = C.id - //First make sure there's space for our batter if (reagents.available_volume() < req+5) var/extra = req+5 - reagents.available_volume() reagents.maximum_volume += extra //Suck the coating out of the holder - 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) - if (!C) - return + coating_holder.transfer_to_holder(src, list(C.id), req) coating = C //Now we have to do the witchcraft with masking images @@ -4086,12 +4076,11 @@ END CITADEL CHANGE */ if (do_coating_prefix == 1) name = "[coating.coated_adj] [name]" - for (var/r in reagents.reagent_list) - var/datum/reagent/R = r - if (istype(R, /datum/reagent/nutriment/coating)) - var/datum/reagent/nutriment/coating/C = R - C.data["cooked"] = 1 - C.name = C.cooked_name + for(var/datum/reagent/nutriment/coating/coating in reagents.get_reagent_datums()) + var/datum/nutriment_data/coating_data = reagents.reagent_datas?[coating.id] + if(!coating_data) + continue + coating_data.cooked = TRUE /obj/item/reagent_containers/food/snacks/proc/on_consume(var/mob/eater, var/mob/feeder = null) if(!reagents.total_volume) @@ -4259,7 +4248,7 @@ END CITADEL CHANGE */ /obj/item/reagent_containers/food/snacks/sliceable/pizza/crunch/Initialize(mapload) . = ..() reagents.add_reagent("batter", 6.5) - coating = reagents.get_reagent("batter") + coating = SSchemistry.fetch_reagent(/datum/reagent/nutriment/coating/batter::id) reagents.add_reagent("cooking_oil", 4) bitesize = 2 diff --git a/code/modules/food/machinery/appliance/_appliance.dm b/code/modules/food/machinery/appliance/_appliance.dm index 7f6356ef76c..c723c8f36aa 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 4005e7741f0..e7990da77b0 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 03ee80157ae..dfc44a5db88 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 @@ -221,10 +211,10 @@ //That would really require coding some sort of filter or better replacement mechanism first //So for now, restrict to oil only var/amount = 0 - for (var/datum/reagent/R in I.reagents.reagent_list) + for (var/datum/reagent/R in I.reagents.get_reagent_datums()) if (istype(R, /datum/reagent/nutriment/triglyceride/oil)) var/delta = oil.available_volume() - delta = min(delta, R.volume) + delta = min(delta, I.reagents.get_reagent_amount(R)) oil.add_reagent(R.id, delta) I.reagents.remove_reagent(R.id, delta) amount += delta diff --git a/code/modules/food/machinery/microwave.dm b/code/modules/food/machinery/microwave.dm index 8318bb6802c..27ba7308e3d 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 5e51f8c1ffe..3e62aca8196 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 707cf82533f..9ee8370ecaa 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -1,3 +1,5 @@ +// todo: use something like /datum/component/atmos_connector_attach +// todo: /obj/machinery/hydroponics_tray or /obj/machinery/portable_atmospherics/hydroponics_tray /obj/machinery/portable_atmospherics/hydroponics name = "hydroponics tray" icon = 'icons/obj/hydroponics_machines.dmi' @@ -233,15 +235,15 @@ //Process reagents being input into the tray. /obj/machinery/portable_atmospherics/hydroponics/proc/process_reagents() - - if(!reagents) return + if(!reagents) + return if(reagents.total_volume <= 0) return 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 a862baf5599..196423ba1cd 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 fad69b5c6d4..50d20cec6b5 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 cf65354fc59..791786e2e70 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() @@ -656,13 +650,11 @@ return if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) return - for(var/datum/reagent/G in source.reagents?.reagent_list) - if (!direc) - if(G.id in demand) - source.reagents.trans_id_to(target, G.id, transfer_amount) - else - if(!(G.id in demand)) - source.reagents.trans_id_to(target, G.id, transfer_amount) + source.reagents.transfer_to_holder( + target, + direc ? source.reagents.reagent_volumes - demand : demand, + transfer_amount, + ) activate_pin(2) push_data() diff --git a/code/modules/loot/packs/misc.dm b/code/modules/loot/packs/misc.dm index c7dad6fb77a..767d4e016aa 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 895abfebedc..31c006470e3 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 4b683e5b746..52c6ad05efa 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/carbon-blood.dm b/code/modules/mob/living/carbon/carbon-blood.dm new file mode 100644 index 00000000000..9827a67625e --- /dev/null +++ b/code/modules/mob/living/carbon/carbon-blood.dm @@ -0,0 +1,174 @@ +//* 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 + + blood_holder.set_host_fragment_to(create_natural_blood_fragment()) + blood_holder.do_assimilate_all() + if(!do_not_regenerate) + blood_holder.set_host_volume(species.blood_volume) + + +/** + * 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.checked_erase(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) + + +/** + * 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 + 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 3d1fbe87312..8a5164b6f00 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 f220de966e8..b64b0e99dad 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() @@ -66,23 +32,16 @@ var/const/CE_STABLE_THRESHOLD = 0.5 var/mechanical_circulation = HAS_TRAIT(src, TRAIT_MECHANICAL_CIRCULATION) if(stat != DEAD && bodytemperature >= 170) //Dead or cryosleep people do not pump the blood. + // assimilate foreign blood separately + blood_holder.do_assimilate(0.5) - 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. //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,125 @@ 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) +/** + * puts blood into container + * + * @return amount transferred + */ +/mob/living/carbon/proc/take_blood_legacy(obj/item/reagent_containers/container, amount) + ASSERT(container.reagents) - 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 - -//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) - - if(!should_have_organ(O_HEART)) - return null - - if(vessel.get_reagent_amount("blood") < amount) - return null - - 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. - - . = ..() - vessel.remove_reagent("blood",amount) // Removes blood if human - -//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() - -//Transfers blood from reagents to vessel, respecting blood types compatability. -/mob/living/carbon/human/inject_blood(var/datum/reagent/blood/injected, var/amount) - - if(!should_have_organ(O_HEART)) - reagents.add_reagent("blood", amount, injected.data) - reagents.update_total() - return - - fixblood_if_broken() - - var/datum/reagent/blood/our = get_blood(vessel) - - 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) + var/wanted = clamp(container.reagents.maximum_volume - container.reagents.total_volume, 0, amount) + if(!wanted) return 0 - if(donor_species && receiver_species) - if(donor_species != receiver_species) - return 1 + 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) - 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(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(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 + return amount_given -/proc/blood_splatter(target, datum/reagent/blood/source, large) +//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 ..() - // 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 +//Transfers blood from container ot vessels +/mob/living/carbon/proc/inject_blood_legacy(datum/blood_mixture/mixture, amount) + return give_blood_mixture(mixture, amount) - 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 +//Transfers blood from reagents to vessel, respecting blood types compatability. +/mob/living/carbon/human/inject_blood_legacy(datum/blood_mixture/mixture, amount) + if(!blood_holder) + return 0 + if(!should_have_organ(O_HEART)) + return ..() - 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) + // todo: ??? why is this here ??? + reset_blood_to_species_if_needed(TRUE) + + // at some point we'll simulate immune system but for now this is just + // raw conversion to toxin + // + // too bad! + + mixture = mixture.clone() + + //! behavior tightly coupled to /datum/blood_mixture. !// + var/total_ratio_incompatible = 0 + var/list/direct_mixture_fragment_ratios = mixture.unsafe_get_fragment_list_ref() + var/datum/blood_fragment/our_host_blood_fragment = blood_holder.unsafe_get_host_fragment_ref() + for(var/datum/blood_fragment/fragment as anything in direct_mixture_fragment_ratios) + if(our_host_blood_fragment.compatible_with_self(fragment)) + continue + // rejected + var/fragment_ratio = direct_mixture_fragment_ratios[fragment] + bloodstr.add_reagent(/datum/reagent/toxin, amount * 0.5 * fragment_ratio) + total_ratio_incompatible += fragment_ratio + direct_mixture_fragment_ratios -= fragment + if(total_ratio_incompatible) + var/total_amount_incompatible = amount * total_ratio_incompatible + amount *= (1 - total_ratio_incompatible) + var/expand_ratio = 1 / total_ratio_incompatible + for(var/datum/blood_fragment/expanding_to_fill as anything in direct_mixture_fragment_ratios) + direct_mixture_fragment_ratios[expanding_to_fill] *= expand_ratio + bloodstr.add_reagent(/datum/reagent/toxin, total_amount_incompatible) + + return ..() + +/proc/blood_splatter_legacy(turf/target, datum/blood_mixture/mixture, large) + if(!istype(target)) + return + + var/splatter_type = /obj/effect/debris/cleanable/blood/splatter + var/obj/effect/debris/cleanable/blood/existing_splatter + + // todo: change how this works, we should stop making effects like this! only one should ever + // exist. - // 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) + if(!large && length(drips) < 3) + splatter_type = /obj/effect/debris/cleanable/blood/drip - var/obj/effect/debris/cleanable/blood/drip/drop = B - if(istype(drop) && drips && drips.len && !large) - drop.add_overlay(drips) - drop.drips |= drips + existing_splatter = locate(splatter_type) in target + if(!existing_splatter) + existing_splatter = new splatter_type(target) - // If there's no data to copy, call it quits here. - if(!istype(source)) - return B + // todo: this is fucking stupid - // Update appearance. - if(source.data["blood_colour"]) - B.basecolor = source.data["blood_colour"] - B.synthblood = synth - B.update_icon() + 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 - if(source.data["blood_name"]) - B.name = source.data["blood_name"] + // is there data to copy? + if(!mixture) + return existing_splatter - // 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+" + existing_splatter.name = mixture.get_name() + existing_splatter.basecolor = mixture.get_color() + existing_splatter.synthblood = mixture.legacy_is_synthetic + existing_splatter.update_icon() - // Update virus information. - if(source.data["virus2"]) - B.virus2 = virus_copylist(source.data["virus2"]) + existing_splatter.blood_DNA = mixture.legacy_get_forensic_blood_dna() + if(mixture.legacy_virus2) + existing_splatter.virus2 = virus_copylist(mixture.legacy_virus2) - B.fluorescent = 0 - B.invisibility = 0 - return B + return existing_splatter diff --git a/code/modules/mob/living/carbon/human/health.dm b/code/modules/mob/living/carbon/human/health.dm index 91ac2b4d3fe..75dee9715c0 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 9c1e36ce3d6..db2adc44321 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 ea4a1cc8488..80f25332e03 100644 --- a/code/modules/mob/living/carbon/human/human-defense-legacy.dm +++ b/code/modules/mob/living/carbon/human/human-defense-legacy.dm @@ -282,7 +282,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 f529701c0e7..d03e74b4411 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 b4de4bb2bf2..186b409125d 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 0a047fe30bf..5cb2687d734 100644 --- a/code/modules/mob/living/carbon/taste.dm +++ b/code/modules/mob/living/carbon/taste.dm @@ -24,11 +24,12 @@ 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 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 08c6945305b..11cff72cf52 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 6c03d9fd4db..40a5a99c387 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 1306b7868f2..6a54ac7e842 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 2f8bf86e6d7..ccea54ce210 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 c0a083a3e38..1da1e4fb853 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 fee458b99ca..e1c506702ea 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 a60c1c140e6..5f7a5050447 100644 --- a/code/modules/organs/external/external.dm +++ b/code/modules/organs/external/external.dm @@ -805,7 +805,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) @@ -1170,7 +1170,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(company) model = company - var/datum/robolimb/R = GLOB.all_robolimbs[company] + var/datum/robolimb/R = GLOB.all_robolimbs[isnum(company) ? GLOB.all_robolimbs[company] : company] if(!R || (species && (species.name in R.species_cannot_use))) R = GLOB.basic_robolimb if(R) diff --git a/code/modules/organs/external/wound.dm b/code/modules/organs/external/wound.dm index f3849e8482f..298ad83a7ce 100644 --- a/code/modules/organs/external/wound.dm +++ b/code/modules/organs/external/wound.dm @@ -19,7 +19,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() - owner.getMinHealth())) * 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 f06ac698a3b..12e784a029e 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 + // 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() + // 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 72167c29ace..0c61ed1b945 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 e8ccb9d844c..f997311b2d3 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 8eb594a5f39..9e6c331d52e 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -367,9 +367,9 @@ rejecting = null if(istype(owner)) - var/datum/reagent/blood/organ_blood = locate(/datum/reagent/blood) in reagents.reagent_list - if(!organ_blood || !organ_blood.data["blood_DNA"]) - owner.vessel.trans_to(src, 5, 1, 1) + if(!reagents.has_reagent(/datum/reagent/blood::id)) + var/datum/blood_mixture/owner_mixture = owner.take_blood_mixture(5, TRUE) + reagents.add_reagent(/datum/reagent/blood::id, 5, owner_mixture) if(owner && vital && !ignore_vital) if(user) @@ -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?.unsafe_get_fragment_ref(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.unsafe_get_fragment_ref(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 @@ -474,10 +475,9 @@ //Process infections if(reagents) - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in reagents.reagent_list - if(B && prob(40)) - reagents.remove_reagent("blood",0.1) - blood_splatter(src,B,1) + if(reagents.has_reagent(/datum/reagent/blood::id) && prob(40) && get_turf(src)) + blood_splatter_legacy(get_turf(src), reagents.reagent_datas[/datum/reagent/blood::id], FALSE) + reagents.remove_reagent(/datum/reagent/blood, 0.1) adjust_germ_level(rand(2,6)) if(germ_level >= INFECTION_LEVEL_TWO) adjust_germ_level(rand(2,6)) @@ -630,7 +630,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. @@ -651,9 +651,13 @@ if(robotic >= ORGAN_ROBOT) return - to_chat(user, SPAN_NOTICE("You take an experimental bite out of \the [src].")) - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in reagents.reagent_list - blood_splatter(src,B,1) + user.visible_message( + SPAN_BOLDWARNING("[user] takes an experimental bite out of \the [src]!"), + SPAN_BOLDWARNING("You take an experimental bite out of \the [src]!"), + SPAN_WARNING("You hear a sickening chewing sound."), + MESSAGE_RANGE_INVENTORY_HARD, + ) + blood_splatter_legacy(get_turf(src), reagents.reagent_datas?[/datum/reagent/blood::id], TRUE) user.temporarily_remove_from_inventory(src, INV_OP_FORCE) 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 496e5bf6677..8ca1f64ee8f 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/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index a94c7898bb9..320ac5d6909 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -288,7 +288,7 @@ . = 0 else return -1 - if ((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + if ((reagents?(reagents.len):(0)) < length(avail_reagents.reagent_volumes)) return 0 return . diff --git a/code/modules/projectiles/guns/gun/projectile/ballistic/dartgun.dm b/code/modules/projectiles/guns/gun/projectile/ballistic/dartgun.dm index 4d2800443ea..0f802cd8444 100644 --- a/code/modules/projectiles/guns/gun/projectile/ballistic/dartgun.dm +++ b/code/modules/projectiles/guns/gun/projectile/ballistic/dartgun.dm @@ -58,18 +58,6 @@ if(istype(dart)) fill_dart(dart) -/obj/item/gun/projectile/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/projectile/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/projectiles/guns/gun/projectile/energy.dm b/code/modules/projectiles/guns/gun/projectile/energy.dm index fc1d79ba3a8..bd179969a2c 100644 --- a/code/modules/projectiles/guns/gun/projectile/energy.dm +++ b/code/modules/projectiles/guns/gun/projectile/energy.dm @@ -127,16 +127,8 @@ H = O.owner if(istype(H)) - var/start_nutrition = H.nutrition - var/end_nutrition = 0 - H.nutrition -= rechargeamt / 10 - end_nutrition = H.nutrition - - if(start_nutrition - max(0, end_nutrition) < rechargeamt / 10) - H.remove_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition))) - obj_cell_slot.cell.give(rechargeamt) //... to recharge 1/5th the battery update_icon() else diff --git a/code/modules/reagents/chemistry/_readme.dm b/code/modules/reagents/chemistry/_readme.dm deleted file mode 100644 index 3833cd11ba3..00000000000 --- 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 e4af0cf7e94..d2909913778 100644 --- a/code/modules/reagents/chemistry/chemical_reaction.dm +++ b/code/modules/reagents/chemistry/chemical_reaction.dm @@ -3,8 +3,8 @@ /datum/chemical_reaction abstract_type = /datum/chemical_reaction - //* core *// + //* core *// /// id - must be unique and in CamelCase. var/id /// reagent reaction flags - see [code/__DEFINES/reagents/flags.dm] @@ -16,7 +16,6 @@ var/___legacy_allow_collision_do_not_use = FALSE //* identity *// - /// name; defaults to reagent produced's name. /// if this is defaulted, it also defaults display name to that reagent if unset. var/name @@ -29,12 +28,10 @@ var/display_description //* logging *// - /// we should care enough about this to log it specifically var/important_for_logging = FALSE //* reaction *// - /// required reagents as ratios. path or id is supported, prefer paths for compile time checking. /// all of these will then make 1 unit of the reagent. var/list/required_reagents = list() @@ -120,8 +117,12 @@ /// todo: reactions being able to go in reverse once reaching equilibrium var/equilibrium = INFINITY - //* Reaction - Feedback *// + /** + * Perform data calculations. This is semi-expensive. + */ + var/has_data_semantics = FALSE + //* Reaction - Feedback *// /// if set, everyone around sees this on react var/reaction_message_instant = "The solution begins to bubble." /// if set, everyone around hears this on react @@ -129,7 +130,6 @@ // todo: soundloop support? maybe ambience subsystem for deduping.. //* guidebook *// - /// guidebook flags var/reaction_guidebook_flags = NONE /// guidebook category @@ -200,12 +200,6 @@ for(var/id in required_reagents) required_reagents_unit_volume += required_reagents[id] -//obtains any special data that will be provided to the reaction products -//this is called just before reactants are removed. -// todo: rework data system -/datum/chemical_reaction/proc/send_data(datum/reagent_holder/holder, reaction_limit) - return null - //* Guidebook *// /** @@ -294,6 +288,20 @@ return FALSE return TRUE +//* Data *// + +/** + * Performs data calculations for the data **initializer** to give to the result. + * + * * Only called if `has_data_semantics` is on. + * + * @params + * * holder - source holder + * * multiplier - reaction multiplier + */ +/datum/chemical_reaction/proc/compute_result_data_initializer(datum/reagent_holder/holder, multiplier) + return + //* Queries *// /** diff --git a/code/modules/reagents/chemistry/colors.dm b/code/modules/reagents/chemistry/colors.dm deleted file mode 100644 index 1f239859b48..00000000000 --- 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/machinery.dm b/code/modules/reagents/chemistry/machinery.dm index 731bb44d72a..e01cb659e1f 100644 --- a/code/modules/reagents/chemistry/machinery.dm +++ b/code/modules/reagents/chemistry/machinery.dm @@ -64,8 +64,8 @@ if(!(machine_stat & (NOPOWER|BROKEN)) && (!no_panel)) . += "The status display reads:\n" if(beaker) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - . += "- [R.volume] units of [R.name]." + for(var/datum/reagent/R in beaker.reagents.get_reagent_datums()) + . += "- [beaker.reagents.get_reagent_amount(R)] units of [R.name]." /obj/machinery/reagentgrinder/update_icon() icon_state = "juicer"+num2text(!isnull(beaker)) @@ -325,12 +325,12 @@ I.identify(IDENTITY_FULL, user) // Now tell us everything that is inside. - if(I.reagents && I.reagents.reagent_list.len) + if(length(I.reagents?.reagent_volumes)) to_chat(user, "
") // To add padding between regular chat and the output. - for(var/datum/reagent/R in I.reagents.reagent_list) + for(var/datum/reagent/R in I.reagents.get_reagent_datums()) if(!R.name) continue - to_chat(user, SPAN_NOTICE("Contains [R.volume]u of [R.name].
[R.description]
")) + to_chat(user, SPAN_NOTICE("Contains [I.reagents.get_reagent_amount(R)]u of [R.name].
[R.description]
")) // Last, unseal it if it's an autoinjector. if(istype(I,/obj/item/reagent_containers/hypospray/autoinjector/biginjector) && !(I.atom_flags & OPENCONTAINER)) diff --git a/code/modules/reagents/chemistry/reactions/paint.dm b/code/modules/reagents/chemistry/reactions/paint.dm index 539b27d7c6f..6e753472a5e 100644 --- a/code/modules/reagents/chemistry/reactions/paint.dm +++ b/code/modules/reagents/chemistry/reactions/paint.dm @@ -1,232 +1,195 @@ -/datum/chemical_reaction/red_paint +/datum/chemical_reaction/paint + abstract_type = /datum/chemical_reaction/paint + var/result_color + +/datum/chemical_reaction/paint/compute_result_data_initializer(datum/reagent_holder/holder, multiplier) + return result_color + +/datum/chemical_reaction/paint/red name = "Red paint" id = "red_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_red" = 1) result_amount = 5 + result_color = "#FE191A" -/datum/chemical_reaction/red_paint/send_data() - return "#FE191A" - -/datum/chemical_reaction/orange_paint +/datum/chemical_reaction/paint/orange name = "Orange paint" id = "orange_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_orange" = 1) result_amount = 5 + result_color = "#FFBE4F" -/datum/chemical_reaction/orange_paint/send_data() - return "#FFBE4F" - -/datum/chemical_reaction/yellow_paint +/datum/chemical_reaction/paint/yellow name = "Yellow paint" id = "yellow_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_yellow" = 1) result_amount = 5 + result_color = "#FDFE7D" -/datum/chemical_reaction/yellow_paint/send_data() - return "#FDFE7D" - -/datum/chemical_reaction/green_paint +/datum/chemical_reaction/paint/green name = "Green paint" id = "green_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_green" = 1) result_amount = 5 + result_color = "#18A31A" -/datum/chemical_reaction/green_paint/send_data() - return "#18A31A" - -/datum/chemical_reaction/blue_paint +/datum/chemical_reaction/paint/blue name = "Blue paint" id = "blue_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_blue" = 1) result_amount = 5 + result_color = "#247CFF" -/datum/chemical_reaction/blue_paint/send_data() - return "#247CFF" - -/datum/chemical_reaction/purple_paint +/datum/chemical_reaction/paint/purple name = "Purple paint" id = "purple_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_purple" = 1) result_amount = 5 + result_color = "#CC0099" -/datum/chemical_reaction/purple_paint/send_data() - return "#CC0099" - -/datum/chemical_reaction/grey_paint //mime +/datum/chemical_reaction/paint/grey //mime name = "Grey paint" id = "grey_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_grey" = 1) result_amount = 5 + result_color = "#808080" -/datum/chemical_reaction/grey_paint/send_data() - return "#808080" - -/datum/chemical_reaction/brown_paint +/datum/chemical_reaction/paint/brown name = "Brown paint" id = "brown_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "marker_ink_brown" = 1) result_amount = 5 + result_color = "#846F35" -/datum/chemical_reaction/brown_paint/send_data() - return "#846F35" - -/datum/chemical_reaction/blood_paint +/datum/chemical_reaction/paint/blood name = "Blood paint" id = "blood_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "blood" = 2) result_amount = 5 -/datum/chemical_reaction/blood_paint/send_data(var/datum/reagent_holder/T) - var/t = T.get_data("blood") - if(t && t["blood_colour"]) - return t["blood_colour"] - return "#FE191A" // Probably red +/datum/chemical_reaction/paint/blood/compute_result_data_initializer(datum/reagent_holder/holder, multiplier) + var/datum/blood_mixture/blood_mixture = holder.reagent_datas?[/datum/reagent/blood::id] + if(!blood_mixture) + return "#ffffff" + return blood_mixture.get_color() -/datum/chemical_reaction/milk_paint +/datum/chemical_reaction/paint/milk name = "Milk paint" id = "milk_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "milk" = 5) result_amount = 5 + result_color = "#F0F8FF" -/datum/chemical_reaction/milk_paint/send_data() - return "#F0F8FF" - -/datum/chemical_reaction/orange_juice_paint +/datum/chemical_reaction/paint/orange_juice name = "Orange juice paint" id = "orange_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "orangejuice" = 5) result_amount = 5 + result_color = "#E78108" -/datum/chemical_reaction/orange_juice_paint/send_data() - return "#E78108" - -/datum/chemical_reaction/tomato_juice_paint +/datum/chemical_reaction/paint/tomato_juice name = "Tomato juice paint" id = "tomato_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "tomatojuice" = 5) result_amount = 5 + result_color = "#731008" -/datum/chemical_reaction/tomato_juice_paint/send_data() - return "#731008" - -/datum/chemical_reaction/lime_juice_paint +/datum/chemical_reaction/paint/lime_juice name = "Lime juice paint" id = "lime_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "limejuice" = 5) result_amount = 5 + result_color = "#365E30" -/datum/chemical_reaction/lime_juice_paint/send_data() - return "#365E30" - -/datum/chemical_reaction/carrot_juice_paint +/datum/chemical_reaction/paint/carrot_juice name = "Carrot juice paint" id = "carrot_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "carrotjuice" = 5) result_amount = 5 + result_color = "#973800" -/datum/chemical_reaction/carrot_juice_paint/send_data() - return "#973800" - -/datum/chemical_reaction/berry_juice_paint +/datum/chemical_reaction/paint/berry_juice name = "Berry juice paint" id = "berry_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "berryjuice" = 5) result_amount = 5 + result_color = "#990066" -/datum/chemical_reaction/berry_juice_paint/send_data() - return "#990066" - -/datum/chemical_reaction/grape_juice_paint +/datum/chemical_reaction/paint/grape_juice name = "Grape juice paint" id = "grape_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "grapejuice" = 5) result_amount = 5 + result_color = "#863333" -/datum/chemical_reaction/grape_juice_paint/send_data() - return "#863333" - -/datum/chemical_reaction/poisonberry_juice_paint +/datum/chemical_reaction/paint/poisonberry_juice name = "Poison berry juice paint" id = "poisonberry_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "poisonberryjuice" = 5) result_amount = 5 + result_color = "#863353" -/datum/chemical_reaction/poisonberry_juice_paint/send_data() - return "#863353" - -/datum/chemical_reaction/watermelon_juice_paint +/datum/chemical_reaction/paint/watermelon_juice name = "Watermelon juice paint" id = "watermelon_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "watermelonjuice" = 5) result_amount = 5 + result_color = "#B83333" -/datum/chemical_reaction/watermelon_juice_paint/send_data() - return "#B83333" - -/datum/chemical_reaction/lemon_juice_paint +/datum/chemical_reaction/paint/lemon_juice name = "Lemon juice paint" id = "lemon_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "lemonjuice" = 5) result_amount = 5 + result_color = "#AFAF00" -/datum/chemical_reaction/lemon_juice_paint/send_data() - return "#AFAF00" - -/datum/chemical_reaction/banana_juice_paint +/datum/chemical_reaction/paint/banana_juice name = "Banana juice paint" id = "banana_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "banana" = 5) result_amount = 5 + result_color = "#C3AF00" -/datum/chemical_reaction/banana_juice_paint/send_data() - return "#C3AF00" - -/datum/chemical_reaction/potato_juice_paint +/datum/chemical_reaction/paint/potato_juice name = "Potato juice paint" id = "potato_juice_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "potatojuice" = 5) result_amount = 5 + result_color = "#302000" -/datum/chemical_reaction/potato_juice_paint/send_data() - return "#302000" - -/datum/chemical_reaction/carbon_paint +/datum/chemical_reaction/paint/carbon name = "Carbon paint" id = "carbon_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, MAT_CARBON = 1) result_amount = 5 + result_color = "#333333" -/datum/chemical_reaction/carbon_paint/send_data() - return "#333333" - -/datum/chemical_reaction/aluminum_paint +/datum/chemical_reaction/paint/aluminum name = "Aluminum paint" id = "aluminum_paint" result = "paint" required_reagents = list("plasticide" = 1, "water" = 3, "aluminum" = 1) result_amount = 5 - -/datum/chemical_reaction/aluminum_paint/send_data() - return "#F0F8FF" + result_color = "#F0F8FF" diff --git a/code/modules/reagents/chemistry/reagent.dm b/code/modules/reagents/chemistry/reagent.dm index 2b8dc312232..a0a48165c98 100644 --- a/code/modules/reagents/chemistry/reagent.dm +++ b/code/modules/reagents/chemistry/reagent.dm @@ -17,20 +17,31 @@ 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 - //* Identity + //* Data *// + /// Supports data system. + /// * If data is supported, the data must be + /// a primitive (number, string), + /// a datum implementing clone(), or + /// a list with the same semantics. + var/holds_data = FALSE + + //* 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 +50,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 +65,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 +80,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 +97,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,22 +112,6 @@ 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 - -/// Acid melting, cleaner cleaning, etc -/datum/reagent/proc/touch_obj(obj/O, amount) - return - -/// Cleaner cleaning, lube lubbing, etc, all go here -/datum/reagent/proc/touch_turf(turf/T, amount) - return - /// 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) if(!istype(M)) @@ -133,7 +124,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 +213,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 +259,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 +267,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 +//* Color *// -/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 +/** + * Only called if holds_data is TRUE. + */ +/datum/reagent/proc/compute_color_with_data(data) + return color + +//* Data *// + +/** + * 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/Destroy() // This should only be called by the holder, so it's already handled clearing its references - holder = null - . = ..() +/** + * 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 -/* DEPRECATED - TODO: REMOVE EVERYWHERE */ +/** + * 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 -/datum/reagent/proc/reaction_turf(var/turf/target, amt) - touch_turf(target, amt) - -/datum/reagent/proc/reaction_obj(var/obj/target, amt) - touch_obj(target, amt) - -/datum/reagent/proc/reaction_mob(var/mob/target, amt) - touch_mob(target, amt) - -/datum/reagent/proc/on_move(mob/M) - return - -/datum/reagent/proc/on_update(atom/A) - return - -//* Guidebook +//* Guidebook *// /** * Guidebook Data for TGUIGuidebookReagent @@ -322,63 +337,71 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) "alcoholStrength" = null, ) -//* Holder - Application +//* Identity *// /** - * called when we first get applied to a mob + * Get name from data + * * May be skipped if reagent has no data semantics. * * @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. - * - * @return amount to inject into the mob side holder. defaults to amount. this can be overriden by the mob / transfer procs. + * * data - the data. It can be casted to the expected type as needed. */ -// 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/compute_name_with_data(data) + return name + +//* Touch *// /** - * called when we first get sprayed/splashed on a non-mob - * - * not called if we're transferred into a holder on the obj + * Called when we're sprayed / splashed onto an obj * * @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 - 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 might be over remaining, due to how call order works. always check remaining. + * * data - our reagent data, if any + * + * todo: add temperature + * + * @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_obj(obj/target, remaining, allocated, data) + return 0 /** - * called when we first get sprayed/splashed on a turf - * - * not called if we're transferred into a holder on the turf, somehow + * Called when we're sprayed / splashed onto a turf * * @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 - 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 might be over remaining, due to how call order works. always check remaining. + * * data - our reagent data, if any + * + * todo: add temperature + * + * @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) - -//* Holder - Mixing +/datum/reagent/proc/on_touch_turf(turf/target, remaining, allocated, data) + return 0 /** - * called when a new reagent is being mixed with this one to mix our data lists. + * This should only be called when the reagent reaches the mob's skin / whatever, not before. * - * 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 might be over remaining, due to how call order works. always check 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 + * + * todo: add temperature + * + * @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_mob(mob/target, remaining, allocated, data, zone) + return 0 diff --git a/code/modules/reagents/chemistry/reagent_holder-application.dm b/code/modules/reagents/chemistry/reagent_holder-application.dm new file mode 100644 index 00000000000..24d20681676 --- /dev/null +++ b/code/modules/reagents/chemistry/reagent_holder-application.dm @@ -0,0 +1,170 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Dip - will consume reagents *// +//* Does not perform any simulation. *// + +/** + * Dip an entity into us. + * + * * This can cause reagent duping. + * + * @params + * * target - what to dip + * * ratio - % to apply + * * zone - body zone to target, if any + * * unsafe_drain_ratio - multiplier to actual drained amount. can result in reagent dupes if below 1. + * + * @return amount used + */ +/datum/reagent_holder/proc/perform_entity_dip(atom/target, ratio, zone, unsafe_drain_ratio = 1) + SHOULD_NOT_OVERRIDE(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + if(target.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD)) + return 0 + + if(isobj(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + var/used = resolved ? resolved.on_touch_obj(target, vol, vol * ratio, reagent_datas?[id]) : 0 + . += used + if(used && unsafe_drain_ratio) + remove_reagent(id, used * unsafe_drain_ratio, TRUE) + else if(isturf(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + var/used = resolved ? resolved.on_touch_turf(target, vol, vol * ratio, reagent_datas?[id]) : 0 + . += used + if(used && unsafe_drain_ratio) + remove_reagent(id, used * unsafe_drain_ratio, TRUE) + else if(ismob(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + var/used = resolved ? resolved.on_touch_mob(target, vol, vol * ratio, reagent_datas?[id], zone) : 0 + . += used + if(used && unsafe_drain_ratio) + remove_reagent(id, used * unsafe_drain_ratio, TRUE) + + if(.) + reconsider_reactions() + +//* Contact - will not consume reagents *// +//* Does not perform any simulation. *// + +/** + * Apply us to an object without draining. + * + * * This can cause reagent duping. + * + * @params + * * target - what to dip + * * ratio - % to apply + * * zone - body zone to target, if any + * + * @return amount 'used'; nothing is actually drained, so this is basically just + * how many units were said to be consumed by the reagent datums + * that object touch was called on. + */ +/datum/reagent_holder/proc/perform_entity_contact(atom/target, ratio, zone) + SHOULD_NOT_OVERRIDE(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + if(target.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD)) + return 0 + + if(isobj(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + . += resolved?.on_touch_obj(target, vol, vol * ratio, reagent_datas?[id]) + else if(isturf(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + . += resolved?.on_touch_turf(target, vol, vol * ratio, reagent_datas?[id]) + else if(ismob(target)) + for(var/id in reagent_volumes) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/vol = reagent_volumes[id] + . += resolved?.on_touch_mob(target, vol, vol * ratio, reagent_datas?[id], zone) + +/** + * Apply us to an atom or everything on a turf. + * + * * This can cause reagent duping. + * + * @params + * * target - atom or turf to hit + * * ratio - % to apply + * * zone - body zone to target, if any + * * reapplication_exclusion - a list that can be provided to provide entities that are already + * sprayed on. this will be modified by this proc to associate + * entities sprayed to TRUE. + * + * @return amount 'used'; nothing is actually drained, so this is basically just + * how many units were said to be consumed by the reagent datums + * that object touch was called on. + */ +/datum/reagent_holder/proc/perform_uniform_contact(atom/target, ratio, zone, list/reapplication_exclusion = list()) + SHOULD_NOT_OVERRIDE(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + if(isturf(target)) + for(var/atom/movable/AM as anything in target.contents) + if(AM.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD)) + continue + if(reapplication_exclusion[AM]) + continue + reapplication_exclusion[AM] = TRUE + . += perform_entity_contact(AM, ratio, zone) + else + if(!reapplication_exclusion[target]) + reapplication_exclusion[target] = TRUE + . += perform_entity_contact(target, ratio, zone) + +//* Splash *// +//* ... It's complicated. *// + +/** + * Splashes onto, and around something. + * + * * This can cause reagent duping. + * + * @params + * * target - what to splash on + * * ratio - % to apply + * * zone - target zone, if any + * * no_drain - do not take splashed reagents out + * * splash_coeff - ratio of reagents to splash. if on a large thing like a human, + * it might first splash to other zones on them then the floor. + * + * @return amount 'used'; nothing is actually drained, so this is basically just + * how many units were said to be consumed by the reagent datums + * that object touch was called on. + */ +/datum/reagent_holder/proc/perform_entity_splash(atom/target, ratio, zone, no_drain, splash_coeff = 0.5) + SHOULD_NOT_OVERRIDE(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + var/ratio_left = ratio + + // splash target + if(!(target.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD))) + perform_entity_contact(target, ratio * (1 - splash_coeff), zone) + ratio_left *= splash_coeff + + // if target was a carbon and zone wasn't specified, splash other zones + // todo: impl + + // splash turf + . += perform_uniform_contact(target.loc, ratio_left) + + // drain if needed + if(!no_drain && ratio > 0) + for(var/id in reagent_volumes) + remove_reagent(id, reagent_volumes[id] * ratio, TRUE) + reconsider_reactions() 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 00000000000..c3cbd023ae4 --- /dev/null +++ b/code/modules/reagents/chemistry/reagent_holder-color.dm @@ -0,0 +1,34 @@ +//* 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 + var/effective_weight = volume * resolved_reagent.color_weight + switch(length(effective_color)) + if(7) + total_a += 255 * effective_weight + if(9) + total_a += hex2num(copytext(resolved_reagent.color, 8, 10)) * effective_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)) * effective_weight + total_g += hex2num(copytext(resolved_reagent.color, 4, 6)) * effective_weight + total_b += hex2num(copytext(resolved_reagent.color, 6, 8)) * effective_weight + total_weight += effective_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 00000000000..894bfe4c074 --- /dev/null +++ b/code/modules/reagents/chemistry/reagent_holder-helpers.dm @@ -0,0 +1,27 @@ +//* 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 + RETURN_TYPE(/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() + var/datum/reagent/resolved = get_majority_reagent_datum() + return resolved ? resolved.name : null + +/** + * Returns reagent ID of highest single reagent in volume, or null if we are empty. + */ +/datum/reagent_holder/proc/get_majority_reagent_id() + var/datum/reagent/resolved = get_majority_reagent_datum() + return resolved ? resolved.id : null diff --git a/code/modules/reagents/chemistry/reagent_holder-legacy.dm b/code/modules/reagents/chemistry/reagent_holder-legacy.dm index 4ed9a85f550..59547b0d229 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 50% rename from code/modules/reagents/chemistry/metabolism.dm rename to code/modules/reagents/chemistry/reagent_holder-metabolism.dm index 469d40a3a8c..ef5a1b2a2f8 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 @@ -18,9 +21,32 @@ var/mob/living/carbon/human/H = parent metabolism_type = H.species.reagent_tag - for(var/datum/reagent/current in reagent_list) + for(var/datum/reagent/current in get_reagent_datums()) 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) + . = ..() + // todo: we need on_reagent_load and on_reagent_unload hooks, none of this add/remove reagent overriding. + if(ispath(id)) + var/datum/reagent/path = id + id = initial(path.id) + if(!reagent_volumes?[id]) + return + if(reagent_metabolisms?[id]) + return + var/datum/reagent_metabolism/metabolism = new + metabolism.peak_dose = max(metabolism.peak_dose, reagent_volumes[id]) + LAZYSET(reagent_metabolisms, id, metabolism) + +/datum/reagent_holder/metabolism/remove_reagent(id, amount, skip_reactions) + . = ..() + // todo: we need on_reagent_load and on_reagent_unload hooks, none of this add/remove reagent overriding. + if(ispath(id)) + var/datum/reagent/path = id + id = initial(path.id) + if(reagent_volumes?[id]) + return + LAZYREMOVE(reagent_metabolisms, id) // "Specialized" metabolism datums /datum/reagent_holder/metabolism/bloodstream diff --git a/code/modules/reagents/chemistry/reagent_holder-reactions.dm b/code/modules/reagents/chemistry/reagent_holder-reactions.dm index a62162d0bb6..d962e162ae9 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 *// @@ -223,7 +219,12 @@ remove_reagent(id, maximum_multiplier * reaction.required_reagents[id], TRUE) ids_to_recheck[id] = TRUE if(reaction.result_amount > 0) - add_reagent(reaction.result, maximum_multiplier * reaction.result_amount, null, TRUE) + add_reagent( + reaction.result, + maximum_multiplier * reaction.result_amount, + reaction.has_data_semantics ? reaction.compute_result_data_initializer(src, maximum_multiplier) : null, + TRUE, + ) ids_to_recheck[reaction.result] = TRUE reaction.on_reaction_tick(src, delta_time, maximum_multiplier) @@ -308,7 +309,12 @@ remove_reagent(id, maximum_multiplier * reaction.required_reagents[id], TRUE) ids_to_recheck[id] = TRUE if(reaction.result && reaction.result_amount > 0) - add_reagent(reaction.result, maximum_multiplier * reaction.result_amount, null, TRUE) + add_reagent( + reaction.result, + maximum_multiplier * reaction.result_amount, + reaction.has_data_semantics ? reaction.compute_result_data_initializer(src, maximum_multiplier) : null, + TRUE, + ) ids_to_recheck[reaction.result] = TRUE reaction.on_reaction_instant(src, maximum_multiplier) diff --git a/code/modules/reagents/chemistry/reagent_holder.dm b/code/modules/reagents/chemistry/reagent_holder.dm index 45505ac908c..a4edaafb2e6 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,24 @@ //* Reagents *// + /// Our reagent volumes + /// + /// * Lazy list. + var/list/reagent_volumes + /// Our reagent datas + /// + /// * Lazy list. + var/list/reagent_datas /// Our temperature + // todo: this is currently half-implemented, we should finish it + // todo: when we do, make sure carbon-types will temperature-stabilize their reagents holders. 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,69 +50,39 @@ 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 ..() +/datum/reagent_holder/clone() + var/datum/reagent_holder/creating = new type + creating.reagent_volumes = reagent_volumes.Copy() + creating.reagent_datas = deep_clone_list(reagent_datas) + creating.temperature = temperature + creating.total_volume = total_volume + return creating + // 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 - /datum/reagent_holder/proc/holder_full() if(total_volume >= maximum_volume) return TRUE @@ -121,55 +106,61 @@ var/datum/reagent/accessing = id id = initial(accessing.id) - if(!isnum(amount) || amount <= 0) + amount = REAGENT_HOLDER_VOLUME_QUANTIZE(min(amount, maximum_volume - total_volume)) + if(amount <= 0) return 0 - // todo: rewrite this entire proc; especially data. + // this reagent may need to be loaded from persistence + var/datum/reagent/reagent = SSchemistry.fetch_reagent(id) - update_total() - amount = min(amount, available_volume()) + if(!reagent) + return 0 - 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 +177,117 @@ if(ispath(id)) var/datum/reagent/path = id id = initial(path.id) - if(!isnum(amount)) + + amount = REAGENT_HOLDER_VOLUME_QUANTIZE(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 >= FLOOR(current, REAGENT_HOLDER_VOLUME_PRECISION)) + reagent_volumes -= id + if(reagent_datas) + reagent_datas -= id + total_volume -= current + . = current + else + reagent_volumes[id] -= amount + total_volume -= amount + . = amount + + // -- deal with floating point inaccuracy incase we went below 0 -- + if(total_volume < REAGENT_HOLDER_VOLUME_PRECISION) clear_reagents() + // -- end -- + 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 + // -- deal with floating point inaccuracy incase we went below 0 -- + if(total_volume < REAGENT_HOLDER_VOLUME_PRECISION) clear_reagents() + // -- end + 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 + total_volume = 0 + temperature = initial(temperature) -/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 = REAGENT_HOLDER_VOLUME_PRECISION) 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 // Transfers [amount] reagents from [src] to [target], multiplying them by [multiplier]. // Returns actual amount removed from [src] (not amount transferred to [target]). +// todo: audit this proc /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 */ @@ -339,9 +297,8 @@ * If for some reason touch effects are bypassed (e.g. injecting stuff directly into a reagent container or person), * call the appropriate trans_to_*() proc. */ +// todo: audit this proc /datum/reagent_holder/proc/trans_to(atom/target, amount = 1, multiplier = 1, copy = 0) - touch(target) //First, handle mere touch effects - if(ismob(target)) return splash_mob(target, amount, copy) if(isturf(target)) @@ -371,55 +328,17 @@ return var/datum/reagent_holder/F = new /datum/reagent_holder(amount) - var/tmpdata = get_data(id) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/tmpdata = resolved.make_copy_data_initializer(reagent_datas?[resolved.id]) F.add_reagent(id, amount, tmpdata) remove_reagent(id, amount) 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. +// todo: audit this proc /datum/reagent_holder/proc/splash_mob(mob/target, amount = 1, copy = 0) var/perm = 1 if(isliving(target)) //will we ever even need to tranfer reagents to non-living mobs? @@ -431,6 +350,7 @@ perm = L.reagent_permeability() return trans_to_mob(target, amount, CHEM_TOUCH, perm, copy) +// todo: audit this proc /datum/reagent_holder/proc/trans_to_mob(mob/target, amount = 1, type = CHEM_INJECT, multiplier = 1, copy = 0) // Transfer after checking into which holder... if(!target || !istype(target)) return @@ -448,18 +368,19 @@ else var/datum/reagent_holder/R = new /datum/reagent_holder(amount) . = trans_to_holder(R, amount, multiplier, copy) - R.touch_mob(target) + R.perform_entity_contact(target, 1) +// todo: audit this proc /datum/reagent_holder/proc/trans_to_turf(turf/target, amount = 1, multiplier = 1, copy = 0) // Turfs don't have any reagents (at least, for now). Just touch it. if(!target) return var/datum/reagent_holder/R = new /datum/reagent_holder(amount * multiplier) . = trans_to_holder(R, amount, multiplier, copy) - R.touch_turf(target, amount) - return + R.perform_entity_contact(target, 1) /// Objects may or may not; if they do, it's probably a beaker or something and we need to transfer properly; otherwise, just touch. +// todo: audit this proc /datum/reagent_holder/proc/trans_to_obj(obj/target, amount = 1, multiplier = 1, copy = 0) if(!target) return @@ -467,7 +388,7 @@ if(!target.reagents) var/datum/reagent_holder/R = new /datum/reagent_holder(amount * multiplier) . = trans_to_holder(R, amount, multiplier, copy) - R.touch_obj(target, amount) + R.perform_entity_contact(target, 1) return return trans_to_holder(target.reagents, amount, multiplier, copy) @@ -479,6 +400,7 @@ return reagents //Spreads the contents of this reagent holder all over the vicinity of the target turf. +// todo: audit this proc /datum/reagent_holder/proc/splash_area(turf/epicentre, range = 3, portion = 1.0, multiplier = 1, copy = 0) var/list/things = dview(range, epicentre, INVISIBILITY_LIGHTING) var/list/turfs = list() @@ -494,11 +416,13 @@ 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) + TR.perform_uniform_contact(T, 1) + // TR.splash_turf(T) qdel(R) //Spreads the contents of this reagent holder all over the target turf, dividing among things in it. //50% is divided between mobs, 20% between objects, and whatever is left on the turf itself +// todo: audit this proc /datum/reagent_holder/proc/splash_turf(turf/T, amount = null, multiplier = 1, copy = 0) if (isnull(amount)) amount = total_volume @@ -527,18 +451,6 @@ 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() - -/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() - //* Filtering *// /** @@ -553,10 +465,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 +483,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 +529,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 +581,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 +601,70 @@ //* Transfers *// /** + * Transfers to a holder. + * + * * Transference is done uniformly within the target reagents, keeping any ratios between them during the transfer. + * * 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) + ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_transferable) + else + ids_to_transfer = reagent_volumes + ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_volume) + + if(!copy) + for(var/id in ids_to_transfer) + var/datum/reagent/resolved = SSchemistry.fetch_reagent(id) + var/transferred = reagent_volumes[id] * ratio + . += transferred + target.add_reagent( + id, + transferred, + resolved.holds_data ? resolved.make_copy_data_initializer(reagent_datas?[id]) : null, + 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 + . += transferred + target.add_reagent( + id, + transferred, + resolved.holds_data ? resolved.make_copy_data_initializer(reagent_datas?[id]) : null, + TRUE, + ) if(!defer_reactions) if(!copy) @@ -713,10 +678,25 @@ */ /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 + +//* Updates *// + +/** + * Updates total volume, quantizing all reagent amounts as well. + */ +/datum/reagent_holder/proc/update_total() + var/new_volume = 0 + for(var/id in reagent_volumes) + var/amount = REAGENT_HOLDER_VOLUME_QUANTIZE(reagent_volumes[id]) + reagent_volumes[id] = amount + new_volume += amount + total_volume = new_volume diff --git a/code/modules/reagents/chemistry/reagent_metabolism.dm b/code/modules/reagents/chemistry/reagent_metabolism.dm new file mode 100644 index 00000000000..e831992e429 --- /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 3030bec93dd..0c63c409a38 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"] - ..() +/datum/reagent/antibodies/make_copy_data_initializer(data) + return data -/// 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 +/datum/reagent/antibodies/preprocess_data(data_initializer) + return data_initializer - 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 - - var/datum/gas_mixture/environment = T.return_air() - var/min_temperature = T0C + 100 // 100C, the boiling point of water - - 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))) + return list() -/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))) - -/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 f65c970acf9..d0b2022f262 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 9fe8bc4606b..fad0fa4c77f 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 && !(M.species.species_flags & NO_NUTRITION_GAIN)) // 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) @@ -151,7 +90,9 @@ nutriment_factor = 1 color = "#FFFFFF" -/datum/reagent/nutriment/flour/touch_turf(turf/simulated/T) +/datum/reagent/nutriment/flour/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + var/turf/T = target if(!istype(T, /turf/space)) new /obj/effect/debris/cleanable/flour(T) @@ -274,20 +215,14 @@ 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/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(allocated >= 5) + if(istype(target, /turf/simulated)) + var/turf/simulated/sim_target = target + sim_target.wet_floor() + . = max(., 5) /datum/reagent/nutriment/peanutbutter name = "Peanut Butter" @@ -319,20 +254,21 @@ 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_mob(mob/target, remaining, allocated, data, zone) + . = ..() + 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() - return ..() + to_chat(target, "[message]") + if(prob(clamp(allocated, 5, 90))) + if(iscarbon(target)) + var/mob/living/carbon/living_target = target + living_target.vomit() -/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 +323,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 +341,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 +380,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 +394,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 +406,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 +426,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 +444,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 +453,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 +472,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 +598,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 +607,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 +632,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 +654,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 +699,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 +715,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 +767,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 +783,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 +822,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 +880,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 +965,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 +987,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 +995,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,14 +1087,14 @@ 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 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) /datum/reagent/drink/tea/icetea/milktea/honeybubbletea name = "Honey Bubble Tea" @@ -1269,17 +1203,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 +1229,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 +1237,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 +1262,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 +1282,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 +1303,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 +1466,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 +1506,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 +1537,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 +1563,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 +1581,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 +1854,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 +1904,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 +1935,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 +1943,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 +2056,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 +2082,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 +2305,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 +2350,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 +2387,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 +2397,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 +2481,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 +2517,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 +2914,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 +3224,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 +3251,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 +3464,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,13 +3484,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) /datum/reagent/ethanol/sakebomb name = "Sake Bomb" @@ -3739,7 +3671,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 +3721,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 +3786,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 +3930,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 +4274,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 +4468,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 +4492,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 +4516,14 @@ 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/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 if (lastburnmessage+100 < world.time ) to_chat(M, SPAN_DANGER("Searing hot oil burns you, wash it off quick!")) lastburnmessage = world.time @@ -4738,7 +4577,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 +4659,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 +4685,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 +4719,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 +4800,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 +4815,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 +4968,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 cb52e4e473d..83b3a6528e4 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) + if(istype(target, /obj/item/stack/medical/crude_pack) && round(allocated) >= 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 @@ -1274,26 +1266,31 @@ M.adjustFireLoss(removed) M.adjustToxLoss(2 * removed) -/datum/reagent/sterilizine/touch_obj(obj/O) - O.germ_level -= min(volume*20, O.germ_level) - O.was_bloodied = null +/datum/reagent/sterilizine/on_touch_obj(obj/target, remaining, allocated, data) + . = ..() + target.germ_level -= min(allocated * 20, target.germ_level) + target.was_bloodied = null -/datum/reagent/sterilizine/touch_turf(turf/T) - T.germ_level -= min(volume*20, T.germ_level) +/datum/reagent/sterilizine/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(!istype(target, /turf/simulated)) + return + var/turf/simulated/T = target + T.germ_level -= min(allocated*20, T.germ_level) for(var/obj/item/I in T.contents) I.was_bloodied = null for(var/obj/effect/debris/cleanable/blood/B in T) qdel(B) -/datum/reagent/sterilizine/touch_mob(mob/living/L, amount) - if(istype(L)) - if(istype(L, /mob/living/simple_mob/slime)) - var/mob/living/simple_mob/slime/S = L - var/amt = rand(15, 25) * amount * (1-S.water_resist) - if(amt>0) - S.adjustToxLoss(rand(15, 25) * amount) // Does more damage than water. - S.visible_message("[S]'s flesh sizzles where the fluid touches it!", "Your flesh burns in the fluid!") - remove_self(amount) +/datum/reagent/sterilizine/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + if(istype(target, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = target + var/amt = rand(15, 25) * allocated * (1-S.water_resist) + if(amt > 0) + S.adjustToxLoss(rand(15, 25) * amt) // Does more damage than water. + S.visible_message("[S]'s flesh sizzles where the fluid touches it!", "Your flesh burns in the fluid!") + . = max(amt, allocated) /datum/reagent/leporazine name = "Leporazine" @@ -1305,7 +1302,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 +1320,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 @@ -1353,7 +1350,7 @@ M.adjustOxyLoss(-2 * removed) M.heal_organ_damage(20 * removed, 20 * removed) M.adjustToxLoss(-20 * removed) - if(dose > 10) + if(metabolism.total_processed_dose > 10) M.make_dizzy(5) M.make_jittery(5) @@ -1368,20 +1365,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 +1387,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 +1409,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 +1436,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 +1453,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 +1476,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 +1501,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 +1524,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 +1556,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) @@ -1572,53 +1565,6 @@ nif.stat = NIF_INSTALLING nif.durability = min(nif.durability + removed, initial(nif.durability)) -/datum/reagent/firefighting_foam - name = "Firefighting Foam" - id = "firefoam" - description = "A historical fire suppressant. Originally believed to simply displace oxygen to starve fires, it actually interferes with the combustion reaction itself. Vastly superior to the cheap water-based extinguishers found on most NT vessels." - reagent_state = REAGENT_LIQUID - color = "#A6FAFF" - taste_description = "the inside of a fire extinguisher" - -/datum/reagent/firefighting_foam/touch_turf(turf/T, reac_volume) - if(reac_volume >= 1) - var/obj/effect/foam/firefighting/F = (locate(/obj/effect/foam/firefighting) in T) - if(!F) - F = new(T) - else if(istype(F)) - F.lifetime = initial(F.lifetime) //reduce object churn a little bit when using smoke by keeping existing foam alive a bit longer - - var/datum/gas_mixture/environment = T.return_air() - var/min_temperature = T0C + 100 // 100C, the boiling point of water - - var/hotspot = (locate(/atom/movable/fire) in T) - if(hotspot && !isspaceturf(T)) - 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 * 19000, -environment.get_thermal_energy_change(min_temperature)) - environment.adjust_thermal_energy(-removed_heat) - if(prob(5)) - T.visible_message("The foam sizzles as it lands on \the [T]!") - -/datum/reagent/firefighting_foam/touch_obj(obj/O, reac_volume) - O.water_act(reac_volume / 5) - -/datum/reagent/firefighting_foam/touch_mob(mob/living/M, reac_volume) - if(istype(M, /mob/living/simple_mob/slime)) //I'm sure foam is water-based! - var/mob/living/simple_mob/slime/S = M - var/amt = 15 * reac_volume * (1-S.water_resist) - if(amt>0) - S.adjustToxLoss(amt) - S.visible_message("[S]'s flesh sizzles where the foam touches it!", "Your flesh burns in the foam!") - - M.adjust_fire_stacks(-reac_volume) - M.ExtinguishMob() - //CRS (Cyberpsychosis) Medication /datum/reagent/neuratrextate name = "Neuratrextate" @@ -1627,19 +1573,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) - remove_self(30) +/datum/reagent/neuratrextate/legacy_affect_ingest(mob/living/carbon/M, datum/reagent_metabolism/metabolism) + metabolism.legacy_current_holder.remove_reagent(id, 30) to_chat(M, "It feels like there's a pile of knives in your stomach!") - M.druggy += 10 + M.druggy = max(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.druggy = max(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 5ed24375776..eaf8e554958 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,34 +25,38 @@ 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) +/datum/reagent/modapplying/cryofluid/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + + var/mob/M = target if(isliving(M)) var/mob/living/L = M - for(var/I = 1 to rand(1, round(amount + 1))) - L.add_modifier(modifier_to_add, amount * rand(modifier_duration / 2, modifier_duration * 2)) - return + for(var/I = 1 to rand(1, round(allocated + 1))) + L.add_modifier(modifier_to_add, allocated * rand(modifier_duration / 2, modifier_duration * 2)) -/datum/reagent/modapplying/cryofluid/touch_turf(turf/T, amount) - if(istype(T, /turf/simulated/floor/water) && prob(amount)) +/datum/reagent/modapplying/cryofluid/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/T = target + if(istype(T, /turf/simulated/floor/water) && prob(allocated)) T.visible_message("\The [T] crackles loudly as the cryogenic fluid causes it to boil away, leaving behind a hard layer of ice.") T.ChangeTurf(/turf/simulated/floor/outdoors/ice, 1, 1, TRUE) else if(istype(T, /turf/simulated)) var/turf/simulated/S = T S.freeze_floor() - return diff --git a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm index aa3fb658bdb..f84ea963eaf 100644 --- a/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm +++ b/code/modules/reagents/chemistry/reagents/Chemistry-Reagents-Other.dm @@ -1,300 +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" - description = "Gold is a dense, soft, shiny metal and the most malleable and ductile metal known." - taste_description = "metal" - reagent_state = REAGENT_SOLID - color = "#F7C430" - -/datum/reagent/silver - name = "Silver" - id = "silver" - description = "A soft, white, lustrous transition metal, it has the highest electrical conductivity of any element and the highest thermal conductivity of any metal." - taste_description = "metal" - reagent_state = REAGENT_SOLID - color = "#D0D0D0" - -/datum/reagent/uranium - name ="Uranium" - id = "uranium" - description = "A silvery-white metallic chemical element in the actinide series, weakly radioactive." - taste_description = "metal" - reagent_state = REAGENT_SOLID - color = "#B8B8C0" - -/datum/reagent/platinum - name = "Platinum" - id = "platinum" - description = "Platinum is a dense, malleable, ductile, highly unreactive, precious, gray-white transition metal. It is very resistant to corrosion." - taste_description = "metal" - 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/affect_blood(mob/living/carbon/M, alien, removed) - M.apply_effect(5 * removed, IRRADIATE, 0) - -/datum/reagent/uranium/touch_turf(turf/T) - if(volume >= 3) - if(!istype(T, /turf/space)) - var/obj/effect/debris/cleanable/greenglow/glow = locate(/obj/effect/debris/cleanable/greenglow, T) - if(!glow) - new /obj/effect/debris/cleanable/greenglow(T) - return - /datum/reagent/adrenaline name = "Adrenaline" id = "adrenaline" @@ -304,34 +9,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 +26,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 +39,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 @@ -384,10 +68,12 @@ reagent_state = REAGENT_LIQUID color = "#009CA8" -/datum/reagent/lube/touch_turf(turf/simulated/T) - if(!istype(T)) +/datum/reagent/lube/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(!istype(target, /turf/simulated)) return - if(volume >= 1) + var/turf/simulated/T = target + if(allocated >= 1) T.wet_floor(2) /datum/reagent/silicate @@ -398,12 +84,12 @@ reagent_state = REAGENT_LIQUID color = "#C7FFFF" -/datum/reagent/silicate/touch_obj(obj/O) - if(istype(O, /obj/structure/window)) - var/obj/structure/window/W = O - W.apply_silicate(volume) - remove_self(volume) - return +/datum/reagent/silicate/on_touch_obj(obj/target, remaining, allocated, data) + . = ..() + if(istype(target, /obj/structure/window)) + var/obj/structure/window/W = target + W.apply_silicate(allocated) + . = max(., allocated) /datum/reagent/glycerol name = "Glycerol" @@ -431,22 +117,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 +132,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 +148,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 +189,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 a5e791f7bb9..ee09fc346d9 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)) @@ -83,25 +56,29 @@ strength = 80 var/fire_mult = 30 -/datum/reagent/toxin/hydrophoron/touch_mob(mob/living/L, amount) +/datum/reagent/toxin/hydrophoron/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + var/mob/living/L = target if(istype(L)) - L.adjust_fire_stacks(amount / fire_mult) + L.adjust_fire_stacks(allocated / 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() -/datum/reagent/toxin/hydrophoron/touch_turf(turf/simulated/T) - if(!istype(T)) +/datum/reagent/toxin/hydrophoron/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(!istype(target, /turf/simulated)) return - T.assume_gas(GAS_ID_PHORON, CEILING(volume/2, 1), T20C) + var/turf/simulated/T = target + T.assume_gas(GAS_ID_PHORON, CEILING(allocated / 2, 1), T20C) for(var/turf/simulated/floor/target_tile in range(0,T)) - target_tile.assume_gas(GAS_ID_PHORON, volume/2, 400+T0C) + target_tile.assume_gas(GAS_ID_PHORON, allocated / 2, 400+T0C) spawn (0) target_tile.hotspot_expose(700, 400) - remove_self(volume) + . = max(., allocated) -/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) @@ -128,11 +105,14 @@ touch_met = 5 skin_danger = 1 -/datum/reagent/toxin/phoron/touch_mob(mob/living/L, amount) - if(istype(L)) - L.adjust_fire_stacks(amount / 5) +/datum/reagent/toxin/phoron/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() -/datum/reagent/toxin/phoron/affect_touch(mob/living/carbon/M, alien, removed) + var/mob/living/L = target + if(istype(L)) + L.adjust_fire_stacks(allocated / 5) + +/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 +121,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. @@ -151,11 +131,14 @@ M.adjust_fire_stacks(removed * 3) //Not quite 'converting' it. It's like mixing fuel into a jelly. You get explosive, or at least combustible, jelly. ..() -/datum/reagent/toxin/phoron/touch_turf(turf/simulated/T, amount) +/datum/reagent/toxin/phoron/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/simulated/T = target if(!istype(T)) return - T.assume_gas(GAS_ID_VOLATILE_FUEL, amount, T20C) - remove_self(amount) + T.assume_gas(GAS_ID_PHORON, allocated, T20C) + . = max(., allocated) /datum/reagent/toxin/cyanide //Fast and Lethal name = "Cyanide" @@ -166,9 +149,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 +164,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 +188,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 +203,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 +229,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 +255,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 +274,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, TYPE_PROC_REF(/mob/living/carbon, __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 +301,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, TYPE_PROC_REF(/mob/living/carbon, __really_shitty_lich_powder_shim)), 0.5 SECONDS) M.adjustOxyLoss(1 * removed) M.silent = max(M.silent, 10) M.tod = stationtime2text() @@ -335,11 +319,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 +335,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 @@ -377,26 +361,29 @@ color = "#49002E" strength = 4 -/datum/reagent/toxin/plantbgone/touch_turf(turf/T) - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T +/datum/reagent/toxin/plantbgone/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target if(locate(/obj/effect/overlay/wallrot) in W) for(var/obj/effect/overlay/wallrot/E in W) qdel(E) W.visible_message("The fungi are completely dissolved by the solution!") -/datum/reagent/toxin/plantbgone/touch_obj(obj/O, volume) +/datum/reagent/toxin/plantbgone/on_touch_obj(obj/target, remaining, allocated, data) + . = ..() + var/obj/O = target if(istype(O, /obj/effect/plant)) qdel(O) - else if(istype(O, /obj/structure/alien/weeds/)) + else if(istype(O, /obj/structure/alien/weeds)) 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) @@ -409,17 +396,22 @@ color = "#8FB97C" strength = 4 -/datum/reagent/toxin/pestbgone/touch_obj(mob/living/simple_mob/animal/A, removed) - if(istype(A, /mob/living/simple_mob/animal/roach)) - A.adjustToxLoss(10 * removed) - else if(istype(A, /mob/living/simple_mob/animal/giant_spider)) - A.adjustToxLoss(5 * removed) +/datum/reagent/toxin/pestbgone/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + if(istype(target, /mob/living)) + var/mob/living/living_target = target + if(istype(target, /mob/living/simple_mob/animal/roach)) + living_target.adjustToxLoss(10 * allocated) + . = max(., allocated) + else if(istype(target, /mob/living/simple_mob/animal/giant_spider)) + living_target.adjustToxLoss(5 * allocated) + . = max(., allocated) -/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 +425,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 +433,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 +442,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 +465,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 +482,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 +503,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 +526,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 +554,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 +648,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 +671,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 +702,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 +717,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 +748,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 +764,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 +787,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 +813,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 +840,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 +857,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 +873,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 +900,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 +918,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 +942,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 +957,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 +995,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 +1024,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 +1054,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 +1090,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 +1111,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 +1123,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/legacy_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 +1136,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 +1162,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,29 +1184,27 @@ 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(0, 10 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) - 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(0, 10 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) - 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(0, 10 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) +/datum/reagent/grubshock/on_touch_mob(mob/target, remaining, allocated, data, zone) + ..() + target.electrocute(0, 10 * allocated, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) + return allocated //This chem is for removing nanites without grubmeat /datum/reagent/lessershock @@ -1238,29 +1213,25 @@ 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(0, 5 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) - 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_ingest(mob/living/carbon/M, alien, removed) +/datum/reagent/lessershock/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) M.electrocute(0, 5 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) - 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) +/datum/reagent/lessershock/legacy_affect_touch(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) M.electrocute(0, 5 * removed, 0, ELECTROCUTE_ACT_FLAG_INTERNAL, null) /datum/reagent/asbestos //asbestos removal handled in under datum/reagent/respirodaxon @@ -1270,15 +1241,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") @@ -1287,7 +1258,8 @@ if(prob(1)) H.asbestos_lung() -/datum/reagent/asbestos/overdose(mob/living/carbon/human/M, alien, removed) +/datum/reagent/asbestos/legacy_affect_overdose(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) + . = ..() var/mob/living/carbon/human/H = M if(prob(4)) M.emote("cough") @@ -1296,13 +1268,16 @@ if(prob(1)) H.asbestos_lung() - -/datum/reagent/asbestos/touch_turf(turf/T) //Great now the dust is airborne! - if(volume >= 20) - var/location = get_turf(holder.my_atom) +/datum/reagent/asbestos/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + var/turf/T = target + if(allocated >= 20) + var/location = T var/datum/effect_system/smoke_spread/chem/S = new /datum/effect_system/smoke_spread/chem S.attach(location) - S.set_up(holder, volume * 0.05, 0, location) //This ghetto smoke solution is 1/8th as effecient as chemsmoke + var/datum/reagent_holder/copy_holder = new + copy_holder.add_reagent(id, allocated) + S.set_up(copy_holder, allocated * 0.05, 0, location) //This ghetto smoke solution is 1/8th as effecient as chemsmoke playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) spawn(0) S.start() @@ -1314,23 +1289,23 @@ 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) - if(volume >= 3) - if(!istype(T, /turf/space)) - var/obj/effect/debris/cleanable/greenglow/glow = locate(/obj/effect/debris/cleanable/greenglow, T) +/datum/reagent/polonium/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(allocated >= 3) + if(!istype(target, /turf/space)) + var/obj/effect/debris/cleanable/greenglow/glow = locate(/obj/effect/debris/cleanable/greenglow, target) if(!glow) - new /obj/effect/debris/cleanable/greenglow(T) - return + new /obj/effect/debris/cleanable/greenglow(target) /datum/reagent/superhol name = "Superhol" @@ -1339,10 +1314,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 76fe7a487c0..be8187b8b0c 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/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/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/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/affect_touch(mob/living/carbon/M, alien, removed) +/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 @@ -111,21 +113,21 @@ M.add_chemical_effect(CE_SPEEDBOOST, 1) if(prob(5))// Speed boost and emotes M.emote(pick("twitch", "blink_r", "shiver")) - if(world.time > (data + (60*10))) - data = world.time + if(world.time > (metabolism.blackboard["last-message"] + (60*10))) + 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... + metabolism.legacy_current_holder.remove_reagent(/datum/reagent/topical/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/blood/blood.dm b/code/modules/reagents/chemistry/reagents/blood/blood.dm new file mode 100644 index 00000000000..5806a8e0ac8 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/blood/blood.dm @@ -0,0 +1,128 @@ +// 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/preprocess_data(data_initializer) + return data_initializer + +/datum/reagent/blood/mix_data(datum/blood_mixture/old_data, old_volume, datum/blood_mixture/new_data, new_volume, datum/reagent_holder/holder) + if(!old_data) + old_data = new + if(new_data) + old_data.unsafe_merge_other_into_self(new_data, new_volume, old_volume) + return old_data + +/datum/reagent/blood/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(allocated < 3) + return + . = max(., min(allocated, 3)) + blood_splatter_legacy(target, data, TRUE) + +/datum/reagent/blood/legacy_affect_ingest(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) + var/datum/blood_mixture/data = metabolism.legacy_data + + var/effective_dose = metabolism.total_processed_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.regen_blood(metabolism.legacy_volume_remaining * volume_mod) + metabolism.legacy_current_holder.remove_reagent(id, metabolism.legacy_current_holder) + 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) + var/datum/blood_mixture/data = metabolism.legacy_data + 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 + + M.inject_blood_legacy(metabolism.legacy_data, volume_mod * metabolism.legacy_volume_remaining) + metabolism.legacy_current_holder.remove_reagent(id, metabolism.legacy_volume_remaining) diff --git a/code/modules/reagents/chemistry/reagents/blood/blood_fragment.dm b/code/modules/reagents/chemistry/reagents/blood/blood_fragment.dm new file mode 100644 index 00000000000..241ba82a5df --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/blood/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/reagents/chemistry/reagents/blood/blood_holder.dm b/code/modules/reagents/chemistry/reagents/blood/blood_holder.dm new file mode 100644 index 00000000000..66a57abfffd --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/blood/blood_holder.dm @@ -0,0 +1,232 @@ +//* 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. + * * This datum is differently optimized than /datum/blood_mixture, which is + * intended as a method of transferring blood around the game's code. + */ +/datum/blood_holder + /// Our host blood. + VAR_PRIVATE/datum/blood_fragment/host_blood + /// Amount of host blood we have + VAR_PRIVATE/host_blood_volume = 0 + /// Our fragments that aren't ourselves, associated to their volume. + VAR_PRIVATE/list/datum/blood_fragment/guest_blood_volumes + /// Total amount of guest blood volume; this is a cached value + VAR_PRIVATE/tmp/cached_guest_blood_volume = 0 + +// todo: serialize +// todo: deserialize +// todo: clone + +//* total blood *// + +/datum/blood_holder/proc/get_total_volume() + return host_blood_volume + cached_guest_blood_volume + +//* host blood *// + +/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 + +/datum/blood_holder/proc/get_host_volume() + return host_blood_volume + +/** + * @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 - . + +//* guest blood *// + +// none yet because we haven't needed any yet + +//* mixture operations *// + +/** + * @return amount injected + */ +/datum/blood_holder/proc/inject_mixture(datum/blood_mixture/mixture, amount) + if(isnull(amount)) + amount = mixture.ctx_return_amount + if(amount <= 0) + return 0 + + var/old_volume = host_blood_volume + cached_guest_blood_volume + var/list/fragment_ratios = mixture.unsafe_get_fragment_list_ref() + for(var/datum/blood_fragment/fragment as anything in fragment_ratios) + var/ratio = fragment_ratios[fragment] + inject_fragment(fragment, amount * ratio) + return (cached_guest_blood_volume + host_blood_volume) - old_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) + + for(var/i in 1 to length(guest_blood_volumes)) + var/datum/blood_fragment/checking = guest_blood_volumes[i] + if(checking.equivalent(fragment)) + guest_blood_volumes[checking] += amount + return + guest_blood_volumes[fragment] = amount + +/** + * Erases blood uniformly. + * + * * Fails if we don't have enough. + * + * @return amount erased + */ +/datum/blood_holder/proc/checked_erase(amount) + return (cached_guest_blood_volume + host_blood_volume) < amount ? 0 : erase(amount) + +/** + * Erases blood uniformly. + * + * @return amount erased + */ +/datum/blood_holder/proc/erase(amount) + var/total = host_blood_volume + cached_guest_blood_volume + . = min(amount, total) + var/multiplier = max(0, 1 - (. / total)) + + host_blood_volume *= multiplier + cached_guest_blood_volume *= multiplier + for(var/datum/blood_fragment/fragment as anything in guest_blood_volumes) + guest_blood_volumes[fragment] *= multiplier + + if(cached_guest_blood_volume <= 0) + guest_blood_volumes.len = 0 + cached_guest_blood_volume = 0 + +/** + * 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 + return (cached_guest_blood_volume + host_blood_volume) < amount ? 0 : draw(amount) + +/** + * 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/total = host_blood_volume + cached_guest_blood_volume + amount = min(amount, total) + var/multiplier = max(0, 1 - (amount / total)) + + var/datum/blood_mixture/creating = new + creating.ctx_return_amount = amount + var/list/fragment_ratio_list = list() + for(var/datum/blood_fragment/fragment as anything in guest_blood_volumes) + fragment_ratio_list[fragment] = guest_blood_volumes[fragment] / total + fragment_ratio_list[host_blood] = host_blood_volume / total + creating.unsafe_set_fragment_list_ref(fragment_ratio_list) + + host_blood_volume *= multiplier + cached_guest_blood_volume *= multiplier + for(var/datum/blood_fragment/fragment as anything in guest_blood_volumes) + guest_blood_volumes[fragment] *= multiplier + + if(cached_guest_blood_volume <= 0) + guest_blood_volumes.len = 0 + cached_guest_blood_volume = 0 + return creating + +/** + * Gets our overall color. + */ +/datum/blood_holder/proc/get_color() + // red, green, blue, alpha, total + var/list/rgbat = decode_rgba(host_blood.color || "#000000") + // index 5 + rgbat += host_blood_volume + + for(var/datum/blood_fragment/fragment as anything in guest_blood_volumes) + var/guest_volume = guest_blood_volumes[fragment] + var/list/decoded_guest = decode_rgba(fragment.color || "#000000") + rgbat[1] += decoded_guest[1] * guest_volume + rgbat[2] += decoded_guest[2] * guest_volume + rgbat[3] += decoded_guest[3] * guest_volume + rgbat[4] += decoded_guest[4] * guest_volume + rgbat[5] += guest_volume + + var/divisor = rgbat[5] + return rgb( + rgbat[1] / divisor, + rgbat[2] / divisor, + rgbat[3] / divisor, + rgbat[4] / divisor, + ) + +//* misc *// + +/** + * assimilate all guest bloods immediately + */ +/datum/blood_holder/proc/do_assimilate_all() + host_blood_volume += max(0, cached_guest_blood_volume) + cached_guest_blood_volume = 0 + guest_blood_volumes = list() + +/** + * assimilates guest blood + */ +/datum/blood_holder/proc/do_assimilate(volume) + if(!length(guest_blood_volumes)) + return + var/max_assimilate_per_iteration = max(volume * (1 / 7), volume / length(guest_blood_volumes)) + var/iterations = 0 + while(volume > 0) + if(++iterations > 20) + CRASH("too many iterations in blood assimilation simualtion") + var/random_index = rand(1, length(guest_blood_volumes)) + var/datum/blood_fragment/random_fragment = guest_blood_volumes[random_index] + var/random_volume = guest_blood_volumes[random_fragment] + var/random_assimilate = min(random_volume, max(volume * 0.5, max_assimilate_per_iteration)) + volume -= random_assimilate + cached_guest_blood_volume -= random_assimilate + guest_blood_volumes[random_fragment] -= random_assimilate + host_blood_volume += random_assimilate + +//* unsafe access to internals *// + +/** + * Get a direct reference to host blood. + * * You are not allowed to edit this. + */ +/datum/blood_holder/proc/unsafe_get_host_fragment_ref() + return host_blood diff --git a/code/modules/reagents/chemistry/reagents/blood/blood_mixture.dm b/code/modules/reagents/chemistry/reagents/blood/blood_mixture.dm new file mode 100644 index 00000000000..8468bf05bb8 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/blood/blood_mixture.dm @@ -0,0 +1,220 @@ +//* 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 + var/legacy_trace_chem + var/legacy_is_synthetic = FALSE + + /// Fragments, associated to volume in units. + VAR_PRIVATE/list/datum/blood_fragment/fragment_ratios + + /// 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 + +// todo: serialize +// todo: deserialize + +/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(fragment_ratios)) + copy.fragment_ratios = list() + for(var/datum/blood_fragment/data as anything in fragment_ratios) + copy.fragment_ratios[data.clone()] = fragment_ratios[data] + return copy + +/** + * Get overall color + */ +/datum/blood_mixture/proc/get_color() + // red, green, blue, alpha, total + var/list/rgbat = list(0, 0, 0, 0, 0) + + for(var/datum/blood_fragment/fragment as anything in fragment_ratios) + var/guest_volume = fragment_ratios[fragment] + var/list/decoded_guest = decode_rgba(fragment.color || "#000000") + rgbat[1] += decoded_guest[1] * guest_volume + rgbat[2] += decoded_guest[2] * guest_volume + rgbat[3] += decoded_guest[3] * guest_volume + rgbat[4] += decoded_guest[4] * guest_volume + rgbat[5] += guest_volume + + var/divisor = rgbat[5] + return rgb( + rgbat[1] / divisor, + rgbat[2] / divisor, + rgbat[3] / divisor, + rgbat[4] / divisor, + ) + +/** + * Get overall name + */ +/datum/blood_mixture/proc/get_name() + var/datum/blood_fragment/highest_fragment + var/highest_ratio + for(var/datum/blood_fragment/fragment as anything in fragment_ratios) + if(fragment_ratios[fragment] > highest_ratio) + highest_ratio = fragment_ratios[fragment] + highest_fragment = fragment + return highest_fragment?.legacy_name + +/** + * Get atom blood DNA variable + * + * * this is shit, ugh + */ +/datum/blood_mixture/proc/legacy_get_forensic_blood_dna() + if(!length(fragment_ratios)) + return null + var/highest_so_far + var/datum/blood_fragment/highest_to_use + for(var/datum/blood_fragment/fragment as anything in fragment_ratios) + if(fragment_ratios[fragment] > highest_so_far) + highest_so_far = fragment_ratios[fragment] + highest_to_use = fragment + return list( + (highest_to_use.legacy_blood_dna) = (highest_to_use.legacy_blood_type), + ) + +/** + * Sets our internal fragment list reference to a given value. + * + * * passed in fragment ratios are not auto-normalized; you must do that. + * * passed in fragment ratios are directly referenced, not copied. + * + * @params + * * fragment_ratios - list of /datum/blood_fragment to number as a ratio from (0, 1] + */ +/datum/blood_mixture/proc/unsafe_set_fragment_list_ref(list/fragment_ratios) + src.fragment_ratios = fragment_ratios + +/** + * Returns an immutable reference to our internal fragment list. + * + * * You may only use the keys of the returned list to access fragments in a read-only fashion. + * You may not use this to infer the ratio or volume of any fragment. + * * You are not allowed to edit this list or the datums in its entries in any way, + * unless you clone() said datums or deep_list_clone() the list first. + */ +/datum/blood_mixture/proc/unsafe_get_fragment_list_ref() as /list + RETURN_TYPE(/list) + return fragment_ratios + +/** + * Returns an immutable reference to a fragment. + * + * * You are not allowed to edit the returned datum without clone()ing it first. + * * This is checked to ensure the index isn't out of bounds on the high end. + */ +/datum/blood_mixture/proc/unsafe_get_fragment_ref(index) as /datum/blood_fragment + RETURN_TYPE(/datum/blood_fragment) + if(index < length(fragment_ratios)) + return + return fragment_ratios[index] + +/** + * Merge other into self. This changes self. + * * Don't call unless you know what you're doing. + * + * @params + * * other - the mixture being merged into self + * * other_volume - their volume + * * self_volume - our volume + */ +/datum/blood_mixture/proc/unsafe_merge_other_into_self(datum/blood_mixture/other, other_volume, self_volume) + if(other.legacy_antibodies) + LAZYDISTINCTADD(legacy_antibodies, other.legacy_antibodies) + if(other.legacy_is_synthetic) + legacy_is_synthetic ||= other.legacy_is_synthetic + if(other.legacy_virus2) + LAZYDISTINCTADD(legacy_virus2, other.legacy_virus2) + if(other.legacy_trace_chem) + var/list/yikes = length(legacy_trace_chem) ? params2list(legacy_trace_chem) : list() + yikes ||= params2list(other.legacy_trace_chem) + legacy_trace_chem = list2params(yikes) + + if(!self_volume) + fragment_ratios = list() + if(length(other.fragment_ratios)) + for(var/datum/blood_fragment/data as anything in other.fragment_ratios) + fragment_ratios[data.clone()] = other.fragment_ratios[data] + return + + var/their_ratio_to_us = other_volume / self_volume + for(var/datum/blood_fragment/their_fragment as anything in other.fragment_ratios) + var/their_fragment_ratio = other.fragment_ratios[their_fragment] + var/datum/blood_fragment/found_our_fragment = their_fragment + + for(var/datum/blood_fragment/our_fragment as anything in fragment_ratios) + if(!our_fragment.equivalent(their_fragment)) + continue + found_our_fragment = our_fragment + break + + fragment_ratios[found_our_fragment] += their_ratio_to_us * their_fragment_ratio + + // auto prune from 11+ to 5 if needed + if(length(fragment_ratios) > 10) + fragment_ratios = tim_sort(fragment_ratios, /proc/cmp_numeric_dsc, TRUE) + var/pruned_ratio = 0 + for(var/i in 6 to length(fragment_ratios)) + pruned_ratio += fragment_ratios[fragment_ratios[i]] + fragment_ratios[fragment_ratios[length(fragment_ratios)]] += pruned_ratio + // perform ratio fixup + var/total_ratio = 0 + for(var/key in fragment_ratios) + total_ratio += fragment_ratios[key] + var/fixup_multiplier = 1 / total_ratio + for(var/key in fragment_ratios) + fragment_ratios[key] *= fixup_multiplier + +//* Subtypes *// + +/datum/blood_mixture/preset + +/datum/blood_mixture/preset/New() + ..() + setup() + +/datum/blood_mixture/preset/proc/setup() + return + +/datum/blood_mixture/preset/single + var/tmp/prefill_volume + +/datum/blood_mixture/preset/single/setup() + ..() + LAZYINITLIST(fragment_ratios) + fragment_ratios[create_preset_fragment()] = 1 + +/datum/blood_mixture/preset/single/proc/create_preset_fragment() + return new /datum/blood_fragment + +/datum/blood_mixture/preset/single/synthblood + +/datum/blood_mixture/preset/single/synthblood/setup() + ..() + legacy_is_synthetic = TRUE + +/datum/blood_mixture/preset/single/synthblood/create_preset_fragment() + var/datum/blood_fragment/creating = ..() + creating.color = "#999966" + creating.legacy_blood_type = "O-" + return creating 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 00000000000..b07a754fbc1 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/core/acid.dm @@ -0,0 +1,91 @@ +/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.") + metabolism.legacy_current_holder.remove_reagent(id, metabolism.legacy_volume_remaining) + 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.") + metabolism.legacy_current_holder.remove_reagent(id, metabolism.legacy_volume_remaining) + 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(metabolism.legacy_volume_remaining < meltdose) // Not enough to melt anything + M.take_random_targeted_damage(brute = 0, burn = 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) && metabolism.legacy_volume_remaining >= 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/on_touch_obj(obj/target, remaining, allocated, data, spread_between) + . = ..() + var/obj/O = target + if(O.integrity_flags & (INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF)) + return + // todo: newacid + if((istype(O, /obj/item) || istype(O, /obj/effect/plant)) && (allocated > 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) + . += meltdose + diff --git a/code/modules/reagents/chemistry/reagents/core/elements.dm b/code/modules/reagents/chemistry/reagents/core/elements.dm index afcd425c111..cfee51c01a9 100644 --- a/code/modules/reagents/chemistry/reagents/core/elements.dm +++ b/code/modules/reagents/chemistry/reagents/core/elements.dm @@ -26,24 +26,27 @@ 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) -/datum/reagent/carbon/touch_turf(turf/T) +/datum/reagent/carbon/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/T = target if(!istype(T, /turf/space)) var/obj/effect/debris/cleanable/dirt/dirtoverlay = locate(/obj/effect/debris/cleanable/dirt, T) if (!dirtoverlay) dirtoverlay = new/obj/effect/debris/cleanable/dirt(T) - dirtoverlay.alpha = volume * 30 + dirtoverlay.alpha = allocated * 30 else - dirtoverlay.alpha = min(dirtoverlay.alpha + volume * 30, 255) + dirtoverlay.alpha = min(dirtoverlay.alpha + allocated * 30, 255) /datum/reagent/chlorine name = "Chlorine" @@ -53,10 +56,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,12 +77,20 @@ 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/gold + name = "Gold" + id = "gold" + description = "Gold is a dense, soft, shiny metal and the most malleable and ductile metal known." + taste_description = "metal" + reagent_state = REAGENT_SOLID + color = "#F7C430" + /datum/reagent/hydrogen name = "Hydrogen" id = "hydrogen" @@ -96,7 +107,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 +119,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 +134,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 +158,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,10 +170,18 @@ 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 +/datum/reagent/platinum + name = "Platinum" + id = "platinum" + description = "Platinum is a dense, malleable, ductile, highly unreactive, precious, gray-white transition metal. It is very resistant to corrosion." + taste_description = "metal" + reagent_state = REAGENT_SOLID + color = "#777777" + /datum/reagent/potassium name = "Potassium" id = "potassium" @@ -179,7 +198,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)) @@ -189,13 +208,15 @@ if(prob(5)) M.antibodies |= V.antigen -/datum/reagent/radium/touch_turf(turf/T) - if(volume >= 3) +/datum/reagent/radium/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/T = target + if(allocated >= 3) if(!istype(T, /turf/space)) var/obj/effect/debris/cleanable/greenglow/glow = locate(/obj/effect/debris/cleanable/greenglow, T) if(!glow) new /obj/effect/debris/cleanable/greenglow(T) - return /datum/reagent/silicon name = "Silicon" @@ -205,6 +226,14 @@ reagent_state = REAGENT_SOLID color = "#A8A8A8" +/datum/reagent/silver + name = "Silver" + id = "silver" + description = "A soft, white, lustrous transition metal, it has the highest electrical conductivity of any element and the highest thermal conductivity of any metal." + taste_description = "metal" + reagent_state = REAGENT_SOLID + color = "#D0D0D0" + /datum/reagent/sodium name = "Sodium" id = "sodium" @@ -221,3 +250,25 @@ taste_mult = 0 //no taste reagent_state = REAGENT_SOLID color = "#DCDCDC" + +/datum/reagent/uranium + name ="Uranium" + id = "uranium" + description = "A silvery-white metallic chemical element in the actinide series, weakly radioactive." + taste_description = "metal" + reagent_state = REAGENT_SOLID + color = "#B8B8C0" + +/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/legacy_affect_blood(mob/living/carbon/M, alien, removed, datum/reagent_metabolism/metabolism) + M.apply_effect(5 * removed, IRRADIATE, 0) + +/datum/reagent/uranium/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + if(allocated >= 3) + if(!istype(target, /turf/space)) + var/obj/effect/debris/cleanable/greenglow/glow = locate(/obj/effect/debris/cleanable/greenglow, target) + if(!glow) + new /obj/effect/debris/cleanable/greenglow(target) 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 00000000000..0b0dffb6b45 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/core/ethanol.dm @@ -0,0 +1,162 @@ +#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/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + var/mob/living/L = target + if(istype(L)) + L.adjust_fire_stacks(allocated / 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 = metabolism.legacy_volume_remaining * strength_mod * ABV * min(1,metabolism.total_processed_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) + var/metabolized = 0 + if(hydration_removal>0) + M.adjust_hydration(-hydration_removal) + metabolized += 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) + metabolized += 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(metabolism.legacy_volume_remaining>DOSE_LEVEL*7) + metabolized += REM // liver working overtime, or whatever (mostly to prevent people from always just dying from this) + if(metabolized) + metabolism.legacy_current_holder.remove_reagent(id, metabolized) + #undef DOSE_LEVEL + +/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/on_touch_obj(obj/target, remaining, allocated, data) + . = ..() + + var/obj/O = target + 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(allocated < 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 00000000000..f1d6d8994e5 --- /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 00000000000..2eb4677a8ab --- /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) + 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 00000000000..f9c5f0290bd --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/nutrition/nutriment.dm @@ -0,0 +1,103 @@ +/** + * 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) + LAZYSET(taste, description, amount) + +/datum/nutriment_data/proc/add_taste(description, amount, skip_culling) + LAZYINITLIST(taste) + taste[description] = taste[description] + amount + + if(!skip_culling) + cull_taste() + +/datum/nutriment_data/proc/remove_taste(description, amount) + if(!amount) + LAZYREMOVE(taste, description) + else if(taste[description]) + taste[description] -= amount + if(taste[description] <= 0) + taste -= description + +/datum/nutriment_data/proc/cull_taste() + // this should be a define (10 being max) but idrc lol + tim_sort(taste, /proc/cmp_numeric_dsc, TRUE) + 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 + +/** + * * data: /datum/nutriment_data + */ +/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 && !(M.species.species_flags & NO_NUTRITION_GAIN)) // 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 00000000000..1f87af0b867 --- /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() + ..() + +/datum/reagent/nutriment/coating/compute_name_with_data(datum/nutriment_data/data) + return data.cooked ? 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 00000000000..771128523c1 --- /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 00000000000..5344a51cbd1 --- /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 00000000000..78938ad0685 --- /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 37009458c0f..908c3d634e4 100644 --- a/code/modules/reagents/chemistry/reagents/other/cleaner.dm +++ b/code/modules/reagents/chemistry/reagents/other/cleaner.dm @@ -7,15 +7,24 @@ color = "#A5F0EE" touch_met = 50 -/datum/reagent/space_cleaner/touch_obj(var/obj/O, amount) +/datum/reagent/space_cleaner/on_touch_obj(obj/target, remaining, allocated, data) + . = ..() + + var/obj/O = target O.clean_blood() - O.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (amount / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (amount / 10))) + O.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (allocated / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (allocated / 10))) -/datum/reagent/space_cleaner/touch_mob(mob/M, amount) - M.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (amount / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (amount / 10))) +/datum/reagent/space_cleaner/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() -/datum/reagent/space_cleaner/touch_turf(turf/T) - if(volume >= 1) + var/mob/M = target + M.clean_radiation(RAD_CONTAMINATION_CLEANSE_POWER * (allocated / 10), RAD_CONTAMINATION_CLEANSE_FACTOR ** (1 / (allocated / 10))) + +/datum/reagent/space_cleaner/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/T = target + if(allocated >= 1) if(istype(T, /turf/simulated)) var/turf/simulated/S = T S.dirt = 0 @@ -24,7 +33,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 +60,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 00000000000..d25059e2f6b --- /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/firefighting_foam.dm b/code/modules/reagents/chemistry/reagents/other/firefighting_foam.dm new file mode 100644 index 00000000000..e45cee3aaf4 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/other/firefighting_foam.dm @@ -0,0 +1,57 @@ +/datum/reagent/firefighting_foam + name = "Firefighting Foam" + id = "firefoam" + description = "A historical fire suppressant. Originally believed to simply displace oxygen to starve fires, it actually interferes with the combustion reaction itself. Vastly superior to the cheap water-based extinguishers found on most NT vessels." + reagent_state = REAGENT_LIQUID + color = "#A6FAFF" + taste_description = "the inside of a fire extinguisher" + +/datum/reagent/firefighting_foam/on_touch_turf(turf/target, remaining, allocated, data) + . = ..() + + var/turf/T = target + if(allocated >= 1) + var/obj/effect/foam/firefighting/F = (locate(/obj/effect/foam/firefighting) in T) + if(!F) + F = new(T) + else if(istype(F)) + F.lifetime = initial(F.lifetime) //reduce object churn a little bit when using smoke by keeping existing foam alive a bit longer + + var/datum/gas_mixture/environment = T.return_air() + var/min_temperature = T0C + 100 // 100C, the boiling point of water + + var/hotspot = (locate(/atom/movable/fire) in T) + if(hotspot && !isspaceturf(T)) + var/datum/gas_mixture/lowertemp = T.remove_cell_volume() + lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), TCMB) + lowertemp.react() + T.assume_air(lowertemp) + qdel(hotspot) + + if (environment && environment.temperature > min_temperature) // Abstracted as steam or something + var/removed_heat = between(0, allocated * 19000, -environment.get_thermal_energy_change(min_temperature)) + environment.adjust_thermal_energy(-removed_heat) + if(prob(5)) + T.visible_message("The foam sizzles as it lands on \the [T]!") + +/datum/reagent/firefighting_foam/on_touch_obj(obj/target, remaining, allocated, data, spread_between) + . = ..() + + var/obj/O = target + O.water_act(allocated / 5) + +/datum/reagent/firefighting_foam/on_touch_mob(mob/target, remaining, allocated, data, zone) + . = ..() + + var/mob/M = target + if(istype(M, /mob/living/simple_mob/slime)) //I'm sure foam is water-based! + var/mob/living/simple_mob/slime/S = M + var/amt = 15 * allocated * (1-S.water_resist) + if(amt>0) + S.adjustToxLoss(amt) + S.visible_message("[S]'s flesh sizzles where the foam touches it!", "Your flesh burns in the foam!") + + if(isliving(target)) + var/mob/living/L = target + L.adjust_fire_stacks(-allocated) + L.ExtinguishMob() 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 00000000000..97737d4e7b7 --- /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 00000000000..8243c77680f --- /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) + 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 00000000000..2e9a0deacd0 --- /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 00000000000..6d201c72742 --- /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) + 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 a7b6b7ef82d..a28b950de21 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 28fbe79acca..fac249035fe 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/items/hypovial.dm b/code/modules/reagents/items/hypovial.dm index 2326605f29e..17689b32f04 100644 --- a/code/modules/reagents/items/hypovial.dm +++ b/code/modules/reagents/items/hypovial.dm @@ -47,81 +47,81 @@ //* subtypes - regular /obj/item/reagent_containers/glass/hypovial/bicaridine - start_reagent = /datum/reagent/bicaridine + start_with_single_reagent = /datum/reagent/bicaridine /obj/item/reagent_containers/glass/hypovial/kelotane - start_reagent = /datum/reagent/kelotane + start_with_single_reagent = /datum/reagent/kelotane /obj/item/reagent_containers/glass/hypovial/dylovene - start_reagent = /datum/reagent/dylovene + start_with_single_reagent = /datum/reagent/dylovene /obj/item/reagent_containers/glass/hypovial/dexalin - start_reagent = /datum/reagent/dexalin + start_with_single_reagent = /datum/reagent/dexalin /obj/item/reagent_containers/glass/hypovial/tricordrazine - start_reagent = /datum/reagent/tricordrazine + start_with_single_reagent = /datum/reagent/tricordrazine /obj/item/reagent_containers/glass/hypovial/peridaxon - start_reagent = /datum/reagent/peridaxon + start_with_single_reagent = /datum/reagent/peridaxon /obj/item/reagent_containers/glass/hypovial/tramadol - start_reagent = /datum/reagent/tramadol + start_with_single_reagent = /datum/reagent/tramadol /obj/item/reagent_containers/glass/hypovial/inaprovaline - start_reagent = /datum/reagent/inaprovaline + start_with_single_reagent = /datum/reagent/inaprovaline /obj/item/reagent_containers/glass/hypovial/imidalky - start_with = list( + start_with_reagents = list( /datum/reagent/imidazoline = 30, /datum/reagent/alkysine = 30, ) /obj/item/reagent_containers/glass/hypovial/arithrazine - start_reagent = /datum/reagent/arithrazine + start_with_single_reagent = /datum/reagent/arithrazine /obj/item/reagent_containers/glass/hypovial/spaceacillin - start_reagent = /datum/reagent/spaceacillin + start_with_single_reagent = /datum/reagent/spaceacillin /obj/item/reagent_containers/glass/hypovial/dexalin_plus - start_reagent = /datum/reagent/dexalinp + start_with_single_reagent = /datum/reagent/dexalinp //* subtypes - large /obj/item/reagent_containers/glass/hypovial/large/bicaridine - start_reagent = /datum/reagent/bicaridine + start_with_single_reagent = /datum/reagent/bicaridine /obj/item/reagent_containers/glass/hypovial/large/kelotane - start_reagent = /datum/reagent/kelotane + start_with_single_reagent = /datum/reagent/kelotane /obj/item/reagent_containers/glass/hypovial/large/dylovene - start_reagent = /datum/reagent/dylovene + start_with_single_reagent = /datum/reagent/dylovene /obj/item/reagent_containers/glass/hypovial/large/dexalin - start_reagent = /datum/reagent/dexalin + start_with_single_reagent = /datum/reagent/dexalin /obj/item/reagent_containers/glass/hypovial/large/tricordrazine - start_reagent = /datum/reagent/tricordrazine + start_with_single_reagent = /datum/reagent/tricordrazine /obj/item/reagent_containers/glass/hypovial/large/peridaxon - start_reagent = /datum/reagent/peridaxon + start_with_single_reagent = /datum/reagent/peridaxon /obj/item/reagent_containers/glass/hypovial/large/tramadol - start_reagent = /datum/reagent/tramadol + start_with_single_reagent = /datum/reagent/tramadol /obj/item/reagent_containers/glass/hypovial/large/inaprovaline - start_reagent = /datum/reagent/inaprovaline + start_with_single_reagent = /datum/reagent/inaprovaline /obj/item/reagent_containers/glass/hypovial/large/imidalky - start_with = list( + start_with_reagents = list( /datum/reagent/imidazoline = 30, /datum/reagent/alkysine = 30, ) /obj/item/reagent_containers/glass/hypovial/large/arithrazine - start_reagent = /datum/reagent/arithrazine + start_with_single_reagent = /datum/reagent/arithrazine /obj/item/reagent_containers/glass/hypovial/large/spaceacillin - start_reagent = /datum/reagent/spaceacillin + start_with_single_reagent = /datum/reagent/spaceacillin /obj/item/reagent_containers/glass/hypovial/large/dexalin_plus - start_reagent = /datum/reagent/dexalinp + start_with_single_reagent = /datum/reagent/dexalinp 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 914e7182d4a..a3cd14b1fa3 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/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/Initialize(mapload) +/obj/item/reagent_containers/spray/windowsealant/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 + 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 00000000000..554aca748d6 --- /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 00000000000..808a7b87258 --- /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 050d55ef6f8..32d211057ca 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 f78c043b66d..cb7cab583be 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 e28b3074f4e..01014b915cb 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 95e3b73397d..6e019b73f0b 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.dm b/code/modules/reagents/reagent_containers.dm index 8ff48d2f083..9512d4099ad 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -6,32 +6,50 @@ w_class = WEIGHT_CLASS_SMALL item_flags = ITEM_CAREFUL_BLUDGEON | ITEM_ENCUMBERS_WHILE_HELD - /// start reagent list. overrides reagent/volume. list(id = volume); volume must be specified. - var/list/start_with + /// start reagent list. list(id = volume); volume must be specified. + /// * overrides [start_with_single_reagent], [start_with_single_volume], and [start_with_single_data_initializer] + var/list/start_with_reagents + /// start reagent datas. only checked if [start_with_reagents] is set. list(id = data). this is optional. + var/list/start_with_reagents_data_initializers + /// start reagent id or path - var/start_reagent + var/start_with_single_reagent /// start reagent amount. null for max. - var/start_volume + var/start_with_single_volume + /// start reagent data initializer + var/start_with_single_data_initializer + /// volume of our default reagents holder + // todo: rename to something else var/volume = 30 - /// automatically rename to [[start_reagent]] + /// automatically rename to [[start_with_single_reagent]] + // todo: rename to something else var/start_rename = FALSE + // todo: shouldn't be settable for drinking / feeding var/amount_per_transfer_from_this = 5 + // todo: typelist? var/possible_transfer_amounts = list(5,10,15,25,30) /obj/item/reagent_containers/Initialize(mapload) . = ..() + // todo: shouldn't be a verb if(!possible_transfer_amounts) remove_obj_verb(src, /obj/item/reagent_containers/verb/set_APTFT) + // create reagents create_reagents(volume) - if(!isnull(start_with)) - for(var/id in start_with) - reagents.add_reagent(id, start_with[id]) - else if(!isnull(start_reagent)) - reagents.add_reagent(start_reagent, isnull(start_volume)? volume : start_volume) + // use multi if provided + if(start_with_reagents) + for(var/id in start_with_reagents) + reagents.add_reagent(id, start_with_reagents[id], start_with_reagents_data_initializers?[id]) + start_with_reagents = null + if(start_with_reagents_data_initializers) + start_with_reagents_data_initializers = null + // else use single if provided + else if(start_with_single_reagent) + reagents.add_reagent(start_with_single_reagent, isnull(start_with_single_volume)? volume : start_with_single_volume, start_with_single_data_initializer) if(start_rename) - var/datum/reagent/R = start_reagent + var/datum/reagent/R = start_with_single_reagent name = "[name] ([initial(R.name)])" /obj/item/reagent_containers/verb/set_APTFT() //set amount_per_transfer_from_this diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 92bade4d10e..04287205164 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -36,7 +36,14 @@ 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)) + // todo: standard way of doing this + var/datum/blood_mixture/mixture = new + var/datum/blood_fragment/fragment = new + fragment.legacy_blood_type = blood_type + mixture.unsafe_set_fragment_list_ref(list( + (fragment) = 1, + )) + reagents.add_reagent("blood", 200, mixture) 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 7a7c947e218..7dc1b88aa12 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 9d911d7b0e3..09cb9d1497d 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 6088bce7071..f0614b65c89 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.perform_entity_dip(W, 1) /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." @@ -420,7 +420,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(10,20,50,100) volume = 60 - start_reagent = /datum/reagent/fuel + start_with_single_reagent = /datum/reagent/fuel /obj/item/reagent_containers/portable_fuelcan/afterattack(atom/target, mob/user, clickchain_flags, list/params) if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) @@ -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 c925720aa71..4e1a1e9573c 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" @@ -179,7 +180,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." @@ -442,7 +443,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 c6e8ade3ade..2945bfd9064 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.perform_entity_dip(W, 1) /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 73915d211eb..00000000000 --- 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 3e824eadb78..4066e03bf85 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -99,7 +99,6 @@ to_chat(user, "You are already drawing blood from [T.name].") return - var/datum/reagent/B drawing = 1 if(istype(T, /mob/living/carbon/human)) var/mob/living/carbon/human/H = T @@ -110,20 +109,15 @@ if(!do_mob(user, target, time)) drawing = 0 return - B = T.take_blood(src, amount) + T.take_blood_legacy(src, amount) drawing = 0 else if(!do_mob(user, target, time)) drawing = 0 return - B = T.take_blood(src,amount) + 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 ef76dd32287..9707ad37077 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 079c6d55080..63aa99eb2c8 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -814,14 +814,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 8cd39108f69..1a4d170fec6 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 4f807228f54..c4f452a936b 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/modules/tools/sleeper.dm b/code/modules/vehicles/sealed/mecha/modules/tools/sleeper.dm index f10ddc8bad7..5bf899048dd 100644 --- a/code/modules/vehicles/sealed/mecha/modules/tools/sleeper.dm +++ b/code/modules/vehicles/sealed/mecha/modules/tools/sleeper.dm @@ -246,15 +246,15 @@ // filter 3 units per chem-type inside them, or remaining volume, whichever is lesser // we will also pump out 1/3 of that volume as their blood. var/filtered_volume = M.reagents?.filter_to_void( - length(M.reagents.reagent_list) * 3, + length(M.reagents.reagent_volumes) * 3, dialysis_reagent_filter_flags ) - M.vessel.filter_to_void(filtered_volume * (1 / 3),dialysis_reagent_filter_flags) + M.erase_blood(filtered_volume * (1 / 3)) S.chassis.use_power(S.energy_drain) if(S.pumping > 0) // filter 3 units per chem-type inside them, or remaining volume, whichever is lesser M.ingested?.filter_to_void( - length(M.ingested.reagent_list) * 3, + length(M.ingested.reagent_volumes) * 3, dialysis_reagent_filter_flags, ) S.chassis.use_power(S.energy_drain) diff --git a/code/modules/vehicles/sealed/mecha/modules/tools/syringe_gun.dm b/code/modules/vehicles/sealed/mecha/modules/tools/syringe_gun.dm index 25b3ba703ca..524e22506ba 100644 --- a/code/modules/vehicles/sealed/mecha/modules/tools/syringe_gun.dm +++ b/code/modules/vehicles/sealed/mecha/modules/tools/syringe_gun.dm @@ -192,9 +192,8 @@ /obj/item/vehicle_module/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 ffe41f72a5b..ec60b701afb 100644 --- a/code/modules/virus2/centrifuge.dm +++ b/code/modules/virus2/centrifuge.dm @@ -56,12 +56,12 @@ data["busy"] = "Isolating pathogens..." else if(sample) - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list - if(B) - data["antibodies"] = antigens2string(B.data["antibodies"], none=null) + var/datum/blood_mixture/sample_blood_mixture = legacy_virus2_access_blood_mixture(sample.reagents) + if(sample_blood_mixture) + data["antibodies"] = antigens2string(sample_blood_mixture.legacy_antibodies, none=null) var/list/pathogens[0] - var/list/virus = B.data["virus2"] + var/list/virus = sample_blood_mixture.legacy_virus2 for(var/ID in virus) var/datum/disease2/disease/V = virus[ID] pathogens.Add(list(list("name" = V.name(), "spread_type" = V.spreadtype, "reference" = "\ref[V]"))) @@ -69,9 +69,8 @@ data["pathogens"] = pathogens else - var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list - if(A) - data["antibodies"] = antigens2string(A.data["antibodies"], none=null) + if(sample.reagents.reagent_datas?[/datum/reagent/antibodies::id]) + data["antibodies"] = antigens2string(sample.reagents.reagent_datas?[/datum/reagent/antibodies::id], none=null) data["is_antibody_sample"] = 1 return data @@ -101,8 +100,7 @@ print(user) . = TRUE if("isolate") - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list - if(B) + if(sample.reagents.has_reagent(/datum/reagent/blood::id)) var/datum/disease2/disease/virus = locate(params["isolate"]) virus2 = virus.getcopy() isolating = 40 @@ -110,12 +108,11 @@ . = TRUE if("antibody") var/delay = 20 - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list - if(!B) + if(!sample.reagents.has_reagent(/datum/reagent/blood::id)) state("\The [src] buzzes, \"No antibody carrier detected.\"", "blue") return TRUE - var/has_toxins = locate(/datum/reagent/toxin) in sample.reagents.reagent_list + var/has_toxins = sample.reagents.has_reagent(/datum/reagent/toxin::id) var/has_radium = sample.reagents.has_reagent("radium") if(has_toxins || has_radium) state("\The [src] beeps, \"Pathogen purging speed above nominal.\"", "blue") @@ -136,11 +133,12 @@ . = TRUE /obj/machinery/computer/centrifuge/proc/cure() - if(!sample) return - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list - if(!B) return - - var/list/data = list("antibodies" = B.data["antibodies"]) + if(!sample) + return + var/datum/blood_mixture/sample_blood_mixture = legacy_virus2_access_blood_mixture(sample.reagents) + if(!sample_blood_mixture) + return + var/list/data = list("antibodies" = sample_blood_mixture.legacy_antibodies) var/amt= sample.reagents.get_reagent_amount("blood") sample.reagents.remove_reagent("blood", amt) sample.reagents.add_reagent("antibodies", amt, data) @@ -150,7 +148,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 @@ -172,13 +171,13 @@ P.info += "
" - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list - if(B) + var/datum/blood_mixture/sample_blood_mixture = legacy_virus2_access_blood_mixture(sample.reagents) + if(sample_blood_mixture) P.info += "Antibodies: " - P.info += antigens2string(B.data["antibodies"]) + P.info += antigens2string(sample_blood_mixture.legacy_antibodies) P.info += "
" - var/list/virus = B.data["virus2"] + var/list/virus = sample_blood_mixture.legacy_virus2 P.info += "Pathogens:
" if(virus.len > 0) for(var/ID in virus) @@ -188,10 +187,10 @@ P.info += "None
" else - var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list - if(A) + var/list/antibodies_list = sample.reagents.reagent_datas?[/datum/reagent/antibodies::id] + if(antibodies_list) P.info += "The following antibodies have been isolated from the blood sample: " - P.info += antigens2string(A.data["antibodies"]) + P.info += antigens2string(antibodies_list) P.info += "
" P.info += {" diff --git a/code/modules/virus2/curer.dm b/code/modules/virus2/curer.dm index 4735ccdf43c..6b988fd0ae2 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) @@ -47,11 +47,11 @@ dat = "Virus production in progress" else if(container) // see if there's any blood in the container - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in container.reagents.reagent_list + var/datum/blood_mixture/mixture_data = container.reagents.get_reagent_data(/datum/reagent/blood) - if(B) + if(mixture_data) dat = "Blood sample inserted." - dat += "
Antibodies: [antigens2string(B.data["antibodies"])]" + dat += "
Antibodies: [antigens2string(mixture_data.legacy_antibodies)]" dat += "
Begin antibody production" else dat += "
Please check container contents." @@ -94,10 +94,10 @@ /obj/machinery/computer/curer/proc/createcure(var/obj/item/reagent_containers/container) var/obj/item/reagent_containers/glass/beaker/product = new(src.loc) - var/datum/reagent/blood/B = locate() in container.reagents.reagent_list + var/datum/blood_mixture/mixture_data = container.reagents.get_reagent_data(/datum/reagent/blood) var/list/data = list() - data["antibodies"] = B.data["antibodies"] + data["antibodies"] = mixture_data.legacy_antibodies product.reagents.add_reagent("antibodies",30,data) state("\The [src.name] buzzes", "blue") diff --git a/code/modules/virus2/dishincubator.dm b/code/modules/virus2/dishincubator.dm index eb32bee10fb..782aa06a39a 100644 --- a/code/modules/virus2/dishincubator.dm +++ b/code/modules/virus2/dishincubator.dm @@ -71,16 +71,11 @@ data["blood_already_infected"] = null if (beaker) - var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list - data["can_breed_virus"] = dish && dish.virus2 && B + var/datum/blood_mixture/sample_blood_mixture = legacy_virus2_access_blood_mixture(beaker.reagents) + data["can_breed_virus"] = dish && dish.virus2 && sample_blood_mixture - if (B) - if (!B.data["virus2"]) - B.data["virus2"] = list() - - var/list/virus = B.data["virus2"] - for (var/ID in virus) - data["blood_already_infected"] = virus[ID] + for (var/ID in sample_blood_mixture?.legacy_virus2) + data["blood_already_infected"] = sample_blood_mixture.legacy_virus2[ID] ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -133,10 +128,10 @@ foodsupply += 10 SSnanoui.update_uis(src) - if (locate(/datum/reagent/toxin) in beaker.reagents.reagent_list && toxins < 100) - for(var/datum/reagent/toxin/T in beaker.reagents.reagent_list) - toxins += max(T.strength,1) - beaker.reagents.remove_reagent(T.id,1) + if(toxins < 100) + for(var/datum/reagent/toxin/tox in beaker.reagents.get_reagent_datums()) + toxins += max(tox.strength,1) + beaker.reagents.remove_reagent(tox.id,1) if(toxins > 100) toxins = 100 break @@ -186,15 +181,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/helpers.dm b/code/modules/virus2/helpers.dm index d32f1c3e53c..241f32df26b 100644 --- a/code/modules/virus2/helpers.dm +++ b/code/modules/virus2/helpers.dm @@ -178,3 +178,7 @@ if(V && V.spreadtype != vector) continue if(!infection_spreading_check(victim, V.spreadtype)) continue infect_virus2(src,V) + +/proc/legacy_virus2_access_blood_mixture(datum/reagent_holder/reagents) as /datum/blood_mixture + RETURN_TYPE(/datum/blood_mixture) + return reagents?.reagent_datas?[/datum/reagent/blood::id] diff --git a/code/modules/virus2/isolator.dm b/code/modules/virus2/isolator.dm index 80093b43806..7bda74e75b0 100644 --- a/code/modules/virus2/isolator.dm +++ b/code/modules/virus2/isolator.dm @@ -61,22 +61,21 @@ var/list/pathogen_pool = list() if(sample) - for(var/datum/reagent/blood/B in sample.reagents.reagent_list) - var/list/virus = B.data["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/datum/blood_mixture/mixture = sample.reagents.get_reagent_data(/datum/reagent/blood) + var/list/virus = mixture.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"] - pathogen_pool.Add(list(list(\ - "name" = "[D.get_true_species_name()] [B.name]", \ - "dna" = B.data["blood_DNA"], \ - "unique_id" = V.uniqueID, \ - "reference" = "\ref[V]", \ - "is_in_database" = !!R, \ - "record" = "\ref[R]"))) + pathogen_pool.Add(list(list(\ + "name" = "[ID]", \ + "dna" = "", \ + "unique_id" = V.uniqueID, \ + "reference" = "\ref[V]", \ + "is_in_database" = !!R, \ + "record" = "\ref[R]"))) data["pathogen_pool"] = pathogen_pool var/list/db = list() @@ -126,7 +125,7 @@ if("isolate") var/datum/disease2/disease/V = locate(params["isolate"]) - if (V) + if (istype(V)) virus2 = V isolating = 20 update_icon() @@ -158,11 +157,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.unsafe_get_fragment_list_ref()) + var/mob/living/carbon/human/D = blood_data.legacy_donor + P.info += "[D.get_true_species_name()] [blood_data.legacy_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 d7a14a5e4bf..fda8e62b9ed 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 4cd6acd9c21..3bc9eba681b 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 98b8a1f7ced..327c097c72a 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 d994f8a2ccf..0b4a607016f 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 fba7646ce2b..27a8348740c 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" = ( diff --git a/maps/triumph/levels/deck2.dmm b/maps/triumph/levels/deck2.dmm index b0eda9092d2..92dfb088c46 100644 --- a/maps/triumph/levels/deck2.dmm +++ b/maps/triumph/levels/deck2.dmm @@ -2274,7 +2274,7 @@ /turf/simulated/floor/tiled, /area/station/stairs_two) "gL" = ( -/obj/structure/reagent_dispensers/cookingoil, +/obj/structure/reagent_dispensers/oil, /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/plating, /area/maintenance/bar/lower) diff --git a/maps/triumph/levels/deck4.dmm b/maps/triumph/levels/deck4.dmm index 58afd98e6b9..25cc266c53c 100644 --- a/maps/triumph/levels/deck4.dmm +++ b/maps/triumph/levels/deck4.dmm @@ -2071,7 +2071,7 @@ /turf/simulated/floor/tiled/old_tile/gray, /area/security/range) "btC" = ( -/obj/structure/reagent_dispensers/cookingoil, +/obj/structure/reagent_dispensers/oil, /turf/simulated/floor/plating, /area/maintenance/central) "bul" = (