]> git.pond.sub.org Git - empserver/blob - src/lib/subs/supply.c
Avoid seqno mismatch oops in recursive land unit supply
[empserver] / src / lib / subs / supply.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  *  supply.c: Supply subroutines
29  * 
30  *  Known contributors to this file:
31  *  
32  */
33
34 #include <config.h>
35
36 #include <math.h>
37 #include "file.h"
38 #include "land.h"
39 #include "nat.h"
40 #include "optlist.h"
41 #include "player.h"
42 #include "prototypes.h"
43 #include "sect.h"
44 #include "ship.h"
45
46 static int get_minimum(struct lndstr *, i_type);
47 static int s_commod(int, int, int, i_type, int, int);
48
49 /*
50  * We want to get enough guns to be maxed out, enough shells to
51  *      fire once, one update's worth of food, enough fuel for
52  *      one update.
53  *
54  * Firts, try to forage in the sector
55  * Second look for a warehouse or headquarters to leech
56  * Third, look for a ship we own in a harbor
57  * Fourth, look for supplies in a supply unit we own
58  *              (one good reason to do this last is that the supply
59  *               unit will then call resupply, taking more time)
60  *
61  * May want to put code to resupply with SAMs here, later --ts
62  */
63
64 void
65 resupply_all(struct lndstr *lp)
66 {
67     if (!opt_NOFOOD)
68         resupply_commod(lp, I_FOOD);
69     resupply_commod(lp, I_SHELL);
70     if (opt_FUEL)
71         resupply_commod(lp, I_PETROL);
72 }
73
74 /*
75  * If the unit has less than it's minimum level of a
76  * certain commodity, fill it, to the best of our abilities.
77  */
78
79 void
80 resupply_commod(struct lndstr *lp, i_type type)
81 {
82     int amt;
83     struct shpstr ship;
84
85     /* Ok, do we now have enough? */
86     amt = get_minimum(lp, type) - lp->lnd_item[type];
87     if (amt > 0) {
88         lp->lnd_item[type] += supply_commod(lp->lnd_own,
89                                             lp->lnd_x, lp->lnd_y,
90                                             type, amt);
91         amt = get_minimum(lp, type) - lp->lnd_item[type];
92     }
93     /* Now, check again to see if we have enough. */
94     if (amt > 0) {
95         /* Are we on a ship?  if so, try to get it from the ship first. */
96         if (lp->lnd_ship >= 0) {
97             getship(lp->lnd_ship, &ship);
98             /* Now, determine how much we can get */
99             if (amt > ship.shp_item[type])
100                 amt = ship.shp_item[type];
101             /* Now, add and subtract */
102             lp->lnd_item[type] += amt;
103             ship.shp_item[type] -= amt;
104             putship(lp->lnd_ship, &ship);
105         }
106     }
107
108     if (opt_FUEL && type == I_PETROL) {
109         int fuel_needed = lchr[lp->lnd_type].l_fuelu
110             * ((float)etu_per_update * land_mob_scale) / 10.0;
111
112         while ((lp->lnd_fuel < fuel_needed) && lp->lnd_item[I_PETROL]) {
113             lp->lnd_fuel += 10;
114             if (lp->lnd_fuel > lchr[lp->lnd_type].l_fuelc)
115                 lp->lnd_fuel = lchr[lp->lnd_type].l_fuelc;
116             lp->lnd_item[I_PETROL]--;
117         }
118     }
119 }
120
121 /*
122  * Actually get the commod
123  */
124 int
125 supply_commod(int own, int x, int y, i_type type, int total_wanted)
126 {
127     if (total_wanted <= 0)
128         return 0;
129     return s_commod(own, x, y, type, total_wanted, !player->simulation);
130 }
131
132 /*
133  * Just return the number you COULD get, without doing it
134  */
135 static int
136 try_supply_commod(int own, int x, int y, i_type type, int total_wanted)
137 {
138     if (total_wanted <= 0)
139         return 0;
140
141     return s_commod(own, x, y, type, total_wanted, 0);
142 }
143
144 /* Get supplies of a certain type */
145 static int
146 s_commod(int own, int x, int y, i_type type, int total_wanted,
147          int actually_doit)
148 {
149     int wanted = total_wanted;
150     int gotten = 0, lookrange;
151     struct sctstr sect, dest;
152     struct nstr_sect ns;
153     struct nstr_item ni;
154     struct lchrstr *lcp;
155     struct shpstr ship;
156     struct lndstr land;
157     /* leave at least 1 military in sectors/ships */
158     int minimum = (type == I_MILIT ? 1 : 0);
159     int can_move;
160     double move_cost, weight, mobcost;
161     int packing;
162     struct dchrstr *dp;
163     struct ichrstr *ip;
164     char buf[1024];
165
166     /* try to get it from sector we're in */
167     getsect(x, y, &dest);
168     getsect(x, y, &sect);
169     if (sect.sct_own == own) {
170         if (sect.sct_item[type] - wanted >= minimum) {
171             sect.sct_item[type] -= wanted;
172             if (actually_doit)
173                 putsect(&sect);
174             return total_wanted;
175         } else if (sect.sct_item[type] - minimum > 0) {
176             gotten += sect.sct_item[type] - minimum;
177             wanted -= sect.sct_item[type] - minimum;
178             sect.sct_item[type] = minimum;
179             if (actually_doit)
180                 putsect(&sect);
181         }
182     }
183     /* look for a headquarters or warehouse */
184     lookrange = tfact(own, 10.0);
185     snxtsct_dist(&ns, x, y, lookrange);
186     while (nxtsct(&ns, &sect) && wanted) {
187         if (sect.sct_own != own)
188             continue;
189         if ((sect.sct_type != SCT_WAREH) &&
190             (sect.sct_type != SCT_HEADQ) && (sect.sct_type != SCT_HARBR))
191             continue;
192         if ((sect.sct_type == SCT_HEADQ) &&
193             (sect.sct_dist_x == sect.sct_x) &&
194             (sect.sct_dist_y == sect.sct_y))
195             continue;
196         if (sect.sct_effic < 60)
197             continue;
198         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
199             continue;
200         if (!opt_NOFOOD && type == I_FOOD)
201             minimum = 1 + (int)ceil(food_needed(sect.sct_item,
202                                                 etu_per_update));
203         if (sect.sct_item[type] <= minimum) {
204             /* Don't bother... */
205             continue;
206         }
207         ip = &ichr[type];
208         dp = &dchr[sect.sct_type];
209         packing = ip->i_pkg[dp->d_pkg];
210         if (packing > 1 && sect.sct_effic < 60)
211             packing = 1;
212         weight = (double)ip->i_lbs / packing;
213         mobcost = move_cost * weight;
214         if (mobcost > 0)
215             can_move = (double)sect.sct_mobil / mobcost;
216         else
217             can_move = sect.sct_item[type] - minimum;
218         if (can_move > sect.sct_item[type] - minimum)
219             can_move = sect.sct_item[type] - minimum;
220
221         if (can_move >= wanted) {
222             int n;
223
224             sect.sct_item[type] -= wanted;
225
226             /* take off mobility for delivering sect */
227             n = roundavg(total_wanted * weight * move_cost);
228             if (n < 0)
229                 n = 0;
230             if (n > sect.sct_mobil)
231                 n = sect.sct_mobil;
232             sect.sct_mobil -= n;
233
234             if (actually_doit)
235                 putsect(&sect);
236
237             return total_wanted;
238         } else if (can_move > 0) {
239             int n;
240             gotten += can_move;
241             wanted -= can_move;
242             sect.sct_item[type] -= can_move;
243
244             /* take off mobility for delivering sect */
245             n = roundavg(can_move * weight * move_cost);
246             if (n < 0)
247                 n = 0;
248             if (n > sect.sct_mobil)
249                 n = sect.sct_mobil;
250             sect.sct_mobil -= n;
251
252             if (actually_doit)
253                 putsect(&sect);
254         }
255     }
256
257     /* look for an owned ship in a harbor */
258     snxtitem_dist(&ni, EF_SHIP, x, y, lookrange);
259
260     while (nxtitem(&ni, &ship) && wanted) {
261         if (ship.shp_own != own)
262             continue;
263
264         if (!(mchr[(int)ship.shp_type].m_flags & M_SUPPLY))
265             continue;
266         getsect(ship.shp_x, ship.shp_y, &sect);
267         if (sect.sct_type != SCT_HARBR)
268             continue;
269         if (sect.sct_effic < 2)
270             continue;
271         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
272             continue;
273         if (!opt_NOFOOD && type == I_FOOD)
274             minimum = 1 + (int)ceil(food_needed(ship.shp_item,
275                                                 etu_per_update));
276         if (ship.shp_item[type] <= minimum) {
277             /* Don't bother... */
278             continue;
279         }
280         ip = &ichr[type];
281         dp = &dchr[sect.sct_type];
282         packing = ip->i_pkg[dp->d_pkg];
283         if (packing > 1 && sect.sct_effic < 60)
284             packing = 1;
285         weight = (double)ip->i_lbs / packing;
286         mobcost = move_cost * weight;
287         if (mobcost > 0)
288             can_move = (double)sect.sct_mobil / mobcost;
289         else
290             can_move = ship.shp_item[type] - minimum;
291         if (can_move > ship.shp_item[type] - minimum)
292             can_move = ship.shp_item[type] - minimum;
293         if (can_move >= wanted) {
294             int n;
295             ship.shp_item[type] -= wanted;
296
297             n = roundavg(wanted * weight * move_cost);
298             if (n < 0)
299                 n = 0;
300             if (n > sect.sct_mobil)
301                 n = sect.sct_mobil;
302             sect.sct_mobil -= n;
303             if (actually_doit) {
304                 putship(ship.shp_uid, &ship);
305                 putsect(&sect);
306             }
307             return total_wanted;
308         } else if (can_move > 0) {
309             int n;
310             gotten += can_move;
311             wanted -= can_move;
312             ship.shp_item[type] -= can_move;
313
314             n = roundavg(can_move * weight * move_cost);
315             if (n < 0)
316                 n = 0;
317             if (n > sect.sct_mobil)
318                 n = sect.sct_mobil;
319             sect.sct_mobil -= n;
320
321             if (actually_doit) {
322                 putship(ship.shp_uid, &ship);
323                 putsect(&sect);
324             }
325         }
326     }
327
328     /* look for an owned supply unit */
329     snxtitem_dist(&ni, EF_LAND, x, y, lookrange);
330
331     while (nxtitem(&ni, &land) && wanted) {
332         int min;
333
334         if (land.lnd_own != own)
335             continue;
336
337         lcp = &lchr[(int)land.lnd_type];
338         if (!(lcp->l_flags & L_SUPPLY))
339             continue;
340
341         if (land.lnd_item[type] <= get_minimum(&land, type))
342             continue;
343
344         getsect(land.lnd_x, land.lnd_y, &sect);
345         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
346             continue;
347
348         if ((land.lnd_ship >= 0) && (sect.sct_type != SCT_HARBR))
349             continue;
350
351         if ((land.lnd_ship >= 0) && (sect.sct_effic < 2))
352             continue;
353
354         if (land.lnd_item[type] - wanted < get_minimum(&land, type)) {
355             struct lndstr save;
356
357             /*
358              * Temporarily zap this unit's store, so the recursion
359              * avoids it.
360              */
361             save = land;
362             land.lnd_item[type] = 0;
363             putland(land.lnd_uid, &land);
364             save.lnd_seqno = land.lnd_seqno;
365
366             land.lnd_item[type] =
367                 save.lnd_item[type] + s_commod(own, land.lnd_x, land.lnd_y,
368                                                type, wanted, actually_doit);
369             if (actually_doit)
370                 putland(land.lnd_uid, &land);
371             else
372                 putland(save.lnd_uid, &save);
373         }
374
375         min = get_minimum(&land, type);
376         ip = &ichr[type];
377         weight = ip->i_lbs;
378         mobcost = move_cost * weight;
379         if (mobcost > 0)
380             can_move = (double)land.lnd_mobil / mobcost;
381         else
382             can_move = land.lnd_item[type] - min;
383         if (can_move > land.lnd_item[type] - min)
384             can_move = land.lnd_item[type] - min;
385
386         if (can_move >= wanted) {
387             land.lnd_item[type] -= wanted;
388
389             /* resupply the supply unit */
390             resupply_commod(&land, type);
391
392             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
393
394             if (actually_doit)
395                 putland(land.lnd_uid, &land);
396             return total_wanted;
397         } else if (can_move > 0) {
398             gotten += can_move;
399             wanted -= can_move;
400             land.lnd_item[type] -= can_move;
401
402             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
403
404             if (actually_doit)
405                 putland(land.lnd_uid, &land);
406         }
407     }
408
409     /* We've done the best we could */
410     /* return the number gotten */
411     return gotten;
412 }
413
414
415 /*
416  * We want to get enough shells to fire once,
417  * one update's worth of food, enough fuel for
418  * one update.
419  */
420
421 static int
422 get_minimum(struct lndstr *lp, i_type type)
423 {
424     struct lchrstr *lcp;
425     int max, want = 0;
426
427     lcp = &lchr[(int)lp->lnd_type];
428     max = lcp->l_item[type];
429
430     switch (type) {
431     case I_FOOD:
432         if (opt_NOFOOD)
433             return 0;           /* no food reqd, get out */
434         want = (int)ceil(food_needed(lp->lnd_item, etu_per_update));
435         break;
436     case I_SHELL:
437         want = lcp->l_ammo;
438         break;
439
440         /*
441          * return the amount of pet we'd need to get to 
442          * enough fuel for 1 update
443          */
444     case I_PETROL:
445         if (opt_FUEL == 0)
446             return 0;
447         want = lcp->l_fuelu * ((float)etu_per_update * land_mob_scale)
448             / 10.0;
449         want -= lp->lnd_fuel;
450         if (want > 0) {
451             want = want / 10;
452             if (want == 0)
453                 want++;
454         }
455
456         max = want;
457         break;
458     default:
459         return 0;
460     }
461
462     if (want > max)
463         want = max;
464
465     return want;
466 }
467
468 int
469 has_supply(struct lndstr *lp)
470 {
471     int shells_needed, shells, keepshells;
472     int food, food_needed, keepfood;
473     int fuel_needed, fuel, petrol_needed, petrol, keeppetrol;
474
475     if (!opt_NOFOOD) {
476         food_needed = get_minimum(lp, I_FOOD);
477         food = keepfood = lp->lnd_item[I_FOOD];
478         if (food < food_needed) {
479             lp->lnd_item[I_FOOD] = 0;
480             putland(lp->lnd_uid, lp);
481             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
482                                       I_FOOD, (food_needed - food));
483             lp->lnd_item[I_FOOD] = keepfood;
484             putland(lp->lnd_uid, lp);
485         }
486         if (food < food_needed)
487             return 0;
488
489     }
490
491     shells_needed = lchr[lp->lnd_type].l_ammo;
492     shells = keepshells = lp->lnd_item[I_SHELL];
493     if (shells < shells_needed) {
494         lp->lnd_item[I_SHELL] = 0;
495         putland(lp->lnd_uid, lp);
496         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
497                                     I_SHELL, (shells_needed - shells));
498         lp->lnd_item[I_SHELL] = keepshells;
499         putland(lp->lnd_uid, lp);
500     }
501
502     if (shells < shells_needed)
503         return 0;
504
505     if (opt_FUEL) {
506         fuel_needed = lchr[lp->lnd_type].l_fuelu;
507         fuel = lp->lnd_fuel;
508         if (fuel < fuel_needed) {
509             petrol_needed =
510                 ldround((fuel_needed - fuel) / 10.0, 1);
511             petrol = keeppetrol = lp->lnd_item[I_PETROL];
512             if (petrol < petrol_needed) {
513                 lp->lnd_item[I_PETROL] = 0;
514                 putland(lp->lnd_uid, lp);
515                 petrol += try_supply_commod(lp->lnd_own,
516                                             lp->lnd_x, lp->lnd_y,
517                                             I_PETROL,
518                                             (petrol_needed - petrol));
519                 lp->lnd_item[I_PETROL] = keeppetrol;
520                 putland(lp->lnd_uid, lp);
521             }
522             fuel += petrol * 10;
523         }
524
525         if (fuel < fuel_needed)
526             return 0;
527     }
528     /* end opt_FUEL */
529     return 1;
530 }