]> git.pond.sub.org Git - empserver/blob - src/lib/subs/supply.c
Don't let automatic supply starve the sector containing the sink
[empserver] / src / lib / subs / supply.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, 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 <math.h>
37 #include "file.h"
38 #include "land.h"
39 #include "nat.h"
40 #include "optlist.h"
41 #include "player.h"
42 #include "prototypes.h"
43 #include "sect.h"
44 #include "ship.h"
45
46 static int get_minimum(struct lndstr *, i_type);
47 static int s_commod(int, int, int, i_type, int, int);
48
49 /*
50  * We want to get enough guns to be maxed out, enough shells to
51  *      fire once, one update's worth of food.
52  *
53  * Firts, try to forage in the sector
54  * Second look for a warehouse or headquarters to leech
55  * Third, look for a ship we own in a harbor
56  * Fourth, look for supplies in a supply unit we own
57  *              (one good reason to do this last is that the supply
58  *               unit will then call resupply, taking more time)
59  *
60  * May want to put code to resupply with SAMs here, later --ts
61  */
62
63 void
64 resupply_all(struct lndstr *lp)
65 {
66     if (!opt_NOFOOD)
67         resupply_commod(lp, I_FOOD);
68     resupply_commod(lp, I_SHELL);
69 }
70
71 /*
72  * If the unit has less than it's minimum level of a
73  * certain commodity, fill it, to the best of our abilities.
74  */
75
76 void
77 resupply_commod(struct lndstr *lp, i_type type)
78 {
79     int amt;
80
81     amt = get_minimum(lp, type) - lp->lnd_item[type];
82     if (amt > 0) {
83         lp->lnd_item[type] += supply_commod(lp->lnd_own,
84                                             lp->lnd_x, lp->lnd_y,
85                                             type, amt);
86     }
87 }
88
89 int
90 lnd_in_supply(struct lndstr *lp)
91 {
92     if (!opt_NOFOOD) {
93         if (lp->lnd_item[I_FOOD] < get_minimum(lp, I_FOOD))
94             return 0;
95     }
96     return lp->lnd_item[I_SHELL] >= get_minimum(lp, I_SHELL);
97 }
98
99 /*
100  * Actually get the commod
101  */
102 int
103 supply_commod(int own, int x, int y, i_type type, int total_wanted)
104 {
105     if (total_wanted <= 0)
106         return 0;
107     return s_commod(own, x, y, type, total_wanted, !player->simulation);
108 }
109
110 /*
111  * Just return the number you COULD get, without doing it
112  */
113 static int
114 try_supply_commod(int own, int x, int y, i_type type, int total_wanted)
115 {
116     if (total_wanted <= 0)
117         return 0;
118
119     return s_commod(own, x, y, type, total_wanted, 0);
120 }
121
122 /* Get supplies of a certain type */
123 static int
124 s_commod(int own, int x, int y, i_type type, int total_wanted,
125          int actually_doit)
126 {
127     int wanted = total_wanted;
128     int gotten = 0, lookrange;
129     struct sctstr sect, dest;
130     struct nstr_sect ns;
131     struct nstr_item ni;
132     struct lchrstr *lcp;
133     struct shpstr ship;
134     struct lndstr land;
135     /* leave at least 1 military in sectors/ships */
136     int minimum = 0;
137     int can_move;
138     double move_cost, weight, mobcost;
139     int packing;
140     struct dchrstr *dp;
141     struct ichrstr *ip;
142     char buf[1024];
143
144     /* try to get it from sector we're in */
145     getsect(x, y, &dest);
146     getsect(x, y, &sect);
147     if (sect.sct_own == own) {
148         if (!opt_NOFOOD && type == I_FOOD)
149             minimum = 1 + (int)ceil(food_needed(sect.sct_item,
150                                                 etu_per_update));
151         if (sect.sct_item[type] - wanted >= minimum) {
152             sect.sct_item[type] -= wanted;
153             if (actually_doit)
154                 putsect(&sect);
155             return total_wanted;
156         } else if (sect.sct_item[type] - minimum > 0) {
157             gotten += sect.sct_item[type] - minimum;
158             wanted -= sect.sct_item[type] - minimum;
159             sect.sct_item[type] = minimum;
160             if (actually_doit)
161                 putsect(&sect);
162         }
163     }
164     /* look for a headquarters or warehouse */
165     lookrange = tfact(own, 10.0);
166     snxtsct_dist(&ns, x, y, lookrange);
167     while (nxtsct(&ns, &sect) && wanted) {
168         if (sect.sct_own != own)
169             continue;
170         if ((sect.sct_type != SCT_WAREH) &&
171             (sect.sct_type != SCT_HEADQ) && (sect.sct_type != SCT_HARBR))
172             continue;
173         if ((sect.sct_type == SCT_HEADQ) &&
174             (sect.sct_dist_x == sect.sct_x) &&
175             (sect.sct_dist_y == sect.sct_y))
176             continue;
177         if (sect.sct_effic < 60)
178             continue;
179         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
180             continue;
181         if (!opt_NOFOOD && type == I_FOOD)
182             minimum = 1 + (int)ceil(food_needed(sect.sct_item,
183                                                 etu_per_update));
184         if (sect.sct_item[type] <= minimum) {
185             /* Don't bother... */
186             continue;
187         }
188         ip = &ichr[type];
189         dp = &dchr[sect.sct_type];
190         packing = ip->i_pkg[dp->d_pkg];
191         if (packing > 1 && sect.sct_effic < 60)
192             packing = 1;
193         weight = (double)ip->i_lbs / packing;
194         mobcost = move_cost * weight;
195         if (mobcost > 0)
196             can_move = (double)sect.sct_mobil / mobcost;
197         else
198             can_move = sect.sct_item[type] - minimum;
199         if (can_move > sect.sct_item[type] - minimum)
200             can_move = sect.sct_item[type] - minimum;
201
202         if (can_move >= wanted) {
203             int n;
204
205             sect.sct_item[type] -= wanted;
206
207             /* take off mobility for delivering sect */
208             n = roundavg(wanted * weight * move_cost);
209             if (n < 0)
210                 n = 0;
211             if (n > sect.sct_mobil)
212                 n = sect.sct_mobil;
213             sect.sct_mobil -= n;
214
215             if (actually_doit)
216                 putsect(&sect);
217
218             return total_wanted;
219         } else if (can_move > 0) {
220             int n;
221             gotten += can_move;
222             wanted -= can_move;
223             sect.sct_item[type] -= can_move;
224
225             /* take off mobility for delivering sect */
226             n = roundavg(can_move * weight * move_cost);
227             if (n < 0)
228                 n = 0;
229             if (n > sect.sct_mobil)
230                 n = sect.sct_mobil;
231             sect.sct_mobil -= n;
232
233             if (actually_doit)
234                 putsect(&sect);
235         }
236     }
237
238     /* look for an owned ship in a harbor */
239     snxtitem_dist(&ni, EF_SHIP, x, y, lookrange);
240
241     while (nxtitem(&ni, &ship) && wanted) {
242         if (ship.shp_own != own)
243             continue;
244
245         if (!(mchr[(int)ship.shp_type].m_flags & M_SUPPLY))
246             continue;
247         getsect(ship.shp_x, ship.shp_y, &sect);
248         if (sect.sct_type != SCT_HARBR)
249             continue;
250         if (sect.sct_effic < 2)
251             continue;
252         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
253             continue;
254         if (!opt_NOFOOD && type == I_FOOD)
255             minimum = 1 + (int)ceil(food_needed(ship.shp_item,
256                                                 etu_per_update));
257         if (ship.shp_item[type] <= minimum) {
258             /* Don't bother... */
259             continue;
260         }
261         ip = &ichr[type];
262         dp = &dchr[sect.sct_type];
263         packing = ip->i_pkg[dp->d_pkg];
264         if (packing > 1 && sect.sct_effic < 60)
265             packing = 1;
266         weight = (double)ip->i_lbs / packing;
267         mobcost = move_cost * weight;
268         if (mobcost > 0)
269             can_move = (double)sect.sct_mobil / mobcost;
270         else
271             can_move = ship.shp_item[type] - minimum;
272         if (can_move > ship.shp_item[type] - minimum)
273             can_move = ship.shp_item[type] - minimum;
274         if (can_move >= wanted) {
275             int n;
276             ship.shp_item[type] -= wanted;
277
278             n = roundavg(wanted * weight * move_cost);
279             if (n < 0)
280                 n = 0;
281             if (n > sect.sct_mobil)
282                 n = sect.sct_mobil;
283             sect.sct_mobil -= n;
284             if (actually_doit) {
285                 putship(ship.shp_uid, &ship);
286                 putsect(&sect);
287             }
288             return total_wanted;
289         } else if (can_move > 0) {
290             int n;
291             gotten += can_move;
292             wanted -= can_move;
293             ship.shp_item[type] -= can_move;
294
295             n = roundavg(can_move * weight * move_cost);
296             if (n < 0)
297                 n = 0;
298             if (n > sect.sct_mobil)
299                 n = sect.sct_mobil;
300             sect.sct_mobil -= n;
301
302             if (actually_doit) {
303                 putship(ship.shp_uid, &ship);
304                 putsect(&sect);
305             }
306         }
307     }
308
309     /* look for an owned supply unit */
310     snxtitem_dist(&ni, EF_LAND, x, y, lookrange);
311
312     while (nxtitem(&ni, &land) && wanted) {
313         int min;
314
315         if (land.lnd_own != own)
316             continue;
317
318         lcp = &lchr[(int)land.lnd_type];
319         if (!(lcp->l_flags & L_SUPPLY))
320             continue;
321
322         if (land.lnd_item[type] <= get_minimum(&land, type))
323             continue;
324
325         getsect(land.lnd_x, land.lnd_y, &sect);
326         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
327             continue;
328
329         if ((land.lnd_ship >= 0) && (sect.sct_type != SCT_HARBR))
330             continue;
331
332         if ((land.lnd_ship >= 0) && (sect.sct_effic < 2))
333             continue;
334
335 #if 0
336         /*
337          * Recursive supply is disabled for now.  It can introduce
338          * cycles into the "resupplies from" relation.  The code below
339          * attempts to break these cycles by temporarily zapping the
340          * commodity being supplied.  That puts the land file in a
341          * funny state temporarily, risking loss of supplies when
342          * something goes wrong on the way.  Worse, it increases
343          * lnd_seqno even when !actually_doit, which can lead to
344          * spurious seqno mismatch oopses in users of
345          * lnd_could_be_supplied().  I can't be bothered to clean up
346          * this mess right now, because recursive resupply is too dumb
347          * to be really useful anyway: each step uses the first source
348          * it finds, without consideration of mobility cost.  If you
349          * re-enable it, don't forget to uncomment its documentation
350          * in supply.t as well.
351          */
352         if (land.lnd_item[type] - wanted < get_minimum(&land, type)) {
353             struct lndstr save;
354
355             /*
356              * Temporarily zap this unit's store, so the recursion
357              * avoids it.
358              */
359             save = land;
360             land.lnd_item[type] = 0;
361             putland(land.lnd_uid, &land);
362             save.lnd_seqno = land.lnd_seqno;
363
364             land.lnd_item[type] =
365                 save.lnd_item[type] + s_commod(own, land.lnd_x, land.lnd_y,
366                                                type, wanted, actually_doit);
367             if (actually_doit)
368                 putland(land.lnd_uid, &land);
369             else
370                 putland(save.lnd_uid, &save);
371         }
372 #endif
373
374         min = get_minimum(&land, type);
375         ip = &ichr[type];
376         weight = ip->i_lbs;
377         mobcost = move_cost * weight;
378         if (mobcost > 0)
379             can_move = (double)land.lnd_mobil / mobcost;
380         else
381             can_move = land.lnd_item[type] - min;
382         if (can_move > land.lnd_item[type] - min)
383             can_move = land.lnd_item[type] - min;
384
385         if (can_move >= wanted) {
386             land.lnd_item[type] -= wanted;
387             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
388
389             if (actually_doit)
390                 putland(land.lnd_uid, &land);
391             return total_wanted;
392         } else if (can_move > 0) {
393             gotten += can_move;
394             wanted -= can_move;
395             land.lnd_item[type] -= can_move;
396
397             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
398
399             if (actually_doit)
400                 putland(land.lnd_uid, &land);
401         }
402     }
403
404     /* We've done the best we could */
405     /* return the number gotten */
406     return gotten;
407 }
408
409
410 /*
411  * We want to get enough shells to fire once,
412  * one update's worth of food.
413  */
414
415 static int
416 get_minimum(struct lndstr *lp, i_type type)
417 {
418     struct lchrstr *lcp;
419     int max, want = 0;
420
421     lcp = &lchr[(int)lp->lnd_type];
422     max = lcp->l_item[type];
423
424     switch (type) {
425     case I_FOOD:
426         if (opt_NOFOOD)
427             return 0;           /* no food reqd, get out */
428         want = (int)ceil(food_needed(lp->lnd_item, etu_per_update));
429         break;
430     case I_SHELL:
431         want = lcp->l_ammo;
432         break;
433     default:
434         return 0;
435     }
436
437     if (want > max)
438         want = max;
439
440     return want;
441 }
442
443 int
444 lnd_could_be_supplied(struct lndstr *lp)
445 {
446     int shells_needed, shells, keepshells;
447     int food, food_needed, keepfood;
448
449     if (!opt_NOFOOD) {
450         food_needed = get_minimum(lp, I_FOOD);
451         food = keepfood = lp->lnd_item[I_FOOD];
452         if (food < food_needed) {
453             lp->lnd_item[I_FOOD] = 0;
454             putland(lp->lnd_uid, lp);
455             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
456                                       I_FOOD, (food_needed - food));
457             lp->lnd_item[I_FOOD] = keepfood;
458             putland(lp->lnd_uid, lp);
459         }
460         if (food < food_needed)
461             return 0;
462
463     }
464
465     shells_needed = lchr[lp->lnd_type].l_ammo;
466     shells = keepshells = lp->lnd_item[I_SHELL];
467     if (shells < shells_needed) {
468         lp->lnd_item[I_SHELL] = 0;
469         putland(lp->lnd_uid, lp);
470         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
471                                     I_SHELL, (shells_needed - shells));
472         lp->lnd_item[I_SHELL] = keepshells;
473         putland(lp->lnd_uid, lp);
474     }
475
476     if (shells < shells_needed)
477         return 0;
478
479     return 1;
480 }