Crossfire Server, Trunk  1.75.0
cfweather.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2021 The Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
20 #include "global.h"
21 #include "server.h"
22 #include "map.h"
23 #include "object.h"
24 #include "output_file.h"
25 #include "sproto.h"
26 
27 #include <string.h>
28 #include <assert.h>
29 #include <math.h>
30 
31 /* weather constants */
32 
33 #define POLAR_BASE_TEMP 0 /* C */
34 #define EQUATOR_BASE_TEMP 30 /* C */
35 #define SEASONAL_ADJUST 10 /* polar distance */
36 #define GULF_STREAM_WIDTH 3 /* width of gulf stream */
37 #define GULF_STREAM_BASE_SPEED 40 /* base speed of gulf stream */
38 
39 /* don't muck with these unless you are sure you know what they do */
40 #define PRESSURE_ITERATIONS 30
41 #define PRESSURE_AREA 180
42 #define PRESSURE_ROUNDING_FACTOR 2
43 #define PRESSURE_ROUNDING_ITER 1
44 #define PRESSURE_SPIKES 3
45 #define PRESSURE_MAX 1040
46 #define PRESSURE_MIN 960
47 
48 /* sky conditions */
49 #define SKY_CLEAR 0
50 #define SKY_LIGHTCLOUD 1
51 #define SKY_OVERCAST 2
52 #define SKY_LIGHT_RAIN 3
53 #define SKY_RAIN 4 /* rain -> storm has lightning */
54 #define SKY_HEAVY_RAIN 5
55 #define SKY_HURRICANE 6
56 /* wierd weather 7-12 */
57 #define SKY_FOG 7
58 #define SKY_HAIL 8
59 /* snow */
60 #define SKY_LIGHT_SNOW 13 /* add 10 to rain to get snow */
61 #define SKY_SNOW 14
62 #define SKY_HEAVY_SNOW 15
63 #define SKY_BLIZZARD 16
64 
72 #define WIND_FACTOR 4.0
73 
74 /* editing the below might require actual changes to code */
75 #define WEATHERMAPTILESX 100
76 #define WEATHERMAPTILESY 100
77 
78 static int worldmap_to_weathermap(const int x, const int y, int * const wx, int * const wy, mapstruct * const m);
79 
80 /********************************************************************************************
81  * Section -- weather structures
82  * Structures to handle various aspects of the weather system.
83  ********************************************************************************************/
84 
94 struct weathermap_t {
95  int16_t temp;
96  int16_t pressure;
97  int8_t humid;
98  int8_t windspeed;
99  int8_t winddir;
100  int8_t sky;
101  int32_t avgelev;
102  uint32_t rainfall;
103  uint8_t darkness;
104  int8_t water;
105  int8_t forestry;
106  /*Dynamic parts*/
107  int16_t realtemp;
108 };
109 
117  // Use shared strings so we can do pointer comparisons.
119  // 0 if name is the arch name, 1 if it is the object name.
120  int is_obj;
121  // The density the tile type counts for.
123  // Pointer to the next item in the list
124  // We're scanning all of these when we check anyway,
125  // so might as well use a structure that works fine that way.
127 };
128 
134  int snow;
137 };
138 
148 };
149 
154  const char *herb;
155  const char *tile;
156  int random;
157  float rfmin;
158  float rfmax;
159  int humin;
160  int humax;
161  int tempmin;
162  int tempmax;
163  int elevmin;
164  int elevmax;
165  int season;
166 };
167 
173  uint32_t worldmaptilesizex;
174  uint32_t worldmaptilesizey;
175  uint16_t dynamiclevel;
176 };
177 
178 /********************************************************************************************
179  * Section END -- weather structures
180  ********************************************************************************************/
181 
182 extern unsigned long todtick;
184 
192 
203 static int gulf_stream_start;
205 
207 static int wmperformstartx;
209 static int wmperformstarty;
210 
212  .worldmaptilesizex = 50,
213  .worldmaptilesizey = 50,
214  .dynamiclevel = 1,
215 };
216 
217 /*
218  * @todo
219  * The following static tables should probably be defined by files.
220  */
225 static const weather_grow_t weather_grow[] = {
226  /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax, season */
227  {"mint", "grass", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
228  {"rose_red", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
229  {"rose_red", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
230  //{"rose_yellow", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
231  //{"rose_yellow", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
232  //{"rose_pink", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
233  //{"rose_pink", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
234  {"mint", "brush", 8, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
235  {"blackroot", "swamp", 15, 1.6, 2.0, 60, 100, 20, 30, -100, 1500, 0},
236  {"mushroom_1", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
237  {"mushroom_2", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
238  {"mushroom_1", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
239  {"mushroom_2", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
240  {"mushroom_1", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
241  {"mushroom_2", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
242  {"pipeweed", "farmland", 20, 1.0, 2.0, 30, 100, 10, 25, 100, 5000, 0},
243  {"cabbage", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 0},
244  {"onion", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
245  {"carrot", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
246  {"thorns", "brush", 15, 0.5, 1.3, 30, 100, 10, 25, -100, 9999, 0},
247  {"mountain_foilage", "mountain", 6, 1.0, 2.0, 25, 100, 5, 30, 0, 15999, 2},
248  {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
249 };
250 
256 static const weather_grow_t weather_tile[] = {
257  /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax */
258  {"dunes", NULL, 2, 0.0, 0.03, 0, 20, 10, 99, 0, 4000, 0},
259  {"desert", NULL, 1, 0.0, 0.05, 0, 20, 10, 99, 0, 4000, 0},
260  {"pstone_2", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
261  {"pstone_3", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
262  {"grassbrown", NULL, 1, 0.05, 1.0, 20, 80, -20, -3, 0, 5000, 0},
263  {"grass_br_gr", NULL, 1, 0.05, 1.0, 20, 80, -3, 5, 0, 5000, 0},
264  {"grass", NULL, 1, 0.05, 1.0, 20, 80, 5, 15, 0, 5000, 0},
265  {"grassmedium", NULL, 1, 0.05, 1.0, 20, 80, 15, 25, 0, 5000, 0},
266  {"grassdark", NULL, 1, 0.05, 1.0, 20, 80, 25, 35, 0, 5000, 0},
267  {"brush", NULL, 1, 0.2, 1.0, 25, 70, 0, 30, 500, 6000, 0},
268  /* small */
269  {"evergreens2", "brush", 1, 0.5, 1.8, 30, 90, -30, 24, 3000, 8000, 0},
270  {"fernsdense", "brush", 1, 0.9, 2.5, 50, 100, 10, 35, 1000, 6000, 0},
271  {"fernssparse", "brush", 1, 0.7, 2.0, 30, 90, -15, 35, 0, 4000, 0},
272  {"woods4", "brush", 1, 0.1, 0.8, 30, 60, -5, 25, 1000, 4500, 0},
273  {"woods5", "brush", 1, 0.6, 1.5, 20, 70, -15, 20, 2000, 5500, 0},
274  {"forestsparse", "brush", 1, 0.3, 1.5, 15, 60, -20, 25, 0, 4500, 0},
275  /* big */
276  /*
277  {"ytree_2", "brush", 2, 0.1, 0.6, 30, 60, 10, 25, 1000, 3500, 0},
278  {"tree3", "grass", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
279  {"tree5", "grass", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
280  {"tree3", "grassmeduim", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
281  {"tree5", "grassmedium", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
282  {"tree3", "grassdark", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
283  {"tree5", "grassdark", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},*/
284  /* mountians */
285  {"steppe", NULL, 1, 0.5, 1.3, 0, 30, -20, 35, 1000, 6000, 0},
286  {"steppelight", NULL, 1, 0.0, 0.6, 0, 20, -50, 35, 0, 5000, 0},
287  {"hills", NULL, 1, 0.1, 0.9, 20, 80, -10, 30, 5000, 8500, 0},
288  {"hills_rocky", NULL, 1, 0.0, 0.9, 0, 100, -50, 50, 5000, 8500, 0},
289  {"swamp", NULL, 1, 1.0, 9.9, 55, 80, 10, 50, 0, 1000, 0},
290  {"deep_swamp", NULL, 1, 1.0, 9.9, 80, 100, 10, 50, 0, 1000, 0},
291  {"mountain", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 8000, 10000, 0},
292  {"mountain2", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 9500, 11000, 0},
293  {"mountain4", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 10500, 12000, 0},
294  {"mountain5", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 11500, 13500, 0},
295  {"wasteland", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 13000, 99999, 0},
296  /* catchalls */
297  {"palms", "pstone_1", 1, 0.01, 0.1, 0, 30, 5, 99, 0, 4000, 0},
298  {"large_stones", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 6000, 8000, 0},
299  {"earth", NULL, 1, 0.0, 1.0, 0, 70, -30, 15, 0, 6000, 0},
300  {"medium_stones", NULL, 1, 1.0, 3.0, 70, 100, -30, 10, 0, 4000, 0}, /*unsure*/
301  {"earth", NULL, 1, 0.1, 0.9, 20, 80, -30, 30, 0, 4999, 0}, /* tundra */
302  {"swamp", NULL, 1, 1.0, 9.9, 50, 100, -30, 10, 0, 4000, 0},/* cold marsh */
303  {"earth", NULL, 1, 0.0, 99.9, 0, 100, -99, 99, 0, 99999, 0}, /* debug */
304  {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
305 };
306 
307 
308 /********************************************************************************************
309  * Section -- weather data helpers
310  * These functions do important things like convert weathermap location to worldmap location.
311  * They are used by multiple sections, and as a result are general helpers.
312  ********************************************************************************************/
313 
333 static char *weathermap_to_worldmap_corner(const int wx, const int wy, int * const x, int * const y, const int dir, char * const buffer, const int bufsize) {
336  int tx, ty, nx, ny;
337 
338  // Load the position on the map the corner of the weathermap takes.
339  // Since each map is larger than each weathermap, there can be no more than four
340  // map existing in a weathermap.
341  switch (dir) {
342  case 2:
343  tx = (wx+1)*spwtx-1;
344  ty = wy*spwty;
345  break;
346  case 4:
347  tx = (wx+1)*spwtx-1;
348  ty = (wy+1)*spwty-1;
349  break;
350  case 6:
351  tx = wx*spwtx;
352  ty = (wy+1)*spwty-1;
353  break;
354  case 8:
355  tx = wx*spwtx;
356  ty = wy*spwty;
357  break;
358  // If an incorrect direction is given, bail.
359  default:
360  LOG(llevError, "weathermap_to_worldmap_corner: Invalid direction %d given, should be in set {2,4,6,8}.\n", dir);
361  return NULL;
362  }
363 
366  snprintf(buffer, bufsize, "world/world_%d_%d", nx, ny);
367 
368  *x = tx % wset.worldmaptilesizex;
369  *y = ty % wset.worldmaptilesizey;
370  return buffer;
371 }
372 
384 static int polar_distance(int x, int y, const int equator) {
385  if ((x+y) > equator) { /* south pole */
386  x = WEATHERMAPTILESX - x;
387  y = WEATHERMAPTILESY - y;
388  return ((x+y)/2);
389  } else if ((x+y) < equator) { /* north pole */
390  return ((x+y)/2);
391  } else {
392  return equator/2;
393  }
394 }
395 
416 static int get_config_tile(const int x, const int y, const mapstruct *m, const DensityConfig *list) {
417  // If no list specified, shortcut the exit.
418  if (list == NULL)
419  return 0;
420  object *ob = GET_MAP_OB(m, x, y);
421  const DensityConfig *tmp;
422  // Our trees are not always the floor. Look higher if need be.
423  // Even for types that are floor, check anyway. This ensures
424  // that no-magic tiles hiding underneath floor don't cause problems.
425  while (ob) {
426  // Look at our config data for the associated amounts.
427  tmp = list;
428  while (tmp) {
429  // Does object name match?
430  if ((tmp->is_obj && tmp->name == ob->name) ||
431  // Does arch name match?
432  (!tmp->is_obj && tmp->name == ob->arch->name)) {
433  return tmp->value_density;
434  }
435 
436  tmp = tmp->next;
437  }
438  ob = ob->above;
439  }
440  // If we get here, there were no matches.
441  return 0;
442 }
443 
458 static int worldmap_to_weathermap(const int x, const int y, int * const wx, int * const wy, mapstruct * const m) {
461  int fx, fy;
462  int nx, ny;
463  const char *filename = m->path;
464 
465  while (*filename == '/') {
466  filename++;
467  }
468 
469  fx = MAP_WORLDPARTX(m);
470  fy = MAP_WORLDPARTY(m);
471 
472  // -2 is our sentinel value to say that we tried to load and could not.
473  // If either is -2, then this is not a world map.
474  if (fx == -2 || fy == -2) {
475  return -1;
476  }
478  fx < settings.worldmapstartx ||
480  fy < settings.worldmapstarty) {
481  LOG(llevDebug, "worldmap_to_weathermap(%s)\n", filename);
482  // If we don't populate the variables, mark as -2.
483  // This tells us to not check again, as it is not a world map.
484  int amt = sscanf(filename, "world/world_%d_%d", &fx, &fy);
485  if (amt == 2) {
486  MAP_WORLDPARTX(m) = fx;
487  MAP_WORLDPARTY(m) = fy;
488  }
489  else {
490  MAP_WORLDPARTX(m) = -2;
491  MAP_WORLDPARTY(m) = -2;
492  }
493 
494  }
496  fx < settings.worldmapstartx) {
497  return -1;
498  }
500  fy < settings.worldmapstarty) {
501  return -1;
502  }
503  fx -= settings.worldmapstartx;
504  fy -= settings.worldmapstarty;
505 
506  nx = fx * wset.worldmaptilesizex+x;
507  ny = fy * wset.worldmaptilesizey+y;
508 
509  *wx = nx/spwtx;
510  *wy = ny/spwty;
511 
512  return 0;
513 }
514 
534 static object *avoid_weather(int * const av, const mapstruct *m, const int x, const int y, int * const gs, const int grow) {
535  int avoid, gotsnow, i;
536  object *tmp, *snow = NULL;
537  const weather_avoids_t *cur;
538 
539  avoid = 0;
540  gotsnow = 0;
541  if (grow) {
542  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
543  /* look for things like walls, holes, etc */
544  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !(tmp->material&M_ICE || tmp->material&M_LIQUID)) {
545  gotsnow++;
546  snow = tmp;
547  }
548  for (cur = growth_avoids; cur; cur = cur->next) {
549  // Due to the use of shared strings, we can do pointer comparison here.
550  if (tmp->arch->name == cur->name) {
551  avoid++;
552  break;
553  }
554  }
555  if (avoid && gotsnow) {
556  break;
557  }
558  }
559  } else {
560  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
561  for (cur = weather_avoids; cur; cur = cur->next) {
562  if (tmp->arch->name == cur->name) {
563  // We clear FLAG_IS_FLOOR for our snow. The map's default snow does not.
564  // Avoid weirdness on the pathway to Brest and at the south pole by checking for non-floor snow
565  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && cur->snow == 1) {
566  gotsnow++;
567  snow = tmp;
568  } else {
569  avoid++;
570  }
571  break;
572  }
573  }
574  if (avoid && gotsnow) {
575  break;
576  }
577  }
578  }
579  *gs = gotsnow;
580  *av = avoid;
581 
582  return snow;
583 }
584 
602 static int check_replace_match(const object *ob, const weather_replace_t *rep_struct) {
603  if (rep_struct->arch_or_name == 1) {
604  if (ob->arch->name == rep_struct->tile) {
605  return 1;
606  }
607  } else {
608  if (ob->name == rep_struct->tile) {
609  return 1;
610  }
611  }
612  return 0;
613 }
614 
618 #define WEATHER_OVERLAY 1 /* If set, we set FLAG_OVERLAY_FLOOR */
619 #define WEATHER_NO_FLOOR 2 /* If set, we clear FLAG_IS_FLOOR */
620 #define WEATHER_NO_SAVE 4 /* If set, we set FLAG_NO_SAVE */
621 
648 static void do_weather_insert(mapstruct * const m, int x, int y, const archetype *at, const int8_t object_flags, uint16_t material, int insert_flags) {
649  if (at != NULL) {
650  object *ob = object_new();
651  object_copy(&at->clone, ob);
652  ob->x = x;
653  ob->y = y;
654  if (object_flags & WEATHER_OVERLAY)
656  if (object_flags & WEATHER_NO_FLOOR)
658  if (object_flags & WEATHER_NO_SAVE)
659  SET_FLAG(ob, FLAG_NO_SAVE);
660  if (material)
661  ob->material = material;
662  object_insert_in_map(ob, m, ob, insert_flags);
663  }
664 }
665 
681 static char *get_next_field(char *line) {
682  // The comma is the end of the field
683  line = strchr(line, ',');
684  if (line == NULL)
685  return NULL;
686  // Move past the known field seperator, but null terminate over it for the previous field.
687  *(line++) = '\0';
688  // While spaces or commas, skip the character
689  while (*line == ' ' || *line == ',')
690  ++line;
691  // Next field begins here.
692  return line;
693 }
694 
695 /********************************************************************************************
696  * Section END -- weather data helpers
697  ********************************************************************************************/
698 
699 /********************************************************************************************
700  * Section -- weather data calculators
701  * These functions determine the progression of weather data over time.
702  * This is the bread-and-butter of the weather system.
703  ********************************************************************************************/
704 
705 // We need to declare init_temperature, since it is defined below this area.
706 static void init_temperature();
707 
713 static void smooth_wind() {
714  int x, y;
715  int tx, ty, dx, dy;
716  int minp;
717 
718  /* skip the outer squares.. it makes handling alot easier */
719  dx = 0;
720  dy = 0;
721  for (x = 1; x < WEATHERMAPTILESX-1; x++)
722  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
723  minp = PRESSURE_MAX + 1;
724  for (tx = -1; tx < 2; tx++) {
725  for (ty = -1; ty < 2; ty++) {
726  if (!(tx == 0 && ty == 0)) {
727  if (weathermap[x+tx][y+ty].pressure < minp) {
728  minp = weathermap[x+tx][y+ty].pressure;
729  dx = tx;
730  dy = ty;
731  }
732  }
733  }
734  }
735 
736  /* if the wind is strong, the pressure won't decay it completely */
737  if (weathermap[x][y].windspeed > 5 && !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
738  weathermap[x][y].windspeed -= 2*2;
739  } else {
740  weathermap[x][y].winddir = find_dir_2(dx, dy);
742  }
743  // Disrupt the wind where trees are present (a reduction of up to 5 is possible).
744  weathermap[x][y].windspeed -= weathermap[x][y].forestry/20;
745  if (weathermap[x][y].windspeed < 0) {
746  weathermap[x][y].windspeed = 0;
747  }
748  // The wind moves some of the higher pressure to the lower pressure.
749  weathermap[x][y].pressure -= (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
750  weathermap[x+dx][y+dy].pressure += (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
751  }
752 
753  /* now, lets crank on the speed. When surrounding tiles all have
754  the same speed, inc ours. If it's chaos. drop it.
755  */
756  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
757  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
758  minp = 0;
759  for (tx = -1; tx < 2; tx++) {
760  for (ty = -1; ty < 2; ty++) {
761  if (tx != 0 && ty != 0) {
762  if (similar_direction(weathermap[x][y].winddir, weathermap[x+tx][y+ty].winddir)) {
763  minp++;
764  }
765  }
766  }
767  }
768  if (minp > 4) {
769  weathermap[x][y].windspeed++;
770  }
771  if (minp > 6) {
772  weathermap[x][y].windspeed += 2;
773  }
774  if (minp < 2) {
775  weathermap[x][y].windspeed--;
776  }
777  if (weathermap[x][y].windspeed < 0) {
778  weathermap[x][y].windspeed = 0;
779  }
780  }
781  }
782 }
783 
788 static void smooth_pressure() {
789  int x, y;
790  int k;
791 
792  for (k = 0; k < PRESSURE_ROUNDING_ITER; k++) {
793  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
794  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
795  weathermap[x][y].pressure = (weathermap[x][y].pressure*
797  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
798  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
799  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
801  }
802  }
803  for (x = WEATHERMAPTILESX-2; x > 0; x--) {
804  for (y = WEATHERMAPTILESY-2; y > 0; y--) {
805  weathermap[x][y].pressure = (weathermap[x][y].pressure*
807  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
808  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
809  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
811  }
812  }
813  }
814 
815  // Clip to our valid pressure range
816  for (x = 0; x < WEATHERMAPTILESX; x++)
817  for (y = 0; y < WEATHERMAPTILESY; y++) {
818  weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
819  weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
820  }
821 
822 }
823 
828 static void perform_pressure() {
829  int x, y, l, n, j, k, is_storm;
830 
831  /* create random spikes in the pressure */
832  for (l = 0; l < PRESSURE_SPIKES; l++) {
833  x = rndm(0, WEATHERMAPTILESX-1);
834  y = rndm(0, WEATHERMAPTILESY-1);
835  // This goes beyond the valid bounds so that the smoothing proces ends up
836  // making different-sized pressure spikes.
837  n = rndm(600, 1300);
838  weathermap[x][y].pressure = n;
839  // Get close to the edge. But, to make things cleaner, don't go off the edge.
840  if (x >= 2 && y >= 2 && x < WEATHERMAPTILESX-2 && y < WEATHERMAPTILESY-2) {
841  /* occasionally add a storm
842  * and make sure the whole pressure spot is a storm, not just pieces of it
843  *
844  * Also, only try to make storms out of low pressure spikes. 1013 mbar
845  * Is standard pressure at sea level.
846  */
847  is_storm = (n < 1013 && rndm(1, 10) == 1);
848  for (j = x-2; j < x+2; j++) {
849  for (k = y-2; k < y+2; k++) {
850  weathermap[j][k].pressure = n;
851  if (is_storm) {
852  weathermap[j][k].humid = rndm(50, 90);
853  }
854  }
855  }
856  }
857  }
858 
859  for (x = 0; x < WEATHERMAPTILESX; x++) {
860  for (y = 0; y < WEATHERMAPTILESY; y++) {
861  weathermap[x][y].pressure += rndm(-1, 4);
862  }
863  }
864 
865  smooth_pressure();
866 }
867 
875 static const int season_tempchange[HOURS_PER_DAY] = {
876 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 */
877  0, 0, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
878 
896 static int real_temperature(int x, int y, const timeofday_t *tod) {
897  int i, temp, adj;
898 
899  // Clear and partly-cloudy skies have a stronger temperature effect
900  // than overcast skies, since clouds create a barrier to heat escaping
901  // and sunlight entering. Super thick clouds add additional buffer.
902  // If adj is set to one, then the weather provides some amount of buffer effect.
903  // This buffer will override the forestry one if it is set.
904  switch (weathermap[x][y].sky) {
905  case SKY_CLEAR:
906  case SKY_LIGHTCLOUD:
907  adj = 0;
908  break;
909  case SKY_HURRICANE:
910  case SKY_BLIZZARD:
911  adj = 2;
912  break;
913  default:
914  adj = 1;
915  break;
916  }
917 
918  /* adjust for time of day */
919  temp = weathermap[x][y].temp;
920  for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
921  temp += season_tempchange[i];
922  /* high amounts of water has a buffering effect on the temp */
923  if (weathermap[x][y].water > 33) {
924  i += weathermap[x][y].water/33;
925  }
926  // Cloudy skies will have a buffering effect on the temperature
927  if (adj >= 1)
928  i += adj;
929  // High amounts of trees also provide some amount of buffering under clear skies
930  else if (weathermap[x][y].forestry > 60) {
931  i++;
932  }
933  }
934  for (i = 0; i <= tod->hour; i++) {
935  temp += season_tempchange[i];
936  if (weathermap[x][y].water > 33) {
937  i += weathermap[x][y].water/33;
938  }
939  // Cloudy skies will have a buffering effect on the temperature
940  if (adj >= 1)
941  i += adj;
942  // High amounts of trees also provide some amount of buffering under clear skies
943  else if (weathermap[x][y].forestry > 60) {
944  i++;
945  }
946  }
947 
948  /* windchill */
949  for (i = 1; i < weathermap[x][y].windspeed; i += i) {
950  temp--;
951  }
952 
953  return temp;
954 }
955 
969 int real_world_temperature(int x, int y, mapstruct *m) {
970  int wx, wy, temp, eleva, elevb, trees;
971  object *op;
972  timeofday_t tod;
973 
974  // Get the time of day for real_temperature
975  // Since real_temperature is sometimes called in a loop, it expects
976  // the time of day to be provided to it instead of calculating it directly.
977  get_tod(&tod);
978 
979  /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
980  worldmap_to_weathermap(x, y, &wx, &wy, m);
981  temp = real_temperature(wx, wy, &tod);
982  if (weathermap[wx][wy].avgelev < 0) {
983  eleva = 0;
984  } else {
985  eleva = weathermap[x][y].avgelev;
986  }
987 
988  op = GET_MAP_OB(m, x, y);
989  if (!op) {
990  return eleva;
991  }
992 
993  elevb = op->elevation;
994  if (elevb < 0) {
995  elevb = 0;
996  }
997  if (elevb > eleva) {
998  elevb -= eleva;
999  temp -= elevb/1000;
1000  } else {
1001  elevb = eleva - elevb;
1002  temp += elevb/1000;
1003  }
1004 
1005  // Get localized effects from trees, too.
1006  trees = get_config_tile(x, y, m, forest_list);
1007  // Sparse trees reduce local temp by 1.
1008  // Dense trees raise it by one.
1009  if (trees > 0) {
1010  if (trees < 4)
1011  --temp;
1012  else
1013  ++temp;
1014  }
1015  // And done!
1016  return temp;
1017 }
1018 
1029 static void temperature_calc(const int x, const int y, const timeofday_t *tod) {
1030  int dist, equator, elev, n, trees;
1031  float diff, tdiff;
1032 
1033  // Warmer air has higher pressure than colder air.
1034  // Store the old value for temperature.
1035  int oldtemp = weathermap[x][y].temp, tempdiff;
1036 
1037  equator = (WEATHERMAPTILESX+WEATHERMAPTILESY)/4;
1038  diff = (float)(EQUATOR_BASE_TEMP-POLAR_BASE_TEMP)/(float)equator;
1039  tdiff = (float)SEASONAL_ADJUST/(float)(MONTHS_PER_YEAR/2.0);
1040  equator *= 2;
1041  n = 0;
1042  /* we essentially move the equator during the season */
1043  if (tod->month > (MONTHS_PER_YEAR/2)) { /* EOY */
1044  n -= (tod->month*tdiff);
1045  } else {
1046  n = (MONTHS_PER_YEAR - tod->month)*tdiff;
1047  }
1048  dist = polar_distance(x-n/2, y-n/2, equator);
1049 
1050  /* now we have the base temp, unadjusted for time. Time adjustment
1051  is not recorded on the map, rather, it's done JIT. */
1052  weathermap[x][y].temp = (int)(dist * diff);
1053  /* just scrap these for now, its mostly ocean */
1054  if (weathermap[x][y].avgelev < 0) {
1055  elev = 0;
1056  } else {
1057  // Make sure that higher elevations cause lower temps.
1058  elev = MIN(20000, weathermap[x][y].avgelev)/1000;
1059  }
1060  weathermap[x][y].temp -= elev;
1061 
1068  // Arbitrarily make the cutoff threshold for heat-hold as 60
1069  trees = weathermap[x][y].forestry;
1070  // Dense trees can raise the temperature up to ~3 degrees, per the calculations below.
1071  if (trees >= 60) {
1072  weathermap[x][y].temp += (trees-60)/15;
1073  }
1074  // If not, then we have heat reduction, most effective (~4 degrees) at 30.
1075  else if (trees >= 30){
1076  weathermap[x][y].temp -= (60-trees)/8;
1077  }
1078  else {
1079  weathermap[x][y].temp -= trees/8;
1080  }
1081 
1082  // Now we determine the difference in temperature and adjust the pressure accordingly.
1083  tempdiff = weathermap[x][y].temp - oldtemp;
1084  // The rate (arbitrarily chosen) for temperature-to-pressure change is 1 degrees per millibar
1085  // I'd have to keep track of partial millibar changes if I wanted to be coarser in this.
1086  if (tempdiff != 0)
1087  weathermap[x][y].pressure += tempdiff;
1088 }
1089 
1102 void compute_sky() {
1103  int x, y;
1104  int temp;
1105  int calc, inv_pressure;
1106  float press_root, max_root = sqrt(PRESSURE_MAX-PRESSURE_MIN);
1107  timeofday_t tod;
1108 
1109  // Before we begin the loops, we get the time of day for real_temperature()
1110  get_tod(&tod);
1111 
1112  for (x = 0; x < WEATHERMAPTILESX; x++) {
1113  for (y = 0; y < WEATHERMAPTILESY; y++) {
1114  temp = real_temperature(x, y, &tod);
1115  // Make sure we clip to the allowed pressure range.
1116  inv_pressure = MAX(0, MIN(PRESSURE_MAX-PRESSURE_MIN, (PRESSURE_MAX - weathermap[x][y].pressure)));
1117  // Take the square root. This allows us to have values weighted toward
1118  // producing rain. max_root holds the maximum value this could be.
1119  press_root = sqrt(inv_pressure);
1120  calc = MAX(0, MIN((int)(max_root*100), (int)(press_root * weathermap[x][y].humid)));
1121  // max_root*100 / 7 is the smallest we can feasibly divide by without side effects
1122  // So as long as we divide by a number greater than that, we're good.
1123  // If we divide by smaller, we overrun the sequential weather numbers, and reach FOG and HAIL
1124  // when not encountering their special cases.
1125  calc /= (int)(max_root*100 / 7) + 1;
1126 
1127  // If wind speed is high enough and we have rain, we can add one.
1128  if (calc >= SKY_LIGHT_RAIN && calc < SKY_HURRICANE && weathermap[x][y].windspeed > 30)
1129  ++calc;
1130  // If we are cold enough we have snow.
1131  if (temp <= 0 && calc >= SKY_LIGHT_RAIN)
1132  calc += 10;
1133 
1134  // Keep the old fog/hail generation for now
1135  if (weathermap[x][y].pressure >= 980 && weathermap[x][y].pressure < 1000) {
1136  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
1137  weathermap[x][y].windspeed < 3) {
1138  calc = SKY_FOG; /* rare */
1139  }
1140  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
1141  weathermap[x][y].windspeed > 35) {
1142  calc = SKY_HAIL; /* rare */
1143  }
1144  }
1145  weathermap[x][y].sky = calc;
1146  }
1147  }
1148 }
1149 
1159 static void spin_globe() {
1160  int x, xy, xy_eff;
1161  int buffer_humid;
1162  int buffer_sky;
1163  int buffer_pressure;
1164 
1165  // On each pass, x + y is a constant. We shift down and to the left, and wraparound to the upper right.
1166  // The cornermost tiles by the poles to not move as a result, so we can skip them.
1167  for (xy = 1; xy < WEATHERMAPTILESX + WEATHERMAPTILESY - 1; ++xy) {
1168  // Effective xy is essentially clipped to the end.
1169  // xy-xy_eff is thus the bounds on the other side of the map to care about for wraparound.
1170  xy_eff = MIN(xy, WEATHERMAPTILESX-1);
1171  buffer_humid = weathermap[xy-xy_eff][xy_eff].humid;
1172  buffer_sky = weathermap[xy-xy_eff][xy_eff].sky;
1173  buffer_pressure = weathermap[xy-xy_eff][xy_eff].pressure;
1174  for (x = xy-xy_eff; x < xy_eff; ++x) {
1175  /* Using xy directly here *looks* wrong, but is actually not,
1176  * since x = xy-xy_eff+c, where c is one less than the loop count;
1177  * thus, xy-x = xy-xy+xy_eff-c = xy_eff-c.
1178  * This is within the bounds of the map at all times.
1179  */
1180  weathermap[x][xy-x].humid = weathermap[x+1][xy-x-1].humid;
1181  weathermap[x][xy-x].sky = weathermap[x+1][xy-x-1].sky;
1182  weathermap[x][xy-x].pressure = weathermap[x+1][xy-x-1].pressure;
1183  }
1184  weathermap[xy_eff][xy-xy_eff].humid = buffer_humid;
1185  weathermap[xy_eff][xy-xy_eff].sky = buffer_sky;
1186  weathermap[xy_eff][xy-xy_eff].pressure = buffer_pressure;
1187  }
1188 }
1189 
1203 static int humid_tile(const int x, const int y, const int dark) {
1204  // ox and oy denote the neighbor that is influencing us (due to winds from there)
1205  int ox = x, oy = y, humid, evap, tempeffect, lightness;
1206 
1207  /* find the square the wind is coming from, without going out of bounds */
1208 
1209  if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
1210  if (y != 0) {
1211  oy = y-1;
1212  }
1213  }
1214  if (weathermap[x][y].winddir >= 6) {
1215  if (x != 0) {
1216  ox = x-1;
1217  }
1218  }
1219  if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
1220  if (y < WEATHERMAPTILESY-1) {
1221  oy = y+1;
1222  }
1223  }
1224  if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
1225  if (x < WEATHERMAPTILESX-1) {
1226  ox = x+1;
1227  }
1228  }
1229  // Determine the effect of sunlight on evaporation.
1230  int light = MAX_DARKNESS - dark;
1231  // The sky conditions affect how strong an effect the sunlight has.
1232  switch (weathermap[x][y].sky) {
1233  case SKY_CLEAR:
1234  tempeffect = light*light/4;
1235  break;
1236  case SKY_LIGHTCLOUD:
1237  tempeffect = light*light/5;
1238  break;
1239  case SKY_OVERCAST:
1240  tempeffect = light;
1241  break;
1242  case SKY_LIGHT_RAIN:
1243  case SKY_LIGHT_SNOW:
1244  tempeffect = light*4/5;
1245  break;
1246  case SKY_RAIN:
1247  case SKY_SNOW:
1248  tempeffect = light/2;
1249  break;
1250  case SKY_HEAVY_RAIN:
1251  case SKY_HEAVY_SNOW:
1252  tempeffect = light/3;
1253  break;
1254  case SKY_HURRICANE:
1255  case SKY_BLIZZARD:
1256  case SKY_HAIL:
1257  tempeffect = light/5;
1258  break;
1259  case SKY_FOG:
1260  default:
1261  tempeffect = 0;
1262  }
1263  // Determine the evaporative component contributing to the humidity.
1264  // The amount of water, the temperature, the wind, the pressure, the time of day, the cloudcover, and the previous humidity all affect the evaporation.
1265  // The exact formula is arbitrary, but it gives values that make some sense.
1266  evap = (weathermap[x][y].water/2+20+tempeffect)*(weathermap[x][y].temp+tempeffect)*weathermap[x][y].windspeed*10*(100-weathermap[x][y].humid)/(weathermap[x][y].pressure*weathermap[x][y].humid+1);
1267  // Don't go negative if temperature gets too low.
1268  evap = MAX(0, evap);
1269  // This is where the magic happens
1270  // If humidity is unstable over time, this is what will need to be tweaked
1271  // (or one of the values it depends on, if not this)
1272  humid = (weathermap[x][y].humid*2 +
1273  (weathermap[ox][oy].humid)*weathermap[ox][oy].windspeed/100 +
1274  // Evaporative components.
1275  evap + weathermap[x][y].forestry/10 + rndm(-3, 7))/
1276  (weathermap[ox][oy].windspeed/100+3)+rndm(-3, 3);
1277  if (humid < 0) {
1278  humid = 0;
1279  }
1280  if (humid > 100) {
1281  humid = 100;
1282  }
1283  return humid;
1284 }
1285 
1291 static void update_humid() {
1292  int x, y, dark = get_world_darkness();
1293 
1294  for (y = 0; y < WEATHERMAPTILESY; y++) {
1295  for (x = 0; x < WEATHERMAPTILESX; x++) {
1296  weathermap[x][y].humid = humid_tile(x, y, dark);
1297  }
1298  }
1299 }
1300 
1305 static void plot_gulfstream() {
1306  int x, y, tx, diroffset, dirdiff, ystart, ydiff, ylimup, ylimlow;
1307 
1308  x = gulf_stream_start;
1309 
1310  // Use the same offset/multiplier formula we used in gulf stream initialization
1311  // to make the code here much cleaner to look at.
1312  if (gulf_stream_direction) {
1313  diroffset = 0;
1314  dirdiff = -1;
1315  // We go from WEATHERMAPTILESY-1 down to 1 for the loop
1316  ystart = WEATHERMAPTILESY-1;
1317  ydiff = -1;
1318  ylimup = WEATHERMAPTILESY;
1319  ylimlow = 0;
1320  }
1321  else {
1322  diroffset = 10;
1323  dirdiff = 1;
1324  // We go from 0 to WEATHERMAPTILESY-2 for the loop
1325  ystart = 0;
1326  ydiff = 1;
1327  ylimup = WEATHERMAPTILESY-1;
1328  ylimlow = -1;
1329  }
1330  for (y = ystart; y > ylimlow && y < ylimup; y += ydiff) {
1331  for (tx = 0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
1332  if (similar_direction(weathermap[x+tx][y].winddir, gulf_stream_dir[tx][y]) && weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5) {
1333  weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
1334  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1335  } else if (gulf_stream_speed[tx][y] > weathermap[x+tx][y].windspeed) {
1336  // Preserve the wind speed of things with a higher speed that the gulf stream itself.
1337  weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
1338  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1339  } // If a storm moves through the gulf stream, it supercedes it with its own high winds.
1340 
1341  if (tx == GULF_STREAM_WIDTH-1) {
1342  switch ((diroffset-gulf_stream_dir[tx][y])*dirdiff) {
1343  case 6: x--; break;
1344  case 7: break;
1345  case 8: x++; break;
1346  }
1347  if (x < 0) {
1348  x++;
1349  }
1351  x--;
1352  }
1353  }
1354  }
1355  }
1356  /* occasionally move the stream
1357  * Arbitrary code from the original implementation says
1358  * 1 in 500 to switch, then 1 in 2 the switch actually is relevant.
1359  *
1360  * So, if we make the outer effect 1 in 1000, we cover both.
1361  */
1362  if (rndm(1, 1000) == 1) {
1363  // Reverse the stream direction.
1365  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
1366  for (y = 0; y < WEATHERMAPTILESY-1; y++) {
1367  // The direction changes here are dir + 4 mod 8, but 8 instead of 0 on those ones.
1368  gulf_stream_dir[tx][y] = (gulf_stream_dir[tx][y] + 4) & 7;
1369  // And we want 8 as a direction instead of 0.
1370  if (gulf_stream_dir[tx][y] == 0)
1371  gulf_stream_dir[tx][y] = 8;
1372  }
1373  }
1374  }
1375  /* Occasionally move the gulf stream starting point.
1376  * Original code had 1 in 25 to try, but 1 in 3 that the move was 0.
1377  *
1378  * So, the chance of actual movement was 2 in 75.
1379  *
1380  * We will use that and redesign the inner offset generation to determine + or - movement.
1381  */
1382  if (rndm(1, 75) <= 2) {
1383  // Only get +1 or -1
1384  gulf_stream_start += 1-2*rndm(0, 1);
1385  // Make sure we don't go off the map.
1388  }
1389  if (gulf_stream_start < 1) {
1391  }
1392  }
1393 }
1394 
1402  int x, y, rain;
1403 
1404  for (x = 0; x < WEATHERMAPTILESX; x++) {
1405  for (y = 0; y < WEATHERMAPTILESY; y++) {
1406  rain = weathermap[x][y].sky;
1407  if (rain >= SKY_LIGHT_SNOW) {
1408  rain -= 10;
1409  }
1410  if (rain > SKY_OVERCAST && rain < SKY_FOG) {
1411  rain -= SKY_OVERCAST;
1412  weathermap[x][y].rainfall += rain;
1413  }
1414  }
1415  }
1416 }
1417 
1430  int x,y, wx, wy;
1431  assert(worldmap_to_weathermap(0, 0, &wx, &wy, m) == 0);
1432  for (x = 0; x < wset.worldmaptilesizex; x++) {
1433  for (y = 0; y < wset.worldmaptilesizey; y++) {
1434  worldmap_to_weathermap(x, y, &wx, &wy, m);
1435  weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1436  }
1437  }
1438 }
1439 
1446  assert(wset.dynamiclevel > 0);
1447  update_humid(); /* Run the humidity updates based on prior pressure, temperature, and wind */
1448  perform_pressure(); /* pressure is the random factor */
1449  smooth_wind(); /* calculate the wind. depends on pressure */
1450  plot_gulfstream();
1451  init_temperature();
1452  spin_globe();
1453  //compute_sky(); This is done in perform_weather
1454 }
1455 
1456 /********************************************************************************************
1457  * Section END -- weather data calculators
1458  ********************************************************************************************/
1459 
1460 /********************************************************************************************
1461  * Section -- Weather effect methods
1462  * Functions to provide weather effects.
1463  * This includes precipitation, puddles, ice on water, snowfall, growing plants,
1464  * defacing the world (since some comments seemed to imply that one was broken, and I never changed it)
1465  ********************************************************************************************/
1466 
1482 static void do_precipitation(mapstruct * const m, const int x, const int y, const int temp, const int sky) {
1483  // Do falling rain/snow here
1484  const archetype *at = NULL;
1485  object *tmp = NULL;
1486  int avoid = 0;
1487  int pct_precip = 0; // 0-100: percent tiles with precipitation
1488  switch (sky) {
1489  case SKY_LIGHT_RAIN:
1490  case SKY_LIGHT_SNOW:
1491  pct_precip = 10;
1492  break;
1493  case SKY_RAIN:
1494  case SKY_SNOW:
1495  pct_precip = 30;
1496  break;
1497  case SKY_HEAVY_RAIN:
1498  case SKY_HEAVY_SNOW:
1499  pct_precip = 60;
1500  break;
1501  case SKY_HURRICANE:
1502  case SKY_BLIZZARD:
1503  pct_precip = 99;
1504  break;
1505  }
1506  // TODO: Move these globally? Pretty sure they will be in a consistent spot in memory for execution duration.
1507  sstring snowc = find_string("snow_c");
1508  sstring rain = find_string("rain");
1509  if (rndm(0, 99) + pct_precip >= 100) {
1510  // Do our weather inserts here.
1511  // t < -2 == always snow
1512  // 2 >= t > -2 == rain/snow mix
1513  // t > 2 == always rain
1514  if (temp < -2 || (temp <= 2 && rndm(0, 2+temp) - 2 <= 0)) {
1515  at = find_archetype("snow_c");
1516  }
1517  else {
1518  at = find_archetype("rain");
1519  }
1520  if (at) {
1521  // Make sure we don't stack rains/snow ad nauseam on tiles. Also allow to switch the precip on the tile.
1522  tmp = GET_MAP_OB(m, x, y);
1523  avoid = 0;
1524  while (tmp) {
1525  if (tmp->arch == at) {
1526  avoid++;
1527  break;
1528  }
1529  else if ((tmp->arch->name == snowc && at->name == rain) ||
1530  (tmp->arch->name == rain && at->name == snowc)) {
1531  // Remove the wrong precipitation
1532  object_remove(tmp);
1533  object_free(tmp, 0);
1534  }
1535  tmp = tmp->above;
1536  }
1537  if (!avoid)
1539  }
1540  }
1541  else {
1542  // Look for a rain/snow on this tile and remove it.
1543  tmp = GET_MAP_OB(m, x, y);
1544  while (tmp) {
1545  if (tmp->arch->name == rain || tmp->arch->name == snowc) {
1546  object_remove(tmp);
1547  object_free(tmp, 0);
1548  }
1549  tmp = tmp->above;
1550  }
1551  }
1552 }
1553 
1560 static void do_map_precipitation(mapstruct * const m) {
1561  if (!m)
1562  return;
1563  // Make sure it has a weather map.
1564  int x, y, temp, sky, wx, wy;
1565  if (worldmap_to_weathermap(0, 0, &x, &y, m) != 0)
1566  return;
1567  // Now we re-do the precipitation on the map.
1568  for (x = 0; x < m->width; ++x)
1569  for (y = 0; y < m->height; ++y) {
1570  worldmap_to_weathermap(x, y, &wx, &wy, m);
1571  temp = weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1572  sky = weathermap[wx][wy].sky;
1573  do_precipitation(m, x, y, temp, sky);
1574  }
1575 }
1576 
1583 static void let_it_snow(mapstruct * const m) {
1584  int x, y, i, wx, wy;
1585  int nx, ny, j, d;
1586  int avoid, temp, sky, gotsnow, found, nodstk;
1587  object *ob, *tmp, *oldsnow, *topfloor;
1588  archetype *at, *doublestack, *doublestack2;
1589 
1590  sstring dungmag = find_string("dungeon_magic");
1591 
1592  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1593  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1594  /* jitter factor */
1595  if (rndm(0, 2) > 0) {
1596  x = y = d = -1;
1597  while (OUT_OF_REAL_MAP(m, x, y)) {
1598  d++;
1599  j = rndm(1, 8);
1600  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1601  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1602  if (d > 15) {
1603  x = nx;
1604  y = ny;
1605  }
1606  }
1607  } else {
1608  x = nx;
1609  y = ny;
1610  }
1611  /* we use the unjittered coordinates */
1612  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1613  ob = NULL;
1614  at = NULL;
1615  doublestack = NULL;
1616  /* this will definately need tuning */
1617  avoid = 0;
1618  gotsnow = 0;
1619  nodstk = 0;
1620  /*temp = real_world_temperature(x, y, m);*/
1621  temp = weathermap[wx][wy].realtemp;
1622  sky = weathermap[wx][wy].sky;
1623  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG) {
1624  sky += 10; /*let it snow*/
1625  }
1626  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1627  if (!avoid) {
1628  if (sky >= SKY_LIGHT_SNOW && sky < SKY_HEAVY_SNOW) {
1629  at = find_archetype("snow5");
1630  }
1631  if (sky >= SKY_HEAVY_SNOW) {
1632  at = find_archetype("snow4");
1633  }
1634  if (sky >= SKY_LIGHT_SNOW) {
1635  /* the bottom floor of scorn is not IS_FLOOR */
1636  topfloor = NULL;
1637  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp, tmp = tmp->above) {
1638  if (tmp->arch->name != dungmag) {
1639  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1640  break;
1641  }
1642  }
1643  }
1644  /* topfloor should now be the topmost IS_FLOOR=1 */
1645  if (topfloor == NULL) {
1646  continue;
1647  }
1648  if (tmp != NULL) {
1649  nodstk++;
1650  }
1651  /* something is wrong with that sector. just skip it */
1652  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1653  if (check_replace_match(topfloor, repl)) {
1654  if (repl->special_snow != NULL) {
1655  at = repl->special_snow;
1656  }
1657  if (repl->doublestack_arch != NULL && !nodstk) {
1658  doublestack = repl->doublestack_arch;
1659  }
1660  break;
1661  }
1662  }
1663  }
1664  if (gotsnow && at) {
1665  if (oldsnow->arch == at) {
1666  at = NULL;
1667  } else {
1668  object_remove(oldsnow);
1669  object_free(oldsnow,0);
1670  tmp = GET_MAP_OB(m, x, y);
1671  /* clean up the trees we put over the snow */
1672  doublestack2 = NULL;
1673  if (tmp) {
1674  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1675  if (repl->doublestack_arch == NULL) {
1676  continue;
1677  }
1678  if (check_replace_match(tmp, repl)) {
1679  tmp = tmp->above;
1680  doublestack2 = repl->doublestack_arch;
1681  break;
1682  }
1683  }
1684  }
1685  if (tmp != NULL && doublestack2 != NULL) {
1686  if (tmp->arch == doublestack2) {
1687  object_remove(tmp);
1688  object_free(tmp,0);
1689  }
1690  }
1691  }
1692  }
1693  if (at != NULL) {
1695  if (doublestack != NULL) {
1696  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1697  }
1698  }
1699  }
1700  if (temp > 8 && GET_MAP_OB(m, x, y) != NULL) {
1701  /* melt some snow */
1702  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1703  avoid = 0;
1704  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1705  if (repl->special_snow == NULL) {
1706  continue;
1707  }
1708 
1709  if (tmp->arch == repl->special_snow) {
1710  avoid++;
1711  }
1712  if (avoid) {
1713  break;
1714  }
1715  }
1716  if (avoid) {
1717  /* replace snow with a big puddle */
1718  /* If it is a floor tile we're melting, try to place earth there to have *some* floor.
1719  * Don't mark as overlay, or it will be stuck there forever, rather than until the map resets.
1720  */
1721  if (!tmp->below || QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1722  at = find_archetype("earth");
1723  if (at)
1725  }
1726  object_remove(tmp);
1727  object_free(tmp,0);
1728  tmp = GET_MAP_OB(m, x, y);
1729  at = NULL; // Reset what arch we are looking at
1730  if (tmp) {
1731  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1732  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1733  if (tmp->arch->name == melt->tile) {
1734  at = melt->special_snow;
1735  }
1736  }
1737  }
1738  // Default
1739  if (!at) {
1740  at = find_archetype("rain5_weather");
1741  }
1742  if (at != NULL) {
1744  }
1745  }
1746  }
1747  }
1748  /* woo it's cold out */
1749  if (temp < -8) {
1750  avoid = 0;
1751  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1752  if (!strcasecmp(tmp->name, "ice")) {
1753  avoid--;
1754  }
1755  }
1756  tmp = GET_MAP_OB(m, x, y);
1757  if (tmp && (!strcasecmp(tmp->name, "sea"))) {
1758  avoid++;
1759  } else if (tmp && (!strcasecmp(tmp->name, "sea1"))) {
1760  avoid++;
1761  } else if (tmp && (!strcasecmp(tmp->name, "deep sea"))) {
1762  avoid++;
1763  } else if (tmp && (!strcasecmp(tmp->name, "shallow sea"))) {
1764  avoid++;
1765  }
1766  if (avoid > 0) {
1767  at = find_archetype("ice");
1769  }
1770  }
1771  }
1772  }
1773 }
1774 
1781 static void singing_in_the_rain(mapstruct * const m) {
1782  int x, y, i, wx, wy;
1783  int nx, ny, d, j;
1784  int avoid, temp, sky, gotsnow, /*found,*/ nodstk;
1785  object *ob, *tmp, *oldsnow, *topfloor;
1786  archetype *at, *doublestack, *doublestack2;
1787 
1788  sstring dungmag = find_string("dungeon_magic");
1789 
1790  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1791  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1792  /* jitter factor */
1793  if (rndm(0, 2) > 0) {
1794  x = y = d = -1;
1795  while (OUT_OF_REAL_MAP(m, x, y)) {
1796  // Save some processing when d > 15
1797  if (++d > 15) {
1798  x = nx;
1799  y = ny;
1800  }
1801  else {
1802  j = rndm(1, 8);
1803  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1804  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1805  }
1806  }
1807  } else {
1808  x = nx;
1809  y = ny;
1810  }
1811  /* we use the unjittered coordinates */
1812  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1813  ob = NULL;
1814  at = NULL;
1815  doublestack = NULL;
1816  avoid = 0;
1817  gotsnow = 0;
1818  nodstk = 0;
1819  /*temp = real_world_temperature(x, y, m);*/
1820  temp = weathermap[wx][wy].realtemp;
1821  sky = weathermap[wx][wy].sky;
1822  /* Handle adding precipitation here. */
1823  do_precipitation(m, x, y, temp, sky);
1824 
1825  /* it's probably allready snowing */
1826  if (temp < 0) {
1827  continue;
1828  }
1829 
1830  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1831  if (!avoid) {
1832  tmp = GET_MAP_OB(m, x, y);
1833  if (tmp) {
1834  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1835  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1836  if (tmp->arch->name == melt->tile) {
1837  at = melt->special_snow;
1838  }
1839  }
1840  if (at)
1841  break;
1842  }
1843  if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1844  switch (rndm(0, SKY_HAIL-sky)) {
1845  case 0: at = find_archetype("rain1_weather"); break;
1846  case 1: at = find_archetype("rain2_weather"); break;
1847  default: at = NULL; break;
1848  }
1849  }
1850  if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE) {
1851  switch (rndm(0, SKY_HAIL-sky)) {
1852  case 0: at = find_archetype("rain3_weather"); break;
1853  case 1: at = find_archetype("rain4_weather"); break;
1854  case 2: at = find_archetype("rain5_weather"); break;
1855  default: at = NULL; break;
1856  }
1857  }
1858  /* the bottom floor of scorn is not IS_FLOOR */
1859  topfloor = NULL;
1860  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp,tmp = tmp->above) {
1861  if (tmp->arch->name != dungmag) {
1862  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1863  break;
1864  }
1865  }
1866  }
1867  /* topfloor should now be the topmost IS_FLOOR=1 */
1868  if (topfloor == NULL) {
1869  continue;
1870  }
1871  if (tmp != NULL) {
1872  nodstk++;
1873  }
1874  /* something is wrong with that sector. just skip it */
1875  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1876  if (check_replace_match(topfloor, repl)) {
1877  if (repl->doublestack_arch != NULL && !nodstk) {
1878  doublestack = repl->doublestack_arch;
1879  }
1880  break;
1881  }
1882  }
1883  if (gotsnow && at) {
1884  if (oldsnow->arch == at) {
1885  at = NULL;
1886  } else {
1887  tmp = GET_MAP_OB(m, x, y);
1888  object_remove(oldsnow);
1889  /* clean up the trees we put over the snow */
1890  doublestack2 = NULL;
1891  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1892  if (repl->doublestack_arch == NULL) {
1893  continue;
1894  }
1895  if (check_replace_match(tmp, repl)) {
1896  tmp = tmp->above;
1897  doublestack2 = repl->doublestack_arch;
1898  break;
1899  }
1900  }
1901  object_free(oldsnow,0);
1902  if (tmp != NULL && doublestack2 != NULL) {
1903  if (tmp->arch == doublestack2) {
1904  object_remove(tmp);
1905  object_free(tmp,0);
1906  }
1907  }
1908  }
1909  }
1910  if (at != NULL) {
1912  if (doublestack != NULL) {
1913  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1914  }
1915  }
1916  }
1917  /* Things evaporate fast in the heat */
1918  if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1919  /* evaporate */
1920  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1921  // Find a tile to evaporate
1922  weather_replace_t *evap;
1923  for (evap = weather_evaporate; evap; evap = evap->next) {
1924  if (tmp->arch->name == evap->tile)
1925  break;
1926  }
1927  // If we found it, then evaporate it
1928  if (evap) {
1929  object_remove(tmp);
1930  object_free(tmp,0);
1931  if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0) {
1932  weathermap[wx][wy].humid++;
1933  }
1934  // If the evaporation is done, clean up the doublestack on this tile.
1935  if (evap->special_snow == NULL) {
1936  tmp = GET_MAP_OB(m, x, y);
1937  /* clean up the trees we put over the rain */
1938  doublestack2 = NULL;
1939  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1940  if (repl->doublestack_arch == NULL) {
1941  continue;
1942  }
1943  if (check_replace_match(tmp, repl)) {
1944  tmp = tmp->above;
1945  doublestack2 = repl->doublestack_arch;
1946  break;
1947  }
1948  }
1949  if (tmp != NULL && doublestack2 != NULL) {
1950  if (tmp->arch == doublestack2) {
1951  object_remove(tmp);
1952  object_free(tmp,0);
1953  }
1954  }
1955  }
1956  else {
1957  // Apply the replacement puddle
1959  }
1960  break;
1961  }
1962  }
1963  }
1964  }
1965  }
1966 }
1967 
1974 static void plant_a_garden(mapstruct *const m) {
1975  int x, y, i, wx, wy;
1976  int avoid, two, temp, sky, gotsnow, found, days;
1977  object *ob, *tmp;
1978  archetype *at;
1979 
1981  for (x = 0; x < wset.worldmaptilesizex; x++) {
1982  for (y = 0; y < wset.worldmaptilesizey; y++) {
1983  (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1984  ob = NULL;
1985  at = NULL;
1986  avoid = 0;
1987  two = 0;
1988  gotsnow = 0;
1989  /*temp = real_world_temperature(x, y, m);*/
1990  temp = weathermap[wx][wy].realtemp;
1991  sky = weathermap[wx][wy].sky;
1992  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1993  if (!avoid) {
1994  found = 0;
1995  for (i = 0; weather_grow[i].herb != NULL; i++) {
1996  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1997  if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0) {
1998  continue;
1999  }
2000 
2001  /* we found there is a herb here allready */
2002  found++;
2003  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2004  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
2005  weathermap[wx][wy].humid < weather_grow[i].humin ||
2006  weathermap[wx][wy].humid > weather_grow[i].humax ||
2007  temp < weather_grow[i].tempmin ||
2008  temp > weather_grow[i].tempmax ||
2009  rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
2010  /* the herb does not belong, randomly delete
2011  herbs to prevent overgrowth. */
2012  object_remove(tmp);
2013  object_free(tmp,0);
2014  break;
2015  }
2016  }
2017  /* don't doublestack herbs */
2018  if (found) {
2019  continue;
2020  }
2021  /* add a random factor */
2022  if (rndm(1, weather_grow[i].random) != 1) {
2023  continue;
2024  }
2025  /* we look up through two tiles for a matching tile */
2026  if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
2027  if (strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_grow[i].tile) != 0) {
2028  if (GET_MAP_OB(m, x, y)->above != NULL) {
2029  if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name, weather_grow[i].tile) != 0) {
2030  continue;
2031  }
2032  } else {
2033  continue;
2034  }
2035  }
2036  }
2037  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2038  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax) {
2039  continue;
2040  }
2041  if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2042  weathermap[wx][wy].humid > weather_grow[i].humax) {
2043  continue;
2044  }
2045  if (temp < weather_grow[i].tempmin ||
2046  temp > weather_grow[i].tempmax) {
2047  continue;
2048  }
2049  if ((!GET_MAP_OB(m, x, y)) ||
2050  GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2051  GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax) {
2052  continue;
2053  }
2054  /* we got this far.. must be a match */
2055  at = find_archetype(weather_grow[i].herb);
2056  break;
2057  }
2058  if (at != NULL) {
2059  /* XXX is overlay_floor right? maybe.. */
2061  }
2062  }
2063  }
2064  }
2065 }
2066 
2073 static void change_the_world(mapstruct * const m) {
2074  int x, y, i, wx, wy;
2075  int nx, ny, j, d;
2076  int avoid, two, temp, sky, gotsnow, found, days;
2077  object *ob, *tmp, *doublestack;
2078  archetype *at, *dat;
2079 
2081  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
2082  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
2083  /* jitter factor */
2084  if (rndm(0, 2) > 0) {
2085  x = y = d = -1;
2086  while (OUT_OF_REAL_MAP(m, x, y)) {
2087  d++;
2088  j = rndm(1, 8);
2089  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2090  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2091  if (d > 15) {
2092  x = nx;
2093  y = ny;
2094  }
2095  }
2096  } else {
2097  x = nx;
2098  y = ny;
2099  }
2100  /* we use the unjittered coordinates */
2101  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2102  ob = NULL;
2103  at = NULL;
2104  dat = NULL;
2105  avoid = 0;
2106  two = 0;
2107  gotsnow = 0;
2108  /*temp = real_world_temperature(x, y, m);*/
2109  temp = weathermap[wx][wy].realtemp;
2110  sky = weathermap[wx][wy].sky;
2111  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2112  if (!avoid) {
2113  for (i = 0; weather_tile[i].herb != NULL; i++) {
2114  found = 0;
2115  doublestack = NULL;
2116  if (GET_MAP_OB(m, x, y)) {
2117  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2118  if (weather_tile[i].tile != NULL) {
2119  if (strcmp(tmp->arch->name, weather_tile[i].tile) == 0) {
2120  doublestack = tmp;
2121  continue;
2122  }
2123  }
2124  if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0) {
2125  continue;
2126  }
2127 
2128  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2129  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2130  weathermap[wx][wy].humid < weather_tile[i].humin ||
2131  weathermap[wx][wy].humid > weather_tile[i].humax ||
2132  temp < weather_tile[i].tempmin ||
2133  temp > weather_tile[i].tempmax) {
2134  object_remove(tmp);
2135  object_free(tmp,0);
2136  if (doublestack) {
2137  object_remove(doublestack);
2138  object_free(doublestack,0);
2139  }
2140  break;
2141  } else {
2142  found++; /* there is one here allready. leave it */
2143  break;
2144  }
2145  }
2146  }
2147  if (found) {
2148  break;
2149  }
2150 
2151  /* add a random factor */
2152  if (rndm(1, weather_tile[i].random) != 1) {
2153  continue;
2154  }
2155  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2156  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax) {
2157  continue;
2158  }
2159  if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2160  weathermap[wx][wy].humid > weather_tile[i].humax) {
2161  continue;
2162  }
2163  if (temp < weather_tile[i].tempmin ||
2164  temp > weather_tile[i].tempmax) {
2165  continue;
2166  }
2167  if ( (!GET_MAP_OB(m, x, y)) ||
2168  GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2169  GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax) {
2170  continue;
2171  }
2172  /* we got this far.. must be a match */
2173  if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_tile[i].herb) == 0) {
2174  break; /* no sense in doubling up */
2175  }
2176  at = find_archetype(weather_tile[i].herb);
2177  break;
2178  }
2179  if (at != NULL) {
2180  if (weather_tile[i].tile != NULL && GET_MAP_OB(m, x, y) && strcmp(weather_tile[i].tile, GET_MAP_OB(m, x, y)->arch->name) != 0) {
2181  dat = find_archetype(weather_tile[i].tile);
2182  }
2183  if (dat != NULL) {
2185  }
2186  if (gotsnow == 0) {
2188  }
2189  }
2190  }
2191  }
2192  }
2193 }
2194 
2207 static void weather_effect(mapstruct * const m) {
2208  int wx, wy, x, y;
2209 
2210  /* if the dm shut off weather, go home */
2211  if (wset.dynamiclevel < 1) {
2212  return;
2213  }
2214 
2215  if (!m->outdoor) {
2216  return;
2217  }
2218 
2219  x = 0;
2220  y = 0;
2221  /* for now, just bail if it's not the worldmap */
2222  if (worldmap_to_weathermap(x, y, &wx, &wy, m) != 0) {
2223  return;
2224  }
2225 
2226  /*First, calculate temperature*/
2228  /* we change the world first, if needed */
2229  if (wset.dynamiclevel >= 5) {
2231  }
2232  if (wset.dynamiclevel >= 2) {
2233  let_it_snow(m);
2235  }
2236  if (wset.dynamiclevel >= 3) {
2237  plant_a_garden(m);
2238  }
2239 }
2240 
2250  mapstruct *m;
2251  char filename[MAX_BUF];
2252  FILE *fp;
2253 
2254  if (!wset.dynamiclevel) {
2255  return;
2256  }
2257 
2258  /* move right to left, top to bottom */
2260  wmperformstartx = 0;
2262  wmperformstarty = 0;
2263  }
2264  }
2265 
2266  // Whenever we load a map for effects, recalculate the weather.
2267  // Do this before the actual map load so that precipitation is done with the new sky computation rather than the old
2268  compute_sky();
2269 
2270  snprintf(filename, sizeof(filename), "world/world_%d_%d", wmperformstartx+settings.worldmapstartx, wmperformstarty+settings.worldmapstarty);
2271 
2272  m = ready_map_name(filename, 0);
2273  if (m == NULL) {
2274  return; /* hrmm */
2275  }
2276 
2277  // Run weather effects here.
2278  // We do this here rather than on any map load so that the weather effects remain consistent.
2279  weather_effect(m);
2280 
2281  /* done */
2282  save_map(m, SAVE_MODE_OVERLAY); /* write the overlay */
2283  m->in_memory = MAP_IN_MEMORY; /*reset this*/
2284  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings.localdir);
2285  if ((fp = fopen(filename, "w")) == NULL) {
2286  LOG(llevError, "Cannot open %s for writing\n", filename);
2287  return;
2288  }
2289 
2290  if (players_on_map(m, TRUE) == 0) {
2291  delete_map(m);
2292  }
2293 
2294  fprintf(fp, "%d %d", wmperformstartx, wmperformstarty);
2295  fclose(fp);
2296 }
2297 
2319 static uint8_t wind_blow_object(mapstruct * const m, const int x, const int y, const MoveType move_type, int32_t wt, const living *stats) {
2320  // If we're inside, the weather can't get us :P
2321  if (!m || !m->outdoor)
2322  return 0;
2323  // First, we get the weathermap for this location
2324  int nx, ny;
2325  if (worldmap_to_weathermap(x, y, &nx, &ny, m))
2326  return 0;
2327  int windspeed = weathermap[nx][ny].windspeed;
2328  int is_fly = move_type & MOVE_FLYING;
2329  // If not flying, then strong winds are needed to affect you.
2330  if (!is_fly)
2331  windspeed -= 20;
2332  // If no wind, then no push.
2333  if (windspeed <= 0)
2334  return 0;
2335  // Reduce effect from carrying more stuff.
2336  // Also, being on the ground makes the same wind increase affect you less as well.
2337  // Higher strength characters can also resist being blown by the wind when on the ground.
2338  if (!is_fly)
2339  wt /= 10000 * ((stats && stats->Str) ? stats->Str : 1);
2340  // When flying, we care about Dex over Str.
2341  else
2342  wt /= 20000 * ((stats && stats->Dex) ? stats->Dex : 1);
2343  // Massive things are pushed around less easily.
2344  if (windspeed*2 < wt)
2345  return 0;
2346  // The push will not happen every try. The greater the wind, the more often it succeeds.
2347  // Also, the lighter the object, the more often it succeeds
2348  // We do two rolls because it normalizes the effects better than a single roll.
2349  if (rndm(0, windspeed)+rndm(0, windspeed) < wt)
2350  return 0;
2351  // winddir is the direction the wind is coming from.
2352  // so we need to reverse it to push where the wind is going to.
2353  return absdir(weathermap[nx][ny].winddir+4);
2354 }
2355 
2356 /********************************************************************************************
2357  * Section END -- weather effect methods
2358  ********************************************************************************************/
2359 
2360 /********************************************************************************************
2361  * Section -- Initializations
2362  * Functions to load in config for determining certain weathermap attributes,
2363  * functions to initialize missing weathermap attributes,
2364  * and their helper functions.
2365  ********************************************************************************************/
2366 
2387 static int init_config_vals(const Settings *settings, const char *conf_filename, DensityConfig **list) {
2388  char filename[MAX_BUF], *line, *name;
2389  BufferReader *bfr;
2390  int found, is_obj_name, tree_count;
2391 
2392  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2393  // Open the file with the buffer reader.
2394  bfr = bufferreader_init_from_file(NULL, filename,
2395  "init_config_vals: Could not open file %s. No forestry data is defined. %s\n",
2396  llevError);
2397  if (bfr == NULL) {
2398  // The error was printed by bufferreader_init_from_file already, so just bail.
2399  return 1;
2400  }
2401  // Now we read in from the buffer.
2402  while ((line = bufferreader_next_line(bfr)) != NULL) {
2403  // Now we parse the line
2404  // Start by examining the first character.
2405  switch (*line) {
2406  // Ignore empty lines and comment lines (denoted by # at front)
2407  case '\0':
2408  case '#':
2409  // Handling \r means Windows should work right, too.
2410  case '\r':
2411  case '\n':
2412  break;
2413  default:
2414  // Actually parse the line
2415  // Format is like this:
2416  // name, (0 if arch, 1 if object name), # trees
2417  // [spaces are expected after commas]
2418 
2419  // sscanf on strings is wonky (it always reads to whitespace),
2420  // so I'm gonna do it by just nabbing part of the buffer.
2421  name = line; // Each line starts with name
2423  if (line == NULL) {
2424  LOG(llevError, "init_config_vals: Malformed name entry in %s, line %ld.\n",
2425  filename, bufferreader_current_line(bfr));
2426  // Move on to the next line and hope it is fine.
2427  continue;
2428  }
2429 
2430  found = sscanf(line, "%d, %d\n", &is_obj_name, &tree_count);
2431  if (found != 2) {
2432  // Print an error for the malformed line
2433  LOG(llevError, "init_config_vals: Malformed forestry entry in %s, line %ld.\n",
2434  filename, bufferreader_current_line(bfr));
2435  }
2436  else {
2437  // Add a struct to the list.
2438  DensityConfig *frst = (DensityConfig *)malloc(sizeof(DensityConfig));
2439  if (!frst) {
2441  }
2442  // Shared strings are friend, not food
2443  frst->name = add_string(name);
2444  frst->is_obj = is_obj_name;
2445  frst->value_density = tree_count;
2446  // Attach to front of list, since order doesn't matter much, if at all.
2447  frst->next = *list;
2448  *list = frst;
2449  }
2450  }
2451  }
2452  bufferreader_destroy(bfr);
2453  return 0;
2454 }
2455 
2472 static int init_weatheravoid(const Settings *settings, const char *conf_filename, weather_avoids_t **wa) {
2473  char filename[MAX_BUF], *line, *name;
2474  BufferReader *bfr;
2475  int found, is_effect;
2476 
2477  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2478  // Open the file with the buffer reader.
2479  bfr = bufferreader_init_from_file(NULL, filename,
2480  "init_weatheravoid: Could not open file %s. No weatheravoid data is defined. %s\n", llevError);
2481  // If the bufferreader failed, it return NULL and printed an error, so just bail if failure.
2482  if (bfr == NULL)
2483  return 1;
2484  // Now we read in from the buffer.
2485  while ((line = bufferreader_next_line(bfr)) != NULL) {
2486  // Now we parse the line
2487  // Start by examining the first character.
2488  switch (*line) {
2489  // Ignore empty lines and comment lines (denoted by # at front)
2490  case '\0':
2491  case '#':
2492  // Handling \r means Windows should work right, too.
2493  case '\r':
2494  case '\n':
2495  break;
2496  default:
2497  // Actually parse the line
2498  // Format is like this:
2499  // name, (1 if weather effect, 0 if regular tile)
2500  // [spaces are expected after commas]
2501 
2502  // sscanf on strings is wonky (it always reads to whitespace),
2503  // so I'm gonna do it by just nabbing part of the buffer.
2504  name = line; // Each line starts with name
2506  if (line == NULL) {
2507  LOG(llevError, "init_weatheravoid: Malformed name entry in %s, line %ld.\n",
2508  filename, bufferreader_current_line(bfr));
2509  // Move on to the next line and hope it is fine.
2510  continue;
2511  }
2512 
2513  found = sscanf(line, "%d\n", &is_effect);
2514  if (found != 1) {
2515  // Print an error for the malformed line
2516  LOG(llevError, "init_weatheravoid: Malformed effect flag entry in %s, line %ld.\n",
2517  filename, bufferreader_current_line(bfr));
2518  }
2519  else {
2520  // Add a struct to the list.
2521  weather_avoids_t *frst = (weather_avoids_t *)malloc(sizeof(weather_avoids_t));
2522  if (!frst) {
2524  }
2525  // Shared strings are friend, not food
2526  frst->name = add_string(name);
2527  frst->snow = is_effect;
2528  // Attach to front of list, since order doesn't matter much, if at all.
2529  frst->next = *wa;
2530  *wa = frst;
2531  }
2532  }
2533  }
2534  bufferreader_destroy(bfr);
2535  return 0;
2536 }
2537 
2554 static int init_weather_replace(const Settings *settings, const char *conf_filename, weather_replace_t **list) {
2555  char filename[MAX_BUF], *line, *name, *repl, *doublestack;
2556  BufferReader *bfr;
2557  int found, is_arch;
2558 
2559  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2560  // Open the file with the buffer reader.
2561  bfr = bufferreader_init_from_file(NULL, filename, "init_weather_replace: Could not open file %s. No weather replace data is defined. %s\n", llevError);
2562  // If failed, we already printed an error.
2563  if (bfr == NULL)
2564  return 1;
2565  // Now we read in from the buffer.
2566  while ((line = bufferreader_next_line(bfr)) != NULL) {
2567  // Now we parse the line
2568  // Start by examining the first character.
2569  switch (*line) {
2570  // Ignore empty lines and comment lines (denoted by # at front)
2571  case '\0':
2572  case '#':
2573  // Handling \r means Windows should work right, too.
2574  case '\r':
2575  case '\n':
2576  break;
2577  default:
2578  // Actually parse the line
2579  // Format is like this:
2580  // name, replacement tile arch name, additional tile arch name (or NONE if not), (1 if arch name, 0 if object name)
2581  // [spaces are expected after commas]
2582 
2583  // sscanf on strings is wonky (it always reads to whitespace),
2584  // so I'm gonna do it by just nabbing part of the buffer.
2585  name = line; // Each line starts with name
2587  if (line == NULL) {
2588  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2589  LOG(llevError, "init_weather_replace: Malformed name entry in %s, line %ld.\n",
2590  filename, bufferreader_current_line(bfr));
2591  // Move on to the next line and hope it is fine.
2592  continue;
2593  }
2594 
2595  repl = line; // Each line starts with name
2597  if (line == NULL) {
2598  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2599  LOG(llevError, "init_weather_replace: Malformed replacement entry in %s, line %ld.\n",
2600  filename, bufferreader_current_line(bfr));
2601  // Move on to the next line and hope it is fine.
2602  continue;
2603  }
2604 
2605  doublestack = line; // Each line starts with name
2607  if (line == NULL) {
2608  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2609  LOG(llevError, "init_weather_replace: Malformed doublestack entry in %s, line %ld.\n",
2610  filename, bufferreader_current_line(bfr));
2611  // Move on to the next line and hope it is fine.
2612  continue;
2613  }
2614 
2615  found = sscanf(line, "%d\n", &is_arch);
2616  if (found != 1) {
2617  // Print an error for the malformed line
2618  LOG(llevError, "init_weatheravoid: Malformed archetype/object flag entry in %s, line %ld.\n",
2619  filename, bufferreader_current_line(bfr));
2620  }
2621  else {
2622  // Add a struct to the list.
2623  weather_replace_t *frst = (weather_replace_t *)malloc(sizeof(weather_replace_t));
2624  if (!frst) {
2626  }
2627  // Shared strings are friend, not food
2628  frst->tile = add_string(name);
2629  // Some replcement definitions can have NONE here to denote removal
2630  if (strcmp(repl, "NONE") == 0)
2631  frst->special_snow = NULL;
2632  else
2633  frst->special_snow = find_archetype(repl);
2634  // if doublestack is NONE, then set the arch to NULL
2635  if (strcmp(doublestack, "NONE") == 0)
2636  frst->doublestack_arch = NULL;
2637  else
2638  frst->doublestack_arch = find_archetype(doublestack);
2639  frst->arch_or_name = is_arch;
2640  // Attach to front of list, since order doesn't matter much, if at all.
2641  frst->next = *list;
2642  *list = frst;
2643  }
2644  }
2645  }
2646  bufferreader_destroy(bfr);
2647  return 0;
2648 }
2649 
2669 static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int * const tx, int * const ty) {
2670  char mapname[MAX_BUF];
2671  if (!m || !tx || !ty)
2672  return -1;
2673  // Now we do what was wanted.
2674  weathermap_to_worldmap_corner(x, y, tx, ty, dir, mapname, sizeof(mapname));
2675  *m = mapfile_load(mapname, 0);
2676  if (*m == NULL) {
2677  return -1;
2678  }
2679 
2680  int res = load_overlay_map(mapname, *m);
2681  if (res != 0) {
2682  return -1;
2683  }
2684  return 0;
2685 }
2686 
2712 static int do_water_elev_calc(mapstruct * const m, const int x, const int y, int * const water, int64_t * const elev, int * const trees) {
2713  if (!m || !water || !elev || !trees)
2714  return -1;
2715  object *ob = GET_MAP_OB(m, x, y), *obtmp;
2716  if (ob) {
2717  DensityConfig *tmp;
2718  if (QUERY_FLAG(ob, FLAG_IS_WATER)) {
2719  (*water)++;
2720  }
2721  // Deserts will reduce the humidity/precipitation in the spaces they exist in.
2722  // Since the config entries are all negative, we can add the value here.
2723  (*water) += get_config_tile(x, y, m, water_list);
2724 
2725  // Handle forestry
2726  (*trees) += get_config_tile(x, y, m, forest_list);
2727 
2728  (*elev) += ob->elevation;
2729  }
2730  return 0;
2731 }
2732 
2743 static void init_humid_elev(const Settings *settings) {
2744  // Variable uses:
2745  // x, y: weathermap tile being affected
2746  // tx, ty: the in-map coordinates of the corner of the weathermap we are calculating.
2747  // nx, ny: coordinates within the weathermap
2748  // ax, ay: the location on the map we are examining
2749  // j: temporary variable for when a specific ny needs to be initialized from within a loop.
2750  int x, y, tx, ty, nx, ny, ax, ay, j;
2751  // spwtx, spwty: The number of tiles in a single weathermap in the associated (x or y) direction
2754  int64_t elev;
2755  int water, space, trees;
2756  mapstruct *m;
2757 
2758  /* handling of this is kinda nasty. For that reason,
2759  * we do the elevation here too. Not because it makes the
2760  * code cleaner, or makes handling easier, but because I do *not*
2761  * want to maintain two of these nightmares.
2762  */
2763 
2764  for (x = 0; x < WEATHERMAPTILESX; x++) {
2765  for (y = 0; y < WEATHERMAPTILESY; y++) {
2766  water = space = trees = 0;
2767  elev = 0;
2768  nx = ny = 0;
2769 
2770  /* top left */
2771  if (load_humidity_map_part(&m, 8, x, y, &tx, &ty) == -1)
2772  continue;
2773 
2774  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2775  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2776  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2777  //LOG(llevInfo, "%s %d %d (8)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2778  }
2779  }
2780  delete_map(m);
2781 
2782 
2783  // Sanely skip some processing if the entire weathermap fit on one world map.
2784  // Since we are the same size for x/y direction on both weathermaps and on world maps,
2785  // we will either need to load one map, two maps, or four maps. When two maps are loaded, it
2786  // will be one of bottom left or top right, since bottom right only is relevant when we intersect maps
2787  // in both x and y directions.
2788  if (space < spwtx*spwty) {
2789  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2790  if (ny < spwty) {
2791  /* bottom left */
2792  if (load_humidity_map_part(&m, 6, x, y, &tx, &ty) == -1)
2793  continue;
2794 
2795  // If we get here, then we didn't have the whole weathermap reside on one map.
2796  // Since we are continuing from top left, maintaining our position in the y direction
2797  // allows us to correctly check when we reach the end of the weathermap bounds.
2798  j = ny;
2799  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2800  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2801  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2802  //LOG(llevInfo, "%s %d %d (6)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2803  }
2804  }
2805  delete_map(m);
2806  }
2807 
2808  // If we gotall the way to the right on the left calculations, skip both right-side calculations.
2809  if (nx < spwtx) {
2810  /* top right */
2811  if (load_humidity_map_part(&m, 2, x, y, &tx, &ty) == -1)
2812  continue;
2813 
2814  for (ax = MAX(0, tx-(spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2815  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2816  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2817  //LOG(llevInfo, "%s %d %d (2)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2818  }
2819  }
2820  delete_map(m);
2821 
2822  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2823  if (ny < spwty) {
2824  /* bottom right */
2825  if (load_humidity_map_part(&m, 4, x, y, &tx, &ty) == -1)
2826  continue;
2827 
2828  // Moving from top to bottom should behave the same on both right and left.
2829  j = ny;
2830  for (nx = 0, ax = MAX(0, tx - (spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2831  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2832  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2833  //LOG(llevInfo, "%s %d %d (4)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2834  }
2835  }
2836  delete_map(m);
2837  }
2838  }
2839  }
2840 
2841  /* jesus thats confusing as all hell */
2842  // Per meteorology, full ocean usually only gets to 80% humidity at the standard height it is measured.
2843  // And, even in the desert, relative humidity averages like 20%. So, in non-deserts, it should be like 40%.
2844  // This should help prevent a forever-hurricane over the ocean.
2845  weathermap[x][y].humid = 40+water*40/(spwtx*spwty);
2846  weathermap[x][y].avgelev = elev/(spwtx*spwty);
2847  weathermap[x][y].water = water*100/(spwtx*spwty);
2848  // Cap at 100 for tree values. Denser trees stop having any effect.
2849  weathermap[x][y].forestry = MIN(100, trees*100/(spwtx*spwty));
2850  }
2851  }
2852 
2853  /* and this does all the real work */
2854  update_humid();
2855 }
2856 
2862 static void init_temperature() {
2863  int x, y;
2864  timeofday_t tod;
2865 
2866  get_tod(&tod);
2867  for (x = 0; x < WEATHERMAPTILESX; x++) {
2868  for (y = 0; y < WEATHERMAPTILESY; y++) {
2869  temperature_calc(x, y, &tod);
2870  }
2871  }
2872 }
2873 
2879 static void init_rainfall()
2880 {
2881  int x, y;
2882  int days = todtick/HOURS_PER_DAY;
2883 
2884  for (x = 0; x < WEATHERMAPTILESX; x++) {
2885  for (y = 0; y < WEATHERMAPTILESY; y++) {
2886  if (weathermap[x][y].humid < 10) {
2887  weathermap[x][y].rainfall = days/20;
2888  } else if (weathermap[x][y].humid < 20) {
2889  weathermap[x][y].rainfall = days/15;
2890  } else if (weathermap[x][y].humid < 30) {
2891  weathermap[x][y].rainfall = days/10;
2892  } else if (weathermap[x][y].humid < 40) {
2893  weathermap[x][y].rainfall = days/5;
2894  } else if (weathermap[x][y].humid < 50) {
2895  weathermap[x][y].rainfall = days/2;
2896  } else if (weathermap[x][y].humid < 60) {
2897  weathermap[x][y].rainfall = days;
2898  } else if (weathermap[x][y].humid < 80) {
2899  weathermap[x][y].rainfall = days*2;
2900  } else {
2901  weathermap[x][y].rainfall = days*3;
2902  }
2903  }
2904  }
2905 }
2906 
2910 static void init_gulfstreammap() {
2911  int x, y, tx, starty, ymul, diroffset, dirdiff;
2912 
2913  /* build a gulf stream */
2915  /* doth the great bob inhale or exhale? */
2916  gulf_stream_direction = rndm(0, 1);
2917  gulf_stream_start = x;
2918 
2919  // Handle both gulf stream directions
2920  if (gulf_stream_direction) {
2921  // These variables allow us to only define the loop once.
2922  // That should make the code less awful to see
2923  starty = WEATHERMAPTILESY-1;
2924  ymul = -1;
2925  // The diroffset pieces allow us to merge the meat of the loops
2926  // the mapping between the different directions is as follows
2927  // 8 <-> 2
2928  // 7 <-> 3
2929  // 6 <-> 4
2930  // Thus setting diroffset to 10 when dirdiff is 1 gives us one direction
2931  // and setting diroffset to 0 when dirdiff is -1 gives us the other.
2932  diroffset = 0;
2933  dirdiff = -1;
2934  }
2935  else {
2936  starty = 0;
2937  ymul = 1;
2938  diroffset = 10;
2939  dirdiff = 1;
2940  }
2941  // Huzzah! a loop common to both directions!
2942  for (y = starty; y >= 0 && y < WEATHERMAPTILESY; y += ymul) {
2943  switch (rndm(0, 6)) {
2944  case 0:
2945  case 1:
2946  case 2:
2947  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2949  if (x == 0) {
2950  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2951  } else {
2952  gulf_stream_dir[tx][y] = (diroffset-8)*dirdiff;
2953  if (tx == 0) {
2954  x--;
2955  }
2956  }
2957  }
2958  break;
2959 
2960  case 3:
2961  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2963  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2964  }
2965  break;
2966 
2967  case 4:
2968  case 5:
2969  case 6:
2970  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2972  if (x == WEATHERMAPTILESX-1) {
2973  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2974  } else {
2975  gulf_stream_dir[tx][y] = (diroffset-6)*dirdiff;
2976  if (tx == 0) {
2977  x++;
2978  }
2979  }
2980  }
2981  break;
2982  }
2983  }
2984 }
2985 
2992 static void init_wind() {
2993  int x, y;
2994 
2995  for (x = 0; x < WEATHERMAPTILESX; x++) {
2996  for (y = 0; y < WEATHERMAPTILESY; y++) {
2997  weathermap[x][y].winddir = rndm(1, 8);
2998  weathermap[x][y].windspeed = rndm(1, 10);
2999  }
3000  }
3001 }
3002 
3009 static void init_pressure() {
3010  int x, y;
3011  int l, n, k;
3012 
3013  for (x = 0; x < WEATHERMAPTILESX; x++) {
3014  for (y = 0; y < WEATHERMAPTILESY; y++) {
3015  weathermap[x][y].pressure = 1013;
3016  }
3017  }
3018  // Add medium patches of low noise.
3019  for (l = 0; l < PRESSURE_ITERATIONS; l++) {
3020  x = rndm(0, WEATHERMAPTILESX-1);
3021  y = rndm(0, WEATHERMAPTILESY-1);
3023  for (k = 1; k < PRESSURE_AREA; k++) {
3024  switch (rndm(0, 3)) {
3025  case 0: if (x < WEATHERMAPTILESX-1) x++; break;
3026  case 1: if (y < WEATHERMAPTILESY-1) y++; break;
3027  case 2: if (x) x--; break;
3028  case 3: if (y) y--; break;
3029  }
3030  weathermap[x][y].pressure = (weathermap[x][y].pressure+n)/2;
3031  }
3032  }
3033  /* create random spikes in the pressure
3034  * These go way beyond the bounds of allowed pressure, but smooth_pressure
3035  * turns that into a sizable pressure blob.
3036  */
3037  for (l = 0; l < PRESSURE_SPIKES; l++) {
3038  x = rndm(0, WEATHERMAPTILESX-1);
3039  y = rndm(0, WEATHERMAPTILESY-1);
3040  n = rndm(500, 2000);
3041  weathermap[x][y].pressure = n;
3042  }
3043  smooth_pressure();
3044 }
3045 
3047  char buf[MAX_BUF], *cp;
3048  FILE *fp;
3049 
3050  snprintf(buf, sizeof(buf), "%s/wsettings", settings->confdir);
3051 
3052  if ((fp = fopen(buf, "r")) == NULL) {
3053  LOG(llevError, "Warning: No wsettings file found\n");
3054  return;
3055  }
3056  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
3057  if (buf[0] == '#')
3058  continue;
3059  /* eliminate newline */
3060  if ((cp = strrchr(buf, '\n')) != NULL)
3061  *cp = '\0';
3062 
3063  /* Skip over empty lines */
3064  if (buf[0] == 0)
3065  continue;
3066 
3067  /* Skip all the spaces and set them to nulls. If not space,
3068  * set cp to "" to make strcpy's and the like easier down below.
3069  */
3070  if ((cp = strchr(buf, ' ')) != NULL) {
3071  while (*cp == ' ')
3072  *cp++ = 0;
3073 
3074  // These only matter when we have a value, only look when we have a value
3075  // Even in the case of all trailing spaces and no value, cp points to a null-terminator,
3076  // which resolves to 0 from atoi().
3077  if (!strcasecmp(buf, "worldmaptilesizex")) {
3078  int size = atoi(cp);
3079 
3080  if (size < 1)
3081  LOG(llevError, "init_weather_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
3082  else
3083  wset.worldmaptilesizex = size;
3084  } else if (!strcasecmp(buf, "worldmaptilesizey")) {
3085  int size = atoi(cp);
3086 
3087  if (size < 1)
3088  LOG(llevError, "init_weather_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
3089  else
3090  wset.worldmaptilesizey = size;
3091  } else if (!strcasecmp(buf, "dynamiclevel")) {
3092  int lev = atoi(cp);
3093 
3094  if (lev < 0)
3095  LOG(llevError, "init_weather_settings: dynamiclevel must be at least 0, %d is invalid\n", lev);
3096  else
3097  wset.dynamiclevel = lev;
3098  }
3099  }
3100  else {
3101  LOG(llevError, "init_weather_settings: line %s ends after specifier, skipping...\n", buf);
3102  }
3103  }
3104 
3105  // Remember to clean up the file pointer
3106  fclose(fp);
3107 }
3108 
3109 /********************************************************************************************
3110  * Section END -- initializations
3111  ********************************************************************************************/
3112 
3113 /********************************************************************************************
3114  * Section -- weather data writers
3115  * These functions write the current state of the weather to file,
3116  * allowing persistence across server runs.
3117  ********************************************************************************************/
3118 
3132  char filename[MAX_BUF];
3133  FILE *fp;
3134  OutputFile of;
3135  int x, y;
3136 
3137  // First, allocate our file.
3138  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3139  // We use the output_file handling for atomic file operations.
3140  fp = of_open(&of, filename);
3141  if (fp == NULL) {
3142  LOG(llevError, "Failed to open %s for writing.\n", filename);
3143  return 1;
3144  }
3145  LOG(llevDebug, "Writing forestry map to file.\n");
3146  // Actually write the forestry amounts to the file
3147  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3148  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3149  fprintf(fp, "%d ", weathermap[x][y].forestry);
3150  }
3151  fprintf(fp, "\n");
3152  }
3153  of_close(&of);
3154  return 0;
3155 }
3156 
3167  char filename[MAX_BUF];
3168  FILE *fp;
3169  OutputFile of;
3170  int x, y;
3171 
3172  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3173  fp = of_open(&of, filename);
3174  if (fp == NULL) {
3175  LOG(llevError, "Cannot open %s for writing\n", filename);
3176  return 1;
3177  }
3178  LOG(llevDebug, "Writing humidity map to file.\n");
3179  for (x = 0; x < WEATHERMAPTILESX; x++) {
3180  for (y = 0; y < WEATHERMAPTILESY; y++) {
3181  fprintf(fp, "%d ", weathermap[x][y].humid);
3182  }
3183  fprintf(fp, "\n");
3184  }
3185  of_close(&of);
3186  return 0;
3187 }
3188 
3201 static int write_elevmap(const Settings *settings) {
3202  char filename[MAX_BUF];
3203  FILE *fp;
3204  OutputFile of;
3205  int x, y;
3206 
3207  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3208  fp = of_open(&of, filename);
3209  if (fp == NULL) {
3210  LOG(llevError, "Cannot open %s for writing\n", filename);
3211  return 1;
3212  }
3213  LOG(llevDebug, "Writing elevation map to file.\n");
3214  for (x = 0; x < WEATHERMAPTILESX; x++) {
3215  for (y = 0; y < WEATHERMAPTILESY; y++) {
3216  fprintf(fp, "%d ", weathermap[x][y].avgelev);
3217  }
3218  fprintf(fp, "\n");
3219  }
3220  of_close(&of);
3221  return 0;
3222 }
3223 
3224 
3236 static int write_watermap(const Settings *settings) {
3237  char filename[MAX_BUF];
3238  FILE *fp;
3239  OutputFile of;
3240  int x, y;
3241 
3242  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3243  fp = of_open(&of, filename);
3244  if (fp == NULL) {
3245  LOG(llevError, "Cannot open %s for writing\n", filename);
3246  return 1;
3247  }
3248  LOG(llevDebug, "Writing water map to file.\n");
3249  for (x = 0; x < WEATHERMAPTILESX; x++) {
3250  for (y = 0; y < WEATHERMAPTILESY; y++) {
3251  fprintf(fp, "%d ", weathermap[x][y].water);
3252  }
3253  fprintf(fp, "\n");
3254  }
3255  of_close(&of);
3256  return 0;
3257 }
3258 
3271  char filename[MAX_BUF];
3272  FILE *fp;
3273  OutputFile of;
3274  int x, y;
3275 
3276  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
3277  fp = of_open(&of, filename);
3278  if (fp == NULL) {
3279  LOG(llevError, "Cannot open %s for writing\n", filename);
3280  return 1;
3281  }
3282  LOG(llevDebug, "Writing temperature map to file.\n");
3283  for (x = 0; x < WEATHERMAPTILESX; x++) {
3284  for (y = 0; y < WEATHERMAPTILESY; y++) {
3285  fprintf(fp, "%d ", weathermap[x][y].temp);
3286  }
3287  fprintf(fp, "\n");
3288  }
3289  of_close(&of);
3290  return 0;
3291 }
3292 
3304  char filename[MAX_BUF];
3305  FILE *fp;
3306  OutputFile of;
3307  int x, y;
3308 
3309  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
3310  fp = of_open(&of, filename);
3311  if (fp == NULL) {
3312  LOG(llevError, "Cannot open %s for writing\n", filename);
3313  return 1;
3314  }
3315  LOG(llevDebug, "Writing rainfall map to file.\n");
3316  for (x = 0; x < WEATHERMAPTILESX; x++) {
3317  for (y = 0; y < WEATHERMAPTILESY; y++) {
3318  fprintf(fp, "%u ", weathermap[x][y].rainfall);
3319  }
3320  fprintf(fp, "\n");
3321  }
3322  of_close(&of);
3323  return 0;
3324 }
3325 
3337  char filename[MAX_BUF];
3338  FILE *fp;
3339  OutputFile of;
3340  int x, y;
3341 
3342  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
3343  fp = of_open(&of, filename);
3344  if (fp == NULL) {
3345  LOG(llevError, "Cannot open %s for writing\n", filename);
3346  return 1;
3347  }
3348  LOG(llevDebug, "Writing gulf stream map to file.\n");
3349  // First block is speed
3350  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3351  for (y = 0; y < WEATHERMAPTILESY; y++) {
3352  fprintf(fp, "%d ", gulf_stream_speed[x][y]);
3353  }
3354  fprintf(fp, "\n");
3355  }
3356  // second block is direction
3357  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3358  for (y = 0; y < WEATHERMAPTILESY; y++) {
3359  fprintf(fp, "%d ", gulf_stream_dir[x][y]);
3360  }
3361  fprintf(fp, "\n");
3362  }
3363  // And the last line is the starting position, so we don't always have to initialize it.
3364  fprintf(fp, "%d\n", gulf_stream_start);
3365  of_close(&of);
3366  return 0;
3367 }
3368 
3380  char filename[MAX_BUF];
3381  FILE *fp;
3382  OutputFile of;
3383  int x, y;
3384 
3385  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
3386  fp = of_open(&of, filename);
3387  if (fp == NULL) {
3388  LOG(llevError, "Cannot open %s for writing\n", filename);
3389  return 1;
3390  }
3391  LOG(llevDebug, "Writing wind speed map to file.\n");
3392  for (x = 0; x < WEATHERMAPTILESX; x++) {
3393  for (y = 0; y < WEATHERMAPTILESY; y++) {
3394  fprintf(fp, "%hd ", weathermap[x][y].windspeed);
3395  }
3396  fprintf(fp, "\n");
3397  }
3398  of_close(&of);
3399  return 0;
3400 }
3401 
3413  char filename[MAX_BUF];
3414  FILE *fp;
3415  OutputFile of;
3416  int x, y;
3417 
3418  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
3419  fp = of_open(&of, filename);
3420  if (fp == NULL) {
3421  LOG(llevError, "Cannot open %s for writing\n", filename);
3422  return 1;
3423  }
3424  LOG(llevDebug, "Writing wind direction map to file.\n");
3425  for (x = 0; x < WEATHERMAPTILESX; x++) {
3426  for (y = 0; y < WEATHERMAPTILESY; y++) {
3427  fprintf(fp, "%d ", weathermap[x][y].winddir);
3428  }
3429  fprintf(fp, "\n");
3430  }
3431  of_close(&of);
3432  return 0;
3433 }
3434 
3446  char filename[MAX_BUF];
3447  FILE *fp;
3448  OutputFile of;
3449  int x, y;
3450 
3451  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
3452  fp = of_open(&of, filename);
3453  if (fp == NULL) {
3454  LOG(llevError, "Cannot open %s for writing\n", filename);
3455  return 1;
3456  }
3457  LOG(llevDebug, "Writing pressure map to file.\n");
3458  for (x = 0; x < WEATHERMAPTILESX; x++) {
3459  for (y = 0; y < WEATHERMAPTILESY; y++) {
3460  fprintf(fp, "%d ", weathermap[x][y].pressure);
3461  }
3462  fprintf(fp, "\n");
3463  }
3464  of_close(&of);
3465  return 0;
3466 }
3467 
3474 int write_skymap(void) {
3475  char filename[MAX_BUF];
3476  FILE *fp;
3477  OutputFile of;
3478  int x, y;
3479 
3480  snprintf(filename, sizeof(filename), "%s/skymap", settings.localdir);
3481  fp = of_open(&of, filename);
3482  if (fp == NULL) {
3483  LOG(llevError, "Cannot open %s for writing\n", filename);
3484  return 1;
3485  }
3486  LOG(llevDebug, "Writing sky conditions map to file.\n");
3487  for (x = 0; x < WEATHERMAPTILESX; x++) {
3488  for (y = 0; y < WEATHERMAPTILESY; y++) {
3489  fprintf(fp, "%d ", weathermap[x][y].sky);
3490  }
3491  fprintf(fp, "\n");
3492  }
3493  of_close(&of);
3494  return 0;
3495 }
3496 
3497 /* This stuff is for creating the images,
3498  * and is only used by write_weather_images()
3499  */
3500 
3501 /* Colour offsets into pixel array. */
3502 #define RED 0
3503 #define GREEN 1
3504 #define BLUE 2
3505 
3513 static const uint32_t directions[] = {
3514  0x0000FFFF, /* south */
3515  0x000000FF, /* south west */
3516  0x00FF00FF, /* west */
3517  0x00FFFFFF, /* north west */
3518  0x00000000, /* north */
3519  0x00FF0000, /* north east */
3520  0x00FFFF00, /* east */
3521  0x0000FF00 /* south east */
3522 };
3523 
3527 static const uint32_t skies[] = {
3528  0x000000FF, /* SKY_CLEAR 0 */
3529  0x000000BD, /* SKY_LIGHTCLOUD 1 */
3530  0x0000007E, /* SKY_OVERCAST 2 */
3531  0x0000FF00, /* SKY_LIGHT_RAIN 3 */
3532  0x0000BD00, /* SKY_RAIN 4 */
3533  0x00007E00, /* SKY_HEAVY_RAIN 5 */
3534  0x00FFFF00, /* SKY_HURRICANE 6 */
3535 /* wierd weather 7-12 */
3536  0x00FF0000, /* SKY_FOG 7 */
3537  0x00FF00FF, /* SKY_HAIL 8 */
3538  0x00000000,
3539  0x00000000,
3540  0x00000000,
3541  0x00000000,
3542 /* snow */
3543  0x003F3F3F, /* SKY_LIGHT_SNOW 13 */
3544  0x007E7E7E, /* SKY_SNOW 14 */
3545  0x00BDBDBD, /* SKY_HEAVY_SNOW 15 */
3546  0x00FFFFFF /* SKY_BLIZZARD 16 */
3547 };
3548 
3562  char filename[MAX_BUF];
3563  FILE *fp;
3564  OutputFile of;
3565  int x, y;
3566  int32_t min[8], max[8], avgrain, avgwind, realmaxwind;
3567  double scale[8], realscalewind;
3568  uint8_t pixels[3*3*WEATHERMAPTILESX];
3569  int64_t total_rainfall = 0;
3570  int64_t total_wind = 0;
3571  timeofday_t tod;
3572 
3573  // Get the time of day.
3574  // This is important for weather output later.
3575  get_tod(&tod);
3576 
3577  // Determine the output file's limits.
3578  min[0] = -100; max[0] = 100;
3579  min[1] = 0; max[1] = 0;
3580  min[2] = 0; max[2] = 0;
3581  min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
3582  // Minimum wind speed is always 0. Don't track it. We just define it so scale[4] is valid
3583  min[4] = 0; max[4] = 0;
3584  // The 6th tile is raw wind direction, and thus does not need limits
3585  min[6] = 0; max[6] = 100;
3586  min[7] = -45; max[7] = 45;
3587  // The 9th tile is raw sky data and does not need limits
3588  for (x = 0; x < WEATHERMAPTILESX; x++) {
3589  for (y = 0; y < WEATHERMAPTILESY; y++) {
3590 /* min[0] = MIN(min[0], weathermap[x][y].water); */
3591  min[1] = MIN(min[1], weathermap[x][y].avgelev);
3592  min[2] = MIN(min[2], weathermap[x][y].rainfall);
3593 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
3594 /* min[4] = MIN(min[4], weathermap[x][y].windspeed); */
3595 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
3596 /* min[7] = MIN(min[7], real_temp[x][y]); */
3597 
3598 /* max[0] = MAX(max[0], weathermap[x][y].water); */
3599  max[1] = MAX(max[1], weathermap[x][y].avgelev);
3600  max[2] = MAX(max[2], weathermap[x][y].rainfall);
3601 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
3602  max[4] = MAX(max[4], weathermap[x][y].windspeed);
3603 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
3604 /* max[7] = MAX(max[7], real_temp[x][y]); */
3605  total_rainfall += weathermap[x][y].rainfall;
3606  total_wind += weathermap[x][y].windspeed;
3607  }
3608  }
3609  // Twiddle the data on total rainfall, since they have a different color for above/below average
3610  // This allows us to have the full scale of color range on each above average and below average.
3611  avgrain = total_rainfall/(WEATHERMAPTILESX*WEATHERMAPTILESY);
3612  avgwind = (total_wind /(WEATHERMAPTILESX*WEATHERMAPTILESY));
3613  max[2] = avgrain-1;
3614  realscalewind = 255.0l/(max[4]);
3615  realmaxwind = max[4];
3616  max[4] = avgwind-1;
3617  for (x = 0; x < 8; x++) {
3618  scale[x] = 255.0l/(max[x]-min[x]);
3619  }
3620 
3621  LOG(llevDebug, "Writing weather conditions map.\n");
3622 
3623  snprintf(filename, sizeof(filename), "%s/weather.ppm", settings.localdir);
3624  fp = of_open(&of, filename);
3625  if (fp == NULL) {
3626  LOG(llevError, "Cannot open %s for writing\n", filename);
3627  return 1;
3628  }
3629  fprintf(fp, "P6\n%d %d\n", 3*WEATHERMAPTILESX, 3*WEATHERMAPTILESY);
3630  fprintf(fp, "255\n");
3631  // First row of maps
3632  for (y = 0; y < WEATHERMAPTILESY; y++) {
3633  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3634  for (x = 0; x < WEATHERMAPTILESX; x++) {
3635  // water/tree map -- first map of row
3636  // blue = high water amount, black = low water amount, red = desert-like, green = trees
3637  if (weathermap[x][y].water < 0)
3638  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(weathermap[x][y].water-min[0])*scale[0]*2);
3639  else
3640  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].water)*scale[0]*2);
3641  // Either way, we use green to highlight the trees, too
3642  // Make this real simple since it is established that forestry values range from 0 to 100.
3643  // As a result, our values go from 0-250.
3644  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].forestry)*5/2);
3645  // elevation map -- second map of row.
3646  // green -- mostly land --> brighter green is higher elevation
3647  // blue -- mostly water --> deeper blue is lower elevation
3648  if (weathermap[x][y].avgelev >= 0) {
3649  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3650  } else {
3651  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3652  }
3653  // rainfall map -- third map of row
3654  // magenta = high rainfall, blue = average rainfall, black = low rainfall
3655  if (weathermap[x][y].rainfall >= avgrain) { /* rainfall is rather spikey, this gives us more detail. */
3656  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = 255;
3657  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((weathermap[x][y].rainfall-avgrain)*(255.0/(max[2]-avgrain)));
3658  } else {
3659  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].rainfall-min[2])*(avgrain-min[2]));
3660  }
3661  }
3662  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3663  }
3664  // Second row of maps.
3665  for (y = 0; y < WEATHERMAPTILESY; y++) {
3666  for (x = 0; x < WEATHERMAPTILESX; x++) {
3667  uint32_t dir = directions[weathermap[x][y].winddir-1];
3668  uint32_t speed = weathermap[x][y].windspeed;
3669  int16_t pressure = (weathermap[x][y].pressure-PRESSURE_MIN)*scale[3];
3670  // Make sure we don't get artifacting from Post-smoothing pressure adjustments.
3671  // or round-off error in the above calculation.
3672  pressure = MIN(255, MAX(0, pressure));
3673  // Pressure -- first map of row
3674  // light = high pressure, dark = low pressure
3675  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = pressure;
3676  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = pressure;
3677  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = pressure;
3678  // Wind speed -- second map of row
3679  // very high wind = red, else grey = average wind, dark = low wind
3680  if (speed < avgwind) {
3681  speed = (speed)*scale[4]/2;
3682  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = speed;
3683  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = speed;
3684  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = speed;
3685  } else {
3686  speed = (speed-avgwind)*realscalewind/2;
3687  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(MIN(255,(avgwind)*scale[4]/2+speed));
3688  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (avgwind)*scale[4]/2 - speed;
3689  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (avgwind)*scale[4]/2 - speed;
3690  }
3691  // Wind direction -- third map of row
3692  // red = northeast, yellow = east, green = southeast, cyan = south,
3693  // blue = southwest, magenta = west, white = northwest, black = north
3694  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3695  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3696  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3697  }
3698  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3699  }
3700  // Third row of maps
3701  for (y = 0; y < WEATHERMAPTILESY; y++) {
3702  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3703  for (x = 0; x < WEATHERMAPTILESX; x++) {
3704  uint32_t dir = skies[weathermap[x][y].sky];
3705  // Humidity -- first map of row.
3706  // blue = high humidity, black = low humidity, red = droughty (even lower humidity)
3707  // Didn't adjust min for this one, so it should look a little different than the others.
3708  if (weathermap[x][y].humid < 0)
3709  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(-weathermap[x][y].humid)*scale[6]);
3710  else
3711  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].humid-min[6])*scale[6]);
3712  // Real temperature -- second map of row
3713  // temp < 0 --> scale from white to blue
3714  // temp > 0 --> scale from blue to green to yellow to red
3715  // green is 20 C
3716  // yellow is 30 C
3717  int temp = real_temperature(x, y, &tod);
3718  // white -> cyan
3719  if (temp < 0) {
3720  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(-temp * scale[7]*2);
3721  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)0xFF;
3722  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)0xFF;
3723  }
3724  // cyan->blue is the boundary for above/below freezing
3725  // blue -> green
3726  else if (temp < 20) {
3727  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 0;
3728  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)(temp * scale[7]*9/2);
3729  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((20-temp) * scale[7]*9/2);
3730  }
3731  // green -> yellow
3732  else if (temp < 30) {
3733  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)((temp-20) * scale[7]*9);
3734  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = 255;
3735  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3736  }
3737  // yellow -> red
3738  else {
3739  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 255;
3740  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((45-temp) * scale[7]*6);
3741  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3742  }
3743  // current weather -- third map of row
3744  // blue = clear, medium blue = light clouds, dark blue = overcast, green = light rain
3745  // medium green = rain, dark green = heavy rain, yellow = hurricane, red = fog, magenta = hail,
3746  // dark gray = light snow, medium gray = snow, gray = heavy snow, white = blizzard
3747  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3748  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3749  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3750  }
3751  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3752  }
3753  of_close(&of);
3754  return 0;
3755 }
3756 
3757 /********************************************************************************************
3758  * Section END -- weather data writers
3759  ********************************************************************************************/
3760 
3761 /********************************************************************************************
3762  * Section -- weather data readers
3763  * These read weather data that gets periodically stored to file to restore
3764  * the prior state on a server restart. They also handle first-load initialization.
3765  ********************************************************************************************/
3766 
3784 static int read_forestrymap(const Settings *settings) {
3785  char filename[MAX_BUF], *data, *tmp;
3786  BufferReader *bfr;
3787  int trees, x, y, res;
3788 
3789  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3790  LOG(llevDebug, "Reading forestry data from %s...\n", filename);
3791  // Set up the bufferreader and read in the file.
3792  // We do it through the bufferreader so that we only dip into I/O once,
3793  // and the rest is just parsing it in memory.
3794  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3795  // We already printed our error if we failed, so just bail in that case.
3796  if (bfr == NULL)
3797  return -1;
3798  // Parse the file. Since this is auto-generated by the weather system,
3799  // just bail if the file is malformed.
3800  data = bufferreader_data(bfr);
3801  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3802  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3803  res = sscanf(data, "%d ", &trees);
3804  if (res != 1) {
3805  LOG(llevError, "Forestry data is corrupted and should be regenerated.\n"
3806  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3807  bufferreader_destroy(bfr);
3808  return -1;
3809  }
3810  // Limit the range from 0 to 100
3811  weathermap[x][y].forestry = MIN(100, MAX(0, trees));
3812  // Now we move where we're looking, since we want to read more than just the first entry.
3813  // Use strpbrk so that we can handle newlines more cleanly.
3814  tmp = strpbrk(data, " \n");
3815  if (tmp != NULL)
3816  data = tmp + 1;
3817  else {
3818  LOG(llevError, "Unexpected end of forestry file. Forestry file may need to be regenerated.\n"
3819  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3820  bufferreader_destroy(bfr);
3821  return -1;
3822  }
3823  }
3824  // Due to the way the file is written, the end of the line should have a space and a newline.
3825  // Handle the newline if it is the front of the string now.
3826  if (*data == '\n')
3827  ++data;
3828  }
3829  bufferreader_destroy(bfr);
3830  return 0;
3831 }
3832 
3847 static int read_humidmap(const Settings *settings) {
3848  char filename[MAX_BUF], *data, *tmp;
3849  BufferReader *bfr;
3850  int x, y, hmd, res;
3851 
3852  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3853  LOG(llevDebug, "Reading humidity data from %s...\n", filename);
3854  // Open and read in the file all at once
3855  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3856  // If we fail, we do initializations instead.
3857  if (bfr == NULL) {
3858  LOG(llevInfo, "Initializing humidity and elevation maps...\n");
3864  LOG(llevDebug, "Done\n");
3865  return 1;
3866  }
3867  data = bufferreader_data(bfr);
3868  for (x = 0; x < WEATHERMAPTILESX; x++) {
3869  for (y = 0; y < WEATHERMAPTILESY; y++) {
3870  res = sscanf(data, "%d ", &hmd);
3871  if (res != 1) {
3872  LOG(llevError, "Humidity data is corrupted and cannot be loaded.\n"
3873  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3874  bufferreader_destroy(bfr);
3875  return -1;
3876  }
3877  // Limit range from 0 to 100. Clip entries that surpass these bounds.
3878  weathermap[x][y].humid = MAX(0, MIN(100, hmd));
3879  // Move to the next spot in the buffer.
3880  tmp = strpbrk(data, " \n");
3881  if (tmp == NULL) {
3882  LOG(llevError, "Unexpected end of humidity file.\n"
3883  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3884  bufferreader_destroy(bfr);
3885  return -1;
3886  }
3887  // If found, move data to the next character after the space/newline found.
3888  data = tmp + 1;
3889  }
3890  // If the newline from the previous line hasn't been parsed yet, skip it.
3891  if (*data == '\n')
3892  ++data;
3893  }
3894  bufferreader_destroy(bfr);
3895  LOG(llevDebug, "Done.\n");
3896  return 0;
3897 }
3898 
3911 static int read_elevmap(const Settings *settings) {
3912  char filename[MAX_BUF], *data, *tmp;
3913  BufferReader *bfr;
3914  int x, y, elev, res;
3915 
3916  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3917  LOG(llevDebug, "Reading elevation data from %s...\n", filename);
3918  // Read file into a buffer.
3919  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3920  // If failed, we bail.
3921  if (bfr == NULL) {
3922  /* initializing these is expensive, and should have been done
3923  by the humidity. It's not worth the wait to do it twice. */
3924  return -1;
3925  }
3926  data = bufferreader_data(bfr);
3927  for (x = 0; x < WEATHERMAPTILESX; x++) {
3928  for (y = 0; y < WEATHERMAPTILESY; y++) {
3929  res = sscanf(data, "%d ", &elev);
3930  if (res != 1) {
3931  LOG(llevError, "Elevation data is corrupted and cannot be loaded.\n"
3932  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3933  bufferreader_destroy(bfr);
3934  return -1;
3935  }
3936  // Individual elevation values should be in the range [-32000, 32000]
3937  // Averages can be capped to these as well.
3938  weathermap[x][y].avgelev = MAX(-32000, MIN(32000, elev));
3939  // Now we move to the next entry in the buffer.
3940  tmp = strpbrk(data, " \n");
3941  if (tmp == NULL) {
3942  LOG(llevError, "Unexpected end of file in elevation data.\n"
3943  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3944  bufferreader_destroy(bfr);
3945  return -1;
3946  }
3947  // If found, we move to the character after the space/newline.
3948  data = tmp + 1;
3949  }
3950  // If there's still a newline after passing a space, then skip past it.
3951  if (*data == '\n')
3952  ++data;
3953  }
3954  bufferreader_destroy(bfr);
3955  LOG(llevDebug, "Done.\n");
3956  return 0;
3957 }
3958 
3970 static int read_watermap(const Settings *settings) {
3971  char filename[MAX_BUF], *data, *tmp;
3972  BufferReader *bfr;
3973  int x, y, wtr, res;
3974 
3975  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3976  LOG(llevDebug, "Reading water data from %s...\n", filename);
3977  // Read the file into a buffer.
3978  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3979  // If failed, we bail
3980  if (bfr == NULL) {
3981  /* initializing these is expensive, and should have been done
3982  by the humidity. It's not worth the wait to do it twice. */
3983  return -1;
3984  }
3985  data = bufferreader_data(bfr);
3986  for (x = 0; x < WEATHERMAPTILESX; x++) {
3987  for (y = 0; y < WEATHERMAPTILESY; y++) {
3988  res = sscanf(data, "%d ", &wtr);
3989  if (res != 1) {
3990  LOG(llevError, "Water map is corrupted and cannot be loaded.\n"
3991  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3992  bufferreader_destroy(bfr);
3993  return -1;
3994  }
3995  // Range is -100 to 100 due to deserts and such being negative.
3996  weathermap[x][y].water = MAX(-100, MIN(100, wtr));
3997  // Adjust the data pointer.
3998  tmp = strpbrk(data, " \n");
3999  if (tmp == NULL) {
4000  LOG(llevError, "Unexpected end of file in water map.\n"
4001  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
4002  bufferreader_destroy(bfr);
4003  return -1;
4004  }
4005  // Okay, so we want the first character after the space/newline.
4006  data = tmp + 1;
4007  }
4008  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4009  if (*data == '\n')
4010  ++data;
4011  }
4012  bufferreader_destroy(bfr);
4013  LOG(llevDebug, "Done.\n");
4014  return 0;
4015 }
4016 
4029  char filename[MAX_BUF], *data, *tmp;
4030  BufferReader *bfr;
4031  int x, y, res;
4032  int16_t temperature;
4033 
4034  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
4035  LOG(llevDebug, "Reading temperature data from %s...\n", filename);
4036  // Read the file into a buffer.
4037  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4038  // If it fails, we initialize the temperature map and write it to a file.
4039  if (bfr == NULL) {
4040  LOG(llevInfo, "Initializing temperature map.\n");
4041  init_temperature();
4042  // If writing was successful, then consider this a success.
4043  if (write_temperaturemap(settings) == 0)
4044  return 1;
4045  return -1;
4046  }
4047  data = bufferreader_data(bfr);
4048  for (x = 0; x < WEATHERMAPTILESX; x++) {
4049  for (y = 0; y < WEATHERMAPTILESY; y++) {
4050  res = sscanf(data, "%hd ", &temperature);
4051  if (res != 1) {
4052  LOG(llevError, "Temperature file is malformed, unable to load temps from file.\n");
4053  bufferreader_destroy(bfr);
4054  return -1;
4055  }
4056  // Clip to reasonable bounds.
4057  weathermap[x][y].temp = MIN(60, MAX(-30, temperature));
4058  // Adjust the data pointer.
4059  tmp = strpbrk(data, " \n");
4060  if (tmp == NULL) {
4061  LOG(llevError, "Unexpected end of file in temperature map.\n");
4062  bufferreader_destroy(bfr);
4063  return -1;
4064  }
4065  // Okay, so we want the first character after the space/newline.
4066  data = tmp + 1;
4067  }
4068  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4069  if (*data == '\n')
4070  ++data;
4071  }
4072  bufferreader_destroy(bfr);
4073  LOG(llevDebug, "Done.\n");
4074  return 0;
4075 }
4076 
4090 static int read_rainfallmap(const Settings *settings) {
4091  char filename[MAX_BUF], *data, *tmp;
4092  BufferReader *bfr;
4093  int x, y, res;
4094 
4095  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
4096  LOG(llevDebug, "Reading rainfall data from %s...\n", filename);
4097  // Read file into a buffer.
4098  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4099  // If it fails, we initialize and write to file.
4100  if (bfr == NULL) {
4101  LOG(llevInfo, "Initializing rainfall map...\n");
4102  init_rainfall();
4103  // If we write to file successfully, consider initialization a success.
4104  if (write_rainfallmap(settings) != 0)
4105  return -1;
4106  return 1;
4107  }
4108  data = bufferreader_data(bfr);
4109  for (x = 0; x < WEATHERMAPTILESX; x++) {
4110  for (y = 0; y < WEATHERMAPTILESY; y++) {
4111  res = sscanf(data, "%u ", &weathermap[x][y].rainfall);
4112  if (res != 1) {
4113  LOG(llevError, "Rainfall file is corrupted, cannot load rainfall from file.\n");
4114  bufferreader_destroy(bfr);
4115  return -1;
4116  }
4117  // Now we update the pointer to data to move to the next item.
4118  tmp = strpbrk(data, " \n");
4119  if (tmp == NULL) {
4120  LOG(llevError, "Unexpected end of file in rainfall map.\n");
4121  bufferreader_destroy(bfr);
4122  return -1;
4123  }
4124  // Okay, so we want the first character after the space/newline.
4125  data = tmp + 1;
4126  }
4127  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4128  if (*data == '\n')
4129  ++data;
4130  }
4131  bufferreader_destroy(bfr);
4132  LOG(llevDebug, "Done.\n");
4133  return 0;
4134 }
4135 
4148  char filename[MAX_BUF], *data, *tmp;
4149  BufferReader *bfr;
4150  int x, y, in, res;
4151 
4152  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
4153  LOG(llevDebug, "Reading gulf stream data from %s...\n", filename);
4154  // Read the file into a buffer
4155  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4156  // If it fails, we initialize and write to file.
4157  if (bfr == NULL) {
4158  LOG(llevInfo, "Initializing gulf stream maps...\n");
4161  LOG(llevDebug, "Done\n");
4162  if (res == 0)
4163  return 1;
4164  return -1;
4165  }
4166  data = bufferreader_data(bfr);
4167  // First we read in the speeds
4168  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4169  for (y = 0; y < WEATHERMAPTILESY; y++) {
4170  res = sscanf(data, "%d ", &in);
4171  if (res != 1) {
4172  LOG(llevError, "Gulf stream speed definitions are malformed. Cannot load gulf stream from file.\n");
4173  bufferreader_destroy(bfr);
4174  return -1;
4175  }
4176  gulf_stream_speed[x][y] = MIN(120, MAX(0, in));
4177  // Now we update the pointer to data to move to the next item.
4178  tmp = strpbrk(data, " \n");
4179  if (tmp == NULL) {
4180  LOG(llevError, "Unexpected end of file in gulfstream speed map.\n");
4181  bufferreader_destroy(bfr);
4182  return -1;
4183  }
4184  // Okay, so we want the first character after the space/newline.
4185  data = tmp + 1;
4186  }
4187  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4188  if (*data == '\n')
4189  ++data;
4190  }
4191  // Then we read in the directions.
4192  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4193  for (y = 0; y < WEATHERMAPTILESY; y++) {
4194  res = sscanf(data, "%d ", &in);
4195  if (res != 1) {
4196  LOG(llevError, "Gulf stream direction definitions are malformed. Cannot load gulf stream from file.\n");
4197  bufferreader_destroy(bfr);
4198  return -1;
4199  }
4200  gulf_stream_dir[x][y] = MAX(1, MIN(8, in));
4201  // Now we update the pointer to data to move to the next item.
4202  tmp = strpbrk(data, " \n");
4203  if (tmp == NULL) {
4204  LOG(llevError, "Unexpected end of file in gulfstream direction map.\n");
4205  bufferreader_destroy(bfr);
4206  return -1;
4207  }
4208  // Okay, so we want the first character after the space/newline.
4209  data = tmp + 1;
4210  }
4211  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4212  if (*data == '\n')
4213  ++data;
4214  }
4215  // Then we read in the start point, if it exists
4216  // For backward compatability, we randomly initialize this if we can't read it.
4217  res = sscanf(data, "%d\n", &in);
4218  if (res != 1) {
4219  LOG(llevInfo, "Gulf stream file lacks start position, and is assumed to be old; initializing it randomly.\n");
4221  }
4223  // If we add any more parsing here, we'll need to affect the data pointer.
4224  // But, since we don't, leave it stale until it falls out of scope.
4225  bufferreader_destroy(bfr);
4226  LOG(llevDebug, "Done.\n");
4227  return 0;
4228 }
4229 
4244  char filename[MAX_BUF], *data, *tmp;
4245  BufferReader *bfr;
4246  int x, y, res;
4247  int8_t spd;
4248 
4249  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
4250  LOG(llevDebug, "Reading wind speed data from %s...\n", filename);
4251  // Read the file into a buffer
4252  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4253  // If it fails, we bail
4254  if (bfr == NULL) {
4255  // Wind direction is done before this, and should have initialized this already.
4256  return -1;
4257  }
4258  data = bufferreader_data(bfr);
4259  for (x = 0; x < WEATHERMAPTILESX; x++) {
4260  for (y = 0; y < WEATHERMAPTILESY; y++) {
4261  res = sscanf(data, "%hhd ", &spd);
4262  if (res != 1) {
4263  LOG(llevError, "Wind speed file is malformed. Cannot load wind speed file.\n");
4264  bufferreader_destroy(bfr);
4265  return -1;
4266  }
4267  // Clip to reasonable bounds
4268  weathermap[x][y].windspeed = MIN(120, MAX(0, spd));
4269  // Now we update the pointer to data to move to the next item.
4270  tmp = strpbrk(data, " \n");
4271  if (tmp == NULL) {
4272  LOG(llevError, "Unexpected end of file in wind speed map.\n");
4273  bufferreader_destroy(bfr);
4274  return -1;
4275  }
4276  // Okay, so we want the first character after the space/newline.
4277  data = tmp + 1;
4278  }
4279  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4280  if (*data == '\n')
4281  ++data;
4282  }
4283  bufferreader_destroy(bfr);
4284  LOG(llevDebug, "Done.\n");
4285  return 0;
4286 }
4287 
4298 static int read_winddirmap(const Settings *settings) {
4299  char filename[MAX_BUF], *data, *tmp;
4300  BufferReader *bfr;
4301  int x, y, d, res;
4302 
4303  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
4304  LOG(llevDebug, "Reading wind direction data from %s...\n", filename);
4305  // Read the file into a buffer
4306  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4307  // If it fails, initialize the wind
4308  if (bfr == NULL) {
4309  LOG(llevInfo, "Initializing wind direction and speed maps...\n");
4310  init_wind();
4311  // If both of these succeed, the end result is 0.
4312  res = write_winddirmap(settings);
4313  res += write_windspeedmap(settings);
4314  LOG(llevDebug, "Done\n");
4315  if (res == 0)
4316  return 1;
4317  return -1;
4318  }
4319  data = bufferreader_data(bfr);
4320  for (x = 0; x < WEATHERMAPTILESX; x++) {
4321  for (y = 0; y < WEATHERMAPTILESY; y++) {
4322  res = sscanf(data, "%d ", &d);
4323  if (res != 1) {
4324  LOG(llevError, "Wind direction map is malformed. Could not load wind direction.\n");
4325  bufferreader_destroy(bfr);
4326  return -1;
4327  }
4328  // If the direction is not valid, just give it one randomly.
4329  if (d < 1 || d > 8) {
4330  d = rndm(1, 8);
4331  }
4332  weathermap[x][y].winddir = d;
4333  // Now we update the pointer to data to move to the next item.
4334  tmp = strpbrk(data, " \n");
4335  if (tmp == NULL) {
4336  LOG(llevError, "Unexpected end of file in wind direction map.\n");
4337  bufferreader_destroy(bfr);
4338  return -1;
4339  }
4340  // Okay, so we want the first character after the space/newline.
4341  data = tmp + 1;
4342  }
4343  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4344  if (*data == '\n')
4345  ++data;
4346  }
4347  bufferreader_destroy(bfr);
4348  LOG(llevDebug, "Done.\n");
4349  return 0;
4350 }
4351 
4362 static int read_pressuremap(const Settings *settings) {
4363  char filename[MAX_BUF], *data, *tmp;
4364  BufferReader *bfr;
4365  int x, y, res;
4366  int16_t press;
4367 
4368  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
4369  LOG(llevDebug, "Reading pressure data from %s...\n", filename);
4370  // Read the file into a buffer.
4371  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading\n", llevError);
4372  // If it fails, we initialize the pressure.
4373  if (bfr == NULL) {
4374  LOG(llevInfo, "Initializing pressure maps...\n");
4375  init_pressure();
4376  res = write_pressuremap(settings);
4377  LOG(llevDebug, "Done\n");
4378  if (res == 0)
4379  return 1;
4380  return -1;
4381  }
4382  data = bufferreader_data(bfr);
4383  for (x = 0; x < WEATHERMAPTILESX; x++) {
4384  for (y = 0; y < WEATHERMAPTILESY; y++) {
4385  res = sscanf(data, "%hd ", &press);
4386  if (res != 1) {
4387  LOG(llevError, "Pressure map is malformed. Could not load pressure.\n");
4388  bufferreader_destroy(bfr);
4389  return -1;
4390  }
4391  // Apply clipping to the pressure.
4392  weathermap[x][y].pressure = MIN(PRESSURE_MAX, MAX(PRESSURE_MIN, press));
4393  // Now we update the pointer to data to move to the next item.
4394  tmp = strpbrk(data, " \n");
4395  if (tmp == NULL) {
4396  LOG(llevError, "Unexpected end of file in pressure map.\n");
4397  bufferreader_destroy(bfr);
4398  return -1;
4399  }
4400  // Okay, so we want the first character after the space/newline.
4401  data = tmp + 1;
4402  }
4403  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4404  if (*data == '\n')
4405  ++data;
4406  }
4407  bufferreader_destroy(bfr);
4408  LOG(llevDebug, "Done.\n");
4409  return 0;
4410 }
4411 
4425  char filename[MAX_BUF], *data;
4426  BufferReader *bfr;
4427  int sx, sy, res;
4428 
4429  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings->localdir);
4430  LOG(llevDebug, "Reading current weather position from %s...\n", filename);
4431  // Read the file into a buffer.
4432  bfr = bufferreader_init_from_file(NULL, filename, "Can't open %s: %s.\n", llevError);
4433  // If we fail, set wmperformstartx/y to their default values.
4434  if (bfr == NULL) {
4435  wmperformstartx = -1;
4436  wmperformstarty = 0;
4437  return 1;
4438  }
4439  data = bufferreader_data(bfr);
4440  // Read from the buffer
4441  res = sscanf(data, "%d %d", &sx, &sy);
4442  // Whether success or failure, we're done with the buffer.
4443  bufferreader_destroy(bfr);
4444  // Now we check our result.
4445  if (res != 2) {
4446  LOG(llevError, "Weather position file was malformed. Using default position.\n");
4447  wmperformstartx = -1;
4448  wmperformstarty = 0;
4449  return 1;
4450  }
4451  LOG(llevDebug, "curposx=%d curposy=%d\n", sx, sy);
4452 
4453  if (sx > settings->worldmaptilesx) {
4454  sx = -1;
4455  }
4456  if (sy > settings->worldmaptilesy) {
4457  sy = 0;
4458  }
4459  // Now we apply these to the static variables.
4460  wmperformstartx = sx;
4461  wmperformstarty = sy;
4462  return 0;
4463 }
4464 
4465 /********************************************************************************************
4466  * Section END -- weather data readers
4467  ********************************************************************************************/
4468 
4469 /********************************************************************************************
4470  * Section -- weather event listeners
4471  ********************************************************************************************/
4472 
4482 static int weather_listener(int *type, ...) {
4483  va_list args;
4484  int code;
4485  mapstruct *m;
4486 
4487  va_start(args, type);
4488  code = va_arg(args, int);
4489 
4490  switch (code) {
4491  case EVENT_MAPENTER:
4492  // At this point, we don't need the entering object for this.
4493  // but it is passed as an arg, so just skip it.
4494  va_arg(args, object *);
4495  /* FALLTHROUGH */
4496  case EVENT_MAPLOAD:
4497  case EVENT_MAPREADY:
4498  m = va_arg(args, mapstruct *);
4499  if (m->outdoor)
4501  break;
4502  }
4503 
4504  va_end(args);
4505 
4506  return 0;
4507 }
4508 
4521 static int weather_clock_listener(int *type, ...) {
4522  va_list args;
4523  int code;
4524 
4525  va_start(args, type);
4526  code = va_arg(args, int);
4527  va_end(args);
4528 
4529  switch (code) {
4530  case EVENT_CLOCK:
4531  if (!(pticks%PTICKS_PER_CLOCK)) {
4532  /* call the weather calculators, here, in order */
4533  tick_weather();
4534  // At every hour, measure the rainfall.
4535  // pticks%PTICKS_PER_CLOCK is already triggering every hour.
4536  process_rain();
4537  /* perform_weather must follow calculators */
4538  perform_weather();
4539  }
4540  // Handle weather printouts after enough server time.
4541  // By using primes here, rather than inside tick_the_clock(), we can reduce the load on a given tick.
4542  // (pticks%1500 is real busy otherwise)
4543  // This produces the side-effect that the server actually has to be on for that length
4544  // of time without interruption for the save to happen, but most servers are run long-term
4545  // anyway, so this should not be an issue
4546  if (!(pticks%1511))
4548  if (!(pticks%31511))
4550  if (!(pticks%33013))
4552  if (!(pticks%34501))
4554  if (!(pticks%36007))
4556  if (!(pticks%39019))
4558  if (!(pticks%40507))
4560  if (settings.fastclock > 0 && !(pticks%42013))
4561  write_skymap();
4562  if (!(pticks%43517))
4564  break;
4565  }
4566 
4567  return 0;
4568 }
4569 
4579 static int weather_object_listener(int *type, ...) {
4580  va_list args;
4581  int code;
4582  object *op;
4583 
4584  va_start(args, type);
4585  code = va_arg(args, int);
4586  // At this point, we don't need the entering object for this.
4587  // but it is passed as an arg, so just skip it.
4588  op = va_arg(args, object *);
4589 
4590  va_end(args);
4591 
4592  switch (code) {
4593  case EVENT_TIME:
4594  // Have weather affect the position of the object. Do not blow the floors, though -- that is bad.
4595  if (op->map && !QUERY_FLAG(op, FLAG_IS_FLOOR)) {
4596  uint8_t dir = wind_blow_object(op->map, op->x, op->y, op->move_type, op->weight+op->carrying, &op->stats);
4597  mapstruct *m;
4598  int16_t x, y;
4599  // By checking only the head space, we can actually caue sailing galleons to irrecoverably crash ashore.
4600  // This is deliberate behavior.
4601  if (dir &&
4602  // We will avoid pushing empty transports. Assume they are anchored/parked.
4603  !(op->type == TRANSPORT && op->inv == NULL) &&
4604  // If our player is in a transport, do not push the player
4605  !(op->type == PLAYER && op->contr && op->contr->transport) &&
4606  !(get_map_flags(op->map, &m, op->x+freearr_x[dir], op->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP) &&
4607  !blocked_link(op, m, x, y)) {
4608  object_remove(op);
4609  object_insert_in_map_at(op, m, op, 0, x, y);
4610 
4611  // Make sure to update the player view
4612  if (op->type == PLAYER) {
4613  esrv_map_scroll(op->contr->socket, freearr_x[dir], freearr_y[dir]);
4614  op->contr->socket->update_look = 1;
4615  op->contr->socket->look_position = 0;
4616  } else if (op->type == TRANSPORT) {
4617  FOR_INV_PREPARE(op, pl)
4618  if (pl->type == PLAYER) {
4619  pl->contr->do_los = 1;
4620  pl->map = op->map;
4621  pl->x = op->x;
4622  pl->y = op->y;
4623  esrv_map_scroll(pl->contr->socket, freearr_x[dir], freearr_y[dir]);
4624  pl->contr->socket->update_look = 1;
4625  pl->contr->socket->look_position = 0;
4626  }
4627  FOR_INV_FINISH();
4628  }
4629  }
4630  }
4631  }
4632  return 0;
4633 }
4634 
4643 static void command_weather (object *op, const char *params) {
4644  int wx, wy, temp, sky;
4645  const char *buf;
4646 
4647  if (wset.dynamiclevel < 1) {
4649  "The weather is perpetually great around here.");
4650  return;
4651  }
4652 
4653  if (op->map == NULL)
4654  return;
4655 
4656  if (worldmap_to_weathermap(op->x, op->y, &wx, &wy, op->map) != 0) {
4658  "You can't see the weather from here.");
4659  return;
4660  }
4661 
4662  if (QUERY_FLAG(op, FLAG_WIZ)) {
4663  /* dump the weather, Dm style! Yo! */
4664 
4666  "Real temp: %d", real_world_temperature(op->x, op->y, op->map));
4667 
4669  "Base temp: %d", weathermap[wx][wy].temp);
4670 
4672  "Humid: %d", weathermap[wx][wy].humid);
4673 
4675  "Wind: dir=%d speed=%d", weathermap[wx][wy].winddir, weathermap[wx][wy].windspeed);
4676 
4678  "Pressure: %d", weathermap[wx][wy].pressure);
4679 
4681  "Avg Elevation: %d", weathermap[wx][wy].avgelev);
4682 
4684  "Rainfall: %d Water: %d", weathermap[wx][wy].rainfall, weathermap[wx][wy].water);
4685  }
4686 
4687  temp = real_world_temperature(op->x, op->y, op->map);
4689  "It's currently %d degrees Centigrade out.", temp);
4690 
4691  /* humid */
4692  // We use buf real quick here for the same reason we use it for sky conditions: clarity
4693  if (weathermap[wx][wy].humid < 20)
4694  buf = "It is very dry.";
4695  else if (weathermap[wx][wy].humid < 40)
4696  buf = "It is rather dry.";
4697  else if (weathermap[wx][wy].humid < 60)
4698  buf = "It is very comfortable today.";
4699  else if (weathermap[wx][wy].humid < 75)
4700  buf = "It is a bit muggy.";
4701  else if (weathermap[wx][wy].humid < 85)
4702  buf = "It is muggy.";
4703  else
4704  buf = "It is uncomfortably muggy.";
4705  // buf will have a string, based on the if clauses above
4707 
4708  /* wind */
4709  switch (weathermap[wx][wy].winddir) {
4710  case 1:
4711  buf = "north";
4712  break;
4713  case 2:
4714  buf = "northeast";
4715  break;
4716  case 3:
4717  buf = "east";
4718  break;
4719  case 4:
4720  buf = "southeast";
4721  break;
4722  case 5:
4723  buf = "south";
4724  break;
4725  case 6:
4726  buf = "southwest";
4727  break;
4728  case 7:
4729  buf = "west";
4730  break;
4731  case 8:
4732  buf = "northwest";
4733  break;
4734  }
4735  if (weathermap[wx][wy].windspeed < 5)
4737  "There is a mild breeze coming from the %s.", buf);
4738  else if (weathermap[wx][wy].windspeed < 10)
4740  "There is a strong breeze coming from the %s.", buf);
4741  else if (weathermap[wx][wy].windspeed < 15)
4743  "There is a light wind coming from the %s.", buf);
4744  else if (weathermap[wx][wy].windspeed < 25)
4746  "There is a strong wind coming from the %s.", buf);
4747  else if (weathermap[wx][wy].windspeed < 35)
4749  "There is a heavy wind coming from the %s.", buf);
4750  else
4752  "The wind from the %s is incredibly strong!", buf);
4753 
4754  // If the current tile is below freezing, refer to the tile as snowing, even if the weathermap says rain.
4755  sky = weathermap[wx][wy].sky;
4756  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG)
4757  sky += 10; /*let it snow*/
4758  // Likewise, if the given tile is above freezing when the weathermap as a whole is below, say rain
4759  else if (temp > 0 && sky >= SKY_LIGHT_SNOW)
4760  sky -= 10;
4761 
4762  // Recycle buf for printing the various sky conditions strings.
4763  // This way we can cleanly call draw_ext_info once and not have
4764  // a bajillion calls with the same parameters other than the text.
4765  switch (sky) {
4766  case SKY_CLEAR:
4767  buf = "There isn't a cloud in the sky.";
4768  break;
4769  case SKY_LIGHTCLOUD:
4770  buf = "There are a few light clouds in the sky";
4771  break;
4772  case SKY_OVERCAST:
4773  buf = "The sky is cloudy and dreary.";
4774  break;
4775  case SKY_LIGHT_RAIN:
4776  buf = "It is raining softly.";
4777  break;
4778  case SKY_RAIN:
4779  buf = "It is raining.";
4780  break;
4781  case SKY_HEAVY_RAIN:
4782  buf = "It is raining heavily.";
4783  break;
4784  case SKY_HURRICANE:
4785  buf = "There is a heavy storm! You should go inside!";
4786  break;
4787  case SKY_FOG:
4788  buf = "It's foggy and miserable.";
4789  break;
4790  case SKY_HAIL:
4791  buf = "It's hailing out! Take cover!";
4792  break;
4793  case SKY_LIGHT_SNOW:
4794  buf = "Snow is gently falling from the sky.";
4795  break;
4796  case SKY_SNOW:
4797  buf = "It is snowing out.";
4798  break;
4799  case SKY_HEAVY_SNOW:
4800  buf = "Snow is falling very heavily.";
4801  break;
4802  case SKY_BLIZZARD:
4803  buf = "A full blown blizzard is in effect. You might want to take cover!";
4804  break;
4805  default:
4806  buf = NULL;
4807  }
4808  // If the sky is valid, then print the conditions.
4809  if (buf != NULL)
4811 }
4812 
4813 /********************************************************************************************
4814  * Section END -- weather event listeners
4815  ********************************************************************************************/
4816 
4817 // Event/command handler ids start at 1, so 0 is an unset flag.
4822 
4824 
4830  int x, tx, ty;
4831 
4832  // Initialize weather settings
4834 
4835  /* all this stuff needs to be set, otherwise this function will cause
4836  * chaos and destruction.
4837  */
4838  if (wset.dynamiclevel < 1) {
4839  LOG(llevInfo, "cfweather_init: dynamic level set to %d. Not loading weather.\n", wset.dynamiclevel);
4840  return;
4841  }
4842 
4843  if (settings->worldmapstartx < 1 || settings->worldmapstarty < 1 ||
4846  return;
4847  }
4848  // Initialize the forestry information from file.
4849  init_config_vals(settings, "treedefs", &forest_list);
4850  init_config_vals(settings, "waterdefs", &water_list);
4851 
4852  /*prepare structures used for avoidance*/
4853  init_weatheravoid(settings, "wavoiddefs", &weather_avoids);
4854  init_weatheravoid(settings, "gavoiddefs", &growth_avoids);
4855 
4856  init_weather_replace(settings, "wreplacedefs", &weather_replace);
4859 
4860  // Set up weathermap grid. This is needed for just about everything
4861  LOG(llevDebug, "Initializing the weathermap...\n");
4862 
4863  weathermap = (weathermap_t **)malloc(sizeof(weathermap_t *)*WEATHERMAPTILESX);
4864  if (weathermap == NULL) {
4866  }
4867  for (x = 0; x < WEATHERMAPTILESX; x++) {
4868  weathermap[x] = (weathermap_t *)calloc(WEATHERMAPTILESY, sizeof(weathermap_t));
4869  if (weathermap[x] == NULL) {
4871  }
4872  }
4873  /* Unless you know what you're doing, do not re-order these
4874  * I think I got all the dependencies noted, but it works this way
4875  * and I'd advise against changing the order unless you have a good reason.
4876  */
4878  // Begin to initialize the various data pieces for the weather
4879  // If wind direction did initialization, we don't need to read the wind speed from file.
4880  if (read_winddirmap(settings) == 0)
4883  // Some gulf stream fiddling that happens every startup.
4884  gulf_stream_direction = rndm(0, 1);
4885  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
4886  for (ty = 0; ty < WEATHERMAPTILESY-1; ty++) {
4887  if (gulf_stream_direction) {
4888  switch (gulf_stream_dir[tx][ty]) {
4889  case 2: gulf_stream_dir[tx][ty] = 6; break;
4890  case 3: gulf_stream_dir[tx][ty] = 7; break;
4891  case 4: gulf_stream_dir[tx][ty] = 8; break;
4892  }
4893  } else {
4894  switch (gulf_stream_dir[tx][ty]) {
4895  case 6: gulf_stream_dir[tx][ty] = 2; break;
4896  case 7: gulf_stream_dir[tx][ty] = 3; break;
4897  case 8: gulf_stream_dir[tx][ty] = 4; break;
4898  }
4899  }
4900  }
4901  }
4902  // Trees help stabilize local temperature and evaporate water from deeper underground.
4903  // This is calculated at the same time as elevation and humidity.
4904  int result = read_humidmap(settings);
4905  // If result is 1, then we initialized all these.
4906  // When that is the case, we don't need to read in from the file.
4907  // If result is -1, everything's jacked up anyway, so still don't load.
4908  if (result == 0) {
4909  read_watermap(settings); /* On first run, we want to do this after humidity. Otherwise, it doesn't seem to matter */
4910  read_elevmap(settings); /* elevation must allways follow humidity */
4912  }
4915 
4916  LOG(llevDebug, "Done reading weathermaps\n");
4917  // Initialize the sky so we can get accurate precipitation at initial load.
4918  compute_sky();
4919 
4920  // Get current map position
4922 
4923  // Connect the events after initialization, since we don't need to do
4924  // precipitation when we're initializing.
4926  //global_mapload_handler = events_register_global_handler(EVENT_MAPLOAD, weather_listener);
4929  // Register the 'weather command
4931  /* Disable the plugin in case it's still there */
4932  serv->disabled_plugins.push_back(strdup("cfweather"));
4933 }
4934 
4936  // Define temp pointers for clearing up the linked lists
4937  DensityConfig *cur;
4938  weather_avoids_t *avcur;
4939  weather_replace_t *rpcur;
4940  // Unregister handlers.
4941  if (global_map_handler != 0)
4943  if (global_clock_handler != 0)
4945  if (global_object_handler != 0)
4947  if (command_handler != 0)
4949  // Free the weathermap
4950  // Turns out that asset collection reaches here with weathermap as NULL,
4951  // so we need to make sure it is nonnull before trying to free it.
4952  if (weathermap != NULL) {
4953  for (int x = 0; x < WEATHERMAPTILESX; x++) {
4955  }
4957  }
4958  // Deallocate our linked list of forest entries.
4959  while (forest_list != NULL) {
4960  cur = forest_list;
4962  free_string(cur->name);
4963  free(cur);
4964  }
4965  // Do the same for the water list
4966  while (water_list != NULL) {
4967  cur = water_list;
4969  free_string(cur->name);
4970  free(cur);
4971  }
4972  // Free our avoid lists
4973  while (weather_avoids != NULL) {
4974  avcur = weather_avoids;
4976  free_string(avcur->name);
4977  free(avcur);
4978  }
4979  while (growth_avoids != NULL) {
4980  avcur = growth_avoids;
4982  free_string(avcur->name);
4983  free(avcur);
4984  }
4985  // Free our replacement lists
4986  while (weather_replace != NULL) {
4987  rpcur = weather_replace;
4989  free_string(rpcur->tile);
4990  free(rpcur);
4991  }
4992  while (weather_evaporate != NULL) {
4993  rpcur = weather_evaporate;
4995  free_string(rpcur->tile);
4996  free(rpcur);
4997  }
4998  while (weather_snowmelt != NULL) {
4999  rpcur = weather_snowmelt;
5001  free_string(rpcur->tile);
5002  free(rpcur);
5003  }
5004 }
5005 
5006 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:175
PLAYER
@ PLAYER
Definition: object.h:112
output_file.h
init_wind
static void init_wind()
Initialize the wind randomly.
Definition: cfweather.cpp:2992
weathermap_t::realtemp
int16_t realtemp
Temperature at a given calculation step for this tile.
Definition: cfweather.cpp:110
global.h
weather_grow_t::tempmax
int tempmax
Maximum temperature for herb to grow.
Definition: cfweather.cpp:162
weathermap_t::forestry
int8_t forestry
Range of forestedness.
Definition: cfweather.cpp:108
weather_tile
static const weather_grow_t weather_tile[]
The table below uses the same format as the one above.
Definition: cfweather.cpp:256
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
calculate_temperature
static void calculate_temperature(mapstruct *m)
Temperature is used in a lot of weather function.
Definition: cfweather.cpp:1429
global_mapload_handler
static event_registration global_mapload_handler
Definition: cfweather.cpp:4821
bufferreader_data
char * bufferreader_data(BufferReader *br)
Get the whole buffer, independently of the calls to bufferreader_next_line().
Definition: bufferreader.cpp:150
load_overlay_map
int load_overlay_map(const char *filename, mapstruct *m)
Loads an overlay for a map, which has been loaded earlier, from file.
Definition: map.cpp:1305
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Return the index of the last line returned by bufferreader_next_line().
Definition: bufferreader.cpp:142
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
weathermap_t::sky
int8_t sky
Sky conditions.
Definition: cfweather.cpp:103
EQUATOR_BASE_TEMP
#define EQUATOR_BASE_TEMP
Definition: cfweather.cpp:34
init_rainfall
static void init_rainfall()
Initialize rainfall.
Definition: cfweather.cpp:2879
MOVE_FLYING
#define MOVE_FLYING
Combo of fly_low and fly_high.
Definition: define.h:386
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
PTICKS_PER_CLOCK
#define PTICKS_PER_CLOCK
Number of ticks per in-game hour.
Definition: tod.h:12
weather_avoids_t::next
weather_avoids_t * next
The next item in the avoid list.
Definition: cfweather.cpp:136
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
weather_replace_t::special_snow
archetype * special_snow
The archetype name of the tile to place over specified tile.
Definition: cfweather.cpp:144
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.cpp:1759
blocked_link
int blocked_link(object *ob, mapstruct *m, int16_t sx, int16_t sy)
Returns true if the given coordinate is blocked except by the object passed is not blocking.
Definition: map.cpp:354
init_pressure
static void init_pressure()
Reset pressure map.
Definition: cfweather.cpp:3009
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
socket_struct::look_position
uint16_t look_position
Start of drawing of look window.
Definition: newserver.h:119
ready_map_name
mapstruct * ready_map_name(const char *name, int flags)
Makes sure the given map is loaded and swapped in.
Definition: map.cpp:1785
get_world_darkness
int get_world_darkness(void)
Get the darkness of the world map at this point.
Definition: weather.cpp:72
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
WEATHERMAPTILESY
#define WEATHERMAPTILESY
Definition: cfweather.cpp:76
ServerSettings::disabled_plugins
std::vector< std::string > disabled_plugins
List of disabled plugins, 'All' means all.
Definition: server.h:16
SEASONAL_ADJUST
#define SEASONAL_ADJUST
Definition: cfweather.cpp:35
read_winddirmap
static int read_winddirmap(const Settings *settings)
Read the wind direction.
Definition: cfweather.cpp:4298
weathermap_t::avgelev
int32_t avgelev
Average elevation.
Definition: cfweather.cpp:104
write_rainfallmap
int write_rainfallmap(const Settings *settings)
Save rainfall information to localdir.
Definition: cfweather.cpp:3303
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:424
absdir
int absdir(int d)
Computes an absolute direction.
Definition: object.cpp:3699
PRESSURE_ROUNDING_FACTOR
#define PRESSURE_ROUNDING_FACTOR
Definition: cfweather.cpp:42
write_humidmap
int write_humidmap(const Settings *settings)
Save humidity information to localdir.
Definition: cfweather.cpp:3166
temperature_calc
static void temperature_calc(const int x, const int y, const timeofday_t *tod)
Calculate temperature of a spot.
Definition: cfweather.cpp:1029
SKY_LIGHT_RAIN
#define SKY_LIGHT_RAIN
Definition: cfweather.cpp:52
HOURS_PER_DAY
#define HOURS_PER_DAY
Definition: tod.h:15
object::x
int16_t x
Definition: object.h:335
player::transport
object * transport
Transport the player is in.
Definition: player.h:216
PRESSURE_SPIKES
#define PRESSURE_SPIKES
Definition: cfweather.cpp:44
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:131
SKY_CLEAR
#define SKY_CLEAR
Definition: cfweather.cpp:49
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
Settings::worldmaptilesy
uint32_t worldmaptilesy
Number of tiles high the worldmap is.
Definition: global.h:299
read_pressuremap
static int read_pressuremap(const Settings *settings)
Read the pressure information from disk.
Definition: cfweather.cpp:4362
MoveType
unsigned char MoveType
Typdef here to define type large enough to hold bitmask of all movement types.
Definition: define.h:409
PRESSURE_MAX
#define PRESSURE_MAX
Definition: cfweather.cpp:45
timeofday_t
Represents the ingame time.
Definition: tod.h:38
weathermap_t::darkness
uint8_t darkness
Indicates level of darkness of map.
Definition: cfweather.cpp:106
GULF_STREAM_WIDTH
#define GULF_STREAM_WIDTH
Definition: cfweather.cpp:36
weathermap_t::pressure
int16_t pressure
Barometric pressure (mb).
Definition: cfweather.cpp:99
Settings::worldmapstartx
uint32_t worldmapstartx
Starting x tile for the worldmap.
Definition: global.h:296
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:218
MIN
#define MIN(x, y)
Definition: compat.h:21
weather_grow
static const weather_grow_t weather_grow[]
The table below is used to grow things on the map.
Definition: cfweather.cpp:225
GULF_STREAM_BASE_SPEED
#define GULF_STREAM_BASE_SPEED
Definition: cfweather.cpp:37
in
same as sound ncom command like but with extra the client want tick commands so it knows animation timing the client wants to be informed of pickup mode changes Mode will be sent when the player successfully logs in
Definition: protocol.txt:408
bufferreader_destroy
void bufferreader_destroy(BufferReader *br)
Destroy a BufferReader.
Definition: bufferreader.cpp:41
MAP_WORLDPARTY
#define MAP_WORLDPARTY(m)
Definition: map.h:88
weather_replace_t::tile
sstring tile
Tile archetype or object name.
Definition: cfweather.cpp:143
weathermap_t::windspeed
int8_t windspeed
Windspeed of this tile.
Definition: cfweather.cpp:101
SKY_LIGHT_SNOW
#define SKY_LIGHT_SNOW
Definition: cfweather.cpp:60
WEATHER_OVERLAY
#define WEATHER_OVERLAY
Weather insert flags.
Definition: cfweather.cpp:618
weather_grow_t::random
int random
Random apparition factor.
Definition: cfweather.cpp:156
read_gulfstreammap
static int read_gulfstreammap(const Settings *settings)
Read the gulf stream, or initialize it if no saved information.
Definition: cfweather.cpp:4147
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Copy object first frees everything allocated by the second object, and then copies the contents of th...
Definition: object.cpp:1177
SKY_LIGHTCLOUD
#define SKY_LIGHTCLOUD
Definition: cfweather.cpp:50
do_map_precipitation
static void do_map_precipitation(mapstruct *const m)
Do the precipitation for a given map.
Definition: cfweather.cpp:1560
get_tod
void get_tod(timeofday_t *tod)
Computes the ingame time of the day.
Definition: time.cpp:219
load_humidity_map_part
static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int *const tx, int *const ty)
Method to abstract some of the mess of the humidity map.
Definition: cfweather.cpp:2669
COMMAND_TYPE_NORMAL
#define COMMAND_TYPE_NORMAL
Standard commands.
Definition: commands.h:35
TRANSPORT
@ TRANSPORT
see doc/Developers/objects
Definition: object.h:113
PRESSURE_AREA
#define PRESSURE_AREA
Definition: cfweather.cpp:41
read_humidmap
static int read_humidmap(const Settings *settings)
Attempt to read humidity information, or barring that, read the maps and initialize elevation,...
Definition: cfweather.cpp:3847
rndm
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.cpp:162
DensityConfig::is_obj
int is_obj
Definition: cfweather.cpp:120
weather_avoids_t::snow
int snow
Is this a long-term weather effect, like snow or a puddle? Used for various tests.
Definition: cfweather.cpp:134
init_weather_replace
static int init_weather_replace(const Settings *settings, const char *conf_filename, weather_replace_t **list)
Load the weather replacement definitions from file.
Definition: cfweather.cpp:2554
POLAR_BASE_TEMP
#define POLAR_BASE_TEMP
Definition: cfweather.cpp:33
read_windspeedmap
static int read_windspeedmap(const Settings *settings)
Read the wind speed.
Definition: cfweather.cpp:4243
weathermap
weathermap_t ** weathermap
Definition: cfweather.cpp:183
weather_avoids_t::name
sstring name
Tile archetype name.
Definition: cfweather.cpp:133
pticks
uint32_t pticks
Number of ticks since time reset.
Definition: time.cpp:47
buf
StringBuffer * buf
Definition: readable.cpp:1564
worldmap_to_weathermap
static int worldmap_to_weathermap(const int x, const int y, int *const wx, int *const wy, mapstruct *const m)
Convert coordinates from world map to weather tiles.
Definition: cfweather.cpp:458
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
weather_replace_t::arch_or_name
int arch_or_name
If set, tile matches the archetype name, else the object's name.
Definition: cfweather.cpp:146
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:408
MAX
#define MAX(x, y)
Definition: compat.h:24
let_it_snow
static void let_it_snow(mapstruct *const m)
Put or remove snow.
Definition: cfweather.cpp:1583
weathermap_t
This is an overlay structure of the whole world.
Definition: cfweather.cpp:94
command_handler
static command_registration command_handler
Definition: cfweather.cpp:4823
plant_a_garden
static void plant_a_garden(mapstruct *const m)
Process growth of various plants.
Definition: cfweather.cpp:1974
init_humid_elev
static void init_humid_elev(const Settings *settings)
Initialize humidity, water, forestry, and elevation.
Definition: cfweather.cpp:2743
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
Settings::worldmaptilesx
uint32_t worldmaptilesx
Number of tiles wide the worldmap is.
Definition: global.h:298
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
events_register_global_handler
event_registration events_register_global_handler(int eventcode, f_plug_event hook)
Register a global event handler.
Definition: events.cpp:19
socket_struct::update_look
uint32_t update_look
If true, we need to send the look window.
Definition: newserver.h:109
PRESSURE_ROUNDING_ITER
#define PRESSURE_ROUNDING_ITER
Definition: cfweather.cpp:43
read_weatherposition
static int read_weatherposition(const Settings *settings)
Read the weather map position from file.
Definition: cfweather.cpp:4424
read_watermap
static int read_watermap(const Settings *settings)
Load water information from localdir.
Definition: cfweather.cpp:3970
object::carrying
int32_t carrying
How much weight this object contains.
Definition: object.h:377
cfweather_init
void cfweather_init(Settings *settings, ServerSettings *serv)
Weather module initialisation.
Definition: cfweather.cpp:4829
weather_snowmelt
weather_replace_t * weather_snowmelt
Definition: cfweather.cpp:191
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:424
forest_list
DensityConfig * forest_list
Definition: cfweather.cpp:185
SKY_FOG
#define SKY_FOG
Definition: cfweather.cpp:57
WEATHER_NO_FLOOR
#define WEATHER_NO_FLOOR
Definition: cfweather.cpp:619
SKY_SNOW
#define SKY_SNOW
Definition: cfweather.cpp:61
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
bufferreader_init_from_file
BufferReader * bufferreader_init_from_file(BufferReader *br, const char *filepath, const char *failureMessage, LogLevel failureLevel)
Initialize or create a BufferReader from a file path.
Definition: bufferreader.cpp:67
directions
static const uint32_t directions[]
Colours used for wind directions.
Definition: cfweather.cpp:3513
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
gulf_stream_speed
static int gulf_stream_speed[GULF_STREAM_WIDTH][WEATHERMAPTILESY]
Gulf stream variables.
Definition: cfweather.cpp:200
write_gulfstreammap
int write_gulfstreammap(const Settings *settings)
Save the gulf stream to localdir.
Definition: cfweather.cpp:3336
do_precipitation
static void do_precipitation(mapstruct *const m, const int x, const int y, const int temp, const int sky)
Handle adding precipitation to the map.
Definition: cfweather.cpp:1482
DensityConfig::next
DensityConfig * next
Definition: cfweather.cpp:126
weather_settings_t::worldmaptilesizey
uint32_t worldmaptilesizey
Number of squares high in a wm tile.
Definition: cfweather.cpp:174
command_unregister
void command_unregister(command_registration command)
Unregister a previously registered command.
Definition: commands.cpp:534
polar_distance
static int polar_distance(int x, int y, const int equator)
Calculates the distance to the nearest pole.
Definition: cfweather.cpp:384
freearr_y
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.cpp:305
WIND_FACTOR
#define WIND_FACTOR
This is a multiplier for the wind caused by pressure differences.
Definition: cfweather.cpp:72
avoid_weather
static object * avoid_weather(int *const av, const mapstruct *m, const int x, const int y, int *const gs, const int grow)
Check the current square to see if we should avoid this one for weather processing.
Definition: cfweather.cpp:534
RED
#define RED
Definition: cfweather.cpp:3502
read_elevmap
static int read_elevmap(const Settings *settings)
Load the elevation information.
Definition: cfweather.cpp:3911
DensityConfig::value_density
int value_density
Definition: cfweather.cpp:122
skies
static const uint32_t skies[]
Colours used for weather types.
Definition: cfweather.cpp:3527
do_weather_insert
static void do_weather_insert(mapstruct *const m, int x, int y, const archetype *at, const int8_t object_flags, uint16_t material, int insert_flags)
Do an object insert for weather effects.
Definition: cfweather.cpp:648
GREEN
#define GREEN
Definition: cfweather.cpp:3503
weathermap_t::temp
int16_t temp
Base temperature of this tile (F).
Definition: cfweather.cpp:98
gulf_stream_start
static int gulf_stream_start
Definition: cfweather.cpp:203
plot_gulfstream
static void plot_gulfstream()
Plot the gulfstream map over the wind map.
Definition: cfweather.cpp:1305
events_unregister_global_handler
void events_unregister_global_handler(int eventcode, event_registration id)
Remove a global event handler.
Definition: events.cpp:26
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
tick_weather
void tick_weather()
Do the weather calculations in order.
Definition: cfweather.cpp:1445
Settings::worldmapstarty
uint32_t worldmapstarty
Starting y tile for the worldmap.
Definition: global.h:297
add_string
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
growth_avoids
weather_avoids_t * growth_avoids
Definition: cfweather.cpp:188
speed
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters speed
Definition: stats.txt:23
SKY_RAIN
#define SKY_RAIN
Definition: cfweather.cpp:53
wmperformstartx
static int wmperformstartx
Current weather tile position.
Definition: cfweather.cpp:207
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
smooth_pressure
static void smooth_pressure()
This code simply smooths the pressure map.
Definition: cfweather.cpp:788
object::move_type
MoveType move_type
Type of movement this object uses.
Definition: object.h:436
write_windspeedmap
int write_windspeedmap(const Settings *settings)
Save the wind speed to localdir.
Definition: cfweather.cpp:3379
global_clock_handler
static event_registration global_clock_handler
Definition: cfweather.cpp:4819
find_string
sstring find_string(const char *str)
Searches a string in the shared strings.
Definition: shstr.cpp:250
weather_grow_t::rfmin
float rfmin
Minimum rainfall for herb to grow (inches/day).
Definition: cfweather.cpp:157
MAX_DARKNESS
#define MAX_DARKNESS
Maximum map darkness, there is no practical reason to exceed this.
Definition: define.h:439
write_winddirmap
int write_winddirmap(const Settings *settings)
Save wind direction to localdir.
Definition: cfweather.cpp:3412
todtick
unsigned long todtick
Game world time, in in-game hours.
Definition: time.cpp:38
init_temperature
static void init_temperature()
Initialize the temperature based on the time.
Definition: cfweather.cpp:2862
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
EVENT_CLOCK
#define EVENT_CLOCK
Global time event.
Definition: events.h:53
SAVE_MODE_OVERLAY
#define SAVE_MODE_OVERLAY
Map is persisted as an overlay.
Definition: map.h:123
object_free
void object_free(object *ob, int flags)
Frees everything allocated by an object, removes it from the list of used objects,...
Definition: object.cpp:1577
read_forestrymap
static int read_forestrymap(const Settings *settings)
Read the forestry map from the localdir.
Definition: cfweather.cpp:3784
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:661
FLAG_NO_SAVE
#define FLAG_NO_SAVE
If set (through plugins), the object is not saved on maps.
Definition: define.h:231
INS_ON_TOP
#define INS_ON_TOP
Always put object on top.
Definition: object.h:583
BLUE
#define BLUE
Definition: cfweather.cpp:3504
WEATHER_NO_SAVE
#define WEATHER_NO_SAVE
Definition: cfweather.cpp:620
weather_replace_t::doublestack_arch
archetype * doublestack_arch
If set, this other archetype will be added atop special_snow.
Definition: cfweather.cpp:145
get_config_tile
static int get_config_tile(const int x, const int y, const mapstruct *m, const DensityConfig *list)
Get config tile retrieves the desired tile's associated value from a given space.
Definition: cfweather.cpp:416
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
EVENT_TIME
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:40
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:254
Settings::confdir
const char * confdir
Configuration files.
Definition: global.h:252
weather_grow_t::herb
const char * herb
Arch name of item to grow.
Definition: cfweather.cpp:154
sproto.h
wind_blow_object
static uint8_t wind_blow_object(mapstruct *const m, const int x, const int y, const MoveType move_type, int32_t wt, const living *stats)
Calculate the direction to push an object from wind.
Definition: cfweather.cpp:2319
weathermap_to_worldmap_corner
static char * weathermap_to_worldmap_corner(const int wx, const int wy, int *const x, int *const y, const int dir, char *const buffer, const int bufsize)
Return the path of the map in specified direction.
Definition: cfweather.cpp:333
FLAG_IS_FLOOR
#define FLAG_IS_FLOOR
Can't see what's underneath this object.
Definition: define.h:289
real_temperature
static int real_temperature(int x, int y, const timeofday_t *tod)
Compute the real (adjusted) temperature of a given weathermap tile.
Definition: cfweather.cpp:896
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1713
weathermap_t::water
int8_t water
-100 - 100 percentage of water tiles.
Definition: cfweather.cpp:107
similar_direction
int similar_direction(int a, int b)
Is direction a similar to direction b? Find out in this exciting function below.
Definition: player.cpp:2289
FLAG_OVERLAY_FLOOR
#define FLAG_OVERLAY_FLOOR
Object is an overlay floor.
Definition: define.h:242
timeofday_t::month
int month
Definition: tod.h:40
global_map_handler
static event_registration global_map_handler
Definition: cfweather.cpp:4818
INS_NO_WALK_ON
#define INS_NO_WALK_ON
Don't call check_walk_on against the originator.
Definition: object.h:582
singing_in_the_rain
static void singing_in_the_rain(mapstruct *const m)
Process rain.
Definition: cfweather.cpp:1781
spin_globe
static void spin_globe()
The world spinning drags the weather with it.
Definition: cfweather.cpp:1159
DensityConfig::name
sstring name
Definition: cfweather.cpp:118
init_gulfstreammap
static void init_gulfstreammap()
Initialize the gulf stream.
Definition: cfweather.cpp:2910
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.cpp:2085
living
Various statistics of objects.
Definition: living.h:35
water_list
DensityConfig * water_list
Definition: cfweather.cpp:186
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:595
map.h
real_world_temperature
int real_world_temperature(int x, int y, mapstruct *m)
Compute the temperature for a specific square.
Definition: cfweather.cpp:969
weather_replace
weather_replace_t * weather_replace
Definition: cfweather.cpp:189
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
EVENT_MAPREADY
#define EVENT_MAPREADY
A map is ready, either first load or after reload.
Definition: events.h:62
d
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for d
Definition: INSTALL_WIN32.txt:13
object_new
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it.
Definition: object.cpp:1258
object_insert_in_map
object * object_insert_in_map(object *op, mapstruct *m, object *originator, int flag)
This function inserts the object in the two-way linked list which represents what is on a map.
Definition: object.cpp:2346
object::weight
int32_t weight
Attributes of the object.
Definition: object.h:375
process_rain
void process_rain()
Keep track of how much rain has fallen in a given weathermap square.
Definition: cfweather.cpp:1401
free_string
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.cpp:294
cfweather_close
void cfweather_close()
Definition: cfweather.cpp:4935
weather_settings_t::worldmaptilesizex
uint32_t worldmaptilesizex
Number of squares wide in a wm tile.
Definition: cfweather.cpp:173
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
perform_weather
void perform_weather()
This routine slowly loads the world, patches it up due to the weather, and saves it back to disk.
Definition: cfweather.cpp:2249
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Checks if a square is out of the map.
Definition: map.h:222
wmperformstarty
static int wmperformstarty
Current weather tile position.
Definition: cfweather.cpp:209
MONTHS_PER_YEAR
#define MONTHS_PER_YEAR
Definition: tod.h:18
find_dir_2
int find_dir_2(int x, int y)
Computes a direction which you should travel to move of x and y.
Definition: object.cpp:3662
write_elevmap
static int write_elevmap(const Settings *settings)
Save the average elevation information to localdir.
Definition: cfweather.cpp:3201
Settings
Server settings.
Definition: global.h:245
weather_settings_t::dynamiclevel
uint16_t dynamiclevel
How dynamic is the world?
Definition: cfweather.cpp:175
above
Magical Runes Runes are magical inscriptions on the dungeon which cast a spell or detonate when something steps on them Flying objects don t detonate runes Beware ! Runes are invisible most of the time They are only visible occasionally ! There are several runes which are there are some special runes which may only be called with the invoke and people may apply it to read it Maybe useful for mazes ! This rune will not nor is it ordinarily invisible Partial Visibility of they ll be visible only part of the time They have so the higher your the better hidden the runes you make are Examples of whichever way you re facing invoke magic rune transfer as above
Definition: runes-guide.txt:50
write_forestrymap
static int write_forestrymap(const Settings *settings)
Write the forestry map to the localdir.
Definition: cfweather.cpp:3131
llevInfo
@ llevInfo
Information.
Definition: logger.h:14
WEATHERMAPTILESX
#define WEATHERMAPTILESX
Definition: cfweather.cpp:75
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
global_object_handler
static event_registration global_object_handler
Definition: cfweather.cpp:4820
season_tempchange
static const int season_tempchange[HOURS_PER_DAY]
How to alter the temperature, based on the hour of the day.
Definition: cfweather.cpp:875
weather_grow_t::rfmax
float rfmax
Maximum rainfall for herb to grow (inches/day).
Definition: cfweather.cpp:158
SKY_HEAVY_RAIN
#define SKY_HEAVY_RAIN
Definition: cfweather.cpp:54
SKY_HEAVY_SNOW
#define SKY_HEAVY_SNOW
Definition: cfweather.cpp:62
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
weathermap_t::winddir
int8_t winddir
Direction of wind.
Definition: cfweather.cpp:102
event_registration
unsigned long event_registration
Registration identifier type.
Definition: events.h:84
INS_ABOVE_FLOOR_ONLY
#define INS_ABOVE_FLOOR_ONLY
Put object immediatly above the floor.
Definition: object.h:581
compute_sky
void compute_sky()
Let the madness, begin.
Definition: cfweather.cpp:1102
M_LIQUID
#define M_LIQUID
Liquid.
Definition: material.h:23
FREE_AND_CLEAR
#define FREE_AND_CLEAR(xyz)
Free the pointer and then set it to NULL.
Definition: global.h:199
weather_grow_t::elevmax
int elevmax
Maximum elevation for herb to grow.
Definition: cfweather.cpp:164
weather_grow_t::elevmin
int elevmin
Minimum elevation for herb to grow.
Definition: cfweather.cpp:163
weather_grow_t::tempmin
int tempmin
Minimum temperature for herb to grow.
Definition: cfweather.cpp:161
SKY_HURRICANE
#define SKY_HURRICANE
Definition: cfweather.cpp:55
Settings::fastclock
uint8_t fastclock
If true, clock goes warp 9.
Definition: global.h:300
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_.
Definition: map.cpp:300
weathermap_t::rainfall
uint32_t rainfall
Cumulative rainfall.
Definition: cfweather.cpp:105
check_replace_match
static int check_replace_match(const object *ob, const weather_replace_t *rep_struct)
Refactor the code to look for arch or object name as it's own code.
Definition: cfweather.cpp:602
weather_object_listener
static int weather_object_listener(int *type,...)
Global object-process handling for weather.
Definition: cfweather.cpp:4579
mapstruct
This is a game-map.
Definition: map.h:320
update_humid
static void update_humid()
Update the humidity for all weathermap tiles.
Definition: cfweather.cpp:1291
ServerSettings
Definition: server.h:15
FLAG_IS_WATER
#define FLAG_IS_WATER
Definition: define.h:351
sstring
const typedef char * sstring
Definition: sstring.h:2
EVENT_MAPENTER
#define EVENT_MAPENTER
A player entered a map.
Definition: events.h:59
gulf_stream_direction
static int gulf_stream_direction
Definition: cfweather.cpp:204
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:270
weather_avoids_t
Defines a tile the weather system should avoid.
Definition: cfweather.cpp:132
get_next_field
static char * get_next_field(char *line)
Finds the start of the next field in the provided string Skips past interceding commas and spaces.
Definition: cfweather.cpp:681
init_weather_settings
static void init_weather_settings(Settings *settings)
Definition: cfweather.cpp:3046
timeofday_t::hour
int hour
Definition: tod.h:43
buffer
if you malloc the data for the buffer
Definition: protocol.txt:2106
MSG_TYPE_COMMAND_WEATHER
#define MSG_TYPE_COMMAND_WEATHER
Definition: newclient.h:527
DensityConfig
Structure to hold density data entries.
Definition: cfweather.cpp:116
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:370
weather_grow_t::humin
int humin
Minimum humidity for herb to grow.
Definition: cfweather.cpp:159
write_skymap
int write_skymap(void)
Write the sky map.
Definition: cfweather.cpp:3474
SKY_OVERCAST
#define SKY_OVERCAST
Definition: cfweather.cpp:51
read_temperaturemap
static int read_temperaturemap(const Settings *settings)
Load or initialize temperature information.
Definition: cfweather.cpp:4028
command_weather
static void command_weather(object *op, const char *params)
Player is wondering about the weather.
Definition: cfweather.cpp:4643
MAP_WORLDPARTX
#define MAP_WORLDPARTX(m)
Definition: map.h:87
INS_NO_MERGE
#define INS_NO_MERGE
Don't try to merge with other items.
Definition: object.h:580
stats
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various stats
Definition: stats.txt:2
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
smooth_wind
static void smooth_wind()
It doesn't really smooth it as such.
Definition: cfweather.cpp:713
strcasecmp
int strcasecmp(const char *s1, const char *s2)
write_pressuremap
int write_pressuremap(const Settings *settings)
Save pressure information to localdir.
Definition: cfweather.cpp:3445
weather_grow_t::humax
int humax
Maximum humidity for herb to grow.
Definition: cfweather.cpp:160
SKY_BLIZZARD
#define SKY_BLIZZARD
Definition: cfweather.cpp:63
read_rainfallmap
static int read_rainfallmap(const Settings *settings)
Read or initialize rainfall information.
Definition: cfweather.cpp:4090
weather_settings_t
Weather settings definition structure Stolen from the settings file, as they are unused by everything...
Definition: cfweather.cpp:172
object::elevation
int elevation
Definition: object.h:450
write_temperaturemap
int write_temperaturemap(const Settings *settings)
Save temperature information to localdir.
Definition: cfweather.cpp:3270
command_register
command_registration command_register(const char *name, uint8_t type, command_function func, float time)
Register a player-issued command.
Definition: commands.cpp:101
weather_listener
static int weather_listener(int *type,...)
Global event handling for weather.
Definition: cfweather.cpp:4482
object_remove
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to.
Definition: object.cpp:1818
SKY_HAIL
#define SKY_HAIL
Definition: cfweather.cpp:58
EVENT_MAPLOAD
#define EVENT_MAPLOAD
A map is loaded (pristine state)
Definition: events.h:61
command_registration
uint64_t command_registration
Identifier when registering a command.
Definition: commands.h:32
weather_grow_t::season
int season
Season the herb can grow.
Definition: cfweather.cpp:165
weather_avoids
weather_avoids_t * weather_avoids
Definition: cfweather.cpp:187
M_ICE
#define M_ICE
Ice.
Definition: material.h:26
weather_replace_t
Defines a tile the weather system can change to another tile.
Definition: cfweather.cpp:142
weather_grow_t
Defines a tile where something can grow.
Definition: cfweather.cpp:153
write_watermap
static int write_watermap(const Settings *settings)
Save water percent information to localdir.
Definition: cfweather.cpp:3236
weather_effect
static void weather_effect(mapstruct *const m)
Perform actual effect of weather.
Definition: cfweather.cpp:2207
wset
static weather_settings_t wset
Definition: cfweather.cpp:211
code
Crossfire Architecture the general intention is to enhance the enjoyability and playability of CF In this code
Definition: arch-handbook.txt:14
save_map
int save_map(mapstruct *m, int flag)
Saves a map to file.
Definition: map.cpp:1412
change_the_world
static void change_the_world(mapstruct *const m)
Process worldmap regrowth.
Definition: cfweather.cpp:2073
weathermap_t::humid
int8_t humid
Humitidy of this tile.
Definition: cfweather.cpp:100
archetype::name
sstring name
More definite name, like "generate_kobold".
Definition: object.h:484
PRESSURE_ITERATIONS
#define PRESSURE_ITERATIONS
Definition: cfweather.cpp:40
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:109
object::stats
living stats
Str, Con, Dex, etc.
Definition: object.h:378
list
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for but you habe then to change the pathes in the VC settings Go in Settings C and Settings Link and change the optional include and libs path to the new python installation path o except the maps ! You must download a map package and install them the share folder Its must look like doubleclick on crossfire32 dsw There are projects in your libcross lib and plugin_python You need to compile all Easiest way is to select the plugin_python ReleaseLog as active this will compile all others too Then in Visual C press< F7 > to compile If you don t have an appropriate compiler you can try to get the the VC copies the crossfire32 exe in the crossfire folder and the plugin_python dll in the crossfire share plugins folder we will remove it when we get time for it o Last showing lots of weird write to the Crossfire mailing list
Definition: INSTALL_WIN32.txt:50
init_config_vals
static int init_config_vals(const Settings *settings, const char *conf_filename, DensityConfig **list)
Read a config file that tells how many units (for an arbitrary purpose) a given arch is worth during ...
Definition: cfweather.cpp:2387
server.h
weather_grow_t::tile
const char * tile
Arch tile to grow on, NULL if anything.
Definition: cfweather.cpp:155
freearr_x
short freearr_x[SIZEOFFREE]
X offset when searching around a spot.
Definition: object.cpp:299
gulf_stream_dir
static int gulf_stream_dir[GULF_STREAM_WIDTH][WEATHERMAPTILESY]
Direction of the gulf stream.
Definition: cfweather.cpp:202
weather_clock_listener
static int weather_clock_listener(int *type,...)
Global clock event handling for weather.
Definition: cfweather.cpp:4521
days
static const char * days[]
Ingame days.
Definition: server.cpp:51
TRUE
#define TRUE
Definition: compat.h:11
mapfile_load
mapstruct * mapfile_load(const char *map, int flags)
Opens the file "filename" and reads information about the map from the given file,...
Definition: map.cpp:1233
weather_evaporate
weather_replace_t * weather_evaporate
Definition: cfweather.cpp:190
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
dirdiff
int dirdiff(int dir1, int dir2)
Computes a direction difference.
Definition: object.cpp:3717
BufferReader
Definition: bufferreader.cpp:22
object::material
uint16_t material
What materials this object consist of.
Definition: object.h:357
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:654
humid_tile
static int humid_tile(const int x, const int y, const int dark)
Calculate the humidity of the given weather tile.
Definition: cfweather.cpp:1203
object.h
weather_replace_t::next
weather_replace_t * next
The next item in the replace list.
Definition: cfweather.cpp:147
PRESSURE_MIN
#define PRESSURE_MIN
Definition: cfweather.cpp:46
write_weather_images
int write_weather_images()
Dump all the weather data as an image.
Definition: cfweather.cpp:3561
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:15
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
do_water_elev_calc
static int do_water_elev_calc(mapstruct *const m, const int x, const int y, int *const water, int64_t *const elev, int *const trees)
Do the water and elevation updates on the given map tile.
Definition: cfweather.cpp:2712
perform_pressure
static void perform_pressure()
Perform small randomizations in the pressure map.
Definition: cfweather.cpp:828
OutputFile
Definition: output_file.h:41
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
Definition: bufferreader.cpp:104
players_on_map
int players_on_map(mapstruct *m, int show_all)
Returns the count of players on a map, calculated from player list.
Definition: main.cpp:377
init_weatheravoid
static int init_weatheravoid(const Settings *settings, const char *conf_filename, weather_avoids_t **wa)
Load the weather/growth avoid defintions from file.
Definition: cfweather.cpp:2472
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254