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