]> git.pond.sub.org Git - empserver/blob - src/lib/commands/orde.c
236dbe8c31725c36e0f46231737bece445d64a0d
[empserver] / src / lib / commands / orde.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  orde.c: Turn on/off autonavigation
29  *
30  *  Known contributors to this file:
31  *     Chad Zabel, 1994
32  *     Steve McClure, 2000
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 diffeachship = 0;
63     int orders, sub, level;
64     int scuttling = 0;
65     struct nstr_item nb;
66     struct shpstr ship;
67     struct shpstr start;        /* Used for checking database */
68     struct ichrstr *i1;
69     coord p0x, p0y, p1x, p1y;
70     int i;
71     char *p, *p1, *dest;
72     char buf1[128];
73     char buf[1024];
74     char prompt[128];
75
76     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
77         return RET_SYN;
78     while (!player->aborted && nxtitem(&nb, (&ship))) {
79         if (!player->owner || ship.shp_own == 0)
80             continue;
81         if (opt_SAIL) {
82             if (*ship.shp_path) {
83                 if (!diffeachship)
84                     pr("Ship #%d has a \"sail\" path!\n", ship.shp_uid);
85                 continue;
86             }
87         }
88         memcpy(&start, &ship, sizeof(struct shpstr));
89         sprintf(prompt,
90                 "Ship #%d, declare, cancel, suspend, resume, level? ",
91                 ship.shp_uid);
92         p = getstarg(player->argp[2], prompt, buf);
93         if (player->aborted || !p)
94             return RET_FAIL;
95         if (!*p) {
96             if (!diffeachship)
97                 return RET_FAIL;
98             else
99                 continue;
100         }
101         switch (*p) {
102         default:
103             pr("Bad order type!\n");
104             return RET_SYN;
105         case 'c':               /* clear ship fields  */
106             ship.shp_mission = 0;
107             ship.shp_autonav &= ~(AN_AUTONAV + AN_STANDBY + AN_LOADING);
108             for (i = 0; i < TMAX; i++) {
109                 ship.shp_tstart[i] = I_NONE;
110                 ship.shp_tend[i] = I_NONE;
111                 ship.shp_lstart[i] = 0;
112                 ship.shp_lend[i] = 0;
113             }
114             break;
115         case 's':               /* suspend ship movement  */
116             ship.shp_mission = 0;
117             ship.shp_autonav |= AN_STANDBY;
118             break;
119         case 'r':               /* resume ship movement   */
120             ship.shp_mission = 0;
121             ship.shp_autonav &= ~AN_STANDBY;
122             break;
123         case 'd':               /* declare path */
124             orders = 0;
125             scuttling = 0;
126             /* Need location */
127             if ((p = getstarg(player->argp[3], "Destination? ", buf)) == 0
128                 || *p == 0)
129                 return RET_SYN;
130             if (!sarg_xy(p, &p0x, &p0y))
131                 return RET_SYN;
132             p1x = p0x;
133             p1y = p0y;
134
135
136
137             if (!orders) {
138                 p = getstarg(player->argp[4], "Second dest? ", buf);
139                 if (!p)
140                     return RET_FAIL;
141                 if (!*p || !strcmp(p, "-")) {
142                     orders = 1;
143                     pr("A one-way order has been accepted.\n");
144                 } else if (!strncmp(p, "s", 1)) {
145                     if (opt_TRADESHIPS) {
146                         if (!(mchr[(int)ship.shp_type].m_flags & M_TRADE)) {
147                             pr("You can't auto-scuttle that ship!\n");
148                             return RET_SYN;
149                         }
150                     } else {
151                         pr("You can't auto-scuttle that ship!\n");
152                         return RET_SYN;
153                     }
154                     pr("A scuttle order has been accepted.\n");
155                     scuttling = 1;
156                 } else {
157                     if (!sarg_xy(p, &p1x, &p1y))
158                         return RET_SYN;
159                     pr("A circular order has been accepted.\n");
160                 }
161             }
162
163             /*
164              *  Set new destination and trade type fields.
165              */
166             ship.shp_mission = 0;
167             ship.shp_destx[1] = p1x;
168             ship.shp_desty[1] = p1y;
169             ship.shp_destx[0] = p0x;
170             ship.shp_desty[0] = p0y;
171
172             ship.shp_autonav &= ~(AN_STANDBY | AN_LOADING);
173             ship.shp_autonav |= AN_AUTONAV;
174
175             if (scuttling)
176                 ship.shp_autonav |= AN_SCUTTLE;
177             break;
178
179             /* set cargo levels on the ship */
180
181         case 'l':
182             /* convert player->argp[3] to an integer */
183             if (player->argp[3])
184                 sub = atoi(player->argp[3]);
185             else {
186                 sprintf(buf1, "Field (1-%d) ", TMAX);
187                 if (getstarg(player->argp[3], buf1, buf) == 0)
188                     return RET_SYN;
189                 sub = atoi(buf);
190             }
191             /* check to make sure value in within range. */
192             if (sub > TMAX || sub < 1) {
193                 pr("Value must range from 1 to %d\n", TMAX);
194                 break;
195             }
196
197             /* to keep sub in range of our arrays
198                subtract 1 so the new range is 0-(TMAX-1)
199              */
200             sub = sub - 1;;
201
202             if (ship.shp_autonav & AN_AUTONAV) {
203                 orders = 1;
204                 dest = getstarg(player->argp[4], "Start or End? ", buf);
205                 if (orders) {   /* before dest check */
206                     if (!dest)
207                         break;
208                     switch (*dest) {
209                     default:
210                         pr("You must enter 'start' or 'end'\n");
211                         return RET_SYN;
212                     case 'e':
213                     case 'E':
214                         i1 = whatitem(player->argp[5], "Commodity? ");
215                         if (!i1)
216                             break;
217                         else {
218                             p1 = getstarg(player->argp[6], "Amount? ",
219                                           buf);
220                             if (!p1)
221                                 return RET_SYN;
222                             level = atoi(p1);
223                         }
224                         if (level < 0) {
225                             level = 0;  /* prevent negatives. */
226                             pr("You must use positive number! Level set to 0.\n");
227                         }
228                         ship.shp_tstart[sub] = i1->i_uid;
229                         ship.shp_lstart[sub] = level;
230                         pr("Order Set \n");
231                         break;
232                     case 's':
233                     case 'S':
234                         i1 = whatitem(player->argp[5], "Commodity? ");
235                         if (!i1)
236                             break;
237                         else {
238                             p1 = getstarg(player->argp[6], "Amount? ",
239                                           buf);
240                             if (!p1)
241                                 return RET_SYN;
242                             level = atoi(p1);
243                         }
244                         if (level < 0) {
245                             level = 0;
246                             pr("You must use positive number! Level set to 0.\n");
247                         }
248                         ship.shp_tend[sub] = i1->i_uid;
249                         ship.shp_lend[sub] = level;
250                         pr("Order Set \n");
251                         break;
252                     }
253                 }
254             } else
255                 pr("You need to 'declare' a ship path first, see 'info order'\n");
256
257             break;
258         }                       /* end of switch (*p) */
259
260
261
262         /*
263          *  Set loading flag if ship is already in one
264          *  of the specified harbors and a cargo has been
265          *  specified.
266          */
267
268         if (((ship.shp_x == ship.shp_destx[0])
269              && (ship.shp_y == ship.shp_desty[0])
270              && (ship.shp_lstart[0] != ' '))
271             || ((ship.shp_x == ship.shp_desty[1])
272                 && (ship.shp_y == ship.shp_desty[1])
273                 && (ship.shp_lstart[1] != ' '))) {
274
275             coord tcord;
276             i_type tcomm;
277             short lev[TMAX];
278             int i;
279
280             ship.shp_autonav |= AN_LOADING;
281
282             /*  swap variables, this keeps
283                the load_it() procedure happy. CZ
284              */
285             tcord = ship.shp_destx[0];
286             ship.shp_destx[0] = ship.shp_destx[1];
287             ship.shp_destx[1] = tcord;
288             tcord = ship.shp_desty[0];
289             ship.shp_desty[0] = ship.shp_desty[1];
290             ship.shp_desty[1] = tcord;
291
292             for (i = 0; i < TMAX; i++) {
293                 lev[i] = ship.shp_lstart[i];
294                 ship.shp_lstart[i] = ship.shp_lend[i];
295                 ship.shp_lend[i] = lev[i];
296                 tcomm = ship.shp_tstart[i];
297                 ship.shp_tstart[i] = ship.shp_tend[i];
298                 ship.shp_tend[i] = tcomm;
299             }
300         }
301         /*
302            **  Write ship back to database, then give it
303            **  a kick down the autonav route if necessary.
304          */
305
306
307         /* Now do a sanity check. */
308         if (!check_ship_ok(&start))
309             return RET_SYN;
310
311         putship(ship.shp_uid, &ship);
312     }
313     return RET_OK;
314 }
315
316 static void
317 eta_calc(struct shpstr *sp, char *path, int *len, int *nupdates)
318 {
319     double mobcost, mobil;
320     int i;
321
322     i = strlen(path);
323     *len = i;
324     *nupdates = 1;
325
326     mobcost = shp_mobcost(sp);
327     mobil = sp->shp_mobil;
328     while (i) {
329         if (mobil > 0) {
330             mobil -= mobcost;
331             i--;
332         } else {
333             mobil += (ship_mob_scale * (float)etu_per_update);
334             (*nupdates)++;
335         }
336     }
337 }
338
339 static void
340 prhold(int hold, i_type itype, int amt)
341 {
342     if (itype != I_NONE && amt != 0) {
343         if (CANT_HAPPEN(itype <= I_NONE || itype > I_MAX))
344             return;
345         pr("%d-", hold + 1);
346         pr("%c", ichr[itype].i_mnem);
347         pr(":");
348         pr("%d ", amt);
349     }
350 }
351
352 int
353 qorde(void)
354 {
355     int nships = 0;
356     int i;
357     struct nstr_item nb;
358     struct shpstr ship;
359
360     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
361         return RET_SYN;
362     while (nxtitem(&nb, (&ship))) {
363         if (!player->owner || ship.shp_own == 0)
364             continue;
365         if (!(ship.shp_autonav & AN_AUTONAV)
366             && (!opt_SAIL || !ship.shp_path[0]))
367             continue;
368
369         if (!nships) {          /* 1st ship, print banner */
370             if (player->god)
371                 pr("own ");
372             pr("shp#     ship type    ");
373             pr("[Starting]       (Ending)    \n");
374         }
375         nships++;
376         if (player->god)
377             pr("%3d ", ship.shp_own);
378         pr("%4d", nb.cur);
379         pr(" %-16.16s", mchr[(int)ship.shp_type].m_name);
380
381         if (ship.shp_autonav & AN_AUTONAV) {
382             pr(" [");
383             for (i = 0; i < TMAX; i++)
384                 prhold(i, ship.shp_tend[i], ship.shp_lend[i]);
385             pr("] , (");
386             for (i = 0; i < TMAX; i++)
387                 prhold(i, ship.shp_tstart[i], ship.shp_lstart[i]);
388             pr(")");
389             if (ship.shp_autonav & AN_SCUTTLE)
390                 pr(" scuttling");
391             pr("\n");
392         } else
393             pr(" has a sail path\n");
394
395         if (ship.shp_name[0] != 0) {
396             if (player->god)
397                 pr("    ");
398             pr("       %s\n", ship.shp_name);
399         }
400     }
401     if (!nships) {
402         if (player->argp[1])
403             pr("%s: No ship(s)\n", player->argp[1]);
404         else
405             pr("%s: No ship(s)\n", "");
406         return RET_FAIL;
407     } else
408         pr("%d ship%s\n", nships, splur(nships));
409     return RET_OK;
410 }
411
412 int
413 sorde(void)
414 {
415     int nships = 0;
416     int len, updates;
417     char *c;
418     struct nstr_item nb;
419     struct shpstr ship;
420     char buf[1024];
421
422     if (!snxtitem(&nb, EF_SHIP, player->argp[1], NULL))
423         return RET_SYN;
424     while (nxtitem(&nb, (&ship))) {
425         if (!player->owner || ship.shp_own == 0)
426             continue;
427         if (!(ship.shp_autonav & AN_AUTONAV)
428             && (!opt_SAIL || !ship.shp_path[0]))
429             continue;
430
431         if (!nships) {          /* 1st ship, print banner */
432             if (player->god)
433                 pr("own ");
434             pr("shp#     ship type      x,y    ");
435             pr("start    end   ");
436             pr("len  eta\n");
437         }
438         nships++;
439         if (player->god)
440             pr("%3d ", ship.shp_own);
441         pr("%4d", nb.cur);
442         pr(" %-16.16s", mchr[(int)ship.shp_type].m_name);
443         prxy(" %3d,%-3d", ship.shp_x, ship.shp_y, player->cnum);
444
445         if (ship.shp_autonav & AN_AUTONAV) {
446             /* Destination 1 */
447             prxy(" %3d,%-3d",
448                  ship.shp_destx[1], ship.shp_desty[1], player->cnum);
449
450             /* Destination 2 */
451             if ((ship.shp_destx[1] != ship.shp_destx[0])
452                 || (ship.shp_desty[1] != ship.shp_desty[0])) {
453                 prxy(" %3d,%-3d",
454                      ship.shp_destx[0], ship.shp_desty[0], player->cnum);
455             } else
456                 pr("        ");
457
458             if (ship.shp_autonav & AN_STANDBY)
459                 pr(" suspended");
460             else if (ship.shp_autonav & AN_LOADING)
461                 pr(" loading");
462             else {
463                 /* ETA calculation */
464
465                 c = BestShipPath(buf, ship.shp_x, ship.shp_y,
466                                  ship.shp_destx[0], ship.shp_desty[0],
467                                  ship.shp_own);
468                 if (!c)
469                     pr(" no route possible");
470                 else if (*c == 'h')
471                     pr(" has arrived");
472                 else {
473                     /* distance to destination */
474                     eta_calc(&ship, c, &len, &updates);
475                     pr(" %3d %4d", len, updates);
476                 }
477             }
478             if (ship.shp_autonav & AN_SCUTTLE)
479                 pr(" (scuttling)");
480             pr("\n");
481         } else
482             pr(" has a sail path\n");
483
484         if (ship.shp_name[0] != 0) {
485             if (player->god)
486                 pr("    ");
487             pr("       %s\n", ship.shp_name);
488         }
489     }
490     if (!nships) {
491         if (player->argp[1])
492             pr("%s: No ship(s)\n", player->argp[1]);
493         else
494             pr("%s: No ship(s)\n", "");
495         return RET_FAIL;
496     } else
497         pr("%d ship%s\n", nships, splur(nships));
498     return RET_OK;
499 }