The automatic supply interface has design flaws that make it hard to
use correctly. Its current uses are in fact all wrong (until commit
0179fd86, the update had a few uses that might have been correct).
Some of the bugs can only bite with land unit capability combinations
that don't exist in the stock game, though.
Automatic supply draws supplies from supply sources in range. Since
that can update any supply source in range, all copies of potential
supply sources a caller may keep can get invalidated. Writing back
such an invalid copy wipes out the deduction of supplies and mobility
from a source, triggering a seqno mismatch oops.
This commit redesigns the interface so that callers can safely keep a
copy of the object drawing the supplies (the "supply sink"). The idea
is to pass the sink to the supply code, so it can avoid using it as
source. The actual avoiding will be implemented in a later commit.
Copies other than the supply sink still need to be eliminated. See
commit
65410d16 for an example.
Other improvements to help avoid common errors:
* Supply functions are commonly used to ensure the sink has a certain
amount of supplies. A common error is to fetch that amount
regardless of how many the sink already has. It's more convenient
for such users to pass how many they need to have instead of how
many to get.
* A common use of supply functions is to get supplies for immediate
use. If that use turns out not to be possible after all, the
supplies need to be added somewhere, which is all too easy to
forget. Many bugs of this kind have been fixed over time, and there
are still some left. This class of bugs can be avoided by adding
the supplies to the sink automatically.
In fact, this commit fixes precisely such bugs in mission_pln_equip()
and shp_missile_defense(): plane interception and support missions,
missile interception (abms), launch of ballistic missiles and
anti-sats could all lose shells, or supply more than needed.
Replace supply_commod() by new sct_supply(), shp_supply(),
lnd_supply(), and resupply_all() by new lnd_supply_all(). Simplify
users accordingly.
There's just one use of resupply_commod() left, in landmine(). Use
lnd_supply_all() there, and remove resupply_commod().
extern void snxtsct_rewind(struct nstr_sect *);
extern void snxtsct_dist(struct nstr_sect *, coord, coord, int);
/* supply.c */
-extern void resupply_all(struct lndstr *);
-extern void resupply_commod(struct lndstr *, i_type);
-extern int supply_commod(int, int, int, i_type, int);
+extern int sct_supply(struct sctstr *, i_type, int);
+extern int shp_supply(struct shpstr *, i_type, int);
+extern int lnd_supply(struct lndstr *, i_type, int);
+extern int lnd_supply_all(struct lndstr *);
extern int lnd_in_supply(struct lndstr *);
extern int lnd_could_be_supplied(struct lndstr *);
/* takeover.c */
gift(sp->shp_own, player->cnum, &land, buf);
land.lnd_ship = sp->shp_uid;
land.lnd_harden = 0;
+ putland(land.lnd_uid, &land);
#if 0
/*
* FIXME if this supplies from the sector, the putsect in
* load() / lload() duplicates those supplies, causing a
* seqno mismatch
*/
- resupply_all(&land);
-#endif
- putland(land.lnd_uid, &land);
+ if (!lnd_supply_all(&land))
+ pr("WARNING: %s is out of supply!\n", prland(&land));
+#else
if (!lnd_in_supply(&land))
pr("WARNING: %s is out of supply!\n", prland(&land));
+#endif
} else {
sprintf(buf, "unloaded in your %s at %s",
dchr[sectp->sct_type].d_name,
gift(lp->lnd_own, player->cnum, &land, buf);
land.lnd_land = lp->lnd_uid;
land.lnd_harden = 0;
+ putland(land.lnd_uid, &land);
#if 0
/* FIXME same issue as in load_land_ship() */
- resupply_all(&land);
-#endif
- putland(land.lnd_uid, &land);
+ if (!lnd_supply_all(&land))
+ pr("WARNING: %s is out of supply!\n", prland(&land));
+#else
if (!lnd_in_supply(&land))
pr("WARNING: %s is out of supply!\n", prland(&land));
+#endif
} else {
sprintf(buf, "unloaded in your %s at %s",
dchr[sectp->sct_type].d_name,
struct lndstr land;
struct sctstr sect;
struct nstr_item ni;
- int shells, todo;
+ int todo;
int mines_wanted;
int mines_laid;
int total_mines_laid;
todo = MIN(mines_wanted, land.lnd_mobil);
total_mines_laid = 0;
do {
- shells = land.lnd_item[I_SHELL];
- if (shells < todo)
- shells += supply_commod(land.lnd_own,
- land.lnd_x, land.lnd_y, I_SHELL,
- todo - shells);
- mines_laid = MIN(todo, shells);
- land.lnd_item[I_SHELL] = shells - mines_laid;
+ lnd_supply(&land, I_SHELL, todo);
+ mines_laid = MIN(todo, land.lnd_item[I_SHELL]);
+ land.lnd_item[I_SHELL] -= mines_laid;
land.lnd_mobil -= mines_laid;
putland(land.lnd_uid, &land);
total_mines_laid += mines_laid;
todo -= mines_laid;
} while (todo && mines_laid);
- resupply_commod(&land, I_SHELL);
- putland(land.lnd_uid, &land);
+ lnd_supply_all(&land);
getsect(sect.sct_x, sect.sct_y, §);
sect.sct_mines = MIN(sect.sct_mines + total_mines_laid, MINES_MAX);
putsect(§);
if (!player->owner || land.lnd_own == 0)
continue;
nunits++;
- resupply_all(&land);
- putland(land.lnd_uid, &land);
- if (lnd_in_supply(&land))
+ if (lnd_supply_all(&land))
pr("%s has supplies\n", prland(&land));
else
pr("%s is out of supply\n", prland(&land));
pr("%s has no offensive strength\n", prland(&land));
continue;
}
- resupply_all(&land);
- putland(land.lnd_uid, &land);
- if (!lnd_in_supply(&land)) {
+ if (!lnd_supply_all(&land)) {
pr("%s is out of supply, and cannot %s\n",
prland(&land), att_mode[combat_mode]);
continue;
}
memset(llp, 0, sizeof(struct ulist));
emp_insque(&llp->queue, list);
- resupply_all(&land);
- putland(land.lnd_uid, &land);
- llp->supplied = lnd_in_supply(&land);
+ llp->supplied = lnd_supply_all(&land);
if (!get_land(A_DEFEND, def, land.lnd_uid, llp, 1))
continue;
if (lnd_spyval(&land) > *d_spyp)
continue;
/* Only supplied units can react */
- if (list) {
- resupply_all(&land);
- putland(land.lnd_uid, &land);
- if (!lnd_in_supply(&land))
- continue;
- } else {
- if (!lnd_could_be_supplied(&land))
- continue;
- }
+ if (list ? !lnd_supply_all(&land) : !lnd_could_be_supplied(&land))
+ continue;
if (!in_oparea((struct empobj *)&land, def->x, def->y))
continue;
fort_fire(struct sctstr *sp)
{
int guns = sp->sct_item[I_GUN];
- int shells;
if (sp->sct_type != SCT_FORTR || sp->sct_effic < FORTEFF)
return -1;
if (sp->sct_item[I_MILIT] < 5 || guns == 0)
return -1;
- shells = sp->sct_item[I_SHELL];
- shells += supply_commod(sp->sct_own, sp->sct_x, sp->sct_y,
- I_SHELL, 1 - shells);
- if (shells == 0)
+ if (!sct_supply(sp, I_SHELL, 1))
return -1;
- sp->sct_item[I_SHELL] = shells - 1;
+ sp->sct_item[I_SHELL]--;
return (int)fortgun(sp->sct_effic, guns);
}
int
shp_fire(struct shpstr *sp)
{
- int guns, shells;
+ int guns;
if (sp->shp_effic < 60)
return -1;
guns = MIN(guns, (sp->shp_item[I_MILIT] + 1) / 2);
if (guns == 0)
return -1;
- shells = sp->shp_item[I_SHELL];
- shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y,
- I_SHELL, (guns + 1) / 2 - shells);
- guns = MIN(guns, shells * 2);
+ shp_supply(sp, I_SHELL, (guns + 1) / 2);
+ guns = MIN(guns, sp->shp_item[I_SHELL] * 2);
if (guns == 0)
return -1;
- sp->shp_item[I_SHELL] = shells - (guns + 1) / 2;
+ sp->shp_item[I_SHELL] -= (guns + 1) / 2;
return (int)seagun(sp->shp_effic, guns);
}
int
shp_dchrg(struct shpstr *sp)
{
- int shells, dchrgs;
+ int dchrgs;
if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_DCH) == 0)
return -1;
if (sp->shp_item[I_MILIT] == 0)
return -1;
- shells = sp->shp_item[I_SHELL];
- shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y,
- I_SHELL, 2 - shells);
- if (shells == 0)
- return -1;
- dchrgs = MIN(2, shells);
- sp->shp_item[I_SHELL] = shells - dchrgs;
+ shp_supply(sp, I_SHELL, 2);
+ dchrgs = MIN(2, sp->shp_item[I_SHELL]);
+ if (dchrgs == 0)
+ return -1;
+ sp->shp_item[I_SHELL] -= dchrgs;
return (int)seagun(sp->shp_effic, 2 * dchrgs - 1);
}
int
shp_torp(struct shpstr *sp, int usemob)
{
- int shells;
-
if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_TORP) == 0)
return -1;
if (sp->shp_item[I_MILIT] == 0 || sp->shp_item[I_GUN] == 0)
return -1;
if (usemob && sp->shp_mobil <= 0)
return -1;
- shells = sp->shp_item[I_SHELL];
- shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y,
- I_SHELL, SHP_TORP_SHELLS - shells);
- if (shells < SHP_TORP_SHELLS)
- return -1;
- sp->shp_item[I_SHELL] = shells - SHP_TORP_SHELLS;
+ if (!shp_supply(sp, I_SHELL, SHP_TORP_SHELLS))
+ return -1;
+ sp->shp_item[I_SHELL] -= SHP_TORP_SHELLS;
if (usemob)
sp->shp_mobil -= (int)shp_mobcost(sp) / 2.0;
return TORP_DAMAGE();
ammo = lchr[lp->lnd_type].l_ammo;
if (CANT_HAPPEN(ammo == 0))
ammo = 1;
+ lnd_supply(lp, I_SHELL, ammo);
shells = lp->lnd_item[I_SHELL];
- shells += supply_commod(lp->lnd_own, lp->lnd_x, lp->lnd_y,
- I_SHELL, ammo - shells);
if (shells == 0)
return -1;
d = landunitgun(lp->lnd_effic, guns);
d *= (double)shells / (double)ammo;
ammo = shells;
}
- lp->lnd_item[I_SHELL] = shells - ammo;
+ lp->lnd_item[I_SHELL] -= ammo;
return d;
}
if (itype != I_NONE && needed <= 0)
return -1;
if (itype != I_NONE) {
- if (itype == I_SHELL && item[itype] < needed)
- item[itype] += supply_commod(plp->plane.pln_own,
- plp->plane.pln_x,
- plp->plane.pln_y,
- I_SHELL, needed);
+ if (itype == I_SHELL && item[itype] < needed) {
+ if (pp->pln_ship >= 0)
+ shp_supply(&ship, I_SHELL, needed);
+ else if (pp->pln_land >= 0)
+ lnd_supply(&land, I_SHELL, needed);
+ else
+ sct_supply(§, I_SHELL, needed);
+ }
if (item[itype] < needed)
return -1;
item[itype] -= needed;
struct nstr_item ni;
struct shpstr ship;
int hitchance;
- int shell;
double gun, eff, teff;
snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
if (ship.shp_effic < 60)
continue;
- shell = ship.shp_item[I_SHELL];
if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
continue;
- if (shell < 2) { /* do we need shells */
- shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
- I_SHELL, 2);
- if (shell < 2)
- continue;
- }
if (ship.shp_item[I_GUN] < 1) /* we need at least 1 gun */
continue;
+ if (!shp_supply(&ship, I_SHELL, 2))
+ continue;
+ ship.shp_item[I_SHELL] -= 2;
+ putship(ship.shp_uid, &ship);
/* now calculate the odds */
gun = shp_usable_guns(&ship);
mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
ship.shp_uid);
mpr(ship.shp_own, "%d%% hitchance...", hitchance);
- /* use ammo */
- ship.shp_item[I_SHELL] = shell - 2;
- putship(ship.shp_uid, &ship);
if (roll(100) <= hitchance) {
mpr(bombown, "KABOOOM!! Missile destroyed\n\n");
* supply.c: Supply subroutines
*
* Known contributors to this file:
- *
+ * Markus Armbruster, 2009
*/
#include <config.h>
#include "sect.h"
#include "ship.h"
-static int get_minimum(struct lndstr *, i_type);
+static int supply_commod(int, int, int, i_type, int);
static int s_commod(int, int, int, i_type, int, int);
+static int get_minimum(struct lndstr *, i_type);
-/*
- * We want to get enough guns to be maxed out, enough shells to
- * fire once, one update's worth of food.
- *
- * Firts, try to forage in the sector
- * Second look for a warehouse or headquarters to leech
- * Third, look for a ship we own in a harbor
- * Fourth, look for supplies in a supply unit we own
- * (one good reason to do this last is that the supply
- * unit will then call resupply, taking more time)
- *
- * May want to put code to resupply with SAMs here, later --ts
- */
-
-void
-resupply_all(struct lndstr *lp)
+int
+sct_supply(struct sctstr *sp, i_type type, int wanted)
{
- if (!opt_NOFOOD)
- resupply_commod(lp, I_FOOD);
- resupply_commod(lp, I_SHELL);
+ if (sp->sct_item[type] < wanted) {
+ sp->sct_item[type] += supply_commod(sp->sct_own,
+ sp->sct_x, sp->sct_y, type,
+ wanted - sp->sct_item[type]);
+ putsect(sp);
+ }
+ return sp->sct_item[type] >= wanted;
}
-/*
- * If the unit has less than it's minimum level of a
- * certain commodity, fill it, to the best of our abilities.
- */
-
-void
-resupply_commod(struct lndstr *lp, i_type type)
+int
+shp_supply(struct shpstr *sp, i_type type, int wanted)
{
- int amt;
+ if (sp->shp_item[type] < wanted) {
+ sp->shp_item[type] += supply_commod(sp->shp_own,
+ sp->shp_x, sp->shp_y, type,
+ wanted - sp->shp_item[type]);
+ putship(sp->shp_uid, sp);
+ }
+ return sp->shp_item[type] >= wanted;
+}
- amt = get_minimum(lp, type) - lp->lnd_item[type];
- if (amt > 0) {
+int
+lnd_supply(struct lndstr *lp, i_type type, int wanted)
+{
+ if (lp->lnd_item[type] < wanted) {
lp->lnd_item[type] += supply_commod(lp->lnd_own,
- lp->lnd_x, lp->lnd_y,
- type, amt);
+ lp->lnd_x, lp->lnd_y, type,
+ wanted - lp->lnd_item[type]);
+ putland(lp->lnd_uid, lp);
}
+ return lp->lnd_item[type] >= wanted;
}
int
return lp->lnd_item[I_SHELL] >= get_minimum(lp, I_SHELL);
}
+int
+lnd_supply_all(struct lndstr *lp)
+{
+ int fail = 0;
+
+ if (!opt_NOFOOD)
+ fail |= !lnd_supply(lp, I_FOOD, get_minimum(lp, I_FOOD));
+ fail |= !lnd_supply(lp, I_SHELL, get_minimum(lp, I_SHELL));
+ return !fail;
+}
+
/*
* Actually get the commod
*/
-int
+static int
supply_commod(int own, int x, int y, i_type type, int total_wanted)
{
if (total_wanted <= 0)
return s_commod(own, x, y, type, total_wanted, 0);
}
-/* Get supplies of a certain type */
+/*
+ * Actually get the commod
+ *
+ * First, try to forage in the sector
+ * Second look for a warehouse or headquarters to leech
+ * Third, look for a ship we own in a harbor
+ * Fourth, look for supplies in a supply unit we own
+ * (one good reason to do this last is that the supply
+ * unit will then call resupply, taking more time)
+ *
+ * May want to put code to resupply with SAMs here, later --ts
+ */
static int
s_commod(int own, int x, int y, i_type type, int total_wanted,
int actually_doit)