Split the save flow into explicit manual and auto save kinds and return a SaveResult instead of overloading gbValidSaveFile as the outcome of the last save attempt.
Redesign backup handling so the persistent backup slot represents the last manual save, while autosave uses its own temporary restore copy and no longer overwrites the manual backup.
Manual save UI now reports failure separately from preserved-save recovery, and autosave only reports success when the new save actually succeeds.
Narrow the backup save helper to full game saves only.
Replace pfile_write_hero_with_backup(bool writeGameData) with
pfile_write_game_with_backup(), and always write full game data
before validating the resulting archive with ArchiveContainsGame().
This makes the helper's name, behavior, and post-write validation
consistent, and removes an API parameter that made the function look
more general than its actual responsibility.
Also update SaveGame() to use the new helper name and a clearer local
variable.
Fall back to the regular save flow when UNPACKED_SAVES builds have no filesystem support, compile backup-copy restore paths only when filesystem support is available, and keep demo helpers as documented stubs in that configuration.
Rename save backup and restore helpers to use location terminology, pass source and target locations explicitly, and reuse the copy helper for stash backups.
Implement an autosave subsystem and safer save handling, plus related UI and hooks.
- Add autosave state and logic (diablo.cpp): periodic timer, pending queue, priorities (Timer, TownEntry, BossKill, UniquePickup), cooldowns, combat cooldowns, enemy proximity safety checks, and helper APIs (QueueAutoSave, AttemptAutoSave, IsAutoSaveSafe, etc.).
- Integrate autosave triggers: queue on town entry (loadsave), unique item pickup (inv.cpp), boss kills (monster.cpp), and mark combat activity from player actions and hits (player.cpp).
- Add gameplay options to enable autosave and set interval (options.h/.cpp) and display countdown/ready label in the game menu (gamemenu.cpp/gmenu.cpp). Menu text retrieval updated to show remaining seconds or "ready".
- Make SaveGame robust (loadsave.cpp): write hero and stash via new pfile_write_hero_with_backup() and pfile_write_stash_with_backup() that create backups and restore on failure. Add utilities to copy/restore unpacked save directories safely (pfile.cpp) and adjust stash path handling signature.
- Minor fixes and cleanups: restrict mouse-motion handling to KeyboardAndMouse path, small reordering in player sprite width switch, and a few safety/formatting tweaks.
Autosave only runs in single-player and when IsAutoSaveSafe() conditions are met. Backup save logic attempts to preserve the previous save on failure.
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`.
For monsters with the same sprite, load the sprite from the file system only once.
Example:
```
VERBOSE: Loaded monster graphics: falspear\phall 452 KiB x1
VERBOSE: Loaded monster graphics: skelbow\sklbw 618 KiB x1
VERBOSE: Loaded monster graphics: skelsd\sklsr 610 KiB x1
VERBOSE: Loaded monster graphics: goatbow\goatb 832 KiB x1
VERBOSE: Loaded monster graphics: bat\bat 282 KiB x2 <-- here we only load the sprite once
VERBOSE: Loaded monster graphics: rhino\rhino 1306 KiB x1
VERBOSE: Loaded monster graphics: golem\golem 298 KiB x1
VERBOSE: Total monster graphics: 4401 KiB 4684 KiB
```
Here, the bat sprite will be loaded from the MPQ only once.
For the second sprite, we simply clone the first sprite before applying TRNs.
This also reduces the size of `MonsterData` from 88 bytes to 80.
When we migrate monster data to a TSV, the sprite IDs can be generated automatically at load time.
Adds a `ParseInt` function which uses `std::from_chars`.
From `std::from_chars` documentation:
> Unlike other parsing functions in C++ and C libraries,
> std::from_chars is locale-independent, non-allocating, and non-throwing.
Co-authored-by: Andrew James <ephphatha@thelettereph.com>
Done with the following script:
```ruby
Dir["Source/**/*.{h,c,cc,cpp,hpp}"].each do |path|
v = File.read(path)
next if !v.include?("uint32_t") || v.include?("cstdint")
lines = v.lines
line_num = if lines[2].start_with?(" *")
lines.index { |l| l.start_with?(" */") } + 3
else
3
end
lines.insert(line_num, "#include <cstdint>\n")
File.write(path, lines.join(""))
end
```
then fixed-up manually
Removes most `FMT_COMPILE` calls.
`FMT_COMPILE` results in better performance but larger code size.
Removes `FMT_COMPILE` calls for places that are called infrequently,
i.e. not on every frame.
RG-99 binary size reduced by ~4 KiB.
Rather than using the Windows-like `MAX_PATH`, what we really want is 2
things:
1. Maximum path of a file in an MPQ. This is now `MaxMpqPathSize`.
2. Max size for the known monster paths. This is now 64 for sounds and
TRNs, which is more than enough (picked arbitrarily).