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