ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/weather.C
Revision: 1.2
Committed: Sat Aug 26 23:36:35 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +12 -23 lines
Log Message:
intermediate check-in, per-object events work

File Contents

# Content
1 /*
2 * static char *rcsid_weather_c =
3 * "$Id: weather.C,v 1.1 2006-08-13 17:16:05 elmex Exp $";
4 */
5 /*
6 CrossFire, A Multiplayer game for X-windows
7
8 Copyright (C) 2002 Tim Rightnour
9 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
10 Copyright (C) 1992 Frank Tore Johansen
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 The authors can be reached via e-mail to crossfire-devel@real-time.com
27 */
28
29 /* This weather system was written for crossfire by Tim Rightnour */
30
31 #include <global.h>
32 #include <tod.h>
33 #include <map.h>
34 #ifndef __CEXTRACT__
35 #include <sproto.h>
36 #endif
37
38 extern unsigned long todtick;
39 extern weathermap_t **weathermap;
40
41 static void dawn_to_dusk(const timeofday_t *tod);
42 static void write_skymap(void);
43 static void write_pressuremap(void);
44 static void read_pressuremap(void);
45 static void init_pressure(void);
46 static void write_winddirmap(void);
47 static void read_winddirmap(void);
48 static void write_windspeedmap(void);
49 static void read_windspeedmap(void);
50 static void init_wind(void);
51 static void write_gulfstreammap(void);
52 static void read_gulfstreammap(void);
53 static void init_gulfstreammap(void);
54 static void write_humidmap(void);
55 static void read_humidmap(void);
56 static void write_elevmap(void);
57 static void read_elevmap(void);
58 static void write_watermap(void);
59 static void read_watermap(void);
60 static void init_humid_elev(void);
61 static void write_temperaturemap(void);
62 static void read_temperaturemap(void);
63 static void init_temperature(void);
64 static void write_rainfallmap(void);
65 static void read_rainfallmap(void);
66 static void init_rainfall(void);
67 static void init_weatheravoid (weather_avoids_t wa[]);
68 static void perform_weather(void);
69 static object *avoid_weather(int *av, mapstruct *m, int x, int y, int *gs, int grow);
70 static void calculate_temperature(mapstruct *m, int wx, int wy);
71 static void let_it_snow(mapstruct *m, int wx, int wy);
72 static void singing_in_the_rain(mapstruct *m, int wx, int wy);
73 static void plant_a_garden(mapstruct *m, int wx, int wy);
74 static void change_the_world(mapstruct *m, int wx, int wy);
75 static void feather_map(mapstruct *m, int wx, int wy);
76 static const char *weathermap_to_worldmap_corner(int wx, int wy, int *x, int *y, int dir);
77 static int polar_distance(int x, int y, int equator);
78 static void update_humid(void);
79 static int humid_tile(int x, int y);
80 static void temperature_calc(int x, int y, const timeofday_t *tod);
81 static int real_temperature(int x, int y);
82 static void smooth_pressure(void);
83 static void perform_pressure(void);
84 static void smooth_wind(void);
85 static void plot_gulfstream(void);
86 static void compute_sky(void);
87 static void process_rain(void);
88 static void spin_globe(void);
89 static void write_weather_images(void);
90
91 static int gulf_stream_speed[GULF_STREAM_WIDTH][WEATHERMAPTILESY];
92 static int gulf_stream_dir[GULF_STREAM_WIDTH][WEATHERMAPTILESY];
93 static int gulf_stream_start;
94 static int gulf_stream_direction;
95
96 static const int season_timechange[5][HOURS_PER_DAY] = {
97 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7
98 8 9 10 11 12 13 */
99 {0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1,
100 1, 1, 1, 1, 1, 1},
101 {0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
102 1, 1, 1, 1, 1, 0},
103 {0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
104 1, 1, 1, 1, 1, 0},
105 {0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 1, 1, 1, 1, 1, 0},
107 {0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
108 1, 1, 1, 1, 1, 0}
109 };
110
111 static const int season_tempchange[HOURS_PER_DAY] = {
112 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7
113 8 9 10 11 12 13 */
114 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1,
115 1, 1, 1, 1, 1, 1};
116
117 /*
118 * The table below is used to set which tiles the weather will avoid
119 * processing. This keeps it from putting snow on snow, and putting snow
120 * on the ocean, and other things like that.
121 */
122
123 static weather_avoids_t weather_avoids[] = {
124 {"snow", 1, NULL},
125 {"snow2", 1, NULL},
126 {"snow3", 1, NULL},
127 {"snow4", 1, NULL},
128 {"snow5", 1, NULL},
129 {"mountain1_snow", 1, NULL},
130 {"mountain2_snow", 1, NULL},
131 {"rain1", 1, NULL},
132 {"rain2", 1, NULL},
133 {"rain3", 1, NULL},
134 {"rain4", 1, NULL},
135 {"rain5", 1, NULL},
136 {"mountain1_rivlets", 1, NULL},
137 {"mountain2_rivlets", 1, NULL},
138 {"drifts", 0, NULL},
139 {"glacier", 0, NULL},
140 {"cforest1", 0, NULL},
141 {"sea", 0, NULL},
142 {"sea1", 0, NULL},
143 {"deep_sea", 0, NULL},
144 {"shallow_sea", 0, NULL},
145 {"lava", 0, NULL},
146 {"permanent_lava", 0, NULL},
147 {NULL, 0, NULL}
148 };
149
150 /*
151 * this table is identical to the one above, except these are tiles to avoid
152 * when processing growth. IE, don't grow herbs in the ocean. The second
153 * field is unused.
154 */
155
156 static weather_avoids_t growth_avoids[] = {
157 {"cobblestones", 0, NULL},
158 {"cobblestones2", 0, NULL},
159 {"flagstone", 0, NULL},
160 {"stonefloor2", 0, NULL},
161 {"lava", 0, NULL},
162 {"permanent_lava", 0, NULL},
163 {"sea", 0, NULL},
164 {"sea1", 0, NULL},
165 {"deep_sea", 0, NULL},
166 {"shallow_sea", 0, NULL},
167 {"farmland", 0, NULL},
168 {"dungeon_magic", 0, NULL},
169 {"dungeon_floor", 0, NULL},
170 {"lake", 0, NULL},
171 {"grasspond", 0, NULL},
172 {NULL, 0, NULL}
173 };
174
175 /*
176 * The table below is used in let_it_snow() and singing_in_the_rain() to
177 * decide what type of snow/rain/etc arch to put down. The first field is the
178 * name of the arch we want to match. The second field is the special snow
179 * type we use to cover that arch. The third field is the doublestack arch,
180 * NULL if none, used to stack over the snow after covering the tile.
181 * The fourth field is 1 if you want to match arch->name, 0 to match ob->name.
182 */
183
184 static weather_replace_t weather_replace[] = {
185 {"impossible_match", "snow5", NULL, 0},
186 {"impossible_match2", "snow4", NULL, 0}, /* placeholders */
187 {"impossible_match3", "snow3", NULL, 0},
188 {"hills", "drifts", NULL, 0},
189 {"treed_hills", "drifts", "woods5", 1},
190 {"grass", "snow", NULL, 0},
191 {"sand", "snow", NULL, 0},
192 {"stones", "snow2", NULL, 0},
193 {"steppe", "snow2", NULL, 0},
194 {"brush", "snow2", NULL, 0},
195 {"farmland", "snow3", NULL, 0},
196 {"wasteland", "glacier", NULL, 0},
197 {"mountain", "mountain1_snow", NULL, 1},
198 {"mountain2", "mountain2_snow", NULL, 1},
199 {"mountain4", "mountain2_snow", NULL, 1},
200 {"evergreens", "snow", "evergreens2", 1},
201 {"evergreen","snow", "tree5", 1},
202 {"tree", "snow", "tree3", 0},
203 {"woods", "snow3", "woods4", 1},
204 {"woods_3", "snow", "woods5", 1},
205 {NULL, NULL, NULL, 0},
206 };
207
208 /*
209 * The table below is used to grow things on the map. See include/tod.h for
210 * the meanings of all of the fields.
211 */
212
213 static const weather_grow_t weather_grow[] = {
214 /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax, season */
215 {"mint", "grass", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
216 {"rose_red", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
217 {"rose_red", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
218 {"mint", "brush", 8, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
219 {"blackroot", "swamp", 15, 1.6, 2.0, 60, 100, 20, 30, -100, 1500, 0},
220 {"mushroom_1", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
221 {"mushroom_2", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
222 {"mushroom_1", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
223 {"mushroom_2", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
224 {"mushroom_1", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
225 {"mushroom_2", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
226 {"pipeweed", "farmland", 20, 1.0, 2.0, 30, 100, 10, 25, 100, 5000, 0},
227 {"cabbage", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 0},
228 {"onion", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
229 {"carrot", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
230 {"thorns", "brush", 15, 0.5, 1.3, 30, 100, 10, 25, -100, 9999, 0},
231 {"mountain_foilage", "mountain", 6, 1.0, 2.0, 25, 100, 5, 30, 0, 15999, 2},
232 {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
233 };
234
235 /*
236 * The table below uses the same format as the one above. However this
237 * table is used to change the layout of the worldmap itself. The tile
238 * parameter is a base tile to lay down underneath the herb tile.
239 */
240
241 static const weather_grow_t weather_tile[] = {
242 /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax */
243 {"dunes", NULL, 2, 0.0, 0.03, 0, 20, 10, 99, 0, 4000, 0},
244 {"desert", NULL, 1, 0.0, 0.05, 0, 20, 10, 99, 0, 4000, 0},
245 {"pstone_2", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
246 {"pstone_3", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
247 {"grassbrown", NULL, 1, 0.05, 1.0, 20, 80, -20, -3, 0, 5000, 0},
248 {"grass_br_gr", NULL, 1, 0.05, 1.0, 20, 80, -3, 5, 0, 5000, 0},
249 {"grass", NULL, 1, 0.05, 1.0, 20, 80, 5, 15, 0, 5000, 0},
250 {"grassmedium", NULL, 1, 0.05, 1.0, 20, 80, 15, 25, 0, 5000, 0},
251 {"grassdark", NULL, 1, 0.05, 1.0, 20, 80, 25, 35, 0, 5000, 0},
252 {"brush", NULL, 1, 0.2, 1.0, 25, 70, 0, 30, 500, 6000, 0},
253 /* small */
254 {"evergreens2", "brush", 1, 0.5, 1.8, 30, 90, -30, 24, 3000, 8000, 0},
255 {"fernsdense", "brush", 1, 0.9, 2.5, 50, 100, 10, 35, 1000, 6000, 0},
256 {"fernssparse", "brush", 1, 0.7, 2.0, 30, 90, -15, 35, 0, 4000, 0},
257 {"woods4", "brush", 1, 0.1, 0.8, 30, 60, -5, 25, 1000, 4500, 0},
258 {"woods5", "brush", 1, 0.6, 1.5, 20, 70, -15, 20, 2000, 5500, 0},
259 {"forestsparse", "brush", 1, 0.3, 1.5, 15, 60, -20, 25, 0, 4500, 0},
260 /* big */
261 /*
262 {"ytree_2", "brush", 2, 0.1, 0.6, 30, 60, 10, 25, 1000, 3500, 0},
263 {"tree3", "grass", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
264 {"tree5", "grass", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
265 {"tree3", "grassmeduim", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
266 {"tree5", "grassmedium", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
267 {"tree3", "grassdark", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
268 {"tree5", "grassdark", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},*/
269 /* mountians */
270 {"steppe", NULL, 1, 0.5, 1.3, 0, 30, -20, 35, 1000, 6000, 0},
271 {"steppelight", NULL, 1, 0.0, 0.6, 0, 20, -50, 35, 0, 5000, 0},
272 {"hills", NULL, 1, 0.1, 0.9, 20, 80, -10, 30, 5000, 8500, 0},
273 {"hills_rocky", NULL, 1, 0.0, 0.9, 0, 100, -50, 50, 5000, 8500, 0},
274 {"swamp", NULL, 1, 1.0, 9.9, 55, 80, 10, 50, 0, 1000, 0},
275 {"deep_swamp", NULL, 1, 1.0, 9.9, 80, 100, 10, 50, 0, 1000, 0},
276 {"mountain", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 8000, 10000, 0},
277 {"mountain2", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 9500, 11000, 0},
278 {"mountain4", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 10500, 12000, 0},
279 {"mountain5", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 11500, 13500, 0},
280 {"wasteland", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 13000, 99999, 0},
281 /* catchalls */
282 {"palms", "pstone_1", 1, 0.01, 0.1, 0, 30, 5, 99, 0, 4000, 0},
283 {"large_stones", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 6000, 8000, 0},
284 {"earth", NULL, 1, 0.0, 1.0, 0, 70, -30, 15, 0, 6000, 0},
285 {"medium_stones", NULL, 1, 1.0, 3.0, 70, 100, -30, 10, 0, 4000, 0}, /*unsure*/
286 {"earth", NULL, 1, 0.1, 0.9, 20, 80, -30, 30, 0, 4999, 0}, /* tundra */
287 {"swamp", NULL, 1, 1.0, 9.9, 50, 100, -30, 10, 0, 4000, 0},/* cold marsh */
288 {"earth", NULL, 1, 0.0, 99.9, 0, 100, -99, 99, 0, 99999, 0}, /* debug */
289 {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
290 };
291
292 /* This stuff is for creating the images. */
293
294 /* Colour offsets into pixel array. */
295 #define RED 0
296 #define GREEN 1
297 #define BLUE 2
298
299 /* Colours used for wind directions.
300 * winddir is the directoin wind is coming from.
301 * 812 456
302 * 7 3 3 7
303 * 654 218
304 */
305 static const uint32 directions[] = {
306 0x0000FFFF, /* south */
307 0x000000FF, /* south west */
308 0x00FF00FF, /* west */
309 0x00FFFFFF, /* north west */
310 0x00000000, /* north */
311 0x00FF0000, /* north east */
312 0x00FFFF00, /* east */
313 0x0000FF00 /* south east */
314 };
315
316 /* Colours used for weather types. */
317 static const uint32 skies[] = {
318 0x000000FF, /* SKY_CLEAR 0 */
319 0x000000BD, /* SKY_LIGHTCLOUD 1 */
320 0x0000007E, /* SKY_OVERCAST 2 */
321 0x0000FF00, /* SKY_LIGHT_RAIN 3 */
322 0x0000BD00, /* SKY_RAIN 4 */
323 0x00007E00, /* SKY_HEAVY_RAIN 5 */
324 0x00FFFF00, /* SKY_HURRICANE 6 */
325 /* wierd weather 7-12 */
326 0x00FF0000, /* SKY_FOG 7 */
327 0x00FF00FF, /* SKY_HAIL 8 */
328 0x00000000,
329 0x00000000,
330 0x00000000,
331 0x00000000,
332 /* snow */
333 0x003F3F3F, /* SKY_LIGHT_SNOW 13 */
334 0x007E7E7E, /* SKY_SNOW 14 */
335 0x00BDBDBD, /* SKY_HEAVY_SNOW 15 */
336 0x00FFFFFF /* SKY_BLIZZARD 16 */
337 };
338
339
340 /*
341 * Set the darkness level for a map. Requires the map pointer.
342 */
343
344 void set_darkness_map(mapstruct *m)
345 {
346 int i;
347 timeofday_t tod;
348
349 if (!m->outdoor)
350 return;
351
352 get_tod(&tod);
353 m->darkness = 0;
354 for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++)
355 change_map_light(m, season_timechange[tod.season][i]);
356 for (i = 0; i <= tod.hour; i++)
357 change_map_light(m, season_timechange[tod.season][i]);
358 }
359
360 /*
361 * Compute the darkness level for all maps in the game. Requires the
362 * time of day as an argument.
363 */
364
365 static void dawn_to_dusk(const timeofday_t *tod)
366 {
367 mapstruct *m;
368
369 /* If the light level isn't changing, no reason to do all
370 * the work below.
371 */
372 if (season_timechange[tod->season][tod->hour] == 0) return;
373
374 for(m=first_map;m!=NULL;m=m->next) {
375 if (!m->outdoor)
376 continue;
377 change_map_light(m, season_timechange[tod->season][tod->hour]);
378 }
379 }
380
381 /*
382 * This performs the basic function of advancing the clock one tick
383 * forward. Every 20 ticks, the clock is saved to disk. It is also
384 * saved on shutdown. Any time dependant functions should be called
385 * from this function, and probably be passed tod as an argument.
386 * Please don't modify tod in the dependant function.
387 */
388
389 void tick_the_clock(void)
390 {
391 timeofday_t tod;
392
393 todtick++;
394 if (todtick%20 == 0)
395 write_todclock();
396 if (settings.dynamiclevel > 0) {
397 if (todtick%21 == 0)
398 write_pressuremap();
399 if (todtick%22 == 0)
400 write_winddirmap();
401 if (todtick%23 == 0)
402 write_windspeedmap();
403 if (todtick%24 == 0)
404 write_humidmap();
405 /* if (todtick%25 == 0)
406 write_elevmap(); */
407 if (todtick%26 == 0)
408 write_temperaturemap();
409 if (todtick%27 == 0)
410 write_gulfstreammap();
411 if (todtick%28 == 0)
412 write_skymap();
413 if (todtick%29 == 0)
414 write_rainfallmap();
415 }
416 get_tod(&tod);
417 dawn_to_dusk(&tod);
418 /* call the weather calculators, here, in order */
419 if (settings.dynamiclevel > 0) {
420 perform_pressure(); /* pressure is the random factor */
421 smooth_wind(); /* calculate the wind. depends on pressure */
422 plot_gulfstream();
423 update_humid();
424 init_temperature();
425 compute_sky();
426 if (tod.hour == 0)
427 process_rain();
428 }
429 /* perform_weather must follow calculators */
430 perform_weather();
431 if (settings.dynamiclevel > 0) {
432 write_weather_images();
433 spin_globe();
434 }
435 }
436
437 /*
438 * This batch of routines reads and writes the various
439 * weathermap structures. Each type of data is stored
440 * in a separate file to allow the size of these structures to be
441 * changed more or less on the fly. If weather goes haywire, the admin
442 * can simply delete and boot the server, and it will regen.
443 *
444 * The write functions should be called occasionally to keep the data
445 * in the maps current. Whereas the read functions should only be called
446 * at boot. If the read function cannot find the appropriate map, it
447 * calls the init function, to initialize that map.
448 */
449
450 /* sky. We never read this map, only write it for debugging purposes */
451
452 static void write_skymap(void)
453 {
454 char filename[MAX_BUF];
455 FILE *fp;
456 int x, y;
457
458 sprintf(filename, "%s/skymap", settings.localdir);
459 if ((fp = fopen(filename, "w")) == NULL) {
460 LOG(llevError, "Cannot open %s for writing\n", filename);
461 return;
462 }
463 for (x=0; x < WEATHERMAPTILESX; x++) {
464 for (y=0; y < WEATHERMAPTILESY; y++)
465 fprintf(fp, "%d ", weathermap[x][y].sky);
466 fprintf(fp, "\n");
467 }
468 fclose(fp);
469 }
470
471 /* pressure */
472
473 static void write_pressuremap(void)
474 {
475 char filename[MAX_BUF];
476 FILE *fp;
477 int x, y;
478
479 sprintf(filename, "%s/pressuremap", settings.localdir);
480 if ((fp = fopen(filename, "w")) == NULL) {
481 LOG(llevError, "Cannot open %s for writing\n", filename);
482 return;
483 }
484 for (x=0; x < WEATHERMAPTILESX; x++) {
485 for (y=0; y < WEATHERMAPTILESY; y++)
486 fprintf(fp, "%d ", weathermap[x][y].pressure);
487 fprintf(fp, "\n");
488 }
489 fclose(fp);
490 }
491
492 static void read_pressuremap(void)
493 {
494 char filename[MAX_BUF];
495 FILE *fp;
496 int x, y;
497
498 sprintf(filename, "%s/pressuremap", settings.localdir);
499 LOG(llevDebug, "Reading pressure data from %s...", filename);
500 if ((fp = fopen(filename, "r")) == NULL) {
501 LOG(llevError, "Cannot open %s for reading\n", filename);
502 LOG(llevDebug, "Initializing pressure maps...");
503 init_pressure();
504 write_pressuremap();
505 LOG(llevDebug, "Done\n");
506 return;
507 }
508 for (x=0; x < WEATHERMAPTILESX; x++) {
509 for (y=0; y < WEATHERMAPTILESY; y++) {
510 fscanf(fp, "%hd ", &weathermap[x][y].pressure);
511 if (weathermap[x][y].pressure < 960 ||
512 weathermap[x][y].pressure > 1040)
513 weathermap[x][y].pressure = rndm(960, 1040);
514 }
515 fscanf(fp, "\n");
516 }
517 LOG(llevDebug, "Done.\n");
518 fclose(fp);
519 }
520
521 static void init_pressure(void)
522 {
523 int x, y;
524 int l, n, k, r;
525
526 for (x=0; x < WEATHERMAPTILESX; x++)
527 for (y=0; y < WEATHERMAPTILESY; y++)
528 weathermap[x][y].pressure = 1000;
529
530 for (l=0; l < PRESSURE_ITERATIONS; l++) {
531 x = rndm(0, WEATHERMAPTILESX-1);
532 y = rndm(0, WEATHERMAPTILESY-1);
533 n = rndm(PRESSURE_MIN, PRESSURE_MAX);
534 for (k=1; k < PRESSURE_AREA; k++) {
535 r = rndm(0,3);
536 switch (r) {
537 case 0: if (x < WEATHERMAPTILESX-1) x++; break;
538 case 1: if (y < WEATHERMAPTILESY-1) y++; break;
539 case 2: if (x) x--; break;
540 case 3: if (y) y--; break;
541 }
542 weathermap[x][y].pressure = (weathermap[x][y].pressure+n)/2;
543 }
544 }
545 /* create random spikes in the pressure */
546 for (l=0; l < PRESSURE_SPIKES; l++) {
547 x = rndm(0, WEATHERMAPTILESX-1);
548 y = rndm(0, WEATHERMAPTILESY-1);
549 n = rndm(500, 2000);
550 weathermap[x][y].pressure = n;
551 }
552 smooth_pressure();
553 }
554
555 /* winddir */
556
557 static void write_winddirmap(void)
558 {
559 char filename[MAX_BUF];
560 FILE *fp;
561 int x, y;
562
563 sprintf(filename, "%s/winddirmap", settings.localdir);
564 if ((fp = fopen(filename, "w")) == NULL) {
565 LOG(llevError, "Cannot open %s for writing\n", filename);
566 return;
567 }
568 for (x=0; x < WEATHERMAPTILESX; x++) {
569 for (y=0; y < WEATHERMAPTILESY; y++)
570 fprintf(fp, "%d ", weathermap[x][y].winddir);
571 fprintf(fp, "\n");
572 }
573 fclose(fp);
574 }
575
576 static void read_winddirmap(void)
577 {
578 char filename[MAX_BUF];
579 FILE *fp;
580 int x, y, d;
581
582 sprintf(filename, "%s/winddirmap", settings.localdir);
583 LOG(llevDebug, "Reading wind direction data from %s...", filename);
584 if ((fp = fopen(filename, "r")) == NULL) {
585 LOG(llevError, "Cannot open %s for reading\n", filename);
586 LOG(llevDebug, "Initializing wind maps...");
587 init_wind();
588 write_winddirmap();
589 LOG(llevDebug, "Done\n");
590 return;
591 }
592 for (x=0; x < WEATHERMAPTILESX; x++) {
593 for (y=0; y < WEATHERMAPTILESY; y++) {
594 fscanf(fp, "%d ", &d);
595 weathermap[x][y].winddir = d;
596 if (weathermap[x][y].winddir < 1 ||
597 weathermap[x][y].winddir > 8)
598 weathermap[x][y].winddir = rndm(1, 8);
599 }
600 fscanf(fp, "\n");
601 }
602 LOG(llevDebug, "Done.\n");
603 fclose(fp);
604 }
605
606 /* windspeed */
607
608 static void write_windspeedmap(void)
609 {
610 char filename[MAX_BUF];
611 FILE *fp;
612 int x, y;
613
614 sprintf(filename, "%s/windspeedmap", settings.localdir);
615 if ((fp = fopen(filename, "w")) == NULL) {
616 LOG(llevError, "Cannot open %s for writing\n", filename);
617 return;
618 }
619 for (x=0; x < WEATHERMAPTILESX; x++) {
620 for (y=0; y < WEATHERMAPTILESY; y++)
621 fprintf(fp, "%d ", weathermap[x][y].windspeed);
622 fprintf(fp, "\n");
623 }
624 fclose(fp);
625 }
626
627 static void read_windspeedmap(void)
628 {
629 char filename[MAX_BUF];
630 FILE *fp;
631 int x, y, d;
632
633 sprintf(filename, "%s/windspeedmap", settings.localdir);
634 LOG(llevDebug, "Reading wind speed data from %s...", filename);
635 if ((fp = fopen(filename, "r")) == NULL) {
636 LOG(llevError, "Cannot open %s for reading\n", filename);
637 LOG(llevDebug, "Initializing wind maps...");
638 init_wind();
639 write_windspeedmap();
640 LOG(llevDebug, "Done\n");
641 return;
642 }
643 for (x=0; x < WEATHERMAPTILESX; x++) {
644 for (y=0; y < WEATHERMAPTILESY; y++) {
645 fscanf(fp, "%d ", &d);
646 weathermap[x][y].windspeed = d;
647 if (weathermap[x][y].windspeed < 0 ||
648 weathermap[x][y].windspeed > 120)
649 weathermap[x][y].windspeed = rndm(1, 30);
650 }
651 fscanf(fp, "\n");
652 }
653 LOG(llevDebug, "Done.\n");
654 fclose(fp);
655 }
656
657 /* initialize the wind randomly. Does both direction and speed in one pass */
658
659 static void init_wind(void)
660 {
661 int x, y;
662
663 for (x=0; x < WEATHERMAPTILESX; x++)
664 for (y=0; y < WEATHERMAPTILESY; y++) {
665 weathermap[x][y].winddir = rndm(1, 8);
666 weathermap[x][y].windspeed = rndm(1, 10);
667 }
668 }
669
670 /* gulf stream */
671
672 static void write_gulfstreammap(void)
673 {
674 char filename[MAX_BUF];
675 FILE *fp;
676 int x, y;
677
678 sprintf(filename, "%s/gulfstreammap", settings.localdir);
679 if ((fp = fopen(filename, "w")) == NULL) {
680 LOG(llevError, "Cannot open %s for writing\n", filename);
681 return;
682 }
683 for (x=0; x < GULF_STREAM_WIDTH; x++) {
684 for (y=0; y < WEATHERMAPTILESY; y++)
685 fprintf(fp, "%d ", gulf_stream_speed[x][y]);
686 fprintf(fp, "\n");
687 }
688 for (x=0; x < GULF_STREAM_WIDTH; x++) {
689 for (y=0; y < WEATHERMAPTILESY; y++)
690 fprintf(fp, "%d ", gulf_stream_dir[x][y]);
691 fprintf(fp, "\n");
692 }
693 fclose(fp);
694 }
695
696 static void read_gulfstreammap(void)
697 {
698 char filename[MAX_BUF];
699 FILE *fp;
700 int x, y;
701
702 sprintf(filename, "%s/gulfstreammap", settings.localdir);
703 LOG(llevDebug, "Reading gulf stream data from %s...", filename);
704 if ((fp = fopen(filename, "r")) == NULL) {
705 LOG(llevError, "Cannot open %s for reading\n", filename);
706 LOG(llevDebug, "Initializing gulf stream maps...");
707 init_gulfstreammap();
708 write_gulfstreammap();
709 LOG(llevDebug, "Done\n");
710 return;
711 }
712 for (x=0; x < GULF_STREAM_WIDTH; x++) {
713 for (y=0; y < WEATHERMAPTILESY; y++) {
714 fscanf(fp, "%d ", &gulf_stream_speed[x][y]);
715 if (gulf_stream_speed[x][y] < 0 ||
716 gulf_stream_speed[x][y] > 120)
717 gulf_stream_speed[x][y] =
718 rndm(GULF_STREAM_BASE_SPEED, GULF_STREAM_BASE_SPEED+10);
719 }
720 fscanf(fp, "\n");
721 }
722 for (x=0; x < GULF_STREAM_WIDTH; x++) {
723 for (y=0; y < WEATHERMAPTILESY; y++) {
724 fscanf(fp, "%d ", &gulf_stream_dir[x][y]);
725 if (gulf_stream_dir[x][y] < 0 ||
726 gulf_stream_dir[x][y] > 120)
727 gulf_stream_dir[x][y] = rndm(1, 8);
728 }
729 fscanf(fp, "\n");
730 }
731 LOG(llevDebug, "Done.\n");
732 fclose(fp);
733 }
734
735 static void init_gulfstreammap(void)
736 {
737 int x, y, tx;
738
739 /* build a gulf stream */
740 x = rndm(GULF_STREAM_WIDTH, WEATHERMAPTILESX-GULF_STREAM_WIDTH);
741 /* doth the great bob inhale or exhale? */
742 gulf_stream_direction = rndm(0, 1);
743 gulf_stream_start = x;
744
745 if (gulf_stream_direction) {
746 for (y=WEATHERMAPTILESY-1; y >= 0; y--) {
747 switch(rndm(0, 6)) {
748 case 0:
749 case 1:
750 case 2:
751 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
752 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
753 GULF_STREAM_BASE_SPEED+10);
754 if (x==0)
755 gulf_stream_dir[tx][y] = 7;
756 else {
757 gulf_stream_dir[tx][y] = 8;
758 if (tx == 0)
759 x--;
760 }
761 }
762 break;
763 case 3:
764 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
765 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
766 GULF_STREAM_BASE_SPEED+10);
767 gulf_stream_dir[tx][y] = 7;
768 }
769 break;
770 case 4:
771 case 5:
772 case 6:
773 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
774 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
775 GULF_STREAM_BASE_SPEED+10);
776 if (x==WEATHERMAPTILESX-1)
777 gulf_stream_dir[tx][y] = 7;
778 else {
779 gulf_stream_dir[tx][y] = 6;
780 if (tx == 0)
781 x++;
782 }
783 }
784 break;
785 }
786 }
787 } else { /* go right to left */
788 for (y=0; y < WEATHERMAPTILESY; y++) {
789 switch(rndm(0, 6)) {
790 case 0:
791 case 1:
792 case 2:
793 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
794 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
795 GULF_STREAM_BASE_SPEED+10);
796 if (x==0)
797 gulf_stream_dir[tx][y] = 3;
798 else {
799 gulf_stream_dir[tx][y] = 2;
800 if (tx == 0)
801 x--;
802 }
803 }
804 break;
805 case 3:
806 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
807 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
808 GULF_STREAM_BASE_SPEED+10);
809 gulf_stream_dir[tx][y] = 3;
810 }
811 break;
812 case 4:
813 case 5:
814 case 6:
815 for (tx=0; tx < GULF_STREAM_WIDTH; tx++) {
816 gulf_stream_speed[tx][y] = rndm(GULF_STREAM_BASE_SPEED,
817 GULF_STREAM_BASE_SPEED+10);
818 if (x==WEATHERMAPTILESX-1)
819 gulf_stream_dir[tx][y] = 3;
820 else {
821 gulf_stream_dir[tx][y] = 4;
822 if (tx == 0)
823 x++;
824 }
825 }
826 break;
827 }
828 }
829 } /* done */
830 }
831
832 /* humidity */
833
834 static void write_humidmap(void)
835 {
836 char filename[MAX_BUF];
837 FILE *fp;
838 int x, y;
839
840 sprintf(filename, "%s/humidmap", settings.localdir);
841 if ((fp = fopen(filename, "w")) == NULL) {
842 LOG(llevError, "Cannot open %s for writing\n", filename);
843 return;
844 }
845 for (x=0; x < WEATHERMAPTILESX; x++) {
846 for (y=0; y < WEATHERMAPTILESY; y++)
847 fprintf(fp, "%d ", weathermap[x][y].humid);
848 fprintf(fp, "\n");
849 }
850 fclose(fp);
851 }
852
853 static void read_humidmap(void)
854 {
855 char filename[MAX_BUF];
856 FILE *fp;
857 int x, y, d;
858
859 sprintf(filename, "%s/humidmap", settings.localdir);
860 LOG(llevDebug, "Reading humidity data from %s...", filename);
861 if ((fp = fopen(filename, "r")) == NULL) {
862 LOG(llevError, "Cannot open %s for reading\n", filename);
863 LOG(llevDebug, "Initializing humidity and elevation maps...");
864 init_humid_elev();
865 write_elevmap();
866 write_humidmap();
867 write_watermap();
868 LOG(llevDebug, "Done\n");
869 return;
870 }
871 for (x=0; x < WEATHERMAPTILESX; x++) {
872 for (y=0; y < WEATHERMAPTILESY; y++) {
873 fscanf(fp, "%d ", &d);
874 weathermap[x][y].humid = d;
875 if (weathermap[x][y].humid < 0 ||
876 weathermap[x][y].humid > 100)
877 weathermap[x][y].humid = rndm(0, 100);
878 }
879 fscanf(fp, "\n");
880 }
881 LOG(llevDebug, "Done.\n");
882 fclose(fp);
883 }
884
885 /* average elevation */
886
887 static void write_elevmap(void)
888 {
889 char filename[MAX_BUF];
890 FILE *fp;
891 int x, y;
892
893 sprintf(filename, "%s/elevmap", settings.localdir);
894 if ((fp = fopen(filename, "w")) == NULL) {
895 LOG(llevError, "Cannot open %s for writing\n", filename);
896 return;
897 }
898 for (x=0; x < WEATHERMAPTILESX; x++) {
899 for (y=0; y < WEATHERMAPTILESY; y++)
900 fprintf(fp, "%d ", weathermap[x][y].avgelev);
901 fprintf(fp, "\n");
902 }
903 fclose(fp);
904 }
905
906 static void read_elevmap(void)
907 {
908 char filename[MAX_BUF];
909 FILE *fp;
910 int x, y;
911
912 sprintf(filename, "%s/elevmap", settings.localdir);
913 LOG(llevDebug, "Reading elevation data from %s...", filename);
914 if ((fp = fopen(filename, "r")) == NULL) {
915 LOG(llevError, "Cannot open %s for reading\n", filename);
916 /* initializing these is expensive, and should have been done
917 by the humidity. It's not worth the wait to do it twice. */
918 return;
919 }
920 for (x=0; x < WEATHERMAPTILESX; x++) {
921 for (y=0; y < WEATHERMAPTILESY; y++) {
922 fscanf(fp, "%d ", &weathermap[x][y].avgelev);
923 if (weathermap[x][y].avgelev < -10000 ||
924 weathermap[x][y].avgelev > 15000)
925 weathermap[x][y].avgelev = rndm(-1000, 10000);
926 }
927 fscanf(fp, "\n");
928 }
929 LOG(llevDebug, "Done.\n");
930 fclose(fp);
931 }
932
933 /* water % */
934
935 static void write_watermap(void)
936 {
937 char filename[MAX_BUF];
938 FILE *fp;
939 int x, y;
940
941 sprintf(filename, "%s/watermap", settings.localdir);
942 if ((fp = fopen(filename, "w")) == NULL) {
943 LOG(llevError, "Cannot open %s for writing\n", filename);
944 return;
945 }
946 for (x=0; x < WEATHERMAPTILESX; x++) {
947 for (y=0; y < WEATHERMAPTILESY; y++)
948 fprintf(fp, "%d ", weathermap[x][y].water);
949 fprintf(fp, "\n");
950 }
951 fclose(fp);
952 }
953
954 static void read_watermap(void)
955 {
956 char filename[MAX_BUF];
957 FILE *fp;
958 int x, y, d;
959
960 sprintf(filename, "%s/watermap", settings.localdir);
961 LOG(llevDebug, "Reading water data from %s...", filename);
962 if ((fp = fopen(filename, "r")) == NULL) {
963 LOG(llevError, "Cannot open %s for reading\n", filename);
964 /* initializing these is expensive, and should have been done
965 by the humidity. It's not worth the wait to do it twice. */
966 return;
967 }
968 for (x=0; x < WEATHERMAPTILESX; x++) {
969 for (y=0; y < WEATHERMAPTILESY; y++) {
970 fscanf(fp, "%d ", &d);
971 weathermap[x][y].water = d;
972 if (weathermap[x][y].water > 100)
973 weathermap[x][y].water = rndm(0, 100);
974 }
975 fscanf(fp, "\n");
976 }
977 LOG(llevDebug, "Done.\n");
978 fclose(fp);
979 }
980
981 /*
982 * initialize both humidity and elevation
983 */
984
985 static void init_humid_elev(void)
986 {
987 int x, y, tx, ty, nx, ny, ax, ay, j;
988 int spwtx, spwty;
989 const char *mapname;
990 long int elev;
991 int water, space;
992 mapstruct *m;
993
994 /* handling of this is kinda nasty. For that reason,
995 * we do the elevation here too. Not because it makes the
996 * code cleaner, or makes handling easier, but because I do *not*
997 * want to maintain two of these nightmares.
998 */
999
1000 spwtx = (settings.worldmaptilesx * settings.worldmaptilesizex) / WEATHERMAPTILESX;
1001 spwty = (settings.worldmaptilesy * settings.worldmaptilesizey) / WEATHERMAPTILESY;
1002 for (x=0; x < WEATHERMAPTILESX; x++) {
1003 for (y=0; y < WEATHERMAPTILESY; y++) {
1004 water = 0;
1005 elev = 0;
1006 nx = 0;
1007 ny = 0;
1008 space = 0;
1009
1010 /* top left */
1011 mapname=weathermap_to_worldmap_corner(x, y, &tx, &ty, 8);
1012 m = load_original_map(mapname, 0);
1013 if (m == NULL)
1014 continue;
1015 m = load_overlay_map(mapname, m);
1016 if (m == NULL)
1017 continue;
1018 for (nx=0,ax=tx; (nx < spwtx && ax < settings.worldmaptilesizex &&
1019 space < spwtx*spwty); ax++,nx++) {
1020 for (ny=0,ay=ty; (ny < spwty && ay < settings.worldmaptilesizey &&
1021 space < spwtx*spwty);
1022 ay++,ny++,space++)
1023 if(GET_MAP_OB(m, ax, ay)){
1024 if (QUERY_FLAG(GET_MAP_OB(m, ax, ay), FLAG_IS_WATER))
1025 water++;
1026 elev += GET_MAP_OB(m, ax, ay)->elevation;
1027 }
1028 }
1029 delete_map(m);
1030
1031 /* bottom left */
1032 mapname=weathermap_to_worldmap_corner(x, y, &tx, &ty, 6);
1033 m = load_original_map(mapname, 0);
1034 if (m == NULL)
1035 continue;
1036 m = load_overlay_map(mapname, m);
1037 if (m == NULL)
1038 continue;
1039 j = ny;
1040 for (nx=0,ax=tx; (nx < spwtx && ax < settings.worldmaptilesizex &&
1041 space < spwtx*spwty); ax++,nx++) {
1042 for (ny=j,ay=MAX(0, ty - (spwty-1)); (ny < spwty && ay <= ty &&
1043 space < spwtx*spwty);
1044 space++,ay++,ny++)
1045 if(GET_MAP_OB(m, ax, ay)){
1046 if (QUERY_FLAG(GET_MAP_OB(m, ax, ay), FLAG_IS_WATER))
1047 water++;
1048 elev += GET_MAP_OB(m, ax, ay)->elevation;
1049 }
1050 }
1051 delete_map(m);
1052
1053 /* top right */
1054 mapname=weathermap_to_worldmap_corner(x, y, &tx, &ty, 2);
1055 m = load_original_map(mapname, 0);
1056 if (m == NULL)
1057 continue;
1058 m = load_overlay_map(mapname, m);
1059 if (m == NULL)
1060 continue;
1061 for (ax=MAX(0, tx - (spwtx-1)); (nx < spwtx && ax < tx &&
1062 space < spwtx*spwty); ax++,nx++) {
1063 for (ny=0,ay=ty; (ny < spwty && ay < settings.worldmaptilesizey &&
1064 space < spwtx*spwty);
1065 ay++,ny++,space++)
1066 if(GET_MAP_OB(m, ax, ay)){
1067 if (QUERY_FLAG(GET_MAP_OB(m, ax, ay), FLAG_IS_WATER))
1068 water++;
1069 elev += GET_MAP_OB(m, ax, ay)->elevation;
1070 }
1071 }
1072 delete_map(m);
1073
1074 /* bottom left */
1075 mapname=weathermap_to_worldmap_corner(x, y, &tx, &ty, 4);
1076 m = load_original_map(mapname, 0);
1077 if (m == NULL)
1078 continue;
1079 m = load_overlay_map(mapname, m);
1080 if (m == NULL)
1081 continue;
1082 for (nx=0,ax=MAX(0, tx - (spwtx-1)); (nx < spwtx && ax < tx &&
1083 space < spwtx*spwty); ax++,nx++) {
1084 for (ny=0,ay=MAX(0, ty - (spwty-1)); (ny < spwty && ay <= ty &&
1085 space < spwtx*spwty);
1086 space++,ay++,ny++)
1087 if(GET_MAP_OB(m, ax, ay)){
1088 if (QUERY_FLAG(GET_MAP_OB(m, ax, ay), FLAG_IS_WATER))
1089 water++;
1090 elev += GET_MAP_OB(m, ax, ay)->elevation;
1091 }
1092 }
1093 delete_map(m);
1094 /* jesus thats confusing as all hell */
1095 weathermap[x][y].humid = water*100/(spwtx*spwty);
1096 weathermap[x][y].avgelev = elev/(spwtx*spwty);
1097 weathermap[x][y].water = weathermap[x][y].humid;
1098 }
1099 }
1100
1101 /* and this does all the real work */
1102 for (x=0; x < WEATHERMAPTILESX; x++)
1103 for (y=0; y < WEATHERMAPTILESY; y++)
1104 weathermap[x][y].humid = humid_tile(x, y);
1105 }
1106
1107 /* temperature */
1108
1109 static void write_temperaturemap(void)
1110 {
1111 char filename[MAX_BUF];
1112 FILE *fp;
1113 int x, y;
1114
1115 sprintf(filename, "%s/temperaturemap", settings.localdir);
1116 if ((fp = fopen(filename, "w")) == NULL) {
1117 LOG(llevError, "Cannot open %s for writing\n", filename);
1118 return;
1119 }
1120 for (x=0; x < WEATHERMAPTILESX; x++) {
1121 for (y=0; y < WEATHERMAPTILESY; y++)
1122 fprintf(fp, "%d ", weathermap[x][y].temp);
1123 fprintf(fp, "\n");
1124 }
1125 fclose(fp);
1126 }
1127
1128 static void read_temperaturemap(void)
1129 {
1130 char filename[MAX_BUF];
1131 FILE *fp;
1132 int x, y;
1133
1134 sprintf(filename, "%s/temperaturemap", settings.localdir);
1135 LOG(llevDebug, "Reading temperature data from %s...", filename);
1136 if ((fp = fopen(filename, "r")) == NULL) {
1137 LOG(llevError, "Cannot open %s for reading\n", filename);
1138 init_temperature();
1139 write_temperaturemap();
1140 return;
1141 }
1142 for (x=0; x < WEATHERMAPTILESX; x++) {
1143 for (y=0; y < WEATHERMAPTILESY; y++) {
1144 fscanf(fp, "%hd ", &weathermap[x][y].temp);
1145 if (weathermap[x][y].temp < -30 ||
1146 weathermap[x][y].temp > 60)
1147 weathermap[x][y].temp = rndm(-10, 40);
1148 }
1149 fscanf(fp, "\n");
1150 }
1151 LOG(llevDebug, "Done.\n");
1152 fclose(fp);
1153 }
1154
1155 static void init_temperature(void)
1156 {
1157 int x, y;
1158 timeofday_t tod;
1159
1160 get_tod(&tod);
1161 for (x=0; x < WEATHERMAPTILESX; x++)
1162 for (y=0; y < WEATHERMAPTILESY; y++)
1163 temperature_calc(x, y, &tod);
1164 }
1165
1166 /* rainfall */
1167
1168 static void write_rainfallmap(void)
1169 {
1170 char filename[MAX_BUF];
1171 FILE *fp;
1172 int x, y;
1173
1174 sprintf(filename, "%s/rainfallmap", settings.localdir);
1175 if ((fp = fopen(filename, "w")) == NULL) {
1176 LOG(llevError, "Cannot open %s for writing\n", filename);
1177 return;
1178 }
1179 for (x=0; x < WEATHERMAPTILESX; x++) {
1180 for (y=0; y < WEATHERMAPTILESY; y++)
1181 fprintf(fp, "%u ", weathermap[x][y].rainfall);
1182 fprintf(fp, "\n");
1183 }
1184 fclose(fp);
1185 }
1186
1187 static void read_rainfallmap(void)
1188 {
1189 char filename[MAX_BUF];
1190 FILE *fp;
1191 int x, y;
1192
1193 sprintf(filename, "%s/rainfallmap", settings.localdir);
1194 LOG(llevDebug, "Reading rainfall data from %s...", filename);
1195 if ((fp = fopen(filename, "r")) == NULL) {
1196 LOG(llevError, "Cannot open %s for reading\n", filename);
1197 init_rainfall();
1198 write_rainfallmap();
1199 return;
1200 }
1201 for (x=0; x < WEATHERMAPTILESX; x++) {
1202 for (y=0; y < WEATHERMAPTILESY; y++) {
1203 fscanf(fp, "%u ", &weathermap[x][y].rainfall);
1204 }
1205 fscanf(fp, "\n");
1206 }
1207 LOG(llevDebug, "Done.\n");
1208 fclose(fp);
1209 }
1210
1211 static void init_rainfall(void)
1212 {
1213 int x, y;
1214 int days;
1215
1216 for (x=0; x < WEATHERMAPTILESX; x++)
1217 for (y=0; y < WEATHERMAPTILESY; y++) {
1218 days = todtick / HOURS_PER_DAY;
1219 if (weathermap[x][y].humid < 10)
1220 weathermap[x][y].rainfall = days / 20;
1221 else if (weathermap[x][y].humid < 20)
1222 weathermap[x][y].rainfall = days / 15;
1223 else if (weathermap[x][y].humid < 30)
1224 weathermap[x][y].rainfall = days / 10;
1225 else if (weathermap[x][y].humid < 40)
1226 weathermap[x][y].rainfall = days / 5;
1227 else if (weathermap[x][y].humid < 50)
1228 weathermap[x][y].rainfall = days / 2;
1229 else if (weathermap[x][y].humid < 60)
1230 weathermap[x][y].rainfall = days;
1231 else if (weathermap[x][y].humid < 80)
1232 weathermap[x][y].rainfall = days * 2;
1233 else
1234 weathermap[x][y].rainfall = days * 3;
1235 }
1236 }
1237
1238 /* END of read/write/init */
1239
1240
1241
1242 static void init_weatheravoid (weather_avoids_t wa[]){
1243 int i;
1244 for (i=0; wa[i].name != NULL; i++) {
1245 wa[i].what=find_archetype(wa[i].name);
1246 }
1247 }
1248
1249 static int wmperformstartx;
1250 static int wmperformstarty;
1251
1252 /*
1253 * This function initializes the weather system. It should be called once,
1254 * at game startup only.
1255 */
1256
1257
1258 void init_weather(void)
1259 {
1260 int y, tx, ty;
1261 char filename[MAX_BUF];
1262 FILE *fp;
1263
1264 /* all this stuff needs to be set, otherwise this function will cause
1265 * chaos and destruction.
1266 */
1267 if (settings.dynamiclevel < 1)
1268 return;
1269 if (settings.worldmapstartx < 1 || settings.worldmapstarty < 1 ||
1270 settings.worldmaptilesx < 1 || settings.worldmaptilesy < 1 ||
1271 settings.worldmaptilesizex < 1 || settings.worldmaptilesizex < 1)
1272 return;
1273 /*prepare structures used for avoidance*/
1274 init_weatheravoid (weather_avoids);
1275 init_weatheravoid (growth_avoids);
1276
1277
1278 LOG(llevDebug, "Initializing the weathermap...\n");
1279
1280 weathermap = (weathermap_t **)malloc(sizeof(weathermap_t *) *
1281 WEATHERMAPTILESX);
1282 if (weathermap == NULL)
1283 fatal(OUT_OF_MEMORY);
1284 for (y=0; y < WEATHERMAPTILESY; y++) {
1285 weathermap[y] = (weathermap_t *)malloc(sizeof(weathermap_t) *
1286 WEATHERMAPTILESY);
1287 if (weathermap[y] == NULL)
1288 fatal(OUT_OF_MEMORY);
1289 }
1290 /* now we load the values in the big worldmap weather array */
1291 /* do not re-order these */
1292 read_pressuremap();
1293 read_winddirmap();
1294 read_windspeedmap();
1295 read_gulfstreammap();
1296 read_watermap();
1297 read_humidmap();
1298 read_elevmap(); /* elevation must allways follow humidity */
1299 read_temperaturemap();
1300 gulf_stream_direction = rndm(0, 1);
1301 for (tx=0; tx < GULF_STREAM_WIDTH; tx++)
1302 for (ty=0; ty < WEATHERMAPTILESY-1; ty++)
1303 if (gulf_stream_direction)
1304 switch (gulf_stream_dir[tx][ty]) {
1305 case 2: gulf_stream_dir[tx][ty] = 6; break;
1306 case 3: gulf_stream_dir[tx][ty] = 7; break;
1307 case 4: gulf_stream_dir[tx][ty] = 8; break;
1308 }
1309 else
1310 switch (gulf_stream_dir[tx][ty]) {
1311 case 6: gulf_stream_dir[tx][ty] = 2; break;
1312 case 7: gulf_stream_dir[tx][ty] = 3; break;
1313 case 8: gulf_stream_dir[tx][ty] = 4; break;
1314 }
1315 gulf_stream_start = rndm(GULF_STREAM_WIDTH, WEATHERMAPTILESY-GULF_STREAM_WIDTH);
1316 read_rainfallmap();
1317
1318 LOG(llevDebug, "Done reading weathermaps\n");
1319 sprintf(filename, "%s/wmapcurpos", settings.localdir);
1320 LOG(llevDebug, "Reading current weather position from %s...", filename);
1321 if ((fp = fopen(filename, "r")) == NULL) {
1322 LOG(llevError, "Can't open %s.\n", filename);
1323 wmperformstartx = -1;
1324 return;
1325 }
1326 fscanf(fp, "%d %d", &wmperformstartx, &wmperformstarty);
1327 LOG(llevDebug, "curposx=%d curposy=%d\n", wmperformstartx, wmperformstarty);
1328 fclose(fp);
1329 if (wmperformstartx > settings.worldmaptilesx)
1330 wmperformstartx = -1;
1331 if (wmperformstarty > settings.worldmaptilesy)
1332 wmperformstarty = 0;
1333 }
1334
1335 /*
1336 * This routine slowly loads the world, patches it up due to the weather,
1337 * and saves it back to disk. In this way, the world constantly feels the
1338 * effects of weather uniformly, without relying on players wandering.
1339 *
1340 * The main point of this is stuff like growing herbs, soil, decaying crap,
1341 * etc etc etc. Not actual *weather*, but weather *effects*.
1342 */
1343
1344 static void perform_weather(void)
1345 {
1346 mapstruct *m;
1347 char filename[MAX_BUF];
1348 FILE *fp;
1349
1350 if (!settings.dynamiclevel)
1351 return;
1352
1353 /* move right to left, top to bottom */
1354 if (wmperformstartx+1 == settings.worldmaptilesx) {
1355 wmperformstartx = 0;
1356 wmperformstarty++;
1357 } else
1358 wmperformstartx++;
1359 if (wmperformstarty == settings.worldmaptilesy)
1360 wmperformstartx = wmperformstarty = 0;
1361
1362 sprintf(filename, "world/world_%d_%d",
1363 wmperformstartx+settings.worldmapstartx,
1364 wmperformstarty+settings.worldmapstarty);
1365
1366 m = ready_map_name(filename, 0);
1367 if (m == NULL)
1368 return; /* hrmm */
1369
1370 /* for now, all we do is decay stuff. more to come */
1371 decay_objects(m);
1372 weather_effect(filename);
1373
1374 /* done */
1375 new_save_map(m, 2); /* write the overlay */
1376 m->in_memory = MAP_IN_MEMORY; /*reset this*/
1377 sprintf(filename, "%s/wmapcurpos", settings.localdir);
1378 if ((fp = fopen(filename, "w")) == NULL) {
1379 LOG(llevError, "Cannot open %s for writing\n", filename);
1380 return;
1381 }
1382
1383 if (players_on_map(m, TRUE) == 0)
1384 delete_map(m);
1385
1386 fprintf(fp, "%d %d", wmperformstartx, wmperformstarty);
1387 fclose(fp);
1388 }
1389
1390 /*
1391 * perform actual effect of weather. Should be called from perform_weather,
1392 * or when a map is loaded. (player enter map). Filename is the name of
1393 * the map. The map *must allready be loaded*.
1394 *
1395 * This is where things like snow, herbs, earthly rototilling, etc should
1396 * occur.
1397 */
1398
1399 void weather_effect(const char *filename)
1400 {
1401 mapstruct *m;
1402 int wx, wy, x, y;
1403
1404 /* if the dm shut off weather, go home */
1405 if (settings.dynamiclevel < 1)
1406 return;
1407
1408 m = ready_map_name(filename, 0);
1409 if (!m->outdoor)
1410 return;
1411
1412 x = 0;
1413 y = 0;
1414 /* for now, just bail if it's not the worldmap */
1415 if (worldmap_to_weathermap(x, y, &wx, &wy, m) != 0)
1416 return;
1417 /*First, calculate temperature*/
1418 calculate_temperature(m, wx, wy);
1419 /* we change the world first, if needed */
1420 if (settings.dynamiclevel >= 5) {
1421 change_the_world(m, wx, wy);
1422 }
1423 if (settings.dynamiclevel >= 2) {
1424 let_it_snow(m, wx, wy);
1425 singing_in_the_rain(m, wx, wy);
1426 }
1427 /* if (settings.dynamiclevel >= 4) {
1428 feather_map(m, wx, wy, filename);
1429 }*/
1430 if (settings.dynamiclevel >= 3) {
1431 plant_a_garden(m, wx, wy);
1432 }
1433 }
1434
1435 /*
1436 * Check the current square to see if we should avoid this one for
1437 * weather processing. Must pass av and gs, which will be filled in
1438 * with 1 or 0. gs will be 1 if we found snow/rain here. av will be
1439 * 1 if we should avoid processing this tile. (don't rain on lakes)
1440 * x and y are the coordinates inside the current map m. If grow is
1441 * 1, we use the growth table, rather than the avoidance table.
1442 *
1443 * Returns the object pointer for any snow item it found, so you can
1444 * destroy/melt it.
1445 */
1446
1447 static object *avoid_weather(int *av, mapstruct *m, int x, int y, int *gs, int grow)
1448 {
1449 int avoid, gotsnow, i, n;
1450
1451 object *tmp;
1452 avoid = 0;
1453 gotsnow = 0;
1454 if (grow) {
1455 for (tmp=GET_MAP_OB(m, x, y), n=0; tmp; tmp = tmp->above, n++) {
1456 /* look for things like walls, holes, etc */
1457 if (n)
1458 if (!QUERY_FLAG (tmp, FLAG_IS_FLOOR) &&
1459 !(tmp->material & M_ICE || tmp->material & M_LIQUID))
1460 gotsnow++;
1461 for (i=0; growth_avoids[i].name != NULL; i++) {
1462 /*if (!strcmp(tmp->arch->name, growth_avoids[i].name)) {*/
1463 if (tmp->arch== growth_avoids[i].what) {
1464 avoid++;
1465 break;
1466 }
1467 }
1468 if (!strncmp(tmp->arch->name, "biglake_", 8)) {
1469 avoid++;
1470 break;
1471 }
1472 if (avoid)
1473 break;
1474 }
1475 } else {
1476 for (tmp=GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1477 for (i=0; weather_avoids[i].name != NULL; i++) {
1478 /*if (!strcmp(tmp->arch->name, weather_avoids[i].name)) {*/
1479 if (tmp->arch == weather_avoids[i].what) {
1480 if (weather_avoids[i].snow == 1)
1481 gotsnow++;
1482 else
1483 avoid++;
1484 break;
1485 }
1486 }
1487 if (avoid || gotsnow)
1488 break;
1489 }
1490 }
1491 *gs = gotsnow;
1492 *av = avoid;
1493 return tmp;
1494 }
1495
1496 /* Temperature is used in a lot of weather function.
1497 * This need to be precalculated before used.
1498 */
1499 static void calculate_temperature(mapstruct *m, int wx, int wy){
1500 int x,y;
1501 for (x=0; x < settings.worldmaptilesizex; x++) {
1502 for (y=0; y < settings.worldmaptilesizey; y++) {
1503 weathermap[wx][wy].realtemp=real_world_temperature(x, y, m);
1504 }
1505 }
1506 }
1507
1508 /*
1509 * Process snow. m is the map we are currently processing. wx and wy are
1510 * the weathermap coordinates for the weathermap square we want to work on.
1511 * filename is the pathname for the current map. This should be called from
1512 * weather_effect()
1513 */
1514
1515 static void let_it_snow(mapstruct *m, int wx, int wy)
1516 {
1517 int x, y, i;
1518 int nx, ny, j, d;
1519 int avoid, two, temp, sky, gotsnow, found, nodstk;
1520 const char *doublestack, *doublestack2;
1521 object *ob, *tmp, *oldsnow, *topfloor;
1522 archetype *at;
1523
1524 for (nx=0; nx < settings.worldmaptilesizex; nx++) {
1525 for (ny=0; ny < settings.worldmaptilesizey; ny++) {
1526 /* jitter factor */
1527 if (rndm(0, 2) > 0) {
1528 x=y=d=-1;
1529 while (OUT_OF_REAL_MAP(m, x, y)) {
1530 d++;
1531 j=rndm(1, 8);
1532 x = nx + freearr_x[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1533 y = ny + freearr_y[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1534 if (d > 15) {
1535 x = nx;
1536 y = ny;
1537 }
1538 }
1539 } else {
1540 x = nx;
1541 y = ny;
1542 }
1543 /* we use the unjittered coordinates */
1544 (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1545 ob = NULL;
1546 at = NULL;
1547 /* this will definately need tuning */
1548 avoid = 0;
1549 two = 0;
1550 gotsnow = 0;
1551 nodstk = 0;
1552 /*temp = real_world_temperature(x, y, m);*/
1553 temp = weathermap[wx][wy].realtemp;
1554 sky = weathermap[wx][wy].sky;
1555 if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG)
1556 sky += 10; /*let it snow*/
1557 oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1558 if (!avoid) {
1559 if (sky >= SKY_LIGHT_SNOW && sky < SKY_HEAVY_SNOW)
1560 at = find_archetype(weather_replace[0].special_snow);
1561 if (sky >= SKY_HEAVY_SNOW)
1562 at = find_archetype(weather_replace[1].special_snow);
1563 if (sky >= SKY_LIGHT_SNOW) {
1564 /* the bottom floor of scorn is not IS_FLOOR */
1565 topfloor=NULL;
1566 for (tmp=GET_MAP_OB(m, x, y); tmp;
1567 topfloor = tmp,tmp = tmp->above) {
1568 if (strcmp(tmp->arch->name, "dungeon_magic") != 0)
1569 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR))
1570 break;
1571 }
1572 /* topfloor should now be the topmost IS_FLOOR=1 */
1573 if (topfloor == NULL)
1574 continue;
1575 if (tmp != NULL)
1576 nodstk++;
1577 /* something is wrong with that sector. just skip it */
1578 found = 0;
1579 for (i=0; weather_replace[i].tile != NULL; i++) {
1580 if (weather_replace[i].arch_or_name == 1) {
1581 if (!strcmp(topfloor->arch->name,
1582 weather_replace[i].tile))
1583 found++;
1584 } else {
1585 if (!strcmp(topfloor->name, weather_replace[i].tile))
1586 found++;
1587 }
1588 if (found) {
1589 if (weather_replace[i].special_snow != NULL)
1590 at = find_archetype(weather_replace[i].special_snow);
1591 if (weather_replace[i].doublestack_arch != NULL
1592 && !nodstk) {
1593 two++;
1594 doublestack = weather_replace[i].doublestack_arch;
1595 }
1596 break;
1597 }
1598 }
1599 }
1600 if (gotsnow && at) {
1601 if (!strcmp(oldsnow->arch->name, at->name))
1602 at = NULL;
1603 else {
1604 remove_ob(oldsnow);
1605 free_object(oldsnow);
1606 tmp=GET_MAP_OB(m, x, y);
1607 /* clean up the trees we put over the snow */
1608 found = 0;
1609 doublestack2 = NULL;
1610 if (tmp)
1611 for (i=0; weather_replace[i].tile != NULL; i++) {
1612 if (weather_replace[i].doublestack_arch == NULL)
1613 continue;
1614 if (weather_replace[i].arch_or_name == 1) {
1615 if (!strcmp(tmp->arch->name,
1616 weather_replace[i].tile))
1617 found++;
1618 } else {
1619 if (!strcmp(tmp->name, weather_replace[i].tile))
1620 found++;
1621 }
1622 if (found) {
1623 tmp = tmp->above;
1624 doublestack2 = weather_replace[i].doublestack_arch;
1625 break;
1626 }
1627 }
1628 if (tmp != NULL && doublestack2 != NULL)
1629 if (strcmp(tmp->arch->name, doublestack2) == 0) {
1630 remove_ob(tmp);
1631 free_object(tmp);
1632 }
1633 }
1634 }
1635 if (at != NULL) {
1636 ob = arch_to_object (at);
1637 ob->x = x;
1638 ob->y = y;
1639 ob->material = M_ICE;
1640 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1641 CLEAR_FLAG(ob, FLAG_IS_FLOOR);
1642 insert_ob_in_map(ob, m, ob,
1643 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1644 if (two) {
1645 at = NULL;
1646 at = find_archetype(doublestack);
1647 if (at != NULL) {
1648 ob = arch_to_object (at);
1649 ob->x = x;
1650 ob->y = y;
1651 insert_ob_in_map(ob, m, ob,
1652 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
1653 }
1654 }
1655 }
1656 }
1657 if (temp > 8 && GET_MAP_OB(m, x, y) !=NULL) {
1658 /* melt some snow */
1659 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1660 avoid = 0;
1661 for (i=0; weather_replace[i].tile != NULL; i++) {
1662 if (weather_replace[i].special_snow == NULL)
1663 continue;
1664 if (!strcmp(tmp->arch->name, weather_replace[i].special_snow))
1665 avoid++;
1666 if (avoid)
1667 break;
1668 }
1669 if (avoid) {
1670 /* replace snow with a big puddle */
1671 remove_ob(tmp);
1672 free_object(tmp);
1673 tmp=GET_MAP_OB(m, x, y);
1674 if (tmp &&(!strcmp(tmp->arch->name, "mountain"))){
1675 at = find_archetype("mountain1_rivlets");}
1676 else if ( tmp && (!strcmp(tmp->arch->name, "mountain2"))){
1677 at = find_archetype("mountain2_rivlets");}
1678 else if (tmp && (!strcmp(tmp->arch->name, "mountain4"))){
1679 at = find_archetype("mountain2_rivlets");}
1680 else {at = find_archetype("rain5");}
1681 if (at != NULL) {
1682 ob = arch_to_object (at);
1683 ob->x = x;
1684 ob->y = y;
1685 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1686 ob->material = M_LIQUID;
1687 insert_ob_in_map(ob, m, ob, INS_NO_MERGE |
1688 INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1689 }
1690 }
1691 }
1692 }
1693 /* woo it's cold out */
1694 if (temp < -8) {
1695 avoid = 0;
1696 for (tmp=GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1697 if (!strcasecmp(tmp->name, "ice"))
1698 avoid--;
1699 }
1700 tmp = GET_MAP_OB(m, x, y);
1701 if (tmp && (!strcasecmp(tmp->name, "sea")))
1702 avoid++;
1703 else if (tmp && (!strcasecmp(tmp->name, "sea1")))
1704 avoid++;
1705 else if (tmp && (!strcasecmp(tmp->name, "deep_sea")))
1706 avoid++;
1707 else if (tmp && (!strcasecmp(tmp->name, "shallow_sea")))
1708 avoid++;
1709 if (avoid > 0) {
1710 at = find_archetype("ice");
1711 ob = arch_to_object (at);
1712 ob->x = x;
1713 ob->y = y;
1714 insert_ob_in_map(ob, m, ob,
1715 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1716 }
1717 }
1718 }
1719 }
1720 }
1721
1722 /*
1723 * Process rain. m is the map we are currently processing. wx and wy are
1724 * the weathermap coordinates for the weathermap square we want to work on.
1725 * filename is the pathname for the current map. This should be called from
1726 * weather_effect()
1727 */
1728
1729 static void singing_in_the_rain(mapstruct *m, int wx, int wy)
1730 {
1731 int x, y, i;
1732 int nx, ny, d, j;
1733 int avoid, two, temp, sky, gotsnow, found, nodstk;
1734 object *ob, *tmp, *oldsnow, *topfloor;
1735 const char *doublestack, *doublestack2;
1736 archetype *at;
1737
1738 for (nx=0; nx < settings.worldmaptilesizex; nx++) {
1739 for (ny=0; ny < settings.worldmaptilesizey; ny++) {
1740 /* jitter factor */
1741 if (rndm(0, 2) > 0) {
1742 x=y=d=-1;
1743 while (OUT_OF_REAL_MAP(m, x, y)) {
1744 d++;
1745 j=rndm(1, 8);
1746 x = nx + freearr_x[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1747 y = ny + freearr_y[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1748 if (d > 15) {
1749 x = nx;
1750 y = ny;
1751 }
1752 }
1753 } else {
1754 x = nx;
1755 y = ny;
1756 }
1757 /* we use the unjittered coordinates */
1758 (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1759 ob = NULL;
1760 at = NULL;
1761 avoid = 0;
1762 two = 0;
1763 gotsnow = 0;
1764 nodstk = 0;
1765 /*temp = real_world_temperature(x, y, m);*/
1766 temp = weathermap[wx][wy].realtemp;
1767 sky = weathermap[wx][wy].sky;
1768 /* it's probably allready snowing */
1769 if (temp < 0)
1770 continue;
1771 oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1772 if (!avoid) {
1773 tmp=GET_MAP_OB(m, x, y);
1774 if (tmp && (!strcmp(tmp->arch->name, "mountain"))){
1775 at = find_archetype("mountain1_rivlets"); break;}
1776 else if (tmp && (!strcmp(tmp->arch->name, "mountain2"))){
1777 at = find_archetype("mountain2_rivlets"); break;}
1778 else if (tmp && (!strcmp(tmp->arch->name, "mountain4"))){
1779 at = find_archetype("mountain2_rivlets"); break;}
1780 if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1781 switch (rndm(0, SKY_HAIL-sky)) {
1782 case 0: at = find_archetype("rain1"); break;
1783 case 1: at = find_archetype("rain2"); break;
1784 default: at = NULL;
1785 }
1786 }
1787 if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE){
1788 switch (rndm(0, SKY_HAIL-sky)) {
1789 case 0: at = find_archetype("rain3"); break;
1790 case 1: at = find_archetype("rain4"); break;
1791 case 2: at = find_archetype("rain5"); break;
1792 default: at = NULL;
1793 }
1794 }
1795 /* the bottom floor of scorn is not IS_FLOOR */
1796 topfloor=NULL;
1797 for (tmp=GET_MAP_OB(m, x, y); tmp;
1798 topfloor = tmp,tmp = tmp->above) {
1799 if (strcmp(tmp->arch->name, "dungeon_magic") != 0)
1800 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR))
1801 break;
1802 }
1803 /* topfloor should now be the topmost IS_FLOOR=1 */
1804 if (topfloor == NULL)
1805 continue;
1806 if (tmp != NULL)
1807 nodstk++;
1808 /* something is wrong with that sector. just skip it */
1809 found = 0;
1810 for (i=0; weather_replace[i].tile != NULL; i++) {
1811 if (weather_replace[i].arch_or_name == 1) {
1812 if (!strcmp(topfloor->arch->name,
1813 weather_replace[i].tile))
1814 found++;
1815 } else {
1816 if (!strcmp(topfloor->name, weather_replace[i].tile))
1817 found++;
1818 }
1819 if (found) {
1820 if (weather_replace[i].doublestack_arch != NULL
1821 && !nodstk) {
1822 two++;
1823 doublestack = weather_replace[i].doublestack_arch;
1824 }
1825 break;
1826 }
1827 }
1828 if (gotsnow && at) {
1829 if (!strcmp(oldsnow->arch->name, at->name))
1830 at = NULL;
1831 else {
1832 tmp=GET_MAP_OB(m, x, y);
1833 remove_ob(oldsnow);
1834 /* clean up the trees we put over the snow */
1835 found = 0;
1836 doublestack2 = NULL;
1837 for (i=0; weather_replace[i].tile != NULL; i++) {
1838 if (weather_replace[i].doublestack_arch == NULL)
1839 continue;
1840 if (weather_replace[i].arch_or_name == 1) {
1841 if (!strcmp(tmp->arch->name,
1842 weather_replace[i].tile))
1843 found++;
1844 } else {
1845 if (!strcmp(tmp->name, weather_replace[i].tile))
1846 found++;
1847 }
1848 if (found) {
1849 tmp = tmp->above;
1850 doublestack2 = weather_replace[i].doublestack_arch;
1851 break;
1852 }
1853 }
1854 free_object(oldsnow);
1855 if (tmp != NULL && doublestack2 != NULL)
1856 if (strcmp(tmp->arch->name, doublestack2) == 0) {
1857 remove_ob(tmp);
1858 free_object(tmp);
1859 }
1860 }
1861 }
1862 if (at != NULL) {
1863 ob = arch_to_object (at);
1864 ob->x = x;
1865 ob->y = y;
1866 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1867 ob->material = M_LIQUID;
1868 insert_ob_in_map(ob, m, ob,
1869 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1870 if (two) {
1871 at = find_archetype(doublestack);
1872 if (at != NULL) {
1873 ob = arch_to_object (at);
1874 ob->x = x;
1875 ob->y = y;
1876 insert_ob_in_map(ob, m, ob,
1877 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
1878 }
1879 }
1880 }
1881 }
1882 /* Things evaporate fast in the heat */
1883 if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1884 /* evaporate */
1885 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1886 avoid = 0;
1887 if (!strcmp(tmp->arch->name, "rain1"))
1888 avoid++;
1889 else if (!strcmp(tmp->arch->name, "rain2"))
1890 avoid++;
1891 else if (!strcmp(tmp->arch->name, "rain3"))
1892 avoid++;
1893 else if (!strcmp(tmp->arch->name, "rain4"))
1894 avoid++;
1895 else if (!strcmp(tmp->arch->name, "rain5"))
1896 avoid++;
1897 else if (!strcmp(tmp->arch->name, "mountain1_rivlets"))
1898 avoid++;
1899 else if (!strcmp(tmp->arch->name, "mountain2_rivlets"))
1900 avoid++;
1901 if (avoid) {
1902 remove_ob(tmp);
1903 free_object(tmp);
1904 if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0)
1905 weathermap[wx][wy].humid++;
1906 tmp=GET_MAP_OB(m, x, y);
1907 /* clean up the trees we put over the rain */
1908 found = 0;
1909 doublestack2 = NULL;
1910 for (i=0; weather_replace[i].tile != NULL; i++) {
1911 if (weather_replace[i].doublestack_arch == NULL)
1912 continue;
1913 if (weather_replace[i].arch_or_name == 1) {
1914 if (!strcmp(tmp->arch->name,
1915 weather_replace[i].tile))
1916 found++;
1917 } else {
1918 if (!strcmp(tmp->name, weather_replace[i].tile))
1919 found++;
1920 }
1921 if (found) {
1922 tmp = tmp->above;
1923 doublestack2 = weather_replace[i].doublestack_arch;
1924 break;
1925 }
1926 }
1927 if (tmp != NULL && doublestack2 != NULL)
1928 if (strcmp(tmp->arch->name, doublestack2) == 0) {
1929 remove_ob(tmp);
1930 free_object(tmp);
1931 }
1932 break;
1933 }
1934 }
1935 }
1936 }
1937 }
1938 }
1939
1940 /*
1941 * Process growth. m is the map we are currently processing. wx and wy are
1942 * the weathermap coordinates for the weathermap square we want to work on.
1943 * filename is the pathname for the current map. This should be called from
1944 * weather_effect()
1945 */
1946
1947 static void plant_a_garden(mapstruct *m, int wx, int wy)
1948 {
1949 int x, y, i;
1950 int avoid, two, temp, sky, gotsnow, found, days;
1951 object *ob, *tmp;
1952 archetype *at;
1953
1954 days = todtick / HOURS_PER_DAY;
1955 for (x=0; x < settings.worldmaptilesizex; x++) {
1956 for (y=0; y < settings.worldmaptilesizey; y++) {
1957 (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1958 ob = NULL;
1959 at = NULL;
1960 avoid = 0;
1961 two = 0;
1962 gotsnow = 0;
1963 /*temp = real_world_temperature(x, y, m);*/
1964 temp = weathermap[wx][wy].realtemp;
1965 sky = weathermap[wx][wy].sky;
1966 (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1967 if (!avoid) {
1968 found = 0;
1969 for (i=0; weather_grow[i].herb != NULL; i++) {
1970 for (tmp=GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1971 if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0)
1972 continue;
1973 /* we found there is a herb here allready */
1974 found++;
1975 if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
1976 (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
1977 weathermap[wx][wy].humid < weather_grow[i].humin ||
1978 weathermap[wx][wy].humid > weather_grow[i].humax ||
1979 temp < weather_grow[i].tempmin ||
1980 temp > weather_grow[i].tempmax ||
1981 rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
1982 /* the herb does not belong, randomly delete
1983 herbs to prevent overgrowth. */
1984 remove_ob(tmp);
1985 free_object(tmp);
1986 break;
1987 }
1988 }
1989 /* don't doublestack herbs */
1990 if (found)
1991 continue;
1992 /* add a random factor */
1993 if (rndm(1, weather_grow[i].random) != 1)
1994 continue;
1995 /* we look up through two tiles for a matching tile */
1996 if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
1997 if (strcmp(GET_MAP_OB(m, x, y)->arch->name,
1998 weather_grow[i].tile) != 0) {
1999 if (GET_MAP_OB(m, x, y)->above != NULL) {
2000 if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name,
2001 weather_grow[i].tile) != 0)
2002 continue;
2003 } else
2004 continue;
2005 }
2006 }
2007 if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2008 (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax)
2009 continue;
2010 if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2011 weathermap[wx][wy].humid > weather_grow[i].humax)
2012 continue;
2013 if (temp < weather_grow[i].tempmin ||
2014 temp > weather_grow[i].tempmax)
2015 continue;
2016 if ((!GET_MAP_OB(m, x, y)) ||
2017 GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2018 GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax)
2019 continue;
2020 /* we got this far.. must be a match */
2021 at = find_archetype(weather_grow[i].herb);
2022 break;
2023 }
2024 if (at != NULL) {
2025 ob = arch_to_object (at);
2026 ob->x = x;
2027 ob->y = y;
2028 /* XXX is this right? maybe.. */
2029 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
2030 insert_ob_in_map(ob, m, ob,
2031 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2032 }
2033 }
2034 }
2035 }
2036 }
2037
2038 /*
2039 * Process worldmap regrowth. m is the map we are currently processing.
2040 * wx and wy are
2041 * the weathermap coordinates for the weathermap square we want to work on.
2042 * This should be called from weather_effect()
2043 */
2044
2045 static void change_the_world(mapstruct *m, int wx, int wy)
2046 {
2047 int x, y, i;
2048 int nx, ny, j, d;
2049 int avoid, two, temp, sky, gotsnow, found, days;
2050 object *ob, *tmp, *doublestack;
2051 archetype *at, *dat;
2052
2053 days = todtick / HOURS_PER_DAY;
2054 for (nx=0; nx < settings.worldmaptilesizex; nx++) {
2055 for (ny=0; ny < settings.worldmaptilesizey; ny++) {
2056 /* jitter factor */
2057 if (rndm(0, 2) > 0) {
2058 x=y=d=-1;
2059 while (OUT_OF_REAL_MAP(m, x, y)) {
2060 d++;
2061 j=rndm(1, 8);
2062 x = nx + freearr_x[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
2063 y = ny + freearr_y[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
2064 if (d > 15) {
2065 x = nx;
2066 y = ny;
2067 }
2068 }
2069 } else {
2070 x = nx;
2071 y = ny;
2072 }
2073 /* we use the unjittered coordinates */
2074 (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2075 ob = NULL;
2076 at = NULL;
2077 dat = NULL;
2078 avoid = 0;
2079 two = 0;
2080 gotsnow = 0;
2081 /*temp = real_world_temperature(x, y, m);*/
2082 temp = weathermap[wx][wy].realtemp;
2083 sky = weathermap[wx][wy].sky;
2084 (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2085 if (!avoid) {
2086 for (i=0; weather_tile[i].herb != NULL; i++) {
2087 found=0;
2088 doublestack=NULL;
2089 if (GET_MAP_OB(m, x, y))
2090 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2091 if (weather_tile[i].tile != NULL)
2092 if (strcmp(tmp->arch->name,
2093 weather_tile[i].tile) == 0) {
2094 doublestack=tmp;
2095 continue;
2096 }
2097 if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0)
2098 continue;
2099 if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2100 (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2101 weathermap[wx][wy].humid < weather_tile[i].humin ||
2102 weathermap[wx][wy].humid > weather_tile[i].humax ||
2103 temp < weather_tile[i].tempmin ||
2104 temp > weather_tile[i].tempmax) {
2105 remove_ob(tmp);
2106 free_object(tmp);
2107 if (doublestack) {
2108 remove_ob(doublestack);
2109 free_object(doublestack);
2110 }
2111 break;
2112 } else {
2113 found++; /* there is one here allready. leave it */
2114 break;
2115 }
2116 }
2117 if (found)
2118 break;
2119 /* add a random factor */
2120 if (rndm(1, weather_tile[i].random) != 1)
2121 continue;
2122 if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2123 (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax)
2124 continue;
2125 if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2126 weathermap[wx][wy].humid > weather_tile[i].humax)
2127 continue;
2128 if (temp < weather_tile[i].tempmin ||
2129 temp > weather_tile[i].tempmax)
2130 continue;
2131 if ( (!GET_MAP_OB(m, x, y)) ||
2132 GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2133 GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax)
2134 continue;
2135 /* we got this far.. must be a match */
2136 if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name,
2137 weather_tile[i].herb) == 0)
2138 break; /* no sense in doubling up */
2139 at = find_archetype(weather_tile[i].herb);
2140 break;
2141 }
2142 if (at != NULL) {
2143 if (weather_tile[i].tile != NULL && GET_MAP_OB(m, x, y) &&
2144 strcmp(weather_tile[i].tile,
2145 GET_MAP_OB(m, x, y)->arch->name) != 0)
2146 dat = find_archetype(weather_tile[i].tile);
2147 if (dat != NULL) {
2148 ob = arch_to_object (dat);
2149 ob->x = x;
2150 ob->y = y;
2151 insert_ob_in_map(ob, m, ob,
2152 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2153 }
2154 if (gotsnow == 0) {
2155 ob = arch_to_object (at);
2156 ob->x = x;
2157 ob->y = y;
2158 if (dat != NULL)
2159 insert_ob_in_map(ob, m, ob,
2160 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
2161 else
2162 insert_ob_in_map(ob, m, ob,
2163 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2164 }
2165 }
2166 }
2167 }
2168 }
2169 }
2170
2171
2172 /*
2173 * Reduce the blockiness of the maps. m is the map we are currently processing.
2174 * wx and wy are
2175 * the weathermap coordinates for the weathermap square we want to work on.
2176 * This should be called from weather_effect()
2177 */
2178
2179 static void feather_map(mapstruct *m, int wx, int wy)
2180 {
2181 int x, y, i, nx, ny, j;
2182 int avoid, two, gotsnow, nodstk;
2183 object *ob, *tmp, *oldsnow, *topfloor, *ntmp, *ntopfloor;
2184 archetype *at;
2185
2186 for (x=0; x < settings.worldmaptilesizex; x++) {
2187 for (y=0; y < settings.worldmaptilesizey; y++) {
2188 (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
2189 ob = NULL;
2190 at = NULL;
2191 avoid = 0;
2192 two = 0;
2193 j = 0;
2194 gotsnow = 0;
2195 nodstk = 0;
2196 oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2197 if (avoid)
2198 continue;
2199 if (rndm(0, 20) == 0)
2200 continue;
2201 /* the bottom floor of scorn is not IS_FLOOR */
2202 topfloor=NULL;
2203 for (tmp=GET_MAP_OB(m, x, y); tmp;
2204 topfloor = tmp,tmp = tmp->above) {
2205 if (strcmp(tmp->arch->name, "dungeon_magic") != 0)
2206 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) &&
2207 !QUERY_FLAG(tmp, FLAG_OVERLAY_FLOOR))
2208 break;
2209 }
2210 /* topfloor should now be the topmost IS_FLOOR=1 */
2211 if (topfloor == NULL)
2212 continue;
2213 if (tmp != NULL)
2214 nodstk++;
2215 /* something is wrong with that sector. just skip it */
2216
2217 j=rndm(1, 8);
2218 nx = freearr_x[j]+x;
2219 ny = freearr_y[j]+y;
2220 if (OUT_OF_REAL_MAP(m, nx, ny))
2221 continue;
2222 oldsnow = avoid_weather(&avoid, m, nx, ny, &gotsnow, 1);
2223 if (avoid)
2224 continue;
2225 ntopfloor=NULL;
2226 for (ntmp=GET_MAP_OB(m, nx, ny); ntmp;
2227 ntopfloor = ntmp,ntmp = ntmp->above) {
2228 if (strcmp(ntmp->arch->name, "dungeon_magic") != 0)
2229 if (!QUERY_FLAG(ntmp, FLAG_IS_FLOOR) &&
2230 !QUERY_FLAG(ntmp, FLAG_OVERLAY_FLOOR))
2231 break;
2232 }
2233 if (ntopfloor != NULL && QUERY_FLAG(ntopfloor, FLAG_IS_FLOOR)) {
2234 remove_ob(topfloor);
2235 free_object(topfloor);
2236 if (tmp != NULL) {
2237 for (i=0; weather_tile[i].herb != NULL; i++) {
2238 if (strcmp(tmp->arch->name, weather_tile[i].herb) == 0) {
2239 remove_ob(tmp);
2240 free_object(tmp);
2241 break;
2242 }
2243 }
2244 }
2245 } else {
2246 continue;
2247 }
2248 ob = arch_to_object (ntopfloor->arch);
2249 ob->x = x;
2250 ob->y = y;
2251 insert_ob_in_map(ob, m, ob, INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2252 if (ntmp != NULL && nodstk == 0) {
2253 for (i=0; weather_tile[i].herb != NULL; i++) {
2254 if (strcmp(ntmp->arch->name, weather_tile[i].herb) == 0) {
2255 ob = arch_to_object (ntmp->arch);
2256 ob->x = x;
2257 ob->y = y;
2258 insert_ob_in_map(ob, m, ob, INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
2259 break;
2260 }
2261 }
2262 }
2263 }
2264 }
2265 }
2266
2267
2268 /* provide wx and wy. Will fill in with weathermap coordinates. Requires
2269 the current mapname (must be a worldmap), and your coordinates on the
2270 map. returns -1 if you give it something it can't figure out. 0 normally.
2271 */
2272
2273 int worldmap_to_weathermap(int x, int y, int *wx, int *wy, mapstruct* m)
2274 {
2275 int spwtx, spwty;
2276 int fx, fy;
2277 int nx, ny;
2278 char* filename=m->path;
2279 spwtx = (settings.worldmaptilesx * settings.worldmaptilesizex) / WEATHERMAPTILESX;
2280 spwty = (settings.worldmaptilesy * settings.worldmaptilesizey) / WEATHERMAPTILESY;
2281
2282 while (*filename == '/')
2283 *filename++;
2284
2285 fx = MAP_WORLDPARTX(m);
2286 fy = MAP_WORLDPARTY(m);
2287 if (fx > settings.worldmapstartx + settings.worldmaptilesx ||
2288 fx < settings.worldmapstartx ||
2289 fy > settings.worldmapstarty + settings.worldmaptilesy ||
2290 fy < settings.worldmapstarty){
2291 LOG(llevDebug, "worldmap_to_weathermap(%s)\n", filename);
2292 sscanf(filename, "world/world_%d_%d", &fx, &fy);
2293 MAP_WORLDPARTX(m)=fx;
2294 MAP_WORLDPARTY(m)=fy;
2295 }
2296 if (fx > settings.worldmapstartx + settings.worldmaptilesx ||
2297 fx < settings.worldmapstartx)
2298 return -1;
2299 if (fy > settings.worldmapstarty + settings.worldmaptilesy ||
2300 fy < settings.worldmapstarty)
2301 return -1;
2302 fx -= settings.worldmapstartx;
2303 fy -= settings.worldmapstarty;
2304
2305 nx = fx * settings.worldmaptilesizex + x;
2306 ny = fy * settings.worldmaptilesizey + y;
2307
2308 *wx = nx/spwtx;
2309 *wy = ny/spwty;
2310
2311 return 0;
2312 }
2313
2314 /* provide x and y. Will fill in with the requested corner of the real
2315 * world map, given the current weathermap section. dir selects which
2316 * corner to return. Valid values are 2 4 6 8 for the corners. return
2317 * value is the name of the map that corner resides in.
2318 */
2319
2320 static const char *weathermap_to_worldmap_corner(int wx, int wy, int *x, int *y, int dir)
2321 {
2322 int spwtx, spwty;
2323 int tx, ty, nx, ny;
2324 static char mapname[ MAX_BUF ];
2325
2326 spwtx = (settings.worldmaptilesx * settings.worldmaptilesizex) / WEATHERMAPTILESX;
2327 spwty = (settings.worldmaptilesy * settings.worldmaptilesizey) / WEATHERMAPTILESY;
2328 switch (dir) {
2329 case 2: wx++; break;
2330 case 4: wx++; wy++; break;
2331 case 6: wy++; break;
2332 case 8: break;
2333 }
2334 if (wx > 0)
2335 tx = (wx*spwtx)-1;
2336 else
2337 tx = wx;
2338 if (wy > 0)
2339 ty = (wy*spwty)-1;
2340 else
2341 ty = wy;
2342
2343 nx = (tx / settings.worldmaptilesizex) + settings.worldmapstartx;
2344 ny = (ty / settings.worldmaptilesizey) + settings.worldmapstarty;
2345 snprintf(mapname, MAX_BUF, "world/world_%d_%d", nx, ny);
2346
2347 *x = tx%settings.worldmaptilesizex;
2348 *y = ty%settings.worldmaptilesizey;
2349 return mapname;
2350 }
2351
2352 /*
2353 * Calculates the distance to the nearest pole. x,y are the weathermap
2354 * coordinates, equator is the current location of the equator. returns
2355 * distance as an int.
2356 */
2357
2358
2359 static int polar_distance(int x, int y, int equator)
2360 {
2361 if ((x+y) > equator) { /* south pole */
2362 x = WEATHERMAPTILESX - x;
2363 y = WEATHERMAPTILESY - y;
2364 return ((x+y)/2);
2365 } else if ((x+y) < equator) { /* north pole */
2366 return ((x+y)/2);
2367 } else {
2368 return equator/2;
2369 }
2370 }
2371
2372 /*
2373 * update the humidity for all weathermap tiles.
2374 */
2375
2376 static void update_humid(void)
2377 {
2378 int x, y;
2379
2380 for (y=0; y < WEATHERMAPTILESY; y++)
2381 for (x=0; x < WEATHERMAPTILESX; x++)
2382 weathermap[x][y].humid = humid_tile(x, y);
2383 }
2384
2385 /*
2386 * calculate the humidity of this tile. x and y are the weathermap coordinates
2387 * we wish to calculate humidity for. Returns the humidity of the weathermap
2388 * square.
2389 */
2390
2391 static int humid_tile(int x, int y)
2392 {
2393 int ox, oy, humid;
2394
2395 ox = x;
2396 oy = y;
2397
2398 /* find the square the wind is coming from, without going out of bounds */
2399
2400 if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
2401 if (y != 0)
2402 oy = y - 1;
2403 }
2404 if (weathermap[x][y].winddir >= 6) {
2405 if (x != 0)
2406 ox = x - 1;
2407 }
2408 if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
2409 if (y < WEATHERMAPTILESY - 1)
2410 oy = y + 1;
2411 }
2412 if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
2413 if (x < WEATHERMAPTILESX - 1)
2414 ox = x + 1;
2415 }
2416 humid = (weathermap[x][y].humid * 2 +
2417 weathermap[ox][oy].humid * weathermap[ox][oy].windspeed +
2418 weathermap[x][y].water + rndm(0, 10)) /
2419 (weathermap[ox][oy].windspeed+3) + rndm(0, 5);
2420 if (humid < 0)
2421 humid = 1;
2422 if (humid > 100)
2423 humid = 100;
2424 return humid;
2425 }
2426
2427 /*
2428 * calculate temperature of the weathermap square x,y. Requires the current
2429 * time of day in *tod.
2430 */
2431
2432 static void temperature_calc(int x, int y, const timeofday_t *tod)
2433 {
2434 int dist, equator, elev, n;
2435 float diff, tdiff;
2436
2437 equator = (WEATHERMAPTILESX + WEATHERMAPTILESY) / 4;
2438 diff = (float)(EQUATOR_BASE_TEMP - POLAR_BASE_TEMP) / (float)equator;
2439 tdiff = (float)SEASONAL_ADJUST / ((float)MONTHS_PER_YEAR / 2.0);
2440 equator *= 2;
2441 n = 0;
2442 /* we essentially move the equator during the season */
2443 if (tod->month > (MONTHS_PER_YEAR / 2)) { /* EOY */
2444 n -= (int) (tod->month * tdiff);
2445 } else {
2446 n = (int) ((MONTHS_PER_YEAR - tod->month) * tdiff);
2447 }
2448 dist = polar_distance(x-n/2, y-n/2, equator);
2449
2450 /* now we have the base temp, unadjusted for time. Time adjustment
2451 is not recorded on the map, rather, it's done JIT. */
2452 weathermap[x][y].temp = (int)(dist * diff);
2453 /* just scrap these for now, its mostly ocean */
2454 if (weathermap[x][y].avgelev < 0)
2455 elev = 0;
2456 else
2457 elev = MAX(10000, weathermap[x][y].avgelev)/1000;
2458 weathermap[x][y].temp -= elev;
2459 }
2460
2461 /* Compute the real (adjusted) temperature of a given weathermap tile.
2462 * This takes into account the wind, base temp, sunlight, and other fun
2463 * things. Seasons are automatically handled by moving the equator.
2464 * Elevation is partially considered in the base temp. x and y are the
2465 * weathermap coordinates.
2466 */
2467
2468 static int real_temperature(int x, int y)
2469 {
2470 int i, temp;
2471 timeofday_t tod;
2472
2473 /* adjust for time of day */
2474 temp = weathermap[x][y].temp;
2475 get_tod(&tod);
2476 for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
2477 temp += season_tempchange[i];
2478 /* high amounts of water has a buffering effect on the temp */
2479 if (weathermap[x][y].water > 33)
2480 i++;
2481 }
2482 for (i = 0; i <= tod.hour; i++) {
2483 temp += season_tempchange[i];
2484 if (weathermap[x][y].water > 33)
2485 i++;
2486 }
2487
2488 /* windchill */
2489 for (i=1; i < weathermap[x][y].windspeed; i+=i)
2490 temp--;
2491
2492 return temp;
2493 }
2494
2495 /* Given a worldmap name, and x and y on that map, compute the temperature
2496 for a specific square. Used to normalize elevation.
2497 */
2498
2499 int real_world_temperature(int x, int y, mapstruct *m)
2500 {
2501 int wx, wy, temp, eleva, elevb;
2502 object *op;
2503 /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
2504 worldmap_to_weathermap(x, y, &wx, &wy, /*m->path*/m);
2505 temp = real_temperature(wx, wy);
2506 if (weathermap[wx][wy].avgelev < 0)
2507 eleva = 0;
2508 else
2509 eleva = weathermap[x][y].avgelev;
2510
2511 op= GET_MAP_OB(m, x, y);
2512 if (!op) return eleva;
2513
2514 elevb = op->elevation;
2515 if (elevb < 0)
2516 elevb = 0;
2517 if (elevb > eleva) {
2518 elevb -= eleva;
2519 temp -= elevb/1000;
2520 } else {
2521 elevb = eleva - elevb;
2522 temp += elevb/1000;
2523 }
2524 return temp;
2525 }
2526
2527 /* this code simply smooths the pressure map */
2528
2529 static void smooth_pressure(void)
2530 {
2531 int x, y;
2532 int k;
2533
2534 for (k=0; k < PRESSURE_ROUNDING_ITER; k++) {
2535 for (x=2; x < WEATHERMAPTILESX-2; x++) {
2536 for (y=2; y < WEATHERMAPTILESY-2; y++) {
2537 weathermap[x][y].pressure = (weathermap[x][y].pressure *
2538 PRESSURE_ROUNDING_FACTOR + weathermap[x-1][y].pressure +
2539 weathermap[x][y-1].pressure + weathermap[x-1][y-1].pressure +
2540 weathermap[x+1][y].pressure + weathermap[x][y+1].pressure +
2541 weathermap[x+1][y+1].pressure + weathermap[x+1][y-1].pressure +
2542 weathermap[x-1][y+1].pressure) / (PRESSURE_ROUNDING_FACTOR+8);
2543 }
2544 }
2545 for (x=WEATHERMAPTILESX-2; x > 2; x--) {
2546 for (y=WEATHERMAPTILESY-2; y > 2; y--) {
2547 weathermap[x][y].pressure = (weathermap[x][y].pressure *
2548 PRESSURE_ROUNDING_FACTOR + weathermap[x-1][y].pressure +
2549 weathermap[x][y-1].pressure + weathermap[x-1][y-1].pressure +
2550 weathermap[x+1][y].pressure + weathermap[x][y+1].pressure +
2551 weathermap[x+1][y+1].pressure + weathermap[x+1][y-1].pressure +
2552 weathermap[x-1][y+1].pressure) / (PRESSURE_ROUNDING_FACTOR+8);
2553 }
2554 }
2555 }
2556
2557 for (x=0; x < WEATHERMAPTILESX; x++)
2558 for (y=0; y < WEATHERMAPTILESY; y++) {
2559 weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
2560 weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
2561 }
2562
2563 }
2564
2565 /*
2566 * perform small randomizations in the pressure map. Then, apply the
2567 * smoothing algorithim.. This causes the pressure to change very slowly
2568 */
2569
2570 static void perform_pressure(void)
2571 {
2572 int x, y, l, n, j, k;
2573
2574 /* create random spikes in the pressure */
2575 for (l=0; l < PRESSURE_SPIKES; l++) {
2576 x = rndm(0, WEATHERMAPTILESX-1);
2577 y = rndm(0, WEATHERMAPTILESY-1);
2578 n = rndm(600, 1300);
2579 weathermap[x][y].pressure = n;
2580 if (x > 5 && y > 5 && x < WEATHERMAPTILESX-5 && y < WEATHERMAPTILESY-5){
2581 for (j=x-2; j<x+2; j++)
2582 for (k=y-2; k<y+2; k++) {
2583 weathermap[j][k].pressure = n;
2584 /* occasionally add a storm */
2585 if (rndm(1, 20) == 1)
2586 weathermap[j][k].humid = rndm(50, 80);
2587 }
2588 }
2589 }
2590
2591 for (x=0; x < WEATHERMAPTILESX; x++)
2592 for (y=0; y < WEATHERMAPTILESY; y++)
2593 weathermap[x][y].pressure += rndm(-1, 4);
2594
2595 smooth_pressure();
2596 }
2597
2598
2599 /*
2600 * is direction a similar to direction b? Find out in this exciting function
2601 * below. Returns 1 if true, 0 for false.
2602 */
2603
2604 int similar_direction(int a, int b)
2605 {
2606 /* shortcut the obvious */
2607 if (a == b)
2608 return 1;
2609
2610 switch(a) {
2611 case 1: if (b <= 2 || b == 8) return 1; break;
2612 case 2: if (b > 0 && b < 4) return 1; break;
2613 case 3: if (b > 1 && b < 5) return 1; break;
2614 case 4: if (b > 2 && b < 6) return 1; break;
2615 case 5: if (b > 3 && b < 7) return 1; break;
2616 case 6: if (b > 4 && b < 8) return 1; break;
2617 case 7: if (b > 5) return 1; break;
2618 case 8: if (b > 6 || b == 1) return 1; break;
2619 }
2620 return 0;
2621 }
2622
2623 /*
2624 * It doesn't really smooth it as such. The main function of this is to
2625 * apply the pressuremap to the wind direction and speed. Then, we run
2626 * a quick pass to update the windspeed.
2627 */
2628
2629 static void smooth_wind(void)
2630 {
2631 int x, y;
2632 int tx, ty, dx, dy;
2633 int minp;
2634
2635 /* skip the outer squares.. it makes handling alot easier */
2636 dx = 0;
2637 dy = 0;
2638 for (x=1; x < WEATHERMAPTILESX-1; x++)
2639 for (y=1; y < WEATHERMAPTILESY-1; y++) {
2640 minp = PRESSURE_MAX + 1;
2641 for (tx=-1; tx < 2; tx++)
2642 for (ty=-1; ty < 2; ty++)
2643 if (!(tx == 0 && ty == 0))
2644 if (weathermap[x+tx][y+ty].pressure < minp) {
2645 minp = weathermap[x+tx][y+ty].pressure;
2646 dx = tx;
2647 dy = ty;
2648 }
2649
2650 /* if the wind is strong, the pressure won't decay it completely */
2651 if (weathermap[x][y].windspeed > 5 &&
2652 !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
2653 weathermap[x][y].windspeed -= 2 * 2;
2654 } else {
2655 weathermap[x][y].winddir = find_dir_2(dx, dy);
2656 weathermap[x][y].windspeed = (sint8) ((weathermap[x][y].pressure -
2657 weathermap[x+dx][y+dy].pressure) * WIND_FACTOR);
2658 }
2659 /* Add in sea breezes. */
2660 weathermap[x][y].windspeed += weathermap[x][y].water / 4;
2661 if (weathermap[x][y].windspeed < 0)
2662 weathermap[x][y].windspeed = 0;
2663 }
2664
2665 /* now, lets crank on the speed. When surrounding tiles all have
2666 the same speed, inc ours. If it's chaos. drop it.
2667 */
2668 for (x=1; x < WEATHERMAPTILESX-1; x++)
2669 for (y=1; y < WEATHERMAPTILESY-1; y++) {
2670 minp = 0;
2671 for (tx=-1; tx < 2; tx++)
2672 for (ty=-1; ty < 2; ty++)
2673 if (ty != 0 && ty != 0)
2674 if (similar_direction(weathermap[x][y].winddir,
2675 weathermap[x+tx][y+ty].winddir))
2676 minp++;
2677 if (minp > 4)
2678 weathermap[x][y].windspeed++;
2679 if (minp > 6)
2680 weathermap[x][y].windspeed += 2;
2681 if (minp < 2)
2682 weathermap[x][y].windspeed--;
2683 if (weathermap[x][y].windspeed < 0)
2684 weathermap[x][y].windspeed = 0;
2685 }
2686 }
2687
2688 /*
2689 * Plot the gulfstream map over the wind map. This is done after the wind,
2690 * to avoid the windsmoothing scrambling the jet stream.
2691 */
2692
2693 static void plot_gulfstream(void)
2694 {
2695 int x, y, tx;
2696
2697 x = gulf_stream_start;
2698
2699 if (gulf_stream_direction) {
2700 for (y=WEATHERMAPTILESY-1; y > 0; y--) {
2701 for (tx=0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
2702 if (similar_direction(weathermap[x+tx][y].winddir,
2703 gulf_stream_dir[tx][y]) &&
2704 weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5)
2705 weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
2706 else
2707 weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
2708 weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
2709 if (tx == GULF_STREAM_WIDTH-1) {
2710 switch (gulf_stream_dir[tx][y]) {
2711 case 6: x--; break;
2712 case 7: break;
2713 case 8: x++; ; break;
2714 }
2715 }
2716 if (x < 0)
2717 x++;
2718 if (x >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2719 x--;
2720 }
2721 }
2722 } else {
2723 for (y=0; y < WEATHERMAPTILESY-1; y++) {
2724 for (tx=0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
2725 if (similar_direction(weathermap[x+tx][y].winddir,
2726 gulf_stream_dir[tx][y]) &&
2727 weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5)
2728 weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
2729 else
2730 weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
2731 weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
2732 if (tx == GULF_STREAM_WIDTH-1) {
2733 switch (gulf_stream_dir[tx][y]) {
2734 case 2: x++; break;
2735 case 3: break;
2736 case 4: x--; break;
2737 }
2738 }
2739 if (x < 0)
2740 x++;
2741 if (x >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2742 x--;
2743 }
2744 }
2745 }
2746 /* occasionally move the stream */
2747 if (rndm(1, 500) == 1) {
2748 gulf_stream_direction = rndm(0, 1);
2749 for (tx=0; tx < GULF_STREAM_WIDTH; tx++)
2750 for (y=0; y < WEATHERMAPTILESY-1; y++)
2751 if (gulf_stream_direction)
2752 switch (gulf_stream_dir[tx][y]) {
2753 case 2: gulf_stream_dir[tx][y] = 6; break;
2754 case 3: gulf_stream_dir[tx][y] = 7; break;
2755 case 4: gulf_stream_dir[tx][y] = 8; break;
2756 }
2757 else
2758 switch (gulf_stream_dir[tx][y]) {
2759 case 6: gulf_stream_dir[tx][y] = 2; break;
2760 case 7: gulf_stream_dir[tx][y] = 3; break;
2761 case 8: gulf_stream_dir[tx][y] = 4; break;
2762 }
2763 }
2764 if (rndm(1, 25) == 1)
2765 gulf_stream_start += rndm(-1, 1);
2766 if (gulf_stream_start >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2767 gulf_stream_start--;
2768 if (gulf_stream_start < 1)
2769 gulf_stream_start++;
2770
2771 }
2772
2773 /*
2774 * let the madness, begin.
2775 *
2776 * This function is the one that ties everything together. Here we loop
2777 * over all the weathermaps, and compare the various conditions we have
2778 * calculated up to now, to figure out what the sky conditions are for this
2779 * square.
2780 */
2781
2782 static void compute_sky(void)
2783 {
2784 int x, y;
2785 int temp;
2786
2787 for (x=0; x < WEATHERMAPTILESX; x++) {
2788 for (y=0; y < WEATHERMAPTILESY; y++) {
2789 temp = real_temperature(x, y);
2790 if (weathermap[x][y].pressure < 980) {
2791 if (weathermap[x][y].humid < 20)
2792 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2793 else if (weathermap[x][y].humid < 30)
2794 weathermap[x][y].sky = SKY_OVERCAST;
2795 else if (weathermap[x][y].humid < 40)
2796 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2797 else if (weathermap[x][y].humid < 55)
2798 weathermap[x][y].sky = SKY_RAIN;
2799 else if (weathermap[x][y].humid < 70)
2800 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2801 else
2802 weathermap[x][y].sky = SKY_HURRICANE;
2803 if (weathermap[x][y].sky < SKY_HURRICANE &&
2804 weathermap[x][y].windspeed > 30)
2805 weathermap[x][y].sky++;
2806 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2807 weathermap[x][y].sky += 10; /*let it snow*/
2808 } else if (weathermap[x][y].pressure < 1000) {
2809 if (weathermap[x][y].humid < 10)
2810 weathermap[x][y].sky = SKY_CLEAR;
2811 else if (weathermap[x][y].humid < 25)
2812 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2813 else if (weathermap[x][y].humid < 45)
2814 weathermap[x][y].sky = SKY_OVERCAST;
2815 else if (weathermap[x][y].humid < 60)
2816 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2817 else if (weathermap[x][y].humid < 75)
2818 weathermap[x][y].sky = SKY_RAIN;
2819 else
2820 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2821 if (weathermap[x][y].sky < SKY_HURRICANE &&
2822 weathermap[x][y].windspeed > 30)
2823 weathermap[x][y].sky++;
2824 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2825 weathermap[x][y].sky += 10; /*let it snow*/
2826 if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
2827 weathermap[x][y].windspeed < 3)
2828 weathermap[x][y].sky = SKY_FOG; /* rare */
2829 if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
2830 weathermap[x][y].windspeed > 35)
2831 weathermap[x][y].sky = SKY_HAIL; /* rare */
2832 } else if (weathermap[x][y].pressure < 1020) {
2833 if (weathermap[x][y].humid < 20)
2834 weathermap[x][y].sky = SKY_CLEAR;
2835 else if (weathermap[x][y].humid < 30)
2836 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2837 else if (weathermap[x][y].humid < 40)
2838 weathermap[x][y].sky = SKY_OVERCAST;
2839 else if (weathermap[x][y].humid < 55)
2840 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2841 else if (weathermap[x][y].humid < 70)
2842 weathermap[x][y].sky = SKY_RAIN;
2843 else
2844 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2845 if (weathermap[x][y].sky < SKY_HURRICANE &&
2846 weathermap[x][y].windspeed > 30)
2847 weathermap[x][y].sky++;
2848 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2849 weathermap[x][y].sky += 10; /*let it snow*/
2850 } else {
2851 if (weathermap[x][y].humid < 35)
2852 weathermap[x][y].sky = SKY_CLEAR;
2853 else if (weathermap[x][y].humid < 55)
2854 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2855 else if (weathermap[x][y].humid < 70)
2856 weathermap[x][y].sky = SKY_OVERCAST;
2857 else if (weathermap[x][y].humid < 85)
2858 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2859 else if (weathermap[x][y].humid < 95)
2860 weathermap[x][y].sky = SKY_RAIN;
2861 else
2862 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2863 if (weathermap[x][y].sky < SKY_HURRICANE &&
2864 weathermap[x][y].windspeed > 30)
2865 weathermap[x][y].sky++;
2866 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2867 weathermap[x][y].sky += 10; /*let it snow*/
2868 }
2869 }
2870 }
2871 }
2872
2873 /*
2874 * Keep track of how much rain has fallen in a given weathermap square.
2875 */
2876
2877 static void process_rain(void)
2878 {
2879 int x, y, rain;
2880
2881 for (x=0; x < WEATHERMAPTILESX; x++)
2882 for (y=0; y < WEATHERMAPTILESY; y++) {
2883 rain = weathermap[x][y].sky;
2884 if (rain >= SKY_LIGHT_SNOW)
2885 rain -= 10;
2886 if (rain > SKY_OVERCAST && rain < SKY_FOG) {
2887 rain -= SKY_OVERCAST;
2888 weathermap[x][y].rainfall += rain;
2889 }
2890 }
2891 }
2892
2893 /*
2894 * The world spinning drags the weather with it.
2895 * The equator is diagonal, and the poles are 45 degrees from north /south.
2896 * What the hell, lets spin the planet backwards.
2897 */
2898
2899 static void spin_globe(void)
2900 {
2901 int x, y;
2902 int buffer_humid;
2903 int buffer_sky;
2904
2905 for (y=0; y < WEATHERMAPTILESY; y++) {
2906 buffer_humid = weathermap[0][y].humid;
2907 buffer_sky = weathermap[0][y].sky;
2908 for (x=0; x < (WEATHERMAPTILESX - 1); x++) {
2909 weathermap[x][y].humid = weathermap[x + 1][y].humid;
2910 weathermap[x][y].sky = weathermap[x + 1][y].sky;
2911 }
2912 weathermap[WEATHERMAPTILESX - 1][y].humid = buffer_humid;
2913 weathermap[WEATHERMAPTILESX - 1][y].sky = buffer_sky;
2914 }
2915 }
2916
2917 /*
2918 * Dump all the weather data as an image.
2919 * Writes two other files that are useful for creating animations and web pages.
2920 */
2921
2922 static void write_weather_images(void)
2923 {
2924 char filename[MAX_BUF];
2925 FILE *fp;
2926 int x, y;
2927 sint32 min[10], max[10], avgrain, avgwind, realmaxwind;
2928 double scale[10], realscalewind;
2929 uint8 pixels[3 * 3 * WEATHERMAPTILESX];
2930 sint64 total_rainfall = 0;
2931 sint64 total_wind = 0;
2932
2933 min[0] = 0; max[0] = 100;
2934 min[1] = 0; max[1] = 0;
2935 min[2] = 0; max[2] = 0;
2936 min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
2937 min[4] = 0; max[4] = 0;
2938 min[5] = 1; max[5] = 8;
2939 min[6] = 0; max[6] = 100;
2940 min[7] = -45; max[7] = 45;
2941 min[8] = 0; max[8] = 16;
2942 min[9] = 0; max[9] = 0;
2943 for (x=0; x < WEATHERMAPTILESX; x++) {
2944 for (y=0; y < WEATHERMAPTILESY; y++) {
2945 /* min[0] = MIN(min[0], weathermap[x][y].water); */
2946 min[1] = MIN(min[1], weathermap[x][y].avgelev);
2947 min[2] = MIN(min[2], weathermap[x][y].rainfall);
2948 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
2949 min[4] = MIN(min[4], weathermap[x][y].windspeed);
2950 /* min[5] = MIN(min[5], weathermap[x][y].winddir); */
2951 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
2952 /* min[7] = MIN(min[7], real_temp[x][y]); */
2953 /* min[8] = MIN(min[8], weathermap[x][y].sky); */
2954 /* min[9] = MIN(min[9], weathermap[x][y].darkness); */
2955
2956 /* max[0] = MAX(max[0], weathermap[x][y].water); */
2957 max[1] = MAX(max[1], weathermap[x][y].avgelev);
2958 max[2] = MAX(max[2], weathermap[x][y].rainfall);
2959 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
2960 max[4] = MAX(max[4], weathermap[x][y].windspeed);
2961 /* max[5] = MAX(max[5], weathermap[x][y].winddir); */
2962 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
2963 /* max[7] = MAX(max[7], real_temp[x][y]); */
2964 /* max[8] = MAX(max[8], weathermap[x][y].sky); */
2965 /* max[9] = MAX(max[9], weathermap[x][y].darkness); */
2966 total_rainfall += weathermap[x][y].rainfall;
2967 total_wind += weathermap[x][y].windspeed;
2968 }
2969 }
2970 avgrain = total_rainfall / (WEATHERMAPTILESX * WEATHERMAPTILESY);
2971 avgwind = (total_wind / ((WEATHERMAPTILESX * WEATHERMAPTILESY) * 3 / 2));
2972 max[2] = avgrain - 1;
2973 realscalewind = 255.0l / (max[4] - min[4]);
2974 realmaxwind = max[4];
2975 max[4] = avgwind - 1;
2976 for (x=0; x < 10; x++)
2977 scale[x] = 255.0l / (max[x] - min[x]);
2978
2979 sprintf(filename, "%s/weather.ppm", settings.localdir);
2980 if ((fp = fopen(filename, "w")) == NULL) {
2981 LOG(llevError, "Cannot open %s for writing\n", filename);
2982 return;
2983 }
2984 fprintf(fp, "P6\n%d %d\n", 3 * WEATHERMAPTILESX, 3 * WEATHERMAPTILESY);
2985 fprintf(fp, "255\n");
2986 for (y=0; y < WEATHERMAPTILESY; y++) {
2987 for (x=0; x < (3 * 3 * WEATHERMAPTILESX); x++)
2988 pixels[x] = 0;
2989 for (x=0; x < WEATHERMAPTILESX; x++) {
2990 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].water - min[0]) * scale[0]);
2991 if (weathermap[x][y].avgelev >= 0)
2992 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((weathermap[x][y].avgelev - min[1]) * scale[1]);
2993 else
2994 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].avgelev - min[1]) * scale[1]);
2995 if (weathermap[x][y].rainfall >= avgrain) /* rainfall is rather spikey, this gives us more detail. */
2996 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = 255;
2997 else
2998 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].rainfall - min[2]) * scale[2]);
2999 }
3000 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3001 }
3002 for (y=0; y < WEATHERMAPTILESY; y++) {
3003 for (x=0; x < WEATHERMAPTILESX; x++) {
3004 uint32 dir = directions[weathermap[x][y].winddir - 1];
3005 uint32 speed = weathermap[x][y].windspeed;
3006 uint8 pressure = (uint8) ((weathermap[x][y].pressure - min[3]) * scale[3]);
3007 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + RED)] = pressure;
3008 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + GREEN)] = pressure;
3009 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = pressure;
3010 if (speed < avgwind) {
3011 speed = (uint32) ((speed - min[4]) * scale[4]);
3012 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = speed;
3013 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = speed;
3014 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = speed;
3015 } else {
3016 speed = (uint32) ((speed - realmaxwind) * realscalewind);
3017 /* if (speed < 100)*/
3018 /* speed = 100;*/
3019 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = speed;
3020 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = 0;
3021 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = 0;
3022 }
3023 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((dir & 0x00FF0000) >> 16);
3024 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((dir & 0x0000FF00) >> 8);
3025 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((dir & 0x000000FF));
3026 }
3027 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3028 }
3029 for (y=0; y < WEATHERMAPTILESY; y++) {
3030 for (x=0; x < (3 * 3 * WEATHERMAPTILESX); x++)
3031 pixels[x] = 0;
3032 for (x=0; x < WEATHERMAPTILESX; x++) {
3033 uint32 dir = skies[weathermap[x][y].sky];
3034 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].humid - min[6]) * scale[6]);
3035 /*pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((real_temp[x][y] - min[7]) * scale[7]);*/
3036 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = 1;
3037 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((dir & 0x00FF0000) >> 16);
3038 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((dir & 0x0000FF00) >> 8);
3039 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((dir & 0x000000FF));
3040
3041 }
3042 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3043 }
3044 fclose(fp);
3045
3046 sprintf(filename, "%s/todtick", settings.localdir);
3047 if ((fp = fopen(filename, "w")) == NULL) {
3048 LOG(llevError, "Cannot open %s for writing\n", filename);
3049 return;
3050 }
3051 fprintf(fp, "%lu", todtick);
3052 fclose(fp);
3053 }