diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index b3ff7722c9..dfd735d2f7 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 0e0da75e2a..29482308d6 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 2912a392cb..1414d959e5 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 acb7ad075d..c7fc1a28b8 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