Skip to content

Commit

Permalink
refactor: fahrenheit to celsius in codebase, part 1 (#2570)
Browse files Browse the repository at this point in the history
* refactor: constexpr for `TOOL_LIFT_FACTOR` and `JACK_LIMIT`

* feat: add long double override for celsius literal

* refactor: use celsius on namespace `temperatures`

* refactor: `average_annual_termperature` to `units::temperature`

* refactor: use `_seconds` notation

* refactor: use early return for `weather_effect::acid`

* refactor: `calc_hourly_rotpoints_at_temp` to accept `units::temperature`

please forgive me for shilling SI unit this much.

Hardcoded as changing to celsius reduces array size a lot,
and i've spent 6h unsuccessfully porting this calc_hourly_rotpoints_at_temp to work with celsius

* fix: make annual average namespaced

* fix: remove `SPRING_TEMPERATURE`

already managed by `weather_manager::update_weather`

* refactor: use early return

* fix: redundant comment

---------

Co-authored-by: olanti-p <[email protected]>
  • Loading branch information
scarf005 and olanti-p authored Sep 21, 2023
1 parent 134c3b5 commit 0085ef6
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 153 deletions.
1 change: 0 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,6 @@ bool game::start_game()
}
u.process_turn(); // process_turn adds the initial move points
u.set_stamina( u.get_stamina_max() );
get_weather().temperature = SPRING_TEMPERATURE;
get_weather().update_weather();
u.next_climate_control_check = calendar::before_time_starts; // Force recheck at startup
u.last_climate_control_ret = false;
Expand Down
41 changes: 16 additions & 25 deletions src/game_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,34 +78,37 @@ static constexpr int PLUTONIUM_CHARGES = 500;
// Temperature constants.
namespace temperatures
{

/// Average annual temperature used for climate, weather and temperature calculation.
constexpr units::temperature annual_average = 6_c;

// temperature at which something starts is considered HOT.
constexpr units::temperature hot = 100_f; // ~ 38 Celsius
constexpr units::temperature hot = 38_c;

// the "normal" temperature midpoint between cold and hot.
constexpr units::temperature normal = 70_f; // ~ 21 Celsius
constexpr units::temperature normal = 21_c;

// Temperature inside an active fridge in Fahrenheit.
constexpr units::temperature fridge = 37_f; // ~ 2.7 Celsius
// Temperature inside an active fridge
constexpr units::temperature fridge = 2_c;

// Temperature at which things are considered "cold".
constexpr units::temperature cold = 40_f; // ~4.4 C
constexpr units::temperature cold = 5_c;

// Temperature inside an active freezer in Fahrenheit.
constexpr units::temperature freezer = 23_f; // -5 Celsius
// Temperature inside an active freezer.
constexpr units::temperature freezer = -5_c;

// Temperature in which water freezes in Fahrenheit.
constexpr units::temperature freezing = 32_f; // 0 Celsius
// Temperature in which water freezes.
constexpr units::temperature freezing = 0_c;

// Arbitrary constant for root cellar temperature
// Should be equal to AVERAGE_ANNUAL_TEMPERATURE, but is declared before it...
constexpr units::temperature root_cellar = 43_f;
constexpr units::temperature root_cellar = annual_average;
} // namespace temperatures

// Weight per level of LIFT/JACK tool quality.
#define TOOL_LIFT_FACTOR 500_kilogram // 500kg/level
static constexpr units::mass TOOL_LIFT_FACTOR = 500_kilogram; // 500kg/level

// Cap JACK requirements to support arbitrarily large vehicles.
#define JACK_LIMIT 8500_kilogram // 8500kg ( 8.5 metric tonnes )
static constexpr units::mass JACK_LIMIT = 8500_kilogram;

// Slowest speed at which a gun can be aimed.
static constexpr int MAX_AIM_COST = 10;
Expand Down Expand Up @@ -139,18 +142,6 @@ static constexpr int BIO_CQB_LEVEL = 5;
// Minimum size of a horde to show up on the minimap.
static constexpr int HORDE_VISIBILITY_SIZE = 3;

/**
* Average annual temperature in F used for climate, weather and temperature calculation.
* Average New England temperature = 43F/6C rounded to int.
*/
static constexpr int AVERAGE_ANNUAL_TEMPERATURE = 43;

/**
* Base starting spring temperature in F used for climate, weather and temperature calculation.
* New England base spring temperature = 65F/18C rounded to int.
*/
static constexpr int SPRING_TEMPERATURE = 65;

