Galactic Bloodshed
doplanet.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 /* doplanet.c -- do one turn on a planet. */
6 
7 #include "gb/doplanet.h"
8 
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 
13 #include "gb/GB_server.h"
14 #include "gb/VN.h"
15 #include "gb/bombard.h"
16 #include "gb/buffers.h"
17 #include "gb/build.h"
18 #include "gb/dosector.h"
19 #include "gb/doship.h"
20 #include "gb/doturn.h"
21 #include "gb/files_shl.h"
22 #include "gb/fire.h"
23 #include "gb/load.h"
24 #include "gb/max.h"
25 #include "gb/move.h"
26 #include "gb/moveship.h"
27 #include "gb/perm.h"
28 #include "gb/power.h"
29 #include "gb/races.h"
30 #include "gb/ships.h"
31 #include "gb/shlmisc.h"
32 #include "gb/shootblast.h"
33 #include "gb/tech.h"
34 #include "gb/tele.h"
35 #include "gb/tweakables.h"
36 #include "gb/utils/rand.h"
37 #include "gb/vars.h"
38 
39 static void do_dome(Ship *, SectorMap &);
40 static void do_quarry(Ship *, Planet *, SectorMap &);
41 static void do_berserker(Ship *, Planet *);
42 static void do_recover(Planet *, int, int);
43 static double est_production(const Sector &);
44 static int moveship_onplanet(Ship *, Planet *);
45 static void plow(Ship *, Planet *, SectorMap &);
46 static void terraform(Ship *, Planet *, SectorMap &);
47 
48 int doplanet(const int starnum, Planet *planet, const int planetnum) {
49  int shipno;
50  int x;
51  int y;
52  int nukex;
53  int nukey;
54  int o = 0;
55  int i;
56  Ship *ship;
57  double fadd;
58  int timer = 20;
59  unsigned char allmod = 0;
60  unsigned char allexp = 0;
61 
62  auto smap = getsmap(*planet);
63  PermuteSects(*planet);
64  bzero((char *)Sectinfo, sizeof(Sectinfo));
65 
66  bzero((char *)avg_mob, sizeof(avg_mob));
67  bzero((char *)sects_gained, sizeof(sects_gained));
68  bzero((char *)sects_lost, sizeof(sects_lost));
69  bzero((char *)prod_res, sizeof(prod_res));
70  bzero((char *)prod_fuel, sizeof(prod_fuel));
71  bzero((char *)prod_destruct, sizeof(prod_destruct));
72  bzero((char *)prod_crystals, sizeof(prod_crystals));
73 
74  tot_resdep = prod_eff = prod_mob = tot_captured = 0;
75  Claims = 0;
76 
77  planet->maxpopn = 0;
78 
79  planet->popn = 0; /* initialize population for recount */
80  planet->troops = 0;
81  planet->total_resources = 0;
82 
83  /* reset global variables */
84  for (i = 1; i <= Num_races; i++) {
85  Compat[i - 1] = compatibility(*planet, races[i - 1]);
86  planet->info[i - 1].numsectsowned = 0;
87  planet->info[i - 1].troops = 0;
88  planet->info[i - 1].popn = 0;
89  planet->info[i - 1].est_production = 0.0;
90  prod_crystals[i - 1] = 0;
91  prod_fuel[i - 1] = 0;
92  prod_destruct[i - 1] = 0;
93  prod_res[i - 1] = 0;
94  avg_mob[i - 1] = 0;
95  }
96 
97  shipno = planet->ships;
98  while (shipno) {
99  ship = ships[shipno];
100  if (ship->alive && !ship->rad) {
101  /* planet level functions - do these here because they use the sector map
102  or affect planet production */
103  switch (ship->type) {
104  case ShipType::OTYPE_VN:
105  planet_doVN(ship, planet, smap);
106  break;
107  case ShipType::OTYPE_BERS:
108  if (!ship->destruct || !ship->bombard)
109  planet_doVN(ship, planet, smap);
110  else
111  do_berserker(ship, planet);
112  break;
113  case ShipType::OTYPE_TERRA:
114  if ((ship->on && landed(*ship) && ship->popn)) {
115  if (ship->fuel >= (double)FUEL_COST_TERRA)
116  terraform(ship, planet, smap);
117  else if (!ship->notified) {
118  ship->notified = 1;
119  msg_OOF(ship);
120  }
121  }
122  break;
123  case ShipType::OTYPE_PLOW:
124  if (ship->on && landed(*ship)) {
125  if (ship->fuel >= (double)FUEL_COST_PLOW)
126  plow(ship, planet, smap);
127  else if (!ship->notified) {
128  ship->notified = 1;
129  msg_OOF(ship);
130  }
131  } else if (ship->on) {
132  sprintf(buf, "K%lu is not landed.", ship->number);
133  push_telegram(ship->owner, ship->governor, buf);
134  } else {
135  sprintf(buf, "K%lu is not switched on.", ship->number);
136  push_telegram(ship->owner, ship->governor, buf);
137  }
138  break;
139  case ShipType::OTYPE_DOME:
140  if (ship->on && landed(*ship)) {
141  if (ship->resource >= RES_COST_DOME)
142  do_dome(ship, smap);
143  else {
144  sprintf(buf, "Y%lu does not have enough resources.",
145  ship->number);
146  push_telegram(ship->owner, ship->governor, buf);
147  }
148  } else if (ship->on) {
149  sprintf(buf, "Y%lu is not landed.", ship->number);
150  push_telegram(ship->owner, ship->governor, buf);
151  } else {
152  sprintf(buf, "Y%lu is not switched on.", ship->number);
153  push_telegram(ship->owner, ship->governor, buf);
154  }
155  break;
156  case ShipType::OTYPE_WPLANT:
157  if (landed(*ship))
158  if (ship->resource >= RES_COST_WPLANT &&
159  ship->fuel >= FUEL_COST_WPLANT)
160  prod_destruct[ship->owner - 1] += do_weapon_plant(ship);
161  else {
162  if (ship->resource < RES_COST_WPLANT) {
163  sprintf(buf, "W%lu does not have enough resources.",
164  ship->number);
165  push_telegram(ship->owner, ship->governor, buf);
166  } else {
167  sprintf(buf, "W%lu does not have enough fuel.", ship->number);
168  push_telegram(ship->owner, ship->governor, buf);
169  }
170  }
171  else {
172  sprintf(buf, "W%lu is not landed.", ship->number);
173  push_telegram(ship->owner, ship->governor, buf);
174  }
175  break;
176  case ShipType::OTYPE_QUARRY:
177  if ((ship->on && landed(*ship) && ship->popn)) {
178  if (ship->fuel >= FUEL_COST_QUARRY)
179  do_quarry(ship, planet, smap);
180  else if (!ship->notified) {
181  ship->on = 0;
182  msg_OOF(ship);
183  }
184  } else {
185  if (!ship->on) {
186  sprintf(buf, "q%lu is not switched on.", ship->number);
187  }
188  if (!landed(*ship)) {
189  sprintf(buf, "q%lu is not landed.", ship->number);
190  }
191  if (!ship->popn) {
192  sprintf(buf, "q%lu does not have workers aboard.", ship->number);
193  }
194  push_telegram(ship->owner, ship->governor, buf);
195  }
196  break;
197  default:
198  break;
199  }
200  /* add fuel for ships orbiting a gas giant */
201  if (!landed(*ship) && planet->type == PlanetType::GASGIANT) {
202  switch (ship->type) {
203  case ShipType::STYPE_TANKER:
204  fadd = FUEL_GAS_ADD_TANKER;
205  break;
206  case ShipType::STYPE_HABITAT:
207  fadd = FUEL_GAS_ADD_HABITAT;
208  break;
209  default:
210  fadd = FUEL_GAS_ADD;
211  break;
212  }
213  fadd = std::min((double)max_fuel(*ship) - ship->fuel, fadd);
214  rcv_fuel(ship, fadd);
215  }
216  }
217  shipno = ship->nextship;
218  }
219 
220  /* check for space mirrors (among other things) warming the planet */
221  /* if a change in any artificial warming/cooling trends */
222  planet->conditions[TEMP] = planet->conditions[RTEMP] +
223  Stinfo[starnum][planetnum].temp_add +
224  int_rand(-5, 5);
225 
226  (void)Getxysect(*planet, &x, &y, 1);
227 
228  while (Getxysect(*planet, &x, &y, 0)) {
229  auto &p = smap.get(x, y);
230 
231  if (p.owner && (p.popn || p.troops)) {
232  allmod = 1;
233  if (!Stars[starnum]->nova_stage) {
234  produce(Stars[starnum], *planet, p);
235  if (p.owner)
236  planet->info[p.owner - 1].est_production += est_production(p);
237  spread(*planet, p, x, y, smap);
238  } else {
239  /* damage sector from supernova */
240  p.resource++;
241  p.fert *= 0.8;
242  if (Stars[starnum]->nova_stage == 14)
243  p.popn = p.owner = p.troops = 0;
244  else
245  p.popn = round_rand((double)p.popn * .50);
246  }
247  Sectinfo[x][y].done = 1;
248  }
249 
250  if ((!p.popn && !p.troops) || !p.owner) {
251  p.owner = 0;
252  p.popn = p.troops = 0;
253  }
254 
255  /*
256  if (p->wasted) {
257  if (x>1 && x<planet->Maxx-2) {
258  if (p->des==DES_SEA || p->des==DES_GAS) {
259  if ( y>1 && y<planet->Maxy-2 &&
260  (!(p-1)->wasted || !(p+1)->wasted) && !random()%5)
261  p->wasted = 0;
262  } else if (p->des==DES_LAND || p->des==DES_MOUNT
263  || p->des==DES_ICE) {
264  if ( y>1 && y<planet->Maxy-2 && ((p-1)->popn || (p+1)->popn)
265  && !random()%10)
266  p->wasted = 0;
267  }
268  }
269  }
270  */
271  /*
272  if (Stars[starnum]->nova_stage) {
273  if (p->des==DES_ICE)
274  if(random()&01)
275  p->des = DES_LAND;
276  else if (p->des==DES_SEA)
277  if(random()&01)
278  if ( (x>0 && (p-1)->des==DES_LAND) ||
279  (x<planet->Maxx-1 && (p+1)->des==DES_LAND) ||
280  (y>0 && (p-planet->Maxx)->des==DES_LAND) ||
281  (y<planet->Maxy-1 && (p+planet->Maxx)->des==DES_LAND
282  ) ) {
283  p->des = DES_LAND;
284  p->popn = p->owner = p->troops = 0;
285  p->resource += int_rand(1,5);
286  p->fert = int_rand(1,4);
287  }
288  }
289  */
290  }
291 
292  (void)Getxysect(*planet, &x, &y, 1);
293  while (Getxysect(*planet, &x, &y, 0)) {
294  auto &p = smap.get(x, y);
295  if (p.owner) planet->info[p.owner - 1].numsectsowned++;
296  }
297 
298  if (planet->expltimer >= 1) planet->expltimer--;
299  if (!Stars[starnum]->nova_stage && !planet->expltimer) {
300  if (!planet->expltimer) planet->expltimer = 5;
301  for (i = 1; !Claims && !allexp && i <= Num_races; i++) {
302  /* sectors have been modified for this player*/
303  if (planet->info[i - 1].numsectsowned)
304  while (!Claims && !allexp && timer > 0) {
305  timer -= 1;
306  o = 1;
307  (void)Getxysect(*planet, &x, &y, 1);
308  while (!Claims && Getxysect(*planet, &x, &y, 0)) {
309  /* find out if all sectors have been explored */
310  o &= Sectinfo[x][y].explored;
311  auto &p = smap.get(x, y);
312  if (((Sectinfo[x][y].explored == i) && !(random() & 02)) &&
313  (!p.owner && p.condition != SectorType::SEC_WASTED &&
314  p.condition == races[i - 1]->likesbest)) {
315  /* explorations have found an island */
316  Claims = i;
317  p.popn = races[i - 1]->number_sexes;
318  p.owner = i;
319  tot_captured = 1;
320  } else
321  explore(*planet, p, x, y, i);
322  }
323  allexp |= o; /* all sectors explored for this player */
324  }
325  }
326  }
327 
328  if (allexp) planet->expltimer = 5;
329 
330  /* environment nukes a random sector */
331  if (planet->conditions[TOXIC] > ENVIR_DAMAGE_TOX) {
332  // TODO(jeffbailey): Replace this with getrandom.
333  nukex = int_rand(0, (int)planet->Maxx - 1);
334  nukey = int_rand(0, (int)planet->Maxy - 1);
335  auto &p = smap.get(nukex, nukey);
336  p.condition = SectorType::SEC_WASTED;
337  p.popn = p.owner = p.troops = 0;
338  }
339 
340  for (i = 1; i <= Num_races; i++)
341  if (sects_gained[i - 1] || sects_lost[i - 1]) {
342  sprintf(telegram_buf, "****** Report: Planet /%s/%s ******\n",
343  Stars[starnum]->name, Stars[starnum]->pnames[planetnum]);
344  sprintf(buf, " WAR STATUS: %d sectors gained, %d sectors lost.\n",
345  sects_gained[i - 1], sects_lost[i - 1]);
346  strcat(telegram_buf, buf);
347  push_telegram(i, (int)Stars[starnum]->governor[i - 1], telegram_buf);
348  }
349  for (i = 1; i <= Num_races; i++) {
350  planet->info[i - 1].prod_crystals = prod_crystals[i - 1];
351  planet->info[i - 1].prod_res = prod_res[i - 1];
352  planet->info[i - 1].prod_fuel = prod_fuel[i - 1];
353  planet->info[i - 1].prod_dest = prod_destruct[i - 1];
354  if (planet->info[i - 1].autorep) {
355  planet->info[i - 1].autorep--;
356  sprintf(telegram_buf, "\nFrom /%s/%s\n", Stars[starnum]->name,
357  Stars[starnum]->pnames[planetnum]);
358 
359  if (Stinfo[starnum][planetnum].temp_add) {
360  sprintf(buf, "Temp: %d to %d\n", planet->conditions[RTEMP],
361  planet->conditions[TEMP]);
362  strcat(telegram_buf, buf);
363  }
364  sprintf(buf, "Total Prod: %ldr %ldf %ldd\n", prod_res[i - 1],
365  prod_fuel[i - 1], prod_destruct[i - 1]);
366  strcat(telegram_buf, buf);
367  if (prod_crystals[i - 1]) {
368  sprintf(buf, " %ld crystals found\n", prod_crystals[i - 1]);
369  strcat(telegram_buf, buf);
370  }
371  if (tot_captured) {
372  sprintf(buf, "%ld sectors captured\n", tot_captured);
373  strcat(telegram_buf, buf);
374  }
375  if (Stars[starnum]->nova_stage) {
376  sprintf(buf, "This planet's primary is in a Stage %d nova.\n",
377  Stars[starnum]->nova_stage);
378  strcat(telegram_buf, buf);
379  }
380  /* remind the player that he should clean up the environment. */
381  if (planet->conditions[TOXIC] > ENVIR_DAMAGE_TOX) {
382  sprintf(buf, "Environmental damage on sector %d,%d\n", nukex, nukey);
383  strcat(telegram_buf, buf);
384  }
385  if (planet->slaved_to) {
386  sprintf(buf, "ENSLAVED to player %d\n", planet->slaved_to);
387  strcat(telegram_buf, buf);
388  }
389  push_telegram(i, Stars[starnum]->governor[i - 1], telegram_buf);
390  }
391  }
392 
393  /* find out who is on this planet, for nova notification */
394  if (Stars[starnum]->nova_stage == 1) {
395  sprintf(telegram_buf, "BULLETIN from /%s/%s\n", Stars[starnum]->name,
396  Stars[starnum]->pnames[planetnum]);
397  sprintf(buf, "\nStar %s is undergoing nova.\n", Stars[starnum]->name);
398  strcat(telegram_buf, buf);
399  if (planet->type == PlanetType::EARTH ||
400  planet->type == PlanetType::WATER ||
401  planet->type == PlanetType::FOREST) {
402  sprintf(buf, "Seas and rivers are boiling!\n");
403  strcat(telegram_buf, buf);
404  }
405  sprintf(buf, "This planet must be evacuated immediately!\n%c", TELEG_DELIM);
406  strcat(telegram_buf, buf);
407  for (i = 1; i <= Num_races; i++)
408  if (planet->info[i - 1].numsectsowned)
409  push_telegram(i, Stars[starnum]->governor[i - 1], telegram_buf);
410  }
411 
412  do_recover(planet, starnum, planetnum);
413 
414  planet->popn = 0;
415  planet->troops = 0;
416  planet->maxpopn = 0;
417  planet->total_resources = 0;
418 
419  for (i = 1; i <= Num_races; i++) {
420  planet->info[i - 1].numsectsowned = 0;
421  planet->info[i - 1].popn = 0;
422  planet->info[i - 1].troops = 0;
423  }
424 
425  (void)Getxysect(*planet, &x, &y, 1);
426  while (Getxysect(*planet, &x, &y, 0)) {
427  auto &p = smap.get(x, y);
428  if (p.owner) {
429  planet->info[p.owner - 1].numsectsowned++;
430  planet->info[p.owner - 1].troops += p.troops;
431  planet->info[p.owner - 1].popn += p.popn;
432  planet->popn += p.popn;
433  planet->troops += p.troops;
434  planet->maxpopn += maxsupport(races[p.owner - 1], p, Compat[p.owner - 1],
435  planet->conditions[TOXIC]);
436  Power[p.owner - 1].troops += p.troops;
437  Power[p.owner - 1].popn += p.popn;
438  Power[p.owner - 1].sum_eff += p.eff;
439  Power[p.owner - 1].sum_mob += p.mobilization;
440  starpopns[starnum][p.owner - 1] += p.popn;
441  } else {
442  p.popn = 0;
443  p.troops = 0;
444  }
445  planet->total_resources += p.resource;
446  }
447 
448  /* deal with enslaved planets */
449  if (planet->slaved_to) {
450  if (planet->info[planet->slaved_to - 1].popn > planet->popn / 1000) {
451  for (i = 1; i <= Num_races; i++)
452  /* add production to slave holder of planet */
453  if (planet->info[i - 1].numsectsowned) {
454  planet->info[planet->slaved_to - 1].resource += prod_res[i - 1];
455  prod_res[i - 1] = 0;
456  planet->info[planet->slaved_to - 1].fuel += prod_fuel[i - 1];
457  prod_fuel[i - 1] = 0;
458  planet->info[planet->slaved_to - 1].destruct += prod_destruct[i - 1];
459  prod_destruct[i - 1] = 0;
460  }
461  } else {
462  /* slave revolt! */
463  /* first nuke some random sectors from the revolt */
464  i = planet->popn / 1000 + 1;
465  while (--i) {
466  auto &p = smap.get(int_rand(0, (int)planet->Maxx - 1),
467  int_rand(0, (int)planet->Maxy - 1));
468  if (p.popn + p.troops) {
469  p.owner = p.popn = p.troops = 0;
470  p.condition = SectorType::SEC_WASTED;
471  }
472  }
473  /* now nuke all sectors belonging to former master */
474  (void)Getxysect(*planet, &x, &y, 1);
475  while (Getxysect(*planet, &x, &y, 0)) {
476  if (Stinfo[starnum][planetnum].intimidated && random() & 01) {
477  auto &p = smap.get(x, y);
478  if (p.owner == planet->slaved_to) {
479  p.owner = 0;
480  p.popn = 0;
481  p.troops = 0;
482  p.condition = SectorType::SEC_WASTED;
483  }
484  }
485  /* also add up the populations while here */
486  }
487  sprintf(telegram_buf, "\nThere has been a SLAVE REVOLT on /%s/%s!\n",
488  Stars[starnum]->name, Stars[starnum]->pnames[planetnum]);
489  strcat(telegram_buf, buf);
490  sprintf(buf,
491  "All population belonging to player #%d on the planet have "
492  "been killed!\n",
493  planet->slaved_to);
494  strcat(telegram_buf, buf);
495  sprintf(buf, "Productions now go to their rightful owners.\n");
496  strcat(telegram_buf, buf);
497  for (i = 1; i <= Num_races; i++)
498  if (planet->info[i - 1].numsectsowned)
499  push_telegram(i, (int)Stars[starnum]->governor[i - 1], telegram_buf);
500  planet->slaved_to = 0;
501  }
502  }
503 
504  /* add production to all people here */
505  for (i = 1; i <= Num_races; i++)
506  if (planet->info[i - 1].numsectsowned) {
507  planet->info[i - 1].fuel += prod_fuel[i - 1];
508  planet->info[i - 1].resource += prod_res[i - 1];
509  planet->info[i - 1].destruct += prod_destruct[i - 1];
510  planet->info[i - 1].crystals += prod_crystals[i - 1];
511 
512  /* tax the population - set new tax rate when done */
513  if (races[i - 1]->Gov_ship) {
514  planet->info[i - 1].prod_money =
515  round_rand(INCOME_FACTOR * (double)planet->info[i - 1].tax *
516  (double)planet->info[i - 1].popn);
517  races[i - 1]->governor[Stars[starnum]->governor[i - 1]].money +=
518  planet->info[i - 1].prod_money;
519  planet->info[i - 1].tax += std::min(
520  (int)planet->info[i - 1].newtax - (int)planet->info[i - 1].tax, 5);
521  } else
522  planet->info[i - 1].prod_money = 0;
523  races[i - 1]->governor[Stars[starnum]->governor[i - 1]].income +=
524  planet->info[i - 1].prod_money;
525 
526  /* do tech investments */
527  if (races[i - 1]->Gov_ship) {
528  if (races[i - 1]->governor[Stars[starnum]->governor[i - 1]].money >=
529  planet->info[i - 1].tech_invest) {
530  planet->info[i - 1].prod_tech =
531  tech_prod((int)(planet->info[i - 1].tech_invest),
532  (int)(planet->info[i - 1].popn));
533  races[i - 1]->governor[Stars[starnum]->governor[i - 1]].money -=
534  planet->info[i - 1].tech_invest;
535  races[i - 1]->tech += planet->info[i - 1].prod_tech;
536  races[i - 1]->governor[Stars[starnum]->governor[i - 1]].cost_tech +=
537  planet->info[i - 1].tech_invest;
538  } else
539  planet->info[i - 1].prod_tech = 0;
540  } else
541  planet->info[i - 1].prod_tech = 0;
542 
543  /* build wc's if it's been ordered */
544  if (planet->info[i - 1].tox_thresh > 0 &&
545  planet->conditions[TOXIC] >= planet->info[i - 1].tox_thresh &&
546  planet->info[i - 1].resource >=
547  Shipcost(ShipType::OTYPE_TOXWC, races[i - 1])) {
548  Ship *s2;
549  int t;
550  ++Num_ships;
551  ships = (Ship **)realloc(ships,
552  (unsigned)((Num_ships + 1) * sizeof(Ship *)));
553  s2 = ships[Num_ships] = (Ship *)malloc(sizeof(Ship));
554  bzero((char *)s2, sizeof(Ship));
555  s2->number = Num_ships;
556  s2->type = ShipType::OTYPE_TOXWC;
557 
558  s2->armor = Shipdata[ShipType::OTYPE_TOXWC][ABIL_ARMOR];
559  s2->guns = GTYPE_NONE;
560  s2->primary = Shipdata[ShipType::OTYPE_TOXWC][ABIL_GUNS];
561  s2->primtype = Shipdata[ShipType::OTYPE_TOXWC][ABIL_PRIMARY];
562  s2->secondary = Shipdata[ShipType::OTYPE_TOXWC][ABIL_GUNS];
563  s2->sectype = Shipdata[ShipType::OTYPE_TOXWC][ABIL_SECONDARY];
564  s2->max_crew = Shipdata[ShipType::OTYPE_TOXWC][ABIL_MAXCREW];
565  s2->max_resource = Shipdata[ShipType::OTYPE_TOXWC][ABIL_CARGO];
566  s2->max_fuel = Shipdata[ShipType::OTYPE_TOXWC][ABIL_FUELCAP];
567  s2->max_destruct = Shipdata[ShipType::OTYPE_TOXWC][ABIL_DESTCAP];
568  s2->max_speed = Shipdata[ShipType::OTYPE_TOXWC][ABIL_SPEED];
569  s2->build_cost = Shipcost(ShipType::OTYPE_TOXWC, races[i - 1]);
570  s2->size = ship_size(*s2);
571  s2->base_mass = 1.0; /* a hack */
572  s2->mass = s2->base_mass;
573  s2->alive = 1;
574  s2->active = 1;
575  sprintf(s2->name, "Scum%04ld", Num_ships);
576 
577  insert_sh_plan(planet, s2);
578 
579  s2->whatorbits = ScopeLevel::LEVEL_PLAN;
580  s2->storbits = starnum;
581  s2->pnumorbits = planetnum;
582  s2->docked = 1;
583  s2->xpos = Stars[starnum]->xpos + planet->xpos;
584  s2->ypos = Stars[starnum]->ypos + planet->ypos;
585  s2->land_x = int_rand(0, (int)planet->Maxx - 1);
586  s2->land_y = int_rand(0, (int)planet->Maxy - 1);
587  s2->whatdest = ScopeLevel::LEVEL_PLAN;
588  s2->deststar = starnum;
589  s2->destpnum = planetnum;
590  s2->owner = i;
591  s2->governor = Stars[starnum]->governor[i - 1];
592  t = std::min(TOXMAX, planet->conditions[TOXIC]); /* amt of tox */
593  planet->conditions[TOXIC] -= t;
594  s2->special.waste.toxic = t;
595  }
596  } /* (if numsectsowned[i]) */
597 
598  if (planet->maxpopn > 0 && planet->conditions[TOXIC] < 100)
599  planet->conditions[TOXIC] += planet->popn / planet->maxpopn;
600 
601  if (planet->conditions[TOXIC] > 100)
602  planet->conditions[TOXIC] = 100;
603  else if (planet->conditions[TOXIC] < 0)
604  planet->conditions[TOXIC] = 0;
605 
606  for (i = 1; i <= Num_races; i++) {
607  Power[i - 1].resource += planet->info[i - 1].resource;
608  Power[i - 1].destruct += planet->info[i - 1].destruct;
609  Power[i - 1].fuel += planet->info[i - 1].fuel;
610  Power[i - 1].sectors_owned += planet->info[i - 1].numsectsowned;
611  Power[i - 1].planets_owned += !!planet->info[i - 1].numsectsowned;
612  if (planet->info[i - 1].numsectsowned) {
613  /* combat readiness naturally moves towards the avg mobilization */
614  planet->info[i - 1].mob_points = avg_mob[i - 1];
615  avg_mob[i - 1] /= (int)planet->info[i - 1].numsectsowned;
616  planet->info[i - 1].comread = avg_mob[i - 1];
617  } else
618  planet->info[i - 1].comread = 0;
619  planet->info[i - 1].guns = planet_guns(planet->info[i - 1].mob_points);
620  }
621  putsmap(smap, *planet);
622  return allmod;
623 }
624 
625 static int moveship_onplanet(Ship *ship, Planet *planet) {
626  int x;
627  int y;
628  int bounced = 0;
629 
630  if (ship->shipclass[ship->special.terraform.index] == 's') {
631  ship->on = 0;
632  return 0;
633  }
634  if (ship->shipclass[ship->special.terraform.index] == 'c')
635  ship->special.terraform.index = 0; /* reset the orders */
636 
637  (void)get_move(ship->shipclass[ship->special.terraform.index], ship->land_x,
638  ship->land_y, &x, &y, *planet);
639  if (y >= planet->Maxy) {
640  bounced = 1;
641  y -= 2; /* bounce off of south pole! */
642  } else if (y < 0)
643  bounced = y = 1; /* bounce off of north pole! */
644  if (planet->Maxy == 1) y = 0;
645  if (ship->shipclass[ship->special.terraform.index + 1] != '\0') {
646  ++ship->special.terraform.index;
647  if ((ship->shipclass[ship->special.terraform.index + 1] == '\0') &&
648  (!ship->notified)) {
649  char teleg_buf[1000];
650  ship->notified = 1;
651  sprintf(teleg_buf, "%s is out of orders at %s.",
652  ship_to_string(*ship).c_str(), prin_ship_orbits(ship));
653  push_telegram((int)(ship->owner), (int)ship->governor, teleg_buf);
654  }
655  } else if (bounced)
656  ship->shipclass[ship->special.terraform.index] +=
657  ((ship->shipclass[ship->special.terraform.index] > '5') ? -6 : 6);
658  ship->land_x = x;
659  ship->land_y = y;
660  return 1;
661 }
662 
663 static void terraform(Ship *ship, Planet *planet, SectorMap &smap) {
664  /* move, and then terraform. */
665  if (!moveship_onplanet(ship, planet)) return;
666  auto &s = smap.get(ship->land_x, ship->land_y);
667  if ((s.condition != races[ship->owner - 1]->likesbest) &&
668  (s.condition != SectorType::SEC_GAS) &&
669  success((100 - (int)ship->damage) * ship->popn / ship->max_crew)) {
670  /* gas sectors can't be terraformed. */
671  /* only condition can be terraformed, type doesn't change */
672  s.condition = races[ship->owner - 1]->likesbest;
673  s.eff = 0;
674  s.mobilization = 0;
675  s.popn = s.troops = 0;
676  s.owner = 0;
678  if ((random() & 01) && (planet->conditions[TOXIC] < 100))
679  planet->conditions[TOXIC] += 1;
680  if ((ship->fuel < (double)FUEL_COST_TERRA) && (!ship->notified)) {
681  ship->notified = 1;
682  msg_OOF(ship);
683  }
684  } else if (s.condition == races[ship->owner - 1]->likesbest) {
685  sprintf(buf, " T%lu is full of zealots!!!", ship->number);
686  push_telegram(ship->owner, ship->governor, buf);
687  }
688  if (s.condition == SectorType::SEC_GAS) {
689  sprintf(buf, " T%lu is trying to terraform gas.", ship->number);
690  push_telegram(ship->owner, ship->governor, buf);
691  }
692 }
693 
694 static void plow(Ship *ship, Planet *planet, SectorMap &smap) {
695  if (!moveship_onplanet(ship, planet)) return;
696  auto &s = smap.get(ship->land_x, ship->land_y);
697  if ((races[ship->owner - 1]->likes[s.condition]) && (s.fert < 100)) {
698  int adjust = round_rand(
699  10 * (0.01 * (100.0 - (double)ship->damage) * (double)ship->popn) /
700  ship->max_crew);
701  if ((ship->fuel < (double)FUEL_COST_PLOW) && (!ship->notified)) {
702  ship->notified = 1;
703  msg_OOF(ship);
704  return;
705  }
706  s.fert = std::min(100u, s.fert + adjust);
707  if (s.fert >= 100) {
708  sprintf(buf, " K%lu is full of zealots!!!", ship->number);
709  push_telegram(ship->owner, ship->governor, buf);
710  }
712  if ((random() & 01) && (planet->conditions[TOXIC] < 100))
713  planet->conditions[TOXIC] += 1;
714  }
715 }
716 
717 static void do_dome(Ship *ship, SectorMap &smap) {
718  int adjust;
719 
720  auto &s = smap.get(ship->land_x, ship->land_y);
721  if (s.eff >= 100) {
722  sprintf(buf, " Y%lu is full of zealots!!!", ship->number);
723  push_telegram(ship->owner, ship->governor, buf);
724  return;
725  }
726  adjust = round_rand(.05 * (100. - (double)ship->damage) * (double)ship->popn /
727  ship->max_crew);
728  s.eff += adjust;
729  if (s.eff > 100) s.eff = 100;
731 }
732 
733 static void do_quarry(Ship *ship, Planet *planet, SectorMap &smap) {
734  int prod;
735  int tox;
736 
737  auto &s = smap.get(ship->land_x, ship->land_y);
738 
739  if ((ship->fuel < (double)FUEL_COST_QUARRY)) {
740  if (!ship->notified) msg_OOF(ship);
741  ship->notified = 1;
742  return;
743  }
744  /* nuke the sector */
745  s.condition = SectorType::SEC_WASTED;
746  prod = round_rand(races[ship->owner - 1]->metabolism * (double)ship->popn /
747  (double)ship->max_crew);
748  ship->fuel -= FUEL_COST_QUARRY;
749  prod_res[ship->owner - 1] += prod;
750  tox = int_rand(0, int_rand(0, prod));
751  planet->conditions[TOXIC] = std::min(100, planet->conditions[TOXIC] + tox);
752  if (s.fert >= prod)
753  s.fert -= prod;
754  else
755  s.fert = 0;
756 }
757 
758 static void do_berserker(Ship *ship, Planet *planet) {
759  if (ship->whatdest == ScopeLevel::LEVEL_PLAN &&
760  ship->whatorbits == ScopeLevel::LEVEL_PLAN && !landed(*ship) &&
761  ship->storbits == ship->deststar && ship->pnumorbits == ship->destpnum) {
762  if (!bombard(ship, planet, races[ship->owner - 1]))
763  ship->destpnum = int_rand(0, Stars[ship->storbits]->numplanets - 1);
764  else if (Sdata.VN_hitlist[ship->special.mind.who_killed - 1] > 0)
765  --Sdata.VN_hitlist[ship->special.mind.who_killed - 1];
766  }
767 }
768 
769 static void do_recover(Planet *planet, int starnum, int planetnum) {
770  int owners = 0;
771  int i;
772  int j;
773  int ownerbits[2];
774  int stolenres = 0;
775  int stolendes = 0;
776  int stolenfuel = 0;
777  int stolencrystals = 0;
778  int all_buddies_here = 1;
779 
780  ownerbits[0] = ownerbits[1] = 0;
781 
782  for (i = 1; i <= Num_races && all_buddies_here; i++) {
783  if (planet->info[i - 1].numsectsowned > 0) {
784  owners++;
785  setbit(ownerbits, i);
786  for (j = 1; j < i && all_buddies_here; j++)
787  if (isset(ownerbits, j) && (!isset(races[i - 1]->allied, j) ||
788  !isset(races[j - 1]->allied, i)))
789  all_buddies_here = 0;
790  } else { /* Player i owns no sectors */
791  if (i != 1) { /* Can't steal from God */
792  stolenres += planet->info[i - 1].resource;
793  stolendes += planet->info[i - 1].destruct;
794  stolenfuel += planet->info[i - 1].fuel;
795  stolencrystals += planet->info[i - 1].crystals;
796  }
797  }
798  }
799  if (all_buddies_here && owners != 0 &&
800  (stolenres > 0 || stolendes > 0 || stolenfuel > 0 ||
801  stolencrystals > 0)) {
802  /* Okay, we've got some loot to divvy up */
803  int shares = owners;
804  int res;
805  int des;
806  int fuel;
807  int crystals;
808  int givenres = 0;
809  int givendes = 0;
810  int givenfuel = 0;
811  int givencrystals = 0;
812 
813  for (i = 1; i <= Num_races; i++)
814  if (isset(ownerbits, i)) {
815  sprintf(telegram_buf, "Recovery Report: Planet /%s/%s\n",
816  Stars[starnum]->name, Stars[starnum]->pnames[planetnum]);
817  push_telegram(i, (int)Stars[starnum]->governor[i - 1], telegram_buf);
818  sprintf(telegram_buf, "%-14.14s %5s %5s %5s %5s\n", "", "res", "destr",
819  "fuel", "xtal");
820  push_telegram(i, (int)Stars[starnum]->governor[i - 1], telegram_buf);
821  }
822  /* First: give the loot the the conquerers */
823  for (i = 1; i <= Num_races && owners > 1; i++)
824  if (isset(ownerbits, i)) { /* We have a winnah! */
825  if ((res = round_rand((double)stolenres / shares)) + givenres >
826  stolenres)
827  res = stolenres - givenres;
828  if ((des = round_rand((double)stolendes / shares)) + givendes >
829  stolendes)
830  des = stolendes - givendes;
831  if ((fuel = round_rand((double)stolenfuel / shares)) + givenfuel >
832  stolenfuel)
833  fuel = stolenfuel - givenfuel;
834  if ((crystals = round_rand((double)stolencrystals / shares)) +
835  givencrystals >
836  stolencrystals)
837  crystals = stolencrystals - givencrystals;
838  planet->info[i - 1].resource += res;
839  givenres += res;
840  planet->info[i - 1].destruct += des;
841  givendes += des;
842  planet->info[i - 1].fuel += fuel;
843  givenfuel += fuel;
844  planet->info[i - 1].crystals += crystals;
845  givencrystals += crystals;
846 
847  owners--;
848  sprintf(telegram_buf, "%-14.14s %5d %5d %5d %5d", races[i - 1]->name,
849  res, des, fuel, crystals);
850  for (j = 1; j <= Num_races; j++)
851  if (isset(ownerbits, j))
852  push_telegram(j, (int)Stars[starnum]->governor[j - 1],
853  telegram_buf);
854  }
855  /* Leftovers for last player */
856  for (; i <= Num_races; i++)
857  if (isset(ownerbits, i)) break;
858  if (i <= Num_races) { /* It should be */
859  res = stolenres - givenres;
860  des = stolendes - givendes;
861  fuel = stolenfuel - givenfuel;
862  crystals = stolencrystals - givencrystals;
863 
864  planet->info[i - 1].resource += res;
865  planet->info[i - 1].destruct += des;
866  planet->info[i - 1].fuel += fuel;
867  planet->info[i - 1].crystals += crystals;
868  sprintf(telegram_buf, "%-14.14s %5d %5d %5d %5d", races[i - 1]->name, res,
869  des, fuel, crystals);
870  sprintf(buf, "%-14.14s %5d %5d %5d %5d\n", "Total:", stolenres, stolendes,
871  stolenfuel, stolencrystals);
872  for (j = 1; j <= Num_races; j++)
873  if (isset(ownerbits, j)) {
874  push_telegram(j, (int)Stars[starnum]->governor[j - 1], telegram_buf);
875  push_telegram(j, (int)Stars[starnum]->governor[j - 1], buf);
876  }
877  } else
878  push_telegram(1, 0, "Bug in stealing resources\n");
879  /* Next: take all the loot away from the losers */
880  for (i = 2; i <= Num_races; i++)
881  if (!isset(ownerbits, i)) {
882  planet->info[i - 1].resource = 0;
883  planet->info[i - 1].destruct = 0;
884  planet->info[i - 1].fuel = 0;
885  planet->info[i - 1].crystals = 0;
886  }
887  }
888 }
889 
890 static double est_production(const Sector &s) {
891  return (races[s.owner - 1]->metabolism * (double)s.eff * (double)s.eff /
892  200.0);
893 }
void msg_OOF(Ship *)
Definition: moveship.cc:355
void use_fuel(Ship *s, double amt)
Definition: load.cc:876
#define RES_COST_WPLANT
Definition: tweakables.h:175
static int moveship_onplanet(Ship *, Planet *)
Definition: doplanet.cc:625
static void do_recover(Planet *, int, int)
Definition: doplanet.cc:769
#define RES_COST_DOME
Definition: tweakables.h:174
#define FUEL_GAS_ADD
Definition: tweakables.h:128
#define ENVIR_DAMAGE_TOX
Definition: tweakables.h:187
void rcv_fuel(Ship *s, double amt)
Definition: load.cc:891
void explore(const Planet &, Sector &, int, int, int)
Definition: dosector.cc:170
void PermuteSects(const Planet &planet)
Definition: perm.cc:16
static void do_quarry(Ship *, Planet *, SectorMap &)
Definition: doplanet.cc:733
void spread(const Planet &, Sector &, int, int, SectorMap &)
Definition: dosector.cc:108
int do_weapon_plant(Ship *)
Definition: doship.cc:752
static void plow(Ship *, Planet *, SectorMap &)
Definition: doplanet.cc:694
#define isset(a, i)
Definition: vars.h:312
int planet_guns(int)
Definition: shootblast.cc:629
#define setbit(a, i)
Definition: vars.h:310
#define FUEL_GAS_ADD_TANKER
Definition: tweakables.h:129
#define FUEL_COST_TERRA
Definition: tweakables.h:171
#define FUEL_GAS_ADD_HABITAT
Definition: tweakables.h:130
static double est_production(const Sector &)
Definition: doplanet.cc:890
#define TELEG_DELIM
Definition: tweakables.h:95
#define TOXIC
Definition: tweakables.h:39
void putsmap(SectorMap &map, Planet &p)
Definition: files_shl.cc:1108
#define TEMP
Definition: tweakables.h:30
static void terraform(Ship *, Planet *, SectorMap &)
Definition: doplanet.cc:663
static void do_berserker(Ship *, Planet *)
Definition: doplanet.cc:758
double tech_prod(int investment, int popn)
Definition: tech.cc:69
int doplanet(const int, Planet *, const int)
Definition: doplanet.cc:48
int get_move(char direction, int x, int y, int *x2, int *y2, const Planet &planet)
Definition: move.cc:624
void use_resource(Ship *s, int amt)
Definition: load.cc:886
#define TOXMAX
Definition: tweakables.h:266
SectorMap getsmap(const Planet &p)
Definition: files_shl.cc:522
#define FUEL_COST_WPLANT
Definition: tweakables.h:176
int Getxysect(const Planet &p, int *x, int *y, int r)
Definition: perm.cc:39
#define RTEMP
Definition: tweakables.h:29
#define FUEL_COST_QUARRY
Definition: tweakables.h:172
#define INCOME_FACTOR
Definition: tweakables.h:274
#define FUEL_COST_PLOW
Definition: tweakables.h:173
static void do_dome(Ship *, SectorMap &)
Definition: doplanet.cc:717
void produce(startype *, const Planet &, Sector &)
Definition: dosector.cc:21