]> git.pond.sub.org Git - empserver/blob - src/lib/common/cargo.c
Cargo lists storing lists of cargo for each carrier
[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     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     short *head = clink_headp(cl, type);
159     struct clink *linkv;
160     int n;
161     short *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 }
175
176 /*
177  * Update cargo lists for a change of CARGO's carrier.
178  * Carrier is of type TYPE, and changes from uid OLD to NEW.
179  * Negative uids mean no carrier.
180  */
181 void
182 unit_carrier_change(struct empobj *cargo, int type, int old, int new)
183 {
184     if (CANT_HAPPEN(type < 0 || type > EF_NUKE))
185         return;
186     if (old >= 0)
187         clink_rem(&clink[type][old], cargo->ef_type, cargo->uid);
188     if (new >= 0)
189         clink_add(&clink[type][new], cargo->ef_type, cargo->uid);
190 }
191
192 /*
193  * Update cargo lists for a change of PP's carrier.
194  * Carrier is of type TYPE, and changes from uid OLD to NEW.
195  * Negative uids mean no carrier.
196  */
197 void
198 pln_carrier_change(struct plnstr *pp, int type, int old, int new)
199 {
200     unit_carrier_change((struct empobj *)pp, type, old, new);
201 }
202
203 /*
204  * Update cargo lists for a change of LP's carrier.
205  * Carrier is of type TYPE, and changes from uid OLD to NEW.
206  * Negative uids mean no carrier.
207  */
208 void
209 lnd_carrier_change(struct lndstr *lp, int type, int old, int new)
210 {
211     unit_carrier_change((struct empobj *)lp, type, old, new);
212 }
213
214 /*
215  * Update cargo lists for a change of NP's carrier.
216  * Carrier is of type TYPE, and changes from uid OLD to NEW.
217  * Negative uids mean no carrier.
218  */
219 void
220 nuk_carrier_change(struct nukstr *np, int type, int old, int new)
221 {
222     unit_carrier_change((struct empobj *)np, type, old, new);
223 }
224
225 /*
226  * Initialize cargo lists from game state.
227  */
228 void
229 unit_cargo_init(void)
230 {
231     int i;
232     struct plnstr *pp;
233     struct lndstr *lp;
234     struct nukstr *np;
235
236     for (i = EF_SHIP; i <= EF_NUKE; i++) {
237         nclink[i] = 0;
238         unit_onresize(i);
239     }
240
241     for (i = 0; (pp = getplanep(i)); i++) {
242         if (!pp->pln_own)
243             continue;
244         if (CANT_HAPPEN(pp->pln_ship >= 0 && pp->pln_land >= 0))
245             pp->pln_land = -1;
246         pln_carrier_change(pp, EF_SHIP, -1, pp->pln_ship);
247         pln_carrier_change(pp, EF_LAND, -1, pp->pln_land);
248     }
249     for (i = 0; (lp = getlandp(i)); i++) {
250         if (!lp->lnd_own)
251             continue;
252         if (CANT_HAPPEN(lp->lnd_ship >= 0 && lp->lnd_land >= 0))
253             lp->lnd_land = -1;
254         lnd_carrier_change(lp, EF_SHIP, -1, lp->lnd_ship);
255         lnd_carrier_change(lp, EF_LAND, -1, lp->lnd_land);
256     }
257     for (i = 0; (np = getnukep(i)); i++) {
258         if (!np->nuk_own)
259             continue;
260         nuk_carrier_change(np, EF_PLANE, -1, np->nuk_plane);
261     }
262 }
263
264 /*
265  * Resize clink[TYPE] to match ef_nelem(TYPE).
266  * Return 0 on success, -1 on error.
267  * This is the struct empfile onresize callback for units.
268  */
269 int
270 unit_onresize(int type)
271 {
272     int n, i;
273     struct clink *cl;
274
275     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
276         return -1;
277
278     n = ef_nelem(type);
279     cl = realloc(clink[type], n * sizeof(*clink[type]));
280     if (!cl)
281         return -1;
282     for (i = nclink[type]; i < n; i++)
283         clink_init(&cl[i]);
284     clink[type] = cl;
285     nclink[type] = n;
286     clink_check(type);
287     return 0;
288 }
289
290 /*
291  * Find first unit on a carrier's cargo list for file type CARGO_TYPE.
292  * Search carrier UID of type TYPE.
293  * Return first unit's uid, or -1 if the carrier isn't carrying such
294  * units.
295  */
296 int
297 unit_cargo_first(int type, int uid, int cargo_type)
298 {
299     short *headp;
300
301     if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE))
302         return -1;
303     if (CANT_HAPPEN(uid < 0 || uid >= nclink[type]))
304         return -1;
305     headp = clink_headp(&clink[type][uid], cargo_type);
306     if (CANT_HAPPEN(!headp))
307         return -1;
308     return *headp;
309 }
310
311 /*
312  * Find the next unit on a cargo list for file type CARGO_TYPE.
313  * Get the unit after CARGO_UID.
314  * Return its uid, or -1 if there are no more on this list.
315  */
316 int
317 unit_cargo_next(int cargo_type, int cargo_uid)
318 {
319     if (CANT_HAPPEN(cargo_type < EF_SHIP || cargo_type > EF_NUKE))
320         return -1;
321     if (CANT_HAPPEN(cargo_uid < 0 || cargo_uid >= nclink[cargo_type]))
322         return -1;
323     return clink[cargo_type][cargo_uid].next;
324 }
325
326 /*
327  * If SP carries planes, return the uid of the first one, else -1.
328  */
329 int
330 pln_first_on_ship(struct shpstr *sp)
331 {
332     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_PLANE);
333 }
334
335 /*
336  * If LP carries planes, return the uid of the first one, else -1.
337  */
338 int
339 pln_first_on_land(struct lndstr *lp)
340 {
341     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_PLANE);
342 }
343
344 /*
345  * Find the next plane on the same carrier as plane#UID.
346  * Return its uid, or -1 if there are no more.
347  */
348 int
349 pln_next_on_unit(int uid)
350 {
351     return unit_cargo_next(EF_PLANE, uid);
352 }
353
354 /*
355  * If SP carries land units, return the uid of the first one, else -1.
356  */
357 int
358 lnd_first_on_ship(struct shpstr *sp)
359 {
360     return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_LAND);
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_land(struct lndstr *lp)
368 {
369     return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_LAND);
370 }
371
372 /*
373  * Find the next land unit on the same carrier as land#UID.
374  * Return its uid, or -1 if there are no more.
375  */
376 int
377 lnd_next_on_unit(int uid)
378 {
379     return unit_cargo_next(EF_LAND, uid);
380 }
381
382 #if 0 /* clashes with the one in src/lib/subs/nuke.c */
383 /*
384  * If PP carries a nuke, return its uid, else -1.
385  */
386 int
387 nuk_on_plane(struct plnstr *pp)
388 {
389     return unit_cargo_first(EF_PLANE, pp->pln_uid, EF_NUKE);
390 }
391 #endif