Sitelet https://github.com/NetHack/NetHack/pull/1684
Skip to content

Protect timed sleepers from light blindness - #1684

Open
crissman wants to merge 5 commits into
NetHack:NetHack-5.0from
crissman:timed-sleep-blindness
Open

crissman wants to merge 5 commits into
NetHack:NetHack-5.0from
crissman:timed-sleep-blindness

Conversation

@crissman

@crissman crissman commented Sep 21, 2026 •

Copy link
Copy Markdown

Monsters put to sleep for a fixed duration can still be blinded by light.

This addresses an existing BUG comment in the source. The change is recorded in doc/fixes5-0-1.txt.

A monster put to sleep for a fixed duration can still be blinded by light: sleep_monst() uses mfrozen, while resists_blnd() recognizes only indefinite sleep through msleeping. Checking all frozen monsters would incorrectly protect paralysis and delays such as donning armor.

Record whether the active frozen interval represents timed sleep, then include that state in light-blindness resistance. Reuse the existing unused long slot in struct monst, retaining its type and position. Clear the marker when paralysis or an armor/jelly delay replaces sleep, and when the normal timer expires. Require an active timer and blocked movement, so an explicit thaw makes any leftover marker ineffective. Existing noise, attack, and timer behavior is unchanged.

Validation: source-extracted C tests compiled with AddressSanitizer and UndefinedBehaviorSanitizer reproduce three failing expectations in the baseline; the patched version passes all 17 checks. Cases cover timed and indefinite sleep, expiry, paralysis, replacement by paralysis, explicit thaw, resisted sleep, duration caps, and inactive stale markers. The actual old/new monster declarations have identical size and checked member offsets on this macOS x86-64 build. A raw-structure round trip preserves the marker. All five modified C translation units plus save, restore, and serialization translation units pass clang syntax checking. The exported patch applies to the installed build's source.

Legacy saves cannot reveal whether an already-frozen monster was asleep or paralyzed, because the old format never recorded that distinction. Those existing intervals retain the old behavior; sleep applied after the patch gets the new protection. No attempt is made to guess the cause.

Full-game integration on macOS x86-64 now passes six sleep scenarios: a timed sleeper resists a light flash; a paralyzed monster is blinded; protection survives a full save/reload and a level round trip; both persistence cases also pass with elapsed turns. The saved sleeper resumed with 97 turns remaining; after three local turns and ten turns on another level, the returning sleeper had 87 turns remaining and still resisted the flash. Tests used wizard-only fixture hooks calling the real sleep and flash routines, real game turns, and normal save/restore and level-teleport commands. External save compression was blocked by the sandbox, so save/reload was verified with NetHack's retained uncompressed save. Bones and other platform ABIs remain untested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant