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