Freeciv21
Develop your civilization from humble roots to a global empire
height_map.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
3  __ __ part of Freeciv21. Freeciv21 is free software: you can
4 / \\..// \ redistribute it and/or modify it under the terms of the GNU
5  ( oo ) General Public License as published by the Free Software
6  \__/ Foundation, either version 3 of the License, or (at your
7  option) any later version. You should have received
8  a copy of the GNU General Public License along with Freeciv21. If not,
9  see https://www.gnu.org/licenses/.
10  */
11 // utility
12 #include "rand.h"
13 
14 // common
15 #include "map.h"
16 
17 /* server/generator */
18 #include "mapgen_topology.h"
19 #include "mapgen_utils.h"
20 
21 #include "height_map.h"
22 
23 int *height_map = nullptr;
25 
29 static float hmap_pole_factor(struct tile *ptile)
30 {
31  float factor = 1.0;
32 
33  if (near_singularity(ptile)) {
34  /* Map edge near pole: clamp to what linear ramp would give us at pole
35  * (maybe greater than 0) */
36  factor = (100 - wld.map.server.flatpoles) / 100.0;
37  } else if (wld.map.server.flatpoles > 0) {
38  /* Linear ramp down from 100% at 2.5*ICE_BASE_LEVEL to (100-flatpoles) %
39  * at the poles */
40  factor = 1
41  - ((1 - (map_colatitude(ptile) / (2.5 * ICE_BASE_LEVEL)))
42  * wld.map.server.flatpoles / 100);
43  }
44  if (wld.map.server.separatepoles
45  && map_colatitude(ptile) >= 2 * ICE_BASE_LEVEL) {
46  /* A band of low height to try to separate the pole (this function is
47  * only assumed to be called <= 2.5*ICE_BASE_LEVEL) */
48  factor = MIN(factor, 0.1);
49  }
50  return factor;
51 }
52 
60 {
61  whole_map_iterate(&(wld.map), ptile)
62  {
63  if (map_colatitude(ptile) <= 2.5 * ICE_BASE_LEVEL) {
64  hmap(ptile) *= hmap_pole_factor(ptile);
65  } else if (near_singularity(ptile)) {
66  // Near map edge but not near pole.
67  hmap(ptile) = 0;
68  }
69  }
71 }
72 
78 {
79  whole_map_iterate(&(wld.map), ptile)
80  {
81  if (hmap(ptile) == 0) {
82  // Nothing left to restore.
83  } else if (map_colatitude(ptile) <= 2.5 * ICE_BASE_LEVEL) {
84  float factor = hmap_pole_factor(ptile);
85 
86  if (factor > 0) {
87  // Invert the previously applied function
88  hmap(ptile) /= factor;
89  }
90  }
91  }
93 }
94 
100 {
101  int smooth = MAX(1, 1 + get_sqsize()
102  - (MAPSTARTPOS_DEFAULT != wld.map.server.startpos
103  ? player_count() / 4
104  : 0));
105 
106  height_map = new int[MAP_INDEX_SIZE];
107 
109 
110  for (int i = 0; i < smooth; i++) {
111  smooth_int_map(height_map, true);
112  }
113 
115 }
116 
122 static void gen5rec(int step, int xl, int yt, int xr, int yb)
123 {
124  int val[2][2];
125  int x1wrap = xr; // to wrap correctly
126  int y1wrap = yb;
127 
128  // All x and y values are native.
129 
130  if (((yb - yt <= 0) || (xr - xl <= 0))
131  || ((yb - yt == 1) && (xr - xl == 1))) {
132  return;
133  }
134 
135  if (xr == wld.map.xsize) {
136  x1wrap = 0;
137  }
138  if (yb == wld.map.ysize) {
139  y1wrap = 0;
140  }
141 
142  val[0][0] = hmap(native_pos_to_tile(&(wld.map), xl, yt));
143  val[0][1] = hmap(native_pos_to_tile(&(wld.map), xl, y1wrap));
144  val[1][0] = hmap(native_pos_to_tile(&(wld.map), x1wrap, yt));
145  val[1][1] = hmap(native_pos_to_tile(&(wld.map), x1wrap, y1wrap));
146 
147  // set midpoints of sides to avg of side's vertices plus a random factor
148  // unset points are zero, don't reset if set
149 #define set_midpoints(X, Y, V) \
150  { \
151  struct tile *ptile = native_pos_to_tile(&(wld.map), (X), (Y)); \
152  if (map_colatitude(ptile) <= ICE_BASE_LEVEL / 2) { \
153  /* possibly flatten poles, or possibly not (even at map edge) */ \
154  hmap(ptile) = (V) * (100 - wld.map.server.flatpoles) / 100; \
155  } else if (near_singularity(ptile) || hmap(ptile) != 0) { \
156  /* do nothing */ \
157  } else { \
158  hmap(ptile) = (V); \
159  } \
160  }
161 
162  set_midpoints((xl + xr) / 2, yt,
163  (val[0][0] + val[1][0]) / 2 + (int) fc_rand(step)
164  - step / 2);
165  set_midpoints((xl + xr) / 2, y1wrap,
166  (val[0][1] + val[1][1]) / 2 + (int) fc_rand(step)
167  - step / 2);
168  set_midpoints(xl, (yt + yb) / 2,
169  (val[0][0] + val[0][1]) / 2 + (int) fc_rand(step)
170  - step / 2);
171  set_midpoints(x1wrap, (yt + yb) / 2,
172  (val[1][0] + val[1][1]) / 2 + (int) fc_rand(step)
173  - step / 2);
174 
175  // set middle to average of midpoints plus a random factor, if not set
176  set_midpoints((xl + xr) / 2, (yt + yb) / 2,
177  ((val[0][0] + val[0][1] + val[1][0] + val[1][1]) / 4
178  + (int) fc_rand(step) - step / 2));
179 
180 #undef set_midpoints
181 
182  // now call recursively on the four subrectangles
183  gen5rec(2 * step / 3, xl, yt, (xr + xl) / 2, (yb + yt) / 2);
184  gen5rec(2 * step / 3, xl, (yb + yt) / 2, (xr + xl) / 2, yb);
185  gen5rec(2 * step / 3, (xr + xl) / 2, yt, xr, (yb + yt) / 2);
186  gen5rec(2 * step / 3, (xr + xl) / 2, (yb + yt) / 2, xr, yb);
187 }
188 
204 {
205  const bool xnowrap = !current_topo_has_flag(TF_WRAPX);
206  const bool ynowrap = !current_topo_has_flag(TF_WRAPY);
207 
208  /*
209  * How many blocks should the x and y directions be divided into
210  * initially.
211  */
212  const int extra_div = 1
213  + ((MAPSTARTPOS_DEFAULT == wld.map.server.startpos
214  || MAPSTARTPOS_ALL == wld.map.server.startpos)
215  ? 0
216  : player_count());
217  const int xdiv = 5 + extra_div;
218  const int ydiv = 5 + extra_div;
219 
220  int xdiv2 = xdiv + (xnowrap ? 1 : 0);
221  int ydiv2 = ydiv + (ynowrap ? 1 : 0);
222 
223  int xmax = wld.map.xsize - (xnowrap ? 1 : 0);
224  int ymax = wld.map.ysize - (ynowrap ? 1 : 0);
225  int x_current, y_current;
226  // just need something > log(max(xsize, ysize)) for the recursion
227  int step = wld.map.xsize + wld.map.ysize;
228  // edges are avoided more strongly as this increases
229  int avoidedge = (100 - wld.map.server.landpercent) * step / 100 + step / 3;
230 
231  height_map = new int[MAP_INDEX_SIZE];
232 
233  // initialize map
235 
236  // set initial points
237  for (x_current = 0; x_current < xdiv2; x_current++) {
238  for (y_current = 0; y_current < ydiv2; y_current++) {
239  do_in_map_pos(&(wld.map), ptile, (x_current * xmax / xdiv),
240  (y_current * ymax / ydiv))
241  {
242  // set initial points
243  hmap(ptile) = fc_rand(2 * step) - (2 * step) / 2;
244 
245  if (near_singularity(ptile)) {
246  // avoid edges (topological singularities)
247  hmap(ptile) -= avoidedge;
248  }
249 
250  if (map_colatitude(ptile) <= ICE_BASE_LEVEL / 2) {
251  // separate poles and avoid too much land at poles
252  hmap(ptile) -= fc_rand(avoidedge * wld.map.server.flatpoles / 100);
253  }
254  }
256  }
257  }
258 
259  // calculate recursively on each block
260  for (x_current = 0; x_current < xdiv; x_current++) {
261  for (y_current = 0; y_current < ydiv; y_current++) {
262  gen5rec(step, x_current * xmax / xdiv, y_current * ymax / ydiv,
263  (x_current + 1) * xmax / xdiv, (y_current + 1) * ymax / ydiv);
264  }
265  }
266 
267  // put in some random fuzz
268  whole_map_iterate(&(wld.map), ptile)
269  {
270  hmap(ptile) = 8 * hmap(ptile) + fc_rand(4) - 2;
271  }
273 
275 }
276 
285 bool area_is_too_flat(struct tile *ptile, int thill, int my_height)
286 {
287  int higher_than_me = 0;
288 
289  square_iterate(&(wld.map), ptile, 2, tile1)
290  {
291  if (hmap(tile1) > thill) {
292  return false;
293  }
294  if (hmap(tile1) > my_height) {
295  if (map_distance(ptile, tile1) == 1) {
296  return false;
297  }
298  if (++higher_than_me > 2) {
299  return false;
300  }
301  }
302  }
304 
305  return (thill - hmap_shore_level) * higher_than_me
306  <= (my_height - hmap_shore_level) * 4;
307 }
struct world wld
Definition: game.cpp:48
static void gen5rec(int step, int xl, int yt, int xr, int yb)
Recursive function which does the work for generator 5.
Definition: height_map.cpp:122
int hmap_mountain_level
Definition: height_map.cpp:24
static float hmap_pole_factor(struct tile *ptile)
Factor by which to lower height map near poles in normalize_hmap_poles.
Definition: height_map.cpp:29
void make_random_hmap()
Create uncorrelated rand map and do some call to smoth to correlate it a little and create randoms sh...
Definition: height_map.cpp:99
void renormalize_hmap_poles()
Invert (most of) the effects of normalize_hmap_poles so that we have accurate heights for texturing t...
Definition: height_map.cpp:77
void normalize_hmap_poles()
Lower the land near the map edges and (optionally) the polar region to avoid too much land there.
Definition: height_map.cpp:59
int hmap_shore_level
Definition: height_map.cpp:24
#define set_midpoints(X, Y, V)
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
void make_pseudofractal_hmap()
Generator 5 makes earthlike worlds with one or more large continents and a scattering of smaller isla...
Definition: height_map.cpp:203
int * height_map
Definition: height_map.cpp:23
#define hmap_max_level
Definition: height_map.h:30
#define hmap(_tile)
Definition: height_map.h:14
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
int map_distance(const struct tile *tile0, const struct tile *tile1)
Return Manhattan distance between two tiles.
Definition: map.cpp:623
#define current_topo_has_flag(flag)
Definition: map.h:37
#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
@ MAPSTARTPOS_ALL
Definition: map_types.h:55
@ MAPSTARTPOS_DEFAULT
Definition: map_types.h:52
int get_sqsize()
An estimate of the linear (1-dimensional) size of the map.
int map_colatitude(const struct tile *ptile)
Returns the colatitude of this map position.
bool near_singularity(const struct tile *ptile)
Return TRUE if the map in a typical city radius is SINGULAR.
#define ICE_BASE_LEVEL
void smooth_int_map(int *int_map, bool zeroes_at_edges)
Apply a Gaussian diffusion filter on the map.
#define do_in_map_pos_end
Definition: mapgen_utils.h:44
#define do_in_map_pos(nmap, ptile, nat_x, nat_y)
Definition: mapgen_utils.h:39
#define adjust_int_map(int_map, int_map_max)
Definition: mapgen_utils.h:101
int player_count()
Return the number of players.
Definition: player.cpp:739
#define fc_rand(_size)
Definition: rand.h:16
#define INITIALIZE_ARRAY(array, size, value)
Definition: shared.h:99
#define MIN(x, y)
Definition: shared.h:49
#define MAX(x, y)
Definition: shared.h:48
int step
Definition: specpq.h:83
int xsize
Definition: map_types.h:73
int ysize
Definition: map_types.h:73
struct civ_map::@39::@41 server
Definition: tile.h:42
struct civ_map map
Definition: world_object.h:21