Skip to content

Commit

Permalink
[MIRROR] Code compression for reagent holder. Lowers plumbing reactio…
Browse files Browse the repository at this point in the history
…n chamber tick usage [MDB IGNORE] (#675)

* Code compression for reagent holder. Lowers plumbing reaction chamber tick usage (#79686)

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: SyncIt21 <[email protected]>
Co-authored-by: Giz <[email protected]>
  • Loading branch information
4 people authored Nov 17, 2023
1 parent a8b1956 commit 75ec17d
Show file tree
Hide file tree
Showing 45 changed files with 188 additions and 272 deletions.
8 changes: 2 additions & 6 deletions code/datums/components/plumbing/_plumbing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
var/ducting_layer = DUCT_LAYER_DEFAULT
///In-case we don't want the main machine to get the reagents, but perhaps whoever is buckled to it
var/recipient_reagents_holder
///How do we apply the new reagents to the receiver? Generally doesn't matter, but some stuff, like people, does care if its injected or whatevs
var/methods
///What color is our demand connect?
var/demand_color = COLOR_RED
///What color is our supply connect?
Expand Down Expand Up @@ -139,10 +137,8 @@
/datum/component/plumbing/proc/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net)
if(!reagents || !target || !target.reagents)
return FALSE
if(reagent)
reagents.trans_id_to(target.recipient_reagents_holder, reagent, amount)
else
reagents.trans_to(target.recipient_reagents_holder, amount, methods = methods)

reagents.trans_to(target.recipient_reagents_holder, amount, target_id = reagent)

///We create our luxurious piping overlays/underlays, to indicate where we do what. only called once if use_overlays = TRUE in Initialize()
/datum/component/plumbing/proc/create_overlays(atom/movable/parent_movable, list/overlays)
Expand Down
4 changes: 2 additions & 2 deletions code/datums/components/plumbing/filter.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
direction = get_original_direction(text2num(A))
break
if(reagent)
reagents.trans_id_to(target.parent, reagent, amount)
reagents.trans_to(target.parent, amount, target_id = reagent)
else
for(var/A in reagents.reagent_list)
var/datum/reagent/R = A
Expand All @@ -40,7 +40,7 @@
var/new_amount
if(R.volume < amount)
new_amount = amount - R.volume
reagents.trans_id_to(target.parent, R.type, amount)
reagents.trans_to(target.parent, amount, target_id = R.type)
amount = new_amount
if(amount <= 0)
break
Expand Down
8 changes: 4 additions & 4 deletions code/datums/components/reagent_refiller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@
. |= COMPONENT_AFTERATTACK_PROCESSED_ITEM

var/obj/item/reagent_containers/container = parent
var/refill = container.reagents.get_master_reagent_id()
var/amount = min((container.amount_per_transfer_from_this + container.reagents.total_volume), container.reagents.total_volume)

if (amount == 0)
return
if (!is_path_in_list(refill, whitelisted_reagents))

var/datum/reagent/refill = container.reagents.get_master_reagent()
if (!is_path_in_list(refill?.type, whitelisted_reagents))
return

addtimer(CALLBACK(src, PROC_REF(add_reagents), container, container.loc, refill, amount), time_to_refill)
addtimer(CALLBACK(src, PROC_REF(add_reagents), container, container.loc, refill.type, amount), time_to_refill)

/// Refills the reagent container, and uses cell power if applicable
/datum/component/reagent_refiller/proc/add_reagents(obj/item/reagent_containers/target, oldloc, reagent_to_refill, amount)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/diseases/advance/symptoms/sensory.dm
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

M.adjust_confusion(-2 SECONDS)
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)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/elements/chemical_transfer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
return
var/built_attacker_message = replacetext(attacker_message, "%VICTIM", transfer_victim)
var/built_victim_message = replacetext(attacker_message, "%ATTACKER", transfer_attacker)
transfer_attacker.reagents?.trans_to(transfer_victim, transfer_attacker.reagents.total_volume, multiplier = 1, preserve_data = 1, no_react = 0, transferred_by = transfer_attacker)
transfer_attacker.reagents?.trans_to(transfer_victim, transfer_attacker.reagents.total_volume, transferred_by = transfer_attacker)
to_chat(transfer_attacker, built_attacker_message)
to_chat(transfer_victim, built_victim_message)

