]> git.pond.sub.org Git - empserver/blob - src/lib/subs/supply.c
514c7eb2d84f13af4ce6f81f71feffa9c889c1e3
[empserver] / src / lib / subs / supply.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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 vec[I_MAX + 1];
90     int svec[I_MAX + 1];
91     int amt;
92     struct lchrstr *lcp;
93     struct shpstr ship;
94
95     lcp = &lchr[(int)lp->lnd_type];
96
97     getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
98     /* Ok, do we now have enough? */
99     if (vec[type] < get_minimum(lp, type)) {
100         vec[type] += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y, type,
101                                    get_minimum(lp, type) - vec[type]);
102         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
103     }
104     /* Now, check again to see if we have enough. */
105     if (vec[type] < get_minimum(lp, type)) {
106         /* Nope.  How much do we need? */
107         amt = (get_minimum(lp, type) - vec[type]);
108         /* Are we on a ship?  if so, try to get it from the ship first. */
109         if (lp->lnd_ship >= 0) {
110             getship(lp->lnd_ship, &ship);
111             getvec(VT_ITEM, svec, (s_char *)&ship, EF_SHIP);
112             /* Now, determine how much we can get */
113             amt = (amt < svec[type]) ? amt : svec[type];
114             /* Now, add and subtract */
115             vec[type] += amt;
116             svec[type] -= amt;
117             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
118             putvec(VT_ITEM, svec, (s_char *)&ship, EF_SHIP);
119             putship(lp->lnd_ship, &ship);
120         }
121     }
122
123     if (opt_FUEL && type == I_PETROL) {
124         int fuel_needed = (lp->lnd_fuelu * (((float)etu_per_update
125                                              * land_mob_scale)) / 10.0);
126
127         while ((lp->lnd_fuel < fuel_needed) && vec[I_PETROL]) {
128             lp->lnd_fuel += 10;
129             if (lp->lnd_fuel > lp->lnd_fuelc)
130                 lp->lnd_fuel = lp->lnd_fuelc;
131             vec[I_PETROL]--;
132             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
133         }
134     }
135 }
136
137 /*
138  * Actually get the commod
139  */
140 int
141 supply_commod(int own, int x, int y, int type, int total_wanted)
142 {
143     if (total_wanted < 0)
144         return 0;
145     return s_commod(own, x, y, type, total_wanted, !player->simulation);
146 }
147
148 /*
149  * Just return the number you COULD get, without doing it
150  */
151 int
152 try_supply_commod(int own, int x, int y, int type, int total_wanted)
153 {
154     if (total_wanted < 0)
155         return 0;
156
157     return s_commod(own, x, y, type, total_wanted, 0);
158 }
159
160 /* Get supplies of a certain type */
161 static int
162 s_commod(int own, int x, int y, int type, int total_wanted,
163          int actually_doit)
164 {
165     int wanted = total_wanted;
166     int gotten = 0, lookrange;
167     struct sctstr sect, dest;
168     int vec[I_MAX + 1];
169     struct nstr_sect ns;
170     struct nstr_item ni;
171     struct lchrstr *lcp;
172     struct shpstr ship;
173     struct lndstr land;
174     /* leave at least 1 military in sectors/ships */
175     int minimum = (type == I_MILIT ? 1 : 0);
176     int can_move;
177     double move_cost, weight, mobcost;
178     int packing;
179     struct dchrstr *dp;
180     struct ichrstr *ip;
181     s_char buf[1024];
182
183     /* try to get it from sector we're in */
184     getsect(x, y, &dest);
185     getsect(x, y, &sect);
186     if (sect.sct_own == own) {
187         getvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
188         if ((vec[type] - minimum) >= wanted) {
189             vec[type] -= wanted;
190             if (actually_doit) {
191                 putvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
192                 putsect(&sect);
193             }
194             return total_wanted;
195         } else if ((vec[type] - minimum) > 0) {
196             gotten += (vec[type] - minimum);
197             wanted -= (vec[type] - minimum);
198             vec[type] = minimum;
199             if (actually_doit) {
200                 putvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
201                 putsect(&sect);
202             }
203         }
204     }
205     /* look for a headquarters or warehouse */
206     lookrange = tfact(own, (double)10.0);
207     snxtsct_dist(&ns, x, y, lookrange);
208     while (nxtsct(&ns, &sect) && wanted) {
209         if (sect.sct_own != own)
210             continue;
211         if ((sect.sct_type != SCT_WAREH) &&
212             (sect.sct_type != SCT_HEADQ) && (sect.sct_type != SCT_HARBR))
213             continue;
214         if ((sect.sct_type == SCT_HEADQ) &&
215             (sect.sct_dist_x == sect.sct_x) &&
216             (sect.sct_dist_y == sect.sct_y))
217             continue;
218         if (sect.sct_effic < 60)
219             continue;
220         if (BestLandPath(buf, &dest, &sect, &move_cost, MOB_ROAD) ==
221             (s_char *)0)
222             continue;
223         getvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
224         if (!opt_NOFOOD && type == I_FOOD) {
225             minimum = (((double)etu_per_update * eatrate) *
226                        (double)(vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW]))
227                 + 2;
228         }
229         if (vec[type] <= minimum) {
230             /* Don't bother... */
231             continue;
232         }
233         ip = &ichr[type];
234         dp = &dchr[sect.sct_type];
235         packing = ip->i_pkg[dp->d_pkg];
236         if (packing > 1 && sect.sct_effic < 60)
237             packing = 1;
238         weight = ((double)ip->i_lbs / (double)packing);
239         mobcost = move_cost * weight;
240         if (mobcost > 0)
241             can_move = ((double)sect.sct_mobil / mobcost);
242         else
243             can_move = vec[type] - minimum;
244         if (can_move > (vec[type] - minimum))
245             can_move = (vec[type] - minimum);
246
247         if (can_move >= wanted) {
248             int n;
249
250             vec[type] -= wanted;
251
252             /* take off mobility for delivering sect */
253             n = roundavg(total_wanted * weight * move_cost);
254             if (n < 0)
255                 n = 0;
256             if (n > sect.sct_mobil)
257                 n = sect.sct_mobil;
258             sect.sct_mobil -= (u_char)n;
259
260             if (actually_doit) {
261                 putvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
262                 putsect(&sect);
263             }
264
265             return total_wanted;
266         } else if (can_move > 0) {
267             int n;
268             gotten += can_move;
269             wanted -= can_move;
270             vec[type] -= can_move;
271
272             /* take off mobility for delivering sect */
273             n = roundavg(can_move * weight * move_cost);
274             if (n < 0)
275                 n = 0;
276             if (n > sect.sct_mobil)
277                 n = sect.sct_mobil;
278             sect.sct_mobil -= (u_char)n;
279
280             if (actually_doit) {
281                 putvec(VT_ITEM, vec, (s_char *)&sect, EF_SECTOR);
282                 putsect(&sect);
283             }
284         }
285     }
286
287     /* look for an owned ship in a harbor */
288     snxtitem_dist(&ni, EF_SHIP, x, y, lookrange);
289
290     while (nxtitem(&ni, (s_char *)&ship) && wanted) {
291         if (ship.shp_own != own)
292             continue;
293
294         if (!(mchr[(int)ship.shp_type].m_flags & M_SUPPLY))
295             continue;
296         getsect(ship.shp_x, ship.shp_y, &sect);
297         if (sect.sct_type != SCT_HARBR)
298             continue;
299         if (sect.sct_effic < 2)
300             continue;
301         if (BestLandPath(buf, &dest, &sect, &move_cost, MOB_ROAD) ==
302             (s_char *)0)
303             continue;
304         getvec(VT_ITEM, vec, (s_char *)&ship, EF_SHIP);
305         if (!opt_NOFOOD && type == I_FOOD)
306             minimum = (((double)etu_per_update * eatrate) *
307                        (double)(vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW]))
308                 + 2;
309         if (vec[type] <= minimum) {
310             /* Don't bother... */
311             continue;
312         }
313         ip = &ichr[type];
314         dp = &dchr[sect.sct_type];
315         packing = ip->i_pkg[dp->d_pkg];
316         if (packing > 1 && sect.sct_effic < 60)
317             packing = 1;
318         weight = ((double)ip->i_lbs / (double)packing);
319         mobcost = move_cost * weight;
320         if (mobcost > 0)
321             can_move = ((double)sect.sct_mobil / mobcost);
322         else
323             can_move = vec[type] - minimum;
324         if (can_move > (vec[type] - minimum))
325             can_move = (vec[type] - minimum);
326         if (can_move >= wanted) {
327             int n;
328             vec[type] -= wanted;
329
330             n = roundavg(wanted * weight * move_cost);
331             if (n < 0)
332                 n = 0;
333             if (n > sect.sct_mobil)
334                 n = sect.sct_mobil;
335             sect.sct_mobil -= (u_char)n;
336             if (actually_doit) {
337                 putvec(VT_ITEM, vec, (s_char *)&ship, EF_SHIP);
338                 putship(ship.shp_uid, &ship);
339                 putsect(&sect);
340             }
341             return total_wanted;
342         } else if (can_move > 0) {
343             int n;
344             gotten += can_move;
345             wanted -= can_move;
346             vec[type] -= can_move;
347
348             n = roundavg(can_move * weight * move_cost);
349             if (n < 0)
350                 n = 0;
351             if (n > sect.sct_mobil)
352                 n = sect.sct_mobil;
353             sect.sct_mobil -= (u_char)n;
354
355             if (actually_doit) {
356                 putvec(VT_ITEM, vec, (s_char *)&ship, EF_SHIP);
357                 putship(ship.shp_uid, &ship);
358                 putsect(&sect);
359             }
360         }
361     }
362
363     /* look for an owned supply unit */
364     snxtitem_dist(&ni, EF_LAND, x, y, lookrange);
365
366     while (nxtitem(&ni, (s_char *)&land) && wanted) {
367         int min;
368
369         if (land.lnd_own != own)
370             continue;
371
372         lcp = &lchr[(int)land.lnd_type];
373         if (!(lcp->l_flags & L_SUPPLY))
374             continue;
375
376         getvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
377         if (vec[type] <= get_minimum(&land, type))
378             continue;
379
380         getsect(land.lnd_x, land.lnd_y, &sect);
381         if (BestLandPath(buf, &dest, &sect, &move_cost, MOB_ROAD) ==
382             (s_char *)0)
383             continue;
384
385         if ((land.lnd_ship >= 0) && (sect.sct_type != SCT_HARBR))
386             continue;
387
388         if ((land.lnd_ship >= 0) && (sect.sct_effic < 2))
389             continue;
390
391         if ((vec[type] - wanted) < get_minimum(&land, type)) {
392             int hold;
393             struct lndstr l2;
394
395             l2 = land;
396             hold = vec[type];
397             vec[type] = 0;
398             putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
399             land.lnd_fuel = 0;
400             putland(land.lnd_uid, &land);
401             hold +=
402                 s_commod(own, land.lnd_x, land.lnd_y, type, wanted,
403                          actually_doit);
404             vec[type] = hold;
405             putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
406             putland(land.lnd_uid, &land);
407             if (!actually_doit)
408                 putland(l2.lnd_uid, &l2);
409         }
410
411         getvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
412         min = get_minimum(&land, type);
413         ip = &ichr[type];
414         weight = ((double)ip->i_lbs);
415         mobcost = move_cost * weight;
416         if (mobcost > 0)
417             can_move = ((double)land.lnd_mobil / mobcost);
418         else
419             can_move = vec[type] - min;
420         if (can_move > (vec[type] - min))
421             can_move = (vec[type] - min);
422
423         if (can_move >= wanted) {
424             vec[type] -= wanted;
425
426             /* resupply the supply unit */
427             resupply_commod(&land, type);
428
429             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
430
431             if (actually_doit) {
432                 putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
433                 putland(land.lnd_uid, &land);
434             }
435             return total_wanted;
436         } else if (can_move > 0) {
437             gotten += can_move;
438             wanted -= can_move;
439             vec[type] -= can_move;
440
441             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
442
443             if (actually_doit) {
444                 putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
445                 putland(land.lnd_uid, &land);
446             }
447         }
448     }
449
450     /* We've done the best we could */
451     /* return the number gotten */
452     return gotten;
453 }
454
455
456 /*
457  * We want to get enough shells to fire once,
458  * one update's worth of food, enough fuel for
459  * one update.
460  */
461
462 static int
463 get_minimum(struct lndstr *lp, int type)
464 {
465     struct lchrstr *lcp;
466     int max, want = 0;
467
468     lcp = &lchr[(int)lp->lnd_type];
469     max = vl_find(V_ITEM(type), lcp->l_vtype, lcp->l_vamt, (int)lcp->l_nv);
470
471     switch (type) {
472     case I_FOOD:
473         if (opt_NOFOOD)
474             return 0;           /* no food reqd, get out */
475         want = (((double)etu_per_update * eatrate) *
476                 (double)total_mil(lp)) + 1;
477         break;
478     case I_SHELL:
479         want = lp->lnd_ammo;
480         break;
481
482         /*
483          * return the amount of pet we'd need to get to 
484          * enough fuel for 1 update
485          *
486          */
487     case I_PETROL:
488         if (opt_FUEL == 0)
489             return 0;
490         want = (lp->lnd_fuelu * (((float)etu_per_update *
491                                   land_mob_scale)) / 10.0);
492         want -= lp->lnd_fuel;
493         if (want > 0) {
494             double d;
495             d = (double)want / 10.0;
496             want = (int)d;
497             if (want == 0)
498                 want++;
499         }
500
501         max = want;
502         break;
503     default:
504         return 0;
505     }
506
507     if (want > max)
508         want = max;
509
510     return want;
511 }
512
513 int
514 has_supply(struct lndstr *lp)
515 {
516     struct lchrstr *lcp;
517     int vec[I_MAX + 1], shells_needed, shells, keepshells;
518     int food, food_needed, keepfood;
519     int fuel_needed, fuel, petrol_needed, petrol, keeppetrol;
520
521     lcp = &lchr[(int)lp->lnd_type];
522     getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
523
524     if (!opt_NOFOOD) {
525         food_needed = get_minimum(lp, I_FOOD);
526         food = keepfood = vec[I_FOOD];
527         if (food < food_needed) {
528             vec[I_FOOD] = 0;
529             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
530             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
531                                       I_FOOD, (food_needed - food));
532             vec[I_FOOD] = keepfood;
533             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
534         }
535         if (food < food_needed)
536             return 0;
537
538     }
539
540     shells_needed = lp->lnd_ammo;
541     shells = keepshells = vec[I_SHELL];
542     if (shells < shells_needed) {
543         vec[I_SHELL] = 0;
544         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
545         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
546                                     I_SHELL, (shells_needed - shells));
547         vec[I_SHELL] = keepshells;
548         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
549     }
550
551     if (shells < shells_needed)
552         return 0;
553
554     if (opt_FUEL) {
555         fuel_needed = lp->lnd_fuelu;
556
557         fuel = lp->lnd_fuel;
558
559         petrol = petrol_needed = 0;
560
561         if (fuel < fuel_needed) {
562             petrol_needed =
563                 ldround(((double)(fuel_needed - fuel) / 10.0), 1);
564             petrol = keeppetrol = vec[I_PETROL];
565         }
566
567         if (petrol < petrol_needed) {
568             vec[I_PETROL] = 0;
569             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
570             petrol += try_supply_commod(lp->lnd_own,
571                                         lp->lnd_x, lp->lnd_y,
572                                         I_PETROL,
573                                         (petrol_needed - petrol));
574             vec[I_PETROL] = keeppetrol;
575             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
576             fuel += petrol * 10;
577         }
578
579         if (fuel < fuel_needed)
580             return 0;
581     }
582     /* end opt_FUEL */
583     return 1;
584 }
585
586 int
587 use_supply(struct lndstr *lp)
588 {
589     struct lchrstr *lcp;
590     int vec[I_MAX + 1], shells_needed, shells, food, food_needed;
591     int fuel_needed, fuel, petrol_needed, petrol;
592
593     lcp = &lchr[(int)lp->lnd_type];
594     getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
595
596     shells_needed = lp->lnd_ammo;
597     shells = vec[I_SHELL];
598     if (shells < shells_needed) {
599         vec[I_SHELL] = 0;
600         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
601         shells += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y, I_SHELL,
602                                 (shells_needed - shells));
603         vec[I_SHELL] = shells;
604     }
605
606     vec[I_SHELL] = max(vec[I_SHELL] - shells_needed, 0);
607
608     if (lp->lnd_frg)            /* artillery */
609         goto artillery;
610
611     food_needed = get_minimum(lp, I_FOOD);
612     food = vec[I_SHELL];
613
614     if (food < food_needed) {
615         vec[I_FOOD] = 0;
616         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
617         food += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y, I_FOOD,
618                               (food_needed - food));
619         vec[I_FOOD] = food;
620     }
621
622     vec[I_FOOD] = max(vec[I_FOOD] - food_needed, 0);
623
624     if (opt_FUEL) {
625         fuel_needed = lp->lnd_fuelu;
626         fuel = lp->lnd_fuel;
627
628         petrol = petrol_needed = 0;
629
630         if (fuel < fuel_needed) {
631             petrol_needed =
632                 ldround(((double)(fuel_needed - fuel) / 10.0), 1);
633             petrol = vec[I_PETROL];
634         }
635
636         if (petrol < petrol_needed) {
637             vec[I_PETROL] = 0;
638             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
639             petrol += supply_commod(lp->lnd_own,
640                                     lp->lnd_x, lp->lnd_y,
641                                     I_PETROL, (petrol_needed - petrol));
642             vec[I_PETROL] = petrol;
643         }
644
645         if (petrol_needed) {
646             if (petrol >= petrol_needed) {
647                 vec[I_PETROL] = max(vec[I_PETROL] - petrol_needed, 0);
648                 lp->lnd_fuel += petrol_needed * 10;
649             } else {
650                 lp->lnd_fuel += vec[I_PETROL] * 10;
651                 vec[I_PETROL] = 0;
652             }
653         }
654
655         lp->lnd_fuel = max(lp->lnd_fuel - fuel_needed, 0);
656     }
657     /* end opt_FUEL */
658   artillery:
659     putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
660     putland(lp->lnd_uid, lp);
661     return 1;
662 }