1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, 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 *
clink_headp(struct clink * cl,int type)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
clink_init(struct clink * cl)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
clink_check1(int * uidp,int type)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
clink_check(int type)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
clink_add(struct clink * cl,int type,int uid)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
clink_rem(struct clink * cl,int type,int uid)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
unit_carrier_change(struct empobj * cargo,int type,int old,int new)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
pln_carrier_change(struct plnstr * pp,int type,int old,int new)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
lnd_carrier_change(struct lndstr * lp,int type,int old,int new)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
nuk_carrier_change(struct nukstr * np,int type,int old,int new)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
unit_cargo_init(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
unit_onresize(int type)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
unit_cargo_first(int type,int uid,int cargo_type)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
unit_cargo_next(int cargo_type,int cargo_uid)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
pln_first_on_ship(struct shpstr * sp)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
pln_first_on_land(struct lndstr * lp)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
pln_next_on_unit(int uid)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
lnd_first_on_ship(struct shpstr * sp)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
lnd_first_on_land(struct lndstr * lp)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
lnd_next_on_unit(int uid)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
nuk_on_plane(struct plnstr * pp)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
unit_cargo_count(int type,int uid,int cargo_type)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
shp_nland(struct shpstr * sp)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
lnd_nland(struct lndstr * lp)431 lnd_nland(struct lndstr *lp)
432 {
433     return unit_cargo_count(EF_LAND, lp->lnd_uid, EF_LAND);
434 }
435 
436 int
unit_nplane(int type,int uid,int * nchopper,int * nxlight,int * nmsl)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
shp_nplane(struct shpstr * sp,int * nchopper,int * nxlight,int * nmsl)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
lnd_nxlight(struct lndstr * lp)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 }
480