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