]> git.pond.sub.org Git - empserver/blob - src/lib/subs/unitsub.c
7b1d3f0b8dd9ce9a62a0d65c8dcf8b02663014ad
[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 void
122 unit_view(struct emp_qelem *list)
123 {
124     struct sctstr sect;
125     struct emp_qelem *qp;
126     struct emp_qelem *next;
127     struct ulist *ulp;
128
129     for (qp = list->q_back; qp != list; qp = next) {
130         next = qp->q_back;
131         ulp = (struct ulist *)qp;
132         if (CANT_HAPPEN(!(ef_flags(ulp->unit.gen.ef_type) & EFF_XY)))
133             continue;
134         getsect(ulp->unit.gen.x, ulp->unit.gen.y, &sect);
135         if (ulp->unit.gen.ef_type == EF_SHIP) {
136             if (mchr[ulp->unit.ship.shp_type].m_flags & M_FOOD)
137                 pr("[fert:%d] ", sect.sct_fertil);
138             if (mchr[ulp->unit.ship.shp_type].m_flags & M_OIL)
139                 pr("[oil:%d] ", sect.sct_oil);
140         }
141         pr("%s @ %s %d%% %s\n", unit_nameof(&ulp->unit.gen),
142            xyas(ulp->unit.gen.x, ulp->unit.gen.y, player->cnum),
143            sect.sct_effic, dchr[sect.sct_type].d_name);
144     }
145 }
146
147 void
148 unit_rad_map_set(struct emp_qelem *list)
149 {
150     struct emp_qelem *qp;
151     struct empobj *unit;
152
153     for (qp = list->q_back; qp != list; qp = qp->q_back) {
154         unit = &((struct ulist *)qp)->unit.gen;
155         rad_map_set(unit->own, unit->x, unit->y, unit->effic, unit->tech,
156                     unit->ef_type == EF_SHIP
157                     ? mchr[unit->type].m_vrnge : lchr[unit->type].l_spy);
158     }
159 }
160
161 static struct empobj *
162 get_leader(struct emp_qelem *list)
163 {
164     return &((struct ulist *)(list->q_back))->unit.gen;
165 }
166
167 static void
168 switch_leader(struct emp_qelem *list, char *arg)
169 {
170     int uid = arg ? atoi(arg) : -1;
171
172     struct emp_qelem *qp, *save;
173     struct ulist *ulp;
174
175     if (QEMPTY(list))
176         return;
177
178     save = qp = list->q_back;
179     do {
180         emp_remque(qp);
181         emp_insque(qp, list);
182         qp = list->q_back;
183         ulp = (struct ulist *)qp;
184         if (ulp->unit.gen.uid == uid || uid == -1)
185             break;
186     } while (list->q_back != save);
187 }
188
189 static char *
190 unit_move_parse(char *cp, char *arg1_default)
191 {
192     int ac;
193
194     ac = parse(cp, player->argbuf, player->argp, NULL, NULL, NULL);
195     if (CANT_HAPPEN(ac <= 0)) {
196         player->argp[0] = "";
197         return "";
198     }
199     if (ac == 1) {
200         player->argp[1] = arg1_default;
201         return cp + 1;
202     }
203     return "";
204 }
205
206 static char *
207 unit_move_non_dir(struct emp_qelem *list, char *cp, int *map_shown)
208 {
209     struct empobj *leader = get_leader(list);
210     int bmap = 0, stopping;
211     char leader_str[32];
212
213     *map_shown = 0;
214     sprintf(leader_str, "%d", leader->uid);
215
216     switch (*cp) {
217     case 'B':
218         bmap = 'b';
219         /* fall through */
220     case 'M':
221         cp = unit_move_parse(cp, leader_str);
222         display_region_map(bmap, leader->ef_type, leader->x, leader->y,
223                            player->argp[1], player->argp[2]);
224         *map_shown = 1;
225         break;
226     case 'f':
227         cp = unit_move_parse(cp, NULL);
228         switch_leader(list, player->argp[1]);
229         break;
230     case 'i':
231         cp++;
232         unit_list(list);
233         break;
234     case 'm':
235         cp++;
236         if (leader->ef_type == EF_SHIP)
237             stopping = shp_sweep(list, 1, 1, player->cnum);
238         else
239             stopping = lnd_sweep(list, 1, 1, player->cnum);
240         if (stopping)
241             cp = "";
242         break;
243     case 'r':
244         cp = unit_move_parse(cp, leader_str);
245         radar(leader->ef_type);
246         player->btused++;       /* FIXME use player_coms[].c_cost */
247         *map_shown = 1;
248         break;
249     case 'l':
250         cp = unit_move_parse(cp, leader_str);
251         do_look(leader->ef_type);
252         player->btused++;       /* FIXME likewise */
253         break;
254     case 's':
255         if (leader->ef_type != EF_SHIP)
256             return NULL;
257         cp = unit_move_parse(cp, leader_str);
258         sona();
259         player->btused++;       /* FIXME likewise */
260         *map_shown = 1;
261         break;
262     case 'd':
263         cp = unit_move_parse(cp, NULL);
264         if (!player->argp[1]) {
265             player->argp[1] = leader_str;
266             player->argp[2] = "1";
267         } else if (!player->argp[2]) {
268             player->argp[2] = player->argp[1];
269             player->argp[1] = leader_str;
270         }
271         if (leader->ef_type == EF_SHIP)
272             mine();
273         else
274             landmine();
275         player->btused++;       /* FIXME likewise */
276         *map_shown = 1;
277         break;
278     case 'v':
279         cp++;
280         unit_view(list);
281         break;
282     default:
283         return NULL;
284     }
285
286     return cp;
287 }
288
289 static char *
290 unit_move_getpath(struct emp_qelem *list, int suppress_map, char *path)
291 {
292     struct empobj *leader = get_leader(list);
293     double minmob, maxmob;
294     struct emp_qelem *qp;
295     struct ulist *ulp;
296     char prompt[64];
297
298     minmob = HUGE_VAL;
299     maxmob = -HUGE_VAL;
300     for (qp = list->q_back; qp != list; qp = qp->q_back) {
301         ulp = (struct ulist *)qp;
302         if (ulp->mobil < minmob)
303             minmob = ulp->mobil;
304         if (ulp->mobil > maxmob)
305             maxmob = ulp->mobil;
306     }
307     if (!suppress_map)
308         nav_map(leader->x, leader->y,
309                 leader->ef_type == EF_SHIP
310                 ? !(mchr[leader->type].m_flags & M_SUB) : 1);
311     snprintf(prompt, sizeof(prompt), "<%.1f:%.1f: %s> ",
312              maxmob, minmob,
313              xyas(leader->x, leader->y, player->cnum));
314     return getstring(prompt, path);
315 }
316
317 static char *
318 unit_move_route(struct empobj *unit, char *buf, size_t bufsz)
319 {
320     coord destx;
321     coord desty;
322     struct sctstr sect;
323     size_t len;
324     double c;
325     int mtype;
326
327     if (CANT_HAPPEN(unit->ef_type != EF_LAND && unit->ef_type != EF_SHIP))
328         return NULL;
329
330     if (!sarg_xy(buf, &destx, &desty))
331         return buf;
332     if (unit->ef_type == EF_SHIP) {
333         c = path_find(unit->x, unit->y, destx, desty,
334                       player->cnum, MOB_SAIL);
335         if (c < 0 || unit->mobil <= 0) {
336             pr("Can't get to '%s' right now.\n",
337                xyas(destx, desty, player->cnum));
338             return NULL;
339         }
340     } else {
341         getsect(unit->x, unit->y, &sect);
342         mtype = lnd_mobtype((struct lndstr *)unit);
343         /*
344          * Note: passing sect.sct_own for actor is funny, but works:
345          * its only effect is to confine the search to that nation's
346          * land.  It doesn't affect mobility costs.  The real actor is
347          * different for marching in allied land, and passing it would
348          * break path finding there.
349          */
350         c = path_find(unit->x, unit->y, destx, desty, sect.sct_own, mtype);
351         if (c < 0) {
352             pr("No owned %s from %s to %s!\n",
353                mtype == MOB_RAIL ? "railway" : "path",
354                xyas(unit->x, unit->y, player->cnum),
355                xyas(destx, desty, player->cnum));
356             return NULL;
357         }
358     }
359     len = path_find_route(buf, bufsz, unit->x, unit->y, destx, desty);
360     if (len == 0 || unit->ef_type == EF_LAND) {
361         if (len + 1 < bufsz)
362             strcpy(buf + len, "h");
363         len++;
364     }
365     if (len >= bufsz) {
366         pr("Can't handle path to %s, it's too long, sorry\n",
367            xyas(destx, desty, player->cnum));
368         return NULL;
369     }
370     pr("Using path '%s'\n", buf);
371     return buf;
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_move_route(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_move_route(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_dir(list, dir, player->cnum)
419                     || shp_nav_gauntlet(list, 1, player->cnum);
420             else {
421                 if (!moved && !lnd_abandon_askyn(list))
422                     return RET_FAIL;
423                 stopping = lnd_mar_dir(list, dir, player->cnum)
424                     || lnd_mar_gauntlet(list, 1, player->cnum);
425             }
426             if (dir == DIR_STOP)
427                 return RET_OK;
428             moved = 1;
429             if (stopping)
430                 cp = "";
431         } else {
432             cp = unit_move_non_dir(list, cp, &suppress_map);
433             if (!cp) {
434                 direrr("`%c' to stop", ", `%c' to view", NULL);
435                 pr(", `i' to list %s, `f' to change %s,\n",
436                    type == EF_SHIP ? "ships" : "units",
437                    type == EF_SHIP ? "flagship" : "leader");
438                 pr("`r' to radar, %s`l' to look, `M' to map, `B' to bmap,\n",
439                    type == EF_SHIP ? "`s' to sonar, " : "");
440                 pr("`d' to drop mines, and `m' to minesweep\n");
441                 cp = "";
442             }
443         }
444
445         if (type == EF_SHIP)
446             shp_nav_stay_behind(list, player->cnum);
447         else
448             lnd_mar_stay_behind(list, player->cnum);
449
450         if (QEMPTY(list)) {
451             pr("No %s left\n", type == EF_SHIP ? "ships" : "lands");
452             return RET_OK;
453         }
454
455         leader = get_leader(list);
456         if (leader->uid != leader_uid) {
457             leader_uid = leader->uid;
458             pr("Changing %s to %s\n",
459                leader->ef_type == EF_SHIP ? "flagship" : "leader",
460                unit_nameof(leader));
461         }
462         unit_rad_map_set(list);
463     }
464 }
465
466 /*
467  * Teleport UNIT to X,Y.
468  * If UNIT's mission op-area is centered on it, keep it centered.
469  */
470 void
471 unit_teleport(struct empobj *unit, coord x, coord y)
472 {
473     if (unit->opx == unit->x && unit->opy == unit->y) {
474         unit->opx = x;
475         unit->opy = y;
476     }
477     unit->x = x;
478     unit->y = y;
479 }
480
481 /*
482  * Update cargo of CARRIER for movement or destruction.
483  * If the carrier is destroyed, destroy its cargo (planes, land units,
484  * nukes).
485  * Else update their location to the carrier's.  Any op sectors equal
486  * to location get updated, too.
487  * Return number of units updated.
488  */
489 int
490 unit_update_cargo(struct empobj *carrier)
491 {
492     int cargo_type;
493     struct nstr_item ni;
494     union empobj_storage obj;
495     int n = 0;
496
497     for (cargo_type = EF_PLANE; cargo_type <= EF_NUKE; cargo_type++) {
498         snxtitem_cargo(&ni, cargo_type, carrier->ef_type, carrier->uid);
499         while (nxtitem(&ni, &obj)) {
500             if (carrier->own)
501                 unit_teleport(&obj.gen, carrier->x, carrier->y);
502             else {
503                 mpr(obj.gen.own, "%s lost!\n", unit_nameof(&obj.gen));
504                 obj.gen.effic = 0;
505             }
506             put_empobj(cargo_type, obj.gen.uid, &obj);
507             n++;
508         }
509     }
510     return n;
511 }
512
513 /*
514  * Drop cargo of UNIT.
515  * Give it to NEWOWN, unless it's zero.
516  */
517 void
518 unit_drop_cargo(struct empobj *unit, natid newown)
519 {
520     int type;
521     struct nstr_item ni;
522     union empobj_storage cargo;
523
524     for (type = EF_PLANE; type <= EF_NUKE; type++) {
525         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
526         while (nxtitem(&ni, &cargo)) {
527             switch (type) {
528             case EF_PLANE:
529                 cargo.plane.pln_ship = cargo.plane.pln_land = -1;
530                 break;
531             case EF_LAND:
532                 cargo.land.lnd_ship = cargo.land.lnd_land = -1;
533                 break;
534             case EF_NUKE:
535                 cargo.nuke.nuk_plane = -1;
536                 break;
537             }
538             mpr(cargo.gen.own, "%s transferred off %s %d to %s\n",
539                 unit_nameof(&cargo.gen),
540                 ef_nameof(unit->ef_type), unit->uid,
541                 xyas(cargo.gen.x, cargo.gen.y, cargo.gen.own));
542             if (newown)
543                 unit_give_away(&cargo.gen, newown, cargo.gen.own);
544             put_empobj(type, cargo.gen.uid, &cargo.gen);
545         }
546     }
547 }
548
549 /*
550  * Give UNIT and its cargo to RECIPIENT.
551  * No action if RECIPIENT already owns UNIT.
552  * If GIVER is non-zero, inform RECIPIENT and GIVER of the transaction.
553  * Clears mission and group on the units given away.
554  */
555 void
556 unit_give_away(struct empobj *unit, natid recipient, natid giver)
557 {
558     int type;
559     struct nstr_item ni;
560     union empobj_storage cargo;
561
562     if (unit->own == recipient)
563         return;
564
565     if (giver) {
566         mpr(unit->own, "%s given to %s\n",
567             unit_nameof(unit), cname(recipient));
568         mpr(recipient, "%s given to you by %s\n",
569             unit_nameof(unit), cname(giver));
570     }
571
572     unit->own = recipient;
573     unit_wipe_orders(unit);
574     put_empobj(unit->ef_type, unit->uid, unit);
575
576     for (type = EF_PLANE; type <= EF_NUKE; type++) {
577         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
578         while (nxtitem(&ni, &cargo))
579             unit_give_away(&cargo.gen, recipient, giver);
580     }
581 }
582
583 /*
584  * Wipe orders and such from UNIT.
585  */
586 void
587 unit_wipe_orders(struct empobj *unit)
588 {
589     struct shpstr *sp;
590     struct plnstr *pp;
591     struct lndstr *lp;
592
593     unit->group = 0;
594     unit->opx = unit->opy = 0;
595     unit->mission = 0;
596     unit->radius = 0;
597
598     switch (unit->ef_type) {
599     case EF_SHIP:
600         sp = (struct shpstr *)unit;
601         sp->shp_rflags = 0;
602         sp->shp_rpath[0] = 0;
603         break;
604     case EF_PLANE:
605         pp = (struct plnstr *)unit;
606         pp->pln_range = pln_range_max(pp);
607         break;
608     case EF_LAND:
609         lp = (struct lndstr *)unit;
610         lp->lnd_retreat = morale_base;
611         lp->lnd_rflags = 0;
612         lp->lnd_rpath[0] = 0;
613         break;
614     case EF_NUKE:
615         break;
616     default:
617         CANT_REACH();
618     }
619 }