]> git.pond.sub.org Git - empserver/blob - src/lib/commands/orde.c
Update copyright notice
[empserver] / src / lib / commands / orde.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  orde.c: Turn on/off autonavigation
28  *
29  *  Known contributors to this file:
30  *     Chad Zabel, 1994
31  *     Steve McClure, 2000
32  *     Markus Armbruster, 2004-2013
33  */
34
35 #include <config.h>
36
37 #include "commands.h"
38 #include "item.h"
39 #include "optlist.h"
40 #include "path.h"
41 #include "ship.h"
42
43 /*
44  *  Command syntax:
45  *
46  *  ORDER <ship>                                  Show orders
47  *  ORDER <ship> c[ancel]                         Cancel orders
48  *  ORDER <ship> s[top]                           Suspend orders
49  *  ORDER <ship> r[esume]                         Resume orders
50  *  ORDER <ship> d[eclare] <dest1>                Set destination
51  *               d[eclare] <dest1> <dest2>
52  *  ORDER <ship> l[evel]   <field> <start/end> <comm> <level>
53  *
54  * New syntax:
55  *  qorder <ship>    display cargo levels
56  *  sorder <ship>    display statistical info
57  */
58
59 int
60 orde(void)
61 {
62     int sub, level;
63     int scuttling = 0;
64     struct nstr_item nb;
65     struct shpstr ship;
66     struct ichrstr *i1;
67     coord p0x, p0y, p1x, p1y;
68     int i;
69     char *p, *p1, *dest;
70     char buf1[128];
71     char buf[1024];
72     char prompt[128];
73
74     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
75         return RET_SYN;
76     while (!player->aborted && nxtitem(&nb, (&ship))) {
77         if (!player->owner || ship.shp_own == 0)
78             continue;
79         if (opt_SAIL) {
80             if (*ship.shp_path) {
81                 pr("Ship #%d has a \"sail\" path!\n", ship.shp_uid);
82                 continue;
83             }
84         }
85         sprintf(prompt,
86                 "Ship #%d, declare, cancel, suspend, resume, level? ",
87                 ship.shp_uid);
88         p = getstarg(player->argp[2], prompt, buf);
89         if (player->aborted || !p || !*p)
90             return RET_FAIL;
91         if (!check_ship_ok(&ship))
92             return RET_FAIL;
93         switch (*p) {
94         default:
95             pr("Bad order type!\n");
96             return RET_SYN;
97         case 'c':               /* clear ship fields  */
98             ship.shp_mission = 0;
99             ship.shp_autonav &= ~(AN_AUTONAV + AN_STANDBY + AN_LOADING);
100             for (i = 0; i < TMAX; i++) {
101                 ship.shp_tstart[i] = I_NONE;
102                 ship.shp_tend[i] = I_NONE;
103                 ship.shp_lstart[i] = 0;
104                 ship.shp_lend[i] = 0;
105             }
106             break;
107         case 's':               /* suspend ship movement  */
108             ship.shp_mission = 0;
109             ship.shp_autonav |= AN_STANDBY;
110             break;
111         case 'r':               /* resume ship movement   */
112             ship.shp_mission = 0;
113             ship.shp_autonav &= ~AN_STANDBY;
114             break;
115         case 'd':               /* declare path */
116             scuttling = 0;
117             /* Need location */
118             p = getstarg(player->argp[3], "Destination? ", buf);
119             if (!p || !*p)
120                 return RET_SYN;
121             if (!sarg_xy(p, &p0x, &p0y))
122                 return RET_SYN;
123             p1x = p0x;
124             p1y = p0y;
125
126             p = getstarg(player->argp[4], "Second dest? ", buf);
127             if (!p)
128                 return RET_FAIL;
129             if (!check_ship_ok(&ship))
130                 return RET_FAIL;
131             if (!*p || !strcmp(p, "-")) {
132                 pr("A one-way order has been accepted.\n");
133             } else if (!strncmp(p, "s", 1)) {
134                 if (!(mchr[(int)ship.shp_type].m_flags & M_TRADE)) {
135                     pr("You can't auto-scuttle that ship!\n");
136                     return RET_SYN;
137                 }
138                 pr("A scuttle order has been accepted.\n");
139                 scuttling = 1;
140             } else {
141                 if (!sarg_xy(p, &p1x, &p1y))
142                     return RET_SYN;
143                 pr("A circular order has been accepted.\n");
144             }
145
146             /*
147              *  Set new destination and trade type fields.
148              */
149             ship.shp_mission = 0;
150             ship.shp_destx[1] = p1x;
151             ship.shp_desty[1] = p1y;
152             ship.shp_destx[0] = p0x;
153             ship.shp_desty[0] = p0y;
154
155             ship.shp_autonav &= ~(AN_STANDBY | AN_LOADING);
156             ship.shp_autonav |= AN_AUTONAV;
157
158             if (scuttling)
159                 ship.shp_autonav |= AN_SCUTTLE;
160             break;
161
162             /* set cargo levels on the ship */
163
164         case 'l':
165             /* convert player->argp[3] to an integer */
166             sprintf(buf1, "Field (1-%d) ", TMAX);
167             if (!getstarg(player->argp[3], buf1, buf))
168                 return RET_SYN;
169             if (!check_ship_ok(&ship))
170                 return RET_FAIL;
171             sub = atoi(buf);
172             /* check to make sure value in within range. */
173             if (sub > TMAX || sub < 1) {
174                 pr("Value must range from 1 to %d\n", TMAX);
175                 return RET_FAIL;
176             }
177
178             /* to keep sub in range of our arrays
179                subtract 1 so the new range is 0-(TMAX-1)
180              */
181             sub = sub - 1;;
182
183             if (ship.shp_autonav & AN_AUTONAV) {
184                 dest = getstarg(player->argp[4], "Start or end? ", buf);
185                 if (!dest)
186                     return RET_FAIL;
187                 switch (*dest) {
188                 default:
189                     pr("You must enter 'start' or 'end'\n");
190                     return RET_SYN;
191                 case 'e':
192                 case 'E':
193                     i1 = whatitem(player->argp[5], "Commodity? ");
194                     if (!i1)
195                         return RET_FAIL;
196                     p1 = getstarg(player->argp[6], "Amount? ", buf);
197                     if (!p1)
198                         return RET_SYN;
199                     if (!check_ship_ok(&ship))
200                         return RET_FAIL;
201                     level = atoi(p1);
202                     if (level < 0) {
203                         level = 0;      /* prevent negatives. */
204                         pr("You must use positive number! Level set to 0.\n");
205                     }
206                     ship.shp_tstart[sub] = i1->i_uid;
207                     ship.shp_lstart[sub] = level;
208                     pr("Order set\n");
209                     break;
210                 case 's':
211                 case 'S':
212                     i1 = whatitem(player->argp[5], "Commodity? ");
213                     if (!i1)
214                         return RET_FAIL;
215                     p1 = getstarg(player->argp[6], "Amount? ", buf);
216                     if (!p1)
217                         return RET_SYN;
218                     if (!check_ship_ok(&ship))
219                         return RET_FAIL;
220                     level = atoi(p1);
221                     if (level < 0) {
222                         level = 0;
223                         pr("You must use positive number! Level set to 0.\n");
224                     }
225                     ship.shp_tend[sub] = i1->i_uid;
226                     ship.shp_lend[sub] = level;
227                     pr("Order Set \n");
228                     break;
229                 }
230             } else
231                 pr("You need to 'declare' a ship path first, see 'info order'\n");
232
233             break;
234         }                       /* end of switch (*p) */
235
236
237
238         /*
239          *  Set loading flag if ship is already in one
240          *  of the specified harbors and a cargo has been
241          *  specified.
242          */
243
244         if (((ship.shp_x == ship.shp_destx[0])
245              && (ship.shp_y == ship.shp_desty[0])
246              && (ship.shp_lstart[0] != ' '))
247             || ((ship.shp_x == ship.shp_desty[1])
248                 && (ship.shp_y == ship.shp_desty[1])
249                 && (ship.shp_lstart[1] != ' '))) {
250
251             coord tcord;
252             i_type tcomm;
253             short lev[TMAX];
254             int i;
255
256             ship.shp_autonav |= AN_LOADING;
257
258             /*  swap variables, this keeps
259                the load_it() procedure happy. CZ
260              */
261             tcord = ship.shp_destx[0];
262             ship.shp_destx[0] = ship.shp_destx[1];
263             ship.shp_destx[1] = tcord;
264             tcord = ship.shp_desty[0];
265             ship.shp_desty[0] = ship.shp_desty[1];
266             ship.shp_desty[1] = tcord;
267
268             for (i = 0; i < TMAX; i++) {
269                 lev[i] = ship.shp_lstart[i];
270                 ship.shp_lstart[i] = ship.shp_lend[i];
271                 ship.shp_lend[i] = lev[i];
272                 tcomm = ship.shp_tstart[i];
273                 ship.shp_tstart[i] = ship.shp_tend[i];
274                 ship.shp_tend[i] = tcomm;
275             }
276         }
277
278         putship(ship.shp_uid, &ship);
279     }
280     return RET_OK;
281 }
282
283 static int
284 eta_calc(struct shpstr *sp, int len)
285 {
286     double mobcost, mobil;
287     int i, nupdates;
288
289     i = len;
290     nupdates = 1;
291
292     mobcost = shp_mobcost(sp);
293     mobil = sp->shp_mobil;
294     while (i) {
295         if (mobil > 0) {
296             mobil -= mobcost;
297             i--;
298         } else {
299             mobil += (ship_mob_scale * (float)etu_per_update);
300             nupdates++;
301         }
302     }
303     return nupdates;
304 }
305
306 static void
307 prhold(int hold, i_type itype, int amt)
308 {
309     if (itype != I_NONE && amt != 0) {
310         if (CANT_HAPPEN(itype <= I_NONE || itype > I_MAX))
311             return;
312         pr("%d-", hold + 1);
313         pr("%c", ichr[itype].i_mnem);
314         pr(":");
315         pr("%d ", amt);
316     }
317 }
318
319 int
320 qorde(void)
321 {
322     int nships = 0;
323     int i;
324     struct nstr_item nb;
325     struct shpstr ship;
326
327     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
328         return RET_SYN;
329     while (nxtitem(&nb, (&ship))) {
330         if (!player->owner || ship.shp_own == 0)
331             continue;
332         if (!(ship.shp_autonav & AN_AUTONAV)
333             && (!opt_SAIL || !ship.shp_path[0]))
334             continue;
335
336         if (!nships) {          /* 1st ship, print banner */
337             if (player->god)
338                 pr("own ");
339             pr("shp#     ship type    ");
340             pr("[Starting]       (Ending)    \n");
341         }
342         nships++;
343         if (player->god)
344             pr("%3d ", ship.shp_own);
345         pr("%4d", nb.cur);
346         pr(" %-16.16s", mchr[(int)ship.shp_type].m_name);
347
348         if (ship.shp_autonav & AN_AUTONAV) {
349             pr(" [");
350             for (i = 0; i < TMAX; i++)
351                 prhold(i, ship.shp_tend[i], ship.shp_lend[i]);
352             pr("] , (");
353             for (i = 0; i < TMAX; i++)
354                 prhold(i, ship.shp_tstart[i], ship.shp_lstart[i]);
355             pr(")");
356             if (ship.shp_autonav & AN_SCUTTLE)
357                 pr(" scuttling");
358             pr("\n");
359         } else
360             pr(" has a sail path\n");
361
362         if (ship.shp_name[0] != 0) {
363             if (player->god)
364                 pr("    ");
365             pr("       %s\n", ship.shp_name);
366         }
367     }
368     if (!nships) {
369         if (player->argp[1])
370             pr("%s: No ship(s)\n", player->argp[1]);
371         else
372             pr("%s: No ship(s)\n", "");
373         return RET_FAIL;
374     } else
375         pr("%d ship%s\n", nships, splur(nships));
376     return RET_OK;
377 }
378
379 int
380 sorde(void)
381 {
382     int nships = 0;
383     int len, updates;
384     double c;
385     struct nstr_item nb;
386     struct shpstr ship;
387
388     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
389         return RET_SYN;
390     while (nxtitem(&nb, (&ship))) {
391         if (!player->owner || ship.shp_own == 0)
392             continue;
393         if (!(ship.shp_autonav & AN_AUTONAV)
394             && (!opt_SAIL || !ship.shp_path[0]))
395             continue;
396
397         if (!nships) {          /* 1st ship, print banner */
398             if (player->god)
399                 pr("own ");
400             pr("shp#     ship type       x,y      start      end   "
401                " len  eta\n");
402         }
403         nships++;
404         if (player->god)
405             pr("%3d ", ship.shp_own);
406         pr("%4d", nb.cur);
407         pr(" %-16.16s", mchr[(int)ship.shp_type].m_name);
408         prxy(" %4d,%-4d", ship.shp_x, ship.shp_y);
409
410         if (ship.shp_autonav & AN_AUTONAV) {
411             /* Destination 1 */
412             prxy(" %4d,%-4d", ship.shp_destx[1], ship.shp_desty[1]);
413
414             /* Destination 2 */
415             if ((ship.shp_destx[1] != ship.shp_destx[0])
416                 || (ship.shp_desty[1] != ship.shp_desty[0])) {
417                 prxy(" %4d,%-4d", ship.shp_destx[0], ship.shp_desty[0]);
418             } else
419                 pr("          ");
420
421             if (ship.shp_autonav & AN_STANDBY)
422                 pr(" suspended");
423             else if (ship.shp_autonav & AN_LOADING)
424                 pr(" loading");
425             else {
426                 /* ETA calculation */
427                 c = path_find(ship.shp_x, ship.shp_y,
428                               ship.shp_destx[0], ship.shp_desty[0],
429                               ship.shp_own, MOB_SAIL);
430                 if (c < 0)
431                     pr(" no route possible");
432                 else if (c == 0)
433                     pr(" has arrived");
434                 else {
435                     /* distance to destination */
436                     len = (int)c;
437                     updates = eta_calc(&ship, len);
438                     pr(" %3d %4d", len, updates);
439                 }
440             }
441             if (ship.shp_autonav & AN_SCUTTLE)
442                 pr(" (scuttling)");
443             pr("\n");
444         } else
445             pr(" has a sail path\n");
446
447         if (ship.shp_name[0] != 0) {
448             if (player->god)
449                 pr("    ");
450             pr("       %s\n", ship.shp_name);
451         }
452     }
453     if (!nships) {
454         if (player->argp[1])
455             pr("%s: No ship(s)\n", player->argp[1]);
456         else
457             pr("%s: No ship(s)\n", "");
458         return RET_FAIL;
459     } else
460         pr("%d ship%s\n", nships, splur(nships));
461     return RET_OK;
462 }