Freeciv-3.2
Loading...
Searching...
No Matches
mapgen_utils.c
Go to the documentation of this file.
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 "mapgen_utils.h"
30
31/**************************************************************************
32 Map that contains, according to circumstances, information on whether
33 we have already placed terrain (special, hut) here.
34**************************************************************************/
35static bool *placed_map;
36
37/**********************************************************************/
41{
42 return placed_map != NULL;
43}
44
45/**********************************************************************/
54
55/**********************************************************************/
64
65
66#define pmap(_tile) (placed_map[tile_index(_tile)])
67
68/**********************************************************************/
71bool not_placed(const struct tile *ptile)
72{
73 return !pmap(ptile);
74}
75
76/**********************************************************************/
79void map_set_placed(struct tile *ptile)
80{
81 pmap(ptile) = TRUE;
82}
83
84/**********************************************************************/
87void map_unset_placed(struct tile *ptile)
88{
89 pmap(ptile) = FALSE;
90}
91
92/**********************************************************************/
96{
97 whole_map_iterate(&(wld.map), ptile) {
98 if (is_ocean_tile(ptile)) {
99 map_set_placed(ptile);
100 }
102}
103
104/**********************************************************************/
107void set_placed_near_pos(struct tile *ptile, int dist)
108{
109 square_iterate(&(wld.map), ptile, dist, tile1) {
112}
113
114/**********************************************************************/
124 int int_map_max, void *data,
125 bool (*filter)(const struct tile *ptile,
126 const void *data))
127{
129 int minval = 0, maxval = 0, total = 0;
130 bool first = TRUE;
131
132 /* Determine minimum and maximum value. */
133 whole_map_iterate_filtered(ptile, data, filter) {
134 if (first) {
135 minval = int_map[tile_index(ptile)];
136 maxval = int_map[tile_index(ptile)];
137 } else {
138 maxval = MAX(maxval, int_map[tile_index(ptile)]);
139 minval = MIN(minval, int_map[tile_index(ptile)]);
140 }
141 first = FALSE;
142 total++;
144
145 if (total == 0) {
146 return;
147 }
148
149 {
150 int const size = 1 + maxval - minval;
151 int i, count = 0, frequencies[size];
152
154
155 /* Translate value so the minimum value is 0
156 and count the number of occurrences of all values to initialize the
157 frequencies[] */
158 whole_map_iterate_filtered(ptile, data, filter) {
159 int_map[tile_index(ptile)] -= minval;
160 frequencies[int_map[tile_index(ptile)]]++;
162
163 /* create the linearize function as "incremental" frequencies */
164 for (i = 0; i < size; i++) {
165 count += frequencies[i];
166 frequencies[i] = int_map_min + (count * int_map_delta) / total;
167 }
168
169 /* apply the linearize function */
170 whole_map_iterate_filtered(ptile, data, filter) {
173 }
174}
175
176/**********************************************************************/
179bool is_normal_nat_pos(int x, int y)
180{
181 NATIVE_TO_MAP_POS(&x, &y, x, y);
182 return is_normal_map_pos(x, y);
183}
184
185/**********************************************************************/
192{
193 static const float weight_standard[5] = { 0.13, 0.19, 0.37, 0.19, 0.13 };
194 static const float weight_isometric[5] = { 0.15, 0.21, 0.29, 0.21, 0.15 };
195 const float *weight;
196 bool axe = TRUE;
197 int *target_map, *source_map;
199
201
202 weight = weight_standard;
205
206 do {
207 whole_map_iterate(&(wld.map), ptile) {
208 float N = 0, D = 0;
209
210 axis_iterate(&(wld.map), ptile, pnear, i, 2, axe) {
211 D += weight[i + 2];
212 N += weight[i + 2] * source_map[tile_index(pnear)];
214 if (zeroes_at_edges) {
215 D = 1;
216 }
217 target_map[tile_index(ptile)] = (float)N / D;
219
220 if (MAP_IS_ISOMETRIC) {
221 weight = weight_isometric;
222 }
223
224 axe = !axe;
225
228
229 } while (!axe);
230
232}
233
234/* These arrays are indexed by continent number (or negative of the
235 * ocean number) so the 0th element is unused and the array is 1 element
236 * larger than you'd expect.
237 *
238 * The lake surrounders array tells how many land continents surround each
239 * ocean (or -1 if the ocean touches more than one continent).
240 *
241 * The _sizes arrays give the sizes (in tiles) of each continent and
242 * ocean.
243 */
245static int *continent_sizes = NULL;
246static int *ocean_sizes = NULL;
247
248/**********************************************************************/
252{
253 const size_t size = (wld.map.num_oceans + 1) * sizeof(*lake_surrounders);
254
257
258 whole_map_iterate(&(wld.map), ptile) {
259 const struct terrain *pterrain = tile_terrain(ptile);
260 Continent_id cont = tile_continent(ptile);
261
262 if (T_UNKNOWN == pterrain) {
263 continue;
264 }
265
266 if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
267 adjc_iterate(&(wld.map), ptile, tile2) {
269
270 if (is_ocean_tile(tile2)) {
271 if (lake_surrounders[-cont2] == 0) {
272 lake_surrounders[-cont2] = cont;
273 } else if (lake_surrounders[-cont2] != cont) {
274 lake_surrounders[-cont2] = -1;
275 }
276 }
278 }
280}
281
282/**********************************************************************/
290static void assign_continent_flood(struct tile *ptile, bool is_land, int nr)
291{
292 struct tile_list *tlist = NULL;
293 const struct terrain *pterrain;
294
295 fc_assert_ret(ptile != NULL);
296
297#ifndef FREECIV_NDEBUG
298 pterrain = tile_terrain(ptile);
299#endif
300 /* Check if the initial tile is a valid tile for continent / ocean. */
301 fc_assert_ret(tile_continent(ptile) == 0
302 && T_UNKNOWN != pterrain
304
305 /* Create tile list and insert the initial tile. */
307 tile_list_append(tlist, ptile);
308
309 while (tile_list_size(tlist) > 0) {
310 struct tile *ptile2 = tile_list_get(tlist, 0);
311
312 /* Iterate over the adjacent tiles. */
314 pterrain = tile_terrain(ptile3);
315
316 /* Check if it is a valid tile for continent / ocean. */
317 if (tile_continent(ptile3) != 0
318 || T_UNKNOWN == pterrain
319 || !XOR(is_land, terrain_type_terrain_class(pterrain) == TC_OCEAN)) {
320 continue;
321 }
322
323 /* Add the tile to the list of tiles to check. */
326 }
328
329 /* Set the continent data and remove the tile from the list. */
332
333 /* Count the tile */
334 if (nr < 0) {
335 ocean_sizes[-nr]++;
336 } else {
338 }
339 }
340
342}
343
344/**********************************************************************/
351{
353
354 {
355 struct terrain *lakes[2][5];
356 int num_laketypes[2] = { 0, 0 };
357 int i;
358
363
366 } else {
367 log_verbose("Ruleset has more than %d %s lake types, ignoring %s",
368 (int) ARRAY_SIZE(lakes[frozen]),
369 frozen ? "frozen" : "unfrozen",
371 }
372 }
374
375 /* We don't want to generate any boundaries between fresh and
376 * non-fresh water.
377 * If there are no unfrozen lake types, just give up.
378 * Else if there are no frozen lake types, use unfrozen lake instead.
379 * If both are available, preserve frozenness of previous terrain. */
380 if (num_laketypes[0] == 0) {
381 return;
382 } else if (num_laketypes[1] == 0) {
383 for (i = 0; i < wld.map.num_oceans; i++) {
385 = lakes[0][fc_rand(num_laketypes[0])];
386 }
387 } else {
388 for (i = 0; i < wld.map.num_oceans; i++) {
389 int frozen;
390 for (frozen = 0; frozen < 2; frozen++) {
393 }
394 }
395 }
396 }
397
398 whole_map_iterate(&(wld.map), ptile) {
399 struct terrain *pterrain = tile_terrain(ptile);
401
402 if (T_UNKNOWN == pterrain) {
403 continue;
404 }
405 if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
406 continue;
407 }
408 if (0 < lake_surrounders[-here]) {
409 if (terrain_control.lake_max_size >= ocean_sizes[-here]) {
410 int frozen = terrain_has_flag(pterrain, TER_FROZEN);
412 }
413 }
415}
416
417/**********************************************************************/
421{
422 return lake_surrounders[-cont];
423}
424
425/**********************************************************************/
429{
430 fc_assert_ret_val(id > 0, -1);
431 return continent_sizes[id];
432}
433
434/**********************************************************************/
439{
440 fc_assert_ret_val(id > 0, -1);
441 return ocean_sizes[id];
442}
443
444/**********************************************************************/
453{
454 /* Initialize */
456 wld.map.num_oceans = 0;
457
458 whole_map_iterate(&(wld.map), ptile) {
459 tile_set_continent(ptile, 0);
461
462 /* Assign new numbers */
463 whole_map_iterate(&(wld.map), ptile) {
464 const struct terrain *pterrain = tile_terrain(ptile);
465
466 if (tile_continent(ptile) != 0) {
467 /* Already assigned. */
468 continue;
469 }
470
471 if (T_UNKNOWN == pterrain) {
472 continue; /* Can't assign this. */
473 }
474
475 if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
478 (wld.map.num_continents + 1) * sizeof(*continent_sizes));
481 } else {
484 (wld.map.num_oceans + 1) * sizeof(*ocean_sizes));
487 }
489
491
492 log_verbose("Map has %d continents and %d oceans",
494}
495
496/**********************************************************************/
501{
502 bool oceans = FALSE, frozenmatch = FALSE;
503 struct terrain *shallow = NULL;
504
509
510 if (!oceans && nonfresh) {
511 /* First ocean type seen, reset even if frozenness doesn't match */
512 oceans = TRUE;
513 shallow = pterr;
515 continue;
516 } else if (oceans && !nonfresh) {
517 /* Dismiss any step backward on freshness */
518 continue;
519 }
520 if (!frozenmatch && frozen_ok) {
521 /* Prefer terrain that matches frozenness (as long as we don't go
522 * backwards on freshness) */
524 shallow = pterr;
525 continue;
526 } else if (frozenmatch && !frozen_ok) {
527 /* Dismiss any step backward on frozenness */
528 continue;
529 }
530 if (!shallow
531 || pterr->property[MG_OCEAN_DEPTH] <
532 shallow->property[MG_OCEAN_DEPTH]) {
533 shallow = pterr;
534 }
535 }
537
538 return shallow;
539}
540
541/**********************************************************************/
546struct terrain *pick_ocean(int depth, bool frozen)
547{
548 struct terrain *best_terrain = NULL;
550
551 terrain_type_iterate(pterrain) {
552 if (terrain_type_terrain_class(pterrain) == TC_OCEAN
554 && !!frozen == terrain_has_flag(pterrain, TER_FROZEN)
555 && !terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
556 int match = abs(depth - pterrain->property[MG_OCEAN_DEPTH]);
557
558 if (best_match > match) {
559 best_match = match;
560 best_terrain = pterrain;
561 }
562 }
564
565 return best_terrain;
566}
567
568/**********************************************************************/
571static int real_distance_to_land(const struct tile *ptile, int max)
572{
573 square_dxy_iterate(&(wld.map), ptile, max, atile, dx, dy) {
575 return map_vector_to_real_distance(dx, dy);
576 }
578
579 return max + 1;
580}
581
582/**********************************************************************/
586static struct terrain *most_adjacent_ocean_type(const struct tile *ptile)
587{
588 const int need = 2 * wld.map.num_valid_dirs / 3;
589 int count;
590
591 terrain_type_iterate(pterrain) {
592 if (terrain_type_terrain_class(pterrain) != TC_OCEAN) {
593 continue;
594 }
595
596 count = 0;
597 adjc_iterate(&(wld.map), ptile, atile) {
598 if (pterrain == tile_terrain(atile) && need <= ++count) {
599 return pterrain;
600 }
603
604 return NULL;
605}
606
607/**********************************************************************/
613{
614 const int OCEAN_DEPTH_STEP = 25;
615 const int OCEAN_DEPTH_RAND = 15;
617 struct terrain *ocean;
618 int dist;
619
620 /* First, improve the coasts. */
621 whole_map_iterate(&(wld.map), ptile) {
623 continue;
624 }
625
627 if (dist <= OCEAN_DIST_MAX) {
628 /* Overwrite the terrain (but preserve frozenness). */
629 ocean = pick_ocean(dist * OCEAN_DEPTH_STEP
632 if (NULL != ocean && ocean != tile_terrain(ptile)) {
633 log_debug("Replacing %s by %s at (%d, %d) "
634 "to have shallow ocean on coast.",
636 terrain_rule_name(ocean), TILE_XY(ptile));
637 tile_set_terrain(ptile, ocean);
638 }
639 }
641
642 /* Now, try to have something more continuous. */
643 whole_map_iterate(&(wld.map), ptile) {
645 continue;
646 }
647
648 ocean = most_adjacent_ocean_type(ptile);
649 if (NULL != ocean && ocean != tile_terrain(ptile)) {
650 log_debug("Replacing %s by %s at (%d, %d) "
651 "to smooth the ocean types.",
653 terrain_rule_name(ocean), TILE_XY(ptile));
654 tile_set_terrain(ptile, ocean);
655 }
657}
658
659/**********************************************************************/
663{
664 if (lake_surrounders != NULL) {
667 }
668 if (continent_sizes != NULL) {
671 }
672 if (ocean_sizes != NULL) {
675 }
676}
677
678/**********************************************************************/
683{
684 bool has_flag[terrain_count()];
685 int count = 0;
686
687 terrain_type_iterate(pterrain) {
688 if ((has_flag[terrain_index(pterrain)]
689 = (terrain_has_flag(pterrain, flag)
690 && !terrain_has_flag(pterrain, TER_NOT_GENERATED)))) {
691 count++;
692 }
694
695 count = fc_rand(count);
696 terrain_type_iterate(pterrain) {
697 if (has_flag[terrain_index(pterrain)]) {
698 if (count == 0) {
699 return pterrain;
700 }
701 count--;
702 }
704
705 return T_UNKNOWN;
706}
707
708
709/**********************************************************************/
726 enum mapgen_terrain_property prefer,
727 enum mapgen_terrain_property avoid)
728{
729 int sum = 0;
730
731 /* Find the total weight. */
732 terrain_type_iterate(pterrain) {
733 if (!terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
734 if (avoid != MG_UNUSED && pterrain->property[avoid] > 0) {
735 continue;
736 }
737 if (prefer != MG_UNUSED && pterrain->property[prefer] == 0) {
738 continue;
739 }
740
741 if (target != MG_UNUSED) {
742 sum += pterrain->property[target];
743 } else {
744 sum++;
745 }
746 }
748
749 /* Now pick. */
750 sum = fc_rand(sum);
751
752 /* Finally figure out which one we picked. */
753 terrain_type_iterate(pterrain) {
754 if (!terrain_has_flag(pterrain, TER_NOT_GENERATED)) {
755 int property;
756
757 if (avoid != MG_UNUSED && pterrain->property[avoid] > 0) {
758 continue;
759 }
760 if (prefer != MG_UNUSED && pterrain->property[prefer] == 0) {
761 continue;
762 }
763
764 if (target != MG_UNUSED) {
765 property = pterrain->property[target];
766 } else {
767 property = 1;
768 }
769 if (sum < property) {
770 return pterrain;
771 }
772 sum -= property;
773 }
775
776 /* This can happen with sufficient quantities of preferred and avoided
777 * characteristics. Drop a requirement and try again. */
778 if (prefer != MG_UNUSED) {
779 log_debug("pick_terrain(target: %s, [dropping prefer: %s], avoid: %s)",
783 return pick_terrain(target, MG_UNUSED, avoid);
784 } else if (avoid != MG_UNUSED) {
785 log_debug("pick_terrain(target: %s, prefer: MG_UNUSED, [dropping avoid: %s])",
788 return pick_terrain(target, prefer, MG_UNUSED);
789 } else {
790 log_debug("pick_terrain([dropping target: %s], prefer: MG_UNUSED, avoid: MG_UNUSED)",
792 return pick_terrain(MG_UNUSED, prefer, avoid);
793 }
794}
795
796/**********************************************************************/
800struct extra_type *pick_resource(const struct terrain *pterrain)
801{
802 int freq_sum = 0;
803 struct extra_type *result = NULL;
804
805 fc_assert_ret_val(NULL != pterrain, NULL);
806
808 /* This is a standard way to get a weighted random element from
809 * pterrain->resources with weights from pterrain->resource_freq,
810 * without computing its length or total weight in advance.
811 * Note that if *(pterrain->resources) == NULL,
812 * then this loop is a no-op. */
813
814 if (res->generated && freq > 0) {
815 freq_sum += freq;
816 if (freq > fc_rand(freq_sum)) {
817 result = res;
818 }
819 }
821
822 return result;
823}
char * incite_cost
Definition comments.c:75
int int id
Definition editgui_g.h:28
signed short Continent_id
Definition fc_types.h:372
#define MG_UNUSED
struct world wld
Definition game.c:63
#define fc_assert_ret(condition)
Definition log.h:191
#define log_verbose(message,...)
Definition log.h:109
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_debug(message,...)
Definition log.h:115
bool is_normal_map_pos(int x, int y)
Definition map.c:963
struct terrain_misc terrain_control
Definition map.c:69
int map_vector_to_real_distance(int dx, int dy)
Definition map.c:578
#define adjc_iterate_end
Definition map.h:433
#define MAP_INDEX_SIZE
Definition map.h:137
#define square_iterate(nmap, center_tile, radius, tile_itr)
Definition map.h:391
#define MAP_IS_ISOMETRIC
Definition map.h:42
#define adjc_iterate(nmap, center_tile, itr_tile)
Definition map.h:428
#define NATIVE_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition map.h:166
#define square_iterate_end
Definition map.h:394
#define whole_map_iterate(_map, _tile)
Definition map.h:545
#define square_dxy_iterate(nmap, center_tile, radius, tile_itr, dx_itr, dy_itr)
Definition map.h:381
#define whole_map_iterate_end
Definition map.h:554
#define square_dxy_iterate_end
Definition map.h:384
int get_continent_size(Continent_id id)
struct extra_type * pick_resource(const struct terrain *pterrain)
static struct terrain * most_adjacent_ocean_type(const struct tile *ptile)
static int * continent_sizes
static int * ocean_sizes
void smooth_int_map(int *int_map, bool zeroes_at_edges)
void set_all_ocean_tiles_placed(void)
void create_placed_map(void)
static int real_distance_to_land(const struct tile *ptile, int max)
#define pmap(_tile)
static bool * placed_map
void map_set_placed(struct tile *ptile)
void destroy_placed_map(void)
void adjust_int_map_filtered(int *int_map, int int_map_min, int int_map_max, void *data, bool(*filter)(const struct tile *ptile, const void *data))
bool not_placed(const struct tile *ptile)
struct terrain * pick_terrain_by_flag(enum terrain_flag_id flag)
void assign_continent_numbers(void)
static Continent_id * lake_surrounders
void regenerate_lakes(void)
static void recalculate_lake_surrounders(void)
void set_placed_near_pos(struct tile *ptile, int dist)
int get_ocean_size(Continent_id id)
void generator_free(void)
void smooth_water_depth(void)
bool placed_map_is_initialized(void)
bool is_normal_nat_pos(int x, int y)
struct terrain * pick_terrain(enum mapgen_terrain_property target, enum mapgen_terrain_property prefer, enum mapgen_terrain_property avoid)
struct terrain * most_shallow_ocean(bool frozen)
struct terrain * pick_ocean(int depth, bool frozen)
static void assign_continent_flood(struct tile *ptile, bool is_land, int nr)
void map_unset_placed(struct tile *ptile)
int get_lake_surrounders(Continent_id cont)
#define whole_map_iterate_filtered(_tile, pdata, pfilter)
#define axis_iterate(nmap, center_tile, _tile, _index, dist, is_X_axis)
#define whole_map_iterate_filtered_end
#define axis_iterate_end
#define fc_calloc(n, esz)
Definition mem.h:38
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_realloc(ptr, sz)
Definition mem.h:36
#define fc_malloc(sz)
Definition mem.h:34
#define fc_rand(_size)
Definition rand.h:56
#define INITIALIZE_ARRAY(array, size, value)
Definition shared.h:101
#define ARRAY_SIZE(x)
Definition shared.h:85
#define MIN(x, y)
Definition shared.h:55
#define MAX(x, y)
Definition shared.h:54
#define XOR(p, q)
Definition shared.h:71
size_t size
Definition specvec.h:72
struct sprite int int y
Definition sprite_g.h:31
struct sprite int x
Definition sprite_g.h:31
Continent_id num_continents
Definition map_types.h:81
int num_valid_dirs
Definition map_types.h:75
Continent_id num_oceans
Definition map_types.h:82
int property[MG_COUNT]
Definition terrain.h:253
Definition tile.h:50
struct civ_map map
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
Terrain_type_id terrain_count(void)
Definition terrain.c:118
Terrain_type_id terrain_index(const struct terrain *pterrain)
Definition terrain.c:138
const char * terrain_rule_name(const struct terrain *pterrain)
Definition terrain.c:247
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
Definition terrain.c:582
#define terrain_type_iterate(_p)
Definition terrain.h:373
#define T_UNKNOWN
Definition terrain.h:57
#define is_ocean(pterrain)
Definition terrain.h:301
#define is_ocean_tile(ptile)
Definition terrain.h:303
#define terrain_type_iterate_end
Definition terrain.h:379
#define TERRAIN_OCEAN_DEPTH_MAXIMUM
Definition terrain.h:255
#define terrain_has_flag(terr, flag)
Definition terrain.h:283
#define terrain_resources_iterate_end
Definition terrain.h:401
#define terrain_resources_iterate(pterrain, _res, _freq)
Definition terrain.h:392
void tile_set_terrain(struct tile *ptile, struct terrain *pterrain)
Definition tile.c:124
void tile_change_terrain(struct tile *ptile, struct terrain *pterrain)
Definition tile.c:496
void tile_set_continent(struct tile *ptile, Continent_id val)
Definition tile.c:382
#define tile_index(_pt_)
Definition tile.h:88
#define tile_terrain(_tile)
Definition tile.h:110
#define TILE_XY(ptile)
Definition tile.h:43
#define tile_continent(_tile)
Definition tile.h:92