Galactic Bloodshed
makeplanet.cc
Go to the documentation of this file.
1 // Copyright 2014 The Galactic Bloodshed Authors. All rights reserved.
2 // Use of this source code is governed by a license that can be
3 // found in the COPYING file.
4 
5 /* makeplanet.c -- makes one planet. */
6 
7 #include "gb/creator/makeplanet.h"
8 
9 #include <cmath>
10 #include <cstdlib>
11 #include <cstring>
12 #include <iostream>
13 #include <tuple>
14 #include <vector>
15 
16 #include "gb/creator/makestar.h"
17 #include "gb/files_shl.h"
18 #include "gb/tweakables.h"
19 #include "gb/utils/rand.h"
20 #include "gb/vars.h"
21 
22 /* @ o O # ~ . ( - */
23 static const int xmin[] = {15, 2, 4, 4, 26, 12, 12, 12};
24 static const int xmax[] = {23, 4, 8, 8, 32, 20, 20, 20};
25 
26 /*
27  * Fmin, Fmax, rmin, rmax are now all based on the sector type as well
28  * as the planet type.
29  */
30 
31 /* . * ^ ~ # ( - */
32 static const int x_chance[] = {5, 15, 10, 4, 5, 7, 6};
33 static const int Fmin[][8] = {{25, 20, 10, 0, 20, 45, 5}, /* @ */
34  {0, 1, 2, 0, 0, 0, 1}, /* o */
35  {0, 3, 2, 0, 0, 0, 2}, /* O */
36  {0, 0, 8, 0, 25, 0, 0}, /* # */
37  {0, 0, 0, 35, 0, 0, 0}, /* ~ */
38  {30, 0, 0, 0, 20, 0, 0}, /* . */
39  {30, 25, 20, 0, 30, 60, 15}, /* ( */
40  {0, 5, 2, 0, 0, 0, 2}}; /* - */
41 
42 /* . * ^ ~ # ( - */
43 static const int Fmax[][8] = {{40, 35, 20, 0, 40, 65, 15}, /* @ */
44  {0, 2, 3, 0, 0, 0, 2}, /* o */
45  {0, 5, 4, 0, 0, 0, 3}, /* O */
46  {0, 0, 15, 0, 35, 0, 0}, /* # */
47  {0, 0, 0, 55, 0, 0, 0}, /* ~ */
48  {50, 0, 0, 0, 40, 0, 0}, /* . */
49  {60, 45, 30, 0, 50, 90, 25}, /* ( */
50  {0, 10, 6, 0, 0, 0, 8}}; /* - */
51 
52 /* . * ^ ~ # ( - */
53 static const int rmin[][8] = {{200, 225, 300, 0, 200, 0, 250}, /* @ */
54  {0, 250, 350, 0, 0, 0, 300}, /* o */
55  {0, 225, 275, 0, 0, 0, 250}, /* O */
56  {0, 0, 250, 0, 225, 0, 0}, /* # */
57  {0, 0, 0, 30, 0, 0, 0}, /* ~ */
58  {175, 0, 0, 0, 200, 0, 0}, /* . */
59  {150, 0, 0, 0, 150, 150, 0}, /* ( */
60  {0, 200, 300, 0, 0, 0, 250}}; /* - */
61 
62 /* . * ^ ~ # ( - */
63 static const int rmax[][8] = {{250, 325, 400, 0, 250, 0, 300}, /* @ */
64  {0, 300, 600, 0, 0, 0, 400}, /* o */
65  {0, 300, 500, 0, 0, 0, 300}, /* O */
66  {0, 0, 350, 0, 300, 0, 0}, /* # */
67  {0, 0, 0, 60, 0, 0, 0}, /* ~ */
68  {225, 0, 0, 0, 250, 0, 0}, /* . */
69  {250, 0, 0, 0, 250, 200, 0}, /* ( */
70  {0, 200, 500, 0, 0, 0, 350}}; /* - */
71 
72 /* The starting conditions of the sectors given a planet types */
73 /* @ o O # ~ . ( _ */
74 static const int cond[] = {SectorType::SEC_SEA, SectorType::SEC_MOUNT,
75  SectorType::SEC_LAND, SectorType::SEC_ICE,
76  SectorType::SEC_GAS, SectorType::SEC_SEA,
77  SectorType::SEC_FOREST, SectorType::SEC_DESERT};
78 
79 static int neighbors(SectorMap &, int, int, int);
80 static void MakeEarthAtmosphere(Planet &, int);
81 static void Makesurface(const Planet &, SectorMap &);
82 static int SectTemp(const Planet &, const int);
83 static void seed(SectorMap &, int, int);
84 static void grow(SectorMap &, int, int, int);
85 
86 Planet Makeplanet(double dist, short stemp, PlanetType type) {
87  static planetnum_t planet_id = 0;
88  int x;
89  int y;
90  Planet planet;
91  int atmos;
92  int total_sects;
93  char c;
94  char t;
95  double f;
96 
97  bzero(&planet, sizeof(planet));
98  planet.planet_id = planet_id;
99  planet_id++;
100  planet.type = type;
101  planet.expltimer = 5;
102  planet.conditions[TEMP] = planet.conditions[RTEMP] = Temperature(dist, stemp);
103 
104  planet.Maxx = int_rand(xmin[type], xmax[type]);
105  f = (double)planet.Maxx / RATIOXY;
106  planet.Maxy = round_rand(f) + 1;
107  if (!(planet.Maxy % 2)) planet.Maxy++; /* make odd number of latitude bands */
108 
109  if (type == PlanetType::ASTEROID)
110  planet.Maxy = int_rand(1, 3); /* Asteroids have funny shapes. */
111 
112  t = c = cond[type];
113 
114  // Initialize with the correct number of sectors.
115  SectorMap smap(planet, true);
116  for (y = 0; y < planet.Maxy; y++) {
117  for (x = 0; x < planet.Maxx; x++) {
118  auto &s = smap.get(x, y);
119  s.type = s.condition = t;
120  }
121  }
122 
123  total_sects = (planet.Maxy - 1) * (planet.Maxx - 1);
124 
125  switch (type) {
126  case PlanetType::GASGIANT: /* gas giant Planet */
127  /* either lots of meth or not too much */
128  if (int_rand(0, 1)) { /* methane planet */
129  atmos = 100 - (planet.conditions[METHANE] = int_rand(70, 80));
130  atmos -= planet.conditions[HYDROGEN] = int_rand(1, atmos / 2);
131  atmos -= planet.conditions[HELIUM] = 1;
132  atmos -= planet.conditions[OXYGEN] = 0;
133  atmos -= planet.conditions[CO2] = 1;
134  atmos -= planet.conditions[NITROGEN] = int_rand(1, atmos / 2);
135  atmos -= planet.conditions[SULFUR] = 0;
136  planet.conditions[OTHER] = atmos;
137  } else {
138  atmos = 100 - (planet.conditions[HYDROGEN] = int_rand(30, 75));
139  atmos -= planet.conditions[HELIUM] = int_rand(20, atmos / 2);
140  atmos -= planet.conditions[METHANE] = random() & 01;
141  atmos -= planet.conditions[OXYGEN] = 0;
142  atmos -= planet.conditions[CO2] = random() & 01;
143  atmos -= planet.conditions[NITROGEN] = int_rand(1, atmos / 2);
144  atmos -= planet.conditions[SULFUR] = 0;
145  planet.conditions[OTHER] = atmos;
146  }
147  break;
148  case PlanetType::MARS:
149  planet.conditions[HYDROGEN] = 0;
150  planet.conditions[HELIUM] = 0;
151  planet.conditions[METHANE] = 0;
152  planet.conditions[OXYGEN] = 0;
153  if (random() & 01) { /* some have an atmosphere, some don't */
154  atmos = 100 - (planet.conditions[CO2] = int_rand(30, 45));
155  atmos -= planet.conditions[NITROGEN] = int_rand(10, atmos / 2);
156  atmos -= planet.conditions[SULFUR] =
157  (random() & 01) ? 0 : int_rand(20, atmos / 2);
158  atmos -= planet.conditions[OTHER] = atmos;
159  } else {
160  planet.conditions[CO2] = 0;
161  planet.conditions[NITROGEN] = 0;
162  planet.conditions[SULFUR] = 0;
163  planet.conditions[OTHER] = 0;
164  }
165  seed(smap, SectorType::SEC_DESERT, int_rand(1, total_sects));
166  seed(smap, SectorType::SEC_MOUNT, int_rand(1, total_sects));
167  break;
168  case PlanetType::ASTEROID: /* asteroid */
169  /* no atmosphere */
170  for (y = 0; y < planet.Maxy; y++)
171  for (x = 0; x < planet.Maxx; x++)
172  if (!int_rand(0, 3)) {
173  auto &s = smap.get_random();
174  s.type = s.condition = SectorType::SEC_LAND;
175  }
176  seed(smap, DESERT, int_rand(1, total_sects));
177  break;
178  case PlanetType::ICEBALL: /* ball of ice */
179  /* no atmosphere */
180  planet.conditions[HYDROGEN] = 0;
181  planet.conditions[HELIUM] = 0;
182  planet.conditions[METHANE] = 0;
183  planet.conditions[OXYGEN] = 0;
184  if (planet.Maxx * planet.Maxy > int_rand(0, 20)) {
185  atmos = 100 - (planet.conditions[CO2] = int_rand(30, 45));
186  atmos -= planet.conditions[NITROGEN] = int_rand(10, atmos / 2);
187  atmos -= planet.conditions[SULFUR] =
188  (random() & 01) ? 0 : int_rand(20, atmos / 2);
189  atmos -= planet.conditions[OTHER] = atmos;
190  } else {
191  planet.conditions[CO2] = 0;
192  planet.conditions[NITROGEN] = 0;
193  planet.conditions[SULFUR] = 0;
194  planet.conditions[OTHER] = 0;
195  }
196  seed(smap, SectorType::SEC_MOUNT, int_rand(1, total_sects / 2));
197  break;
198  case PlanetType::EARTH:
199  MakeEarthAtmosphere(planet, 33);
200  seed(smap, SectorType::SEC_LAND,
201  int_rand(total_sects / 30, total_sects / 20));
202  grow(smap, SectorType::SEC_LAND, 1, 1);
203  grow(smap, SectorType::SEC_LAND, 1, 2);
204  grow(smap, SectorType::SEC_LAND, 2, 3);
205  grow(smap, SectorType::SEC_SEA, 1, 4);
206  break;
207  case PlanetType::FOREST:
208  MakeEarthAtmosphere(planet, 0);
209  seed(smap, SectorType::SEC_SEA,
210  int_rand(total_sects / 30, total_sects / 20));
211  grow(smap, SectorType::SEC_SEA, 1, 1);
212  grow(smap, SectorType::SEC_SEA, 1, 3);
213  grow(smap, SectorType::SEC_FOREST, 1, 3);
214  break;
215  case PlanetType::WATER:
216  MakeEarthAtmosphere(planet, 25);
217  break;
218  case PlanetType::DESERT:
219  MakeEarthAtmosphere(planet, 50);
220  seed(smap, SectorType::SEC_MOUNT,
221  int_rand(total_sects / 50, total_sects / 25));
222  grow(smap, SectorType::SEC_MOUNT, 1, 1);
223  grow(smap, SectorType::SEC_MOUNT, 1, 2);
224  seed(smap, SectorType::SEC_LAND,
225  int_rand(total_sects / 50, total_sects / 25));
226  grow(smap, SectorType::SEC_LAND, 1, 1);
227  grow(smap, SectorType::SEC_LAND, 1, 3);
228  grow(smap, SectorType::SEC_DESERT, 1, 3);
229  break;
230  }
231  Makesurface(planet,
232  smap); /* determine surface geology based on environment */
233  putsmap(smap, planet);
234  return planet;
235 }
236 
237 static void MakeEarthAtmosphere(Planet &planet, const int chance) {
238  int atmos = 100;
239 
240  if (int_rand(0, 99) > chance) {
241  /* oxygen-reducing atmosphere */
242  atmos -= planet.conditions[OXYGEN] = int_rand(10, 25);
243  atmos -= planet.conditions[NITROGEN] = int_rand(20, atmos - 20);
244  atmos -= planet.conditions[CO2] = int_rand(10, atmos / 2);
245  atmos -= planet.conditions[HELIUM] = int_rand(2, atmos / 8 + 1);
246  atmos -= planet.conditions[METHANE] = random() & 01;
247  atmos -= planet.conditions[SULFUR] = 0;
248  atmos -= planet.conditions[HYDROGEN] = 0;
249  planet.conditions[OTHER] = atmos;
250  } else {
251  /* methane atmosphere */
252  atmos -= planet.conditions[METHANE] = int_rand(70, 80);
253  atmos -= planet.conditions[HYDROGEN] = int_rand(1, atmos / 2);
254  atmos -= planet.conditions[HELIUM] = 1 + (random() & 01);
255  atmos -= planet.conditions[OXYGEN] = 0;
256  atmos -= planet.conditions[CO2] = 1 + (random() & 01);
257  atmos -= planet.conditions[SULFUR] = (random() & 01);
258  atmos -= planet.conditions[NITROGEN] = int_rand(1, atmos / 2);
259  planet.conditions[OTHER] = atmos;
260  }
261 }
262 
263 //! Returns # of neighbors of a given designation that a sector has.
264 static int neighbors(SectorMap &smap, int x, int y, int type) {
265  int l = x - 1;
266  int r = x + 1; /* Left and right columns. */
267  int n = 0; /* Number of neighbors so far. */
268 
269  if (x == 0)
270  l = smap.get_maxx() - 1;
271  else if (r == smap.get_maxx())
272  r = 0;
273  if (y > 0)
274  n += (smap.get(x, y - 1).type == type) + (smap.get(l, y - 1).type == type) +
275  (smap.get(r, y - 1).type == type);
276 
277  n += (smap.get(l, y).type == type) + (smap.get(r, y).type == type);
278 
279  if (y < smap.get_maxy() - 1)
280  n += (smap.get(x, y + 1).type == type) + (smap.get(l, y + 1).type == type) +
281  (smap.get(r, y + 1).type == type);
282 
283  return (n);
284 }
285 
286 //! Randomly places n sectors of designation type on a planet.
287 static void seed(SectorMap &smap, int type, int n) {
288  while (n-- > 0) {
289  auto &s = smap.get_random();
290  s.type = s.condition = type;
291  }
292 }
293 
294 /*! Spread out a sector of a certain type over the planet. Rate is the number
295  * of adjacent sectors of the same type that must be found for the sector to
296  * become type.
297  */
298 static void grow(SectorMap &smap, int type, int n, int rate) {
299  std::vector<std::tuple<int, int, int>> worklist; // x, y, type
300 
301  // We don't want to alter the current map, as this is iterative.
302  // So we store a worklist and apply it after we've done a scan of
303  // the map.
304  while (n-- > 0) {
305  for (int x = 0; x < smap.get_maxx(); x++) {
306  for (int y = 0; y < smap.get_maxy(); y++) {
307  if (neighbors(smap, x, y, type) >= rate) {
308  worklist.emplace_back(std::make_tuple(x, y, type));
309  }
310  }
311  }
312  }
313 
314  for (auto &[x, y, sector_type] : worklist) {
315  auto &s = smap.get(x, y);
316  s.condition = s.type = sector_type;
317  }
318 }
319 
320 static void Makesurface(const Planet &p, SectorMap &smap) {
321  for (int x = 0; x < smap.get_maxx(); x++) {
322  for (int y = 0; y < smap.get_maxy(); y++) {
323  auto &s = smap.get(x, y);
324  int temp = SectTemp(p, y);
325  switch (s.type) {
326  case SectorType::SEC_SEA:
327  if (success(-temp) && ((y == 0) || (y == smap.get_maxy() - 1)))
328  s.condition = SectorType::SEC_ICE;
329  break;
330  case SectorType::SEC_LAND:
331  if (p.type == PlanetType::EARTH) {
332  if (success(-temp) && (y == 0 || y == smap.get_maxy() - 1))
333  s.condition = SectorType::SEC_ICE;
334  }
335  break;
336  case SectorType::SEC_FOREST:
337  if (p.type == PlanetType::FOREST) {
338  if (success(-temp) && (y == 0 || y == smap.get_maxy() - 1))
339  s.condition = SectorType::SEC_ICE;
340  }
341  }
342  s.type = s.condition;
343  s.resource = int_rand(rmin[p.type][s.type], rmax[p.type][s.type]);
344  s.fert = int_rand(Fmin[p.type][s.type], Fmax[p.type][s.type]);
345  if (int_rand(0, 1000) < x_chance[s.type])
346  s.crystals = int_rand(4, 8);
347  else
348  s.crystals = 0;
349  }
350  }
351 }
352 
353 int SectTemp(const Planet &p, const int y) {
354  const int TFAC = 10;
355 
356  int temp = p.conditions[TEMP];
357  int mid = (p.Maxy + 1) / 2 - 1;
358  int dy = abs(y - mid);
359 
360  temp -= TFAC * dy * dy;
361  return temp;
362 }
#define HELIUM
Definition: tweakables.h:37
#define SULFUR
Definition: tweakables.h:36
#define METHANE
Definition: tweakables.h:31
static void Makesurface(const Planet &, SectorMap &)
Definition: makeplanet.cc:320
Planet Makeplanet(double, short, PlanetType)
Definition: makeplanet.cc:86
static const int xmax[]
Definition: makeplanet.cc:24
static void MakeEarthAtmosphere(Planet &, int)
Definition: makeplanet.cc:237
static const int Fmax[][8]
Definition: makeplanet.cc:43
#define OXYGEN
Definition: tweakables.h:32
static const int rmax[][8]
Definition: makeplanet.cc:63
static const int rmin[][8]
Definition: makeplanet.cc:53
#define CO2
Definition: tweakables.h:33
void putsmap(SectorMap &map, Planet &p)
Definition: files_shl.cc:1108
#define TEMP
Definition: tweakables.h:30
#define OTHER
Definition: tweakables.h:38
static int SectTemp(const Planet &, const int)
Definition: makeplanet.cc:353
static int neighbors(SectorMap &, int, int, int)
Returns # of neighbors of a given designation that a sector has.
Definition: makeplanet.cc:264
static void grow(SectorMap &, int, int, int)
Definition: makeplanet.cc:298
int Temperature(double dist, int stemp)
Definition: makestar.cc:68
static const int cond[]
Definition: makeplanet.cc:74
#define HYDROGEN
Definition: tweakables.h:34
#define NITROGEN
Definition: tweakables.h:35
#define RTEMP
Definition: tweakables.h:29
static void seed(SectorMap &, int, int)
Randomly places n sectors of designation type on a planet.
Definition: makeplanet.cc:287
static const int x_chance[]
Definition: makeplanet.cc:32
static const int xmin[]
Definition: makeplanet.cc:23
#define RATIOXY
Definition: tweakables.h:77
static const int Fmin[][8]
Definition: makeplanet.cc:33