From 121462a6fba2eb02eba428a015ea12ff077c4b64 Mon Sep 17 00:00:00 2001 From: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:08:36 +0530 Subject: [PATCH] Fixes reactions ending prematurely when nearing min conditions (#89109) ## About The Pull Request So when a reaction is approaching its minimum conditions (temps are approaching `required_temp` or ph is appraching `optimal_ph_min`) It would end some good distance from these min values & not very close to them. For e.g. Flurosulphuric acid has a minimum required temp of 380K but the reaction would stop/won't occur if the temps go below 400K itself i.e. 20K from its minimum which is not ideal when we are trying to maximise results at all times. Now it goes all the way down to these min values ensuring the reaction rate never goes below 0.005(which in players terms means the rate of reagents poduced per tick will never go below 0.01u for any given reaction) This also improves the code of debug chem master which caught this bug. ## Changelog :cl: fix: chemical reactions can now go all the way to their min required temp/ph code: improved code for debug chem master. /:cl: --- .../modules/reagents/chemistry/equilibrium.dm | 10 ++- .../reagents/chemistry/holder/holder.dm | 2 +- .../chemistry/machinery/chem_recipe_debug.dm | 76 +++++++++++-------- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index bc7cbd37f48..76f45e77cab 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -34,6 +34,8 @@ var/delta_t ///How pure our step is var/delta_ph + ///Min reaction rate possible below which rounding errors occur + VAR_PRIVATE/min_rate ///Modifiers from catalysts, do not use negative numbers. ///I should write a better handiler for modifying these ///Speed mod @@ -67,7 +69,6 @@ LAZYADD(holder.reaction_list, src) SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] attempts") - /datum/equilibrium/Destroy() if(reacted_vol < target_vol) //We did NOT finish from reagents - so we can restart this reaction given property changes in the beaker. (i.e. if it stops due to low temp, this will allow it to fast restart when heated up again) LAZYADD(holder.failed_but_capable_reactions, reaction) //Consider replacing check with calculate_yield() @@ -114,6 +115,8 @@ product_ratio += reaction.results[product] else product_ratio = 1 + min_rate = product_ratio * (CHEMICAL_VOLUME_ROUNDING / 2) + return TRUE /** @@ -321,10 +324,13 @@ purity *= purity_modifier //Now we calculate how much to add - this is normalised to the rate up limiter - var/delta_chem_factor = reaction.rate_up_lim * delta_t * seconds_per_tick//add/remove factor + var/delta_chem_factor = reaction.rate_up_lim * delta_t * seconds_per_tick //keep limited if(delta_chem_factor > step_target_vol) delta_chem_factor = step_target_vol + //ensure its above minimum rate below which rounding errors occur + else if(delta_chem_factor < min_rate) + delta_chem_factor = min_rate //Normalise to multiproducts delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_VOLUME_ROUNDING) if(delta_chem_factor <= 0) diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index 7a599edc133..be74e5cfb85 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -266,7 +266,7 @@ for(var/datum/reagent/removed_reagent as anything in removed_reagents) SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, removed_reagent, removed_reagents[removed_reagent]) - return round(total_removed_amount, CHEMICAL_VOLUME_ROUNDING) + return total_removed_amount /** * Removes all reagents either proportionally(amount is the direct volume to remove) diff --git a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm index 9b0773a8316..3cf570250d6 100644 --- a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm +++ b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm @@ -29,7 +29,7 @@ icon = 'icons/obj/medical/chemical.dmi' icon_state = "HPLC_debug" density = TRUE - idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.4 + use_power = NO_POWER_USE resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE ///Temperature to be imposed on the reaction @@ -63,7 +63,7 @@ ///The target reagents to we are working with. can vary if an reaction requires a specific container var/datum/reagents/target_reagents ///The beaker inside this machine, if null will create a new one - var/obj/item/reagent_containers/cup/beaker/bluespace/beaker + var/obj/item/reagent_containers/container ///The default reagent container required for the selected test reaction if any var/obj/item/reagent_containers/required_container @@ -85,47 +85,47 @@ reactions_to_test.Cut() target_reagents = null edit_reaction = null - QDEL_NULL(beaker) + QDEL_NULL(container) QDEL_NULL(required_container) - UnregisterSignal(reagents, COMSIG_REAGENTS_REACTION_STEP) - . = ..() + return ..() /obj/machinery/chem_recipe_debug/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() if(isnull(held_item) || (held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1)) return NONE - if(!QDELETED(beaker)) + if(!QDELETED(container)) if(is_reagent_container(held_item) && held_item.is_open_container()) - context[SCREENTIP_CONTEXT_LMB] = "Replace beaker" + context[SCREENTIP_CONTEXT_LMB] = "Replace container" return CONTEXTUAL_SCREENTIP_SET else if(is_reagent_container(held_item) && held_item.is_open_container()) - context[SCREENTIP_CONTEXT_LMB] = "Insert beaker" + context[SCREENTIP_CONTEXT_LMB] = "Insert container" return CONTEXTUAL_SCREENTIP_SET /obj/machinery/chem_recipe_debug/examine(mob/user) . = ..() - if(!QDELETED(beaker)) - . += span_notice("A beaker of [beaker.reagents.maximum_volume]u capacity is inside.") + if(!QDELETED(container)) + . += span_notice("A container of [container.reagents.maximum_volume]u capacity is inside.") else - . += span_notice("No beaker is present. A new will be created when ejecting.") + . += span_notice("No container is present. A new will be created when ejecting.") /obj/machinery/chem_recipe_debug/Exited(atom/movable/gone, direction) . = ..() - if(gone == beaker) - beaker = null + if(gone == container) + container = null -/obj/machinery/chem_recipe_debug/attackby(obj/item/held_item, mob/user, params) +/obj/machinery/chem_recipe_debug/item_interaction(mob/living/user, obj/item/held_item, list/modifiers) + . = NONE if((held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1)) - return ..() + return if(is_reagent_container(held_item) && held_item.is_open_container()) - . = TRUE - if(!QDELETED(beaker)) - try_put_in_hand(beaker, user) + if(!QDELETED(container)) + try_put_in_hand(container, user) if(!user.transferItemToLoc(held_item, src)) - return - beaker = held_item + return ITEM_INTERACT_FAILURE + container = held_item + return ITEM_INTERACT_SUCCESS /** * Extracts a human readable name for this chemical reaction @@ -155,13 +155,12 @@ var/datum/chemical_reaction/test_reaction = reactions_to_test[current_reaction_index || 1] switch(temp_mode) if(USE_MINIMUM_TEMPERATURE) - return test_reaction.required_temp + (test_reaction.is_cold_recipe ? - 20 : 20) //20k is good enough offset to account for reaction rate rounding + return test_reaction.required_temp if(USE_OPTIMAL_TEMPERATURE) return test_reaction.optimal_temp if(USE_OVERHEAT_TEMPERATURE) return test_reaction.overheat_temp - /** * Adjusts the temperature, ph & purity of the holder * Arguments @@ -173,7 +172,7 @@ var/target_temperature = decode_target_temperature() if(!isnull(target_temperature)) - target_reagents.adjust_thermal_energy((target_temperature - target_reagents.chem_temp) * 0.4 * seconds_per_tick * SPECIFIC_HEAT_DEFAULT * target_reagents.total_volume) + target_reagents.adjust_thermal_energy((target_temperature - target_reagents.chem_temp) * 0.45 * seconds_per_tick * target_reagents.heat_capacity()) if(use_forced_purity) target_reagents.set_all_reagents_purity(forced_purity) @@ -187,7 +186,7 @@ /obj/machinery/chem_recipe_debug/process(seconds_per_tick) if(!target_reagents.is_reacting) adjust_environment(seconds_per_tick) - target_reagents.handle_reactions() + target_reagents.handle_reactions() //send updates to ui. faster than SStgui.update_uis for(var/datum/tgui/ui in src.open_uis) @@ -334,11 +333,10 @@ .["editReaction"] = reaction_data var/list/beaker_data = null - if(target_reagents.reagent_list.len) + if(!QDELETED(container) || target_reagents.total_volume) beaker_data = list() beaker_data["maxVolume"] = target_reagents.maximum_volume beaker_data["pH"] = round(target_reagents.ph, 0.01) - beaker_data["purity"] = round(target_reagents.get_average_purity(), 0.01) beaker_data["currentVolume"] = round(target_reagents.total_volume, CHEMICAL_VOLUME_ROUNDING) beaker_data["currentTemp"] = round(target_reagents.chem_temp, 1) beaker_data["purity"] = round(target_reagents.get_average_purity(), 0.001) @@ -666,12 +664,26 @@ tgui_alert(ui.user, "Saved to [dest]") if("eject") - if(!target_reagents.total_volume) - return - if(QDELETED(beaker)) - beaker = new /obj/item/reagent_containers/cup/beaker/bluespace(src) - target_reagents.trans_to(beaker, target_reagents.total_volume) - try_put_in_hand(beaker, ui.user) + //initialize a new container for us + if(QDELETED(container)) + if(QDELETED(required_container)) + container = new /obj/item/reagent_containers/cup/beaker/bluespace(src) + else + container = new required_container.type(src) + + //transfer all reagents & ingredients if we are using a soup pot + container.reagents.clear_reagents() + target_reagents.trans_to(container, target_reagents.total_volume) + if(istype(container, /obj/item/reagent_containers/cup/soup_pot) && istype(required_container, /obj/item/reagent_containers/cup/soup_pot)) + var/obj/item/reagent_containers/cup/soup_pot/pot = container + for(var/obj/item as anything in pot.added_ingredients) + qdel(item) + var/obj/item/reagent_containers/cup/soup_pot/holder = required_container + for(var/obj/item as anything in holder.added_ingredients) + item.forceMove(pot) + LAZYADD(pot.added_ingredients, item) + try_put_in_hand(container, ui.user) + return TRUE #undef USE_REACTION_TEMPERATURE