Galactic Bloodshed
order.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 /* order.c -- give orders to ship */
6 
7 #include "gb/order.h"
8 
9 #include <cmath>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 
14 #include "gb/GB_server.h"
15 #include "gb/buffers.h"
16 #include "gb/build.h"
17 #include "gb/files_shl.h"
18 #include "gb/fire.h"
19 #include "gb/getplace.h"
20 #include "gb/load.h"
21 #include "gb/moveship.h"
22 #include "gb/ships.h"
23 #include "gb/shlmisc.h"
24 #include "gb/shootblast.h"
25 #include "gb/tweakables.h"
26 #include "gb/vars.h"
27 
28 static std::string prin_aimed_at(const Ship &);
29 static void mk_expl_aimed_at(int, int, Ship *);
30 static void DispOrdersHeader(int, int);
31 static void DispOrders(int, int, Ship *);
32 static void give_orders(GameObj &, const command_t &, int, Ship *);
33 
34 void order(const command_t &argv, GameObj &g) {
35  player_t Playernum = g.player;
36  governor_t Governor = g.governor;
37  int APcount = 1;
38  shipnum_t shipno;
39  shipnum_t nextshipno;
40  Ship *ship;
41 
42  if (argv.size() == 1) { /* display all ship orders */
43  DispOrdersHeader(Playernum, Governor);
44  nextshipno = start_shiplist(g, "");
45  while ((shipno = do_shiplist(&ship, &nextshipno)))
46  if (ship->owner == Playernum && authorized(Governor, *ship)) {
47  DispOrders(Playernum, Governor, ship);
48  free(ship);
49  } else
50  free(ship);
51  } else if (argv.size() >= 2) {
52  DispOrdersHeader(Playernum, Governor);
53  nextshipno = start_shiplist(g, argv[1]);
54  while ((shipno = do_shiplist(&ship, &nextshipno)))
55  if (in_list(Playernum, argv[1], *ship, &nextshipno) &&
56  authorized(Governor, *ship)) {
57  if (argv.size() > 2) give_orders(g, argv, APcount, ship);
58  DispOrders(Playernum, Governor, ship);
59  free(ship);
60  } else
61  free(ship);
62  } else
63  g.out << "I don't understand what you mean.\n";
64 }
65 
66 // TODO(jeffbailey): We take in a non-zero APcount, and do nothing with it!
67 static void give_orders(GameObj &g, const command_t &argv, int /* APcount */,
68  Ship *ship) {
69  player_t Playernum = g.player;
70  governor_t Governor = g.governor;
71  int j;
72  placetype where;
73  placetype pl;
74 
75  if (!ship->active) {
76  sprintf(buf, "%s is irradiated (%d); it cannot be given orders.\n",
77  ship_to_string(*ship).c_str(), ship->rad);
78  notify(Playernum, Governor, buf);
79  return;
80  }
81  if (ship->type != ShipType::OTYPE_TRANSDEV && !ship->popn &&
82  max_crew(*ship)) {
83  sprintf(buf, "%s has no crew and is not a robotic ship.\n",
84  ship_to_string(*ship).c_str());
85  notify(Playernum, Governor, buf);
86  return;
87  }
88 
89  if (argv[2] == "defense") {
90  if (can_bombard(*ship)) {
91  if (argv[3] == "off")
92  ship->protect.planet = 0;
93  else
94  ship->protect.planet = 1;
95  } else {
96  notify(Playernum, Governor,
97  "That ship cannot be assigned those orders.\n");
98  return;
99  }
100  } else if (argv[2] == "scatter") {
101  if (ship->type != ShipType::STYPE_MISSILE) {
102  g.out << "Only missiles can be given this order.\n";
103  return;
104  }
105  ship->special.impact.scatter = 1;
106  } else if (argv[2] == "impact") {
107  int x;
108  int y;
109  if (ship->type != ShipType::STYPE_MISSILE) {
110  notify(Playernum, Governor,
111  "Only missiles can be designated for this.\n");
112  return;
113  }
114  sscanf(argv[3].c_str(), "%d,%d", &x, &y);
115  ship->special.impact.x = x;
116  ship->special.impact.y = y;
117  ship->special.impact.scatter = 0;
118  } else if (argv[2] == "jump") {
119  if (ship->docked) {
120  notify(Playernum, Governor,
121  "That ship is docked. Use 'launch' or 'undock' first.\n");
122  return;
123  }
124  if (ship->hyper_drive.has) {
125  if (argv[3] == "off")
126  ship->hyper_drive.on = 0;
127  else {
128  if (ship->whatdest != ScopeLevel::LEVEL_STAR &&
129  ship->whatdest != ScopeLevel::LEVEL_PLAN) {
130  g.out << "Destination must be star or planet.\n";
131  return;
132  }
133  ship->hyper_drive.on = 1;
134  ship->navigate.on = 0;
135  if (ship->mounted) {
136  ship->hyper_drive.charge = 1;
137  ship->hyper_drive.ready = 1;
138  }
139  }
140  } else {
141  notify(Playernum, Governor,
142  "This ship does not have hyper drive capability.\n");
143  return;
144  }
145  } else if (argv[2] == "protect") {
146  if (argv.size() > 3)
147  sscanf(argv[3].c_str() + (argv[3][0] == '#'), "%d", &j);
148  else
149  j = 0;
150  if (j == ship->number) {
151  g.out << "You can't do that.\n";
152  return;
153  }
154  if (can_bombard(*ship)) {
155  if (!j) {
156  ship->protect.on = 0;
157  } else {
158  ship->protect.on = 1;
159  ship->protect.ship = j;
160  }
161  } else {
162  g.out << "That ship cannot protect.\n";
163  return;
164  }
165  } else if (argv[2] == "navigate") {
166  if (argv.size() >= 5) {
167  ship->navigate.on = 1;
168  ship->navigate.bearing = std::stoi(argv[3]);
169  ship->navigate.turns = std::stoi(argv[4]);
170  } else
171  ship->navigate.on = 0;
172  if (ship->hyper_drive.on) ship->hyper_drive.on = 0;
173  } else if (argv[2] == "switch") {
174  if (ship->type == ShipType::OTYPE_FACTORY) {
175  g.out << "Use \"on\" to bring factory online.\n";
176  return;
177  }
178  if (has_switch(*ship)) {
179  if (ship->whatorbits == ScopeLevel::LEVEL_SHIP) {
180  g.out << "That ship is being transported.\n";
181  return;
182  }
183  ship->on = !ship->on;
184  } else {
185  sprintf(buf, "That ship does not have an on/off setting.\n");
186  notify(Playernum, Governor, buf);
187  return;
188  }
189  if (ship->on) {
190  switch (ship->type) {
191  case ShipType::STYPE_MINE:
192  g.out << "Mine armed and ready.\n";
193  break;
194  case ShipType::OTYPE_TRANSDEV:
195  g.out << "Transporter ready to receive.\n";
196  break;
197  default:
198  break;
199  }
200  } else {
201  switch (ship->type) {
202  case ShipType::STYPE_MINE:
203  g.out << "Mine disarmed.\n";
204  break;
205  case ShipType::OTYPE_TRANSDEV:
206  g.out << "No longer receiving.\n";
207  break;
208  default:
209  break;
210  }
211  }
212  } else if (argv[2] == "destination") {
213  if (speed_rating(*ship)) {
214  if (ship->docked) {
215  notify(Playernum, Governor,
216  "That ship is docked; use undock or launch first.\n");
217  return;
218  }
219  where = getplace(g, argv[3], 1);
220  if (!where.err) {
221  if (where.level == ScopeLevel::LEVEL_SHIP) {
222  auto tmpship = getship(where.shipno);
223  if (!followable(ship, &*tmpship)) {
224  g.out << "Warning: that ship is out of range.\n";
225  return;
226  }
227  ship->destshipno = where.shipno;
228  ship->whatdest = ScopeLevel::LEVEL_SHIP;
229  } else {
230  /* to foil cheaters */
231  if (where.level != ScopeLevel::LEVEL_UNIV &&
232  ((ship->storbits != where.snum) &&
233  where.level != ScopeLevel::LEVEL_STAR) &&
234  isclr(Stars[where.snum]->explored, ship->owner)) {
235  g.out << "You haven't explored this system.\n";
236  return;
237  }
238  ship->whatdest = where.level;
239  ship->deststar = where.snum;
240  ship->destpnum = where.pnum;
241  }
242  } else
243  return;
244  } else {
245  g.out << "That ship cannot be launched.\n";
246  return;
247  }
248  } else if (argv[2] == "evade") {
249  if (max_crew(*ship) && max_speed(*ship)) {
250  if (argv[3] == "on")
251  ship->protect.evade = 1;
252  else if (argv[3] == "off")
253  ship->protect.evade = 0;
254  } else
255  return;
256  } else if (argv[2] == "bombard") {
257  if (ship->type != ShipType::OTYPE_OMCL) {
258  if (can_bombard(*ship)) {
259  if (argv[3] == "off")
260  ship->bombard = 0;
261  else if (argv[3] == "on")
262  ship->bombard = 1;
263  } else
264  notify(Playernum, Governor,
265  "This type of ship cannot be set to retaliate.\n");
266  }
267  } else if (argv[2] == "retaliate") {
268  if (ship->type != ShipType::OTYPE_OMCL) {
269  if (can_bombard(*ship)) {
270  if (argv[3] == "off")
271  ship->protect.self = 0;
272  else if (argv[3] == "on")
273  ship->protect.self = 1;
274  } else
275  notify(Playernum, Governor,
276  "This type of ship cannot be set to retaliate.\n");
277  }
278  } else if (argv[2] == "focus") {
279  if (ship->laser) {
280  if (argv[3] == "on")
281  ship->focus = 1;
282  else
283  ship->focus = 0;
284  } else
285  g.out << "No laser.\n";
286  } else if (argv[2] == "laser") {
287  if (ship->laser) {
288  if (can_bombard(*ship)) {
289  if (ship->mounted) {
290  if (argv[3] == "on")
291  ship->fire_laser = std::stoi(argv[4]);
292  else
293  ship->fire_laser = 0;
294  } else
295  g.out << "You do not have a crystal mounted.\n";
296  } else
297  notify(Playernum, Governor,
298  "This type of ship cannot be set to retaliate.\n");
299  } else
300  notify(Playernum, Governor,
301  "This ship is not equipped with combat lasers.\n");
302  } else if (argv[2] == "merchant") {
303  if (argv[3] == "off")
304  ship->merchant = 0;
305  else {
306  j = std::stoi(argv[3]);
307  if (j < 0 || j > MAX_ROUTES) {
308  g.out << "Bad route number.\n";
309  return;
310  }
311  ship->merchant = j;
312  }
313  } else if (argv[2] == "speed") {
314  if (speed_rating(*ship)) {
315  j = std::stoi(argv[3]);
316  if (j < 0) {
317  g.out << "Specify a positive speed.\n";
318  return;
319  }
320  if (j > speed_rating(*ship)) j = speed_rating(*ship);
321  ship->speed = j;
322 
323  } else {
324  g.out << "This ship does not have a speed rating.\n";
325  return;
326  }
327  } else if (argv[2] == "salvo") {
328  if (can_bombard(*ship)) {
329  j = std::stoi(argv[3]);
330  if (j < 0) {
331  g.out << "Specify a positive number of guns.\n";
332  return;
333  }
334  if (ship->guns == PRIMARY && j > ship->primary)
335  j = ship->primary;
336  else if (ship->guns == SECONDARY && j > ship->secondary)
337  j = ship->secondary;
338  else if (ship->guns == GTYPE_NONE)
339  j = 0;
340 
341  ship->retaliate = j;
342 
343  } else {
344  g.out << "This ship cannot be set to retaliate.\n";
345  return;
346  }
347  } else if (argv[2] == "primary") {
348  if (ship->primary) {
349  if (argv.size() < 4) {
350  ship->guns = PRIMARY;
351  if (ship->retaliate > ship->primary) ship->retaliate = ship->primary;
352  } else {
353  j = std::stoi(argv[3]);
354  if (j < 0) {
355  notify(Playernum, Governor,
356  "Specify a nonnegative number of guns.\n");
357  return;
358  }
359  if (j > ship->primary) j = ship->primary;
360  ship->retaliate = j;
361  ship->guns = PRIMARY;
362  }
363  } else {
364  g.out << "This ship does not have primary guns.\n";
365  return;
366  }
367  } else if (argv[2] == "secondary") {
368  if (ship->secondary) {
369  if (argv.size() < 4) {
370  ship->guns = SECONDARY;
371  if (ship->retaliate > ship->secondary)
372  ship->retaliate = ship->secondary;
373  } else {
374  j = std::stoi(argv[3]);
375  if (j < 0) {
376  notify(Playernum, Governor,
377  "Specify a nonnegative number of guns.\n");
378  return;
379  }
380  if (j > ship->secondary) j = ship->secondary;
381  ship->retaliate = j;
382  ship->guns = SECONDARY;
383  }
384  } else {
385  g.out << "This ship does not have secondary guns.\n";
386  return;
387  }
388  } else if (argv[2] == "explosive") {
389  switch (ship->type) {
390  case ShipType::STYPE_MINE:
391  case ShipType::OTYPE_GR:
392  ship->mode = 0;
393  break;
394  default:
395  return;
396  }
397  } else if (argv[2] == "radiative") {
398  switch (ship->type) {
399  case ShipType::STYPE_MINE:
400  case ShipType::OTYPE_GR:
401  ship->mode = 1;
402  break;
403  default:
404  return;
405  }
406  } else if (argv[2] == "move") {
407  if ((ship->type != ShipType::OTYPE_TERRA) &&
408  (ship->type != ShipType::OTYPE_PLOW)) {
409  g.out << "That ship is not a terraformer or a space plow.\n";
410  return;
411  }
412  std::string moveseq;
413  if (argv.size() > 3) {
414  moveseq = argv[3];
415  } else { /* The move list might be empty.. */
416  moveseq = "5";
417  }
418  for (auto i = 0; i < moveseq.size(); ++i) {
419  /* Make sure the list of moves is short enough. */
420  if (i == SHIP_NAMESIZE - 1) {
421  sprintf(buf, "Warning: that is more than %d moves.\n",
422  SHIP_NAMESIZE - 1);
423  notify(Playernum, Governor, buf);
424  g.out << "These move orders have been truncated.\n";
425  moveseq.resize(i);
426  break;
427  }
428  /* Make sure this move is OK. */
429  if ((moveseq[i] == 'c') || (moveseq[i] == 's')) {
430  if ((i == 0) && (moveseq[0] == 'c')) {
431  g.out << "Cycling move orders can not be empty!\n";
432  return;
433  }
434  if (moveseq[i + 1]) {
435  sprintf(buf,
436  "Warning: '%c' should be the last character in the "
437  "move order.\n",
438  moveseq[i]);
439  notify(Playernum, Governor, buf);
440  g.out << "These move orders have been truncated.\n";
441  moveseq.resize(i + 1);
442  break;
443  }
444  } else if ((moveseq[i] < '1') || ('9' < moveseq[i])) {
445  sprintf(buf, "'%c' is not a valid move direction.\n", moveseq[i]);
446  notify(Playernum, Governor, buf);
447  return;
448  }
449  }
450  strcpy(ship->shipclass, moveseq.c_str());
451  /* This is the index keeping track of which order in shipclass is next. */
452  ship->special.terraform.index = 0;
453  } else if (argv[2] == "trigger") {
454  if (ship->type == ShipType::STYPE_MINE) {
455  if (std::stoi(argv[3]) < 0)
456  ship->special.trigger.radius = 0;
457  else
458  ship->special.trigger.radius = std::stoi(argv[3]);
459  } else {
460  notify(Playernum, Governor,
461  "This ship cannot be assigned a trigger radius.\n");
462  return;
463  }
464  } else if (argv[2] == "transport") {
465  if (ship->type == ShipType::OTYPE_TRANSDEV) {
466  ship->special.transport.target = std::stoi(argv[3]);
467  if (ship->special.transport.target == ship->number) {
468  notify(Playernum, Governor,
469  "A transporter cannot transport to itself.");
470  ship->special.transport.target = 0;
471  } else {
472  sprintf(buf, "Target ship is %d.\n", ship->special.transport.target);
473  notify(Playernum, Governor, buf);
474  }
475  } else {
476  g.out << "This ship is not a transporter.\n";
477  return;
478  }
479  } else if (argv[2] == "aim") {
480  if (can_aim(*ship)) {
481  if (ship->type == ShipType::OTYPE_GTELE ||
482  ship->type == ShipType::OTYPE_TRACT || ship->fuel >= FUEL_MANEUVER) {
483  if (ship->type == ShipType::STYPE_MIRROR && ship->docked) {
484  sprintf(buf, "docked; use undock or launch first.\n");
485  notify(Playernum, Governor, buf);
486  return;
487  }
488  pl = getplace(g, argv[3], 1);
489  if (pl.err) {
490  g.out << "Error in destination.\n";
491  return;
492  }
493  ship->special.aimed_at.level = pl.level;
494  ship->special.aimed_at.pnum = pl.pnum;
495  ship->special.aimed_at.snum = pl.snum;
496  ship->special.aimed_at.shipno = pl.shipno;
497  if (ship->type != ShipType::OTYPE_TRACT &&
498  ship->type != ShipType::OTYPE_GTELE)
500  if (ship->type == ShipType::OTYPE_GTELE ||
501  ship->type == ShipType::OTYPE_STELE)
502  mk_expl_aimed_at(Playernum, Governor, ship);
503  sprintf(buf, "Aimed at %s\n", prin_aimed_at(*ship).c_str());
504  notify(Playernum, Governor, buf);
505 
506  } else {
507  sprintf(buf, "Not enough maneuvering fuel (%.2f).\n", FUEL_MANEUVER);
508  notify(Playernum, Governor, buf);
509  return;
510  }
511  } else {
512  g.out << "You can't aim that kind of ship.\n";
513  return;
514  }
515  } else if (argv[2] == "intensity") {
516  if (ship->type == ShipType::STYPE_MIRROR) {
517  ship->special.aimed_at.intensity =
518  std::max(0, std::min(100, std::stoi(argv[3])));
519  }
520  } else if (argv[2] == "on") {
521  if (!has_switch(*ship)) {
522  notify(Playernum, Governor,
523  "This ship does not have an on/off setting.\n");
524  return;
525  }
526  if (ship->damage && ship->type != ShipType::OTYPE_FACTORY) {
527  g.out << "Damaged ships cannot be activated.\n";
528  return;
529  }
530  if (ship->on) {
531  g.out << "This ship is already activated.\n";
532  return;
533  }
534  if (ship->type == ShipType::OTYPE_FACTORY) {
535  unsigned int oncost;
536  if (ship->whatorbits == ScopeLevel::LEVEL_SHIP) {
537  int hangerneeded;
538 
539  auto s2 = getship(ship->destshipno);
540  if (s2->type == ShipType::STYPE_HABITAT) {
541  oncost = HAB_FACT_ON_COST * ship->build_cost;
542  if (s2->resource < oncost) {
543  sprintf(buf,
544  "You don't have %d resources on Habitat #%lu to "
545  "activate this factory.\n",
546  oncost, ship->destshipno);
547  notify(Playernum, Governor, buf);
548  return;
549  }
550  hangerneeded = (1 + (int)(HAB_FACT_SIZE * (double)ship_size(*ship))) -
551  ((s2->max_hanger - s2->hanger) + ship->size);
552  if (hangerneeded > 0) {
553  sprintf(
554  buf,
555  "Not enough hanger space free on Habitat #%lu. Need %d more.\n",
556  ship->destshipno, hangerneeded);
557  notify(Playernum, Governor, buf);
558  return;
559  }
560  s2->resource -= oncost;
561  s2->hanger -= ship->size;
562  ship->size = 1 + (int)(HAB_FACT_SIZE * (double)ship_size(*ship));
563  s2->hanger += ship->size;
564  putship(&*s2);
565  } else {
566  g.out << "The factory is currently being transported.\n";
567  return;
568  }
569  } else if (!landed(*ship)) {
570  g.out << "You cannot activate the factory here.\n";
571  return;
572  } else {
573  auto planet = getplanet(ship->deststar, ship->destpnum);
574  oncost = 2 * ship->build_cost;
575  if (planet.info[Playernum - 1].resource < oncost) {
576  sprintf(buf,
577  "You don't have %d resources on the planet to activate "
578  "this factory.\n",
579  oncost);
580  notify(Playernum, Governor, buf);
581  return;
582  }
583  planet.info[Playernum - 1].resource -= oncost;
584  putplanet(planet, Stars[ship->deststar], (int)ship->destpnum);
585  }
586  sprintf(buf, "Factory activated at a cost of %d resources.\n", oncost);
587  notify(Playernum, Governor, buf);
588  }
589  ship->on = 1;
590  } else if (argv[2] == "off") {
591  if (ship->type == ShipType::OTYPE_FACTORY && ship->on) {
592  notify(Playernum, Governor,
593  "You can't deactivate a factory once it's "
594  "online. Consider using 'scrap'.\n");
595  return;
596  }
597  ship->on = 0;
598  }
599  ship->notified = 0;
600  putship(ship);
601 }
602 
603 static std::string prin_aimed_at(const Ship &ship) {
604  placetype targ;
605 
606  targ.level = ship.special.aimed_at.level;
607  targ.snum = ship.special.aimed_at.snum;
608  targ.pnum = ship.special.aimed_at.pnum;
609  targ.shipno = ship.special.aimed_at.shipno;
610  return Dispplace(targ);
611 }
612 
614  placetype dest;
615 
616  dest.level = ship.whatdest;
617  dest.snum = ship.deststar;
618  dest.pnum = ship.destpnum;
619  dest.shipno = ship.destshipno;
620  return Dispplace(dest);
621 }
622 
623 /*
624  * mark wherever the ship is aimed at, as explored by the owning player.
625  */
626 static void mk_expl_aimed_at(int Playernum, int Governor, Ship *s) {
627  double dist;
628  startype *str;
629  double xf;
630  double yf;
631 
632  str = Stars[s->special.aimed_at.snum];
633 
634  xf = s->xpos;
635  yf = s->ypos;
636 
637  switch (s->special.aimed_at.level) {
638  case ScopeLevel::LEVEL_UNIV:
639  sprintf(buf, "There is nothing out here to aim at.");
640  notify(Playernum, Governor, buf);
641  break;
642  case ScopeLevel::LEVEL_STAR:
643  sprintf(buf, "Star %s ", prin_aimed_at(*s).c_str());
644  notify(Playernum, Governor, buf);
645  if ((dist = sqrt(Distsq(xf, yf, str->xpos, str->ypos))) <=
646  tele_range((int)s->type, s->tech)) {
647  getstar(&str, (int)s->special.aimed_at.snum);
648  setbit(str->explored, Playernum);
649  putstar(str, (int)s->special.aimed_at.snum);
650  sprintf(buf, "Surveyed, distance %g.\n", dist);
651  notify(Playernum, Governor, buf);
652  free(str);
653  } else {
654  sprintf(buf, "Too far to see (%g, max %g).\n", dist,
655  tele_range((int)s->type, s->tech));
656  notify(Playernum, Governor, buf);
657  }
658  break;
659  case ScopeLevel::LEVEL_PLAN: {
660  sprintf(buf, "Planet %s ", prin_aimed_at(*s).c_str());
661  notify(Playernum, Governor, buf);
662  auto p = getplanet(s->special.aimed_at.snum, s->special.aimed_at.pnum);
663  if ((dist =
664  sqrt(Distsq(xf, yf, str->xpos + p.xpos, str->ypos + p.ypos))) <=
665  tele_range((int)s->type, s->tech)) {
666  setbit(str->explored, Playernum);
667  p.info[Playernum - 1].explored = 1;
668  putplanet(p, Stars[s->special.aimed_at.snum],
669  (int)s->special.aimed_at.pnum);
670  sprintf(buf, "Surveyed, distance %g.\n", dist);
671  notify(Playernum, Governor, buf);
672  } else {
673  sprintf(buf, "Too far to see (%g, max %g).\n", dist,
674  tele_range((int)s->type, s->tech));
675  notify(Playernum, Governor, buf);
676  }
677  } break;
678  case ScopeLevel::LEVEL_SHIP:
679  sprintf(buf, "You can't see anything of use there.\n");
680  notify(Playernum, Governor, buf);
681  break;
682  }
683 }
684 
685 static void DispOrdersHeader(int Playernum, int Governor) {
686  notify(Playernum, Governor,
687  " # name sp orbits destin options\n");
688 }
689 
690 static void DispOrders(int Playernum, int Governor, Ship *ship) {
691  double distfac;
692 
693  if (ship->owner != Playernum || !authorized(Governor, *ship) || !ship->alive)
694  return;
695 
696  if (ship->docked)
697  if (ship->whatdest == ScopeLevel::LEVEL_SHIP)
698  sprintf(temp, "D#%lu", ship->destshipno);
699  else
700  sprintf(temp, "L%2d,%-2d", ship->land_x, ship->land_y);
701  else
702  strcpy(temp, prin_ship_dest(*ship).c_str());
703 
704  sprintf(buf, "%5lu %c %14.14s %c%1u %-10s %-10.10s ", ship->number,
705  Shipltrs[ship->type], ship->name,
706  ship->hyper_drive.has ? (ship->mounted ? '+' : '*') : ' ',
707  ship->speed, Dispshiploc_brief(ship), temp);
708 
709  if (ship->hyper_drive.on) {
710  sprintf(temp, "/jump %s %d",
711  (ship->hyper_drive.ready ? "ready" : "charging"),
712  ship->hyper_drive.charge);
713  strcat(buf, temp);
714  }
715  if (ship->protect.self) {
716  sprintf(temp, "/retal");
717  strcat(buf, temp);
718  }
719 
720  if (ship->guns == PRIMARY) {
721  switch (ship->primtype) {
722  case GTYPE_LIGHT:
723  sprintf(temp, "/lgt primary");
724  break;
725  case GTYPE_MEDIUM:
726  sprintf(temp, "/med primary");
727  break;
728  case GTYPE_HEAVY:
729  sprintf(temp, "/hvy primary");
730  break;
731  case GTYPE_NONE:
732  sprintf(temp, "/none");
733  break;
734  }
735  strcat(buf, temp);
736  } else if (ship->guns == SECONDARY) {
737  switch (ship->sectype) {
738  case GTYPE_LIGHT:
739  sprintf(temp, "/lgt secondary");
740  break;
741  case GTYPE_MEDIUM:
742  sprintf(temp, "/med secndry");
743  break;
744  case GTYPE_HEAVY:
745  sprintf(temp, "/hvy secndry");
746  break;
747  case GTYPE_NONE:
748  sprintf(temp, "/none");
749  break;
750  }
751  strcat(buf, temp);
752  }
753 
754  if (ship->fire_laser) {
755  sprintf(temp, "/laser %d", ship->fire_laser);
756  strcat(buf, temp);
757  }
758  if (ship->focus) strcat(buf, "/focus");
759 
760  if (ship->retaliate) {
761  sprintf(temp, "/salvo %d", ship->retaliate);
762  strcat(buf, temp);
763  }
764  if (ship->protect.planet) strcat(buf, "/defense");
765  if (ship->protect.on) {
766  sprintf(temp, "/prot %d", ship->protect.ship);
767  strcat(buf, temp);
768  }
769  if (ship->navigate.on) {
770  sprintf(temp, "/nav %d (%d)", ship->navigate.bearing, ship->navigate.turns);
771  strcat(buf, temp);
772  }
773  if (ship->merchant) {
774  sprintf(temp, "/merchant %d", ship->merchant);
775  strcat(buf, temp);
776  }
777  if (has_switch(*ship)) {
778  if (ship->on)
779  strcat(buf, "/on");
780  else
781  strcat(buf, "/off");
782  }
783  if (ship->protect.evade) strcat(buf, "/evade");
784  if (ship->bombard) strcat(buf, "/bomb");
785  if (ship->type == ShipType::STYPE_MINE || ship->type == ShipType::OTYPE_GR) {
786  if (ship->mode)
787  strcat(buf, "/radiate");
788  else
789  strcat(buf, "/explode");
790  }
791  if (ship->type == ShipType::OTYPE_TERRA ||
792  ship->type == ShipType::OTYPE_PLOW) {
793  int i;
794  sprintf(temp, "/move %s",
795  &(ship->shipclass[ship->special.terraform.index]));
796  if (temp[i = (strlen(temp) - 1)] == 'c') {
797  char c = ship->shipclass[ship->special.terraform.index];
798  ship->shipclass[ship->special.terraform.index] = '\0';
799  sprintf(temp + i, "%sc", ship->shipclass);
800  ship->shipclass[ship->special.terraform.index] = c;
801  }
802  strcat(buf, temp);
803  }
804 
805  if (ship->type == ShipType::STYPE_MISSILE &&
806  ship->whatdest == ScopeLevel::LEVEL_PLAN) {
807  if (ship->special.impact.scatter)
808  strcat(buf, "/scatter");
809  else {
810  sprintf(temp, "/impact %d,%d", ship->special.impact.x,
811  ship->special.impact.y);
812  strcat(buf, temp);
813  }
814  }
815 
816  if (ship->type == ShipType::STYPE_MINE) {
817  sprintf(temp, "/trigger %d", ship->special.trigger.radius);
818  strcat(buf, temp);
819  }
820  if (ship->type == ShipType::OTYPE_TRANSDEV) {
821  sprintf(temp, "/target %d", ship->special.transport.target);
822  strcat(buf, temp);
823  }
824  if (ship->type == ShipType::STYPE_MIRROR) {
825  sprintf(temp, "/aim %s/int %d", prin_aimed_at(*ship).c_str(),
826  ship->special.aimed_at.intensity);
827  strcat(buf, temp);
828  }
829 
830  strcat(buf, "\n");
831  notify(Playernum, Governor, buf);
832  /* if hyper space is on estimate how much fuel it will cost to get to the
833  * destination */
834  if (ship->hyper_drive.on) {
835  double dist;
836  double fuse;
837 
838  dist = sqrt(Distsq(ship->xpos, ship->ypos, Stars[ship->deststar]->xpos,
839  Stars[ship->deststar]->ypos));
840  distfac = HYPER_DIST_FACTOR * (ship->tech + 100.0);
841  if (ship->mounted && dist > distfac) {
842  fuse = HYPER_DRIVE_FUEL_USE * sqrt(ship->mass) * (dist / distfac);
843  } else {
844  fuse = HYPER_DRIVE_FUEL_USE * sqrt(ship->mass) * (dist / distfac) *
845  (dist / distfac);
846  }
847 
848  sprintf(buf, " *** distance %.0f - jump will cost %.1ff ***\n", dist,
849  fuse);
850  notify(Playernum, Governor, buf);
851  if (ship->max_fuel < fuse)
852  notify(Playernum, Governor,
853  "Your ship cannot carry enough fuel to do this jump.\n");
854  }
855 }
856 
857 void route(const command_t &argv, GameObj &g) {
858  // TODO(jeffbailey): This seems to segfault with no args.
859  player_t Playernum = g.player;
860  governor_t Governor = g.governor;
861  // TODO(jeffbailey): int APcount = 0;
862  int i;
863  int x;
864  int y;
865  unsigned char star;
866  unsigned char planet;
867  unsigned char load;
868  unsigned char unload;
869  const char *c;
870  placetype where;
871 
872  if (g.level != ScopeLevel::LEVEL_PLAN) {
873  notify(Playernum, Governor,
874  "You have to 'cs' to a planet to examine routes.\n");
875  return;
876  }
877  auto p = getplanet(g.snum, g.pnum);
878  if (argv.size() == 1) { /* display all shipping routes that are active */
879  for (i = 1; i <= MAX_ROUTES; i++)
880  if (p.info[Playernum - 1].route[i - 1].set) {
881  star = p.info[Playernum - 1].route[i - 1].dest_star;
882  planet = p.info[Playernum - 1].route[i - 1].dest_planet;
883  load = p.info[Playernum - 1].route[i - 1].load;
884  unload = p.info[Playernum - 1].route[i - 1].unload;
885  sprintf(buf, "%2d land %2d,%2d ", i,
886  p.info[Playernum - 1].route[i - 1].x,
887  p.info[Playernum - 1].route[i - 1].y);
888  strcat(buf, "load: ");
889  if (Fuel(load))
890  strcat(buf, "f");
891  else
892  strcat(buf, " ");
893  if (Destruct(load))
894  strcat(buf, "d");
895  else
896  strcat(buf, " ");
897  if (Resources(load))
898  strcat(buf, "r");
899  else
900  strcat(buf, " ");
901  if (Crystals(load)) strcat(buf, "x");
902  strcat(buf, " ");
903 
904  strcat(buf, " unload: ");
905  if (Fuel(unload))
906  strcat(buf, "f");
907  else
908  strcat(buf, " ");
909  if (Destruct(unload))
910  strcat(buf, "d");
911  else
912  strcat(buf, " ");
913  if (Resources(unload))
914  strcat(buf, "r");
915  else
916  strcat(buf, " ");
917  if (Crystals(unload))
918  strcat(buf, "x");
919  else
920  strcat(buf, " ");
921  sprintf(temp, " -> %s/%s\n", Stars[star]->name,
922  Stars[star]->pnames[planet]);
923  strcat(buf, temp);
924  notify(Playernum, Governor, buf);
925  }
926  g.out << "Done.\n";
927  return;
928  }
929  if (argv.size() == 2) {
930  i = std::stoi(argv[1]);
931  if (i > MAX_ROUTES || i < 1) {
932  g.out << "Bad route number.\n";
933  return;
934  }
935  if (p.info[Playernum - 1].route[i - 1].set) {
936  star = p.info[Playernum - 1].route[i - 1].dest_star;
937  planet = p.info[Playernum - 1].route[i - 1].dest_planet;
938  load = p.info[Playernum - 1].route[i - 1].load;
939  unload = p.info[Playernum - 1].route[i - 1].unload;
940  sprintf(buf, "%2d land %2d,%2d ", i,
941  p.info[Playernum - 1].route[i - 1].x,
942  p.info[Playernum - 1].route[i - 1].y);
943  if (load) {
944  sprintf(temp, "load: ");
945  strcat(buf, temp);
946  if (Fuel(load)) strcat(buf, "f");
947  if (Destruct(load)) strcat(buf, "d");
948  if (Resources(load)) strcat(buf, "r");
949  if (Crystals(load)) strcat(buf, "x");
950  }
951  if (unload) {
952  sprintf(temp, " unload: ");
953  strcat(buf, temp);
954  if (Fuel(unload)) strcat(buf, "f");
955  if (Destruct(unload)) strcat(buf, "d");
956  if (Resources(unload)) strcat(buf, "r");
957  if (Crystals(unload)) strcat(buf, "x");
958  }
959  sprintf(temp, " -> %s/%s\n", Stars[star]->name,
960  Stars[star]->pnames[planet]);
961  strcat(buf, temp);
962  notify(Playernum, Governor, buf);
963  }
964  g.out << "Done.\n";
965  return;
966  }
967  if (argv.size() == 3) {
968  i = std::stoi(argv[1]);
969  if (i > MAX_ROUTES || i < 1) {
970  g.out << "Bad route number.\n";
971  return;
972  }
973  if (argv[2] == "activate")
974  p.info[Playernum - 1].route[i - 1].set = 1;
975  else if (argv[2] == "deactivate")
976  p.info[Playernum - 1].route[i - 1].set = 0;
977  else {
978  where = getplace(g, argv[2], 1);
979  if (!where.err) {
980  if (where.level != ScopeLevel::LEVEL_PLAN) {
981  g.out << "You have to designate a planet.\n";
982  return;
983  }
984  p.info[Playernum - 1].route[i - 1].dest_star = where.snum;
985  p.info[Playernum - 1].route[i - 1].dest_planet = where.pnum;
986  g.out << "Set.\n";
987  } else {
988  g.out << "Illegal destination.\n";
989  return;
990  }
991  }
992  } else {
993  i = std::stoi(argv[1]);
994  if (i > MAX_ROUTES || i < 1) {
995  g.out << "Bad route number.\n";
996  return;
997  }
998  if (argv[2] == "land") {
999  sscanf(argv[3].c_str(), "%d,%d", &x, &y);
1000  if (x < 0 || x > p.Maxx - 1 || y < 0 || y > p.Maxy - 1) {
1001  g.out << "Bad sector coordinates.\n";
1002  return;
1003  }
1004  p.info[Playernum - 1].route[i - 1].x = x;
1005  p.info[Playernum - 1].route[i - 1].y = y;
1006  } else if (argv[2] == "load") {
1007  p.info[Playernum - 1].route[i - 1].load = 0;
1008  c = argv[3].c_str();
1009  while (*c) {
1010  if (*c == 'f') p.info[Playernum - 1].route[i - 1].load |= M_FUEL;
1011  if (*c == 'd') p.info[Playernum - 1].route[i - 1].load |= M_DESTRUCT;
1012  if (*c == 'r') p.info[Playernum - 1].route[i - 1].load |= M_RESOURCES;
1013  if (*c == 'x') p.info[Playernum - 1].route[i - 1].load |= M_CRYSTALS;
1014  c++;
1015  }
1016  } else if (argv[2] == "unload") {
1017  p.info[Playernum - 1].route[i - 1].unload = 0;
1018  c = argv[3].c_str();
1019  while (*c) {
1020  if (*c == 'f') p.info[Playernum - 1].route[i - 1].unload |= M_FUEL;
1021  if (*c == 'd') p.info[Playernum - 1].route[i - 1].unload |= M_DESTRUCT;
1022  if (*c == 'r') p.info[Playernum - 1].route[i - 1].unload |= M_RESOURCES;
1023  if (*c == 'x') p.info[Playernum - 1].route[i - 1].unload |= M_CRYSTALS;
1024  c++;
1025  }
1026  } else {
1027  g.out << "What are you trying to do?\n";
1028  return;
1029  }
1030  g.out << "Set.\n";
1031  }
1032  putplanet(p, Stars[g.snum], g.pnum);
1033 }
static void give_orders(GameObj &, const command_t &, int, Ship *)
Definition: order.cc:67
void use_fuel(Ship *s, double amt)
Definition: load.cc:876
#define FUEL_MANEUVER
Definition: tweakables.h:193
#define Distsq(x1, y1, x2, y2)
Definition: tweakables.h:218
static void DispOrdersHeader(int, int)
Definition: order.cc:685
char * Dispshiploc_brief(Ship *ship)
Definition: getplace.cc:188
Planet getplanet(const starnum_t star, const planetnum_t pnum)
Definition: files_shl.cc:335
#define Resources(x)
Definition: vars.h:104
#define M_RESOURCES
Definition: vars.h:100
#define SHIP_NAMESIZE
Definition: ships.h:102
#define Fuel(x)
Definition: vars.h:102
void order(const command_t &argv, GameObj &g)
Definition: order.cc:34
#define SECONDARY
Definition: ships.h:16
#define setbit(a, i)
Definition: vars.h:310
#define HYPER_DIST_FACTOR
Definition: tweakables.h:160
#define HYPER_DRIVE_FUEL_USE
Definition: tweakables.h:159
#define PRIMARY
Definition: ships.h:15
#define isclr(a, i)
Definition: vars.h:313
#define M_CRYSTALS
Definition: vars.h:101
static void mk_expl_aimed_at(int, int, Ship *)
Definition: order.cc:626
#define HAB_FACT_SIZE
Definition: tweakables.h:114
void putstar(startype *s, starnum_t snum)
Definition: files_shl.cc:813
#define M_FUEL
Definition: vars.h:98
#define Crystals(x)
Definition: vars.h:105
static std::string prin_aimed_at(const Ship &)
Definition: order.cc:603
shipnum_t do_shiplist(Ship **s, shipnum_t *nextshipno)
Definition: shlmisc.cc:94
void route(const command_t &argv, GameObj &g)
Definition: order.cc:857
void putplanet(const Planet &p, startype *star, const int pnum)
Definition: files_shl.cc:934
std::string prin_ship_dest(const Ship &ship)
Definition: order.cc:613
void getstar(startype **s, int star)
Definition: files_shl.cc:281
void putship(Ship *s)
Definition: files_shl.cc:1317
#define M_DESTRUCT
Definition: vars.h:99
#define HAB_FACT_ON_COST
Definition: tweakables.h:117
#define Destruct(x)
Definition: vars.h:103
static void DispOrders(int, int, Ship *)
Definition: order.cc:690
double tele_range(int, double)
Definition: shootblast.cc:539