Freeciv21
Develop your civilization from humble roots to a global empire
mapgen_topology.cpp
Go to the documentation of this file.
1 /*
2 _ ._ Copyright (c) 1996-2021 Freeciv21 and Freeciv contributors.
3  \ | This file is part of Freeciv21. Freeciv21 is free software: you
4  \_| can redistribute it and/or modify it under the terms of the
5  .' '. GNU General Public License as published by the Free
6  :O O: Software Foundation, either version 3 of the License,
7  '/ \' or (at your option) any later version. You should have
8  :X: received a copy of the GNU General Public License along with
9  :X: Freeciv21. If not, see https://www.gnu.org/licenses/.
10  */
11 #include <cmath> // sqrt
12 
13 // utility
14 #include "log.h"
15 
16 // common
17 #include "game.h"
18 #include "map.h"
19 
20 #include "mapgen_topology.h"
21 
23 
32 int map_colatitude(const struct tile *ptile)
33 {
34  double x, y;
35  int tile_x, tile_y;
36 
37  fc_assert_ret_val(ptile != nullptr, MAX_COLATITUDE / 2);
38 
39  if (wld.map.server.alltemperate) {
40  /* An all-temperate map has "average" temperature everywhere.
41  *
42  * TODO: perhaps there should be a random temperature variation. */
43  return MAX_COLATITUDE / 2;
44  }
45 
46  index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
47  do_in_natural_pos(ntl_x, ntl_y, tile_x, tile_y)
48  {
49  if (wld.map.server.single_pole) {
50  if (!current_topo_has_flag(TF_WRAPY)) {
51  /* Partial planetary map. A polar zone is placed
52  * at the top and the equator is at the bottom. */
53  return MAX_COLATITUDE * ntl_y / (NATURAL_HEIGHT - 1);
54  }
55  if (!current_topo_has_flag(TF_WRAPX)) {
56  return MAX_COLATITUDE * ntl_x / (NATURAL_WIDTH - 1);
57  }
58  }
59 
60  // Otherwise a wrapping map is assumed to be a global planetary map.
61 
62  /* we fold the map to get the base symmetries
63  *
64  * ......
65  * :c__c:
66  * :____:
67  * :____:
68  * :c__c:
69  * ......
70  *
71  * C are the corners. In all cases the 4 corners have equal temperature.
72  * So we fold the map over in both directions and determine
73  * x and y vars in the range [0.0, 1.0].
74  *
75  * ...>x
76  * :C_
77  * :__
78  * V
79  * y
80  *
81  * And now this is what we have - just one-quarter of the map.
82  */
83  x = ((ntl_x > (NATURAL_WIDTH / 2 - 1)
84  ? NATURAL_WIDTH - 1.0 - static_cast<double>(ntl_x)
85  : static_cast<double>(ntl_x))
86  / (NATURAL_WIDTH / 2 - 1));
87  y = ((ntl_y > (NATURAL_HEIGHT / 2 - 1)
88  ? NATURAL_HEIGHT - 1.0 - static_cast<double>(ntl_y)
89  : static_cast<double>(ntl_y))
90  / (NATURAL_HEIGHT / 2 - 1));
91  }
93 
94  if (!current_topo_has_flag(TF_WRAPY)) {
95  /* In an Earth-like topology the polar zones are at north and south.
96  * This is equivalent to a Mercator projection. */
97  return MAX_COLATITUDE * y;
98  }
99 
100  if (!current_topo_has_flag(TF_WRAPX) && current_topo_has_flag(TF_WRAPY)) {
101  /* In a Uranus-like topology the polar zones are at east and west.
102  * This isn't really the way Uranus is; it's the way Earth would look
103  * if you tilted your head sideways. It's still a Mercator
104  * projection. */
105  return MAX_COLATITUDE * x;
106  }
107 
108  /* Otherwise we have a torus topology. We set it up as an approximation
109  * of a sphere with two circular polar zones and a square equatorial
110  * zone. In this case north and south are not constant directions on the
111  * map because we have to use a more complicated (custom) projection.
112  *
113  * Generators 2 and 5 work best if the center of the map is free. So
114  * we want to set up the map with the poles (N,S) along the sides and the
115  * equator (/,\‍) in between.
116  *
117  * ........
118  * :\ NN /:
119  * : \ / :
120  * :S \/ S:
121  * :S /\ S:
122  * : / \ :
123  * :/ NN \:
124  * ''''''''
125  */
126 
127  /* Remember that we've already folded the map into fourths:
128  *
129  * ....
130  * :\ N
131  * : \
132  * :S \
133  *
134  * Now flip it along the X direction to get this:
135  *
136  * ....
137  * :N /
138  * : /
139  * :/ S
140  */
141  x = 1.0 - x;
142 
143  /* Since the north and south poles are equivalent, we can fold along the
144  * diagonal. This leaves us with 1/8 of the map
145  *
146  * .....
147  * :P /
148  * : /
149  * :/
150  *
151  * where P is the polar regions and / is the equator. */
152  if (x + y > 1.0) {
153  x = 1.0 - x;
154  y = 1.0 - y;
155  }
156 
157  /* This projection makes poles with a shape of a quarter-circle along
158  * "P" and the equator as a straight line along "/".
159  *
160  * This is explained more fully in RT 8624; the discussion can be found at
161  * http://thread.gmane.org/gmane.games.freeciv.devel/42648 */
162  return MAX_COLATITUDE
163  * (1.5 * (x * x * y + x * y * y) - 0.5 * (x * x * x + y * y * y)
164  + 1.5 * (x * x + y * y));
165 }
166 
171 bool near_singularity(const struct tile *ptile)
172 {
174 }
175 
180 static void set_sizes(double size, int Xratio, int Yratio)
181 {
182  /* Some code in generator assumes even dimension, so this is set to 2.
183  * Future topologies may also require even dimensions. */
184  /* The generator works also for odd values; keep this here for
185  * autogenerated height and width. */
186  const int even = 2;
187 
188  /* In iso-maps we need to double the map.ysize factor, since xsize is
189  * in native coordinates which are compressed 2x in the X direction. */
190  const int iso = MAP_IS_ISOMETRIC ? 2 : 1;
191 
192  /* We have:
193  *
194  * 1000 * size = xsize * ysize
195  *
196  * And to satisfy the ratios and other constraints we set
197  *
198  * xsize = i_size * xratio * even
199  * ysize = i_size * yratio * even * iso
200  *
201  * For any value of "i_size". So with some substitution
202  *
203  * 1000 * size = i_size * i_size * xratio * yratio * even * even * iso
204  * i_size = sqrt(1000 * size / (xratio * yratio * even * even * iso))
205  *
206  * Make sure to round off i_size to preserve exact wanted ratios,
207  * that may be importante for some topologies.
208  */
209  const int i_size =
210  sqrt(static_cast<float>(size)
211  / static_cast<float>(Xratio * Yratio * iso * even * even))
212  + 0.49;
213 
214  // Now build xsize and ysize value as described above.
215  wld.map.xsize = Xratio * i_size * even;
216  wld.map.ysize = Yratio * i_size * even * iso;
217 
218  /* Now make sure the size isn't too large for this ratio or the overall map
219  * size (MAP_INDEX_SIZE) is larger than the maximal allowed size
220  * (MAP_MAX_SIZE * 1000). If it is then decrease the size and try again. */
223  || MAP_INDEX_SIZE > MAP_MAX_SIZE * 1000) {
224  fc_assert(size > 100.0);
225  set_sizes(size - 100.0, Xratio, Yratio);
226  return;
227  }
228 
229  /* If the ratio is too big for some topology the simplest way to avoid
230  * this error is to set the maximum size smaller for all topologies! */
231  if (wld.map.server.size * 1000 > size + 900.0) {
232  // Warning when size is set uselessly big
233  qCritical("Requested size of %d is too big for this topology.",
234  wld.map.server.size);
235  }
236 
237  /* xsize and ysize must be between MAP_MIN_LINEAR_SIZE and
238  * MAP_MAX_LINEAR_SIZE. */
239  wld.map.xsize =
241  wld.map.ysize =
243 
244  qInfo(_("Creating a map of size %d x %d = %d tiles (%d requested)."),
246  static_cast<int>(size));
247 }
248 
255 static void get_ratios(int *x_ratio, int *y_ratio)
256 {
257  if (current_topo_has_flag(TF_WRAPX)) {
258  if (current_topo_has_flag(TF_WRAPY)) {
259  // Ratios for torus map.
260  *x_ratio = 1;
261  *y_ratio = 1;
262  } else {
263  // Ratios for classic map.
264  *x_ratio = 3;
265  *y_ratio = 2;
266  }
267  } else {
268  if (current_topo_has_flag(TF_WRAPY)) {
269  // Ratios for uranus map.
270  *x_ratio = 2;
271  *y_ratio = 3;
272  } else {
273  // Ratios for flat map.
274  *x_ratio = 1;
275  *y_ratio = 1;
276  }
277  }
278 }
279 
285 void generator_init_topology(bool autosize)
286 {
287  int sqsize;
288  double map_size;
289 
290  /* The server behavior to create the map is defined by
291  * 'map.server.mapsize'. Calculate the xsize/ysize if it is not directly
292  * defined. */
293  if (autosize) {
294  int x_ratio, y_ratio;
295 
296  switch (wld.map.server.mapsize) {
297  case MAPSIZE_XYSIZE:
298  wld.map.server.size =
299  static_cast<float>(wld.map.xsize * wld.map.ysize) / 1000.0 + 0.5;
300  wld.map.server.tilesperplayer =
301  ((map_num_tiles() * wld.map.server.landpercent)
302  / (player_count() * 100));
303  qInfo(_("Creating a map of size %d x %d = %d tiles (map size: "
304  "%d)."),
306  wld.map.server.size);
307  break;
308 
309  case MAPSIZE_PLAYER:
310  map_size = (static_cast<double>(player_count()
311  * wld.map.server.tilesperplayer * 100)
312  / wld.map.server.landpercent);
313 
314  if (map_size < MAP_MIN_SIZE * 1000) {
315  wld.map.server.size = MAP_MIN_SIZE;
316  map_size = MAP_MIN_SIZE * 1000;
317  qInfo(_("Map size calculated for %d (land) tiles per player "
318  "and %d player(s) too small. Setting map size to the "
319  "minimal size %d."),
320  wld.map.server.tilesperplayer, player_count(),
321  wld.map.server.size);
322  } else if (map_size > MAP_MAX_SIZE * 1000) {
323  wld.map.server.size = MAP_MAX_SIZE;
324  map_size = MAP_MAX_SIZE * 1000;
325  qInfo(_("Map size calculated for %d (land) tiles per player "
326  "and %d player(s) too large. Setting map size to the "
327  "maximal size %d."),
328  wld.map.server.tilesperplayer, player_count(),
329  wld.map.server.size);
330  } else {
331  wld.map.server.size = map_size / 1000.0 + 0.5;
332  qInfo(_("Setting map size to %d (approx. %d (land) tiles for "
333  "each of the %d player(s))."),
334  wld.map.server.size, wld.map.server.tilesperplayer,
335  player_count());
336  }
337  get_ratios(&x_ratio, &y_ratio);
338  set_sizes(map_size, x_ratio, y_ratio);
339  break;
340 
341  case MAPSIZE_FULLSIZE:
342  // Set map.xsize and map.ysize based on map.size.
343  get_ratios(&x_ratio, &y_ratio);
344  set_sizes(wld.map.server.size * 1000, x_ratio, y_ratio);
345  wld.map.server.tilesperplayer =
346  ((map_num_tiles() * wld.map.server.landpercent)
347  / (player_count() * 100));
348  break;
349  }
350  } else {
351  wld.map.server.size =
352  static_cast<double>(map_num_tiles()) / 1000.0 + 0.5;
353  wld.map.server.tilesperplayer =
354  ((map_num_tiles() * wld.map.server.landpercent)
355  / (player_count() * 100));
356  }
357 
358  sqsize = get_sqsize();
359 
360  // initialize the ICE_BASE_LEVEL
361 
362  /* if maps has strip like poles we get smaller poles
363  * (less playables than island poles)
364  * 5% for little maps
365  * 2% for big ones, if map.server.temperature == 50
366  * except if separate poles is set */
367  if (wld.map.server.separatepoles) {
368  // with separatepoles option strip poles are useless
369  ice_base_colatitude = (MAX(0, 100 * COLD_LEVEL / 3 - 1 * MAX_COLATITUDE)
370  + 1 * MAX_COLATITUDE * sqsize)
371  / (100 * sqsize);
372  } else {
373  // any way strip poles are not so playable as isle poles
374  ice_base_colatitude = (MAX(0, 100 * COLD_LEVEL / 3 - 2 * MAX_COLATITUDE)
375  + 2 * MAX_COLATITUDE * sqsize)
376  / (100 * sqsize);
377  }
378 
379  // correction for single pole (Flat Earth)
380  if (wld.map.server.single_pole) {
381  if (!current_topo_has_flag(TF_WRAPY)
382  || !current_topo_has_flag(TF_WRAPX)) {
383  ice_base_colatitude /= 2;
384  }
385  }
386 
387  if (wld.map.server.huts_absolute >= 0) {
388  wld.map.server.huts =
389  wld.map.server.huts_absolute * 1000 / map_num_tiles();
390  wld.map.server.huts_absolute = -1;
391  }
392 
394 }
395 
400 {
401  int sqsize = sqrt(MAP_INDEX_SIZE / 1000);
402 
403  return MAX(1, sqsize);
404 }
#define CITY_MAP_DEFAULT_RADIUS
Definition: city.h:51
#define _(String)
Definition: fcintl.h:50
struct world wld
Definition: game.cpp:48
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
int map_num_tiles()
Returns the total number of (real) positions (or tiles) on the map.
Definition: map.cpp:954
void map_init_topology()
map_init_topology needs to be called after map.topology_id is changed.
Definition: map.cpp:276
bool is_singular_tile(const struct tile *ptile, int dist)
A "SINGULAR" position is any map position that has an abnormal number of tiles in the radius of dist.
Definition: map.cpp:1329
#define MAP_MAX_SIZE
Definition: map.h:517
#define current_topo_has_flag(flag)
Definition: map.h:37
#define MAP_MAX_LINEAR_SIZE
Definition: map.h:530
#define do_in_natural_pos(ntl_x, ntl_y, map_x, map_y)
Definition: map.h:143
#define MAP_INDEX_SIZE
Definition: map.h:91
#define NATURAL_HEIGHT
Definition: map.h:158
#define MAP_IS_ISOMETRIC
Definition: map.h:32
#define MAP_MIN_LINEAR_SIZE
Definition: map.h:531
#define MAP_MIN_SIZE
Definition: map.h:516
#define NATURAL_WIDTH
Definition: map.h:157
#define do_in_natural_pos_end
Definition: map.h:150
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition: map.h:164
@ MAPSIZE_FULLSIZE
Definition: map_types.h:35
@ MAPSIZE_PLAYER
Definition: map_types.h:36
@ MAPSIZE_XYSIZE
Definition: map_types.h:39
int get_sqsize()
An estimate of the linear (1-dimensional) size of the map.
static void get_ratios(int *x_ratio, int *y_ratio)
Return the default ratios for known topologies.
void generator_init_topology(bool autosize)
This function sets sizes in a topology-specific way then calls map_init_topology().
int ice_base_colatitude
static void set_sizes(double size, int Xratio, int Yratio)
Set the map xsize and ysize based on a base size and ratio (in natural coordinates).
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 COLD_LEVEL
#define MAX_COLATITUDE
int player_count()
Return the number of players.
Definition: player.cpp:739
#define CLIP(lower, current, upper)
Definition: shared.h:51
#define MAX(x, y)
Definition: shared.h:48
size_t size
Definition: specvec.h:64
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
#define tile_index(_pt_)
Definition: tile.h:70