Skip to content

Port: autosave a dungeon floor as its load event entered it - #123

Merged
Adubbz merged 1 commit into
TheMoonPeople:masterfrom
carrdylan52:pr/autosave-floor-entry
Oct 19, 2026
Merged

Adubbz merged 1 commit into
TheMoonPeople:masterfrom
carrdylan52:pr/autosave-floor-entry

Conversation

@carrdylan52

@carrdylan52 carrdylan52 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #113.

Before

The dungeon autosave was written from the live game on the first frame the player had control on a floor. Loading a dungeon save goes through the floor load event (1010), which builds the floor and runs its arrival events again from the saved flags. In the Channel Pump Room (Underground Channel floor 4), the scene before Linda sets flag 103 and then sets the boss loose, and control only comes back once the fight has started. The autosave therefore held flag 103 with no boss, and loading it put Max in an empty arena where nothing ever happens.

After

A dungeon autosave is the game as the floor's load event entered the floor, before any of the floor's own events. Loading it reruns the floor load event from that captured state, as entering the floor from the floor list does, so the pump room autosave starts Linda's scene and the fight. Every town's Georama layout is kept in the autosave, including towns with more parts than retail's records hold.

Trade-offs:

  • Nothing that happened on the floor after the entry point is in the autosave. That includes the player's position and whatever the floor's events did before control came back. On loading, those events run again from the entry state, and anything they don't redo is lost. Play time is also the play time at floor entry.
  • The entry state is dropped on the floor's first controllable frame, whether it was written or not. If autosave is off at that frame, the floor is not autosaved, even if autosave is turned on again while still on the floor. Writing the entry state then would undo whatever the player had done on the floor since. The next floor is autosaved as usual.
  • A floor reached without the load event's DNG_FLOOR_INIT call is not autosaved. Town autosaves are unchanged and still write the game as it stands.

How

  • DungeonFloorInit is empty in retail. Every dungeon's 1010 calls it through DNG_FLOOR_INIT once the floor is chosen and before the floor's events. Its port override (port/src/dng_event.cpp) calls SaveSlotRecordFloorEntry.
  • SaveSlotRecordFloorEntry (port/src/memcard.cpp) builds a complete SaveDocument right then, with the same BuildDocument that the save screen uses: the retail image, header and metadata, plus an owned copy of each town's layout. The layouts are stored beside the save data and looked up by the address of the live CEditData records. A copy of CSaveData alone would therefore have found no layouts, then written empty towns, and loading the autosave would have wiped the live Georama. dng_tree_flag is cleared at capture time.
  • SaveSlotWriteAutosave(LOOP_DUNGEON) writes that document. It only gives it the next unique counter. It refuses to write if the entry state belongs to a different stage or floor than the game is on. Nothing else read when saving lives outside the document. SaveJsonWrite takes play time, progress, location (map, dungeon, floor) and unlock bits from the image, and the only other global, the floor-list dng_tree_flag, is forced to 0.
  • AutosaveFloor (port/src/mainloop.cpp) now checks for the floor's first controllable frame before it checks the autosave setting. On that frame it writes the entry state if autosave is enabled, then drops it in either case. Entering a game loop also drops it.
  • docs/PC.md describes what the dungeon autosave keeps and what it loses.

Tests

  • DungeonAutosaveKeepsTheStateTheFloorWasEnteredWith: flags and money changed after entry are not in the autosave. A town autosave still writes the live state.
  • DungeonAutosaveNeedsAnEntryStateOfTheCurrentFloor: no write without an entry state, or for another floor, or for the same floor number in another dungeon.
  • DungeonAutosaveRoundTripsTheTownsLayouts (new): two towns with 340 parts each (retail holds 300), plus houses, a placement log and a river grid. The test changes the live layouts after floor entry, writes the dungeon autosave, reads it back and loads it, and checks that every part, house, log entry and grid cell comes back in the live store. A town autosave and a save-screen save of the loaded game keep the same layouts.
  • ArrivalAutosaveWaitsForControlAndEnabledConfigurationThenRunsOnce: the floor is not written after its first controllable frame passes with autosave off. This is checked separately for capture mode, PortAutosave and the game.autosave setting, each turned back on afterwards.
  • The new round-trip test and the reworked arrival test both fail against this PR's first revision, which copied only CSaveData.
  • Full darkchronicle_tests on Windows x64 Release, with EeKernelThreads.RotateInsidePumpWithNothingReadyDoesNotSwitch excluded because it aborts the process on master too: 1840 tests, 1764 passed. Master: 1837 tests, 1762 passed. The 28 failures shared with master are GPU, file-system, audio and environment tests. GeoramaParts.PaintedFencesMatchRetailRecursion failed in one full run on each build and passes when run on its own on both, so it is flaky on master.
  • In game: I used a scratch-only build, not committed, that enables autosave under --jump and loads a save file into the game before jumping. Loading a save with four built towns (64, 42, 61 and 69 parts) and jumping to Underground Channel floor 2 wrote a dungeon autosave whose five towns are identical to the source save's. The PR's first revision, run the same way, wrote five empty towns.

@carrdylan52
carrdylan52 marked this pull request as draft October 19, 2026 00:55
The dungeon autosave was written from the live game on the first frame the
player had control on a floor. Loading it runs the floor load event (1010)
again, which replays the floor's arrival events from the saved flags. In the
Channel Pump Room (Underground Channel floor 4) the scene before Linda sets
flag 103 and then sets the boss loose, and control only returns once the fight
has started. The autosave therefore held flag 103 with no boss, so loading it
dropped Max into an empty arena where nothing ever happens (TheMoonPeople#113).

DungeonFloorInit, empty in retail, is called by every dungeon's floor load
event once the floor is chosen and before any of the floor's own events. Its
port override now records the whole save document as the floor's entry state:
the save data, the header and a copy of every town's Georama layout. The
layouts live beside the save data, found by the address of the live town
records, so a copy of the save data alone would have written and then loaded
empty towns. The dungeon autosave writes the entry state when the player gets
control, taking only a new counter, so loading replays the floor's arrival
scenes as entering it from the floor list does.

The entry state is dropped on the floor's first controllable frame whether or
not it was written. A floor whose first controllable frame passes with
autosave off is therefore not autosaved when autosave is turned on later,
since the entry state would undo what the player had done there since. A
floor reached without DungeonFloorInit is not autosaved; town autosaves are
unchanged.
@carrdylan52
carrdylan52 force-pushed the pr/autosave-floor-entry branch from 90f0ef2 to da61698 Compare October 19, 2026 01:45
@carrdylan52
carrdylan52 marked this pull request as ready for review October 19, 2026 01:45
@Adubbz
Adubbz merged commit e8e018a into TheMoonPeople:master Oct 19, 2026
3 of 7 checks passed
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.

Save game bug

2 participants