]> git.pond.sub.org Git - empserver/blob - src/lib/subs/unitsub.c
subs: Rename shp_nav(), lnd_mar()
[empserver] / src / lib / subs / unitsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  unitsub.c: Common subroutines for multiple type of units
28  *
29  *  Known contributors to this file:
30  *     Ron Koenderink, 2007
31  *     Markus Armbruster, 2009-2015
32  */
33
34 #include <config.h>
35
36 #include <math.h>
37 #include "file.h"
38 #include "map.h"
39 #include "optlist.h"
40 #include "path.h"
41 #include "player.h"
42 #include "prototypes.h"
43 #include "unit.h"
44
45 char *
46 unit_nameof(struct empobj *gp)
47 {
48     switch (gp->ef_type) {
49     case EF_SHIP:
50         return prship((struct shpstr *)gp);
51     case EF_PLANE:
52         return prplane((struct plnstr *)gp);
53     case EF_LAND:
54         return prland((struct lndstr *)gp);
55     case EF_NUKE:
56         return prnuke((struct nukstr *)gp);
57     }
58     CANT_REACH();
59     return "The Beast #666";
60 }
61
62 static void
63 unit_list(struct emp_qelem *unit_list)
64 {
65     struct emp_qelem *qp;
66     struct emp_qelem *next;
67     struct ulist *ulp;
68     int type, npln, nch, nxl;
69     struct empobj *unit;
70     struct lndstr *lnd;
71     struct shpstr *shp;
72
73     if (CANT_HAPPEN(QEMPTY(unit_list)))
74         return;
75     qp = unit_list->q_back;
76     ulp = (struct ulist *)qp;
77     type = ulp->unit.gen.ef_type;
78     if (CANT_HAPPEN(type != EF_LAND && type != EF_SHIP))
79         return;
80
81     if (type == EF_LAND)
82         pr("lnd#     land type       x,y    a  eff mil  sh gun xl ln  mu tech retr\n");
83     else
84         pr("shp#     ship type       x,y   fl  eff mil  sh gun pn he xl ln mob tech\n");
85
86     for (; qp != unit_list; qp = next) {
87         next = qp->q_back;
88         ulp = (struct ulist *)qp;
89         lnd = &ulp->unit.land;
90         shp = &ulp->unit.ship;
91         unit = &ulp->unit.gen;
92         if (CANT_HAPPEN(type != unit->ef_type))
93             continue;
94         pr("%4d ", unit->uid);
95         pr("%-16.16s ", empobj_chr_name(unit));
96         prxy("%4d,%-4d ", unit->x, unit->y);
97         pr("%1.1s", &unit->group);
98         pr("%4d%%", unit->effic);
99         if (type == EF_LAND) {
100             pr("%4d", lnd->lnd_item[I_MILIT]);
101             pr("%4d", lnd->lnd_item[I_SHELL]);
102             pr("%4d", lnd->lnd_item[I_GUN]);
103             pr("%3d%3d", lnd_nxlight(lnd), lnd_nland(lnd));
104         } else {
105             pr("%4d", shp->shp_item[I_MILIT]);
106             pr("%4d", shp->shp_item[I_SHELL]);
107             pr("%4d", shp->shp_item[I_GUN]);
108             npln = shp_nplane(shp, &nch, &nxl, NULL);
109             pr("%3d%3d%3d", npln - nch - nxl, nch, nxl);
110             pr("%3d", shp_nland(shp));
111         }
112         pr("%4d", unit->mobil);
113         pr("%4d", unit->tech);
114         if (type == EF_LAND) {
115             pr("%4d%%", lnd->lnd_retreat);
116         }
117         pr("\n");
118     }
119 }
120
121 static char *
122 unit_path(struct empobj *unit, char *buf, size_t bufsz)
123 {
124     coord destx;
125     coord desty;
126     struct sctstr sect;
127     size_t len;
128     double c;
129     int mtype;
130
131     if (CANT_HAPPEN(unit->ef_type != EF_LAND && unit->ef_type != EF_SHIP))
132         return NULL;
133
134     if (!sarg_xy(buf, &destx, &desty))
135         return buf;
136     if (unit->ef_type == EF_SHIP) {
137         c = path_find(unit->x, unit->y, destx, desty,
138                       player->cnum, MOB_SAIL);
139         if (c < 0 || unit->mobil <= 0) {
140             pr("Can't get to '%s' right now.\n",
141                xyas(destx, desty, player->cnum));
142             return NULL;
143         }
144     } else {
145         getsect(unit->x, unit->y, &sect);
146         mtype = lnd_mobtype((struct lndstr *)unit);
147         /*
148          * Note: passing sect.sct_own for actor is funny, but works:
149          * its only effect is to confine the search to that nation's
150          * land.  It doesn't affect mobility costs.  The real actor is
151          * different for marching in allied land, and passing it would
152          * break path finding there.
153          */
154         c = path_find(unit->x, unit->y, destx, desty, sect.sct_own, mtype);
155         if (c < 0) {
156             pr("No owned %s from %s to %s!\n",
157                mtype == MOB_RAIL ? "railway" : "path",
158                xyas(unit->x, unit->y, player->cnum),
159                xyas(destx, desty, player->cnum));
160             return NULL;
161         }
162     }
163     len = path_find_route(buf, bufsz, unit->x, unit->y, destx, desty);
164     if (len == 0 || unit->ef_type == EF_LAND) {
165         if (len + 1 < bufsz)
166             strcpy(buf + len, "h");
167         len++;
168     }
169     if (len >= bufsz) {
170         pr("Can't handle path to %s, it's too long, sorry\n",
171            xyas(destx, desty, player->cnum));
172         return NULL;
173     }
174     pr("Using path '%s'\n", buf);
175     return buf;
176 }
177
178 static void
179 unit_view(struct emp_qelem *list)
180 {
181     struct sctstr sect;
182     struct emp_qelem *qp;
183     struct emp_qelem *next;
184     struct ulist *ulp;
185
186     for (qp = list->q_back; qp != list; qp = next) {
187         next = qp->q_back;
188         ulp = (struct ulist *)qp;
189         if (CANT_HAPPEN(!(ef_flags(ulp->unit.gen.ef_type) & EFF_XY)))
190             continue;
191         getsect(ulp->unit.gen.x, ulp->unit.gen.y, &sect);
192         if (ulp->unit.gen.ef_type == EF_SHIP) {
193             if (mchr[ulp->unit.ship.shp_type].m_flags & M_FOOD)
194                 pr("[fert:%d] ", sect.sct_fertil);
195             if (mchr[ulp->unit.ship.shp_type].m_flags & M_OIL)
196                 pr("[oil:%d] ", sect.sct_oil);
197         }
198         pr("%s @ %s %d%% %s\n", unit_nameof(&ulp->unit.gen),
199            xyas(ulp->unit.gen.x, ulp->unit.gen.y, player->cnum),
200            sect.sct_effic, dchr[sect.sct_type].d_name);
201     }
202 }
203
204 void
205 unit_rad_map_set(struct emp_qelem *list)
206 {
207     struct emp_qelem *qp;
208     struct empobj *unit;
209
210     for (qp = list->q_back; qp != list; qp = qp->q_back) {
211         unit = &((struct ulist *)qp)->unit.gen;
212         rad_map_set(unit->own, unit->x, unit->y, unit->effic, unit->tech,
213                     unit->ef_type == EF_SHIP
214                     ? mchr[unit->type].m_vrnge : lchr[unit->type].l_spy);
215     }
216 }
217
218 static struct empobj *
219 get_leader(struct emp_qelem *list)
220 {
221     return &((struct ulist *)(list->q_back))->unit.gen;
222 }
223
224 static void
225 switch_leader(struct emp_qelem *list, char *arg)
226 {
227     int uid = arg ? atoi(arg) : -1;
228
229     struct emp_qelem *qp, *save;
230     struct ulist *ulp;
231
232     if (QEMPTY(list))
233         return;
234
235     save = qp = list->q_back;
236     do {
237         emp_remque(qp);
238         emp_insque(qp, list);
239         qp = list->q_back;
240         ulp = (struct ulist *)qp;
241         if (ulp->unit.gen.uid == uid || uid == -1)
242             break;
243     } while (list->q_back != save);
244 }
245
246 static char *
247 unit_move_parse(char *cp, char *arg1_default)
248 {
249     int ac;
250
251     ac = parse(cp, player->argbuf, player->argp, NULL, NULL, NULL);
252     if (CANT_HAPPEN(ac <= 0)) {
253         player->argp[0] = "";
254         return "";
255     }
256     if (ac == 1) {
257         player->argp[1] = arg1_default;
258         return cp + 1;
259     }
260     return "";
261 }
262
263 static char *
264 unit_move_non_dir(struct emp_qelem *list, char *cp, int *map_shown)
265 {
266     struct empobj *leader = get_leader(list);
267     int bmap = 0, stopping;
268     char leader_str[32];
269
270     *map_shown = 0;
271     sprintf(leader_str, "%d", leader->uid);
272
273     switch (*cp) {
274     case 'B':
275         bmap = 'b';
276         /* fall through */
277     case 'M':
278         cp = unit_move_parse(cp, leader_str);
279         display_region_map(bmap, leader->ef_type, leader->x, leader->y,
280                            player->argp[1], player->argp[2]);
281         *map_shown = 1;
282         break;
283     case 'f':
284         cp = unit_move_parse(cp, NULL);
285         switch_leader(list, player->argp[1]);
286         break;
287     case 'i':
288         cp++;
289         unit_list(list);
290         break;
291     case 'm':
292         cp++;
293         if (leader->ef_type == EF_SHIP)
294             stopping = shp_sweep(list, 1, 1, player->cnum);
295         else
296             stopping = lnd_sweep(list, 1, 1, player->cnum);
297         if (stopping)
298             cp = "";
299         break;
300     case 'r':
301         cp = unit_move_parse(cp, leader_str);
302         radar(leader->ef_type);
303         player->btused++;       /* FIXME use player_coms[].c_cost */
304         *map_shown = 1;
305         break;
306     case 'l':
307         cp = unit_move_parse(cp, leader_str);
308         do_look(leader->ef_type);
309         player->btused++;       /* FIXME likewise */
310         break;
311     case 's':
312         if (leader->ef_type != EF_SHIP)
313             return NULL;
314         cp = unit_move_parse(cp, leader_str);
315         sona();
316         player->btused++;       /* FIXME likewise */
317         *map_shown = 1;
318         break;
319     case 'd':
320         cp = unit_move_parse(cp, NULL);
321         if (!player->argp[1]) {
322             player->argp[1] = leader_str;
323             player->argp[2] = "1";
324         } else if (!player->argp[2]) {
325             player->argp[2] = player->argp[1];
326             player->argp[1] = leader_str;
327         }
328         if (leader->ef_type == EF_SHIP)
329             mine();
330         else
331             landmine();
332         player->btused++;       /* FIXME likewise */
333         *map_shown = 1;
334         break;
335     case 'v':
336         cp++;
337         unit_view(list);
338         break;
339     default:
340         return NULL;
341     }
342
343     return cp;
344 }
345
346 static char *
347 unit_move_getpath(struct emp_qelem *list, int suppress_map, char *path)
348 {
349     struct empobj *leader = get_leader(list);
350     double minmob, maxmob;
351     struct emp_qelem *qp;
352     struct ulist *ulp;
353     char prompt[64];
354
355     minmob = HUGE_VAL;
356     maxmob = -HUGE_VAL;
357     for (qp = list->q_back; qp != list; qp = qp->q_back) {
358         ulp = (struct ulist *)qp;
359         if (ulp->mobil < minmob)
360             minmob = ulp->mobil;
361         if (ulp->mobil > maxmob)
362             maxmob = ulp->mobil;
363     }
364     if (!suppress_map)
365         nav_map(leader->x, leader->y,
366                 leader->ef_type == EF_SHIP
367                 ? !(mchr[leader->type].m_flags & M_SUB) : 1);
368     snprintf(prompt, sizeof(prompt), "<%.1f:%.1f: %s> ",
369              maxmob, minmob,
370              xyas(leader->x, leader->y, player->cnum));
371     return getstring(prompt, path);
372 }
373
374 int
375 unit_move(struct emp_qelem *list)
376 {
377     struct empobj *leader = get_leader(list);
378     int leader_uid = leader->uid;
379     int type = leader->ef_type;
380     int moved, suppress_map, dir, stopping;
381     char *cp;
382     char path[1024];
383
384     unit_rad_map_set(list);
385
386     pr("%s is %s\n",
387         type == EF_SHIP ? "Flagship" : "Leader",
388         unit_nameof(leader));
389
390     cp = "";
391     if (player->argp[2]) {
392         strcpy(path, player->argp[2]);
393         cp = unit_path(leader, path, sizeof(path));
394         if (!cp)
395             cp = "";
396     }
397
398     moved = suppress_map = 0;
399     for (;;) {
400         /*
401          * Invariants:
402          * - shp_may_nav() true for all ships
403          * - lnd_may_mar() true for all land units
404          * - leader is up-to-date
405          * Implies all are in the same sector
406          */
407         if (!*cp) {
408             cp = unit_move_getpath(list, suppress_map, path);
409             if (!cp)
410                 return RET_FAIL;
411             cp = unit_path(leader, path, sizeof(path));
412             if (!cp || !*cp)
413                 cp = "h";
414             suppress_map = 0;
415         } else if ((dir = chkdir(*cp, DIR_STOP, DIR_LAST)) >= 0) {
416             cp++;
417             if (type == EF_SHIP)
418                 stopping = shp_nav_one_sector(list, dir, player->cnum);
419             else {
420                 if (!moved && !lnd_abandon_askyn(list))
421                     return RET_FAIL;
422                 stopping = lnd_mar_one_sector(list, dir, player->cnum);
423             }
424             if (dir == DIR_STOP)
425                 return RET_OK;
426             moved = 1;
427             if (stopping)
428                 cp = "";
429         } else {
430             cp = unit_move_non_dir(list, cp, &suppress_map);
431             if (!cp) {
432                 direrr("`%c' to stop", ", `%c' to view", NULL);
433                 pr(", `i' to list %s, `f' to change %s,\n",
434                    type == EF_SHIP ? "ships" : "units",
435                    type == EF_SHIP ? "flagship" : "leader");
436                 pr("`r' to radar, %s`l' to look, `M' to map, `B' to bmap,\n",
437                    type == EF_SHIP ? "`s' to sonar, " : "");
438                 pr("`d' to drop mines, and `m' to minesweep\n");
439                 cp = "";
440             }
441         }
442
443         if (type == EF_SHIP)
444             shp_nav_stay_behind(list, player->cnum);
445         else
446             lnd_mar_stay_behind(list, player->cnum);
447
448         if (QEMPTY(list)) {
449             pr("No %s left\n", type == EF_SHIP ? "ships" : "lands");
450             return RET_OK;
451         }
452
453         leader = get_leader(list);
454         if (leader->uid != leader_uid) {
455             leader_uid = leader->uid;
456             pr("Changing %s to %s\n",
457                leader->ef_type == EF_SHIP ? "flagship" : "leader",
458                unit_nameof(leader));
459         }
460         unit_rad_map_set(list);
461     }
462 }
463
464 /*
465  * Teleport UNIT to X,Y.
466  * If UNIT's mission op-area is centered on it, keep it centered.
467  */
468 void
469 unit_teleport(struct empobj *unit, coord x, coord y)
470 {
471     if (unit->opx == unit->x && unit->opy == unit->y) {
472         unit->opx = x;
473         unit->opy = y;
474     }
475     unit->x = x;
476     unit->y = y;
477 }
478
479 /*
480  * Update cargo of CARRIER for movement or destruction.
481  * If the carrier is destroyed, destroy its cargo (planes, land units,
482  * nukes).
483  * Else update their location to the carrier's.  Any op sectors equal
484  * to location get updated, too.
485  * Return number of units updated.
486  */
487 int
488 unit_update_cargo(struct empobj *carrier)
489 {
490     int cargo_type;
491     struct nstr_item ni;
492     union empobj_storage obj;
493     int n = 0;
494
495     for (cargo_type = EF_PLANE; cargo_type <= EF_NUKE; cargo_type++) {
496         snxtitem_cargo(&ni, cargo_type, carrier->ef_type, carrier->uid);
497         while (nxtitem(&ni, &obj)) {
498             if (carrier->own)
499                 unit_teleport(&obj.gen, carrier->x, carrier->y);
500             else {
501                 mpr(obj.gen.own, "%s lost!\n", unit_nameof(&obj.gen));
502                 obj.gen.effic = 0;
503             }
504             put_empobj(cargo_type, obj.gen.uid, &obj);
505             n++;
506         }
507     }
508     return n;
509 }
510
511 /*
512  * Drop cargo of UNIT.
513  * Give it to NEWOWN, unless it's zero.
514  */
515 void
516 unit_drop_cargo(struct empobj *unit, natid newown)
517 {
518     int type;
519     struct nstr_item ni;
520     union empobj_storage cargo;
521
522     for (type = EF_PLANE; type <= EF_NUKE; type++) {
523         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
524         while (nxtitem(&ni, &cargo)) {
525             switch (type) {
526             case EF_PLANE:
527                 cargo.plane.pln_ship = cargo.plane.pln_land = -1;
528                 break;
529             case EF_LAND:
530                 cargo.land.lnd_ship = cargo.land.lnd_land = -1;
531                 break;
532             case EF_NUKE:
533                 cargo.nuke.nuk_plane = -1;
534                 break;
535             }
536             mpr(cargo.gen.own, "%s transferred off %s %d to %s\n",
537                 unit_nameof(&cargo.gen),
538                 ef_nameof(unit->ef_type), unit->uid,
539                 xyas(cargo.gen.x, cargo.gen.y, cargo.gen.own));
540             if (newown)
541                 unit_give_away(&cargo.gen, newown, cargo.gen.own);
542             put_empobj(type, cargo.gen.uid, &cargo.gen);
543         }
544     }
545 }
546
547 /*
548  * Give UNIT and its cargo to RECIPIENT.
549  * No action if RECIPIENT already owns UNIT.
550  * If GIVER is non-zero, inform RECIPIENT and GIVER of the transaction.
551  * Clears mission and group on the units given away.
552  */
553 void
554 unit_give_away(struct empobj *unit, natid recipient, natid giver)
555 {
556     int type;
557     struct nstr_item ni;
558     union empobj_storage cargo;
559
560     if (unit->own == recipient)
561         return;
562
563     if (giver) {
564         mpr(unit->own, "%s given to %s\n",
565             unit_nameof(unit), cname(recipient));
566         mpr(recipient, "%s given to you by %s\n",
567             unit_nameof(unit), cname(giver));
568     }
569
570     unit->own = recipient;
571     unit_wipe_orders(unit);
572     put_empobj(unit->ef_type, unit->uid, unit);
573
574     for (type = EF_PLANE; type <= EF_NUKE; type++) {
575         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
576         while (nxtitem(&ni, &cargo))
577             unit_give_away(&cargo.gen, recipient, giver);
578     }
579 }
580
581 /*
582  * Wipe orders and such from UNIT.
583  */
584 void
585 unit_wipe_orders(struct empobj *unit)
586 {
587     struct shpstr *sp;
588     struct plnstr *pp;
589     struct lndstr *lp;
590
591     unit->group = 0;
592     unit->opx = unit->opy = 0;
593     unit->mission = 0;
594     unit->radius = 0;
595
596     switch (unit->ef_type) {
597     case EF_SHIP:
598         sp = (struct shpstr *)unit;
599         sp->shp_rflags = 0;
600         sp->shp_rpath[0] = 0;
601         break;
602     case EF_PLANE:
603         pp = (struct plnstr *)unit;
604         pp->pln_range = pln_range_max(pp);
605         break;
606     case EF_LAND:
607         lp = (struct lndstr *)unit;
608         lp->lnd_retreat = morale_base;
609         lp->lnd_rflags = 0;
610         lp->lnd_rpath[0] = 0;
611         break;
612     case EF_NUKE:
613         break;
614     default:
615         CANT_REACH();
616     }
617 }