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