1 /**********************************************************************
2  Freeciv - Copyright (C) 2004 - Marcelo J. Burda
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 #ifdef HAVE_CONFIG_H
14 #include <fc_config.h>
15 #endif
16 
17 /* utility */
18 #include "fcintl.h"
19 #include "log.h"
20 #include "rand.h"
21 #include "support.h"            /* bool type */
22 
23 /* common */
24 #include "map.h"
25 #include "packets.h"
26 #include "terrain.h"
27 #include "tile.h"
28 
29 #include "utilities.h"
30 
31 /****************************************************************************
32  Map that contains, according to circumstances, information on whether
33  we have already placed terrain (special, hut) here.
34 ****************************************************************************/
35 static bool *placed_map;
36 
37 /**************************************************************************
38  return TRUE if initialized
39 *************************************************************************/
placed_map_is_initialized(void)40 bool placed_map_is_initialized(void)
41 {
42   return placed_map != NULL;
43 }
44 
45 /****************************************************************************
46   Create a clean pmap
47 ****************************************************************************/
create_placed_map(void)48 void create_placed_map(void)
49 {
50   fc_assert_ret(!placed_map_is_initialized());
51   placed_map = fc_malloc (sizeof(bool) * MAP_INDEX_SIZE);
52   INITIALIZE_ARRAY(placed_map, MAP_INDEX_SIZE, FALSE );
53 }
54 
55 /****************************************************************************
56   Free the pmap
57 ****************************************************************************/
destroy_placed_map(void)58 void destroy_placed_map(void)
59 {
60   fc_assert_ret(placed_map_is_initialized());
61   free(placed_map);
62   placed_map = NULL;
63 }
64 
65 
66 
67 #define pmap(_tile) (placed_map[tile_index(_tile)])
68 
69 /**************************************************************************
70   Checks if land has not yet been placed on pmap at (x, y)
71 **************************************************************************/
not_placed(const struct tile * ptile)72 bool not_placed(const struct tile *ptile)
73 {
74   return !pmap(ptile);
75 }
76 
77 /**************************************************************************
78   Mark tile terrain as placed.
79 **************************************************************************/
map_set_placed(struct tile * ptile)80 void map_set_placed(struct tile *ptile)
81 {
82   pmap(ptile) = TRUE;
83 }
84 
85 /**************************************************************************
86   Mark tile terrain as not placed.
87 **************************************************************************/
map_unset_placed(struct tile * ptile)88 void map_unset_placed(struct tile *ptile)
89 {
90   pmap(ptile) = FALSE;
91 }
92 
93 /****************************************************************************
94   set all oceanics tiles in placed_map
95 ****************************************************************************/
set_all_ocean_tiles_placed(void)96 void set_all_ocean_tiles_placed(void)
97 {
98   whole_map_iterate(ptile) {
99     if (is_ocean_tile(ptile)) {
100       map_set_placed(ptile);
101     }
102   } whole_map_iterate_end;
103 }
104 
105 /****************************************************************************
106   Set all nearby tiles as placed on pmap.
107 ****************************************************************************/
set_placed_near_pos(struct tile * ptile,int dist)108 void set_placed_near_pos(struct tile *ptile, int dist)
109 {
110   square_iterate(ptile, dist, tile1) {
111     map_set_placed(tile1);
112   } square_iterate_end;
113 }
114 
115 /**************************************************************************
116   Change the values of the integer map, so that they contain ranking of each
117   tile scaled to [0 .. int_map_max].
118   The lowest 20% of tiles will have values lower than 0.2 * int_map_max.
119 
120   If filter is non-null then it only tiles for which filter(ptile, data) is
121   TRUE will be considered.
122 **************************************************************************/
adjust_int_map_filtered(int * int_map,int int_map_max,void * data,bool (* filter)(const struct tile * ptile,const void * data))123 void adjust_int_map_filtered(int *int_map, int int_map_max, void *data,
124 			     bool (*filter)(const struct tile *ptile,
125 					    const void *data))
126 {
127   int minval = 0, maxval = 0, total = 0;
128   bool first = TRUE;
129 
130   /* Determine minimum and maximum value. */
131   whole_map_iterate_filtered(ptile, data, filter) {
132     if (first) {
133       minval = int_map[tile_index(ptile)];
134       maxval = int_map[tile_index(ptile)];
135     } else {
136       maxval = MAX(maxval, int_map[tile_index(ptile)]);
137       minval = MIN(minval, int_map[tile_index(ptile)]);
138     }
139     first = FALSE;
140     total++;
141   } whole_map_iterate_filtered_end;
142 
143   if (total == 0) {
144     return;
145   }
146 
147   {
148     int const size = 1 + maxval - minval;
149     int i, count = 0, frequencies[size];
150 
151     INITIALIZE_ARRAY(frequencies, size, 0);
152 
153     /* Translate value so the minimum value is 0
154        and count the number of occurencies of all values to initialize the
155        frequencies[] */
156     whole_map_iterate_filtered(ptile, data, filter) {
157       int_map[tile_index(ptile)] -= minval;
158       frequencies[int_map[tile_index(ptile)]]++;
159     } whole_map_iterate_filtered_end;
160 
161     /* create the linearize function as "incremental" frequencies */
162     for(i =  0; i < size; i++) {
163       count += frequencies[i];
164       frequencies[i] = (count * int_map_max) / total;
165     }
166 
167     /* apply the linearize function */
168     whole_map_iterate_filtered(ptile, data, filter) {
169       int_map[tile_index(ptile)] = frequencies[int_map[tile_index(ptile)]];
170     } whole_map_iterate_filtered_end;
171   }
172 }
173 
174 /****************************************************************************
175   Is given native position normal position
176 ****************************************************************************/
is_normal_nat_pos(int x,int y)177 bool is_normal_nat_pos(int x, int y)
178 {
179   NATIVE_TO_MAP_POS(&x, &y, x, y);
180   return is_normal_map_pos(x, y);
181 }
182 
183 /*******************************************************************************
184   Apply a Gaussian diffusion filter on the map. The size of the map is
185   MAP_INDEX_SIZE and the map is indexed by native_pos_to_index function.
186   If zeroes_at_edges is set, any unreal position on diffusion has 0 value
187   if zeroes_at_edges in unset the unreal position are not counted.
188 *******************************************************************************/
smooth_int_map(int * int_map,bool zeroes_at_edges)189 void smooth_int_map(int *int_map, bool zeroes_at_edges)
190 {
191   static const float weight_standard[5] = { 0.13, 0.19, 0.37, 0.19, 0.13 };
192   static const float weight_isometric[5] = { 0.15, 0.21, 0.29, 0.21, 0.15 };
193   const float *weight;
194   bool axe = TRUE;
195   int *target_map, *source_map;
196   int *alt_int_map = fc_calloc(MAP_INDEX_SIZE, sizeof(*alt_int_map));
197 
198   fc_assert_ret(NULL != int_map);
199 
200   weight = weight_standard;
201   target_map = alt_int_map;
202   source_map = int_map;
203 
204   do {
205     whole_map_iterate(ptile) {
206       float N = 0, D = 0;
207 
208       axis_iterate(ptile, pnear, i, 2, axe) {
209         D += weight[i + 2];
210         N += weight[i + 2] * source_map[tile_index(pnear)];
211       } axis_iterate_end;
212       if (zeroes_at_edges) {
213         D = 1;
214       }
215       target_map[tile_index(ptile)] = (float)N / D;
216     } whole_map_iterate_end;
217 
218     if (MAP_IS_ISOMETRIC) {
219       weight = weight_isometric;
220     }
221 
222     axe = !axe;
223 
224     source_map = alt_int_map;
225     target_map = int_map;
226 
227   } while (!axe);
228 
229   FC_FREE(alt_int_map);
230 }
231 
232 /* These arrays are indexed by continent number (or negative of the
233  * ocean number) so the 0th element is unused and the array is 1 element
234  * larger than you'd expect.
235  *
236  * The lake surrounders array tells how many land continents surround each
237  * ocean (or -1 if the ocean touches more than one continent).
238  *
239  * The _sizes arrays give the sizes (in tiles) of each continent and
240  * ocean.
241  */
242 static Continent_id *lake_surrounders = NULL;
243 static int *continent_sizes = NULL;
244 static int *ocean_sizes = NULL;
245 
246 /**************************************************************************
247   Calculate lake_surrounders[] array
248 **************************************************************************/
recalculate_lake_surrounders(void)249 static void recalculate_lake_surrounders(void)
250 {
251   const size_t size = (game.map.num_oceans + 1) * sizeof(*lake_surrounders);
252 
253   lake_surrounders = fc_realloc(lake_surrounders, size);
254   memset(lake_surrounders, 0, size);
255 
256   whole_map_iterate(ptile) {
257     const struct terrain *pterrain = tile_terrain(ptile);
258     Continent_id cont = tile_continent(ptile);
259 
260     if (T_UNKNOWN == pterrain) {
261       continue;
262     }
263 
264     if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
265       adjc_iterate(ptile, tile2) {
266         Continent_id cont2 = tile_continent(tile2);
267 	if (is_ocean_tile(tile2)) {
268 	  if (lake_surrounders[-cont2] == 0) {
269 	    lake_surrounders[-cont2] = cont;
270 	  } else if (lake_surrounders[-cont2] != cont) {
271 	    lake_surrounders[-cont2] = -1;
272 	  }
273 	}
274       } adjc_iterate_end;
275     }
276   } whole_map_iterate_end;
277 }
278 
279 /*******************************************************************************
280   Number this tile and nearby tiles with the specified continent number 'nr'.
281   Due to the number of recursion for large maps a non-recursive algorithm is
282   utilised.
283 
284   is_land tells us whether we are assigning continent numbers or ocean
285   numbers.
286 *******************************************************************************/
assign_continent_flood(struct tile * ptile,bool is_land,int nr)287 static void assign_continent_flood(struct tile *ptile, bool is_land, int nr)
288 {
289   struct tile_list *tlist = NULL;
290   const struct terrain *pterrain = NULL;
291 
292   fc_assert_ret(ptile != NULL);
293 
294   pterrain = tile_terrain(ptile);
295   /* Check if the initial tile is a valid tile for continent / ocean. */
296   fc_assert_ret(tile_continent(ptile) == 0
297                 && T_UNKNOWN != pterrain
298                 && XOR(is_land, terrain_type_terrain_class(pterrain) == TC_OCEAN));
299 
300   /* Create tile list and insert the initial tile. */
301   tlist = tile_list_new();
302   tile_list_append(tlist, ptile);
303 
304   while (tile_list_size(tlist) > 0) {
305     /* Iterate over all unchecked tiles. */
306     tile_list_iterate(tlist, ptile2) {
307       /* Iterate over the adjacent tiles. */
308       adjc_iterate(ptile2, ptile3) {
309         pterrain = tile_terrain(ptile3);
310 
311         /* Check if it is a valid tile for continent / ocean. */
312         if (tile_continent(ptile3) != 0
313             || T_UNKNOWN == pterrain
314             || !XOR(is_land, terrain_type_terrain_class(pterrain) == TC_OCEAN)) {
315           continue;
316         }
317 
318         /* Add the tile to the list of tiles to check. */
319         if (!tile_list_search(tlist, ptile3)) {
320           tile_list_append(tlist, ptile3);
321         }
322       } adjc_iterate_end;
323 
324       /* Set the continent data and remove the tile from the list. */
325       tile_set_continent(ptile2, nr);
326       tile_list_remove(tlist, ptile2);
327       /* count the tile */
328       if (nr < 0) {
329         ocean_sizes[-nr]++;
330       } else {
331         continent_sizes[nr]++;
332       }
333     } tile_list_iterate_end;
334   }
335 
336   tile_list_destroy(tlist);
337 }
338 
339 /**************************************************************************
340   Regenerate all oceanic tiles for small water bodies as lakes.
341   Assumes assign_continent_numbers() and recalculate_lake_surrounders()
342   have already been done!
343   FIXME: insufficiently generalized, use terrain property.
344 **************************************************************************/
regenerate_lakes(void)345 void regenerate_lakes(void)
346 {
347   struct terrain *lake_for_ocean[2][game.map.num_oceans];
348 
349   {
350     struct terrain *lakes[2][5];
351     int num_laketypes[2] = { 0, 0 };
352     int i;
353 
354     terrain_type_iterate(pterr) {
355       if (terrain_has_flag(pterr, TER_FRESHWATER)
356           && !terrain_has_flag(pterr, TER_NOT_GENERATED)) {
357         int frozen = terrain_has_flag(pterr, TER_FROZEN);
358 
359         if (num_laketypes[frozen] < ARRAY_SIZE(lakes[frozen])) {
360           lakes[frozen][num_laketypes[frozen]++] = pterr;
361         } else {
362           log_verbose("Ruleset has more than %d %s lake types, ignoring %s",
363                       (int) ARRAY_SIZE(lakes[frozen]),
364                       frozen ? "frozen" : "unfrozen",
365                       terrain_rule_name(pterr));
366         }
367       }
368     } terrain_type_iterate_end;
369 
370     /* We don't want to generate any boundaries between fresh and
371      * non-fresh water.
372      * If there are no unfrozen lake types, just give up.
373      * Else if there are no frozen lake types, use unfrozen lake instead.
374      * If both are available, preserve frozenness of previous terrain. */
375     if (num_laketypes[0] == 0) {
376       return;
377     } else if (num_laketypes[1] == 0) {
378       for (i = 0; i < game.map.num_oceans; i++) {
379         lake_for_ocean[0][i] = lake_for_ocean[1][i]
380           = lakes[0][fc_rand(num_laketypes[0])];
381       }
382     } else {
383       for (i = 0; i < game.map.num_oceans; i++) {
384         int frozen;
385         for (frozen = 0; frozen < 2; frozen++) {
386           lake_for_ocean[frozen][i]
387             = lakes[frozen][fc_rand(num_laketypes[frozen])];
388         }
389       }
390     }
391   }
392 
393   whole_map_iterate(ptile) {
394     struct terrain *pterrain = tile_terrain(ptile);
395     Continent_id here = tile_continent(ptile);
396 
397     if (T_UNKNOWN == pterrain) {
398       continue;
399     }
400     if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
401       continue;
402     }
403     if (0 < lake_surrounders[-here]) {
404       if (terrain_control.lake_max_size >= ocean_sizes[-here]) {
405         int frozen = terrain_has_flag(pterrain, TER_FROZEN);
406         tile_change_terrain(ptile, lake_for_ocean[frozen][-here-1]);
407       }
408     }
409   } whole_map_iterate_end;
410 }
411 
412 /**************************************************************************
413   Get continent surrounding lake, or -1 if there is multiple continents.
414 **************************************************************************/
get_lake_surrounders(Continent_id cont)415 int get_lake_surrounders(Continent_id cont)
416 {
417   return lake_surrounders[-cont];
418 }
419 
420 /*************************************************************************
421   Return size in tiles of the given continent(not ocean)
422 *************************************************************************/
get_continent_size(Continent_id id)423 int get_continent_size(Continent_id id)
424 {
425   fc_assert_ret_val(id > 0, -1);
426   return continent_sizes[id];
427 }
428 
429 /*************************************************************************
430   Return size in tiles of the given ocean. You should use positive ocean
431   number.
432 *************************************************************************/
get_ocean_size(Continent_id id)433 int get_ocean_size(Continent_id id)
434 {
435   fc_assert_ret_val(id > 0, -1);
436   return ocean_sizes[id];
437 }
438 
439 /**************************************************************************
440   Assigns continent and ocean numbers to all tiles, and set
441   map.num_continents and map.num_oceans.  Recalculates continent and
442   ocean sizes, and lake_surrounders[] arrays.
443 
444   Continents have numbers 1 to map.num_continents _inclusive_.
445   Oceans have (negative) numbers -1 to -map.num_oceans _inclusive_.
446 **************************************************************************/
assign_continent_numbers(void)447 void assign_continent_numbers(void)
448 {
449   /* Initialize */
450   game.map.num_continents = 0;
451   game.map.num_oceans = 0;
452 
453   whole_map_iterate(ptile) {
454     tile_set_continent(ptile, 0);
455   } whole_map_iterate_end;
456 
457   /* Assign new numbers */
458   whole_map_iterate(ptile) {
459     const struct terrain *pterrain = tile_terrain(ptile);
460 
461     if (tile_continent(ptile) != 0) {
462       /* Already assigned. */
463       continue;
464     }
465 
466     if (T_UNKNOWN == pterrain) {
467       continue; /* Can't assign this. */
468     }
469 
470     if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
471       game.map.num_continents++;
472       continent_sizes = fc_realloc(continent_sizes,
473                            (game.map.num_continents + 1) * sizeof(*continent_sizes));
474       continent_sizes[game.map.num_continents] = 0;
475       assign_continent_flood(ptile, TRUE, game.map.num_continents);
476     } else {
477       game.map.num_oceans++;
478       ocean_sizes = fc_realloc(ocean_sizes,
479                        (game.map.num_oceans + 1) * sizeof(*ocean_sizes));
480       ocean_sizes[game.map.num_oceans] = 0;
481       assign_continent_flood(ptile, FALSE, -game.map.num_oceans);
482     }
483   } whole_map_iterate_end;
484 
485   recalculate_lake_surrounders();
486 
487   log_verbose("Map has %d continents and %d oceans",
488               game.map.num_continents, game.map.num_oceans);
489 }
490 
491 /**************************************************************************
492   Return most shallow ocean terrain type. Prefers not to return freshwater
493   terrain, and will ignore 'frozen' rather than do so.
494 **************************************************************************/
most_shallow_ocean(bool frozen)495 struct terrain *most_shallow_ocean(bool frozen)
496 {
497   bool oceans = FALSE, frozenmatch = FALSE;
498   struct terrain *shallow = NULL;
499 
500   terrain_type_iterate(pterr) {
501     if (is_ocean(pterr) && !terrain_has_flag(pterr, TER_NOT_GENERATED)) {
502       bool nonfresh = !terrain_has_flag(pterr, TER_FRESHWATER);
503       bool frozen_ok = terrain_has_flag(pterr, TER_FROZEN) == frozen;
504 
505       if (!oceans && nonfresh) {
506         /* First ocean type seen, reset even if frozenness doesn't match */
507         oceans = TRUE;
508         shallow = pterr;
509         frozenmatch = frozen_ok;
510         continue;
511       } else if (oceans && !nonfresh) {
512         /* Dismiss any step backward on freshness */
513         continue;
514       }
515       if (!frozenmatch && frozen_ok) {
516         /* Prefer terrain that matches frozenness (as long as we don't go
517          * backwards on freshness) */
518         frozenmatch = TRUE;
519         shallow = pterr;
520         continue;
521       } else if (frozenmatch && !frozen_ok) {
522         /* Dismiss any step backward on frozenness */
523         continue;
524       }
525       if (!shallow
526           || pterr->property[MG_OCEAN_DEPTH] <
527           shallow->property[MG_OCEAN_DEPTH]) {
528         shallow = pterr;
529       }
530     }
531   } terrain_type_iterate_end;
532 
533   return shallow;
534 }
535 
536 /**************************************************************************
537   Picks an ocean terrain to match the given depth.
538   Only considers terrains with/without Frozen flag depending on 'frozen'.
539   Return NULL when there is no available ocean.
540 **************************************************************************/
pick_ocean(int depth,bool frozen)541 struct terrain *pick_ocean(int depth, bool frozen)
542 {
543   struct terrain *best_terrain = NULL;
544   int best_match = TERRAIN_OCEAN_DEPTH_MAXIMUM;
545 
546   terrain_type_iterate(pterrain) {
547     if (terrain_type_terrain_class(pterrain) == TC_OCEAN
548         && TERRAIN_OCEAN_DEPTH_MINIMUM <= pterrain->property[MG_OCEAN_DEPTH]
549         && !!frozen == terrain_has_flag(pterrain, TER_FROZEN)
550         && !terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
551       int match = abs(depth - pterrain->property[MG_OCEAN_DEPTH]);
552 
553       if (best_match > match) {
554 	best_match = match;
555 	best_terrain = pterrain;
556       }
557     }
558   } terrain_type_iterate_end;
559 
560   return best_terrain;
561 }
562 
563 /**************************************************************************
564   Determines the minimal distance to the land.
565 **************************************************************************/
real_distance_to_land(const struct tile * ptile,int max)566 static int real_distance_to_land(const struct tile *ptile, int max)
567 {
568   square_dxy_iterate(ptile, max, atile, dx, dy) {
569     if (terrain_type_terrain_class(tile_terrain(atile)) != TC_OCEAN) {
570       return map_vector_to_real_distance(dx, dy);
571     }
572   } square_dxy_iterate_end;
573   return max + 1;
574 }
575 
576 /**************************************************************************
577   Determines what is the most popular ocean type arround (need 2/3 of the
578   adjcacent tiles).
579 **************************************************************************/
most_adjacent_ocean_type(const struct tile * ptile)580 static struct terrain *most_adjacent_ocean_type(const struct tile *ptile)
581 {
582   const int need = 2 * game.map.num_valid_dirs / 3;
583   int count;
584 
585   terrain_type_iterate(pterrain) {
586     if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
587       continue;
588     }
589 
590     count = 0;
591     adjc_iterate(ptile, atile) {
592       if (pterrain == tile_terrain(atile) && need <= ++count) {
593         return pterrain;
594       }
595     } adjc_iterate_end;
596   } terrain_type_iterate_end;
597   return NULL;
598 }
599 
600 /**************************************************************************
601   Makes a simple depth map for all ocean tiles based on their proximity
602   to any land tiles and reassignes ocean terrain types based on their
603   MG_OCEAN_DEPTH property values.
604 **************************************************************************/
smooth_water_depth(void)605 void smooth_water_depth(void)
606 {
607   const int OCEAN_DEPTH_STEP = 25;
608   const int OCEAN_DEPTH_RAND = 15;
609   const int OCEAN_DIST_MAX = TERRAIN_OCEAN_DEPTH_MAXIMUM / OCEAN_DEPTH_STEP;
610   struct terrain *ocean;
611   int dist;
612 
613   /* First, improve the coasts. */
614   whole_map_iterate(ptile) {
615     if (terrain_type_terrain_class(tile_terrain(ptile)) != TC_OCEAN) {
616       continue;
617     }
618 
619     dist = real_distance_to_land(ptile, OCEAN_DIST_MAX);
620     if (dist <= OCEAN_DIST_MAX) {
621       /* Overwrite the terrain (but preserve frozenness). */
622       ocean = pick_ocean(dist * OCEAN_DEPTH_STEP
623                          + fc_rand(OCEAN_DEPTH_RAND),
624                          terrain_has_flag(tile_terrain(ptile), TER_FROZEN));
625       if (NULL != ocean && ocean != tile_terrain(ptile)) {
626         log_debug("Replacing %s by %s at (%d, %d) "
627                   "to have shallow ocean on coast.",
628                   terrain_rule_name(tile_terrain(ptile)),
629                   terrain_rule_name(ocean), TILE_XY(ptile));
630         tile_set_terrain(ptile, ocean);
631       }
632     }
633   } whole_map_iterate_end;
634 
635   /* Now, try to have something more continuous. */
636   whole_map_iterate(ptile) {
637     if (terrain_type_terrain_class(tile_terrain(ptile)) != TC_OCEAN) {
638       continue;
639     }
640 
641     ocean = most_adjacent_ocean_type(ptile);
642     if (NULL != ocean && ocean != tile_terrain(ptile)) {
643       log_debug("Replacing %s by %s at (%d, %d) "
644                 "to smooth the ocean types.",
645                 terrain_rule_name(tile_terrain(ptile)),
646                 terrain_rule_name(ocean), TILE_XY(ptile));
647       tile_set_terrain(ptile, ocean);
648     }
649   } whole_map_iterate_end;
650 }
651 
652 /**************************************************************************
653   Free resources allocated by the generator.
654 **************************************************************************/
generator_free(void)655 void generator_free(void)
656 {
657   if (lake_surrounders != NULL) {
658     free(lake_surrounders);
659     lake_surrounders = NULL;
660   }
661   if (continent_sizes != NULL) {
662     free(continent_sizes);
663     continent_sizes = NULL;
664   }
665   if (ocean_sizes != NULL) {
666     free(ocean_sizes);
667     ocean_sizes = NULL;
668   }
669 }
670 
671 /****************************************************************************
672   Return a random terrain that has the specified flag.
673   Returns T_UNKNOWN when there is no matching terrain.
674 ****************************************************************************/
pick_terrain_by_flag(enum terrain_flag_id flag)675 struct terrain *pick_terrain_by_flag(enum terrain_flag_id flag)
676 {
677   bool has_flag[terrain_count()];
678   int count = 0;
679 
680   terrain_type_iterate(pterrain) {
681     if ((has_flag[terrain_index(pterrain)]
682          = (terrain_has_flag(pterrain, flag)
683             && !terrain_has_flag(pterrain, TER_NOT_GENERATED)))) {
684       count++;
685     }
686   } terrain_type_iterate_end;
687 
688   count = fc_rand(count);
689   terrain_type_iterate(pterrain) {
690     if (has_flag[terrain_index(pterrain)]) {
691       if (count == 0) {
692 	return pterrain;
693       }
694       count--;
695     }
696   } terrain_type_iterate_end;
697 
698   return T_UNKNOWN;
699 }
700 
701 
702 /****************************************************************************
703   Pick a terrain based on the target property and a property to avoid.
704 
705   If the target property is given, then all terrains with that property
706   will be considered and one will be picked at random based on the amount
707   of the property each terrain has.  If no target property is given all
708   terrains will be assigned equal likelihood.
709 
710   If the preferred property is given, only terrains with (some of) that
711   property will be chosen.
712 
713   If the avoid property is given, then any terrain with (any of) that
714   property will be avoided.
715 
716   This function must always return a valid terrain.
717 ****************************************************************************/
pick_terrain(enum mapgen_terrain_property target,enum mapgen_terrain_property prefer,enum mapgen_terrain_property avoid)718 struct terrain *pick_terrain(enum mapgen_terrain_property target,
719                              enum mapgen_terrain_property prefer,
720                              enum mapgen_terrain_property avoid)
721 {
722   int sum = 0;
723 
724   /* Find the total weight. */
725   terrain_type_iterate(pterrain) {
726     if (!terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
727       if (avoid != MG_UNUSED && pterrain->property[avoid] > 0) {
728         continue;
729       }
730       if (prefer != MG_UNUSED && pterrain->property[prefer] == 0) {
731         continue;
732       }
733 
734       if (target != MG_UNUSED) {
735         sum += pterrain->property[target];
736       } else {
737         sum++;
738       }
739     }
740   } terrain_type_iterate_end;
741 
742   /* Now pick. */
743   sum = fc_rand(sum);
744 
745   /* Finally figure out which one we picked. */
746   terrain_type_iterate(pterrain) {
747     if (!terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
748       int property;
749 
750       if (avoid != MG_UNUSED && pterrain->property[avoid] > 0) {
751         continue;
752       }
753       if (prefer != MG_UNUSED && pterrain->property[prefer] == 0) {
754         continue;
755       }
756 
757       if (target != MG_UNUSED) {
758         property = pterrain->property[target];
759       } else {
760         property = 1;
761       }
762       if (sum < property) {
763         return pterrain;
764       }
765       sum -= property;
766     }
767   } terrain_type_iterate_end;
768 
769   /* This can happen with sufficient quantities of preferred and avoided
770    * characteristics.  Drop a requirement and try again. */
771   if (prefer != MG_UNUSED) {
772     log_debug("pick_terrain(target: %s, [dropping prefer: %s], avoid: %s)",
773               mapgen_terrain_property_name(target),
774               mapgen_terrain_property_name(prefer),
775               mapgen_terrain_property_name(avoid));
776     return pick_terrain(target, MG_UNUSED, avoid);
777   } else if (avoid != MG_UNUSED) {
778     log_debug("pick_terrain(target: %s, prefer: MG_UNUSED, [dropping avoid: %s])",
779               mapgen_terrain_property_name(target),
780               mapgen_terrain_property_name(avoid));
781     return pick_terrain(target, prefer, MG_UNUSED);
782   } else {
783     log_debug("pick_terrain([dropping target: %s], prefer: MG_UNUSED, avoid: MG_UNUSED)",
784               mapgen_terrain_property_name(target));
785     return pick_terrain(MG_UNUSED, prefer, avoid);
786   }
787 }
788