Freeciv21
Develop your civilization from humble roots to a global empire
fracture_map.cpp
Go to the documentation of this file.
1 /*
2  ____ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3  / \__ contributors. This file is part of Freeciv21.
4 |\ / @ \ Freeciv21 is free software: you can redistribute it
5 \ \_______| \ .:|> and/or modify it under the terms of the GNU
6  \ ##| | \__/ General Public License as published by the Free
7  | ####\__/ \ Software Foundation, either version 3 of the License,
8  / / ## \| or (at your option) any later version.
9  / /__________\ \ You should have received a copy of the
10  L_JJ \__JJ GNU General Public License along with Freeciv21.
11  If not, see https://www.gnu.org/licenses/.
12  */
13 // utility
14 #include "rand.h"
15 
16 // common
17 #include "map.h"
18 
19 /* server/generator */
20 #include "height_map.h"
21 #include "mapgen_topology.h"
22 #include "mapgen_utils.h"
23 
24 #include "fracture_map.h"
25 
26 static void circle_bresenham(int xc, int yc, int r, int nn);
27 static void fmfill(int x, int y, int c, int r);
28 static int local_ave_elevation(struct tile *ptile);
29 
30 int num_landmass = 50;
31 
32 typedef struct {
33  int x;
34  int y;
35  bool edge;
36 } map_point;
37 
38 typedef struct {
39  int minX, minY;
40  int maxX, maxY;
41  int elevation;
42 } map_landmass;
43 
44 // Landmass: a chunk of rock with common properties
47 
52 {
53  int nn, mm;
54  int rad;
55  int x, y;
56  struct tile *ptile1;
57 
58  const bool wrapx = wld.map.topology_id & TF_WRAPX,
59  wrapy = wld.map.topology_id & TF_WRAPY;
60 
61  /* Calculate the mountain level. map.server.mountains specifies the
62  * percentage of land that is turned into hills and mountains. */
64  * (100 - wld.map.server.steepness))
65  / 100
67 
68  /* For larger maps, increase the number of landmasses - makes the map more
69  * interesting */
70  num_landmass = 20 + 15 * get_sqsize();
71  landmass =
72  new map_landmass[wld.map.xsize / 2 + wld.map.ysize / 2 + num_landmass];
74  new map_point[wld.map.xsize / 2 + wld.map.ysize / 2 + num_landmass];
75  height_map = new int[MAP_INDEX_SIZE];
76 
77  /* Setup a whole bunch of landmasses along the view bordere. These will be
78  sunken to create ocean terrain.*/
79  nn = 0;
80  if (!wrapy) {
81  for (x = 3; x < wld.map.xsize; x += 5, nn++) {
82  fracture_points[nn].x = x;
83  fracture_points[nn].y = 3;
84  fracture_points[nn].edge = true;
85  }
86  for (x = 3; x < wld.map.xsize; x += 5, nn++) {
87  fracture_points[nn].x = x;
88  fracture_points[nn].y = wld.map.ysize - 3;
89  fracture_points[nn].edge = true;
90  }
91  }
92  if (!wrapx) {
93  for (y = 3; y < wld.map.ysize; y += 5, nn++) {
94  fracture_points[nn].x = 3;
95  fracture_points[nn].y = y;
96  fracture_points[nn].edge = true;
97  }
98  for (y = 3; y < wld.map.ysize; y += 5, nn++) {
99  fracture_points[nn].x = wld.map.xsize - 3;
100  fracture_points[nn].y = y;
101  fracture_points[nn].edge = true;
102  }
103  }
104 
105  // pick remaining points randomly, but not too close to non-wrapping edges
106  mm = nn;
107  {
108  const int minx = wrapx ? 0 : 3, maxx = wld.map.xsize - (wrapx ? 0 : 6),
109  miny = wrapy ? 0 : 3, maxy = wld.map.ysize - (wrapy ? 0 : 6);
110  for (; nn < mm + num_landmass; nn++) {
111  fracture_points[nn].x = fc_rand(maxx) + minx;
112  fracture_points[nn].y = fc_rand(maxy) + miny;
113  fracture_points[nn].edge = false;
114  }
115  }
116  for (nn = 0; nn < mm + num_landmass; nn++) {
117  landmass[nn].minX = wld.map.xsize - 1;
118  landmass[nn].minY = wld.map.ysize - 1;
119  landmass[nn].maxX = 0;
120  landmass[nn].maxY = 0;
121  x = fracture_points[nn].x;
122  y = fracture_points[nn].y;
123  ptile1 = native_pos_to_tile(&(wld.map), x, y);
124  ptile1->continent = nn + 1;
125  }
126 
127  // Assign a base elevation to the landmass
128  for (nn = 0; nn < mm + num_landmass; nn++) {
129  if (fracture_points[nn].edge) { // sink the border masses
130  landmass[nn].elevation = 0;
131  } else {
132  landmass[nn].elevation = fc_rand(1000);
133  }
134  }
135 
136  /* Assign cells to landmass. Gradually expand the radius of the
137  fracture point. */
138  for (rad = 1; rad < (wld.map.xsize >> 1); rad++) {
139  for (nn = 0; nn < mm + num_landmass; nn++) {
141  nn + 1);
142  }
143  }
144 
145  // put in some random fuzz
146  whole_map_iterate(&(wld.map), ptile)
147  {
148  if (hmap(ptile) > hmap_shore_level) {
149  hmap(ptile) = hmap(ptile) + fc_rand(4) - 2;
150  }
151  if (hmap(ptile) <= hmap_shore_level) {
152  hmap(ptile) = hmap_shore_level + 1;
153  }
154  }
156 
158  delete[] landmass;
159  delete[] fracture_points;
160 }
161 
167 static void circle_bresenham(int xc, int yc, int r, int nn)
168 {
169  int x = 0;
170  int y = r;
171  int p = 3 - 2 * r;
172 
173  if (!r) {
174  return;
175  }
176 
177  while (y >= x) { /* only formulate 1/8 of circle */
178  fmfill(xc - x, yc - y, nn, r); // upper left left
179  fmfill(xc - y, yc - x, nn, r); // upper upper left
180  fmfill(xc + y, yc - x, nn, r); // upper upper right
181  fmfill(xc + x, yc - y, nn, r); // upper right right
182  fmfill(xc - x, yc + y, nn, r); // lower left left
183  fmfill(xc - y, yc + x, nn, r); // lower lower left
184  fmfill(xc + y, yc + x, nn, r); // lower lower right
185  fmfill(xc + x, yc + y, nn, r); // lower right right
186  if (p < 0) {
187  p += 4 * x++ + 6;
188  } else {
189  p += 4 * (x++ - y--) + 10;
190  }
191  }
192 }
193 
198 static void fmfill(int x, int y, int c, int r)
199 {
200  int x_less, x_more, y_less, y_more;
201  struct tile *ptileXY;
202  struct tile *ptileX2Y;
203  struct tile *ptileX1Y;
204  struct tile *ptileXY2;
205  struct tile *ptileXY1;
206  struct tile *ptileX2Y1;
207  struct tile *ptileX2Y2;
208  struct tile *ptileX1Y2;
209  struct tile *ptileX1Y1;
210 
211  if (x < 0) {
212  x = wld.map.xsize + x;
213  } else if (x > wld.map.xsize) {
214  x = x - wld.map.xsize;
215  }
216  x_less = x - 1;
217  if (x_less < 0) {
218  x_less = wld.map.xsize - 1;
219  }
220  x_more = x + 1;
221  if (x_more >= wld.map.xsize) {
222  x_more = 0;
223  }
224  y_less = y - 1;
225  if (y_less < 0) {
226  y_less = wld.map.ysize - 1;
227  }
228  y_more = y + 1;
229  if (y_more >= wld.map.ysize) {
230  y_more = 0;
231  }
232 
233  if (y >= 0 && y < wld.map.ysize) {
234  ptileXY = native_pos_to_tile(&(wld.map), x, y);
235  ptileX2Y = native_pos_to_tile(&(wld.map), x_more, y);
236  ptileX1Y = native_pos_to_tile(&(wld.map), x_less, y);
237  ptileXY2 = native_pos_to_tile(&(wld.map), x, y_more);
238  ptileXY1 = native_pos_to_tile(&(wld.map), x, y_less);
239  ptileX2Y1 = native_pos_to_tile(&(wld.map), x_more, y_less);
240  ptileX2Y2 = native_pos_to_tile(&(wld.map), x_more, y_more);
241  ptileX1Y2 = native_pos_to_tile(&(wld.map), x_less, y_more);
242  ptileX1Y1 = native_pos_to_tile(&(wld.map), x_less, y_less);
243 
244  if (ptileXY->continent == 0) {
245  ptileXY->continent = c;
246  ptileX2Y->continent = c;
247  ptileX1Y->continent = c;
248  ptileXY2->continent = c;
249  ptileXY1->continent = c;
250  ptileX2Y2->continent = c;
251  ptileX2Y1->continent = c;
252  ptileX1Y2->continent = c;
253  ptileX1Y1->continent = c;
254  hmap(ptileXY) = landmass[c - 1].elevation;
255  hmap(ptileX2Y) = landmass[c - 1].elevation;
256  hmap(ptileX1Y) = landmass[c - 1].elevation;
257  hmap(ptileXY2) = landmass[c - 1].elevation;
258  hmap(ptileXY1) = landmass[c - 1].elevation;
259  hmap(ptileX2Y1) = landmass[c - 1].elevation;
260  hmap(ptileX2Y2) = landmass[c - 1].elevation;
261  hmap(ptileX1Y2) = landmass[c - 1].elevation;
262  hmap(ptileX1Y1) = landmass[c - 1].elevation;
263  /* This bit of code could track the maximum and minimum extent
264  * of the landmass. */
265  if (x < landmass[c - 1].minX) {
266  landmass[c - 1].minX = x;
267  }
268  if (x > landmass[c - 1].maxX) {
269  landmass[c - 1].maxX = x;
270  }
271  if (y < landmass[c - 1].minY) {
272  landmass[c - 1].minY = y;
273  }
274  if (y > landmass[c - 1].maxY) {
275  landmass[c - 1].maxY = y;
276  }
277  }
278  }
279 }
280 
285 static int local_ave_elevation(struct tile *ptile)
286 {
287  int ele;
288  int n;
289 
290  n = ele = 0;
291  square_iterate(&(wld.map), ptile, 3, tile2)
292  {
293  ele = ele + hmap(tile2);
294  n++;
295  }
297  fc_assert_ret_val(n, 0);
298  ele /= n;
299 
300  return ele;
301 }
302 
312 {
313  bool choose_mountain;
314  bool choose_hill;
315  int landarea;
316  int total_mtns;
317  int iter;
318 
319  // compute the land area
320  landarea = 0;
321  whole_map_iterate(&(wld.map), ptile)
322  {
323  if (hmap(ptile) > hmap_shore_level) {
324  landarea++;
325  }
326  }
328 
329  /* Iteration 1
330  Choose hills and mountains based on local elevation.
331  */
332  total_mtns = 0;
333  whole_map_iterate(&(wld.map), ptile)
334  {
335  if (not_placed(ptile)
336  && hmap(ptile) > hmap_shore_level) { // place on land only
337  // mountains
338  choose_mountain =
339  (hmap(ptile) > local_ave_elevation(ptile) * 1.20)
340  || (area_is_too_flat(ptile, hmap_mountain_level, hmap(ptile))
341  && (fc_rand(10) < 4));
342 
343  choose_hill =
344  (hmap(ptile) > local_ave_elevation(ptile) * 1.10)
345  || (area_is_too_flat(ptile, hmap_mountain_level, hmap(ptile))
346  && (fc_rand(10) < 4));
347  /* The following avoids hills and mountains directly along the coast.
348  */
349  if (count_terrain_class_near_tile(ptile, true, true, TC_OCEAN) > 0) {
350  choose_mountain = false;
351  choose_hill = false;
352  }
353  if (choose_mountain) {
354  total_mtns++;
355  tile_set_terrain(ptile,
356  pick_terrain(MG_MOUNTAINOUS, MG_UNUSED, MG_GREEN));
357  map_set_placed(ptile);
358  } else if (choose_hill) {
359  // hills
360  total_mtns++;
361  tile_set_terrain(ptile,
362  pick_terrain(MG_MOUNTAINOUS, MG_GREEN, MG_UNUSED));
363  map_set_placed(ptile);
364  }
365  }
366  }
368 
369  /* Iteration 2
370  Make sure the map has at least the minimum number of mountains according
371  to the map steepness setting. The iteration limit is a failsafe to
372  prevent the iteration from taking forever.
373  */
374  for (iter = 0;
375  total_mtns < (landarea * wld.map.server.steepness) / 100 && iter < 50;
376  iter++) {
377  whole_map_iterate(&(wld.map), ptile)
378  {
379  if (not_placed(ptile)
380  && hmap(ptile) > hmap_shore_level) { // place on land only
381  choose_mountain = fc_rand(10000) < 10;
382  choose_hill = fc_rand(10000) < 10;
383  if (choose_mountain) {
384  total_mtns++;
386  ptile, pick_terrain(MG_MOUNTAINOUS, MG_UNUSED, MG_GREEN));
387  map_set_placed(ptile);
388  } else if (choose_hill) {
389  // hills
390  total_mtns++;
392  ptile, pick_terrain(MG_MOUNTAINOUS, MG_GREEN, MG_UNUSED));
393  map_set_placed(ptile);
394  }
395  }
396  if (total_mtns >= landarea * wld.map.server.steepness / 100) {
397  break;
398  }
399  }
401  }
402 }
void make_fracture_hmap()
Fracture map generator.
static int local_ave_elevation(struct tile *ptile)
Determine the local average elevation.
void make_fracture_relief()
make_fracture_relief() Goes through a couple of iterations.
int num_landmass
static void fmfill(int x, int y, int c, int r)
Assign landmass in 3x3 area increments to avoid "holes" created by the circle algorithm.
static map_landmass * landmass
static void circle_bresenham(int xc, int yc, int r, int nn)
An expanding circle from the fracture point is used to determine the midpoint between fractures.
static map_point * fracture_points
int hmap_mountain_level
Definition: fracture_map.h:25
#define MG_UNUSED
Definition: fracture_map.h:29
int hmap_shore_level
Definition: height_map.cpp:24
int * height_map
Definition: height_map.cpp:23
struct world wld
Definition: game.cpp:48
bool area_is_too_flat(struct tile *ptile, int thill, int my_height)
We don't want huge areas of grass/plains, so we put in a hill here and there, where it gets too 'clea...
Definition: height_map.cpp:285
#define hmap_max_level
Definition: height_map.h:30
#define hmap(_tile)
Definition: height_map.h:14
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
struct tile * native_pos_to_tile(const struct civ_map *nmap, int nat_x, int nat_y)
Return the tile for the given native position.
Definition: map.cpp:416
#define MAP_INDEX_SIZE
Definition: map.h:91
#define square_iterate(nmap, center_tile, radius, tile_itr)
Definition: map.h:312
#define square_iterate_end
Definition: map.h:315
#define whole_map_iterate(_map, _tile)
Definition: map.h:473
#define whole_map_iterate_end
Definition: map.h:480
int get_sqsize()
An estimate of the linear (1-dimensional) size of the map.
void map_set_placed(struct tile *ptile)
Mark tile terrain as placed.
bool not_placed(const struct tile *ptile)
Checks if land has not yet been placed on pmap at (x, y)
struct terrain * pick_terrain(enum mapgen_terrain_property target, enum mapgen_terrain_property prefer, enum mapgen_terrain_property avoid)
Pick a terrain based on the target property and a property to avoid.
#define adjust_int_map(int_map, int_map_max)
Definition: mapgen_utils.h:101
#define fc_rand(_size)
Definition: rand.h:16
int xsize
Definition: map_types.h:73
int ysize
Definition: map_types.h:73
int topology_id
Definition: map_types.h:68
struct civ_map::@39::@41 server
Definition: tile.h:42
Continent_id continent
Definition: tile.h:46
struct civ_map map
Definition: world_object.h:21
int count_terrain_class_near_tile(const struct tile *ptile, bool cardinal_only, bool percentage, enum terrain_class tclass)
Return the number of adjacent tiles that have given terrain class (not including ptile itself).
Definition: terrain.cpp:517
void tile_set_terrain(struct tile *ptile, struct terrain *pterrain)
Set the given terrain at the specified tile.
Definition: tile.cpp:114