Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot air from fires and direct heat radiation applies to tile temperatures #24956

Merged
merged 7 commits into from Aug 27, 2018
Merged

Hot air from fires and direct heat radiation applies to tile temperatures #24956

merged 7 commits into from Aug 27, 2018

Conversation

nexusmrsep
Copy link
Contributor

@nexusmrsep nexusmrsep commented Aug 18, 2018

SUMMARY: Features "Hot air and direct heat radiation from fires affect local temperature and can heat nearby area including interiors."

Resolves #24938

What does it do?
This PR:

  • unlocks hot air, so it can propagate starting from small fires, including contained small fires. This is important, because it allows to heat up room, as hot air will eventually fill the room. It works like smoke, but it's invisible. Outside closed quarters it propagates less (again, imagine smoke) but still affects tiles it gets on.
  • allows hot air to affect tile's temperature (local modifier), this does not stack, but each fire emits separately, and if they emit the same type of hot air it will only modify temperature by intensity that has 3 levels: +2 / +6 / +10 degrees (for small fires, larger ones generate much hotter air)
  • allows direct heat radiation from fire to affect tile temperature (this can stack from multiple fires, and its effect is reduced by distance from the fire.

Conclusion:
It indirectly allows to defrost food or prevent it from freezing by raising adjacent temperature.

Rationale:
You can use a stove in a house to heat up interiors in the winter. Heated room raises temperature of items in that room. You can warm up by the fire, so your items or items stored nearby should too, just by being there.

Implementation
Mainly copied reworked parts of the code that ruled over player being affected by proximity of fires and adjusted them to be used by get_temperature() function in game.cpp
Also changed restriction in field.cpp to allow propagation of hot air from small fires, while restricting it when there is too much fire nearby for optimization.
After reworking functions for heat radiation and heat convection aka hot air propagation I had to rework how heat radiation affects player - blisters, turnout gear, heatsinks, etc. aiming at keeping them close to original balance.

Risks
Where possible I did not change values used by that code in other way then converting them to the applied system, so this should be balanced BUT I did not remove anything that affects player and I know that get_temperature() is also checked in calculations of player-fire player-lava temperature interactions. So that might result in higher impact of those heat sources on the player.

EDIT: This change required re-balancing of blister application and some mechanics of applying heat from fire/lava to the player body temperatures, including how heat-sinks behave. It also - as i pointed out in comments below - introduces higher risk of lethal overheating in big fires. Might also lead to much endurable overheating from small fires in hot seasons. Something to be aware about.

Testing
Test were positive for the main function of this PR:

  • ambient temp outside: -1C , standing next to fire, observed temperatures ranges: +3C - +8C
  • see picture below - hot air propagated throughout the building

8 in the middle is a small fire in a brazier, ~ is hot air from said fire (made visible for testing purposes) color is intensity from white-yellow-red (1-2-3)

Ambient temperature where character stands is -12C, tile is affected by type 1 intensity 3 hot air, and character is one tile away from small fire in a brazier for direct heat radiation, this brings the temperature to -3C overall.

Same room, left upper corner: out of range of direct heat radiation, inside type 1 intensity 3 hot air, temp from -12C --> -7C

image

Request a test?
Yes, please. It is done and tested, but I'll remove the [WIP] flag only after someone does some testing too and compare the results.

@nexusmrsep nexusmrsep added Game: Mechanics Change Code that changes how major features work [C++] Changes (can be) made in C++. Previously named `Code` Fields / Furniture / Terrain / Traps Objects that are part of the map or its features. labels Aug 18, 2018
@stk2008
Copy link

stk2008 commented Aug 18, 2018

Holy crap this released already I so can't wait to test this.
may I request an option in the debug menu to view the heat from the fire? filling the room.

So select display heat radiation and they it overlays heat icon coming from and around fire plus any where else the heat is actually going to.

PR this nowwwww I want lol.

Thanks

Copy link
Contributor

@codemime codemime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-pasted chunks of code are simply a no go. Common code should be extracted and reused as needed.

@codemime
Copy link
Contributor

Also game::get_temperature becomes computationally intensive, but it's used quite a bit. This may impact the performance.

@nexusmrsep
Copy link
Contributor Author

@codemime you are right, I will rework it so the method will have a common place in the code.
As for the second part, yes it can influence performance, any ideas for optimization other then reverting this and freezing mechanics? There is already a mechanic preventing excesive hot air fields from being added with adjacent fires and maybe it can be trimmed down even further. Direct heat radiation does the trick for the lava, without hot air emision, but here we need a way to heat up whole rooms, and there aint no better way then using fields.

@Rod995
Copy link
Contributor

Rod995 commented Aug 18, 2018

Did some testing on the Discord, and that was really fun!
image
image
image
image

The flat lines were a bit boring though, so
image

This is still confusing, btw.
image

Temperature does change, which is cool.
image

More pics of the hot air.
image
image
image

Lava doesn't output any hot air, it just radiates the heat straight towards ya.
image
image

Brazier does output air, as it should.
image

My personal preference is to make normal hot air not be seen.
image

{
        "fd_hot_air1",
        {"hot_air", "hot_air", "hot_air"}, ' ', -1,
        {def_c_white,def_c_yellow,def_c_red}, {true, true, true}, {false, false, false}, 50_minutes,
        {0,0,0},
        GAS,
        false
    },

Copy link
Contributor

@OrenAudeles OrenAudeles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some observations, possible improvement

src/game.cpp Outdated
for( const auto &intensity_dist : fires ) {
const int intensity = intensity_dist.first;
const int distance = intensity_dist.second;
temp_mod = 6 * intensity * intensity / distance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value of temp_mod on exiting this loop is always the value of 6 * fires.back().first * fires.back().first / fires.back().second and effectively ignores all other values. Is this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, should be +=. Heat radiation should sum up from multiple sources, thats the sole purpose of this loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it supposed to be 6* (intensity^2 / distance)? I think radiant heat would follow an inverse-square intensity change more closely ( 6 * (intensity / distance^2) ), dropping off rapidly with distance. A linear drop isn't an issue though, mostly concerned about the intensity being squared.

src/game.cpp Outdated
fires.emplace_back( std::make_pair( heat_intensity, fire_dist ) );
if( fire_dist <= 1 ) {
// Extend limbs/lean over a single adjacent fire to warm up
best_fire = std::max( best_fire, heat_intensity );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best_fire is set here, but is not used elsewhere in the temperature calculations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that too should be removed, it was needed in original, but not here

src/game.cpp Outdated
break;
default:
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the structure is very similar over each of the switch blocks, can make use of a lambda:

auto tile_strength_mod = []( const tripoint &loc, field_id fld, int case_1, int case_2, int case_3 ){
    int strength = g->m.get_field_strength( loc, fld );
    int cases[3] = { case_1, case_2, case_3 };
    return ( strength > 0 && strength < 4 ) ? cases[ strength - 1 ] : 0;
};

Using the lambda:

temp_mod += tile_strength_mod( location, fd_hot_air1,  2,   6,  10 );
temp_mod += tile_strength_mod( location, fd_hot_air2,  6,  16,  20 );
temp_mod += tile_strength_mod( location, fd_hot_air3, 16,  40,  70 );
temp_mod += tile_strength_mod( location, fd_hot_air4, 70, 100, 160 );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are code shenanigans I never suspected i'd be using with my C++ skill level, so I'm thankful for the idea, and of course it is a lot cleaner.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, looking at how field strength is processed don't even need the conditional. Strength will always be in the range [1..3], so both conditions will always be true.
Can change the return line to return cases[ strength - 1 ];

@OrenAudeles
Copy link
Contributor

Since game::temperature gets hit so often it might be worth it to have a temperature cache that gets reset each turn. Individual locations will often get a temperature calculated many times in a single turn (temp at player position gets hit a lot) so a cache would short-circuit the expensive calculation when the temperature shouldn't be changing anyway.

@OrenAudeles
Copy link
Contributor

There may be an odd interaction between game::get_temperature and get_rot_since in weather.cpp while fast-forwarding item rot timers for items under z-level 0. At the least game::get_temperature will be getting hit a lot in a short period of time in that case if the time being fast-forwarded through is high.

@nexusmrsep
Copy link
Contributor Author

@Rod995 Let me explain some of your observations:

My personal preference is to make normal hot air not be seen.

Of course, its visibility is just for testing purposes - imagine how hard it would be to test invisible field. In fact this PR does not have code lines that allow anyone to see hot air.

Also multiple 'hot_air' entries you saw was different types of hot air (there are 4 types with 3 lvls of intensity each) - this is original design by original author, and it does its role. small fire emits type 1 hot air, a mild hot breeze, while raging fire can emit type 3 hot air. The difference is like between a happy campfire and a fire tornado.

And fire has heat radiation too, like lava, but with mild intensity in case of a small_fire. fire and raging fire are not so benevolent in that department.

@nexusmrsep
Copy link
Contributor Author

@OrenAudeles

There may be an odd interaction between game::get_temperature and get_rot_since in weather.cpp while fast-forwarding item rot timers for items under z-level 0

Can you elaborate? Long fast-forward with temperature input affected by the fire will be a rare occurrence, and fire affected temperature applies to food, so rot calculations should include it. If not the effect would be that rot could consider food as frozen while it's sitting in a blazing fire. But perhaps it's not what you meant?

@OrenAudeles
Copy link
Contributor

@nexusmrsep Going to copy over my response from Discord

I think I was tilting at windmills there and there won't be a measurable issue. Either the hot air will decay quickly or it will become frozen in time while the player is off doing other things, making the hot air's influence at a location over the fast-forwarding section either non-existent or constant for the duration

@SilearFlare
Copy link

Question: I seem to remember one of the main reason fires used to be lag machines in the old versions was because of the long list of added checks for smoke and various other variables, which was fixed by Coolthulhu some years ago. Will this change with all its added hidden calculations make the game lag the game to hell and back like it used to?
Because it might not be worth the tradeoff.

@nexusmrsep
Copy link
Contributor Author

@tyrael93 short answer: no it shouldn't. If a smoke grenade doesn't slow your machine, this will neither. There are safeguards preventing excessive generation of smoke in large fires, and if it ever becomes a problem they can be tweaked tighter.

@SaltatorMortis
Copy link

Question: Do you plan to use Thermal conductivity as factor for passive cooling? or will closed doors "freeze" the calculation(after the Temperature spread out evenly) for simplicity?

@nexusmrsep
Copy link
Contributor Author

nexusmrsep commented Aug 19, 2018

@SaltatorMortis short answer: No. I use existing mechanics, that already had hot air coded. It was mostly not used though, but now it serves a purpose. It behaves the same way as smoke both inside and outside houses, so it's rather gas spread mechanics then thermal dissipation, but overall, at this level of simulation its decent enough to use it, without implementing NASA style code that would make your CPU sweat.

@Mecares
Copy link
Contributor

Mecares commented Aug 20, 2018

Might be not related enough to make the change in this PR but could you take a look at Lava it should logically at least spread fire like a raging fire around it but does not seem to do so?

@nexusmrsep
Copy link
Contributor Author

@Mecares that might be a topic for another PR, since I guess you don't refer to simple heat radiation. Mind you, this could in fact lead to some nasty fires, but perhaps would be a nice addition. Make a feature request issue on it.

@nexusmrsep
Copy link
Contributor Author

nexusmrsep commented Aug 20, 2018

@codemime your request is applied. Major reworks done. Also thanks @OrenAudeles for your invaluable help.

  • TODO: balancing blisters. they use newly created functions, so I had to rework them slightly, and in consequence I've got to get the new balance right, including things like Thermal Dissipation CBM and turnout gear.

Copy link
Contributor

@OrenAudeles OrenAudeles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class game doesn't need more functions that can easily be made free functions. Move the functions game::get_heat_radiation and game::get_convection_temperature out of class game. They can stay in game.h for now, and can stay implemented in game.cpp they just shouldn't be members functions.

src/game.h Outdated
// @param location Location affected by heat sources
// @param direct forces return of heat intensity (and not temperature modifier) of
// adjacent hottest heat source
int get_heat_radiation( const tripoint &location, bool direct );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not need to be in class game. Only uses the map m member of game. Extract from class game and modify declaration to int get_heat_radiation( const map& m, const tripoint &location, bool direct);

src/game.h Outdated
// adjacent hottest heat source
int get_heat_radiation( const tripoint &location, bool direct );
// Returns temperature modifier from hot air fields of given location
int get_convection_temperature( const tripoint &location );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oes not need to be in class game. Only uses the map m member of game. Extract from class game and modify declaration to int get_convection_temperature( const map& m, const tripoint &location );

std::vector<std::pair<int, int>> fires;
fires.reserve( 13 * 13 );
int best_fire = 0;
for( const tripoint &dest : g->m.points_in_radius( location, 6 ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether function is moved from class game to be a free function, or remains as is, can shorten all instances of g->m.__func__ to just m.__func__
Other lines impacted:

  • 1804
  • 1807
  • 1810

Function is only called currently as g->get_heat_radiation so it already has access to the global-g's map.

src/game.cpp Outdated
{
// Heat from hot air (fields)
int temp_mod = 0;
const trap &trap_at_pos = g->m.tr_at( location );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether function is moved from class game to be a free function, or remains as is, can shorten all instances of g->m.__func__ to just m.__func__
Other lines impacted:

  • 1840
  • 1846

Function is only called currently as g->get_convection_temperature so it already has access to the global-g's map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's freed from game class it losses access to m which is member of this class so g->m within free functions have to stay.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a map-reference is added as a parameter to the free function you can get rid of the g-> part. You are correct that if the parameter isn't added g->m is the only way you will keep access to the map.

}
// hot air of a fire/lava
auto tile_strength_mod = []( const tripoint &loc, field_id fld, int case_1, int case_2, int case_3 ){
int strength = g->m.get_field_strength( loc, fld );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to changing from g->m.get_field_strength to just m.get_field_strength, if the function is made a free function and its function signature modified to pass in the map may need to modify the lamda to capture the passed in map variable: [&m]( const tripoint &loc, ... ){

auto tile_strength_mod = []( const tripoint &loc, field_id fld, int case_1, int case_2, int case_3 ){
int strength = g->m.get_field_strength( loc, fld );
int cases[3] = { case_1, case_2, case_3 };
return ( strength > 0 && strength < 4 ) ? cases[ strength - 1 ] : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having looked into things more carefully the condition isn't necessary. Can be modified to just return cases[ strength - 1 ];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and it sends the game into infinite loop with insane temperatures, stamina loss, and inevitable death. No, just no...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I'm dumb. I was making an assumption and not validating that assumption. If the field does not exist at the location it's not going to be returning a value in range [1, 3]. Sorry for the bad suggestion.

src/game.cpp Outdated

if( !new_game ) {
temp_mod += g->get_heat_radiation( location, false );
temp_mod += g->get_convection_temperature( location );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If game::get_heat_radiation and game::get_convection_temperature are made into free functions change calls to get_heat_radiation( g->m, location, false ); and get_convection_temperature( g->m, location );

src/player.cpp Outdated
// Hot air from a heat source
// times 50 to convert to weather.h BODYTEMP scale
const int fire_warmth = g->get_convection_temperature( pos() ) * 50;
const int best_fire = g->get_heat_radiation( pos(), true );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If game::get_heat_radiation and game::get_convection_temperature are made into free functions change calls to get_heat_radiation( g->m, pos(), true ); and get_convection_temperature( g->m, pos() );

src/player.cpp Outdated
temp_conv[bp] += 300 * heat_here;
blister_count += heat_here;

const int h_radiation = g->get_heat_radiation( pos(), false );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If game::get_heat_radiation is made into a free function change call to get_heat_radiation( g->m, pos(), false );

@nexusmrsep
Copy link
Contributor Author

nexusmrsep commented Aug 21, 2018

@OrenAudeles does making them free functions serve as optimization purpose or is there another reason? Player.cpp also calls game class directly for both of those new functions and not in general for get_temperature(), and I can see that in the future they may be called from elsewhere too. And also: originaly half of them were player.cpp code. Since temperature now trends to be more of a game.cpp thing (and less map.cpp and weather.cpp) this is the reason why I placed it there.

@OrenAudeles
Copy link
Contributor

It's to keep the interface of class game as small as possible. There is already so very much stuffed into game that shouldn't be there, we don't need to be adding more. Free functions are in general easier to work with, since their definitions can be freely moved around to where they logically fit best and aren't required to stay within the parent object's definition. Because of this, it's possible to extend the functionality of a heavily depended upon class without modifying its interface, requiring everything depending on the class's interface to recompile.

@nexusmrsep
Copy link
Contributor Author

nexusmrsep commented Aug 24, 2018

@codemime please verify validity of your request again. Your suggestions were applied. If you don't, I'll dismiss your review as obsolete.


Due to changes of temperature application I've reworked mechanics for adding blisters from heat radiation sources, and methods of protection from that effect. To my surprise I even found a bug where environmental protection instead of fire protection from items were used to determine protection from blisters (rubber and plastic protecting from scorching heat). I believe after testing this on fires and lava that I achieved proper balance there.
However:
I have ambivalent feelings about the balance of heat application on player. Tests show that big fires in houses and lava fissures can easily kill the player via overheating, even if he is protected by turnout gear, RM13 armor with AC switched on, thermal dissipation CBM or mycus fireproofing mutation. They all protect from blisters and direct fire damage but not one bit from overheating. Before this change it was not so obvious, but because now heat radiation and hot air apply to both player and tiles, this stands out much more, making wild raging fires and lava fissures a potentially deadly trap.

What happens is that even if you are protected from blisters/fire, you will overheat rapidly (near big fire/lava), losing speed, stamina, and looping into a kind of unconscious/coma state in which time passes and you cannot move while being (probably) still affected by the temperature source. With RM13 i was even able to survive the dying out of the fire and lot of time passed before regaining control was even possible (then some zombies ate me).

Problem is - I'm not even sure IF it needs balancing, so I'm pointing this out to the public. This is either intended or partially intended - while I believe its realistic, it might need balancing or might not. Can't make this decision myself.

Steps to recreate: /testing environment/

  • spawn RM13 armor with fuel, wear, activate ALTERNATIVE: no gear, turnout gear, thermal dissipation CBM, mycus fireproofing
  • spawn thermometer
  • set building on fire, ALTERNATIVE: place t_lava tiles on map (create a lava fissure) (small fires don't matter here, you want blazing heat, multiple sources)
  • approach / get some distance from fire/lava, ALT: try crossing through fire/lava
  • observe temperature on thermometer
  • observe body temperature & speed & pain & effects in @-menu & stamina
  • look for blister effects (with and without gear)

@nexusmrsep nexusmrsep dismissed codemime’s stale review August 26, 2018 07:29

Obsolete. Requested changes were applied.

@nexusmrsep nexusmrsep changed the title [WIP] Hot air from fires and direct heat radiation applies to tile temperatures Hot air from fires and direct heat radiation applies to tile temperatures Aug 26, 2018
Copy link
Contributor

@OrenAudeles OrenAudeles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shoulda modified my review earlier, sorry. I have no more issues.

@ZhilkinSerg ZhilkinSerg removed their assignment Aug 27, 2018
@ZhilkinSerg ZhilkinSerg merged commit 4205ba5 into CleverRaven:master Aug 27, 2018
@nexusmrsep nexusmrsep deleted the hot_air_to_temperature branch August 27, 2018 10:14
@Brambor
Copy link
Contributor

Brambor commented Aug 27, 2018

Question: Can more than one hot air of the same type be on one tile? E.g. 2x hot_air_3, not hot_air_3 and hot_air_2. If so, then:

Question: Does raging_fire create hot air 1, 2, 3 OR just 3? If not, then having a small fire in addition to raging fire allows more hot air types to be on one tile. Whereas having two raging fires will only produce hot_air_3. Thus air will be hotter when you have a small fire and a raging fire opposed to 2xraging fire.

This would be true even if hot_air_3 turns to hot_air_2 after cooling, as there will be only hot_air_3 on every tile around many, many raging fires and there will be hot_air_3 and hot_air_1 on every tile around many, many raging fires and small fires.
I'm just outlining the possibility, don't drag the optimization of maximum amout of fires or something into this.

@nexusmrsep
Copy link
Contributor Author

@Brambor to my knowledge:

  • only one field of the same type can occupy a tile, but each can be of 3 types of intensity,
  • raging fire creates type 3 hot air,
  • there is no decay between field types, so type 3 intensity 1 hot air will not cool down to type 2 intensity 3 hot air
  • temperature from each field type sums up for overall tile temperature modificator.

I was expecting decay of higher type fields of hot air to lesser types to be there, but its apparently not coded. While it'd be fun, I don't think I'll do it. Perhaps someone else would.

@Brambor
Copy link
Contributor

Brambor commented Aug 27, 2018

If you are accepting my issue, I'm really proposing a possible bug, the solution to it would be that raging fire would create all three types of air, regular fire would make two and small fire one.
I see the problem, that it could change the balance, but it would be rebalanced if current types (+2 /+6 /+10) would change to (+2 /+4 /+4) as stacking them would produce the same result.
Then this is more CPU heavy, so it is not an ideal solution.

@nexusmrsep
Copy link
Contributor Author

No, i do not recognize this as an issue/bug. Testing did not prove that anything out of ordinary happens because of that. At this level of simulation there is no need to change that, unless someone determined wants to have a try. I'm not determined enough to fix problems that do not add any substantial value to the simulation.

@ZhilkinSerg
Copy link
Contributor

I suggest waiting for feedback from various players for several days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C++] Changes (can be) made in C++. Previously named `Code` Fields / Furniture / Terrain / Traps Objects that are part of the map or its features. Game: Mechanics Change Code that changes how major features work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hot air from a fire should influence measured temperature and may prevent food from freezing
10 participants