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