Redesign automatic supply interface

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().
This commit is contained in:
Markus Armbruster 2009-02-14 19:00:01 +01:00
parent 6ac9ad66e1
commit 322f96ecb7
9 changed files with 109 additions and 121 deletions

View file

@ -679,9 +679,10 @@ extern void snxtsct_all(struct nstr_sect *);
extern void snxtsct_rewind(struct nstr_sect *); extern void snxtsct_rewind(struct nstr_sect *);
extern void snxtsct_dist(struct nstr_sect *, coord, coord, int); extern void snxtsct_dist(struct nstr_sect *, coord, coord, int);
/* supply.c */ /* supply.c */
extern void resupply_all(struct lndstr *); extern int sct_supply(struct sctstr *, i_type, int);
extern void resupply_commod(struct lndstr *, i_type); extern int shp_supply(struct shpstr *, i_type, int);
extern int supply_commod(int, int, int, 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_in_supply(struct lndstr *);
extern int lnd_could_be_supplied(struct lndstr *); extern int lnd_could_be_supplied(struct lndstr *);
/* takeover.c */ /* takeover.c */

View file

@ -644,17 +644,19 @@ load_land_ship(struct sctstr *sectp, struct shpstr *sp, int noisy,
gift(sp->shp_own, player->cnum, &land, buf); gift(sp->shp_own, player->cnum, &land, buf);
land.lnd_ship = sp->shp_uid; land.lnd_ship = sp->shp_uid;
land.lnd_harden = 0; land.lnd_harden = 0;
putland(land.lnd_uid, &land);
#if 0 #if 0
/* /*
* FIXME if this supplies from the sector, the putsect in * FIXME if this supplies from the sector, the putsect in
* load() / lload() duplicates those supplies, causing a * load() / lload() duplicates those supplies, causing a
* seqno mismatch * seqno mismatch
*/ */
resupply_all(&land); if (!lnd_supply_all(&land))
#endif pr("WARNING: %s is out of supply!\n", prland(&land));
putland(land.lnd_uid, &land); #else
if (!lnd_in_supply(&land)) if (!lnd_in_supply(&land))
pr("WARNING: %s is out of supply!\n", prland(&land)); pr("WARNING: %s is out of supply!\n", prland(&land));
#endif
} else { } else {
sprintf(buf, "unloaded in your %s at %s", sprintf(buf, "unloaded in your %s at %s",
dchr[sectp->sct_type].d_name, dchr[sectp->sct_type].d_name,
@ -994,13 +996,15 @@ load_land_land(struct sctstr *sectp, struct lndstr *lp, int noisy,
gift(lp->lnd_own, player->cnum, &land, buf); gift(lp->lnd_own, player->cnum, &land, buf);
land.lnd_land = lp->lnd_uid; land.lnd_land = lp->lnd_uid;
land.lnd_harden = 0; land.lnd_harden = 0;
putland(land.lnd_uid, &land);
#if 0 #if 0
/* FIXME same issue as in load_land_ship() */ /* FIXME same issue as in load_land_ship() */
resupply_all(&land); if (!lnd_supply_all(&land))
#endif pr("WARNING: %s is out of supply!\n", prland(&land));
putland(land.lnd_uid, &land); #else
if (!lnd_in_supply(&land)) if (!lnd_in_supply(&land))
pr("WARNING: %s is out of supply!\n", prland(&land)); pr("WARNING: %s is out of supply!\n", prland(&land));
#endif
} else { } else {
sprintf(buf, "unloaded in your %s at %s", sprintf(buf, "unloaded in your %s at %s",
dchr[sectp->sct_type].d_name, dchr[sectp->sct_type].d_name,

View file

@ -94,7 +94,7 @@ landmine(void)
struct lndstr land; struct lndstr land;
struct sctstr sect; struct sctstr sect;
struct nstr_item ni; struct nstr_item ni;
int shells, todo; int todo;
int mines_wanted; int mines_wanted;
int mines_laid; int mines_laid;
int total_mines_laid; int total_mines_laid;
@ -137,20 +137,15 @@ landmine(void)
todo = MIN(mines_wanted, land.lnd_mobil); todo = MIN(mines_wanted, land.lnd_mobil);
total_mines_laid = 0; total_mines_laid = 0;
do { do {
shells = land.lnd_item[I_SHELL]; lnd_supply(&land, I_SHELL, todo);
if (shells < todo) mines_laid = MIN(todo, land.lnd_item[I_SHELL]);
shells += supply_commod(land.lnd_own, land.lnd_item[I_SHELL] -= mines_laid;
land.lnd_x, land.lnd_y, I_SHELL,
todo - shells);
mines_laid = MIN(todo, shells);
land.lnd_item[I_SHELL] = shells - mines_laid;
land.lnd_mobil -= mines_laid; land.lnd_mobil -= mines_laid;
putland(land.lnd_uid, &land); putland(land.lnd_uid, &land);
total_mines_laid += mines_laid; total_mines_laid += mines_laid;
todo -= mines_laid; todo -= mines_laid;
} while (todo && mines_laid); } while (todo && mines_laid);
resupply_commod(&land, I_SHELL); lnd_supply_all(&land);
putland(land.lnd_uid, &land);
getsect(sect.sct_x, sect.sct_y, &sect); getsect(sect.sct_x, sect.sct_y, &sect);
sect.sct_mines = MIN(sect.sct_mines + total_mines_laid, MINES_MAX); sect.sct_mines = MIN(sect.sct_mines + total_mines_laid, MINES_MAX);
putsect(&sect); putsect(&sect);

View file

@ -51,9 +51,7 @@ supp(void)
if (!player->owner || land.lnd_own == 0) if (!player->owner || land.lnd_own == 0)
continue; continue;
nunits++; nunits++;
resupply_all(&land); if (lnd_supply_all(&land))
putland(land.lnd_uid, &land);
if (lnd_in_supply(&land))
pr("%s has supplies\n", prland(&land)); pr("%s has supplies\n", prland(&land));
else else
pr("%s is out of supply\n", prland(&land)); pr("%s is out of supply\n", prland(&land));

View file

@ -1072,9 +1072,7 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def,
pr("%s has no offensive strength\n", prland(&land)); pr("%s has no offensive strength\n", prland(&land));
continue; continue;
} }
resupply_all(&land); if (!lnd_supply_all(&land)) {
putland(land.lnd_uid, &land);
if (!lnd_in_supply(&land)) {
pr("%s is out of supply, and cannot %s\n", pr("%s is out of supply, and cannot %s\n",
prland(&land), att_mode[combat_mode]); prland(&land), att_mode[combat_mode]);
continue; continue;
@ -1235,9 +1233,7 @@ get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
} }
memset(llp, 0, sizeof(struct ulist)); memset(llp, 0, sizeof(struct ulist));
emp_insque(&llp->queue, list); emp_insque(&llp->queue, list);
resupply_all(&land); llp->supplied = lnd_supply_all(&land);
putland(land.lnd_uid, &land);
llp->supplied = lnd_in_supply(&land);
if (!get_land(A_DEFEND, def, land.lnd_uid, llp, 1)) if (!get_land(A_DEFEND, def, land.lnd_uid, llp, 1))
continue; continue;
if (lnd_spyval(&land) > *d_spyp) if (lnd_spyval(&land) > *d_spyp)
@ -1501,15 +1497,8 @@ att_reacting_units(struct combat *def, struct emp_qelem *list, int a_spy,
continue; continue;
/* Only supplied units can react */ /* Only supplied units can react */
if (list) { if (list ? !lnd_supply_all(&land) : !lnd_could_be_supplied(&land))
resupply_all(&land);
putland(land.lnd_uid, &land);
if (!lnd_in_supply(&land))
continue; continue;
} else {
if (!lnd_could_be_supplied(&land))
continue;
}
if (!in_oparea((struct empobj *)&land, def->x, def->y)) if (!in_oparea((struct empobj *)&land, def->x, def->y))
continue; continue;

View file

@ -86,18 +86,14 @@ int
fort_fire(struct sctstr *sp) fort_fire(struct sctstr *sp)
{ {
int guns = sp->sct_item[I_GUN]; int guns = sp->sct_item[I_GUN];
int shells;
if (sp->sct_type != SCT_FORTR || sp->sct_effic < FORTEFF) if (sp->sct_type != SCT_FORTR || sp->sct_effic < FORTEFF)
return -1; return -1;
if (sp->sct_item[I_MILIT] < 5 || guns == 0) if (sp->sct_item[I_MILIT] < 5 || guns == 0)
return -1; return -1;
shells = sp->sct_item[I_SHELL]; if (!sct_supply(sp, I_SHELL, 1))
shells += supply_commod(sp->sct_own, sp->sct_x, sp->sct_y,
I_SHELL, 1 - shells);
if (shells == 0)
return -1; return -1;
sp->sct_item[I_SHELL] = shells - 1; sp->sct_item[I_SHELL]--;
return (int)fortgun(sp->sct_effic, guns); return (int)fortgun(sp->sct_effic, guns);
} }
@ -109,7 +105,7 @@ fort_fire(struct sctstr *sp)
int int
shp_fire(struct shpstr *sp) shp_fire(struct shpstr *sp)
{ {
int guns, shells; int guns;
if (sp->shp_effic < 60) if (sp->shp_effic < 60)
return -1; return -1;
@ -117,13 +113,11 @@ shp_fire(struct shpstr *sp)
guns = MIN(guns, (sp->shp_item[I_MILIT] + 1) / 2); guns = MIN(guns, (sp->shp_item[I_MILIT] + 1) / 2);
if (guns == 0) if (guns == 0)
return -1; return -1;
shells = sp->shp_item[I_SHELL]; shp_supply(sp, I_SHELL, (guns + 1) / 2);
shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y, guns = MIN(guns, sp->shp_item[I_SHELL] * 2);
I_SHELL, (guns + 1) / 2 - shells);
guns = MIN(guns, shells * 2);
if (guns == 0) if (guns == 0)
return -1; 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); return (int)seagun(sp->shp_effic, guns);
} }
@ -135,19 +129,17 @@ shp_fire(struct shpstr *sp)
int int
shp_dchrg(struct shpstr *sp) shp_dchrg(struct shpstr *sp)
{ {
int shells, dchrgs; int dchrgs;
if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_DCH) == 0) if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_DCH) == 0)
return -1; return -1;
if (sp->shp_item[I_MILIT] == 0) if (sp->shp_item[I_MILIT] == 0)
return -1; return -1;
shells = sp->shp_item[I_SHELL]; shp_supply(sp, I_SHELL, 2);
shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y, dchrgs = MIN(2, sp->shp_item[I_SHELL]);
I_SHELL, 2 - shells); if (dchrgs == 0)
if (shells == 0)
return -1; return -1;
dchrgs = MIN(2, shells); sp->shp_item[I_SHELL] -= dchrgs;
sp->shp_item[I_SHELL] = shells - dchrgs;
return (int)seagun(sp->shp_effic, 2 * dchrgs - 1); return (int)seagun(sp->shp_effic, 2 * dchrgs - 1);
} }
@ -159,20 +151,15 @@ shp_dchrg(struct shpstr *sp)
int int
shp_torp(struct shpstr *sp, int usemob) shp_torp(struct shpstr *sp, int usemob)
{ {
int shells;
if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_TORP) == 0) if (sp->shp_effic < 60 || (mchr[sp->shp_type].m_flags & M_TORP) == 0)
return -1; return -1;
if (sp->shp_item[I_MILIT] == 0 || sp->shp_item[I_GUN] == 0) if (sp->shp_item[I_MILIT] == 0 || sp->shp_item[I_GUN] == 0)
return -1; return -1;
if (usemob && sp->shp_mobil <= 0) if (usemob && sp->shp_mobil <= 0)
return -1; return -1;
shells = sp->shp_item[I_SHELL]; if (!shp_supply(sp, I_SHELL, SHP_TORP_SHELLS))
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; return -1;
sp->shp_item[I_SHELL] = shells - SHP_TORP_SHELLS; sp->shp_item[I_SHELL] -= SHP_TORP_SHELLS;
if (usemob) if (usemob)
sp->shp_mobil -= (int)shp_mobcost(sp) / 2.0; sp->shp_mobil -= (int)shp_mobcost(sp) / 2.0;
return TORP_DAMAGE(); return TORP_DAMAGE();
@ -202,9 +189,8 @@ lnd_fire(struct lndstr *lp)
ammo = lchr[lp->lnd_type].l_ammo; ammo = lchr[lp->lnd_type].l_ammo;
if (CANT_HAPPEN(ammo == 0)) if (CANT_HAPPEN(ammo == 0))
ammo = 1; ammo = 1;
lnd_supply(lp, I_SHELL, ammo);
shells = lp->lnd_item[I_SHELL]; 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) if (shells == 0)
return -1; return -1;
d = landunitgun(lp->lnd_effic, guns); d = landunitgun(lp->lnd_effic, guns);
@ -212,7 +198,7 @@ lnd_fire(struct lndstr *lp)
d *= (double)shells / (double)ammo; d *= (double)shells / (double)ammo;
ammo = shells; ammo = shells;
} }
lp->lnd_item[I_SHELL] = shells - ammo; lp->lnd_item[I_SHELL] -= ammo;
return d; return d;
} }

