Repository navigation
fix(cheats): Teleport and Switch age no longer leave the minimap hidden for good (#2217) - #2402
Conversation
…en for good (NotRexed#2217) An area that hides the minimap while Link is in it sets the game's MapIconDeactivateByAreaActor flag, which is saved, and shows the minimap again as he walks out. The Debug tab's map change took him out without that: the flag stayed set, and no area had a minimap from then on, in that save and every save made from it. oot_teleport now clears the flag once the game's event manager takes its request (Switch age goes through it too), and says so in the log. A teleport that is refused, or that the event manager does not take, leaves it: Link is still in the area. A save the flag is already stuck in is mended by its next teleport.
hckrman101
left a comment
There was a problem hiding this comment.
Nice find and a tidy fix. I read the diff against the full oot_teleport:
- The clear sits after
*c = saved, so the GameData lookups run with the caller's registers, and it only fires whenok && result, so a refused or declined request (event playing, map loading, request in progress, event manager answering 0) leaves the flag alone, as the body says. Switch age goes throughoot_teleport, so it is covered too. oot_gamedata_set_boolalready exists (used by the age toggle); the new forward declaration is all that was needed. The flag hash is the same kind of name hash as theSequence_IsChildhood/IsLoadingBancones already in the file, and the post only names the flag the issue already named. No game files or game-derived data are added: the test's stand-in game is built from addresses and hashes the source already has.- The test cuts the real
oot_teleportout ofhost/hos_main.cand covers the sent, no-op, refused, ignored and answer-0 cases. It prints SKIP on Windows/macOS, and no game is needed in CI.
Not verified by me: I did not build or run it. The in-game check was with the flag set by hand, as the PR says, so a real flag-hiding area and the Linux run are still untested; that is fine for a fix this small.
Separate from #2371 (pictures on the Teleport page), which touches the page and not this function. CI was still running when I looked; the Windows build passed.
Joey-Magnus
left a comment
There was a problem hiding this comment.
The red checks aren't this PR's. Windows part 1 is test_native_astc_texture_count.py, which #2397 fixed on main after this run. The appimage failure was three Launchpad PPA timeouts. The fix only runs when the request was taken (ok && result, after *c = saved), and the test cuts the real oot_teleport from the source. A re-run or branch update should go green.
**What this is for:** keep the minimap restored after saving and restarting following a Debug teleport (#2588). **What is measured:** Linux / RTX 4090, main `09aac439` versus `4b2388fc`: the live flag, the game's own saved flag, and inspected pictures before teleport, after teleport, and after a fresh process restart. **Not measured:** frame rates, Windows/AMD/Steam Deck, the reporter's save, or the exact Lake Hylia route that originally set the flag. ## Problem This completes @jarynclouatre's approach in #2402, based on @Zarigis's flag diagnosis in #2217. The existing accepted-teleport guard is right; the remaining problem is how the flag is written. Two separate results on main: 1. **The live repair works.** An accepted teleport clears `MapIconDeactivateByAreaActor`, and the minimap appears again. The log says `the area left had hidden the minimap ... shown again`. 2. **That repair is not saved.** System → Save writes a fresh save with the flag still set. Loading that save in a new process hides the minimap again. The existing helper writes only the Bool entry's live byte. The engine also keeps a saved copy and update bookkeeping. The normal Bool setter at `0x806a2b20` takes `(manager, value, hash)` and queues the change through that machinery. The queue consumer updates the live and saved values, or marks a pending saved update; a direct byte write bypasses it. Sources: `host/hos_main.c` on main, the local binding trace, and the save/reload comparison below; no generated game code is included. ## What changed - `host/hos_main.c`: after the same accepted-request check, call the engine's Bool setter for the existing minimap-suppression hash instead of the live-only helper. Preserve the caller's registers and log `restore queued`, matching the deferred operation. - `tests/test_teleport_minimap_flag.py`: keep the existing request/refusal coverage and give the fixture separate live and saved state, an engine-update step and a reload. Also check setter arguments, register restoration, and an unavailable setter binding. `--source` allows the same test to exercise main's implementation. - `readme.md`, the debug tools paragraph: how a save that already lost its minimap is mended. The destination/event path, interface settings, and refusal behavior are unchanged. A destination can still hide its own minimap. The age switch's immediate live update remains unchanged because the destination choice immediately reads the new age; the minimap repair in its shared teleport path uses the engine setter. Sources: `host/hos_main.c:1001-1015,1030-1076` at `4b2388fc`. ## How to recover an affected save with this change Load it, use **Debug → Teleport → Hyrule Field**, wait for gameplay to resume, then **System → Save**. The native save and fresh restart in the fixed runs below retain the minimap. Older save slots are not rewritten; repeat the recovery if loading another affected slot. ## How it was tested The runs below were made on `4b2388fc` over main `09aac439`. The branch has since been rebased onto main `de866017` as `03a63ea3` (with the recovery note moved from the developer notes to the readme's debug tools paragraph); the regression test was run again there with GCC and Clang (passes) and against main's `host/hos_main.c` (fails with both). Arch Linux, kernel 7.2.15-1-omarchy, RTX 4090, NVIDIA 615.78.08; headless, 2048×1152 output, 1280×720 captures, frame generation off. Main `09aac43907a90ae62c8ccd98c77b471d2b94eac2`; fix `4b2388fc77f1f5eb078531ab51171294a0007b28`. Runs on 11 November 2026, local UTC−06:00. Prepared caches were reused and shader precompilation disabled for these runs. Sources: both build logs and the runs' `hookshot_log.txt` / recorded environments. ### Game-free regression Both GCC 16.2.1 and Clang 23.1.3: | Source passed to the same updated fixture | Result | |---|---| | Main `09aac439` | **Fails:** `a native save after teleport kept the minimap hidden`; `the minimap disappeared again after a save and reload` | | Fix `4b2388fc` | **Passes:** `accepted teleport repairs live and saved minimap state; refusals and missing bindings leave it alone` | The fixture is Linux-only, as before; it needs no game files. Source: `tests/test_teleport_minimap_flag.py` and the captured GCC/Clang regression output. Whole `tests/run_all.py`, once with `CXX=g++`: **453 passed, 3 failed, 20 skipped**, including a passing minimap test. The failures are outside the changed implementation: - `nx2/test_depth_sample_types.py` and `nx2/test_unsigned_image_types.py`: their C++20 builds reject the unchanged assertion header's static variable inside a constexpr function. - `tests/test_env_on.py`: unchanged `HOS_FPS_LOG` in `host/oot_high_fps.c:16` still uses set-means-on behavior. (Main's failure at that base; since fixed by #2575.) Those failures were retained, not patched or rerun here; no new full-main baseline was run. Source: the full-suite output, ending with `453 passed, 3 failed, 20 skipped` and `No coredumps found.` ### In the game A normal main run from Hyrule Field through the Lake Hylia debug destination and then Lost Woods kept its minimap. To test an already affected save reproducibly, a copy of a local test save had only the previously reported suppression flag set. **This is controlled reproduction of the persistent-flag case, not reproduction of the original area trigger.** Sources: inspected normal-route captures and the asserted one-byte save preparation. | Run / start | Build | Observed result | Native saved flag | |---|---|---|---| | Normal route / 21:00:43 | main | Minimap visible before and after both debug teleports | 0 | | Affected-state recovery / 21:17:22 | main | Hidden flag 1 → live 0; minimap returns; System → Save still writes hidden | **1** | | Reload and repair / 21:49:25 | fix | Main's native save reloads hidden; accepted teleport changes live and saved values to 0; native Save | **0** | | Fresh restart / 22:01:55 | fix | Continue from the repaired native save; minimap remains visible, live/saved values both 0 | **0** | Sources: per-run logs, read-only flag samples, native saved-file inspection, and the corresponding inspected pictures. All four runs ended normally, with no fatal/crash/device-lost, Xid/cache-operation or NVRM lines and no core dumps. They were shared runs: **no performance claim**. Start → end load averages (1/5/15 minute) were respectively `31.34/14.47/12.45 → 10.10/13.48/12.68`, `10.15/9.55/10.30 → 7.76/9.05/10.09`, `66.19/28.82/22.11 → 6.80/18.64/19.76`, and `7.62/9.02/14.16 → 7.62/8.84/13.88` (recorded run summaries). The fixed run's log contains: ```text cheats: teleport: the area left had hidden the minimap (MapIconDeactivateByAreaActor): restore queued ``` One scripting mistake is retained in the evidence: the first-use map screen was still open, so one teleport request timed out without being taken. After inspecting and closing the menu, the same process completed the repair and native save. Opening and closing the actual full map left the stuck flag at 1; it was not a recovery shortcut in this test. Sources: the request replies, map-page captures, and read-only flag samples. ### Pictures and captions Minimap before teleport, after arrival at Lake Hylia, and after teleporting to Lost Woods.  Main's temporary restoration; loss after loading its native save; fixed restoration; minimap still present after native Save and a fresh restart.  Every frame in both labelled comparisons was inspected. ## Not tested The reporter's exact save/route, a naturally entered map-hiding area, the v2.1.0 binary itself, a 90-fps cap, Windows, AMD, Steam Deck, physical controllers, and Switch age as a runtime action. The save was synthetic only at the initial stuck-flag setup; all subsequent saves in the comparison were made by the game. ## Risks The binding is for the already supported game build, like the adjacent teleport bindings. The change is queued rather than synchronously forcing the live byte; the game owns its application and saved-copy bookkeeping. A missing binding leaves suppression unchanged instead of pretending to repair it. No save file is edited by Hookshot's host code. ## Overlaps Refs #2402 and #2217. The overlap check reported clean merges for its code-file overlaps. I read #2371's host hunk (destination-picture capture ticking) and #2566's host/docs hunks (output/aspect handling); neither modifies this repair block. Other PRs sharing these large files were not functionally reviewed here. Refs #2588 (closed as fixed by #2402; this completes that fix for the saved flag. The exact route that first set the flag remains unconfirmed). Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <noreply@openai.com> --------- Co-authored-by: GPT-6 Astra <noreply@openai.com> Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
The Debug tab's Teleport and Switch age can leave a save with no minimap in any area, for good (#2217).
Why
An area that hides the minimap while Link is in it sets the game's
MapIconDeactivateByAreaActorflag and shows the minimap again as he walks out. The flag is saved. The cheats' map change (oot_teleport, which Switch age goes through too) takes him out of the area without that, so the flag stays set: no minimap anywhere from then on, in that save and every save made from it. Zarigis found the flag and confirmed it with a save editor in the issue.The change
oot_teleportclears the flag once the game's event manager takes its request, and logs a line when it was set:A teleport that is refused (an event is playing, a map is loading, a request from a moment ago is still in progress), or that the event manager answers with 0 (it did not take it, and the map does not change), leaves the flag alone: Link is still in the area. A save the flag is already stuck in is mended by its next teleport.
Tested
tests/test_teleport_minimap_flag.py(new, Linux) buildsoot_teleportand what it calls fromhost/hos_main.cas it is, over a stand-in for the game (its flags' table, its event manager, the five game functions the teleport calls): a teleport that is sent clears the flag and logs the line; with the flag clear nothing is written or said; a refused or ignored teleport, and one the event manager answers with 0, leaves it. Onmainit fails with "a teleport out of an area that hid the minimap left it hidden".python -I -B tests/run_all.py: nothing new fails.mainthe flag is still 1 after arriving in Gerudo Valley. With this change the log has the line above at the teleport, the flag goes 1 -> 0 as the request is taken (the event manager's answer is 1), and it is 0 after arriving.Not tested: a real area that hides the minimap (the flag was set by hand, in an area that does not set it); Switch age (it goes through the same function); a look at the screen with a save that has lost its minimap; Linux in the game.
Fixes #2217