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