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 #include "modules.h"
27 
28 #include <string.h>
29 #include <assert.h>
30 #include <math.h>
31 
32 /* weather constants */
33 
34 #define POLAR_BASE_TEMP 0 /* C */
35 #define EQUATOR_BASE_TEMP 30 /* C */
36 #define SEASONAL_ADJUST 10 /* polar distance */
37 #define GULF_STREAM_WIDTH 3 /* width of gulf stream */
38 #define GULF_STREAM_BASE_SPEED 40 /* base speed of gulf stream */
39 
40 /* don't muck with these unless you are sure you know what they do */
41 #define PRESSURE_ITERATIONS 30
42 #define PRESSURE_AREA 180
43 #define PRESSURE_ROUNDING_FACTOR 2
44 #define PRESSURE_ROUNDING_ITER 1
45 #define PRESSURE_SPIKES 3
46 #define PRESSURE_MAX 1040
47 #define PRESSURE_MIN 960
48 
49 /* sky conditions */
50 #define SKY_CLEAR 0
51 #define SKY_LIGHTCLOUD 1
52 #define SKY_OVERCAST 2
53 #define SKY_LIGHT_RAIN 3
54 #define SKY_RAIN 4 /* rain -> storm has lightning */
55 #define SKY_HEAVY_RAIN 5
56 #define SKY_HURRICANE 6
57 /* wierd weather 7-12 */
58 #define SKY_FOG 7
59 #define SKY_HAIL 8
60 /* snow */
61 #define SKY_LIGHT_SNOW 13 /* add 10 to rain to get snow */
62 #define SKY_SNOW 14
63 #define SKY_HEAVY_SNOW 15
64 #define SKY_BLIZZARD 16
65 
73 #define WIND_FACTOR 4.0
74 
75 /* editing the below might require actual changes to code */
76 #define WEATHERMAPTILESX 100
77 #define WEATHERMAPTILESY 100
78 
79 static int worldmap_to_weathermap(const int x, const int y, int * const wx, int * const wy, mapstruct * const m);
80 
81 /********************************************************************************************
82  * Section -- weather structures
83  * Structures to handle various aspects of the weather system.
84  ********************************************************************************************/
85 
95 struct weathermap_t {
96  int16_t temp;
97  int16_t pressure;
98  int8_t humid;
99  int8_t windspeed;
100  int8_t winddir;
101  int8_t sky;
102  int32_t avgelev;
103  uint32_t rainfall;
104  uint8_t darkness;
105  int8_t water;
106  int8_t forestry;
107  /*Dynamic parts*/
108  int16_t realtemp;
109 };
110 
118  // Use shared strings so we can do pointer comparisons.
120  // 0 if name is the arch name, 1 if it is the object name.
121  int is_obj;
122  // The density the tile type counts for.
124  // Pointer to the next item in the list
125  // We're scanning all of these when we check anyway,
126  // so might as well use a structure that works fine that way.
128 };
129 
135  int snow;
138 };
139 
149 };
150 
155  const char *herb;
156  const char *tile;
157  int random;
158  float rfmin;
159  float rfmax;
160  int humin;
161  int humax;
162  int tempmin;
163  int tempmax;
164  int elevmin;
165  int elevmax;
166  int season;
167 };
168 
176  uint16_t dynamiclevel;
177 };
178 
179 /********************************************************************************************
180  * Section END -- weather structures
181  ********************************************************************************************/
182 
183 extern unsigned long todtick;
185 
193 
204 static int gulf_stream_start;
206 
208 static int wmperformstartx;
210 static int wmperformstarty;
211 
213  .worldmaptilesizex = 50,
214  .worldmaptilesizey = 50,
215  .dynamiclevel = 1,
216 };
217 
218 /*
219  * @todo
220  * The following static tables should probably be defined by files.
221  */
226 static const weather_grow_t weather_grow[] = {
227  /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax, season */
228  {"mint", "grass", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
229  {"rose_red", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
230  {"rose_red", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
231  //{"rose_yellow", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
232  //{"rose_yellow", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
233  //{"rose_pink", "grass", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
234  //{"rose_pink", "hills", 15, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
235  {"mint", "brush", 8, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 2},
236  {"blackroot", "swamp", 15, 1.6, 2.0, 60, 100, 20, 30, -100, 1500, 0},
237  {"mushroom_1", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
238  {"mushroom_2", "grass", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
239  {"mushroom_1", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
240  {"mushroom_2", "swamp", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
241  {"mushroom_1", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
242  {"mushroom_2", "hills", 15, 1.6, 2.0, 60, 100, 3, 30, -100, 1500, 0},
243  {"pipeweed", "farmland", 20, 1.0, 2.0, 30, 100, 10, 25, 100, 5000, 0},
244  {"cabbage", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, -100, 9999, 0},
245  {"onion", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
246  {"carrot", "farmland", 10, 1.0, 2.0, 30, 100, 10, 25, 100, 9999, 0},
247  {"thorns", "brush", 15, 0.5, 1.3, 30, 100, 10, 25, -100, 9999, 0},
248  {"mountain_foilage", "mountain", 6, 1.0, 2.0, 25, 100, 5, 30, 0, 15999, 2},
249  {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
250 };
251 
257 static const weather_grow_t weather_tile[] = {
258  /* herb, tile, random, rfmin, rfmax, humin, humax, tempmin, tempmax, elevmin, elevmax */
259  {"dunes", NULL, 2, 0.0, 0.03, 0, 20, 10, 99, 0, 4000, 0},
260  {"desert", NULL, 1, 0.0, 0.05, 0, 20, 10, 99, 0, 4000, 0},
261  {"pstone_2", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
262  {"pstone_3", NULL, 1, 0.0, 0.05, 0, 20, -30, 10, 0, 4000, 0},
263  {"grassbrown", NULL, 1, 0.05, 1.0, 20, 80, -20, -3, 0, 5000, 0},
264  {"grass_br_gr", NULL, 1, 0.05, 1.0, 20, 80, -3, 5, 0, 5000, 0},
265  {"grass", NULL, 1, 0.05, 1.0, 20, 80, 5, 15, 0, 5000, 0},
266  {"grassmedium", NULL, 1, 0.05, 1.0, 20, 80, 15, 25, 0, 5000, 0},
267  {"grassdark", NULL, 1, 0.05, 1.0, 20, 80, 25, 35, 0, 5000, 0},
268  {"brush", NULL, 1, 0.2, 1.0, 25, 70, 0, 30, 500, 6000, 0},
269  /* small */
270  {"evergreens2", "brush", 1, 0.5, 1.8, 30, 90, -30, 24, 3000, 8000, 0},
271  {"fernsdense", "brush", 1, 0.9, 2.5, 50, 100, 10, 35, 1000, 6000, 0},
272  {"fernssparse", "brush", 1, 0.7, 2.0, 30, 90, -15, 35, 0, 4000, 0},
273  {"woods4", "brush", 1, 0.1, 0.8, 30, 60, -5, 25, 1000, 4500, 0},
274  {"woods5", "brush", 1, 0.6, 1.5, 20, 70, -15, 20, 2000, 5500, 0},
275  {"forestsparse", "brush", 1, 0.3, 1.5, 15, 60, -20, 25, 0, 4500, 0},
276  /* big */
277  /*
278  {"ytree_2", "brush", 2, 0.1, 0.6, 30, 60, 10, 25, 1000, 3500, 0},
279  {"tree3", "grass", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
280  {"tree5", "grass", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
281  {"tree3", "grassmeduim", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
282  {"tree5", "grassmedium", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},
283  {"tree3", "grassdark", 2, 0.9, 2.5, 50, 100, 10, 35, 1000, 4000, 0},
284  {"tree5", "grassdark", 2, 0.5, 1.5, 40, 90, -10, 24, 3000, 8000, 0},*/
285  /* mountians */
286  {"steppe", NULL, 1, 0.5, 1.3, 0, 30, -20, 35, 1000, 6000, 0},
287  {"steppelight", NULL, 1, 0.0, 0.6, 0, 20, -50, 35, 0, 5000, 0},
288  {"hills", NULL, 1, 0.1, 0.9, 20, 80, -10, 30, 5000, 8500, 0},
289  {"hills_rocky", NULL, 1, 0.0, 0.9, 0, 100, -50, 50, 5000, 8500, 0},
290  {"swamp", NULL, 1, 1.0, 9.9, 55, 80, 10, 50, 0, 1000, 0},
291  {"deep_swamp", NULL, 1, 1.0, 9.9, 80, 100, 10, 50, 0, 1000, 0},
292  {"mountain", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 8000, 10000, 0},
293  {"mountain2", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 9500, 11000, 0},
294  {"mountain4", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 10500, 12000, 0},
295  {"mountain5", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 11500, 13500, 0},
296  {"wasteland", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 13000, 99999, 0},
297  /* catchalls */
298  {"palms", "pstone_1", 1, 0.01, 0.1, 0, 30, 5, 99, 0, 4000, 0},
299  {"large_stones", NULL, 1, 0.0, 9.9, 0, 100, -50, 50, 6000, 8000, 0},
300  {"earth", NULL, 1, 0.0, 1.0, 0, 70, -30, 15, 0, 6000, 0},
301  {"medium_stones", NULL, 1, 1.0, 3.0, 70, 100, -30, 10, 0, 4000, 0}, /*unsure*/
302  {"earth", NULL, 1, 0.1, 0.9, 20, 80, -30, 30, 0, 4999, 0}, /* tundra */
303  {"swamp", NULL, 1, 1.0, 9.9, 50, 100, -30, 10, 0, 4000, 0},/* cold marsh */
304  {"earth", NULL, 1, 0.0, 99.9, 0, 100, -99, 99, 0, 99999, 0}, /* debug */
305  {NULL, NULL, 1, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0}
306 };
307 
308 
309 /********************************************************************************************
310  * Section -- weather data helpers
311  * These functions do important things like convert weathermap location to worldmap location.
312  * They are used by multiple sections, and as a result are general helpers.
313  ********************************************************************************************/
314 
334 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) {
337  int tx, ty, nx, ny;
338 
339  // Load the position on the map the corner of the weathermap takes.
340  // Since each map is larger than each weathermap, there can be no more than four
341  // map existing in a weathermap.
342  switch (dir) {
343  case 2:
344  tx = (wx+1)*spwtx-1;
345  ty = wy*spwty;
346  break;
347  case 4:
348  tx = (wx+1)*spwtx-1;
349  ty = (wy+1)*spwty-1;
350  break;
351  case 6:
352  tx = wx*spwtx;
353  ty = (wy+1)*spwty-1;
354  break;
355  case 8:
356  tx = wx*spwtx;
357  ty = wy*spwty;
358  break;
359  // If an incorrect direction is given, bail.
360  default:
361  LOG(llevError, "weathermap_to_worldmap_corner: Invalid direction %d given, should be in set {2,4,6,8}.\n", dir);
362  return NULL;
363  }
364 
367  snprintf(buffer, bufsize, "world/world_%d_%d", nx, ny);
368 
369  *x = tx % wset.worldmaptilesizex;
370  *y = ty % wset.worldmaptilesizey;
371  return buffer;
372 }
373 
385 static int polar_distance(int x, int y, const int equator) {
386  if ((x+y) > equator) { /* south pole */
387  x = WEATHERMAPTILESX - x;
388  y = WEATHERMAPTILESY - y;
389  return ((x+y)/2);
390  } else if ((x+y) < equator) { /* north pole */
391  return ((x+y)/2);
392  } else {
393  return equator/2;
394  }
395 }
396 
417 static int get_config_tile(const int x, const int y, const mapstruct *m, const DensityConfig *list) {
418  // If no list specified, shortcut the exit.
419  if (list == NULL)
420  return 0;
421  object *ob = GET_MAP_OB(m, x, y);
422  const DensityConfig *tmp;
423  // Our trees are not always the floor. Look higher if need be.
424  // Even for types that are floor, check anyway. This ensures
425  // that no-magic tiles hiding underneath floor don't cause problems.
426  while (ob) {
427  // Look at our config data for the associated amounts.
428  tmp = list;
429  while (tmp) {
430  // Does object name match?
431  if ((tmp->is_obj && tmp->name == ob->name) ||
432  // Does arch name match?
433  (!tmp->is_obj && tmp->name == ob->arch->name)) {
434  return tmp->value_density;
435  }
436 
437  tmp = tmp->next;
438  }
439  ob = ob->above;
440  }
441  // If we get here, there were no matches.
442  return 0;
443 }
444 
459 static int worldmap_to_weathermap(const int x, const int y, int * const wx, int * const wy, mapstruct * const m) {
462  int fx, fy;
463  int nx, ny;
464  const char *filename = m->path;
465 
466  while (*filename == '/') {
467  filename++;
468  }
469 
470  fx = MAP_WORLDPARTX(m);
471  fy = MAP_WORLDPARTY(m);
472 
473  // -2 is our sentinel value to say that we tried to load and could not.
474  // If either is -2, then this is not a world map.
475  if (fx == -2 || fy == -2) {
476  return -1;
477  }
479  fx < int(settings.worldmapstartx) ||
481  fy < int(settings.worldmapstarty)) {
482  LOG(llevDebug, "worldmap_to_weathermap(%s)\n", filename);
483  // If we don't populate the variables, mark as -2.
484  // This tells us to not check again, as it is not a world map.
485  int amt = sscanf(filename, "world/world_%d_%d", &fx, &fy);
486  if (amt == 2) {
487  MAP_WORLDPARTX(m) = fx;
488  MAP_WORLDPARTY(m) = fy;
489  }
490  else {
491  MAP_WORLDPARTX(m) = -2;
492  MAP_WORLDPARTY(m) = -2;
493  }
494 
495  }
497  fx < int(settings.worldmapstartx)) {
498  return -1;
499  }
501  fy < int(settings.worldmapstarty)) {
502  return -1;
503  }
504  fx -= settings.worldmapstartx;
505  fy -= settings.worldmapstarty;
506 
507  nx = fx * wset.worldmaptilesizex+x;
508  ny = fy * wset.worldmaptilesizey+y;
509 
510  *wx = nx/spwtx;
511  *wy = ny/spwty;
512 
513  return 0;
514 }
515 
535 static object *avoid_weather(int * const av, const mapstruct *m, const int x, const int y, int * const gs, const int grow) {
536  int avoid, gotsnow;
537  object *tmp, *snow = NULL;
538  const weather_avoids_t *cur;
539 
540  avoid = 0;
541  gotsnow = 0;
542  if (grow) {
543  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
544  /* look for things like walls, holes, etc */
545  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !(tmp->material&M_ICE || tmp->material&M_LIQUID)) {
546  gotsnow++;
547  snow = tmp;
548  }
549  for (cur = growth_avoids; cur; cur = cur->next) {
550  // Due to the use of shared strings, we can do pointer comparison here.
551  if (tmp->arch->name == cur->name) {
552  avoid++;
553  break;
554  }
555  }
556  if (avoid && gotsnow) {
557  break;
558  }
559  }
560  } else {
561  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
562  for (cur = weather_avoids; cur; cur = cur->next) {
563  if (tmp->arch->name == cur->name) {
564  // We clear FLAG_IS_FLOOR for our snow. The map's default snow does not.
565  // Avoid weirdness on the pathway to Brest and at the south pole by checking for non-floor snow
566  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR) && cur->snow == 1) {
567  gotsnow++;
568  snow = tmp;
569  } else {
570  avoid++;
571  }
572  break;
573  }
574  }
575  if (avoid && gotsnow) {
576  break;
577  }
578  }
579  }
580  *gs = gotsnow;
581  *av = avoid;
582 
583  return snow;
584 }
585 
603 static int check_replace_match(const object *ob, const weather_replace_t *rep_struct) {
604  if (rep_struct->arch_or_name == 1) {
605  if (ob->arch->name == rep_struct->tile) {
606  return 1;
607  }
608  } else {
609  if (ob->name == rep_struct->tile) {
610  return 1;
611  }
612  }
613  return 0;
614 }
615 
619 #define WEATHER_OVERLAY 1 /* If set, we set FLAG_OVERLAY_FLOOR */
620 #define WEATHER_NO_FLOOR 2 /* If set, we clear FLAG_IS_FLOOR */
621 #define WEATHER_NO_SAVE 4 /* If set, we set FLAG_NO_SAVE */
622 
649 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) {
650  if (at != NULL) {
651  object *ob = object_new();
652  object_copy(&at->clone, ob);
653  ob->x = x;
654  ob->y = y;
655  if (object_flags & WEATHER_OVERLAY)
657  if (object_flags & WEATHER_NO_FLOOR)
659  if (object_flags & WEATHER_NO_SAVE)
660  SET_FLAG(ob, FLAG_NO_SAVE);
661  if (material)
662  ob->material = material;
663  object_insert_in_map(ob, m, ob, insert_flags);
664  }
665 }
666 
682 static char *get_next_field(char *line) {
683  // The comma is the end of the field
684  line = strchr(line, ',');
685  if (line == NULL)
686  return NULL;
687  // Move past the known field seperator, but null terminate over it for the previous field.
688  *(line++) = '\0';
689  // While spaces or commas, skip the character
690  while (*line == ' ' || *line == ',')
691  ++line;
692  // Next field begins here.
693  return line;
694 }
695 
696 /********************************************************************************************
697  * Section END -- weather data helpers
698  ********************************************************************************************/
699 
700 /********************************************************************************************
701  * Section -- weather data calculators
702  * These functions determine the progression of weather data over time.
703  * This is the bread-and-butter of the weather system.
704  ********************************************************************************************/
705 
706 // We need to declare init_temperature, since it is defined below this area.
707 static void init_temperature();
708 
714 static void smooth_wind() {
715  int x, y;
716  int tx, ty, dx, dy;
717  int minp;
718 
719  /* skip the outer squares.. it makes handling alot easier */
720  dx = 0;
721  dy = 0;
722  for (x = 1; x < WEATHERMAPTILESX-1; x++)
723  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
724  minp = PRESSURE_MAX + 1;
725  for (tx = -1; tx < 2; tx++) {
726  for (ty = -1; ty < 2; ty++) {
727  if (!(tx == 0 && ty == 0)) {
728  if (weathermap[x+tx][y+ty].pressure < minp) {
729  minp = weathermap[x+tx][y+ty].pressure;
730  dx = tx;
731  dy = ty;
732  }
733  }
734  }
735  }
736 
737  /* if the wind is strong, the pressure won't decay it completely */
738  if (weathermap[x][y].windspeed > 5 && !similar_direction(weathermap[x][y].winddir, find_dir_2(dx, dy))) {
739  weathermap[x][y].windspeed -= 2*2;
740  } else {
741  weathermap[x][y].winddir = find_dir_2(dx, dy);
743  }
744  // Disrupt the wind where trees are present (a reduction of up to 5 is possible).
745  weathermap[x][y].windspeed -= weathermap[x][y].forestry/20;
746  if (weathermap[x][y].windspeed < 0) {
747  weathermap[x][y].windspeed = 0;
748  }
749  // The wind moves some of the higher pressure to the lower pressure.
750  weathermap[x][y].pressure -= (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
751  weathermap[x+dx][y+dy].pressure += (int)(weathermap[x][y].windspeed/(WIND_FACTOR*3));
752  }
753 
754  /* now, lets crank on the speed. When surrounding tiles all have
755  the same speed, inc ours. If it's chaos. drop it.
756  */
757  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
758  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
759  minp = 0;
760  for (tx = -1; tx < 2; tx++) {
761  for (ty = -1; ty < 2; ty++) {
762  if (tx != 0 && ty != 0) {
763  if (similar_direction(weathermap[x][y].winddir, weathermap[x+tx][y+ty].winddir)) {
764  minp++;
765  }
766  }
767  }
768  }
769  if (minp > 4) {
770  weathermap[x][y].windspeed++;
771  }
772  if (minp > 6) {
773  weathermap[x][y].windspeed += 2;
774  }
775  if (minp < 2) {
776  weathermap[x][y].windspeed--;
777  }
778  if (weathermap[x][y].windspeed < 0) {
779  weathermap[x][y].windspeed = 0;
780  }
781  }
782  }
783 }
784 
789 static void smooth_pressure() {
790  int x, y;
791  int k;
792 
793  for (k = 0; k < PRESSURE_ROUNDING_ITER; k++) {
794  for (x = 1; x < WEATHERMAPTILESX-1; x++) {
795  for (y = 1; y < WEATHERMAPTILESY-1; y++) {
796  weathermap[x][y].pressure = (weathermap[x][y].pressure*
798  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
799  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
800  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
802  }
803  }
804  for (x = WEATHERMAPTILESX-2; x > 0; x--) {
805  for (y = WEATHERMAPTILESY-2; y > 0; y--) {
806  weathermap[x][y].pressure = (weathermap[x][y].pressure*
808  weathermap[x][y-1].pressure+weathermap[x-1][y-1].pressure+
809  weathermap[x+1][y].pressure+weathermap[x][y+1].pressure+
810  weathermap[x+1][y+1].pressure+weathermap[x+1][y-1].pressure+
812  }
813  }
814  }
815 
816  // Clip to our valid pressure range
817  for (x = 0; x < WEATHERMAPTILESX; x++)
818  for (y = 0; y < WEATHERMAPTILESY; y++) {
819  weathermap[x][y].pressure = MIN(weathermap[x][y].pressure, PRESSURE_MAX);
820  weathermap[x][y].pressure = MAX(weathermap[x][y].pressure, PRESSURE_MIN);
821  }
822 
823 }
824 
829 static void perform_pressure() {
830  int x, y, l, n, j, k, is_storm;
831 
832  /* create random spikes in the pressure */
833  for (l = 0; l < PRESSURE_SPIKES; l++) {
834  x = rndm(0, WEATHERMAPTILESX-1);
835  y = rndm(0, WEATHERMAPTILESY-1);
836  // This goes beyond the valid bounds so that the smoothing proces ends up
837  // making different-sized pressure spikes.
838  n = rndm(600, 1300);
839  weathermap[x][y].pressure = n;
840  // Get close to the edge. But, to make things cleaner, don't go off the edge.
841  if (x >= 2 && y >= 2 && x < WEATHERMAPTILESX-2 && y < WEATHERMAPTILESY-2) {
842  /* occasionally add a storm
843  * and make sure the whole pressure spot is a storm, not just pieces of it
844  *
845  * Also, only try to make storms out of low pressure spikes. 1013 mbar
846  * Is standard pressure at sea level.
847  */
848  is_storm = (n < 1013 && rndm(1, 10) == 1);
849  for (j = x-2; j < x+2; j++) {
850  for (k = y-2; k < y+2; k++) {
851  weathermap[j][k].pressure = n;
852  if (is_storm) {
853  weathermap[j][k].humid = rndm(50, 90);
854  }
855  }
856  }
857  }
858  }
859 
860  for (x = 0; x < WEATHERMAPTILESX; x++) {
861  for (y = 0; y < WEATHERMAPTILESY; y++) {
862  weathermap[x][y].pressure += rndm(-1, 4);
863  }
864  }
865 
866  smooth_pressure();
867 }
868 
876 static const int season_tempchange[HOURS_PER_DAY] = {
877 /* 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 */
878  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};
879 
897 static int real_temperature(int x, int y, const timeofday_t *tod) {
898  int i, temp, adj;
899 
900  // Clear and partly-cloudy skies have a stronger temperature effect
901  // than overcast skies, since clouds create a barrier to heat escaping
902  // and sunlight entering. Super thick clouds add additional buffer.
903  // If adj is set to one, then the weather provides some amount of buffer effect.
904  // This buffer will override the forestry one if it is set.
905  switch (weathermap[x][y].sky) {
906  case SKY_CLEAR:
907  case SKY_LIGHTCLOUD:
908  adj = 0;
909  break;
910  case SKY_HURRICANE:
911  case SKY_BLIZZARD:
912  adj = 2;
913  break;
914  default:
915  adj = 1;
916  break;
917  }
918 
919  /* adjust for time of day */
920  temp = weathermap[x][y].temp;
921  for (i = HOURS_PER_DAY/2; i < HOURS_PER_DAY; i++) {
922  temp += season_tempchange[i];
923  /* high amounts of water has a buffering effect on the temp */
924  if (weathermap[x][y].water > 33) {
925  i += weathermap[x][y].water/33;
926  }
927  // Cloudy skies will have a buffering effect on the temperature
928  if (adj >= 1)
929  i += adj;
930  // High amounts of trees also provide some amount of buffering under clear skies
931  else if (weathermap[x][y].forestry > 60) {
932  i++;
933  }
934  }
935  for (i = 0; i <= tod->hour; i++) {
936  temp += season_tempchange[i];
937  if (weathermap[x][y].water > 33) {
938  i += weathermap[x][y].water/33;
939  }
940  // Cloudy skies will have a buffering effect on the temperature
941  if (adj >= 1)
942  i += adj;
943  // High amounts of trees also provide some amount of buffering under clear skies
944  else if (weathermap[x][y].forestry > 60) {
945  i++;
946  }
947  }
948 
949  /* windchill */
950  for (i = 1; i < weathermap[x][y].windspeed; i += i) {
951  temp--;
952  }
953 
954  return temp;
955 }
956 
970 int real_world_temperature(int x, int y, mapstruct *m) {
971  int wx, wy, temp, eleva, elevb, trees;
972  object *op;
973  timeofday_t tod;
974 
975  // Get the time of day for real_temperature
976  // Since real_temperature is sometimes called in a loop, it expects
977  // the time of day to be provided to it instead of calculating it directly.
978  get_tod(&tod);
979 
980  /*LOG(llevDebug, "real_world_temperature: worldmaptoweathermap : %s\n",m->path);*/
981  worldmap_to_weathermap(x, y, &wx, &wy, m);
982  temp = real_temperature(wx, wy, &tod);
983  if (weathermap[wx][wy].avgelev < 0) {
984  eleva = 0;
985  } else {
986  eleva = weathermap[x][y].avgelev;
987  }
988 
989  op = GET_MAP_OB(m, x, y);
990  if (!op) {
991  return eleva;
992  }
993 
994  elevb = op->elevation;
995  if (elevb < 0) {
996  elevb = 0;
997  }
998  if (elevb > eleva) {
999  elevb -= eleva;
1000  temp -= elevb/1000;
1001  } else {
1002  elevb = eleva - elevb;
1003  temp += elevb/1000;
1004  }
1005 
1006  // Get localized effects from trees, too.
1007  trees = get_config_tile(x, y, m, forest_list);
1008  // Sparse trees reduce local temp by 1.
1009  // Dense trees raise it by one.
1010  if (trees > 0) {
1011  if (trees < 4)
1012  --temp;
1013  else
1014  ++temp;
1015  }
1016  // And done!
1017  return temp;
1018 }
1019 
1030 static void temperature_calc(const int x, const int y, const timeofday_t *tod) {
1031  int dist, equator, elev, n, trees;
1032  float diff, tdiff;
1033 
1034  // Warmer air has higher pressure than colder air.
1035  // Store the old value for temperature.
1036  int oldtemp = weathermap[x][y].temp, tempdiff;
1037 
1038  equator = (WEATHERMAPTILESX+WEATHERMAPTILESY)/4;
1039  diff = (float)(EQUATOR_BASE_TEMP-POLAR_BASE_TEMP)/(float)equator;
1040  tdiff = (float)SEASONAL_ADJUST/(float)(MONTHS_PER_YEAR/2.0);
1041  equator *= 2;
1042  n = 0;
1043  /* we essentially move the equator during the season */
1044  if (tod->month > (MONTHS_PER_YEAR/2)) { /* EOY */
1045  n -= (tod->month*tdiff);
1046  } else {
1047  n = (MONTHS_PER_YEAR - tod->month)*tdiff;
1048  }
1049  dist = polar_distance(x-n/2, y-n/2, equator);
1050 
1051  /* now we have the base temp, unadjusted for time. Time adjustment
1052  is not recorded on the map, rather, it's done JIT. */
1053  weathermap[x][y].temp = (int)(dist * diff);
1054  /* just scrap these for now, its mostly ocean */
1055  if (weathermap[x][y].avgelev < 0) {
1056  elev = 0;
1057  } else {
1058  // Make sure that higher elevations cause lower temps.
1059  elev = MIN(20000, weathermap[x][y].avgelev)/1000;
1060  }
1061  weathermap[x][y].temp -= elev;
1062 
1069  // Arbitrarily make the cutoff threshold for heat-hold as 60
1070  trees = weathermap[x][y].forestry;
1071  // Dense trees can raise the temperature up to ~3 degrees, per the calculations below.
1072  if (trees >= 60) {
1073  weathermap[x][y].temp += (trees-60)/15;
1074  }
1075  // If not, then we have heat reduction, most effective (~4 degrees) at 30.
1076  else if (trees >= 30){
1077  weathermap[x][y].temp -= (60-trees)/8;
1078  }
1079  else {
1080  weathermap[x][y].temp -= trees/8;
1081  }
1082 
1083  // Now we determine the difference in temperature and adjust the pressure accordingly.
1084  tempdiff = weathermap[x][y].temp - oldtemp;
1085  // The rate (arbitrarily chosen) for temperature-to-pressure change is 1 degrees per millibar
1086  // I'd have to keep track of partial millibar changes if I wanted to be coarser in this.
1087  if (tempdiff != 0)
1088  weathermap[x][y].pressure += tempdiff;
1089 }
1090 
1103 void compute_sky() {
1104  int x, y;
1105  int temp;
1106  int calc, inv_pressure;
1107  float press_root, max_root = sqrt(PRESSURE_MAX-PRESSURE_MIN);
1108  timeofday_t tod;
1109 
1110  // Before we begin the loops, we get the time of day for real_temperature()
1111  get_tod(&tod);
1112 
1113  for (x = 0; x < WEATHERMAPTILESX; x++) {
1114  for (y = 0; y < WEATHERMAPTILESY; y++) {
1115  temp = real_temperature(x, y, &tod);
1116  // Make sure we clip to the allowed pressure range.
1117  inv_pressure = MAX(0, MIN(PRESSURE_MAX-PRESSURE_MIN, (PRESSURE_MAX - weathermap[x][y].pressure)));
1118  // Take the square root. This allows us to have values weighted toward
1119  // producing rain. max_root holds the maximum value this could be.
1120  press_root = sqrt(inv_pressure);
1121  calc = MAX(0, MIN((int)(max_root*100), (int)(press_root * weathermap[x][y].humid)));
1122  // max_root*100 / 7 is the smallest we can feasibly divide by without side effects
1123  // So as long as we divide by a number greater than that, we're good.
1124  // If we divide by smaller, we overrun the sequential weather numbers, and reach FOG and HAIL
1125  // when not encountering their special cases.
1126  calc /= (int)(max_root*100 / 7) + 1;
1127 
1128  // If wind speed is high enough and we have rain, we can add one.
1129  if (calc >= SKY_LIGHT_RAIN && calc < SKY_HURRICANE && weathermap[x][y].windspeed > 30)
1130  ++calc;
1131  // If we are cold enough we have snow.
1132  if (temp <= 0 && calc >= SKY_LIGHT_RAIN)
1133  calc += 10;
1134 
1135  // Keep the old fog/hail generation for now
1136  if (weathermap[x][y].pressure >= 980 && weathermap[x][y].pressure < 1000) {
1137  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 95 &&
1138  weathermap[x][y].windspeed < 3) {
1139  calc = SKY_FOG; /* rare */
1140  }
1141  if (temp > 0 && temp < 5 && weathermap[x][y].humid > 70 &&
1142  weathermap[x][y].windspeed > 35) {
1143  calc = SKY_HAIL; /* rare */
1144  }
1145  }
1146  weathermap[x][y].sky = calc;
1147  }
1148  }
1149 }
1150 
1160 static void spin_globe() {
1161  int x, xy, xy_eff;
1162  int buffer_humid;
1163  int buffer_sky;
1164  int buffer_pressure;
1165 
1166  // On each pass, x + y is a constant. We shift down and to the left, and wraparound to the upper right.
1167  // The cornermost tiles by the poles to not move as a result, so we can skip them.
1168  for (xy = 1; xy < WEATHERMAPTILESX + WEATHERMAPTILESY - 1; ++xy) {
1169  // Effective xy is essentially clipped to the end.
1170  // xy-xy_eff is thus the bounds on the other side of the map to care about for wraparound.
1171  xy_eff = MIN(xy, WEATHERMAPTILESX-1);
1172  buffer_humid = weathermap[xy-xy_eff][xy_eff].humid;
1173  buffer_sky = weathermap[xy-xy_eff][xy_eff].sky;
1174  buffer_pressure = weathermap[xy-xy_eff][xy_eff].pressure;
1175  for (x = xy-xy_eff; x < xy_eff; ++x) {
1176  /* Using xy directly here *looks* wrong, but is actually not,
1177  * since x = xy-xy_eff+c, where c is one less than the loop count;
1178  * thus, xy-x = xy-xy+xy_eff-c = xy_eff-c.
1179  * This is within the bounds of the map at all times.
1180  */
1181  weathermap[x][xy-x].humid = weathermap[x+1][xy-x-1].humid;
1182  weathermap[x][xy-x].sky = weathermap[x+1][xy-x-1].sky;
1183  weathermap[x][xy-x].pressure = weathermap[x+1][xy-x-1].pressure;
1184  }
1185  weathermap[xy_eff][xy-xy_eff].humid = buffer_humid;
1186  weathermap[xy_eff][xy-xy_eff].sky = buffer_sky;
1187  weathermap[xy_eff][xy-xy_eff].pressure = buffer_pressure;
1188  }
1189 }
1190 
1204 static int humid_tile(const int x, const int y, const int dark) {
1205  // ox and oy denote the neighbor that is influencing us (due to winds from there)
1206  int ox = x, oy = y, humid, evap, tempeffect;
1207 
1208  /* find the square the wind is coming from, without going out of bounds */
1209 
1210  if (weathermap[x][y].winddir == 8 || weathermap[x][y].winddir <= 2) {
1211  if (y != 0) {
1212  oy = y-1;
1213  }
1214  }
1215  if (weathermap[x][y].winddir >= 6) {
1216  if (x != 0) {
1217  ox = x-1;
1218  }
1219  }
1220  if (weathermap[x][y].winddir >= 4 && weathermap[x][y].winddir <= 6) {
1221  if (y < WEATHERMAPTILESY-1) {
1222  oy = y+1;
1223  }
1224  }
1225  if (weathermap[x][y].winddir >= 2 && weathermap[x][y].winddir <= 4) {
1226  if (x < WEATHERMAPTILESX-1) {
1227  ox = x+1;
1228  }
1229  }
1230  // Determine the effect of sunlight on evaporation.
1231  int light = MAX_DARKNESS - dark;
1232  // The sky conditions affect how strong an effect the sunlight has.
1233  switch (weathermap[x][y].sky) {
1234  case SKY_CLEAR:
1235  tempeffect = light*light/4;
1236  break;
1237  case SKY_LIGHTCLOUD:
1238  tempeffect = light*light/5;
1239  break;
1240  case SKY_OVERCAST:
1241  tempeffect = light;
1242  break;
1243  case SKY_LIGHT_RAIN:
1244  case SKY_LIGHT_SNOW:
1245  tempeffect = light*4/5;
1246  break;
1247  case SKY_RAIN:
1248  case SKY_SNOW:
1249  tempeffect = light/2;
1250  break;
1251  case SKY_HEAVY_RAIN:
1252  case SKY_HEAVY_SNOW:
1253  tempeffect = light/3;
1254  break;
1255  case SKY_HURRICANE:
1256  case SKY_BLIZZARD:
1257  case SKY_HAIL:
1258  tempeffect = light/5;
1259  break;
1260  case SKY_FOG:
1261  default:
1262  tempeffect = 0;
1263  }
1264  // Determine the evaporative component contributing to the humidity.
1265  // The amount of water, the temperature, the wind, the pressure, the time of day, the cloudcover, and the previous humidity all affect the evaporation.
1266  // The exact formula is arbitrary, but it gives values that make some sense.
1267  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);
1268  // Don't go negative if temperature gets too low.
1269  evap = MAX(0, evap);
1270  // This is where the magic happens
1271  // If humidity is unstable over time, this is what will need to be tweaked
1272  // (or one of the values it depends on, if not this)
1273  humid = (weathermap[x][y].humid*2 +
1274  (weathermap[ox][oy].humid)*weathermap[ox][oy].windspeed/100 +
1275  // Evaporative components.
1276  evap + weathermap[x][y].forestry/10 + rndm(-3, 7))/
1277  (weathermap[ox][oy].windspeed/100+3)+rndm(-3, 3);
1278  if (humid < 0) {
1279  humid = 0;
1280  }
1281  if (humid > 100) {
1282  humid = 100;
1283  }
1284  return humid;
1285 }
1286 
1292 static void update_humid() {
1293  int x, y, dark = get_world_darkness();
1294 
1295  for (y = 0; y < WEATHERMAPTILESY; y++) {
1296  for (x = 0; x < WEATHERMAPTILESX; x++) {
1297  weathermap[x][y].humid = humid_tile(x, y, dark);
1298  }
1299  }
1300 }
1301 
1306 static void plot_gulfstream() {
1307  int x, y, tx, diroffset, dirdiff, ystart, ydiff, ylimup, ylimlow;
1308 
1309  x = gulf_stream_start;
1310 
1311  // Use the same offset/multiplier formula we used in gulf stream initialization
1312  // to make the code here much cleaner to look at.
1313  if (gulf_stream_direction) {
1314  diroffset = 0;
1315  dirdiff = -1;
1316  // We go from WEATHERMAPTILESY-1 down to 1 for the loop
1317  ystart = WEATHERMAPTILESY-1;
1318  ydiff = -1;
1319  ylimup = WEATHERMAPTILESY;
1320  ylimlow = 0;
1321  }
1322  else {
1323  diroffset = 10;
1324  dirdiff = 1;
1325  // We go from 0 to WEATHERMAPTILESY-2 for the loop
1326  ystart = 0;
1327  ydiff = 1;
1328  ylimup = WEATHERMAPTILESY-1;
1329  ylimlow = -1;
1330  }
1331  for (y = ystart; y > ylimlow && y < ylimup; y += ydiff) {
1332  for (tx = 0; tx < GULF_STREAM_WIDTH && x+tx < WEATHERMAPTILESX; tx++) {
1333  if (similar_direction(weathermap[x+tx][y].winddir, gulf_stream_dir[tx][y]) && weathermap[x+tx][y].windspeed < GULF_STREAM_BASE_SPEED-5) {
1334  weathermap[x+tx][y].windspeed += gulf_stream_speed[tx][y];
1335  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1336  } else if (gulf_stream_speed[tx][y] > weathermap[x+tx][y].windspeed) {
1337  // Preserve the wind speed of things with a higher speed that the gulf stream itself.
1338  weathermap[x+tx][y].windspeed = gulf_stream_speed[tx][y];
1339  weathermap[x+tx][y].winddir = gulf_stream_dir[tx][y];
1340  } // If a storm moves through the gulf stream, it supercedes it with its own high winds.
1341 
1342  if (tx == GULF_STREAM_WIDTH-1) {
1343  switch ((diroffset-gulf_stream_dir[tx][y])*dirdiff) {
1344  case 6: x--; break;
1345  case 7: break;
1346  case 8: x++; break;
1347  }
1348  if (x < 0) {
1349  x++;
1350  }
1352  x--;
1353  }
1354  }
1355  }
1356  }
1357  /* occasionally move the stream
1358  * Arbitrary code from the original implementation says
1359  * 1 in 500 to switch, then 1 in 2 the switch actually is relevant.
1360  *
1361  * So, if we make the outer effect 1 in 1000, we cover both.
1362  */
1363  if (rndm(1, 1000) == 1) {
1364  // Reverse the stream direction.
1366  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
1367  for (y = 0; y < WEATHERMAPTILESY-1; y++) {
1368  // The direction changes here are dir + 4 mod 8, but 8 instead of 0 on those ones.
1369  gulf_stream_dir[tx][y] = (gulf_stream_dir[tx][y] + 4) & 7;
1370  // And we want 8 as a direction instead of 0.
1371  if (gulf_stream_dir[tx][y] == 0)
1372  gulf_stream_dir[tx][y] = 8;
1373  }
1374  }
1375  }
1376  /* Occasionally move the gulf stream starting point.
1377  * Original code had 1 in 25 to try, but 1 in 3 that the move was 0.
1378  *
1379  * So, the chance of actual movement was 2 in 75.
1380  *
1381  * We will use that and redesign the inner offset generation to determine + or - movement.
1382  */
1383  if (rndm(1, 75) <= 2) {
1384  // Only get +1 or -1
1385  gulf_stream_start += 1-2*rndm(0, 1);
1386  // Make sure we don't go off the map.
1389  }
1390  if (gulf_stream_start < 1) {
1392  }
1393  }
1394 }
1395 
1403  int x, y, rain;
1404 
1405  for (x = 0; x < WEATHERMAPTILESX; x++) {
1406  for (y = 0; y < WEATHERMAPTILESY; y++) {
1407  rain = weathermap[x][y].sky;
1408  if (rain >= SKY_LIGHT_SNOW) {
1409  rain -= 10;
1410  }
1411  if (rain > SKY_OVERCAST && rain < SKY_FOG) {
1412  rain -= SKY_OVERCAST;
1413  weathermap[x][y].rainfall += rain;
1414  }
1415  }
1416  }
1417 }
1418 
1431  int x,y, wx, wy;
1432  assert(worldmap_to_weathermap(0, 0, &wx, &wy, m) == 0);
1433  for (x = 0; x < wset.worldmaptilesizex; x++) {
1434  for (y = 0; y < wset.worldmaptilesizey; y++) {
1435  worldmap_to_weathermap(x, y, &wx, &wy, m);
1436  weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1437  }
1438  }
1439 }
1440 
1447  assert(wset.dynamiclevel > 0);
1448  update_humid(); /* Run the humidity updates based on prior pressure, temperature, and wind */
1449  perform_pressure(); /* pressure is the random factor */
1450  smooth_wind(); /* calculate the wind. depends on pressure */
1451  plot_gulfstream();
1452  init_temperature();
1453  spin_globe();
1454  //compute_sky(); This is done in perform_weather
1455 }
1456 
1457 /********************************************************************************************
1458  * Section END -- weather data calculators
1459  ********************************************************************************************/
1460 
1461 /********************************************************************************************
1462  * Section -- Weather effect methods
1463  * Functions to provide weather effects.
1464  * This includes precipitation, puddles, ice on water, snowfall, growing plants,
1465  * defacing the world (since some comments seemed to imply that one was broken, and I never changed it)
1466  ********************************************************************************************/
1467 
1483 static void do_precipitation(mapstruct * const m, const int x, const int y, const int temp, const int sky) {
1484  // Do falling rain/snow here
1485  const archetype *at = NULL;
1486  object *tmp = NULL;
1487  int avoid = 0;
1488  int pct_precip = 0; // 0-100: percent tiles with precipitation
1489  switch (sky) {
1490  case SKY_LIGHT_RAIN:
1491  case SKY_LIGHT_SNOW:
1492  pct_precip = 10;
1493  break;
1494  case SKY_RAIN:
1495  case SKY_SNOW:
1496  pct_precip = 30;
1497  break;
1498  case SKY_HEAVY_RAIN:
1499  case SKY_HEAVY_SNOW:
1500  pct_precip = 60;
1501  break;
1502  case SKY_HURRICANE:
1503  case SKY_BLIZZARD:
1504  pct_precip = 99;
1505  break;
1506  }
1507  // TODO: Move these globally? Pretty sure they will be in a consistent spot in memory for execution duration.
1508  sstring snowc = find_string("snow_c");
1509  sstring rain = find_string("rain");
1510  if (rndm(0, 99) + pct_precip >= 100) {
1511  // Do our weather inserts here.
1512  // t < -2 == always snow
1513  // 2 >= t > -2 == rain/snow mix
1514  // t > 2 == always rain
1515  if (temp < -2 || (temp <= 2 && rndm(0, 2+temp) - 2 <= 0)) {
1516  at = find_archetype("snow_c");
1517  }
1518  else {
1519  at = find_archetype("rain");
1520  }
1521  if (at) {
1522  // Make sure we don't stack rains/snow ad nauseam on tiles. Also allow to switch the precip on the tile.
1523  tmp = GET_MAP_OB(m, x, y);
1524  avoid = 0;
1525  while (tmp) {
1526  if (tmp->arch == at) {
1527  avoid++;
1528  break;
1529  }
1530  else if ((tmp->arch->name == snowc && at->name == rain) ||
1531  (tmp->arch->name == rain && at->name == snowc)) {
1532  // Remove the wrong precipitation
1533  object_remove(tmp);
1534  object_free(tmp, 0);
1535  }
1536  tmp = tmp->above;
1537  }
1538  if (!avoid)
1540  }
1541  }
1542  else {
1543  // Look for a rain/snow on this tile and remove it.
1544  tmp = GET_MAP_OB(m, x, y);
1545  while (tmp) {
1546  if (tmp->arch->name == rain || tmp->arch->name == snowc) {
1547  object_remove(tmp);
1548  object_free(tmp, 0);
1549  }
1550  tmp = tmp->above;
1551  }
1552  }
1553 }
1554 
1561 static void do_map_precipitation(mapstruct * const m) {
1562  if (!m)
1563  return;
1564  // Make sure it has a weather map.
1565  int x, y, temp, sky, wx, wy;
1566  if (worldmap_to_weathermap(0, 0, &x, &y, m) != 0)
1567  return;
1568  // Now we re-do the precipitation on the map.
1569  for (x = 0; x < m->width; ++x)
1570  for (y = 0; y < m->height; ++y) {
1571  worldmap_to_weathermap(x, y, &wx, &wy, m);
1572  temp = weathermap[wx][wy].realtemp = real_world_temperature(x, y, m);
1573  sky = weathermap[wx][wy].sky;
1574  do_precipitation(m, x, y, temp, sky);
1575  }
1576 }
1577 
1584 static void let_it_snow(mapstruct * const m) {
1585  int x, y, wx, wy;
1586  int nx, ny, j, d;
1587  int avoid, temp, sky, gotsnow, nodstk;
1588  object *tmp, *oldsnow, *topfloor;
1589  archetype *at, *doublestack, *doublestack2;
1590 
1591  sstring dungmag = find_string("dungeon_magic");
1592 
1593  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1594  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1595  /* jitter factor */
1596  if (rndm(0, 2) > 0) {
1597  x = y = d = -1;
1598  while (OUT_OF_REAL_MAP(m, x, y)) {
1599  d++;
1600  j = rndm(1, 8);
1601  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1602  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1603  if (d > 15) {
1604  x = nx;
1605  y = ny;
1606  }
1607  }
1608  } else {
1609  x = nx;
1610  y = ny;
1611  }
1612  /* we use the unjittered coordinates */
1613  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1614  at = NULL;
1615  doublestack = NULL;
1616  /* this will definately need tuning */
1617  avoid = 0;
1618  gotsnow = 0;
1619  nodstk = 0;
1620  /*temp = real_world_temperature(x, y, m);*/
1621  temp = weathermap[wx][wy].realtemp;
1622  sky = weathermap[wx][wy].sky;
1623  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG) {
1624  sky += 10; /*let it snow*/
1625  }
1626  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1627  if (!avoid) {
1628  if (sky >= SKY_LIGHT_SNOW && sky < SKY_HEAVY_SNOW) {
1629  at = find_archetype("snow5");
1630  }
1631  if (sky >= SKY_HEAVY_SNOW) {
1632  at = find_archetype("snow4");
1633  }
1634  if (sky >= SKY_LIGHT_SNOW) {
1635  /* the bottom floor of scorn is not IS_FLOOR */
1636  topfloor = NULL;
1637  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp, tmp = tmp->above) {
1638  if (tmp->arch->name != dungmag) {
1639  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1640  break;
1641  }
1642  }
1643  }
1644  /* topfloor should now be the topmost IS_FLOOR=1 */
1645  if (topfloor == NULL) {
1646  continue;
1647  }
1648  if (tmp != NULL) {
1649  nodstk++;
1650  }
1651  /* something is wrong with that sector. just skip it */
1652  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1653  if (check_replace_match(topfloor, repl)) {
1654  if (repl->special_snow != NULL) {
1655  at = repl->special_snow;
1656  }
1657  if (repl->doublestack_arch != NULL && !nodstk) {
1658  doublestack = repl->doublestack_arch;
1659  }
1660  break;
1661  }
1662  }
1663  }
1664  if (gotsnow && at) {
1665  if (oldsnow->arch == at) {
1666  at = NULL;
1667  } else {
1668  object_remove(oldsnow);
1669  object_free(oldsnow,0);
1670  tmp = GET_MAP_OB(m, x, y);
1671  /* clean up the trees we put over the snow */
1672  doublestack2 = NULL;
1673  if (tmp) {
1674  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1675  if (repl->doublestack_arch == NULL) {
1676  continue;
1677  }
1678  if (check_replace_match(tmp, repl)) {
1679  tmp = tmp->above;
1680  doublestack2 = repl->doublestack_arch;
1681  break;
1682  }
1683  }
1684  }
1685  if (tmp != NULL && doublestack2 != NULL) {
1686  if (tmp->arch == doublestack2) {
1687  object_remove(tmp);
1688  object_free(tmp,0);
1689  }
1690  }
1691  }
1692  }
1693  if (at != NULL) {
1695  if (doublestack != NULL) {
1696  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1697  }
1698  }
1699  }
1700  if (temp > 8 && GET_MAP_OB(m, x, y) != NULL) {
1701  /* melt some snow */
1702  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1703  avoid = 0;
1704  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1705  if (repl->special_snow == NULL) {
1706  continue;
1707  }
1708 
1709  if (tmp->arch == repl->special_snow) {
1710  avoid++;
1711  }
1712  if (avoid) {
1713  break;
1714  }
1715  }
1716  if (avoid) {
1717  /* replace snow with a big puddle */
1718  /* If it is a floor tile we're melting, try to place earth there to have *some* floor.
1719  * Don't mark as overlay, or it will be stuck there forever, rather than until the map resets.
1720  */
1721  if (!tmp->below || QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1722  at = find_archetype("earth");
1723  if (at)
1725  }
1726  object_remove(tmp);
1727  object_free(tmp,0);
1728  tmp = GET_MAP_OB(m, x, y);
1729  at = NULL; // Reset what arch we are looking at
1730  if (tmp) {
1731  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1732  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1733  if (tmp->arch->name == melt->tile) {
1734  at = melt->special_snow;
1735  }
1736  }
1737  }
1738  // Default
1739  if (!at) {
1740  at = find_archetype("rain5_weather");
1741  }
1742  if (at != NULL) {
1744  }
1745  }
1746  }
1747  }
1748  /* woo it's cold out */
1749  if (temp < -8) {
1750  avoid = 0;
1751  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1752  if (!strcasecmp(tmp->name, "ice")) {
1753  avoid--;
1754  }
1755  }
1756  tmp = GET_MAP_OB(m, x, y);
1757  if (tmp && (!strcasecmp(tmp->name, "sea"))) {
1758  avoid++;
1759  } else if (tmp && (!strcasecmp(tmp->name, "sea1"))) {
1760  avoid++;
1761  } else if (tmp && (!strcasecmp(tmp->name, "deep sea"))) {
1762  avoid++;
1763  } else if (tmp && (!strcasecmp(tmp->name, "shallow sea"))) {
1764  avoid++;
1765  }
1766  if (avoid > 0) {
1767  at = find_archetype("ice");
1769  }
1770  }
1771  }
1772  }
1773 }
1774 
1781 static void singing_in_the_rain(mapstruct * const m) {
1782  int x, y, wx, wy;
1783  int nx, ny, d, j;
1784  int avoid, temp, sky, gotsnow, /*found,*/ nodstk;
1785  object *tmp, *oldsnow, *topfloor;
1786  archetype *at, *doublestack, *doublestack2;
1787 
1788  sstring dungmag = find_string("dungeon_magic");
1789 
1790  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
1791  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
1792  /* jitter factor */
1793  if (rndm(0, 2) > 0) {
1794  x = y = d = -1;
1795  while (OUT_OF_REAL_MAP(m, x, y)) {
1796  // Save some processing when d > 15
1797  if (++d > 15) {
1798  x = nx;
1799  y = ny;
1800  }
1801  else {
1802  j = rndm(1, 8);
1803  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1804  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
1805  }
1806  }
1807  } else {
1808  x = nx;
1809  y = ny;
1810  }
1811  /* we use the unjittered coordinates */
1812  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
1813  at = NULL;
1814  doublestack = NULL;
1815  avoid = 0;
1816  gotsnow = 0;
1817  nodstk = 0;
1818  /*temp = real_world_temperature(x, y, m);*/
1819  temp = weathermap[wx][wy].realtemp;
1820  sky = weathermap[wx][wy].sky;
1821  /* Handle adding precipitation here. */
1822  do_precipitation(m, x, y, temp, sky);
1823 
1824  /* it's probably allready snowing */
1825  if (temp < 0) {
1826  continue;
1827  }
1828 
1829  oldsnow = avoid_weather(&avoid, m, x, y, &gotsnow, 0);
1830  if (!avoid) {
1831  tmp = GET_MAP_OB(m, x, y);
1832  if (tmp) {
1833  // Put the snowmelt into a data list so it isn't hardcoded mid-code anymore
1834  for (weather_replace_t *melt = weather_snowmelt; melt; melt = melt->next) {
1835  if (tmp->arch->name == melt->tile) {
1836  at = melt->special_snow;
1837  }
1838  }
1839  if (at)
1840  break;
1841  }
1842  if (sky == SKY_LIGHT_RAIN || sky == SKY_RAIN) {
1843  switch (rndm(0, SKY_HAIL-sky)) {
1844  case 0: at = find_archetype("rain1_weather"); break;
1845  case 1: at = find_archetype("rain2_weather"); break;
1846  default: at = NULL; break;
1847  }
1848  }
1849  if (sky >= SKY_HEAVY_RAIN && sky <= SKY_HURRICANE) {
1850  switch (rndm(0, SKY_HAIL-sky)) {
1851  case 0: at = find_archetype("rain3_weather"); break;
1852  case 1: at = find_archetype("rain4_weather"); break;
1853  case 2: at = find_archetype("rain5_weather"); break;
1854  default: at = NULL; break;
1855  }
1856  }
1857  /* the bottom floor of scorn is not IS_FLOOR */
1858  topfloor = NULL;
1859  for (tmp = GET_MAP_OB(m, x, y); tmp; topfloor = tmp,tmp = tmp->above) {
1860  if (tmp->arch->name != dungmag) {
1861  if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1862  break;
1863  }
1864  }
1865  }
1866  /* topfloor should now be the topmost IS_FLOOR=1 */
1867  if (topfloor == NULL) {
1868  continue;
1869  }
1870  if (tmp != NULL) {
1871  nodstk++;
1872  }
1873  /* something is wrong with that sector. just skip it */
1874  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1875  if (check_replace_match(topfloor, repl)) {
1876  if (repl->doublestack_arch != NULL && !nodstk) {
1877  doublestack = repl->doublestack_arch;
1878  }
1879  break;
1880  }
1881  }
1882  if (gotsnow && at) {
1883  if (oldsnow->arch == at) {
1884  at = NULL;
1885  } else {
1886  tmp = GET_MAP_OB(m, x, y);
1887  object_remove(oldsnow);
1888  /* clean up the trees we put over the snow */
1889  doublestack2 = NULL;
1890  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1891  if (repl->doublestack_arch == NULL) {
1892  continue;
1893  }
1894  if (check_replace_match(tmp, repl)) {
1895  tmp = tmp->above;
1896  doublestack2 = repl->doublestack_arch;
1897  break;
1898  }
1899  }
1900  object_free(oldsnow,0);
1901  if (tmp != NULL && doublestack2 != NULL) {
1902  if (tmp->arch == doublestack2) {
1903  object_remove(tmp);
1904  object_free(tmp,0);
1905  }
1906  }
1907  }
1908  }
1909  if (at != NULL) {
1911  if (doublestack != NULL) {
1912  do_weather_insert(m, x, y, doublestack, 0, 0, INS_NO_MERGE|INS_NO_WALK_ON|INS_ON_TOP);
1913  }
1914  }
1915  }
1916  /* Things evaporate fast in the heat */
1917  if (GET_MAP_OB(m, x, y) && temp > 8 && sky < SKY_OVERCAST && rndm(temp, 60) > 50) {
1918  /* evaporate */
1919  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
1920  // Find a tile to evaporate
1921  weather_replace_t *evap;
1922  for (evap = weather_evaporate; evap; evap = evap->next) {
1923  if (tmp->arch->name == evap->tile)
1924  break;
1925  }
1926  // If we found it, then evaporate it
1927  if (evap) {
1928  object_remove(tmp);
1929  object_free(tmp,0);
1930  if (weathermap[wx][wy].humid < 100 && rndm(0, 50) == 0) {
1931  weathermap[wx][wy].humid++;
1932  }
1933  // If the evaporation is done, clean up the doublestack on this tile.
1934  if (evap->special_snow == NULL) {
1935  tmp = GET_MAP_OB(m, x, y);
1936  /* clean up the trees we put over the rain */
1937  doublestack2 = NULL;
1938  for (weather_replace_t *repl = weather_replace; repl; repl = repl->next) {
1939  if (repl->doublestack_arch == NULL) {
1940  continue;
1941  }
1942  if (check_replace_match(tmp, repl)) {
1943  tmp = tmp->above;
1944  doublestack2 = repl->doublestack_arch;
1945  break;
1946  }
1947  }
1948  if (tmp != NULL && doublestack2 != NULL) {
1949  if (tmp->arch == doublestack2) {
1950  object_remove(tmp);
1951  object_free(tmp,0);
1952  }
1953  }
1954  }
1955  else {
1956  // Apply the replacement puddle
1958  }
1959  break;
1960  }
1961  }
1962  }
1963  }
1964  }
1965 }
1966 
1973 static void plant_a_garden(mapstruct *const m) {
1974  int x, y, i, wx, wy;
1975  int avoid, temp, gotsnow, found, days;
1976  object *tmp;
1977  archetype *at;
1978 
1980  for (x = 0; x < wset.worldmaptilesizex; x++) {
1981  for (y = 0; y < wset.worldmaptilesizey; y++) {
1982  (void)worldmap_to_weathermap(x, y, &wx, &wy, m);
1983  at = NULL;
1984  avoid = 0;
1985  gotsnow = 0;
1986  /*temp = real_world_temperature(x, y, m);*/
1987  temp = weathermap[wx][wy].realtemp;
1988  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
1989  if (!avoid) {
1990  found = 0;
1991  for (i = 0; weather_grow[i].herb != NULL; i++) {
1992  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1993  if (strcmp(tmp->arch->name, weather_grow[i].herb) != 0) {
1994  continue;
1995  }
1996 
1997  /* we found there is a herb here allready */
1998  found++;
1999  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2000  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax ||
2001  weathermap[wx][wy].humid < weather_grow[i].humin ||
2002  weathermap[wx][wy].humid > weather_grow[i].humax ||
2003  temp < weather_grow[i].tempmin ||
2004  temp > weather_grow[i].tempmax ||
2005  rndm(0, MIN(weather_grow[i].random/2, 1)) == 0) {
2006  /* the herb does not belong, randomly delete
2007  herbs to prevent overgrowth. */
2008  object_remove(tmp);
2009  object_free(tmp,0);
2010  break;
2011  }
2012  }
2013  /* don't doublestack herbs */
2014  if (found) {
2015  continue;
2016  }
2017  /* add a random factor */
2018  if (rndm(1, weather_grow[i].random) != 1) {
2019  continue;
2020  }
2021  /* we look up through two tiles for a matching tile */
2022  if (weather_grow[i].tile != NULL && GET_MAP_OB(m, x, y) != NULL) {
2023  if (strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_grow[i].tile) != 0) {
2024  if (GET_MAP_OB(m, x, y)->above != NULL) {
2025  if (strcmp(GET_MAP_OB(m, x, y)->above->arch->name, weather_grow[i].tile) != 0) {
2026  continue;
2027  }
2028  } else {
2029  continue;
2030  }
2031  }
2032  }
2033  if ((float)weathermap[wx][wy].rainfall/days < weather_grow[i].rfmin ||
2034  (float)weathermap[wx][wy].rainfall/days > weather_grow[i].rfmax) {
2035  continue;
2036  }
2037  if (weathermap[wx][wy].humid < weather_grow[i].humin ||
2038  weathermap[wx][wy].humid > weather_grow[i].humax) {
2039  continue;
2040  }
2041  if (temp < weather_grow[i].tempmin ||
2042  temp > weather_grow[i].tempmax) {
2043  continue;
2044  }
2045  if ((!GET_MAP_OB(m, x, y)) ||
2046  GET_MAP_OB(m, x, y)->elevation < weather_grow[i].elevmin ||
2047  GET_MAP_OB(m, x, y)->elevation > weather_grow[i].elevmax) {
2048  continue;
2049  }
2050  /* we got this far.. must be a match */
2051  at = find_archetype(weather_grow[i].herb);
2052  break;
2053  }
2054  if (at != NULL) {
2055  /* XXX is overlay_floor right? maybe.. */
2057  }
2058  }
2059  }
2060  }
2061 }
2062 
2069 static void change_the_world(mapstruct * const m) {
2070  int x, y, i, wx, wy;
2071  int nx, ny, j, d;
2072  int avoid, temp, gotsnow, found, days;
2073  object *tmp, *doublestack;
2074  archetype *at, *dat;
2075 
2077  for (nx = 0; nx < wset.worldmaptilesizex; nx++) {
2078  for (ny = 0; ny < wset.worldmaptilesizey; ny++) {
2079  /* jitter factor */
2080  if (rndm(0, 2) > 0) {
2081  x = y = d = -1;
2082  while (OUT_OF_REAL_MAP(m, x, y)) {
2083  d++;
2084  j = rndm(1, 8);
2085  x = nx+freearr_x[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2086  y = ny+freearr_y[j]*(rndm(0, 1)+rndm(0, 1)+rndm(0, 1)+1);
2087  if (d > 15) {
2088  x = nx;
2089  y = ny;
2090  }
2091  }
2092  } else {
2093  x = nx;
2094  y = ny;
2095  }
2096  /* we use the unjittered coordinates */
2097  (void)worldmap_to_weathermap(nx, ny, &wx, &wy, m);
2098  at = NULL;
2099  dat = NULL;
2100  avoid = 0;
2101  gotsnow = 0;
2102  /*temp = real_world_temperature(x, y, m);*/
2103  temp = weathermap[wx][wy].realtemp;
2104  (void)avoid_weather(&avoid, m, x, y, &gotsnow, 1);
2105  if (!avoid) {
2106  for (i = 0; weather_tile[i].herb != NULL; i++) {
2107  found = 0;
2108  doublestack = NULL;
2109  if (GET_MAP_OB(m, x, y)) {
2110  for (tmp = GET_MAP_OB(m, x, y)->above; tmp; tmp = tmp->above) {
2111  if (weather_tile[i].tile != NULL) {
2112  if (strcmp(tmp->arch->name, weather_tile[i].tile) == 0) {
2113  doublestack = tmp;
2114  continue;
2115  }
2116  }
2117  if (strcmp(tmp->arch->name, weather_tile[i].herb) != 0) {
2118  continue;
2119  }
2120 
2121  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2122  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax ||
2123  weathermap[wx][wy].humid < weather_tile[i].humin ||
2124  weathermap[wx][wy].humid > weather_tile[i].humax ||
2125  temp < weather_tile[i].tempmin ||
2126  temp > weather_tile[i].tempmax) {
2127  object_remove(tmp);
2128  object_free(tmp,0);
2129  if (doublestack) {
2130  object_remove(doublestack);
2131  object_free(doublestack,0);
2132  }
2133  break;
2134  } else {
2135  found++; /* there is one here allready. leave it */
2136  break;
2137  }
2138  }
2139  }
2140  if (found) {
2141  break;
2142  }
2143 
2144  /* add a random factor */
2145  if (rndm(1, weather_tile[i].random) != 1) {
2146  continue;
2147  }
2148  if ((float)weathermap[wx][wy].rainfall/days < weather_tile[i].rfmin ||
2149  (float)weathermap[wx][wy].rainfall/days > weather_tile[i].rfmax) {
2150  continue;
2151  }
2152  if (weathermap[wx][wy].humid < weather_tile[i].humin ||
2153  weathermap[wx][wy].humid > weather_tile[i].humax) {
2154  continue;
2155  }
2156  if (temp < weather_tile[i].tempmin ||
2157  temp > weather_tile[i].tempmax) {
2158  continue;
2159  }
2160  if ( (!GET_MAP_OB(m, x, y)) ||
2161  GET_MAP_OB(m, x, y)->elevation < weather_tile[i].elevmin ||
2162  GET_MAP_OB(m, x, y)->elevation > weather_tile[i].elevmax) {
2163  continue;
2164  }
2165  /* we got this far.. must be a match */
2166  if (GET_MAP_OB(m, x, y) && strcmp(GET_MAP_OB(m, x, y)->arch->name, weather_tile[i].herb) == 0) {
2167  break; /* no sense in doubling up */
2168  }
2169  at = find_archetype(weather_tile[i].herb);
2170  break;
2171  }
2172  if (at != NULL) {
2173  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) {
2174  dat = find_archetype(weather_tile[i].tile);
2175  }
2176  if (dat != NULL) {
2178  }
2179  if (gotsnow == 0) {
2181  }
2182  }
2183  }
2184  }
2185  }
2186 }
2187 
2200 static void weather_effect(mapstruct * const m) {
2201  int wx, wy, x, y;
2202 
2203  /* if the dm shut off weather, go home */
2204  if (wset.dynamiclevel < 1) {
2205  return;
2206  }
2207 
2208  if (!m->outdoor) {
2209  return;
2210  }
2211 
2212  x = 0;
2213  y = 0;
2214  /* for now, just bail if it's not the worldmap */
2215  if (worldmap_to_weathermap(x, y, &wx, &wy, m) != 0) {
2216  return;
2217  }
2218 
2219  /*First, calculate temperature*/
2221  /* we change the world first, if needed */
2222  if (wset.dynamiclevel >= 5) {
2224  }
2225  if (wset.dynamiclevel >= 2) {
2226  let_it_snow(m);
2228  }
2229  if (wset.dynamiclevel >= 3) {
2230  plant_a_garden(m);
2231  }
2232 }
2233 
2243  mapstruct *m;
2244  char filename[MAX_BUF];
2245  FILE *fp;
2246 
2247  if (!wset.dynamiclevel) {
2248  return;
2249  }
2250 
2251  /* move right to left, top to bottom */
2252  if (++wmperformstartx == int(settings.worldmaptilesx)) {
2253  wmperformstartx = 0;
2254  if (++wmperformstarty == int(settings.worldmaptilesy)) {
2255  wmperformstarty = 0;
2256  }
2257  }
2258 
2259  // Whenever we load a map for effects, recalculate the weather.
2260  // Do this before the actual map load so that precipitation is done with the new sky computation rather than the old
2261  compute_sky();
2262 
2263  snprintf(filename, sizeof(filename), "world/world_%d_%d", wmperformstartx+settings.worldmapstartx, wmperformstarty+settings.worldmapstarty);
2264 
2265  m = ready_map_name(filename, 0);
2266  if (m == NULL) {
2267  return; /* hrmm */
2268  }
2269 
2270  // Run weather effects here.
2271  // We do this here rather than on any map load so that the weather effects remain consistent.
2272  weather_effect(m);
2273 
2274  /* done */
2275  save_map(m, SAVE_MODE_OVERLAY); /* write the overlay */
2276  m->in_memory = MAP_IN_MEMORY; /*reset this*/
2277  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings.localdir);
2278  if ((fp = fopen(filename, "w")) == NULL) {
2279  LOG(llevError, "Cannot open %s for writing\n", filename);
2280  return;
2281  }
2282 
2283  if (players_on_map(m, TRUE) == 0) {
2284  delete_map(m);
2285  }
2286 
2287  fprintf(fp, "%d %d", wmperformstartx, wmperformstarty);
2288  fclose(fp);
2289 }
2290 
2312 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) {
2313  // If we're inside, the weather can't get us :P
2314  if (!m || !m->outdoor)
2315  return 0;
2316  // First, we get the weathermap for this location
2317  int nx, ny;
2318  if (worldmap_to_weathermap(x, y, &nx, &ny, m))
2319  return 0;
2320  int windspeed = weathermap[nx][ny].windspeed;
2321  int is_fly = move_type & MOVE_FLYING;
2322  // If not flying, then strong winds are needed to affect you.
2323  if (!is_fly)
2324  windspeed -= 20;
2325  // If no wind, then no push.
2326  if (windspeed <= 0)
2327  return 0;
2328  // Reduce effect from carrying more stuff.
2329  // Also, being on the ground makes the same wind increase affect you less as well.
2330  // Higher strength characters can also resist being blown by the wind when on the ground.
2331  if (!is_fly)
2332  wt /= 10000 * ((stats && stats->Str) ? stats->Str : 1);
2333  // When flying, we care about Dex over Str.
2334  else
2335  wt /= 20000 * ((stats && stats->Dex) ? stats->Dex : 1);
2336  // Massive things are pushed around less easily.
2337  if (windspeed*2 < wt)
2338  return 0;
2339  // The push will not happen every try. The greater the wind, the more often it succeeds.
2340  // Also, the lighter the object, the more often it succeeds
2341  // We do two rolls because it normalizes the effects better than a single roll.
2342  if (rndm(0, windspeed)+rndm(0, windspeed) < wt)
2343  return 0;
2344  // winddir is the direction the wind is coming from.
2345  // so we need to reverse it to push where the wind is going to.
2346  return absdir(weathermap[nx][ny].winddir+4);
2347 }
2348 
2349 /********************************************************************************************
2350  * Section END -- weather effect methods
2351  ********************************************************************************************/
2352 
2353 /********************************************************************************************
2354  * Section -- Initializations
2355  * Functions to load in config for determining certain weathermap attributes,
2356  * functions to initialize missing weathermap attributes,
2357  * and their helper functions.
2358  ********************************************************************************************/
2359 
2380 static int init_config_vals(const Settings *settings, const char *conf_filename, DensityConfig **list) {
2381  char filename[MAX_BUF], *line, *name;
2382  BufferReader *bfr;
2383  int found, is_obj_name, tree_count;
2384 
2385  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2386  // Open the file with the buffer reader.
2387  bfr = bufferreader_init_from_file(NULL, filename,
2388  "init_config_vals: Could not open file %s. No forestry data is defined. %s\n",
2389  llevError);
2390  if (bfr == NULL) {
2391  // The error was printed by bufferreader_init_from_file already, so just bail.
2392  return 1;
2393  }
2394  // Now we read in from the buffer.
2395  while ((line = bufferreader_next_line(bfr)) != NULL) {
2396  // Now we parse the line
2397  // Start by examining the first character.
2398  switch (*line) {
2399  // Ignore empty lines and comment lines (denoted by # at front)
2400  case '\0':
2401  case '#':
2402  // Handling \r means Windows should work right, too.
2403  case '\r':
2404  case '\n':
2405  break;
2406  default:
2407  // Actually parse the line
2408  // Format is like this:
2409  // name, (0 if arch, 1 if object name), # trees
2410  // [spaces are expected after commas]
2411 
2412  // sscanf on strings is wonky (it always reads to whitespace),
2413  // so I'm gonna do it by just nabbing part of the buffer.
2414  name = line; // Each line starts with name
2416  if (line == NULL) {
2417  LOG(llevError, "init_config_vals: Malformed name entry in %s, line %ld.\n",
2418  filename, bufferreader_current_line(bfr));
2419  // Move on to the next line and hope it is fine.
2420  continue;
2421  }
2422 
2423  found = sscanf(line, "%d, %d\n", &is_obj_name, &tree_count);
2424  if (found != 2) {
2425  // Print an error for the malformed line
2426  LOG(llevError, "init_config_vals: Malformed forestry entry in %s, line %ld.\n",
2427  filename, bufferreader_current_line(bfr));
2428  }
2429  else {
2430  // Add a struct to the list.
2431  DensityConfig *frst = (DensityConfig *)malloc(sizeof(DensityConfig));
2432  if (!frst) {
2434  }
2435  // Shared strings are friend, not food
2436  frst->name = add_string(name);
2437  frst->is_obj = is_obj_name;
2438  frst->value_density = tree_count;
2439  // Attach to front of list, since order doesn't matter much, if at all.
2440  frst->next = *list;
2441  *list = frst;
2442  }
2443  }
2444  }
2445  bufferreader_destroy(bfr);
2446  return 0;
2447 }
2448 
2465 static int init_weatheravoid(const Settings *settings, const char *conf_filename, weather_avoids_t **wa) {
2466  char filename[MAX_BUF], *line, *name;
2467  BufferReader *bfr;
2468  int found, is_effect;
2469 
2470  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2471  // Open the file with the buffer reader.
2472  bfr = bufferreader_init_from_file(NULL, filename,
2473  "init_weatheravoid: Could not open file %s. No weatheravoid data is defined. %s\n", llevError);
2474  // If the bufferreader failed, it return NULL and printed an error, so just bail if failure.
2475  if (bfr == NULL)
2476  return 1;
2477  // Now we read in from the buffer.
2478  while ((line = bufferreader_next_line(bfr)) != NULL) {
2479  // Now we parse the line
2480  // Start by examining the first character.
2481  switch (*line) {
2482  // Ignore empty lines and comment lines (denoted by # at front)
2483  case '\0':
2484  case '#':
2485  // Handling \r means Windows should work right, too.
2486  case '\r':
2487  case '\n':
2488  break;
2489  default:
2490  // Actually parse the line
2491  // Format is like this:
2492  // name, (1 if weather effect, 0 if regular tile)
2493  // [spaces are expected after commas]
2494 
2495  // sscanf on strings is wonky (it always reads to whitespace),
2496  // so I'm gonna do it by just nabbing part of the buffer.
2497  name = line; // Each line starts with name
2499  if (line == NULL) {
2500  LOG(llevError, "init_weatheravoid: Malformed name entry in %s, line %ld.\n",
2501  filename, bufferreader_current_line(bfr));
2502  // Move on to the next line and hope it is fine.
2503  continue;
2504  }
2505 
2506  found = sscanf(line, "%d\n", &is_effect);
2507  if (found != 1) {
2508  // Print an error for the malformed line
2509  LOG(llevError, "init_weatheravoid: Malformed effect flag entry in %s, line %ld.\n",
2510  filename, bufferreader_current_line(bfr));
2511  }
2512  else {
2513  // Add a struct to the list.
2514  weather_avoids_t *frst = (weather_avoids_t *)malloc(sizeof(weather_avoids_t));
2515  if (!frst) {
2517  }
2518  // Shared strings are friend, not food
2519  frst->name = add_string(name);
2520  frst->snow = is_effect;
2521  // Attach to front of list, since order doesn't matter much, if at all.
2522  frst->next = *wa;
2523  *wa = frst;
2524  }
2525  }
2526  }
2527  bufferreader_destroy(bfr);
2528  return 0;
2529 }
2530 
2547 static int init_weather_replace(const Settings *settings, const char *conf_filename, weather_replace_t **list) {
2548  char filename[MAX_BUF], *line, *name, *repl, *doublestack;
2549  BufferReader *bfr;
2550  int found, is_arch;
2551 
2552  snprintf(filename, sizeof(filename), "%s/%s", settings->confdir, conf_filename);
2553  // Open the file with the buffer reader.
2554  bfr = bufferreader_init_from_file(NULL, filename, "init_weather_replace: Could not open file %s. No weather replace data is defined. %s\n", llevError);
2555  // If failed, we already printed an error.
2556  if (bfr == NULL)
2557  return 1;
2558  // Now we read in from the buffer.
2559  while ((line = bufferreader_next_line(bfr)) != NULL) {
2560  // Now we parse the line
2561  // Start by examining the first character.
2562  switch (*line) {
2563  // Ignore empty lines and comment lines (denoted by # at front)
2564  case '\0':
2565  case '#':
2566  // Handling \r means Windows should work right, too.
2567  case '\r':
2568  case '\n':
2569  break;
2570  default:
2571  // Actually parse the line
2572  // Format is like this:
2573  // name, replacement tile arch name, additional tile arch name (or NONE if not), (1 if arch name, 0 if object name)
2574  // [spaces are expected after commas]
2575 
2576  // sscanf on strings is wonky (it always reads to whitespace),
2577  // so I'm gonna do it by just nabbing part of the buffer.
2578  name = line; // Each line starts with name
2580  if (line == NULL) {
2581  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2582  LOG(llevError, "init_weather_replace: Malformed name entry in %s, line %ld.\n",
2583  filename, bufferreader_current_line(bfr));
2584  // Move on to the next line and hope it is fine.
2585  continue;
2586  }
2587 
2588  repl = line; // Each line starts with name
2590  if (line == NULL) {
2591  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2592  LOG(llevError, "init_weather_replace: Malformed replacement entry in %s, line %ld.\n",
2593  filename, bufferreader_current_line(bfr));
2594  // Move on to the next line and hope it is fine.
2595  continue;
2596  }
2597 
2598  doublestack = line; // Each line starts with name
2600  if (line == NULL) {
2601  // Since we end up tokenizing the line strings, we can't reliably print it in the output.
2602  LOG(llevError, "init_weather_replace: Malformed doublestack entry in %s, line %ld.\n",
2603  filename, bufferreader_current_line(bfr));
2604  // Move on to the next line and hope it is fine.
2605  continue;
2606  }
2607 
2608  found = sscanf(line, "%d\n", &is_arch);
2609  if (found != 1) {
2610  // Print an error for the malformed line
2611  LOG(llevError, "init_weatheravoid: Malformed archetype/object flag entry in %s, line %ld.\n",
2612  filename, bufferreader_current_line(bfr));
2613  }
2614  else {
2615  // Add a struct to the list.
2616  weather_replace_t *frst = (weather_replace_t *)malloc(sizeof(weather_replace_t));
2617  if (!frst) {
2619  }
2620  // Shared strings are friend, not food
2621  frst->tile = add_string(name);
2622  // Some replcement definitions can have NONE here to denote removal
2623  if (strcmp(repl, "NONE") == 0)
2624  frst->special_snow = NULL;
2625  else
2626  frst->special_snow = find_archetype(repl);
2627  // if doublestack is NONE, then set the arch to NULL
2628  if (strcmp(doublestack, "NONE") == 0)
2629  frst->doublestack_arch = NULL;
2630  else
2631  frst->doublestack_arch = find_archetype(doublestack);
2632  frst->arch_or_name = is_arch;
2633  // Attach to front of list, since order doesn't matter much, if at all.
2634  frst->next = *list;
2635  *list = frst;
2636  }
2637  }
2638  }
2639  bufferreader_destroy(bfr);
2640  return 0;
2641 }
2642 
2662 static int load_humidity_map_part(mapstruct **m, const int dir, const int x, const int y, int * const tx, int * const ty) {
2663  char mapname[MAX_BUF];
2664  if (!m || !tx || !ty)
2665  return -1;
2666  // Now we do what was wanted.
2667  weathermap_to_worldmap_corner(x, y, tx, ty, dir, mapname, sizeof(mapname));
2668  *m = mapfile_load(mapname, 0);
2669  if (*m == NULL) {
2670  return -1;
2671  }
2672 
2673  int res = load_overlay_map(mapname, *m);
2674  if (res != 0) {
2675  return -1;
2676  }
2677  return 0;
2678 }
2679 
2705 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) {
2706  if (!m || !water || !elev || !trees)
2707  return -1;
2708  object *ob = GET_MAP_OB(m, x, y);
2709  if (ob) {
2710  if (QUERY_FLAG(ob, FLAG_IS_WATER)) {
2711  (*water)++;
2712  }
2713  // Deserts will reduce the humidity/precipitation in the spaces they exist in.
2714  // Since the config entries are all negative, we can add the value here.
2715  (*water) += get_config_tile(x, y, m, water_list);
2716 
2717  // Handle forestry
2718  (*trees) += get_config_tile(x, y, m, forest_list);
2719 
2720  (*elev) += ob->elevation;
2721  }
2722  return 0;
2723 }
2724 
2735 static void init_humid_elev(const Settings *settings) {
2736  // Variable uses:
2737  // x, y: weathermap tile being affected
2738  // tx, ty: the in-map coordinates of the corner of the weathermap we are calculating.
2739  // nx, ny: coordinates within the weathermap
2740  // ax, ay: the location on the map we are examining
2741  // j: temporary variable for when a specific ny needs to be initialized from within a loop.
2742  int x, y, tx, ty, nx, ny, ax, ay, j;
2743  // spwtx, spwty: The number of tiles in a single weathermap in the associated (x or y) direction
2746  int64_t elev;
2747  int water, space, trees;
2748  mapstruct *m;
2749 
2750  /* handling of this is kinda nasty. For that reason,
2751  * we do the elevation here too. Not because it makes the
2752  * code cleaner, or makes handling easier, but because I do *not*
2753  * want to maintain two of these nightmares.
2754  */
2755 
2756  for (x = 0; x < WEATHERMAPTILESX; x++) {
2757  for (y = 0; y < WEATHERMAPTILESY; y++) {
2758  water = space = trees = 0;
2759  elev = 0;
2760  nx = ny = 0;
2761 
2762  /* top left */
2763  if (load_humidity_map_part(&m, 8, x, y, &tx, &ty) == -1)
2764  continue;
2765 
2766  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2767  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2768  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2769  //LOG(llevInfo, "%s %d %d (8)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2770  }
2771  }
2772  delete_map(m);
2773 
2774 
2775  // Sanely skip some processing if the entire weathermap fit on one world map.
2776  // Since we are the same size for x/y direction on both weathermaps and on world maps,
2777  // we will either need to load one map, two maps, or four maps. When two maps are loaded, it
2778  // will be one of bottom left or top right, since bottom right only is relevant when we intersect maps
2779  // in both x and y directions.
2780  if (space < spwtx*spwty) {
2781  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2782  if (ny < spwty) {
2783  /* bottom left */
2784  if (load_humidity_map_part(&m, 6, x, y, &tx, &ty) == -1)
2785  continue;
2786 
2787  // If we get here, then we didn't have the whole weathermap reside on one map.
2788  // Since we are continuing from top left, maintaining our position in the y direction
2789  // allows us to correctly check when we reach the end of the weathermap bounds.
2790  j = ny;
2791  for (nx = 0, ax = tx; nx < spwtx && ax < wset.worldmaptilesizex && space < spwtx*spwty; ax++, nx++) {
2792  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2793  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2794  //LOG(llevInfo, "%s %d %d (6)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2795  }
2796  }
2797  delete_map(m);
2798  }
2799 
2800  // If we gotall the way to the right on the left calculations, skip both right-side calculations.
2801  if (nx < spwtx) {
2802  /* top right */
2803  if (load_humidity_map_part(&m, 2, x, y, &tx, &ty) == -1)
2804  continue;
2805 
2806  for (ax = MAX(0, tx-(spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2807  for (ny = 0, ay = ty; ny < spwty && ay < wset.worldmaptilesizey && space < spwtx*spwty; ay++, ny++, space++) {
2808  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2809  //LOG(llevInfo, "%s %d %d (2)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2810  }
2811  }
2812  delete_map(m);
2813 
2814  // If we got all the way to the bottom on one map, don't even bother to load the map again.
2815  if (ny < spwty) {
2816  /* bottom right */
2817  if (load_humidity_map_part(&m, 4, x, y, &tx, &ty) == -1)
2818  continue;
2819 
2820  // Moving from top to bottom should behave the same on both right and left.
2821  j = ny;
2822  for (nx = 0, ax = MAX(0, tx - (spwtx-1)); nx < spwtx && ax <= tx && space < spwtx*spwty; ax++, nx++) {
2823  for (ny = j, ay = MAX(0, ty-(spwty-1)); ny < spwty && ay <= ty && space < spwtx*spwty; space++, ay++, ny++) {
2824  do_water_elev_calc(m, ax, ay, &water, &elev, &trees);
2825  //LOG(llevInfo, "%s %d %d (4)->(%d.%d, %d.%d)\n", m->path, ax, ay, x, nx, y, ny);
2826  }
2827  }
2828  delete_map(m);
2829  }
2830  }
2831  }
2832 
2833  /* jesus thats confusing as all hell */
2834  // Per meteorology, full ocean usually only gets to 80% humidity at the standard height it is measured.
2835  // And, even in the desert, relative humidity averages like 20%. So, in non-deserts, it should be like 40%.
2836  // This should help prevent a forever-hurricane over the ocean.
2837  weathermap[x][y].humid = 40+water*40/(spwtx*spwty);
2838  weathermap[x][y].avgelev = elev/(spwtx*spwty);
2839  weathermap[x][y].water = water*100/(spwtx*spwty);
2840  // Cap at 100 for tree values. Denser trees stop having any effect.
2841  weathermap[x][y].forestry = MIN(100, trees*100/(spwtx*spwty));
2842  }
2843  }
2844 
2845  /* and this does all the real work */
2846  update_humid();
2847 }
2848 
2854 static void init_temperature() {
2855  int x, y;
2856  timeofday_t tod;
2857 
2858  get_tod(&tod);
2859  for (x = 0; x < WEATHERMAPTILESX; x++) {
2860  for (y = 0; y < WEATHERMAPTILESY; y++) {
2861  temperature_calc(x, y, &tod);
2862  }
2863  }
2864 }
2865 
2871 static void init_rainfall()
2872 {
2873  int x, y;
2874  int days = todtick/HOURS_PER_DAY;
2875 
2876  for (x = 0; x < WEATHERMAPTILESX; x++) {
2877  for (y = 0; y < WEATHERMAPTILESY; y++) {
2878  if (weathermap[x][y].humid < 10) {
2879  weathermap[x][y].rainfall = days/20;
2880  } else if (weathermap[x][y].humid < 20) {
2881  weathermap[x][y].rainfall = days/15;
2882  } else if (weathermap[x][y].humid < 30) {
2883  weathermap[x][y].rainfall = days/10;
2884  } else if (weathermap[x][y].humid < 40) {
2885  weathermap[x][y].rainfall = days/5;
2886  } else if (weathermap[x][y].humid < 50) {
2887  weathermap[x][y].rainfall = days/2;
2888  } else if (weathermap[x][y].humid < 60) {
2889  weathermap[x][y].rainfall = days;
2890  } else if (weathermap[x][y].humid < 80) {
2891  weathermap[x][y].rainfall = days*2;
2892  } else {
2893  weathermap[x][y].rainfall = days*3;
2894  }
2895  }
2896  }
2897 }
2898 
2902 static void init_gulfstreammap() {
2903  int x, y, tx, starty, ymul, diroffset, dirdiff;
2904 
2905  /* build a gulf stream */
2907  /* doth the great bob inhale or exhale? */
2908  gulf_stream_direction = rndm(0, 1);
2909  gulf_stream_start = x;
2910 
2911  // Handle both gulf stream directions
2912  if (gulf_stream_direction) {
2913  // These variables allow us to only define the loop once.
2914  // That should make the code less awful to see
2915  starty = WEATHERMAPTILESY-1;
2916  ymul = -1;
2917  // The diroffset pieces allow us to merge the meat of the loops
2918  // the mapping between the different directions is as follows
2919  // 8 <-> 2
2920  // 7 <-> 3
2921  // 6 <-> 4
2922  // Thus setting diroffset to 10 when dirdiff is 1 gives us one direction
2923  // and setting diroffset to 0 when dirdiff is -1 gives us the other.
2924  diroffset = 0;
2925  dirdiff = -1;
2926  }
2927  else {
2928  starty = 0;
2929  ymul = 1;
2930  diroffset = 10;
2931  dirdiff = 1;
2932  }
2933  // Huzzah! a loop common to both directions!
2934  for (y = starty; y >= 0 && y < WEATHERMAPTILESY; y += ymul) {
2935  switch (rndm(0, 6)) {
2936  case 0:
2937  case 1:
2938  case 2:
2939  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2941  if (x == 0) {
2942  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2943  } else {
2944  gulf_stream_dir[tx][y] = (diroffset-8)*dirdiff;
2945  if (tx == 0) {
2946  x--;
2947  }
2948  }
2949  }
2950  break;
2951 
2952  case 3:
2953  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2955  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2956  }
2957  break;
2958 
2959  case 4:
2960  case 5:
2961  case 6:
2962  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
2964  if (x == WEATHERMAPTILESX-1) {
2965  gulf_stream_dir[tx][y] = (diroffset-7)*dirdiff;
2966  } else {
2967  gulf_stream_dir[tx][y] = (diroffset-6)*dirdiff;
2968  if (tx == 0) {
2969  x++;
2970  }
2971  }
2972  }
2973  break;
2974  }
2975  }
2976 }
2977 
2984 static void init_wind() {
2985  int x, y;
2986 
2987  for (x = 0; x < WEATHERMAPTILESX; x++) {
2988  for (y = 0; y < WEATHERMAPTILESY; y++) {
2989  weathermap[x][y].winddir = rndm(1, 8);
2990  weathermap[x][y].windspeed = rndm(1, 10);
2991  }
2992  }
2993 }
2994 
3001 static void init_pressure() {
3002  int x, y;
3003  int l, n, k;
3004 
3005  for (x = 0; x < WEATHERMAPTILESX; x++) {
3006  for (y = 0; y < WEATHERMAPTILESY; y++) {
3007  weathermap[x][y].pressure = 1013;
3008  }
3009  }
3010  // Add medium patches of low noise.
3011  for (l = 0; l < PRESSURE_ITERATIONS; l++) {
3012  x = rndm(0, WEATHERMAPTILESX-1);
3013  y = rndm(0, WEATHERMAPTILESY-1);
3015  for (k = 1; k < PRESSURE_AREA; k++) {
3016  switch (rndm(0, 3)) {
3017  case 0: if (x < WEATHERMAPTILESX-1) x++; break;
3018  case 1: if (y < WEATHERMAPTILESY-1) y++; break;
3019  case 2: if (x) x--; break;
3020  case 3: if (y) y--; break;
3021  }
3022  weathermap[x][y].pressure = (weathermap[x][y].pressure+n)/2;
3023  }
3024  }
3025  /* create random spikes in the pressure
3026  * These go way beyond the bounds of allowed pressure, but smooth_pressure
3027  * turns that into a sizable pressure blob.
3028  */
3029  for (l = 0; l < PRESSURE_SPIKES; l++) {
3030  x = rndm(0, WEATHERMAPTILESX-1);
3031  y = rndm(0, WEATHERMAPTILESY-1);
3032  n = rndm(500, 2000);
3033  weathermap[x][y].pressure = n;
3034  }
3035  smooth_pressure();
3036 }
3037 
3039  char buf[MAX_BUF], *cp;
3040  FILE *fp;
3041 
3042  snprintf(buf, sizeof(buf), "%s/wsettings", settings->confdir);
3043 
3044  if ((fp = fopen(buf, "r")) == NULL) {
3045  LOG(llevError, "Warning: No wsettings file found\n");
3046  return;
3047  }
3048  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
3049  if (buf[0] == '#')
3050  continue;
3051  /* eliminate newline */
3052  if ((cp = strrchr(buf, '\n')) != NULL)
3053  *cp = '\0';
3054 
3055  /* Skip over empty lines */
3056  if (buf[0] == 0)
3057  continue;
3058 
3059  /* Skip all the spaces and set them to nulls. If not space,
3060  * set cp to "" to make strcpy's and the like easier down below.
3061  */
3062  if ((cp = strchr(buf, ' ')) != NULL) {
3063  while (*cp == ' ')
3064  *cp++ = 0;
3065 
3066  // These only matter when we have a value, only look when we have a value
3067  // Even in the case of all trailing spaces and no value, cp points to a null-terminator,
3068  // which resolves to 0 from atoi().
3069  if (!strcasecmp(buf, "worldmaptilesizex")) {
3070  int size = atoi(cp);
3071 
3072  if (size < 1)
3073  LOG(llevError, "init_weather_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
3074  else
3075  wset.worldmaptilesizex = size;
3076  } else if (!strcasecmp(buf, "worldmaptilesizey")) {
3077  int size = atoi(cp);
3078 
3079  if (size < 1)
3080  LOG(llevError, "init_weather_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
3081  else
3082  wset.worldmaptilesizey = size;
3083  } else if (!strcasecmp(buf, "dynamiclevel")) {
3084  int lev = atoi(cp);
3085 
3086  if (lev < 0)
3087  LOG(llevError, "init_weather_settings: dynamiclevel must be at least 0, %d is invalid\n", lev);
3088  else
3089  wset.dynamiclevel = lev;
3090  }
3091  }
3092  else {
3093  LOG(llevError, "init_weather_settings: line %s ends after specifier, skipping...\n", buf);
3094  }
3095  }
3096 
3097  // Remember to clean up the file pointer
3098  fclose(fp);
3099 }
3100 
3101 /********************************************************************************************
3102  * Section END -- initializations
3103  ********************************************************************************************/
3104 
3105 /********************************************************************************************
3106  * Section -- weather data writers
3107  * These functions write the current state of the weather to file,
3108  * allowing persistence across server runs.
3109  ********************************************************************************************/
3110 
3124  char filename[MAX_BUF];
3125  FILE *fp;
3126  OutputFile of;
3127  int x, y;
3128 
3129  // First, allocate our file.
3130  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3131  // We use the output_file handling for atomic file operations.
3132  fp = of_open(&of, filename);
3133  if (fp == NULL) {
3134  LOG(llevError, "Failed to open %s for writing.\n", filename);
3135  return 1;
3136  }
3137  LOG(llevDebug, "Writing forestry map to file.\n");
3138  // Actually write the forestry amounts to the file
3139  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3140  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3141  fprintf(fp, "%d ", weathermap[x][y].forestry);
3142  }
3143  fprintf(fp, "\n");
3144  }
3145  of_close(&of);
3146  return 0;
3147 }
3148 
3159  char filename[MAX_BUF];
3160  FILE *fp;
3161  OutputFile of;
3162  int x, y;
3163 
3164  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3165  fp = of_open(&of, filename);
3166  if (fp == NULL) {
3167  LOG(llevError, "Cannot open %s for writing\n", filename);
3168  return 1;
3169  }
3170  LOG(llevDebug, "Writing humidity map to file.\n");
3171  for (x = 0; x < WEATHERMAPTILESX; x++) {
3172  for (y = 0; y < WEATHERMAPTILESY; y++) {
3173  fprintf(fp, "%d ", weathermap[x][y].humid);
3174  }
3175  fprintf(fp, "\n");
3176  }
3177  of_close(&of);
3178  return 0;
3179 }
3180 
3193 static int write_elevmap(const Settings *settings) {
3194  char filename[MAX_BUF];
3195  FILE *fp;
3196  OutputFile of;
3197  int x, y;
3198 
3199  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3200  fp = of_open(&of, filename);
3201  if (fp == NULL) {
3202  LOG(llevError, "Cannot open %s for writing\n", filename);
3203  return 1;
3204  }
3205  LOG(llevDebug, "Writing elevation map to file.\n");
3206  for (x = 0; x < WEATHERMAPTILESX; x++) {
3207  for (y = 0; y < WEATHERMAPTILESY; y++) {
3208  fprintf(fp, "%d ", weathermap[x][y].avgelev);
3209  }
3210  fprintf(fp, "\n");
3211  }
3212  of_close(&of);
3213  return 0;
3214 }
3215 
3216 
3228 static int write_watermap(const Settings *settings) {
3229  char filename[MAX_BUF];
3230  FILE *fp;
3231  OutputFile of;
3232  int x, y;
3233 
3234  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3235  fp = of_open(&of, filename);
3236  if (fp == NULL) {
3237  LOG(llevError, "Cannot open %s for writing\n", filename);
3238  return 1;
3239  }
3240  LOG(llevDebug, "Writing water map to file.\n");
3241  for (x = 0; x < WEATHERMAPTILESX; x++) {
3242  for (y = 0; y < WEATHERMAPTILESY; y++) {
3243  fprintf(fp, "%d ", weathermap[x][y].water);
3244  }
3245  fprintf(fp, "\n");
3246  }
3247  of_close(&of);
3248  return 0;
3249 }
3250 
3263  char filename[MAX_BUF];
3264  FILE *fp;
3265  OutputFile of;
3266  int x, y;
3267 
3268  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
3269  fp = of_open(&of, filename);
3270  if (fp == NULL) {
3271  LOG(llevError, "Cannot open %s for writing\n", filename);
3272  return 1;
3273  }
3274  LOG(llevDebug, "Writing temperature map to file.\n");
3275  for (x = 0; x < WEATHERMAPTILESX; x++) {
3276  for (y = 0; y < WEATHERMAPTILESY; y++) {
3277  fprintf(fp, "%d ", weathermap[x][y].temp);
3278  }
3279  fprintf(fp, "\n");
3280  }
3281  of_close(&of);
3282  return 0;
3283 }
3284 
3296  char filename[MAX_BUF];
3297  FILE *fp;
3298  OutputFile of;
3299  int x, y;
3300 
3301  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
3302  fp = of_open(&of, filename);
3303  if (fp == NULL) {
3304  LOG(llevError, "Cannot open %s for writing\n", filename);
3305  return 1;
3306  }
3307  LOG(llevDebug, "Writing rainfall map to file.\n");
3308  for (x = 0; x < WEATHERMAPTILESX; x++) {
3309  for (y = 0; y < WEATHERMAPTILESY; y++) {
3310  fprintf(fp, "%u ", weathermap[x][y].rainfall);
3311  }
3312  fprintf(fp, "\n");
3313  }
3314  of_close(&of);
3315  return 0;
3316 }
3317 
3329  char filename[MAX_BUF];
3330  FILE *fp;
3331  OutputFile of;
3332  int x, y;
3333 
3334  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
3335  fp = of_open(&of, filename);
3336  if (fp == NULL) {
3337  LOG(llevError, "Cannot open %s for writing\n", filename);
3338  return 1;
3339  }
3340  LOG(llevDebug, "Writing gulf stream map to file.\n");
3341  // First block is speed
3342  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3343  for (y = 0; y < WEATHERMAPTILESY; y++) {
3344  fprintf(fp, "%d ", gulf_stream_speed[x][y]);
3345  }
3346  fprintf(fp, "\n");
3347  }
3348  // second block is direction
3349  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
3350  for (y = 0; y < WEATHERMAPTILESY; y++) {
3351  fprintf(fp, "%d ", gulf_stream_dir[x][y]);
3352  }
3353  fprintf(fp, "\n");
3354  }
3355  // And the last line is the starting position, so we don't always have to initialize it.
3356  fprintf(fp, "%d\n", gulf_stream_start);
3357  of_close(&of);
3358  return 0;
3359 }
3360 
3372  char filename[MAX_BUF];
3373  FILE *fp;
3374  OutputFile of;
3375  int x, y;
3376 
3377  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
3378  fp = of_open(&of, filename);
3379  if (fp == NULL) {
3380  LOG(llevError, "Cannot open %s for writing\n", filename);
3381  return 1;
3382  }
3383  LOG(llevDebug, "Writing wind speed map to file.\n");
3384  for (x = 0; x < WEATHERMAPTILESX; x++) {
3385  for (y = 0; y < WEATHERMAPTILESY; y++) {
3386  fprintf(fp, "%hd ", weathermap[x][y].windspeed);
3387  }
3388  fprintf(fp, "\n");
3389  }
3390  of_close(&of);
3391  return 0;
3392 }
3393 
3405  char filename[MAX_BUF];
3406  FILE *fp;
3407  OutputFile of;
3408  int x, y;
3409 
3410  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
3411  fp = of_open(&of, filename);
3412  if (fp == NULL) {
3413  LOG(llevError, "Cannot open %s for writing\n", filename);
3414  return 1;
3415  }
3416  LOG(llevDebug, "Writing wind direction map to file.\n");
3417  for (x = 0; x < WEATHERMAPTILESX; x++) {
3418  for (y = 0; y < WEATHERMAPTILESY; y++) {
3419  fprintf(fp, "%d ", weathermap[x][y].winddir);
3420  }
3421  fprintf(fp, "\n");
3422  }
3423  of_close(&of);
3424  return 0;
3425 }
3426 
3438  char filename[MAX_BUF];
3439  FILE *fp;
3440  OutputFile of;
3441  int x, y;
3442 
3443  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
3444  fp = of_open(&of, filename);
3445  if (fp == NULL) {
3446  LOG(llevError, "Cannot open %s for writing\n", filename);
3447  return 1;
3448  }
3449  LOG(llevDebug, "Writing pressure map to file.\n");
3450  for (x = 0; x < WEATHERMAPTILESX; x++) {
3451  for (y = 0; y < WEATHERMAPTILESY; y++) {
3452  fprintf(fp, "%d ", weathermap[x][y].pressure);
3453  }
3454  fprintf(fp, "\n");
3455  }
3456  of_close(&of);
3457  return 0;
3458 }
3459 
3466 int write_skymap(void) {
3467  char filename[MAX_BUF];
3468  FILE *fp;
3469  OutputFile of;
3470  int x, y;
3471 
3472  snprintf(filename, sizeof(filename), "%s/skymap", settings.localdir);
3473  fp = of_open(&of, filename);
3474  if (fp == NULL) {
3475  LOG(llevError, "Cannot open %s for writing\n", filename);
3476  return 1;
3477  }
3478  LOG(llevDebug, "Writing sky conditions map to file.\n");
3479  for (x = 0; x < WEATHERMAPTILESX; x++) {
3480  for (y = 0; y < WEATHERMAPTILESY; y++) {
3481  fprintf(fp, "%d ", weathermap[x][y].sky);
3482  }
3483  fprintf(fp, "\n");
3484  }
3485  of_close(&of);
3486  return 0;
3487 }
3488 
3489 /* This stuff is for creating the images,
3490  * and is only used by write_weather_images()
3491  */
3492 
3493 /* Colour offsets into pixel array. */
3494 #define RED 0
3495 #define GREEN 1
3496 #define BLUE 2
3497 
3505 static const uint32_t directions[] = {
3506  0x0000FFFF, /* south */
3507  0x000000FF, /* south west */
3508  0x00FF00FF, /* west */
3509  0x00FFFFFF, /* north west */
3510  0x00000000, /* north */
3511  0x00FF0000, /* north east */
3512  0x00FFFF00, /* east */
3513  0x0000FF00 /* south east */
3514 };
3515 
3519 static const uint32_t skies[] = {
3520  0x000000FF, /* SKY_CLEAR 0 */
3521  0x000000BD, /* SKY_LIGHTCLOUD 1 */
3522  0x0000007E, /* SKY_OVERCAST 2 */
3523  0x0000FF00, /* SKY_LIGHT_RAIN 3 */
3524  0x0000BD00, /* SKY_RAIN 4 */
3525  0x00007E00, /* SKY_HEAVY_RAIN 5 */
3526  0x00FFFF00, /* SKY_HURRICANE 6 */
3527 /* wierd weather 7-12 */
3528  0x00FF0000, /* SKY_FOG 7 */
3529  0x00FF00FF, /* SKY_HAIL 8 */
3530  0x00000000,
3531  0x00000000,
3532  0x00000000,
3533  0x00000000,
3534 /* snow */
3535  0x003F3F3F, /* SKY_LIGHT_SNOW 13 */
3536  0x007E7E7E, /* SKY_SNOW 14 */
3537  0x00BDBDBD, /* SKY_HEAVY_SNOW 15 */
3538  0x00FFFFFF /* SKY_BLIZZARD 16 */
3539 };
3540 
3554  char filename[MAX_BUF];
3555  FILE *fp;
3556  OutputFile of;
3557  int x, y;
3558  int32_t min[8], max[8], avgrain, avgwind;
3559  double scale[8], realscalewind;
3560  uint8_t pixels[3*3*WEATHERMAPTILESX];
3561  int64_t total_rainfall = 0;
3562  int64_t total_wind = 0;
3563  timeofday_t tod;
3564 
3565  // Get the time of day.
3566  // This is important for weather output later.
3567  get_tod(&tod);
3568 
3569  // Determine the output file's limits.
3570  min[0] = -100; max[0] = 100;
3571  min[1] = 0; max[1] = 0;
3572  min[2] = 0; max[2] = 0;
3573  min[3] = PRESSURE_MIN; max[3] = PRESSURE_MAX;
3574  // Minimum wind speed is always 0. Don't track it. We just define it so scale[4] is valid
3575  min[4] = 0; max[4] = 0;
3576  // The 6th tile is raw wind direction, and thus does not need limits
3577  min[6] = 0; max[6] = 100;
3578  min[7] = -45; max[7] = 45;
3579  // The 9th tile is raw sky data and does not need limits
3580  for (x = 0; x < WEATHERMAPTILESX; x++) {
3581  for (y = 0; y < WEATHERMAPTILESY; y++) {
3582 /* min[0] = MIN(min[0], weathermap[x][y].water); */
3583  min[1] = MIN(min[1], weathermap[x][y].avgelev);
3584  min[2] = MIN(min[2], int32_t(weathermap[x][y].rainfall));
3585 /* min[3] = MIN(min[3], weathermap[x][y].pressure); */
3586 /* min[4] = MIN(min[4], weathermap[x][y].windspeed); */
3587 /* min[6] = MIN(min[6], weathermap[x][y].humid); */
3588 /* min[7] = MIN(min[7], real_temp[x][y]); */
3589 
3590 /* max[0] = MAX(max[0], weathermap[x][y].water); */
3591  max[1] = MAX(max[1], weathermap[x][y].avgelev);
3592  max[2] = MAX(max[2], int32_t(weathermap[x][y].rainfall));
3593 /* max[3] = MAX(max[3], weathermap[x][y].pressure); */
3594  max[4] = MAX(max[4], weathermap[x][y].windspeed);
3595 /* max[6] = MAX(max[6], weathermap[x][y].humid); */
3596 /* max[7] = MAX(max[7], real_temp[x][y]); */
3597  total_rainfall += weathermap[x][y].rainfall;
3598  total_wind += weathermap[x][y].windspeed;
3599  }
3600  }
3601  // Twiddle the data on total rainfall, since they have a different color for above/below average
3602  // This allows us to have the full scale of color range on each above average and below average.
3603  avgrain = total_rainfall/(WEATHERMAPTILESX*WEATHERMAPTILESY);
3604  assert(avgrain >= 0);
3605  avgwind = (total_wind /(WEATHERMAPTILESX*WEATHERMAPTILESY));
3606  max[2] = avgrain >= 1 ? avgrain-1 : 0;
3607  realscalewind = 255.0l/(max[4]);
3608  max[4] = avgwind >= 1 ? avgwind-1 : 0;
3609  for (x = 0; x < 8; x++) {
3610  scale[x] = 255.0l/(max[x] != min[x] ? max[x] - min[x] : 1);
3611  }
3612 
3613  LOG(llevDebug, "Writing weather conditions map.\n");
3614 
3615  snprintf(filename, sizeof(filename), "%s/weather.ppm", settings.localdir);
3616  fp = of_open(&of, filename);
3617  if (fp == NULL) {
3618  LOG(llevError, "Cannot open %s for writing\n", filename);
3619  return 1;
3620  }
3621  fprintf(fp, "P6\n%d %d\n", 3*WEATHERMAPTILESX, 3*WEATHERMAPTILESY);
3622  fprintf(fp, "255\n");
3623  // First row of maps
3624  for (y = 0; y < WEATHERMAPTILESY; y++) {
3625  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3626  for (x = 0; x < WEATHERMAPTILESX; x++) {
3627  // water/tree map -- first map of row
3628  // blue = high water amount, black = low water amount, red = desert-like, green = trees
3629  if (weathermap[x][y].water < 0)
3630  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(weathermap[x][y].water-min[0])*scale[0]*2);
3631  else
3632  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].water)*scale[0]*2);
3633  // Either way, we use green to highlight the trees, too
3634  // Make this real simple since it is established that forestry values range from 0 to 100.
3635  // As a result, our values go from 0-250.
3636  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].forestry)*5/2);
3637  // elevation map -- second map of row.
3638  // green -- mostly land --> brighter green is higher elevation
3639  // blue -- mostly water --> deeper blue is lower elevation
3640  if (weathermap[x][y].avgelev >= 0) {
3641  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3642  } else {
3643  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].avgelev-min[1])*scale[1]);
3644  }
3645  // rainfall map -- third map of row
3646  // magenta = high rainfall, blue = average rainfall, black = low rainfall
3647  if (weathermap[x][y].rainfall >= uint32_t(avgrain)) { /* rainfall is rather spikey, this gives us more detail. */
3648  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = 255;
3649  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((weathermap[x][y].rainfall-avgrain)*(255.0/(max[2]-avgrain)));
3650  } else {
3651  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].rainfall-min[2])*(avgrain-min[2]));
3652  }
3653  }
3654  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3655  }
3656  // Second row of maps.
3657  for (y = 0; y < WEATHERMAPTILESY; y++) {
3658  for (x = 0; x < WEATHERMAPTILESX; x++) {
3659  uint32_t dir = directions[weathermap[x][y].winddir-1];
3660  int32_t speed = weathermap[x][y].windspeed;
3661  int16_t pressure = (weathermap[x][y].pressure-PRESSURE_MIN)*scale[3];
3662  // Make sure we don't get artifacting from Post-smoothing pressure adjustments.
3663  // or round-off error in the above calculation.
3664  pressure = MIN(255, MAX(0, pressure));
3665  // Pressure -- first map of row
3666  // light = high pressure, dark = low pressure
3667  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = pressure;
3668  pixels[3*x+(0*WEATHERMAPTILESX*3+GREEN)] = pressure;
3669  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = pressure;
3670  // Wind speed -- second map of row
3671  // very high wind = red, else grey = average wind, dark = low wind
3672  if (speed < avgwind) {
3673  speed = (speed)*scale[4]/2;
3674  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = speed;
3675  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = speed;
3676  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = speed;
3677  } else {
3678  speed = (speed-avgwind)*realscalewind/2;
3679  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(MIN(255,(avgwind)*scale[4]/2+speed));
3680  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (avgwind)*scale[4]/2 - speed;
3681  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (avgwind)*scale[4]/2 - speed;
3682  }
3683  // Wind direction -- third map of row
3684  // red = northeast, yellow = east, green = southeast, cyan = south,
3685  // blue = southwest, magenta = west, white = northwest, black = north
3686  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3687  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3688  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3689  }
3690  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3691  }
3692  // Third row of maps
3693  for (y = 0; y < WEATHERMAPTILESY; y++) {
3694  memset(pixels, 0, 3 * 3 * WEATHERMAPTILESX);
3695  for (x = 0; x < WEATHERMAPTILESX; x++) {
3696  uint32_t dir = skies[weathermap[x][y].sky];
3697  // Humidity -- first map of row.
3698  // blue = high humidity, black = low humidity, red = droughty (even lower humidity)
3699  // Didn't adjust min for this one, so it should look a little different than the others.
3700  if (weathermap[x][y].humid < 0)
3701  pixels[3*x+(0*WEATHERMAPTILESX*3+RED)] = (uint8_t)(255-(-weathermap[x][y].humid)*scale[6]);
3702  else
3703  pixels[3*x+(0*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((weathermap[x][y].humid-min[6])*scale[6]);
3704  // Real temperature -- second map of row
3705  // temp < 0 --> scale from white to blue
3706  // temp > 0 --> scale from blue to green to yellow to red
3707  // green is 20 C
3708  // yellow is 30 C
3709  int temp = real_temperature(x, y, &tod);
3710  // white -> cyan
3711  if (temp < 0) {
3712  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)(-temp * scale[7]*2);
3713  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)0xFF;
3714  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)0xFF;
3715  }
3716  // cyan->blue is the boundary for above/below freezing
3717  // blue -> green
3718  else if (temp < 20) {
3719  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 0;
3720  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)(temp * scale[7]*9/2);
3721  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((20-temp) * scale[7]*9/2);
3722  }
3723  // green -> yellow
3724  else if (temp < 30) {
3725  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = (uint8_t)((temp-20) * scale[7]*9);
3726  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = 255;
3727  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3728  }
3729  // yellow -> red
3730  else {
3731  pixels[3*x+(1*WEATHERMAPTILESX*3+RED)] = 255;
3732  pixels[3*x+(1*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((45-temp) * scale[7]*6);
3733  pixels[3*x+(1*WEATHERMAPTILESX*3+BLUE)] = 0;
3734  }
3735  // current weather -- third map of row
3736  // blue = clear, medium blue = light clouds, dark blue = overcast, green = light rain
3737  // medium green = rain, dark green = heavy rain, yellow = hurricane, red = fog, magenta = hail,
3738  // dark gray = light snow, medium gray = snow, gray = heavy snow, white = blizzard
3739  pixels[3*x+(2*WEATHERMAPTILESX*3+RED)] = (uint8_t)((dir&0x00FF0000)>>16);
3740  pixels[3*x+(2*WEATHERMAPTILESX*3+GREEN)] = (uint8_t)((dir&0x0000FF00)>>8);
3741  pixels[3*x+(2*WEATHERMAPTILESX*3+BLUE)] = (uint8_t)((dir&0x000000FF));
3742  }
3743  fwrite(pixels, sizeof(uint8_t), (3*3*WEATHERMAPTILESX), fp);
3744  }
3745  of_close(&of);
3746  return 0;
3747 }
3748 
3749 /********************************************************************************************
3750  * Section END -- weather data writers
3751  ********************************************************************************************/
3752 
3753 /********************************************************************************************
3754  * Section -- weather data readers
3755  * These read weather data that gets periodically stored to file to restore
3756  * the prior state on a server restart. They also handle first-load initialization.
3757  ********************************************************************************************/
3758 
3776 static int read_forestrymap(const Settings *settings) {
3777  char filename[MAX_BUF], *data, *tmp;
3778  BufferReader *bfr;
3779  int trees, x, y, res;
3780 
3781  snprintf(filename, sizeof(filename), "%s/treemap", settings->localdir);
3782  LOG(llevDebug, "Reading forestry data from %s...\n", filename);
3783  // Set up the bufferreader and read in the file.
3784  // We do it through the bufferreader so that we only dip into I/O once,
3785  // and the rest is just parsing it in memory.
3786  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3787  // We already printed our error if we failed, so just bail in that case.
3788  if (bfr == NULL)
3789  return -1;
3790  // Parse the file. Since this is auto-generated by the weather system,
3791  // just bail if the file is malformed.
3792  data = bufferreader_data(bfr);
3793  for (x = 0; x < WEATHERMAPTILESX; ++x) {
3794  for (y = 0; y < WEATHERMAPTILESY; ++y) {
3795  res = sscanf(data, "%d ", &trees);
3796  if (res != 1) {
3797  LOG(llevError, "Forestry data is corrupted and should be regenerated.\n"
3798  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3799  bufferreader_destroy(bfr);
3800  return -1;
3801  }
3802  // Limit the range from 0 to 100
3803  weathermap[x][y].forestry = MIN(100, MAX(0, trees));
3804  // Now we move where we're looking, since we want to read more than just the first entry.
3805  // Use strpbrk so that we can handle newlines more cleanly.
3806  tmp = strpbrk(data, " \n");
3807  if (tmp != NULL)
3808  data = tmp + 1;
3809  else {
3810  LOG(llevError, "Unexpected end of forestry file. Forestry file may need to be regenerated.\n"
3811  "Please delete %s/humidmap and restart the server at your earliest convenience to regenerate the forestry map.\n", settings->localdir);
3812  bufferreader_destroy(bfr);
3813  return -1;
3814  }
3815  }
3816  // Due to the way the file is written, the end of the line should have a space and a newline.
3817  // Handle the newline if it is the front of the string now.
3818  if (*data == '\n')
3819  ++data;
3820  }
3821  bufferreader_destroy(bfr);
3822  return 0;
3823 }
3824 
3839 static int read_humidmap(const Settings *settings) {
3840  char filename[MAX_BUF], *data, *tmp;
3841  BufferReader *bfr;
3842  int x, y, hmd, res;
3843 
3844  snprintf(filename, sizeof(filename), "%s/humidmap", settings->localdir);
3845  LOG(llevDebug, "Reading humidity data from %s...\n", filename);
3846  // Open and read in the file all at once
3847  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3848  // If we fail, we do initializations instead.
3849  if (bfr == NULL) {
3850  LOG(llevInfo, "Initializing humidity and elevation maps...\n");
3856  LOG(llevDebug, "Done\n");
3857  return 1;
3858  }
3859  data = bufferreader_data(bfr);
3860  for (x = 0; x < WEATHERMAPTILESX; x++) {
3861  for (y = 0; y < WEATHERMAPTILESY; y++) {
3862  res = sscanf(data, "%d ", &hmd);
3863  if (res != 1) {
3864  LOG(llevError, "Humidity data is corrupted and cannot be loaded.\n"
3865  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3866  bufferreader_destroy(bfr);
3867  return -1;
3868  }
3869  // Limit range from 0 to 100. Clip entries that surpass these bounds.
3870  weathermap[x][y].humid = MAX(0, MIN(100, hmd));
3871  // Move to the next spot in the buffer.
3872  tmp = strpbrk(data, " \n");
3873  if (tmp == NULL) {
3874  LOG(llevError, "Unexpected end of humidity file.\n"
3875  "Please delete %s and restart the server to regenerate humidity data.\n", filename);
3876  bufferreader_destroy(bfr);
3877  return -1;
3878  }
3879  // If found, move data to the next character after the space/newline found.
3880  data = tmp + 1;
3881  }
3882  // If the newline from the previous line hasn't been parsed yet, skip it.
3883  if (*data == '\n')
3884  ++data;
3885  }
3886  bufferreader_destroy(bfr);
3887  LOG(llevDebug, "Done.\n");
3888  return 0;
3889 }
3890 
3903 static int read_elevmap(const Settings *settings) {
3904  char filename[MAX_BUF], *data, *tmp;
3905  BufferReader *bfr;
3906  int x, y, elev, res;
3907 
3908  snprintf(filename, sizeof(filename), "%s/elevmap", settings->localdir);
3909  LOG(llevDebug, "Reading elevation data from %s...\n", filename);
3910  // Read file into a buffer.
3911  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3912  // If failed, we bail.
3913  if (bfr == NULL) {
3914  /* initializing these is expensive, and should have been done
3915  by the humidity. It's not worth the wait to do it twice. */
3916  return -1;
3917  }
3918  data = bufferreader_data(bfr);
3919  for (x = 0; x < WEATHERMAPTILESX; x++) {
3920  for (y = 0; y < WEATHERMAPTILESY; y++) {
3921  res = sscanf(data, "%d ", &elev);
3922  if (res != 1) {
3923  LOG(llevError, "Elevation data is corrupted and cannot be loaded.\n"
3924  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3925  bufferreader_destroy(bfr);
3926  return -1;
3927  }
3928  // Individual elevation values should be in the range [-32000, 32000]
3929  // Averages can be capped to these as well.
3930  weathermap[x][y].avgelev = MAX(-32000, MIN(32000, elev));
3931  // Now we move to the next entry in the buffer.
3932  tmp = strpbrk(data, " \n");
3933  if (tmp == NULL) {
3934  LOG(llevError, "Unexpected end of file in elevation data.\n"
3935  "Please delete %s/humidmap and restart your server to regenerate elevation data.\n", settings->localdir);
3936  bufferreader_destroy(bfr);
3937  return -1;
3938  }
3939  // If found, we move to the character after the space/newline.
3940  data = tmp + 1;
3941  }
3942  // If there's still a newline after passing a space, then skip past it.
3943  if (*data == '\n')
3944  ++data;
3945  }
3946  bufferreader_destroy(bfr);
3947  LOG(llevDebug, "Done.\n");
3948  return 0;
3949 }
3950 
3962 static int read_watermap(const Settings *settings) {
3963  char filename[MAX_BUF], *data, *tmp;
3964  BufferReader *bfr;
3965  int x, y, wtr, res;
3966 
3967  snprintf(filename, sizeof(filename), "%s/watermap", settings->localdir);
3968  LOG(llevDebug, "Reading water data from %s...\n", filename);
3969  // Read the file into a buffer.
3970  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
3971  // If failed, we bail
3972  if (bfr == NULL) {
3973  /* initializing these is expensive, and should have been done
3974  by the humidity. It's not worth the wait to do it twice. */
3975  return -1;
3976  }
3977  data = bufferreader_data(bfr);
3978  for (x = 0; x < WEATHERMAPTILESX; x++) {
3979  for (y = 0; y < WEATHERMAPTILESY; y++) {
3980  res = sscanf(data, "%d ", &wtr);
3981  if (res != 1) {
3982  LOG(llevError, "Water map is corrupted and cannot be loaded.\n"
3983  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3984  bufferreader_destroy(bfr);
3985  return -1;
3986  }
3987  // Range is -100 to 100 due to deserts and such being negative.
3988  weathermap[x][y].water = MAX(-100, MIN(100, wtr));
3989  // Adjust the data pointer.
3990  tmp = strpbrk(data, " \n");
3991  if (tmp == NULL) {
3992  LOG(llevError, "Unexpected end of file in water map.\n"
3993  "Please delete %s/humidmap and restart your server to regenerate the water map.\n", settings->localdir);
3994  bufferreader_destroy(bfr);
3995  return -1;
3996  }
3997  // Okay, so we want the first character after the space/newline.
3998  data = tmp + 1;
3999  }
4000  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4001  if (*data == '\n')
4002  ++data;
4003  }
4004  bufferreader_destroy(bfr);
4005  LOG(llevDebug, "Done.\n");
4006  return 0;
4007 }
4008 
4021  char filename[MAX_BUF], *data, *tmp;
4022  BufferReader *bfr;
4023  int x, y, res;
4024  int16_t temperature;
4025 
4026  snprintf(filename, sizeof(filename), "%s/temperaturemap", settings->localdir);
4027  LOG(llevDebug, "Reading temperature data from %s...\n", filename);
4028  // Read the file into a buffer.
4029  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4030  // If it fails, we initialize the temperature map and write it to a file.
4031  if (bfr == NULL) {
4032  LOG(llevInfo, "Initializing temperature map.\n");
4033  init_temperature();
4034  // If writing was successful, then consider this a success.
4035  if (write_temperaturemap(settings) == 0)
4036  return 1;
4037  return -1;
4038  }
4039  data = bufferreader_data(bfr);
4040  for (x = 0; x < WEATHERMAPTILESX; x++) {
4041  for (y = 0; y < WEATHERMAPTILESY; y++) {
4042  res = sscanf(data, "%hd ", &temperature);
4043  if (res != 1) {
4044  LOG(llevError, "Temperature file is malformed, unable to load temps from file.\n");
4045  bufferreader_destroy(bfr);
4046  return -1;
4047  }
4048  // Clip to reasonable bounds.
4049  weathermap[x][y].temp = MIN(60, MAX(-30, temperature));
4050  // Adjust the data pointer.
4051  tmp = strpbrk(data, " \n");
4052  if (tmp == NULL) {
4053  LOG(llevError, "Unexpected end of file in temperature map.\n");
4054  bufferreader_destroy(bfr);
4055  return -1;
4056  }
4057  // Okay, so we want the first character after the space/newline.
4058  data = tmp + 1;
4059  }
4060  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4061  if (*data == '\n')
4062  ++data;
4063  }
4064  bufferreader_destroy(bfr);
4065  LOG(llevDebug, "Done.\n");
4066  return 0;
4067 }
4068 
4082 static int read_rainfallmap(const Settings *settings) {
4083  char filename[MAX_BUF], *data, *tmp;
4084  BufferReader *bfr;
4085  int x, y, res;
4086 
4087  snprintf(filename, sizeof(filename), "%s/rainfallmap", settings->localdir);
4088  LOG(llevDebug, "Reading rainfall data from %s...\n", filename);
4089  // Read file into a buffer.
4090  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4091  // If it fails, we initialize and write to file.
4092  if (bfr == NULL) {
4093  LOG(llevInfo, "Initializing rainfall map...\n");
4094  init_rainfall();
4095  // If we write to file successfully, consider initialization a success.
4096  if (write_rainfallmap(settings) != 0)
4097  return -1;
4098  return 1;
4099  }
4100  data = bufferreader_data(bfr);
4101  for (x = 0; x < WEATHERMAPTILESX; x++) {
4102  for (y = 0; y < WEATHERMAPTILESY; y++) {
4103  res = sscanf(data, "%u ", &weathermap[x][y].rainfall);
4104  if (res != 1) {
4105  LOG(llevError, "Rainfall file is corrupted, cannot load rainfall from file.\n");
4106  bufferreader_destroy(bfr);
4107  return -1;
4108  }
4109  // Now we update the pointer to data to move to the next item.
4110  tmp = strpbrk(data, " \n");
4111  if (tmp == NULL) {
4112  LOG(llevError, "Unexpected end of file in rainfall map.\n");
4113  bufferreader_destroy(bfr);
4114  return -1;
4115  }
4116  // Okay, so we want the first character after the space/newline.
4117  data = tmp + 1;
4118  }
4119  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4120  if (*data == '\n')
4121  ++data;
4122  }
4123  bufferreader_destroy(bfr);
4124  LOG(llevDebug, "Done.\n");
4125  return 0;
4126 }
4127 
4140  char filename[MAX_BUF], *data, *tmp;
4141  BufferReader *bfr;
4142  int x, y, in, res;
4143 
4144  snprintf(filename, sizeof(filename), "%s/gulfstreammap", settings->localdir);
4145  LOG(llevDebug, "Reading gulf stream data from %s...\n", filename);
4146  // Read the file into a buffer
4147  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4148  // If it fails, we initialize and write to file.
4149  if (bfr == NULL) {
4150  LOG(llevInfo, "Initializing gulf stream maps...\n");
4153  LOG(llevDebug, "Done\n");
4154  if (res == 0)
4155  return 1;
4156  return -1;
4157  }
4158  data = bufferreader_data(bfr);
4159  // First we read in the speeds
4160  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4161  for (y = 0; y < WEATHERMAPTILESY; y++) {
4162  res = sscanf(data, "%d ", &in);
4163  if (res != 1) {
4164  LOG(llevError, "Gulf stream speed definitions are malformed. Cannot load gulf stream from file.\n");
4165  bufferreader_destroy(bfr);
4166  return -1;
4167  }
4168  gulf_stream_speed[x][y] = MIN(120, MAX(0, in));
4169  // Now we update the pointer to data to move to the next item.
4170  tmp = strpbrk(data, " \n");
4171  if (tmp == NULL) {
4172  LOG(llevError, "Unexpected end of file in gulfstream speed map.\n");
4173  bufferreader_destroy(bfr);
4174  return -1;
4175  }
4176  // Okay, so we want the first character after the space/newline.
4177  data = tmp + 1;
4178  }
4179  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4180  if (*data == '\n')
4181  ++data;
4182  }
4183  // Then we read in the directions.
4184  for (x = 0; x < GULF_STREAM_WIDTH; x++) {
4185  for (y = 0; y < WEATHERMAPTILESY; y++) {
4186  res = sscanf(data, "%d ", &in);
4187  if (res != 1) {
4188  LOG(llevError, "Gulf stream direction definitions are malformed. Cannot load gulf stream from file.\n");
4189  bufferreader_destroy(bfr);
4190  return -1;
4191  }
4192  gulf_stream_dir[x][y] = MAX(1, MIN(8, in));
4193  // Now we update the pointer to data to move to the next item.
4194  tmp = strpbrk(data, " \n");
4195  if (tmp == NULL) {
4196  LOG(llevError, "Unexpected end of file in gulfstream direction map.\n");
4197  bufferreader_destroy(bfr);
4198  return -1;
4199  }
4200  // Okay, so we want the first character after the space/newline.
4201  data = tmp + 1;
4202  }
4203  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4204  if (*data == '\n')
4205  ++data;
4206  }
4207  // Then we read in the start point, if it exists
4208  // For backward compatability, we randomly initialize this if we can't read it.
4209  res = sscanf(data, "%d\n", &in);
4210  if (res != 1) {
4211  LOG(llevInfo, "Gulf stream file lacks start position, and is assumed to be old; initializing it randomly.\n");
4213  }
4215  // If we add any more parsing here, we'll need to affect the data pointer.
4216  // But, since we don't, leave it stale until it falls out of scope.
4217  bufferreader_destroy(bfr);
4218  LOG(llevDebug, "Done.\n");
4219  return 0;
4220 }
4221 
4236  char filename[MAX_BUF], *data, *tmp;
4237  BufferReader *bfr;
4238  int x, y, res;
4239  int8_t spd;
4240 
4241  snprintf(filename, sizeof(filename), "%s/windspeedmap", settings->localdir);
4242  LOG(llevDebug, "Reading wind speed data from %s...\n", filename);
4243  // Read the file into a buffer
4244  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4245  // If it fails, we bail
4246  if (bfr == NULL) {
4247  // Wind direction is done before this, and should have initialized this already.
4248  return -1;
4249  }
4250  data = bufferreader_data(bfr);
4251  for (x = 0; x < WEATHERMAPTILESX; x++) {
4252  for (y = 0; y < WEATHERMAPTILESY; y++) {
4253  res = sscanf(data, "%hhd ", &spd);
4254  if (res != 1) {
4255  LOG(llevError, "Wind speed file is malformed. Cannot load wind speed file.\n");
4256  bufferreader_destroy(bfr);
4257  return -1;
4258  }
4259  // Clip to reasonable bounds
4260  weathermap[x][y].windspeed = MIN(120, MAX(0, spd));
4261  // Now we update the pointer to data to move to the next item.
4262  tmp = strpbrk(data, " \n");
4263  if (tmp == NULL) {
4264  LOG(llevError, "Unexpected end of file in wind speed map.\n");
4265  bufferreader_destroy(bfr);
4266  return -1;
4267  }
4268  // Okay, so we want the first character after the space/newline.
4269  data = tmp + 1;
4270  }
4271  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4272  if (*data == '\n')
4273  ++data;
4274  }
4275  bufferreader_destroy(bfr);
4276  LOG(llevDebug, "Done.\n");
4277  return 0;
4278 }
4279 
4290 static int read_winddirmap(const Settings *settings) {
4291  char filename[MAX_BUF], *data, *tmp;
4292  BufferReader *bfr;
4293  int x, y, d, res;
4294 
4295  snprintf(filename, sizeof(filename), "%s/winddirmap", settings->localdir);
4296  LOG(llevDebug, "Reading wind direction data from %s...\n", filename);
4297  // Read the file into a buffer
4298  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading: %s\n", llevError);
4299  // If it fails, initialize the wind
4300  if (bfr == NULL) {
4301  LOG(llevInfo, "Initializing wind direction and speed maps...\n");
4302  init_wind();
4303  // If both of these succeed, the end result is 0.
4304  res = write_winddirmap(settings);
4305  res += write_windspeedmap(settings);
4306  LOG(llevDebug, "Done\n");
4307  if (res == 0)
4308  return 1;
4309  return -1;
4310  }
4311  data = bufferreader_data(bfr);
4312  for (x = 0; x < WEATHERMAPTILESX; x++) {
4313  for (y = 0; y < WEATHERMAPTILESY; y++) {
4314  res = sscanf(data, "%d ", &d);
4315  if (res != 1) {
4316  LOG(llevError, "Wind direction map is malformed. Could not load wind direction.\n");
4317  bufferreader_destroy(bfr);
4318  return -1;
4319  }
4320  // If the direction is not valid, just give it one randomly.
4321  if (d < 1 || d > 8) {
4322  d = rndm(1, 8);
4323  }
4324  weathermap[x][y].winddir = d;
4325  // Now we update the pointer to data to move to the next item.
4326  tmp = strpbrk(data, " \n");
4327  if (tmp == NULL) {
4328  LOG(llevError, "Unexpected end of file in wind direction map.\n");
4329  bufferreader_destroy(bfr);
4330  return -1;
4331  }
4332  // Okay, so we want the first character after the space/newline.
4333  data = tmp + 1;
4334  }
4335  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4336  if (*data == '\n')
4337  ++data;
4338  }
4339  bufferreader_destroy(bfr);
4340  LOG(llevDebug, "Done.\n");
4341  return 0;
4342 }
4343 
4354 static int read_pressuremap(const Settings *settings) {
4355  char filename[MAX_BUF], *data, *tmp;
4356  BufferReader *bfr;
4357  int x, y, res;
4358  int16_t press;
4359 
4360  snprintf(filename, sizeof(filename), "%s/pressuremap", settings->localdir);
4361  LOG(llevDebug, "Reading pressure data from %s...\n", filename);
4362  // Read the file into a buffer.
4363  bfr = bufferreader_init_from_file(NULL, filename, "Cannot open %s for reading\n", llevError);
4364  // If it fails, we initialize the pressure.
4365  if (bfr == NULL) {
4366  LOG(llevInfo, "Initializing pressure maps...\n");
4367  init_pressure();
4368  res = write_pressuremap(settings);
4369  LOG(llevDebug, "Done\n");
4370  if (res == 0)
4371  return 1;
4372  return -1;
4373  }
4374  data = bufferreader_data(bfr);
4375  for (x = 0; x < WEATHERMAPTILESX; x++) {
4376  for (y = 0; y < WEATHERMAPTILESY; y++) {
4377  res = sscanf(data, "%hd ", &press);
4378  if (res != 1) {
4379  LOG(llevError, "Pressure map is malformed. Could not load pressure.\n");
4380  bufferreader_destroy(bfr);
4381  return -1;
4382  }
4383  // Apply clipping to the pressure.
4384  weathermap[x][y].pressure = MIN(PRESSURE_MAX, MAX(PRESSURE_MIN, press));
4385  // Now we update the pointer to data to move to the next item.
4386  tmp = strpbrk(data, " \n");
4387  if (tmp == NULL) {
4388  LOG(llevError, "Unexpected end of file in pressure map.\n");
4389  bufferreader_destroy(bfr);
4390  return -1;
4391  }
4392  // Okay, so we want the first character after the space/newline.
4393  data = tmp + 1;
4394  }
4395  // Make sure we don't leave a newline in the event of a trailing space on a given line.
4396  if (*data == '\n')
4397  ++data;
4398  }
4399  bufferreader_destroy(bfr);
4400  LOG(llevDebug, "Done.\n");
4401  return 0;
4402 }
4403 
4417  char filename[MAX_BUF], *data;
4418  BufferReader *bfr;
4419  int sx, sy, res;
4420 
4421  snprintf(filename, sizeof(filename), "%s/wmapcurpos", settings->localdir);
4422  LOG(llevDebug, "Reading current weather position from %s...\n", filename);
4423  // Read the file into a buffer.
4424  bfr = bufferreader_init_from_file(NULL, filename, "Can't open %s: %s.\n", llevError);
4425  // If we fail, set wmperformstartx/y to their default values.
4426  if (bfr == NULL) {
4427  wmperformstartx = -1;
4428  wmperformstarty = 0;
4429  return 1;
4430  }
4431  data = bufferreader_data(bfr);
4432  // Read from the buffer
4433  res = sscanf(data, "%d %d", &sx, &sy);
4434  // Whether success or failure, we're done with the buffer.
4435  bufferreader_destroy(bfr);
4436  // Now we check our result.
4437  if (res != 2) {
4438  LOG(llevError, "Weather position file was malformed. Using default position.\n");
4439  wmperformstartx = -1;
4440  wmperformstarty = 0;
4441  return 1;
4442  }
4443  LOG(llevDebug, "curposx=%d curposy=%d\n", sx, sy);
4444 
4445  if (sx > int(settings->worldmaptilesx)) {
4446  sx = -1;
4447  }
4448  if (sy > int(settings->worldmaptilesy)) {
4449  sy = 0;
4450  }
4451  // Now we apply these to the static variables.
4452  wmperformstartx = sx;
4453  wmperformstarty = sy;
4454  return 0;
4455 }
4456 
4457 /********************************************************************************************
4458  * Section END -- weather data readers
4459  ********************************************************************************************/
4460 
4461 /********************************************************************************************
4462  * Section -- weather event listeners
4463  ********************************************************************************************/
4464 
4474 static int weather_listener(int *type, ...) {
4475  va_list args;
4476  int code;
4477  mapstruct *m;
4478 
4479  va_start(args, type);
4480  code = va_arg(args, int);
4481 
4482  switch (code) {
4483  case EVENT_MAPENTER:
4484  // At this point, we don't need the entering object for this.
4485  // but it is passed as an arg, so just skip it.
4486  va_arg(args, object *);
4487  /* FALLTHROUGH */
4488  case EVENT_MAPLOAD:
4489  case EVENT_MAPREADY:
4490  m = va_arg(args, mapstruct *);
4491  if (m->outdoor)
4493  break;
4494  }
4495 
4496  va_end(args);
4497 
4498  return 0;
4499 }
4500 
4513 static int weather_clock_listener(int *type, ...) {
4514  va_list args;
4515  int code;
4516 
4517  va_start(args, type);
4518  code = va_arg(args, int);
4519  va_end(args);
4520 
4521  switch (code) {
4522  case EVENT_CLOCK:
4523  if (!(pticks%PTICKS_PER_CLOCK)) {
4524  /* call the weather calculators, here, in order */
4525  tick_weather();
4526  // At every hour, measure the rainfall.
4527  // pticks%PTICKS_PER_CLOCK is already triggering every hour.
4528  process_rain();
4529  /* perform_weather must follow calculators */
4530  perform_weather();
4531  }
4532  // Handle weather printouts after enough server time.
4533  // By using primes here, rather than inside tick_the_clock(), we can reduce the load on a given tick.
4534  // (pticks%1500 is real busy otherwise)
4535  // This produces the side-effect that the server actually has to be on for that length
4536  // of time without interruption for the save to happen, but most servers are run long-term
4537  // anyway, so this should not be an issue
4538  if (!(pticks%1511))
4540  if (!(pticks%31511))
4542  if (!(pticks%33013))
4544  if (!(pticks%34501))
4546  if (!(pticks%36007))
4548  if (!(pticks%39019))
4550  if (!(pticks%40507))
4552  if (settings.fastclock > 0 && !(pticks%42013))
4553  write_skymap();
4554  if (!(pticks%43517))
4556  break;
4557  }
4558 
4559  return 0;
4560 }
4561 
4571 static int weather_object_listener(int *type, ...) {
4572  va_list args;
4573  int code;
4574  object *op;
4575 
4576  va_start(args, type);
4577  code = va_arg(args, int);
4578  // At this point, we don't need the entering object for this.
4579  // but it is passed as an arg, so just skip it.
4580  op = va_arg(args, object *);
4581 
4582  va_end(args);
4583 
4584  switch (code) {
4585  case EVENT_TIME:
4586  // Have weather affect the position of the object. Do not blow the floors, though -- that is bad.
4587  if (op->map && !QUERY_FLAG(op, FLAG_IS_FLOOR)) {
4588  uint8_t dir = wind_blow_object(op->map, op->x, op->y, op->move_type, op->weight+op->carrying, &op->stats);
4589  mapstruct *m;
4590  int16_t x, y;
4591  // By checking only the head space, we can actually caue sailing galleons to irrecoverably crash ashore.
4592  // This is deliberate behavior.
4593  if (dir &&
4594  // We will avoid pushing empty transports. Assume they are anchored/parked.
4595  !(op->type == TRANSPORT && op->inv == NULL) &&
4596  // If our player is in a transport, do not push the player
4597  !(op->type == PLAYER && op->contr && op->contr->transport) &&
4598  !(get_map_flags(op->map, &m, op->x+freearr_x[dir], op->y+freearr_y[dir], &x, &y)&P_OUT_OF_MAP) &&
4599  !blocked_link(op, m, x, y)) {
4600  object_remove(op);
4601  object_insert_in_map_at(op, m, op, 0, x, y);
4602 
4603  // Make sure to update the player view
4604  if (op->type == PLAYER) {
4605  esrv_map_scroll(op->contr->socket, freearr_x[dir], freearr_y[dir]);
4606  op->contr->socket->update_look = 1;
4607  op->contr->socket->look_position = 0;
4608  } else if (op->type == TRANSPORT) {
4609  FOR_INV_PREPARE(op, pl)
4610  if (pl->type == PLAYER) {
4611  pl->contr->do_los = 1;
4612  pl->map = op->map;
4613  pl->x = op->x;
4614  pl->y = op->y;
4615  esrv_map_scroll(pl->contr->socket, freearr_x[dir], freearr_y[dir]);
4616  pl->contr->socket->update_look = 1;
4617  pl->contr->socket->look_position = 0;
4618  }
4619  FOR_INV_FINISH();
4620  }
4621  }
4622  }
4623  }
4624  return 0;
4625 }
4626 
4635 static void command_weather (object *op, const char * /*params*/) {
4636  int wx, wy, temp, sky;
4637  const char *buf;
4638 
4639  if (wset.dynamiclevel < 1) {
4641  "The weather is perpetually great around here.");
4642  return;
4643  }
4644 
4645  if (op->map == NULL)
4646  return;
4647 
4648  if (worldmap_to_weathermap(op->x, op->y, &wx, &wy, op->map) != 0) {
4650  "You can't see the weather from here.");
4651  return;
4652  }
4653 
4654  if (QUERY_FLAG(op, FLAG_WIZ)) {
4655  /* dump the weather, Dm style! Yo! */
4656 
4658  "Real temp: %d", real_world_temperature(op->x, op->y, op->map));
4659 
4661  "Base temp: %d", weathermap[wx][wy].temp);
4662 
4664  "Humid: %d", weathermap[wx][wy].humid);
4665 
4667  "Wind: dir=%d speed=%d", weathermap[wx][wy].winddir, weathermap[wx][wy].windspeed);
4668 
4670  "Pressure: %d", weathermap[wx][wy].pressure);
4671 
4673  "Avg Elevation: %d", weathermap[wx][wy].avgelev);
4674 
4676  "Rainfall: %d Water: %d", weathermap[wx][wy].rainfall, weathermap[wx][wy].water);
4677  }
4678 
4679  temp = real_world_temperature(op->x, op->y, op->map);
4681  "It's currently %d degrees Centigrade out.", temp);
4682 
4683  /* humid */
4684  // We use buf real quick here for the same reason we use it for sky conditions: clarity
4685  if (weathermap[wx][wy].humid < 20)
4686  buf = "It is very dry.";
4687  else if (weathermap[wx][wy].humid < 40)
4688  buf = "It is rather dry.";
4689  else if (weathermap[wx][wy].humid < 60)
4690  buf = "It is very comfortable today.";
4691  else if (weathermap[wx][wy].humid < 75)
4692  buf = "It is a bit muggy.";
4693  else if (weathermap[wx][wy].humid < 85)
4694  buf = "It is muggy.";
4695  else
4696  buf = "It is uncomfortably muggy.";
4697  // buf will have a string, based on the if clauses above
4699 
4700  /* wind */
4701  switch (weathermap[wx][wy].winddir) {
4702  case 1:
4703  buf = "north";
4704  break;
4705  case 2:
4706  buf = "northeast";
4707  break;
4708  case 3:
4709  buf = "east";
4710  break;
4711  case 4:
4712  buf = "southeast";
4713  break;
4714  case 5:
4715  buf = "south";
4716  break;
4717  case 6:
4718  buf = "southwest";
4719  break;
4720  case 7:
4721  buf = "west";
4722  break;
4723  case 8:
4724  buf = "northwest";
4725  break;
4726  }
4727  if (weathermap[wx][wy].windspeed < 5)
4729  "There is a mild breeze coming from the %s.", buf);
4730  else if (weathermap[wx][wy].windspeed < 10)
4732  "There is a strong breeze coming from the %s.", buf);
4733  else if (weathermap[wx][wy].windspeed < 15)
4735  "There is a light wind coming from the %s.", buf);
4736  else if (weathermap[wx][wy].windspeed < 25)
4738  "There is a strong wind coming from the %s.", buf);
4739  else if (weathermap[wx][wy].windspeed < 35)
4741  "There is a heavy wind coming from the %s.", buf);
4742  else
4744  "The wind from the %s is incredibly strong!", buf);
4745 
4746  // If the current tile is below freezing, refer to the tile as snowing, even if the weathermap says rain.
4747  sky = weathermap[wx][wy].sky;
4748  if (temp <= 0 && sky > SKY_OVERCAST && sky < SKY_FOG)
4749  sky += 10; /*let it snow*/
4750  // Likewise, if the given tile is above freezing when the weathermap as a whole is below, say rain
4751  else if (temp > 0 && sky >= SKY_LIGHT_SNOW)
4752  sky -= 10;
4753 
4754  // Recycle buf for printing the various sky conditions strings.
4755  // This way we can cleanly call draw_ext_info once and not have
4756  // a bajillion calls with the same parameters other than the text.
4757  switch (sky) {
4758  case SKY_CLEAR:
4759  buf = "There isn't a cloud in the sky.";
4760  break;
4761  case SKY_LIGHTCLOUD:
4762  buf = "There are a few light clouds in the sky";
4763  break;
4764  case SKY_OVERCAST:
4765  buf = "The sky is cloudy and dreary.";
4766  break;
4767  case SKY_LIGHT_RAIN:
4768  buf = "It is raining softly.";
4769  break;
4770  case SKY_RAIN:
4771  buf = "It is raining.";
4772  break;
4773  case SKY_HEAVY_RAIN:
4774  buf = "It is raining heavily.";
4775  break;
4776  case SKY_HURRICANE:
4777  buf = "There is a heavy storm! You should go inside!";
4778  break;
4779  case SKY_FOG:
4780  buf = "It's foggy and miserable.";
4781  break;
4782  case SKY_HAIL:
4783  buf = "It's hailing out! Take cover!";
4784  break;
4785  case SKY_LIGHT_SNOW:
4786  buf = "Snow is gently falling from the sky.";
4787  break;
4788  case SKY_SNOW:
4789  buf = "It is snowing out.";
4790  break;
4791  case SKY_HEAVY_SNOW:
4792  buf = "Snow is falling very heavily.";
4793  break;
4794  case SKY_BLIZZARD:
4795  buf = "A full blown blizzard is in effect. You might want to take cover!";
4796  break;
4797  default:
4798  buf = NULL;
4799  }
4800  // If the sky is valid, then print the conditions.
4801  if (buf != NULL)
4803 }
4804 
4805 /********************************************************************************************
4806  * Section END -- weather event listeners
4807  ********************************************************************************************/
4808 
4809 // Event/command handler ids start at 1, so 0 is an unset flag.
4813 
4815 
4821  if (stage != STARTUP_STAGE_BEFORE_SERVER)
4822  return;
4823 
4824  int x, tx, ty;
4825 
4826  // Initialize weather settings
4828 
4829  /* all this stuff needs to be set, otherwise this function will cause
4830  * chaos and destruction.
4831  */
4832  if (wset.dynamiclevel < 1) {
4833  LOG(llevInfo, "cfweather_init: dynamic level set to %d. Not loading weather.\n", wset.dynamiclevel);
4834  return;
4835  }
4836 
4837  if (settings->worldmapstartx < 1 || settings->worldmapstarty < 1 ||
4840  return;
4841  }
4842  // Initialize the forestry information from file.
4843  init_config_vals(settings, "treedefs", &forest_list);
4844  init_config_vals(settings, "waterdefs", &water_list);
4845 
4846  /*prepare structures used for avoidance*/
4847  init_weatheravoid(settings, "wavoiddefs", &weather_avoids);
4848  init_weatheravoid(settings, "gavoiddefs", &growth_avoids);
4849 
4850  init_weather_replace(settings, "wreplacedefs", &weather_replace);
4853 
4854  // Set up weathermap grid. This is needed for just about everything
4855  LOG(llevDebug, "Initializing the weathermap...\n");
4856 
4857  weathermap = (weathermap_t **)malloc(sizeof(weathermap_t *)*WEATHERMAPTILESX);
4858  if (weathermap == NULL) {
4860  }
4861  for (x = 0; x < WEATHERMAPTILESX; x++) {
4862  weathermap[x] = (weathermap_t *)calloc(WEATHERMAPTILESY, sizeof(weathermap_t));
4863  if (weathermap[x] == NULL) {
4865  }
4866  }
4867  /* Unless you know what you're doing, do not re-order these
4868  * I think I got all the dependencies noted, but it works this way
4869  * and I'd advise against changing the order unless you have a good reason.
4870  */
4872  // Begin to initialize the various data pieces for the weather
4873  // If wind direction did initialization, we don't need to read the wind speed from file.
4874  if (read_winddirmap(settings) == 0)
4877  // Some gulf stream fiddling that happens every startup.
4878  gulf_stream_direction = rndm(0, 1);
4879  for (tx = 0; tx < GULF_STREAM_WIDTH; tx++) {
4880  for (ty = 0; ty < WEATHERMAPTILESY-1; ty++) {
4881  if (gulf_stream_direction) {
4882  switch (gulf_stream_dir[tx][ty]) {
4883  case 2: gulf_stream_dir[tx][ty] = 6; break;
4884  case 3: gulf_stream_dir[tx][ty] = 7; break;
4885  case 4: gulf_stream_dir[tx][ty] = 8; break;
4886  }
4887  } else {
4888  switch (gulf_stream_dir[tx][ty]) {
4889  case 6: gulf_stream_dir[tx][ty] = 2; break;
4890  case 7: gulf_stream_dir[tx][ty] = 3; break;
4891  case 8: gulf_stream_dir[tx][ty] = 4; break;
4892  }
4893  }
4894  }
4895  }
4896  // Trees help stabilize local temperature and evaporate water from deeper underground.
4897  // This is calculated at the same time as elevation and humidity.
4898  int result = read_humidmap(settings);
4899  // If result is 1, then we initialized all these.
4900  // When that is the case, we don't need to read in from the file.
4901  // If result is -1, everything's jacked up anyway, so still don't load.
4902  if (result == 0) {
4903  read_watermap(settings); /* On first run, we want to do this after humidity. Otherwise, it doesn't seem to matter */
4904  read_elevmap(settings); /* elevation must allways follow humidity */
4906  }
4909 
4910  LOG(llevDebug, "Done reading weathermaps\n");
4911  // Initialize the sky so we can get accurate precipitation at initial load.
4912  compute_sky();
4913 
4914  // Get current map position
4916 
4917  // Connect the events after initialization, since we don't need to do
4918  // precipitation when we're initializing.
4922  // Register the 'weather command
4924  /* Disable the plugin in case it's still there */
4925  server->disabled_plugins.push_back(strdup("cfweather"));
4926 }
4927 
4929  // Define temp pointers for clearing up the linked lists
4930  DensityConfig *cur;
4931  weather_avoids_t *avcur;
4932  weather_replace_t *rpcur;
4933  // Unregister handlers.
4934  if (global_map_handler != 0)
4936  if (global_clock_handler != 0)
4938  if (global_object_handler != 0)
4940  if (command_handler != 0)
4942  // Free the weathermap
4943  // Turns out that asset collection reaches here with weathermap as NULL,
4944  // so we need to make sure it is nonnull before trying to free it.
4945  if (weathermap != NULL) {
4946  for (int x = 0; x < WEATHERMAPTILESX; x++) {
4948  }
4950  }
4951  // Deallocate our linked list of forest entries.
4952  while (forest_list != NULL) {
4953  cur = forest_list;
4955  free_string(cur->name);
4956  free(cur);
4957  }
4958  // Do the same for the water list
4959  while (water_list != NULL) {
4960  cur = water_list;
4962  free_string(cur->name);
4963  free(cur);
4964  }
4965  // Free our avoid lists
4966  while (weather_avoids != NULL) {
4967  avcur = weather_avoids;
4969  free_string(avcur->name);
4970  free(avcur);
4971  }
4972  while (growth_avoids != NULL) {
4973  avcur = growth_avoids;
4975  free_string(avcur->name);
4976  free(avcur);
4977  }
4978  // Free our replacement lists
4979  while (weather_replace != NULL) {
4980  rpcur = weather_replace;
4982  free_string(rpcur->tile);
4983  free(rpcur);
4984  }
4985  while (weather_evaporate != NULL) {
4986  rpcur = weather_evaporate;
4988  free_string(rpcur->tile);
4989  free(rpcur);
4990  }
4991  while (weather_snowmelt != NULL) {
4992  rpcur = weather_snowmelt;
4994  free_string(rpcur->tile);
4995  free(rpcur);
4996  }
4997 }
4998 
4999 
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:2984
weathermap_t::realtemp
int16_t realtemp
Temperature at a given calculation step for this tile.
Definition: cfweather.cpp:111
global.h
weather_grow_t::tempmax
int tempmax
Maximum temperature for herb to grow.
Definition: cfweather.cpp:163
weathermap_t::forestry
int8_t forestry
Range of forestedness.
Definition: cfweather.cpp:109
weather_tile
static const weather_grow_t weather_tile[]
The table below uses the same format as the one above.
Definition: cfweather.cpp:257
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:1430
bufferreader_data
char * bufferreader_data(BufferReader *br)
Get the whole buffer, independently of the calls to bufferreader_next_line().
Definition: bufferreader.cpp:156
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:148
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
weathermap_t::sky
int8_t sky
Sky conditions.
Definition: cfweather.cpp:104
EQUATOR_BASE_TEMP
#define EQUATOR_BASE_TEMP
Definition: cfweather.cpp:35
init_rainfall
static void init_rainfall()
Initialize rainfall.
Definition: cfweather.cpp:2871
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:137
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:145
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:3001
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:300
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:1756
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:77
SEASONAL_ADJUST
#define SEASONAL_ADJUST
Definition: cfweather.cpp:36
read_winddirmap
static int read_winddirmap(const Settings *settings)
Read the wind direction.
Definition: cfweather.cpp:4290
weathermap_t::avgelev
int32_t avgelev
Average elevation.
Definition: cfweather.cpp:105
write_rainfallmap
int write_rainfallmap(const Settings *settings)
Save rainfall information to localdir.
Definition: cfweather.cpp:3295
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:426
absdir
int absdir(int d)
Computes an absolute direction.
Definition: object.cpp:3713
PRESSURE_ROUNDING_FACTOR
#define PRESSURE_ROUNDING_FACTOR
Definition: cfweather.cpp:43
write_humidmap
int write_humidmap(const Settings *settings)
Save humidity information to localdir.
Definition: cfweather.cpp:3158
temperature_calc
static void temperature_calc(const int x, const int y, const timeofday_t *tod)
Calculate temperature of a spot.
Definition: cfweather.cpp:1030
server
if you malloc the data for the make sure to free it when done There is also the newclient h file which is shared between the client and server This file contains the definition of the as well as many defined values for constants of varying you will need to grab these constant values for yourself Many of the constants in this file are used in the protocol to denote types Image Caching ~ Image caching has been implemented on the with necessary server support to handle it This section will briefly describe how image caching works on the protocol as well as how the current client does it the client checks for an option denoting the image caching is desired If we initialize all the images to a default value this means we don t need to put special checks into the drawing code to see if we have an image we just draw the default we know what filename to store it as we request the server to do image caching This is done by or ing the cache directive to the image mode we want C when the server finds an image number that it has not send to the it sends us a name command information us the number to name and there is no space between that the and the name Such formating is difficult but the above example illustrates the data is sent The client then checks for the existence of the image locally It is up to the client to organize images and then splits them into sub directories based on the first letters in the above the file would be crossfire images CS CSword If the client does not have the image or otherwise needs a copy from the server
Definition: protocol.txt:2156
SKY_LIGHT_RAIN
#define SKY_LIGHT_RAIN
Definition: cfweather.cpp:53
HOURS_PER_DAY
#define HOURS_PER_DAY
Definition: tod.h:15
object::x
int16_t x
Definition: object.h:337
player::transport
object * transport
Transport the player is in.
Definition: player.h:216
PRESSURE_SPIKES
#define PRESSURE_SPIKES
Definition: cfweather.cpp:45
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:131
SKY_CLEAR
#define SKY_CLEAR
Definition: cfweather.cpp:50
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:307
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:4354
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:46
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:107
GULF_STREAM_WIDTH
#define GULF_STREAM_WIDTH
Definition: cfweather.cpp:37
weathermap_t::pressure
int16_t pressure
Barometric pressure (mb).
Definition: cfweather.cpp:100
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:226
GULF_STREAM_BASE_SPEED
#define GULF_STREAM_BASE_SPEED
Definition: cfweather.cpp:38
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:144
weathermap_t::windspeed
int8_t windspeed
Windspeed of this tile.
Definition: cfweather.cpp:102
SKY_LIGHT_SNOW
#define SKY_LIGHT_SNOW
Definition: cfweather.cpp:61
WEATHER_OVERLAY
#define WEATHER_OVERLAY
Weather insert flags.
Definition: cfweather.cpp:619
weather_grow_t::random
int random
Random apparition factor.
Definition: cfweather.cpp:157
read_gulfstreammap
static int read_gulfstreammap(const Settings *settings)
Read the gulf stream, or initialize it if no saved information.
Definition: cfweather.cpp:4139
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:1189
SKY_LIGHTCLOUD
#define SKY_LIGHTCLOUD
Definition: cfweather.cpp:51
do_map_precipitation
static void do_map_precipitation(mapstruct *const m)
Do the precipitation for a given map.
Definition: cfweather.cpp:1561
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:2662
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:42
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:3839
rndm
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.cpp:163
DensityConfig::is_obj
int is_obj
Definition: cfweather.cpp:121
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:135
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:2547
POLAR_BASE_TEMP
#define POLAR_BASE_TEMP
Definition: cfweather.cpp:34
read_windspeedmap
static int read_windspeedmap(const Settings *settings)
Read the wind speed.
Definition: cfweather.cpp:4235
weathermap
weathermap_t ** weathermap
Definition: cfweather.cpp:184
weather_avoids_t::name
sstring name
Tile archetype name.
Definition: cfweather.cpp:134
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:459
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:298
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:147
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:1584
weathermap_t
This is an overlay structure of the whole world.
Definition: cfweather.cpp:95
command_handler
static command_registration command_handler
Definition: cfweather.cpp:4814
plant_a_garden
static void plant_a_garden(mapstruct *const m)
Process growth of various plants.
Definition: cfweather.cpp:1973
init_humid_elev
static void init_humid_elev(const Settings *settings)
Initialize humidity, water, forestry, and elevation.
Definition: cfweather.cpp:2735
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:175
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:44
read_weatherposition
static int read_weatherposition(const Settings *settings)
Read the weather map position from file.
Definition: cfweather.cpp:4416
read_watermap
static int read_watermap(const Settings *settings)
Load water information from localdir.
Definition: cfweather.cpp:3962
object::carrying
int32_t carrying
How much weight this object contains.
Definition: object.h:379
weather_snowmelt
weather_replace_t * weather_snowmelt
Definition: cfweather.cpp:192
object::y
int16_t y
Position in the map for this object.
Definition: object.h:337
m
static event_registration m
Definition: citylife.cpp:427
forest_list
DensityConfig * forest_list
Definition: cfweather.cpp:186
SKY_FOG
#define SKY_FOG
Definition: cfweather.cpp:58
WEATHER_NO_FLOOR
#define WEATHER_NO_FLOOR
Definition: cfweather.cpp:620
SKY_SNOW
#define SKY_SNOW
Definition: cfweather.cpp:62
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:286
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:3505
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:201
write_gulfstreammap
int write_gulfstreammap(const Settings *settings)
Save the gulf stream to localdir.
Definition: cfweather.cpp:3328
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:1483
DensityConfig::next
DensityConfig * next
Definition: cfweather.cpp:127
command_unregister
void command_unregister(command_registration command)
Unregister a previously registered command.
Definition: commands.cpp:541
polar_distance
static int polar_distance(int x, int y, const int equator)
Calculates the distance to the nearest pole.
Definition: cfweather.cpp:385
freearr_y
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.cpp:309
WIND_FACTOR
#define WIND_FACTOR
This is a multiplier for the wind caused by pressure differences.
Definition: cfweather.cpp:73
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:535
RED
#define RED
Definition: cfweather.cpp:3494
read_elevmap
static int read_elevmap(const Settings *settings)
Load the elevation information.
Definition: cfweather.cpp:3903
DensityConfig::value_density
int value_density
Definition: cfweather.cpp:123
skies
static const uint32_t skies[]
Colours used for weather types.
Definition: cfweather.cpp:3519
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:649
GREEN
#define GREEN
Definition: cfweather.cpp:3495
weathermap_t::temp
int16_t temp
Base temperature of this tile (F).
Definition: cfweather.cpp:99
gulf_stream_start
static int gulf_stream_start
Definition: cfweather.cpp:204
plot_gulfstream
static void plot_gulfstream()
Plot the gulfstream map over the wind map.
Definition: cfweather.cpp:1306
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:489
tick_weather
void tick_weather()
Do the weather calculations in order.
Definition: cfweather.cpp:1446
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:189
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:54
wmperformstartx
static int wmperformstartx
Current weather tile position.
Definition: cfweather.cpp:208
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:297
smooth_pressure
static void smooth_pressure()
This code simply smooths the pressure map.
Definition: cfweather.cpp:789
object::move_type
MoveType move_type
Type of movement this object uses.
Definition: object.h:438
write_windspeedmap
int write_windspeedmap(const Settings *settings)
Save the wind speed to localdir.
Definition: cfweather.cpp:3371
global_clock_handler
static event_registration global_clock_handler
Definition: cfweather.cpp:4811
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:158
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:3404
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:2854
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:350
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:1590
read_forestrymap
static int read_forestrymap(const Settings *settings)
Read the forestry map from the localdir.
Definition: cfweather.cpp:3776
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:584
BLUE
#define BLUE
Definition: cfweather.cpp:3496
WEATHER_NO_SAVE
#define WEATHER_NO_SAVE
Definition: cfweather.cpp:621
weather_replace_t::doublestack_arch
archetype * doublestack_arch
If set, this other archetype will be added atop special_snow.
Definition: cfweather.cpp:146
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:417
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:485
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:155
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:2312
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:334
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:897
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1684
weathermap_t::water
int8_t water
-100 - 100 percentage of water tiles.
Definition: cfweather.cpp:108
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:4810
STARTUP_STAGE_BEFORE_SERVER
@ STARTUP_STAGE_BEFORE_SERVER
Called when everything is loaded but before the sockets are opened.
Definition: modules.h:24
INS_NO_WALK_ON
#define INS_NO_WALK_ON
Don't call check_walk_on against the originator.
Definition: object.h:583
singing_in_the_rain
static void singing_in_the_rain(mapstruct *const m)
Process rain.
Definition: cfweather.cpp:1781
spin_globe
static void spin_globe()
The world spinning drags the weather with it.
Definition: cfweather.cpp:1160
DensityConfig::name
sstring name
Definition: cfweather.cpp:119
modules.h
init_gulfstreammap
static void init_gulfstreammap()
Initialize the gulf stream.
Definition: cfweather.cpp:2902
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:2099
living
Various statistics of objects.
Definition: living.h:35
water_list
DensityConfig * water_list
Definition: cfweather.cpp:187
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:970
weather_replace
weather_replace_t * weather_replace
Definition: cfweather.cpp:190
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:1270
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:2360
object::weight
int32_t weight
Attributes of the object.
Definition: object.h:377
process_rain
void process_rain()
Keep track of how much rain has fallen in a given weathermap square.
Definition: cfweather.cpp:1402
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
StartupStage
StartupStage
Definition: modules.h:21
cfweather_close
void cfweather_close()
Definition: cfweather.cpp:4928
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:2242
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:210
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:3676
write_elevmap
static int write_elevmap(const Settings *settings)
Save the average elevation information to localdir.
Definition: cfweather.cpp:3193
Settings
Server settings.
Definition: global.h:245
weather_settings_t::dynamiclevel
uint16_t dynamiclevel
How dynamic is the world?
Definition: cfweather.cpp:176
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:3123
llevInfo
@ llevInfo
Information.
Definition: logger.h:14
WEATHERMAPTILESX
#define WEATHERMAPTILESX
Definition: cfweather.cpp:76
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:4812
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:876
weather_grow_t::rfmax
float rfmax
Maximum rainfall for herb to grow (inches/day).
Definition: cfweather.cpp:159
SKY_HEAVY_RAIN
#define SKY_HEAVY_RAIN
Definition: cfweather.cpp:55
SKY_HEAVY_SNOW
#define SKY_HEAVY_SNOW
Definition: cfweather.cpp:63
object::name
sstring name
The name of the object, obviously...
Definition: object.h:321
weathermap_t::winddir
int8_t winddir
Direction of wind.
Definition: cfweather.cpp:103
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:582
compute_sky
void compute_sky()
Let the madness, begin.
Definition: cfweather.cpp:1103
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:165
weather_settings_t::worldmaptilesizex
int32_t worldmaptilesizex
Number of squares wide in a wm tile.
Definition: cfweather.cpp:174
weather_grow_t::elevmin
int elevmin
Minimum elevation for herb to grow.
Definition: cfweather.cpp:164
weather_grow_t::tempmin
int tempmin
Minimum temperature for herb to grow.
Definition: cfweather.cpp:162
SKY_HURRICANE
#define SKY_HURRICANE
Definition: cfweather.cpp:56
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:106
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:603
weather_object_listener
static int weather_object_listener(int *type,...)
Global object-process handling for weather.
Definition: cfweather.cpp:4571
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:1292
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:205
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:133
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:682
init_weather_settings
static void init_weather_settings(Settings *settings)
Definition: cfweather.cpp:3038
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:117
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:160
write_skymap
int write_skymap(void)
Write the sky map.
Definition: cfweather.cpp:3466
SKY_OVERCAST
#define SKY_OVERCAST
Definition: cfweather.cpp:52
read_temperaturemap
static int read_temperaturemap(const Settings *settings)
Load or initialize temperature information.
Definition: cfweather.cpp:4020
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:581
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:714
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:3437
weather_grow_t::humax
int humax
Maximum humidity for herb to grow.
Definition: cfweather.cpp:161
SKY_BLIZZARD
#define SKY_BLIZZARD
Definition: cfweather.cpp:64
read_rainfallmap
static int read_rainfallmap(const Settings *settings)
Read or initialize rainfall information.
Definition: cfweather.cpp:4082
weather_settings_t
Weather settings definition structure Stolen from the settings file, as they are unused by everything...
Definition: cfweather.cpp:173
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:452
write_temperaturemap
int write_temperaturemap(const Settings *settings)
Save temperature information to localdir.
Definition: cfweather.cpp:3262
command_weather
static void command_weather(object *op, const char *)
Player is wondering about the weather.
Definition: cfweather.cpp:4635
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:4474
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:1832
SKY_HAIL
#define SKY_HAIL
Definition: cfweather.cpp:59
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:166
weather_avoids
weather_avoids_t * weather_avoids
Definition: cfweather.cpp:188
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:143
weather_grow_t
Defines a tile where something can grow.
Definition: cfweather.cpp:154
write_watermap
static int write_watermap(const Settings *settings)
Save water percent information to localdir.
Definition: cfweather.cpp:3228
weather_effect
static void weather_effect(mapstruct *const m)
Perform actual effect of weather.
Definition: cfweather.cpp:2200
wset
static weather_settings_t wset
Definition: cfweather.cpp:212
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:2069
weathermap_t::humid
int8_t humid
Humitidy of this tile.
Definition: cfweather.cpp:101
archetype::name
sstring name
More definite name, like "generate_kobold".
Definition: object.h:486
PRESSURE_ITERATIONS
#define PRESSURE_ITERATIONS
Definition: cfweather.cpp:41
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:380
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:2380
server.h
weather_grow_t::tile
const char * tile
Arch tile to grow on, NULL if anything.
Definition: cfweather.cpp:156
freearr_x
short freearr_x[SIZEOFFREE]
X offset when searching around a spot.
Definition: object.cpp:303
gulf_stream_dir
static int gulf_stream_dir[GULF_STREAM_WIDTH][WEATHERMAPTILESY]
Direction of the gulf stream.
Definition: cfweather.cpp:203
weather_clock_listener
static int weather_clock_listener(int *type,...)
Global clock event handling for weather.
Definition: cfweather.cpp:4513
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:191
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
cfweather_init
void cfweather_init(Settings *settings, ServerSettings *server, StartupStage stage)
Weather module initialisation.
Definition: cfweather.cpp:4820
dirdiff
int dirdiff(int dir1, int dir2)
Computes a direction difference.
Definition: object.cpp:3731
BufferReader
Definition: bufferreader.cpp:22
object::material
uint16_t material
What materials this object consist of.
Definition: object.h:359
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:1204
object.h
weather_replace_t::next
weather_replace_t * next
The next item in the replace list.
Definition: cfweather.cpp:148
PRESSURE_MIN
#define PRESSURE_MIN
Definition: cfweather.cpp:47
write_weather_images
int write_weather_images()
Dump all the weather data as an image.
Definition: cfweather.cpp:3553
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:2705
perform_pressure
static void perform_pressure()
Perform small randomizations in the pressure map.
Definition: cfweather.cpp:829
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:110
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:2465
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254