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