Skip to content

Commit caf9800

Browse files
committed
Fix: prevent monsters from changing gear while reading a spellbook.
A spellcasting monster's multi-turn spellbook read should be a focused activity, but its gear-management machinery could fire mid-read. In movemon(), the I_SPECIAL gear handler (re-equip armor, re-eval weapon) runs before dochug()/use_misc() advances the read, so a pet that picked up or was handed better gear would swap it and burn the turn, delaying the read by a round. Throwing gear directly at a tame monster took the gear immediately by a separate route. Enforce mutual exclusion: while a read is in progress, defer the gear change until it finishes or is interrupted, reusing the existing I_SPECIAL deferral path. * src/mon.c: gate the I_SPECIAL gear handler on the monster not currently reading (msp_reading != 0). The test sits before the I_SPECIAL clear, so the flag stays set and the deferred wear/wield resumes once the read completes. * src/dothrow.c: when a tame monster catches thrown gear mid-read, pocket the item and call check_gear_next_turn() instead of wielding/wearing immediately, so the gated movemon() handler equips it after the read.
1 parent 06b7606 commit caf9800

3 files changed

Lines changed: 19 additions & 7 deletions

File tree

‎doc/evilhack-changelog.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5488,4 +5488,5 @@ The following changes to date are:
54885488
- Prevent Yendorian army types and guards from spawning as aasimar
54895489
- Add a dmonsfree detector for monsters freed without m_detach
54905490
- Monster re-wield a better weapon gained outside of combat
5491+
- Fix: prevent monsters from changing gear while reading a spellbook
54915492

‎src/dothrow.c‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,12 +2123,18 @@ struct obj *obj; /* thrownobj or kickedobj or uwep */
21232123
pline("%s catches %s.", Monnam(mon), the(xname(obj)));
21242124
obj_extract_self(obj);
21252125
(void) mpickobj(mon,obj);
2126-
if (attacktype(mon->data, AT_WEAP)
2127-
&& mon->weapon_check == NEED_WEAPON) {
2128-
mon->weapon_check = NEED_HTH_WEAPON;
2129-
(void) mon_wield_item(mon);
2126+
/* don't break a spellbook read to gear up; defer the
2127+
wield/wear until the monster finishes reading */
2128+
if (has_emsp(mon) && EMSP(mon)->msp_reading != 0) {
2129+
check_gear_next_turn(mon);
2130+
} else {
2131+
if (attacktype(mon->data, AT_WEAP)
2132+
&& mon->weapon_check == NEED_WEAPON) {
2133+
mon->weapon_check = NEED_HTH_WEAPON;
2134+
(void) mon_wield_item(mon);
2135+
}
2136+
m_dowear(mon, FALSE);
21302137
}
2131-
m_dowear(mon, FALSE);
21322138
newsym(mon->mx, mon->my);
21332139
return 1;
21342140
}

‎src/mon.c‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,8 +1606,13 @@ movemon()
16061606

16071607
/* after losing equipment, try to put on replacement;
16081608
skip monsters parked at (0,0) since m_dowear_type() may call
1609-
begin_burn() which needs get_obj_location() to succeed */
1610-
if ((mtmp->misc_worn_check & I_SPECIAL) && mtmp->mx) {
1609+
begin_burn() which needs get_obj_location() to succeed; also
1610+
skip a monster concentrating on reading a spellbook so it
1611+
does not swap armor or weapons mid-read -- I_SPECIAL stays
1612+
set and the change is deferred until the read finishes or is
1613+
interrupted */
1614+
if ((mtmp->misc_worn_check & I_SPECIAL) && mtmp->mx
1615+
&& !(has_emsp(mtmp) && EMSP(mtmp)->msp_reading != 0)) {
16111616
long oldworn;
16121617

16131618
/* hostiles only try to equip things if they think hero isn't

0 commit comments

Comments
 (0)