Expand Down
2 changes: 1 addition & 1 deletion code/datums/mutations/tongue_spike.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
return FALSE

to_chat(transferred, span_warning("You feel a tiny prick!"))
transferer.reagents.trans_to(transferred, transferer.reagents.total_volume, 1, 1, 0, transferred_by = transferer)
transferer.reagents.trans_to(transferred, transferer.reagents.total_volume, transferred_by = transferer)

var/obj/item/hardened_spike/chem/chem_spike = target
var/obj/item/bodypart/spike_location = chem_spike.check_embedded()
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/syndicatebomb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@
var/datum/reagents/reactants = new(time_release)
reactants.my_atom = src
for(var/obj/item/reagent_containers/RC in beakers)
RC.reagents.trans_to(reactants, RC.reagents.total_volume*fraction, 1, 1, 1)
RC.reagents.trans_to(reactants, RC.reagents.total_volume * fraction, no_react = TRUE)
chem_splash(get_turf(src), reagents, spread_range, list(reactants), temp_boost)

// Detonate it again in one second, until it's out of juice.
Expand Down
14 changes: 9 additions & 5 deletions code/game/objects/items/devices/portable_chem_mixer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,15 @@
/obj/item/storage/portable_chem_mixer/proc/update_contents()
dispensable_reagents.Cut()
for (var/obj/item/reagent_containers/container in contents)
var/key = container.reagents.get_master_reagent_id()
if (!(key in dispensable_reagents))
dispensable_reagents[key] = list()
dispensable_reagents[key]["reagents"] = list()
dispensable_reagents[key]["reagents"] += container.reagents
var/datum/reagent/key = container.reagents.get_master_reagent()
if(isnull(key)) //no reagent inside container
continue

var/key_type = key.type
if (!(key_type in dispensable_reagents))
dispensable_reagents[key_type] = list()
dispensable_reagents[key_type]["reagents"] = list()
dispensable_reagents[key_type]["reagents"] += container.reagents

/obj/item/storage/portable_chem_mixer/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
Expand Down
11 changes: 8 additions & 3 deletions code/game/objects/items/grenades/chem_grenade.dm
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,11 @@
var/container_ratio = available_extract_volume / beaker_total_volume
var/datum/reagents/tmp_holder = new/datum/reagents(beaker_total_volume)
for(var/obj/item/container as anything in other_containers)
container.reagents.trans_to(tmp_holder, container.reagents.total_volume * container_ratio, 1, preserve_data = TRUE, no_react = TRUE)
container.reagents.trans_to(tmp_holder, container.reagents.total_volume * container_ratio, no_react = TRUE)

for(var/obj/item/slime_extract/extract as anything in extracts)
var/available_volume = extract.reagents.maximum_volume - extract.reagents.total_volume
tmp_holder.trans_to(extract, beaker_total_volume * (available_volume / available_extract_volume), 1, preserve_data = TRUE, no_react = TRUE)
tmp_holder.trans_to(extract, beaker_total_volume * (available_volume / available_extract_volume), no_react = TRUE)

extract.reagents.handle_reactions() // Reaction handling in the transfer proc is reciprocal and we don't want to blow up the tmp holder early.
if(QDELETED(extract))
Expand Down Expand Up @@ -390,7 +390,12 @@
var/datum/reagents/reactants = new(unit_spread)
reactants.my_atom = src
for(var/obj/item/reagent_containers/reagent_container in beakers)
reagent_container.reagents.trans_to(reactants, reagent_container.reagents.total_volume*fraction, threatscale, 1, 1)
reagent_container.reagents.trans_to(
reactants,
reagent_container.reagents.total_volume * fraction,
threatscale,
no_react = TRUE
)
chem_splash(get_turf(src), reagents, affected_area, list(reactants), ignition_temp, threatscale)

var/turf/detonated_turf = get_turf(src)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/tanks/watertank.dm
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@

