Galactic Bloodshed
land.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 /* land.c -- land a ship
6  * also.... dock -- dock a ship w/ another ship
7  * and..... assault -- a very un-PC version of land/dock
8  */
9 
10 #include "gb/land.h"
11 
12 #include <cmath>
13 #include <cstdio>
14 #include <cstdlib>
15 
16 #include "gb/GB_server.h"
17 #include "gb/buffers.h"
18 #include "gb/config.h"
19 #include "gb/files.h"
20 #include "gb/files_shl.h"
21 #include "gb/fire.h"
22 #include "gb/getplace.h"
23 #include "gb/load.h"
24 #include "gb/max.h"
25 #include "gb/races.h"
26 #include "gb/ships.h"
27 #include "gb/shlmisc.h"
28 #include "gb/shootblast.h"
29 #include "gb/tele.h"
30 #include "gb/tweakables.h"
31 #include "gb/utils/rand.h"
32 #include "gb/vars.h"
33 
34 /// Determine whether the ship crashed or not.
35 static std::tuple<bool, int> crash(const Ship *s, const double fuel) noexcept {
36  // Crash from insufficient fuel.
37  if (s->fuel < fuel) return {true, 0};
38 
39  // Damaged ships stand of chance of crash landing.
40  if (auto roll = int_rand(1, 100); roll <= s->damage) return {true, roll};
41 
42  // No crash.
43  return {false, 0};
44 }
45 
46 void land(const command_t &argv, GameObj &g) {
47  player_t Playernum = g.player;
48  governor_t Governor = g.governor;
49  int APcount = 1;
50  Ship *s;
51 
52  shipnum_t shipno;
53  int x = -1;
54  int y = -1;
55  int i;
56  int numdest;
57  int strength;
58  double fuel;
59  double Dist;
60  racetype *Race;
61  racetype *alien;
62  shipnum_t nextshipno;
63 
64  numdest = 0; // TODO(jeffbailey): Init to zero.
65 
66  if (argv.size() < 2) {
67  g.out << "Land what?\n";
68  return;
69  }
70 
71  nextshipno = start_shiplist(g, argv[1]);
72 
73  while ((shipno = do_shiplist(&s, &nextshipno)))
74  if (in_list(Playernum, argv[1], *s, &nextshipno) &&
75  authorized(Governor, *s)) {
76  if (overloaded(s)) {
77  sprintf(buf, "%s is too overloaded to land.\n",
78  ship_to_string(*s).c_str());
79  notify(Playernum, Governor, buf);
80  free(s);
81  continue;
82  }
83  if (s->type == ShipType::OTYPE_QUARRY) {
84  g.out << "You can't load quarries onto ship.\n";
85  free(s);
86  continue;
87  }
88  if (docked(s)) {
89  g.out << "That ship is docked to another ship.\n";
90  free(s);
91  continue;
92  }
93 
94  /* attempting to land on a friendly ship (for carriers/stations/etc) */
95  if (argv[2][0] == '#') {
96  auto ship2tmp = string_to_shipnum(argv[2]);
97  if (!ship2tmp) {
98  sprintf(buf, "Ship %s wasn't found.\n", argv[2].c_str());
99  notify(Playernum, Governor, buf);
100  free(s);
101  continue;
102  }
103  auto s2 = getship(*ship2tmp);
104  if (!s2) {
105  sprintf(buf, "Ship #%lu wasn't found.\n", *ship2tmp);
106  notify(Playernum, Governor, buf);
107  free(s);
108  continue;
109  }
110  auto ship2no = *ship2tmp;
111  if (testship(Playernum, Governor, *s2)) {
112  g.out << "Illegal format.\n";
113  free(s);
114  continue;
115  }
116  if (s2->type == ShipType::OTYPE_FACTORY) {
117  g.out << "Can't land on factories.\n";
118  free(s);
119  continue;
120  }
121  if (landed(*s)) {
122  if (!landed(*s2)) {
123  sprintf(buf, "%s is not landed on a planet.\n",
124  ship_to_string(*s2).c_str());
125  notify(Playernum, Governor, buf);
126  free(s);
127  continue;
128  }
129  if (s2->storbits != s->storbits) {
130  notify(Playernum, Governor,
131  "These ships are not in the same star system.\n");
132  free(s);
133  continue;
134  }
135  if (s2->pnumorbits != s->pnumorbits) {
136  notify(Playernum, Governor,
137  "These ships are not landed on the same planet.\n");
138  free(s);
139  continue;
140  }
141  if ((s2->land_x != s->land_x) || (s2->land_y != s->land_y)) {
142  notify(Playernum, Governor,
143  "These ships are not in the same sector.\n");
144  free(s);
145  continue;
146  }
147  if (s->on) {
148  sprintf(buf, "%s must be turned off before loading.\n",
149  ship_to_string(*s).c_str());
150  notify(Playernum, Governor, buf);
151  free(s);
152  continue;
153  }
154  if (size(*s) > hanger(*s2)) {
155  sprintf(buf,
156  "Mothership does not have %d hanger space available "
157  "to load ship.\n",
158  size(*s));
159  notify(Playernum, Governor, buf);
160  free(s);
161  continue;
162  }
163  /* ok, load 'em up */
164  remove_sh_plan(*s);
165  /* get the target ship again because it had a pointer changed (and put
166  * to disk) in the remove routines */
167  s2 = getship(ship2no);
168  insert_sh_ship(s, &*s2);
169  /* increase mass of mothership */
170  s2->mass += s->mass;
171  s2->hanger += size(*s);
172  fuel = 0.0;
173  sprintf(buf, "%s loaded onto %s using %.1f fuel.\n",
174  ship_to_string(*s).c_str(), ship_to_string(*s2).c_str(),
175  fuel);
176  notify(Playernum, Governor, buf);
177  s->docked = 1;
178  putship(&*s2);
179  } else if (s->docked) {
180  sprintf(buf, "%s is already docked or landed.\n",
181  ship_to_string(*s).c_str());
182  notify(Playernum, Governor, buf);
183  free(s);
184  continue;
185  } else {
186  /* Check if the ships are in the same scope level. Maarten */
187  if (s->whatorbits != s2->whatorbits) {
188  notify(Playernum, Governor,
189  "Those ships are not in the same scope.\n");
190  free(s);
191  continue;
192  }
193 
194  /* check to see if close enough to land */
195  Dist = sqrt((double)Distsq(s2->xpos, s2->ypos, s->xpos, s->ypos));
196  if (Dist > DIST_TO_DOCK) {
197  sprintf(buf, "%s must be %.2f or closer to %s.\n",
198  ship_to_string(*s).c_str(), DIST_TO_DOCK,
199  ship_to_string(*s2).c_str());
200  notify(Playernum, Governor, buf);
201  free(s);
202  continue;
203  }
204  fuel = 0.05 + Dist * 0.025 * sqrt(s->mass);
205  if (s->fuel < fuel) {
206  sprintf(buf, "Not enough fuel.\n");
207  notify(Playernum, Governor, buf);
208  free(s);
209  continue;
210  }
211  if (size(*s) > hanger(*s2)) {
212  sprintf(buf,
213  "Mothership does not have %d hanger space available "
214  "to load ship.\n",
215  size(*s));
216  notify(Playernum, Governor, buf);
217  free(s);
218  continue;
219  }
220  use_fuel(s, fuel);
221 
222  /* remove the ship from whatever scope it is currently in */
223  if (s->whatorbits == ScopeLevel::LEVEL_PLAN)
224  remove_sh_plan(*s);
225  else if (s->whatorbits == ScopeLevel::LEVEL_STAR)
226  remove_sh_star(*s);
227  else {
228  g.out << "Ship is not in planet or star scope.\n";
229  free(s);
230  continue;
231  }
232  /* get the target ship again because it had a pointer changed (and put
233  * to disk) in the remove routines */
234  s2 = getship(ship2no);
235  insert_sh_ship(s, &*s2);
236  /* increase mass of mothership */
237  s2->mass += s->mass;
238  s2->hanger += size(*s);
239  sprintf(buf, "%s landed on %s using %.1f fuel.\n",
240  ship_to_string(*s).c_str(), ship_to_string(*s2).c_str(),
241  fuel);
242  notify(Playernum, Governor, buf);
243  s->docked = 1;
244  putship(&*s2);
245  }
246  } else { /* attempting to land on a planet */
247  if (s->docked) {
248  sprintf(buf, "%s is docked.\n", ship_to_string(*s).c_str());
249  notify(Playernum, Governor, buf);
250  free(s);
251  continue;
252  }
253  sscanf(argv[2].c_str(), "%d,%d", &x, &y);
254  if (s->whatorbits != ScopeLevel::LEVEL_PLAN) {
255  sprintf(buf, "%s doesn't orbit a planet.\n",
256  ship_to_string(*s).c_str());
257  notify(Playernum, Governor, buf);
258  free(s);
259  continue;
260  }
261  if (!Shipdata[s->type][ABIL_CANLAND]) {
262  sprintf(buf, "This ship is not equipped to land.\n");
263  notify(Playernum, Governor, buf);
264  free(s);
265  continue;
266  }
267  if ((s->storbits != g.snum) || (s->pnumorbits != g.pnum)) {
268  sprintf(buf, "You have to cs to the planet it orbits.\n");
269  notify(Playernum, Governor, buf);
270  free(s);
271  continue;
272  }
273  if (!speed_rating(*s)) {
274  sprintf(buf, "This ship is not rated for maneuvering.\n");
275  notify(Playernum, Governor, buf);
276  free(s);
277  continue;
278  }
279  if (!enufAP(Playernum, Governor, Stars[s->storbits]->AP[Playernum - 1],
280  APcount)) {
281  free(s);
282  continue;
283  }
284 
285  auto p = getplanet((int)s->storbits, (int)s->pnumorbits);
286 
287  sprintf(buf, "Planet /%s/%s has gravity field of %.2f.\n",
288  Stars[s->storbits]->name,
289  Stars[s->storbits]->pnames[s->pnumorbits], gravity(p));
290  notify(Playernum, Governor, buf);
291 
292  sprintf(buf, "Distance to planet: %.2f.\n",
293  Dist = sqrt((double)Distsq(Stars[s->storbits]->xpos + p.xpos,
294  Stars[s->storbits]->ypos + p.ypos,
295  s->xpos, s->ypos)));
296  notify(Playernum, Governor, buf);
297 
298  if (Dist > DIST_TO_LAND) {
299  sprintf(buf, "%s must be %.3g or closer to the planet (%.2f).\n",
300  ship_to_string(*s).c_str(), DIST_TO_LAND, Dist);
301  notify(Playernum, Governor, buf);
302  free(s);
303  continue;
304  }
305 
306  fuel = s->mass * gravity(p) * LAND_GRAV_MASS_FACTOR;
307 
308  if ((x < 0) || (y < 0) || (x > p.Maxx - 1) || (y > p.Maxy - 1)) {
309  sprintf(buf, "Illegal coordinates.\n");
310  notify(Playernum, Governor, buf);
311  free(s);
312  continue;
313  }
314 
315 #ifdef DEFENSE
316  /* people who have declared war on you will fire at your landing ship */
317  for (i = 1; i <= Num_races; i++)
318  if (s->alive && i != Playernum && p.info[i - 1].popn &&
319  p.info[i - 1].guns && p.info[i - 1].destruct) {
320  alien = races[i - 1];
321  if (isset(alien->atwar, (int)s->owner)) {
322  /* attack the landing ship */
323  strength =
324  MIN((int)p.info[i - 1].guns, (int)p.info[i - 1].destruct);
325  if (strength) {
326  post(temp, COMBAT);
327  notify_star(0, 0, s->storbits, temp);
328  warn(i, Stars[s->storbits]->governor[i - 1], buf);
329  notify((int)s->owner, (int)s->governor, buf);
330  p.info[i - 1].destruct -= strength;
331  }
332  }
333  }
334  if (!s->alive) {
335  putplanet(p, Stars[s->storbits], (int)s->pnumorbits);
336  putship(s);
337  free(s);
338  continue;
339  }
340 #endif
341  /* check to see if the ship crashes from lack of fuel or damage */
342  if (auto [did_crash, roll] = crash(s, fuel); did_crash) {
343  /* damaged ships stand of chance of crash landing */
344  auto smap = getsmap(p);
345  numdest = shoot_ship_to_planet(
346  s, &p, round_rand((double)(s->destruct) / 3.), x, y, smap, 0,
347  GTYPE_HEAVY, long_buf, short_buf);
348  putsmap(smap, p);
349  sprintf(
350  buf,
351  "BOOM!! %s crashes on sector %d,%d with blast radius of %d.\n",
352  ship_to_string(*s).c_str(), x, y, numdest);
353  for (i = 1; i <= Num_races; i++)
354  if (p.info[i - 1].numsectsowned || i == Playernum)
355  warn(i, Stars[s->storbits]->governor[i - 1], buf);
356  if (roll)
357  sprintf(buf, "Ship damage %d%% (you rolled a %d)\n", (int)s->damage,
358  roll);
359  else
360  sprintf(buf, "You had %.1ff while the landing required %.1ff\n",
361  s->fuel, fuel);
362  notify(Playernum, Governor, buf);
363  kill_ship((int)s->owner, s);
364  } else {
365  s->land_x = x;
366  s->land_y = y;
367  s->xpos = p.xpos + Stars[s->storbits]->xpos;
368  s->ypos = p.ypos + Stars[s->storbits]->ypos;
369  use_fuel(s, fuel);
370  s->docked = 1;
371  s->whatdest = ScopeLevel::LEVEL_PLAN; /* no destination */
372  s->deststar = s->storbits;
373  s->destpnum = s->pnumorbits;
374  }
375 
376  auto sect = getsector(p, x, y);
377 
378  if (sect.condition == SectorType::SEC_WASTED) {
379  sprintf(buf, "Warning: That sector is a wasteland!\n");
380  notify(Playernum, Governor, buf);
381  } else if (sect.owner && sect.owner != Playernum) {
382  Race = races[Playernum - 1];
383  alien = races[sect.owner - 1];
384  if (!(isset(Race->allied, sect.owner) &&
385  isset(alien->allied, Playernum))) {
386  sprintf(buf, "You have landed on an alien sector (%s).\n",
387  alien->name);
388  notify(Playernum, Governor, buf);
389  } else {
390  sprintf(buf, "You have landed on allied sector (%s).\n",
391  alien->name);
392  notify(Playernum, Governor, buf);
393  }
394  }
395  if (s->whatorbits == ScopeLevel::LEVEL_UNIV)
396  deductAPs(Playernum, Governor, APcount, 0, 1);
397  else
398  deductAPs(Playernum, Governor, APcount, (int)s->storbits, 0);
399 
400  putplanet(p, Stars[s->storbits], (int)s->pnumorbits);
401 
402  if (numdest) putsector(sect, p, x, y);
403 
404  /* send messages to anyone there */
405  sprintf(buf, "%s observed landing on sector %d,%d,planet /%s/%s.\n",
406  ship_to_string(*s).c_str(), s->land_x, s->land_y,
407  Stars[s->storbits]->name,
408  Stars[s->storbits]->pnames[s->pnumorbits]);
409  for (i = 1; i <= Num_races; i++)
410  if (p.info[i - 1].numsectsowned && i != Playernum)
411  notify(i, (int)Stars[s->storbits]->governor[i - 1], buf);
412 
413  sprintf(buf, "%s landed on planet.\n", ship_to_string(*s).c_str());
414  notify(Playernum, Governor, buf);
415  }
416  putship(s);
417  free(s);
418  } else
419  free(s);
420 }
421 
422 int docked(Ship *s) {
423  return (s->docked && s->whatdest == ScopeLevel::LEVEL_SHIP);
424 }
425 
426 int overloaded(Ship *s) {
427  return ((s->resource > max_resource(*s)) || (s->fuel > max_fuel(*s)) ||
428  (s->popn + s->troops > s->max_crew) ||
429  (s->destruct > max_destruct(*s)));
430 }
void use_fuel(Ship *s, double amt)
Definition: load.cc:876
#define LAND_GRAV_MASS_FACTOR
Definition: tweakables.h:126
#define Distsq(x1, y1, x2, y2)
Definition: tweakables.h:218
void post(const char *origmsg, int type)
Definition: tele.cc:63
#define DIST_TO_LAND
Definition: tweakables.h:194
Planet getplanet(const starnum_t star, const planetnum_t pnum)
Definition: files_shl.cc:335
int overloaded(Ship *s)
Definition: land.cc:426
void land(const command_t &argv, GameObj &g)
Definition: land.cc:46
#define isset(a, i)
Definition: vars.h:312
void putsector(const Sector &s, const Planet &p, const int x, const int y)
Definition: files_shl.cc:1076
int docked(Ship *s)
Definition: land.cc:422
int shoot_ship_to_planet(Ship *, Planet *, int, int, int, SectorMap &, int, int, char *, char *)
Definition: shootblast.cc:188
#define MIN(x, y)
Definition: tweakables.h:213
void putsmap(SectorMap &map, Planet &p)
Definition: files_shl.cc:1108
static std::tuple< bool, int > crash(const Ship *s, const double fuel) noexcept
Determine whether the ship crashed or not.
Definition: land.cc:35
void deductAPs(const player_t Playernum, const governor_t Governor, unsigned int n, starnum_t snum, int sdata)
Definition: shlmisc.cc:214
int enufAP(int Playernum, int Governor, unsigned short AP, int x)
Definition: shlmisc.cc:131
#define DEFENSE
Definition: config.h:30
shipnum_t do_shiplist(Ship **s, shipnum_t *nextshipno)
Definition: shlmisc.cc:94
void putplanet(const Planet &p, startype *star, const int pnum)
Definition: files_shl.cc:934
void putship(Ship *s)
Definition: files_shl.cc:1317
SectorMap getsmap(const Planet &p)
Definition: files_shl.cc:522
#define COMBAT
Definition: files.h:19
double gravity(const Planet &p)
Definition: max.cc:59
#define DIST_TO_DOCK
Definition: tweakables.h:195
Sector getsector(const Planet &p, const int x, const int y)
Definition: files_shl.cc:480