diff --git a/code/modules/atmospherics/machinery/components/binary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/binary_devices/thermomachine.dm
index 89accea8432..1eea0d09533 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/thermomachine.dm
@@ -31,14 +31,15 @@
var/cooling = TRUE
var/base_heating = 140
var/base_cooling = 170
- var/obj/item/tank/holding
var/use_enviroment_heat = FALSE
var/skipping_work = FALSE
- var/auto_thermal_regulator = FALSE
var/safeties = TRUE
var/lastwarning
var/color_index = 1
+ // Efficiency dictates how much we throttle the heat exchange process.
+ var/efficiency = 1
+
/obj/machinery/atmospherics/components/binary/thermomachine/Initialize()
. = ..()
RefreshParts()
@@ -99,9 +100,6 @@
. = ..()
. += getpipeimage(icon, "pipe", dir, COLOR_LIME, piping_layer)
. += getpipeimage(icon, "pipe", turn(dir, 180), COLOR_MOSTLY_PURE_RED, piping_layer)
- if(holding)
- var/mutable_appearance/holding = mutable_appearance(icon, "holding")
- . += holding
if(skipping_work && on)
var/mutable_appearance/skipping = mutable_appearance(icon, "blinking")
. += skipping
@@ -115,21 +113,25 @@
. += "-use a multitool with left-click to change the piping layer and right-click to change the piping color."
. += "The thermostat is set to [target_temperature]K ([(T0C-target_temperature)*-1]C)."
if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Efficiency [(heat_capacity/7500)*100]%."
+ . += "Heat capacity at [heat_capacity] Joules per Kelvin."
. += "Temperature range [min_temperature]K - [max_temperature]K ([(T0C-min_temperature)*-1]C - [(T0C-max_temperature)*-1]C)."
/obj/machinery/atmospherics/components/binary/thermomachine/AltClick(mob/living/user)
if(!can_interact(user))
return
- if(cooling)
- target_temperature = min_temperature
- investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS)
- to_chat(user, "You minimize the target temperature on [src] to [target_temperature] K.")
- else
- target_temperature = max_temperature
- investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS)
- to_chat(user, "You maximize the target temperature on [src] to [target_temperature] K.")
+ target_temperature = T20C
+ investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS)
+ to_chat(user, "You reset the target temperature on [src] to [target_temperature] K.")
+/** Performs heat calculation for the freezer. The full equation for this whole process is:
+ * T3 = (C1*T1 + (C1*C2)/(C1+C2)*(T2-T1)*E) / C1.
+ * T4 = (C1*T1 - (C1*C2)/(C1+C2)*(T2-T1)*E + M) / C1.
+ * C1 is main port heat capacity, T1 is the temp.
+ * C2 and T2 is for the heat capacity of the freezer and temperature that we desire respectively.
+ * T3 is the temperature we get, T4 is the exchange target (heat reservoir).
+ * M is the motor heat.
+ * E is the efficiency variable. At E=1 and M=0 it works out to be ((C1*T1)+(C2*T2))/(C1+C2).
+ */
/obj/machinery/atmospherics/components/binary/thermomachine/process_atmos()
if(!is_operational || !on) //if it has no power or its switched off, dont process atmos
on = FALSE
@@ -141,100 +143,90 @@
on = FALSE
update_appearance()
return
- var/datum/gas_mixture/enviroment = local_turf.return_air()
+ // The gas we want to cool/heat
var/datum/gas_mixture/main_port = airs[1]
- var/datum/gas_mixture/thermal_exchange_port = airs[2]
- var/main_heat_capacity = main_port.heat_capacity()
- var/thermal_heat_capacity = thermal_exchange_port.heat_capacity()
- var/temperature_delta = main_port.temperature - target_temperature
- if(auto_thermal_regulator)
- cooling = temperature_delta > 0
- else
- temperature_delta = cooling ? max(temperature_delta, 0) : min(temperature_delta, 0) //no cheesy strats
- var/motor_heat = 2500
- if(abs(temperature_delta) < 1.5) //allow the machine to work more finely
+ // The difference between target and what we need to heat/cool. Positive if heating, negative if cooling.
+ var/temperature_target_delta = target_temperature - main_port.temperature
+
+ // This variable holds the (C1*C2)/(C1+C2)*(T2-T1) part of the equation.
+ var/heat_amount = temperature_target_delta * (main_port.heat_capacity() * heat_capacity / (main_port.heat_capacity() + heat_capacity))
+
+ // Motor heat is the heat added to both ports of the thermomachine at every tick.
+ var/motor_heat = 5000
+ if(abs(temperature_target_delta) < 5) //Allow the machine to work more finely on lower temperature differences.
motor_heat = 0
+
+ // Automatic Switching. Longer if check to prevent unecessary update_appearances.
+ if ((cooling && temperature_target_delta > 0) || (!cooling && temperature_target_delta < 0))
+ cooling = temperature_target_delta <= 0 // Thermomachines that reached the target will default to cooling.
+ update_appearance()
- var/heat_amount = temperature_delta * (main_heat_capacity * heat_capacity / (main_heat_capacity + heat_capacity))
- var/efficiency = 1
- var/temperature_difference = 0
- var/skip_tick = TRUE
- if(!use_enviroment_heat && main_port.total_moles() > 0.01)
- if(cooling && thermal_exchange_port.total_moles() > 0.01 && nodes[2] && (thermal_exchange_port.temperature <= THERMOMACHINE_SAFE_TEMPERATURE || !safeties))
- thermal_exchange_port.temperature = max(thermal_exchange_port.temperature + heat_amount / thermal_heat_capacity + motor_heat / thermal_heat_capacity, TCMB)
- else if(cooling && (!thermal_exchange_port.total_moles() || !nodes[2]))
- skipping_work = skip_tick
- update_appearance()
- update_parents()
+ skipping_work = FALSE
+
+ if (main_port.total_moles() < 0.01)
+ skipping_work = TRUE
+ return
+
+ // Efficiency should be a proc level variable, but we need it for the ui.
+ // This is to reset the value when we are heating.
+ efficiency = 1
+
+ if(cooling)
+ var/datum/gas_mixture/exchange_target
+ // Exchange target is the thing we are paired with, be it enviroment or the red port.
+ if(use_enviroment_heat)
+ exchange_target = local_turf.return_air()
+ else
+ exchange_target = airs[2]
+
+ if (exchange_target.total_moles() < 0.01)
+ skipping_work = TRUE
return
- if(thermal_exchange_port.temperature > THERMOMACHINE_SAFE_TEMPERATURE && safeties)
+
+ // The hotter the heat reservoir is, the larger the malus.
+ var/temperature_exchange_delta = exchange_target.temperature - main_port.temperature
+ // Log 1 is already 0, going any lower will result in a negative number.
+ efficiency = clamp(1 - log(10, max(1, temperature_exchange_delta)) * 0.08, 0.65, 1)
+ // We take an extra efficiency malus for enviroments where the mol is too low.
+ // Cases of log(0) will be caught by the early return above.
+ if (use_enviroment_heat)
+ efficiency *= clamp(log(1.55, exchange_target.total_moles()) * 0.15, 0.65, 1)
+
+ if (exchange_target.temperature > THERMOMACHINE_SAFE_TEMPERATURE && safeties)
on = FALSE
- visible_message("The thermal exchange port's temperature has reached critical levels, shutting down...")
+ visible_message("The heat reservoir has reached critical levels, shutting down...")
update_appearance()
return
- else if(thermal_exchange_port.temperature > THERMOMACHINE_SAFE_TEMPERATURE && !safeties)
+
+ else if(exchange_target.temperature > THERMOMACHINE_SAFE_TEMPERATURE && !safeties)
if((REALTIMEOFDAY - lastwarning) / 5 >= WARNING_DELAY)
lastwarning = REALTIMEOFDAY
- visible_message("The thermal exchange port's temperature has reached critical levels!")
- if(check_explosion(thermal_exchange_port.temperature))
+ visible_message("The heat reservoir has reached critical levels!")
+ if(check_explosion(exchange_target.temperature))
explode()
- return PROCESS_KILL //we dying anyway, so let's stop processing
- temperature_difference = thermal_exchange_port.temperature - main_port.temperature
- temperature_difference = cooling ? temperature_difference : 0
- if(temperature_difference > 0)
- efficiency = max(1 - log(10, temperature_difference) * 0.08, 0.65)
- main_port.temperature = max(main_port.temperature - (heat_amount * efficiency)/ main_heat_capacity + motor_heat / main_heat_capacity, TCMB)
- skip_tick = FALSE
- if(use_enviroment_heat && main_port.total_moles() > 0.01)
- var/enviroment_efficiency = 1
- if(cooling && enviroment.total_moles() > 0.01 && (thermal_exchange_port.temperature <= THERMOMACHINE_SAFE_TEMPERATURE || !safeties))
- var/enviroment_heat_capacity = enviroment.heat_capacity()
- if(enviroment.total_moles())
- enviroment_efficiency = clamp(log(1.55, enviroment.total_moles()) * 0.15, 0.65, 1)
- enviroment.temperature = max(enviroment.temperature + heat_amount / enviroment_heat_capacity, TCMB)
- air_update_turf(FALSE, FALSE)
- else if(cooling && !enviroment.total_moles())
- skipping_work = skip_tick
- update_appearance()
- update_parents()
- return
- if(enviroment.temperature > THERMOMACHINE_SAFE_TEMPERATURE && safeties)
- on = FALSE
- visible_message("The enviroment's temperature has reached critical levels, shutting down...")
- update_appearance()
- return
- else if(enviroment.temperature > THERMOMACHINE_SAFE_TEMPERATURE && !safeties)
- if((REALTIMEOFDAY - lastwarning) / 5 >= WARNING_DELAY)
- lastwarning = REALTIMEOFDAY
- visible_message("The enviroment's temperature has reached critical levels!")
- if(check_explosion(enviroment.temperature))
- explode()
- return PROCESS_KILL //we dying anyway, so let's stop processing
- temperature_difference = enviroment.temperature - main_port.temperature
- temperature_difference = cooling ? temperature_difference : 0
- if(temperature_difference > 0)
- efficiency = max(1 - log(10, temperature_difference) * 0.08, 0.65)
- main_port.temperature = max(main_port.temperature - (heat_amount * efficiency * enviroment_efficiency) / main_heat_capacity + motor_heat / main_heat_capacity, TCMB)
- skip_tick = FALSE
+ return PROCESS_KILL //We're dying anyway, so let's stop processing
+
+ exchange_target.temperature = max((THERMAL_ENERGY(exchange_target) - (heat_amount * efficiency) + motor_heat) / exchange_target.heat_capacity(), TCMB)
- skipping_work = skip_tick
+ main_port.temperature = max((THERMAL_ENERGY(main_port) + (heat_amount * efficiency)) / main_port.heat_capacity(), TCMB)
heat_amount = abs(heat_amount)
var/power_usage = 0
- if(temperature_delta > 1)
+ if(abs(temperature_target_delta) > 1)
power_usage = (heat_amount * 0.35 + idle_power_usage) ** (1.25 - (5e7 * efficiency) / (max(5e7, heat_amount)))
else
power_usage = idle_power_usage
if(power_usage > 1e6)
power_usage *= efficiency
+
use_power(power_usage)
update_appearance()
update_parents()
/obj/machinery/atmospherics/components/binary/thermomachine/attackby(obj/item/item, mob/user, params)
- if(!on && !holding && item.tool_behaviour == TOOL_SCREWDRIVER)
+ if(!on && item.tool_behaviour == TOOL_SCREWDRIVER)
if(!anchored)
to_chat(user, "Anchor [src] first!")
return
@@ -246,16 +238,6 @@
if(default_deconstruction_crowbar(item))
return
- if(istype(item, /obj/item/tank))
- var/obj/item/tank/tank = item
- if(!user.transferItemToLoc(tank, src))
- return FALSE
- to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [tank]" : "You insert [tank] into [src]"].")
- investigate_log("had its internal [holding] swapped with [tank] by [key_name(user)].", INVESTIGATE_ATMOS)
- replace_tank(user, tank)
- update_appearance()
- return
-
if(panel_open && item.tool_behaviour == TOOL_MULTITOOL)
piping_layer = (piping_layer >= PIPING_LAYER_MAX) ? PIPING_LAYER_MIN : (piping_layer + 1)
to_chat(user, "You change the circuitboard to layer [piping_layer].")
@@ -267,6 +249,7 @@
if(!..())
return FALSE
SetInitDirections()
+ update_appearance()
return TRUE
/obj/machinery/atmospherics/components/binary/thermomachine/proc/change_pipe_connection(disconnect)
@@ -358,17 +341,6 @@
obj_flags |= EMAGGED
safeties = FALSE
-/obj/machinery/atmospherics/components/binary/thermomachine/proc/replace_tank(mob/living/user, obj/item/tank/new_tank)
- if(!user)
- return FALSE
- if(holding)
- user.put_in_hands(holding)
- holding = null
- if(new_tank)
- holding = new_tank
- update_appearance()
- return TRUE
-
/obj/machinery/atmospherics/components/binary/thermomachine/proc/check_explosion(temperature)
if(temperature < THERMOMACHINE_SAFE_TEMPERATURE + 2000)
return FALSE
@@ -378,11 +350,11 @@
/obj/machinery/atmospherics/components/binary/thermomachine/proc/explode()
explosion(loc, 0, 0, 3, 3, TRUE)
var/datum/gas_mixture/main_port = airs[1]
- var/datum/gas_mixture/thermal_exchange_port = airs[2]
+ var/datum/gas_mixture/exchange_target = airs[2]
if(main_port)
loc.assume_air(main_port.remove_ratio(1))
- if(thermal_exchange_port)
- loc.assume_air(thermal_exchange_port.remove_ratio(1))
+ if(exchange_target)
+ loc.assume_air(exchange_target.remove_ratio(1))
qdel(src)
/obj/machinery/atmospherics/components/binary/thermomachine/ui_status(mob/user)
@@ -411,15 +383,10 @@
var/datum/gas_mixture/air1 = airs[1]
data["temperature"] = air1.temperature
data["pressure"] = air1.return_pressure()
+ data["efficiency"] = efficiency
- data["holding"] = holding ? TRUE : FALSE
- data["tank_gas"] = FALSE
- if(holding)
- var/datum/gas_mixture/holding_mix = holding.return_air()
- data["tank_gas"] = !!holding_mix.total_moles()
data["use_env_heat"] = use_enviroment_heat
data["skipping_work"] = skipping_work
- data["auto_thermal_regulator"] = auto_thermal_regulator
data["safeties"] = safeties
var/hacked = (obj_flags & EMAGGED) ? TRUE : FALSE
data["hacked"] = hacked
@@ -456,23 +423,9 @@
if(.)
target_temperature = clamp(target, min_temperature, max_temperature)
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
- if("pumping")
- if(holding && nodes[2])
- var/datum/gas_mixture/thermal_exchange_port = airs[2]
- var/datum/gas_mixture/holding_mix = holding.return_air()
- var/datum/gas_mixture/remove = holding_mix.remove_ratio(1)
- thermal_exchange_port.merge(remove)
- . = TRUE
- if("eject")
- if(holding)
- replace_tank(usr)
- . = TRUE
if("use_env_heat")
use_enviroment_heat = !use_enviroment_heat
. = TRUE
- if("auto_thermal_regulator")
- auto_thermal_regulator = !auto_thermal_regulator
- . = TRUE
if("safeties")
safeties = !safeties
investigate_log("[key_name(usr)] turned off the [src] safeties", INVESTIGATE_ATMOS)
diff --git a/tgui/packages/tgui/interfaces/ThermoMachine.js b/tgui/packages/tgui/interfaces/ThermoMachine.js
index 49536c95511..87043b8cd26 100644
--- a/tgui/packages/tgui/interfaces/ThermoMachine.js
+++ b/tgui/packages/tgui/interfaces/ThermoMachine.js
@@ -1,6 +1,6 @@
import { toFixed } from 'common/math';
import { useBackend } from '../backend';
-import { AnimatedNumber, Box, Button, LabeledList, Modal, NumberInput, Section } from '../components';
+import { AnimatedNumber, Box, Button, LabeledList, Modal, NumberInput, Section, ProgressBar } from '../components';
import { Window } from '../layouts';
export const ThermoMachine = (props, context) => {
@@ -16,6 +16,29 @@ export const ThermoMachine = (props, context) => {
);
+ const cooling_efficiency_infos = !!data.cooling &&(
+
+
+ {Math.round(data.efficiency * 10000)/100 + " %"}
+
+
+ );
+ const cooling_enviroment_reservoir = !!data.cooling &&(
+
+
+ );
return (
{
format={value => toFixed(value, 2)} />
{' kPa'}
+
+ {data.cooling? 'Cooling' : 'Heating'}
+
+ {cooling_efficiency_infos}
{
disabled={!data.hacked}
onClick={() => act('safeties')} />
-
-
-
-
-
-
-
-
+ {cooling_enviroment_reservoir}