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