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