var/inj_am = injection_amount * seconds_per_tick
var/used_amount = inj_am / usage_ratio
reagents.trans_to(user, used_amount, multiplier=usage_ratio, methods = INJECT)
reagents.trans_to(user, used_amount, usage_ratio, methods = INJECT)
update_appearance()
user.update_worn_back() //for overlays update

Expand Down
4 changes: 2 additions & 2 deletions code/game/objects/structures/cannons/cannon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@
to_chat(user, span_warning("[powder_keg] doesn't have at least 15u of gunpowder to fill [src]!"))
return
if(has_enough_gunpowder)
powder_keg.reagents.trans_id_to(src, /datum/reagent/gunpowder, amount = charge_size)
powder_keg.reagents.trans_to(src, charge_size, target_id = /datum/reagent/gunpowder)
balloon_alert(user, "[src] loaded with gunpowder")
return
if(has_enough_alt_fuel)
powder_keg.reagents.trans_id_to(src, /datum/reagent/fuel, amount = charge_size)
powder_keg.reagents.trans_to(src, charge_size, target_id = /datum/reagent/fuel)
balloon_alert(user, "[src] loaded with welding fuel")
return
..()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,12 @@

if(air1.total_moles() > CRYO_MIN_GAS_MOLES)
if(beaker)
beaker.reagents.trans_to(occupant, (CRYO_TX_QTY / (efficiency * CRYO_MULTIPLY_FACTOR)) * seconds_per_tick, efficiency * CRYO_MULTIPLY_FACTOR, methods = VAPOR) // Transfer reagents.
beaker.reagents.trans_to(
occupant,
(CRYO_TX_QTY / (efficiency * CRYO_MULTIPLY_FACTOR)) * seconds_per_tick,
efficiency * CRYO_MULTIPLY_FACTOR,
methods = VAPOR
)
consume_gas = TRUE
return TRUE

Expand Down
2 changes: 1 addition & 1 deletion code/modules/food_and_drinks/machinery/icecream_vat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
return
for(var/datum/reagent/R in beaker.reagents.reagent_list)
if(R.type in icecream_vat_reagents)
beaker.reagents.trans_id_to(src, R.type, R.volume)
beaker.reagents.trans_to(src, R.volume, target_id = R.type)
say("Internalizing reagent.")
playsound(src, 'sound/items/drink.ogg', 25, TRUE)
return
Expand Down
3 changes: 2 additions & 1 deletion code/modules/food_and_drinks/restaurant/custom_order.dm
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@

var/datum/reagents/holder = object_used.reagents
// The container must be majority reagent
if(holder.get_master_reagent_id() != reagent_type)
var/datum/reagent/master_reagent = holder.get_master_reagent()
if(master_reagent?.type != reagent_type)
return FALSE
// We must fulfill the sample size threshold
if(reagents_needed > holder.total_volume)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/hydroponics/grown.dm
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
var/average_purity = reagents.get_average_purity()
var/total_nutriment_amount = reagents.get_reagent_amount(/datum/reagent/consumable/nutriment, include_subtypes = TRUE)
var/single_reagent_amount = grind_results_num > 1 ? round(total_nutriment_amount / grind_results_num, CHEMICAL_QUANTISATION_LEVEL) : total_nutriment_amount
reagents.remove_all_type(/datum/reagent/consumable/nutriment, total_nutriment_amount)
reagents.remove_reagent(/datum/reagent/consumable/nutriment, total_nutriment_amount, include_subtypes = TRUE)
for(var/reagent in grind_results)
reagents.add_reagent(reagent, single_reagent_amount, added_purity = average_purity)

Expand Down
4 changes: 2 additions & 2 deletions code/modules/hydroponics/hydroponics.dm
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@
// Move the leaked water from nutrients to... water
var/leaking_water_amount = nutri_reagents.get_reagent_amount(/datum/reagent/water)
if(leaking_water_amount)
nutri_reagents.trans_id_to(water_reagents, /datum/reagent/water, leaking_water_amount)
nutri_reagents.trans_to(water_reagents, leaking_water_amount, target_id = /datum/reagent/water)

