/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
// Filename : OWEATHER.CPP
// Description : class Weather
// Ownership : Gilbert
#include
#include
#include
#include
//---------- Define constant -----------//
#define RAIN_CLOUD 1
#define LIGHTNING_CLOUD 2
#define WINDY 4
#define HOT_WAVE 8
#define COLD_WAVE 0x10
#define M_PI 3.14159265359
//-------- Begin of function Lightning::rand_seed ----------//
unsigned Weather::rand_seed(unsigned max)
{
#define MULTIPLIER 0x015a4e35L
#define INCREMENT 1
seed = MULTIPLIER * seed + INCREMENT;
return seed % max;
}
//-------- End of function Lightning::rand_seed ----------//
//---------- Begin of function Weather::init_date ----------//
//
void Weather::init_date(short year, short month, short day, short latitude, int quakeFreq)
{
// ----------- initialize random seed
seed = 2*year+1;
(void) rand_seed(10);
// ----------- calculate season_phase from month, day ------------//
season_phase = (short) ( month * 30.4 + day);
season_phase = (season_phase + 365 - 98) % 365; // 7th Mar becomes 0
// ----------- random number to earthquake -----------//
quake_frequency = quakeFreq;
day_to_quake = quakeFreq + rand_seed(quakeFreq);
// ----------- determine avg_temp and temp_amp from latitude
double angle = latitude * M_PI / 180.0;
avg_temp = (short)( 35.0 - fabs(latitude / 90.0 * 40.0));
temp_amp = (short)( 17.0 * sin(angle)); // negative for South Hemisphere
// ----------- determine cloud ----------- //
cur_cloud_str = rand_seed(4);
cur_cloud_len = 5 + rand_seed(5);
cur_cloud_type = 0;
// ----------- determine wind ---------------//
wind_dir = rand_seed(360);
wind_spd = 10;
high_wind_day = 0;
windy_speed = 0;
tornado_count = 1;
}
//---------- End of function Weather::init_date ----------//
//---------- Begin of function Weather::next_day ----------//
//
void Weather::next_day()
{
season_phase = (season_phase + 1 )% 365;
//---------- update/determine earthquake day ---------//
if( day_to_quake)
{
day_to_quake--;
if ( is_quake() )
{
// generate quake_x, quake_y
quake_x = rand_seed(0x10000) * MAX_MAP_WIDTH / 0x10000;
quake_y = rand_seed(0x10000) * MAX_MAP_HEIGHT / 0x10000;
}
}
else
day_to_quake = quake_frequency + rand_seed(quake_frequency);
//---------- update wind ----------//
wind_dir = (wind_dir + rand_seed(5) ) % 360;
wind_spd += rand_seed(9) - 4 - (high_wind_day/16);
if( wind_spd < -10)
wind_spd = -10;
if( wind_spd > 110)
wind_spd = 110;
if( wind_spd >= 20 )
{
high_wind_day++;
}
else
{
high_wind_day--;
}
//---------- generate cloud --------//
if( cur_cloud_len > 0)
{
cur_cloud_len--;
}
else
{
short t = base_temp();
short maxCloudStr;
if( t >= 30)
maxCloudStr = 10;
else if( t <= 18)
maxCloudStr = 4;
else
maxCloudStr = (t -18)/2 + 4;
cur_cloud_str = rand_seed(maxCloudStr+4)-3; // range : -2 to maxCloudStr
if(cur_cloud_str < 0)
cur_cloud_str = 0;
cur_cloud_len = 2 + rand_seed(3) + rand_seed(3);
cur_cloud_type = 0;
// ------- summer weather
if( cur_cloud_str > 4)
{
if( (char) rand_seed(10) < cur_cloud_str )
cur_cloud_type |= RAIN_CLOUD;
if( cur_cloud_str >= 6 && (char) rand_seed(10) < cur_cloud_str-4)
cur_cloud_type |= WINDY;
}
if( cur_cloud_str <= 1 && t >= 30 && rand_seed(10) <= 1)
{
cur_cloud_type |= HOT_WAVE;
}
// ------- winter weather
if( t < 15)
{
if( rand_seed(20) < 2 )
cur_cloud_type |= COLD_WAVE;
if( t >= 10 && rand_seed(10) < 3)
cur_cloud_type |= WINDY;
if( t < 10 && rand_seed(10) < 7)
cur_cloud_type |= WINDY;
}
if( cur_cloud_type & WINDY)
windy_speed = 10 + cur_cloud_str * 5 + rand_seed(2*cur_cloud_str+1);
else
{
windy_speed = 0;
if( cur_cloud_str > 4 && (char)rand_seed(50) < cur_cloud_str + 2 )
cur_cloud_type |= LIGHTNING_CLOUD;
}
// ---- double the time of snow ------ //
if( snow_scale() )
cur_cloud_len += cur_cloud_len;
}
// -------- update tornado_count, at least 20 days between two tornadoes -------//
if( tornado_count > 20 && base_temp() >= 30 && wind_speed() >= 40
&& rand_seed(10)==0 )
{
tornado_count = 0; // today has a tornado
}
else
{
tornado_count++;
}
}
//---------- End of function Weather::next_day ----------//
//---------- Begin of function Weather::base_temp ----------//
//
short Weather::base_temp()
{
return( (short)(avg_temp + temp_amp * sin(season_phase / 365.0
* 2 * M_PI) ));
}
//---------- End of function Weather::base_temp ----------//
//---------- Begin of function Weather::cloud ----------//
//
short Weather::cloud()
{
if( cur_cloud_str < 0)
return 0;
if( cur_cloud_str > 10)
return 10;
return cur_cloud_str;
}
//---------- End of function Weather::cloud ----------//
//---------- Begin of function Weather::temp_c ----------//
//
short Weather::temp_c()
{
return base_temp() - (cur_cloud_str < 1 ? 0 : (cur_cloud_str < 4 ? 2:4)) +
(cur_cloud_type & HOT_WAVE ? 8:0) - (cur_cloud_type & COLD_WAVE ? 10:0);
}
//---------- End of function Weather::temp_c ----------//
//---------- Begin of function Weather::temp_f ----------//
//
short Weather::temp_f()
{
return ((short) (base_temp() / 5.0 * 9.0 + 32.0 ));
}
//---------- End of function Weather::temp_f ----------//
//---------- Begin of function Weather::wind_speed ----------//
//
short Weather::wind_speed()
{
if( this == &weather && magic_weather.wind_day > 0)
return magic_weather.wind_speed();
short w = wind_spd + windy_speed;
if(w < 0)
return 0;
if(w > 100)
return 100;
return w;
}
//---------- End of function Weather::wind_speed ----------//
//---------- Begin of function Weather::wind_direct ----------//
//
short Weather::wind_direct()
{
if( this == &weather && magic_weather.wind_day > 0)
return magic_weather.wind_direct();
return wind_dir;
}
//---------- End of function Weather::wind_direct ----------//
//---------- Begin of function Weather::wind_direct_rad ----------//
//
double Weather::wind_direct_rad()
{
if( this == &weather && magic_weather.wind_day > 0)
return magic_weather.wind_direct_rad();
return wind_dir * M_PI / 180.0;
}
//---------- End of function Weather::wind_direct ----------//
//---------- Begin of function Weather::rain_scale ----------//
//
short Weather::rain_scale()
{
if( this == &weather && magic_weather.rain_day > 0)
return magic_weather.rain_scale();
return cur_cloud_str > 4 ? cur_cloud_str * 2 -8 : 0;
}
//---------- End of function Weather::rain_scale ----------//
//---------- Begin of function Weather::snow_scale ----------//
//
short Weather::snow_scale()
{
short t = temp_c();
if( t > 0)
return 0;
if( t <= -15)
{
if( t <= -30)
return 8;
if( t <= -25)
return 7;
if( t <= -20)
return 6;
return 5;
}
else
{
if(t <= -10)
return 4;
if( t <= -5)
return 3;
if( t <= -2)
return 2;
return 1;
}
}
//---------- End of function Weather::snow_scale ----------//
//---------- Begin of function Weather::is_lightning ----------//
//
char Weather::is_lightning()
{
if( magic_weather.lightning_day > 0 )
return LIGHTNING_CLOUD;
return( cur_cloud_type & LIGHTNING_CLOUD );
}
//---------- End of function Weather::is_lightning ----------//
//---------- Begin of function Weather::is_quake ----------//
//
char Weather::is_quake()
{
return( day_to_quake == 0);
}
//---------- End of function Weather::is_quake ----------//
//---------- Begin of function Weather::desc ---------//
//
WeatherType Weather::desc()
{
int w = WEATHER_SUNNY;
if( rain_scale() > 0 )
w |= WEATHER_RAIN;
if( is_lightning() )
w |= WEATHER_LIGHTNING;
if( snow_scale() > 0 )
w |= WEATHER_SNOW;
else if( cur_cloud_type & COLD_WAVE)
w |= WEATHER_COLD_WAVE;
if( cur_cloud_type & HOT_WAVE)
w |= WEATHER_HOT_WAVE;
if( cur_cloud_type & WINDY)
w |= WEATHER_WINDY;
if( w == WEATHER_SUNNY && cloud() >= 4)
w |= WEATHER_CLOUDY;
return (WeatherType)w;
}
//---------- End of function Weather::desc ---------//
//---------- Begin of function Weather::has_tornado -------//
char Weather::has_tornado()
{
return tornado_count == 0;
}
//---------- End of function Weather::has_tornado -------//
//---------- Begin of function Weather::tornado_x_loc -------//
//
// return where a new tornado should create
//
short Weather::tornado_x_loc(short maxXLoc, short)
{
short dir = (wind_direct() + 180) % 360;
err_when(dir < 0 || dir > 360);
if( dir < 45)
{
// north side
return maxXLoc*(dir+45)/90;
}
else if( dir < 135)
{
// east side
return maxXLoc-1;
}
else if( dir < 225)
{
// south side
return maxXLoc*(224-dir)/90;
}
else if( dir < 315)
{
// west side
return 0;
}
else
{
// north side
return maxXLoc*(dir-315)/90;
}
}
//---------- End of function Weather::tornado_x_loc -------//
//---------- Begin of function Weather::tornado_y_loc -------//
short Weather::tornado_y_loc(short , short maxYLoc)
{
short dir = (wind_direct() + 180) % 360;
err_when(dir < 0 || dir > 360);
if( dir < 45)
{
// north side
return 0;
}
else if( dir < 135)
{
// east side
return maxYLoc*(dir-45)/90;
}
else if( dir < 225)
{
// south side
return maxYLoc -1;
}
else if( dir < 315)
{
// west side
return maxYLoc*(314-dir)/90;
}
else
{
// north side
return 0;
}
}
//---------- End of function Weather::tornado_y_loc -------//
//---------- Begin of function Weather::quake_rate -------//
short Weather::quake_rate(short x, short y)
{
err_when( !is_quake() );
short dist = max( abs(x - quake_x), abs(y - quake_y) );
short damage = 100 - dist / 2;
return damage > 0 ? damage : 0;
}
//---------- End of function Weather::quake_rate -------//
//--------- Begin of function MagicWeather::init ----------//
void MagicWeather::init()
{
rain_day = 0;
wind_day = 0;
}
//--------- End function MagicWeather::init ----------//
//--------- Begin of function MagicWeather::next_day ----------//
void MagicWeather::next_day()
{
if( rain_day > 0 )
--rain_day;
if( wind_day > 0 )
--wind_day;
if( lightning_day > 0 )
--lightning_day;
}
//--------- End function MagicWeather::next_day ----------//
//--------- Begin of function MagicWeather::cast_rain ----------//
void MagicWeather::cast_rain(short duration, char rainScale)
{
// override last cast_rain
rain_day = duration;
rain_str = rainScale;
}
//--------- End of function MagicWeather::cast_rain ----------//
//--------- Begin of function MagicWeather::cast_wind ----------//
void MagicWeather::cast_wind(short duration, short speed, short direction)
{
// override last cast_wind
wind_day = duration;
wind_spd = speed;
wind_dir = direction;
}
//--------- End of function MagicWeather::cast_wind ----------//
//--------- Begin of function MagicWeather::cast_lightning ----------//
void MagicWeather::cast_lightning(short duration)
{
// override last cast_lightning
lightning_day = duration;
}
//--------- End of function MagicWeather::cast_lightning ----------//
//--------- Begin of function MagicWeather::wind_speed ----------//
short MagicWeather::wind_speed()
{
return wind_spd;
}
//--------- End of function MagicWeather::wind_speed ----------//
//--------- Begin of function MagicWeather::wind_direct ----------//
short MagicWeather::wind_direct()
{
return wind_dir;
}
//--------- End of function MagicWeather::wind_direct ----------//
//--------- Begin of function MagicWeather::wind_direct_rad ----------//
double MagicWeather::wind_direct_rad()
{
return wind_dir * M_PI / 180.0;
}
//--------- End of function MagicWeather::wind_direct_rad ----------//
//--------- Begin of function MagicWeather::rain_scale ----------//
short MagicWeather::rain_scale()
{
return rain_str;
}
//--------- End of function MagicWeather::rain_scale ----------//