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