]> git.pond.sub.org Git - empserver/blob - src/lib/common/cargo.c
Zap next uid in clink_rem(), check it in clink_add()
[empserver] / src / lib / common / cargo.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  cargo.c: Cargo lists
29  * 
30  *  Known contributors to this file:
31  *     Markus Armbruster, 2008
32  */
33
34 #include <config.h>
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include "file.h"
39 #include "unit.h"
40
41 struct clink {
42     short next;
43     short 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 short nclink[EF_NUKE + 1];
73
74 /*
75  * Return pointer to CL's cargo list head for file type TYPE.
76  */
77 static short *
78 clink_headp(struct clink *cl, int type)
79 {
80     static short 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(short *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     short *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     short *head = clink_headp(cl, type);
160     struct clink *linkv;
161     int n;
162     short *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)
189         clink_rem(&clink[type][old], cargo->ef_type, cargo->uid);
190     if (new >= 0)
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     for (i = EF_SHIP; i <= EF_NUKE; i++) {
239         nclink[i] = 0;
240         unit_onresize(i);
241     }
242
243     for (i = 0; (pp = getplanep(i)); i++) {
244         if (!pp->pln_own)
245             continue;
246         if (CANT_HAPPEN(pp->pln_ship >= 0 && pp->pln_land >= 0))
247             pp->pln_land = -1;
248         pln_carrier_change(pp, EF_SHIP, -1, pp->pln_ship);
249         pln_carrier_change(pp, EF_LAND, -1, pp->pln_land);
250     }
251     for (i = 0; (lp = getlandp(i)); i++) {
252         if (!lp->lnd_own)
253             continue;
254         if (CANT_HAPPEN(lp->lnd_ship >= 0 && lp->lnd_land >= 0))
255             lp->lnd_land = -1;
256         lnd_carrier_change(lp, EF_SHIP, -1, lp->lnd_ship);
257         lnd_carrier_change(lp, EF_LAND, -1, lp->lnd_land);
258     }
259     for (i = 0; (np = getnukep(i)); i++) {
260         if (!np->nuk_own)
261             continue;
262         nuk_carrier_change(np, EF_PLANE, -1, np->nuk_plane);
263     }
264 }
265
266 /*
267  * Resize clink[TYPE] to match ef_nelem(TYPE).
268  * Return 0 on success, -1 on error.
269  * This is the struct empfile onresize callback for units.
270  */
271 int
272 unit_onresize(int type)
273 {
274     int n, i;
275     struct clink *cl;
276
277     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
278         return -1;
279
280     n = ef_nelem(type);
281     cl = realloc(clink[type], n * sizeof(*clink[type]));
282     if (!cl && n)
283         exit_nomem();
284     for (i = nclink[type]; i < n; i++)
285         clink_init(&cl[i]);
286     clink[type] = cl;
287     nclink[type] = n;
288     clink_check(type);
289     return 0;
290 }
291
292 /*
293  * Find first unit on a carrier's cargo list for file type CARGO_TYPE.
294  * Search carrier UID of type TYPE.
295  * Return first unit's uid, or -1 if the carrier isn't carrying such
296  * units.
297  */
298 int
299 unit_cargo_first(int type, int uid, int cargo_type)
300 {
301     short *headp;
302
303     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
304         return -1;
305     if (CANT_HAPPEN(uid < 0 || uid >= nclink[type]))
306         return -1;
307     headp = clink_headp(&clink[type][uid], cargo_type);
308     if (CANT_HAPPEN(!headp))
309         return -1;
310     return *headp;
311 }
312
313 /*
314  * Find the next unit on a cargo list for file type CARGO_TYPE.
315  * Get the unit after CARGO_UID.
316  * Return its uid, or -1 if there are no more on this list.
317  */
318 int
319 unit_cargo_next(int cargo_type, int cargo_uid)
320 {
321     if (CANT_HAPPEN(cargo_type < EF_SHIP || cargo_type > EF_NUKE))
322         return -1;
323     if (CANT_HAPPEN(cargo_uid < 0 || cargo_uid >= nclink[cargo_type]))
324         return -1;
325     return clink[cargo_type][cargo_uid].next;
326 }
327
328 /*
329  * If SP carries planes, return the uid of the first one, else -1.
330  */
331 int
332 pln_first_on_ship(struct shpstr *sp)
333 {
334     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_PLANE);
335 }
336
337 /*
338  * If LP carries planes, return the uid of the first one, else -1.
339  */
340 int
341 pln_first_on_land(struct lndstr *lp)
342 {
343     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_PLANE);
344 }
345
346 /*
347  * Find the next plane on the same carrier as plane#UID.
348  * Return its uid, or -1 if there are no more.
349  */
350 int
351 pln_next_on_unit(int uid)
352 {
353     return unit_cargo_next(EF_PLANE, uid);
354 }
355
356 /*
357  * If SP carries land units, return the uid of the first one, else -1.
358  */
359 int
360 lnd_first_on_ship(struct shpstr *sp)
361 {
362     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_LAND);
363 }
364
365 /*
366  * If SP carries land units, return the uid of the first one, else -1.
367  */
368 int
369 lnd_first_on_land(struct lndstr *lp)
370 {
371     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_LAND);
372 }
373
374 /*
375  * Find the next land unit on the same carrier as land#UID.
376  * Return its uid, or -1 if there are no more.
377  */
378 int
379 lnd_next_on_unit(int uid)
380 {
381     return unit_cargo_next(EF_LAND, uid);
382 }
383
384 /*
385  * If PP carries a nuke, return its uid, else -1.
386  */
387 int
388 nuk_on_plane(struct plnstr *pp)
389 {
390     return unit_cargo_first(EF_PLANE, pp->pln_uid, EF_NUKE);
391 }
392
393 /*
394  * Return length of a carrier's cargo list for file type CARGO_TYPE.
395  */
396 int
397 unit_cargo_count(int type, int uid, int cargo_type)
398 {
399     int n, cargo;
400
401     n = 0;
402     for (cargo = unit_cargo_first(type, uid, cargo_type);
403          cargo >= 0;
404          cargo = unit_cargo_next(cargo_type, cargo))
405         n++;
406
407     return n;
408 }
409
410 /*
411  * Return number of land units loaded on SP.
412  */
413 int
414 shp_nland(struct shpstr *sp)
415 {
416     return unit_cargo_count(EF_SHIP, sp->shp_uid, EF_LAND);
417 }
418
419 /*
420  * Return number of land units loaded on LP.
421  */
422 int
423 lnd_nland(struct lndstr *lp)
424 {
425     return unit_cargo_count(EF_LAND, lp->lnd_uid, EF_LAND);
426 }
427
428 int
429 unit_nplane(int type, int uid, int *nchopper, int *nxlight, int *nmsl)
430 {
431     int n, nch, nxl, nms, cargo;
432     struct plnstr *pp;
433     struct plchrstr *pcp;
434
435     n = nxl = nch = nms = 0;
436     for (cargo = unit_cargo_first(type, uid, EF_PLANE);
437          (pp = getplanep(cargo));
438          cargo = pln_next_on_unit(cargo)) {
439         pcp = &plchr[pp->pln_type];
440         if (pcp->pl_flags & P_K)
441             nch++;
442         else if (pcp->pl_flags & P_E)
443             nxl++;
444         else if (pcp->pl_flags & P_M)
445             nms++;
446         n++;
447     }
448
449     if (nchopper)
450         *nchopper = nch;
451     if (nxlight)
452         *nxlight = nxl;
453     if (nmsl)
454         *nmsl = nms;
455     return n;
456 }
457
458 int
459 shp_nplane(struct shpstr *sp, int *nchopper, int *nxlight, int *nmsl)
460 {
461     return unit_nplane(EF_SHIP, sp->shp_uid, nchopper, nxlight, nmsl);
462 }
463
464 int
465 lnd_nxlight(struct lndstr *lp)
466 {
467     int n;
468
469     unit_nplane(EF_LAND, lp->lnd_uid, NULL, &n, NULL);
470     return n;
471 }