]> git.pond.sub.org Git - empserver/blob - src/lib/subs/supply.c
Update copyright notice.
[empserver] / src / lib / subs / supply.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, 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 = lp->lnd_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 > lp->lnd_fuelc)
115                 lp->lnd_fuel = lp->lnd_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 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
365             land.lnd_item[type] =
366                 save.lnd_item[type] + s_commod(own, land.lnd_x, land.lnd_y,
367                                                type, wanted, actually_doit);
368             if (actually_doit)
369                 putland(land.lnd_uid, &land);
370             else
371                 putland(save.lnd_uid, &save);
372         }
373
374         min = get_minimum(&land, type);
375         ip = &ichr[type];
376         weight = ip->i_lbs;
377         mobcost = move_cost * weight;
378         if (mobcost > 0)
379             can_move = (double)land.lnd_mobil / mobcost;
380         else
381             can_move = land.lnd_item[type] - min;
382         if (can_move > land.lnd_item[type] - min)
383             can_move = land.lnd_item[type] - min;
384
385         if (can_move >= wanted) {
386             land.lnd_item[type] -= wanted;
387
388             /* resupply the supply unit */
389             resupply_commod(&land, type);
390
391             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
392
393             if (actually_doit)
394                 putland(land.lnd_uid, &land);
395             return total_wanted;
396         } else if (can_move > 0) {
397             gotten += can_move;
398             wanted -= can_move;
399             land.lnd_item[type] -= can_move;
400
401             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
402
403             if (actually_doit)
404                 putland(land.lnd_uid, &land);
405         }
406     }
407
408     /* We've done the best we could */
409     /* return the number gotten */
410     return gotten;
411 }
412
413
414 /*
415  * We want to get enough shells to fire once,
416  * one update's worth of food, enough fuel for
417  * one update.
418  */
419
420 static int
421 get_minimum(struct lndstr *lp, i_type type)
422 {
423     struct lchrstr *lcp;
424     int max, want = 0;
425
426     lcp = &lchr[(int)lp->lnd_type];
427     max = lcp->l_item[type];
428
429     switch (type) {
430     case I_FOOD:
431         if (opt_NOFOOD)
432             return 0;           /* no food reqd, get out */
433         want = (int)ceil(food_needed(lp->lnd_item, etu_per_update));
434         break;
435     case I_SHELL:
436         want = lp->lnd_ammo;
437         break;
438
439         /*
440          * return the amount of pet we'd need to get to 
441          * enough fuel for 1 update
442          */
443     case I_PETROL:
444         if (opt_FUEL == 0)
445             return 0;
446         want = lp->lnd_fuelu * ((float)etu_per_update * land_mob_scale)
447             / 10.0;
448         want -= lp->lnd_fuel;
449         if (want > 0) {
450             want = want / 10;
451             if (want == 0)
452                 want++;
453         }
454
455         max = want;
456         break;
457     default:
458         return 0;
459     }
460
461     if (want > max)
462         want = max;
463
464     return want;
465 }
466
467 int
468 has_supply(struct lndstr *lp)
469 {
470     int shells_needed, shells, keepshells;
471     int food, food_needed, keepfood;
472     int fuel_needed, fuel, petrol_needed, petrol, keeppetrol;
473
474     if (!opt_NOFOOD) {
475         food_needed = get_minimum(lp, I_FOOD);
476         food = keepfood = lp->lnd_item[I_FOOD];
477         if (food < food_needed) {
478             lp->lnd_item[I_FOOD] = 0;
479             putland(lp->lnd_uid, lp);
480             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
481                                       I_FOOD, (food_needed - food));
482             lp->lnd_item[I_FOOD] = keepfood;
483             putland(lp->lnd_uid, lp);
484         }
485         if (food < food_needed)
486             return 0;
487
488     }
489
490     shells_needed = lp->lnd_ammo;
491     shells = keepshells = lp->lnd_item[I_SHELL];
492     if (shells < shells_needed) {
493         lp->lnd_item[I_SHELL] = 0;
494         putland(lp->lnd_uid, lp);
495         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
496                                     I_SHELL, (shells_needed - shells));
497         lp->lnd_item[I_SHELL] = keepshells;
498         putland(lp->lnd_uid, lp);
499     }
500
501     if (shells < shells_needed)
502         return 0;
503
504     if (opt_FUEL) {
505         fuel_needed = lp->lnd_fuelu;
506         fuel = lp->lnd_fuel;
507         if (fuel < fuel_needed) {
508             petrol_needed =
509                 ldround((fuel_needed - fuel) / 10.0, 1);
510             petrol = keeppetrol = lp->lnd_item[I_PETROL];
511             if (petrol < petrol_needed) {
512                 lp->lnd_item[I_PETROL] = 0;
513                 putland(lp->lnd_uid, lp);
514                 petrol += try_supply_commod(lp->lnd_own,
515                                             lp->lnd_x, lp->lnd_y,
516                                             I_PETROL,
517                                             (petrol_needed - petrol));
518                 lp->lnd_item[I_PETROL] = keeppetrol;
519                 putland(lp->lnd_uid, lp);
520             }
521             fuel += petrol * 10;
522         }
523
524         if (fuel < fuel_needed)
525             return 0;
526     }
527     /* end opt_FUEL */
528     return 1;
529 }
530
531 int
532 use_supply(struct lndstr *lp)
533 {
534     int shells_needed, shells, food, food_needed;
535     int fuel_needed, fuel, petrol_needed, petrol;
536
537     shells_needed = lp->lnd_ammo;
538     shells = lp->lnd_item[I_SHELL];
539     if (shells < shells_needed) {
540         lp->lnd_item[I_SHELL] = 0;
541         putland(lp->lnd_uid, lp);
542         shells += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
543                                 I_SHELL, shells_needed - shells);
544         lp->lnd_item[I_SHELL] = shells;
545     }
546
547     lp->lnd_item[I_SHELL] = MAX(lp->lnd_item[I_SHELL] - shells_needed, 0);
548
549     if (lp->lnd_frg)            /* artillery */
550         goto done;
551
552     food_needed = get_minimum(lp, I_FOOD);
553     food = lp->lnd_item[I_SHELL];
554
555     if (food < food_needed) {
556         lp->lnd_item[I_FOOD] = 0;
557         putland(lp->lnd_uid, lp);
558         food += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
559                               I_FOOD, food_needed - food);
560         lp->lnd_item[I_FOOD] = food;
561     }
562
563     lp->lnd_item[I_FOOD] = MAX(lp->lnd_item[I_FOOD] - food_needed, 0);
564
565     if (opt_FUEL) {
566         fuel_needed = lp->lnd_fuelu;
567         fuel = lp->lnd_fuel;
568
569         petrol = petrol_needed = 0;
570
571         if (fuel < fuel_needed) {
572             petrol_needed =
573                 ldround((fuel_needed - fuel) / 10.0, 1);
574             petrol = lp->lnd_item[I_PETROL];
575         }
576
577         if (petrol < petrol_needed) {
578             lp->lnd_item[I_PETROL] = 0;
579             putland(lp->lnd_uid, lp);
580             petrol += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
581                                     I_PETROL, petrol_needed - petrol);
582             lp->lnd_item[I_PETROL] = petrol;
583         }
584
585         if (petrol_needed) {
586             if (petrol >= petrol_needed) {
587                 lp->lnd_item[I_PETROL]
588                     = MAX(lp->lnd_item[I_PETROL] - petrol_needed, 0);
589                 lp->lnd_fuel += petrol_needed * 10;
590             } else {
591                 lp->lnd_fuel += lp->lnd_item[I_PETROL] * 10;
592                 lp->lnd_item[I_PETROL] = 0;
593             }
594         }
595
596         lp->lnd_fuel = MAX(lp->lnd_fuel - fuel_needed, 0);
597     }
598     /* end opt_FUEL */
599   done:
600     putland(lp->lnd_uid, lp);
601     return 1;
602 }