diff --git a/code/_globalvars/lists/reagents.dm b/code/_globalvars/lists/reagents.dm index cbed6116d05..1c16e9d8030 100644 --- a/code/_globalvars/lists/reagents.dm +++ b/code/_globalvars/lists/reagents.dm @@ -81,6 +81,13 @@ GLOBAL_LIST_INIT(stacked_metabolization_effect, init_chemical_side_effects()) return reagent_list +/proc/check_recipe_for_conflicts(datum/chemical_reaction/reaction, list/reaction_lookup) + for(var/x in reaction.required_reagents) + for(var/datum/chemical_reaction/competitor in reaction_lookup[x]) + if(chem_recipes_do_conflict(competitor, reaction)) + return TRUE + return FALSE + /** * Chemical Reactions - Initialises all /datum/chemical_reaction into a list * It is filtered into multiple lists within a list. @@ -108,20 +115,19 @@ GLOBAL_LIST_INIT(stacked_metabolization_effect, init_chemical_side_effects()) var/list/datum/chemical_reaction/reactions = list() for(var/datum/chemical_reaction/reaction as anything in paths) - if(!ispath(reaction, /datum/chemical_reaction/randomized)) + if(ispath(reaction, /datum/chemical_reaction/randomized)) + reaction = new reaction(LAZYACCESS(json, "[reaction]")) + else reaction = new reaction - reactions += reaction - + if(!QDELETED(reaction)) // in case random recipe generation fail + reactions += reaction // Ok so we're gonna do a thingTM here // I want to distribute all our reactions such that each reagent id links to as few as possible // I get the feeling there's a canonical way of doing this, but I don't know it // So instead, we're gonna wing it var/list/reagent_to_react_count = list() - var/list/randomized_reaction_retry_attempts = list() for(var/datum/chemical_reaction/reaction as anything in reactions) - if(ispath(reaction, /datum/chemical_reaction/randomized)) - randomized_reaction_retry_attempts[reaction] = 0 - else + if(!istype(reaction, /datum/chemical_reaction/randomized)) for(var/reagent_id in reaction.required_reagents) reagent_to_react_count[reagent_id] += 1 @@ -131,33 +137,22 @@ GLOBAL_LIST_INIT(stacked_metabolization_effect, init_chemical_side_effects()) // Doing this separately because it relies on the loop above, and this is easier to parse for(var/datum/chemical_reaction/reaction as anything in reactions) //check for collisions - if(ispath(reaction, /datum/chemical_reaction/randomized)) - var/target_path = reaction - var/index = reactions.Find(reaction) - reaction = new target_path(LAZYACCESS(json, "[target_path]")) + if(istype(reaction, /datum/chemical_reaction/randomized)) + var/datum/chemical_reaction/randomized/random_reaction = reaction + var/retry_attempts = 0 + while(check_recipe_for_conflicts(random_reaction, reaction_lookup)) + if(retry_attempts >= MAX_RANDOMIZED_REACTION_RETRY_ATTEMPTS || !random_reaction.generate_recipe()) + reactions -= reaction + qdel(reaction) + break + retry_attempts++ - //failed to init + // log results if(QDELETED(reaction)) - reactions -= target_path + log_game("Couldn't regenerate [reaction] due to conflicts in [retry_attempts] attempts.") continue - - //failed to resolve so retry - outer: - for(var/x in reaction.required_reagents) - for(var/datum/chemical_reaction/R in reaction_lookup[x]) - if(chem_recipes_do_conflict(R, reaction)) - reactions -= target_path - QDEL_NULL(reaction) - if(randomized_reaction_retry_attempts[target_path] < MAX_RANDOMIZED_REACTION_RETRY_ATTEMPTS) - reactions += target_path - randomized_reaction_retry_attempts[target_path] += 1 - break outer - - //add to list - if(!reaction) - continue - else - reactions[index] = reaction + else if(retry_attempts > 0) + log_game("Regenerated [reaction] due to conflicts in [retry_attempts] attempts.") var/preferred_id = null for(var/reagent_id in reaction.required_reagents) diff --git a/code/controllers/subsystem/persistence/recipes.dm b/code/controllers/subsystem/persistence/recipes.dm index 828f856b76a..59b94f4906b 100644 --- a/code/controllers/subsystem/persistence/recipes.dm +++ b/code/controllers/subsystem/persistence/recipes.dm @@ -22,7 +22,6 @@ determin_ph_range = R.determin_ph_range, H_ion_release = R.H_ion_release, purity_min = R.purity_min, - results = R.results, required_container = R.required_container ) diff --git a/code/modules/reagents/chemistry/recipes/special.dm b/code/modules/reagents/chemistry/recipes/special.dm index 7a26d8b7a1a..eb1eee1a268 100644 --- a/code/modules/reagents/chemistry/recipes/special.dm +++ b/code/modules/reagents/chemistry/recipes/special.dm @@ -41,98 +41,115 @@ /datum/chemical_reaction/randomized/New(recipe_data) . = ..() - //creation time, decides if we are random generating or not - created = recipe_data ? text2num(recipe_data["timestamp"]) : world.realtime + if(!recipe_data || !load_recipe(recipe_data)) + log_game("Generating recipe for [src]") + if(!generate_recipe()) + log_game("Couldn't generate recipe for [src]") + qdel(src) + + +/datum/chemical_reaction/randomized/proc/load_recipe(recipe_data) + PRIVATE_PROC(TRUE) + // Timestamp + created = text2num(recipe_data["timestamp"]) if(daysSince(created) > persistence_period) - created = world.realtime - recipe_data = null - - //all reagents - if(recipe_data) - required_reagents = unwrap_reagent_list(recipe_data["required_reagents"]) - if(!required_reagents) - qdel(src) - return - required_catalysts = unwrap_reagent_list(recipe_data["required_catalysts"]) - if(!required_catalysts) - qdel(src) - return - results = unwrap_reagent_list(recipe_data["results"]) - if(!results) - qdel(src) - return - else - var/list/remaining_possible_reagents = GetPossibleReagents(RNGCHEM_INPUT) - var/list/remaining_possible_catalysts = GetPossibleReagents(RNGCHEM_CATALYSTS) - //We're going to assume we're not doing any weird partial reactions for now. - for(var/reagent_type in results) - remaining_possible_catalysts -= reagent_type - remaining_possible_reagents -= reagent_type - - var/in_reagent_count = min(rand(min_input_reagents, max_input_reagents),remaining_possible_reagents.len) - if(in_reagent_count <= 0) - qdel(src) - return - - required_reagents = list() - for(var/i in 1 to in_reagent_count) - var/r_id = pick_n_take(remaining_possible_reagents) - required_reagents[r_id] = rand(min_input_reagent_amount,max_input_reagent_amount) - remaining_possible_catalysts -= r_id //Can't have same reagents both as catalyst and reagent. Or can we ? - - required_catalysts = list() - var/in_catalyst_count = min(rand(min_catalysts,max_catalysts),remaining_possible_catalysts.len) - for(var/i in 1 to in_catalyst_count) - required_catalysts[pick_n_take(remaining_possible_catalysts)] = rand(min_input_reagent_amount,max_input_reagent_amount) - - //temperature - if(recipe_data) - is_cold_recipe = recipe_data["is_cold_recipe"] - required_temp = recipe_data["required_temp"] - optimal_temp = recipe_data["optimal_temp"] - overheat_temp = recipe_data["overheat_temp"] - thermic_constant = recipe_data["thermic_constant"] - else - is_cold_recipe = pick(TRUE, FALSE) - if(is_cold_recipe) - required_temp = rand(min_temp + 50, max_temp) - optimal_temp = rand(min_temp + 25, required_temp - 10) - overheat_temp = rand(min_temp, optimal_temp - 10) - if(overheat_temp >= 200) //Otherwise it can disappear when you're mixing and I don't want this to happen here - overheat_temp = 200 - else - required_temp = rand(min_temp, max_temp - 50) - optimal_temp = rand(required_temp + 10, max_temp - 25) - overheat_temp = rand(optimal_temp, max_temp + 50) - if(overheat_temp <= 400) - overheat_temp = 400 - - //ph - if(recipe_data) - optimal_ph_min = recipe_data["optimal_ph_min"] - optimal_ph_max = recipe_data["optimal_ph_max"] - determin_ph_range = recipe_data["determin_ph_range"] - H_ion_release = recipe_data["H_ion_release"] - else - optimal_ph_min = CHEMICAL_MIN_PH + rand(0, suboptimal_range_ph) - optimal_ph_max = max((CHEMICAL_MAX_PH + rand(0, suboptimal_range_ph)), (CHEMICAL_MIN_PH + 1)) //Always ensure we've a window of 1 - determin_ph_range = suboptimal_range_ph - H_ion_release = (rand(0, 25) / 100)// 0 - 0.25 - - //purity - purity_min = recipe_data ? recipe_data["purity_min"] : (rand(0, 4) / 10) + log_game("Recipe [src] expired.") + return FALSE + //ingredients and catalysts + required_reagents = unwrap_reagent_list(recipe_data["required_reagents"]) + if(!required_reagents) + log_game("Couldn't load reagents for [src]") + return FALSE + required_catalysts = unwrap_reagent_list(recipe_data["required_catalysts"]) + if(!required_catalysts) + log_game("Couldn't load catalysts for [src]") + return FALSE //container - if(recipe_data) - var/container = recipe_data["required_container"] - if(container) - container = text2path(container) - if(!container) - qdel(src) - return - required_container = container - else if(length(possible_containers)) + if(possible_containers) + var/container = recipe_data["required_container"] ? text2path(recipe_data["required_container"]) : null + if(!container) + log_game("Couldn't load container for [src]") + return FALSE + required_container = container + + //temperature + is_cold_recipe = recipe_data["is_cold_recipe"] + required_temp = recipe_data["required_temp"] + optimal_temp = recipe_data["optimal_temp"] + overheat_temp = recipe_data["overheat_temp"] + thermic_constant = recipe_data["thermic_constant"] + + //pH + optimal_ph_min = recipe_data["optimal_ph_min"] + optimal_ph_max = recipe_data["optimal_ph_max"] + determin_ph_range = recipe_data["determin_ph_range"] + H_ion_release = recipe_data["H_ion_release"] + + //purity + purity_min = recipe_data["purity_min"] + + return TRUE + +/datum/chemical_reaction/randomized/proc/generate_recipe() + // Timestamp + created = world.realtime + + // Reagents and catalysts + var/list/remaining_possible_reagents = GetPossibleReagents(RNGCHEM_INPUT) + var/list/remaining_possible_catalysts = GetPossibleReagents(RNGCHEM_CATALYSTS) + //We're going to assume we're not doing any weird partial reactions for now. + for(var/reagent_type in results) + remaining_possible_catalysts -= reagent_type + remaining_possible_reagents -= reagent_type + + if(remaining_possible_reagents.len < min_input_reagents) + log_game("Couldn't find enough reagents for [src]") + return FALSE + + required_reagents = list() + var/in_reagent_count = min(rand(min_input_reagents, max_input_reagents),remaining_possible_reagents.len) + for(var/i in 1 to in_reagent_count) + var/r_id = pick_n_take(remaining_possible_reagents) + required_reagents[r_id] = rand(min_input_reagent_amount,max_input_reagent_amount) + remaining_possible_catalysts -= r_id //Can't have same reagents both as catalyst and reagent. Or can we ? + + if(remaining_possible_catalysts.len < min_catalysts) + log_game("Couldn't find enough catalysts for [src]") + return FALSE + + required_catalysts = list() + var/in_catalyst_count = min(rand(min_catalysts,max_catalysts),remaining_possible_catalysts.len) + for(var/i in 1 to in_catalyst_count) + required_catalysts[pick_n_take(remaining_possible_catalysts)] = rand(min_input_reagent_amount,max_input_reagent_amount) + + // Container + if(possible_containers) required_container = pick(possible_containers) + // Temperature + is_cold_recipe = pick(TRUE, FALSE) + if(is_cold_recipe) + required_temp = rand(min_temp + 50, max_temp) + optimal_temp = rand(min_temp + 25, required_temp - 10) + overheat_temp = rand(min_temp, optimal_temp - 10) + if(overheat_temp >= 200) //Otherwise it can disappear when you're mixing and I don't want this to happen here + overheat_temp = 200 + else + required_temp = rand(min_temp, max_temp - 50) + optimal_temp = rand(required_temp + 10, max_temp - 25) + overheat_temp = rand(optimal_temp, max_temp + 50) + if(overheat_temp <= 400) + overheat_temp = 400 + + //pH + optimal_ph_min = CHEMICAL_MIN_PH + rand(0, suboptimal_range_ph) + optimal_ph_max = max((CHEMICAL_MAX_PH - rand(0, suboptimal_range_ph)), (optimal_ph_min + 1)) //Always ensure we've a window of 1 + determin_ph_range = suboptimal_range_ph + H_ion_release = (rand(0, 25) / 100)// 0 - 0.25 + + // Purity + purity_min = (rand(0, 4) / 10) + return TRUE /** * Returns the reagents to select for randomizing