Skip to content

fix(cheats): Teleport and Switch age no longer leave the minimap hidden for good (#2217) - #2402

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
jarynclouatre:fix/teleport-minimap-flag
Nov 11, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
jarynclouatre:fix/teleport-minimap-flag

Conversation

@jarynclouatre

@jarynclouatre jarynclouatre commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

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 MapIconDeactivateByAreaActor flag 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_teleport clears the flag once the game's event manager takes its request, and logs a line when it was set:

cheats: teleport: the area left had hidden the minimap (MapIconDeactivateByAreaActor): shown again

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) builds oot_teleport and what it calls from host/hos_main.c as 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. On main it 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.
  • In the game (Windows 11, RTX 4070 Ti SUPER, headless, a scripted run): teleport to Hyrule Field, the flag set there by hand (a local debug build that writes it and logs its value; that patch is not part of this change), then teleport to Gerudo Valley. On main the 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

…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 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 when ok && 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 through oot_teleport, so it is covered too.
  • oot_gamedata_set_bool already 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 the Sequence_IsChildhood / IsLoadingBanc ones 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_teleport out of host/hos_main.c and 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 Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hckrman101
hckrman101 merged commit 21e0eec into NotRexed:main Nov 11, 2026
11 of 13 checks passed
hckrman101 pushed a commit that referenced this pull request Nov 12, 2026
**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.

![the minimap before a teleport, at the Lake Hylia destination and after
teleporting to the Lost
Woods](https://gist.githubusercontent.com/mooki000/b86bd3f35969803fb032f71f035844f8/raw/1fb9bb1cdd607879a6d127222106fbf1c146d6a8/hookshot-2588-teleport-minimap-normal.jpg)

Main's temporary restoration; loss after loading its native save; fixed
restoration; minimap still present after native Save and a fresh
restart.

![main restores the minimap only until the save is loaded again; with
the fix it stays after a save and a
restart](https://gist.githubusercontent.com/mooki000/b86bd3f35969803fb032f71f035844f8/raw/1fb9bb1cdd607879a6d127222106fbf1c146d6a8/hookshot-2588-teleport-minimap-save-reload.jpg)

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>
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.

Debug tab: Teleport / Switch age can leave the minimap hidden for good (MapIconDeactivateByAreaActor stays set in the save)

3 participants