]> git.pond.sub.org Git - empserver/blob - src/lib/subs/unitsub.c
5441624150dd51abd7bd177b4c11f7bbbbf95db2
[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_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 }