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