Disclaimer: I am don’t know much c++. Anything I say about the code may be wrong. There may be some typos and such in the math too. If it doesn’t seem to make sense it may be wrong.
The current temperature system has issues.
- Items of different size/mass change temperature at same rate.
- Items of different size/mass freeze at same rate.
- Freezing happens instantly once freezing temperature is reached.
The last one in particular is a big problem. Items freeze and unfreeze too easily.
For example freezing 1 kg of water requires taking 333 kJ of energy from it.
Cooling water from 79 °C to 0 °C also removes 333 kJ of energy from it (and the nergy is removed much much faster due to higher temperature difference). Freezing water takes much much longer than cooling the water but currently the game seems to completely ignore this.
As far as I understood the code the game works by tracking the temperature of the item. This approach is flawed and can never give satisfactory results.
The proper way to do it is to track the energy in the item and then calculating the temperature from the energy when needed.
Physics:
Rate of heat flow (Q) tells how much the item gains/loses energy. From there we can use approximation
Q = -ΔT × I × A × Δt
where Q is the ammount of energy transferred, ΔT is the temperature difference (kelvin or celcius. But we need everything to be in kelvin later so just use kelvin), I is insulation term, A is surface area and Δt is the time interval.
The proper way to do this would be to use integrals instead. In certain cases this approach may cool/heat the item below/above enviroment temperature incorrectly so there needs to be a small extra check. This approach also ignores that the insulation term is temperature dependant (mostly due to convection but also radiation). But this should be good enough for a game.
To get the surface area we can just assume all items are qubes and use the volume.
A = V2/3
After we know the ammount of energy change the energy in the item (E) is trivial.
E = E + Q
Now we can calculate the temperature of the item (T). First with naive assumption that the item is not frozen. Note that all the temperatures from now on must be in kelvins.
T = E / (cliquid×m)
where cliquid is the specific heat of the item (in not frozen state) and m is the mass of the item. This isn’t the “proper” way to calculate it as this is same as saying that at 0 K the item has 0 J of energy and then calculate temperature from there but it is good enough for a game.
Now if the above temperature is greater than the freezing temperature of the item everything is OK. But if it is less than the freezing temperature we need to check how frozen the item is and calculate the real temperature.
Lets check first if the item is completely frozen.
Tf × m+ csolid × m × Tfreezing > E
where Tf is the enthalpy of fusion, csolid is the specific heat of the solid and Tfreezing is the freezing temperature. If the above equation is true then the item is completely frozen.
And the temperature of a completely frozen item is
T = (E - Tf × mass) / (csolid×m)
If the term is negative then the item is partially frozen. Partially frozen items always have temperature exactly the freezing temperature.
T = Tfreezing
I would suggest that if the item is less than 50% frozen (arbitrary percentage that could be changed) then it can be used normally as if it was not frozen. To check if the item is 50% frozen use this equation
Tf × m × 0.5 + csolid × m × Tfreezing × 0.5 + cliquid × m × 0.5 > E
If the above equation is true the item is more than 50% frozen.
After all this has been done you would need to check if the item is being cooled below enviroment temperature/heated above enviroment temperature. If that is happening just set the temperature of the item to enviroment temperature and calculate the energy from there.
Mixing together items of different temperatures would be simply adding together the energies in them and then recalculating the temperature with above equations.
When consuming part of an item simply remove energy stored in the consumed mass. The temperature would not change in this.
E = T × m × c
Implementing this system would require all temperature tracked items to have their specific heats for frozen and not frozen states and also enthalpy of fusion. Easiest way for foods would probably be to use something based on water % of the food or some quesstimation for different kind of foods. The insulation term would also need to be determined.
Probably all items (non insulated) could use same insulation term. About 0.0025 would seem ok based on this study on water bottles (I just took the slope of the fig. 3 at 35 °C in gimp and calculated from that). Some much smaller value for thermos and other insulated items.
Now some pseudo code:
temperature_difference = item_temperature - enviroment_temperature
if temperature_difference == 0
//There is nothing to do. No energy will flow. No temperature will change. No phase change will occur.
//Don't waste time for the math in this case. Nothing has changed.
surface_area = volume^(2/3)
energy_change = - temperature_difference * insulation_term * surface_area * duration
theremal_energy += energy_change
// Assume it is not frozen
new_item_temperature = thermal_energy / ( specific_heat_liquid * mass )
if new_item_temperature < freezing_temperature
// It is at least partially frozen
if entalpy_of_fusion * mass + specific_heat_solid * mass * freezing_temperature > theremal_energy
// Completely frozen
item_tags.insert( "FROZEN" );
new_item_temperature = (thermal_energy - entalpy_of_fusion * mass ) / ( specific_heat_solid * mass )
if entalpy_of_fusion * mass * 0.5 + specific_heat_solid * mass * freezing_temperature * 0.5 + freezing_temperature * specific_heat_liquid * mass > theremal_energy
//Not fully frozen but more than half frozen. Same use limits as frozen items.
item_tags.insert( "FROZEN" );
new_item_temperature = freezing_temperature
else
//Partially frozen but still useable as if it was not frozen
new_item_temperature = freezing_temperature
//Prevent cooling below enviroment temperature
if energy_change < 0 && new_item_temperature < enviroment_temperature
new_item_temperature = enviroment_temperature
if enviroment_temperature > freezing_temperature
//Not frozen even if the above would say it is
theremal_energy = specific_heat_liquid * mass * enviroment_temperature
item_tags.erase( "FROZEN" );
//Prevent heating above enviroment temperature
elif energy_change > 0 && new_item_temperature > enviroment_temperature
new_item_temperature = enviroment_temperature
if enviroment_temperature < freezing_temperature
//Is frozen even if above says it is not
theremal_energy = specific_heat_solid * mass * enviroment_temperature
item_tags.insert( "FROZEN" );
if new_item_temperature < cold_temperature && not frozen
//It is cold
elif new_item_temperature > hot_temperature
//It is hot
elif new_item_temperature > warm_temperature
//It is warm