Recognize legacy hotkeys blobs by their exact payload size so the loader does not misinterpret them as the newer header-based format. Update the legacy scroll regression test to validate selection preservation against engine-backed scroll availability without depending on UI redraw side effects.
Introduce SyncPlayerSpellStateFromSelections and call it in hotkey load paths so queued/executed spell metadata and spellFrom are reset consistently after sanitization.
This centralizes post-load spell-state normalization, removes duplicated per-call-site resync code, and adds writehero regression tests for missing hotkeys data and legacy 4-slot hotkeys format handling.
Add a slot-aware LoadHotkeys overload and use it in player load paths so hero data and hotkeys are always read from the same save number.
This removes the saveNum/gSaveNumber mismatch risk in pfile_read_player_from_save while keeping the existing wrapper for current-player call sites.
Move hotkey loading out of InitPlayer so it runs only after spell sources are rebuilt. Also make LoadHotkeys sanitize the selected spell and hotkeys itself, including when the sidecar hotkeys file is missing, and resync queuedSpell in the player load paths.
Expose ValidatePlayer directly instead of routing load-time validation through a one-off wrapper.
Add a persisted-state regression test that rewrites a Hellfire-origin save under Diablo and verifies invalid spell selections stay cleared after saving and reloading hotkeys.
* Fix broken catacombs wall tile
When tile 15 is followed by tile 1 below, change tile 1 to tile 8
(left corner) to fix a visual glitch in the catacombs.
* Silence fmt catch warnings and guard SaveHelper copy
The previous implementation didn't behave quite like A-* is supposed to.
After trying to figure out what's causing it and giving up,
I've reimplemented it in a straightforward manner.
Now it seems to work a lot better.
Also increases maximum player path length to 100 steps.
We still only store the first 25 steps in the save file for vanilla
compatibility.
1. Moves more assets-related stuff from `init` to `engine/assets`.
2. Removes `SDL_audiolib` dependency from `soundsample.h`.
3. Cleans up some unused/missing includes.
Untangles dependencies by splitting up `engine.{h,cpp}` into 3 files:
1. `primitive_render`
2. `ticks` -- only contains `GetAnimationFrame` for now.
3. `GetWidth2` renamed to `CalculateSpriteTileCenterX` and moved to `levels/dun_tile.hpp`.