1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 1997,1998 Enlight Software Ltd.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 // Filename : OWEATHER.CPP
22 // Description : class Weather
23 // Ownership : Gilbert
24
25
26 #include <OWEATHER.h>
27 #include <ALL.h>
28 #include <math.h>
29 #include <OWORLDMT.h>
30 #include <stdlib.h>
31
32 //---------- Define constant -----------//
33 #define RAIN_CLOUD 1
34 #define LIGHTNING_CLOUD 2
35 #define WINDY 4
36 #define HOT_WAVE 8
37 #define COLD_WAVE 0x10
38 #define PI 3.14159265359
39
40
41 //-------- Begin of function Lightning::rand_seed ----------//
rand_seed(unsigned MAX)42 unsigned Weather::rand_seed(unsigned MAX)
43 {
44 #define MULTIPLIER 0x015a4e35L
45 #define INCREMENT 1
46 seed = MULTIPLIER * seed + INCREMENT;
47 return seed % MAX;
48 }
49 //-------- End of function Lightning::rand_seed ----------//
50
51 //---------- Begin of function Weather::init_date ----------//
52 //
init_date(short year,short month,short day,short latitude,int quakeFreq)53 void Weather::init_date(short year, short month, short day, short latitude, int quakeFreq)
54 {
55 // ----------- initialize random seed
56 seed = 2*year+1;
57 (void) rand_seed(10);
58
59 // ----------- calculate season_phase from month, day ------------//
60 season_phase = (short) ( month * 30.4 + day);
61 season_phase = (season_phase + 365 - 98) % 365; // 7th Mar becomes 0
62
63 // ----------- random number to earthquake -----------//
64 quake_frequency = quakeFreq;
65 day_to_quake = quakeFreq + rand_seed(quakeFreq);
66
67 // ----------- determine avg_temp and temp_amp from latitude
68 double angle = latitude * PI / 180.0;
69 avg_temp = (short)( 35.0 - fabs(latitude / 90.0 * 40.0));
70 temp_amp = (short)( 17.0 * sin(angle)); // negative for South Hemisphere
71
72 // ----------- determine cloud ----------- //
73 cur_cloud_str = rand_seed(4);
74 cur_cloud_len = 5 + rand_seed(5);
75 cur_cloud_type = 0;
76
77 // ----------- determine wind ---------------//
78 wind_dir = rand_seed(360);
79 wind_spd = 10;
80 high_wind_day = 0;
81 windy_speed = 0;
82
83 tornado_count = 1;
84
85 }
86 //---------- End of function Weather::init_date ----------//
87
88 //---------- Begin of function Weather::next_day ----------//
89 //
next_day()90 void Weather::next_day()
91 {
92
93 season_phase = (season_phase + 1 )% 365;
94
95 //---------- update/determine earthquake day ---------//
96 if( day_to_quake)
97 {
98 day_to_quake--;
99 if ( is_quake() )
100 {
101 // generate quake_x, quake_y
102 quake_x = rand_seed(0x10000) * MAX_MAP_WIDTH / 0x10000;
103 quake_y = rand_seed(0x10000) * MAX_MAP_HEIGHT / 0x10000;
104 }
105 }
106 else
107 day_to_quake = quake_frequency + rand_seed(quake_frequency);
108
109 //---------- update wind ----------//
110 wind_dir = (wind_dir + rand_seed(5) ) % 360;
111 wind_spd += rand_seed(9) - 4 - (high_wind_day/16);
112 if( wind_spd < -10)
113 wind_spd = -10;
114 if( wind_spd > 110)
115 wind_spd = 110;
116 if( wind_spd >= 20 )
117 {
118 high_wind_day++;
119 }
120 else
121 {
122 high_wind_day--;
123 }
124
125 //---------- generate cloud --------//
126 if( cur_cloud_len > 0)
127 {
128 cur_cloud_len--;
129 }
130 else
131 {
132 short t = base_temp();
133 short maxCloudStr;
134 if( t >= 30)
135 maxCloudStr = 10;
136 else if( t <= 18)
137 maxCloudStr = 4;
138 else
139 maxCloudStr = (t -18)/2 + 4;
140 cur_cloud_str = rand_seed(maxCloudStr+4)-3; // range : -2 to maxCloudStr
141 if(cur_cloud_str < 0)
142 cur_cloud_str = 0;
143 cur_cloud_len = 2 + rand_seed(3) + rand_seed(3);
144
145 cur_cloud_type = 0;
146
147 // ------- summer weather
148 if( cur_cloud_str > 4)
149 {
150 if( (char) rand_seed(10) < cur_cloud_str )
151 cur_cloud_type |= RAIN_CLOUD;
152 if( cur_cloud_str >= 6 && (char) rand_seed(10) < cur_cloud_str-4)
153 cur_cloud_type |= WINDY;
154 }
155 if( cur_cloud_str <= 1 && t >= 30 && rand_seed(10) <= 1)
156 {
157 cur_cloud_type |= HOT_WAVE;
158 }
159
160 // ------- winter weather
161 if( t < 15)
162 {
163 if( rand_seed(20) < 2 )
164 cur_cloud_type |= COLD_WAVE;
165
166 if( t >= 10 && rand_seed(10) < 3)
167 cur_cloud_type |= WINDY;
168 if( t < 10 && rand_seed(10) < 7)
169 cur_cloud_type |= WINDY;
170 }
171 if( cur_cloud_type & WINDY)
172 windy_speed = 10 + cur_cloud_str * 5 + rand_seed(2*cur_cloud_str+1);
173 else
174 {
175 windy_speed = 0;
176 if( cur_cloud_str > 4 && (char)rand_seed(50) < cur_cloud_str + 2 )
177 cur_cloud_type |= LIGHTNING_CLOUD;
178 }
179
180 // ---- double the time of snow ------ //
181 if( snow_scale() )
182 cur_cloud_len += cur_cloud_len;
183
184 }
185
186 // -------- update tornado_count, at least 20 days between two tornadoes -------//
187 if( tornado_count > 20 && base_temp() >= 30 && wind_speed() >= 40
188 && rand_seed(10)==0 )
189 {
190 tornado_count = 0; // today has a tornado
191 }
192 else
193 {
194 tornado_count++;
195 }
196 }
197 //---------- End of function Weather::next_day ----------//
198
199
200 //---------- Begin of function Weather::base_temp ----------//
201 //
base_temp()202 short Weather::base_temp()
203 {
204 return( (short)(avg_temp + temp_amp * sin(season_phase / 365.0
205 * 2 * PI) ));
206 }
207 //---------- End of function Weather::base_temp ----------//
208
209 //---------- Begin of function Weather::cloud ----------//
210 //
cloud()211 short Weather::cloud()
212 {
213 if( cur_cloud_str < 0)
214 return 0;
215 if( cur_cloud_str > 10)
216 return 10;
217 return cur_cloud_str;
218 }
219 //---------- End of function Weather::cloud ----------//
220
221 //---------- Begin of function Weather::temp_c ----------//
222 //
temp_c()223 short Weather::temp_c()
224 {
225 return base_temp() - (cur_cloud_str < 1 ? 0 : (cur_cloud_str < 4 ? 2:4)) +
226 (cur_cloud_type & HOT_WAVE ? 8:0) - (cur_cloud_type & COLD_WAVE ? 10:0);
227 }
228 //---------- End of function Weather::temp_c ----------//
229
230 //---------- Begin of function Weather::temp_f ----------//
231 //
temp_f()232 short Weather::temp_f()
233 {
234 return ((short) (base_temp() / 5.0 * 9.0 + 32.0 ));
235 }
236 //---------- End of function Weather::temp_f ----------//
237
238 //---------- Begin of function Weather::wind_speed ----------//
239 //
wind_speed()240 short Weather::wind_speed()
241 {
242 if( this == &weather && magic_weather.wind_day > 0)
243 return magic_weather.wind_speed();
244 short w = wind_spd + windy_speed;
245 if(w < 0)
246 return 0;
247 if(w > 100)
248 return 100;
249 return w;
250 }
251 //---------- End of function Weather::wind_speed ----------//
252
253 //---------- Begin of function Weather::wind_direct ----------//
254 //
wind_direct()255 short Weather::wind_direct()
256 {
257 if( this == &weather && magic_weather.wind_day > 0)
258 return magic_weather.wind_direct();
259 return wind_dir;
260 }
261 //---------- End of function Weather::wind_direct ----------//
262
263 //---------- Begin of function Weather::wind_direct_rad ----------//
264 //
wind_direct_rad()265 double Weather::wind_direct_rad()
266 {
267 if( this == &weather && magic_weather.wind_day > 0)
268 return magic_weather.wind_direct_rad();
269 return wind_dir * PI / 180.0;
270 }
271 //---------- End of function Weather::wind_direct ----------//
272
273
274 //---------- Begin of function Weather::rain_scale ----------//
275 //
rain_scale()276 short Weather::rain_scale()
277 {
278 if( this == &weather && magic_weather.rain_day > 0)
279 return magic_weather.rain_scale();
280 return cur_cloud_str > 4 ? cur_cloud_str * 2 -8 : 0;
281 }
282 //---------- End of function Weather::rain_scale ----------//
283
284 //---------- Begin of function Weather::snow_scale ----------//
285 //
snow_scale()286 short Weather::snow_scale()
287 {
288 short t = temp_c();
289 if( t > 0)
290 return 0;
291
292 if( t <= -15)
293 {
294 if( t <= -30)
295 return 8;
296 if( t <= -25)
297 return 7;
298 if( t <= -20)
299 return 6;
300 return 5;
301 }
302 else
303 {
304 if(t <= -10)
305 return 4;
306 if( t <= -5)
307 return 3;
308 if( t <= -2)
309 return 2;
310 return 1;
311 }
312 }
313 //---------- End of function Weather::snow_scale ----------//
314
315 //---------- Begin of function Weather::is_lightning ----------//
316 //
is_lightning()317 char Weather::is_lightning()
318 {
319 if( magic_weather.lightning_day > 0 )
320 return LIGHTNING_CLOUD;
321 return( cur_cloud_type & LIGHTNING_CLOUD );
322 }
323 //---------- End of function Weather::is_lightning ----------//
324
325 //---------- Begin of function Weather::is_quake ----------//
326 //
is_quake()327 char Weather::is_quake()
328 {
329 return( day_to_quake == 0);
330 }
331 //---------- End of function Weather::is_quake ----------//
332
333 //---------- Begin of function Weather::desc ---------//
334 //
desc()335 WeatherType Weather::desc()
336 {
337 int w = WEATHER_SUNNY;
338 if( rain_scale() > 0 )
339 w |= WEATHER_RAIN;
340 if( is_lightning() )
341 w |= WEATHER_LIGHTNING;
342 if( snow_scale() > 0 )
343 w |= WEATHER_SNOW;
344 else if( cur_cloud_type & COLD_WAVE)
345 w |= WEATHER_COLD_WAVE;
346 if( cur_cloud_type & HOT_WAVE)
347 w |= WEATHER_HOT_WAVE;
348 if( cur_cloud_type & WINDY)
349 w |= WEATHER_WINDY;
350
351 if( w == WEATHER_SUNNY && cloud() >= 4)
352 w |= WEATHER_CLOUDY;
353
354 return (WeatherType)w;
355 }
356 //---------- End of function Weather::desc ---------//
357
358
359 //---------- Begin of function Weather::has_tornado -------//
has_tornado()360 char Weather::has_tornado()
361 {
362 return tornado_count == 0;
363 }
364 //---------- End of function Weather::has_tornado -------//
365
366 //---------- Begin of function Weather::tornado_x_loc -------//
367 //
368 // return where a new tornado should create
369 //
tornado_x_loc(short maxXLoc,short)370 short Weather::tornado_x_loc(short maxXLoc, short)
371 {
372 short dir = (wind_direct() + 180) % 360;
373 err_when(dir < 0 || dir > 360);
374
375 if( dir < 45)
376 {
377 // north side
378 return maxXLoc*(dir+45)/90;
379 }
380 else if( dir < 135)
381 {
382 // east side
383 return maxXLoc-1;
384 }
385 else if( dir < 225)
386 {
387 // south side
388 return maxXLoc*(224-dir)/90;
389 }
390 else if( dir < 315)
391 {
392 // west side
393 return 0;
394 }
395 else
396 {
397 // north side
398 return maxXLoc*(dir-315)/90;
399 }
400 }
401 //---------- End of function Weather::tornado_x_loc -------//
402
403 //---------- Begin of function Weather::tornado_y_loc -------//
tornado_y_loc(short,short maxYLoc)404 short Weather::tornado_y_loc(short , short maxYLoc)
405 {
406 short dir = (wind_direct() + 180) % 360;
407 err_when(dir < 0 || dir > 360);
408
409 if( dir < 45)
410 {
411 // north side
412 return 0;
413 }
414 else if( dir < 135)
415 {
416 // east side
417 return maxYLoc*(dir-45)/90;
418 }
419 else if( dir < 225)
420 {
421 // south side
422 return maxYLoc -1;
423 }
424 else if( dir < 315)
425 {
426 // west side
427 return maxYLoc*(314-dir)/90;
428 }
429 else
430 {
431 // north side
432 return 0;
433 }
434 }
435 //---------- End of function Weather::tornado_y_loc -------//
436
437
438 //---------- Begin of function Weather::quake_rate -------//
quake_rate(short x,short y)439 short Weather::quake_rate(short x, short y)
440 {
441 err_when( !is_quake() );
442
443 short dist = MAX( abs(x - quake_x), abs(y - quake_y) );
444 short damage = 100 - dist / 2;
445 return damage > 0 ? damage : 0;
446 }
447 //---------- End of function Weather::quake_rate -------//
448
449
450 //--------- Begin of function MagicWeather::init ----------//
init()451 void MagicWeather::init()
452 {
453 rain_day = 0;
454 wind_day = 0;
455 }
456 //--------- End function MagicWeather::init ----------//
457
458
459 //--------- Begin of function MagicWeather::next_day ----------//
next_day()460 void MagicWeather::next_day()
461 {
462 if( rain_day > 0 )
463 --rain_day;
464 if( wind_day > 0 )
465 --wind_day;
466 if( lightning_day > 0 )
467 --lightning_day;
468 }
469 //--------- End function MagicWeather::next_day ----------//
470
471
472 //--------- Begin of function MagicWeather::cast_rain ----------//
cast_rain(short duration,char rainScale)473 void MagicWeather::cast_rain(short duration, char rainScale)
474 {
475 // override last cast_rain
476 rain_day = duration;
477 rain_str = rainScale;
478 }
479 //--------- End of function MagicWeather::cast_rain ----------//
480
481
482 //--------- Begin of function MagicWeather::cast_wind ----------//
cast_wind(short duration,short speed,short direction)483 void MagicWeather::cast_wind(short duration, short speed, short direction)
484 {
485 // override last cast_wind
486 wind_day = duration;
487 wind_spd = speed;
488 wind_dir = direction;
489 }
490 //--------- End of function MagicWeather::cast_wind ----------//
491
492
493 //--------- Begin of function MagicWeather::cast_lightning ----------//
cast_lightning(short duration)494 void MagicWeather::cast_lightning(short duration)
495 {
496 // override last cast_lightning
497 lightning_day = duration;
498 }
499 //--------- End of function MagicWeather::cast_lightning ----------//
500
501
502 //--------- Begin of function MagicWeather::wind_speed ----------//
wind_speed()503 short MagicWeather::wind_speed()
504 {
505 return wind_spd;
506 }
507 //--------- End of function MagicWeather::wind_speed ----------//
508
509
510 //--------- Begin of function MagicWeather::wind_direct ----------//
wind_direct()511 short MagicWeather::wind_direct()
512 {
513 return wind_dir;
514 }
515 //--------- End of function MagicWeather::wind_direct ----------//
516
517
518 //--------- Begin of function MagicWeather::wind_direct_rad ----------//
wind_direct_rad()519 double MagicWeather::wind_direct_rad()
520 {
521 return wind_dir * PI / 180.0;
522 }
523 //--------- End of function MagicWeather::wind_direct_rad ----------//
524
525
526 //--------- Begin of function MagicWeather::rain_scale ----------//
rain_scale()527 short MagicWeather::rain_scale()
528 {
529 return rain_str;
530 }
531 //--------- End of function MagicWeather::rain_scale ----------//
532