/**
* Used to limit the random seed during noise calculation. A large value flattens the noise generator to zero.
* Windows has a rand limit of 32768, other operating systems can have higher limits.
Expand Down
111 changes: 44 additions & 67 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
#include "text_snippets.h"
#include "translations.h"
#include "units.h"
#include "units_temperature.h"
#include "units_utility.h"
#include "value_ptr.h"
#include "vehicle.h"
Expand Down Expand Up @@ -5610,78 +5611,57 @@ int item::spoilage_sort_order() const
return bottom;
}

namespace
{

/**
* Food decay calculation.
* Calculate how much food rots per hour, based on 10 = 1 minute of decay @ 65 F.
* Hardcoded lookup table for food rots per hour calculation.
*
* IRL this tends to double every 10c a few degrees above freezing, but past a certain
* point the rate decreases until even extremophiles find it too hot. Here we just stop
* further acceleration at 105 F. This should only need to run once when the game starts.
* @see calc_rot_array
* @see rot_chart
* further acceleration at 40C.
*
* Original formula:
* @see https://github.com/cataclysmbnteam/Cataclysm-BN/blob/033901af4b52ad0bfcfd6abfe06bca4e403d44b1/src/item.cpp#L5612-L5640
*/
static int calc_hourly_rotpoints_at_temp( const int temp )
constexpr auto rot_chart = std::array<int, 44>
{
// default temp = 65, so generic->rotten() assumes 600 decay points per hour
const int dropoff = 38; // ditch our fancy equation and do a linear approach to 0 rot at 31f
const int cutoff = 105; // stop torturing the player at this temperature, which is
const int cutoffrot = 21240; // ..almost 6 times the base rate. bacteria hate the heat too

const int dsteps = dropoff - units::to_fahrenheit( temperatures::freezing );
const int dstep = ( 215.46 * std::pow( 2.0, static_cast<float>( dropoff ) / 16.0 ) / dsteps );

if( temp < units::to_fahrenheit( temperatures::freezing ) ) {
return 0;
} else if( temp > cutoff ) {
return cutoffrot;
} else if( temp < dropoff ) {
return ( temp - units::to_fahrenheit( temperatures::freezing ) ) * dstep;
} else {
return std::lround( 215.46 * std::pow( 2.0, static_cast<float>( temp ) / 16.0 ) );
}
}
0, 372, 744, 1118, 1219, 1273, 1388, 1514, 1651, 1800,
1880, 2050, 2235, 2438, 2658, 2776, 3027, 3301, 3600, 3926,
4100, 4471, 4875, 5317, 5798, 6054, 6602, 7200, 7852, 8562,
8941, 9751, 10633, 11595, 12645, 13205, 14400, 15703, 17125, 18674,
19501,
};

/**
* Initialize the rot table.
* @see rot_chart
*/
static std::vector<int> calc_rot_array( const size_t cap )
{
std::vector<int> ret;
ret.reserve( cap );
for( size_t i = 0; i < cap; ++i ) {
ret.push_back( calc_hourly_rotpoints_at_temp( static_cast<int>( i ) ) );
}
return ret;
}
} // namespace

/**
* Get the hourly rot for a given temperature from the precomputed table.
* @see rot_chart
*/
int get_hourly_rotpoints_at_temp( const int temp )
auto get_hourly_rotpoints_at_temp( const units::temperature temp ) -> int
{
/**
* Precomputed rot lookup table.
*/
static const std::vector<int> rot_chart = calc_rot_array( 200 );

if( temp < 0 ) {
if( temp < temperatures::freezing ) {
return 0;
}
if( temp > 150 ) {
if( temp > 40_c ) {
return 21240;
}
return rot_chart[temp];
const int temp_c = units::to_celsius( temp );
return rot_chart[temp_c];
}

time_duration item::calc_rot( time_point time, int temp ) const
auto item::calc_rot( time_point time, const units::temperature temp ) const -> time_duration
{
// Avoid needlessly calculating already rotten things. Corpses should
// always rot away and food rots away at twice the shelf life. If the food
// is in a sealed container they won't rot away, this avoids needlessly
// calculating their rot in that case.
if( !is_corpse() && get_relative_rot() > 2.0 ) {
return time_duration::from_seconds( 0 );
return 0_seconds;
}

// rot modifier
Expand All @@ -5690,7 +5670,7 @@ time_duration item::calc_rot( time_point time, int temp ) const
factor = 0.75;
}

time_duration added_rot = time_duration::from_seconds( 0 );
time_duration added_rot = 0_seconds;
// simulation of different age of food at the start of the game and good/bad storage
// conditions by applying starting variation bonus/penalty of +/- 20% of base shelf-life
// positive = food was produced some time before calendar::start and/or bad storage
Expand All @@ -5699,34 +5679,38 @@ time_duration item::calc_rot( time_point time, int temp ) const
time_duration spoil_variation = get_shelf_life() * 0.2f;
added_rot += rng( -spoil_variation, spoil_variation );
}

time_duration time_delta = time - last_rot_check;
added_rot += factor * time_delta / 1_hours * get_hourly_rotpoints_at_temp( temp ) * 1_turns;
return added_rot;
}

