]> git.pond.sub.org Git - empserver/blob - src/lib/subs/supply.c
(s_commod): Clarify and comment the trickery required to make the
[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 save;
394
395             /*
396              * Temporarily zap this unit's store, so the recursion
397              * avoids it.
398              */
399             save = land;
400             hold = vec[type];
401             vec[type] = 0;
402             putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
403             land.lnd_fuel = 0;
404             putland(land.lnd_uid, &land);
405
406             hold += s_commod(own, land.lnd_x, land.lnd_y, type, wanted,
407                              actually_doit);
408
409             vec[type] = hold;
410             if (actually_doit) {
411                 putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
412                 putland(land.lnd_uid, &land);
413             } else
414                 putland(save.lnd_uid, &save);
415         }
416
417         getvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
418         min = get_minimum(&land, type);
419         ip = &ichr[type];
420         weight = ((double)ip->i_lbs);
421         mobcost = move_cost * weight;
422         if (mobcost > 0)
423             can_move = ((double)land.lnd_mobil / mobcost);
424         else
425             can_move = vec[type] - min;
426         if (can_move > (vec[type] - min))
427             can_move = (vec[type] - min);
428
429         if (can_move >= wanted) {
430             vec[type] -= wanted;
431
432             /* resupply the supply unit */
433             resupply_commod(&land, type);
434
435             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
436
437             if (actually_doit) {
438                 putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
439                 putland(land.lnd_uid, &land);
440             }
441             return total_wanted;
442         } else if (can_move > 0) {
443             gotten += can_move;
444             wanted -= can_move;
445             vec[type] -= can_move;
446
447             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
448
449             if (actually_doit) {
450                 putvec(VT_ITEM, vec, (s_char *)&land, EF_LAND);
451                 putland(land.lnd_uid, &land);
452             }
453         }
454     }
455
456     /* We've done the best we could */
457     /* return the number gotten */
458     return gotten;
459 }
460
461
462 /*
463  * We want to get enough shells to fire once,
464  * one update's worth of food, enough fuel for
465  * one update.
466  */
467
468 static int
469 get_minimum(struct lndstr *lp, int type)
470 {
471     struct lchrstr *lcp;
472     int max, want = 0;
473
474     lcp = &lchr[(int)lp->lnd_type];
475     max = vl_find(V_ITEM(type), lcp->l_vtype, lcp->l_vamt, (int)lcp->l_nv);
476
477     switch (type) {
478     case I_FOOD:
479         if (opt_NOFOOD)
480             return 0;           /* no food reqd, get out */
481         want = (((double)etu_per_update * eatrate) *
482                 (double)total_mil(lp)) + 1;
483         break;
484     case I_SHELL:
485         want = lp->lnd_ammo;
486         break;
487
488         /*
489          * return the amount of pet we'd need to get to 
490          * enough fuel for 1 update
491          *
492          */
493     case I_PETROL:
494         if (opt_FUEL == 0)
495             return 0;
496         want = (lp->lnd_fuelu * (((float)etu_per_update *
497                                   land_mob_scale)) / 10.0);
498         want -= lp->lnd_fuel;
499         if (want > 0) {
500             double d;
501             d = (double)want / 10.0;
502             want = (int)d;
503             if (want == 0)
504                 want++;
505         }
506
507         max = want;
508         break;
509     default:
510         return 0;
511     }
512
513     if (want > max)
514         want = max;
515
516     return want;
517 }
518
519 int
520 has_supply(struct lndstr *lp)
521 {
522     struct lchrstr *lcp;
523     int vec[I_MAX + 1], shells_needed, shells, keepshells;
524     int food, food_needed, keepfood;
525     int fuel_needed, fuel, petrol_needed, petrol, keeppetrol;
526
527     lcp = &lchr[(int)lp->lnd_type];
528     getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
529
530     if (!opt_NOFOOD) {
531         food_needed = get_minimum(lp, I_FOOD);
532         food = keepfood = vec[I_FOOD];
533         if (food < food_needed) {
534             vec[I_FOOD] = 0;
535             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
536             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
537                                       I_FOOD, (food_needed - food));
538             vec[I_FOOD] = keepfood;
539             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
540         }
541         if (food < food_needed)
542             return 0;
543
544     }
545
546     shells_needed = lp->lnd_ammo;
547     shells = keepshells = vec[I_SHELL];
548     if (shells < shells_needed) {
549         vec[I_SHELL] = 0;
550         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
551         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
552                                     I_SHELL, (shells_needed - shells));
553         vec[I_SHELL] = keepshells;
554         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
555     }
556
557     if (shells < shells_needed)
558         return 0;
559
560     if (opt_FUEL) {
561         fuel_needed = lp->lnd_fuelu;
562
563         fuel = lp->lnd_fuel;
564
565         petrol = petrol_needed = 0;
566
567         if (fuel < fuel_needed) {
568             petrol_needed =
569                 ldround(((double)(fuel_needed - fuel) / 10.0), 1);
570             petrol = keeppetrol = vec[I_PETROL];
571         }
572
573         if (petrol < petrol_needed) {
574             vec[I_PETROL] = 0;
575             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
576             petrol += try_supply_commod(lp->lnd_own,
577                                         lp->lnd_x, lp->lnd_y,
578                                         I_PETROL,
579                                         (petrol_needed - petrol));
580             vec[I_PETROL] = keeppetrol;
581             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
582             fuel += petrol * 10;
583         }
584
585         if (fuel < fuel_needed)
586             return 0;
587     }
588     /* end opt_FUEL */
589     return 1;
590 }
591
592 int
593 use_supply(struct lndstr *lp)
594 {
595     struct lchrstr *lcp;
596     int vec[I_MAX + 1], shells_needed, shells, food, food_needed;
597     int fuel_needed, fuel, petrol_needed, petrol;
598
599     lcp = &lchr[(int)lp->lnd_type];
600     getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
601
602     shells_needed = lp->lnd_ammo;
603     shells = vec[I_SHELL];
604     if (shells < shells_needed) {
605         vec[I_SHELL] = 0;
606         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
607         shells += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y, I_SHELL,
608                                 (shells_needed - shells));
609         vec[I_SHELL] = shells;
610     }
611
612     vec[I_SHELL] = max(vec[I_SHELL] - shells_needed, 0);
613
614     if (lp->lnd_frg)            /* artillery */
615         goto artillery;
616
617     food_needed = get_minimum(lp, I_FOOD);
618     food = vec[I_SHELL];
619
620     if (food < food_needed) {
621         vec[I_FOOD] = 0;
622         putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
623         food += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y, I_FOOD,
624                               (food_needed - food));
625         vec[I_FOOD] = food;
626     }
627
628     vec[I_FOOD] = max(vec[I_FOOD] - food_needed, 0);
629
630     if (opt_FUEL) {
631         fuel_needed = lp->lnd_fuelu;
632         fuel = lp->lnd_fuel;
633
634         petrol = petrol_needed = 0;
635
636         if (fuel < fuel_needed) {
637             petrol_needed =
638                 ldround(((double)(fuel_needed - fuel) / 10.0), 1);
639             petrol = vec[I_PETROL];
640         }
641
642         if (petrol < petrol_needed) {
643             vec[I_PETROL] = 0;
644             putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
645             petrol += supply_commod(lp->lnd_own,
646                                     lp->lnd_x, lp->lnd_y,
647                                     I_PETROL, (petrol_needed - petrol));
648             vec[I_PETROL] = petrol;
649         }
650
651         if (petrol_needed) {
652             if (petrol >= petrol_needed) {
653                 vec[I_PETROL] = max(vec[I_PETROL] - petrol_needed, 0);
654                 lp->lnd_fuel += petrol_needed * 10;
655             } else {
656                 lp->lnd_fuel += vec[I_PETROL] * 10;
657                 vec[I_PETROL] = 0;
658             }
659         }
660
661         lp->lnd_fuel = max(lp->lnd_fuel - fuel_needed, 0);
662     }
663     /* end opt_FUEL */
664   artillery:
665     putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
666     putland(lp->lnd_uid, lp);
667     return 1;
668 }