From 322f96ecb78e55cc285536a0bff380c48c59e2ec Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 14 Feb 2009 19:00:01 +0100 Subject: [PATCH] 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(). --- include/prototypes.h | 7 ++-- src/lib/commands/load.c | 16 ++++--- src/lib/commands/mine.c | 15 +++---- src/lib/commands/supp.c | 4 +- src/lib/subs/attsub.c | 19 ++------- src/lib/subs/landgun.c | 48 ++++++++------------- src/lib/subs/mission.c | 13 +++--- src/lib/subs/shpsub.c | 15 ++----- src/lib/subs/supply.c | 93 +++++++++++++++++++++++++---------------- 9 files changed, 109 insertions(+), 121 deletions(-) diff --git a/include/prototypes.h b/include/prototypes.h index 1d3ac26f..667430e7 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -679,9 +679,10 @@ extern void snxtsct_all(struct nstr_sect *); 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 */ diff --git a/src/lib/commands/load.c b/src/lib/commands/load.c index b809a08b..fcdb8ffa 100644 --- a/src/lib/commands/load.c +++ b/src/lib/commands/load.c @@ -644,17 +644,19 @@ load_land_ship(struct sctstr *sectp, struct shpstr *sp, int noisy, 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, @@ -994,13 +996,15 @@ load_land_land(struct sctstr *sectp, struct lndstr *lp, int noisy, 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, diff --git a/src/lib/commands/mine.c b/src/lib/commands/mine.c index 3d97136a..bf232098 100644 --- a/src/lib/commands/mine.c +++ b/src/lib/commands/mine.c @@ -94,7 +94,7 @@ landmine(void) 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; @@ -137,20 +137,15 @@ landmine(void) 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(§); diff --git a/src/lib/commands/supp.c b/src/lib/commands/supp.c index 61f64c04..5eed7a5c 100644 --- a/src/lib/commands/supp.c +++ b/src/lib/commands/supp.c @@ -51,9 +51,7 @@ supp(void) 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)); diff --git a/src/lib/subs/attsub.c b/src/lib/subs/attsub.c index 7f47f132..ce6ac812 100644 --- a/src/lib/subs/attsub.c +++ b/src/lib/subs/attsub.c @@ -1072,9 +1072,7 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def, 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; @@ -1235,9 +1233,7 @@ get_dlist(struct combat *def, struct emp_qelem *list, int a_spy, } 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) @@ -1501,15 +1497,8 @@ att_reacting_units(struct combat *def, struct emp_qelem *list, int a_spy, 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; diff --git a/src/lib/subs/landgun.c b/src/lib/subs/landgun.c index 0cddbd97..ef271346 100644 --- a/src/lib/subs/landgun.c +++ b/src/lib/subs/landgun.c @@ -86,18 +86,14 @@ int 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); } @@ -109,7 +105,7 @@ fort_fire(struct sctstr *sp) int shp_fire(struct shpstr *sp) { - int guns, shells; + int guns; if (sp->shp_effic < 60) return -1; @@ -117,13 +113,11 @@ shp_fire(struct shpstr *sp) 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); } @@ -135,19 +129,17 @@ shp_fire(struct shpstr *sp) 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); } @@ -159,20 +151,15 @@ shp_dchrg(struct shpstr *sp) 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(); @@ -202,9 +189,8 @@ lnd_fire(struct lndstr *lp) 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); @@ -212,7 +198,7 @@ lnd_fire(struct lndstr *lp) d *= (double)shells / (double)ammo; ammo = shells; } - lp->lnd_item[I_SHELL] = shells - ammo; + lp->lnd_item[I_SHELL] -= ammo; return d; } diff --git a/src/lib/subs/mission.c b/src/lib/subs/mission.c index 5c7d3cd0..140520de 100644 --- a/src/lib/subs/mission.c +++ b/src/lib/subs/mission.c @@ -939,11 +939,14 @@ mission_pln_equip(struct plist *plp, struct ichrstr *ip, int flags, 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; diff --git a/src/lib/subs/shpsub.c b/src/lib/subs/shpsub.c index 1ea88f0d..37984367 100644 --- a/src/lib/subs/shpsub.c +++ b/src/lib/subs/shpsub.c @@ -824,7 +824,6 @@ shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget) struct nstr_item ni; struct shpstr ship; int hitchance; - int shell; double gun, eff, teff; 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) 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); @@ -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", 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"); diff --git a/src/lib/subs/supply.c b/src/lib/subs/supply.c index dbe72572..805f6a41 100644 --- a/src/lib/subs/supply.c +++ b/src/lib/subs/supply.c @@ -28,7 +28,7 @@ * supply.c: Supply subroutines * * Known contributors to this file: - * + * Markus Armbruster, 2009 */ #include @@ -43,47 +43,44 @@ #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; - - amt = get_minimum(lp, type) - lp->lnd_item[type]; - if (amt > 0) { - lp->lnd_item[type] += supply_commod(lp->lnd_own, - lp->lnd_x, lp->lnd_y, - type, 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; +} + +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 @@ -96,10 +93,21 @@ lnd_in_supply(struct lndstr *lp) 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) @@ -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); } -/* 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)