v3.5.0: fix NPC schedules, monster health, mine spawn-in-wall, dropped items#18
Open
0Aredjay0 wants to merge 5 commits into
Open
v3.5.0: fix NPC schedules, monster health, mine spawn-in-wall, dropped items#180Aredjay0 wants to merge 5 commits into
0Aredjay0 wants to merge 5 commits into
Conversation
…ropped items - Fix villagers freezing or walking through walls/water after loading a mid-day save. Vanilla precomputes each NPC's daily route from their morning position and replays it without collision checks; after a mid-day reload NPCs weren't where those routes expected. Villagers now get a fresh route to their current schedule destination computed from the tile they actually stand on (using the game's own schedule pathfinding), with the clock held back via ignoreScheduleToday until positions are restored, instead of the previous TryLoadSchedule() call (which reset schedule state) racing the clock advance. - Stop stripping vanilla monsters before a mid-day save. The mod's own cleanMonsters() was deleting every monster from the save file (the game's real save code never touches them); reloading meant any fight in progress reset to full health or vanished, letting players save-scum through combat. Only monster types from other mods' assemblies are still stripped-and-restored in-session, since those aren't covered by the base game's XmlInclude list and would crash the save otherwise. - Fix RestoreMonsters() only clearing its tracking dictionary on one code path, which threw an ArgumentException on a location's second mid-day save of a session; add a null guard for the dictionary and the Forest lookup. - Validate the player's saved tile against the actual location before warping on load, using the game's own recursiveFindOpenTileForCharacter (the same recovery it uses for a stale mail/warp target). Mine and Volcano Dungeon floors regenerate a new random layout every time they're entered and were never part of the save file to begin with, so a saved tile could land inside solid rock in the new layout. - Preserve single-item ground debris (loot, quest items, tools/weapons, unpicked forage) across a mid-day save via a new DebrisData model and GetDebris/RestoreDebris pair in SaveManager, reconstructing dropped items through the game's own ItemRegistry factory on load. Loose resource chunks from a tool swing are out of scope (re-swingable, not an exploit). - Stop overwriting Game1.weatherForTomorrow at save time; it deleted rain forecasts and corrupted weather on festival days. - Block mid-day saves with remote players connected; the vanilla save menu's ready-check barrier can never complete mid-day, which hard-froze multiplayer sessions. Add a save-in-progress guard against re-triggering a mid-day save while one is already running. - Guard OnSaveLoaded/LoadData against farmhands connecting (they'd warp to the host's saved position) and against missing/corrupted save data (would otherwise null-ref on load). - Skip hidden off-map NPCs (Marlon at -42,-42) in saved position data. - Bump ModBuildConfig 4.1.1 -> 4.4.0; fix CI workflow's dotnet-version (5.0.x -> 6.0.x) to match the net6.0 target. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion - GetDebris()/RestoreDebris() iterated Game1.locations directly, which does NOT include building interiors (farmhouse, sheds, coops, barns, cabins) — those live under each Building's own .indoors and are only reachable via Utility.ForEachLocation(includeInteriors: true). Any item dropped indoors was silently invisible to the debris save/restore, which is almost certainly why it looked like dropped items weren't being preserved at all. Also switched from one try/catch wrapping the entire scan (a single bad debris object silently zeroed out everything) to per-item/per-location handling, and added Trace-level [SA] logging of capture/restore counts for the next round of testing. - Skip SaveGameMenu's ~1.5s "has been saved" sparkle-text hold during a mid-day save. The actual file write is done the moment Game1.game1.IsSaving flips back to false; at that point we now set the menu's public `quit` field early (the same externally-settable field vanilla itself uses to close the menu when a client times out), so its own update() closes it on the very next tick instead of waiting out the decorative animation. Doesn't touch the actual save-driving or multiplayer-sync logic at all, just skips the cosmetic wait. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The debris fix so far only covered debris backed by a live .item (regular single-item drops) -- confirmed via decompile that resource piles from a tool swing never get one; InitializeResource/InitializeItem only set the raw itemId string for those, since Debris.collect() treats each chunk as a separate 1-unit pickup regardless of any stack value. This is exactly the case the user hit: chop a tree, save before walking over the wood, reload, wood is gone. - DebrisData gains an IsResourcePile flag. - GetDebris no longer requires a live .item; it also captures resource piles via the raw itemId field, using the remaining chunk count (not a stack value, which collect() would ignore) as the quantity. - RestoreDebris reconstructs a resource pile as that many separate single-item Debris objects (matching the original one-chunk-per-pickup behavior) instead of one item with a big stack, which would have silently discarded everything past the first pickup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the long-standing "characters disappearing / walking on water / schedules resetting" bug family (multiple open reports on the Nexus bug tracker going back to v3.2.7), plus closes a save-scum exploit and a few correctness gaps found while investigating. Root-caused against the actual decompiled game (1.6.15), not guessed — happy to share the specific game-code references for any of these on request.
TryLoadSchedule()+ clock-advance ordering reset/raced their state, sending them beelining through walls and water or leaving them frozen. Villagers now get a fresh, collision-valid path to their current schedule destination computed from wherever they actually are, and NPCs already at their destination resume idle behavior immediately instead of standing blank.cleanMonsters()stripped every monster from the world before the real save ran; the game's own save code never touches monsters, so this was a mod-introduced regression, not a vanilla limitation. It let players save-scum out of any fight. Vanilla monsters now save/reload with their current health; monster types from other mods are still excluded (some aren't safe to serialize).ArgumentException) on a location's second mid-day save in a session —RestoreMonsters()'s tracking dictionary was only cleared on one code path.dotnet-versionfixed to match the net6.0 target,ModBuildConfigbumped to 4.4.0.Full changelog with more detail is in the README diff.
Test plan
All of the above verified in-game this session on SMAPI 4.3.2 / SDV 1.6.15:
🤖 Generated with Claude Code