Skip to content

Commit d63c0db

Browse files
committed
Fix: paying an angry shopkeeper away from his shop hit an impossible.
dopay() assumes an angry shopkeeper can never have a live bill while the hero is not their shop's resident customer: the "after your hide / after blood" branch only handles the robbed total, and everything past it requires shkp == resident, else impossible("dopay: not to shopkeeper?"). That invariant leaks. Angering a shk by attacking them (rile_shk()/setmangry()) does not touch the bill, and u_left_shop() returns early without rob_shop() when the shk is not inside their shop, so a hero who steps out while the angry shk is already outside chasing keeps billct/debit live. Pressing 'p' next to that shk then took the irate-shk shortcut straight to the impossible (a panic under the fuzzer's impossible-escalation mode). The hero-inside variant, shk adjacent just beyond their door, reached it too. The same latent assumption exists upstream in 3.6.7 and 5.0. * src/shk.c: factor the "past due" conversion out of make_angry_shk() into new past_due_charges() (bill, debit, and loan fold into robbed, offset by credit, then setpaid() clears them); dopay() now settles the books with it when a non-resident angry shk still has billct or debit, so payment falls through to the existing "after blood, not money" compensation branch and pacifies the shk. The impossible remains as a safety net.
1 parent a23c53e commit d63c0db

2 files changed

Lines changed: 28 additions & 8 deletions

File tree

‎doc/evilhack-changelog.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5540,4 +5540,5 @@ The following changes to date are:
55405540
- Fix: pet kills of unique monsters were missing from the livelog
55415541
- Tweak vampiric monk loadout at game start
55425542
- Fix: paranoid swim prompt fired while flying via amulet of flying
5543+
- Fix: paying an angry shopkeeper away from his shop hit an impossible
55435544

‎src/shk.c‎

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ STATIC_DCL long FDECL(get_pricing_units, (struct obj *));
7272
STATIC_DCL boolean NDECL(angry_shk_exists);
7373
STATIC_DCL void FDECL(rile_shk, (struct monst *));
7474
STATIC_DCL void FDECL(rouse_shk, (struct monst *, BOOLEAN_P));
75+
STATIC_DCL void FDECL(past_due_charges, (struct monst *));
7576
STATIC_DCL void FDECL(remove_damage, (struct monst *, BOOLEAN_P));
7677
STATIC_DCL void FDECL(sub_one_frombill, (struct obj *, struct monst *));
7778
STATIC_DCL void FDECL(add_one_tobill, (struct obj *, BOOLEAN_P,
@@ -1238,15 +1239,11 @@ struct monst *shkp;
12381239
ESHK(shkp)->following = 1;
12391240
}
12401241

1241-
/* Used when the shkp is teleported or falls (ox == 0) out of his shop, or
1242-
when the player is not on a costly_spot and he damages something inside
1243-
the shop. These conditions must be checked by the calling function. */
1244-
/*ARGSUSED*/
1245-
void
1246-
make_angry_shk(shkp, ox, oy)
1242+
/* an angry shk no longer does business normally; fold any pending
1243+
transactions into the amount owed for robbery */
1244+
STATIC_OVL void
1245+
past_due_charges(shkp)
12471246
struct monst *shkp;
1248-
xchar ox UNUSED; /* <ox,oy> predate 'noit_Monnam()', let alone Shknam() */
1249-
xchar oy UNUSED;
12501247
{
12511248
struct eshk *eshkp = ESHK(shkp);
12521249

@@ -1259,6 +1256,19 @@ xchar oy UNUSED;
12591256
/* billct, debit, loan, and credit will be cleared by setpaid */
12601257
setpaid(shkp);
12611258
}
1259+
}
1260+
1261+
/* Used when the shkp is teleported or falls (ox == 0) out of his shop, or
1262+
when the player is not on a costly_spot and he damages something inside
1263+
the shop. These conditions must be checked by the calling function */
1264+
/*ARGSUSED*/
1265+
void
1266+
make_angry_shk(shkp, ox, oy)
1267+
struct monst *shkp;
1268+
xchar ox UNUSED; /* <ox,oy> predate 'noit_Monnam()', let alone Shknam() */
1269+
xchar oy UNUSED;
1270+
{
1271+
past_due_charges(shkp);
12621272

12631273
pline("%s %s!", Shknam(shkp), !ANGRY(shkp) ? "gets angry" : "is furious");
12641274
hot_pursuit(shkp);
@@ -1438,6 +1448,15 @@ dopay()
14381448
return 1;
14391449
}
14401450

1451+
/* an angry shk who isn't tending shop couldn't collect when the
1452+
transactions went past due (hero and/or shk outside the shop);
1453+
settle the books now so payment proceeds as compensation */
1454+
if (shkp != resident && ANGRY(shkp)
1455+
&& (eshkp->billct || eshkp->debit)) {
1456+
past_due_charges(shkp);
1457+
ltmp = eshkp->robbed;
1458+
}
1459+
14411460
/* ltmp is still eshkp->robbed here */
14421461
if (!eshkp->billct && !eshkp->debit) {
14431462
umoney = money_cnt(invent);

0 commit comments

Comments
 (0)