static int temperature_flag_to_highest_temperature( temperature_flag temperature )
namespace
{

auto temperature_flag_to_highest_temperature( temperature_flag temperature ) -> units::temperature
{
switch( temperature ) {
case temperature_flag::TEMP_NORMAL:
return INT_MAX;
case temperature_flag::TEMP_HEATER:
return INT_MAX;
return units::temperature_max;
case temperature_flag::TEMP_FRIDGE:
return to_fahrenheit( temperatures::fridge );
return temperatures::fridge;
case temperature_flag::TEMP_FREEZER:
return to_fahrenheit( temperatures::freezer );
return temperatures::freezer;
case temperature_flag::TEMP_ROOT_CELLAR:
return to_fahrenheit( temperatures::root_cellar );
return temperatures::root_cellar;
}

return INT_MAX;
return units::temperature_max;
}

} // namespace


time_duration item::minimum_freshness_duration( temperature_flag temperature ) const
{
int temperature_f = temperature_flag_to_highest_temperature( temperature );
unsigned long long rot_per_hour = get_hourly_rotpoints_at_temp( temperature_f );
const units::temperature temp = temperature_flag_to_highest_temperature( temperature );
unsigned long long rot_per_hour = get_hourly_rotpoints_at_temp( temp );

if( rot_per_hour <= 0 || !type->comestible ) {
return calendar::INDEFINITELY_LONG_DURATION;
Expand Down Expand Up @@ -9008,7 +8992,7 @@ bool item::process_rot( const bool seals, const tripoint &pos,

// process rot at most once every 100_turns (10 min)
// note we're also gated by item::processing_speed
time_duration smallest_interval = 10_minutes;
constexpr time_duration smallest_interval = 10_minutes;

units::temperature temp = units::from_fahrenheit( weather.get_temperature( pos ) );
temp = clip_by_temperature_flag( temp, flag );
Expand Down Expand Up @@ -9040,17 +9024,13 @@ bool item::process_rot( const bool seals, const tripoint &pos,
calendar::config, seed );
env_temperature_raw = weather_temperature + local_mod;
} else {
env_temperature_raw = units::from_fahrenheit( AVERAGE_ANNUAL_TEMPERATURE ) + local_mod;
env_temperature_raw = temperatures::annual_average + local_mod;
}

units::temperature env_temperature_clipped = clip_by_temperature_flag( env_temperature_raw, flag );

// Lookup table is in F
int final_temperature_in_fahrenheit = static_cast<int>( std::round( units::to_fahrenheit<float>
( env_temperature_clipped ) ) );

// Calculate item rot
rot += calc_rot( time, final_temperature_in_fahrenheit );
rot += calc_rot( time, env_temperature_clipped );
last_rot_check = time;

if( has_rotten_away() && carrier == nullptr && !seals ) {
Expand All @@ -9063,10 +9043,7 @@ bool item::process_rot( const bool seals, const tripoint &pos,
// Remaining <1 h from above
// and items that are held near the player
if( now - time > smallest_interval ) {
int final_temperature_in_fahrenheit = static_cast<int>( std::round( units::to_fahrenheit<float>
( temp ) ) );

rot += calc_rot( now, final_temperature_in_fahrenheit );
rot += calc_rot( now, temp );
last_rot_check = now;

return has_rotten_away() && carrier == nullptr && !seals;
Expand Down
2 changes: 1 addition & 1 deletion src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ class item : public visitable<item>
* @param time Time point to which rot is calculated
* @param temp Temperature at which the rot is calculated
*/
time_duration calc_rot( time_point time, int temp ) const;
auto calc_rot( time_point time, const units::temperature temp ) const -> time_duration;

/**
* Time that this item is guaranteed to stay fresh.
Expand Down
2 changes: 1 addition & 1 deletion src/timed_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ void timed_event::per_turn()
}
}

if( calendar::once_every( time_duration::from_seconds( 10 ) ) && faults ) {
if( calendar::once_every( 10_seconds ) && faults ) {
add_msg( m_info, "You hear someone whispering \"%s\"",
SNIPPET.random_from_category( "amigara_whispers" ).value_or( translation() ) );
}
Expand Down
5 changes: 5 additions & 0 deletions src/units_temperature.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ inline constexpr units::temperature operator"" _c( const unsigned long long v )
return units::from_celsius<int>( v );
}

inline constexpr units::temperature operator"" _c( const long double v )
{
return units::from_celsius<double>( static_cast<double>( v ) );
}

inline constexpr units::temperature operator"" _f( const unsigned long long v )
{
return units::from_fahrenheit<int>( v );
Expand Down
Loading

0 comments on commit 0085ef6

Please sign in to comment.