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