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