src/lib/subs/supply.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  supply.c: Supply subroutines
00029  * 
00030  *  Known contributors to this file:
00031  *  
00032  */
00033 
00034 #include <config.h>
00035 
00036 #include <math.h>
00037 #include "file.h"
00038 #include "land.h"
00039 #include "nat.h"
00040 #include "optlist.h"
00041 #include "player.h"
00042 #include "prototypes.h"
00043 #include "sect.h"
00044 #include "ship.h"
00045 
00046 static int get_minimum(struct lndstr *, i_type);
00047 static int s_commod(int, int, int, i_type, int, int);
00048 
00049 /*
00050  * We want to get enough guns to be maxed out, enough shells to
00051  *      fire once, one update's worth of food, enough fuel for
00052  *      one update.
00053  *
00054  * Firts, try to forage in the sector
00055  * Second look for a warehouse or headquarters to leech
00056  * Third, look for a ship we own in a harbor
00057  * Fourth, look for supplies in a supply unit we own
00058  *              (one good reason to do this last is that the supply
00059  *               unit will then call resupply, taking more time)
00060  *
00061  * May want to put code to resupply with SAMs here, later --ts
00062  */
00063 
00064 void
00065 resupply_all(struct lndstr *lp)
00066 {
00067     if (!opt_NOFOOD)
00068         resupply_commod(lp, I_FOOD);
00069     resupply_commod(lp, I_SHELL);
00070     if (opt_FUEL)
00071         resupply_commod(lp, I_PETROL);
00072 }
00073 
00074 /*
00075  * If the unit has less than it's minimum level of a
00076  * certain commodity, fill it, to the best of our abilities.
00077  */
00078 
00079 void
00080 resupply_commod(struct lndstr *lp, i_type type)
00081 {
00082     int amt;
00083     struct shpstr ship;
00084 
00085     /* Ok, do we now have enough? */
00086     amt = get_minimum(lp, type) - lp->lnd_item[type];
00087     if (amt > 0) {
00088         lp->lnd_item[type] += supply_commod(lp->lnd_own,
00089                                             lp->lnd_x, lp->lnd_y,
00090                                             type, amt);
00091         amt = get_minimum(lp, type) - lp->lnd_item[type];
00092     }
00093     /* Now, check again to see if we have enough. */
00094     if (amt > 0) {
00095         /* Are we on a ship?  if so, try to get it from the ship first. */
00096         if (lp->lnd_ship >= 0) {
00097             getship(lp->lnd_ship, &ship);
00098             /* Now, determine how much we can get */
00099             if (amt > ship.shp_item[type])
00100                 amt = ship.shp_item[type];
00101             /* Now, add and subtract */
00102             lp->lnd_item[type] += amt;
00103             ship.shp_item[type] -= amt;
00104             putship(lp->lnd_ship, &ship);
00105         }
00106     }
00107 
00108     if (opt_FUEL && type == I_PETROL) {
00109         int fuel_needed = lp->lnd_fuelu
00110             * ((float)etu_per_update * land_mob_scale) / 10.0;
00111 
00112         while ((lp->lnd_fuel < fuel_needed) && lp->lnd_item[I_PETROL]) {
00113             lp->lnd_fuel += 10;
00114             if (lp->lnd_fuel > lp->lnd_fuelc)
00115                 lp->lnd_fuel = lp->lnd_fuelc;
00116             lp->lnd_item[I_PETROL]--;
00117         }
00118     }
00119 }
00120 
00121 /*
00122  * Actually get the commod
00123  */
00124 int
00125 supply_commod(int own, int x, int y, i_type type, int total_wanted)
00126 {
00127     if (total_wanted < 0)
00128         return 0;
00129     return s_commod(own, x, y, type, total_wanted, !player->simulation);
00130 }
00131 
00132 /*
00133  * Just return the number you COULD get, without doing it
00134  */
00135 int
00136 try_supply_commod(int own, int x, int y, i_type type, int total_wanted)
00137 {
00138     if (total_wanted < 0)
00139         return 0;
00140 
00141     return s_commod(own, x, y, type, total_wanted, 0);
00142 }
00143 
00144 /* Get supplies of a certain type */
00145 static int
00146 s_commod(int own, int x, int y, i_type type, int total_wanted,
00147          int actually_doit)
00148 {
00149     int wanted = total_wanted;
00150     int gotten = 0, lookrange;
00151     struct sctstr sect, dest;
00152     struct nstr_sect ns;
00153     struct nstr_item ni;
00154     struct lchrstr *lcp;
00155     struct shpstr ship;
00156     struct lndstr land;
00157     /* leave at least 1 military in sectors/ships */
00158     int minimum = (type == I_MILIT ? 1 : 0);
00159     int can_move;
00160     double move_cost, weight, mobcost;
00161     int packing;
00162     struct dchrstr *dp;
00163     struct ichrstr *ip;
00164     char buf[1024];
00165 
00166     /* try to get it from sector we're in */
00167     getsect(x, y, &dest);
00168     getsect(x, y, &sect);
00169     if (sect.sct_own == own) {
00170         if (sect.sct_item[type] - wanted >= minimum) {
00171             sect.sct_item[type] -= wanted;
00172             if (actually_doit)
00173                 putsect(&sect);
00174             return total_wanted;
00175         } else if (sect.sct_item[type] - minimum > 0) {
00176             gotten += sect.sct_item[type] - minimum;
00177             wanted -= sect.sct_item[type] - minimum;
00178             sect.sct_item[type] = minimum;
00179             if (actually_doit)
00180                 putsect(&sect);
00181         }
00182     }
00183     /* look for a headquarters or warehouse */
00184     lookrange = tfact(own, 10.0);
00185     snxtsct_dist(&ns, x, y, lookrange);
00186     while (nxtsct(&ns, &sect) && wanted) {
00187         if (sect.sct_own != own)
00188             continue;
00189         if ((sect.sct_type != SCT_WAREH) &&
00190             (sect.sct_type != SCT_HEADQ) && (sect.sct_type != SCT_HARBR))
00191             continue;
00192         if ((sect.sct_type == SCT_HEADQ) &&
00193             (sect.sct_dist_x == sect.sct_x) &&
00194             (sect.sct_dist_y == sect.sct_y))
00195             continue;
00196         if (sect.sct_effic < 60)
00197             continue;
00198         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
00199             continue;
00200         if (!opt_NOFOOD && type == I_FOOD)
00201             minimum = 1 + (int)ceil(food_needed(sect.sct_item,
00202                                                 etu_per_update));
00203         if (sect.sct_item[type] <= minimum) {
00204             /* Don't bother... */
00205             continue;
00206         }
00207         ip = &ichr[type];
00208         dp = &dchr[sect.sct_type];
00209         packing = ip->i_pkg[dp->d_pkg];
00210         if (packing > 1 && sect.sct_effic < 60)
00211             packing = 1;
00212         weight = (double)ip->i_lbs / packing;
00213         mobcost = move_cost * weight;
00214         if (mobcost > 0)
00215             can_move = (double)sect.sct_mobil / mobcost;
00216         else
00217             can_move = sect.sct_item[type] - minimum;
00218         if (can_move > sect.sct_item[type] - minimum)
00219             can_move = sect.sct_item[type] - minimum;
00220 
00221         if (can_move >= wanted) {
00222             int n;
00223 
00224             sect.sct_item[type] -= wanted;
00225 
00226             /* take off mobility for delivering sect */
00227             n = roundavg(total_wanted * weight * move_cost);
00228             if (n < 0)
00229                 n = 0;
00230             if (n > sect.sct_mobil)
00231                 n = sect.sct_mobil;
00232             sect.sct_mobil -= n;
00233 
00234             if (actually_doit)
00235                 putsect(&sect);
00236 
00237             return total_wanted;
00238         } else if (can_move > 0) {
00239             int n;
00240             gotten += can_move;
00241             wanted -= can_move;
00242             sect.sct_item[type] -= can_move;
00243 
00244             /* take off mobility for delivering sect */
00245             n = roundavg(can_move * weight * move_cost);
00246             if (n < 0)
00247                 n = 0;
00248             if (n > sect.sct_mobil)
00249                 n = sect.sct_mobil;
00250             sect.sct_mobil -= n;
00251 
00252             if (actually_doit)
00253                 putsect(&sect);
00254         }
00255     }
00256 
00257     /* look for an owned ship in a harbor */
00258     snxtitem_dist(&ni, EF_SHIP, x, y, lookrange);
00259 
00260     while (nxtitem(&ni, &ship) && wanted) {
00261         if (ship.shp_own != own)
00262             continue;
00263 
00264         if (!(mchr[(int)ship.shp_type].m_flags & M_SUPPLY))
00265             continue;
00266         getsect(ship.shp_x, ship.shp_y, &sect);
00267         if (sect.sct_type != SCT_HARBR)
00268             continue;
00269         if (sect.sct_effic < 2)
00270             continue;
00271         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
00272             continue;
00273         if (!opt_NOFOOD && type == I_FOOD)
00274             minimum = 1 + (int)ceil(food_needed(ship.shp_item,
00275                                                 etu_per_update));
00276         if (ship.shp_item[type] <= minimum) {
00277             /* Don't bother... */
00278             continue;
00279         }
00280         ip = &ichr[type];
00281         dp = &dchr[sect.sct_type];
00282         packing = ip->i_pkg[dp->d_pkg];
00283         if (packing > 1 && sect.sct_effic < 60)
00284             packing = 1;
00285         weight = (double)ip->i_lbs / packing;
00286         mobcost = move_cost * weight;
00287         if (mobcost > 0)
00288             can_move = (double)sect.sct_mobil / mobcost;
00289         else
00290             can_move = ship.shp_item[type] - minimum;
00291         if (can_move > ship.shp_item[type] - minimum)
00292             can_move = ship.shp_item[type] - minimum;
00293         if (can_move >= wanted) {
00294             int n;
00295             ship.shp_item[type] -= wanted;
00296 
00297             n = roundavg(wanted * weight * move_cost);
00298             if (n < 0)
00299                 n = 0;
00300             if (n > sect.sct_mobil)
00301                 n = sect.sct_mobil;
00302             sect.sct_mobil -= n;
00303             if (actually_doit) {
00304                 putship(ship.shp_uid, &ship);
00305                 putsect(&sect);
00306             }
00307             return total_wanted;
00308         } else if (can_move > 0) {
00309             int n;
00310             gotten += can_move;
00311             wanted -= can_move;
00312             ship.shp_item[type] -= can_move;
00313 
00314             n = roundavg(can_move * weight * move_cost);
00315             if (n < 0)
00316                 n = 0;
00317             if (n > sect.sct_mobil)
00318                 n = sect.sct_mobil;
00319             sect.sct_mobil -= n;
00320 
00321             if (actually_doit) {
00322                 putship(ship.shp_uid, &ship);
00323                 putsect(&sect);
00324             }
00325         }
00326     }
00327 
00328     /* look for an owned supply unit */
00329     snxtitem_dist(&ni, EF_LAND, x, y, lookrange);
00330 
00331     while (nxtitem(&ni, &land) && wanted) {
00332         int min;
00333 
00334         if (land.lnd_own != own)
00335             continue;
00336 
00337         lcp = &lchr[(int)land.lnd_type];
00338         if (!(lcp->l_flags & L_SUPPLY))
00339             continue;
00340 
00341         if (land.lnd_item[type] <= get_minimum(&land, type))
00342             continue;
00343 
00344         getsect(land.lnd_x, land.lnd_y, &sect);
00345         if (!BestLandPath(buf, &dest, &sect, &move_cost, MOB_MOVE))
00346             continue;
00347 
00348         if ((land.lnd_ship >= 0) && (sect.sct_type != SCT_HARBR))
00349             continue;
00350 
00351         if ((land.lnd_ship >= 0) && (sect.sct_effic < 2))
00352             continue;
00353 
00354         if (land.lnd_item[type] - wanted < get_minimum(&land, type)) {
00355             struct lndstr save;
00356 
00357             /*
00358              * Temporarily zap this unit's store, so the recursion
00359              * avoids it.
00360              */
00361             save = land;
00362             land.lnd_item[type] = 0;
00363             putland(land.lnd_uid, &land);
00364 
00365             land.lnd_item[type] =
00366                 save.lnd_item[type] + s_commod(own, land.lnd_x, land.lnd_y,
00367                                                type, wanted, actually_doit);
00368             if (actually_doit)
00369                 putland(land.lnd_uid, &land);
00370             else
00371                 putland(save.lnd_uid, &save);
00372         }
00373 
00374         min = get_minimum(&land, type);
00375         ip = &ichr[type];
00376         weight = ip->i_lbs;
00377         mobcost = move_cost * weight;
00378         if (mobcost > 0)
00379             can_move = (double)land.lnd_mobil / mobcost;
00380         else
00381             can_move = land.lnd_item[type] - min;
00382         if (can_move > land.lnd_item[type] - min)
00383             can_move = land.lnd_item[type] - min;
00384 
00385         if (can_move >= wanted) {
00386             land.lnd_item[type] -= wanted;
00387 
00388             /* resupply the supply unit */
00389             resupply_commod(&land, type);
00390 
00391             land.lnd_mobil -= roundavg(wanted * weight * move_cost);
00392 
00393             if (actually_doit)
00394                 putland(land.lnd_uid, &land);
00395             return total_wanted;
00396         } else if (can_move > 0) {
00397             gotten += can_move;
00398             wanted -= can_move;
00399             land.lnd_item[type] -= can_move;
00400 
00401             land.lnd_mobil -= roundavg(can_move * weight * move_cost);
00402 
00403             if (actually_doit)
00404                 putland(land.lnd_uid, &land);
00405         }
00406     }
00407 
00408     /* We've done the best we could */
00409     /* return the number gotten */
00410     return gotten;
00411 }
00412 
00413 
00414 /*
00415  * We want to get enough shells to fire once,
00416  * one update's worth of food, enough fuel for
00417  * one update.
00418  */
00419 
00420 static int
00421 get_minimum(struct lndstr *lp, i_type type)
00422 {
00423     struct lchrstr *lcp;
00424     int max, want = 0;
00425 
00426     lcp = &lchr[(int)lp->lnd_type];
00427     max = lcp->l_item[type];
00428 
00429     switch (type) {
00430     case I_FOOD:
00431         if (opt_NOFOOD)
00432             return 0;           /* no food reqd, get out */
00433         want = (int)ceil(food_needed(lp->lnd_item, etu_per_update));
00434         break;
00435     case I_SHELL:
00436         want = lp->lnd_ammo;
00437         break;
00438 
00439         /*
00440          * return the amount of pet we'd need to get to 
00441          * enough fuel for 1 update
00442          */
00443     case I_PETROL:
00444         if (opt_FUEL == 0)
00445             return 0;
00446         want = lp->lnd_fuelu * ((float)etu_per_update * land_mob_scale)
00447             / 10.0;
00448         want -= lp->lnd_fuel;
00449         if (want > 0) {
00450             want = want / 10;
00451             if (want == 0)
00452                 want++;
00453         }
00454 
00455         max = want;
00456         break;
00457     default:
00458         return 0;
00459     }
00460 
00461     if (want > max)
00462         want = max;
00463 
00464     return want;
00465 }
00466 
00467 int
00468 has_supply(struct lndstr *lp)
00469 {
00470     int shells_needed, shells, keepshells;
00471     int food, food_needed, keepfood;
00472     int fuel_needed, fuel, petrol_needed, petrol, keeppetrol;
00473 
00474     if (!opt_NOFOOD) {
00475         food_needed = get_minimum(lp, I_FOOD);
00476         food = keepfood = lp->lnd_item[I_FOOD];
00477         if (food < food_needed) {
00478             lp->lnd_item[I_FOOD] = 0;
00479             putland(lp->lnd_uid, lp);
00480             food += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
00481                                       I_FOOD, (food_needed - food));
00482             lp->lnd_item[I_FOOD] = keepfood;
00483             putland(lp->lnd_uid, lp);
00484         }
00485         if (food < food_needed)
00486             return 0;
00487 
00488     }
00489 
00490     shells_needed = lp->lnd_ammo;
00491     shells = keepshells = lp->lnd_item[I_SHELL];
00492     if (shells < shells_needed) {
00493         lp->lnd_item[I_SHELL] = 0;
00494         putland(lp->lnd_uid, lp);
00495         shells += try_supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
00496                                     I_SHELL, (shells_needed - shells));
00497         lp->lnd_item[I_SHELL] = keepshells;
00498         putland(lp->lnd_uid, lp);
00499     }
00500 
00501     if (shells < shells_needed)
00502         return 0;
00503 
00504     if (opt_FUEL) {
00505         fuel_needed = lp->lnd_fuelu;
00506         fuel = lp->lnd_fuel;
00507         if (fuel < fuel_needed) {
00508             petrol_needed =
00509                 ldround((fuel_needed - fuel) / 10.0, 1);
00510             petrol = keeppetrol = lp->lnd_item[I_PETROL];
00511             if (petrol < petrol_needed) {
00512                 lp->lnd_item[I_PETROL] = 0;
00513                 putland(lp->lnd_uid, lp);
00514                 petrol += try_supply_commod(lp->lnd_own,
00515                                             lp->lnd_x, lp->lnd_y,
00516                                             I_PETROL,
00517                                             (petrol_needed - petrol));
00518                 lp->lnd_item[I_PETROL] = keeppetrol;
00519                 putland(lp->lnd_uid, lp);
00520             }
00521             fuel += petrol * 10;
00522         }
00523 
00524         if (fuel < fuel_needed)
00525             return 0;
00526     }
00527     /* end opt_FUEL */
00528     return 1;
00529 }
00530 
00531 int
00532 use_supply(struct lndstr *lp)
00533 {
00534     int shells_needed, shells, food, food_needed;
00535     int fuel_needed, fuel, petrol_needed, petrol;
00536 
00537     shells_needed = lp->lnd_ammo;
00538     shells = lp->lnd_item[I_SHELL];
00539     if (shells < shells_needed) {
00540         lp->lnd_item[I_SHELL] = 0;
00541         putland(lp->lnd_uid, lp);
00542         shells += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
00543                                 I_SHELL, shells_needed - shells);
00544         lp->lnd_item[I_SHELL] = shells;
00545     }
00546 
00547     lp->lnd_item[I_SHELL] = MAX(lp->lnd_item[I_SHELL] - shells_needed, 0);
00548 
00549     if (lp->lnd_frg)            /* artillery */
00550         goto done;
00551 
00552     food_needed = get_minimum(lp, I_FOOD);
00553     food = lp->lnd_item[I_SHELL];
00554 
00555     if (food < food_needed) {
00556         lp->lnd_item[I_FOOD] = 0;
00557         putland(lp->lnd_uid, lp);
00558         food += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
00559                               I_FOOD, food_needed - food);
00560         lp->lnd_item[I_FOOD] = food;
00561     }
00562 
00563     lp->lnd_item[I_FOOD] = MAX(lp->lnd_item[I_FOOD] - food_needed, 0);
00564 
00565     if (opt_FUEL) {
00566         fuel_needed = lp->lnd_fuelu;
00567         fuel = lp->lnd_fuel;
00568 
00569         petrol = petrol_needed = 0;
00570 
00571         if (fuel < fuel_needed) {
00572             petrol_needed =
00573                 ldround((fuel_needed - fuel) / 10.0, 1);
00574             petrol = lp->lnd_item[I_PETROL];
00575         }
00576 
00577         if (petrol < petrol_needed) {
00578             lp->lnd_item[I_PETROL] = 0;
00579             putland(lp->lnd_uid, lp);
00580             petrol += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
00581                                     I_PETROL, petrol_needed - petrol);
00582             lp->lnd_item[I_PETROL] = petrol;
00583         }
00584 
00585         if (petrol_needed) {
00586             if (petrol >= petrol_needed) {
00587                 lp->lnd_item[I_PETROL]
00588                     = MAX(lp->lnd_item[I_PETROL] - petrol_needed, 0);
00589                 lp->lnd_fuel += petrol_needed * 10;
00590             } else {
00591                 lp->lnd_fuel += lp->lnd_item[I_PETROL] * 10;
00592                 lp->lnd_item[I_PETROL] = 0;
00593             }
00594         }
00595 
00596         lp->lnd_fuel = MAX(lp->lnd_fuel - fuel_needed, 0);
00597     }
00598     /* end opt_FUEL */
00599   done:
00600     putland(lp->lnd_uid, lp);
00601     return 1;
00602 }

Generated on Fri Mar 28 11:01:16 2008 for empserver by  doxygen 1.5.2