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;
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 
533 static object *avoid_weather(int * const av, const mapstruct *m, const int x, const int y, int * const gs, const int grow) {
534  int avoid, gotsnow, i;
535  object *tmp, *snow;
536  const weather_avoids_t *cur;
537 
538  avoid = 0;
539  gotsnow = 0;
540  if (grow) {
541  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
542  /* look for things like walls, holes, etc */
543  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !(tmp->material&M_ICE || tmp->material&M_LIQUID)) {
544  gotsnow++;
545  snow = tmp;
546  }
547  for (cur = growth_avoids; cur; cur = cur->next) {
548  // Due to the use of shared strings, we can do pointer comparison here.
549  if (tmp->arch->name == cur->name) {
550  avoid++;
551  break;
552  }
553  }
554  if (avoid && gotsnow) {
555  break;
556  }
557  }
558  } else {
559  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
560  for (cur = weather_avoids; cur; cur = cur->next) {
561  if (tmp->arch->name == cur->name) {
562  // We clear FLAG_IS_FLOOR for our snow. The map's default snow does not.
563  // Avoid weirdness on the pathway to Brest and at the south pole by checking for non-floor snow
564  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && cur->snow == 1) {
565  gotsnow++;
566  snow = tmp;
567  } else {
568  avoid++;
569  }
570  break;
571  }
572  }
573  if (avoid && gotsnow) {
574  break;
575  }
576  }
577  }
578  *gs = gotsnow;
579  *av = avoid;
580 
581  return snow;
582 }
583 
601 static int check_replace_match(const object *ob, const weather_replace_t *rep_struct) {
602  if (rep_struct->arch_or_name == 1) {
603  if (ob->arch->name == rep_struct->tile) {
604  return 1;
605  }
606  } else {
607  if (ob->name == rep_struct->tile) {
608  return 1;
609  }
610  }
611  return 0;
612 }
613 
617 #define WEATHER_OVERLAY 1 /* If set, we set FLAG_OVERLAY_FLOOR */
618 #define WEATHER_NO_FLOOR 2 /* If set, we clear FLAG_IS_FLOOR */
619 #define WEATHER_NO_SAVE 4 /* If set, we set FLAG_NO_SAVE */
620 
647 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) {
648  if (at != NULL) {
649  object *ob = object_new();
650  object_copy(&at->clone, ob);
651  ob->x = x;
652  ob->y = y;
653  if (object_flags & WEATHER_OVERLAY)
655  if (object_flags & WEATHER_NO_FLOOR)
657  if (object_flags & WEATHER_NO_SAVE)
658  SET_FLAG(ob, FLAG_NO_SAVE);
659  if (material)
660  ob->material = material;
661  object_insert_in_map(ob, m, ob, insert_flags);
662  }
663 }
664 
680 static char *get_next_field(char *line) {
681  // The comma is the end of the field
682  line = strchr(line, ',');
683  if (line == NULL)
684  return NULL;
685  // Move past the known field seperator, but null terminate over it for the previous field.
686  *(line++) = '\0';
687  // While spaces or commas, skip the character
688  while (*line == ' ' || *line == ',')
689  ++line;
690  // Next field begins here.
691  return line;
692 }
693 
694 /********************************************************************************************
695  * Section END -- weather data helpers
696  ********************************************************************************************/
697 
698 /********************************************************************************************
699  * Section -- weather data calculators
700  * These functions determine the progression of weather data over time.
701  * This is the bread-and-butter of the weather system.
702  ********************************************************************************************/
703 
704 // We need to declare init_temperature, since it is defined below this area.
705 static void init_temperature();
706 
712 static void smooth_wind() {
713  int x, y;
714  int tx, ty, dx, dy;
715  int minp;
716 
717  /* skip the outer squares.. it makes handling alot easier */
718  dx = 0;
719  dy = 0;
720  for (x = 1; x < WEATHERMAPTILESX-1; x++)
721  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
722  minp = PRESSURE_MAX + 1;
723  for (tx = -1; tx < 2; tx++) {
724  for (ty = -1; ty < 2; ty++) {
725  if (!(tx == 0 && ty == 0)) {
726  if (weathermap[x+tx][y+ty].pressure < minp) {
727  minp = weathermap[x+tx][y+ty].pressure;
728  dx = tx;
729  dy = ty;
730  }
731  }
732  }
733  }
734 
735  /* if the wind is strong, the pressure won't decay it completely */
736  if (weathermap[x][y].windspeed > 5 && !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
737  weathermap[x][y].windspeed -= 2*2;
738  } else {
739  weathermap[x][y].winddir = find_dir_2(dx, dy);
741  }
742  // Disrupt the wind where trees are present (a reduction of up to 5 is possible).
743  weathermap[x][y].windspeed -= weathermap[x][y].forestry/20;
744  if (weathermap[x][y].windspeed < 0) {
745  weathermap[x][y].windspeed = 0;
746  }
747  // The wind moves some of the higher pressure to the lower pressure.
748  weathermap[x][y].pressure -= (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
749  weathermap[x+dx][y+dy].pressure += (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
750  }
751 
752  /* now, lets crank on the speed. When surrounding tiles all have
753  the same speed, inc ours. If it's chaos. drop it.
754  */
755  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
756  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
757  minp = 0;
758  for (tx = -1; tx < 2; tx++) {
759  for (ty = -1; ty < 2; ty++) {
760  if (tx != 0 && ty != 0) {
761  if (similar_direction(weathermap[x][y].winddir, weathermap[x+tx][y+ty].winddir)) {
762  minp++;
763  }
764  }
765  }
766  }
767  if (minp > 4) {
768  weathermap[x][y].windspeed++;
769  }
770  if (minp > 6) {
771  weathermap[x][y].windspeed += 2;
772  }
773  if (minp < 2) {
774  weathermap[x][y].windspeed--;
775  }
776  if (weathermap[x][y].windspeed < 0) {
777  weathermap[x][y].windspeed = 0;
778  }
779  }
780  }
781 }
782 
787 static void smooth_pressure() {
788  int x, y;
789  int k;
790 
791  for (k = 0; k < PRESSURE_ROUNDING_ITER; k++) {
792  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
793  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
794  weathermap[x][y].pressure = (weathermap[x][y].pressure*
796  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
797  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
798  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
800  }
801  }
802  for (x = WEATHERMAPTILESX-2; x > 0; x--) {
803  for (y = WEATHERMAPTILESY-2; y > 0; y--) {
804  weathermap[x][y].pressure = (weathermap[x][y].pressure*
806  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
807  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
808  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
810  }
811  }
812  }
813 
814  // Clip to our valid pressure range
815  for (x = 0; x < WEATHERMAPTILESX; x++)
816  for (y = 0; y < WEATHERMAPTILESY; y++) {
817  weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
818  weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
819  }
820 
821 }
822 
827 static void perform_pressure() {
828  int x, y, l, n, j, k, is_storm;
829 
830  /* create random spikes in the pressure */
831  for (l = 0; l < PRESSURE_SPIKES; l++) {
832  x = rndm(0, WEATHERMAPTILESX-1);
833  y = rndm(0, WEATHERMAPTILESY-1);
834  // This goes beyond the valid bounds so that the smoothing proces ends up
835  // making different-sized pressure spikes.
836  n = rndm(600, 1300);
837  weathermap[x][y].pressure = n;
838  // Get close to the edge. But, to make things cleaner, don't go off the edge.
839  if (x >= 2 && y >= 2 && x < WEATHERMAPTILESX-2 && y < WEATHERMAPTILESY-2) {
840  /* occasionally add a storm
841  * and make sure the whole pressure spot is a storm, not just pieces of it
842  *
843  * Also, only try to make storms out of low pressure spikes. 1013 mbar
844  * Is standard pressure at sea level.
845  */
846  is_storm = (n < 1013 && rndm(1, 10) == 1);
847  for (j = x-2; j < x+2; j++) {
848  for (k = y-2; k < y+2; k++) {
849  weathermap[j][k].pressure = n;
850  if (is_storm) {
851  weathermap[j][k].humid = rndm(50, 90);
852  }
853  }
854  }
855  }
856  }
857 
858  for (x = 0; x < WEATHERMAPTILESX; x++) {
859  for (y = 0; y < WEATHERMAPTILESY; y++) {
860  weathermap[x][y].pressure += rndm(-1, 4);
861  }
862  }
863 
864  smooth_pressure();
865 }
866 
874 static const int season_tempchange[HOURS_PER_DAY] = {
875 /* 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 */
876  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};
877 
895 static int real_temperature(int x, int y, const timeofday_t *tod) {
896  int i, temp, adj;
897 
898  // Clear and partly-cloudy skies have a stronger temperature effect
899  // than overcast skies, since clouds create a barrier to heat escaping
900  // and sunlight entering. Super thick clouds add additional buffer.
901  // If adj is set to one, then the weather provides some amount of buffer effect.
902  // This buffer will override the forestry one if it is set.
903  switch (weathermap[x][y].sky) {
904  case SKY_CLEAR:
905  case SKY_LIGHTCLOUD:
906  adj = 0;
907  break;
908  case SKY_HURRICANE:
909  case SKY_BLIZZARD:
910  adj = 2;
911  break;
912  default:
913  adj = 1;
914  break;
915  }
916 
917  /* adjust for time of day */
918  temp = weathermap[x][y].temp;
919  for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
920  temp += season_tempchange[i];
921  /* high amounts of water has a buffering effect on the temp */
922  if (weathermap[x][y].water > 33) {
923  i += weathermap[x][y].water/33;
924  }
925  // Cloudy skies will have a buffering effect on the temperature
926  if (adj >= 1)
927  i += adj;
928  // High amounts of trees also provide some amount of buffering under clear skies
929  else if (weathermap[x][y].forestry > 60) {
930  i++;
931  }
932  }
933  for (i = 0; i <= tod->hour; i++) {
934  temp += season_tempchange[i];
935  if (weathermap[x][y].water > 33) {
936  i += weathermap[x][y].water/33;
937  }
938  // Cloudy skies will have a buffering effect on the temperature
939  if (adj >= 1)
940  i += adj;
941  // High amounts of trees also provide some amount of buffering under clear skies
942  else if (weathermap[x][y].forestry > 60) {
943  i++;
944  }
945  }
946 
947  /* windchill */
948  for (i = 1; i < weathermap[x][y].windspeed; i += i) {
949  temp--;
950  }
951 
952  return temp;
953 }
954 
968 int real_world_temperature(int x, int y, mapstruct *m) {
969  int wx, wy, temp, eleva, elevb, trees;
970  object *op;
971  timeofday_t tod;
972 
973  // Get the time of day for real_temperature
974  // Since real_temperature is sometimes called in a loop, it expects
975  // the time of day to be provided to it instead of calculating it directly.
976  get_tod(&tod);
977 
978  /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
979  worldmap_to_weathermap(x, y, &wx, &wy, m);
980  temp = real_temperature(wx, wy, &tod);
981  if (weathermap[wx][wy].avgelev < 0) {
982  eleva = 0;
983  } else {
984  eleva = weathermap[x][y].avgelev;
985  }
986 
987  op = GET_MAP_OB(m, x, y);
988  if (!op) {
989  return eleva;
990  }
991 
992  elevb = op->elevation;
993  if (elevb < 0) {
994  elevb = 0;
995  }
996  if (elevb > eleva) {
997  elevb -= eleva;
998  temp -= elevb/1000;
999  } else {
1000  elevb = eleva - elevb;
1001  temp += elevb/1000;
1002  }
1003 
1004  // Get localized effects from trees, too.
1005  trees = get_config_tile(x, y, m, forest_list);
1006  // Sparse trees reduce local temp by 1.
1007  // Dense trees raise it by one.
1008  if (trees > 0) {
1009  if (trees < 4)
1010  --temp;
1011  else
1012  ++temp;
1013  }
1014  // And done!
1015  return temp;
1016 }
1017 
1028 static void temperature_calc(const int x, const int y, const timeofday_t *tod) {
1029  int dist, equator, elev, n, trees;
1030  float diff, tdiff;
1031 
1032  // Warmer air has higher pressure than colder air.
1033  // Store the old value for temperature.
1034  int oldtemp = weathermap[x][y].temp, tempdiff;
1035 
1036  equator = (WEATHERMAPTILESX+WEATHERMAPTILESY)/4;
1037  diff = (float)(EQUATOR_BASE_TEMP-POLAR_BASE_TEMP)/(float)equator;
1038  tdiff = (float)SEASONAL_ADJUST/(float)(MONTHS_PER_YEAR/2.0);
1039  equator *= 2;
1040  n = 0;
1041  /* we essentially move the equator during the season */
1042  if (tod->month > (MONTHS_PER_YEAR/2)) { /* EOY */
1043  n -= (tod->month*tdiff);
1044  } else {
1045  n = (MONTHS_PER_YEAR - tod->month)*tdiff;
1046  }
1047  dist = polar_distance(x-n/2, y-n/2, equator);
1048 
1049  /* now we have the base temp, unadjusted for time. Time adjustment
1050  is not recorded on the map, rather, it's done JIT. */
1051  weathermap[x][y].temp = (int)(dist * diff);
1052  /* just scrap these for now, its mostly ocean */
1053  if (weathermap[x][y].avgelev < 0) {
1054  elev = 0;
1055  } else {
1056  // Make sure that higher elevations cause lower temps.
1057  elev = MIN(20000, weathermap[x][y].avgelev)/1000;
1058  }
1059  weathermap[x][y].temp -= elev;
1060 
1067  // Arbitrarily make the cutoff threshold for heat-hold as 60
1068  trees = weathermap[x][y].forestry;
1069  // Dense trees can raise the temperature up to ~3 degrees, per the calculations below.
1070  if (trees >= 60) {
1071  weathermap[x][y].temp += (trees-60)/15;
1072  }
1073  // If not, then we have heat reduction, most effective (~4 degrees) at 30.
1074  else if (trees >= 30){
1075  weathermap[x][y].temp -= (60-trees)/8;
1076  }
1077  else {
1078  weathermap[x][y].temp -= trees/8;
1079  }
1080 
1081  // Now we determine the difference in temperature and adjust the pressure accordingly.
1082  tempdiff = weathermap[x][y].temp - oldtemp;
1083  // The rate (arbitrarily chosen) for temperature-to-pressure change is 1 degrees per millibar
1084  // I'd have to keep track of partial millibar changes if I wanted to be coarser in this.
1085  if (tempdiff != 0)
1086  weathermap[x][y].pressure += tempdiff;
1087 }
1088 
1101 void compute_sky() {
1102  int x, y;
1103  int temp;
1104  int calc, inv_pressure;
1105  float press_root, max_root = sqrt(PRESSURE_MAX-PRESSURE_MIN);
1106  timeofday_t tod;
1107 
1108  // Before we begin the loops, we get the time of day for real_temperature()
1109  get_tod(&tod);
1110 
1111  for (x = 0; x < WEATHERMAPTILESX; x++) {
1112  for (y = 0; y < WEATHERMAPTILESY; y++) {
1113  temp = real_temperature(x, y, &tod);
1114  // Make sure we clip to the allowed pressure range.
1115  inv_pressure = MAX(0, MIN(PRESSURE_MAX-PRESSURE_MIN, (PRESSURE_MAX - weathermap[x][y].pressure)));
1116  // Take the square root. This allows us to have values weighted toward
1117  // producing rain. max_root holds the maximum value this could be.
1118  press_root = sqrt(inv_pressure);
1119  calc = MAX(0, MIN((int)(max_root*100), (int)(press_root * weathermap[x][y].humid)));
1120  // max_root*100 / 7 is the smallest we can feasibly divide by without side effects
1121  // So as long as we divide by a number greater than that, we're good.
1122  // If we divide by smaller, we overrun the sequential weather numbers, and reach FOG and HAIL
1123  // when not encountering their special cases.
1124  calc /= (int)(max_root*100 / 7) + 1;
1125 
1126  // If wind speed is high enough and we have rain, we can add one.
1127  if (calc >= SKY_LIGHT_RAIN && calc < SKY_HURRICANE && weathermap[x][y].windspeed > 30)
1128  ++calc;
1129  // If we are cold enough we have snow.
1130  if (temp <= 0 && calc >= SKY_LIGHT_RAIN)
1131  calc += 10;
1132 
1133  // Keep the old fog/hail generation for now
1134  if (weathermap[x][y].pressure >= 980 && weathermap[x][y].pressure < 1000) {
1135  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
1136  weathermap[x][y].windspeed < 3) {
1137  calc = SKY_FOG; /* rare */
1138  }
1139  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
1140  weathermap[x][y].windspeed > 35) {
1141  calc = SKY_HAIL; /* rare */
1142  }
1143  }
1144  weathermap[x][y].sky = calc;
1145  }
1146  }
1147 }
1148 
1158 static void spin_globe() {
1159  int x, xy, xy_eff;
1160  int buffer_humid;
1161  int buffer_sky;
1162  int buffer_pressure;
1163 
1164  // On each pass, x + y is a constant. We shift down and to the left, and wraparound to the upper right.
1165  // The cornermost tiles by the poles to not move as a result, so we can skip them.
1166  for (xy = 1; xy < WEATHERMAPTILESX + WEATHERMAPTILESY - 1; ++xy) {
1167  // Effective xy is essentially clipped to the end.
1168  // xy-xy_eff is thus the bounds on the other side of the map to care about for wraparound.
1169  xy_eff = MIN(xy, WEATHERMAPTILESX-1);
1170  buffer_humid = weathermap[xy-xy_eff][xy_eff].humid;
1171  buffer_sky = weathermap[xy-xy_eff][xy_eff].sky;
1172  buffer_pressure = weathermap[xy-xy_eff][xy_eff].pressure;
1173  for (x = xy-xy_eff; x < xy_eff; ++x) {
1174  /* Using xy directly here *looks* wrong, but is actually not,
1175  * since x = xy-xy_eff+c, where c is one less than the loop count;
1176  * thus, xy-x = xy-xy+xy_eff-c = xy_eff-c.
1177  * This is within the bounds of the map at all times.
1178  */
1179  weathermap[x][xy-x].humid = weathermap[x+1][xy-x-1].humid;
1180  weathermap[x][xy-x].sky = weathermap[x+1][xy-x-1].sky;
1181  weathermap[x][xy-x].pressure = weathermap[x+1][xy-x-1].pressure;
1182  }
1183  weathermap[xy_eff][xy-xy_eff].humid = buffer_humid;
1184  weathermap[xy_eff][xy-xy_eff].sky = buffer_sky;
1185  weathermap[xy_eff][xy-xy_eff].pressure = buffer_pressure;
1186  }
1187 }
1188 
1202 static int humid_tile(const int x, const int y, const int dark) {
1203  // ox and oy denote the neighbor that is influencing us (due to winds from there)
1204  int ox = x, oy = y, humid, evap, tempeffect, lightness;
1205 
1206  /* find the square the wind is coming from, without going out of bounds */
1207 
1208  if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
1209  if (y != 0) {
1210  oy = y-1;
1211  }
1212  }
1213  if (weathermap[x][y].winddir >= 6) {
1214  if (x != 0) {
1215  ox = x-1;
1216  }
1217  }
1218  if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
1219  if (y < WEATHERMAPTILESY-1) {
1220  oy = y+1;
1221  }
1222  }
1223  if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
1224  if (x < WEATHERMAPTILESX-1) {
1225  ox = x+1;
1226  }
1227  }
1228  // Determine the effect of sunlight on evaporation.
1229  int light = MAX_DARKNESS - dark;
1230  // The sky conditions affect how strong an effect the sunlight has.
1231  switch (weathermap[x][y].sky) {
1232  case SKY_CLEAR:
1233  tempeffect = light*light/4;
1234  break;
1235  case SKY_LIGHTCLOUD:
1236  tempeffect = light*light/5;
1237  break;
1238  case SKY_OVERCAST:
1239  tempeffect = light;
1240  break;
1241  case SKY_LIGHT_RAIN:
1242  case SKY_LIGHT_SNOW:
1243  tempeffect = light*4/5;
1244  break;
1245  case SKY_RAIN:
1246  case SKY_SNOW:
1247  tempeffect = light/2;
1248  break;
1249  case SKY_HEAVY_RAIN:
1250  case SKY_HEAVY_SNOW:
1251  tempeffect = light/3;
1252  break;
1253  case SKY_HURRICANE:
1254  case SKY_BLIZZARD:
1255  case SKY_HAIL:
1256  tempeffect = light/5;
1257  break;
1258  case SKY_FOG:
1259  default:
1260  tempeffect = 0;
1261  }
1262  // Determine the evaporative component contributing to the humidity.
1263  // The amount of water, the temperature, the wind, the pressure, the time of day, the cloudcover, and the previous humidity all affect the evaporation.
1264  // The exact formula is arbitrary, but it gives values that make some sense.
1265  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);
1266  // Don't go negative if temperature gets too low.
1267  evap = MAX(0, evap);
1268  // This is where the magic happens
1269  // If humidity is unstable over time, this is what will need to be tweaked
1270  // (or one of the values it depends on, if not this)
1271  humid = (weathermap[x][y].humid*2 +
1272  (weathermap[ox][oy].humid)*weathermap[ox][oy].windspeed/100 +
1273  // Evaporative components.
1274  evap + weathermap[x][y].forestry/10 + rndm(-3, 7))/
1275  (weathermap[ox][oy].windspeed/100+3)+rndm(-3, 3);
1276  if (humid < 0) {
1277  humid = 0;
1278  }
1279  if (humid > 100) {
1280  humid = 100;
1281  }
1282  return humid;
1283 }
1284 
1290 static void update_humid() {
1291  int x, y, dark = get_world_darkness();
1292 
1293  for (y = 0; y < WEATHERMAPTILESY; y++) {
1294  for (x = 0; x < WEATHERMAPTILESX; x++) {
1295  weathermap[x][y].humid = humid_tile(x, y, dark);
1296  }
1297  }
1298 }
1299 
1304 static void plot_gulfstream() {
1305  int x, y, tx, diroffset, dirdiff, ystart, ydiff, ylimup, ylimlow;
1306 
1307  x = gulf_stream_start;
1308 
1309  // Use the same offset/multiplier formula we used in gulf stream initialization
1310  // to make the code here much cleaner to look at.
1311  if (gulf_stream_direction) {
1312  diroffset = 0;
1313  dirdiff = -1;
1314  // We go from WEATHERMAPTILESY-1 down to 1 for the loop
1315  ystart = WEATHERMAPTILESY-1;
1316  ydiff = -1;
1317  ylimup = WEATHERMAPTILESY;
1318  ylimlow = 0;
1319  }
1320  else {
1321  diroffset = 10;
1322  dirdiff = 1;
1323  // We go from 0 to WEATHERMAPTILESY-2 for the loop
1324  ystart = 0;
1325  ydiff = 1;
1326  ylimup = WEATHERMAPTILESY-1;
1327  ylimlow = -1;
1328  }
1329  for (y = ystart; y > ylimlow && y < ylimup; y += ydiff) {
1330  for (tx = 0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
1331  if (similar_direction(weathermap[x+tx][y].winddir, gulf_stream_dir[tx][y]) && weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5) {
1332  weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
1333  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1334  } else if (gulf_stream_speed[tx][y] > weathermap[x+tx][y].windspeed) {
1335  // Preserve the wind speed of things with a higher speed that the gulf stream itself.
1336  weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
1337  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1338  } // If a storm moves through the gulf stream, it supercedes it with its own high winds.
1339 
1340  if (tx == GULF_STREAM_WIDTH-1) {
1341  switch ((diroffset-gulf_stream_dir[tx][y])*dirdiff) {
1342  case 6: x--; break;
1343  case 7: break;
1344  case 8: x++; break;
1345  }
1346  if (x < 0) {
1347  x++;
1348  }
1350  x--;
1351  }
1352  }
1353  }
1354  }
1355  /* occasionally move the stream
1356  * Arbitrary code from the original implementation says
1357  * 1 in 500 to switch, then 1 in 2 the switch actually is relevant.
1358  *
1359  * So, if we make the outer effect 1 in 1000, we cover both.
1360  */
1361  if (rndm(1, 1000) == 1) {
1362  // Reverse the stream direction.
1364  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
1365  for (y = 0; y < WEATHERMAPTILESY-1; y++) {
1366  // The direction changes here are dir + 4 mod 8, but 8 instead of 0 on those ones.
1367  gulf_stream_dir[tx][y] = (gulf_stream_dir[tx][y] + 4) & 7;
1368  // And we want 8 as a direction instead of 0.
1369  if (gulf_stream_dir[tx][y] == 0)
1370  gulf_stream_dir[tx][y] = 8;
1371  }
1372  }
1373  }
1374  /* Occasionally move the gulf stream starting point.
1375  * Original code had 1 in 25 to try, but 1 in 3 that the move was 0.
1376  *
1377  * So, the chance of actual movement was 2 in 75.
1378  *
1379  * We will use that and redesign the inner offset generation to determine + or - movement.
1380  */
1381  if (rndm(1, 75) <= 2) {
1382  // Only get +1 or -1
1383  gulf_stream_start += 1-2*rndm(0, 1);
1384  // Make sure we don't go off the map.
1387  }
1388  if (gulf_stream_start < 1) {
1390  }
1391  }
1392 }
1393 
1401  int x, y, rain;
1402 
1403  for (x = 0; x < WEATHERMAPTILESX; x++) {
1404  for (y = 0; y < WEATHERMAPTILESY; y++) {
1405  rain = weathermap[x][y].sky;
1406  if (rain >= SKY_LIGHT_SNOW) {
1407  rain -= 10;
1408  }
1409  if (rain > SKY_OVERCAST && rain < SKY_FOG) {
1410  rain -= SKY_OVERCAST;
1411  weathermap[x][y].rainfall += rain;
1412  }
1413  }
1414  }
1415 }
1416 
1429  int x,y, wx, wy;
1430  assert(worldmap_to_weathermap(0, 0, &wx, &wy, m) == 0);
1431  for (x = 0; x < wset.worldmaptilesizex; x++) {
1432  for (y = 0; y < wset.worldmaptilesizey; y++) {
1433  worldmap_to_weathermap(x, y, &wx, &wy, m);
1434  weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1435  }
1436  }
1437 }
1438 
1445  assert(wset.dynamiclevel > 0);
1446  update_humid(); /* Run the humidity updates based on prior pressure, temperature, and wind */
1447  perform_pressure(); /* pressure is the random factor */
1448  smooth_wind(); /* calculate the wind. depends on pressure */
1449  plot_gulfstream();
1450  init_temperature();
1451  spin_globe();
1452  //compute_sky(); This is done in perform_weather
1453 }
1454 
1455 /********************************************************************************************
1456  * Section END -- weather data calculators
1457  ********************************************************************************************/
1458 
1459 /********************************************************************************************
1460  * Section -- Weather effect methods
1461  * Functions to provide weather effects.
1462  * This includes precipitation, puddles, ice on water, snowfall, growing plants,
1463  * defacing the world (since some comments seemed to imply that one was broken, and I never changed it)
1464  ********************************************************************************************/
1465 
1481 static void do_precipitation(mapstruct * const m, const int x, const int y, const int temp, const int sky) {
1482  // Do falling rain/snow here
1483  const archetype *at = NULL;
1484  object *tmp = NULL;
1485  int avoid = 0;
1486  int pct_precip = 0; // 0-100: percent tiles with precipitation
1487  switch (sky) {
1488  case SKY_LIGHT_RAIN:
1489  case SKY_LIGHT_SNOW:
1490  pct_precip = 10;
1491  break;
1492  case SKY_RAIN:
1493  case SKY_SNOW:
1494  pct_precip = 30;
1495  break;
1496  case SKY_HEAVY_RAIN:
1497  case SKY_HEAVY_SNOW:
1498  pct_precip = 60;
1499  break;
1500  case SKY_HURRICANE:
1501  case SKY_BLIZZARD:
1502  pct_precip = 99;
1503  break;
1504  }
1505  // TODO: Move these globally? Pretty sure they will be in a consistent spot in memory for execution duration.
1506  sstring snowc = find_string("snow_c");
1507  sstring rain = find_string("rain");
1508  if (rndm(0, 99) + pct_precip >= 100) {
1509  // Do our weather inserts here.
1510  // t < -2 == always snow
1511  // 2 >= t > -2 == rain/snow mix
1512  // t > 2 == always rain
1513  if (temp < -2 || (temp <= 2 && rndm(0, 2+temp) - 2 <= 0)) {
1514  at = find_archetype("snow_c");
1515  }
1516  else {
1517  at = find_archetype("rain");
1518  }
1519  if (at) {
1520  // Make sure we don't stack rains/snow ad nauseam on tiles. Also allow to switch the precip on the tile.
1521  tmp = GET_MAP_OB(m, x, y);
1522  avoid = 0;
1523  while (tmp) {
1524  if (tmp->arch == at) {
1525  avoid++;
1526  break;
1527  }
1528  else if ((tmp->arch->name == snowc && at->name == rain) ||
1529  (tmp->arch->name == rain && at->name == snowc)) {
1530  // Remove the wrong precipitation
1531  object_remove(tmp);
1532  object_free(tmp, 0);
1533  }
1534  tmp = tmp->above;
1535  }
1536  if (!avoid)
1538  }
1539  }
1540  else {
1541  // Look for a rain/snow on this tile and remove it.
1542  tmp = GET_MAP_OB(m, x, y);
1543  while (tmp) {
1544  if (tmp->arch->name == rain || tmp->arch->name == snowc) {
1545  object_remove(tmp);
1546  object_free(tmp, 0);
1547  }
1548  tmp = tmp->above;
1549  }
1550  }
1551 }
1552 
1559 static void do_map_precipitation(mapstruct * const m) {
1560  if (!m)
1561  return;
1562  // Make sure it has a weather map.
1563  int x, y, temp, sky, wx, wy;
1564  if (worldmap_to_weathermap(0, 0, &x, &y, m) != 0)
1565  return;
1566  // Now we re-do the precipitation on the map.
1567  for (x = 0; x < m->width; ++x)
1568  for (y = 0; y < m->height; ++y) {
1569  worldmap_to_weathermap(x, y, &wx, &wy, m);
1570  temp = weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1571  sky = weathermap[wx][wy].sky;
1572  do_precipitation(m, x, y, temp, sky);
1573  }
1574 }
1575 
1582 static void let_it_snow(mapstruct * const m) {
1583  int x, y, i, wx, wy;
1584  int nx, ny, j, d;
1585  int avoid, temp, sky, gotsnow, found, nodstk;
1586  object *ob, *tmp, *oldsnow, *topfloor;
1587  archetype *at, *doublestack, *doublestack2;
1588 
1589  sstring dungmag = find_string("dungeon_magic");
1590 
1591  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1592  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1593  /* jitter factor */
1594  if (rndm(0, 2) > 0) {
1595  x = y = d = -1;
1596  while (OUT_OF_REAL_MAP(m, x, y)) {
1597  d++;
1598  j = rndm(1, 8);
1599  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1600  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1601  if (d > 15) {
1602  x = nx;
1603  y = ny;
1604  }
1605  }
1606  } else {
1607  x = nx;
1608  y = ny;
1609  }
1610  /* we use the unjittered coordinates */
1611  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1612  ob = NULL;
1613  at = NULL;
1614  doublestack = NULL;
1615  /* this will definately need tuning */
1616  avoid = 0;
1617  gotsnow = 0;
1618  nodstk = 0;
1619  /*temp = real_world_temperature(x, y, m);*/
1620  temp = weathermap[wx][wy].realtemp;
1621  sky = weathermap[wx][wy].sky;
1622  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG) {
1623  sky += 10; /*let it snow*/
1624  }
1625  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1626  if (!avoid) {
1627  if (sky >= SKY_LIGHT_SNOW && sky < SKY_HEAVY_SNOW) {
1628  at = find_archetype("snow5");
1629  }
1630  if (sky >= SKY_HEAVY_SNOW) {
1631  at = find_archetype("snow4");
1632  }
1633  if (sky >= SKY_LIGHT_SNOW) {
1634  /* the bottom floor of scorn is not IS_FLOOR */
1635  topfloor = NULL;
1636  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp, tmp = tmp->above) {
1637  if (tmp->arch->name != dungmag) {
1638  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1639  break;
1640  }
1641  }
1642  }
1643  /* topfloor should now be the topmost IS_FLOOR=1 */
1644  if (topfloor == NULL) {
1645  continue;
1646  }
1647  if (tmp != NULL) {
1648  nodstk++;
1649  }
1650  /* something is wrong with that sector. just skip it */
1651  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1652  if (check_replace_match(topfloor, repl)) {
1653  if (repl->special_snow != NULL) {
1654  at = repl->special_snow;
1655  }
1656  if (repl->doublestack_arch != NULL && !nodstk) {
1657  doublestack = repl->doublestack_arch;
1658  }
1659  break;
1660  }
1661  }
1662  }
1663  if (gotsnow && at) {
1664  if (oldsnow->arch == at) {
1665  at = NULL;
1666  } else {
1667  object_remove(oldsnow);
1668  object_free(oldsnow,0);
1669  tmp = GET_MAP_OB(m, x, y);
1670  /* clean up the trees we put over the snow */
1671  doublestack2 = NULL;
1672  if (tmp) {
1673  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1674  if (repl->doublestack_arch == NULL) {
1675  continue;
1676  }
1677  if (check_replace_match(tmp, repl)) {
1678  tmp = tmp->above;
1679  doublestack2 = repl->doublestack_arch;
1680  break;
1681  }
1682  }
1683  }
1684  if (tmp != NULL && doublestack2 != NULL) {
1685  if (tmp->arch == doublestack2) {
1686  object_remove(tmp);
1687  object_free(tmp,0);
1688  }
1689  }
1690  }
1691  }
1692  if (at != NULL) {
1694  if (doublestack != NULL) {
1695  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1696  }
1697  }
1698  }
1699  if (temp > 8 && GET_MAP_OB(m, x, y) != NULL) {
1700  /* melt some snow */
1701  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1702  avoid = 0;
1703  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1704  if (repl->special_snow == NULL) {
1705  continue;
1706  }
1707 
1708  if (tmp->arch == repl->special_snow) {
1709  avoid++;
1710  }
1711  if (avoid) {
1712  break;
1713  }
1714  }
1715  if (avoid) {
1716  /* replace snow with a big puddle */
1717  /* If it is a floor tile we're melting, try to place earth there to have *some* floor.
1718  * Don't mark as overlay, or it will be stuck there forever, rather than until the map resets.
1719  */
1720  if (!tmp->below || QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1721  at = find_archetype("earth");
1722  if (at)
1724  }
1725  object_remove(tmp);
1726  object_free(tmp,0);
1727  tmp = GET_MAP_OB(m, x, y);
1728  at = NULL; // Reset what arch we are looking at
1729  if (tmp) {
1730  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1731  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1732  if (tmp->arch->name == melt->tile) {
1733  at = melt->special_snow;
1734  }
1735  }
1736  }
1737  // Default
1738  if (!at) {
1739  at = find_archetype("rain5_weather");
1740  }
1741  if (at != NULL) {
1743  }
1744  }
1745  }
1746  }
1747  /* woo it's cold out */
1748  if (temp < -8) {
1749  avoid = 0;
1750  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1751  if (!strcasecmp(tmp->name, "ice")) {
1752  avoid--;
1753  }
1754  }
1755  tmp = GET_MAP_OB(m, x, y);
1756  if (tmp && (!strcasecmp(tmp->name, "sea"))) {
1757  avoid++;
1758  } else if (tmp && (!strcasecmp(tmp->name, "sea1"))) {
1759  avoid++;
1760  } else if (tmp && (!strcasecmp(tmp->name, "deep sea"))) {
1761  avoid++;
1762  } else if (tmp && (!strcasecmp(tmp->name, "shallow sea"))) {
1763  avoid++;
1764  }
1765  if (avoid > 0) {
1766  at = find_archetype("ice");
1768  }
1769  }
1770  }
1771  }
1772 }
1773 
1780 static void singing_in_the_rain(mapstruct * const m) {
1781  int x, y, i, wx, wy;
1782  int nx, ny, d, j;
1783  int avoid, temp, sky, gotsnow, /*found,*/ nodstk;
1784  object *ob, *tmp, *oldsnow, *topfloor;
1785  archetype *at, *doublestack, *doublestack2;
1786 
1787  sstring dungmag = find_string("dungeon_magic");
1788 
1789  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1790  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1791  /* jitter factor */
1792  if (rndm(0, 2) > 0) {
1793  x = y = d = -1;
1794  while (OUT_OF_REAL_MAP(m, x, y)) {
1795  // Save some processing when d > 15
1796  if (++d > 15) {
1797  x = nx;
1798  y = ny;
1799  }
1800  else {
1801  j = rndm(1, 8);
1802  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1803  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1804  }
1805  }
1806  } else {
1807  x = nx;
1808  y = ny;
1809  }
1810  /* we use the unjittered coordinates */
1811  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1812  ob = NULL;
1813  at = NULL;
1814  doublestack = NULL;
1815  avoid = 0;
1816  gotsnow = 0;
1817  nodstk = 0;
1818  /*temp = real_world_temperature(x, y, m);*/
1819  temp = weathermap[wx][wy].realtemp;
1820  sky = weathermap[wx][wy].sky;
1821  /* Handle adding precipitation here. */
1822  do_precipitation(m, x, y, temp, sky);
1823 
1824  /* it's probably allready snowing */
1825  if (temp < 0) {
1826  continue;
1827  }
1828 
1829  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1830  if (!avoid) {
1831  tmp = GET_MAP_OB(m, x, y);
1832  if (tmp) {
1833  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1834  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1835  if (tmp->arch->name == melt->tile) {
1836  at = melt->special_snow;
1837  }
1838  }
1839  if (at)
1840  break;
1841  }
1842  if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1843  switch (rndm(0, SKY_HAIL-sky)) {
1844  case 0: at = find_archetype("rain1_weather"); break;
1845  case 1: at = find_archetype("rain2_weather"); break;
1846  default: at = NULL; break;
1847  }
1848  }
1849  if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE) {
1850  switch (rndm(0, SKY_HAIL-sky)) {
1851  case 0: at = find_archetype("rain3_weather"); break;
1852  case 1: at = find_archetype("rain4_weather"); break;
1853  case 2: at = find_archetype("rain5_weather"); break;
1854  default: at = NULL; break;
1855  }
1856  }
1857  /* the bottom floor of scorn is not IS_FLOOR */
1858  topfloor = NULL;
1859  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp,tmp = tmp->above) {
1860  if (tmp->arch->name != dungmag) {
1861  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1862  break;
1863  }
1864  }
1865  }
1866  /* topfloor should now be the topmost IS_FLOOR=1 */
1867  if (topfloor == NULL) {
1868  continue;
1869  }
1870  if (tmp != NULL) {
1871  nodstk++;
1872  }
1873  /* something is wrong with that sector. just skip it */
1874  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1875  if (check_replace_match(topfloor, repl)) {
1876  if (repl->doublestack_arch != NULL && !nodstk) {
1877  doublestack = repl->doublestack_arch;
1878  }
1879  break;
1880  }
1881  }
1882  if (gotsnow && at) {
1883  if (oldsnow->arch == at) {
1884  at = NULL;
1885  } else {
1886  tmp = GET_MAP_OB(m, x, y);
1887  object_remove(oldsnow);
1888  /* clean up the trees we put over the snow */
1889  doublestack2 = NULL;
1890  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1891  if (repl->doublestack_arch == NULL) {
1892  continue;
1893  }
1894  if (check_replace_match(tmp, repl)) {
1895  tmp = tmp->above;
1896  doublestack2 = repl->doublestack_arch;
1897  break;
1898  }
1899  }
1900  object_free(oldsnow,0);
1901  if (tmp != NULL && doublestack2 != NULL) {
1902  if (tmp->arch == doublestack2) {
1903  object_remove(tmp);
1904  object_free(tmp,0);
1905  }
1906  }
1907  }
1908  }
1909  if (at != NULL) {
1911  if (doublestack != NULL) {
1912  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1913  }
1914  }
1915  }
1916  /* Things evaporate fast in the heat */
1917  if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1918  /* evaporate */
1919  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1920  // Find a tile to evaporate
1921  weather_replace_t *evap;
1922  for (evap = weather_evaporate; evap; evap = evap->next) {
1923  if (tmp->arch->name == evap->tile)
1924  break;
1925  }
1926  // If we found it, then evaporate it
1927  if (evap) {
1928  object_remove(tmp);
1929  object_free(tmp,0);
1930  if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0) {
1931  weathermap[wx][wy].humid++;
1932  }
1933  // If the evaporation is done, clean up the doublestack on this tile.
1934  if (evap->special_snow == NULL) {
1935  tmp = GET_MAP_OB(m, x, y);
1936  /* clean up the trees we put over the rain */
1937  doublestack2 = NULL;
1938  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1939  if (repl->doublestack_arch == NULL) {
1940  continue;
1941  }
1942  if (check_replace_match(tmp, repl)) {
1943  tmp = tmp->above;
1944  doublestack2 = repl->doublestack_arch;
1945  break;
1946  }
1947  }
1948  if (tmp != NULL && doublestack2 != NULL) {
1949  if (tmp->arch == doublestack2) {
1950  object_remove(tmp);
1951  object_free(tmp,0);
1952  }
1953  }
1954  }
1955  else {
1956  // Apply the replacement puddle
1958  }
1959  break;
1960  }
1961  }
1962  }
1963  }
1964  }
1965 }
1966 
1973 static void plant_a_garden(mapstruct *const m) {
1974  int x, y, i, wx, wy;
1975  int avoid, two, temp, sky, gotsnow, found, days;
1976  object *ob, *tmp;
1977  archetype *at;
1978 
1980  for (x = 0; x < wset.worldmaptilesizex; x++) {
1981  for (y = 0; y < wset.worldmaptilesizey; y++) {
1982  (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1983  ob = NULL;
1984  at = NULL;
1985  avoid = 0;
1986  two = 0;
1987  gotsnow = 0;
1988  /*temp = real_world_temperature(x, y, m);*/
1989  temp = weathermap[wx][wy].realtemp;
1990  sky = weathermap[wx][wy].sky;
1991  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1992  if (!avoid) {
1993  found = 0;
1994  for (i = 0; weather_grow[i].herb != NULL; i++) {
1995  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1996  if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0) {
1997  continue;
1998  }
1999 
2000  /* we found there is a herb here allready */
2001  found++;
2002  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2003  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
2004  weathermap[wx][wy].humid < weather_grow[i].humin ||
2005  weathermap[wx][wy].humid > weather_grow[i].humax ||
2006  temp < weather_grow[i].tempmin ||
2007  temp > weather_grow[i].tempmax ||
2008  rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
2009  /* the herb does not belong, randomly delete
2010  herbs to prevent overgrowth. */
2011  object_remove(tmp);
2012  object_free(tmp,0);
2013  break;
2014  }
2015  }
2016  /* don't doublestack herbs */
2017  if (found) {
2018  continue;
2019  }
2020  /* add a random factor */
2021  if (rndm(1, weather_grow[i].random) != 1) {
2022  continue;
2023  }
2024  /* we look up through two tiles for a matching tile */
2025  if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
2026  if (strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_grow[i].tile) != 0) {
2027  if (GET_MAP_OB(m, x, y)->above != NULL) {
2028  if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name, weather_grow[i].tile) != 0) {
2029  continue;
2030  }
2031  } else {
2032  continue;
2033  }
2034  }
2035  }
2036  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2037  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax) {
2038  continue;
2039  }
2040  if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2041  weathermap[wx][wy].humid > weather_grow[i].humax) {
2042  continue;
2043  }
2044  if (temp < weather_grow[i].tempmin ||
2045  temp > weather_grow[i].tempmax) {
2046  continue;
2047  }
2048  if ((!GET_MAP_OB(m, x, y)) ||
2049  GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2050  GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax) {
2051  continue;
2052  }
2053  /* we got this far.. must be a match */
2054  at = find_archetype(weather_grow[i].herb);
2055  break;
2056  }
2057  if (at != NULL) {
2058  /* XXX is overlay_floor right? maybe.. */
2060  }
2061  }
2062  }
2063  }
2064 }
2065 
2072 static void change_the_world(mapstruct * const m) {
2073  int x, y, i, wx, wy;
2074  int nx, ny, j, d;
2075  int avoid, two, temp, sky, gotsnow, found, days;
2076  object *ob, *tmp, *doublestack;
2077  archetype *at, *dat;
2078 
2080  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
2081  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
2082  /* jitter factor */
2083  if (rndm(0, 2) > 0) {
2084  x = y = d = -1;
2085  while (OUT_OF_REAL_MAP(m, x, y)) {
2086  d++;
2087  j = rndm(1, 8);
2088  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2089  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2090  if (d > 15) {
2091  x = nx;
2092  y = ny;
2093  }
2094  }
2095  } else {
2096  x = nx;
2097  y = ny;
2098  }
2099  /* we use the unjittered coordinates */
2100  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2101  ob = NULL;
2102  at = NULL;
2103  dat = NULL;
2104  avoid = 0;
2105  two = 0;
2106  gotsnow = 0;
2107  /*temp = real_world_temperature(x, y, m);*/
2108  temp = weathermap[wx][wy].realtemp;
2109  sky = weathermap[wx][wy].sky;
2110  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2111  if (!avoid) {
2112  for (i = 0; weather_tile[i].herb != NULL; i++) {
2113  found = 0;
2114  doublestack = NULL;
2115  if (GET_MAP_OB(m, x, y)) {
2116  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2117  if (weather_tile[i].tile != NULL) {
2118  if (strcmp(tmp->arch->name, weather_tile[i].tile) == 0) {
2119  doublestack = tmp;
2120  continue;
2121  }
2122  }
2123  if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0) {
2124  continue;
2125  }
2126 
2127  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2128  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2129  weathermap[wx][wy].humid < weather_tile[i].humin ||
2130  weathermap[wx][wy].humid > weather_tile[i].humax ||
2131  temp < weather_tile[i].tempmin ||
2132  temp > weather_tile[i].tempmax) {
2133  object_remove(tmp);
2134  object_free(tmp,0);
2135  if (doublestack) {
2136  object_remove(doublestack);
2137  object_free(doublestack,0);
2138  }
2139  break;
2140  } else {
2141  found++; /* there is one here allready. leave it */
2142  break;
2143  }
2144  }
2145  }
2146  if (found) {
2147  break;
2148  }
2149 
2150  /* add a random factor */
2151  if (rndm(1, weather_tile[i].random) != 1) {
2152  continue;
2153  }
2154  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2155  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax) {
2156  continue;
2157  }
2158  if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2159  weathermap[wx][wy].humid > weather_tile[i].humax) {
2160  continue;
2161  }
2162  if (temp < weather_tile[i].tempmin ||
2163  temp > weather_tile[i].tempmax) {
2164  continue;
2165  }
2166  if ( (!GET_MAP_OB(m, x, y)) ||
2167  GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2168  GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax) {
2169  continue;
2170  }
2171  /* we got this far.. must be a match */
2172  if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_tile[i].herb) == 0) {
2173  break; /* no sense in doubling up */
2174  }
2175  at = find_archetype(weather_tile[i].herb);
2176  break;
2177  }
2178  if (at != NULL) {
2179  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) {
2180  dat = find_archetype(weather_tile[i].tile);
2181  }
2182  if (dat != NULL) {
2184  }
2185  if (gotsnow == 0) {
2187  }
2188  }
2189  }
2190  }
2191  }
2192 }
2193 
2206 static void weather_effect(mapstruct * const m) {
2207  int wx, wy, x, y;
2208 
2209  /* if the dm shut off weather, go home */
2210  if (wset.dynamiclevel < 1) {
2211  return;
2212  }
2213 
2214  if (!m->outdoor) {
2215  return;
2216  }
2217 
2218  x = 0;
2219  y = 0;
2220  /* for now, just bail if it's not the worldmap */
2221  if (worldmap_to_weathermap(x, y, &wx, &wy, m) != 0) {
2222  return;
2223  }
2224 
2225  /*First, calculate temperature*/
2227  /* we change the world first, if needed */
2228  if (wset.dynamiclevel >= 5) {
2230  }
2231  if (wset.dynamiclevel >= 2) {
2232  let_it_snow(m);
2234  }
2235  if (wset.dynamiclevel >= 3) {
2236  plant_a_garden(m);
2237  }
2238 }
2239 
2249  mapstruct *m;
2250  char filename[MAX_BUF];
2251  FILE *fp;
2252 
2253  if (!wset.dynamiclevel) {
2254  return;
2255  }
2256 
2257  /* move right to left, top to bottom */
2259  wmperformstartx = 0;
2261  wmperformstarty = 0;
2262  }
2263  }
2264 
2265  // Whenever we load a map for effects, recalculate the weather.
2266  // Do this before the actual map load so that precipitation is done with the new sky computation rather than the old
2267  compute_sky();
2268 
2269  snprintf(filename, sizeof(filename), "world/world_%d_%d", wmperformstartx+settings.worldmapstartx, wmperformstarty+settings.worldmapstarty);
2270 
2271  m = ready_map_name(filename, 0);
2272  if (m == NULL) {
2273  return; /* hrmm */
2274  }
2275 
2276  // Run weather effects here.
2277  // We do this here rather than on any map load so that the weather effects remain consistent.
2278  weather_effect(m);
2279 
2280  /* done */
2281  save_map(m, SAVE_MODE_OVERLAY); /* write the overlay */
2282  m->in_memory = MAP_IN_MEMORY; /*reset this*/
2283  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings.localdir);
2284  if ((fp = fopen(filename, "w")) == NULL) {
2285  LOG(llevError, "Cannot open %s for writing\n", filename);
2286  return;
2287  }
2288 
2289  if (players_on_map(m, TRUE) == 0) {
2290  delete_map(m);
2291  }
2292 
2293  fprintf(fp, "%d %d", wmperformstartx, wmperformstarty);
2294  fclose(fp);
2295 }
2296 
2318 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) {
2319  // If we're inside, the weather can't get us :P
2320  if (!m || !m->outdoor)
2321  return 0;
2322  // First, we get the weathermap for this location
2323  int nx, ny;
2324  if (worldmap_to_weathermap(x, y, &nx, &ny, m))
2325  return 0;
2326  int windspeed = weathermap[nx][ny].windspeed;
2327  int is_fly = move_type & MOVE_FLYING;
2328  // If not flying, then strong winds are needed to affect you.
2329  if (!is_fly)
2330  windspeed -= 20;
2331  // If no wind, then no push.
2332  if (windspeed <= 0)
2333  return 0;
2334  // Reduce effect from carrying more stuff.
2335  // Also, being on the ground makes the same wind increase affect you less as well.
2336  // Higher strength characters can also resist being blown by the wind when on the ground.
2337  if (!is_fly)
2338  wt /= (10000 * (stats && stats->Str) ? stats->Str : 1);
2339  // When flying, we care about Dex over Str.
2340  else
2341  wt /= (20000 * (stats && stats->Dex) ? stats->Dex : 1);
2342  // Massive things are pushed around less easily.
2343  if (windspeed*2 < wt)
2344  return 0;
2345  // The push will not happen every try. The greater the wind, the more often it succeeds.
2346  // Also, the lighter the object, the more often it succeeds
2347  // We do two rolls because it normalizes the effects better than a single roll.
2348  if (rndm(0, windspeed)+rndm(0, windspeed) < wt)
2349  return 0;
2350  // winddir is the direction the wind is coming from.
2351  // so we need to reverse it to push where the wind is going to.
2352  return absdir(weathermap[nx][ny].winddir+4);
2353 }
2354 
2355 /********************************************************************************************
2356  * Section END -- weather effect methods
2357  ********************************************************************************************/
2358 
2359 /********************************************************************************************
2360  * Section -- Initializations
2361  * Functions to load in config for determining certain weathermap attributes,
2362  * functions to initialize missing weathermap attributes,
2363  * and their helper functions.
2364  ********************************************************************************************/
2365 
2386 static int init_config_vals(const Settings *settings, const char *conf_filename, DensityConfig **list) {
2387  char filename[MAX_BUF], *line, *name;
2388  BufferReader *bfr;
2389  int found, is_obj_name, tree_count;
2390 
2391  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2392  // Open the file with the buffer reader.
2393  bfr = bufferreader_init_from_file(NULL, filename,
2394  "init_config_vals: Could not open file %s. No forestry data is defined. %s\n",
2395  llevError);
2396  if (bfr == NULL) {
2397  // The error was printed by bufferreader_init_from_file already, so just bail.
2398  return 1;
2399  }
2400  // Now we read in from the buffer.
2401  while ((line = bufferreader_next_line(bfr)) != NULL) {
2402  // Now we parse the line
2403  // Start by examining the first character.
2404  switch (*line) {
2405  // Ignore empty lines and comment lines (denoted by # at front)
2406  case '\0':
2407  case '#':
2408  // Handling \r means Windows should work right, too.
2409  case '\r':
2410  case '\n':
2411  break;
2412  default:
2413  // Actually parse the line
2414  // Format is like this:
2415  // name, (0 if arch, 1 if object name), # trees
2416  // [spaces are expected after commas]
2417 
2418  // sscanf on strings is wonky (it always reads to whitespace),
2419  // so I'm gonna do it by just nabbing part of the buffer.
2420  name = line; // Each line starts with name
2422  if (line == NULL) {
2423  LOG(llevError, "init_config_vals: Malformed name entry in %s, line %ld.\n",
2424  filename, bufferreader_current_line(bfr));
2425  // Move on to the next line and hope it is fine.
2426  continue;
2427  }
2428 
2429  found = sscanf(line, "%d, %d\n", &is_obj_name, &tree_count);
2430  if (found != 2) {
2431  // Print an error for the malformed line
2432  LOG(llevError, "init_config_vals: Malformed forestry entry in %s, line %ld.\n",
2433  filename, bufferreader_current_line(bfr));
2434  }
2435  else {
2436  // Add a struct to the list.
2437  DensityConfig *frst = (DensityConfig *)malloc(sizeof(DensityConfig));
2438  if (!frst) {
2440  }
2441  // Shared strings are friend, not food
2442  frst->name = add_string(name);
2443  frst->is_obj = is_obj_name;
2444  frst->value_density = tree_count;
2445  // Attach to front of list, since order doesn't matter much, if at all.
2446  frst->next = *list;
2447  *list = frst;
2448  }
2449  }
2450  }
2451  bufferreader_destroy(bfr);
2452  return 0;
2453 }
2454 
2471 static int init_weatheravoid(const Settings *settings, const char *conf_filename, weather_avoids_t **wa) {
2472  char filename[MAX_BUF], *line, *name;
2473  BufferReader *bfr;
2474  int found, is_effect;
2475 
2476  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2477  // Open the file with the buffer reader.
2478  bfr = bufferreader_init_from_file(NULL, filename,
2479  "init_weatheravoid: Could not open file %s. No weatheravoid data is defined. %s\n", llevError);
2480  // If the bufferreader failed, it return NULL and printed an error, so just bail if failure.
2481  if (bfr == NULL)
2482  return 1;
2483  // Now we read in from the buffer.
2484  while ((line = bufferreader_next_line(bfr)) != NULL) {
2485  // Now we parse the line
2486  // Start by examining the first character.
2487  switch (*line) {
2488  // Ignore empty lines and comment lines (denoted by # at front)
2489  case '\0':
2490  case '#':
2491  // Handling \r means Windows should work right, too.
2492  case '\r':
2493  case '\n':
2494  break;
2495  default:
2496  // Actually parse the line
2497  // Format is like this:
2498  // name, (1 if weather effect, 0 if regular tile)
2499  // [spaces are expected after commas]
2500 
2501  // sscanf on strings is wonky (it always reads to whitespace),
2502  // so I'm gonna do it by just nabbing part of the buffer.
2503  name = line; // Each line starts with name
2505  if (line == NULL) {
2506  LOG(llevError, "init_weatheravoid: Malformed name entry in %s, line %ld.\n",
2507  filename, bufferreader_current_line(bfr));
2508  // Move on to the next line and hope it is fine.
2509  continue;
2510  }
2511 
2512  found = sscanf(line, "%d\n", &is_effect);
2513  if (found != 1) {
2514  // Print an error for the malformed line
2515  LOG(llevError, "init_weatheravoid: Malformed effect flag entry in %s, line %ld.\n",
2516  filename, bufferreader_current_line(bfr));
2517  }
2518  else {
2519  // Add a struct to the list.
2520  weather_avoids_t *frst = (weather_avoids_t *)malloc(sizeof(weather_avoids_t));
2521  if (!frst) {
2523  }
2524  // Shared strings are friend, not food
2525  frst->name = add_string(name);
2526  frst->snow = is_effect;
2527  // Attach to front of list, since order doesn't matter much, if at all.
2528  frst->next = *wa;
2529  *wa = frst;
2530  }
2531  }
2532  }
2533  bufferreader_destroy(bfr);
2534  return 0;
2535 }
2536 
2553 static int init_weather_replace(const Settings *settings, const char *conf_filename, weather_replace_t **list) {
2554  char filename[MAX_BUF], *line, *name, *repl, *doublestack;
2555  BufferReader *bfr;
2556  int found, is_arch;
2557 
2558  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2559  // Open the file with the buffer reader.
2560  bfr = bufferreader_init_from_file(NULL, filename, "init_weather_replace: Could not open file %s. No weather replace data is defined. %s\n", llevError);
2561  // If failed, we already printed an error.
2562  if (bfr == NULL)
2563  return 1;
2564  // Now we read in from the buffer.
2565  while ((line = bufferreader_next_line(bfr)) != NULL) {
2566  // Now we parse the line
2567  // Start by examining the first character.
2568  switch (*line) {
2569  // Ignore empty lines and comment lines (denoted by # at front)
2570  case '\0':
2571  case '#':
2572  // Handling \r means Windows should work right, too.
2573  case '\r':
2574  case '\n':
2575  break;
2576  default:
2577  // Actually parse the line
2578  // Format is like this:
2579  // name, replacement tile arch name, additional tile arch name (or NONE if not), (1 if arch name, 0 if object name)
2580  // [spaces are expected after commas]
2581 
2582  // sscanf on strings is wonky (it always reads to whitespace),
2583  // so I'm gonna do it by just nabbing part of the buffer.
2584  name = line; // Each line starts with name
2586  if (line == NULL) {
2587  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2588  LOG(llevError, "init_weather_replace: Malformed name entry in %s, line %ld.\n",
2589  filename, bufferreader_current_line(bfr));
2590  // Move on to the next line and hope it is fine.
2591  continue;
2592  }
2593 
2594  repl = line; // Each line starts with name
2596  if (line == NULL) {
2597  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2598  LOG(llevError, "init_weather_replace: Malformed replacement entry in %s, line %ld.\n",
2599  filename, bufferreader_current_line(bfr));
2600  // Move on to the next line and hope it is fine.
2601  continue;
2602  }
2603 
2604  doublestack = line; // Each line starts with name
2606  if (line == NULL) {
2607  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2608  LOG(llevError, "init_weather_replace: Malformed doublestack entry in %s, line %ld.\n",
2609  filename, bufferreader_current_line(bfr));
2610  // Move on to the next line and hope it is fine.
2611  continue;
2612  }
2613 
2614  found = sscanf(line, "%d\n", &is_arch);
2615  if (found != 1) {
2616  // Print an error for the malformed line
2617  LOG(llevError, "init_weatheravoid: Malformed archetype/object flag entry in %s, line %ld.\n",
2618  filename, bufferreader_current_line(bfr));
2619  }
2620  else {
2621  // Add a struct to the list.
2622  weather_replace_t *frst = (weather_replace_t *)malloc(sizeof(weather_replace_t));
2623  if (!frst) {
2625  }
2626  // Shared strings are friend, not food
2627  frst->tile = add_string(name);
2628  // Some replcement definitions can have NONE here to denote removal
2629  if (strcmp(repl, "NONE") == 0)
2630  frst->special_snow = NULL;
2631  else
2632  frst->special_snow = find_archetype(repl);
2633  // if doublestack is NONE, then set the arch to NULL
2634  if (strcmp(doublestack, "NONE") == 0)
2635  frst->doublestack_arch = NULL;
2636  else
2637  frst->doublestack_arch = find_archetype(doublestack);
2638  frst->arch_or_name = is_arch;
2639  // Attach to front of list, since order doesn't matter much, if at all.
2640  frst->next = *list;
2641  *list = frst;
2642  }
2643  }
2644  }
2645  bufferreader_destroy(bfr);
2646  return 0;
2647 }
2648 
2668 static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int * const tx, int * const ty) {
2669  char mapname[MAX_BUF];
2670  if (!m || !tx || !ty)
2671  return -1;
2672  // Now we do what was wanted.
2673  weathermap_to_worldmap_corner(x, y, tx, ty, dir, mapname, sizeof(mapname));
2674  *m = mapfile_load(mapname, 0);
2675  if (*m == NULL) {
2676  return -1;
2677  }
2678 
2679  int res = load_overlay_map(mapname, *m);
2680  if (res != 0) {
2681  return -1;
2682  }
2683  return 0;
2684 }
2685 
2711 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) {
2712  if (!m || !water || !elev || !trees)
2713  return -1;
2714  object *ob = GET_MAP_OB(m, x, y), *obtmp;
2715  if (ob) {
2716  DensityConfig *tmp;
2717  if (QUERY_FLAG(ob, FLAG_IS_WATER)) {
2718  (*water)++;
2719  }
2720  // Deserts will reduce the humidity/precipitation in the spaces they exist in.
2721  // Since the config entries are all negative, we can add the value here.
2722  (*water) += get_config_tile(x, y, m, water_list);
2723 
2724  // Handle forestry
2725  (*trees) += get_config_tile(x, y, m, forest_list);
2726 
2727  (*elev) += ob->elevation;
2728  }
2729  return 0;
2730 }
2731 
2742 static void init_humid_elev(const Settings *settings) {
2743  // Variable uses:
2744  // x, y: weathermap tile being affected
2745  // tx, ty: the in-map coordinates of the corner of the weathermap we are calculating.
2746  // nx, ny: coordinates within the weathermap
2747  // ax, ay: the location on the map we are examining
2748  // j: temporary variable for when a specific ny needs to be initialized from within a loop.
2749  int x, y, tx, ty, nx, ny, ax, ay, j;
2750  // spwtx, spwty: The number of tiles in a single weathermap in the associated (x or y) direction
2753  int64_t elev;
2754  int water, space, trees;
2755  mapstruct *m;
2756 
2757  /* handling of this is kinda nasty. For that reason,
2758  * we do the elevation here too. Not because it makes the
2759  * code cleaner, or makes handling easier, but because I do *not*
2760  * want to maintain two of these nightmares.
2761  */
2762 
2763  for (x = 0; x < WEATHERMAPTILESX; x++) {
2764  for (y = 0; y < WEATHERMAPTILESY; y++) {
2765  water = space = trees = 0;
2766  elev = 0;
2767  nx = ny = 0;
2768 
2769  /* top left */
2770  if (load_humidity_map_part(&m, 8, x, y, &tx, &ty) == -1)
2771  continue;
2772 
2773  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2774  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2775  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2776  //LOG(llevInfo, "%s %d %d (8)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2777  }
2778  }
2779  delete_map(m);
2780 
2781 
2782  // Sanely skip some processing if the entire weathermap fit on one world map.
2783  // Since we are the same size for x/y direction on both weathermaps and on world maps,
2784  // we will either need to load one map, two maps, or four maps. When two maps are loaded, it
2785  // will be one of bottom left or top right, since bottom right only is relevant when we intersect maps
2786  // in both x and y directions.
2787  if (space < spwtx*spwty) {
2788  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2789  if (ny < spwty) {
2790  /* bottom left */
2791  if (load_humidity_map_part(&m, 6, x, y, &tx, &ty) == -1)
2792  continue;
2793 
2794  // If we get here, then we didn't have the whole weathermap reside on one map.
2795  // Since we are continuing from top left, maintaining our position in the y direction
2796  // allows us to correctly check when we reach the end of the weathermap bounds.
2797  j = ny;
2798  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2799  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2800  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2801  //LOG(llevInfo, "%s %d %d (6)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2802  }
2803  }
2804  delete_map(m);
2805  }
2806 
2807  // If we gotall the way to the right on the left calculations, skip both right-side calculations.
2808  if (nx < spwtx) {
2809  /* top right */
2810  if (load_humidity_map_part(&m, 2, x, y, &tx, &ty) == -1)
2811  continue;
2812 
2813  for (ax = MAX(0, tx-(spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2814  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2815  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2816  //LOG(llevInfo, "%s %d %d (2)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2817  }
2818  }
2819  delete_map(m);
2820 
2821  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2822  if (ny < spwty) {
2823  /* bottom right */
2824  if (load_humidity_map_part(&m, 4, x, y, &tx, &ty) == -1)
2825  continue;
2826 
2827  // Moving from top to bottom should behave the same on both right and left.
2828  j = ny;
2829  for (nx = 0, ax = MAX(0, tx - (spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2830  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2831  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2832  //LOG(llevInfo, "%s %d %d (4)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2833  }
2834  }
2835  delete_map(m);
2836  }
2837  }
2838  }
2839 
2840  /* jesus thats confusing as all hell */
2841  // Per meteorology, full ocean usually only gets to 80% humidity at the standard height it is measured.
2842  // And, even in the desert, relative humidity averages like 20%. So, in non-deserts, it should be like 40%.
2843  // This should help prevent a forever-hurricane over the ocean.
2844  weathermap[x][y].humid = 40+water*40/(spwtx*spwty);
2845  weathermap[x][y].avgelev = elev/(spwtx*spwty);
2846  weathermap[x][y].water = water*100/(spwtx*spwty);
2847  // Cap at 100 for tree values. Denser trees stop having any effect.
2848  weathermap[x][y].forestry = MIN(100, trees*100/(spwtx*spwty));
2849  }
2850  }
2851 
2852  /* and this does all the real work */
2853  update_humid();
2854 }
2855 
2861 static void init_temperature() {
2862  int x, y;
2863  timeofday_t tod;
2864 
2865  get_tod(&tod);
2866  for (x = 0; x < WEATHERMAPTILESX; x++) {
2867  for (y = 0; y < WEATHERMAPTILESY; y++) {
2868  temperature_calc(x, y, &tod);
2869  }
2870  }
2871 }
2872 
2878 static void init_rainfall()
2879 {
2880  int x, y;
2881  int days = todtick/HOURS_PER_DAY;
2882 
2883  for (x = 0; x < WEATHERMAPTILESX; x++) {
2884  for (y = 0; y < WEATHERMAPTILESY; y++) {
2885  if (weathermap[x][y].humid < 10) {
2886  weathermap[x][y].rainfall = days/20;
2887  } else if (weathermap[x][y].humid < 20) {
2888  weathermap[x][y].rainfall = days/15;
2889  } else if (weathermap[x][y].humid < 30) {
2890  weathermap[x][y].rainfall = days/10;
2891  } else if (weathermap[x][y].humid < 40) {
2892  weathermap[x][y].rainfall = days/5;
2893  } else if (weathermap[x][y].humid < 50) {
2894  weathermap[x][y].rainfall = days/2;
2895  } else if (weathermap[x][y].humid < 60) {
2896  weathermap[x][y].rainfall = days;
2897  } else if (weathermap[x][y].humid < 80) {
2898  weathermap[x][y].rainfall = days*2;
2899  } else {
2900  weathermap[x][y].rainfall = days*3;
2901  }
2902  }
2903  }
2904 }
2905 
2909 static void init_gulfstreammap() {
2910  int x, y, tx, starty, ymul, diroffset, dirdiff;
2911 
2912  /* build a gulf stream */
2914  /* doth the great bob inhale or exhale? */
2915  gulf_stream_direction = rndm(0, 1);
2916  gulf_stream_start = x;
2917 
2918  // Handle both gulf stream directions
2919  if (gulf_stream_direction) {
2920  // These variables allow us to only define the loop once.
2921  // That should make the code less awful to see
2922  starty = WEATHERMAPTILESY-1;
2923  ymul = -1;
2924  // The diroffset pieces allow us to merge the meat of the loops
2925  // the mapping between the different directions is as follows
2926  // 8 <-> 2
2927  // 7 <-> 3
2928  // 6 <-> 4
2929  // Thus setting diroffset to 10 when dirdiff is 1 gives us one direction
2930  // and setting diroffset to 0 when dirdiff is -1 gives us the other.
2931  diroffset = 0;
2932  dirdiff = -1;
2933  }
2934  else {
2935  starty = 0;
2936  ymul = 1;
2937  diroffset = 10;
2938  dirdiff = 1;
2939  }
2940  // Huzzah! a loop common to both directions!
2941  for (y = starty; y >= 0 && y < WEATHERMAPTILESY; y += ymul) {
2942  switch (rndm(0, 6)) {
2943  case 0:
2944  case 1:
2945  case 2:
2946  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2948  if (x == 0) {
2949  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2950  } else {
2951  gulf_stream_dir[tx][y] = (diroffset-8)*dirdiff;
2952  if (tx == 0) {
2953  x--;
2954  }
2955  }
2956  }
2957  break;
2958 
2959  case 3:
2960  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2962  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2963  }
2964  break;
2965 
2966  case 4:
2967  case 5:
2968  case 6:
2969  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2971  if (x == WEATHERMAPTILESX-1) {
2972  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2973  } else {
2974  gulf_stream_dir[tx][y] = (diroffset-6)*dirdiff;
2975  if (tx == 0) {
2976  x++;
2977  }
2978  }
2979  }
2980  break;
2981  }
2982  }
2983 }
2984 
2991 static void init_wind() {
2992  int x, y;
2993 
2994  for (x = 0; x < WEATHERMAPTILESX; x++) {
2995  for (y = 0; y < WEATHERMAPTILESY; y++) {
2996  weathermap[x][y].winddir = rndm(1, 8);
2997  weathermap[x][y].windspeed = rndm(1, 10);
2998  }
2999  }
3000 }
3001 
3008 static void init_pressure() {
3009  int x, y;
3010  int l, n, k;
3011 
3012  for (x = 0; x < WEATHERMAPTILESX; x++) {
3013  for (y = 0; y < WEATHERMAPTILESY; y++) {
3014  weathermap[x][y].pressure = 1013;
3015  }
3016  }
3017  // Add medium patches of low noise.
3018  for (l = 0; l < PRESSURE_ITERATIONS; l++) {
3019  x = rndm(0, WEATHERMAPTILESX-1);
3020  y = rndm(0, WEATHERMAPTILESY-1);
3022  for (k = 1; k < PRESSURE_AREA; k++) {
3023  switch (rndm(0, 3)) {
3024  case 0: if (x < WEATHERMAPTILESX-1) x++; break;
3025  case 1: if (y < WEATHERMAPTILESY-1) y++; break;
3026  case 2: if (x) x--; break;
3027  case 3: if (y) y--; break;
3028  }
3029  weathermap[x][y].pressure = (weathermap[x][y].pressure+n)/2;
3030  }
3031  }
3032  /* create random spikes in the pressure
3033  * These go way beyond the bounds of allowed pressure, but smooth_pressure
3034  * turns that into a sizable pressure blob.
3035  */
3036  for (l = 0; l < PRESSURE_SPIKES; l++) {
3037  x = rndm(0, WEATHERMAPTILESX-1);
3038  y = rndm(0, WEATHERMAPTILESY-1);
3039  n = rndm(500, 2000);
3040  weathermap[x][y].pressure = n;
3041  }
3042  smooth_pressure();
3043 }
3044 
3046  char buf[MAX_BUF], *cp, dummy[1];
3047  FILE *fp;
3048  int has_val;
3049 
3050  snprintf(buf, sizeof(buf), "%s/wsettings", settings->confdir);
3051 
3052  if ((fp = fopen(buf, "r")) == NULL) {
3053  LOG(llevError, "Warning: No settings 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  has_val = 1;
3074  } else {
3075  cp = dummy;
3076  has_val = 0;
3077  }
3078 
3079  if (!strcasecmp(buf, "worldmaptilesizex")) {
3080  int size = atoi(cp);
3081 
3082  if (size < 1)
3083  LOG(llevError, "load_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
3084  else
3085  wset.worldmaptilesizex = size;
3086  } else if (!strcasecmp(buf, "worldmaptilesizey")) {
3087  int size = atoi(cp);
3088 
3089  if (size < 1)
3090  LOG(llevError, "load_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
3091  else
3092  wset.worldmaptilesizey = size;
3093  } else if (!strcasecmp(buf, "dynamiclevel")) {
3094  int lev = atoi(cp);
3095 
3096  if (lev < 0)
3097  LOG(llevError, "load_settings: dynamiclevel must be at least 0, %d is invalid\n", lev);
3098  else
3099  wset.dynamiclevel = lev;
3100  }
3101  }
3102 }
3103 
3104 /********************************************************************************************
3105  * Section END -- initializations
3106  ********************************************************************************************/
3107 
3108 /********************************************************************************************
3109  * Section -- weather data writers
3110  * These functions write the current state of the weather to file,
3111  * allowing persistence across server runs.
3112  ********************************************************************************************/
3113 
3127  char filename[MAX_BUF];
3128  FILE *fp;
3129  OutputFile of;
3130  int x, y;
3131 
3132  // First, allocate our file.
3133  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3134  // We use the output_file handling for atomic file operations.
3135  fp = of_open(&of, filename);
3136  if (fp == NULL) {
3137  LOG(llevError, "Failed to open %s for writing.\n", filename);
3138  return 1;
3139  }
3140  LOG(llevDebug, "Writing forestry map to file.\n");
3141  // Actually write the forestry amounts to the file
3142  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3143  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3144  fprintf(fp, "%d ", weathermap[x][y].forestry);
3145  }
3146  fprintf(fp, "\n");
3147  }
3148  of_close(&of);
3149  return 0;
3150 }
3151 
3162  char filename[MAX_BUF];
3163  FILE *fp;
3164  OutputFile of;
3165  int x, y;
3166 
3167  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3168  fp = of_open(&of, filename);
3169  if (fp == NULL) {
3170  LOG(llevError, "Cannot open %s for writing\n", filename);
3171  return 1;
3172  }
3173  LOG(llevDebug, "Writing humidity map to file.\n");
3174  for (x = 0; x < WEATHERMAPTILESX; x++) {
3175  for (y = 0; y < WEATHERMAPTILESY; y++) {
3176  fprintf(fp, "%d ", weathermap[x][y].humid);
3177  }
3178  fprintf(fp, "\n");
3179  }
3180  of_close(&of);
3181  return 0;
3182 }
3183 
3196 static int write_elevmap(const Settings *settings) {
3197  char filename[MAX_BUF];
3198  FILE *fp;
3199  OutputFile of;
3200  int x, y;
3201 
3202  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3203  fp = of_open(&of, filename);
3204  if (fp == NULL) {
3205  LOG(llevError, "Cannot open %s for writing\n", filename);
3206  return 1;
3207  }
3208  LOG(llevDebug, "Writing elevation map to file.\n");
3209  for (x = 0; x < WEATHERMAPTILESX; x++) {
3210  for (y = 0; y < WEATHERMAPTILESY; y++) {
3211  fprintf(fp, "%d ", weathermap[x][y].avgelev);
3212  }
3213  fprintf(fp, "\n");
3214  }
3215  of_close(&of);
3216  return 0;
3217 }
3218 
3219 
3231 static int write_watermap(const Settings *settings) {
3232  char filename[MAX_BUF];
3233  FILE *fp;
3234  OutputFile of;
3235  int x, y;
3236 
3237  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3238  fp = of_open(&of, filename);
3239  if (fp == NULL) {
3240  LOG(llevError, "Cannot open %s for writing\n", filename);
3241  return 1;
3242  }
3243  LOG(llevDebug, "Writing water map to file.\n");
3244  for (x = 0; x < WEATHERMAPTILESX; x++) {
3245  for (y = 0; y < WEATHERMAPTILESY; y++) {
3246  fprintf(fp, "%d ", weathermap[x][y].water);
3247  }
3248  fprintf(fp, "\n");
3249  }
3250  of_close(&of);
3251  return 0;
3252 }
3253 
3266  char filename[MAX_BUF];
3267  FILE *fp;
3268  OutputFile of;
3269  int x, y;
3270 
3271  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
3272  fp = of_open(&of, filename);
3273  if (fp == NULL) {
3274  LOG(llevError, "Cannot open %s for writing\n", filename);
3275  return 1;
3276  }
3277  LOG(llevDebug, "Writing temperature map to file.\n");
3278  for (x = 0; x < WEATHERMAPTILESX; x++) {
3279  for (y = 0; y < WEATHERMAPTILESY; y++) {
3280  fprintf(fp, "%d ", weathermap[x][y].temp);
3281  }
3282  fprintf(fp, "\n");
3283  }
3284  of_close(&of);
3285  return 0;
3286 }
3287 
3299  char filename[MAX_BUF];
3300  FILE *fp;
3301  OutputFile of;
3302  int x, y;
3303 
3304  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
3305  fp = of_open(&of, filename);
3306  if (fp == NULL) {
3307  LOG(llevError, "Cannot open %s for writing\n", filename);
3308  return 1;
3309  }
3310  LOG(llevDebug, "Writing rainfall map to file.\n");
3311  for (x = 0; x < WEATHERMAPTILESX; x++) {
3312  for (y = 0; y < WEATHERMAPTILESY; y++) {
3313  fprintf(fp, "%u ", weathermap[x][y].rainfall);
3314  }
3315  fprintf(fp, "\n");
3316  }
3317  of_close(&of);
3318  return 0;
3319 }
3320 
3332  char filename[MAX_BUF];
3333  FILE *fp;
3334  OutputFile of;
3335  int x, y;
3336 
3337  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
3338  fp = of_open(&of, filename);
3339  if (fp == NULL) {
3340  LOG(llevError, "Cannot open %s for writing\n", filename);
3341  return 1;
3342  }
3343  LOG(llevDebug, "Writing gulf stream map to file.\n");
3344  // First block is speed
3345  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3346  for (y = 0; y < WEATHERMAPTILESY; y++) {
3347  fprintf(fp, "%d ", gulf_stream_speed[x][y]);
3348  }
3349  fprintf(fp, "\n");
3350  }
3351  // second block is direction
3352  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3353  for (y = 0; y < WEATHERMAPTILESY; y++) {
3354  fprintf(fp, "%d ", gulf_stream_dir[x][y]);
3355  }
3356  fprintf(fp, "\n");
3357  }
3358  // And the last line is the starting position, so we don't always have to initialize it.
3359  fprintf(fp, "%d\n", gulf_stream_start);
3360  of_close(&of);
3361  return 0;
3362 }
3363 
3375  char filename[MAX_BUF];
3376  FILE *fp;
3377  OutputFile of;
3378  int x, y;
3379 
3380  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
3381  fp = of_open(&of, filename);
3382  if (fp == NULL) {
3383  LOG(llevError, "Cannot open %s for writing\n", filename);
3384  return 1;
3385  }
3386  LOG(llevDebug, "Writing wind speed map to file.\n");
3387  for (x = 0; x < WEATHERMAPTILESX; x++) {
3388  for (y = 0; y < WEATHERMAPTILESY; y++) {
3389  fprintf(fp, "%hd ", weathermap[x][y].windspeed);
3390  }
3391  fprintf(fp, "\n");
3392  }
3393  of_close(&of);
3394  return 0;
3395 }
3396 
3408  char filename[MAX_BUF];
3409  FILE *fp;
3410  OutputFile of;
3411  int x, y;
3412 
3413  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
3414  fp = of_open(&of, filename);
3415  if (fp == NULL) {
3416  LOG(llevError, "Cannot open %s for writing\n", filename);
3417  return 1;
3418  }
3419  LOG(llevDebug, "Writing wind direction map to file.\n");
3420  for (x = 0; x < WEATHERMAPTILESX; x++) {
3421  for (y = 0; y < WEATHERMAPTILESY; y++) {
3422  fprintf(fp, "%d ", weathermap[x][y].winddir);
3423  }
3424  fprintf(fp, "\n");
3425  }
3426  of_close(&of);
3427  return 0;
3428 }
3429 
3441  char filename[MAX_BUF];
3442  FILE *fp;
3443  OutputFile of;
3444  int x, y;
3445 
3446  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
3447  fp = of_open(&of, filename);
3448  if (fp == NULL) {
3449  LOG(llevError, "Cannot open %s for writing\n", filename);
3450  return 1;
3451  }
3452  LOG(llevDebug, "Writing pressure map to file.\n");
3453  for (x = 0; x < WEATHERMAPTILESX; x++) {
3454  for (y = 0; y < WEATHERMAPTILESY; y++) {
3455  fprintf(fp, "%d ", weathermap[x][y].pressure);
3456  }
3457  fprintf(fp, "\n");
3458  }
3459  of_close(&of);
3460  return 0;
3461 }
3462 
3469 int write_skymap(void) {
3470  char filename[MAX_BUF];
3471  FILE *fp;
3472  OutputFile of;
3473  int x, y;
3474 
3475  snprintf(filename, sizeof(filename), "%s/skymap", settings.localdir);
3476  fp = of_open(&of, filename);
3477  if (fp == NULL) {
3478  LOG(llevError, "Cannot open %s for writing\n", filename);
3479  return 1;
3480  }
3481  LOG(llevDebug, "Writing sky conditions map to file.\n");
3482  for (x = 0; x < WEATHERMAPTILESX; x++) {
3483  for (y = 0; y < WEATHERMAPTILESY; y++) {
3484  fprintf(fp, "%d ", weathermap[x][y].sky);
3485  }
3486  fprintf(fp, "\n");
3487  }
3488  of_close(&of);
3489  return 0;
3490 }
3491 
3492 /* This stuff is for creating the images,
3493  * and is only used by write_weather_images()
3494  */
3495 
3496 /* Colour offsets into pixel array. */
3497 #define RED 0
3498 #define GREEN 1
3499 #define BLUE 2
3500 
3508 static const uint32_t directions[] = {
3509  0x0000FFFF, /* south */
3510  0x000000FF, /* south west */
3511  0x00FF00FF, /* west */
3512  0x00FFFFFF, /* north west */
3513  0x00000000, /* north */
3514  0x00FF0000, /* north east */
3515  0x00FFFF00, /* east */
3516  0x0000FF00 /* south east */
3517 };
3518 
3522 static const uint32_t skies[] = {
3523  0x000000FF, /* SKY_CLEAR 0 */
3524  0x000000BD, /* SKY_LIGHTCLOUD 1 */
3525  0x0000007E, /* SKY_OVERCAST 2 */
3526  0x0000FF00, /* SKY_LIGHT_RAIN 3 */
3527  0x0000BD00, /* SKY_RAIN 4 */
3528  0x00007E00, /* SKY_HEAVY_RAIN 5 */
3529  0x00FFFF00, /* SKY_HURRICANE 6 */
3530 /* wierd weather 7-12 */
3531  0x00FF0000, /* SKY_FOG 7 */
3532  0x00FF00FF, /* SKY_HAIL 8 */
3533  0x00000000,
3534  0x00000000,
3535  0x00000000,
3536  0x00000000,
3537 /* snow */
3538  0x003F3F3F, /* SKY_LIGHT_SNOW 13 */
3539  0x007E7E7E, /* SKY_SNOW 14 */
3540  0x00BDBDBD, /* SKY_HEAVY_SNOW 15 */
3541  0x00FFFFFF /* SKY_BLIZZARD 16 */
3542 };
3543 
3557  char filename[MAX_BUF];
3558  FILE *fp;
3559  OutputFile of;
3560  int x, y;
3561  int32_t min[8], max[8], avgrain, avgwind, realmaxwind;
3562  double scale[8], realscalewind;
3563  uint8_t pixels[3*3*WEATHERMAPTILESX];
3564  int64_t total_rainfall = 0;
3565  int64_t total_wind = 0;
3566  timeofday_t tod;
3567 
3568  // Get the time of day.
3569  // This is important for weather output later.
3570  get_tod(&tod);
3571 
3572  // Determine the output file's limits.
3573  min[0] = -100; max[0] = 100;
3574  min[1] = 0; max[1] = 0;
3575  min[2] = 0; max[2] = 0;
3576  min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
3577  // Minimum wind speed is always 0. Don't track it. We just define it so scale[4] is valid
3578  min[4] = 0; max[4] = 0;
3579  // The 6th tile is raw wind direction, and thus does not need limits
3580  min[6] = 0; max[6] = 100;
3581  min[7] = -45; max[7] = 45;
3582  // The 9th tile is raw sky data and does not need limits
3583  for (x = 0; x < WEATHERMAPTILESX; x++) {
3584  for (y = 0; y < WEATHERMAPTILESY; y++) {
3585 /* min[0] = MIN(min[0], weathermap[x][y].water); */
3586  min[1] = MIN(min[1], weathermap[x][y].avgelev);
3587  min[2] = MIN(min[2], weathermap[x][y].rainfall);
3588 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
3589 /* min[4] = MIN(min[4], weathermap[x][y].windspeed); */
3590 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
3591 /* min[7] = MIN(min[7], real_temp[x][y]); */
3592 
3593 /* max[0] = MAX(max[0], weathermap[x][y].water); */
3594  max[1] = MAX(max[1], weathermap[x][y].avgelev);
3595  max[2] = MAX(max[2], weathermap[x][y].rainfall);
3596 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
3597  max[4] = MAX(max[4], weathermap[x][y].windspeed);
3598 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
3599 /* max[7] = MAX(max[7], real_temp[x][y]); */
3600  total_rainfall += weathermap[x][y].rainfall;
3601  total_wind += weathermap[x][y].windspeed;
3602  }
3603  }
3604  // Twiddle the data on total rainfall, since they have a different color for above/below average
3605  // This allows us to have the full scale of color range on each above average and below average.
3606  avgrain = total_rainfall/(WEATHERMAPTILESX*WEATHERMAPTILESY);
3607  avgwind = (total_wind /(WEATHERMAPTILESX*WEATHERMAPTILESY));
3608  max[2] = avgrain-1;
3609  realscalewind = 255.0l/(max[4]);
3610  realmaxwind = max[4];
3611  max[4] = avgwind-1;
3612  for (x = 0; x < 8; x++) {
3613  scale[x] = 255.0l/(max[x]-min[x]);
3614  }
3615 
3616  LOG(llevDebug, "Writing weather conditions map.\n");
3617 
3618  snprintf(filename, sizeof(filename), "%s/weather.ppm", settings.localdir);
3619  fp = of_open(&of, filename);
3620  if (fp == NULL) {
3621  LOG(llevError, "Cannot open %s for writing\n", filename);
3622  return 1;
3623  }
3624  fprintf(fp, "P6\n%d %d\n", 3*WEATHERMAPTILESX, 3*WEATHERMAPTILESY);
3625  fprintf(fp, "255\n");
3626  // First row of maps
3627  for (y = 0; y < WEATHERMAPTILESY; y++) {
3628  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3629  for (x = 0; x < WEATHERMAPTILESX; x++) {
3630  // water/tree map -- first map of row
3631  // blue = high water amount, black = low water amount, red = desert-like, green = trees
3632  if (weathermap[x][y].water < 0)
3633  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(weathermap[x][y].water-min[0])*scale[0]*2);
3634  else
3635  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].water)*scale[0]*2);
3636  // Either way, we use green to highlight the trees, too
3637  // Make this real simple since it is established that forestry values range from 0 to 100.
3638  // As a result, our values go from 0-250.
3639  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].forestry)*5/2);
3640  // elevation map -- second map of row.
3641  // green -- mostly land --> brighter green is higher elevation
3642  // blue -- mostly water --> deeper blue is lower elevation
3643  if (weathermap[x][y].avgelev >= 0) {
3644  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3645  } else {
3646  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3647  }
3648  // rainfall map -- third map of row
3649  // magenta = high rainfall, blue = average rainfall, black = low rainfall
3650  if (weathermap[x][y].rainfall >= avgrain) { /* rainfall is rather spikey, this gives us more detail. */
3651  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = 255;
3652  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((weathermap[x][y].rainfall-avgrain)*(255.0/(max[2]-avgrain)));
3653  } else {
3654  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].rainfall-min[2])*(avgrain-min[2]));
3655  }
3656  }
3657  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3658  }
3659  // Second row of maps.
3660  for (y = 0; y < WEATHERMAPTILESY; y++) {
3661  for (x = 0; x < WEATHERMAPTILESX; x++) {
3662  uint32_t dir = directions[weathermap[x][y].winddir-1];
3663  uint32_t speed = weathermap[x][y].windspeed;
3664  int16_t pressure = (weathermap[x][y].pressure-PRESSURE_MIN)*scale[3];
3665  // Make sure we don't get artifacting from Post-smoothing pressure adjustments.
3666  // or round-off error in the above calculation.
3667  pressure = MIN(255, MAX(0, pressure));
3668  // Pressure -- first map of row
3669  // light = high pressure, dark = low pressure
3670  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = pressure;
3671  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = pressure;
3672  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = pressure;
3673  // Wind speed -- second map of row
3674  // very high wind = red, else grey = average wind, dark = low wind
3675  if (speed < avgwind) {
3676  speed = (speed)*scale[4]/2;
3677  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = speed;
3678  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = speed;
3679  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = speed;
3680  } else {
3681  speed = (speed-avgwind)*realscalewind/2;
3682  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(MIN(255,(avgwind)*scale[4]/2+speed));
3683  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (avgwind)*scale[4]/2 - speed;
3684  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (avgwind)*scale[4]/2 - speed;
3685  }
3686  // Wind direction -- third map of row
3687  // red = northeast, yellow = east, green = southeast, cyan = south,
3688  // blue = southwest, magenta = west, white = northwest, black = north
3689  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3690  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3691  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3692  }
3693  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3694  }
3695  // Third row of maps
3696  for (y = 0; y < WEATHERMAPTILESY; y++) {
3697  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3698  for (x = 0; x < WEATHERMAPTILESX; x++) {
3699  uint32_t dir = skies[weathermap[x][y].sky];
3700  // Humidity -- first map of row.
3701  // blue = high humidity, black = low humidity, red = droughty (even lower humidity)
3702  // Didn't adjust min for this one, so it should look a little different than the others.
3703  if (weathermap[x][y].humid < 0)
3704  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(-weathermap[x][y].humid)*scale[6]);
3705  else
3706  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].humid-min[6])*scale[6]);
3707  // Real temperature -- second map of row
3708  // temp < 0 --> scale from white to blue
3709  // temp > 0 --> scale from blue to green to yellow to red
3710  // green is 20 C
3711  // yellow is 30 C
3712  int temp = real_temperature(x, y, &tod);
3713  // white -> cyan
3714  if (temp < 0) {
3715  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(-temp * scale[7]*2);
3716  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)0xFF;
3717  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)0xFF;
3718  }
3719  // cyan->blue is the boundary for above/below freezing
3720  // blue -> green
3721  else if (temp < 20) {
3722  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 0;
3723  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)(temp * scale[7]*9/2);
3724  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((20-temp) * scale[7]*9/2);
3725  }
3726  // green -> yellow
3727  else if (temp < 30) {
3728  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)((temp-20) * scale[7]*9);
3729  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = 255;
3730  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3731  }
3732  // yellow -> red
3733  else {
3734  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 255;
3735  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((45-temp) * scale[7]*6);
3736  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3737  }
3738  // current weather -- third map of row
3739  // blue = clear, medium blue = light clouds, dark blue = overcast, green = light rain
3740  // medium green = rain, dark green = heavy rain, yellow = hurricane, red = fog, magenta = hail,
3741  // dark gray = light snow, medium gray = snow, gray = heavy snow, white = blizzard
3742  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3743  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3744  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3745  }
3746  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3747  }
3748  of_close(&of);
3749  return 0;
3750 }
3751 
3752 /********************************************************************************************
3753  * Section END -- weather data writers
3754  ********************************************************************************************/
3755 
3756 /********************************************************************************************
3757  * Section -- weather data readers
3758  * These read weather data that gets periodically stored to file to restore
3759  * the prior state on a server restart. They also handle first-load initialization.
3760  ********************************************************************************************/
3761 
3779 static int read_forestrymap(const Settings *settings) {
3780  char filename[MAX_BUF], *data, *tmp;
3781  BufferReader *bfr;
3782  int trees, x, y, res;
3783 
3784  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3785  LOG(llevDebug, "Reading forestry data from %s...\n", filename);
3786  // Set up the bufferreader and read in the file.
3787  // We do it through the bufferreader so that we only dip into I/O once,
3788  // and the rest is just parsing it in memory.
3789  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3790  // We already printed our error if we failed, so just bail in that case.
3791  if (bfr == NULL)
3792  return -1;
3793  // Parse the file. Since this is auto-generated by the weather system,
3794  // just bail if the file is malformed.
3795  data = bufferreader_data(bfr);
3796  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3797  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3798  res = sscanf(data, "%d ", &trees);
3799  if (res != 1) {
3800  LOG(llevError, "Forestry data is corrupted and should be regenerated.\n"
3801  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3802  bufferreader_destroy(bfr);
3803  return -1;
3804  }
3805  // Limit the range from 0 to 100
3806  weathermap[x][y].forestry = MIN(100, MAX(0, trees));
3807  // Now we move where we're looking, since we want to read more than just the first entry.
3808  // Use strpbrk so that we can handle newlines more cleanly.
3809  tmp = strpbrk(data, " \n");
3810  if (tmp != NULL)
3811  data = tmp + 1;
3812  else {
3813  LOG(llevError, "Unexpected end of forestry file. Forestry file may need to be regenerated.\n"
3814  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3815  bufferreader_destroy(bfr);
3816  return -1;
3817  }
3818  }
3819  // Due to the way the file is written, the end of the line should have a space and a newline.
3820  // Handle the newline if it is the front of the string now.
3821  if (*data == '\n')
3822  ++data;
3823  }
3824  bufferreader_destroy(bfr);
3825  return 0;
3826 }
3827 
3842 static int read_humidmap(const Settings *settings) {
3843  char filename[MAX_BUF], *data, *tmp;
3844  BufferReader *bfr;
3845  int x, y, hmd, res;
3846 
3847  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3848  LOG(llevDebug, "Reading humidity data from %s...\n", filename);
3849  // Open and read in the file all at once
3850  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3851  // If we fail, we do initializations instead.
3852  if (bfr == NULL) {
3853  LOG(llevInfo, "Initializing humidity and elevation maps...\n");
3859  LOG(llevDebug, "Done\n");
3860  return 1;
3861  }
3862  data = bufferreader_data(bfr);
3863  for (x = 0; x < WEATHERMAPTILESX; x++) {
3864  for (y = 0; y < WEATHERMAPTILESY; y++) {
3865  res = sscanf(data, "%d ", &hmd);
3866  if (res != 1) {
3867  LOG(llevError, "Humidity data is corrupted and cannot be loaded.\n"
3868  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3869  bufferreader_destroy(bfr);
3870  return -1;
3871  }
3872  // Limit range from 0 to 100. Clip entries that surpass these bounds.
3873  weathermap[x][y].humid = MAX(0, MIN(100, hmd));
3874  // Move to the next spot in the buffer.
3875  tmp = strpbrk(data, " \n");
3876  if (tmp == NULL) {
3877  LOG(llevError, "Unexpected end of humidity file.\n"
3878  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3879  bufferreader_destroy(bfr);
3880  return -1;
3881  }
3882  // If found, move data to the next character after the space/newline found.
3883  data = tmp + 1;
3884  }
3885  // If the newline from the previous line hasn't been parsed yet, skip it.
3886  if (*data == '\n')
3887  ++data;
3888  }
3889  bufferreader_destroy(bfr);
3890  LOG(llevDebug, "Done.\n");
3891  return 0;
3892 }
3893 
3906 static int read_elevmap(const Settings *settings) {
3907  char filename[MAX_BUF], *data, *tmp;
3908  BufferReader *bfr;
3909  int x, y, elev, res;
3910 
3911  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3912  LOG(llevDebug, "Reading elevation data from %s...\n", filename);
3913  // Read file into a buffer.
3914  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3915  // If failed, we bail.
3916  if (bfr == NULL) {
3917  /* initializing these is expensive, and should have been done
3918  by the humidity. It's not worth the wait to do it twice. */
3919  return -1;
3920  }
3921  data = bufferreader_data(bfr);
3922  for (x = 0; x < WEATHERMAPTILESX; x++) {
3923  for (y = 0; y < WEATHERMAPTILESY; y++) {
3924  res = sscanf(data, "%d ", &elev);
3925  if (res != 1) {
3926  LOG(llevError, "Elevation data is corrupted and cannot be loaded.\n"
3927  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3928  bufferreader_destroy(bfr);
3929  return -1;
3930  }
3931  // Individual elevation values should be in the range [-32000, 32000]
3932  // Averages can be capped to these as well.
3933  weathermap[x][y].avgelev = MAX(-32000, MIN(32000, elev));
3934  // Now we move to the next entry in the buffer.
3935  tmp = strpbrk(data, " \n");
3936  if (tmp == NULL) {
3937  LOG(llevError, "Unexpected end of file in elevation data.\n"
3938  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3939  bufferreader_destroy(bfr);
3940  return -1;
3941  }
3942  // If found, we move to the character after the space/newline.
3943  data = tmp + 1;
3944  }
3945  // If there's still a newline after passing a space, then skip past it.
3946  if (*data == '\n')
3947  ++data;
3948  }
3949  bufferreader_destroy(bfr);
3950  LOG(llevDebug, "Done.\n");
3951  return 0;
3952 }
3953 
3965 static int read_watermap(const Settings *settings) {
3966  char filename[MAX_BUF], *data, *tmp;
3967  BufferReader *bfr;
3968  int x, y, wtr, res;
3969 
3970  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3971  LOG(llevDebug, "Reading water data from %s...\n", filename);
3972  // Read the file into a buffer.
3973  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3974  // If failed, we bail
3975  if (bfr == NULL) {
3976  /* initializing these is expensive, and should have been done
3977  by the humidity. It's not worth the wait to do it twice. */
3978  return -1;
3979  }
3980  data = bufferreader_data(bfr);
3981  for (x = 0; x < WEATHERMAPTILESX; x++) {
3982  for (y = 0; y < WEATHERMAPTILESY; y++) {
3983  res = sscanf(data, "%d ", &wtr);
3984  if (res != 1) {
3985  LOG(llevError, "Water map is corrupted and cannot be loaded.\n"
3986  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3987  bufferreader_destroy(bfr);
3988  return -1;
3989  }
3990  // Range is -100 to 100 due to deserts and such being negative.
3991  weathermap[x][y].water = MAX(-100, MIN(100, wtr));
3992  // Adjust the data pointer.
3993  tmp = strpbrk(data, " \n");
3994  if (tmp == NULL) {
3995  LOG(llevError, "Unexpected end of file in water map.\n"
3996  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3997  bufferreader_destroy(bfr);
3998  return -1;
3999  }
4000  // Okay, so we want the first character after the space/newline.
4001  data = tmp + 1;
4002  }
4003  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4004  if (*data == '\n')
4005  ++data;
4006  }
4007  bufferreader_destroy(bfr);
4008  LOG(llevDebug, "Done.\n");
4009  return 0;
4010 }
4011 
4024  char filename[MAX_BUF], *data, *tmp;
4025  BufferReader *bfr;
4026  int x, y, res;
4027  int16_t temperature;
4028 
4029  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
4030  LOG(llevDebug, "Reading temperature data from %s...\n", filename);
4031  // Read the file into a buffer.
4032  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4033  // If it fails, we initialize the temperature map and write it to a file.
4034  if (bfr == NULL) {
4035  LOG(llevInfo, "Initializing temperature map.\n");
4036  init_temperature();
4037  // If writing was successful, then consider this a success.
4038  if (write_temperaturemap(settings) == 0)
4039  return 1;
4040  return -1;
4041  }
4042  data = bufferreader_data(bfr);
4043  for (x = 0; x < WEATHERMAPTILESX; x++) {
4044  for (y = 0; y < WEATHERMAPTILESY; y++) {
4045  res = sscanf(data, "%hd ", &temperature);
4046  if (res != 1) {
4047  LOG(llevError, "Temperature file is malformed, unable to load temps from file.\n");
4048  bufferreader_destroy(bfr);
4049  return -1;
4050  }
4051  // Clip to reasonable bounds.
4052  weathermap[x][y].temp = MIN(60, MAX(-30, temperature));
4053  // Adjust the data pointer.
4054  tmp = strpbrk(data, " \n");
4055  if (tmp == NULL) {
4056  LOG(llevError, "Unexpected end of file in temperature map.\n");
4057  bufferreader_destroy(bfr);
4058  return -1;
4059  }
4060  // Okay, so we want the first character after the space/newline.
4061  data = tmp + 1;
4062  }
4063  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4064  if (*data == '\n')
4065  ++data;
4066  }
4067  bufferreader_destroy(bfr);
4068  LOG(llevDebug, "Done.\n");
4069  return 0;
4070 }
4071 
4085 static int read_rainfallmap(const Settings *settings) {
4086  char filename[MAX_BUF], *data, *tmp;
4087  BufferReader *bfr;
4088  int x, y, res;
4089 
4090  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
4091  LOG(llevDebug, "Reading rainfall data from %s...\n", filename);
4092  // Read file into a buffer.
4093  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4094  // If it fails, we initialize and write to file.
4095  if (bfr == NULL) {
4096  LOG(llevInfo, "Initializing rainfall map...\n");
4097  init_rainfall();
4098  // If we write to file successfully, consider initialization a success.
4099  if (write_rainfallmap(settings) != 0);
4100  return -1;
4101  return 1;
4102  }
4103  data = bufferreader_data(bfr);
4104  for (x = 0; x < WEATHERMAPTILESX; x++) {
4105  for (y = 0; y < WEATHERMAPTILESY; y++) {
4106  res = sscanf(data, "%u ", &weathermap[x][y].rainfall);
4107  if (res != 1) {
4108  LOG(llevError, "Rainfall file is corrupted, cannot load rainfall from file.\n");
4109  bufferreader_destroy(bfr);
4110  return -1;
4111  }
4112  // Now we update the pointer to data to move to the next item.
4113  tmp = strpbrk(data, " \n");
4114  if (tmp == NULL) {
4115  LOG(llevError, "Unexpected end of file in rainfall map.\n");
4116  bufferreader_destroy(bfr);
4117  return -1;
4118  }
4119  // Okay, so we want the first character after the space/newline.
4120  data = tmp + 1;
4121  }
4122  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4123  if (*data == '\n')
4124  ++data;
4125  }
4126  bufferreader_destroy(bfr);
4127  LOG(llevDebug, "Done.\n");
4128  return 0;
4129 }
4130 
4143  char filename[MAX_BUF], *data, *tmp;
4144  BufferReader *bfr;
4145  int x, y, in, res;
4146 
4147  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
4148  LOG(llevDebug, "Reading gulf stream data from %s...\n", filename);
4149  // Read the file into a buffer
4150  bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4151  // If it fails, we initialize and write to file.
4152  if (bfr == NULL) {
4153  LOG(llevInfo, "Initializing gulf stream maps...\n");
4156  LOG(llevDebug, "Done\n");
4157  if (res == 0)
4158  return 1;
4159  return -1;
4160  }
4161  data = bufferreader_data(bfr);
4162  // First we read in the speeds
4163  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4164  for (y = 0; y < WEATHERMAPTILESY; y++) {
4165  res = sscanf(data, "%d ", &in);
4166  if (res != 1) {
4167  LOG(llevError, "Gulf stream speed definitions are malformed. Cannot load gulf stream from file.\n");
4168  bufferreader_destroy(bfr);
4169  return -1;
4170  }
4171  gulf_stream_speed[x][y] = MIN(120, MAX(0, in));
4172  // Now we update the pointer to data to move to the next item.
4173  tmp = strpbrk(data, " \n");
4174  if (tmp == NULL) {
4175  LOG(llevError, "Unexpected end of file in gulfstream speed map.\n");
4176  bufferreader_destroy(bfr);
4177  return -1;
4178  }
4179  // Okay, so we want the first character after the space/newline.
4180  data = tmp + 1;
4181  }
4182  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4183  if (*data == '\n')
4184  ++data;
4185  }
4186  // Then we read in the directions.
4187  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4188  for (y = 0; y < WEATHERMAPTILESY; y++) {
4189  res = sscanf(data, "%d ", &in);
4190  if (res != 1) {
4191  LOG(llevError, "Gulf stream direction definitions are malformed. Cannot load gulf stream from file.\n");
4192  bufferreader_destroy(bfr);
4193  return -1;
4194  }
4195  gulf_stream_dir[x][y] = MAX(1, MIN(8, in));
4196  // Now we update the pointer to data to move to the next item.
4197  tmp = strpbrk(data, " \n");
4198  if (tmp == NULL) {
4199  LOG(llevError, "Unexpected end of file in gulfstream direction map.\n");
4200  bufferreader_destroy(bfr);
4201  return -1;
4202  }
4203  // Okay, so we want the first character after the space/newline.
4204  data = tmp + 1;
4205  }
4206  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4207  if (*data == '\n')
4208  ++data;
4209  }
4210  // Then we read in the start point, if it exists
4211  // For backward compatability, we randomly initialize this if we can't read it.
4212  res = sscanf(data, "%d\n", &in);
4213  if (res != 1) {
4214  LOG(llevInfo, "Gulf stream file lacks start position, and is assumed to be old; initializing it randomly.\n");
4216  }
4218  // If we add any more parsing here, we'll need to affect the data pointer.
4219  // But, since we don't, leave it stale until it falls out of scope.
4220  bufferreader_destroy(bfr);
4221  LOG(llevDebug, "Done.\n");
4222  return 0;
4223 }
4224 
4239  char filename[MAX_BUF], *data, *tmp;
4240  BufferReader *bfr;
4241  int x, y, res;
4242  int8_t spd;
4243 
4244  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
4245  LOG(llevDebug, "Reading wind speed data from %s...\n", filename);
4246  // Read the file into a buffer
4247  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4248  // If it fails, we bail
4249  if (bfr == NULL) {
4250  // Wind direction is done before this, and should have initialized this already.
4251  return -1;
4252  }
4253  data = bufferreader_data(bfr);
4254  for (x = 0; x < WEATHERMAPTILESX; x++) {
4255  for (y = 0; y < WEATHERMAPTILESY; y++) {
4256  res = sscanf(data, "%hhd ", &spd);
4257  if (res != 1) {
4258  LOG(llevError, "Wind speed file is malformed. Cannot load wind speed file.\n");
4259  bufferreader_destroy(bfr);
4260  return -1;
4261  }
4262  // Clip to reasonable bounds
4263  weathermap[x][y].windspeed = MIN(120, MAX(0, spd));
4264  // Now we update the pointer to data to move to the next item.
4265  tmp = strpbrk(data, " \n");
4266  if (tmp == NULL) {
4267  LOG(llevError, "Unexpected end of file in wind speed map.\n");
4268  bufferreader_destroy(bfr);
4269  return -1;
4270  }
4271  // Okay, so we want the first character after the space/newline.
4272  data = tmp + 1;
4273  }
4274  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4275  if (*data == '\n')
4276  ++data;
4277  }
4278  bufferreader_destroy(bfr);
4279  LOG(llevDebug, "Done.\n");
4280  return 0;
4281 }
4282 
4293 static int read_winddirmap(const Settings *settings) {
4294  char filename[MAX_BUF], *data, *tmp;
4295  BufferReader *bfr;
4296  int x, y, d, res;
4297 
4298  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
4299  LOG(llevDebug, "Reading wind direction data from %s...\n", filename);
4300  // Read the file into a buffer
4301  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4302  // If it fails, initialize the wind
4303  if (bfr == NULL) {
4304  LOG(llevInfo, "Initializing wind direction and speed maps...\n");
4305  init_wind();
4306  // If both of these succeed, the end result is 0.
4307  res = write_winddirmap(settings);
4308  res += write_windspeedmap(settings);
4309  LOG(llevDebug, "Done\n");
4310  if (res == 0)
4311  return 1;
4312  return -1;
4313  }
4314  data = bufferreader_data(bfr);
4315  for (x = 0; x < WEATHERMAPTILESX; x++) {
4316  for (y = 0; y < WEATHERMAPTILESY; y++) {
4317  res = sscanf(data, "%d ", &d);
4318  if (res != 1) {
4319  LOG(llevError, "Wind direction map is malformed. Could not load wind direction.\n");
4320  bufferreader_destroy(bfr);
4321  return -1;
4322  }
4323  // If the direction is not valid, just give it one randomly.
4324  if (d < 1 || d > 8) {
4325  d = rndm(1, 8);
4326  }
4327  weathermap[x][y].winddir = d;
4328  // Now we update the pointer to data to move to the next item.
4329  tmp = strpbrk(data, " \n");
4330  if (tmp == NULL) {
4331  LOG(llevError, "Unexpected end of file in wind direction map.\n");
4332  bufferreader_destroy(bfr);
4333  return -1;
4334  }
4335  // Okay, so we want the first character after the space/newline.
4336  data = tmp + 1;
4337  }
4338  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4339  if (*data == '\n')
4340  ++data;
4341  }
4342  bufferreader_destroy(bfr);
4343  LOG(llevDebug, "Done.\n");
4344  return 0;
4345 }
4346 
4357 static int read_pressuremap(const Settings *settings) {
4358  char filename[MAX_BUF], *data, *tmp;
4359  BufferReader *bfr;
4360  int x, y, res;
4361  int16_t press;
4362 
4363  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
4364  LOG(llevDebug, "Reading pressure data from %s...\n", filename);
4365  // Read the file into a buffer.
4366  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading\n", llevError);
4367  // If it fails, we initialize the pressure.
4368  if (bfr == NULL) {
4369  LOG(llevInfo, "Initializing pressure maps...\n");
4370  init_pressure();
4371  res = write_pressuremap(settings);
4372  LOG(llevDebug, "Done\n");
4373  if (res == 0)
4374  return 1;
4375  return -1;
4376  }
4377  data = bufferreader_data(bfr);
4378  for (x = 0; x < WEATHERMAPTILESX; x++) {
4379  for (y = 0; y < WEATHERMAPTILESY; y++) {
4380  res = sscanf(data, "%hd ", &press);
4381  if (res != 1) {
4382  LOG(llevError, "Pressure map is malformed. Could not load pressure.\n");
4383  bufferreader_destroy(bfr);
4384  return -1;
4385  }
4386  // Apply clipping to the pressure.
4387  weathermap[x][y].pressure = MIN(PRESSURE_MAX, MAX(PRESSURE_MIN, press));
4388  // Now we update the pointer to data to move to the next item.
4389  tmp = strpbrk(data, " \n");
4390  if (tmp == NULL) {
4391  LOG(llevError, "Unexpected end of file in pressure map.\n");
4392  bufferreader_destroy(bfr);
4393  return -1;
4394  }
4395  // Okay, so we want the first character after the space/newline.
4396  data = tmp + 1;
4397  }
4398  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4399  if (*data == '\n')
4400  ++data;
4401  }
4402  bufferreader_destroy(bfr);
4403  LOG(llevDebug, "Done.\n");
4404  return 0;
4405 }
4406 
4420  char filename[MAX_BUF], *data;
4421  BufferReader *bfr;
4422  int sx, sy, res;
4423 
4424  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings->localdir);
4425  LOG(llevDebug, "Reading current weather position from %s...\n", filename);
4426  // Read the file into a buffer.
4427  bfr = bufferreader_init_from_file(NULL, filename, "Can't open %s: %s.\n", llevError);
4428  // If we fail, set wmperformstartx/y to their default values.
4429  if (bfr == NULL) {
4430  wmperformstartx = -1;
4431  wmperformstarty = 0;
4432  return 1;
4433  }
4434  data = bufferreader_data(bfr);
4435  // Read from the buffer
4436  res = sscanf(data, "%d %d", &sx, &sy);
4437  // Whether success or failure, we're done with the buffer.
4438  bufferreader_destroy(bfr);
4439  // Now we check our result.
4440  if (res != 2) {
4441  LOG(llevError, "Weather position file was malformed. Using default position.\n");
4442  wmperformstartx = -1;
4443  wmperformstarty = 0;
4444  return 1;
4445  }
4446  LOG(llevDebug, "curposx=%d curposy=%d\n", sx, sy);
4447 
4448  if (sx > settings->worldmaptilesx) {
4449  sx = -1;
4450  }
4451  if (sy > settings->worldmaptilesy) {
4452  sy = 0;
4453  }
4454  // Now we apply these to the static variables.
4455  wmperformstartx = sx;
4456  wmperformstarty = sy;
4457  return 0;
4458 }
4459 
4460 /********************************************************************************************
4461  * Section END -- weather data readers
4462  ********************************************************************************************/
4463 
4464 /********************************************************************************************
4465  * Section -- weather event listeners
4466  ********************************************************************************************/
4467 
4477 static int weather_listener(int *type, ...) {
4478  va_list args;
4479  int code;
4480  mapstruct *m;
4481 
4482  va_start(args, type);
4483  code = va_arg(args, int);
4484 
4485  switch (code) {
4486  case EVENT_MAPENTER:
4487  // At this point, we don't need the entering object for this.
4488  // but it is passed as an arg, so just skip it.
4489  va_arg(args, object *);
4490  /* FALLTHROUGH */
4491  case EVENT_MAPLOAD:
4492  case EVENT_MAPREADY:
4493  m = va_arg(args, mapstruct *);
4494  if (m->outdoor)
4496  break;
4497  }
4498 
4499  va_end(args);
4500 
4501  return 0;
4502 }
4503 
4516 static int weather_clock_listener(int *type, ...) {
4517  va_list args;
4518  int code;
4519 
4520  va_start(args, type);
4521  code = va_arg(args, int);
4522  va_end(args);
4523 
4524  switch (code) {
4525  case EVENT_CLOCK:
4526  if (!(pticks%PTICKS_PER_CLOCK)) {
4527  /* call the weather calculators, here, in order */
4528  tick_weather();
4529  // At every hour, measure the rainfall.
4530  // pticks%PTICKS_PER_CLOCK is already triggering every hour.
4531  process_rain();
4532  /* perform_weather must follow calculators */
4533  perform_weather();
4534  }
4535  // Handle weather printouts after enough server time.
4536  // By using primes here, rather than inside tick_the_clock(), we can reduce the load on a given tick.
4537  // (pticks%1500 is real busy otherwise)
4538  // This produces the side-effect that the server actually has to be on for that length
4539  // of time without interruption for the save to happen, but most servers are run long-term
4540  // anyway, so this should not be an issue
4541  if (!(pticks%1511))
4543  if (!(pticks%31511))
4545  if (!(pticks%33013))
4547  if (!(pticks%34501))
4549  if (!(pticks%36007))
4551  if (!(pticks%39019))
4553  if (!(pticks%40507))
4555  if (settings.fastclock > 0 && !(pticks%42013))
4556  write_skymap();
4557  if (!(pticks%43517))
4559  break;
4560  }
4561 
4562  return 0;
4563 }
4564 
4574 static int weather_object_listener(int *type, ...) {
4575  va_list args;
4576  int code;
4577  object *op;
4578 
4579  va_start(args, type);
4580  code = va_arg(args, int);
4581  // At this point, we don't need the entering object for this.
4582  // but it is passed as an arg, so just skip it.
4583  op = va_arg(args, object *);
4584 
4585  va_end(args);
4586 
4587  switch (code) {
4588  case EVENT_TIME:
4589  // Have weather affect the position of the object. Do not blow the floors, though -- that is bad.
4590  if (op->map && !QUERY_FLAG(op, FLAG_IS_FLOOR)) {
4591  uint8_t dir = wind_blow_object(op->map, op->x, op->y, op->move_type, op->weight+op->carrying, &op->stats);
4592  mapstruct *m;
4593  int16_t x, y;
4594  // By checking only the head space, we can actually caue sailing galleons to irrecoverably crash ashore.
4595  // This is deliberate behavior.
4596  if (dir &&
4597  // We will avoid pushing empty transports. Assume they are anchored/parked.
4598  !(op->type == TRANSPORT && op->inv == NULL) &&
4599  // If our player is in a transport, do not push the player
4600  !(op->type == PLAYER && op->contr && op->contr->transport) &&
4601  !(get_map_flags(op->map, &m, op->x+freearr_x[dir], op->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP) &&
4602  !blocked_link(op, m, x, y)) {
4603  object_remove(op);
4604  object_insert_in_map_at(op, m, op, 0, x, y);
4605 
4606  // Make sure to update the player view
4607  if (op->type == PLAYER) {
4608  esrv_map_scroll(op->contr->socket, freearr_x[dir], freearr_y[dir]);
4609  op->contr->socket->update_look = 1;
4610  op->contr->socket->look_position = 0;
4611  } else if (op->type == TRANSPORT) {
4612  FOR_INV_PREPARE(op, pl)
4613  if (pl->type == PLAYER) {
4614  pl->contr->do_los = 1;
4615  pl->map = op->map;
4616  pl->x = op->x;
4617  pl->y = op->y;
4618  esrv_map_scroll(pl->contr->socket, freearr_x[dir], freearr_y[dir]);
4619  pl->contr->socket->update_look = 1;
4620  pl->contr->socket->look_position = 0;
4621  }
4622  FOR_INV_FINISH();
4623  }
4624  }
4625  }
4626  }
4627  return 0;
4628 }
4629 
4638 static void command_weather (object *op, const char *params) {
4639  int wx, wy, temp, sky;
4640  const char *buf;
4641 
4642  if (wset.dynamiclevel < 1) {
4644  "The weather is perpetually great around here.");
4645  return;
4646  }
4647 
4648  if (op->map == NULL)
4649  return;
4650 
4651  if (worldmap_to_weathermap(op->x, op->y, &wx, &wy, op->map) != 0) {
4653  "You can't see the weather from here.");
4654  return;
4655  }
4656 
4657  if (QUERY_FLAG(op, FLAG_WIZ)) {
4658  /* dump the weather, Dm style! Yo! */
4659 
4661  "Real temp: %d", real_world_temperature(op->x, op->y, op->map));
4662 
4664  "Base temp: %d", weathermap[wx][wy].temp);
4665 
4667  "Humid: %d", weathermap[wx][wy].humid);
4668 
4670  "Wind: dir=%d speed=%d", weathermap[wx][wy].winddir, weathermap[wx][wy].windspeed);
4671 
4673  "Pressure: %d", weathermap[wx][wy].pressure);
4674 
4676  "Avg Elevation: %d", weathermap[wx][wy].avgelev);
4677 
4679  "Rainfall: %d Water: %d", weathermap[wx][wy].rainfall, weathermap[wx][wy].water);
4680  }
4681 
4682  temp = real_world_temperature(op->x, op->y, op->map);
4684  "It's currently %d degrees Centigrade out.", temp);
4685 
4686  /* humid */
4687  // We use buf real quick here for the same reason we use it for sky conditions: clarity
4688  if (weathermap[wx][wy].humid < 20)
4689  buf = "It is very dry.";
4690  else if (weathermap[wx][wy].humid < 40)
4691  buf = "It is rather dry.";
4692  else if (weathermap[wx][wy].humid < 60)
4693  buf = "It is very comfortable today.";
4694  else if (weathermap[wx][wy].humid < 75)
4695  buf = "It is a bit muggy.";
4696  else if (weathermap[wx][wy].humid < 85)
4697  buf = "It is muggy.";
4698  else
4699  buf = "It is uncomfortably muggy.";
4700  // buf will have a string, based on the if clauses above
4702 
4703  /* wind */
4704  switch (weathermap[wx][wy].winddir) {
4705  case 1:
4706  buf = "north";
4707  break;
4708  case 2:
4709  buf = "northeast";
4710  break;
4711  case 3:
4712  buf = "east";
4713  break;
4714  case 4:
4715  buf = "southeast";
4716  break;
4717  case 5:
4718  buf = "south";
4719  break;
4720  case 6:
4721  buf = "southwest";
4722  break;
4723  case 7:
4724  buf = "west";
4725  break;
4726  case 8:
4727  buf = "northwest";
4728  break;
4729  }
4730  if (weathermap[wx][wy].windspeed < 5)
4732  "There is a mild breeze coming from the %s.", buf);
4733  else if (weathermap[wx][wy].windspeed < 10)
4735  "There is a strong breeze coming from the %s.", buf);
4736  else if (weathermap[wx][wy].windspeed < 15)
4738  "There is a light wind coming from the %s.", buf);
4739  else if (weathermap[wx][wy].windspeed < 25)
4741  "There is a strong wind coming from the %s.", buf);
4742  else if (weathermap[wx][wy].windspeed < 35)
4744  "There is a heavy wind coming from the %s.", buf);
4745  else
4747  "The wind from the %s is incredibly strong!", buf);
4748 
4749  // If the current tile is below freezing, refer to the tile as snowing, even if the weathermap says rain.
4750  sky = weathermap[wx][wy].sky;
4751  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG)
4752  sky += 10; /*let it snow*/
4753  // Likewise, if the given tile is above freezing when the weathermap as a whole is below, say rain
4754  else if (temp > 0 && sky >= SKY_LIGHT_SNOW)
4755  sky -= 10;
4756 
4757  // Recycle buf for printing the various sky conditions strings.
4758  // This way we can cleanly call draw_ext_info once and not have
4759  // a bajillion calls with the same parameters other than the text.
4760  switch (sky) {
4761  case SKY_CLEAR:
4762  buf = "There isn't a cloud in the sky.";
4763  break;
4764  case SKY_LIGHTCLOUD:
4765  buf = "There are a few light clouds in the sky";
4766  break;
4767  case SKY_OVERCAST:
4768  buf = "The sky is cloudy and dreary.";
4769  break;
4770  case SKY_LIGHT_RAIN:
4771  buf = "It is raining softly.";
4772  break;
4773  case SKY_RAIN:
4774  buf = "It is raining.";
4775  break;
4776  case SKY_HEAVY_RAIN:
4777  buf = "It is raining heavily.";
4778  break;
4779  case SKY_HURRICANE:
4780  buf = "There is a heavy storm! You should go inside!";
4781  break;
4782  case SKY_FOG:
4783  buf = "It's foggy and miserable.";
4784  break;
4785  case SKY_HAIL:
4786  buf = "It's hailing out! Take cover!";
4787  break;
4788  case SKY_LIGHT_SNOW:
4789  buf = "Snow is gently falling from the sky.";
4790  break;
4791  case SKY_SNOW:
4792  buf = "It is snowing out.";
4793  break;
4794  case SKY_HEAVY_SNOW:
4795  buf = "Snow is falling very heavily.";
4796  break;
4797  case SKY_BLIZZARD:
4798  buf = "A full blown blizzard is in effect. You might want to take cover!";
4799  break;
4800  default:
4801  buf = NULL;
4802  }
4803  // If the sky is valid, then print the conditions.
4804  if (buf != NULL)
4806 }
4807 
4808 /********************************************************************************************
4809  * Section END -- weather event listeners
4810  ********************************************************************************************/
4811 
4812 // Event/command handler ids start at 1, so 0 is an unset flag.
4817 
4819 
4825  int x, tx, ty;
4826 
4827  // Initialize weather settings
4829 
4830  /* all this stuff needs to be set, otherwise this function will cause
4831  * chaos and destruction.
4832  */
4833  if (wset.dynamiclevel < 1) {
4834  LOG(llevInfo, "cfweather_init: dynamic level set to %d. Not loading weather.\n", wset.dynamiclevel);
4835  return;
4836  }
4837 
4838  if (settings->worldmapstartx < 1 || settings->worldmapstarty < 1 ||
4841  return;
4842  }
4843  // Initialize the forestry information from file.
4844  init_config_vals(settings, "treedefs", &forest_list);
4845  init_config_vals(settings, "waterdefs", &water_list);
4846 
4847  /*prepare structures used for avoidance*/
4848  init_weatheravoid(settings, "wavoiddefs", &weather_avoids);
4849  init_weatheravoid(settings, "gavoiddefs", &growth_avoids);
4850 
4851  init_weather_replace(settings, "wreplacedefs", &weather_replace);
4854 
4855  // Set up weathermap grid. This is needed for just about everything
4856  LOG(llevDebug, "Initializing the weathermap...\n");
4857 
4858  weathermap = (weathermap_t **)malloc(sizeof(weathermap_t *)*WEATHERMAPTILESX);
4859  if (weathermap == NULL) {
4861  }
4862  for (x = 0; x < WEATHERMAPTILESX; x++) {
4863  weathermap[x] = (weathermap_t *)calloc(WEATHERMAPTILESY, sizeof(weathermap_t));
4864  if (weathermap[x] == NULL) {
4866  }
4867  }
4868  /* Unless you know what you're doing, do not re-order these
4869  * I think I got all the dependencies noted, but it works this way
4870  * and I'd advise against changing the order unless you have a good reason.
4871  */
4873  // Begin to initialize the various data pieces for the weather
4874  // If wind direction did initialization, we don't need to read the wind speed from file.
4875  if (read_winddirmap(settings) == 0)
4878  // Some gulf stream fiddling that happens every startup.
4879  gulf_stream_direction = rndm(0, 1);
4880  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
4881  for (ty = 0; ty < WEATHERMAPTILESY-1; ty++) {
4882  if (gulf_stream_direction) {
4883  switch (gulf_stream_dir[tx][ty]) {
4884  case 2: gulf_stream_dir[tx][ty] = 6; break;
4885  case 3: gulf_stream_dir[tx][ty] = 7; break;
4886  case 4: gulf_stream_dir[tx][ty] = 8; break;
4887  }
4888  } else {
4889  switch (gulf_stream_dir[tx][ty]) {
4890  case 6: gulf_stream_dir[tx][ty] = 2; break;
4891  case 7: gulf_stream_dir[tx][ty] = 3; break;
4892  case 8: gulf_stream_dir[tx][ty] = 4; break;
4893  }
4894  }
4895  }
4896  }
4897  // Trees help stabilize local temperature and evaporate water from deeper underground.
4898  // This is calculated at the same time as elevation and humidity.
4899  int result = read_humidmap(settings);
4900  // If result is 1, then we initialized all these.
4901  // When that is the case, we don't need to read in from the file.
4902  // If result is -1, everything's jacked up anyway, so still don't load.
4903  if (result == 0) {
4904  read_watermap(settings); /* On first run, we want to do this after humidity. Otherwise, it doesn't seem to matter */
4905  read_elevmap(settings); /* elevation must allways follow humidity */
4907  }
4910 
4911  LOG(llevDebug, "Done reading weathermaps\n");
4912  // Initialize the sky so we can get accurate precipitation at initial load.
4913  compute_sky();
4914 
4915  // Get current map position
4917 
4918  // Connect the events after initialization, since we don't need to do
4919  // precipitation when we're initializing.
4921  //global_mapload_handler = events_register_global_handler(EVENT_MAPLOAD, weather_listener);
4924  // Register the 'weather command
4926  /* Disable the plugin in case it's still there */
4927  serv->disabled_plugins.push_back(strdup("cfweather"));
4928 }
4929 
4931  // Define temp pointers for clearing up the linked lists
4932  DensityConfig *cur;
4933  weather_avoids_t *avcur;
4934  weather_replace_t *rpcur;
4935  // Unregister handlers.
4936  if (global_map_handler != 0)
4938  if (global_clock_handler != 0)
4940  if (global_object_handler != 0)
4942  if (command_handler != 0)
4944  // Free the weathermap
4945  // Turns out that asset collection reaches here with weathermap as NULL,
4946  // so we need to make sure it is nonnull before trying to free it.
4947  if (weathermap != NULL) {
4948  for (int x = 0; x < WEATHERMAPTILESX; x++) {
4950  }
4952  }
4953  // Deallocate our linked list of forest entries.
4954  while (forest_list != NULL) {
4955  cur = forest_list;
4957  free_string(cur->name);
4958  free(cur);
4959  }
4960  // Do the same for the water list
4961  while (water_list != NULL) {
4962  cur = water_list;
4964  free_string(cur->name);
4965  free(cur);
4966  }
4967  // Free our avoid lists
4968  while (weather_avoids != NULL) {
4969  avcur = weather_avoids;
4971  free_string(avcur->name);
4972  free(avcur);
4973  }
4974  while (growth_avoids != NULL) {
4975  avcur = growth_avoids;
4977  free_string(avcur->name);
4978  free(avcur);
4979  }
4980  // Free our replacement lists
4981  while (weather_replace != NULL) {
4982  rpcur = weather_replace;
4984  free_string(rpcur->tile);
4985  free(rpcur);
4986  }
4987  while (weather_evaporate != NULL) {
4988  rpcur = weather_evaporate;
4990  free_string(rpcur->tile);
4991  free(rpcur);
4992  }
4993  while (weather_snowmelt != NULL) {
4994  rpcur = weather_snowmelt;
4996  free_string(rpcur->tile);
4997  free(rpcur);
4998  }
4999 }
5000 
5001 
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:2991
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:1428
global_mapload_handler
static event_registration global_mapload_handler
Definition: cfweather.cpp:4816
bufferreader_data
char * bufferreader_data(BufferReader *br)
Get the whole buffer, independently of the calls to bufferreader_next_line().
Definition: bufferreader.cpp:148
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:1300
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:140
llevError
@ llevError
Error, serious thing.
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:2878
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:58
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:1757
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:3008
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:1780
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:4293
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:3298
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:3161
temperature_calc
static void temperature_calc(const int x, const int y, const timeofday_t *tod)
Calculate temperature of a spot.
Definition: cfweather.cpp:1028
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:4357
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:40
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:617
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:4142
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:1559
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:2668
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:3842
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:2553
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:4238
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:1582
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:4818
plant_a_garden
static void plant_a_garden(mapstruct *const m)
Process growth of various plants.
Definition: cfweather.cpp:1973
init_humid_elev
static void init_humid_elev(const Settings *settings)
Initialize humidity, water, forestry, and elevation.
Definition: cfweather.cpp:2742
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:4419
read_watermap
static int read_watermap(const Settings *settings)
Load water information from localdir.
Definition: cfweather.cpp:3965
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:4824
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:618
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:65
directions
static const uint32_t directions[]
Colours used for wind directions.
Definition: cfweather.cpp:3508
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:3331
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:1481
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:535
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:533
RED
#define RED
Definition: cfweather.cpp:3497
read_elevmap
static int read_elevmap(const Settings *settings)
Load the elevation information.
Definition: cfweather.cpp:3906
DensityConfig::value_density
int value_density
Definition: cfweather.cpp:122
skies
static const uint32_t skies[]
Colours used for weather types.
Definition: cfweather.cpp:3522
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:647
GREEN
#define GREEN
Definition: cfweather.cpp:3498
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:1304
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:1444
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:787
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:3374
global_clock_handler
static event_registration global_clock_handler
Definition: cfweather.cpp:4814
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:3407
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:2861
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:3779
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:3499
WEATHER_NO_SAVE
#define WEATHER_NO_SAVE
Definition: cfweather.cpp:619
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:2318
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:895
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1708
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:4813
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:1780
spin_globe
static void spin_globe()
The world spinning drags the weather with it.
Definition: cfweather.cpp:1158
DensityConfig::name
sstring name
Definition: cfweather.cpp:118
init_gulfstreammap
static void init_gulfstreammap()
Initialize the gulf stream.
Definition: cfweather.cpp:2909
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:968
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:1400
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:4930
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:2248
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:3196
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:3126
llevInfo
@ llevInfo
Information.
Definition: logger.h:12
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:4815
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:874
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:1101
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:601
weather_object_listener
static int weather_object_listener(int *type,...)
Global object-process handling for weather.
Definition: cfweather.cpp:4574
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:1290
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:680
init_weather_settings
static void init_weather_settings(Settings *settings)
Definition: cfweather.cpp:3045
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:3469
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:4023
command_weather
static void command_weather(object *op, const char *params)
Player is wondering about the weather.
Definition: cfweather.cpp:4638
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:712
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:3440
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:4085
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:3265
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:4477
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:3231
weather_effect
static void weather_effect(mapstruct *const m)
Perform actual effect of weather.
Definition: cfweather.cpp:2206
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:1407
change_the_world
static void change_the_world(mapstruct *const m)
Process worldmap regrowth.
Definition: cfweather.cpp:2072
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:2386
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:4516
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:1228
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:21
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:1202
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:3556
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
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:2711
perform_pressure
static void perform_pressure()
Perform small randomizations in the pressure map.
Definition: cfweather.cpp:827
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:102
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:375
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:2471
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254