Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| #include "weather_gen.h" | |
| #include "weather.h" | |
| #include "enums.h" | |
| #include "calendar.h" | |
| #include "simplexnoise.h" | |
| #include "json.h" | |
| #include <cmath> | |
| #include <fstream> | |
| #include <cstdlib> | |
| namespace | |
| { | |
| // GCC doesn't like M_PI here for some reason | |
| constexpr double PI = 3.141592653589793238463; | |
| constexpr double tau = 2 * PI; | |
| } //namespace | |
| weather_generator::weather_generator() = default; | |
| w_point weather_generator::get_weather( const tripoint &location, const calendar &t, | |
| unsigned seed ) const | |
| { | |
| const double x( location.x / | |
| 2000.0 ); // Integer x position / widening factor of the Perlin function. | |
| const double y( location.y / | |
| 2000.0 ); // Integer y position / widening factor of the Perlin function. | |
| const double z( double( t.get_turn() + to_turns<int>( calendar::season_length() ) ) / | |
| 2000.0 ); // Integer turn / widening factor of the Perlin function. | |
| const double dayFraction = time_past_midnight( t ) / 1_days; | |
| //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 | |
| const unsigned modSEED = seed % 32768; | |
| // Noise factors | |
| double T( raw_noise_4d( x, y, z, modSEED ) * 4.0 ); | |
| double H( raw_noise_4d( x, y, z / 5, modSEED + 101 ) ); | |
| double H2( raw_noise_4d( x, y, z, modSEED + 151 ) / 4 ); | |
| double P( raw_noise_4d( x, y, z / 3, modSEED + 211 ) * 70 ); | |
| double A( raw_noise_4d( x, y, z, modSEED ) * 8.0 ); | |
| double W; | |
| const double now( double( t.turn_of_year() + to_turns<int>( calendar::season_length() ) / 2 ) / | |
| to_turns<int>( calendar::year_length() ) ); // [0,1) | |
| const double ctn( cos( tau * now ) ); | |
| // Temperature variation | |
| const double mod_t( 0 ); // TODO: make this depend on latitude and altitude? | |
| const double current_t( base_temperature + | |
| mod_t ); // Current baseline temperature. Degrees Celsius. | |
| const double seasonal_variation( ctn * -1 ); // Start and end at -1 going up to 1 in summer. | |
| const double season_atenuation( ctn / 2 + 1 ); // Harsh winter nights, hot summers. | |
| const double season_dispersion( pow( 2, | |
| ctn + 1 ) - 2.3 ); // Make summers peak faster and winters not perma-frozen. | |
| const double daily_variation( cos( tau * dayFraction - tau / 8 ) * -1 * season_atenuation / 2 + | |
| season_dispersion * -1 ); // Day-night temperature variation. | |
| T += current_t; // Add baseline to the noise. | |
| T += seasonal_variation * 8 * exp( -pow( current_t * 2.7 / 10 - 0.5, | |
| 2 ) ); // Add season curve offset to account for the winter-summer difference in day-night difference. | |
| T += daily_variation * 8 * exp( -pow( current_t / 30, | |
| 2 ) ); // Add daily variation scaled to the inverse of the current baseline. A very specific and finicky adjustment curve. | |
| T = T * 9 / 5 + 32; // Convert to imperial. =| | |
| // Humidity variation | |
| const double mod_h( 0 ); | |
| const double current_h( base_humidity + mod_h ); | |
| H = std::max( std::min( ( ctn / 10.0 + ( -pow( H, 2 ) * 3 + H2 ) ) * current_h / 2.0 + current_h, | |
| 100.0 ), | |
| 0.0 ); // Humidity stays mostly at the mean level, but has low peaks rarely. It's a percentage. | |
| // Pressure variation | |
| P += seasonal_variation * 20 + | |
| base_pressure; // Pressure is mostly random, but a bit higher on summer and lower on winter. In millibars. | |
| // Wind power | |
| W = std::max( 0, 1020 - ( int )P ); | |
| // Acid rains | |
| const double acid_content = base_acid * A; | |
| bool acid = acid_content >= 1.0; | |
| return w_point {T, H, P, W, acid}; | |
| } | |
| weather_type weather_generator::get_weather_conditions( const tripoint &location, | |
| const calendar &t, unsigned seed ) const | |
| { | |
| w_point w( get_weather( location, t, seed ) ); | |
| weather_type wt = get_weather_conditions( w ); | |
| // Make sure we don't say it's sunny at night! =P | |
| if( wt == WEATHER_SUNNY && t.is_night() ) { | |
| return WEATHER_CLEAR; | |
| } | |
| return wt; | |
| } | |
| weather_type weather_generator::get_weather_conditions( const w_point &w ) const | |
| { | |
| weather_type r( WEATHER_CLEAR ); | |
| if( w.pressure > 1030 && w.humidity < 70 ) { | |
| r = WEATHER_SUNNY; | |
| } | |
| if( w.pressure < 1030 && w.humidity > 40 ) { | |
| r = WEATHER_CLOUDY; | |
| } | |
| if( r == WEATHER_CLOUDY && ( w.humidity > 60 || w.pressure < 1010 ) ) { | |
| r = WEATHER_DRIZZLE; | |
| } | |
| if( r == WEATHER_DRIZZLE && ( w.humidity > 70 || w.pressure < 1000 ) ) { | |
| r = WEATHER_RAINY; | |
| } | |
| if( r == WEATHER_RAINY && w.pressure < 985 ) { | |
| r = WEATHER_THUNDER; | |
| } | |
| if( r == WEATHER_THUNDER && w.pressure < 970 ) { | |
| r = WEATHER_LIGHTNING; | |
| } | |
| if( w.temperature <= 32 ) { | |
| if( r == WEATHER_DRIZZLE ) { | |
| r = WEATHER_FLURRIES; | |
| } else if( r > WEATHER_DRIZZLE ) { | |
| r = WEATHER_SNOW; | |
| } else if( r > WEATHER_THUNDER ) { | |
| r = WEATHER_SNOWSTORM; | |
| } | |
| } | |
| if( r == WEATHER_DRIZZLE && w.acidic ) { | |
| r = WEATHER_ACID_DRIZZLE; | |
| } | |
| if( r > WEATHER_DRIZZLE && w.acidic ) { | |
| r = WEATHER_ACID_RAIN; | |
| } | |
| return r; | |
| } | |
| int weather_generator::get_water_temperature() const | |
| { | |
| /** | |
| WATER TEMPERATURE | |
| source : http://echo2.epfl.ch/VICAIRE/mod_2/chapt_5/main.htm | |
| source : http://www.grandriver.ca/index/document.cfm?Sec=2&Sub1=7&sub2=1 | |
| **/ | |
| int season_length = to_days<int>( calendar::season_length() ); | |
| int day = calendar::turn.day_of_year(); | |
| int hour = hour_of_day<int>( calendar::turn ); | |
| int water_temperature = 0; | |
| if( season_length == 0 ) { | |
| season_length = 1; | |
| } | |
| // Temperature varies between 33.8F and 75.2F depending on the time of year. Day = 0 corresponds to the start of spring. | |
| int annual_mean_water_temperature = 54.5 + 20.7 * sin( tau * ( day - season_length * 0.5 ) / | |
| ( season_length * 4.0 ) ); | |
| // Temperature varies between +2F and -2F depending on the time of day. Hour = 0 corresponds to midnight. | |
| int daily_water_temperature_varaition = 2.0 + 2.0 * sin( tau * ( hour - 6.0 ) / 24.0 ); | |
| water_temperature = annual_mean_water_temperature + daily_water_temperature_varaition; | |
| return water_temperature; | |
| } | |
| void weather_generator::test_weather() const | |
| { | |
| // Outputs a Cata year's worth of weather data to a CSV file. | |
| // Usage: | |
| //@todo: this is wrong. weather_generator does not have such a constructor | |
| // weather_generator WEATHERGEN(0); // Seeds the weather object. | |
| // WEATHERGEN.test_weather(); // Runs this test. | |
| std::ofstream testfile; | |
| testfile.open( "weather.output", std::ofstream::trunc ); | |
| testfile << "turn,temperature(F),humidity(%),pressure(mB)" << std::endl; | |
| const time_point begin = calendar::turn; | |
| const time_point end = begin + 2 * calendar::year_length(); | |
| for( time_point i = begin; i < end; i += 200_turns ) { | |
| //@todo: a new random value for each call to get_weather? Is this really intended? | |
| w_point w = get_weather( tripoint( 0, 0, 0 ), to_turn<int>( i ), rand() ); | |
| testfile << to_turn<int>( i ) << "," << w.temperature << "," << w.humidity << "," << w.pressure << | |
| std::endl; | |
| } | |
| } | |
| weather_generator weather_generator::load( JsonObject &jo ) | |
| { | |
| weather_generator ret; | |
| ret.base_temperature = jo.get_float( "base_temperature", 6.5 ); | |
| ret.base_humidity = jo.get_float( "base_humidity", 66.0 ); | |
| ret.base_pressure = jo.get_float( "base_pressure", 1015.0 ); | |
| ret.base_acid = jo.get_float( "base_acid", 1015.0 ); | |
| return ret; | |
| } |