// We should only take MACHINE_REAGENT_TRANSFER every tick; this is the remaining amount we can take
var/remaining_transfer_amount = max(MACHINE_REAGENT_TRANSFER - (nutri_reagents.total_volume - initial_nutri_amount), 0)
Expand Down Expand Up @@ -887,7 +887,7 @@
if(istype(not_water_reagent,/datum/reagent/water))
continue
var/transfer_me_to_tray = reagent_source.reagents.get_reagent_amount(not_water_reagent.type) * transfer_amount / reagent_source.reagents.total_volume
reagent_source.reagents.trans_id_to(H.reagents, not_water_reagent.type, transfer_me_to_tray)
reagent_source.reagents.trans_to(H.reagents, transfer_me_to_tray, target_id = not_water_reagent.type)
else
reagent_source.reagents.trans_to(H.reagents, transfer_amount, transferred_by = user)
lastuser = WEAKREF(user)
Expand Down
4 changes: 2 additions & 2 deletions code/modules/hydroponics/seeds.dm
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,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)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mod/modules/modules_ninja.dm
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@
if(reagents.has_reagent(reagent_required, reagent_required_amount))
balloon_alert(mod.wearer, "already charged!")
return FALSE
if(!attacking_item.reagents.trans_id_to(src, reagent_required, reagent_required_amount))
if(!attacking_item.reagents.trans_to(src, reagent_required_amount, target_id = reagent_required))
return FALSE
balloon_alert(mod.wearer, "charge [reagents.has_reagent(reagent_required, reagent_required_amount) ? "fully" : "partially"] reloaded")
return TRUE
Expand Down
2 changes: 1 addition & 1 deletion code/modules/paperwork/fax.dm
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ GLOBAL_VAR_INIT(nt_fax_department, pick("NT HR Department", "NT Legal Department
var/obj/item/reagent_containers/spray/clean_spray = item
if(!clean_spray.reagents.has_reagent(/datum/reagent/space_cleaner, clean_spray.amount_per_transfer_from_this))
return FALSE
clean_spray.reagents.remove_reagent(/datum/reagent/space_cleaner, clean_spray.amount_per_transfer_from_this, 1)
clean_spray.reagents.remove_reagent(/datum/reagent/space_cleaner, clean_spray.amount_per_transfer_from_this)
playsound(loc, 'sound/effects/spray3.ogg', 50, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
user.visible_message(span_notice("[user] cleans \the [src]."), span_notice("You clean \the [src]."))
jammed = FALSE
Expand Down
6 changes: 3 additions & 3 deletions code/modules/plumbing/plumbers/bottler.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@
var/obj/item/B = AM
///see if it would overflow else inject
if((B.reagents.total_volume + wanted_amount) <= B.reagents.maximum_volume)
reagents.trans_to(B, wanted_amount, transferred_by = src)
reagents.trans_to(B, wanted_amount)
B.forceMove(goodspot)
return
///glass was full so we move it away
AM.forceMove(badspot)
else if(istype(AM, /obj/item/slime_extract)) ///slime extracts need inject
AM.forceMove(goodspot)
reagents.trans_to(AM, wanted_amount, transferred_by = src, methods = INJECT)
reagents.trans_to(AM, wanted_amount, methods = INJECT)
else if(istype(AM, /obj/item/slimecross/industrial)) ///no need to move slimecross industrial things
reagents.trans_to(AM, wanted_amount, transferred_by = src, methods = INJECT)
reagents.trans_to(AM, wanted_amount, methods = INJECT)
10 changes: 1 addition & 9 deletions code/modules/plumbing/plumbers/reaction_chamber.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
/// coefficient to convert temperature to joules. same lvl as acclimator
#define HEATER_COEFFICIENT 0.05

/// maximum number of attempts the reaction chamber will make to balance the ph(More means better results but higher tick usage)
#define MAX_PH_ADJUSTMENTS 3

/obj/machinery/plumbing/reaction_chamber
name = "mixing chamber"
desc = "Keeps chemicals separated until given conditions are met."
Expand Down Expand Up @@ -175,8 +172,7 @@
return ..()

/obj/machinery/plumbing/reaction_chamber/chem/handle_reagents(seconds_per_tick)
var/ph_balance_attempts = 0
while(ph_balance_attempts < MAX_PH_ADJUSTMENTS && (reagents.ph < acidic_limit || reagents.ph > alkaline_limit))
if(reagents.ph < acidic_limit || reagents.ph > alkaline_limit)
//no power
if(machine_stat & NOPOWER)
return
Expand All @@ -197,15 +193,12 @@

//transfer buffer and handle reactions
var/ph_change = max((reagents.ph > alkaline_limit ? (reagents.ph - alkaline_limit) : (acidic_limit - reagents.ph)), 0.25)
if(ph_change <= 0.7) //make big jumps towards the end so we can end our work quickly
ph_change *= 2
var/buffer_amount = ((ph_change * reagents.total_volume) / (BUFFER_IONIZING_STRENGTH * num_of_reagents)) * seconds_per_tick
if(!buffer.trans_to(reagents, buffer_amount))
return

//some power for accurate ph balancing & keep track of attempts made
use_power(active_power_usage * 0.03 * buffer_amount)
ph_balance_attempts += 1

/obj/machinery/plumbing/reaction_chamber/chem/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
Expand All @@ -231,4 +224,3 @@
return FALSE

#undef HEATER_COEFFICIENT
#undef MAX_PH_ADJUSTMENTS
8 changes: 5 additions & 3 deletions code/modules/projectiles/guns/special/hand_of_midas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!victim.reagents)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!victim.reagents.has_reagent(/datum/reagent/gold, check_subtypes = TRUE))

var/gold_amount = victim.reagents.get_reagent_amount(/datum/reagent/gold, include_subtypes = TRUE)
if(!gold_amount)
balloon_alert(user, "no gold in bloodstream")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/gold_beam = user.Beam(victim, icon_state="drain_gold")
if(!do_after(user = user, delay = 1 SECONDS, target = victim, timed_action_flags = (IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE), extra_checks = CALLBACK(src, PROC_REF(check_gold_range), user, victim)))
qdel(gold_beam)
balloon_alert(user, "link broken")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
handle_gold_charges(user, victim.reagents.get_reagent_amount(/datum/reagent/gold, include_subtypes = TRUE))
victim.reagents.remove_all_type(/datum/reagent/gold, victim.reagents.get_reagent_amount(/datum/reagent/gold, include_subtypes = TRUE))
handle_gold_charges(user, gold_amount)
victim.reagents.remove_reagent(/datum/reagent/gold, gold_amount, include_subtypes = TRUE)
victim.remove_status_effect(/datum/status_effect/midas_blight)
qdel(gold_beam)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
Expand Down
2 changes: 1 addition & 1 deletion code/modules/reagents/chem_splash.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
holder.multiply_reagents(threatscale)

for(var/datum/reagents/reactant as anything in reactants)
reactant.trans_to(holder, reactant.total_volume, threatscale, preserve_data = TRUE, no_react = TRUE)
reactant.trans_to(holder, reactant.total_volume, threatscale, no_react = TRUE)

holder.chem_temp += extra_heat // Average temperature of reagents + extra heat.
holder.handle_reactions() // React them now.
Expand Down
4 changes: 2 additions & 2 deletions code/modules/reagents/chemistry/equilibrium.dm
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@

//Calculate how much product to make and how much reactant to remove factors..
for(var/reagent in reaction.required_reagents)
holder.remove_reagent(reagent, (delta_chem_factor * reaction.required_reagents[reagent]), safety = TRUE)
holder.remove_reagent(reagent, (delta_chem_factor * reaction.required_reagents[reagent]))
//Apply pH changes
var/pH_adjust
if(reaction.reaction_flags & REACTION_PH_VOL_CONSTANT)
Expand Down Expand Up @@ -414,4 +414,4 @@
///Panic stop a reaction - cleanup should be handled by the next timestep
/datum/equilibrium/proc/force_clear_reactive_agents()
for(var/reagent in reaction.required_reagents)
holder.remove_reagent(reagent, (multiplier * reaction.required_reagents[reagent]), safety = 1)
holder.remove_reagent(reagent, (multiplier * reaction.required_reagents[reagent]))
Loading

0 comments on commit 75ec17d

Please sign in to comment.