From 99a2db7dd39b84a6a83304f9fb292a8cc06ba65b Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:43:03 -0500 Subject: [PATCH] [MIRROR] Reactions now use volume averaged purity (#803) * Reactions now use volume averaged purity (#81246) ## About The Pull Request Cause it only makes sense Currently the purity of reagents created in a reaction is computed as follows `total sum of purity of all reagents present / total number of reagents` This is incorrect because regardless of how much "volume" of an impure/pure reagent is present the purity of the final solution is unaffected. Logically if we have more amount of an impure reagent the more impure the final solution should be & same for opposite case. This is the case for ph, where if we have a large volume of say "acidic" reagent then changes in other reagents have a small effect to the overall "acidity" of the solution. The same concept now applies for purity as well `get_average_purity()`accounts for volume thus yielding more realistic results. The effect becomes more significant with larger volumes of reagents. :cl: fix: reactions now compute purity of reagents based on their volume, meaning larger amounts of reagents created will have more significant effects on the final purity of the solution /:cl: * Reactions now use volume averaged purity * Merge #818 --------- Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: SomeRandomOwl <2568378+SomeRandomOwl@users.noreply.github.com> --- .../modules/reagents/chemistry/equilibrium.dm | 39 +++++-------------- .../reagents/chemistry/holder/reactions.dm | 38 ++++++++++-------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index d2a037f91d1..c3ccc000207 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -104,7 +104,7 @@ return FALSE //To prevent reactions outside of the pH window from starting. - if(!((holder.ph >= (reaction.optimal_ph_min - reaction.determin_ph_range)) && (holder.ph <= (reaction.optimal_ph_max + reaction.determin_ph_range)))) + if(holder.ph < (reaction.optimal_ph_min - reaction.determin_ph_range) || holder.ph > (reaction.optimal_ph_max + reaction.determin_ph_range)) return FALSE //All checks pass. cache the product ratio @@ -265,24 +265,23 @@ //Begin checks //Calculate DeltapH (Deviation of pH from optimal) //Within mid range + var/acceptable_ph if (cached_ph >= reaction.optimal_ph_min && cached_ph <= reaction.optimal_ph_max) delta_ph = 1 //100% purity for this step //Lower range else if (cached_ph < reaction.optimal_ph_min) //If we're outside of the optimal lower bound - if (cached_ph < (reaction.optimal_ph_min - reaction.determin_ph_range)) //If we're outside of the deterministic bound + acceptable_ph = reaction.optimal_ph_min - reaction.determin_ph_range + if (cached_ph < acceptable_ph) //If we're outside of the deterministic bound delta_ph = 0 //0% purity else //We're in the deterministic phase - delta_ph = (((cached_ph - (reaction.optimal_ph_min - reaction.determin_ph_range)) ** reaction.ph_exponent_factor) / ((reaction.determin_ph_range ** reaction.ph_exponent_factor))) //main pH calculation + delta_ph = ((cached_ph - acceptable_ph) / reaction.determin_ph_range) ** reaction.ph_exponent_factor //Upper range else if (cached_ph > reaction.optimal_ph_max) //If we're above of the optimal lower bound - if (cached_ph > (reaction.optimal_ph_max + reaction.determin_ph_range)) //If we're outside of the deterministic bound + acceptable_ph = reaction.optimal_ph_max + reaction.determin_ph_range + if (cached_ph > acceptable_ph) //If we're outside of the deterministic bound delta_ph = 0 //0% purity else //We're in the deterministic phase - delta_ph = (((- cached_ph + (reaction.optimal_ph_max + reaction.determin_ph_range)) ** reaction.ph_exponent_factor) / (reaction.determin_ph_range ** reaction.ph_exponent_factor))//Reverse - to + to prevent math operation failures. - - //This should never proc, but it's a catch incase someone puts in incorrect values - else - stack_trace("[holder.my_atom] attempted to determine FermiChem pH for '[reaction.type]' which had an invalid pH of [cached_ph] for set recipie pH vars. It's likely the recipe vars are wrong.") + delta_ph = ((acceptable_ph - cached_ph) / reaction.determin_ph_range) ** reaction.ph_exponent_factor //Calculate DeltaT (Deviation of T from optimal) if(!reaction.is_cold_recipe) @@ -316,7 +315,7 @@ purity = delta_ph //Then adjust purity of result with beaker reagent purity. - purity *= average_purity() + purity *= holder.get_average_purity() //Then adjust it from the input modifier purity *= purity_modifier @@ -399,23 +398,3 @@ //i.e. we have created all the reagents needed for this reaction if(total_step_added >= step_target_vol) to_delete = TRUE - -/* -* Calculates the total sum normalised purity of ALL reagents in a holder -* Currently calculates it irrespective of required reagents at the start, but this should be changed if this is powergamed to required reagents -* It's not currently because overly_impure affects all reagents -*/ -/datum/equilibrium/proc/average_purity() - PRIVATE_PROC(TRUE) - - var/list/cached_reagents = holder.reagent_list - - var/num_of_reagents = cached_reagents.len - if(!num_of_reagents)//I've never seen it get here with 0, but in case - it gets here when it blows up from overheat - stack_trace("No reactants found mid reaction for [reaction.type]. Beaker: [holder.my_atom]") - return 0 //we exploded and cleared reagents - but lets not kill the process - - var/cached_purity - for(var/datum/reagent/reagent as anything in cached_reagents) - cached_purity += reagent.purity - return cached_purity / num_of_reagents diff --git a/code/modules/reagents/chemistry/holder/reactions.dm b/code/modules/reagents/chemistry/holder/reactions.dm index e9f0bbbe433..8005e45427b 100644 --- a/code/modules/reagents/chemistry/holder/reactions.dm +++ b/code/modules/reagents/chemistry/holder/reactions.dm @@ -308,36 +308,39 @@ var/list/cached_required_reagents = selected_reaction.required_reagents var/list/cached_results = selected_reaction.results var/datum/cached_my_atom = my_atom - var/multiplier = INFINITY - for(var/reagent in cached_required_reagents) - multiplier = round(min(multiplier, get_reagent_amount(reagent) / cached_required_reagents[reagent])) - if(!multiplier)//Incase we're missing reagents - usually from on_reaction being called in an equlibrium when the results.len == 0 handlier catches a misflagged reaction + //find how much ration of products to create + var/multiplier = INFINITY + for(var/datum/reagent/requirement as anything in cached_required_reagents) + multiplier = min(multiplier, get_reagent_amount(requirement) / cached_required_reagents[requirement]) + multiplier = round(multiplier, CHEMICAL_QUANTISATION_LEVEL) + if(!multiplier)//Incase we're missing reagents - usually from on_reaction being called in an equlibrium when the results.len == 0 handler catches a misflagged reaction return FALSE - var/sum_purity = 0 - for(var/_reagent in cached_required_reagents)//this is not an object - var/datum/reagent/reagent = has_reagent(_reagent) - if (!reagent) - continue - sum_purity += reagent.purity - remove_reagent(_reagent, (multiplier * cached_required_reagents[_reagent])) - sum_purity /= cached_required_reagents.len - for(var/product in selected_reaction.results) - multiplier = max(multiplier, 1) //this shouldn't happen ... - var/yield = (cached_results[product]*multiplier)*sum_purity + //average purity to be used in scaling the yield of products formed + var/average_purity = get_average_purity() + + //remove the required reagents + for(var/datum/reagent/requirement as anything in cached_required_reagents)//this is not an object + remove_reagent(requirement, cached_required_reagents[requirement] * multiplier) + + //add the result reagents whose yield depend on the average purity + var/yield + for(var/datum/reagent/product as anything in cached_results) + yield = cached_results[product] * multiplier * average_purity SSblackbox.record_feedback("tally", "chemical_reaction", yield, product) - add_reagent(product, yield, null, chem_temp, sum_purity) + add_reagent(product, yield, null, chem_temp, average_purity) + //play sounds on the target atom var/list/seen = viewers(4, get_turf(my_atom)) var/iconhtml = icon2html(cached_my_atom, seen) if(cached_my_atom) if(!ismob(cached_my_atom)) // No bubbling mobs if(selected_reaction.mix_sound) playsound(get_turf(cached_my_atom), selected_reaction.mix_sound, 80, TRUE) - my_atom.audible_message(span_notice("[iconhtml] [selected_reaction.mix_message]")) + //use slime extract if(istype(cached_my_atom, /obj/item/slime_extract)) var/obj/item/slime_extract/extract = my_atom extract.extract_uses-- @@ -353,4 +356,5 @@ my_turf.pollute_turf(selected_reaction.pollutant_type, selected_reaction.pollutant_amount * multiplier) //NOVA EDIT END + //finish the reaction selected_reaction.on_reaction(src, null, multiplier)