Galactic Bloodshed
survey.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 /* survey.c -- print out survey for planets */
6 
7 #include "gb/survey.h"
8 
9 #include <strings.h>
10 
11 #include <cctype>
12 #include <cstdio>
13 #include <cstdlib>
14 
15 #include "gb/GB_server.h"
16 #include "gb/buffers.h"
17 #include "gb/csp.h"
18 #include "gb/csp_types.h"
19 #include "gb/files_shl.h"
20 #include "gb/fire.h"
21 #include "gb/getplace.h"
22 #include "gb/map.h"
23 #include "gb/max.h"
24 #include "gb/races.h"
25 #include "gb/ships.h"
26 #include "gb/tweakables.h"
27 #include "gb/vars.h"
28 
29 #define MAX_SHIPS_PER_SECTOR 10
30 
31 static const char *Tox[] = {
32  "Stage 0, mild",
33  "Stage 1, mild",
34  "Stage 2, semi-mild",
35  "Stage 3, semi-semi mild",
36  "Stage 4, ecologically unsound",
37  "Stage 5: ecologically unsound",
38  "Stage 6: below birth threshold",
39  "Stage 7: ecologically unstable--below birth threshold",
40  "Stage 8: ecologically poisonous --below birth threshold",
41  "Stage 9: WARNING: nearing 100% toxicity",
42  "Stage 10: WARNING: COMPLETELY TOXIC!!!",
43  "???"};
44 
45 static void get4args(const char *, int *, int *, int *, int *);
46 
47 void survey(const command_t &argv, GameObj &g) {
48  const player_t Playernum = g.player;
49  const governor_t Governor = g.governor;
50  int lowx;
51  int hix;
52  int lowy;
53  int hiy;
54  int x2;
55  char d;
56  char sect_char;
57  int tindex;
58  placetype where;
59  double compat;
60  int avg_fert;
61  int avg_resource;
62  int crystal_count;
63  racetype *Race;
64  int all = 0; /* full survey 1, specific 0 */
65  struct numshipstuff {
66  int pos;
67  struct shipstuff {
68  int shipno;
69  char ltr;
70  unsigned char owner;
71  } shipstuffs[MAX_SHIPS_PER_SECTOR];
72  };
73  struct numshipstuff shiplocs[MAX_X][MAX_Y];
74  int inhere = 0; // TODO(jeffbailey): Force init for some cases below
75  int shiplist;
76  int i;
77 
78  int mode;
79  if (argv[0] == "survey")
80  mode = 0;
81  else
82  mode = 1;
83 
84  /* general code -- jpd -- */
85 
86  if (argv.size() == 1) { /* no args */
87  where.level = g.level;
88  where.snum = g.snum;
89  where.pnum = g.pnum;
90  } else {
91  /* they are surveying a sector */
92  if ((isdigit(argv[1][0]) && index(argv[1].c_str(), ',') != nullptr) ||
93  ((argv[1][0] == '-') && (all = 1))) {
94  if (g.level != ScopeLevel::LEVEL_PLAN) {
95  g.out << "There are no sectors here.\n";
96  return;
97  }
98  where.level = ScopeLevel::LEVEL_PLAN;
99  where.snum = g.snum;
100  where.pnum = g.pnum;
101 
102  } else {
103  where = getplace(g, argv[1], 0);
104  if (where.err || where.level == ScopeLevel::LEVEL_SHIP) return;
105  }
106  }
107 
108  Race = races[Playernum - 1];
109 
110  if (where.level == ScopeLevel::LEVEL_PLAN) {
111  const auto p = getplanet(where.snum, where.pnum);
112 
113  compat = compatibility(p, Race);
114 
115  if ((isdigit(argv[1][0]) && index(argv[1].c_str(), ',') != nullptr) ||
116  all) {
117  auto smap = getsmap(p);
118 
119  if (!all) {
120  get4args(argv[1].c_str(), &x2, &hix, &lowy, &hiy);
121  /* ^^^ translate from lowx:hix,lowy:hiy */
122  x2 = std::max(0, x2);
123  hix = std::min(hix, p.Maxx - 1);
124  lowy = std::max(0, lowy);
125  hiy = std::min(hiy, p.Maxy - 1);
126  } else {
127  x2 = 0;
128  hix = p.Maxx - 1;
129  lowy = 0;
130  hiy = p.Maxy - 1;
131  }
132 
133  if (!mode) {
134  g.out << " x,y cond/type owner race eff mob frt res mil popn "
135  "^popn xtals\n";
136  }
137  if (mode) {
138  if (all) {
139  sprintf(buf, "%c %d %d %d %s %s %lu %d %d %ld %ld %d %.2f %d\n",
140  CSP_CLIENT, CSP_SURVEY_INTRO, p.Maxx, p.Maxy,
141  Stars[where.snum]->name,
142  Stars[where.snum]->pnames[where.pnum],
143  p.info[Playernum - 1].resource, p.info[Playernum - 1].fuel,
144  p.info[Playernum - 1].destruct, p.popn, p.maxpopn,
145  p.conditions[TOXIC], compatibility(p, Race), p.slaved_to);
146  notify(Playernum, Governor, buf);
147  }
148  bzero((struct shipstuff *)shiplocs, sizeof(shiplocs));
149  inhere = p.info[Playernum - 1].numsectsowned;
150  shiplist = p.ships;
151  while (shiplist) {
152  auto shipa = getship(shiplist);
153  if (shipa->owner == Playernum &&
154  (shipa->popn || (shipa->type == ShipType::OTYPE_PROBE)))
155  inhere = 1;
156  if (shipa->alive && landed(*shipa) &&
157  shiplocs[shipa->land_x][shipa->land_y].pos <
159  shiplocs[shipa->land_x][shipa->land_y]
160  .shipstuffs[shiplocs[shipa->land_x][shipa->land_y].pos]
161  .shipno = shiplist;
162  shiplocs[shipa->land_x][shipa->land_y]
163  .shipstuffs[shiplocs[shipa->land_x][shipa->land_y].pos]
164  .owner = shipa->owner;
165  shiplocs[shipa->land_x][shipa->land_y]
166  .shipstuffs[shiplocs[shipa->land_x][shipa->land_y].pos]
167  .ltr = Shipltrs[shipa->type];
168  shiplocs[shipa->land_x][shipa->land_y].pos++;
169  }
170  shiplist = shipa->nextship;
171  }
172  }
173  for (; lowy <= hiy; lowy++)
174  for (lowx = x2; lowx <= hix; lowx++) {
175  auto &s = smap.get(lowx, lowy);
176  /* if (s->owner==Playernum) */
177  if (!mode) {
178  sprintf(buf, "%2d,%-2d ", lowx, lowy);
179  notify(Playernum, Governor, buf);
180  if ((d = desshow(Playernum, Governor, Race, s)) == CHAR_CLOAKED) {
181  sprintf(buf, "? ( ? )\n");
182  notify(Playernum, Governor, buf);
183  } else {
184  sprintf(
185  buf, " %c %c %6u%5u%4u%4u%4u%5lu%5lu%5lu%6d%s\n",
186  Dessymbols[s.condition], Dessymbols[s.type], s.owner, s.race,
187  s.eff, s.mobilization, s.fert, s.resource, s.troops, s.popn,
188  maxsupport(Race, s, compat, p.conditions[TOXIC]),
189  ((s.crystals && (Race->discoveries[D_CRYSTAL] || Race->God))
190  ? " yes"
191  : " "));
192  notify(Playernum, Governor, buf);
193  }
194  } else { /* mode */
195  switch (s.condition) {
196  case SectorType::SEC_SEA:
197  sect_char = CHAR_SEA;
198  break;
199  case SectorType::SEC_LAND:
200  sect_char = CHAR_LAND;
201  break;
202  case SectorType::SEC_MOUNT:
203  sect_char = CHAR_MOUNT;
204  break;
205  case SectorType::SEC_GAS:
206  sect_char = CHAR_GAS;
207  break;
208  case SectorType::SEC_PLATED:
209  sect_char = CHAR_PLATED;
210  break;
211  case SectorType::SEC_ICE:
212  sect_char = CHAR_ICE;
213  break;
214  case SectorType::SEC_DESERT:
215  sect_char = CHAR_DESERT;
216  break;
217  case SectorType::SEC_FOREST:
218  sect_char = CHAR_FOREST;
219  break;
220  default:
221  sect_char = '?';
222  break;
223  }
224  sprintf(buf, "%c %d %d %d %c %c %d %u %u %u %u %d %lu %lu %lu %d",
225  CSP_CLIENT, CSP_SURVEY_SECTOR, lowx, lowy, sect_char,
226  desshow(Playernum, Governor, Race, s),
227  ((s.condition == SectorType::SEC_WASTED) ? 1 : 0), s.owner,
228  s.eff, s.fert, s.mobilization,
229  ((s.crystals && (Race->discoveries[D_CRYSTAL] || Race->God))
230  ? 1
231  : 0),
232  s.resource, s.popn, s.troops,
233  maxsupport(Race, s, compat, p.conditions[TOXIC]));
234  notify(Playernum, Governor, buf);
235 
236  if (shiplocs[lowx][lowy].pos && inhere) {
237  g.out << ";";
238  for (i = 0; i < shiplocs[lowx][lowy].pos; i++) {
239  sprintf(buf, " %d %c %u;",
240  shiplocs[lowx][lowy].shipstuffs[i].shipno,
241  shiplocs[lowx][lowy].shipstuffs[i].ltr,
242  shiplocs[lowx][lowy].shipstuffs[i].owner);
243  notify(Playernum, Governor, buf);
244  }
245  }
246  g.out << "\n";
247  }
248  }
249  if (mode) {
250  sprintf(buf, "%c %d\n", CSP_CLIENT, CSP_SURVEY_END);
251  notify(Playernum, Governor, buf);
252  }
253  } else {
254  /* survey of planet */
255  sprintf(buf, "%s:\n", Stars[where.snum]->pnames[where.pnum]);
256  notify(Playernum, Governor, buf);
257  sprintf(buf, "gravity x,y absolute x,y relative to %s\n",
258  Stars[where.snum]->name);
259  notify(Playernum, Governor, buf);
260  sprintf(buf, "%7.2f %7.1f,%7.1f %8.1f,%8.1f\n", gravity(p),
261  p.xpos + Stars[where.snum]->xpos,
262  p.ypos + Stars[where.snum]->ypos, p.xpos, p.ypos);
263  notify(Playernum, Governor, buf);
264  g.out << "======== Planetary conditions: ========\n";
265  g.out << "atmosphere concentrations:\n";
266  sprintf(buf, " methane %02d%%(%02d%%) oxygen %02d%%(%02d%%)\n",
267  p.conditions[METHANE], Race->conditions[METHANE],
268  p.conditions[OXYGEN], Race->conditions[OXYGEN]);
269  notify(Playernum, Governor, buf);
270  sprintf(buf,
271  " CO2 %02d%%(%02d%%) hydrogen %02d%%(%02d%%) "
272  "temperature: %3d (%3d)\n",
273  p.conditions[CO2], Race->conditions[CO2], p.conditions[HYDROGEN],
274  Race->conditions[HYDROGEN], p.conditions[TEMP],
275  Race->conditions[TEMP]);
276  notify(Playernum, Governor, buf);
277  sprintf(buf,
278  " nitrogen %02d%%(%02d%%) sulfur %02d%%(%02d%%) "
279  " normal: %3d\n",
280  p.conditions[NITROGEN], Race->conditions[NITROGEN],
281  p.conditions[SULFUR], Race->conditions[SULFUR],
282  p.conditions[RTEMP]);
283  notify(Playernum, Governor, buf);
284  sprintf(buf, " helium %02d%%(%02d%%) other %02d%%(%02d%%)\n",
285  p.conditions[HELIUM], Race->conditions[HELIUM],
286  p.conditions[OTHER], Race->conditions[OTHER]);
287  notify(Playernum, Governor, buf);
288  if ((tindex = p.conditions[TOXIC] / 10) < 0)
289  tindex = 0;
290  else if (tindex > 10)
291  tindex = 11;
292  sprintf(buf, " Toxicity: %d%% (%s)\n",
293  p.conditions[TOXIC], Tox[tindex]);
294  notify(Playernum, Governor, buf);
295  sprintf(buf, "Total planetary compatibility: %.2f%%\n",
296  compatibility(p, Race));
297  notify(Playernum, Governor, buf);
298 
299  auto smap = getsmap(p);
300 
301  crystal_count = avg_fert = avg_resource = 0;
302  for (lowx = 0; lowx < p.Maxx; lowx++)
303  for (lowy = 0; lowy < p.Maxy; lowy++) {
304  auto &s = smap.get(lowx, lowy);
305  avg_fert += s.fert;
306  avg_resource += s.resource;
307  if (Race->discoveries[D_CRYSTAL] || Race->God)
308  crystal_count += !!s.crystals;
309  }
310  sprintf(buf, "%29s: %d\n%29s: %d\n%29s: %d\n", "Average fertility",
311  avg_fert / (p.Maxx * p.Maxy), "Average resource",
312  avg_resource / (p.Maxx * p.Maxy), "Crystal sectors",
313  crystal_count);
314  notify(Playernum, Governor, buf);
315  if (LIMITED_RESOURCES) {
316  sprintf(buf, "%29s: %ld\n", "Total resource deposits",
317  p.total_resources);
318  notify(Playernum, Governor, buf);
319  }
320  sprintf(buf, "fuel_stock resource_stock dest_pot. %s ^%s\n",
321  Race->Metamorph ? "biomass" : "popltn",
322  Race->Metamorph ? "biomass" : "popltn");
323  notify(Playernum, Governor, buf);
324  sprintf(buf, "%10u %14lu %9u %7lu%11lu\n", p.info[Playernum - 1].fuel,
325  p.info[Playernum - 1].resource, p.info[Playernum - 1].destruct,
326  p.popn, p.maxpopn);
327  notify(Playernum, Governor, buf);
328  if (p.slaved_to) {
329  sprintf(buf, "This planet ENSLAVED to player %d!\n", p.slaved_to);
330  notify(Playernum, Governor, buf);
331  }
332  }
333  } else if (where.level == ScopeLevel::LEVEL_STAR) {
334  sprintf(buf, "Star %s\n", Stars[where.snum]->name);
335  notify(Playernum, Governor, buf);
336  sprintf(buf, "locn: %f,%f\n", Stars[where.snum]->xpos,
337  Stars[where.snum]->ypos);
338  notify(Playernum, Governor, buf);
339  if (Race->God) {
340  for (i = 0; i < Stars[where.snum]->numplanets; i++) {
341  sprintf(buf, " \"%s\"\n", Stars[where.snum]->pnames[i]);
342  notify(Playernum, Governor, buf);
343  }
344  }
345  sprintf(buf, "Gravity: %.2f\tInstability: ", Stars[where.snum]->gravity);
346  notify(Playernum, Governor, buf);
347 
348  if (Race->tech >= TECH_SEE_STABILITY || Race->God) {
349  sprintf(buf, "%d%% (%s)\n", Stars[where.snum]->stability,
350  Stars[where.snum]->stability < 20
351  ? "stable"
352  : Stars[where.snum]->stability < 40
353  ? "unstable"
354  : Stars[where.snum]->stability < 60
355  ? "dangerous"
356  : Stars[where.snum]->stability < 100
357  ? "WARNING! Nova iminent!"
358  : "undergoing nova");
359  notify(Playernum, Governor, buf);
360  } else {
361  sprintf(buf, "(cannot determine)\n");
362  notify(Playernum, Governor, buf);
363  }
364  sprintf(buf, "temperature class (1->10) %d\n",
365  Stars[where.snum]->temperature);
366  notify(Playernum, Governor, buf);
367  sprintf(buf, "%d planets are ", Stars[where.snum]->numplanets);
368  notify(Playernum, Governor, buf);
369  for (x2 = 0; x2 < Stars[where.snum]->numplanets; x2++) {
370  sprintf(buf, "%s ", Stars[where.snum]->pnames[x2]);
371  notify(Playernum, Governor, buf);
372  }
373  sprintf(buf, "\n");
374  notify(Playernum, Governor, buf);
375  } else if (where.level == ScopeLevel::LEVEL_UNIV) {
376  g.out << "It's just _there_, you know?\n";
377  } else {
378  g.out << "Illegal scope.\n";
379  }
380 } /* end survey */
381 
382 void repair(const command_t &argv, GameObj &g) {
383  const player_t Playernum = g.player;
384  const governor_t Governor = g.governor;
385  int lowx;
386  int hix;
387  int lowy;
388  int hiy;
389  int x2;
390  int sectors;
391  int cost;
392  placetype where;
393 
394  /* general code -- jpd -- */
395  if (argv.size() == 1) { /* no args */
396  where.level = g.level;
397  where.snum = g.snum;
398  where.pnum = g.pnum;
399  } else {
400  /* repairing a sector */
401  if (isdigit(argv[1][0]) && index(argv[1].c_str(), ',') != nullptr) {
402  if (g.level != ScopeLevel::LEVEL_PLAN) {
403  sprintf(buf, "There are no sectors here.\n");
404  notify(Playernum, Governor, buf);
405  return;
406  }
407  where.level = ScopeLevel::LEVEL_PLAN;
408  where.snum = g.snum;
409  where.pnum = g.pnum;
410 
411  } else {
412  where = getplace(g, argv[1], 0);
413  if (where.err || where.level == ScopeLevel::LEVEL_SHIP) return;
414  }
415  }
416 
417  if (where.level == ScopeLevel::LEVEL_PLAN) {
418  auto p = getplanet((int)where.snum, (int)where.pnum);
419  if (!p.info[Playernum - 1].numsectsowned) {
420  notify(Playernum, Governor,
421  "You don't own any sectors on this planet.\n");
422  return;
423  }
424  auto smap = getsmap(p);
425  if (isdigit(argv[1][0]) && index(argv[1].c_str(), ',') != nullptr) {
426  get4args(argv[1].c_str(), &x2, &hix, &lowy, &hiy);
427  /* ^^^ translate from lowx:hix,lowy:hiy */
428  x2 = std::max(0, x2);
429  hix = std::min(hix, p.Maxx - 1);
430  lowy = std::max(0, lowy);
431  hiy = std::min(hiy, p.Maxy - 1);
432  } else {
433  /* repair entire planet */
434  x2 = 0;
435  hix = p.Maxx - 1;
436  lowy = 0;
437  hiy = p.Maxy - 1;
438  }
439  sectors = 0;
440  cost = 0;
441 
442  for (; lowy <= hiy; lowy++)
443  for (lowx = x2; lowx <= hix; lowx++) {
444  if (p.info[Playernum - 1].resource >= SECTOR_REPAIR_COST) {
445  auto &s = smap.get(lowx, lowy);
446  if (s.condition == SectorType::SEC_WASTED &&
447  (s.owner == Playernum || !s.owner)) {
448  s.condition = s.type;
449  s.fert = std::min(100U, s.fert + 20);
450  p.info[Playernum - 1].resource -= SECTOR_REPAIR_COST;
451  cost += SECTOR_REPAIR_COST;
452  sectors += 1;
453  putsector(s, p, lowx, lowy);
454  }
455  }
456  }
457  putplanet(p, Stars[where.snum], (int)where.pnum);
458 
459  sprintf(buf, "%d sectors repaired at a cost of %d resources.\n", sectors,
460  cost);
461  notify(Playernum, Governor, buf);
462  } else {
463  sprintf(buf, "scope must be a planet.\n");
464  notify(Playernum, Governor, buf);
465  }
466 }
467 
468 static void get4args(const char *s, int *xl, int *xh, int *yl, int *yh) {
469  char s1[17];
470  char s2[17];
471  const char *p = s;
472 
473  sscanf(p, "%[^,]", s1);
474  while ((*p != ':') && (*p != ',')) p++;
475  if (*p == ':') {
476  sscanf(s1, "%d:%d", xl, xh);
477  while (*p != ',') p++;
478  } else if (*p == ',') {
479  sscanf(s1, "%d", xl);
480  *xh = (*xl);
481  }
482 
483  sscanf(p, "%s", s2);
484  while ((*p != ':') && (*p != '\0')) p++;
485  if (*p == ':') {
486  sscanf(s2, ",%d:%d", yl, yh);
487  } else {
488  sscanf(s2, ",%d,", yl);
489  *yh = (*yl);
490  }
491 }
#define CSP_SURVEY_SECTOR
Definition: csp.h:40
#define HELIUM
Definition: tweakables.h:37
#define SULFUR
Definition: tweakables.h:36
#define METHANE
Definition: tweakables.h:31
#define CSP_SURVEY_INTRO
Definition: csp.h:39
static void get4args(const char *, int *, int *, int *, int *)
Definition: survey.cc:468
void repair(const command_t &argv, GameObj &g)
Definition: survey.cc:382
Planet getplanet(const starnum_t star, const planetnum_t pnum)
Definition: files_shl.cc:335
#define MAX_SHIPS_PER_SECTOR
Definition: survey.cc:29
static const char * Tox[]
Definition: survey.cc:31
#define OXYGEN
Definition: tweakables.h:32
void putsector(const Sector &s, const Planet &p, const int x, const int y)
Definition: files_shl.cc:1076
#define TECH_SEE_STABILITY
Definition: tweakables.h:183
#define CSP_SURVEY_END
Definition: csp.h:41
#define TOXIC
Definition: tweakables.h:39
#define CO2
Definition: tweakables.h:33
#define CSP_CLIENT
Definition: csp_types.h:13
#define TEMP
Definition: tweakables.h:30
double compatibility(const Planet &planet, const Race *race)
Definition: max.cc:37
#define MAX_X
Definition: tweakables.h:75
#define OTHER
Definition: tweakables.h:38
#define SECTOR_REPAIR_COST
Definition: tweakables.h:139
void survey(const command_t &argv, GameObj &g)
Definition: survey.cc:47
#define MAX_Y
Definition: tweakables.h:76
void putplanet(const Planet &p, startype *star, const int pnum)
Definition: files_shl.cc:934
#define HYDROGEN
Definition: tweakables.h:34
#define D_CRYSTAL
Definition: races.h:111
SectorMap getsmap(const Planet &p)
Definition: files_shl.cc:522
#define NITROGEN
Definition: tweakables.h:35
#define RTEMP
Definition: tweakables.h:29
#define LIMITED_RESOURCES
Definition: tweakables.h:62
double gravity(const Planet &p)
Definition: max.cc:59
int maxsupport(const Race *r, const Sector &s, const double c, const int toxic)
Definition: max.cc:26
char desshow(const player_t Playernum, const governor_t Governor, const Race *r, const Sector &s)
Definition: map.cc:179