From b72abc8b64c0dfc5780944e3dafca8c0dc19c2b2 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Thu, 21 May 2015 23:29:50 -0400 Subject: [PATCH 1/4] Chem reaction cleanup Reorganizes handle_reactions() in preparation for implementing reactions-over-time. Moves chemistry initializations into global_init. --- code/modules/reagents/Chemistry-Holder.dm | 90 +++++---------------- code/modules/reagents/Chemistry-Reagents.dm | 12 +++ code/modules/reagents/Chemistry-Recipes.dm | 76 ++++++++++++++++- code/world.dm | 16 +++- 4 files changed, 119 insertions(+), 75 deletions(-) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index b3ff7722c9d..dfd735d2f75 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -6,42 +6,8 @@ var/reacting = 0 // Reacting right now /datum/reagents/New(var/max = 100) + ..() maximum_volume = max - //I dislike having these here but map-objects are initialised before world/New() is called. >_> - if(!chemical_reagents_list) - //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id - var/paths = typesof(/datum/reagent) - /datum/reagent - chemical_reagents_list = list() - for(var/path in paths) - var/datum/reagent/D = new path() - if(!D.name) - continue - chemical_reagents_list[D.id] = D - - if(!chemical_reactions_list) - //Chemical Reactions - Initialises all /datum/chemical_reaction into a list - // It is filtered into multiple lists within a list. - // For example: - // chemical_reaction_list["phoron"] is a list of all reactions relating to phoron - - var/paths = typesof(/datum/chemical_reaction) - /datum/chemical_reaction - chemical_reactions_list = list() - - for(var/path in paths) - - var/datum/chemical_reaction/D = new path() - var/list/reaction_ids = list() - - if(D.required_reagents && D.required_reagents.len) - for(var/reaction in D.required_reagents) - reaction_ids += reaction - - // Create filters based on each reagent id in the required reagents list - for(var/id in reaction_ids) - if(!chemical_reactions_list[id]) - chemical_reactions_list[id] = list() - chemical_reactions_list[id] += D - break // Don't bother adding ourselves to other reagent ids, it is redundant. /datum/reagents/Destroy() ..() @@ -106,50 +72,30 @@ /datum/reagents/proc/handle_reactions() if(!my_atom) // No reactions in temporary holders return + if(!my_atom.loc) //No reactions inside GC'd containers + return if(my_atom.flags & NOREACT) // No reactions here return - + + var/list/eligible_reactions = list() + var/reaction_occured = 0 do reaction_occured = 0 + + //need to rebuild this to account for chain reactions for(var/datum/reagent/R in reagent_list) - for(var/datum/chemical_reaction/C in chemical_reactions_list[R.id]) - var/reagents_suitable = 1 - for(var/B in C.required_reagents) - if(!has_reagent(B)) - reagents_suitable = 0 - for(var/B in C.catalysts) - if(!has_reagent(B, C.catalysts[B])) - reagents_suitable = 0 - for(var/B in C.inhibitors) - if(has_reagent(B, C.inhibitors[B])) - reagents_suitable = 0 - - if(!reagents_suitable || !C.can_happen(src)) - continue - - var/use = -1 - for(var/B in C.required_reagents) - if(use == -1) - use = get_reagent_amount(B) / C.required_reagents[B] - else - use = min(use, get_reagent_amount(B) / C.required_reagents[B]) - - var/newdata = C.send_data(src) // We need to get it before reagents are removed. See blood paint. - for(var/B in C.required_reagents) - remove_reagent(B, use * C.required_reagents[B], safety = 1) - - if(C.result) - add_reagent(C.result, C.result_amount * use, newdata) - - if(!ismob(my_atom) && C.mix_message) - var/list/seen = viewers(4, get_turf(my_atom)) - for(var/mob/M in seen) - M << "\icon[my_atom] [C.mix_message]" - playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 80, 1) - - C.on_reaction(src, C.result_amount * use) + eligible_reactions |= chemical_reactions_list[R.id] + + for(var/datum/chemical_reaction/C in eligible_reactions) + if(!C.can_happen(src)) + continue + + if(C.process(src)) reaction_occured = 1 + + eligible_reactions.Cut() + while(reaction_occured) update_total() return diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index 0e0da75e2a6..29482308d60 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -1,3 +1,15 @@ + +//Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id +/proc/initialize_chemical_reagents() + var/paths = typesof(/datum/reagent) - /datum/reagent + chemical_reagents_list = list() + for(var/path in paths) + var/datum/reagent/D = new path() + if(!D.name) + continue + chemical_reagents_list[D.id] = D + + /datum/reagent var/name = "Reagent" var/id = "reagent" diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 2912a392cbf..1414d959e57 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -1,3 +1,21 @@ + +//Chemical Reactions - Initialises all /datum/chemical_reaction into a list +// It is filtered into multiple lists within a list. +// For example: +// chemical_reaction_list["phoron"] is a list of all reactions relating to phoron +// Note that entries in the list are NOT duplicated. So if a reaction pertains to +// more than one chemical it will still only appear in only one of the sublists. +/proc/initialize_chemical_reactions() + var/paths = typesof(/datum/chemical_reaction) - /datum/chemical_reaction + chemical_reactions_list = list() + + for(var/path in paths) + var/datum/chemical_reaction/D = new path() + + if(D.required_reagents && D.required_reagents.len) + var/reagent_id = D.required_reagents[1] + chemical_reactions_list[reagent_id] += D + /datum/chemical_reaction var/name = null var/id = null @@ -8,14 +26,68 @@ var/result_amount = 0 var/mix_message = "The solution begins to bubble." + var/reaction_sound = 'sound/effects/bubbles.ogg' /datum/chemical_reaction/proc/can_happen(var/datum/reagents/holder) + //check that all the required reagents are present + for(var/reagent in required_reagents) + if(!holder.has_reagent(reagent)) + return 0 + + //check that all the required catalysts are present in the required amount + for(var/reagent in catalysts) + var/min_req_amt = catalysts[reagent] + if(!holder.has_reagent(reagent, min_req_amt)) + return 0 + + //check that none of the inhibitors are present in the required amount + for(var/reagent in inhibitors) + var/min_req_amt = inhibitors[reagent] + if(holder.has_reagent(reagent, min_req_amt)) + return 0 + return 1 +/datum/chemical_reaction/proc/process(var/datum/reagents/holder) + //determine how far the reaction can proceed + var/list/reaction_limits = list() + for(var/reactant in required_reagents) + reaction_limits += holder.get_reagent_amount(reactant) / required_reagents[reactant] + var/reaction_limit = min(reaction_limits) + + //need to obtain the new reagent's data before anything is altered + var/data = send_data(holder, reaction_limit) + + //remove the reactants + for(var/reactant in required_reagents) + var/amt_used = required_reagents[reactant] * reaction_limit + holder.remove_reagent(reactant, amt_used, safety = 1) + + //add the product + var/amt_produced = result_amount * reaction_limit + if(result) + holder.add_reagent(result, amt_produced, data) + + //produces messages and sounds + var/atom/container = holder.my_atom + if(mix_message && container && !ismob(container)) + var/turf/T = get_turf(container) + var/list/seen = viewers(4, T) + for(var/mob/M in seen) + M.show_message("\icon[container] [mix_message]", 1) + playsound(T, reaction_sound, 80, 1) + + on_reaction(holder, amt_produced) + + return reaction_limit + +//called after a reaction occurs /datum/chemical_reaction/proc/on_reaction(var/datum/reagents/holder, var/created_volume) return -/datum/chemical_reaction/proc/send_data(var/datum/reagents/T) +//obtains any special data that will be provided to the reaction products +//this is called just before reactants are removed. +/datum/chemical_reaction/proc/send_data(var/datum/reagents/holder, var/reaction_limit) return null /* Common reactions */ @@ -875,7 +947,7 @@ if(holder.my_atom && istype(holder.my_atom, required)) var/obj/item/slime_extract/T = holder.my_atom if(T.Uses > 0) - return 1 + return ..() return 0 /datum/chemical_reaction/slime/on_reaction(var/datum/reagents/holder) diff --git a/code/world.dm b/code/world.dm index acb7ad075d8..c7fc1a28b8d 100644 --- a/code/world.dm +++ b/code/world.dm @@ -1,3 +1,14 @@ + +/* + The initialization of the game happens roughly like this: + + 1. All global variables are initialized (including the global_init instance). + 2. The map is initialized, and map objects are created. + 3. world/New() runs, creating the process scheduler (and the old master controller) and spawning their setup. + 4. processScheduler/setup() runs, creating all the processes. game_controller/setup() runs, calling initialize() on all movable atoms in the world. + 5. The gameticker is created. + +*/ var/global/datum/global_init/init = new () /* @@ -7,8 +18,11 @@ var/global/datum/global_init/init = new () makeDatumRefLists() load_configuration() + + initialize_chemical_reagents() + initialize_chemical_reactions() - qdel(src) + qdel(src) //we're done /world From 33487c76fa2e8a4f334806fa425c62c9d27a582f Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sat, 23 May 2015 01:36:22 -0400 Subject: [PATCH 2/4] Implements reaction-over-time and reaction yield --- baystation12.dme | 1 + code/controllers/Processes/chemistry.dm | 33 ++++++++ code/controllers/verbs.dm | 5 +- code/modules/reagents/Chemistry-Holder.dm | 37 ++++++--- code/modules/reagents/Chemistry-Recipes.dm | 88 ++++++++++++++++++---- 5 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 code/controllers/Processes/chemistry.dm diff --git a/baystation12.dme b/baystation12.dme index 2d7b5948f05..633f6c9a61b 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -99,6 +99,7 @@ #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.dm" #include "code\controllers\Processes\alarm.dm" +#include "code\controllers\Processes\chemistry.dm" #include "code\controllers\Processes\disease.dm" #include "code\controllers\Processes\emergencyShuttle.dm" #include "code\controllers\Processes\event.dm" diff --git a/code/controllers/Processes/chemistry.dm b/code/controllers/Processes/chemistry.dm new file mode 100644 index 00000000000..169bd15ee5e --- /dev/null +++ b/code/controllers/Processes/chemistry.dm @@ -0,0 +1,33 @@ +var/datum/controller/process/chemistry/chemistryProcess + +/datum/controller/process/chemistry + var/tmp/datum/updateQueue/updateQueueInstance + var/list/active_holders + var/list/chemical_reactions + var/list/chemical_reagents + +/datum/controller/process/chemistry/setup() + name = "chemistry" + schedule_interval = 20 // every 2 seconds + updateQueueInstance = new + chemistryProcess = src + active_holders = list() + chemical_reactions = chemical_reactions_list + chemical_reagents = chemical_reagents_list + +/datum/controller/process/chemistry/getStatName() + return ..()+"([active_holders.len])" + +/datum/controller/process/chemistry/doWork() + for(var/datum/reagents/holder in active_holders) + if(!holder.process_reactions()) + active_holders -= holder + scheck() + +/datum/controller/process/chemistry/proc/mark_for_update(var/datum/reagents/holder) + if(holder in active_holders) + return + + //Process once, right away. If we still need to continue then add to the active_holders list and continue later + if(holder.process_reactions()) + active_holders += holder diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index ddfd99f5d6c..d849bc836b8 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -55,7 +55,7 @@ usr.client.debug_variables(antag) message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") -/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano")) +/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" @@ -119,5 +119,8 @@ if("Nano") debug_variables(nanomanager) feedback_add_details("admin_verb", "DNano") + if("Chemistry") + debug_variables(chemistryProcess) + feedback_add_details("admin_verb", "DChem") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") return diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index dfd735d2f75..4ad6e7337cf 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -1,3 +1,5 @@ +#define PROCESS_REACTION_ITER 5 //when processing a reaction, iterate this many times + /datum/reagents var/list/datum/reagent/reagent_list = list() var/total_volume = 0 @@ -11,6 +13,9 @@ /datum/reagents/Destroy() ..() + if(chemistryProcess) + chemistryProcess.active_holders -= src + for(var/datum/reagent/R in reagent_list) qdel(R) reagent_list.Cut() @@ -70,17 +75,22 @@ my_atom.reagents = null /datum/reagents/proc/handle_reactions() + if(chemistryProcess) + chemistryProcess.mark_for_update(src) + +//returns 1 if the holder should continue reactiong, 0 otherwise. +/datum/reagents/proc/process_reactions() if(!my_atom) // No reactions in temporary holders - return + return 0 if(!my_atom.loc) //No reactions inside GC'd containers - return + return 0 if(my_atom.flags & NOREACT) // No reactions here - return + return 0 + var/reaction_occured + var/list/effect_reactions = list() var/list/eligible_reactions = list() - - var/reaction_occured = 0 - do + for(var/i in 1 to PROCESS_REACTION_ITER) reaction_occured = 0 //need to rebuild this to account for chain reactions @@ -88,17 +98,20 @@ eligible_reactions |= chemical_reactions_list[R.id] for(var/datum/chemical_reaction/C in eligible_reactions) - if(!C.can_happen(src)) - continue - - if(C.process(src)) + if(C.can_happen(src) && C.process(src)) + effect_reactions |= C reaction_occured = 1 eligible_reactions.Cut() - while(reaction_occured) + if(!reaction_occured) + break + + for(var/datum/chemical_reaction/C in effect_reactions) + C.post_reaction(src) + update_total() - return + return reaction_occured /* Holder-to-chemical */ diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 1414d959e57..8615b73452e 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -11,11 +11,24 @@ for(var/path in paths) var/datum/chemical_reaction/D = new path() - if(D.required_reagents && D.required_reagents.len) var/reagent_id = D.required_reagents[1] + if(!chemical_reactions_list[reagent_id]) + chemical_reactions_list[reagent_id] = list() chemical_reactions_list[reagent_id] += D +//helper that ensures the reaction rate holds after iterating +//Ex. REACTION_RATE(0.3) means that 30% of the reagents will react each chemistry tick (~2 seconds by default). +#define REACTION_RATE(rate) (1.0 - (1.0-rate)**(1.0/PROCESS_REACTION_ITER)) + +//helper to define reaction rate in terms of half-life +//Ex. +//HALF_LIFE(0) -> Reaction completes immediately (default chems) +//HALF_LIFE(1) -> Half of the reagents react immediately, the rest after 1 chemistry ticks +//HALF_LIFE(2) -> Half of the reagents are consumed after 2 chemistry ticks. +//HALF_LIFE(3) -> Half of the reagents are consumed after 3 chemistry ticks. +#define HALF_LIFE(ticks) (ticks? 1.0 - (0.5)**(1.0/(ticks*PROCESS_REACTION_ITER)) : 1.0) + /datum/chemical_reaction var/name = null var/id = null @@ -23,8 +36,19 @@ var/list/required_reagents = list() var/list/catalysts = list() var/list/inhibitors = list() - var/result_amount = 0 + + //how far the reaction proceeds each time it is processed. Used with either REACTION_RATE or HALF_LIFE macros. + var/reaction_rate = HALF_LIFE(0) + + //if less than 1, the reaction will be inhibited if the ratio of products/reagents is too high. + //0.5 = 50% yield -> reaction will only proceed halfway until products are removed. + var/yield = 1.0 + + //If limits on reaction rate would leave less than this amount of any reagent (adjusted by the reaction ratios), + //the reaction goes to completion. This is to prevent reactions from going on forever with tiny reagent amounts. + var/min_reaction = 2 + var/mix_message = "The solution begins to bubble." var/reaction_sound = 'sound/effects/bubbles.ogg' @@ -48,27 +72,69 @@ return 1 +/datum/chemical_reaction/proc/calc_reaction_progress(var/datum/reagents/holder, var/reaction_limit) + var/progress = reaction_limit * reaction_rate //simple exponential progression + + //calculate yield + if(1-yield > 0.001) //if yield ratio is big enough just assume it goes to completion + /* + Determine the max amount of product by applying the yield condition: + (max_product/result_amount) / reaction_limit == yield/(1-yield) + + We make use of the fact that: + reaction_limit = (holder.get_reagent_amount(reactant) / required_reagents[reactant]) of the limiting reagent. + */ + var/yield_ratio = yield/(1-yield) + var/max_product = yield_ratio * reaction_limit * result_amount //rearrange to obtain max_product + var/yield_limit = max(0, max_product - holder.get_reagent_amount(result))/result_amount + + progress = min(progress, yield_limit) //apply yield limit + + //apply min reaction progress - wasn't sure if this should go before or after applying yield + //I guess people can just have their miniscule reactions go to completion regardless of yield. + for(var/reactant in required_reagents) + var/remainder = holder.get_reagent_amount(reactant) - progress*required_reagents[reactant] + if(remainder <= min_reaction*required_reagents[reactant]) + progress = reaction_limit + break + + return progress + /datum/chemical_reaction/proc/process(var/datum/reagents/holder) //determine how far the reaction can proceed var/list/reaction_limits = list() for(var/reactant in required_reagents) reaction_limits += holder.get_reagent_amount(reactant) / required_reagents[reactant] + + //determine how far the reaction proceeds var/reaction_limit = min(reaction_limits) + var/progress_limit = calc_reaction_progress(holder, reaction_limit) + + var/reaction_progress = min(reaction_limit, progress_limit) //no matter what, the reaction progress cannot exceed the stoichiometric limit. //need to obtain the new reagent's data before anything is altered - var/data = send_data(holder, reaction_limit) + var/data = send_data(holder, reaction_progress) //remove the reactants for(var/reactant in required_reagents) - var/amt_used = required_reagents[reactant] * reaction_limit + var/amt_used = required_reagents[reactant] * reaction_progress holder.remove_reagent(reactant, amt_used, safety = 1) //add the product - var/amt_produced = result_amount * reaction_limit + var/amt_produced = result_amount * reaction_progress if(result) - holder.add_reagent(result, amt_produced, data) + holder.add_reagent(result, amt_produced, data, safety = 1) - //produces messages and sounds + on_reaction(holder, amt_produced) + + return reaction_progress + +//called when a reaction processes +/datum/chemical_reaction/proc/on_reaction(var/datum/reagents/holder, var/created_volume) + return + +//called after processing reactions, if they occurred +/datum/chemical_reaction/proc/post_reaction(var/datum/reagents/holder) var/atom/container = holder.my_atom if(mix_message && container && !ismob(container)) var/turf/T = get_turf(container) @@ -76,14 +142,6 @@ for(var/mob/M in seen) M.show_message("\icon[container] [mix_message]", 1) playsound(T, reaction_sound, 80, 1) - - on_reaction(holder, amt_produced) - - return reaction_limit - -//called after a reaction occurs -/datum/chemical_reaction/proc/on_reaction(var/datum/reagents/holder, var/created_volume) - return //obtains any special data that will be provided to the reaction products //this is called just before reactants are removed. From 2d92a871aa92182bf20c55229da7cde0df8b573b Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sat, 30 May 2015 17:17:45 -0400 Subject: [PATCH 3/4] Optimizes reaction checks --- code/modules/reagents/Chemistry-Holder.dm | 18 ++++++++++++++++++ code/modules/reagents/Chemistry-Recipes.dm | 17 ++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 4ad6e7337cf..775da2bbb39 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -183,6 +183,24 @@ return 0 return 0 +/datum/reagents/proc/has_any_reagent(var/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 + +/datum/reagents/proc/has_all_reagents(var/list/check_reagents) + //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]) + missing-- + return !missing + /datum/reagents/proc/clear_reagents() for(var/datum/reagent/current in reagent_list) del_reagent(current.id) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 8615b73452e..5db9f290e7d 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -54,21 +54,16 @@ /datum/chemical_reaction/proc/can_happen(var/datum/reagents/holder) //check that all the required reagents are present - for(var/reagent in required_reagents) - if(!holder.has_reagent(reagent)) - return 0 + if(!holder.has_all_reagents(required_reagents)) + return 0 //check that all the required catalysts are present in the required amount - for(var/reagent in catalysts) - var/min_req_amt = catalysts[reagent] - if(!holder.has_reagent(reagent, min_req_amt)) - return 0 + if(!holder.has_all_reagents(catalysts)) + return 0 //check that none of the inhibitors are present in the required amount - for(var/reagent in inhibitors) - var/min_req_amt = inhibitors[reagent] - if(holder.has_reagent(reagent, min_req_amt)) - return 0 + if(holder.has_any_reagent(inhibitors)) + return 0 return 1 From fd7830fd1b1bcd1129de5a4ea1712c1ddc135c9c Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sat, 30 May 2015 17:21:33 -0400 Subject: [PATCH 4/4] Adjusts most catalysts values --- code/modules/reagents/Chemistry-Recipes.dm | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 5db9f290e7d..f6a0a19e95a 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -178,7 +178,7 @@ id = "oxycodone" result = "oxycodone" required_reagents = list("ethanol" = 1, "tramadol" = 1) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) result_amount = 1 /datum/chemical_reaction/sterilizine @@ -277,7 +277,7 @@ id = "peridaxon" result = "peridaxon" required_reagents = list("bicaridine" = 2, "clonexadone" = 2) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) result_amount = 2 /datum/chemical_reaction/virus_food @@ -292,7 +292,7 @@ id = "leporazine" result = "leporazine" required_reagents = list("silicon" = 1, "copper" = 1) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) result_amount = 2 /datum/chemical_reaction/cryptobiolin @@ -321,7 +321,7 @@ id = "dexalin" result = "dexalin" required_reagents = list("oxygen" = 2, "phoron" = 0.1) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) inhibitors = list("water" = 1) // Messes with cryox result_amount = 1 @@ -373,7 +373,7 @@ id = "clonexadone" result = "clonexadone" required_reagents = list("cryoxadone" = 1, "sodium" = 1, "phoron" = 0.1) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) result_amount = 2 /datum/chemical_reaction/spaceacillin @@ -508,7 +508,7 @@ id = "condensedcapsaicin" result = "condensedcapsaicin" required_reagents = list("capsaicin" = 2) - catalysts = list("phoron" = 1) + catalysts = list("phoron" = 5) result_amount = 1 /datum/chemical_reaction/coolant @@ -1330,7 +1330,7 @@ id = "tofu" result = null required_reagents = list("soymilk" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 1 /datum/chemical_reaction/tofu/on_reaction(var/datum/reagents/holder, var/created_volume) @@ -1384,7 +1384,7 @@ id = "cheesewheel" result = null required_reagents = list("milk" = 40) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 1 /datum/chemical_reaction/cheesewheel/on_reaction(var/datum/reagents/holder, var/created_volume) @@ -1495,7 +1495,7 @@ id = "moonshine" result = "moonshine" required_reagents = list("nutriment" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/grenadine @@ -1503,7 +1503,7 @@ id = "grenadine" result = "grenadine" required_reagents = list("berryjuice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/wine @@ -1511,7 +1511,7 @@ id = "wine" result = "wine" required_reagents = list("grapejuice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/pwine @@ -1519,7 +1519,7 @@ id = "pwine" result = "pwine" required_reagents = list("poisonberryjuice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/melonliquor @@ -1527,7 +1527,7 @@ id = "melonliquor" result = "melonliquor" required_reagents = list("watermelonjuice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/bluecuracao @@ -1535,7 +1535,7 @@ id = "bluecuracao" result = "bluecuracao" required_reagents = list("orangejuice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/spacebeer @@ -1543,7 +1543,7 @@ id = "spacebeer" result = "beer" required_reagents = list("cornoil" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/vodka @@ -1551,7 +1551,7 @@ id = "vodka" result = "vodka" required_reagents = list("potato" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/sake @@ -1559,7 +1559,7 @@ id = "sake" result = "sake" required_reagents = list("rice" = 10) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 10 /datum/chemical_reaction/kahlua @@ -1567,7 +1567,7 @@ id = "kahlua" result = "kahlua" required_reagents = list("coffee" = 5, "sugar" = 5) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 5 /datum/chemical_reaction/gin_tonic @@ -1855,7 +1855,7 @@ id = "mead" result = "mead" required_reagents = list("sugar" = 1, "water" = 1) - catalysts = list("enzyme" = 1) + catalysts = list("enzyme" = 5) result_amount = 2 /datum/chemical_reaction/iced_beer