]> git.pond.sub.org Git - empserver/blob - src/lib/subs/unitsub.c
Update copyright notice
[empserver] / src / lib / subs / unitsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2014, 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-2013
32  */
33
34 #include <config.h>
35
36 #include "file.h"
37 #include "path.h"
38 #include "player.h"
39 #include "optlist.h"
40 #include "prototypes.h"
41 #include "unit.h"
42
43 char *
44 unit_nameof(struct empobj *gp)
45 {
46     switch (gp->ef_type) {
47     case EF_SHIP:
48         return prship((struct shpstr *)gp);
49     case EF_PLANE:
50         return prplane((struct plnstr *)gp);
51     case EF_LAND:
52         return prland((struct lndstr *)gp);
53     case EF_NUKE:
54         return prnuke((struct nukstr *)gp);
55     }
56     CANT_REACH();
57     return "The Beast #666";
58 }
59
60 void
61 unit_list(struct emp_qelem *unit_list)
62 {
63     struct emp_qelem *qp;
64     struct emp_qelem *next;
65     struct ulist *ulp;
66     int type, npln, nch, nxl;
67     struct empobj *unit;
68     struct lndstr *lnd;
69     struct shpstr *shp;
70
71     if (CANT_HAPPEN(QEMPTY(unit_list)))
72         return;
73     qp = unit_list->q_back;
74     ulp = (struct ulist *)qp;
75     type = ulp->unit.gen.ef_type;
76     if (CANT_HAPPEN(type != EF_LAND && type != EF_SHIP))
77         return;
78
79     if (type == EF_LAND)
80         pr("lnd#     land type       x,y    a  eff mil  sh gun xl ln  mu tech retr\n");
81     else
82         pr("shp#     ship type       x,y   fl  eff mil  sh gun pn he xl ln mob tech\n");
83
84     for (; qp != unit_list; qp = next) {
85         next = qp->q_back;
86         ulp = (struct ulist *)qp;
87         lnd = &ulp->unit.land;
88         shp = &ulp->unit.ship;
89         unit = &ulp->unit.gen;
90         if (CANT_HAPPEN(type != unit->ef_type))
91             continue;
92         pr("%4d ", unit->uid);
93         pr("%-16.16s ", empobj_chr_name(unit));
94         prxy("%4d,%-4d ", unit->x, unit->y);
95         pr("%1.1s", &unit->group);
96         pr("%4d%%", unit->effic);
97         if (type == EF_LAND) {
98             pr("%4d", lnd->lnd_item[I_MILIT]);
99             pr("%4d", lnd->lnd_item[I_SHELL]);
100             pr("%4d", lnd->lnd_item[I_GUN]);
101             pr("%3d%3d", lnd_nxlight(lnd), lnd_nland(lnd));
102         } else {
103             pr("%4d", shp->shp_item[I_MILIT]);
104             pr("%4d", shp->shp_item[I_SHELL]);
105             pr("%4d", shp->shp_item[I_GUN]);
106             npln = shp_nplane(shp, &nch, &nxl, NULL);
107             pr("%3d%3d%3d", npln - nch - nxl, nch, nxl);
108             pr("%3d", shp_nland(shp));
109         }
110         pr("%4d", unit->mobil);
111         pr("%4d", unit->tech);
112         if (type == EF_LAND) {
113             pr("%4d%%", lnd->lnd_retreat);
114         }
115         pr("\n");
116     }
117 }
118
119 void
120 unit_put(struct emp_qelem *list, natid actor)
121 {
122     struct emp_qelem *qp;
123     struct emp_qelem *newqp;
124     struct ulist *ulp;
125     struct empobj *unit;
126
127     qp = list->q_back;
128     while (qp != list) {
129         ulp = (struct ulist *)qp;
130         unit = &ulp->unit.gen;
131         if (CANT_HAPPEN(unit->ef_type != EF_LAND
132                         && unit->ef_type != EF_SHIP))
133             continue;
134         if (actor) {
135             mpr(actor, "%s stopped at %s\n", unit_nameof(unit),
136                 xyas(unit->x, unit->y, actor));
137             if (unit->ef_type == EF_LAND) {
138                 if (ulp->mobil < -127)
139                     ulp->mobil = -127;
140                 unit->mobil = ulp->mobil;
141             }
142         }
143         if (unit->ef_type == EF_SHIP)
144             unit->mobil = (int)ulp->mobil;
145         put_empobj(unit->ef_type, unit->uid, unit);
146         newqp = qp->q_back;
147         emp_remque(qp);
148         free(qp);
149         qp = newqp;
150     }
151 }
152
153 char *
154 unit_path(int together, struct empobj *unit, char *buf, size_t bufsz)
155 {
156     coord destx;
157     coord desty;
158     struct sctstr sect;
159     size_t len;
160     double c;
161     int mtype;
162
163     if (CANT_HAPPEN(unit->ef_type != EF_LAND && unit->ef_type != EF_SHIP))
164         return NULL;
165
166     if (!sarg_xy(buf, &destx, &desty))
167         return buf;
168     if (!together) {
169         pr("Cannot go to a destination sector if not all starting in the same sector\n");
170         return NULL;
171     }
172     if (unit->ef_type == EF_SHIP) {
173         c = path_find(unit->x, unit->y, destx, desty,
174                       player->cnum, MOB_SAIL);
175         if (c < 0 || unit->mobil <= 0) {
176             pr("Can't get to '%s' right now.\n",
177                xyas(destx, desty, player->cnum));
178             return NULL;
179         }
180     } else {
181         getsect(unit->x, unit->y, &sect);
182         mtype = lnd_mobtype((struct lndstr *)unit);
183         /*
184          * Note: passing sect.sct_own for actor is funny, but works:
185          * its only effect is to confine the search to that nation's
186          * land.  It doesn't affect mobility costs.  The real actor is
187          * different for marching in allied land, and passing it would
188          * break path finding there.
189          */
190         c = path_find(unit->x, unit->y, destx, desty, sect.sct_own, mtype);
191         if (c < 0) {
192             pr("No owned %s from %s to %s!\n",
193                mtype == MOB_RAIL ? "railway" : "path",
194                xyas(unit->x, unit->y, player->cnum),
195                xyas(destx, desty, player->cnum));
196             return NULL;
197         }
198     }
199     len = path_find_route(buf, bufsz, unit->x, unit->y, destx, desty);
200     if (len == 0 || unit->ef_type == EF_LAND) {
201         if (len + 1 < bufsz)
202             strcpy(buf + len, "h");
203         len++;
204     }
205     if (len >= bufsz) {
206         pr("Can't handle path to %s, it's too long, sorry\n",
207            xyas(destx, desty, player->cnum));
208         return NULL;
209     }
210     if (unit->ef_type == EF_LAND)
211         pr("Using path '%s'\n", buf);
212     return buf;
213 }
214
215 void
216 unit_view(struct emp_qelem *list)
217 {
218     struct sctstr sect;
219     struct emp_qelem *qp;
220     struct emp_qelem *next;
221     struct ulist *ulp;
222
223     for (qp = list->q_back; qp != list; qp = next) {
224         next = qp->q_back;
225         ulp = (struct ulist *)qp;
226         if (CANT_HAPPEN(!(ef_flags(ulp->unit.gen.ef_type) & EFF_XY)))
227             continue;
228         getsect(ulp->unit.gen.x, ulp->unit.gen.y, &sect);
229         if (ulp->unit.gen.ef_type == EF_SHIP) {
230             if (((struct mchrstr *)ulp->chrp)->m_flags & M_FOOD)
231                 pr("[fert:%d] ", sect.sct_fertil);
232             if (((struct mchrstr *)ulp->chrp)->m_flags & M_OIL)
233                 pr("[oil:%d] ", sect.sct_oil);
234         }
235         pr("%s @ %s %d%% %s\n", unit_nameof(&ulp->unit.gen),
236            xyas(ulp->unit.gen.x, ulp->unit.gen.y, player->cnum),
237            sect.sct_effic, dchr[sect.sct_type].d_name);
238     }
239 }
240
241 /*
242  * Teleport UNIT to X,Y.
243  * If UNIT's mission op-area is centered on it, keep it centered.
244  */
245 void
246 unit_teleport(struct empobj *unit, coord x, coord y)
247 {
248     if (unit->opx == unit->x && unit->opy == unit->y) {
249         unit->opx = x;
250         unit->opy = y;
251     }
252     unit->x = x;
253     unit->y = y;
254 }
255
256 /*
257  * Update cargo of CARRIER for movement or destruction.
258  * If the carrier is destroyed, destroy its cargo (planes, land units,
259  * nukes).
260  * Else update their location to the carrier's.  Any op sectors equal
261  * to location get updated, too.
262  * Return number of units updated.
263  */
264 int
265 unit_update_cargo(struct empobj *carrier)
266 {
267     int cargo_type;
268     struct nstr_item ni;
269     union empobj_storage obj;
270     int n = 0;
271
272     for (cargo_type = EF_PLANE; cargo_type <= EF_NUKE; cargo_type++) {
273         snxtitem_cargo(&ni, cargo_type, carrier->ef_type, carrier->uid);
274         while (nxtitem(&ni, &obj)) {
275             if (carrier->own)
276                 unit_teleport(&obj.gen, carrier->x, carrier->y);
277             else {
278                 mpr(obj.gen.own, "%s lost!\n", unit_nameof(&obj.gen));
279                 obj.gen.effic = 0;
280             }
281             put_empobj(cargo_type, obj.gen.uid, &obj);
282             n++;
283         }
284     }
285     return n;
286 }
287
288 /*
289  * Drop cargo of UNIT.
290  * Give it to NEWOWN, unless it's zero.
291  */
292 void
293 unit_drop_cargo(struct empobj *unit, natid newown)
294 {
295     int type;
296     struct nstr_item ni;
297     union empobj_storage cargo;
298
299     for (type = EF_PLANE; type <= EF_NUKE; type++) {
300         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
301         while (nxtitem(&ni, &cargo)) {
302             switch (type) {
303             case EF_PLANE:
304                 cargo.plane.pln_ship = cargo.plane.pln_land = -1;
305                 break;
306             case EF_LAND:
307                 cargo.land.lnd_ship = cargo.land.lnd_land = -1;
308                 break;
309             case EF_NUKE:
310                 cargo.nuke.nuk_plane = -1;
311                 break;
312             }
313             mpr(cargo.gen.own, "%s transferred off %s %d to %s\n",
314                 unit_nameof(&cargo.gen),
315                 ef_nameof(unit->ef_type), unit->uid,
316                 xyas(cargo.gen.x, cargo.gen.y, cargo.gen.own));
317             if (newown)
318                 unit_give_away(&cargo.gen, newown, cargo.gen.own);
319             put_empobj(type, cargo.gen.uid, &cargo.gen);
320         }
321     }
322 }
323
324 /*
325  * Give UNIT and its cargo to RECIPIENT.
326  * No action if RECIPIENT already owns UNIT.
327  * If GIVER is non-zero, inform RECIPIENT and GIVER of the transaction.
328  * Clears mission and group on the units given away.
329  */
330 void
331 unit_give_away(struct empobj *unit, natid recipient, natid giver)
332 {
333     int type;
334     struct nstr_item ni;
335     union empobj_storage cargo;
336
337     if (unit->own == recipient)
338         return;
339
340     if (giver) {
341         mpr(unit->own, "%s given to %s\n",
342             unit_nameof(unit), cname(recipient));
343         mpr(recipient, "%s given to you by %s\n",
344             unit_nameof(unit), cname(giver));
345     }
346
347     unit->own = recipient;
348     unit_wipe_orders(unit);
349     put_empobj(unit->ef_type, unit->uid, unit);
350
351     for (type = EF_PLANE; type <= EF_NUKE; type++) {
352         snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
353         while (nxtitem(&ni, &cargo))
354             unit_give_away(&cargo.gen, recipient, giver);
355     }
356 }
357
358 /*
359  * Wipe orders and such from UNIT.
360  */
361 void
362 unit_wipe_orders(struct empobj *unit)
363 {
364     struct shpstr *sp;
365     struct plnstr *pp;
366     struct lndstr *lp;
367     int i;
368
369     unit->group = 0;
370     unit->opx = unit->opy = 0;
371     unit->mission = 0;
372     unit->radius = 0;
373
374     switch (unit->ef_type) {
375     case EF_SHIP:
376         sp = (struct shpstr *)unit;
377         sp->shp_destx[0] = sp->shp_desty[0] = 0;
378         sp->shp_destx[1] = sp->shp_desty[1] = 0;
379         for (i = 0; i < TMAX; ++i) {
380             sp->shp_tstart[i] = I_NONE;
381             sp->shp_tend[i] = I_NONE;
382             sp->shp_lstart[i] = 0;
383             sp->shp_lend[i] = 0;
384         }
385         sp->shp_autonav = 0;
386         sp->shp_mobquota = 0;
387         sp->shp_path[0] = 0;
388         sp->shp_follow = sp->shp_uid;
389         sp->shp_rflags = 0;
390         sp->shp_rpath[0] = 0;
391         break;
392     case EF_PLANE:
393         pp = (struct plnstr *)unit;
394         pp->pln_range = pln_range_max(pp);
395         break;
396     case EF_LAND:
397         lp = (struct lndstr *)unit;
398         lp->lnd_retreat = morale_base;
399         lp->lnd_rflags = 0;
400         lp->lnd_rpath[0] = 0;
401         break;
402     case EF_NUKE:
403         break;
404     default:
405         CANT_REACH();
406     }
407 }