]> git.pond.sub.org Git - empserver/blob - src/lib/common/cargo.c
e399a0b812746a19144b043a7862ac20861a8b8a
[empserver] / src / lib / common / cargo.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2020, 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  *  cargo.c: Cargo lists
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2009-2020
31  */
32
33 #include <config.h>
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include "unit.h"
39
40 struct clink {
41     int next;
42     int head[EF_NUKE - EF_PLANE + 1];
43 };
44
45 /*
46  * Cargo lists
47  *
48  * Persistent game state encodes "who carries what" by storing the
49  * carrier UID in the cargo.  Cargo lists augment that: they store
50  * lists of cargo for each carrier.
51  *
52  * clink[TYPE] points to an array of cargo list links.  The array has
53  * nclink[TYPE] elements.  nclink[TYPE] tracks ef_nelem(TYPE).
54  * clink[TYPE][UID] is the cargo list link for unit UID of type TYPE.
55  * TYPE must be a unit file type: EF_SHIP, EF_PLANE, EF_LAND or
56  * EF_NUKE.  Other slots of clink[] and nclink[] are unused and remain
57  * zero.
58  *
59  * clink[TYPE][UID].next is the UID of the next unit of the same type
60  * in the same carrier, -1 if none.
61  *
62  * clink[TYPE][UID].head[CARGO-EF_PLANE] is the UID of the first unit
63  * of type CARGO carried by this unit, -1 if none.  The next unit, if
64  * any, is clink[CARGO][clink[TYPE][UID].head[CARGO-EF_PLANE]].next,
65  * and so forth.
66  *
67  * Each type of carrier can only carry certain types of cargo, but
68  * cargo lists know nothing about that.
69  */
70 static struct clink *clink[EF_NUKE + 1];
71 static int nclink[EF_NUKE + 1];
72
73 /*
74  * Return pointer to @cl's cargo list head for file type @type.
75  */
76 static int *
77 clink_headp(struct clink *cl, int type)
78 {
79     static int dummy;
80
81     if (CANT_HAPPEN(type < EF_PLANE || type > EF_NUKE)) {
82         dummy = -1;
83         return &dummy;
84     }
85     return &cl->head[type - EF_PLANE];
86 }
87
88 /*
89  * Initialize cargo list link @cl to empty.
90  */
91 static void
92 clink_init(struct clink *cl)
93 {
94     unsigned i;
95
96     cl->next = -1;
97     for (i = 0; i < ARRAY_SIZE(cl->head); i++)
98         cl->head[i] = -1;
99 }
100
101 /*
102  * Check whether *@uidp is a valid UID for file type @type.
103  */
104 static void
105 clink_check1(int *uidp, int type)
106 {
107     if (CANT_HAPPEN(*uidp >= nclink[type]))
108         *uidp = -1;
109 }
110
111 /*
112  * Check validity of cargo lists for file type @type.
113  */
114 static void
115 clink_check(int type)
116 {
117     int carr_type, i;
118
119     /* check the heads for all carriers */
120     if (type != EF_SHIP) {
121         for (carr_type = EF_PLANE; carr_type <= EF_NUKE; carr_type++) {
122             for (i = 0; i < nclink[carr_type]; i++)
123                 clink_check1(clink_headp(&clink[carr_type][i], type),
124                              type);
125         }
126     }
127     /* check the nexts */
128     for (i = 0; i < nclink[type]; i++)
129         clink_check1(&clink[type][i].next, type);
130 }
131
132 /*
133  * Add to @cl's cargo list for type @type the UID @uid.
134  * @uid must not be on any cargo list already.
135  */
136 static void
137 clink_add(struct clink *cl, int type, int uid)
138 {
139     int *head = clink_headp(cl, type);
140
141     if (CANT_HAPPEN(type < 0 || type > EF_NUKE
142                     || uid < 0 || uid >= nclink[type]))
143         return;
144     if (CANT_HAPPEN(*head >= nclink[type]))
145         *head = -1;
146     CANT_HAPPEN(clink[type][uid].next >= 0);
147     clink[type][uid].next = *head;
148     *head = uid;
149 }
150
151 /*
152  * Remove from @cl's cargo list for type @type the UID @uid.
153  * @uid must be on that cargo list.
154  */
155 static void
156 clink_rem(struct clink *cl, int type, int uid)
157 {
158     int *head = clink_headp(cl, type);
159     struct clink *linkv;
160     int n;
161     int *p;
162
163     if (CANT_HAPPEN(type < 0 || type > EF_NUKE))
164         return;
165     linkv = clink[type];
166     n = nclink[type];
167
168     for (p = head; *p != uid; p = &linkv[*p].next) {
169         if (CANT_HAPPEN(*p < 0 || *p >= n))
170             return;
171     }
172
173     *p = linkv[uid].next;
174     linkv[uid].next = -1;
175 }
176
177 /*
178  * Update cargo lists for a change of @cargo's carrier.
179  * Carrier is of type @type, and changes from UID @old to @new.
180  * Negative UIDs mean no carrier.
181  */
182 void
183 unit_carrier_change(struct empobj *cargo, int type, int old, int new)
184 {
185     if (CANT_HAPPEN(type < 0 || type > EF_NUKE))
186         return;
187     if (old >= 0 && !CANT_HAPPEN(old >= nclink[type]))
188         clink_rem(&clink[type][old], cargo->ef_type, cargo->uid);
189     if (new >= 0 && !CANT_HAPPEN(new >= nclink[type]))
190         clink_add(&clink[type][new], cargo->ef_type, cargo->uid);
191 }
192
193 /*
194  * Update cargo lists for a change of @pp's carrier.
195  * Carrier is of type @type, and changes from UID @old to @new.
196  * Negative UIDs mean no carrier.
197  */
198 void
199 pln_carrier_change(struct plnstr *pp, int type, int old, int new)
200 {
201     unit_carrier_change((struct empobj *)pp, type, old, new);
202 }
203
204 /*
205  * Update cargo lists for a change of @lp's carrier.
206  * Carrier is of type @type, and changes from UID @old to @new.
207  * Negative UIDs mean no carrier.
208  */
209 void
210 lnd_carrier_change(struct lndstr *lp, int type, int old, int new)
211 {
212     unit_carrier_change((struct empobj *)lp, type, old, new);
213 }
214
215 /*
216  * Update cargo lists for a change of @np's carrier.
217  * Carrier is of type @type, and changes from UID @old to @new.
218  * Negative UIDs mean no carrier.
219  */
220 void
221 nuk_carrier_change(struct nukstr *np, int type, int old, int new)
222 {
223     unit_carrier_change((struct empobj *)np, type, old, new);
224 }
225
226 /*
227  * Initialize cargo lists from game state.
228  */
229 void
230 unit_cargo_init(void)
231 {
232     int i;
233     struct plnstr *pp;
234     struct lndstr *lp;
235     struct nukstr *np;
236
237     memset(nclink, 0, sizeof(nclink));
238     for (i = EF_SHIP; i <= EF_NUKE; i++)
239         unit_onresize(i);
240
241     for (i = 0; (pp = getplanep(i)); i++) {
242         if (!pp->pln_own) {
243             if (CANT_HAPPEN(pp->pln_ship >= 0 || pp->pln_land >= 0))
244                 pp->pln_ship = pp->pln_land = -1;
245             continue;
246         }
247         if (CANT_HAPPEN(pp->pln_ship >= 0 && pp->pln_land >= 0))
248             pp->pln_land = -1;
249         pln_carrier_change(pp, EF_SHIP, -1, pp->pln_ship);
250         pln_carrier_change(pp, EF_LAND, -1, pp->pln_land);
251     }
252     for (i = 0; (lp = getlandp(i)); i++) {
253         if (!lp->lnd_own) {
254             if (CANT_HAPPEN(lp->lnd_ship >= 0 || lp->lnd_land >= 0))
255                 lp->lnd_ship = lp->lnd_land = -1;
256             continue;
257         }
258         if (CANT_HAPPEN(lp->lnd_ship >= 0 && lp->lnd_land >= 0))
259             lp->lnd_land = -1;
260         lnd_carrier_change(lp, EF_SHIP, -1, lp->lnd_ship);
261         lnd_carrier_change(lp, EF_LAND, -1, lp->lnd_land);
262     }
263     for (i = 0; (np = getnukep(i)); i++) {
264         if (!np->nuk_own) {
265             if (CANT_HAPPEN(np->nuk_plane >= 0))
266                 np->nuk_plane = -1;
267             continue;
268         }
269         nuk_carrier_change(np, EF_PLANE, -1, np->nuk_plane);
270     }
271 }
272
273 /*
274  * Resize clink[@type] to match ef_nelem(@type).
275  * Return 0 on success, -1 on error.
276  * This is the struct empfile onresize callback for units.
277  */
278 void
279 unit_onresize(int type)
280 {
281     int n, i;
282     struct clink *cl;
283
284     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
285         return;
286
287     n = ef_nelem(type);
288     cl = realloc(clink[type], n * sizeof(*clink[type]));
289     if (!cl && n)
290         exit_nomem();
291     for (i = nclink[type]; i < n; i++)
292         clink_init(&cl[i]);
293     clink[type] = cl;
294     nclink[type] = n;
295     if (ef_flags(type) & EFF_MEM)
296         clink_check(type);
297 }
298
299 /*
300  * Find first unit on a carrier's cargo list for file type @cargo_type.
301  * Search carrier @uid of type @type.
302  * Return first unit's UID, or -1 if the carrier isn't carrying such
303  * units.
304  */
305 int
306 unit_cargo_first(int type, int uid, int cargo_type)
307 {
308     int *headp;
309
310     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
311         return -1;
312     if (CANT_HAPPEN(uid < 0 || uid >= nclink[type]))
313         return -1;
314     headp = clink_headp(&clink[type][uid], cargo_type);
315     if (CANT_HAPPEN(!headp))
316         return -1;
317     return *headp;
318 }
319
320 /*
321  * Find the next unit on a cargo list for file type @cargo_type.
322  * Get the unit after @cargo_uid.
323  * Return its UID, or -1 if there are no more on this list.
324  */
325 int
326 unit_cargo_next(int cargo_type, int cargo_uid)
327 {
328     if (CANT_HAPPEN(cargo_type < EF_SHIP || cargo_type > EF_NUKE))
329         return -1;
330     if (CANT_HAPPEN(cargo_uid < 0 || cargo_uid >= nclink[cargo_type]))
331         return -1;
332     return clink[cargo_type][cargo_uid].next;
333 }
334
335 /*
336  * If @sp carries planes, return the UID of the first one, else -1.
337  */
338 int
339 pln_first_on_ship(struct shpstr *sp)
340 {
341     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_PLANE);
342 }
343
344 /*
345  * If @lp carries planes, return the UID of the first one, else -1.
346  */
347 int
348 pln_first_on_land(struct lndstr *lp)
349 {
350     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_PLANE);
351 }
352
353 /*
354  * Find the next plane on the same carrier as plane#@uid.
355  * Return its UID, or -1 if there are no more.
356  */
357 int
358 pln_next_on_unit(int uid)
359 {
360     return unit_cargo_next(EF_PLANE, uid);
361 }
362
363 /*
364  * If @sp carries land units, return the UID of the first one, else -1.
365  */
366 int
367 lnd_first_on_ship(struct shpstr *sp)
368 {
369     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_LAND);
370 }
371
372 /*
373  * If @lp carries land units, return the UID of the first one, else -1.
374  */
375 int
376 lnd_first_on_land(struct lndstr *lp)
377 {
378     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_LAND);
379 }
380
381 /*
382  * Find the next land unit on the same carrier as land#@uid.
383  * Return its UID, or -1 if there are no more.
384  */
385 int
386 lnd_next_on_unit(int uid)
387 {
388     return unit_cargo_next(EF_LAND, uid);
389 }
390
391 /*
392  * If @pp carries a nuke, return its UID, else -1.
393  */
394 int
395 nuk_on_plane(struct plnstr *pp)
396 {
397     return unit_cargo_first(EF_PLANE, pp->pln_uid, EF_NUKE);
398 }
399
400 /*
401  * Return length of a carrier's cargo list for file type @cargo_type.
402  * Use carrier @uid of type @type.
403  */
404 int
405 unit_cargo_count(int type, int uid, int cargo_type)
406 {
407     int n, cargo;
408
409     n = 0;
410     for (cargo = unit_cargo_first(type, uid, cargo_type);
411          cargo >= 0;
412          cargo = unit_cargo_next(cargo_type, cargo))
413         n++;
414
415     return n;
416 }
417
418 /*
419  * Return number of land units loaded on @sp.
420  */
421 int
422 shp_nland(struct shpstr *sp)
423 {
424     return unit_cargo_count(EF_SHIP, sp->shp_uid, EF_LAND);
425 }
426
427 /*
428  * Return number of land units loaded on @lp.
429  */
430 int
431 lnd_nland(struct lndstr *lp)
432 {
433     return unit_cargo_count(EF_LAND, lp->lnd_uid, EF_LAND);
434 }
435
436 int
437 unit_nplane(int type, int uid, int *nchopper, int *nxlight, int *nmsl)
438 {
439     int n, nch, nxl, nms, cargo;
440     struct plnstr *pp;
441     struct plchrstr *pcp;
442
443     n = nxl = nch = nms = 0;
444     for (cargo = unit_cargo_first(type, uid, EF_PLANE);
445          (pp = getplanep(cargo));
446          cargo = pln_next_on_unit(cargo)) {
447         pcp = &plchr[pp->pln_type];
448         if (pcp->pl_flags & P_K)
449             nch++;
450         else if (pcp->pl_flags & P_E)
451             nxl++;
452         else if (pcp->pl_flags & P_M)
453             nms++;
454         n++;
455     }
456
457     if (nchopper)
458         *nchopper = nch;
459     if (nxlight)
460         *nxlight = nxl;
461     if (nmsl)
462         *nmsl = nms;
463     return n;
464 }
465
466 int
467 shp_nplane(struct shpstr *sp, int *nchopper, int *nxlight, int *nmsl)
468 {
469     return unit_nplane(EF_SHIP, sp->shp_uid, nchopper, nxlight, nmsl);
470 }
471
472 int
473 lnd_nxlight(struct lndstr *lp)
474 {
475     int n;
476
477     unit_nplane(EF_LAND, lp->lnd_uid, NULL, &n, NULL);
478     return n;
479 }