View file

@ -939,11 +939,14 @@ mission_pln_equip(struct plist *plp, struct ichrstr *ip, int flags,
if (itype != I_NONE && needed <= 0) if (itype != I_NONE && needed <= 0)
return -1; return -1;
if (itype != I_NONE) { if (itype != I_NONE) {
if (itype == I_SHELL && item[itype] < needed) if (itype == I_SHELL && item[itype] < needed) {
item[itype] += supply_commod(plp->plane.pln_own, if (pp->pln_ship >= 0)
plp->plane.pln_x, shp_supply(&ship, I_SHELL, needed);
plp->plane.pln_y, else if (pp->pln_land >= 0)
I_SHELL, needed); lnd_supply(&land, I_SHELL, needed);
else
sct_supply(&sect, I_SHELL, needed);
}
if (item[itype] < needed) if (item[itype] < needed)
return -1; return -1;
item[itype] -= needed; item[itype] -= needed;

View file

@ -824,7 +824,6 @@ shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
struct nstr_item ni; struct nstr_item ni;
struct shpstr ship; struct shpstr ship;
int hitchance; int hitchance;
int shell;
double gun, eff, teff; double gun, eff, teff;
snxtitem_dist(&ni, EF_SHIP, dx, dy, 1); snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
@ -842,17 +841,14 @@ shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
if (ship.shp_effic < 60) if (ship.shp_effic < 60)
continue; continue;
shell = ship.shp_item[I_SHELL];
if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */ if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
continue; 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 */ if (ship.shp_item[I_GUN] < 1) /* we need at least 1 gun */
continue; continue;
if (!shp_supply(&ship, I_SHELL, 2))
continue;
ship.shp_item[I_SHELL] -= 2;
putship(ship.shp_uid, &ship);
/* now calculate the odds */ /* now calculate the odds */
gun = shp_usable_guns(&ship); gun = shp_usable_guns(&ship);
@ -870,9 +866,6 @@ shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n", mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
ship.shp_uid); ship.shp_uid);
mpr(ship.shp_own, "%d%% hitchance...", hitchance); 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) { if (roll(100) <= hitchance) {
mpr(bombown, "KABOOOM!! Missile destroyed\n\n"); mpr(bombown, "KABOOOM!! Missile destroyed\n\n");

View file

@ -28,7 +28,7 @@
* supply.c: Supply subroutines * supply.c: Supply subroutines
* *
* Known contributors to this file: * Known contributors to this file:
* * Markus Armbruster, 2009
*/ */
#include <config.h> #include <config.h>
@ -43,47 +43,44 @@
#include "sect.h" #include "sect.h"
#include "ship.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 s_commod(int, int, int, i_type, int, int);
static int get_minimum(struct lndstr *, i_type);
/* int
* We want to get enough guns to be maxed out, enough shells to sct_supply(struct sctstr *sp, i_type type, int wanted)
* 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)
{ {
if (!opt_NOFOOD) if (sp->sct_item[type] < wanted) {
resupply_commod(lp, I_FOOD); sp->sct_item[type] += supply_commod(sp->sct_own,
resupply_commod(lp, I_SHELL); sp->sct_x, sp->sct_y, type,
wanted - sp->sct_item[type]);
putsect(sp);
}
return sp->sct_item[type] >= wanted;
} }
/* int
* If the unit has less than it's minimum level of a shp_supply(struct shpstr *sp, i_type type, int wanted)
* certain commodity, fill it, to the best of our abilities.
*/
void
resupply_commod(struct lndstr *lp, i_type type)
{ {
int amt; if (sp->shp_item[type] < wanted) {
sp->shp_item[type] += supply_commod(sp->shp_own,
amt = get_minimum(lp, type) - lp->lnd_item[type]; sp->shp_x, sp->shp_y, type,
if (amt > 0) { wanted - sp->shp_item[type]);
lp->lnd_item[type] += supply_commod(lp->lnd_own, putship(sp->shp_uid, sp);
lp->lnd_x, lp->lnd_y,
type, amt);
} }
return sp->shp_item[type] >= wanted;
}
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,
wanted - lp->lnd_item[type]);
putland(lp->lnd_uid, lp);
}
return lp->lnd_item[type] >= wanted;
} }
int int
@ -96,10 +93,21 @@ lnd_in_supply(struct lndstr *lp)
return lp->lnd_item[I_SHELL] >= get_minimum(lp, I_SHELL); 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 * Actually get the commod
*/ */
int static int
supply_commod(int own, int x, int y, i_type type, int total_wanted) supply_commod(int own, int x, int y, i_type type, int total_wanted)
{ {
if (total_wanted <= 0) if (total_wanted <= 0)
@ -119,7 +127,18 @@ try_supply_commod(int own, int x, int y, i_type type, int total_wanted)
return s_commod(own, x, y, type, 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 static int
s_commod(int own, int x, int y, i_type type, int total_wanted, s_commod(int own, int x, int y, i_type type, int total_wanted,
int actually_doit) int actually_doit)