Random recipe logging (and other stuff) (#96148)

## About The Pull Request
-Log outcomes of random recipe init
* Whether recipe failed to load, and why
* Whether recipe failed to generate, and why
* Whether recipe regenerated due to conflicts, and how many attempts it
took

-Stop saving random recipe results to the persistence file
* They aren't randomized anyway

-Split recipe loading and recipe generation into separate procs
* It was kind of a mess
* This way, you can regenerate recipe without qdel'ing it and then
reinitializing

-Generate recipe if it failed to load
* We used to qdel it instead

-Consider recipe generation to be a failure if there aren't enough valid
ingredients or catalysts
* We used to only check whether possible ingredient list is empty
* Since the possible ingredient/catalyst lists don't change from round
to round, this could probably be made into a unit test in the future

-Reorganize random recipe conflict checks to make them more streamlined
* I had to do it in order to log regeneration outcomes

-Fix bugs which won't ever matter anyway
* optimal_ph_max no longer rolls above 14
* optimal_ph_max can no longer be below optimal_ph_min + 1 (we used to
check for CHEMICAL_MIN_PH + 1, which is just 1)
## Why It's Good For The Game

Logs help spot&diagnose issues which might not come up when testing
locally, like the issue where timestamps wouldn't update which was fixed
by #95895
## Changelog
🆑
fix: random recipes can no longer have upper pH bound above 14
/🆑

---------

Co-authored-by: l0 <-->
This commit is contained in:
levels0
2026-06-03 08:04:56 +02:00
committed by GitHub
co-authored by l0 <-->
parent bdd311735a
commit f5fc272810
3 changed files with 131 additions and 120 deletions
@@ -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