diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index b47d71beb83f..cd502f98485e 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -1,7 +1,9 @@ // Remove these once we have Byond implementation. -#define ISNAN(a) (!(a==a)) -#define ISINF(a) (!ISNAN(a) && ISNAN(a-a)) -#define IS_INF_OR_NAN(a) (ISNAN(a-a)) +// ------------------------------------ +#define IS_FINITE(a) (isnum(a) && !isinf(a)) + +#define IS_INF_OR_NAN(a) (isnan(a) || isinf(a)) + // Aight dont remove the rest // Credits to Nickr5 for the useful procs I've taken from his library resource. diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index dd3b481c3b20..3a9fe61ee9d5 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -60,7 +60,7 @@ #define CHEMICAL_QUANTISATION_LEVEL 0.0001 ///The smallest amount of volume allowed - prevents tiny numbers #define CHEMICAL_VOLUME_MINIMUM 0.001 -///Round to this, to prevent extreme decimal magic and to keep reagent volumes in line with perceived values. +//Sanity check limit to clamp chems to sane amounts and prevent rounding errors during transfer. #define CHEMICAL_VOLUME_ROUNDING 0.01 ///Default pH for reagents datum #define CHEMICAL_NORMAL_PH 7.000 diff --git a/code/datums/components/udder.dm b/code/datums/components/udder.dm index eb11de1fd8a1..c2cf27c735b7 100644 --- a/code/datums/components/udder.dm +++ b/code/datums/components/udder.dm @@ -141,4 +141,4 @@ return slug_turf.wash(CLEAN_SCRUB) reagents.expose(slug_turf, TOUCH, 5) - reagents.remove_any(5) + reagents.remove_all(5) diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 8bf038238fa2..326977fcf385 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -51,7 +51,7 @@ M.adjust_timed_status_effect(-2 SECONDS, /datum/status_effect/confusion) if(purge_alcohol) - M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3) + M.reagents.remove_reagent(/datum/reagent/consumable/ethanol, 3, include_subtypes = TRUE) M.adjust_drunk_effect(-5) if(A.stage >= 4) diff --git a/code/datums/elements/chewable.dm b/code/datums/elements/chewable.dm index aa2ff5db9284..d83fa859f067 100644 --- a/code/datums/elements/chewable.dm +++ b/code/datums/elements/chewable.dm @@ -52,7 +52,7 @@ var/metabolism_amount = metabolization_amount * delta_time if (!reagents.trans_to(item.loc, metabolism_amount, methods = INGEST)) - reagents.remove_any(metabolism_amount) + reagents.remove_all(metabolism_amount) /datum/element/chewable/proc/on_dropped(datum/source) SIGNAL_HANDLER diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d8eb1a4d966b..683646f8b2ba 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -742,7 +742,7 @@ if(user.can_see_reagents()) //Show each individual reagent . += span_notice("You see the following reagents:") for(var/datum/reagent/current_reagent as anything in reagents.reagent_list) - . += span_notice("* [round(current_reagent.volume, 0.01)] units of [current_reagent.name].") + . += span_notice("* [round(current_reagent.volume, CHEMICAL_VOLUME_ROUNDING)] units of [current_reagent.name].") if(reagents.is_reacting) . += span_alert("A chemical reaction is taking place.") diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 32fed44f4b69..7f49881f6dbd 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -101,7 +101,7 @@ else name = dryname desc = drydesc - reagents.remove_all_type(/datum/reagent/blood, INFINITY) + reagents.remove_reagent(/datum/reagent/blood, INFINITY, , include_subtypes = TRUE) var/list/temp_color = rgb2hsv(color || COLOR_WHITE) color = hsv2rgb(temp_color[1], temp_color[2], max(temp_color[3] - 100, 0)) qdel(GetComponent(/datum/component/smell)) diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index c3c747485e69..d36c5880f6c8 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -324,12 +324,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/to_smoke = smoke_all ? (reagents.total_volume * (dragtime / smoketime)) : REAGENTS_METABOLISM if(!istype(user) || ((src != user.wear_mask) && !drag)) - reagents.remove_any(to_smoke) + reagents.remove_all(to_smoke) return reagents.expose(user, INJECT, min(to_smoke / reagents.total_volume, 1)) if(!reagents.trans_to(user, to_smoke, methods = INJECT)) - reagents.remove_any(to_smoke) + reagents.remove_all(to_smoke) if(drag || COOLDOWN_FINISHED(src, smoke_cooldown)) new /obj/effect/temp_visual/cig_smoke(drop_location()) @@ -1075,7 +1075,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/mob/living/carbon/vaper = loc if(!iscarbon(vaper) || src != vaper.wear_mask) - reagents.remove_any(REAGENTS_METABOLISM) + reagents.remove_all(REAGENTS_METABOLISM) return if(reagents.get_reagent_amount(/datum/reagent/fuel)) @@ -1090,7 +1090,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM qdel(src) if(!reagents.trans_to(vaper, REAGENTS_METABOLISM, methods = INJECT)) //Going right into the bloodstream - reagents.remove_any(REAGENTS_METABOLISM) + reagents.remove_all(REAGENTS_METABOLISM) /obj/item/clothing/mask/vape/process(delta_time) var/mob/living/M = loc diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index 78a04042b564..df5387c2850b 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -44,7 +44,7 @@ var/val2remove = 1 if(cleaner?.mind) val2remove = round(cleaner.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER),0.1) - reagents.remove_any(val2remove) //reaction() doesn't use up the reagents + reagents.remove_all(val2remove) //reaction() doesn't use up the reagents /obj/item/mop/afterattack(atom/A, mob/user, proximity) diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm index 3f8bc105cdf4..47835f5f6513 100644 --- a/code/game/objects/items/tanks/watertank.dm +++ b/code/game/objects/items/tanks/watertank.dm @@ -309,7 +309,7 @@ balloon_alert(user, "still recharging!") return COOLDOWN_START(src, resin_cooldown, 10 SECONDS) - R.remove_any(100) + R.remove_all(100) var/obj/effect/resin_container/resin = new (get_turf(src)) log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].") playsound(src,'sound/items/syringeproj.ogg',40,TRUE) diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 68d001152443..9f1987903a4c 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -158,7 +158,7 @@ if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above wash_atom(movable_content) // Reagent exposure is handled in wash_atom - reagents.remove_any(SHOWER_SPRAY_VOLUME) + reagents.remove_all(SHOWER_SPRAY_VOLUME) return on = FALSE soundloop.stop() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 2358b4243e3c..e34f9a74589d 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -320,7 +320,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32) return busy = FALSE - reagents.remove_any(5) + reagents.remove_all(5) reagents.expose(user, TOUCH, 5 / max(reagents.total_volume, 5)) begin_reclamation() if(washing_face) diff --git a/code/modules/atmospherics/ZAS/Fire.dm b/code/modules/atmospherics/ZAS/Fire.dm index 23cc381f3687..9bcde7e99261 100644 --- a/code/modules/atmospherics/ZAS/Fire.dm +++ b/code/modules/atmospherics/ZAS/Fire.dm @@ -304,7 +304,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/fuel_to_remove = GASFUEL_AMOUNT_TO_LIQUID(used_liquid_fuel) //convert back to liquid volume units - liquid_fuel.reagents.remove_any(fuel_to_remove) + liquid_fuel.reagents.remove_all(fuel_to_remove) if(liquid_fuel.reagents.total_volume <= 0.1) //Precision loss kinda fucks with us here so qdel(liquid_fuel) diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm index 093871883e38..7cf9dcb0188a 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm @@ -160,7 +160,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list( dunking_target.apply_damage(min(30 * permeability * cold_multiplier, reagents.total_volume), BURN, BODY_ZONE_HEAD) if(reagents.reagent_list) //This can runtime if reagents has nothing in it. - reagents.remove_any((reagents.total_volume/2)) + reagents.remove_all((reagents.total_volume/2)) dunking_target.Paralyze(60) user.changeNext_move(CLICK_CD_MELEE) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index a7c8ce3e3f29..40542225c4e9 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -276,9 +276,9 @@ // Nutrients deplete at a constant rate, since new nutrients can boost stats far easier. apply_chemicals(lastuser?.resolve()) if(self_sustaining) - reagents.remove_any(min(0.5, nutridrain)) + reagents.remove_all(min(0.5, nutridrain)) else - reagents.remove_any(nutridrain) + reagents.remove_all(nutridrain) // Lack of nutrients hurts non-weeds if(reagents.total_volume <= 0 && !myseed.get_gene(/datum/plant_gene/trait/plant_type/weed_hardy)) diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 5b2ad5465955..280c12b1aff8 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -293,14 +293,14 @@ // Heats up the plant's contents by 25 kelvin per 1 unit of nutriment. Mutually exclusive with cooling. if(get_gene(/datum/plant_gene/trait/chem_heating)) T.visible_message(span_notice("[T] releases freezing air, consuming its nutriments to heat its contents.")) - T.reagents.remove_all_type(/datum/reagent/consumable/nutriment, num_nutriment, strict = TRUE) + T.reagents.remove_reagent(/datum/reagent/consumable/nutriment, num_nutriment) T.reagents.chem_temp = min(1000, (T.reagents.chem_temp + num_nutriment * 25)) T.reagents.handle_reactions() playsound(T.loc, 'sound/effects/wounds/sizzle2.ogg', 5) // Cools down the plant's contents by 5 kelvin per 1 unit of nutriment. Mutually exclusive with heating. else if(get_gene(/datum/plant_gene/trait/chem_cooling)) T.visible_message(span_notice("[T] releases a blast of hot air, consuming its nutriments to cool its contents.")) - T.reagents.remove_all_type(/datum/reagent/consumable/nutriment, num_nutriment, strict = TRUE) + T.reagents.remove_reagent(/datum/reagent/consumable/nutriment, num_nutriment) T.reagents.chem_temp = max(3, (T.reagents.chem_temp + num_nutriment * -5)) T.reagents.handle_reactions() playsound(T.loc, 'sound/effects/space_wind.ogg', 50) diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index 7d1f3b0c4ef0..caa5e8378415 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -549,7 +549,7 @@ reagents.expose(our_turf, TOUCH, min(1, 10 / reagents.total_volume)) // We use more water doing this then mopping - reagents.remove_any(2) //reaction() doesn't use up the reagents + reagents.remove_all(2) //reaction() doesn't use up the reagents /datum/action/toggle_buffer/build_all_button_icons(status_only = FALSE, force = FALSE) if(buffer_on) diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index 2b307e23e4d9..203e65696798 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -275,11 +275,11 @@ //keep limited if(delta_chem_factor > step_target_vol) delta_chem_factor = step_target_vol - else if (delta_chem_factor < CHEMICAL_VOLUME_MINIMUM) - delta_chem_factor = CHEMICAL_VOLUME_MINIMUM - //Normalise to multiproducts - delta_chem_factor /= product_ratio - //delta_chem_factor = round(delta_chem_factor, CHEMICAL_QUANTISATION_LEVEL) // Might not be needed - left here incase testmerge shows that it does. Remove before full commit. + + delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_VOLUME_ROUNDING) + if(delta_chem_factor <= 0) + to_delete = TRUE + return //Calculate how much product to make and how much reactant to remove factors.. for(var/reagent in reaction.required_reagents) diff --git a/code/modules/reagents/chemistry/goon_reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/goon_reagents/medicine_reagents.dm index 42a3ff15165d..54580aa72522 100644 --- a/code/modules/reagents/chemistry/goon_reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/goon_reagents/medicine_reagents.dm @@ -9,10 +9,10 @@ C.set_drowsyness(0) C.remove_status_effect(/datum/status_effect/speech/slurring/drunk) C.remove_status_effect(/datum/status_effect/confusion) - holder.remove_all_type(/datum/reagent/consumable/ethanol, 3 * removed, FALSE, TRUE) + holder.remove_reagent(/datum/reagent/consumable/ethanol, 3 * removed, include_subtypes = TRUE) var/obj/item/organ/stomach = C.getorganslot(ORGAN_SLOT_STOMACH) if(stomach) - stomach.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3 * removed, FALSE, TRUE) + stomach.reagents.remove_reagent(/datum/reagent/consumable/ethanol, 3 * removed, include_subtypes = TRUE) C.adjustToxLoss(-0.2 * removed, 0) C.adjust_drunk_effect(-10 * removed) . = TRUE diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index ec86b8386879..0880dfaa00d8 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -91,9 +91,11 @@ update_total() var/cached_total = total_volume if(cached_total + amount > maximum_volume) - amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. shouldn't happen. Will happen. - if(amount <= 0) - return FALSE + amount = maximum_volume - cached_total //Doesnt fit in. Make it disappear. shouldn't happen. Will happen. + + amount = round(amount, CHEMICAL_QUANTISATION_LEVEL) + if(amount <= 0) + return FALSE var/cached_temp = chem_temp var/list/cached_reagents = reagent_list @@ -107,7 +109,7 @@ //add the reagent to the existing if it exists for(var/datum/reagent/iter_reagent as anything in cached_reagents) if(iter_reagent.type == reagent) - iter_reagent.volume += round(amount, CHEMICAL_QUANTISATION_LEVEL) + iter_reagent.volume += amount update_total() iter_reagent.on_merge(data, amount) @@ -133,10 +135,6 @@ if(isliving(my_atom)) new_reagent.on_mob_add(my_atom, amount, metabolism_class) //Must occur before it could posibly run on_mob_delete - /* - if(has_split) //prevent it from splitting again - new_reagent.chemical_flags |= REAGENT_DONOTSPLIT - */ update_total() if(reagtemp != cached_temp) @@ -151,120 +149,112 @@ handle_reactions() return TRUE -/// Remove a specific reagent -/datum/reagents/proc/remove_reagent(reagent, amount, safety = TRUE)//Added a safety check for the trans_id_to - if(isnull(amount)) - amount = 0 - CRASH("null amount passed to reagent code") +/** + * Removes a specific reagent. can supress reactions if needed + * Arguments + * + * * [reagent_type][datum/reagent] - the type of reagent + * * amount - the volume to remove + * * safety - if FALSE will initiate reactions upon removing. used for trans_id_to + * * include_subtypes - if TRUE will remove the specified amount from all subtypes of reagent_type as well + */ +/datum/reagents/proc/remove_reagent(datum/reagent/reagent_type, amount, safety = TRUE, include_subtypes = FALSE) + if(!ispath(reagent_type)) + stack_trace("invalid reagent passed to remove reagent [reagent_type]") + return FALSE - if(!isnum(amount)) + if(!IS_FINITE(amount)) + stack_trace("non finite amount passed to remove reagent [amount] [reagent_type]") return FALSE - if(amount < 0) + amount = round(amount, CHEMICAL_QUANTISATION_LEVEL) + if(amount <= 0) return FALSE + var/total_removed_amount = 0 + var/remove_amount = 0 var/list/cached_reagents = reagent_list for(var/datum/reagent/cached_reagent as anything in cached_reagents) - if(cached_reagent.type == reagent) - //clamp the removal amount to be between current reagent amount - //and zero, to prevent removing more than the holder has stored - amount = clamp(amount, 0, cached_reagent.volume) - cached_reagent.volume -= amount - update_total() - if(!safety)//So it does not handle reactions when it need not to - handle_reactions() - SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(cached_reagent) ? reagent : cached_reagent, amount) - - return TRUE - return FALSE - -/// Remove an amount of reagents without caring about what they are -/datum/reagents/proc/remove_any(amount = 1) - var/list/cached_reagents = reagent_list - var/total_removed = 0 - var/current_list_element = 1 - var/initial_list_length = cached_reagents.len //stored here because removing can cause some reagents to be deleted, ergo length change. + //check for specific type or subtypes + if(!include_subtypes) + if(cached_reagent.type != reagent_type) + continue + else if(!istype(cached_reagent, reagent_type)) + continue - current_list_element = rand(1, cached_reagents.len) + remove_amount = min(cached_reagent.volume, amount) + cached_reagent.volume -= remove_amount - while(total_removed < amount) - // There's nothing left in the container - if(total_volume <= 0 || !cached_reagents.len) - break + update_total() + if(!safety)//So it does not handle reactions when it need not to + handle_reactions() + SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(cached_reagent) ? reagent_type : cached_reagent, amount) - if(current_list_element > cached_reagents.len) - current_list_element = 1 + total_removed_amount += remove_amount - var/datum/reagent/R = cached_reagents[current_list_element] - var/remove_amt = min(amount-total_removed,round(amount/rand(2,initial_list_length),round(amount/10,0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers. - // If the logic above means removing really tiny amounts (or even zero if it's a remove amount of 10) instead choose a sensible smallish number - // so this proc will actually finish instead of looping forever - remove_amt = max(CHEMICAL_VOLUME_ROUNDING, remove_amt) - remove_reagent(R.type, remove_amt) + //if we reached here means we have found our specific reagent type so break + if(!include_subtypes) + return total_removed_amount - current_list_element++ - total_removed += remove_amt - update_total() + return round(total_removed_amount, CHEMICAL_VOLUME_ROUNDING) - handle_reactions() - return total_removed //this should be amount unless the loop is prematurely broken, in which case it'll be lower. It shouldn't ever go OVER amount. +/** + * Removes all reagents either proportionally(amount is the direct volume to remove) + * when proportional the total volume of all reagents removed will equal to amount + * or relatively(amount is a percentile between 0->1) when relative amount is the % + * of each reagent to be removed + * + * Arguments + * + * * amount - the amount to remove + * * relative - if TRUE amount is treated as an percentage between 0->1. If FALSE amount is the direct volume to remove + */ +/datum/reagents/proc/remove_all(amount = 1, relative = FALSE) + if(!total_volume) + return FALSE -/// Removes all reagents from this holder -/datum/reagents/proc/remove_all(amount = 1) - var/list/cached_reagents = reagent_list - if(total_volume > 0) - var/part = amount / total_volume - for(var/datum/reagent/reagent as anything in cached_reagents) - remove_reagent(reagent.type, reagent.volume * part) + if(!IS_FINITE(amount)) + stack_trace("non finite amount passed to remove all reagents [amount]") + return FALSE + if(relative && (amount < 0 || amount > 1)) + stack_trace("illegal percentage value passed to remove all reagents [amount]") + return FALSE - //finish_reacting() //A just in case - update total is in here - should be unneeded, make sure to test this - handle_reactions() - return amount + amount = round(amount, CHEMICAL_QUANTISATION_LEVEL) + if(amount <= 0) + return FALSE -/// Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included. -/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1) - if(!isnum(amount)) - return 1 var/list/cached_reagents = reagent_list - var/has_removed_reagent = 0 - + var/total_removed_amount = 0 + var/part = amount + if(!relative) + part /= total_volume for(var/datum/reagent/reagent as anything in cached_reagents) - var/matches = 0 - // Switch between how we check the reagent type - if(strict) - if(reagent.type == reagent_type) - matches = 1 - else - if(istype(reagent, reagent_type)) - matches = 1 - // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type. - if(matches) - // Have our other proc handle removement - has_removed_reagent = remove_reagent(reagent.type, amount, safety) + total_removed_amount += remove_reagent(reagent.type, reagent.volume * part) + handle_reactions() - return has_removed_reagent + return round(total_removed_amount, CHEMICAL_VOLUME_ROUNDING) -/// Fuck this one reagent -/datum/reagents/proc/del_reagent(target_reagent_typepath) +/** + * Removes an specific reagent from this holder + * Arguments + * + * * [target_reagent_typepath][datum/reagent] - type typepath of the reagent to remove + */ +/datum/reagents/proc/del_reagent(datum/reagent/target_reagent_typepath) + if(!ispath(target_reagent_typepath)) + stack_trace("invalid reagent path passed to del reagent [target_reagent_typepath]") + return FALSE + + //setting the volume to 0 will allow update_total() to clear it up for us var/list/cached_reagents = reagent_list for(var/datum/reagent/reagent as anything in cached_reagents) if(reagent.type == target_reagent_typepath) - if(isliving(my_atom)) - if(reagent.metabolizing) - reagent.metabolizing = FALSE - reagent.on_mob_end_metabolize(my_atom, metabolism_class) - - if(reagent.overdosed && iscarbon(my_atom)) - reagent.overdose_end(my_atom) - - reagent.on_mob_delete(my_atom, metabolism_class) - - reagent_list -= reagent - LAZYREMOVE(previous_reagent_list, reagent.type) - qdel(reagent) + reagent.volume = 0 update_total() - SEND_SIGNAL(src, COMSIG_REAGENTS_DEL_REAGENT, reagent) - return TRUE + return TRUE + + return FALSE /// Remove every reagent except this one /datum/reagents/proc/isolate_reagent(reagent) @@ -308,7 +298,13 @@ return FALSE return TRUE -/// Like add_reagent but you can enter a list. Format it like this: list(/datum/reagent/toxin = 10, "beer" = 15) +/** + * Like add_reagent but you can enter a list. + * Arguments + * + * * [list_reagents][list] - list to add. Format it like this: list(/datum/reagent/toxin = 10, "beer" = 15) + * * [data][list] - additional data to add + */ /datum/reagents/proc/add_reagent_list(list/list_reagents, list/data) for(var/r_id in list_reagents) var/amt = list_reagents[r_id] @@ -360,12 +356,14 @@ * * round_robin - if round_robin=TRUE, so transfer 5 from 15 water, 15 sugar and 15 plasma becomes 10, 15, 15 instead of 13.3333, 13.3333 13.3333. Good if you hate floating point errors */ /datum/reagents/proc/trans_to(obj/target, amount = 1, multiplier = 1, preserve_data = TRUE, no_react = FALSE, mob/transfered_by, remove_blacklisted = FALSE, methods = NONE, show_message = TRUE, round_robin = FALSE) - var/list/cached_reagents = reagent_list - if(!target || !total_volume) - return - if(amount < 0) - return + if(QDELETED(target) || !total_volume) + return FALSE + + if(!IS_FINITE(amount)) + stack_trace("non finite amount passed to trans_to [amount] amount of reagents") + return FALSE + var/list/cached_reagents = reagent_list var/cached_amount = amount var/atom/target_atom var/datum/reagents/R @@ -481,7 +479,7 @@ holder = target.reagents else return - if(amount < 0) + if(amount <= CHEMICAL_QUANTISATION_LEVEL) return var/cached_amount = amount @@ -882,9 +880,6 @@ /datum/reagents/proc/finish_reacting() STOP_PROCESSING(SSreagents, src) is_reacting = FALSE - //Cap off values - for(var/datum/reagent/reagent as anything in reagent_list) - reagent.volume = round(reagent.volume, CHEMICAL_VOLUME_ROUNDING)//To prevent runaways. LAZYNULL(previous_reagent_list) //reset it to 0 - because any change will be different now. update_total() if(!QDELING(src)) @@ -987,10 +982,11 @@ var/datum/cached_my_atom = my_atom var/multiplier = INFINITY for(var/reagent in cached_required_reagents) - multiplier = min(multiplier, round(get_reagent_amount(reagent) / cached_required_reagents[reagent])) + multiplier = round(min(multiplier, round(get_reagent_amount(reagent) / cached_required_reagents[reagent]))) - if(multiplier == 0)//Incase we're missing reagents - usually from on_reaction being called in an equlibrium when the results.len == 0 handlier catches a misflagged reaction + if(!multiplier)//Incase we're missing reagents - usually from on_reaction being called in an equlibrium when the results.len == 0 handlier catches a misflagged reaction return FALSE + for(var/_reagent in cached_required_reagents)//this is not an object var/datum/reagent/reagent = has_reagent(_reagent) if (!reagent) @@ -1041,15 +1037,51 @@ /// Updates [/datum/reagents/var/total_volume] /datum/reagents/proc/update_total() var/list/cached_reagents = reagent_list - . = 0 // This is a relatively hot proc. - for(var/datum/reagent/reagent as anything in cached_reagents) - if((reagent.volume < 0.05) && !is_reacting) - del_reagent(reagent.type) - else if(reagent.volume <= CHEMICAL_VOLUME_MINIMUM)//For clarity - del_reagent(reagent.type) - else - . += reagent.volume - total_volume = . + var/list/deleted_reagents = list() + var/chem_index = 1 + var/num_reagents = length(cached_reagents) + var/reagent_volume = 0 + . = 0 + + //responsible for removing reagents and computing total ph & volume + //all it's code was taken out of del_reagent() initially for efficiency purposes + while(chem_index <= num_reagents) + var/datum/reagent/reagent = cached_reagents[chem_index] + chem_index += 1 + reagent_volume = round(reagent.volume, CHEMICAL_QUANTISATION_LEVEL) //round to this many decimal places + + //remove very small amounts of reagents + if(reagent_volume <= 0 || (!is_reacting && reagent_volume < CHEMICAL_VOLUME_ROUNDING)) + //end metabolization + if(isliving(my_atom)) + if(reagent.metabolizing) + reagent.metabolizing = FALSE + reagent.on_mob_end_metabolize(my_atom) + reagent.on_mob_delete(my_atom) + + //removing it and store in a seperate list for processing later + cached_reagents -= reagent + LAZYREMOVE(previous_reagent_list, reagent.type) + deleted_reagents += reagent + + //move pointer back so we don't overflow & decrease length + chem_index -= 1 + num_reagents -= 1 + continue + + //compute volume & ph like we would normally + . += reagent_volume + + //reasign rounded value + reagent.volume = reagent_volume + + //assign the final values, rounding up can sometimes cause overflow so bring it down + total_volume = min(round(., CHEMICAL_VOLUME_ROUNDING), maximum_volume) + + //now send the signals after the volume & ph has been computed + for(var/datum/reagent/deleted_reagent as anything in deleted_reagents) + SEND_SIGNAL(src, COMSIG_REAGENTS_DEL_REAGENT, deleted_reagent) + qdel(deleted_reagent) /** * Applies the relevant expose_ proc for every reagent in this holder @@ -1092,16 +1124,14 @@ /// Is this holder full or not /datum/reagents/proc/holder_full() - if(total_volume >= maximum_volume) - return TRUE - return FALSE + return total_volume >= maximum_volume /// Get the amount of this reagent /datum/reagents/proc/get_reagent_amount(reagent) var/list/cached_reagents = reagent_list for(var/datum/reagent/cached_reagent as anything in cached_reagents) if(cached_reagent.type == reagent) - return round(cached_reagent.volume, CHEMICAL_QUANTISATION_LEVEL) + return round(cached_reagent.volume, CHEMICAL_VOLUME_ROUNDING) return 0 /// Get a comma separated string of every reagent name in this holder. UNUSED diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index c981bfe4e4dc..f0458c75b8ea 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -171,12 +171,12 @@ var/beakerCurrentVolume = 0 if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents.Add(list(list("name" = R.name, "volume" = round(R.volume, 0.01)))) // list in a list because Byond merges the first list... + beakerContents.Add(list(list("name" = R.name, "volume" = round(R.volume, CHEMICAL_VOLUME_ROUNDING)))) // list in a list because Byond merges the first list... beakerCurrentVolume += R.volume data["beakerContents"] = beakerContents if (beaker) - data["beakerCurrentVolume"] = round(beakerCurrentVolume, 0.01) + data["beakerCurrentVolume"] = round(beakerCurrentVolume, CHEMICAL_VOLUME_ROUNDING) data["beakerMaxVolume"] = beaker.volume data["beakerTransferAmounts"] = beaker.possible_transfer_amounts else diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index b07cf456ae54..a326d68686a3 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -166,7 +166,7 @@ data["isBeakerLoaded"] = beaker ? 1 : 0 data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null - data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, 0.01) : null + data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, CHEMICAL_VOLUME_ROUNDING) : null data["beakerMaxVolume"] = beaker ? beaker.volume : null var/upgrade_level = heater_coefficient*10 data["upgradeLevel"] = upgrade_level @@ -175,7 +175,7 @@ for(var/r in beaker?.reagents.reagent_list) var/datum/reagent/reagent = r beaker_contents.len++ - beaker_contents[length(beaker_contents)] = list("name" = reagent.name, "volume" = round(reagent.volume, 0.01)) + beaker_contents[length(beaker_contents)] = list("name" = reagent.name, "volume" = round(reagent.volume, CHEMICAL_VOLUME_ROUNDING)) data["beakerContents"] = beaker_contents var/list/active_reactions = list() diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 342f94731e55..f24b7dd122ce 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -221,7 +221,7 @@ /obj/machinery/chem_master/ui_data(mob/user) var/list/data = list() data["isBeakerLoaded"] = beaker ? 1 : 0 - data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, 0.01) : null + data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, CHEMICAL_VOLUME_ROUNDING) : null data["beakerMaxVolume"] = beaker ? beaker.volume : null data["mode"] = mode data["condi"] = condi @@ -238,13 +238,13 @@ var/beaker_contents[0] if(beaker) for(var/datum/reagent/R in beaker.reagents.reagent_list) - beaker_contents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = round(R.volume, 0.01)))) // list in a list because Byond merges the first list... + beaker_contents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = round(R.volume, CHEMICAL_VOLUME_ROUNDING)))) // list in a list because Byond merges the first list... data["beakerContents"] = beaker_contents var/buffer_contents[0] if(reagents.total_volume) for(var/datum/reagent/N in reagents.reagent_list) - buffer_contents.Add(list(list("name" = N.name, "id" = ckey(N.name), "volume" = round(N.volume, 0.01)))) // ^ + buffer_contents.Add(list(list("name" = N.name, "id" = ckey(N.name), "volume" = round(N.volume, CHEMICAL_VOLUME_ROUNDING)))) // ^ data["bufferContents"] = buffer_contents //Calculated once since it'll never change diff --git a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm index 646c1641f8b8..fd2c5824c45e 100644 --- a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm +++ b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm @@ -197,7 +197,7 @@ var/list/beaker_contents = list() for(var/datum/reagent/reagent as anything in reagents.reagent_list) beaker_contents.len++ - beaker_contents[length(beaker_contents)] = list("name" = reagent.name, "volume" = round(reagent.volume, 0.01)) + beaker_contents[length(beaker_contents)] = list("name" = reagent.name, "volume" = round(reagent.volume, CHEMICAL_VOLUME_ROUNDING)) data["chamberContents"] = beaker_contents var/list/queued_reactions = list() diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index a4f5deb54d65..eb1d5491ba20 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -164,7 +164,7 @@ if(beaker) data["has_beaker"] = TRUE data["beaker"] = list( - "volume" = round(beaker.reagents?.total_volume, 0.01) || 0, + "volume" = round(beaker.reagents?.total_volume, CHEMICAL_VOLUME_ROUNDING) || 0, "capacity" = beaker.volume, ) var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index 13f61ea0db17..40892101bdc0 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -21,7 +21,7 @@ /datum/effect_system/smoke_spread/chem/smoke_machine/set_up(datum/reagents/carry, setting=1, efficiency=10, loc, silent=FALSE) amount = setting carry.copy_to(chemholder, 20) - carry.remove_any(amount * 16 / efficiency) + carry.remove_all(amount * 16 / efficiency) location = loc /datum/effect_system/smoke_spread/chem/smoke_machine diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 4e3c58bf0b4a..3ecdf668418d 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -63,7 +63,7 @@ C.remove_status_effect(/datum/status_effect/jitter) C.hallucination = 0 REMOVE_TRAITS_NOT_IN(C, list(SPECIES_TRAIT, ROUNDSTART_TRAIT, ORGAN_TRAIT)) - C.reagents.remove_all_type(/datum/reagent/toxin, 2 * removed, FALSE, TRUE) + C.reagents.remove_reagent(/datum/reagent/toxin, 2 * removed, include_subtypes = TRUE) if(C.blood_volume < BLOOD_VOLUME_NORMAL) C.setBloodVolume(BLOOD_VOLUME_NORMAL)