Galactic Bloodshed
fuel.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 /* fuel.c -- See estimations in fuel consumption and travel time. */
6 
7 #include "gb/fuel.h"
8 
9 #include <cmath>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 #include <ctime>
14 
15 #include "gb/GB_server.h"
16 #include "gb/doship.h"
17 #include "gb/files_shl.h"
18 #include "gb/fire.h"
19 #include "gb/getplace.h"
20 #include "gb/max.h"
21 #include "gb/moveship.h"
22 #include "gb/order.h"
23 #include "gb/ships.h"
24 #include "gb/shlmisc.h"
25 #include "gb/tweakables.h"
26 #include "gb/vars.h"
27 
28 static char plan_buf[1024];
29 
30 static segments_t number_segments;
31 static double x_0, y_0, x_1, y_1;
32 static Ship *tmpship;
33 
34 static int do_trip(const placetype &, double fuel, double gravity_factor);
35 static void fuel_output(int Playernum, int Governor, double dist, double fuel,
36  double grav, double mass, segments_t segs);
37 
38 void proj_fuel(const command_t &argv, GameObj &g) {
39  const player_t Playernum = g.player;
40  const governor_t Governor = g.governor;
41  int opt_settings;
42  int current_settings;
43  int computing = 1;
44  segments_t current_segs;
45  double fuel_usage;
46  double level;
47  double dist;
48  char buf[1024];
49  double current_fuel = 0.0;
50  double gravity_factor = 0.0;
51  placetype tmpdest;
52 
53  if ((argv.size() < 2) || (argv.size() > 3)) {
54  notify(Playernum, Governor,
55  "Invalid number of options.\n\"fuel "
56  "#<shipnumber> [destination]\"...\n");
57  return;
58  }
59  if (argv[1][0] != '#') {
60  notify(Playernum, Governor,
61  "Invalid first option.\n\"fuel #<shipnumber> [destination]\"...\n");
62  return;
63  }
64  auto shipno = string_to_shipnum(argv[1]);
65  if (!shipno || *shipno > Numships() || *shipno < 1) {
66  sprintf(buf, "rst: no such ship #%lu \n", *shipno);
67  notify(Playernum, Governor, buf);
68  return;
69  }
70  auto ship = getship(*shipno);
71  if (ship->owner != Playernum) {
72  g.out << "You do not own this ship.\n";
73  return;
74  }
75  if (landed(*ship) && (argv.size() == 2)) {
76  g.out << "You must specify a destination for landed or docked ships...\n";
77  return;
78  }
79  if (!ship->speed) {
80  g.out << "That ship is not moving!\n";
81  return;
82  }
83  if ((!speed_rating(*ship)) || (ship->type == ShipType::OTYPE_FACTORY)) {
84  g.out << "That ship does not have a speed rating...\n";
85  return;
86  }
87  if (landed(*ship) && (ship->whatorbits == ScopeLevel::LEVEL_PLAN)) {
88  const auto p = getplanet(ship->storbits, ship->pnumorbits);
89  gravity_factor = gravity(p);
90  sprintf(plan_buf, "/%s/%s", Stars[(int)ship->storbits]->name,
91  Stars[(int)ship->storbits]->pnames[(int)ship->pnumorbits]);
92  }
93  std::string deststr;
94  if (argv.size() == 2) {
95  deststr = prin_ship_dest(*ship);
96  } else {
97  deststr = argv[2];
98  }
99  tmpdest = getplace(g, deststr, 1);
100  if (tmpdest.err) {
101  g.out << "fuel: bad scope.\n";
102  return;
103  }
104  if (tmpdest.level == ScopeLevel::LEVEL_SHIP) {
105  (void)getship(&tmpship, tmpdest.shipno);
106  if (!followable(&*ship, tmpship)) {
107  g.out << "The ship's destination is out of range.\n";
108  free(tmpship);
109  return;
110  }
111  free(tmpship);
112  }
113  if (tmpdest.level != ScopeLevel::LEVEL_UNIV &&
114  tmpdest.level != ScopeLevel::LEVEL_SHIP &&
115  ((ship->storbits != tmpdest.snum) &&
116  tmpdest.level != ScopeLevel::LEVEL_STAR) &&
117  isclr(Stars[tmpdest.snum]->explored, ship->owner)) {
118  g.out << "You haven't explored the destination system.\n";
119  return;
120  }
121  if (tmpdest.level == ScopeLevel::LEVEL_UNIV) {
122  g.out << "Invalid ship destination.\n";
123  return;
124  }
125  x_0 = y_0 = x_1 = y_1 = 0.0;
126 
127  x_0 = ship->xpos;
128  y_0 = ship->ypos;
129 
130  if (tmpdest.level == ScopeLevel::LEVEL_UNIV) {
131  notify(Playernum, Governor,
132  "That ship currently has no destination orders...\n");
133  return;
134  }
135  if (tmpdest.level == ScopeLevel::LEVEL_SHIP) {
136  (void)getship(&tmpship, tmpdest.shipno);
137  if (tmpship->owner != Playernum) {
138  g.out << "Nice try.\n";
139  return;
140  }
141  x_1 = tmpship->xpos;
142  y_1 = tmpship->ypos;
143  free(tmpship);
144  } else if (tmpdest.level == ScopeLevel::LEVEL_PLAN) {
145  const auto p = getplanet(tmpdest.snum, tmpdest.pnum);
146  x_1 = p.xpos + Stars[tmpdest.snum]->xpos;
147  y_1 = p.ypos + Stars[tmpdest.snum]->ypos;
148  } else if (tmpdest.level == ScopeLevel::LEVEL_STAR) {
149  x_1 = Stars[tmpdest.snum]->xpos;
150  y_1 = Stars[tmpdest.snum]->ypos;
151  } else
152  printf("ERROR 99\n");
153 
154  /* compute the distance */
155  dist = sqrt(Distsq(x_0, y_0, x_1, y_1));
156 
157  if (dist <= DIST_TO_LAND) {
158  notify(Playernum, Governor,
159  "That ship is within 10.0 units of the destination.\n");
160  return;
161  }
162 
163  /* First get the results based on current fuel load. */
164  (void)getship(&tmpship, *shipno);
165  level = tmpship->fuel;
166  current_settings = do_trip(tmpdest, tmpship->fuel, gravity_factor);
167  current_segs = number_segments;
168  if (current_settings) current_fuel = level - tmpship->fuel;
169  free(tmpship);
170 
171  /* 2nd loop to determine lowest fuel needed... */
172  fuel_usage = level = tmpship->max_fuel;
173  opt_settings = 0;
174  while (computing) {
175  (void)getship(&tmpship, *shipno);
176  computing = do_trip(tmpdest, level, gravity_factor);
177  if ((computing) && (tmpship->fuel >= 0.05)) {
178  fuel_usage = level;
179  opt_settings = 1;
180  level -= tmpship->fuel;
181  } else if (computing) {
182  computing = 0;
183  fuel_usage = level;
184  }
185  free(tmpship);
186  }
187 
188  (void)getship(&tmpship, *shipno);
189  sprintf(buf,
190  "\n ----- ===== FUEL ESTIMATES ===== ----\n\nAt Current Fuel "
191  "Cargo (%.2ff):\n",
192  tmpship->fuel);
194  notify(Playernum, Governor, buf);
195  if (!current_settings) {
196  sprintf(buf, "The ship will not be able to complete the trip.\n");
197  notify(Playernum, Governor, buf);
198  } else
199  fuel_output(Playernum, Governor, dist, current_fuel, gravity_factor,
200  tmpship->mass, current_segs);
201  sprintf(buf, "At Optimum Fuel Level (%.2ff):\n", fuel_usage);
202  notify(Playernum, Governor, buf);
203  if (!opt_settings) {
204  sprintf(buf, "The ship will not be able to complete the trip.\n");
205  notify(Playernum, Governor, buf);
206  } else {
207  tmpship->fuel = fuel_usage;
209  fuel_output(Playernum, Governor, dist, fuel_usage, gravity_factor,
211  }
212  free(tmpship);
213 }
214 
215 static void fuel_output(int Playernum, int Governor, double dist, double fuel,
216  double grav, double mass, segments_t segs) {
217  char buf[1024];
218  char grav_buf[1024];
219 
220  if (grav > 0.00)
221  sprintf(grav_buf, " (%.2f used to launch from %s)\n",
222  (double)(grav * mass * (double)LAUNCH_GRAV_MASS_FACTOR), plan_buf);
223  else
224  sprintf(grav_buf, " ");
225  sprintf(buf,
226  "Total Distance = %.2f Number of Segments = %d\nFuel = %.2f%s ",
227  dist, segs, fuel, grav_buf);
228  notify(Playernum, Governor, buf);
229  if (nsegments_done > segments)
230  notify(
231  Playernum, Governor,
232  "Estimated arrival time not available due to segment # discrepancy.\n");
233  else {
234  time_t effective_time =
235  next_segment_time + ((segs - 1) * (update_time / segments) * 60);
236  if (segments == 1)
237  effective_time =
238  next_update_time + (long)((segs - 1) * (update_time * 60));
239  sprintf(buf, "ESTIMATED Arrival Time: %s\n", ctime(&effective_time));
240  notify(Playernum, Governor, buf);
241  return;
242  }
243 }
244 
245 static int do_trip(const placetype &tmpdest, double fuel,
246  double gravity_factor) {
247  segments_t effective_segment_number;
248  int trip_resolved;
249  double gravity_fuel;
250  double tmpdist;
251  double fuel_level1;
252 
253  tmpship->fuel = fuel; /* load up the pseudo-ship */
254  effective_segment_number = nsegments_done;
255 
256  /* Set our temporary destination.... */
257  tmpship->destshipno = (unsigned short)tmpdest.shipno;
258  tmpship->whatdest = tmpdest.level;
259  tmpship->deststar = tmpdest.snum;
260  tmpship->destpnum = tmpdest.pnum;
261  if (tmpship->whatdest == ScopeLevel::LEVEL_SHIP || tmpship->ships) {
262  /* Bring in the other ships. moveship() uses ships[]. */
263  Num_ships = Numships();
264  ships = (Ship **)malloc(sizeof(Ship *) * (Num_ships) + 1);
265  for (shipnum_t i = 1; i <= Num_ships; i++) (void)getship(&ships[i], i);
266  }
267 
268  trip_resolved = 0;
269  number_segments = 0; /* Reset counter. */
270 
271  /* Launch the ship if it's on a planet. */
272  gravity_fuel = gravity_factor * tmpship->mass * LAUNCH_GRAV_MASS_FACTOR;
273  tmpship->fuel -= gravity_fuel;
274  tmpship->docked = 0;
275 
276  while (trip_resolved == 0) {
278  fuel_level1 = tmpship->fuel;
279  moveship(tmpship, (effective_segment_number == segments), 0, 1);
280  number_segments++;
281  effective_segment_number++;
282  if (effective_segment_number == (segments + 1))
283  effective_segment_number = 1;
284  x_0 = (double)tmpship->xpos;
285  y_0 = (double)tmpship->ypos;
286  tmpdist = sqrt(Distsq(x_0, y_0, x_1, y_1));
287  switch (tmpship->whatdest) {
288  case ScopeLevel::LEVEL_STAR:
289  if (tmpdist <= (double)SYSTEMSIZE) trip_resolved = 1;
290  break;
291  case ScopeLevel::LEVEL_PLAN:
292  if (tmpdist <= (double)PLORBITSIZE) trip_resolved = 1;
293  break;
294  case ScopeLevel::LEVEL_SHIP:
295  if (tmpdist <= (double)DIST_TO_LAND) trip_resolved = 1;
296  break;
297  default:
298  trip_resolved = 1;
299  }
300  if (((tmpship->fuel == fuel_level1) && (!tmpship->hyper_drive.on)) &&
301  (trip_resolved == 0)) {
302  if (tmpship->whatdest == ScopeLevel::LEVEL_SHIP) {
303  for (shipnum_t i = 1; i <= Num_ships; i++) free(ships[i]);
304  free(ships);
305  }
306  return (0);
307  }
308  }
309  if (tmpship->whatdest == ScopeLevel::LEVEL_SHIP || tmpship->ships) {
310  for (shipnum_t i = 1; i <= Num_ships; i++) free(ships[i]);
311  free(ships);
312  }
313  return (1);
314 }
#define Distsq(x1, y1, x2, y2)
Definition: tweakables.h:218
static double y_0
Definition: fuel.cc:31
#define SYSTEMSIZE
Definition: tweakables.h:81
#define DIST_TO_LAND
Definition: tweakables.h:194
Planet getplanet(const starnum_t star, const planetnum_t pnum)
Definition: files_shl.cc:335
static char plan_buf[1024]
Definition: fuel.cc:28
shipnum_t Numships()
Definition: files_shl.cc:1516
#define LAUNCH_GRAV_MASS_FACTOR
Definition: tweakables.h:125
#define isclr(a, i)
Definition: vars.h:313
static double y_1
Definition: fuel.cc:31
#define PLORBITSIZE
Definition: tweakables.h:82
static void fuel_output(int Playernum, int Governor, double dist, double fuel, double grav, double mass, segments_t segs)
Definition: fuel.cc:215
void moveship(Ship *, int, int, int)
Definition: moveship.cc:41
void proj_fuel(const command_t &argv, GameObj &g)
Definition: fuel.cc:38
void domass(Ship *)
Definition: doship.cc:204
static Ship * tmpship
Definition: fuel.cc:32
static double x_1
Definition: fuel.cc:31
static double x_0
Definition: fuel.cc:31
static segments_t number_segments
Definition: fuel.cc:30
static int do_trip(const placetype &, double fuel, double gravity_factor)
Definition: fuel.cc:245