ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/weather.c
Revision: 1.3
Committed: Sun Aug 13 17:16:05 2006 UTC (17 years, 9 months ago) by elmex
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +1 -1 lines
State: FILE REMOVED
Log Message:
Made server compile with C++.
Removed cfanim plugin and crossedit.
C++ here we come.

File Contents

# Content
1 /*
2 * static char *rcsid_weather_c =
3 * "$Id$";
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 = get_object();
1637 copy_object(&at->clone, ob);
1638 ob->x = x;
1639 ob->y = y;
1640 ob->material = M_ICE;
1641 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1642 CLEAR_FLAG(ob, FLAG_IS_FLOOR);
1643 insert_ob_in_map(ob, m, ob,
1644 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1645 if (two) {
1646 at = NULL;
1647 at = find_archetype(doublestack);
1648 if (at != NULL) {
1649 ob = get_object();
1650 copy_object(&at->clone, ob);
1651 ob->x = x;
1652 ob->y = y;
1653 insert_ob_in_map(ob, m, ob,
1654 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
1655 }
1656 }
1657 }
1658 }
1659 if (temp > 8 && GET_MAP_OB(m, x, y) !=NULL) {
1660 /* melt some snow */
1661 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1662 avoid = 0;
1663 for (i=0; weather_replace[i].tile != NULL; i++) {
1664 if (weather_replace[i].special_snow == NULL)
1665 continue;
1666 if (!strcmp(tmp->arch->name, weather_replace[i].special_snow))
1667 avoid++;
1668 if (avoid)
1669 break;
1670 }
1671 if (avoid) {
1672 /* replace snow with a big puddle */
1673 remove_ob(tmp);
1674 free_object(tmp);
1675 tmp=GET_MAP_OB(m, x, y);
1676 if (tmp &&(!strcmp(tmp->arch->name, "mountain"))){
1677 at = find_archetype("mountain1_rivlets");}
1678 else if ( tmp && (!strcmp(tmp->arch->name, "mountain2"))){
1679 at = find_archetype("mountain2_rivlets");}
1680 else if (tmp && (!strcmp(tmp->arch->name, "mountain4"))){
1681 at = find_archetype("mountain2_rivlets");}
1682 else {at = find_archetype("rain5");}
1683 if (at != NULL) {
1684 ob = get_object();
1685 copy_object(&at->clone, ob);
1686 ob->x = x;
1687 ob->y = y;
1688 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1689 ob->material = M_LIQUID;
1690 insert_ob_in_map(ob, m, ob, INS_NO_MERGE |
1691 INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1692 }
1693 }
1694 }
1695 }
1696 /* woo it's cold out */
1697 if (temp < -8) {
1698 avoid = 0;
1699 for (tmp=GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1700 if (!strcasecmp(tmp->name, "ice"))
1701 avoid--;
1702 }
1703 tmp = GET_MAP_OB(m, x, y);
1704 if (tmp && (!strcasecmp(tmp->name, "sea")))
1705 avoid++;
1706 else if (tmp && (!strcasecmp(tmp->name, "sea1")))
1707 avoid++;
1708 else if (tmp && (!strcasecmp(tmp->name, "deep_sea")))
1709 avoid++;
1710 else if (tmp && (!strcasecmp(tmp->name, "shallow_sea")))
1711 avoid++;
1712 if (avoid > 0) {
1713 at = find_archetype("ice");
1714 ob = get_object();
1715 copy_object(&at->clone, ob);
1716 ob->x = x;
1717 ob->y = y;
1718 insert_ob_in_map(ob, m, ob,
1719 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1720 }
1721 }
1722 }
1723 }
1724 }
1725
1726 /*
1727 * Process rain. m is the map we are currently processing. wx and wy are
1728 * the weathermap coordinates for the weathermap square we want to work on.
1729 * filename is the pathname for the current map. This should be called from
1730 * weather_effect()
1731 */
1732
1733 static void singing_in_the_rain(mapstruct *m, int wx, int wy)
1734 {
1735 int x, y, i;
1736 int nx, ny, d, j;
1737 int avoid, two, temp, sky, gotsnow, found, nodstk;
1738 object *ob, *tmp, *oldsnow, *topfloor;
1739 const char *doublestack, *doublestack2;
1740 archetype *at;
1741
1742 for (nx=0; nx < settings.worldmaptilesizex; nx++) {
1743 for (ny=0; ny < settings.worldmaptilesizey; ny++) {
1744 /* jitter factor */
1745 if (rndm(0, 2) > 0) {
1746 x=y=d=-1;
1747 while (OUT_OF_REAL_MAP(m, x, y)) {
1748 d++;
1749 j=rndm(1, 8);
1750 x = nx + freearr_x[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1751 y = ny + freearr_y[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
1752 if (d > 15) {
1753 x = nx;
1754 y = ny;
1755 }
1756 }
1757 } else {
1758 x = nx;
1759 y = ny;
1760 }
1761 /* we use the unjittered coordinates */
1762 (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1763 ob = NULL;
1764 at = NULL;
1765 avoid = 0;
1766 two = 0;
1767 gotsnow = 0;
1768 nodstk = 0;
1769 /*temp = real_world_temperature(x, y, m);*/
1770 temp = weathermap[wx][wy].realtemp;
1771 sky = weathermap[wx][wy].sky;
1772 /* it's probably allready snowing */
1773 if (temp < 0)
1774 continue;
1775 oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1776 if (!avoid) {
1777 tmp=GET_MAP_OB(m, x, y);
1778 if (tmp && (!strcmp(tmp->arch->name, "mountain"))){
1779 at = find_archetype("mountain1_rivlets"); break;}
1780 else if (tmp && (!strcmp(tmp->arch->name, "mountain2"))){
1781 at = find_archetype("mountain2_rivlets"); break;}
1782 else if (tmp && (!strcmp(tmp->arch->name, "mountain4"))){
1783 at = find_archetype("mountain2_rivlets"); break;}
1784 if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1785 switch (rndm(0, SKY_HAIL-sky)) {
1786 case 0: at = find_archetype("rain1"); break;
1787 case 1: at = find_archetype("rain2"); break;
1788 default: at = NULL;
1789 }
1790 }
1791 if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE){
1792 switch (rndm(0, SKY_HAIL-sky)) {
1793 case 0: at = find_archetype("rain3"); break;
1794 case 1: at = find_archetype("rain4"); break;
1795 case 2: at = find_archetype("rain5"); break;
1796 default: at = NULL;
1797 }
1798 }
1799 /* the bottom floor of scorn is not IS_FLOOR */
1800 topfloor=NULL;
1801 for (tmp=GET_MAP_OB(m, x, y); tmp;
1802 topfloor = tmp,tmp = tmp->above) {
1803 if (strcmp(tmp->arch->name, "dungeon_magic") != 0)
1804 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR))
1805 break;
1806 }
1807 /* topfloor should now be the topmost IS_FLOOR=1 */
1808 if (topfloor == NULL)
1809 continue;
1810 if (tmp != NULL)
1811 nodstk++;
1812 /* something is wrong with that sector. just skip it */
1813 found = 0;
1814 for (i=0; weather_replace[i].tile != NULL; i++) {
1815 if (weather_replace[i].arch_or_name == 1) {
1816 if (!strcmp(topfloor->arch->name,
1817 weather_replace[i].tile))
1818 found++;
1819 } else {
1820 if (!strcmp(topfloor->name, weather_replace[i].tile))
1821 found++;
1822 }
1823 if (found) {
1824 if (weather_replace[i].doublestack_arch != NULL
1825 && !nodstk) {
1826 two++;
1827 doublestack = weather_replace[i].doublestack_arch;
1828 }
1829 break;
1830 }
1831 }
1832 if (gotsnow && at) {
1833 if (!strcmp(oldsnow->arch->name, at->name))
1834 at = NULL;
1835 else {
1836 tmp=GET_MAP_OB(m, x, y);
1837 remove_ob(oldsnow);
1838 /* clean up the trees we put over the snow */
1839 found = 0;
1840 doublestack2 = NULL;
1841 for (i=0; weather_replace[i].tile != NULL; i++) {
1842 if (weather_replace[i].doublestack_arch == NULL)
1843 continue;
1844 if (weather_replace[i].arch_or_name == 1) {
1845 if (!strcmp(tmp->arch->name,
1846 weather_replace[i].tile))
1847 found++;
1848 } else {
1849 if (!strcmp(tmp->name, weather_replace[i].tile))
1850 found++;
1851 }
1852 if (found) {
1853 tmp = tmp->above;
1854 doublestack2 = weather_replace[i].doublestack_arch;
1855 break;
1856 }
1857 }
1858 free_object(oldsnow);
1859 if (tmp != NULL && doublestack2 != NULL)
1860 if (strcmp(tmp->arch->name, doublestack2) == 0) {
1861 remove_ob(tmp);
1862 free_object(tmp);
1863 }
1864 }
1865 }
1866 if (at != NULL) {
1867 ob = get_object();
1868 copy_object(&at->clone, ob);
1869 ob->x = x;
1870 ob->y = y;
1871 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
1872 ob->material = M_LIQUID;
1873 insert_ob_in_map(ob, m, ob,
1874 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
1875 if (two) {
1876 at = find_archetype(doublestack);
1877 if (at != NULL) {
1878 ob = get_object();
1879 copy_object(&at->clone, ob);
1880 ob->x = x;
1881 ob->y = y;
1882 insert_ob_in_map(ob, m, ob,
1883 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
1884 }
1885 }
1886 }
1887 }
1888 /* Things evaporate fast in the heat */
1889 if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1890 /* evaporate */
1891 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1892 avoid = 0;
1893 if (!strcmp(tmp->arch->name, "rain1"))
1894 avoid++;
1895 else if (!strcmp(tmp->arch->name, "rain2"))
1896 avoid++;
1897 else if (!strcmp(tmp->arch->name, "rain3"))
1898 avoid++;
1899 else if (!strcmp(tmp->arch->name, "rain4"))
1900 avoid++;
1901 else if (!strcmp(tmp->arch->name, "rain5"))
1902 avoid++;
1903 else if (!strcmp(tmp->arch->name, "mountain1_rivlets"))
1904 avoid++;
1905 else if (!strcmp(tmp->arch->name, "mountain2_rivlets"))
1906 avoid++;
1907 if (avoid) {
1908 remove_ob(tmp);
1909 free_object(tmp);
1910 if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0)
1911 weathermap[wx][wy].humid++;
1912 tmp=GET_MAP_OB(m, x, y);
1913 /* clean up the trees we put over the rain */
1914 found = 0;
1915 doublestack2 = NULL;
1916 for (i=0; weather_replace[i].tile != NULL; i++) {
1917 if (weather_replace[i].doublestack_arch == NULL)
1918 continue;
1919 if (weather_replace[i].arch_or_name == 1) {
1920 if (!strcmp(tmp->arch->name,
1921 weather_replace[i].tile))
1922 found++;
1923 } else {
1924 if (!strcmp(tmp->name, weather_replace[i].tile))
1925 found++;
1926 }
1927 if (found) {
1928 tmp = tmp->above;
1929 doublestack2 = weather_replace[i].doublestack_arch;
1930 break;
1931 }
1932 }
1933 if (tmp != NULL && doublestack2 != NULL)
1934 if (strcmp(tmp->arch->name, doublestack2) == 0) {
1935 remove_ob(tmp);
1936 free_object(tmp);
1937 }
1938 break;
1939 }
1940 }
1941 }
1942 }
1943 }
1944 }
1945
1946 /*
1947 * Process growth. m is the map we are currently processing. wx and wy are
1948 * the weathermap coordinates for the weathermap square we want to work on.
1949 * filename is the pathname for the current map. This should be called from
1950 * weather_effect()
1951 */
1952
1953 static void plant_a_garden(mapstruct *m, int wx, int wy)
1954 {
1955 int x, y, i;
1956 int avoid, two, temp, sky, gotsnow, found, days;
1957 object *ob, *tmp;
1958 archetype *at;
1959
1960 days = todtick / HOURS_PER_DAY;
1961 for (x=0; x < settings.worldmaptilesizex; x++) {
1962 for (y=0; y < settings.worldmaptilesizey; y++) {
1963 (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1964 ob = NULL;
1965 at = NULL;
1966 avoid = 0;
1967 two = 0;
1968 gotsnow = 0;
1969 /*temp = real_world_temperature(x, y, m);*/
1970 temp = weathermap[wx][wy].realtemp;
1971 sky = weathermap[wx][wy].sky;
1972 (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1973 if (!avoid) {
1974 found = 0;
1975 for (i=0; weather_grow[i].herb != NULL; i++) {
1976 for (tmp=GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1977 if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0)
1978 continue;
1979 /* we found there is a herb here allready */
1980 found++;
1981 if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
1982 (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
1983 weathermap[wx][wy].humid < weather_grow[i].humin ||
1984 weathermap[wx][wy].humid > weather_grow[i].humax ||
1985 temp < weather_grow[i].tempmin ||
1986 temp > weather_grow[i].tempmax ||
1987 rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
1988 /* the herb does not belong, randomly delete
1989 herbs to prevent overgrowth. */
1990 remove_ob(tmp);
1991 free_object(tmp);
1992 break;
1993 }
1994 }
1995 /* don't doublestack herbs */
1996 if (found)
1997 continue;
1998 /* add a random factor */
1999 if (rndm(1, weather_grow[i].random) != 1)
2000 continue;
2001 /* we look up through two tiles for a matching tile */
2002 if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
2003 if (strcmp(GET_MAP_OB(m, x, y)->arch->name,
2004 weather_grow[i].tile) != 0) {
2005 if (GET_MAP_OB(m, x, y)->above != NULL) {
2006 if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name,
2007 weather_grow[i].tile) != 0)
2008 continue;
2009 } else
2010 continue;
2011 }
2012 }
2013 if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2014 (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax)
2015 continue;
2016 if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2017 weathermap[wx][wy].humid > weather_grow[i].humax)
2018 continue;
2019 if (temp < weather_grow[i].tempmin ||
2020 temp > weather_grow[i].tempmax)
2021 continue;
2022 if ((!GET_MAP_OB(m, x, y)) ||
2023 GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2024 GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax)
2025 continue;
2026 /* we got this far.. must be a match */
2027 at = find_archetype(weather_grow[i].herb);
2028 break;
2029 }
2030 if (at != NULL) {
2031 ob = get_object();
2032 copy_object(&at->clone, ob);
2033 ob->x = x;
2034 ob->y = y;
2035 /* XXX is this right? maybe.. */
2036 SET_FLAG(ob, FLAG_OVERLAY_FLOOR);
2037 insert_ob_in_map(ob, m, ob,
2038 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2039 }
2040 }
2041 }
2042 }
2043 }
2044
2045 /*
2046 * Process worldmap regrowth. m is the map we are currently processing.
2047 * wx and wy are
2048 * the weathermap coordinates for the weathermap square we want to work on.
2049 * This should be called from weather_effect()
2050 */
2051
2052 static void change_the_world(mapstruct *m, int wx, int wy)
2053 {
2054 int x, y, i;
2055 int nx, ny, j, d;
2056 int avoid, two, temp, sky, gotsnow, found, days;
2057 object *ob, *tmp, *doublestack;
2058 archetype *at, *dat;
2059
2060 days = todtick / HOURS_PER_DAY;
2061 for (nx=0; nx < settings.worldmaptilesizex; nx++) {
2062 for (ny=0; ny < settings.worldmaptilesizey; ny++) {
2063 /* jitter factor */
2064 if (rndm(0, 2) > 0) {
2065 x=y=d=-1;
2066 while (OUT_OF_REAL_MAP(m, x, y)) {
2067 d++;
2068 j=rndm(1, 8);
2069 x = nx + freearr_x[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
2070 y = ny + freearr_y[j] * (rndm(0, 1) + rndm(0, 1) + rndm (0, 1) +1);
2071 if (d > 15) {
2072 x = nx;
2073 y = ny;
2074 }
2075 }
2076 } else {
2077 x = nx;
2078 y = ny;
2079 }
2080 /* we use the unjittered coordinates */
2081 (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2082 ob = NULL;
2083 at = NULL;
2084 dat = NULL;
2085 avoid = 0;
2086 two = 0;
2087 gotsnow = 0;
2088 /*temp = real_world_temperature(x, y, m);*/
2089 temp = weathermap[wx][wy].realtemp;
2090 sky = weathermap[wx][wy].sky;
2091 (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2092 if (!avoid) {
2093 for (i=0; weather_tile[i].herb != NULL; i++) {
2094 found=0;
2095 doublestack=NULL;
2096 if (GET_MAP_OB(m, x, y))
2097 for (tmp=GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2098 if (weather_tile[i].tile != NULL)
2099 if (strcmp(tmp->arch->name,
2100 weather_tile[i].tile) == 0) {
2101 doublestack=tmp;
2102 continue;
2103 }
2104 if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0)
2105 continue;
2106 if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2107 (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2108 weathermap[wx][wy].humid < weather_tile[i].humin ||
2109 weathermap[wx][wy].humid > weather_tile[i].humax ||
2110 temp < weather_tile[i].tempmin ||
2111 temp > weather_tile[i].tempmax) {
2112 remove_ob(tmp);
2113 free_object(tmp);
2114 if (doublestack) {
2115 remove_ob(doublestack);
2116 free_object(doublestack);
2117 }
2118 break;
2119 } else {
2120 found++; /* there is one here allready. leave it */
2121 break;
2122 }
2123 }
2124 if (found)
2125 break;
2126 /* add a random factor */
2127 if (rndm(1, weather_tile[i].random) != 1)
2128 continue;
2129 if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2130 (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax)
2131 continue;
2132 if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2133 weathermap[wx][wy].humid > weather_tile[i].humax)
2134 continue;
2135 if (temp < weather_tile[i].tempmin ||
2136 temp > weather_tile[i].tempmax)
2137 continue;
2138 if ( (!GET_MAP_OB(m, x, y)) ||
2139 GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2140 GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax)
2141 continue;
2142 /* we got this far.. must be a match */
2143 if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name,
2144 weather_tile[i].herb) == 0)
2145 break; /* no sense in doubling up */
2146 at = find_archetype(weather_tile[i].herb);
2147 break;
2148 }
2149 if (at != NULL) {
2150 if (weather_tile[i].tile != NULL && GET_MAP_OB(m, x, y) &&
2151 strcmp(weather_tile[i].tile,
2152 GET_MAP_OB(m, x, y)->arch->name) != 0)
2153 dat = find_archetype(weather_tile[i].tile);
2154 if (dat != NULL) {
2155 ob = get_object();
2156 copy_object(&dat->clone, ob);
2157 ob->x = x;
2158 ob->y = y;
2159 insert_ob_in_map(ob, m, ob,
2160 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2161 }
2162 if (gotsnow == 0) {
2163 ob = get_object();
2164 copy_object(&at->clone, ob);
2165 ob->x = x;
2166 ob->y = y;
2167 if (dat != NULL)
2168 insert_ob_in_map(ob, m, ob,
2169 INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
2170 else
2171 insert_ob_in_map(ob, m, ob,
2172 INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2173 }
2174 }
2175 }
2176 }
2177 }
2178 }
2179
2180
2181 /*
2182 * Reduce the blockiness of the maps. m is the map we are currently processing.
2183 * wx and wy are
2184 * the weathermap coordinates for the weathermap square we want to work on.
2185 * This should be called from weather_effect()
2186 */
2187
2188 static void feather_map(mapstruct *m, int wx, int wy)
2189 {
2190 int x, y, i, nx, ny, j;
2191 int avoid, two, gotsnow, nodstk;
2192 object *ob, *tmp, *oldsnow, *topfloor, *ntmp, *ntopfloor;
2193 archetype *at;
2194
2195 for (x=0; x < settings.worldmaptilesizex; x++) {
2196 for (y=0; y < settings.worldmaptilesizey; y++) {
2197 (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
2198 ob = NULL;
2199 at = NULL;
2200 avoid = 0;
2201 two = 0;
2202 j = 0;
2203 gotsnow = 0;
2204 nodstk = 0;
2205 oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2206 if (avoid)
2207 continue;
2208 if (rndm(0, 20) == 0)
2209 continue;
2210 /* the bottom floor of scorn is not IS_FLOOR */
2211 topfloor=NULL;
2212 for (tmp=GET_MAP_OB(m, x, y); tmp;
2213 topfloor = tmp,tmp = tmp->above) {
2214 if (strcmp(tmp->arch->name, "dungeon_magic") != 0)
2215 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) &&
2216 !QUERY_FLAG(tmp, FLAG_OVERLAY_FLOOR))
2217 break;
2218 }
2219 /* topfloor should now be the topmost IS_FLOOR=1 */
2220 if (topfloor == NULL)
2221 continue;
2222 if (tmp != NULL)
2223 nodstk++;
2224 /* something is wrong with that sector. just skip it */
2225
2226 j=rndm(1, 8);
2227 nx = freearr_x[j]+x;
2228 ny = freearr_y[j]+y;
2229 if (OUT_OF_REAL_MAP(m, nx, ny))
2230 continue;
2231 oldsnow = avoid_weather(&avoid, m, nx, ny, &gotsnow, 1);
2232 if (avoid)
2233 continue;
2234 ntopfloor=NULL;
2235 for (ntmp=GET_MAP_OB(m, nx, ny); ntmp;
2236 ntopfloor = ntmp,ntmp = ntmp->above) {
2237 if (strcmp(ntmp->arch->name, "dungeon_magic") != 0)
2238 if (!QUERY_FLAG(ntmp, FLAG_IS_FLOOR) &&
2239 !QUERY_FLAG(ntmp, FLAG_OVERLAY_FLOOR))
2240 break;
2241 }
2242 if (ntopfloor != NULL && QUERY_FLAG(ntopfloor, FLAG_IS_FLOOR)) {
2243 remove_ob(topfloor);
2244 free_object(topfloor);
2245 if (tmp != NULL) {
2246 for (i=0; weather_tile[i].herb != NULL; i++) {
2247 if (strcmp(tmp->arch->name, weather_tile[i].herb) == 0) {
2248 remove_ob(tmp);
2249 free_object(tmp);
2250 break;
2251 }
2252 }
2253 }
2254 } else {
2255 continue;
2256 }
2257 ob = get_object();
2258 copy_object(&ntopfloor->arch->clone, ob);
2259 ob->x = x;
2260 ob->y = y;
2261 insert_ob_in_map(ob, m, ob, INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY);
2262 if (ntmp != NULL && nodstk == 0) {
2263 for (i=0; weather_tile[i].herb != NULL; i++) {
2264 if (strcmp(ntmp->arch->name, weather_tile[i].herb) == 0) {
2265 ob = get_object();
2266 copy_object(&ntmp->arch->clone, ob);
2267 ob->x = x;
2268 ob->y = y;
2269 insert_ob_in_map(ob, m, ob, INS_NO_MERGE | INS_NO_WALK_ON | INS_ON_TOP);
2270 break;
2271 }
2272 }
2273 }
2274 }
2275 }
2276 }
2277
2278
2279 /* provide wx and wy. Will fill in with weathermap coordinates. Requires
2280 the current mapname (must be a worldmap), and your coordinates on the
2281 map. returns -1 if you give it something it can't figure out. 0 normally.
2282 */
2283
2284 int worldmap_to_weathermap(int x, int y, int *wx, int *wy, mapstruct* m)
2285 {
2286 int spwtx, spwty;
2287 int fx, fy;
2288 int nx, ny;
2289 char* filename=m->path;
2290 spwtx = (settings.worldmaptilesx * settings.worldmaptilesizex) / WEATHERMAPTILESX;
2291 spwty = (settings.worldmaptilesy * settings.worldmaptilesizey) / WEATHERMAPTILESY;
2292
2293 while (*filename == '/')
2294 *filename++;
2295
2296 fx = MAP_WORLDPARTX(m);
2297 fy = MAP_WORLDPARTY(m);
2298 if (fx > settings.worldmapstartx + settings.worldmaptilesx ||
2299 fx < settings.worldmapstartx ||
2300 fy > settings.worldmapstarty + settings.worldmaptilesy ||
2301 fy < settings.worldmapstarty){
2302 LOG(llevDebug, "worldmap_to_weathermap(%s)\n", filename);
2303 sscanf(filename, "world/world_%d_%d", &fx, &fy);
2304 MAP_WORLDPARTX(m)=fx;
2305 MAP_WORLDPARTY(m)=fy;
2306 }
2307 if (fx > settings.worldmapstartx + settings.worldmaptilesx ||
2308 fx < settings.worldmapstartx)
2309 return -1;
2310 if (fy > settings.worldmapstarty + settings.worldmaptilesy ||
2311 fy < settings.worldmapstarty)
2312 return -1;
2313 fx -= settings.worldmapstartx;
2314 fy -= settings.worldmapstarty;
2315
2316 nx = fx * settings.worldmaptilesizex + x;
2317 ny = fy * settings.worldmaptilesizey + y;
2318
2319 *wx = nx/spwtx;
2320 *wy = ny/spwty;
2321
2322 return 0;
2323 }
2324
2325 /* provide x and y. Will fill in with the requested corner of the real
2326 * world map, given the current weathermap section. dir selects which
2327 * corner to return. Valid values are 2 4 6 8 for the corners. return
2328 * value is the name of the map that corner resides in.
2329 */
2330
2331 static const char *weathermap_to_worldmap_corner(int wx, int wy, int *x, int *y, int dir)
2332 {
2333 int spwtx, spwty;
2334 int tx, ty, nx, ny;
2335 static char mapname[ MAX_BUF ];
2336
2337 spwtx = (settings.worldmaptilesx * settings.worldmaptilesizex) / WEATHERMAPTILESX;
2338 spwty = (settings.worldmaptilesy * settings.worldmaptilesizey) / WEATHERMAPTILESY;
2339 switch (dir) {
2340 case 2: wx++; break;
2341 case 4: wx++; wy++; break;
2342 case 6: wy++; break;
2343 case 8: break;
2344 }
2345 if (wx > 0)
2346 tx = (wx*spwtx)-1;
2347 else
2348 tx = wx;
2349 if (wy > 0)
2350 ty = (wy*spwty)-1;
2351 else
2352 ty = wy;
2353
2354 nx = (tx / settings.worldmaptilesizex) + settings.worldmapstartx;
2355 ny = (ty / settings.worldmaptilesizey) + settings.worldmapstarty;
2356 snprintf(mapname, MAX_BUF, "world/world_%d_%d", nx, ny);
2357
2358 *x = tx%settings.worldmaptilesizex;
2359 *y = ty%settings.worldmaptilesizey;
2360 return mapname;
2361 }
2362
2363 /*
2364 * Calculates the distance to the nearest pole. x,y are the weathermap
2365 * coordinates, equator is the current location of the equator. returns
2366 * distance as an int.
2367 */
2368
2369
2370 static int polar_distance(int x, int y, int equator)
2371 {
2372 if ((x+y) > equator) { /* south pole */
2373 x = WEATHERMAPTILESX - x;
2374 y = WEATHERMAPTILESY - y;
2375 return ((x+y)/2);
2376 } else if ((x+y) < equator) { /* north pole */
2377 return ((x+y)/2);
2378 } else {
2379 return equator/2;
2380 }
2381 }
2382
2383 /*
2384 * update the humidity for all weathermap tiles.
2385 */
2386
2387 static void update_humid(void)
2388 {
2389 int x, y;
2390
2391 for (y=0; y < WEATHERMAPTILESY; y++)
2392 for (x=0; x < WEATHERMAPTILESX; x++)
2393 weathermap[x][y].humid = humid_tile(x, y);
2394 }
2395
2396 /*
2397 * calculate the humidity of this tile. x and y are the weathermap coordinates
2398 * we wish to calculate humidity for. Returns the humidity of the weathermap
2399 * square.
2400 */
2401
2402 static int humid_tile(int x, int y)
2403 {
2404 int ox, oy, humid;
2405
2406 ox = x;
2407 oy = y;
2408
2409 /* find the square the wind is coming from, without going out of bounds */
2410
2411 if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
2412 if (y != 0)
2413 oy = y - 1;
2414 }
2415 if (weathermap[x][y].winddir >= 6) {
2416 if (x != 0)
2417 ox = x - 1;
2418 }
2419 if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
2420 if (y < WEATHERMAPTILESY - 1)
2421 oy = y + 1;
2422 }
2423 if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
2424 if (x < WEATHERMAPTILESX - 1)
2425 ox = x + 1;
2426 }
2427 humid = (weathermap[x][y].humid * 2 +
2428 weathermap[ox][oy].humid * weathermap[ox][oy].windspeed +
2429 weathermap[x][y].water + rndm(0, 10)) /
2430 (weathermap[ox][oy].windspeed+3) + rndm(0, 5);
2431 if (humid < 0)
2432 humid = 1;
2433 if (humid > 100)
2434 humid = 100;
2435 return humid;
2436 }
2437
2438 /*
2439 * calculate temperature of the weathermap square x,y. Requires the current
2440 * time of day in *tod.
2441 */
2442
2443 static void temperature_calc(int x, int y, const timeofday_t *tod)
2444 {
2445 int dist, equator, elev, n;
2446 float diff, tdiff;
2447
2448 equator = (WEATHERMAPTILESX + WEATHERMAPTILESY) / 4;
2449 diff = (float)(EQUATOR_BASE_TEMP - POLAR_BASE_TEMP) / (float)equator;
2450 tdiff = (float)SEASONAL_ADJUST / ((float)MONTHS_PER_YEAR / 2.0);
2451 equator *= 2;
2452 n = 0;
2453 /* we essentially move the equator during the season */
2454 if (tod->month > (MONTHS_PER_YEAR / 2)) { /* EOY */
2455 n -= (tod->month * tdiff);
2456 } else {
2457 n = (MONTHS_PER_YEAR - tod->month) * tdiff;
2458 }
2459 dist = polar_distance(x-n/2, y-n/2, equator);
2460
2461 /* now we have the base temp, unadjusted for time. Time adjustment
2462 is not recorded on the map, rather, it's done JIT. */
2463 weathermap[x][y].temp = (int)(dist * diff);
2464 /* just scrap these for now, its mostly ocean */
2465 if (weathermap[x][y].avgelev < 0)
2466 elev = 0;
2467 else
2468 elev = MAX(10000, weathermap[x][y].avgelev)/1000;
2469 weathermap[x][y].temp -= elev;
2470 }
2471
2472 /* Compute the real (adjusted) temperature of a given weathermap tile.
2473 * This takes into account the wind, base temp, sunlight, and other fun
2474 * things. Seasons are automatically handled by moving the equator.
2475 * Elevation is partially considered in the base temp. x and y are the
2476 * weathermap coordinates.
2477 */
2478
2479 static int real_temperature(int x, int y)
2480 {
2481 int i, temp;
2482 timeofday_t tod;
2483
2484 /* adjust for time of day */
2485 temp = weathermap[x][y].temp;
2486 get_tod(&tod);
2487 for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
2488 temp += season_tempchange[i];
2489 /* high amounts of water has a buffering effect on the temp */
2490 if (weathermap[x][y].water > 33)
2491 i++;
2492 }
2493 for (i = 0; i <= tod.hour; i++) {
2494 temp += season_tempchange[i];
2495 if (weathermap[x][y].water > 33)
2496 i++;
2497 }
2498
2499 /* windchill */
2500 for (i=1; i < weathermap[x][y].windspeed; i+=i)
2501 temp--;
2502
2503 return temp;
2504 }
2505
2506 /* Given a worldmap name, and x and y on that map, compute the temperature
2507 for a specific square. Used to normalize elevation.
2508 */
2509
2510 int real_world_temperature(int x, int y, mapstruct *m)
2511 {
2512 int wx, wy, temp, eleva, elevb;
2513 object *op;
2514 /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
2515 worldmap_to_weathermap(x, y, &wx, &wy, /*m->path*/m);
2516 temp = real_temperature(wx, wy);
2517 if (weathermap[wx][wy].avgelev < 0)
2518 eleva = 0;
2519 else
2520 eleva = weathermap[x][y].avgelev;
2521
2522 op= GET_MAP_OB(m, x, y);
2523 if (!op) return eleva;
2524
2525 elevb = op->elevation;
2526 if (elevb < 0)
2527 elevb = 0;
2528 if (elevb > eleva) {
2529 elevb -= eleva;
2530 temp -= elevb/1000;
2531 } else {
2532 elevb = eleva - elevb;
2533 temp += elevb/1000;
2534 }
2535 return temp;
2536 }
2537
2538 /* this code simply smooths the pressure map */
2539
2540 static void smooth_pressure(void)
2541 {
2542 int x, y;
2543 int k;
2544
2545 for (k=0; k < PRESSURE_ROUNDING_ITER; k++) {
2546 for (x=2; x < WEATHERMAPTILESX-2; x++) {
2547 for (y=2; y < WEATHERMAPTILESY-2; y++) {
2548 weathermap[x][y].pressure = (weathermap[x][y].pressure *
2549 PRESSURE_ROUNDING_FACTOR + weathermap[x-1][y].pressure +
2550 weathermap[x][y-1].pressure + weathermap[x-1][y-1].pressure +
2551 weathermap[x+1][y].pressure + weathermap[x][y+1].pressure +
2552 weathermap[x+1][y+1].pressure + weathermap[x+1][y-1].pressure +
2553 weathermap[x-1][y+1].pressure) / (PRESSURE_ROUNDING_FACTOR+8);
2554 }
2555 }
2556 for (x=WEATHERMAPTILESX-2; x > 2; x--) {
2557 for (y=WEATHERMAPTILESY-2; y > 2; y--) {
2558 weathermap[x][y].pressure = (weathermap[x][y].pressure *
2559 PRESSURE_ROUNDING_FACTOR + weathermap[x-1][y].pressure +
2560 weathermap[x][y-1].pressure + weathermap[x-1][y-1].pressure +
2561 weathermap[x+1][y].pressure + weathermap[x][y+1].pressure +
2562 weathermap[x+1][y+1].pressure + weathermap[x+1][y-1].pressure +
2563 weathermap[x-1][y+1].pressure) / (PRESSURE_ROUNDING_FACTOR+8);
2564 }
2565 }
2566 }
2567
2568 for (x=0; x < WEATHERMAPTILESX; x++)
2569 for (y=0; y < WEATHERMAPTILESY; y++) {
2570 weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
2571 weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
2572 }
2573
2574 }
2575
2576 /*
2577 * perform small randomizations in the pressure map. Then, apply the
2578 * smoothing algorithim.. This causes the pressure to change very slowly
2579 */
2580
2581 static void perform_pressure(void)
2582 {
2583 int x, y, l, n, j, k;
2584
2585 /* create random spikes in the pressure */
2586 for (l=0; l < PRESSURE_SPIKES; l++) {
2587 x = rndm(0, WEATHERMAPTILESX-1);
2588 y = rndm(0, WEATHERMAPTILESY-1);
2589 n = rndm(600, 1300);
2590 weathermap[x][y].pressure = n;
2591 if (x > 5 && y > 5 && x < WEATHERMAPTILESX-5 && y < WEATHERMAPTILESY-5){
2592 for (j=x-2; j<x+2; j++)
2593 for (k=y-2; k<y+2; k++) {
2594 weathermap[j][k].pressure = n;
2595 /* occasionally add a storm */
2596 if (rndm(1, 20) == 1)
2597 weathermap[j][k].humid = rndm(50, 80);
2598 }
2599 }
2600 }
2601
2602 for (x=0; x < WEATHERMAPTILESX; x++)
2603 for (y=0; y < WEATHERMAPTILESY; y++)
2604 weathermap[x][y].pressure += rndm(-1, 4);
2605
2606 smooth_pressure();
2607 }
2608
2609
2610 /*
2611 * is direction a similar to direction b? Find out in this exciting function
2612 * below. Returns 1 if true, 0 for false.
2613 */
2614
2615 int similar_direction(int a, int b)
2616 {
2617 /* shortcut the obvious */
2618 if (a == b)
2619 return 1;
2620
2621 switch(a) {
2622 case 1: if (b <= 2 || b == 8) return 1; break;
2623 case 2: if (b > 0 && b < 4) return 1; break;
2624 case 3: if (b > 1 && b < 5) return 1; break;
2625 case 4: if (b > 2 && b < 6) return 1; break;
2626 case 5: if (b > 3 && b < 7) return 1; break;
2627 case 6: if (b > 4 && b < 8) return 1; break;
2628 case 7: if (b > 5) return 1; break;
2629 case 8: if (b > 6 || b == 1) return 1; break;
2630 }
2631 return 0;
2632 }
2633
2634 /*
2635 * It doesn't really smooth it as such. The main function of this is to
2636 * apply the pressuremap to the wind direction and speed. Then, we run
2637 * a quick pass to update the windspeed.
2638 */
2639
2640 static void smooth_wind(void)
2641 {
2642 int x, y;
2643 int tx, ty, dx, dy;
2644 int minp;
2645
2646 /* skip the outer squares.. it makes handling alot easier */
2647 dx = 0;
2648 dy = 0;
2649 for (x=1; x < WEATHERMAPTILESX-1; x++)
2650 for (y=1; y < WEATHERMAPTILESY-1; y++) {
2651 minp = PRESSURE_MAX + 1;
2652 for (tx=-1; tx < 2; tx++)
2653 for (ty=-1; ty < 2; ty++)
2654 if (!(tx == 0 && ty == 0))
2655 if (weathermap[x+tx][y+ty].pressure < minp) {
2656 minp = weathermap[x+tx][y+ty].pressure;
2657 dx = tx;
2658 dy = ty;
2659 }
2660
2661 /* if the wind is strong, the pressure won't decay it completely */
2662 if (weathermap[x][y].windspeed > 5 &&
2663 !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
2664 weathermap[x][y].windspeed -= 2 * 2;
2665 } else {
2666 weathermap[x][y].winddir = find_dir_2(dx, dy);
2667 weathermap[x][y].windspeed = (weathermap[x][y].pressure -
2668 weathermap[x+dx][y+dy].pressure) * WIND_FACTOR;
2669 }
2670 /* Add in sea breezes. */
2671 weathermap[x][y].windspeed += weathermap[x][y].water / 4;
2672 if (weathermap[x][y].windspeed < 0)
2673 weathermap[x][y].windspeed = 0;
2674 }
2675
2676 /* now, lets crank on the speed. When surrounding tiles all have
2677 the same speed, inc ours. If it's chaos. drop it.
2678 */
2679 for (x=1; x < WEATHERMAPTILESX-1; x++)
2680 for (y=1; y < WEATHERMAPTILESY-1; y++) {
2681 minp = 0;
2682 for (tx=-1; tx < 2; tx++)
2683 for (ty=-1; ty < 2; ty++)
2684 if (ty != 0 && ty != 0)
2685 if (similar_direction(weathermap[x][y].winddir,
2686 weathermap[x+tx][y+ty].winddir))
2687 minp++;
2688 if (minp > 4)
2689 weathermap[x][y].windspeed++;
2690 if (minp > 6)
2691 weathermap[x][y].windspeed += 2;
2692 if (minp < 2)
2693 weathermap[x][y].windspeed--;
2694 if (weathermap[x][y].windspeed < 0)
2695 weathermap[x][y].windspeed = 0;
2696 }
2697 }
2698
2699 /*
2700 * Plot the gulfstream map over the wind map. This is done after the wind,
2701 * to avoid the windsmoothing scrambling the jet stream.
2702 */
2703
2704 static void plot_gulfstream(void)
2705 {
2706 int x, y, tx;
2707
2708 x = gulf_stream_start;
2709
2710 if (gulf_stream_direction) {
2711 for (y=WEATHERMAPTILESY-1; y > 0; y--) {
2712 for (tx=0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
2713 if (similar_direction(weathermap[x+tx][y].winddir,
2714 gulf_stream_dir[tx][y]) &&
2715 weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5)
2716 weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
2717 else
2718 weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
2719 weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
2720 if (tx == GULF_STREAM_WIDTH-1) {
2721 switch (gulf_stream_dir[tx][y]) {
2722 case 6: x--; break;
2723 case 7: break;
2724 case 8: x++; ; break;
2725 }
2726 }
2727 if (x < 0)
2728 x++;
2729 if (x >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2730 x--;
2731 }
2732 }
2733 } else {
2734 for (y=0; y < WEATHERMAPTILESY-1; y++) {
2735 for (tx=0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
2736 if (similar_direction(weathermap[x+tx][y].winddir,
2737 gulf_stream_dir[tx][y]) &&
2738 weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5)
2739 weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
2740 else
2741 weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
2742 weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
2743 if (tx == GULF_STREAM_WIDTH-1) {
2744 switch (gulf_stream_dir[tx][y]) {
2745 case 2: x++; break;
2746 case 3: break;
2747 case 4: x--; break;
2748 }
2749 }
2750 if (x < 0)
2751 x++;
2752 if (x >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2753 x--;
2754 }
2755 }
2756 }
2757 /* occasionally move the stream */
2758 if (rndm(1, 500) == 1) {
2759 gulf_stream_direction = rndm(0, 1);
2760 for (tx=0; tx < GULF_STREAM_WIDTH; tx++)
2761 for (y=0; y < WEATHERMAPTILESY-1; y++)
2762 if (gulf_stream_direction)
2763 switch (gulf_stream_dir[tx][y]) {
2764 case 2: gulf_stream_dir[tx][y] = 6; break;
2765 case 3: gulf_stream_dir[tx][y] = 7; break;
2766 case 4: gulf_stream_dir[tx][y] = 8; break;
2767 }
2768 else
2769 switch (gulf_stream_dir[tx][y]) {
2770 case 6: gulf_stream_dir[tx][y] = 2; break;
2771 case 7: gulf_stream_dir[tx][y] = 3; break;
2772 case 8: gulf_stream_dir[tx][y] = 4; break;
2773 }
2774 }
2775 if (rndm(1, 25) == 1)
2776 gulf_stream_start += rndm(-1, 1);
2777 if (gulf_stream_start >= WEATHERMAPTILESX-GULF_STREAM_WIDTH)
2778 gulf_stream_start--;
2779 if (gulf_stream_start < 1)
2780 gulf_stream_start++;
2781
2782 }
2783
2784 /*
2785 * let the madness, begin.
2786 *
2787 * This function is the one that ties everything together. Here we loop
2788 * over all the weathermaps, and compare the various conditions we have
2789 * calculated up to now, to figure out what the sky conditions are for this
2790 * square.
2791 */
2792
2793 static void compute_sky(void)
2794 {
2795 int x, y;
2796 int temp;
2797
2798 for (x=0; x < WEATHERMAPTILESX; x++) {
2799 for (y=0; y < WEATHERMAPTILESY; y++) {
2800 temp = real_temperature(x, y);
2801 if (weathermap[x][y].pressure < 980) {
2802 if (weathermap[x][y].humid < 20)
2803 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2804 else if (weathermap[x][y].humid < 30)
2805 weathermap[x][y].sky = SKY_OVERCAST;
2806 else if (weathermap[x][y].humid < 40)
2807 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2808 else if (weathermap[x][y].humid < 55)
2809 weathermap[x][y].sky = SKY_RAIN;
2810 else if (weathermap[x][y].humid < 70)
2811 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2812 else
2813 weathermap[x][y].sky = SKY_HURRICANE;
2814 if (weathermap[x][y].sky < SKY_HURRICANE &&
2815 weathermap[x][y].windspeed > 30)
2816 weathermap[x][y].sky++;
2817 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2818 weathermap[x][y].sky += 10; /*let it snow*/
2819 } else if (weathermap[x][y].pressure < 1000) {
2820 if (weathermap[x][y].humid < 10)
2821 weathermap[x][y].sky = SKY_CLEAR;
2822 else if (weathermap[x][y].humid < 25)
2823 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2824 else if (weathermap[x][y].humid < 45)
2825 weathermap[x][y].sky = SKY_OVERCAST;
2826 else if (weathermap[x][y].humid < 60)
2827 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2828 else if (weathermap[x][y].humid < 75)
2829 weathermap[x][y].sky = SKY_RAIN;
2830 else
2831 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2832 if (weathermap[x][y].sky < SKY_HURRICANE &&
2833 weathermap[x][y].windspeed > 30)
2834 weathermap[x][y].sky++;
2835 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2836 weathermap[x][y].sky += 10; /*let it snow*/
2837 if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
2838 weathermap[x][y].windspeed < 3)
2839 weathermap[x][y].sky = SKY_FOG; /* rare */
2840 if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
2841 weathermap[x][y].windspeed > 35)
2842 weathermap[x][y].sky = SKY_HAIL; /* rare */
2843 } else if (weathermap[x][y].pressure < 1020) {
2844 if (weathermap[x][y].humid < 20)
2845 weathermap[x][y].sky = SKY_CLEAR;
2846 else if (weathermap[x][y].humid < 30)
2847 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2848 else if (weathermap[x][y].humid < 40)
2849 weathermap[x][y].sky = SKY_OVERCAST;
2850 else if (weathermap[x][y].humid < 55)
2851 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2852 else if (weathermap[x][y].humid < 70)
2853 weathermap[x][y].sky = SKY_RAIN;
2854 else
2855 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2856 if (weathermap[x][y].sky < SKY_HURRICANE &&
2857 weathermap[x][y].windspeed > 30)
2858 weathermap[x][y].sky++;
2859 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2860 weathermap[x][y].sky += 10; /*let it snow*/
2861 } else {
2862 if (weathermap[x][y].humid < 35)
2863 weathermap[x][y].sky = SKY_CLEAR;
2864 else if (weathermap[x][y].humid < 55)
2865 weathermap[x][y].sky = SKY_LIGHTCLOUD;
2866 else if (weathermap[x][y].humid < 70)
2867 weathermap[x][y].sky = SKY_OVERCAST;
2868 else if (weathermap[x][y].humid < 85)
2869 weathermap[x][y].sky = SKY_LIGHT_RAIN;
2870 else if (weathermap[x][y].humid < 95)
2871 weathermap[x][y].sky = SKY_RAIN;
2872 else
2873 weathermap[x][y].sky = SKY_HEAVY_RAIN;
2874 if (weathermap[x][y].sky < SKY_HURRICANE &&
2875 weathermap[x][y].windspeed > 30)
2876 weathermap[x][y].sky++;
2877 if (temp <= 0 && weathermap[x][y].sky > SKY_OVERCAST)
2878 weathermap[x][y].sky += 10; /*let it snow*/
2879 }
2880 }
2881 }
2882 }
2883
2884 /*
2885 * Keep track of how much rain has fallen in a given weathermap square.
2886 */
2887
2888 static void process_rain(void)
2889 {
2890 int x, y, rain;
2891
2892 for (x=0; x < WEATHERMAPTILESX; x++)
2893 for (y=0; y < WEATHERMAPTILESY; y++) {
2894 rain = weathermap[x][y].sky;
2895 if (rain >= SKY_LIGHT_SNOW)
2896 rain -= 10;
2897 if (rain > SKY_OVERCAST && rain < SKY_FOG) {
2898 rain -= SKY_OVERCAST;
2899 weathermap[x][y].rainfall += rain;
2900 }
2901 }
2902 }
2903
2904 /*
2905 * The world spinning drags the weather with it.
2906 * The equator is diagonal, and the poles are 45 degrees from north /south.
2907 * What the hell, lets spin the planet backwards.
2908 */
2909
2910 static void spin_globe(void)
2911 {
2912 int x, y;
2913 int buffer_humid;
2914 int buffer_sky;
2915
2916 for (y=0; y < WEATHERMAPTILESY; y++) {
2917 buffer_humid = weathermap[0][y].humid;
2918 buffer_sky = weathermap[0][y].sky;
2919 for (x=0; x < (WEATHERMAPTILESX - 1); x++) {
2920 weathermap[x][y].humid = weathermap[x + 1][y].humid;
2921 weathermap[x][y].sky = weathermap[x + 1][y].sky;
2922 }
2923 weathermap[WEATHERMAPTILESX - 1][y].humid = buffer_humid;
2924 weathermap[WEATHERMAPTILESX - 1][y].sky = buffer_sky;
2925 }
2926 }
2927
2928 /*
2929 * Dump all the weather data as an image.
2930 * Writes two other files that are useful for creating animations and web pages.
2931 */
2932
2933 static void write_weather_images(void)
2934 {
2935 char filename[MAX_BUF];
2936 FILE *fp;
2937 int x, y;
2938 sint32 min[10], max[10], avgrain, avgwind, realmaxwind;
2939 double scale[10], realscalewind;
2940 uint8 pixels[3 * 3 * WEATHERMAPTILESX];
2941 sint64 total_rainfall = 0;
2942 sint64 total_wind = 0;
2943
2944 min[0] = 0; max[0] = 100;
2945 min[1] = 0; max[1] = 0;
2946 min[2] = 0; max[2] = 0;
2947 min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
2948 min[4] = 0; max[4] = 0;
2949 min[5] = 1; max[5] = 8;
2950 min[6] = 0; max[6] = 100;
2951 min[7] = -45; max[7] = 45;
2952 min[8] = 0; max[8] = 16;
2953 min[9] = 0; max[9] = 0;
2954 for (x=0; x < WEATHERMAPTILESX; x++) {
2955 for (y=0; y < WEATHERMAPTILESY; y++) {
2956 /* min[0] = MIN(min[0], weathermap[x][y].water); */
2957 min[1] = MIN(min[1], weathermap[x][y].avgelev);
2958 min[2] = MIN(min[2], weathermap[x][y].rainfall);
2959 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
2960 min[4] = MIN(min[4], weathermap[x][y].windspeed);
2961 /* min[5] = MIN(min[5], weathermap[x][y].winddir); */
2962 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
2963 /* min[7] = MIN(min[7], real_temp[x][y]); */
2964 /* min[8] = MIN(min[8], weathermap[x][y].sky); */
2965 /* min[9] = MIN(min[9], weathermap[x][y].darkness); */
2966
2967 /* max[0] = MAX(max[0], weathermap[x][y].water); */
2968 max[1] = MAX(max[1], weathermap[x][y].avgelev);
2969 max[2] = MAX(max[2], weathermap[x][y].rainfall);
2970 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
2971 max[4] = MAX(max[4], weathermap[x][y].windspeed);
2972 /* max[5] = MAX(max[5], weathermap[x][y].winddir); */
2973 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
2974 /* max[7] = MAX(max[7], real_temp[x][y]); */
2975 /* max[8] = MAX(max[8], weathermap[x][y].sky); */
2976 /* max[9] = MAX(max[9], weathermap[x][y].darkness); */
2977 total_rainfall += weathermap[x][y].rainfall;
2978 total_wind += weathermap[x][y].windspeed;
2979 }
2980 }
2981 avgrain = total_rainfall / (WEATHERMAPTILESX * WEATHERMAPTILESY);
2982 avgwind = (total_wind / ((WEATHERMAPTILESX * WEATHERMAPTILESY) * 3 / 2));
2983 max[2] = avgrain - 1;
2984 realscalewind = 255.0l / (max[4] - min[4]);
2985 realmaxwind = max[4];
2986 max[4] = avgwind - 1;
2987 for (x=0; x < 10; x++)
2988 scale[x] = 255.0l / (max[x] - min[x]);
2989
2990 sprintf(filename, "%s/weather.ppm", settings.localdir);
2991 if ((fp = fopen(filename, "w")) == NULL) {
2992 LOG(llevError, "Cannot open %s for writing\n", filename);
2993 return;
2994 }
2995 fprintf(fp, "P6\n%d %d\n", 3 * WEATHERMAPTILESX, 3 * WEATHERMAPTILESY);
2996 fprintf(fp, "255\n");
2997 for (y=0; y < WEATHERMAPTILESY; y++) {
2998 for (x=0; x < (3 * 3 * WEATHERMAPTILESX); x++)
2999 pixels[x] = 0;
3000 for (x=0; x < WEATHERMAPTILESX; x++) {
3001 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].water - min[0]) * scale[0]);
3002 if (weathermap[x][y].avgelev >= 0)
3003 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((weathermap[x][y].avgelev - min[1]) * scale[1]);
3004 else
3005 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].avgelev - min[1]) * scale[1]);
3006 if (weathermap[x][y].rainfall >= avgrain) /* rainfall is rather spikey, this gives us more detail. */
3007 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = 255;
3008 else
3009 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].rainfall - min[2]) * scale[2]);
3010 }
3011 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3012 }
3013 for (y=0; y < WEATHERMAPTILESY; y++) {
3014 for (x=0; x < WEATHERMAPTILESX; x++) {
3015 uint32 dir = directions[weathermap[x][y].winddir - 1];
3016 uint32 speed = weathermap[x][y].windspeed;
3017 uint8 pressure = (weathermap[x][y].pressure - min[3]) * scale[3];
3018 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + RED)] = pressure;
3019 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + GREEN)] = pressure;
3020 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = pressure;
3021 if (speed < avgwind) {
3022 speed = (speed - min[4]) * scale[4];
3023 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = speed;
3024 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = speed;
3025 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = speed;
3026 } else {
3027 speed = (speed - realmaxwind) * realscalewind;
3028 /* if (speed < 100)*/
3029 /* speed = 100;*/
3030 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = speed;
3031 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + GREEN)] = 0;
3032 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + BLUE)] = 0;
3033 }
3034 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((dir & 0x00FF0000) >> 16);
3035 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((dir & 0x0000FF00) >> 8);
3036 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((dir & 0x000000FF));
3037 }
3038 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3039 }
3040 for (y=0; y < WEATHERMAPTILESY; y++) {
3041 for (x=0; x < (3 * 3 * WEATHERMAPTILESX); x++)
3042 pixels[x] = 0;
3043 for (x=0; x < WEATHERMAPTILESX; x++) {
3044 uint32 dir = skies[weathermap[x][y].sky];
3045 pixels[3 * x + (0 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((weathermap[x][y].humid - min[6]) * scale[6]);
3046 /*pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((real_temp[x][y] - min[7]) * scale[7]);*/
3047 pixels[3 * x + (1 * WEATHERMAPTILESX * 3 + RED)] = 1;
3048 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + RED)] = (uint8) ((dir & 0x00FF0000) >> 16);
3049 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + GREEN)] = (uint8) ((dir & 0x0000FF00) >> 8);
3050 pixels[3 * x + (2 * WEATHERMAPTILESX * 3 + BLUE)] = (uint8) ((dir & 0x000000FF));
3051
3052 }
3053 fwrite(pixels, sizeof(uint8), (3 * 3 * WEATHERMAPTILESX), fp);
3054 }
3055 fclose(fp);
3056
3057 sprintf(filename, "%s/todtick", settings.localdir);
3058 if ((fp = fopen(filename, "w")) == NULL) {
3059 LOG(llevError, "Cannot open %s for writing\n", filename);
3060 return;
3061 }
3062 fprintf(fp, "%lu", todtick);
3063 fclose(fp);
3064 }