Crossfire Server, Trunk  1.75.0
cfweather.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2021 The Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
20 #include "global.h"
21 #include "server.h"
22 #include "map.h"
23 #include "object.h"
24 #include "output_file.h"
25 #include "sproto.h"
26 
27 #include <string.h>
28 #include <assert.h>
29 #include <math.h>
30 
31 /* weather constants */
32 
33 #define POLAR_BASE_TEMP 0 /* C */
34 #define EQUATOR_BASE_TEMP 30 /* C */
35 #define SEASONAL_ADJUST 10 /* polar distance */
36 #define GULF_STREAM_WIDTH 3 /* width of gulf stream */
37 #define GULF_STREAM_BASE_SPEED 40 /* base speed of gulf stream */
38 
39 /* don't muck with these unless you are sure you know what they do */
40 #define PRESSURE_ITERATIONS 30
41 #define PRESSURE_AREA 180
42 #define PRESSURE_ROUNDING_FACTOR 2
43 #define PRESSURE_ROUNDING_ITER 1
44 #define PRESSURE_SPIKES 3
45 #define PRESSURE_MAX 1040
46 #define PRESSURE_MIN 960
47 
48 /* sky conditions */
49 #define SKY_CLEAR 0
50 #define SKY_LIGHTCLOUD 1
51 #define SKY_OVERCAST 2
52 #define SKY_LIGHT_RAIN 3
53 #define SKY_RAIN 4 /* rain -> storm has lightning */
54 #define SKY_HEAVY_RAIN 5
55 #define SKY_HURRICANE 6
56 /* wierd weather 7-12 */
57 #define SKY_FOG 7
58 #define SKY_HAIL 8
59 /* snow */
60 #define SKY_LIGHT_SNOW 13 /* add 10 to rain to get snow */
61 #define SKY_SNOW 14
62 #define SKY_HEAVY_SNOW 15
63 #define SKY_BLIZZARD 16
64 
72 #define WIND_FACTOR 4.0
73 
74 /* editing the below might require actual changes to code */
75 #define WEATHERMAPTILESX 100
76 #define WEATHERMAPTILESY 100
77 
78 static int worldmap_to_weathermap(const int x, const int y, int * const wx, int * const wy, mapstruct * const m);
79 
80 /********************************************************************************************
81  * Section -- weather structures
82  * Structures to handle various aspects of the weather system.
83  ********************************************************************************************/
84 
94 struct weathermap_t {
95  int16_t temp;
96  int16_t pressure;
97  int8_t humid;
98  int8_t windspeed;
99  int8_t winddir;
100  int8_t sky;
101  int32_t avgelev;
102  uint32_t rainfall;
103  uint8_t darkness;
104  int8_t water;
105  int8_t forestry;
106  /*Dynamic parts*/
107  int16_t realtemp;
108 };
109 
117  // Use shared strings so we can do pointer comparisons.
119  // 0 if name is the arch name, 1 if it is the object name.
120  int is_obj;
121  // The density the tile type counts for.
123  // Pointer to the next item in the list
124  // We're scanning all of these when we check anyway,
125  // so might as well use a structure that works fine that way.
127 };
128 
134  int snow;
137 };
138 
148 };
149 
154  const char *herb;
155  const char *tile;
156  int random;
157  float rfmin;
158  float rfmax;
159  int humin;
160  int humax;
161  int tempmin;
162  int tempmax;
163  int elevmin;
164  int elevmax;
165  int season;
166 };
167 
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 < int(settings.worldmapstartx) ||
480  fy < int(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 < int(settings.worldmapstartx)) {
497  return -1;
498  }
500  fy < int(settings.worldmapstarty)) {
501  return -1;
502  }
503  fx -= settings.worldmapstartx;
504  fy -= settings.worldmapstarty;
505 
506  nx = fx * wset.worldmaptilesizex+x;
507  ny = fy * wset.worldmaptilesizey+y;
508 
509  *wx = nx/spwtx;
510  *wy = ny/spwty;
511 
512  return 0;
513 }
514 
534 static object *avoid_weather(int * const av, const mapstruct *m, const int x, const int y, int * const gs, const int grow) {
535  int avoid, gotsnow;
536  object *tmp, *snow = NULL;
537  const weather_avoids_t *cur;
538 
539  avoid = 0;
540  gotsnow = 0;
541  if (grow) {
542  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
543  /* look for things like walls, holes, etc */
544  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !(tmp->material&M_ICE || tmp->material&M_LIQUID)) {
545  gotsnow++;
546  snow = tmp;
547  }
548  for (cur = growth_avoids; cur; cur = cur->next) {
549  // Due to the use of shared strings, we can do pointer comparison here.
550  if (tmp->arch->name == cur->name) {
551  avoid++;
552  break;
553  }
554  }
555  if (avoid && gotsnow) {
556  break;
557  }
558  }
559  } else {
560  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
561  for (cur = weather_avoids; cur; cur = cur->next) {
562  if (tmp->arch->name == cur->name) {
563  // We clear FLAG_IS_FLOOR for our snow. The map's default snow does not.
564  // Avoid weirdness on the pathway to Brest and at the south pole by checking for non-floor snow
565  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && cur->snow == 1) {
566  gotsnow++;
567  snow = tmp;
568  } else {
569  avoid++;
570  }
571  break;
572  }
573  }
574  if (avoid && gotsnow) {
575  break;
576  }
577  }
578  }
579  *gs = gotsnow;
580  *av = avoid;
581 
582  return snow;
583 }
584 
602 static int check_replace_match(const object *ob, const weather_replace_t *rep_struct) {
603  if (rep_struct->arch_or_name == 1) {
604  if (ob->arch->name == rep_struct->tile) {
605  return 1;
606  }
607  } else {
608  if (ob->name == rep_struct->tile) {
609  return 1;
610  }
611  }
612  return 0;
613 }
614 
618 #define WEATHER_OVERLAY 1 /* If set, we set FLAG_OVERLAY_FLOOR */
619 #define WEATHER_NO_FLOOR 2 /* If set, we clear FLAG_IS_FLOOR */
620 #define WEATHER_NO_SAVE 4 /* If set, we set FLAG_NO_SAVE */
621 
648 static void do_weather_insert(mapstruct * const m, int x, int y, const archetype *at, const int8_t object_flags, uint16_t material, int insert_flags) {
649  if (at != NULL) {
650  object *ob = object_new();
651  object_copy(&at->clone, ob);
652  ob->x = x;
653  ob->y = y;
654  if (object_flags & WEATHER_OVERLAY)
656  if (object_flags & WEATHER_NO_FLOOR)
658  if (object_flags & WEATHER_NO_SAVE)
659  SET_FLAG(ob, FLAG_NO_SAVE);
660  if (material)
661  ob->material = material;
662  object_insert_in_map(ob, m, ob, insert_flags);
663  }
664 }
665 
681 static char *get_next_field(char *line) {
682  // The comma is the end of the field
683  line = strchr(line, ',');
684  if (line == NULL)
685  return NULL;
686  // Move past the known field seperator, but null terminate over it for the previous field.
687  *(line++) = '\0';
688  // While spaces or commas, skip the character
689  while (*line == ' ' || *line == ',')
690  ++line;
691  // Next field begins here.
692  return line;
693 }
694 
695 /********************************************************************************************
696  * Section END -- weather data helpers
697  ********************************************************************************************/
698 
699 /********************************************************************************************
700  * Section -- weather data calculators
701  * These functions determine the progression of weather data over time.
702  * This is the bread-and-butter of the weather system.
703  ********************************************************************************************/
704 
705 // We need to declare init_temperature, since it is defined below this area.
706 static void init_temperature();
707 
713 static void smooth_wind() {
714  int x, y;
715  int tx, ty, dx, dy;
716  int minp;
717 
718  /* skip the outer squares.. it makes handling alot easier */
719  dx = 0;
720  dy = 0;
721  for (x = 1; x < WEATHERMAPTILESX-1; x++)
722  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
723  minp = PRESSURE_MAX + 1;
724  for (tx = -1; tx < 2; tx++) {
725  for (ty = -1; ty < 2; ty++) {
726  if (!(tx == 0 && ty == 0)) {
727  if (weathermap[x+tx][y+ty].pressure < minp) {
728  minp = weathermap[x+tx][y+ty].pressure;
729  dx = tx;
730  dy = ty;
731  }
732  }
733  }
734  }
735 
736  /* if the wind is strong, the pressure won't decay it completely */
737  if (weathermap[x][y].windspeed > 5 && !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
738  weathermap[x][y].windspeed -= 2*2;
739  } else {
740  weathermap[x][y].winddir = find_dir_2(dx, dy);
742  }
743  // Disrupt the wind where trees are present (a reduction of up to 5 is possible).
744  weathermap[x][y].windspeed -= weathermap[x][y].forestry/20;
745  if (weathermap[x][y].windspeed < 0) {
746  weathermap[x][y].windspeed = 0;
747  }
748  // The wind moves some of the higher pressure to the lower pressure.
749  weathermap[x][y].pressure -= (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
750  weathermap[x+dx][y+dy].pressure += (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
751  }
752 
753  /* now, lets crank on the speed. When surrounding tiles all have
754  the same speed, inc ours. If it's chaos. drop it.
755  */
756  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
757  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
758  minp = 0;
759  for (tx = -1; tx < 2; tx++) {
760  for (ty = -1; ty < 2; ty++) {
761  if (tx != 0 && ty != 0) {
762  if (similar_direction(weathermap[x][y].winddir, weathermap[x+tx][y+ty].winddir)) {
763  minp++;
764  }
765  }
766  }
767  }
768  if (minp > 4) {
769  weathermap[x][y].windspeed++;
770  }
771  if (minp > 6) {
772  weathermap[x][y].windspeed += 2;
773  }
774  if (minp < 2) {
775  weathermap[x][y].windspeed--;
776  }
777  if (weathermap[x][y].windspeed < 0) {
778  weathermap[x][y].windspeed = 0;
779  }
780  }
781  }
782 }
783 
788 static void smooth_pressure() {
789  int x, y;
790  int k;
791 
792  for (k = 0; k < PRESSURE_ROUNDING_ITER; k++) {
793  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
794  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
795  weathermap[x][y].pressure = (weathermap[x][y].pressure*
797  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
798  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
799  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
801  }
802  }
803  for (x = WEATHERMAPTILESX-2; x > 0; x--) {
804  for (y = WEATHERMAPTILESY-2; y > 0; y--) {
805  weathermap[x][y].pressure = (weathermap[x][y].pressure*
807  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
808  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
809  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
811  }
812  }
813  }
814 
815  // Clip to our valid pressure range
816  for (x = 0; x < WEATHERMAPTILESX; x++)
817  for (y = 0; y < WEATHERMAPTILESY; y++) {
818  weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
819  weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
820  }
821 
822 }
823 
828 static void perform_pressure() {
829  int x, y, l, n, j, k, is_storm;
830 
831  /* create random spikes in the pressure */
832  for (l = 0; l < PRESSURE_SPIKES; l++) {
833  x = rndm(0, WEATHERMAPTILESX-1);
834  y = rndm(0, WEATHERMAPTILESY-1);
835  // This goes beyond the valid bounds so that the smoothing proces ends up
836  // making different-sized pressure spikes.
837  n = rndm(600, 1300);
838  weathermap[x][y].pressure = n;
839  // Get close to the edge. But, to make things cleaner, don't go off the edge.
840  if (x >= 2 && y >= 2 && x < WEATHERMAPTILESX-2 && y < WEATHERMAPTILESY-2) {
841  /* occasionally add a storm
842  * and make sure the whole pressure spot is a storm, not just pieces of it
843  *
844  * Also, only try to make storms out of low pressure spikes. 1013 mbar
845  * Is standard pressure at sea level.
846  */
847  is_storm = (n < 1013 && rndm(1, 10) == 1);
848  for (j = x-2; j < x+2; j++) {
849  for (k = y-2; k < y+2; k++) {
850  weathermap[j][k].pressure = n;
851  if (is_storm) {
852  weathermap[j][k].humid = rndm(50, 90);
853  }
854  }
855  }
856  }
857  }
858 
859  for (x = 0; x < WEATHERMAPTILESX; x++) {
860  for (y = 0; y < WEATHERMAPTILESY; y++) {
861  weathermap[x][y].pressure += rndm(-1, 4);
862  }
863  }
864 
865  smooth_pressure();
866 }
867 
875 static const int season_tempchange[HOURS_PER_DAY] = {
876 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 */
877  0, 0, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
878 
896 static int real_temperature(int x, int y, const timeofday_t *tod) {
897  int i, temp, adj;
898 
899  // Clear and partly-cloudy skies have a stronger temperature effect
900  // than overcast skies, since clouds create a barrier to heat escaping
901  // and sunlight entering. Super thick clouds add additional buffer.
902  // If adj is set to one, then the weather provides some amount of buffer effect.
903  // This buffer will override the forestry one if it is set.
904  switch (weathermap[x][y].sky) {
905  case SKY_CLEAR:
906  case SKY_LIGHTCLOUD:
907  adj = 0;
908  break;
909  case SKY_HURRICANE:
910  case SKY_BLIZZARD:
911  adj = 2;
912  break;
913  default:
914  adj = 1;
915  break;
916  }
917 
918  /* adjust for time of day */
919  temp = weathermap[x][y].temp;
920  for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
921  temp += season_tempchange[i];
922  /* high amounts of water has a buffering effect on the temp */
923  if (weathermap[x][y].water > 33) {
924  i += weathermap[x][y].water/33;
925  }
926  // Cloudy skies will have a buffering effect on the temperature
927  if (adj >= 1)
928  i += adj;
929  // High amounts of trees also provide some amount of buffering under clear skies
930  else if (weathermap[x][y].forestry > 60) {
931  i++;
932  }
933  }
934  for (i = 0; i <= tod->hour; i++) {
935  temp += season_tempchange[i];
936  if (weathermap[x][y].water > 33) {
937  i += weathermap[x][y].water/33;
938  }
939  // Cloudy skies will have a buffering effect on the temperature
940  if (adj >= 1)
941  i += adj;
942  // High amounts of trees also provide some amount of buffering under clear skies
943  else if (weathermap[x][y].forestry > 60) {
944  i++;
945  }
946  }
947 
948  /* windchill */
949  for (i = 1; i < weathermap[x][y].windspeed; i += i) {
950  temp--;
951  }
952 
953  return temp;
954 }
955 
969 int real_world_temperature(int x, int y, mapstruct *m) {
970  int wx, wy, temp, eleva, elevb, trees;
971  object *op;
972  timeofday_t tod;
973 
974  // Get the time of day for real_temperature
975  // Since real_temperature is sometimes called in a loop, it expects
976  // the time of day to be provided to it instead of calculating it directly.
977  get_tod(&tod);
978 
979  /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
980  worldmap_to_weathermap(x, y, &wx, &wy, m);
981  temp = real_temperature(wx, wy, &tod);
982  if (weathermap[wx][wy].avgelev < 0) {
983  eleva = 0;
984  } else {
985  eleva = weathermap[x][y].avgelev;
986  }
987 
988  op = GET_MAP_OB(m, x, y);
989  if (!op) {
990  return eleva;
991  }
992 
993  elevb = op->elevation;
994  if (elevb < 0) {
995  elevb = 0;
996  }
997  if (elevb > eleva) {
998  elevb -= eleva;
999  temp -= elevb/1000;
1000  } else {
1001  elevb = eleva - elevb;
1002  temp += elevb/1000;
1003  }
1004 
1005  // Get localized effects from trees, too.
1006  trees = get_config_tile(x, y, m, forest_list);
1007  // Sparse trees reduce local temp by 1.
1008  // Dense trees raise it by one.
1009  if (trees > 0) {
1010  if (trees < 4)
1011  --temp;
1012  else
1013  ++temp;
1014  }
1015  // And done!
1016  return temp;
1017 }
1018 
1029 static void temperature_calc(const int x, const int y, const timeofday_t *tod) {
1030  int dist, equator, elev, n, trees;
1031  float diff, tdiff;
1032 
1033  // Warmer air has higher pressure than colder air.
1034  // Store the old value for temperature.
1035  int oldtemp = weathermap[x][y].temp, tempdiff;
1036 
1037  equator = (WEATHERMAPTILESX+WEATHERMAPTILESY)/4;
1038  diff = (float)(EQUATOR_BASE_TEMP-POLAR_BASE_TEMP)/(float)equator;
1039  tdiff = (float)SEASONAL_ADJUST/(float)(MONTHS_PER_YEAR/2.0);
1040  equator *= 2;
1041  n = 0;
1042  /* we essentially move the equator during the season */
1043  if (tod->month > (MONTHS_PER_YEAR/2)) { /* EOY */
1044  n -= (tod->month*tdiff);
1045  } else {
1046  n = (MONTHS_PER_YEAR - tod->month)*tdiff;
1047  }
1048  dist = polar_distance(x-n/2, y-n/2, equator);
1049 
1050  /* now we have the base temp, unadjusted for time. Time adjustment
1051  is not recorded on the map, rather, it's done JIT. */
1052  weathermap[x][y].temp = (int)(dist * diff);
1053  /* just scrap these for now, its mostly ocean */
1054  if (weathermap[x][y].avgelev < 0) {
1055  elev = 0;
1056  } else {
1057  // Make sure that higher elevations cause lower temps.
1058  elev = MIN(20000, weathermap[x][y].avgelev)/1000;
1059  }
1060  weathermap[x][y].temp -= elev;
1061 
1068  // Arbitrarily make the cutoff threshold for heat-hold as 60
1069  trees = weathermap[x][y].forestry;
1070  // Dense trees can raise the temperature up to ~3 degrees, per the calculations below.
1071  if (trees >= 60) {
1072  weathermap[x][y].temp += (trees-60)/15;
1073  }
1074  // If not, then we have heat reduction, most effective (~4 degrees) at 30.
1075  else if (trees >= 30){
1076  weathermap[x][y].temp -= (60-trees)/8;
1077  }
1078  else {
1079  weathermap[x][y].temp -= trees/8;
1080  }
1081 
1082  // Now we determine the difference in temperature and adjust the pressure accordingly.
1083  tempdiff = weathermap[x][y].temp - oldtemp;
1084  // The rate (arbitrarily chosen) for temperature-to-pressure change is 1 degrees per millibar
1085  // I'd have to keep track of partial millibar changes if I wanted to be coarser in this.
1086  if (tempdiff != 0)
1087  weathermap[x][y].pressure += tempdiff;
1088 }
1089 
1102 void compute_sky() {
1103  int x, y;
1104  int temp;
1105  int calc, inv_pressure;
1106  float press_root, max_root = sqrt(PRESSURE_MAX-PRESSURE_MIN);
1107  timeofday_t tod;
1108 
1109  // Before we begin the loops, we get the time of day for real_temperature()
1110  get_tod(&tod);
1111 
1112  for (x = 0; x < WEATHERMAPTILESX; x++) {
1113  for (y = 0; y < WEATHERMAPTILESY; y++) {
1114  temp = real_temperature(x, y, &tod);
1115  // Make sure we clip to the allowed pressure range.
1116  inv_pressure = MAX(0, MIN(PRESSURE_MAX-PRESSURE_MIN, (PRESSURE_MAX - weathermap[x][y].pressure)));
1117  // Take the square root. This allows us to have values weighted toward
1118  // producing rain. max_root holds the maximum value this could be.
1119  press_root = sqrt(inv_pressure);
1120  calc = MAX(0, MIN((int)(max_root*100), (int)(press_root * weathermap[x][y].humid)));
1121  // max_root*100 / 7 is the smallest we can feasibly divide by without side effects
1122  // So as long as we divide by a number greater than that, we're good.
1123  // If we divide by smaller, we overrun the sequential weather numbers, and reach FOG and HAIL
1124  // when not encountering their special cases.
1125  calc /= (int)(max_root*100 / 7) + 1;
1126 
1127  // If wind speed is high enough and we have rain, we can add one.
1128  if (calc >= SKY_LIGHT_RAIN && calc < SKY_HURRICANE && weathermap[x][y].windspeed > 30)
1129  ++calc;
1130  // If we are cold enough we have snow.
1131  if (temp <= 0 && calc >= SKY_LIGHT_RAIN)
1132  calc += 10;
1133 
1134  // Keep the old fog/hail generation for now
1135  if (weathermap[x][y].pressure >= 980 && weathermap[x][y].pressure < 1000) {
1136  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
1137  weathermap[x][y].windspeed < 3) {
1138  calc = SKY_FOG; /* rare */
1139  }
1140  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
1141  weathermap[x][y].windspeed > 35) {
1142  calc = SKY_HAIL; /* rare */
1143  }
1144  }
1145  weathermap[x][y].sky = calc;
1146  }
1147  }
1148 }
1149 
1159 static void spin_globe() {
1160  int x, xy, xy_eff;
1161  int buffer_humid;
1162  int buffer_sky;
1163  int buffer_pressure;
1164 
1165  // On each pass, x + y is a constant. We shift down and to the left, and wraparound to the upper right.
1166  // The cornermost tiles by the poles to not move as a result, so we can skip them.
1167  for (xy = 1; xy < WEATHERMAPTILESX + WEATHERMAPTILESY - 1; ++xy) {
1168  // Effective xy is essentially clipped to the end.
1169  // xy-xy_eff is thus the bounds on the other side of the map to care about for wraparound.
1170  xy_eff = MIN(xy, WEATHERMAPTILESX-1);
1171  buffer_humid = weathermap[xy-xy_eff][xy_eff].humid;
1172  buffer_sky = weathermap[xy-xy_eff][xy_eff].sky;
1173  buffer_pressure = weathermap[xy-xy_eff][xy_eff].pressure;
1174  for (x = xy-xy_eff; x < xy_eff; ++x) {
1175  /* Using xy directly here *looks* wrong, but is actually not,
1176  * since x = xy-xy_eff+c, where c is one less than the loop count;
1177  * thus, xy-x = xy-xy+xy_eff-c = xy_eff-c.
1178  * This is within the bounds of the map at all times.
1179  */
1180  weathermap[x][xy-x].humid = weathermap[x+1][xy-x-1].humid;
1181  weathermap[x][xy-x].sky = weathermap[x+1][xy-x-1].sky;
1182  weathermap[x][xy-x].pressure = weathermap[x+1][xy-x-1].pressure;
1183  }
1184  weathermap[xy_eff][xy-xy_eff].humid = buffer_humid;
1185  weathermap[xy_eff][xy-xy_eff].sky = buffer_sky;
1186  weathermap[xy_eff][xy-xy_eff].pressure = buffer_pressure;
1187  }
1188 }
1189 
1203 static int humid_tile(const int x, const int y, const int dark) {
1204  // ox and oy denote the neighbor that is influencing us (due to winds from there)
1205  int ox = x, oy = y, humid, evap, tempeffect;
1206 
1207  /* find the square the wind is coming from, without going out of bounds */
1208 
1209  if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
1210  if (y != 0) {
1211  oy = y-1;
1212  }
1213  }
1214  if (weathermap[x][y].winddir >= 6) {
1215  if (x != 0) {
1216  ox = x-1;
1217  }
1218  }
1219  if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
1220  if (y < WEATHERMAPTILESY-1) {
1221  oy = y+1;
1222  }
1223  }
1224  if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
1225  if (x < WEATHERMAPTILESX-1) {
1226  ox = x+1;
1227  }
1228  }
1229  // Determine the effect of sunlight on evaporation.
1230  int light = MAX_DARKNESS - dark;
1231  // The sky conditions affect how strong an effect the sunlight has.
1232  switch (weathermap[x][y].sky) {
1233  case SKY_CLEAR:
1234  tempeffect = light*light/4;
1235  break;
1236  case SKY_LIGHTCLOUD:
1237  tempeffect = light*light/5;
1238  break;
1239  case SKY_OVERCAST:
1240  tempeffect = light;
1241  break;
1242  case SKY_LIGHT_RAIN:
1243  case SKY_LIGHT_SNOW:
1244  tempeffect = light*4/5;
1245  break;
1246  case SKY_RAIN:
1247  case SKY_SNOW:
1248  tempeffect = light/2;
1249  break;
1250  case SKY_HEAVY_RAIN:
1251  case SKY_HEAVY_SNOW:
1252  tempeffect = light/3;
1253  break;
1254  case SKY_HURRICANE:
1255  case SKY_BLIZZARD:
1256  case SKY_HAIL:
1257  tempeffect = light/5;
1258  break;
1259  case SKY_FOG:
1260  default:
1261  tempeffect = 0;
1262  }
1263  // Determine the evaporative component contributing to the humidity.
1264  // The amount of water, the temperature, the wind, the pressure, the time of day, the cloudcover, and the previous humidity all affect the evaporation.
1265  // The exact formula is arbitrary, but it gives values that make some sense.
1266  evap = (weathermap[x][y].water/2+20+tempeffect)*(weathermap[x][y].temp+tempeffect)*weathermap[x][y].windspeed*10*(100-weathermap[x][y].humid)/(weathermap[x][y].pressure*weathermap[x][y].humid+1);
1267  // Don't go negative if temperature gets too low.
1268  evap = MAX(0, evap);
1269  // This is where the magic happens
1270  // If humidity is unstable over time, this is what will need to be tweaked
1271  // (or one of the values it depends on, if not this)
1272  humid = (weathermap[x][y].humid*2 +
1273  (weathermap[ox][oy].humid)*weathermap[ox][oy].windspeed/100 +
1274  // Evaporative components.
1275  evap + weathermap[x][y].forestry/10 + rndm(-3, 7))/
1276  (weathermap[ox][oy].windspeed/100+3)+rndm(-3, 3);
1277  if (humid < 0) {
1278  humid = 0;
1279  }
1280  if (humid > 100) {
1281  humid = 100;
1282  }
1283  return humid;
1284 }
1285 
1291 static void update_humid() {
1292  int x, y, dark = get_world_darkness();
1293 
1294  for (y = 0; y < WEATHERMAPTILESY; y++) {
1295  for (x = 0; x < WEATHERMAPTILESX; x++) {
1296  weathermap[x][y].humid = humid_tile(x, y, dark);
1297  }
1298  }
1299 }
1300 
1305 static void plot_gulfstream() {
1306  int x, y, tx, diroffset, dirdiff, ystart, ydiff, ylimup, ylimlow;
1307 
1308  x = gulf_stream_start;
1309 
1310  // Use the same offset/multiplier formula we used in gulf stream initialization
1311  // to make the code here much cleaner to look at.
1312  if (gulf_stream_direction) {
1313  diroffset = 0;
1314  dirdiff = -1;
1315  // We go from WEATHERMAPTILESY-1 down to 1 for the loop
1316  ystart = WEATHERMAPTILESY-1;
1317  ydiff = -1;
1318  ylimup = WEATHERMAPTILESY;
1319  ylimlow = 0;
1320  }
1321  else {
1322  diroffset = 10;
1323  dirdiff = 1;
1324  // We go from 0 to WEATHERMAPTILESY-2 for the loop
1325  ystart = 0;
1326  ydiff = 1;
1327  ylimup = WEATHERMAPTILESY-1;
1328  ylimlow = -1;
1329  }
1330  for (y = ystart; y > ylimlow && y < ylimup; y += ydiff) {
1331  for (tx = 0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
1332  if (similar_direction(weathermap[x+tx][y].winddir, gulf_stream_dir[tx][y]) && weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5) {
1333  weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
1334  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1335  } else if (gulf_stream_speed[tx][y] > weathermap[x+tx][y].windspeed) {
1336  // Preserve the wind speed of things with a higher speed that the gulf stream itself.
1337  weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
1338  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1339  } // If a storm moves through the gulf stream, it supercedes it with its own high winds.
1340 
1341  if (tx == GULF_STREAM_WIDTH-1) {
1342  switch ((diroffset-gulf_stream_dir[tx][y])*dirdiff) {
1343  case 6: x--; break;
1344  case 7: break;
1345  case 8: x++; break;
1346  }
1347  if (x < 0) {
1348  x++;
1349  }
1351  x--;
1352  }
1353  }
1354  }
1355  }
1356  /* occasionally move the stream
1357  * Arbitrary code from the original implementation says
1358  * 1 in 500 to switch, then 1 in 2 the switch actually is relevant.
1359  *
1360  * So, if we make the outer effect 1 in 1000, we cover both.
1361  */
1362  if (rndm(1, 1000) == 1) {
1363  // Reverse the stream direction.
1365  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
1366  for (y = 0; y < WEATHERMAPTILESY-1; y++) {
1367  // The direction changes here are dir + 4 mod 8, but 8 instead of 0 on those ones.
1368  gulf_stream_dir[tx][y] = (gulf_stream_dir[tx][y] + 4) & 7;
1369  // And we want 8 as a direction instead of 0.
1370  if (gulf_stream_dir[tx][y] == 0)
1371  gulf_stream_dir[tx][y] = 8;
1372  }
1373  }
1374  }
1375  /* Occasionally move the gulf stream starting point.
1376  * Original code had 1 in 25 to try, but 1 in 3 that the move was 0.
1377  *
1378  * So, the chance of actual movement was 2 in 75.
1379  *
1380  * We will use that and redesign the inner offset generation to determine + or - movement.
1381  */
1382  if (rndm(1, 75) <= 2) {
1383  // Only get +1 or -1
1384  gulf_stream_start += 1-2*rndm(0, 1);
1385  // Make sure we don't go off the map.
1388  }
1389  if (gulf_stream_start < 1) {
1391  }
1392  }
1393 }
1394 
1402  int x, y, rain;
1403 
1404  for (x = 0; x < WEATHERMAPTILESX; x++) {
1405  for (y = 0; y < WEATHERMAPTILESY; y++) {
1406  rain = weathermap[x][y].sky;
1407  if (rain >= SKY_LIGHT_SNOW) {
1408  rain -= 10;
1409  }
1410  if (rain > SKY_OVERCAST && rain < SKY_FOG) {
1411  rain -= SKY_OVERCAST;
1412  weathermap[x][y].rainfall += rain;
1413  }
1414  }
1415  }
1416 }
1417 
1430  int x,y, wx, wy;
1431  assert(worldmap_to_weathermap(0, 0, &wx, &wy, m) == 0);
1432  for (x = 0; x < wset.worldmaptilesizex; x++) {
1433  for (y = 0; y < wset.worldmaptilesizey; y++) {
1434  worldmap_to_weathermap(x, y, &wx, &wy, m);
1435  weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1436  }
1437  }
1438 }
1439 
1446  assert(wset.dynamiclevel > 0);
1447  update_humid(); /* Run the humidity updates based on prior pressure, temperature, and wind */
1448  perform_pressure(); /* pressure is the random factor */
1449  smooth_wind(); /* calculate the wind. depends on pressure */
1450  plot_gulfstream();
1451  init_temperature();
1452  spin_globe();
1453  //compute_sky(); This is done in perform_weather
1454 }
1455 
1456 /********************************************************************************************
1457  * Section END -- weather data calculators
1458  ********************************************************************************************/
1459 
1460 /********************************************************************************************
1461  * Section -- Weather effect methods
1462  * Functions to provide weather effects.
1463  * This includes precipitation, puddles, ice on water, snowfall, growing plants,
1464  * defacing the world (since some comments seemed to imply that one was broken, and I never changed it)
1465  ********************************************************************************************/
1466 
1482 static void do_precipitation(mapstruct * const m, const int x, const int y, const int temp, const int sky) {
1483  // Do falling rain/snow here
1484  const archetype *at = NULL;
1485  object *tmp = NULL;
1486  int avoid = 0;
1487  int pct_precip = 0; // 0-100: percent tiles with precipitation
1488  switch (sky) {
1489  case SKY_LIGHT_RAIN:
1490  case SKY_LIGHT_SNOW:
1491  pct_precip = 10;
1492  break;
1493  case SKY_RAIN:
1494  case SKY_SNOW:
1495  pct_precip = 30;
1496  break;
1497  case SKY_HEAVY_RAIN:
1498  case SKY_HEAVY_SNOW:
1499  pct_precip = 60;
1500  break;
1501  case SKY_HURRICANE:
1502  case SKY_BLIZZARD:
1503  pct_precip = 99;
1504  break;
1505  }
1506  // TODO: Move these globally? Pretty sure they will be in a consistent spot in memory for execution duration.
1507  sstring snowc = find_string("snow_c");
1508  sstring rain = find_string("rain");
1509  if (rndm(0, 99) + pct_precip >= 100) {
1510  // Do our weather inserts here.
1511  // t < -2 == always snow
1512  // 2 >= t > -2 == rain/snow mix
1513  // t > 2 == always rain
1514  if (temp < -2 || (temp <= 2 && rndm(0, 2+temp) - 2 <= 0)) {
1515  at = find_archetype("snow_c");
1516  }
1517  else {
1518  at = find_archetype("rain");
1519  }
1520  if (at) {
1521  // Make sure we don't stack rains/snow ad nauseam on tiles. Also allow to switch the precip on the tile.
1522  tmp = GET_MAP_OB(m, x, y);
1523  avoid = 0;
1524  while (tmp) {
1525  if (tmp->arch == at) {
1526  avoid++;
1527  break;
1528  }
1529  else if ((tmp->arch->name == snowc && at->name == rain) ||
1530  (tmp->arch->name == rain && at->name == snowc)) {
1531  // Remove the wrong precipitation
1532  object_remove(tmp);
1533  object_free(tmp, 0);
1534  }
1535  tmp = tmp->above;
1536  }
1537  if (!avoid)
1539  }
1540  }
1541  else {
1542  // Look for a rain/snow on this tile and remove it.
1543  tmp = GET_MAP_OB(m, x, y);
1544  while (tmp) {
1545  if (tmp->arch->name == rain || tmp->arch->name == snowc) {
1546  object_remove(tmp);
1547  object_free(tmp, 0);
1548  }
1549  tmp = tmp->above;
1550  }
1551  }
1552 }
1553 
1560 static void do_map_precipitation(mapstruct * const m) {
1561  if (!m)
1562  return;
1563  // Make sure it has a weather map.
1564  int x, y, temp, sky, wx, wy;
1565  if (worldmap_to_weathermap(0, 0, &x, &y, m) != 0)
1566  return;
1567  // Now we re-do the precipitation on the map.
1568  for (x = 0; x < m->width; ++x)
1569  for (y = 0; y < m->height; ++y) {
1570  worldmap_to_weathermap(x, y, &wx, &wy, m);
1571  temp = weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1572  sky = weathermap[wx][wy].sky;
1573  do_precipitation(m, x, y, temp, sky);
1574  }
1575 }
1576 
1583 static void let_it_snow(mapstruct * const m) {
1584  int x, y, wx, wy;
1585  int nx, ny, j, d;
1586  int avoid, temp, sky, gotsnow, nodstk;
1587  object *tmp, *oldsnow, *topfloor;
1588  archetype *at, *doublestack, *doublestack2;
1589 
1590  sstring dungmag = find_string("dungeon_magic");
1591 
1592  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1593  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1594  /* jitter factor */
1595  if (rndm(0, 2) > 0) {
1596  x = y = d = -1;
1597  while (OUT_OF_REAL_MAP(m, x, y)) {
1598  d++;
1599  j = rndm(1, 8);
1600  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1601  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1602  if (d > 15) {
1603  x = nx;
1604  y = ny;
1605  }
1606  }
1607  } else {
1608  x = nx;
1609  y = ny;
1610  }
1611  /* we use the unjittered coordinates */
1612  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1613  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, wx, wy;
1782  int nx, ny, d, j;
1783  int avoid, temp, sky, gotsnow, /*found,*/ nodstk;
1784  object *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  at = NULL;
1813  doublestack = NULL;
1814  avoid = 0;
1815  gotsnow = 0;
1816  nodstk = 0;
1817  /*temp = real_world_temperature(x, y, m);*/
1818  temp = weathermap[wx][wy].realtemp;
1819  sky = weathermap[wx][wy].sky;
1820  /* Handle adding precipitation here. */
1821  do_precipitation(m, x, y, temp, sky);
1822 
1823  /* it's probably allready snowing */
1824  if (temp < 0) {
1825  continue;
1826  }
1827 
1828  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1829  if (!avoid) {
1830  tmp = GET_MAP_OB(m, x, y);
1831  if (tmp) {
1832  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1833  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1834  if (tmp->arch->name == melt->tile) {
1835  at = melt->special_snow;
1836  }
1837  }
1838  if (at)
1839  break;
1840  }
1841  if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1842  switch (rndm(0, SKY_HAIL-sky)) {
1843  case 0: at = find_archetype("rain1_weather"); break;
1844  case 1: at = find_archetype("rain2_weather"); break;
1845  default: at = NULL; break;
1846  }
1847  }
1848  if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE) {
1849  switch (rndm(0, SKY_HAIL-sky)) {
1850  case 0: at = find_archetype("rain3_weather"); break;
1851  case 1: at = find_archetype("rain4_weather"); break;
1852  case 2: at = find_archetype("rain5_weather"); break;
1853  default: at = NULL; break;
1854  }
1855  }
1856  /* the bottom floor of scorn is not IS_FLOOR */
1857  topfloor = NULL;
1858  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp,tmp = tmp->above) {
1859  if (tmp->arch->name != dungmag) {
1860  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1861  break;
1862  }
1863  }
1864  }
1865  /* topfloor should now be the topmost IS_FLOOR=1 */
1866  if (topfloor == NULL) {
1867  continue;
1868  }
1869  if (tmp != NULL) {
1870  nodstk++;
1871  }
1872  /* something is wrong with that sector. just skip it */
1873  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1874  if (check_replace_match(topfloor, repl)) {
1875  if (repl->doublestack_arch != NULL && !nodstk) {
1876  doublestack = repl->doublestack_arch;
1877  }
1878  break;
1879  }
1880  }
1881  if (gotsnow && at) {
1882  if (oldsnow->arch == at) {
1883  at = NULL;
1884  } else {
1885  tmp = GET_MAP_OB(m, x, y);
1886  object_remove(oldsnow);
1887  /* clean up the trees we put over the snow */
1888  doublestack2 = NULL;
1889  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1890  if (repl->doublestack_arch == NULL) {
1891  continue;
1892  }
1893  if (check_replace_match(tmp, repl)) {
1894  tmp = tmp->above;
1895  doublestack2 = repl->doublestack_arch;
1896  break;
1897  }
1898  }
1899  object_free(oldsnow,0);
1900  if (tmp != NULL && doublestack2 != NULL) {
1901  if (tmp->arch == doublestack2) {
1902  object_remove(tmp);
1903  object_free(tmp,0);
1904  }
1905  }
1906  }
1907  }
1908  if (at != NULL) {
1910  if (doublestack != NULL) {
1911  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1912  }
1913  }
1914  }
1915  /* Things evaporate fast in the heat */
1916  if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1917  /* evaporate */
1918  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1919  // Find a tile to evaporate
1920  weather_replace_t *evap;
1921  for (evap = weather_evaporate; evap; evap = evap->next) {
1922  if (tmp->arch->name == evap->tile)
1923  break;
1924  }
1925  // If we found it, then evaporate it
1926  if (evap) {
1927  object_remove(tmp);
1928  object_free(tmp,0);
1929  if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0) {
1930  weathermap[wx][wy].humid++;
1931  }
1932  // If the evaporation is done, clean up the doublestack on this tile.
1933  if (evap->special_snow == NULL) {
1934  tmp = GET_MAP_OB(m, x, y);
1935  /* clean up the trees we put over the rain */
1936  doublestack2 = NULL;
1937  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1938  if (repl->doublestack_arch == NULL) {
1939  continue;
1940  }
1941  if (check_replace_match(tmp, repl)) {
1942  tmp = tmp->above;
1943  doublestack2 = repl->doublestack_arch;
1944  break;
1945  }
1946  }
1947  if (tmp != NULL && doublestack2 != NULL) {
1948  if (tmp->arch == doublestack2) {
1949  object_remove(tmp);
1950  object_free(tmp,0);
1951  }
1952  }
1953  }
1954  else {
1955  // Apply the replacement puddle
1957  }
1958  break;
1959  }
1960  }
1961  }
1962  }
1963  }
1964 }
1965 
1972 static void plant_a_garden(mapstruct *const m) {
1973  int x, y, i, wx, wy;
1974  int avoid, temp, gotsnow, found, days;
1975  object *tmp;
1976  archetype *at;
1977 
1979  for (x = 0; x < wset.worldmaptilesizex; x++) {
1980  for (y = 0; y < wset.worldmaptilesizey; y++) {
1981  (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1982  at = NULL;
1983  avoid = 0;
1984  gotsnow = 0;
1985  /*temp = real_world_temperature(x, y, m);*/
1986  temp = weathermap[wx][wy].realtemp;
1987  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1988  if (!avoid) {
1989  found = 0;
1990  for (i = 0; weather_grow[i].herb != NULL; i++) {
1991  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1992  if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0) {
1993  continue;
1994  }
1995 
1996  /* we found there is a herb here allready */
1997  found++;
1998  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
1999  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
2000  weathermap[wx][wy].humid < weather_grow[i].humin ||
2001  weathermap[wx][wy].humid > weather_grow[i].humax ||
2002  temp < weather_grow[i].tempmin ||
2003  temp > weather_grow[i].tempmax ||
2004  rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
2005  /* the herb does not belong, randomly delete
2006  herbs to prevent overgrowth. */
2007  object_remove(tmp);
2008  object_free(tmp,0);
2009  break;
2010  }
2011  }
2012  /* don't doublestack herbs */
2013  if (found) {
2014  continue;
2015  }
2016  /* add a random factor */
2017  if (rndm(1, weather_grow[i].random) != 1) {
2018  continue;
2019  }
2020  /* we look up through two tiles for a matching tile */
2021  if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
2022  if (strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_grow[i].tile) != 0) {
2023  if (GET_MAP_OB(m, x, y)->above != NULL) {
2024  if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name, weather_grow[i].tile) != 0) {
2025  continue;
2026  }
2027  } else {
2028  continue;
2029  }
2030  }
2031  }
2032  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2033  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax) {
2034  continue;
2035  }
2036  if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2037  weathermap[wx][wy].humid > weather_grow[i].humax) {
2038  continue;
2039  }
2040  if (temp < weather_grow[i].tempmin ||
2041  temp > weather_grow[i].tempmax) {
2042  continue;
2043  }
2044  if ((!GET_MAP_OB(m, x, y)) ||
2045  GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2046  GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax) {
2047  continue;
2048  }
2049  /* we got this far.. must be a match */
2050  at = find_archetype(weather_grow[i].herb);
2051  break;
2052  }
2053  if (at != NULL) {
2054  /* XXX is overlay_floor right? maybe.. */
2056  }
2057  }
2058  }
2059  }
2060 }
2061 
2068 static void change_the_world(mapstruct * const m) {
2069  int x, y, i, wx, wy;
2070  int nx, ny, j, d;
2071  int avoid, temp, gotsnow, found, days;
2072  object *tmp, *doublestack;
2073  archetype *at, *dat;
2074 
2076  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
2077  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
2078  /* jitter factor */
2079  if (rndm(0, 2) > 0) {
2080  x = y = d = -1;
2081  while (OUT_OF_REAL_MAP(m, x, y)) {
2082  d++;
2083  j = rndm(1, 8);
2084  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2085  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2086  if (d > 15) {
2087  x = nx;
2088  y = ny;
2089  }
2090  }
2091  } else {
2092  x = nx;
2093  y = ny;
2094  }
2095  /* we use the unjittered coordinates */
2096  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2097  at = NULL;
2098  dat = NULL;
2099  avoid = 0;
2100  gotsnow = 0;
2101  /*temp = real_world_temperature(x, y, m);*/
2102  temp = weathermap[wx][wy].realtemp;
2103  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2104  if (!avoid) {
2105  for (i = 0; weather_tile[i].herb != NULL; i++) {
2106  found = 0;
2107  doublestack = NULL;
2108  if (GET_MAP_OB(m, x, y)) {
2109  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2110  if (weather_tile[i].tile != NULL) {
2111  if (strcmp(tmp->arch->name, weather_tile[i].tile) == 0) {
2112  doublestack = tmp;
2113  continue;
2114  }
2115  }
2116  if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0) {
2117  continue;
2118  }
2119 
2120  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2121  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2122  weathermap[wx][wy].humid < weather_tile[i].humin ||
2123  weathermap[wx][wy].humid > weather_tile[i].humax ||
2124  temp < weather_tile[i].tempmin ||
2125  temp > weather_tile[i].tempmax) {
2126  object_remove(tmp);
2127  object_free(tmp,0);
2128  if (doublestack) {
2129  object_remove(doublestack);
2130  object_free(doublestack,0);
2131  }
2132  break;
2133  } else {
2134  found++; /* there is one here allready. leave it */
2135  break;
2136  }
2137  }
2138  }
2139  if (found) {
2140  break;
2141  }
2142 
2143  /* add a random factor */
2144  if (rndm(1, weather_tile[i].random) != 1) {
2145  continue;
2146  }
2147  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2148  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax) {
2149  continue;
2150  }
2151  if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2152  weathermap[wx][wy].humid > weather_tile[i].humax) {
2153  continue;
2154  }
2155  if (temp < weather_tile[i].tempmin ||
2156  temp > weather_tile[i].tempmax) {
2157  continue;
2158  }
2159  if ( (!GET_MAP_OB(m, x, y)) ||
2160  GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2161  GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax) {
2162  continue;
2163  }
2164  /* we got this far.. must be a match */
2165  if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_tile[i].herb) == 0) {
2166  break; /* no sense in doubling up */
2167  }
2168  at = find_archetype(weather_tile[i].herb);
2169  break;
2170  }
2171  if (at != NULL) {
2172  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) {
2173  dat = find_archetype(weather_tile[i].tile);
2174  }
2175  if (dat != NULL) {
2177  }
2178  if (gotsnow == 0) {
2180  }
2181  }
2182  }
2183  }
2184  }
2185 }
2186 
2199 static void weather_effect(mapstruct * const m) {
2200  int wx, wy, x, y;
2201 
2202  /* if the dm shut off weather, go home */
2203  if (wset.dynamiclevel < 1) {
2204  return;
2205  }
2206 
2207  if (!m->outdoor) {
2208  return;
2209  }
2210 
2211  x = 0;
2212  y = 0;
2213  /* for now, just bail if it's not the worldmap */
2214  if (worldmap_to_weathermap(x, y, &wx, &wy, m) != 0) {
2215  return;
2216  }
2217 
2218  /*First, calculate temperature*/
2220  /* we change the world first, if needed */
2221  if (wset.dynamiclevel >= 5) {
2223  }
2224  if (wset.dynamiclevel >= 2) {
2225  let_it_snow(m);
2227  }
2228  if (wset.dynamiclevel >= 3) {
2229  plant_a_garden(m);
2230  }
2231 }
2232 
2242  mapstruct *m;
2243  char filename[MAX_BUF];
2244  FILE *fp;
2245 
2246  if (!wset.dynamiclevel) {
2247  return;
2248  }
2249 
2250  /* move right to left, top to bottom */
2251  if (++wmperformstartx == int(settings.worldmaptilesx)) {
2252  wmperformstartx = 0;
2253  if (++wmperformstarty == int(settings.worldmaptilesy)) {
2254  wmperformstarty = 0;
2255  }
2256  }
2257 
2258  // Whenever we load a map for effects, recalculate the weather.
2259  // Do this before the actual map load so that precipitation is done with the new sky computation rather than the old
2260  compute_sky();
2261 
2262  snprintf(filename, sizeof(filename), "world/world_%d_%d", wmperformstartx+settings.worldmapstartx, wmperformstarty+settings.worldmapstarty);
2263 
2264  m = ready_map_name(filename, 0);
2265  if (m == NULL) {
2266  return; /* hrmm */
2267  }
2268 
2269  // Run weather effects here.
2270  // We do this here rather than on any map load so that the weather effects remain consistent.
2271  weather_effect(m);
2272 
2273  /* done */
2274  save_map(m, SAVE_MODE_OVERLAY); /* write the overlay */
2275  m->in_memory = MAP_IN_MEMORY; /*reset this*/
2276  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings.localdir);
2277  if ((fp = fopen(filename, "w")) == NULL) {
2278  LOG(llevError, "Cannot open %s for writing\n", filename);
2279  return;
2280  }
2281 
2282  if (players_on_map(m, TRUE) == 0) {
2283  delete_map(m);
2284  }
2285 
2286  fprintf(fp, "%d %d", wmperformstartx, wmperformstarty);
2287  fclose(fp);
2288 }
2289 
2311 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) {
2312  // If we're inside, the weather can't get us :P
2313  if (!m || !m->outdoor)
2314  return 0;
2315  // First, we get the weathermap for this location
2316  int nx, ny;
2317  if (worldmap_to_weathermap(x, y, &nx, &ny, m))
2318  return 0;
2319  int windspeed = weathermap[nx][ny].windspeed;
2320  int is_fly = move_type & MOVE_FLYING;
2321  // If not flying, then strong winds are needed to affect you.
2322  if (!is_fly)
2323  windspeed -= 20;
2324  // If no wind, then no push.
2325  if (windspeed <= 0)
2326  return 0;
2327  // Reduce effect from carrying more stuff.
2328  // Also, being on the ground makes the same wind increase affect you less as well.
2329  // Higher strength characters can also resist being blown by the wind when on the ground.
2330  if (!is_fly)
2331  wt /= 10000 * ((stats && stats->Str) ? stats->Str : 1);
2332  // When flying, we care about Dex over Str.
2333  else
2334  wt /= 20000 * ((stats && stats->Dex) ? stats->Dex : 1);
2335  // Massive things are pushed around less easily.
2336  if (windspeed*2 < wt)
2337  return 0;
2338  // The push will not happen every try. The greater the wind, the more often it succeeds.
2339  // Also, the lighter the object, the more often it succeeds
2340  // We do two rolls because it normalizes the effects better than a single roll.
2341  if (rndm(0, windspeed)+rndm(0, windspeed) < wt)
2342  return 0;
2343  // winddir is the direction the wind is coming from.
2344  // so we need to reverse it to push where the wind is going to.
2345  return absdir(weathermap[nx][ny].winddir+4);
2346 }
2347 
2348 /********************************************************************************************
2349  * Section END -- weather effect methods
2350  ********************************************************************************************/
2351 
2352 /********************************************************************************************
2353  * Section -- Initializations
2354  * Functions to load in config for determining certain weathermap attributes,
2355  * functions to initialize missing weathermap attributes,
2356  * and their helper functions.
2357  ********************************************************************************************/
2358 
2379 static int init_config_vals(const Settings *settings, const char *conf_filename, DensityConfig **list) {
2380  char filename[MAX_BUF], *line, *name;
2381  BufferReader *bfr;
2382  int found, is_obj_name, tree_count;
2383 
2384  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2385  // Open the file with the buffer reader.
2386  bfr = bufferreader_init_from_file(NULL, filename,
2387  "init_config_vals: Could not open file %s. No forestry data is defined. %s\n",
2388  llevError);
2389  if (bfr == NULL) {
2390  // The error was printed by bufferreader_init_from_file already, so just bail.
2391  return 1;
2392  }
2393  // Now we read in from the buffer.
2394  while ((line = bufferreader_next_line(bfr)) != NULL) {
2395  // Now we parse the line
2396  // Start by examining the first character.
2397  switch (*line) {
2398  // Ignore empty lines and comment lines (denoted by # at front)
2399  case '\0':
2400  case '#':
2401  // Handling \r means Windows should work right, too.
2402  case '\r':
2403  case '\n':
2404  break;
2405  default:
2406  // Actually parse the line
2407  // Format is like this:
2408  // name, (0 if arch, 1 if object name), # trees
2409  // [spaces are expected after commas]
2410 
2411  // sscanf on strings is wonky (it always reads to whitespace),
2412  // so I'm gonna do it by just nabbing part of the buffer.
2413  name = line; // Each line starts with name
2415  if (line == NULL) {
2416  LOG(llevError, "init_config_vals: Malformed name entry in %s, line %ld.\n",
2417  filename, bufferreader_current_line(bfr));
2418  // Move on to the next line and hope it is fine.
2419  continue;
2420  }
2421 
2422  found = sscanf(line, "%d, %d\n", &is_obj_name, &tree_count);
2423  if (found != 2) {
2424  // Print an error for the malformed line
2425  LOG(llevError, "init_config_vals: Malformed forestry entry in %s, line %ld.\n",
2426  filename, bufferreader_current_line(bfr));
2427  }
2428  else {
2429  // Add a struct to the list.
2430  DensityConfig *frst = (DensityConfig *)malloc(sizeof(DensityConfig));
2431  if (!frst) {
2433  }
2434  // Shared strings are friend, not food
2435  frst->name = add_string(name);
2436  frst->is_obj = is_obj_name;
2437  frst->value_density = tree_count;
2438  // Attach to front of list, since order doesn't matter much, if at all.
2439  frst->next = *list;
2440  *list = frst;
2441  }
2442  }
2443  }
2444  bufferreader_destroy(bfr);
2445  return 0;
2446 }
2447 
2464 static int init_weatheravoid(const Settings *settings, const char *conf_filename, weather_avoids_t **wa) {
2465  char filename[MAX_BUF], *line, *name;
2466  BufferReader *bfr;
2467  int found, is_effect;
2468 
2469  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2470  // Open the file with the buffer reader.
2471  bfr = bufferreader_init_from_file(NULL, filename,
2472  "init_weatheravoid: Could not open file %s. No weatheravoid data is defined. %s\n", llevError);
2473  // If the bufferreader failed, it return NULL and printed an error, so just bail if failure.
2474  if (bfr == NULL)
2475  return 1;
2476  // Now we read in from the buffer.
2477  while ((line = bufferreader_next_line(bfr)) != NULL) {
2478  // Now we parse the line
2479  // Start by examining the first character.
2480  switch (*line) {
2481  // Ignore empty lines and comment lines (denoted by # at front)
2482  case '\0':
2483  case '#':
2484  // Handling \r means Windows should work right, too.
2485  case '\r':
2486  case '\n':
2487  break;
2488  default:
2489  // Actually parse the line
2490  // Format is like this:
2491  // name, (1 if weather effect, 0 if regular tile)
2492  // [spaces are expected after commas]
2493 
2494  // sscanf on strings is wonky (it always reads to whitespace),
2495  // so I'm gonna do it by just nabbing part of the buffer.
2496  name = line; // Each line starts with name
2498  if (line == NULL) {
2499  LOG(llevError, "init_weatheravoid: Malformed name entry in %s, line %ld.\n",
2500  filename, bufferreader_current_line(bfr));
2501  // Move on to the next line and hope it is fine.
2502  continue;
2503  }
2504 
2505  found = sscanf(line, "%d\n", &is_effect);
2506  if (found != 1) {
2507  // Print an error for the malformed line
2508  LOG(llevError, "init_weatheravoid: Malformed effect flag entry in %s, line %ld.\n",
2509  filename, bufferreader_current_line(bfr));
2510  }
2511  else {
2512  // Add a struct to the list.
2513  weather_avoids_t *frst = (weather_avoids_t *)malloc(sizeof(weather_avoids_t));
2514  if (!frst) {
2516  }
2517  // Shared strings are friend, not food
2518  frst->name = add_string(name);
2519  frst->snow = is_effect;
2520  // Attach to front of list, since order doesn't matter much, if at all.
2521  frst->next = *wa;
2522  *wa = frst;
2523  }
2524  }
2525  }
2526  bufferreader_destroy(bfr);
2527  return 0;
2528 }
2529 
2546 static int init_weather_replace(const Settings *settings, const char *conf_filename, weather_replace_t **list) {
2547  char filename[MAX_BUF], *line, *name, *repl, *doublestack;
2548  BufferReader *bfr;
2549  int found, is_arch;
2550 
2551  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2552  // Open the file with the buffer reader.
2553  bfr = bufferreader_init_from_file(NULL, filename, "init_weather_replace: Could not open file %s. No weather replace data is defined. %s\n", llevError);
2554  // If failed, we already printed an error.
2555  if (bfr == NULL)
2556  return 1;
2557  // Now we read in from the buffer.
2558  while ((line = bufferreader_next_line(bfr)) != NULL) {
2559  // Now we parse the line
2560  // Start by examining the first character.
2561  switch (*line) {
2562  // Ignore empty lines and comment lines (denoted by # at front)
2563  case '\0':
2564  case '#':
2565  // Handling \r means Windows should work right, too.
2566  case '\r':
2567  case '\n':
2568  break;
2569  default:
2570  // Actually parse the line
2571  // Format is like this:
2572  // name, replacement tile arch name, additional tile arch name (or NONE if not), (1 if arch name, 0 if object name)
2573  // [spaces are expected after commas]
2574 
2575  // sscanf on strings is wonky (it always reads to whitespace),
2576  // so I'm gonna do it by just nabbing part of the buffer.
2577  name = line; // Each line starts with name
2579  if (line == NULL) {
2580  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2581  LOG(llevError, "init_weather_replace: Malformed name entry in %s, line %ld.\n",
2582  filename, bufferreader_current_line(bfr));
2583  // Move on to the next line and hope it is fine.
2584  continue;
2585  }
2586 
2587  repl = line; // Each line starts with name
2589  if (line == NULL) {
2590  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2591  LOG(llevError, "init_weather_replace: Malformed replacement entry in %s, line %ld.\n",
2592  filename, bufferreader_current_line(bfr));
2593  // Move on to the next line and hope it is fine.
2594  continue;
2595  }
2596 
2597  doublestack = line; // Each line starts with name
2599  if (line == NULL) {
2600  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2601  LOG(llevError, "init_weather_replace: Malformed doublestack entry in %s, line %ld.\n",
2602  filename, bufferreader_current_line(bfr));
2603  // Move on to the next line and hope it is fine.
2604  continue;
2605  }
2606 
2607  found = sscanf(line, "%d\n", &is_arch);
2608  if (found != 1) {
2609  // Print an error for the malformed line
2610  LOG(llevError, "init_weatheravoid: Malformed archetype/object flag entry in %s, line %ld.\n",
2611  filename, bufferreader_current_line(bfr));
2612  }
2613  else {
2614  // Add a struct to the list.
2615  weather_replace_t *frst = (weather_replace_t *)malloc(sizeof(weather_replace_t));
2616  if (!frst) {
2618  }
2619  // Shared strings are friend, not food
2620  frst->tile = add_string(name);
2621  // Some replcement definitions can have NONE here to denote removal
2622  if (strcmp(repl, "NONE") == 0)
2623  frst->special_snow = NULL;
2624  else
2625  frst->special_snow = find_archetype(repl);
2626  // if doublestack is NONE, then set the arch to NULL
2627  if (strcmp(doublestack, "NONE") == 0)
2628  frst->doublestack_arch = NULL;
2629  else
2630  frst->doublestack_arch = find_archetype(doublestack);
2631  frst->arch_or_name = is_arch;
2632  // Attach to front of list, since order doesn't matter much, if at all.
2633  frst->next = *list;
2634  *list = frst;
2635  }
2636  }
2637  }
2638  bufferreader_destroy(bfr);
2639  return 0;
2640 }
2641 
2661 static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int * const tx, int * const ty) {
2662  char mapname[MAX_BUF];
2663  if (!m || !tx || !ty)
2664  return -1;
2665  // Now we do what was wanted.
2666  weathermap_to_worldmap_corner(x, y, tx, ty, dir, mapname, sizeof(mapname));
2667  *m = mapfile_load(mapname, 0);
2668  if (*m == NULL) {
2669  return -1;
2670  }
2671 
2672  int res = load_overlay_map(mapname, *m);
2673  if (res != 0) {
2674  return -1;
2675  }
2676  return 0;
2677 }
2678 
2704 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) {
2705  if (!m || !water || !elev || !trees)
2706  return -1;
2707  object *ob = GET_MAP_OB(m, x, y);
2708  if (ob) {
2709  if (QUERY_FLAG(ob, FLAG_IS_WATER)) {
2710  (*water)++;
2711  }
2712  // Deserts will reduce the humidity/precipitation in the spaces they exist in.
2713  // Since the config entries are all negative, we can add the value here.
2714  (*water) += get_config_tile(x, y, m, water_list);
2715 
2716  // Handle forestry
2717  (*trees) += get_config_tile(x, y, m, forest_list);
2718 
2719  (*elev) += ob->elevation;
2720  }
2721  return 0;
2722 }
2723 
2734 static void init_humid_elev(const Settings *settings) {
2735  // Variable uses:
2736  // x, y: weathermap tile being affected
2737  // tx, ty: the in-map coordinates of the corner of the weathermap we are calculating.
2738  // nx, ny: coordinates within the weathermap
2739  // ax, ay: the location on the map we are examining
2740  // j: temporary variable for when a specific ny needs to be initialized from within a loop.
2741  int x, y, tx, ty, nx, ny, ax, ay, j;
2742  // spwtx, spwty: The number of tiles in a single weathermap in the associated (x or y) direction
2745  int64_t elev;
2746  int water, space, trees;
2747  mapstruct *m;
2748 
2749  /* handling of this is kinda nasty. For that reason,
2750  * we do the elevation here too. Not because it makes the
2751  * code cleaner, or makes handling easier, but because I do *not*
2752  * want to maintain two of these nightmares.
2753  */
2754 
2755  for (x = 0; x < WEATHERMAPTILESX; x++) {
2756  for (y = 0; y < WEATHERMAPTILESY; y++) {
2757  water = space = trees = 0;
2758  elev = 0;
2759  nx = ny = 0;
2760 
2761  /* top left */
2762  if (load_humidity_map_part(&m, 8, x, y, &tx, &ty) == -1)
2763  continue;
2764 
2765  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2766  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2767  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2768  //LOG(llevInfo, "%s %d %d (8)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2769  }
2770  }
2771  delete_map(m);
2772 
2773 
2774  // Sanely skip some processing if the entire weathermap fit on one world map.
2775  // Since we are the same size for x/y direction on both weathermaps and on world maps,
2776  // we will either need to load one map, two maps, or four maps. When two maps are loaded, it
2777  // will be one of bottom left or top right, since bottom right only is relevant when we intersect maps
2778  // in both x and y directions.
2779  if (space < spwtx*spwty) {
2780  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2781  if (ny < spwty) {
2782  /* bottom left */
2783  if (load_humidity_map_part(&m, 6, x, y, &tx, &ty) == -1)
2784  continue;
2785 
2786  // If we get here, then we didn't have the whole weathermap reside on one map.
2787  // Since we are continuing from top left, maintaining our position in the y direction
2788  // allows us to correctly check when we reach the end of the weathermap bounds.
2789  j = ny;
2790  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2791  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2792  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2793  //LOG(llevInfo, "%s %d %d (6)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2794  }
2795  }
2796  delete_map(m);
2797  }
2798 
2799  // If we gotall the way to the right on the left calculations, skip both right-side calculations.
2800  if (nx < spwtx) {
2801  /* top right */
2802  if (load_humidity_map_part(&m, 2, x, y, &tx, &ty) == -1)
2803  continue;
2804 
2805  for (ax = MAX(0, tx-(spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2806  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2807  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2808  //LOG(llevInfo, "%s %d %d (2)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2809  }
2810  }
2811  delete_map(m);
2812 
2813  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2814  if (ny < spwty) {
2815  /* bottom right */
2816  if (load_humidity_map_part(&m, 4, x, y, &tx, &ty) == -1)
2817  continue;
2818 
2819  // Moving from top to bottom should behave the same on both right and left.
2820  j = ny;
2821  for (nx = 0, ax = MAX(0, tx - (spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2822  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2823  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2824  //LOG(llevInfo, "%s %d %d (4)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2825  }
2826  }
2827  delete_map(m);
2828  }
2829  }
2830  }
2831 
2832  /* jesus thats confusing as all hell */
2833  // Per meteorology, full ocean usually only gets to 80% humidity at the standard height it is measured.
2834  // And, even in the desert, relative humidity averages like 20%. So, in non-deserts, it should be like 40%.
2835  // This should help prevent a forever-hurricane over the ocean.
2836  weathermap[x][y].humid = 40+water*40/(spwtx*spwty);
2837  weathermap[x][y].avgelev = elev/(spwtx*spwty);
2838  weathermap[x][y].water = water*100/(spwtx*spwty);
2839  // Cap at 100 for tree values. Denser trees stop having any effect.
2840  weathermap[x][y].forestry = MIN(100, trees*100/(spwtx*spwty));
2841  }
2842  }
2843 
2844  /* and this does all the real work */
2845  update_humid();
2846 }
2847 
2853 static void init_temperature() {
2854  int x, y;
2855  timeofday_t tod;
2856 
2857  get_tod(&tod);
2858  for (x = 0; x < WEATHERMAPTILESX; x++) {
2859  for (y = 0; y < WEATHERMAPTILESY; y++) {
2860  temperature_calc(x, y, &tod);
2861  }
2862  }
2863 }
2864 
2870 static void init_rainfall()
2871 {
2872  int x, y;
2873  int days = todtick/HOURS_PER_DAY;
2874 
2875  for (x = 0; x < WEATHERMAPTILESX; x++) {
2876  for (y = 0; y < WEATHERMAPTILESY; y++) {
2877  if (weathermap[x][y].humid < 10) {
2878  weathermap[x][y].rainfall = days/20;
2879  } else if (weathermap[x][y].humid < 20) {
2880  weathermap[x][y].rainfall = days/15;
2881  } else if (weathermap[x][y].humid < 30) {
2882  weathermap[x][y].rainfall = days/10;
2883  } else if (weathermap[x][y].humid < 40) {
2884  weathermap[x][y].rainfall = days/5;
2885  } else if (weathermap[x][y].humid < 50) {
2886  weathermap[x][y].rainfall = days/2;
2887  } else if (weathermap[x][y].humid < 60) {
2888  weathermap[x][y].rainfall = days;
2889  } else if (weathermap[x][y].humid < 80) {
2890  weathermap[x][y].rainfall = days*2;
2891  } else {
2892  weathermap[x][y].rainfall = days*3;
2893  }
2894  }
2895  }
2896 }
2897 
2901 static void init_gulfstreammap() {
2902  int x, y, tx, starty, ymul, diroffset, dirdiff;
2903 
2904  /* build a gulf stream */
2906  /* doth the great bob inhale or exhale? */
2907  gulf_stream_direction = rndm(0, 1);
2908  gulf_stream_start = x;
2909 
2910  // Handle both gulf stream directions
2911  if (gulf_stream_direction) {
2912  // These variables allow us to only define the loop once.
2913  // That should make the code less awful to see
2914  starty = WEATHERMAPTILESY-1;
2915  ymul = -1;
2916  // The diroffset pieces allow us to merge the meat of the loops
2917  // the mapping between the different directions is as follows
2918  // 8 <-> 2
2919  // 7 <-> 3
2920  // 6 <-> 4
2921  // Thus setting diroffset to 10 when dirdiff is 1 gives us one direction
2922  // and setting diroffset to 0 when dirdiff is -1 gives us the other.
2923  diroffset = 0;
2924  dirdiff = -1;
2925  }
2926  else {
2927  starty = 0;
2928  ymul = 1;
2929  diroffset = 10;
2930  dirdiff = 1;
2931  }
2932  // Huzzah! a loop common to both directions!
2933  for (y = starty; y >= 0 && y < WEATHERMAPTILESY; y += ymul) {
2934  switch (rndm(0, 6)) {
2935  case 0:
2936  case 1:
2937  case 2:
2938  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2940  if (x == 0) {
2941  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2942  } else {
2943  gulf_stream_dir[tx][y] = (diroffset-8)*dirdiff;
2944  if (tx == 0) {
2945  x--;
2946  }
2947  }
2948  }
2949  break;
2950 
2951  case 3:
2952  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2954  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2955  }
2956  break;
2957 
2958  case 4:
2959  case 5:
2960  case 6:
2961  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2963  if (x == WEATHERMAPTILESX-1) {
2964  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2965  } else {
2966  gulf_stream_dir[tx][y] = (diroffset-6)*dirdiff;
2967  if (tx == 0) {
2968  x++;
2969  }
2970  }
2971  }
2972  break;
2973  }
2974  }
2975 }
2976 
2983 static void init_wind() {
2984  int x, y;
2985 
2986  for (x = 0; x < WEATHERMAPTILESX; x++) {
2987  for (y = 0; y < WEATHERMAPTILESY; y++) {
2988  weathermap[x][y].winddir = rndm(1, 8);
2989  weathermap[x][y].windspeed = rndm(1, 10);
2990  }
2991  }
2992 }
2993 
3000 static void init_pressure() {
3001  int x, y;
3002  int l, n, k;
3003 
3004  for (x = 0; x < WEATHERMAPTILESX; x++) {
3005  for (y = 0; y < WEATHERMAPTILESY; y++) {
3006  weathermap[x][y].pressure = 1013;
3007  }
3008  }
3009  // Add medium patches of low noise.
3010  for (l = 0; l < PRESSURE_ITERATIONS; l++) {
3011  x = rndm(0, WEATHERMAPTILESX-1);
3012  y = rndm(0, WEATHERMAPTILESY-1);
3014  for (k = 1; k < PRESSURE_AREA; k++) {
3015  switch (rndm(0, 3)) {
3016  case 0: if (x < WEATHERMAPTILESX-1) x++; break;
3017  case 1: if (y < WEATHERMAPTILESY-1) y++; break;
3018  case 2: if (x) x--; break;
3019  case 3: if (y) y--; break;
3020  }
3021  weathermap[x][y].pressure = (weathermap[x][y].pressure+n)/2;
3022  }
3023  }
3024  /* create random spikes in the pressure
3025  * These go way beyond the bounds of allowed pressure, but smooth_pressure
3026  * turns that into a sizable pressure blob.
3027  */
3028  for (l = 0; l < PRESSURE_SPIKES; l++) {
3029  x = rndm(0, WEATHERMAPTILESX-1);
3030  y = rndm(0, WEATHERMAPTILESY-1);
3031  n = rndm(500, 2000);
3032  weathermap[x][y].pressure = n;
3033  }
3034  smooth_pressure();
3035 }
3036 
3038  char buf[MAX_BUF], *cp;
3039  FILE *fp;
3040 
3041  snprintf(buf, sizeof(buf), "%s/wsettings", settings->confdir);
3042 
3043  if ((fp = fopen(buf, "r")) == NULL) {
3044  LOG(llevError, "Warning: No wsettings file found\n");
3045  return;
3046  }
3047  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
3048  if (buf[0] == '#')
3049  continue;
3050  /* eliminate newline */
3051  if ((cp = strrchr(buf, '\n')) != NULL)
3052  *cp = '\0';
3053 
3054  /* Skip over empty lines */
3055  if (buf[0] == 0)
3056  continue;
3057 
3058  /* Skip all the spaces and set them to nulls. If not space,
3059  * set cp to "" to make strcpy's and the like easier down below.
3060  */
3061  if ((cp = strchr(buf, ' ')) != NULL) {
3062  while (*cp == ' ')
3063  *cp++ = 0;
3064 
3065  // These only matter when we have a value, only look when we have a value
3066  // Even in the case of all trailing spaces and no value, cp points to a null-terminator,
3067  // which resolves to 0 from atoi().
3068  if (!strcasecmp(buf, "worldmaptilesizex")) {
3069  int size = atoi(cp);
3070 
3071  if (size < 1)
3072  LOG(llevError, "init_weather_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
3073  else
3074  wset.worldmaptilesizex = size;
3075  } else if (!strcasecmp(buf, "worldmaptilesizey")) {
3076  int size = atoi(cp);
3077 
3078  if (size < 1)
3079  LOG(llevError, "init_weather_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
3080  else
3081  wset.worldmaptilesizey = size;
3082  } else if (!strcasecmp(buf, "dynamiclevel")) {
3083  int lev = atoi(cp);
3084 
3085  if (lev < 0)
3086  LOG(llevError, "init_weather_settings: dynamiclevel must be at least 0, %d is invalid\n", lev);
3087  else
3088  wset.dynamiclevel = lev;
3089  }
3090  }
3091  else {
3092  LOG(llevError, "init_weather_settings: line %s ends after specifier, skipping...\n", buf);
3093  }
3094  }
3095 
3096  // Remember to clean up the file pointer
3097  fclose(fp);
3098 }
3099 
3100 /********************************************************************************************
3101  * Section END -- initializations
3102  ********************************************************************************************/
3103 
3104 /********************************************************************************************
3105  * Section -- weather data writers
3106  * These functions write the current state of the weather to file,
3107  * allowing persistence across server runs.
3108  ********************************************************************************************/
3109 
3123  char filename[MAX_BUF];
3124  FILE *fp;
3125  OutputFile of;
3126  int x, y;
3127 
3128  // First, allocate our file.
3129  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3130  // We use the output_file handling for atomic file operations.
3131  fp = of_open(&of, filename);
3132  if (fp == NULL) {
3133  LOG(llevError, "Failed to open %s for writing.\n", filename);
3134  return 1;
3135  }
3136  LOG(llevDebug, "Writing forestry map to file.\n");
3137  // Actually write the forestry amounts to the file
3138  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3139  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3140  fprintf(fp, "%d ", weathermap[x][y].forestry);
3141  }
3142  fprintf(fp, "\n");
3143  }
3144  of_close(&of);
3145  return 0;
3146 }
3147 
3158  char filename[MAX_BUF];
3159  FILE *fp;
3160  OutputFile of;
3161  int x, y;
3162 
3163  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3164  fp = of_open(&of, filename);
3165  if (fp == NULL) {
3166  LOG(llevError, "Cannot open %s for writing\n", filename);
3167  return 1;
3168  }
3169  LOG(llevDebug, "Writing humidity map to file.\n");
3170  for (x = 0; x < WEATHERMAPTILESX; x++) {
3171  for (y = 0; y < WEATHERMAPTILESY; y++) {
3172  fprintf(fp, "%d ", weathermap[x][y].humid);
3173  }
3174  fprintf(fp, "\n");
3175  }
3176  of_close(&of);
3177  return 0;
3178 }
3179 
3192 static int write_elevmap(const Settings *settings) {
3193  char filename[MAX_BUF];
3194  FILE *fp;
3195  OutputFile of;
3196  int x, y;
3197 
3198  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3199  fp = of_open(&of, filename);
3200  if (fp == NULL) {
3201  LOG(llevError, "Cannot open %s for writing\n", filename);
3202  return 1;
3203  }
3204  LOG(llevDebug, "Writing elevation map to file.\n");
3205  for (x = 0; x < WEATHERMAPTILESX; x++) {
3206  for (y = 0; y < WEATHERMAPTILESY; y++) {
3207  fprintf(fp, "%d ", weathermap[x][y].avgelev);
3208  }
3209  fprintf(fp, "\n");
3210  }
3211  of_close(&of);
3212  return 0;
3213 }
3214 
3215 
3227 static int write_watermap(const Settings *settings) {
3228  char filename[MAX_BUF];
3229  FILE *fp;
3230  OutputFile of;
3231  int x, y;
3232 
3233  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3234  fp = of_open(&of, filename);
3235  if (fp == NULL) {
3236  LOG(llevError, "Cannot open %s for writing\n", filename);
3237  return 1;
3238  }
3239  LOG(llevDebug, "Writing water map to file.\n");
3240  for (x = 0; x < WEATHERMAPTILESX; x++) {
3241  for (y = 0; y < WEATHERMAPTILESY; y++) {
3242  fprintf(fp, "%d ", weathermap[x][y].water);
3243  }
3244  fprintf(fp, "\n");
3245  }
3246  of_close(&of);
3247  return 0;
3248 }
3249 
3262  char filename[MAX_BUF];
3263  FILE *fp;
3264  OutputFile of;
3265  int x, y;
3266 
3267  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
3268  fp = of_open(&of, filename);
3269  if (fp == NULL) {
3270  LOG(llevError, "Cannot open %s for writing\n", filename);
3271  return 1;
3272  }
3273  LOG(llevDebug, "Writing temperature map to file.\n");
3274  for (x = 0; x < WEATHERMAPTILESX; x++) {
3275  for (y = 0; y < WEATHERMAPTILESY; y++) {
3276  fprintf(fp, "%d ", weathermap[x][y].temp);
3277  }
3278  fprintf(fp, "\n");
3279  }
3280  of_close(&of);
3281  return 0;
3282 }
3283 
3295  char filename[MAX_BUF];
3296  FILE *fp;
3297  OutputFile of;
3298  int x, y;
3299 
3300  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
3301  fp = of_open(&of, filename);
3302  if (fp == NULL) {
3303  LOG(llevError, "Cannot open %s for writing\n", filename);
3304  return 1;
3305  }
3306  LOG(llevDebug, "Writing rainfall map to file.\n");
3307  for (x = 0; x < WEATHERMAPTILESX; x++) {
3308  for (y = 0; y < WEATHERMAPTILESY; y++) {
3309  fprintf(fp, "%u ", weathermap[x][y].rainfall);
3310  }
3311  fprintf(fp, "\n");
3312  }
3313  of_close(&of);
3314  return 0;
3315 }
3316 
3328  char filename[MAX_BUF];
3329  FILE *fp;
3330  OutputFile of;
3331  int x, y;
3332 
3333  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
3334  fp = of_open(&of, filename);
3335  if (fp == NULL) {
3336  LOG(llevError, "Cannot open %s for writing\n", filename);
3337  return 1;
3338  }
3339  LOG(llevDebug, "Writing gulf stream map to file.\n");
3340  // First block is speed
3341  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3342  for (y = 0; y < WEATHERMAPTILESY; y++) {
3343  fprintf(fp, "%d ", gulf_stream_speed[x][y]);
3344  }
3345  fprintf(fp, "\n");
3346  }
3347  // second block is direction
3348  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3349  for (y = 0; y < WEATHERMAPTILESY; y++) {
3350  fprintf(fp, "%d ", gulf_stream_dir[x][y]);
3351  }
3352  fprintf(fp, "\n");
3353  }
3354  // And the last line is the starting position, so we don't always have to initialize it.
3355  fprintf(fp, "%d\n", gulf_stream_start);
3356  of_close(&of);
3357  return 0;
3358 }
3359 
3371  char filename[MAX_BUF];
3372  FILE *fp;
3373  OutputFile of;
3374  int x, y;
3375 
3376  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
3377  fp = of_open(&of, filename);
3378  if (fp == NULL) {
3379  LOG(llevError, "Cannot open %s for writing\n", filename);
3380  return 1;
3381  }
3382  LOG(llevDebug, "Writing wind speed map to file.\n");
3383  for (x = 0; x < WEATHERMAPTILESX; x++) {
3384  for (y = 0; y < WEATHERMAPTILESY; y++) {
3385  fprintf(fp, "%hd ", weathermap[x][y].windspeed);
3386  }
3387  fprintf(fp, "\n");
3388  }
3389  of_close(&of);
3390  return 0;
3391 }
3392 
3404  char filename[MAX_BUF];
3405  FILE *fp;
3406  OutputFile of;
3407  int x, y;
3408 
3409  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
3410  fp = of_open(&of, filename);
3411  if (fp == NULL) {
3412  LOG(llevError, "Cannot open %s for writing\n", filename);
3413  return 1;
3414  }
3415  LOG(llevDebug, "Writing wind direction map to file.\n");
3416  for (x = 0; x < WEATHERMAPTILESX; x++) {
3417  for (y = 0; y < WEATHERMAPTILESY; y++) {
3418  fprintf(fp, "%d ", weathermap[x][y].winddir);
3419  }
3420  fprintf(fp, "\n");
3421  }
3422  of_close(&of);
3423  return 0;
3424 }
3425 
3437  char filename[MAX_BUF];
3438  FILE *fp;
3439  OutputFile of;
3440  int x, y;
3441 
3442  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
3443  fp = of_open(&of, filename);
3444  if (fp == NULL) {
3445  LOG(llevError, "Cannot open %s for writing\n", filename);
3446  return 1;
3447  }
3448  LOG(llevDebug, "Writing pressure map to file.\n");
3449  for (x = 0; x < WEATHERMAPTILESX; x++) {
3450  for (y = 0; y < WEATHERMAPTILESY; y++) {
3451  fprintf(fp, "%d ", weathermap[x][y].pressure);
3452  }
3453  fprintf(fp, "\n");
3454  }
3455  of_close(&of);
3456  return 0;
3457 }
3458 
3465 int write_skymap(void) {
3466  char filename[MAX_BUF];
3467  FILE *fp;
3468  OutputFile of;
3469  int x, y;
3470 
3471  snprintf(filename, sizeof(filename), "%s/skymap", settings.localdir);
3472  fp = of_open(&of, filename);
3473  if (fp == NULL) {
3474  LOG(llevError, "Cannot open %s for writing\n", filename);
3475  return 1;
3476  }
3477  LOG(llevDebug, "Writing sky conditions map to file.\n");
3478  for (x = 0; x < WEATHERMAPTILESX; x++) {
3479  for (y = 0; y < WEATHERMAPTILESY; y++) {
3480  fprintf(fp, "%d ", weathermap[x][y].sky);
3481  }
3482  fprintf(fp, "\n");
3483  }
3484  of_close(&of);
3485  return 0;
3486 }
3487 
3488 /* This stuff is for creating the images,
3489  * and is only used by write_weather_images()
3490  */
3491 
3492 /* Colour offsets into pixel array. */
3493 #define RED 0
3494 #define GREEN 1
3495 #define BLUE 2
3496 
3504 static const uint32_t directions[] = {
3505  0x0000FFFF, /* south */
3506  0x000000FF, /* south west */
3507  0x00FF00FF, /* west */
3508  0x00FFFFFF, /* north west */
3509  0x00000000, /* north */
3510  0x00FF0000, /* north east */
3511  0x00FFFF00, /* east */
3512  0x0000FF00 /* south east */
3513 };
3514 
3518 static const uint32_t skies[] = {
3519  0x000000FF, /* SKY_CLEAR 0 */
3520  0x000000BD, /* SKY_LIGHTCLOUD 1 */
3521  0x0000007E, /* SKY_OVERCAST 2 */
3522  0x0000FF00, /* SKY_LIGHT_RAIN 3 */
3523  0x0000BD00, /* SKY_RAIN 4 */
3524  0x00007E00, /* SKY_HEAVY_RAIN 5 */
3525  0x00FFFF00, /* SKY_HURRICANE 6 */
3526 /* wierd weather 7-12 */
3527  0x00FF0000, /* SKY_FOG 7 */
3528  0x00FF00FF, /* SKY_HAIL 8 */
3529  0x00000000,
3530  0x00000000,
3531  0x00000000,
3532  0x00000000,
3533 /* snow */
3534  0x003F3F3F, /* SKY_LIGHT_SNOW 13 */
3535  0x007E7E7E, /* SKY_SNOW 14 */
3536  0x00BDBDBD, /* SKY_HEAVY_SNOW 15 */
3537  0x00FFFFFF /* SKY_BLIZZARD 16 */
3538 };
3539 
3553  char filename[MAX_BUF];
3554  FILE *fp;
3555  OutputFile of;
3556  int x, y;
3557  int32_t min[8], max[8], avgrain, avgwind;
3558  double scale[8], realscalewind;
3559  uint8_t pixels[3*3*WEATHERMAPTILESX];
3560  int64_t total_rainfall = 0;
3561  int64_t total_wind = 0;
3562  timeofday_t tod;
3563 
3564  // Get the time of day.
3565  // This is important for weather output later.
3566  get_tod(&tod);
3567 
3568  // Determine the output file's limits.
3569  min[0] = -100; max[0] = 100;
3570  min[1] = 0; max[1] = 0;
3571  min[2] = 0; max[2] = 0;
3572  min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
3573  // Minimum wind speed is always 0. Don't track it. We just define it so scale[4] is valid
3574  min[4] = 0; max[4] = 0;
3575  // The 6th tile is raw wind direction, and thus does not need limits
3576  min[6] = 0; max[6] = 100;
3577  min[7] = -45; max[7] = 45;
3578  // The 9th tile is raw sky data and does not need limits
3579  for (x = 0; x < WEATHERMAPTILESX; x++) {
3580  for (y = 0; y < WEATHERMAPTILESY; y++) {
3581 /* min[0] = MIN(min[0], weathermap[x][y].water); */
3582  min[1] = MIN(min[1], weathermap[x][y].avgelev);
3583  min[2] = MIN(min[2], int32_t(weathermap[x][y].rainfall));
3584 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
3585 /* min[4] = MIN(min[4], weathermap[x][y].windspeed); */
3586 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
3587 /* min[7] = MIN(min[7], real_temp[x][y]); */
3588 
3589 /* max[0] = MAX(max[0], weathermap[x][y].water); */
3590  max[1] = MAX(max[1], weathermap[x][y].avgelev);
3591  max[2] = MAX(max[2], int32_t(weathermap[x][y].rainfall));
3592 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
3593  max[4] = MAX(max[4], weathermap[x][y].windspeed);
3594 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
3595 /* max[7] = MAX(max[7], real_temp[x][y]); */
3596  total_rainfall += weathermap[x][y].rainfall;
3597  total_wind += weathermap[x][y].windspeed;
3598  }
3599  }
3600  // Twiddle the data on total rainfall, since they have a different color for above/below average
3601  // This allows us to have the full scale of color range on each above average and below average.
3602  avgrain = total_rainfall/(WEATHERMAPTILESX*WEATHERMAPTILESY);
3603  assert(avgrain >= 0);
3604  avgwind = (total_wind /(WEATHERMAPTILESX*WEATHERMAPTILESY));
3605  max[2] = avgrain >= 1 ? avgrain-1 : 0;
3606  realscalewind = 255.0l/(max[4]);
3607  max[4] = avgwind >= 1 ? avgwind-1 : 0;
3608  for (x = 0; x < 8; x++) {
3609  scale[x] = 255.0l/(max[x] != min[x] ? max[x] - min[x] : 1);
3610  }
3611 
3612  LOG(llevDebug, "Writing weather conditions map.\n");
3613 
3614  snprintf(filename, sizeof(filename), "%s/weather.ppm", settings.localdir);
3615  fp = of_open(&of, filename);
3616  if (fp == NULL) {
3617  LOG(llevError, "Cannot open %s for writing\n", filename);
3618  return 1;
3619  }
3620  fprintf(fp, "P6\n%d %d\n", 3*WEATHERMAPTILESX, 3*WEATHERMAPTILESY);
3621  fprintf(fp, "255\n");
3622  // First row of maps
3623  for (y = 0; y < WEATHERMAPTILESY; y++) {
3624  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3625  for (x = 0; x < WEATHERMAPTILESX; x++) {
3626  // water/tree map -- first map of row
3627  // blue = high water amount, black = low water amount, red = desert-like, green = trees
3628  if (weathermap[x][y].water < 0)
3629  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(weathermap[x][y].water-min[0])*scale[0]*2);
3630  else
3631  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].water)*scale[0]*2);
3632  // Either way, we use green to highlight the trees, too
3633  // Make this real simple since it is established that forestry values range from 0 to 100.
3634  // As a result, our values go from 0-250.
3635  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].forestry)*5/2);
3636  // elevation map -- second map of row.
3637  // green -- mostly land --> brighter green is higher elevation
3638  // blue -- mostly water --> deeper blue is lower elevation
3639  if (weathermap[x][y].avgelev >= 0) {
3640  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3641  } else {
3642  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3643  }
3644  // rainfall map -- third map of row
3645  // magenta = high rainfall, blue = average rainfall, black = low rainfall
3646  if (weathermap[x][y].rainfall >= uint32_t(avgrain)) { /* rainfall is rather spikey, this gives us more detail. */
3647  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = 255;
3648  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((weathermap[x][y].rainfall-avgrain)*(255.0/(max[2]-avgrain)));
3649  } else {
3650  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].rainfall-min[2])*(avgrain-min[2]));
3651  }
3652  }
3653  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3654  }
3655  // Second row of maps.
3656  for (y = 0; y < WEATHERMAPTILESY; y++) {
3657  for (x = 0; x < WEATHERMAPTILESX; x++) {
3658  uint32_t dir = directions[weathermap[x][y].winddir-1];
3659  int32_t speed = weathermap[x][y].windspeed;
3660  int16_t pressure = (weathermap[x][y].pressure-PRESSURE_MIN)*scale[3];
3661  // Make sure we don't get artifacting from Post-smoothing pressure adjustments.
3662  // or round-off error in the above calculation.
3663  pressure = MIN(255, MAX(0, pressure));
3664  // Pressure -- first map of row
3665  // light = high pressure, dark = low pressure
3666  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = pressure;
3667  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = pressure;
3668  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = pressure;
3669  // Wind speed -- second map of row
3670  // very high wind = red, else grey = average wind, dark = low wind
3671  if (speed < avgwind) {
3672  speed = (speed)*scale[4]/2;
3673  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = speed;
3674  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = speed;
3675  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = speed;
3676  } else {
3677  speed = (speed-avgwind)*realscalewind/2;
3678  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(MIN(255,(avgwind)*scale[4]/2+speed));
3679  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (avgwind)*scale[4]/2 - speed;
3680  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (avgwind)*scale[4]/2 - speed;
3681  }
3682  // Wind direction -- third map of row
3683  // red = northeast, yellow = east, green = southeast, cyan = south,
3684  // blue = southwest, magenta = west, white = northwest, black = north
3685  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3686  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3687  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3688  }
3689  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3690  }
3691  // Third row of maps
3692  for (y = 0; y < WEATHERMAPTILESY; y++) {
3693  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3694  for (x = 0; x < WEATHERMAPTILESX; x++) {
3695  uint32_t dir = skies[weathermap[x][y].sky];
3696  // Humidity -- first map of row.
3697  // blue = high humidity, black = low humidity, red = droughty (even lower humidity)
3698  // Didn't adjust min for this one, so it should look a little different than the others.
3699  if (weathermap[x][y].humid < 0)
3700  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(-weathermap[x][y].humid)*scale[6]);
3701  else
3702  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].humid-min[6])*scale[6]);
3703  // Real temperature -- second map of row
3704  // temp < 0 --> scale from white to blue
3705  // temp > 0 --> scale from blue to green to yellow to red
3706  // green is 20 C
3707  // yellow is 30 C
3708  int temp = real_temperature(x, y, &tod);
3709  // white -> cyan
3710  if (temp < 0) {
3711  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(-temp * scale[7]*2);
3712  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)0xFF;
3713  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)0xFF;
3714  }
3715  // cyan->blue is the boundary for above/below freezing
3716  // blue -> green
3717  else if (temp < 20) {
3718  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 0;
3719  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)(temp * scale[7]*9/2);
3720  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((20-temp) * scale[7]*9/2);
3721  }
3722  // green -> yellow
3723  else if (temp < 30) {
3724  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)((temp-20) * scale[7]*9);
3725  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = 255;
3726  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3727  }
3728  // yellow -> red
3729  else {
3730  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 255;
3731  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((45-temp) * scale[7]*6);
3732  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3733  }
3734  // current weather -- third map of row
3735  // blue = clear, medium blue = light clouds, dark blue = overcast, green = light rain
3736  // medium green = rain, dark green = heavy rain, yellow = hurricane, red = fog, magenta = hail,
3737  // dark gray = light snow, medium gray = snow, gray = heavy snow, white = blizzard
3738  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3739  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3740  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3741  }
3742  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3743  }
3744  of_close(&of);
3745  return 0;
3746 }
3747 
3748 /********************************************************************************************
3749  * Section END -- weather data writers
3750  ********************************************************************************************/
3751 
3752 /********************************************************************************************
3753  * Section -- weather data readers
3754  * These read weather data that gets periodically stored to file to restore
3755  * the prior state on a server restart. They also handle first-load initialization.
3756  ********************************************************************************************/
3757 
3775 static int read_forestrymap(const Settings *settings) {
3776  char filename[MAX_BUF], *data, *tmp;
3777  BufferReader *bfr;
3778  int trees, x, y, res;
3779 
3780  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3781  LOG(llevDebug, "Reading forestry data from %s...\n", filename);
3782  // Set up the bufferreader and read in the file.
3783  // We do it through the bufferreader so that we only dip into I/O once,
3784  // and the rest is just parsing it in memory.
3785  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3786  // We already printed our error if we failed, so just bail in that case.
3787  if (bfr == NULL)
3788  return -1;
3789  // Parse the file. Since this is auto-generated by the weather system,
3790  // just bail if the file is malformed.
3791  data = bufferreader_data(bfr);
3792  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3793  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3794  res = sscanf(data, "%d ", &trees);
3795  if (res != 1) {
3796  LOG(llevError, "Forestry data is corrupted and should be regenerated.\n"
3797  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3798  bufferreader_destroy(bfr);
3799  return -1;
3800  }
3801  // Limit the range from 0 to 100
3802  weathermap[x][y].forestry = MIN(100, MAX(0, trees));
3803  // Now we move where we're looking, since we want to read more than just the first entry.
3804  // Use strpbrk so that we can handle newlines more cleanly.
3805  tmp = strpbrk(data, " \n");
3806  if (tmp != NULL)
3807  data = tmp + 1;
3808  else {
3809  LOG(llevError, "Unexpected end of forestry file. Forestry file may need to be regenerated.\n"
3810  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3811  bufferreader_destroy(bfr);
3812  return -1;
3813  }
3814  }
3815  // Due to the way the file is written, the end of the line should have a space and a newline.
3816  // Handle the newline if it is the front of the string now.
3817  if (*data == '\n')
3818  ++data;
3819  }
3820  bufferreader_destroy(bfr);
3821  return 0;
3822 }
3823 
3838 static int read_humidmap(const Settings *settings) {
3839  char filename[MAX_BUF], *data, *tmp;
3840  BufferReader *bfr;
3841  int x, y, hmd, res;
3842 
3843  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3844  LOG(llevDebug, "Reading humidity data from %s...\n", filename);
3845  // Open and read in the file all at once
3846  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3847  // If we fail, we do initializations instead.
3848  if (bfr == NULL) {
3849  LOG(llevInfo, "Initializing humidity and elevation maps...\n");
3855  LOG(llevDebug, "Done\n");
3856  return 1;
3857  }
3858  data = bufferreader_data(bfr);
3859  for (x = 0; x < WEATHERMAPTILESX; x++) {
3860  for (y = 0; y < WEATHERMAPTILESY; y++) {
3861  res = sscanf(data, "%d ", &hmd);
3862  if (res != 1) {
3863  LOG(llevError, "Humidity data is corrupted and cannot be loaded.\n"
3864  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3865  bufferreader_destroy(bfr);
3866  return -1;
3867  }
3868  // Limit range from 0 to 100. Clip entries that surpass these bounds.
3869  weathermap[x][y].humid = MAX(0, MIN(100, hmd));
3870  // Move to the next spot in the buffer.
3871  tmp = strpbrk(data, " \n");
3872  if (tmp == NULL) {
3873  LOG(llevError, "Unexpected end of humidity file.\n"
3874  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3875  bufferreader_destroy(bfr);
3876  return -1;
3877  }
3878  // If found, move data to the next character after the space/newline found.
3879  data = tmp + 1;
3880  }
3881  // If the newline from the previous line hasn't been parsed yet, skip it.
3882  if (*data == '\n')
3883  ++data;
3884  }
3885  bufferreader_destroy(bfr);
3886  LOG(llevDebug, "Done.\n");
3887  return 0;
3888 }
3889 
3902 static int read_elevmap(const Settings *settings) {
3903  char filename[MAX_BUF], *data, *tmp;
3904  BufferReader *bfr;
3905  int x, y, elev, res;
3906 
3907  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3908  LOG(llevDebug, "Reading elevation data from %s...\n", filename);
3909  // Read file into a buffer.
3910  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3911  // If failed, we bail.
3912  if (bfr == NULL) {
3913  /* initializing these is expensive, and should have been done
3914  by the humidity. It's not worth the wait to do it twice. */
3915  return -1;
3916  }
3917  data = bufferreader_data(bfr);
3918  for (x = 0; x < WEATHERMAPTILESX; x++) {
3919  for (y = 0; y < WEATHERMAPTILESY; y++) {
3920  res = sscanf(data, "%d ", &elev);
3921  if (res != 1) {
3922  LOG(llevError, "Elevation data is corrupted and cannot be loaded.\n"
3923  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3924  bufferreader_destroy(bfr);
3925  return -1;
3926  }
3927  // Individual elevation values should be in the range [-32000, 32000]
3928  // Averages can be capped to these as well.
3929  weathermap[x][y].avgelev = MAX(-32000, MIN(32000, elev));
3930  // Now we move to the next entry in the buffer.
3931  tmp = strpbrk(data, " \n");
3932  if (tmp == NULL) {
3933  LOG(llevError, "Unexpected end of file in elevation data.\n"
3934  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3935  bufferreader_destroy(bfr);
3936  return -1;
3937  }
3938  // If found, we move to the character after the space/newline.
3939  data = tmp + 1;
3940  }
3941  // If there's still a newline after passing a space, then skip past it.
3942  if (*data == '\n')
3943  ++data;
3944  }
3945  bufferreader_destroy(bfr);
3946  LOG(llevDebug, "Done.\n");
3947  return 0;
3948 }
3949 
3961 static int read_watermap(const Settings *settings) {
3962  char filename[MAX_BUF], *data, *tmp;
3963  BufferReader *bfr;
3964  int x, y, wtr, res;
3965 
3966  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3967  LOG(llevDebug, "Reading water data from %s...\n", filename);
3968  // Read the file into a buffer.
3969  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3970  // If failed, we bail
3971  if (bfr == NULL) {
3972  /* initializing these is expensive, and should have been done
3973  by the humidity. It's not worth the wait to do it twice. */
3974  return -1;
3975  }
3976  data = bufferreader_data(bfr);
3977  for (x = 0; x < WEATHERMAPTILESX; x++) {
3978  for (y = 0; y < WEATHERMAPTILESY; y++) {
3979  res = sscanf(data, "%d ", &wtr);
3980  if (res != 1) {
3981  LOG(llevError, "Water map is corrupted and cannot be loaded.\n"
3982  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3983  bufferreader_destroy(bfr);
3984  return -1;
3985  }
3986  // Range is -100 to 100 due to deserts and such being negative.
3987  weathermap[x][y].water = MAX(-100, MIN(100, wtr));
3988  // Adjust the data pointer.
3989  tmp = strpbrk(data, " \n");
3990  if (tmp == NULL) {
3991  LOG(llevError, "Unexpected end of file in water map.\n"
3992  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3993  bufferreader_destroy(bfr);
3994  return -1;
3995  }
3996  // Okay, so we want the first character after the space/newline.
3997  data = tmp + 1;
3998  }
3999  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4000  if (*data == '\n')
4001  ++data;
4002  }
4003  bufferreader_destroy(bfr);
4004  LOG(llevDebug, "Done.\n");
4005  return 0;
4006 }
4007 
4020  char filename[MAX_BUF], *data, *tmp;
4021  BufferReader *bfr;
4022  int x, y, res;
4023  int16_t temperature;
4024 
4025  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
4026  LOG(llevDebug, "Reading temperature data from %s...\n", filename);
4027  // Read the file into a buffer.
4028  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4029  // If it fails, we initialize the temperature map and write it to a file.
4030  if (bfr == NULL) {
4031  LOG(llevInfo, "Initializing temperature map.\n");
4032  init_temperature();
4033  // If writing was successful, then consider this a success.
4034  if (write_temperaturemap(settings) == 0)
4035  return 1;
4036  return -1;
4037  }
4038  data = bufferreader_data(bfr);
4039  for (x = 0; x < WEATHERMAPTILESX; x++) {
4040  for (y = 0; y < WEATHERMAPTILESY; y++) {
4041  res = sscanf(data, "%hd ", &temperature);
4042  if (res != 1) {
4043  LOG(llevError, "Temperature file is malformed, unable to load temps from file.\n");
4044  bufferreader_destroy(bfr);
4045  return -1;
4046  }
4047  // Clip to reasonable bounds.
4048  weathermap[x][y].temp = MIN(60, MAX(-30, temperature));
4049  // Adjust the data pointer.
4050  tmp = strpbrk(data, " \n");
4051  if (tmp == NULL) {
4052  LOG(llevError, "Unexpected end of file in temperature map.\n");
4053  bufferreader_destroy(bfr);
4054  return -1;
4055  }
4056  // Okay, so we want the first character after the space/newline.
4057  data = tmp + 1;
4058  }
4059  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4060  if (*data == '\n')
4061  ++data;
4062  }
4063  bufferreader_destroy(bfr);
4064  LOG(llevDebug, "Done.\n");
4065  return 0;
4066 }
4067 
4081 static int read_rainfallmap(const Settings *settings) {
4082  char filename[MAX_BUF], *data, *tmp;
4083  BufferReader *bfr;
4084  int x, y, res;
4085 
4086  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
4087  LOG(llevDebug, "Reading rainfall data from %s...\n", filename);
4088  // Read file into a buffer.
4089  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4090  // If it fails, we initialize and write to file.
4091  if (bfr == NULL) {
4092  LOG(llevInfo, "Initializing rainfall map...\n");
4093  init_rainfall();
4094  // If we write to file successfully, consider initialization a success.
4095  if (write_rainfallmap(settings) != 0)
4096  return -1;
4097  return 1;
4098  }
4099  data = bufferreader_data(bfr);
4100  for (x = 0; x < WEATHERMAPTILESX; x++) {
4101  for (y = 0; y < WEATHERMAPTILESY; y++) {
4102  res = sscanf(data, "%u ", &weathermap[x][y].rainfall);
4103  if (res != 1) {
4104  LOG(llevError, "Rainfall file is corrupted, cannot load rainfall from file.\n");
4105  bufferreader_destroy(bfr);
4106  return -1;
4107  }
4108  // Now we update the pointer to data to move to the next item.
4109  tmp = strpbrk(data, " \n");
4110  if (tmp == NULL) {
4111  LOG(llevError, "Unexpected end of file in rainfall map.\n");
4112  bufferreader_destroy(bfr);
4113  return -1;
4114  }
4115  // Okay, so we want the first character after the space/newline.
4116  data = tmp + 1;
4117  }
4118  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4119  if (*data == '\n')
4120  ++data;
4121  }
4122  bufferreader_destroy(bfr);
4123  LOG(llevDebug, "Done.\n");
4124  return 0;
4125 }
4126 
4139  char filename[MAX_BUF], *data, *tmp;
4140  BufferReader *bfr;
4141  int x, y, in, res;
4142 
4143  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
4144  LOG(llevDebug, "Reading gulf stream data from %s...\n", filename);
4145  // Read the file into a buffer
4146  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4147  // If it fails, we initialize and write to file.
4148  if (bfr == NULL) {
4149  LOG(llevInfo, "Initializing gulf stream maps...\n");
4152  LOG(llevDebug, "Done\n");
4153  if (res == 0)
4154  return 1;
4155  return -1;
4156  }
4157  data = bufferreader_data(bfr);
4158  // First we read in the speeds
4159  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4160  for (y = 0; y < WEATHERMAPTILESY; y++) {
4161  res = sscanf(data, "%d ", &in);
4162  if (res != 1) {
4163  LOG(llevError, "Gulf stream speed definitions are malformed. Cannot load gulf stream from file.\n");
4164  bufferreader_destroy(bfr);
4165  return -1;
4166  }
4167  gulf_stream_speed[x][y] = MIN(120, MAX(0, in));
4168  // Now we update the pointer to data to move to the next item.
4169  tmp = strpbrk(data, " \n");
4170  if (tmp == NULL) {
4171  LOG(llevError, "Unexpected end of file in gulfstream speed map.\n");
4172  bufferreader_destroy(bfr);
4173  return -1;
4174  }
4175  // Okay, so we want the first character after the space/newline.
4176  data = tmp + 1;
4177  }
4178  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4179  if (*data == '\n')
4180  ++data;
4181  }
4182  // Then we read in the directions.
4183  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4184  for (y = 0; y < WEATHERMAPTILESY; y++) {
4185  res = sscanf(data, "%d ", &in);
4186  if (res != 1) {
4187  LOG(llevError, "Gulf stream direction definitions are malformed. Cannot load gulf stream from file.\n");
4188  bufferreader_destroy(bfr);
4189  return -1;
4190  }
4191  gulf_stream_dir[x][y] = MAX(1, MIN(8, in));
4192  // Now we update the pointer to data to move to the next item.
4193  tmp = strpbrk(data, " \n");
4194  if (tmp == NULL) {
4195  LOG(llevError, "Unexpected end of file in gulfstream direction map.\n");
4196  bufferreader_destroy(bfr);
4197  return -1;
4198  }
4199  // Okay, so we want the first character after the space/newline.
4200  data = tmp + 1;
4201  }
4202  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4203  if (*data == '\n')
4204  ++data;
4205  }
4206  // Then we read in the start point, if it exists
4207  // For backward compatability, we randomly initialize this if we can't read it.
4208  res = sscanf(data, "%d\n", &in);
4209  if (res != 1) {
4210  LOG(llevInfo, "Gulf stream file lacks start position, and is assumed to be old; initializing it randomly.\n");
4212  }
4214  // If we add any more parsing here, we'll need to affect the data pointer.
4215  // But, since we don't, leave it stale until it falls out of scope.
4216  bufferreader_destroy(bfr);
4217  LOG(llevDebug, "Done.\n");
4218  return 0;
4219 }
4220 
4235  char filename[MAX_BUF], *data, *tmp;
4236  BufferReader *bfr;
4237  int x, y, res;
4238  int8_t spd;
4239 
4240  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
4241  LOG(llevDebug, "Reading wind speed data from %s...\n", filename);
4242  // Read the file into a buffer
4243  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4244  // If it fails, we bail
4245  if (bfr == NULL) {
4246  // Wind direction is done before this, and should have initialized this already.
4247  return -1;
4248  }
4249  data = bufferreader_data(bfr);
4250  for (x = 0; x < WEATHERMAPTILESX; x++) {
4251  for (y = 0; y < WEATHERMAPTILESY; y++) {
4252  res = sscanf(data, "%hhd ", &spd);
4253  if (res != 1) {
4254  LOG(llevError, "Wind speed file is malformed. Cannot load wind speed file.\n");
4255  bufferreader_destroy(bfr);
4256  return -1;
4257  }
4258  // Clip to reasonable bounds
4259  weathermap[x][y].windspeed = MIN(120, MAX(0, spd));
4260  // Now we update the pointer to data to move to the next item.
4261  tmp = strpbrk(data, " \n");
4262  if (tmp == NULL) {
4263  LOG(llevError, "Unexpected end of file in wind speed map.\n");
4264  bufferreader_destroy(bfr);
4265  return -1;
4266  }
4267  // Okay, so we want the first character after the space/newline.
4268  data = tmp + 1;
4269  }
4270  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4271  if (*data == '\n')
4272  ++data;
4273  }
4274  bufferreader_destroy(bfr);
4275  LOG(llevDebug, "Done.\n");
4276  return 0;
4277 }
4278 
4289 static int read_winddirmap(const Settings *settings) {
4290  char filename[MAX_BUF], *data, *tmp;
4291  BufferReader *bfr;
4292  int x, y, d, res;
4293 
4294  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
4295  LOG(llevDebug, "Reading wind direction data from %s...\n", filename);
4296  // Read the file into a buffer
4297  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4298  // If it fails, initialize the wind
4299  if (bfr == NULL) {
4300  LOG(llevInfo, "Initializing wind direction and speed maps...\n");
4301  init_wind();
4302  // If both of these succeed, the end result is 0.
4303  res = write_winddirmap(settings);
4304  res += write_windspeedmap(settings);
4305  LOG(llevDebug, "Done\n");
4306  if (res == 0)
4307  return 1;
4308  return -1;
4309  }
4310  data = bufferreader_data(bfr);
4311  for (x = 0; x < WEATHERMAPTILESX; x++) {
4312  for (y = 0; y < WEATHERMAPTILESY; y++) {
4313  res = sscanf(data, "%d ", &d);
4314  if (res != 1) {
4315  LOG(llevError, "Wind direction map is malformed. Could not load wind direction.\n");
4316  bufferreader_destroy(bfr);
4317  return -1;
4318  }
4319  // If the direction is not valid, just give it one randomly.
4320  if (d < 1 || d > 8) {
4321  d = rndm(1, 8);
4322  }
4323  weathermap[x][y].winddir = d;
4324  // Now we update the pointer to data to move to the next item.
4325  tmp = strpbrk(data, " \n");
4326  if (tmp == NULL) {
4327  LOG(llevError, "Unexpected end of file in wind direction map.\n");
4328  bufferreader_destroy(bfr);
4329  return -1;
4330  }
4331  // Okay, so we want the first character after the space/newline.
4332  data = tmp + 1;
4333  }
4334  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4335  if (*data == '\n')
4336  ++data;
4337  }
4338  bufferreader_destroy(bfr);
4339  LOG(llevDebug, "Done.\n");
4340  return 0;
4341 }
4342 
4353 static int read_pressuremap(const Settings *settings) {
4354  char filename[MAX_BUF], *data, *tmp;
4355  BufferReader *bfr;
4356  int x, y, res;
4357  int16_t press;
4358 
4359  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
4360  LOG(llevDebug, "Reading pressure data from %s...\n", filename);
4361  // Read the file into a buffer.
4362  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading\n", llevError);
4363  // If it fails, we initialize the pressure.
4364  if (bfr == NULL) {
4365  LOG(llevInfo, "Initializing pressure maps...\n");
4366  init_pressure();
4367  res = write_pressuremap(settings);
4368  LOG(llevDebug, "Done\n");
4369  if (res == 0)
4370  return 1;
4371  return -1;
4372  }
4373  data = bufferreader_data(bfr);
4374  for (x = 0; x < WEATHERMAPTILESX; x++) {
4375  for (y = 0; y < WEATHERMAPTILESY; y++) {
4376  res = sscanf(data, "%hd ", &press);
4377  if (res != 1) {
4378  LOG(llevError, "Pressure map is malformed. Could not load pressure.\n");
4379  bufferreader_destroy(bfr);
4380  return -1;
4381  }
4382  // Apply clipping to the pressure.
4383  weathermap[x][y].pressure = MIN(PRESSURE_MAX, MAX(PRESSURE_MIN, press));
4384  // Now we update the pointer to data to move to the next item.
4385  tmp = strpbrk(data, " \n");
4386  if (tmp == NULL) {
4387  LOG(llevError, "Unexpected end of file in pressure map.\n");
4388  bufferreader_destroy(bfr);
4389  return -1;
4390  }
4391  // Okay, so we want the first character after the space/newline.
4392  data = tmp + 1;
4393  }
4394  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4395  if (*data == '\n')
4396  ++data;
4397  }
4398  bufferreader_destroy(bfr);
4399  LOG(llevDebug, "Done.\n");
4400  return 0;
4401 }
4402 
4416  char filename[MAX_BUF], *data;
4417  BufferReader *bfr;
4418  int sx, sy, res;
4419 
4420  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings->localdir);
4421  LOG(llevDebug, "Reading current weather position from %s...\n", filename);
4422  // Read the file into a buffer.
4423  bfr = bufferreader_init_from_file(NULL, filename, "Can't open %s: %s.\n", llevError);
4424  // If we fail, set wmperformstartx/y to their default values.
4425  if (bfr == NULL) {
4426  wmperformstartx = -1;
4427  wmperformstarty = 0;
4428  return 1;
4429  }
4430  data = bufferreader_data(bfr);
4431  // Read from the buffer
4432  res = sscanf(data, "%d %d", &sx, &sy);
4433  // Whether success or failure, we're done with the buffer.
4434  bufferreader_destroy(bfr);
4435  // Now we check our result.
4436  if (res != 2) {
4437  LOG(llevError, "Weather position file was malformed. Using default position.\n");
4438  wmperformstartx = -1;
4439  wmperformstarty = 0;
4440  return 1;
4441  }
4442  LOG(llevDebug, "curposx=%d curposy=%d\n", sx, sy);
4443 
4444  if (sx > int(settings->worldmaptilesx)) {
4445  sx = -1;
4446  }
4447  if (sy > int(settings->worldmaptilesy)) {
4448  sy = 0;
4449  }
4450  // Now we apply these to the static variables.
4451  wmperformstartx = sx;
4452  wmperformstarty = sy;
4453  return 0;
4454 }
4455 
4456 /********************************************************************************************
4457  * Section END -- weather data readers
4458  ********************************************************************************************/
4459 
4460 /********************************************************************************************
4461  * Section -- weather event listeners
4462  ********************************************************************************************/
4463 
4473 static int weather_listener(int *type, ...) {
4474  va_list args;
4475  int code;
4476  mapstruct *m;
4477 
4478  va_start(args, type);
4479  code = va_arg(args, int);
4480 
4481  switch (code) {
4482  case EVENT_MAPENTER:
4483  // At this point, we don't need the entering object for this.
4484  // but it is passed as an arg, so just skip it.
4485  va_arg(args, object *);
4486  /* FALLTHROUGH */
4487  case EVENT_MAPLOAD:
4488  case EVENT_MAPREADY:
4489  m = va_arg(args, mapstruct *);
4490  if (m->outdoor)
4492  break;
4493  }
4494 
4495  va_end(args);
4496 
4497  return 0;
4498 }
4499 
4512 static int weather_clock_listener(int *type, ...) {
4513  va_list args;
4514  int code;
4515 
4516  va_start(args, type);
4517  code = va_arg(args, int);
4518  va_end(args);
4519 
4520  switch (code) {
4521  case EVENT_CLOCK:
4522  if (!(pticks%PTICKS_PER_CLOCK)) {
4523  /* call the weather calculators, here, in order */
4524  tick_weather();
4525  // At every hour, measure the rainfall.
4526  // pticks%PTICKS_PER_CLOCK is already triggering every hour.
4527  process_rain();
4528  /* perform_weather must follow calculators */
4529  perform_weather();
4530  }
4531  // Handle weather printouts after enough server time.
4532  // By using primes here, rather than inside tick_the_clock(), we can reduce the load on a given tick.
4533  // (pticks%1500 is real busy otherwise)
4534  // This produces the side-effect that the server actually has to be on for that length
4535  // of time without interruption for the save to happen, but most servers are run long-term
4536  // anyway, so this should not be an issue
4537  if (!(pticks%1511))
4539  if (!(pticks%31511))
4541  if (!(pticks%33013))
4543  if (!(pticks%34501))
4545  if (!(pticks%36007))
4547  if (!(pticks%39019))
4549  if (!(pticks%40507))
4551  if (settings.fastclock > 0 && !(pticks%42013))
4552  write_skymap();
4553  if (!(pticks%43517))
4555  break;
4556  }
4557 
4558  return 0;
4559 }
4560 
4570 static int weather_object_listener(int *type, ...) {
4571  va_list args;
4572  int code;
4573  object *op;
4574 
4575  va_start(args, type);
4576  code = va_arg(args, int);
4577  // At this point, we don't need the entering object for this.
4578  // but it is passed as an arg, so just skip it.
4579  op = va_arg(args, object *);
4580 
4581  va_end(args);
4582 
4583  switch (code) {
4584  case EVENT_TIME:
4585  // Have weather affect the position of the object. Do not blow the floors, though -- that is bad.
4586  if (op->map && !QUERY_FLAG(op, FLAG_IS_FLOOR)) {
4587  uint8_t dir = wind_blow_object(op->map, op->x, op->y, op->move_type, op->weight+op->carrying, &op->stats);
4588  mapstruct *m;
4589  int16_t x, y;
4590  // By checking only the head space, we can actually caue sailing galleons to irrecoverably crash ashore.
4591  // This is deliberate behavior.
4592  if (dir &&
4593  // We will avoid pushing empty transports. Assume they are anchored/parked.
4594  !(op->type == TRANSPORT && op->inv == NULL) &&
4595  // If our player is in a transport, do not push the player
4596  !(op->type == PLAYER && op->contr && op->contr->transport) &&
4597  !(get_map_flags(op->map, &m, op->x+freearr_x[dir], op->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP) &&
4598  !blocked_link(op, m, x, y)) {
4599  object_remove(op);
4600  object_insert_in_map_at(op, m, op, 0, x, y);
4601 
4602  // Make sure to update the player view
4603  if (op->type == PLAYER) {
4604  esrv_map_scroll(op->contr->socket, freearr_x[dir], freearr_y[dir]);
4605  op->contr->socket->update_look = 1;
4606  op->contr->socket->look_position = 0;
4607  } else if (op->type == TRANSPORT) {
4608  FOR_INV_PREPARE(op, pl)
4609  if (pl->type == PLAYER) {
4610  pl->contr->do_los = 1;
4611  pl->map = op->map;
4612  pl->x = op->x;
4613  pl->y = op->y;
4614  esrv_map_scroll(pl->contr->socket, freearr_x[dir], freearr_y[dir]);
4615  pl->contr->socket->update_look = 1;
4616  pl->contr->socket->look_position = 0;
4617  }
4618  FOR_INV_FINISH();
4619  }
4620  }
4621  }
4622  }
4623  return 0;
4624 }
4625 
4634 static void command_weather (object *op, const char * /*params*/) {
4635  int wx, wy, temp, sky;
4636  const char *buf;
4637 
4638  if (wset.dynamiclevel < 1) {
4640  "The weather is perpetually great around here.");
4641  return;
4642  }
4643 
4644  if (op->map == NULL)
4645  return;
4646 
4647  if (worldmap_to_weathermap(op->x, op->y, &wx, &wy, op->map) != 0) {
4649  "You can't see the weather from here.");
4650  return;
4651  }
4652 
4653  if (QUERY_FLAG(op, FLAG_WIZ)) {
4654  /* dump the weather, Dm style! Yo! */
4655 
4657  "Real temp: %d", real_world_temperature(op->x, op->y, op->map));
4658 
4660  "Base temp: %d", weathermap[wx][wy].temp);
4661 
4663  "Humid: %d", weathermap[wx][wy].humid);
4664 
4666  "Wind: dir=%d speed=%d", weathermap[wx][wy].winddir, weathermap[wx][wy].windspeed);
4667 
4669  "Pressure: %d", weathermap[wx][wy].pressure);
4670 
4672  "Avg Elevation: %d", weathermap[wx][wy].avgelev);
4673 
4675  "Rainfall: %d Water: %d", weathermap[wx][wy].rainfall, weathermap[wx][wy].water);
4676  }
4677 
4678  temp = real_world_temperature(op->x, op->y, op->map);
4680  "It's currently %d degrees Centigrade out.", temp);
4681 
4682  /* humid */
4683  // We use buf real quick here for the same reason we use it for sky conditions: clarity
4684  if (weathermap[wx][wy].humid < 20)
4685  buf = "It is very dry.";
4686  else if (weathermap[wx][wy].humid < 40)
4687  buf = "It is rather dry.";
4688  else if (weathermap[wx][wy].humid < 60)
4689  buf = "It is very comfortable today.";
4690  else if (weathermap[wx][wy].humid < 75)
4691  buf = "It is a bit muggy.";
4692  else if (weathermap[wx][wy].humid < 85)
4693  buf = "It is muggy.";
4694  else
4695  buf = "It is uncomfortably muggy.";
4696  // buf will have a string, based on the if clauses above
4698 
4699  /* wind */
4700  switch (weathermap[wx][wy].winddir) {
4701  case 1:
4702  buf = "north";
4703  break;
4704  case 2:
4705  buf = "northeast";
4706  break;
4707  case 3:
4708  buf = "east";
4709  break;
4710  case 4:
4711  buf = "southeast";
4712  break;
4713  case 5:
4714  buf = "south";
4715  break;
4716  case 6:
4717  buf = "southwest";
4718  break;
4719  case 7:
4720  buf = "west";
4721  break;
4722  case 8:
4723  buf = "northwest";
4724  break;
4725  }
4726  if (weathermap[wx][wy].windspeed < 5)
4728  "There is a mild breeze coming from the %s.", buf);
4729  else if (weathermap[wx][wy].windspeed < 10)
4731  "There is a strong breeze coming from the %s.", buf);
4732  else if (weathermap[wx][wy].windspeed < 15)
4734  "There is a light wind coming from the %s.", buf);
4735  else if (weathermap[wx][wy].windspeed < 25)
4737  "There is a strong wind coming from the %s.", buf);
4738  else if (weathermap[wx][wy].windspeed < 35)
4740  "There is a heavy wind coming from the %s.", buf);
4741  else
4743  "The wind from the %s is incredibly strong!", buf);
4744 
4745  // If the current tile is below freezing, refer to the tile as snowing, even if the weathermap says rain.
4746  sky = weathermap[wx][wy].sky;
4747  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG)
4748  sky += 10; /*let it snow*/
4749  // Likewise, if the given tile is above freezing when the weathermap as a whole is below, say rain
4750  else if (temp > 0 && sky >= SKY_LIGHT_SNOW)
4751  sky -= 10;
4752 
4753  // Recycle buf for printing the various sky conditions strings.
4754  // This way we can cleanly call draw_ext_info once and not have
4755  // a bajillion calls with the same parameters other than the text.
4756  switch (sky) {
4757  case SKY_CLEAR:
4758  buf = "There isn't a cloud in the sky.";
4759  break;
4760  case SKY_LIGHTCLOUD:
4761  buf = "There are a few light clouds in the sky";
4762  break;
4763  case SKY_OVERCAST:
4764  buf = "The sky is cloudy and dreary.";
4765  break;
4766  case SKY_LIGHT_RAIN:
4767  buf = "It is raining softly.";
4768  break;
4769  case SKY_RAIN:
4770  buf = "It is raining.";
4771  break;
4772  case SKY_HEAVY_RAIN:
4773  buf = "It is raining heavily.";
4774  break;
4775  case SKY_HURRICANE:
4776  buf = "There is a heavy storm! You should go inside!";
4777  break;
4778  case SKY_FOG:
4779  buf = "It's foggy and miserable.";
4780  break;
4781  case SKY_HAIL:
4782  buf = "It's hailing out! Take cover!";
4783  break;
4784  case SKY_LIGHT_SNOW:
4785  buf = "Snow is gently falling from the sky.";
4786  break;
4787  case SKY_SNOW:
4788  buf = "It is snowing out.";
4789  break;
4790  case SKY_HEAVY_SNOW:
4791  buf = "Snow is falling very heavily.";
4792  break;
4793  case SKY_BLIZZARD:
4794  buf = "A full blown blizzard is in effect. You might want to take cover!";
4795  break;
4796  default:
4797  buf = NULL;
4798  }
4799  // If the sky is valid, then print the conditions.
4800  if (buf != NULL)
4802 }
4803 
4804 /********************************************************************************************
4805  * Section END -- weather event listeners
4806  ********************************************************************************************/
4807 
4808 // Event/command handler ids start at 1, so 0 is an unset flag.
4812 
4814 
4820  int x, tx, ty;
4821 
4822  // Initialize weather settings
4824 
4825  /* all this stuff needs to be set, otherwise this function will cause
4826  * chaos and destruction.
4827  */
4828  if (wset.dynamiclevel < 1) {
4829  LOG(llevInfo, "cfweather_init: dynamic level set to %d. Not loading weather.\n", wset.dynamiclevel);
4830  return;
4831  }
4832 
4833  if (settings->worldmapstartx < 1 || settings->worldmapstarty < 1 ||
4836  return;
4837  }
4838  // Initialize the forestry information from file.
4839  init_config_vals(settings, "treedefs", &forest_list);
4840  init_config_vals(settings, "waterdefs", &water_list);
4841 
4842  /*prepare structures used for avoidance*/
4843  init_weatheravoid(settings, "wavoiddefs", &weather_avoids);
4844  init_weatheravoid(settings, "gavoiddefs", &growth_avoids);
4845 
4846  init_weather_replace(settings, "wreplacedefs", &weather_replace);
4849 
4850  // Set up weathermap grid. This is needed for just about everything
4851  LOG(llevDebug, "Initializing the weathermap...\n");
4852 
4853  weathermap = (weathermap_t **)malloc(sizeof(weathermap_t *)*WEATHERMAPTILESX);
4854  if (weathermap == NULL) {
4856  }
4857  for (x = 0; x < WEATHERMAPTILESX; x++) {
4858  weathermap[x] = (weathermap_t *)calloc(WEATHERMAPTILESY, sizeof(weathermap_t));
4859  if (weathermap[x] == NULL) {
4861  }
4862  }
4863  /* Unless you know what you're doing, do not re-order these
4864  * I think I got all the dependencies noted, but it works this way
4865  * and I'd advise against changing the order unless you have a good reason.
4866  */
4868  // Begin to initialize the various data pieces for the weather
4869  // If wind direction did initialization, we don't need to read the wind speed from file.
4870  if (read_winddirmap(settings) == 0)
4873  // Some gulf stream fiddling that happens every startup.
4874  gulf_stream_direction = rndm(0, 1);
4875  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
4876  for (ty = 0; ty < WEATHERMAPTILESY-1; ty++) {
4877  if (gulf_stream_direction) {
4878  switch (gulf_stream_dir[tx][ty]) {
4879  case 2: gulf_stream_dir[tx][ty] = 6; break;
4880  case 3: gulf_stream_dir[tx][ty] = 7; break;
4881  case 4: gulf_stream_dir[tx][ty] = 8; break;
4882  }
4883  } else {
4884  switch (gulf_stream_dir[tx][ty]) {
4885  case 6: gulf_stream_dir[tx][ty] = 2; break;
4886  case 7: gulf_stream_dir[tx][ty] = 3; break;
4887  case 8: gulf_stream_dir[tx][ty] = 4; break;
4888  }
4889  }
4890  }
4891  }
4892  // Trees help stabilize local temperature and evaporate water from deeper underground.
4893  // This is calculated at the same time as elevation and humidity.
4894  int result = read_humidmap(settings);
4895  // If result is 1, then we initialized all these.
4896  // When that is the case, we don't need to read in from the file.
4897  // If result is -1, everything's jacked up anyway, so still don't load.
4898  if (result == 0) {
4899  read_watermap(settings); /* On first run, we want to do this after humidity. Otherwise, it doesn't seem to matter */
4900  read_elevmap(settings); /* elevation must allways follow humidity */
4902  }
4905 
4906  LOG(llevDebug, "Done reading weathermaps\n");
4907  // Initialize the sky so we can get accurate precipitation at initial load.
4908  compute_sky();
4909 
4910  // Get current map position
4912 
4913  // Connect the events after initialization, since we don't need to do
4914  // precipitation when we're initializing.
4918  // Register the 'weather command
4920  /* Disable the plugin in case it's still there */
4921  serv->disabled_plugins.push_back(strdup("cfweather"));
4922 }
4923 
4925  // Define temp pointers for clearing up the linked lists
4926  DensityConfig *cur;
4927  weather_avoids_t *avcur;
4928  weather_replace_t *rpcur;
4929  // Unregister handlers.
4930  if (global_map_handler != 0)
4932  if (global_clock_handler != 0)
4934  if (global_object_handler != 0)
4936  if (command_handler != 0)
4938  // Free the weathermap
4939  // Turns out that asset collection reaches here with weathermap as NULL,
4940  // so we need to make sure it is nonnull before trying to free it.
4941  if (weathermap != NULL) {
4942  for (int x = 0; x < WEATHERMAPTILESX; x++) {
4944  }
4946  }
4947  // Deallocate our linked list of forest entries.
4948  while (forest_list != NULL) {
4949  cur = forest_list;
4951  free_string(cur->name);
4952  free(cur);
4953  }
4954  // Do the same for the water list
4955  while (water_list != NULL) {
4956  cur = water_list;
4958  free_string(cur->name);
4959  free(cur);
4960  }
4961  // Free our avoid lists
4962  while (weather_avoids != NULL) {
4963  avcur = weather_avoids;
4965  free_string(avcur->name);
4966  free(avcur);
4967  }
4968  while (growth_avoids != NULL) {
4969  avcur = growth_avoids;
4971  free_string(avcur->name);
4972  free(avcur);
4973  }
4974  // Free our replacement lists
4975  while (weather_replace != NULL) {
4976  rpcur = weather_replace;
4978  free_string(rpcur->tile);
4979  free(rpcur);
4980  }
4981  while (weather_evaporate != NULL) {
4982  rpcur = weather_evaporate;
4984  free_string(rpcur->tile);
4985  free(rpcur);
4986  }
4987  while (weather_snowmelt != NULL) {
4988  rpcur = weather_snowmelt;
4990  free_string(rpcur->tile);
4991  free(rpcur);
4992  }
4993 }
4994 
4995 
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:2983
weathermap_t::realtemp
int16_t realtemp
Temperature at a given calculation step for this tile.
Definition: cfweather.cpp:110
global.h
weather_grow_t::tempmax
int tempmax
Maximum temperature for herb to grow.
Definition: cfweather.cpp:162
weathermap_t::forestry
int8_t forestry
Range of forestedness.
Definition: cfweather.cpp:108
weather_tile
static const weather_grow_t weather_tile[]
The table below uses the same format as the one above.
Definition: cfweather.cpp:256
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
calculate_temperature
static void calculate_temperature(mapstruct *m)
Temperature is used in a lot of weather function.
Definition: cfweather.cpp:1429
bufferreader_data
char * bufferreader_data(BufferReader *br)
Get the whole buffer, independently of the calls to bufferreader_next_line().
Definition: bufferreader.cpp:150
load_overlay_map
int load_overlay_map(const char *filename, mapstruct *m)
Loads an overlay for a map, which has been loaded earlier, from file.
Definition: map.cpp:1277
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Return the index of the last line returned by bufferreader_next_line().
Definition: bufferreader.cpp:142
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
weathermap_t::sky
int8_t sky
Sky conditions.
Definition: cfweather.cpp:103
EQUATOR_BASE_TEMP
#define EQUATOR_BASE_TEMP
Definition: cfweather.cpp:34
init_rainfall
static void init_rainfall()
Initialize rainfall.
Definition: cfweather.cpp:2870
MOVE_FLYING
#define MOVE_FLYING
Combo of fly_low and fly_high.
Definition: define.h:386
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
PTICKS_PER_CLOCK
#define PTICKS_PER_CLOCK
Number of ticks per in-game hour.
Definition: tod.h:12
weather_avoids_t::next
weather_avoids_t * next
The next item in the avoid list.
Definition: cfweather.cpp:136
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
weather_replace_t::special_snow
archetype * special_snow
The archetype name of the tile to place over specified tile.
Definition: cfweather.cpp:144
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.cpp:1759
blocked_link
int blocked_link(object *ob, mapstruct *m, int16_t sx, int16_t sy)
Returns true if the given coordinate is blocked except by the object passed is not blocking.
Definition: map.cpp:334
init_pressure
static void init_pressure()
Reset pressure map.
Definition: cfweather.cpp:3000
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:1768
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:4289
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:3294
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:3157
temperature_calc
static void temperature_calc(const int x, const int y, const timeofday_t *tod)
Calculate temperature of a spot.
Definition: cfweather.cpp:1029
SKY_LIGHT_RAIN
#define SKY_LIGHT_RAIN
Definition: cfweather.cpp:52
HOURS_PER_DAY
#define HOURS_PER_DAY
Definition: tod.h:15
object::x
int16_t x
Definition: object.h:335
player::transport
object * transport
Transport the player is in.
Definition: player.h:216
PRESSURE_SPIKES
#define PRESSURE_SPIKES
Definition: cfweather.cpp:44
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:131
SKY_CLEAR
#define SKY_CLEAR
Definition: cfweather.cpp:49
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
Settings::worldmaptilesy
uint32_t worldmaptilesy
Number of tiles high the worldmap is.
Definition: global.h:298
read_pressuremap
static int read_pressuremap(const Settings *settings)
Read the pressure information from disk.
Definition: cfweather.cpp:4353
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:295
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:218
MIN
#define MIN(x, y)
Definition: compat.h:21
weather_grow
static const weather_grow_t weather_grow[]
The table below is used to grow things on the map.
Definition: cfweather.cpp:225
GULF_STREAM_BASE_SPEED
#define GULF_STREAM_BASE_SPEED
Definition: cfweather.cpp:37
in
same as sound ncom command like but with extra the client want tick commands so it knows animation timing the client wants to be informed of pickup mode changes Mode will be sent when the player successfully logs in
Definition: protocol.txt:408
bufferreader_destroy
void bufferreader_destroy(BufferReader *br)
Destroy a BufferReader.
Definition: bufferreader.cpp:41
MAP_WORLDPARTY
#define MAP_WORLDPARTY(m)
Definition: map.h:88
weather_replace_t::tile
sstring tile
Tile archetype or object name.
Definition: cfweather.cpp:143
weathermap_t::windspeed
int8_t windspeed
Windspeed of this tile.
Definition: cfweather.cpp:101
SKY_LIGHT_SNOW
#define SKY_LIGHT_SNOW
Definition: cfweather.cpp:60
WEATHER_OVERLAY
#define WEATHER_OVERLAY
Weather insert flags.
Definition: cfweather.cpp:618
weather_grow_t::random
int random
Random apparition factor.
Definition: cfweather.cpp:156
read_gulfstreammap
static int read_gulfstreammap(const Settings *settings)
Read the gulf stream, or initialize it if no saved information.
Definition: cfweather.cpp:4138
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Copy object first frees everything allocated by the second object, and then copies the contents of th...
Definition: object.cpp:1177
SKY_LIGHTCLOUD
#define SKY_LIGHTCLOUD
Definition: cfweather.cpp:50
do_map_precipitation
static void do_map_precipitation(mapstruct *const m)
Do the precipitation for a given map.
Definition: cfweather.cpp:1560
get_tod
void get_tod(timeofday_t *tod)
Computes the ingame time of the day.
Definition: time.cpp:219
load_humidity_map_part
static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int *const tx, int *const ty)
Method to abstract some of the mess of the humidity map.
Definition: cfweather.cpp:2661
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:3838
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:2546
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:4234
weathermap
weathermap_t ** weathermap
Definition: cfweather.cpp:183
weather_avoids_t::name
sstring name
Tile archetype name.
Definition: cfweather.cpp:133
pticks
uint32_t pticks
Number of ticks since time reset.
Definition: time.cpp:47
buf
StringBuffer * buf
Definition: readable.cpp:1564
worldmap_to_weathermap
static int worldmap_to_weathermap(const int x, const int y, int *const wx, int *const wy, mapstruct *const m)
Convert coordinates from world map to weather tiles.
Definition: cfweather.cpp:458
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
weather_replace_t::arch_or_name
int arch_or_name
If set, tile matches the archetype name, else the object's name.
Definition: cfweather.cpp:146
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:408
MAX
#define MAX(x, y)
Definition: compat.h:24
let_it_snow
static void let_it_snow(mapstruct *const m)
Put or remove snow.
Definition: cfweather.cpp:1583
weathermap_t
This is an overlay structure of the whole world.
Definition: cfweather.cpp:94
command_handler
static command_registration command_handler
Definition: cfweather.cpp:4813
plant_a_garden
static void plant_a_garden(mapstruct *const m)
Process growth of various plants.
Definition: cfweather.cpp:1972
init_humid_elev
static void init_humid_elev(const Settings *settings)
Initialize humidity, water, forestry, and elevation.
Definition: cfweather.cpp:2734
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:297
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
weather_settings_t::worldmaptilesizey
int32_t worldmaptilesizey
Number of squares high in a wm tile.
Definition: cfweather.cpp:174
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:4415
read_watermap
static int read_watermap(const Settings *settings)
Load water information from localdir.
Definition: cfweather.cpp:3961
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:4819
weather_snowmelt
weather_replace_t * weather_snowmelt
Definition: cfweather.cpp:191
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:424
forest_list
DensityConfig * forest_list
Definition: cfweather.cpp:185
SKY_FOG
#define SKY_FOG
Definition: cfweather.cpp:57
WEATHER_NO_FLOOR
#define WEATHER_NO_FLOOR
Definition: cfweather.cpp:619
SKY_SNOW
#define SKY_SNOW
Definition: cfweather.cpp:61
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
bufferreader_init_from_file
BufferReader * bufferreader_init_from_file(BufferReader *br, const char *filepath, const char *failureMessage, LogLevel failureLevel)
Initialize or create a BufferReader from a file path.
Definition: bufferreader.cpp:67
directions
static const uint32_t directions[]
Colours used for wind directions.
Definition: cfweather.cpp:3504
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:3327
do_precipitation
static void do_precipitation(mapstruct *const m, const int x, const int y, const int temp, const int sky)
Handle adding precipitation to the map.
Definition: cfweather.cpp:1482
DensityConfig::next
DensityConfig * next
Definition: cfweather.cpp:126
command_unregister
void command_unregister(command_registration command)
Unregister a previously registered command.
Definition: commands.cpp:534
polar_distance
static int polar_distance(int x, int y, const int equator)
Calculates the distance to the nearest pole.
Definition: cfweather.cpp:384
freearr_y
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.cpp:305
WIND_FACTOR
#define WIND_FACTOR
This is a multiplier for the wind caused by pressure differences.
Definition: cfweather.cpp:72
avoid_weather
static object * avoid_weather(int *const av, const mapstruct *m, const int x, const int y, int *const gs, const int grow)
Check the current square to see if we should avoid this one for weather processing.
Definition: cfweather.cpp:534
RED
#define RED
Definition: cfweather.cpp:3493
read_elevmap
static int read_elevmap(const Settings *settings)
Load the elevation information.
Definition: cfweather.cpp:3902
DensityConfig::value_density
int value_density
Definition: cfweather.cpp:122
skies
static const uint32_t skies[]
Colours used for weather types.
Definition: cfweather.cpp:3518
do_weather_insert
static void do_weather_insert(mapstruct *const m, int x, int y, const archetype *at, const int8_t object_flags, uint16_t material, int insert_flags)
Do an object insert for weather effects.
Definition: cfweather.cpp:648
GREEN
#define GREEN
Definition: cfweather.cpp:3494
weathermap_t::temp
int16_t temp
Base temperature of this tile (F).
Definition: cfweather.cpp:98
gulf_stream_start
static int gulf_stream_start
Definition: cfweather.cpp:203
plot_gulfstream
static void plot_gulfstream()
Plot the gulfstream map over the wind map.
Definition: cfweather.cpp:1305
events_unregister_global_handler
void events_unregister_global_handler(int eventcode, event_registration id)
Remove a global event handler.
Definition: events.cpp:26
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
tick_weather
void tick_weather()
Do the weather calculations in order.
Definition: cfweather.cpp:1445
Settings::worldmapstarty
uint32_t worldmapstarty
Starting y tile for the worldmap.
Definition: global.h:296
add_string
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
growth_avoids
weather_avoids_t * growth_avoids
Definition: cfweather.cpp:188
speed
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters speed
Definition: stats.txt:23
SKY_RAIN
#define SKY_RAIN
Definition: cfweather.cpp:53
wmperformstartx
static int wmperformstartx
Current weather tile position.
Definition: cfweather.cpp:207
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
smooth_pressure
static void smooth_pressure()
This code simply smooths the pressure map.
Definition: cfweather.cpp:788
object::move_type
MoveType move_type
Type of movement this object uses.
Definition: object.h:436
write_windspeedmap
int write_windspeedmap(const Settings *settings)
Save the wind speed to localdir.
Definition: cfweather.cpp:3370
global_clock_handler
static event_registration global_clock_handler
Definition: cfweather.cpp:4810
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:3403
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:2853
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
code
Server as a resource for server administrators and developers the server recognizes the following distinct directories The *name *is what it s called in the server code
Definition: server-directories.txt:8
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:3775
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:3495
WEATHER_NO_SAVE
#define WEATHER_NO_SAVE
Definition: cfweather.cpp:620
weather_replace_t::doublestack_arch
archetype * doublestack_arch
If set, this other archetype will be added atop special_snow.
Definition: cfweather.cpp:145
get_config_tile
static int get_config_tile(const int x, const int y, const mapstruct *m, const DensityConfig *list)
Get config tile retrieves the desired tile's associated value from a given space.
Definition: cfweather.cpp:416
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
EVENT_TIME
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:40
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:254
Settings::confdir
const char * confdir
Configuration files.
Definition: global.h:252
weather_grow_t::herb
const char * herb
Arch name of item to grow.
Definition: cfweather.cpp:154
sproto.h
wind_blow_object
static uint8_t wind_blow_object(mapstruct *const m, const int x, const int y, const MoveType move_type, int32_t wt, const living *stats)
Calculate the direction to push an object from wind.
Definition: cfweather.cpp:2311
weathermap_to_worldmap_corner
static char * weathermap_to_worldmap_corner(const int wx, const int wy, int *const x, int *const y, const int dir, char *const buffer, const int bufsize)
Return the path of the map in specified direction.
Definition: cfweather.cpp:333
FLAG_IS_FLOOR
#define FLAG_IS_FLOOR
Can't see what's underneath this object.
Definition: define.h:289
real_temperature
static int real_temperature(int x, int y, const timeofday_t *tod)
Compute the real (adjusted) temperature of a given weathermap tile.
Definition: cfweather.cpp:896
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1696
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:4809
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:1159
DensityConfig::name
sstring name
Definition: cfweather.cpp:118
init_gulfstreammap
static void init_gulfstreammap()
Initialize the gulf stream.
Definition: cfweather.cpp:2901
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.cpp:2085
living
Various statistics of objects.
Definition: living.h:35
water_list
DensityConfig * water_list
Definition: cfweather.cpp:186
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:595
map.h
real_world_temperature
int real_world_temperature(int x, int y, mapstruct *m)
Compute the temperature for a specific square.
Definition: cfweather.cpp:969
weather_replace
weather_replace_t * weather_replace
Definition: cfweather.cpp:189
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
EVENT_MAPREADY
#define EVENT_MAPREADY
A map is ready, either first load or after reload.
Definition: events.h:62
d
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for d
Definition: INSTALL_WIN32.txt:13
object_new
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it.
Definition: object.cpp:1258
object_insert_in_map
object * object_insert_in_map(object *op, mapstruct *m, object *originator, int flag)
This function inserts the object in the two-way linked list which represents what is on a map.
Definition: object.cpp:2346
object::weight
int32_t weight
Attributes of the object.
Definition: object.h:375
process_rain
void process_rain()
Keep track of how much rain has fallen in a given weathermap square.
Definition: cfweather.cpp:1401
free_string
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.cpp:294
cfweather_close
void cfweather_close()
Definition: cfweather.cpp:4924
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:2241
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:3192
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:3122
llevInfo
@ llevInfo
Information.
Definition: logger.h:14
WEATHERMAPTILESX
#define WEATHERMAPTILESX
Definition: cfweather.cpp:75
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
global_object_handler
static event_registration global_object_handler
Definition: cfweather.cpp:4811
season_tempchange
static const int season_tempchange[HOURS_PER_DAY]
How to alter the temperature, based on the hour of the day.
Definition: cfweather.cpp:875
weather_grow_t::rfmax
float rfmax
Maximum rainfall for herb to grow (inches/day).
Definition: cfweather.cpp:158
SKY_HEAVY_RAIN
#define SKY_HEAVY_RAIN
Definition: cfweather.cpp:54
SKY_HEAVY_SNOW
#define SKY_HEAVY_SNOW
Definition: cfweather.cpp:62
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
weathermap_t::winddir
int8_t winddir
Direction of wind.
Definition: cfweather.cpp:102
event_registration
unsigned long event_registration
Registration identifier type.
Definition: events.h:84
INS_ABOVE_FLOOR_ONLY
#define INS_ABOVE_FLOOR_ONLY
Put object immediatly above the floor.
Definition: object.h:581
compute_sky
void compute_sky()
Let the madness, begin.
Definition: cfweather.cpp:1102
M_LIQUID
#define M_LIQUID
Liquid.
Definition: material.h:23
FREE_AND_CLEAR
#define FREE_AND_CLEAR(xyz)
Free the pointer and then set it to NULL.
Definition: global.h:199
weather_grow_t::elevmax
int elevmax
Maximum elevation for herb to grow.
Definition: cfweather.cpp:164
weather_settings_t::worldmaptilesizex
int32_t worldmaptilesizex
Number of squares wide in a wm tile.
Definition: cfweather.cpp:173
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:299
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:280
weathermap_t::rainfall
uint32_t rainfall
Cumulative rainfall.
Definition: cfweather.cpp:105
check_replace_match
static int check_replace_match(const object *ob, const weather_replace_t *rep_struct)
Refactor the code to look for arch or object name as it's own code.
Definition: cfweather.cpp:602
weather_object_listener
static int weather_object_listener(int *type,...)
Global object-process handling for weather.
Definition: cfweather.cpp:4570
mapstruct
This is a game-map.
Definition: map.h:320
update_humid
static void update_humid()
Update the humidity for all weathermap tiles.
Definition: cfweather.cpp:1291
ServerSettings
Definition: server.h:15
FLAG_IS_WATER
#define FLAG_IS_WATER
Definition: define.h:351
sstring
const typedef char * sstring
Definition: sstring.h:2
EVENT_MAPENTER
#define EVENT_MAPENTER
A player entered a map.
Definition: events.h:59
gulf_stream_direction
static int gulf_stream_direction
Definition: cfweather.cpp:204
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:270
weather_avoids_t
Defines a tile the weather system should avoid.
Definition: cfweather.cpp:132
get_next_field
static char * get_next_field(char *line)
Finds the start of the next field in the provided string Skips past interceding commas and spaces.
Definition: cfweather.cpp:681
init_weather_settings
static void init_weather_settings(Settings *settings)
Definition: cfweather.cpp:3037
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:3465
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:4019
MAP_WORLDPARTX
#define MAP_WORLDPARTX(m)
Definition: map.h:87
INS_NO_MERGE
#define INS_NO_MERGE
Don't try to merge with other items.
Definition: object.h:580
stats
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various stats
Definition: stats.txt:2
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
smooth_wind
static void smooth_wind()
It doesn't really smooth it as such.
Definition: cfweather.cpp:713
strcasecmp
int strcasecmp(const char *s1, const char *s2)
write_pressuremap
int write_pressuremap(const Settings *settings)
Save pressure information to localdir.
Definition: cfweather.cpp:3436
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:4081
weather_settings_t
Weather settings definition structure Stolen from the settings file, as they are unused by everything...
Definition: cfweather.cpp:172
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.cpp:316
object::elevation
int elevation
Definition: object.h:450
write_temperaturemap
int write_temperaturemap(const Settings *settings)
Save temperature information to localdir.
Definition: cfweather.cpp:3261
command_weather
static void command_weather(object *op, const char *)
Player is wondering about the weather.
Definition: cfweather.cpp:4634
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:4473
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:3227
weather_effect
static void weather_effect(mapstruct *const m)
Perform actual effect of weather.
Definition: cfweather.cpp:2199
wset
static weather_settings_t wset
Definition: cfweather.cpp:211
save_map
int save_map(mapstruct *m, int flag)
Saves a map to file.
Definition: map.cpp:1470
change_the_world
static void change_the_world(mapstruct *const m)
Process worldmap regrowth.
Definition: cfweather.cpp:2068
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:2379
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:4512
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:1205
weather_evaporate
weather_replace_t * weather_evaporate
Definition: cfweather.cpp:190
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
dirdiff
int dirdiff(int dir1, int dir2)
Computes a direction difference.
Definition: object.cpp:3717
BufferReader
Definition: bufferreader.cpp:22
object::material
uint16_t material
What materials this object consist of.
Definition: object.h:357
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:654
humid_tile
static int humid_tile(const int x, const int y, const int dark)
Calculate the humidity of the given weather tile.
Definition: cfweather.cpp:1203
object.h
weather_replace_t::next
weather_replace_t * next
The next item in the replace list.
Definition: cfweather.cpp:147
PRESSURE_MIN
#define PRESSURE_MIN
Definition: cfweather.cpp:46
write_weather_images
int write_weather_images()
Dump all the weather data as an image.
Definition: cfweather.cpp:3552
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:15
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
do_water_elev_calc
static int do_water_elev_calc(mapstruct *const m, const int x, const int y, int *const water, int64_t *const elev, int *const trees)
Do the water and elevation updates on the given map tile.
Definition: cfweather.cpp:2704
perform_pressure
static void perform_pressure()
Perform small randomizations in the pressure map.
Definition: cfweather.cpp:828
OutputFile
Definition: output_file.h:41
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
Definition: bufferreader.cpp:104
players_on_map
int players_on_map(mapstruct *m, int show_all)
Returns the count of players on a map, calculated from player list.
Definition: main.cpp:383
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:2464
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254