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

File Contents

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