feat(gameplay): a Damage taken row (Half to Quadruple) #138

Merged
HumanBean merged 4 commits from simonwjackson/Longshot:feat/damage-taken into main 2026-11-13 18:14:12 +00:00
Contributor

Why

Players who struggle with combat, or want more challenge, can change how much health a hit removes without using invincibility.

Carried over from https://github.com/NotRexed/Hookshot/pull/3005

What changed

  • Debug → Player: live Damage taken row beside Heart protection in vk_menu.cpp / vk_menu.h, through vk_replay.cpp, with Half / Normal / Double / Quadruple. Normal stays the default. The row follows the Debug tools visibility; hiding the tools does not reset or disable the saved damage setting.
  • Health: host/oot_damage.c uses quarter-heart arithmetic; Half rounds up, lethal damage saturates at zero, healing stays unchanged and Heart protection wins.
  • Hook: patch_life() in nx2/oot_patches.py retains both player/life-pointer guards and is idempotent.
  • Tests/docs: test_oot_damage.py covers arithmetic, guards, build gating, idempotence, wiring, default and Debug membership. test_damage_debug_menu.py compiles the real Debug rows, visibility, group order and keyboard traversal. README and its menu-table test list Damage taken under Debug → Player.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
Debug → Player → Damage taken; damage_taken=0/1/2/3 Half / Normal / Double / Quadruple player damage Normal (1) Keep the current default; opt-in accessibility/presentation choice Adjustable combat difficulty Healing or life writes outside the existing guarded site Windows, other GPUs, physical controllers, every damage source, full playthrough

Measurements

main: 316f9319c8 (2026-11-13 UTC)
this PR: feat/damage-taken @ 84fa59a120
Hardware: AMD RX 7900 XT 20 GB, RADV Mesa 26.2.3; Ryzen 5 7600X, 32 GB RAM; NixOS 26.11, Linux 7.2.7. Private headless Sway 1.12/Xwayland; udmabuf enabled; RX_VK_FORCE_MESH_FALLBACK=1; speakers disabled.
Scene: Hyrule Castle Town (Market_Child), stationary at the copied save's wooden door. 1920×1080 drawable and output, 100% render resolution, game's own upscaler, frame generation Off, game frame-rate cap Uncapped, VSync Auto, HDR Off. Effective menu values were read through the control bridge. No overlays, injectors or external shader-cache tools.
Method: One short session, main → PR → main → PR, with fresh copies of the same save. Each process warms at the stationary scene for at least 10 s, then samples for 20 s; the last five vk: N presents, X fps log lines per run are retained. The per-run median and median of the two run medians are below. These are native-present measurements at the settings below, not a claim about other scenes or 1% lows.
How to reproduce: Load a disposable copy of a Castle Town save and stay by the same wooden door, without movement/camera input. Use the settings above, with Damage taken Double on this PR; baseline main has stock behavior. Alternate the two binaries twice, let each settle, and take the last five presents-log readings per run. The additional feature proof below is separate from these stationary FPS samples.

Observation main @ 316f9319 this PR @ 84fa59a1
Run medians, native FPS (first / second) 53.90 / 53.90 53.90 / 52.70
Median of run medians, native FPS 53.90 53.30
Samples retained per run 5 / 5 5 / 5

Damage proof: after the FPS samples, enable Debug flight on the disposable copy, get onto the nearby roof, rise about 19.4 m and land on the same roof. Stored player life was read through the read-only control bridge, in quarter hearts. A hardware breakpoint located the isolated player once and detached before the falls; no memory writes were used.

Setting Life before Life after Removed
Half 16 14 2
Normal 14 11 3
Double 11 5 6
Quadruple 5 0 5

Half rounds Normal's three-quarter-heart hit up to two quarters. Double removes six quarters. Quadruple would remove twelve but saturates at zero with five left; no underflow. Heart protection was Off. Healing, Heart protection precedence and other damage sources were not freshly played; arithmetic/guard fixtures cover them and the external request asks for gameplay coverage.

At the original stationary spot, before any flight, the live Normal-setting sample was 53.10 FPS (last five), against 52.70 FPS for the immediately preceding Double sample on the same PR binary. This is one short observation, not a universal performance claim. The entire session took 460.2 s.

Normal-setting presents logs
[hos Presentation Thread] vk: 27360 presents, 52.9 fps; a frame: 1596 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1409 textures (3215 MB)
[hos Presentation Thread] vk: 27480 presents, 53.1 fps; a frame: 1592 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1408 textures (3205 MB)
[hos Presentation Thread] vk: 27600 presents, 53.2 fps; a frame: 1596 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1407 textures (3196 MB)
[hos Presentation Thread] vk: 27720 presents, 52.6 fps; a frame: 1597 draws (0 skipped), 108 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1410 textures (3224 MB)
[hos Presentation Thread] vk: 27840 presents, 53.2 fps; a frame: 1594 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1410 textures (3224 MB)
Exact presents-log samples (five per run)
Run 1: main
[hos Presentation Thread] vk: 26040 presents, 53.8 fps; a frame: 1588 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 26160 presents, 53.9 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1392 textures (3169 MB)
[hos Presentation Thread] vk: 26280 presents, 54.4 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1409 textures (3326 MB)
[hos Presentation Thread] vk: 26400 presents, 54.1 fps; a frame: 1587 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1431 textures (3443 MB)
[hos Presentation Thread] vk: 26520 presents, 52.8 fps; a frame: 1605 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1431 textures (3418 MB)
Run 2: this PR
[hos Presentation Thread] vk: 26160 presents, 54.3 fps; a frame: 1600 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1390 textures (3183 MB)
[hos Presentation Thread] vk: 26280 presents, 53.8 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1388 textures (3165 MB)
[hos Presentation Thread] vk: 26400 presents, 53.3 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.2 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1437 textures (3387 MB)
[hos Presentation Thread] vk: 26520 presents, 53.9 fps; a frame: 1607 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1436 textures (3464 MB)
[hos Presentation Thread] vk: 26640 presents, 53.9 fps; a frame: 1606 draws (0 skipped), 109 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1418 textures (3298 MB)
Run 3: main
[hos Presentation Thread] vk: 27000 presents, 53.9 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 27120 presents, 53.9 fps; a frame: 1593 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1391 textures (3160 MB)
[hos Presentation Thread] vk: 27240 presents, 54.1 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1414 textures (3286 MB)
[hos Presentation Thread] vk: 27360 presents, 53.5 fps; a frame: 1587 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1422 textures (3447 MB)
[hos Presentation Thread] vk: 27480 presents, 53.0 fps; a frame: 1608 draws (0 skipped), 108 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1424 textures (3353 MB)
Run 4: this PR
[hos Presentation Thread] vk: 26160 presents, 52.6 fps; a frame: 1610 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 26280 presents, 52.8 fps; a frame: 1611 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1391 textures (3160 MB)
[hos Presentation Thread] vk: 26400 presents, 52.8 fps; a frame: 1611 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1395 textures (3197 MB)
[hos Presentation Thread] vk: 26520 presents, 52.7 fps; a frame: 1601 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1416 textures (3391 MB)
[hos Presentation Thread] vk: 26640 presents, 52.2 fps; a frame: 1614 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1420 textures (3316 MB)

The binaries are the previously built Linux release copies at these exact commits, with all tracked source hashes rechecked against their Git trees before this session. Native shader preparation ran for each copy before measurement. No newer or unbuilt binary is substituted.

Review follow-up: head 7c80c7f472ac65c9e8434a896eb5c97493d62f3a adds one commit directly on the measured PR head 84fa59a120cae5c2e80f99a7fa5690988b5464fa, without a rebase or force-push. At HumanBean's request, Damage taken moves to Debug → Player beside Heart protection. Option ID 55, damage_taken, Normal, the damage arithmetic and the hook are unchanged. The FPS and controlled-fall measurements above remain evidence at their original named commits; no new FPS comparison is claimed. The Debug screenshot below was captured at that follow-up head.

Current-main merge: current head decf471cd896cd7c9b63252d55ae74929cc812c0 merges the Longshot main snapshot fetched for this run, dd02c6cca9582f5bb1b992d5c43268bf5d846c3f, into the existing PR branch with a merge commit and a normal push; no history rewrite. The earlier measurements and screenshots remain evidence only at their named commits, not a new performance comparison on this main. The merge was rebuilt and smoke-tested as described below. Main advanced to fdc2840b during validation; a three-way merge check against that newer head is clean, but it is not included in the tested binary.

Testing

Current merge validation (decf471cd896, 2026-11-13): README keeps main’s text and the Damage taken row under Debug → Player, before Developer rows. Focused feature/option-ID/README/menu checks: 5 passed, 0 failed. Full suite on zao, with /tmp/longshot-carry/env.sh and the shared suite lock: 505 passed, 3 failed, 16 skipped in 512 s (linux, 8 at once). All three failures reproduce on untouched main dd02c6cc: test_display_pointer.py (missing win::rx_option fixture definition), test_texture_staging_publication.py (missing log_debug fixture declaration), and the known Nix-path test_linux_tools.py failure. No new failures. git diff --check dd02c6cc passes for the feature diff; main itself contains an unrelated font-license trailing space. No standalone formatter/typechecker is configured; the suite compiles native fixtures and byte-compiles tracked Python. The pinned source was hash-audited before and after a successful Linux build on aka. The new build loaded a copied Castle Town save (Market_Child, state play) in a private headless Sway/Xwayland session on the RX 7900 XT/RADV; its runtime version and live options were read through the control bridge. Audio was disabled, the GPU lock held throughout, and both private units stopped. No owner game, live save or korri* unit was touched. This was a short startup/gameplay smoke check, not a repeat of the earlier feature/performance measurements. Windows and other GPUs were not retested. The older test counts below describe the historical measured/follow-up heads, not this merge.

Tested: fresh Linux main/PR gameplay and feature proof above. All screenshots linked here were opened and checked. Only isolated copies and copied test saves were used; no installed game, live save, owner game session or korri* unit was changed. No GPU hang/device-lost/crash marker appeared in the completed measured runs.

Review follow-up on zao: focused source/menu checks and tests/test_option_ids.py: 18 passed, 0 failed, 0 skipped. This includes every test that extracts the menu table, plus tab membership, Debug group order and keyboard traversal. The new Debug membership assertion failed on the old PR head before the move. git diff --check passes. No standalone formatter/typechecker is configured; native fixtures compile with warnings as errors.

Full suite: source /tmp/longshot-carry/env.sh; flock /tmp/upstream-prs/suite.lock python -I -B tests/run_all.py --jobs 8 --timeout 1200: 517 passed, 1 failed, 20 skipped in 483 s. The only failure is the known tests/test_linux_tools.py fixture: it hardcodes /usr/bin:/bin, omitting Nix's readlink/dirname; the same failure is documented on untouched main. test_release.py passes with OpenSSL supplied. One skip was a stale dav1d header path in the shared environment: rerunning test_dav1d_av1.py with the installed dav1d headers passed (1 passed, 0 failed, 0 skipped). The other 19 skips require Windows, game data or a Vulkan device. An initial run using only main-test-env.sh had the old HOOKSHOT header names and missing OpenSSL; it is superseded by this corrected full run, not counted as validation.

Linux incremental build on aka succeeded in 7 s (only vk_menu.cpp and the host version stamp compiled; all other objects cached). All 2,144 tracked source hashes matched the committed tree before and after the build. In the isolated Castle Town copy on the RX 7900 XT/RADV 26.2.3, NixOS 26.11, opened F1 → Debug and selected Damage taken by keyboard: Normal appears directly after Heart protection under Player. The screenshot was opened and checked. No GPU-hang/device-lost/crash marker appeared; both private test units stopped and the GPU queue was cleared. No new damage/fall or performance measurement was needed for this menu-only follow-up.

Not tested: Windows native builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, HDR, full playthroughs and every feature combination. No Windows machine is available. Any combination specifically requested below remains unverified; a request is not a passing result.

Needs other testers:

  • Windows, llvm-mingw: Build the PR and main with llvm-mingw and a non-ASCII game path. Repeat one controlled fall/hit with Normal, Half, Double and Quadruple; report life before/after in quarter hearts. Normal matches main, Half rounds up, lethal damage saturates, healing is unchanged and Heart protection wins. Report commits and hardware. (T-WTR8).

These requests are open; if the service marks them waiting for a maintainer, they need the testing wanted label before testers can claim them.

Screenshots

this PR @ 7c80c7f4: Debug → Player, Damage taken beside Heart protection, Normal selected

Risk and rollback

Unknown damage sources may bypass the guarded life-write site. Choose Normal to restore stock damage; Heart protection still wins. Reverting the feature restores the old hook; reverting only the menu follow-up returns the row to Gameplay.

Reviewer notes

One feature only. The follow-up changes menu placement, tests and documentation, not damage behavior. Linux evidence does not certify Windows; that gap is assigned to open test requests rather than marked passed. No public MkwVkHost/MkwVkRenderer structure changed, so no ABI-version bump is needed. The shared tested-configurations file is unchanged; the measured runs are folded below. Authored glslang SPIR-V headers, where present, are source artifacts, not game-dump output.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main. Merged the main snapshot fetched for this run, dd02c6cca958 as decf471cd896, as requested by the author; latest main fdc2840b still merges cleanly. No rebase or force-push.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Screenshots for a change that shows on screen.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Linux host build verified; Windows build/runtime verification assigned to the open request above, not claimed passed.
  • A row in the folded tested-configurations table below for each measured run.
  • Flags noted, and the switch reference updated (README).
  • Docs updated.
Tested configurations: fresh Longshot runs, 2026-11-13 UTC
Run Commit/build Hardware and OS Settings and scene Result
1 main @ 316f9319 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock Last five presents readings: 53.8, 53.9, 54.4, 54.1, 52.8; median 53.90 FPS. Same copied save, separate warm-up.
2 this PR @ 84fa59a1 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Damage taken Double Last five presents readings: 54.3, 53.8, 53.3, 53.9, 53.9; median 53.90 FPS. Same copied save, separate warm-up.
3 main @ 316f9319 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock Last five presents readings: 53.9, 53.9, 54.1, 53.5, 53.0; median 53.90 FPS. Same copied save, separate warm-up.
4 this PR @ 84fa59a1 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Damage taken Double Last five presents readings: 52.6, 52.8, 52.8, 52.7, 52.2; median 52.70 FPS. Same copied save, separate warm-up.

| Review follow-up | this PR @ 7c80c7f4 | RX 7900 XT, RADV 26.2.3; NixOS 26.11 / Linux 7.2.7 | Copied Castle Town save; 1920×1080; Debug tools On, Damage taken Normal | Keyboard navigation to Debug → Player; Damage taken directly below Heart protection. Screenshot inspected. Menu placement only; no new FPS sample. |

Additional same-session feature/off-setting observations and their proof are described above.

## Why Players who struggle with combat, or want more challenge, can change how much health a hit removes without using invincibility. Carried over from https://github.com/NotRexed/Hookshot/pull/3005 ## What changed - Debug → Player: live Damage taken row beside Heart protection in `vk_menu.cpp` / `vk_menu.h`, through `vk_replay.cpp`, with Half / Normal / Double / Quadruple. Normal stays the default. The row follows the Debug tools visibility; hiding the tools does not reset or disable the saved damage setting. - Health: `host/oot_damage.c` uses quarter-heart arithmetic; Half rounds up, lethal damage saturates at zero, healing stays unchanged and Heart protection wins. - Hook: `patch_life()` in `nx2/oot_patches.py` retains both player/life-pointer guards and is idempotent. - Tests/docs: `test_oot_damage.py` covers arithmetic, guards, build gating, idempotence, wiring, default and Debug membership. `test_damage_debug_menu.py` compiles the real Debug rows, visibility, group order and keyboard traversal. README and its menu-table test list Damage taken under Debug → Player. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | Debug → Player → Damage taken; `damage_taken=0/1/2/3` | Half / Normal / Double / Quadruple player damage | Normal (1) | Keep the current default; opt-in accessibility/presentation choice | Adjustable combat difficulty | Healing or life writes outside the existing guarded site | Windows, other GPUs, physical controllers, every damage source, full playthrough | ## Measurements main: 316f9319c87b48b8a077a52bb1673440cf071a1e (2026-11-13 UTC) this PR: feat/damage-taken @ 84fa59a120cae5c2e80f99a7fa5690988b5464fa Hardware: AMD RX 7900 XT 20 GB, RADV Mesa 26.2.3; Ryzen 5 7600X, 32 GB RAM; NixOS 26.11, Linux 7.2.7. Private headless Sway 1.12/Xwayland; udmabuf enabled; `RX_VK_FORCE_MESH_FALLBACK=1`; speakers disabled. Scene: Hyrule Castle Town (`Market_Child`), stationary at the copied save's wooden door. 1920×1080 drawable and output, 100% render resolution, game's own upscaler, frame generation Off, game frame-rate cap Uncapped, VSync Auto, HDR Off. Effective menu values were read through the control bridge. No overlays, injectors or external shader-cache tools. Method: One short session, main → PR → main → PR, with fresh copies of the same save. Each process warms at the stationary scene for at least 10 s, then samples for 20 s; the last five `vk: N presents, X fps` log lines per run are retained. The per-run median and median of the two run medians are below. These are native-present measurements at the settings below, not a claim about other scenes or 1% lows. How to reproduce: Load a disposable copy of a Castle Town save and stay by the same wooden door, without movement/camera input. Use the settings above, with Damage taken Double on this PR; baseline main has stock behavior. Alternate the two binaries twice, let each settle, and take the last five presents-log readings per run. The additional feature proof below is separate from these stationary FPS samples. | Observation | main @ 316f9319 | this PR @ 84fa59a1 | |---|---|---| | Run medians, native FPS (first / second) | 53.90 / 53.90 | 53.90 / 52.70 | | Median of run medians, native FPS | 53.90 | 53.30 | | Samples retained per run | 5 / 5 | 5 / 5 | **Damage proof:** after the FPS samples, enable Debug flight on the disposable copy, get onto the nearby roof, rise about 19.4 m and land on the same roof. Stored player life was read through the read-only control bridge, in quarter hearts. A hardware breakpoint located the isolated player once and detached before the falls; no memory writes were used. | Setting | Life before | Life after | Removed | |---|---:|---:|---:| | Half | 16 | 14 | 2 | | Normal | 14 | 11 | 3 | | Double | 11 | 5 | 6 | | Quadruple | 5 | 0 | 5 | Half rounds Normal's three-quarter-heart hit up to two quarters. Double removes six quarters. Quadruple would remove twelve but saturates at zero with five left; no underflow. Heart protection was Off. Healing, Heart protection precedence and other damage sources were not freshly played; arithmetic/guard fixtures cover them and the external request asks for gameplay coverage. At the original stationary spot, before any flight, the live Normal-setting sample was 53.10 FPS (last five), against 52.70 FPS for the immediately preceding Double sample on the same PR binary. This is one short observation, not a universal performance claim. The entire session took 460.2 s. <details><summary>Normal-setting presents logs</summary> ```text [hos Presentation Thread] vk: 27360 presents, 52.9 fps; a frame: 1596 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1409 textures (3215 MB) [hos Presentation Thread] vk: 27480 presents, 53.1 fps; a frame: 1592 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1408 textures (3205 MB) [hos Presentation Thread] vk: 27600 presents, 53.2 fps; a frame: 1596 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1407 textures (3196 MB) [hos Presentation Thread] vk: 27720 presents, 52.6 fps; a frame: 1597 draws (0 skipped), 108 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1410 textures (3224 MB) [hos Presentation Thread] vk: 27840 presents, 53.2 fps; a frame: 1594 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1410 textures (3224 MB) ``` </details> <details> <summary>Exact presents-log samples (five per run)</summary> ```text Run 1: main [hos Presentation Thread] vk: 26040 presents, 53.8 fps; a frame: 1588 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 26160 presents, 53.9 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1392 textures (3169 MB) [hos Presentation Thread] vk: 26280 presents, 54.4 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1409 textures (3326 MB) [hos Presentation Thread] vk: 26400 presents, 54.1 fps; a frame: 1587 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1431 textures (3443 MB) [hos Presentation Thread] vk: 26520 presents, 52.8 fps; a frame: 1605 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (890 ms), 0 programs (10 failed), 1431 textures (3418 MB) Run 2: this PR [hos Presentation Thread] vk: 26160 presents, 54.3 fps; a frame: 1600 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1390 textures (3183 MB) [hos Presentation Thread] vk: 26280 presents, 53.8 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1388 textures (3165 MB) [hos Presentation Thread] vk: 26400 presents, 53.3 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.2 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1437 textures (3387 MB) [hos Presentation Thread] vk: 26520 presents, 53.9 fps; a frame: 1607 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1436 textures (3464 MB) [hos Presentation Thread] vk: 26640 presents, 53.9 fps; a frame: 1606 draws (0 skipped), 109 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (983 ms), 0 programs (10 failed), 1418 textures (3298 MB) Run 3: main [hos Presentation Thread] vk: 27000 presents, 53.9 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 27120 presents, 53.9 fps; a frame: 1593 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1391 textures (3160 MB) [hos Presentation Thread] vk: 27240 presents, 54.1 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1414 textures (3286 MB) [hos Presentation Thread] vk: 27360 presents, 53.5 fps; a frame: 1587 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1422 textures (3447 MB) [hos Presentation Thread] vk: 27480 presents, 53.0 fps; a frame: 1608 draws (0 skipped), 108 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (926 ms), 0 programs (10 failed), 1424 textures (3353 MB) Run 4: this PR [hos Presentation Thread] vk: 26160 presents, 52.6 fps; a frame: 1610 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 26280 presents, 52.8 fps; a frame: 1611 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1391 textures (3160 MB) [hos Presentation Thread] vk: 26400 presents, 52.8 fps; a frame: 1611 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1395 textures (3197 MB) [hos Presentation Thread] vk: 26520 presents, 52.7 fps; a frame: 1601 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1416 textures (3391 MB) [hos Presentation Thread] vk: 26640 presents, 52.2 fps; a frame: 1614 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (943 ms), 0 programs (10 failed), 1420 textures (3316 MB) ``` </details> The binaries are the previously built Linux release copies at these exact commits, with all tracked source hashes rechecked against their Git trees before this session. Native shader preparation ran for each copy before measurement. No newer or unbuilt binary is substituted. **Review follow-up:** head `7c80c7f472ac65c9e8434a896eb5c97493d62f3a` adds one commit directly on the measured PR head `84fa59a120cae5c2e80f99a7fa5690988b5464fa`, without a rebase or force-push. At HumanBean's request, Damage taken moves to Debug → Player beside Heart protection. Option ID 55, `damage_taken`, Normal, the damage arithmetic and the hook are unchanged. The FPS and controlled-fall measurements above remain evidence at their original named commits; no new FPS comparison is claimed. The Debug screenshot below was captured at that follow-up head. **Current-main merge:** current head `decf471cd896cd7c9b63252d55ae74929cc812c0` merges the Longshot main snapshot fetched for this run, `dd02c6cca9582f5bb1b992d5c43268bf5d846c3f`, into the existing PR branch with a merge commit and a normal push; no history rewrite. The earlier measurements and screenshots remain evidence only at their named commits, not a new performance comparison on this main. The merge was rebuilt and smoke-tested as described below. Main advanced to `fdc2840b` during validation; a three-way merge check against that newer head is clean, but it is not included in the tested binary. ## Testing **Current merge validation (`decf471cd896`, 2026-11-13):** README keeps main’s text and the Damage taken row under Debug → Player, before Developer rows. Focused feature/option-ID/README/menu checks: **5 passed, 0 failed**. Full suite on zao, with `/tmp/longshot-carry/env.sh` and the shared suite lock: **505 passed, 3 failed, 16 skipped in 512 s (linux, 8 at once)**. All three failures reproduce on untouched main `dd02c6cc`: `test_display_pointer.py` (missing `win::rx_option` fixture definition), `test_texture_staging_publication.py` (missing `log_debug` fixture declaration), and the known Nix-path `test_linux_tools.py` failure. No new failures. `git diff --check dd02c6cc` passes for the feature diff; main itself contains an unrelated font-license trailing space. No standalone formatter/typechecker is configured; the suite compiles native fixtures and byte-compiles tracked Python. The pinned source was hash-audited before and after a successful Linux build on aka. The new build loaded a copied Castle Town save (`Market_Child`, state `play`) in a private headless Sway/Xwayland session on the RX 7900 XT/RADV; its runtime version and live options were read through the control bridge. Audio was disabled, the GPU lock held throughout, and both private units stopped. No owner game, live save or `korri*` unit was touched. This was a short startup/gameplay smoke check, not a repeat of the earlier feature/performance measurements. Windows and other GPUs were not retested. The older test counts below describe the historical measured/follow-up heads, not this merge. **Tested:** fresh Linux main/PR gameplay and feature proof above. All screenshots linked here were opened and checked. Only isolated copies and copied test saves were used; no installed game, live save, owner game session or `korri*` unit was changed. No GPU hang/device-lost/crash marker appeared in the completed measured runs. Review follow-up on zao: focused source/menu checks and `tests/test_option_ids.py`: **18 passed, 0 failed, 0 skipped**. This includes every test that extracts the menu table, plus tab membership, Debug group order and keyboard traversal. The new Debug membership assertion failed on the old PR head before the move. `git diff --check` passes. No standalone formatter/typechecker is configured; native fixtures compile with warnings as errors. Full suite: `source /tmp/longshot-carry/env.sh; flock /tmp/upstream-prs/suite.lock python -I -B tests/run_all.py --jobs 8 --timeout 1200`: **517 passed, 1 failed, 20 skipped in 483 s**. The only failure is the known `tests/test_linux_tools.py` fixture: it hardcodes `/usr/bin:/bin`, omitting Nix's `readlink`/`dirname`; the same failure is documented on untouched main. `test_release.py` passes with OpenSSL supplied. One skip was a stale dav1d header path in the shared environment: rerunning `test_dav1d_av1.py` with the installed dav1d headers passed (**1 passed, 0 failed, 0 skipped**). The other 19 skips require Windows, game data or a Vulkan device. An initial run using only `main-test-env.sh` had the old HOOKSHOT header names and missing OpenSSL; it is superseded by this corrected full run, not counted as validation. Linux incremental build on aka succeeded in 7 s (only `vk_menu.cpp` and the host version stamp compiled; all other objects cached). All 2,144 tracked source hashes matched the committed tree before and after the build. In the isolated Castle Town copy on the RX 7900 XT/RADV 26.2.3, NixOS 26.11, opened F1 → Debug and selected Damage taken by keyboard: Normal appears directly after Heart protection under Player. The screenshot was opened and checked. No GPU-hang/device-lost/crash marker appeared; both private test units stopped and the GPU queue was cleared. No new damage/fall or performance measurement was needed for this menu-only follow-up. **Not tested:** Windows native builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, HDR, full playthroughs and every feature combination. No Windows machine is available. Any combination specifically requested below remains unverified; a request is not a passing result. **Needs other testers:** - Windows, llvm-mingw: Build the PR and main with llvm-mingw and a non-ASCII game path. Repeat one controlled fall/hit with Normal, Half, Double and Quadruple; report life before/after in quarter hearts. Normal matches main, Half rounds up, lethal damage saturates, healing is unchanged and Heart protection wins. Report commits and hardware. (T-WTR8). These requests are open; if the service marks them waiting for a maintainer, they need the `testing wanted` label before testers can claim them. ## Screenshots ![this PR @ 7c80c7f4: Debug → Player, Damage taken beside Heart protection, Normal selected](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/9fec610e996ad38cb7723928fba74b8f16aed78d/debug-damage-normal.png) ## Risk and rollback Unknown damage sources may bypass the guarded life-write site. Choose Normal to restore stock damage; Heart protection still wins. Reverting the feature restores the old hook; reverting only the menu follow-up returns the row to Gameplay. ## Reviewer notes One feature only. The follow-up changes menu placement, tests and documentation, not damage behavior. Linux evidence does not certify Windows; that gap is assigned to open test requests rather than marked passed. No public MkwVkHost/MkwVkRenderer structure changed, so no ABI-version bump is needed. The shared tested-configurations file is unchanged; the measured runs are folded below. Authored glslang SPIR-V headers, where present, are source artifacts, not game-dump output. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. Merged the main snapshot fetched for this run, `dd02c6cca958` as `decf471cd896`, as requested by the author; latest main `fdc2840b` still merges cleanly. No rebase or force-push. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Screenshots for a change that shows on screen. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Linux host build verified; Windows build/runtime verification assigned to the open request above, not claimed passed. - [x] A row in the folded tested-configurations table below for each measured run. - [x] Flags noted, and the switch reference updated (README). - [x] Docs updated. <details> <summary>Tested configurations: fresh Longshot runs, 2026-11-13 UTC</summary> | Run | Commit/build | Hardware and OS | Settings and scene | Result | |---|---|---|---|---| | 1 | main @ `316f9319` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock | Last five presents readings: 53.8, 53.9, 54.4, 54.1, 52.8; median 53.90 FPS. Same copied save, separate warm-up. | | 2 | this PR @ `84fa59a1` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Damage taken Double | Last five presents readings: 54.3, 53.8, 53.3, 53.9, 53.9; median 53.90 FPS. Same copied save, separate warm-up. | | 3 | main @ `316f9319` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock | Last five presents readings: 53.9, 53.9, 54.1, 53.5, 53.0; median 53.90 FPS. Same copied save, separate warm-up. | | 4 | this PR @ `84fa59a1` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Damage taken Double | Last five presents readings: 52.6, 52.8, 52.8, 52.7, 52.2; median 52.70 FPS. Same copied save, separate warm-up. | | Review follow-up | this PR @ `7c80c7f4` | RX 7900 XT, RADV 26.2.3; NixOS 26.11 / Linux 7.2.7 | Copied Castle Town save; 1920×1080; Debug tools On, Damage taken Normal | Keyboard navigation to Debug → Player; Damage taken directly below Heart protection. Screenshot inspected. Menu placement only; no new FPS sample. | Additional same-session feature/off-setting observations and their proof are described above. </details>
feat(gameplay): a Damage taken row (Half to Quadruple)
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
84fa59a120
Carried over from https://github.com/NotRexed/Hookshot/pull/3005.
Cherry-picked source: dc31434.
Resolve rename/context conflicts without changing feature behaviour.
Keep measured configurations in the PR description, not the shared table.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Owner

This belongs more in the Debug/cheats

This belongs more in the Debug/cheats
Author
Contributor

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase.

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase.
simonwjackson changed title from WIP: feat(gameplay): a Damage taken row (Half to Quadruple) to feat(gameplay): a Damage taken row (Half to Quadruple) 2026-11-13 07:49:54 +00:00
refactor(menu): Damage taken moves to Debug, beside Heart protection
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
7c80c7f472
Place the saved option in the Debug Player group and keep its normal choice handling. Follow the tools visibility without changing the saved damage setting, option ID, INI key, default, arithmetic or hook. Cover tab membership, group ordering and keyboard traversal, and update the menu documentation.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

Moved Damage taken to Debug → Player, beside Heart protection, as requested. Nothing else about the option changed: ID 55, damage_taken, Normal, damage behavior and hook are unchanged. Updated the README/menu tests and added a checked in-game Debug screenshot. Focused checks: 18 passed, including test_option_ids.py. Full suite under the lock: 517 passed, 1 known test_linux_tools.py failure, 20 skipped; the dav1d environment skip then passed separately, leaving 19 platform/game/hardware skips. Linux incremental build passed. One new commit, 7c80c7f472, pushed normally; no rebase or force-push.

Moved Damage taken to Debug → Player, beside Heart protection, as requested. Nothing else about the option changed: ID 55, damage_taken, Normal, damage behavior and hook are unchanged. Updated the README/menu tests and added a checked in-game Debug screenshot. Focused checks: 18 passed, including test_option_ids.py. Full suite under the lock: 517 passed, 1 known test_linux_tools.py failure, 20 skipped; the dav1d environment skip then passed separately, leaving 19 platform/game/hardware skips. Linux incremental build passed. One new commit, 7c80c7f472ac, pushed normally; no rebase or force-push.
Merge current Longshot main into feat/damage-taken
Some checks failed
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
decf471cd8
Keep the Damage taken row in Debug alongside the current main documentation.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

Merged main snapshot dd02c6cca9 with a merge commit and normal push; resolved README by keeping main’s text and Damage taken under Debug → Player. Five focused checks pass; full suite: 505 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).

Merged main snapshot dd02c6cca9582f5bb1b992d5c43268bf5d846c3f with a merge commit and normal push; resolved README by keeping main’s text and Damage taken under Debug → Player. Five focused checks pass; full suite: 505 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).
HumanBean added this to the v3.0.0 milestone 2026-11-13 17:56:12 +00:00
HumanBean left a comment

Thanks for this. It's a small, careful hook. Carried over from https://github.com/NotRexed/Hookshot/pull/3005.

What's good

  • It reuses Heart protection's existing guarded site in f_804bd8c0 instead of adding a new one. The guard keeps both checks: that the actor is the player and that the write goes to its own life pointer. patch_life still returns None when the head or the two CAS anchors are missing, so report() warns "the player heart protection was found 0 times". It is now idempotent too, which the old guard wasn't. Changing nx2/oot_patches.py changes make_game.lift_key, so updating lifts the game again and nobody keeps a stale guard.
  • I read the two guarded writes in a local lift (not quoted here). The first is the damage path: all of the frame's damage is summed, then old minus damage is clamped to 0..max. The second is the heal path, taken only for a positive amount, and next >= old returns it unscaled. A failed CAS loops back and recomputes the new value, so a retry is never scaled twice. The fairy or revive write is a third CAS that isn't guarded, which is correct because it's a gain. The game's own death or fairy check reads life back from memory after the write, so Double and Quadruple saturating at 0 still go through it.
  • The arithmetic is right. Widening to 64 bits avoids overflow, Half rounds a one-unit hit up, Quadruple saturates rather than wrapping, out-of-range settings fall back to Normal, and Heart protection wins. The default is Normal (1), and the option IDs don't collide (55).
  • Nothing reaches the save. The setting lives in longshot_options.ini as damage_taken, and the only thing written to game memory is the player's life, as the game itself does.
  • The tests cut the real guard from patch_life's output and the real debug_rows and debug_step. Nothing was weakened. Main has moved on to fbd42024 since the merged dd02c6cc, but none of its changes touch these hunks, so it merges cleanly.

Must change

  1. The setting stays active after the row is hidden. row_shown hides Damage taken when Debug tools is off, but hos_oot_damage keeps applying the saved value. A player who picks Quadruple and then turns Debug tools off keeps taking four times the damage, and nothing on screen shows it or offers a way back except turning the tools on again. Every other tool on that tab stops when it's hidden: the Debug tools help says "Off puts flight, the free camera and heart protection away and Link back to his own speed". Since this was moved to Debug at the maintainer's request, make it behave like the tools there. The simplest fix is in vk_replay.cpp: if (id == HOS_OPT_DAMAGE_TAKEN) return cheats_on() ? opts.damage_taken : 1;. The saved value is kept, so turning the tools on again restores it. Then update the README sentence ("Its saved setting still applies when Debug tools is off") and the help text, and flip the test_damage_debug_menu.py assertion to "hidden means Normal". The alternative is a row that is always visible, which would put it back under Gameplay, the placement that was declined.

Should change

  1. Half under-counts hits that would be lethal. The game clamps the new life to 0 before the hook sees it, so old - next is the clamped damage. With 2 units left and a 4-unit hit, Half removes 1 and Link survives, where half of the real hit (2) would kill him. Double and Quadruple aren't affected, because they saturate to 0 anyway. At the damage site the unclamped amount is still in a register (x9) at the CAS. You could pass it through for that site, or document that Half can turn some lethal hits into survivals.
  2. Untested paths that need a run before a player relies on this: the Dark Link fight (hos_oot_is_player only checks the vtable and the "Player" resource name, so if Dark Link shares both, his damage is scaled too, and Heart protection would protect him as well); Ganon's final blows; drowning or cold water; heat-room or lava floors. The description already lists "every damage source" as untested. A test request for at least Dark Link would settle the main doubt.
  3. Nit: the line in README.md's command-line section ("...Damage taken is in the Player group beside") runs to 136 columns, and its neighbours wrap at about 120. The "Rebased on current main" box describes a merge of dd02c6cc, not a rebase. That's fine given the squash and the clean merge with fbd42024, but the box should say what was done.

Reviewed by reading only: no build, no tests, no game run.

Thanks for this. It's a small, careful hook. Carried over from https://github.com/NotRexed/Hookshot/pull/3005. **What's good** - It reuses Heart protection's existing guarded site in `f_804bd8c0` instead of adding a new one. The guard keeps both checks: that the actor is the player and that the write goes to its own life pointer. `patch_life` still returns `None` when the head or the two CAS anchors are missing, so `report()` warns "the player heart protection was found 0 times". It is now idempotent too, which the old guard wasn't. Changing `nx2/oot_patches.py` changes `make_game.lift_key`, so updating lifts the game again and nobody keeps a stale guard. - I read the two guarded writes in a local lift (not quoted here). The first is the damage path: all of the frame's damage is summed, then old minus damage is clamped to 0..max. The second is the heal path, taken only for a positive amount, and `next >= old` returns it unscaled. A failed CAS loops back and recomputes the new value, so a retry is never scaled twice. The fairy or revive write is a third CAS that isn't guarded, which is correct because it's a gain. The game's own death or fairy check reads life back from memory after the write, so Double and Quadruple saturating at 0 still go through it. - The arithmetic is right. Widening to 64 bits avoids overflow, Half rounds a one-unit hit up, Quadruple saturates rather than wrapping, out-of-range settings fall back to Normal, and Heart protection wins. The default is Normal (1), and the option IDs don't collide (55). - Nothing reaches the save. The setting lives in `longshot_options.ini` as `damage_taken`, and the only thing written to game memory is the player's life, as the game itself does. - The tests cut the real guard from `patch_life`'s output and the real `debug_rows` and `debug_step`. Nothing was weakened. Main has moved on to `fbd42024` since the merged `dd02c6cc`, but none of its changes touch these hunks, so it merges cleanly. **Must change** 1. **The setting stays active after the row is hidden.** `row_shown` hides Damage taken when Debug tools is off, but `hos_oot_damage` keeps applying the saved value. A player who picks Quadruple and then turns Debug tools off keeps taking four times the damage, and nothing on screen shows it or offers a way back except turning the tools on again. Every other tool on that tab stops when it's hidden: the Debug tools help says "Off puts flight, the free camera and heart protection away and Link back to his own speed". Since this was moved to Debug at the maintainer's request, make it behave like the tools there. The simplest fix is in `vk_replay.cpp`: `if (id == HOS_OPT_DAMAGE_TAKEN) return cheats_on() ? opts.damage_taken : 1;`. The saved value is kept, so turning the tools on again restores it. Then update the README sentence ("Its saved setting still applies when Debug tools is off") and the help text, and flip the `test_damage_debug_menu.py` assertion to "hidden means Normal". The alternative is a row that is always visible, which would put it back under Gameplay, the placement that was declined. **Should change** 2. **Half under-counts hits that would be lethal.** The game clamps the new life to 0 *before* the hook sees it, so `old - next` is the clamped damage. With 2 units left and a 4-unit hit, Half removes 1 and Link survives, where half of the real hit (2) would kill him. Double and Quadruple aren't affected, because they saturate to 0 anyway. At the damage site the unclamped amount is still in a register (x9) at the CAS. You could pass it through for that site, or document that Half can turn some lethal hits into survivals. 3. **Untested paths that need a run before a player relies on this:** the Dark Link fight (`hos_oot_is_player` only checks the vtable and the "Player" resource name, so if Dark Link shares both, his damage is scaled too, and Heart protection would protect him as well); Ganon's final blows; drowning or cold water; heat-room or lava floors. The description already lists "every damage source" as untested. A test request for at least Dark Link would settle the main doubt. 4. Nit: the line in README.md's command-line section ("...**Damage taken** is in the **Player** group beside") runs to 136 columns, and its neighbours wrap at about 120. The "Rebased on current main" box describes a merge of `dd02c6cc`, not a rebase. That's fine given the squash and the clean merge with `fbd42024`, but the box should say what was done. Reviewed by reading only: no build, no tests, no game run.
README.md Outdated
@ -963,2 +964,3 @@
To enable the debug tools, turn **Debug tools** on in the menu's Debug tab (or add `HOS_OOT_CHEATS=1` to
`longshot_env.txt` in the game folder). The tools then show below it on the same tab. The tab's Developer section,
`longshot_env.txt` in the game folder). The tools then show below it on the same tab. **Damage taken** is in the **Player** group beside
**Heart protection**: Half, Normal (the default), Double or Quadruple. Its saved setting still applies when Debug tools
Owner

This sentence documents the hidden-but-active behaviour. If the effect is gated on Debug tools (see vk_replay.cpp), say instead that it applies only while the tools are on. Nit: the line above runs to 136 columns.

This sentence documents the hidden-but-active behaviour. If the effect is gated on Debug tools (see vk_replay.cpp), say instead that it applies only while the tools are on. Nit: the line above runs to 136 columns.
@ -1647,3 +1654,3 @@
bool row_shown(int i) {
const int* v = rows[i].value;
return (rows[i].cheat < 0 || cheats_on()) && (v != &opts.reflex || reflex_available()) &&
return ((rows[i].cheat < 0 && v != &opts.damage_taken) || cheats_on()) && (v != &opts.reflex || reflex_available()) &&
Owner

Hidden with Debug tools off, but the host still applies it (see vk_replay.cpp). Either gate the effect the same way, or keep the row visible.

Hidden with Debug tools off, but the host still applies it (see vk_replay.cpp). Either gate the effect the same way, or keep the row visible.
@ -5056,6 +5056,7 @@ void fill_replay_table(MkwVkRenderer& t) {
if (id == HOS_OPT_CAMERA_SPEED) return opts.camera_speed;
if (id == HOS_OPT_PROMPTS) return opts.prompts;
if (id == HOS_OPT_FOV) return opts.fov;
if (id == HOS_OPT_DAMAGE_TAKEN) return opts.damage_taken;
Owner

This answers the saved value even while Debug tools is off and the row is hidden, so a hidden Quadruple (or Half) keeps applying with nothing on screen. Every other tool on that tab is put away by Debug tools Off. Suggest: if (id == HOS_OPT_DAMAGE_TAKEN) return cheats_on() ? opts.damage_taken : 1;. The saved value survives, so turning the tools back on restores it.

This answers the saved value even while Debug tools is off and the row is hidden, so a hidden Quadruple (or Half) keeps applying with nothing on screen. Every other tool on that tab is put away by Debug tools Off. Suggest: `if (id == HOS_OPT_DAMAGE_TAKEN) return cheats_on() ? opts.damage_taken : 1;`. The saved value survives, so turning the tools back on restores it.
@ -0,0 +10,4 @@
if (next >= old) return next;
if (hos_oot_hearts_enabled()) return old;
const int setting = rx_option(HOS_OPT_DAMAGE_TAKEN, 1);
uint64_t damage = (uint64_t)old - next;
Owner

old - next is the damage after the game clamped life to 0, so on a lethal hit Half halves the clamped amount. For example, with 2 units left and a 4-unit hit, Half removes 1 and Link survives, where half the real hit (2) kills him. The unclamped damage is still in x9 at the damage-site CAS, if you want Half to be exact. Double and Quadruple aren't affected.

`old - next` is the damage *after* the game clamped life to 0, so on a lethal hit Half halves the clamped amount. For example, with 2 units left and a 4-unit hit, Half removes 1 and Link survives, where half the real hit (2) kills him. The unclamped damage is still in x9 at the damage-site CAS, if you want Half to be exact. Double and Quadruple aren't affected.
Owner

I addressed my review in one commit on top of your merge commit (no rebase, so your history is unchanged). It's pushed as b6d695b4 and merges cleanly with current main.

My commit, "Damage taken is Normal while the Debug tools are off":

  • The saved choice no longer applies when the tools are off. menu_damage_taken() (in gpu/vk/vk_menu.cpp, declared in vk_menu.h) returns the setting only while the Debug tools are on, and Normal otherwise. It's gated like Movement speed and the free camera through cheats_on(), so HOS_OOT_CHEATS=1 counts too. vk_replay.cpp asks it for HOS_OPT_DAMAGE_TAKEN. The saved choice is kept and returns when the tools are turned on again.
  • Help and README.
    • The row's help now ends: "Debug tools Off makes it Normal until they are on again."
    • The Debug tools help says Off puts Link back to "his own speed and damage".
    • The README says the same, and its paragraph is rewrapped to 120 columns.
  • Tests. tests/test_oot_damage.py and tests/test_damage_debug_menu.py now assert the new behaviour: Normal while hidden, with the saved value untouched. They don't drop the old check.

Not done: Half turning some lethal hits into survivals. The game clamps life at 0 before the hook sees the write, so Half halves the clamped damage: with 2 units left, a 4-unit hit leaves 1. I couldn't confirm from code that x9 holds the unclamped damage at the first CAS, including on the retry path. A wrong guess there would change every hit at every setting, so it needs someone with a lift and a run in the game. Until then, please note it in the help or the description as a known limit.

No tests or builds were run here.

Still yours:

  • The description:
    • the setting applies only while Debug tools is on;
    • the Half and lethal-hit limit;
    • the "Rebased on current main" box should say main was merged in.
  • A test request for what nobody could check by reading. Dark Link: hos_oot_is_player checks only the vtable and the "Player" name, so he may be scaled and protected too. Also Ganon's final hits, drowning and cold water, heat rooms and lava. Each with Half, Double and Quadruple, then Normal after turning the tools off with Quadruple saved.
  • WIP: keep it WIP until there's a run in the game.
I addressed my review in one commit on top of your merge commit (no rebase, so your history is unchanged). It's pushed as b6d695b4 and merges cleanly with current main. **My commit**, "Damage taken is Normal while the Debug tools are off": - **The saved choice no longer applies when the tools are off.** `menu_damage_taken()` (in `gpu/vk/vk_menu.cpp`, declared in `vk_menu.h`) returns the setting only while the Debug tools are on, and Normal otherwise. It's gated like Movement speed and the free camera through `cheats_on()`, so `HOS_OOT_CHEATS=1` counts too. `vk_replay.cpp` asks it for `HOS_OPT_DAMAGE_TAKEN`. The saved choice is kept and returns when the tools are turned on again. - **Help and README.** - The row's help now ends: "Debug tools Off makes it Normal until they are on again." - The Debug tools help says Off puts Link back to "his own speed and damage". - The README says the same, and its paragraph is rewrapped to 120 columns. - **Tests.** `tests/test_oot_damage.py` and `tests/test_damage_debug_menu.py` now assert the new behaviour: Normal while hidden, with the saved value untouched. They don't drop the old check. **Not done: Half turning some lethal hits into survivals.** The game clamps life at 0 before the hook sees the write, so Half halves the clamped damage: with 2 units left, a 4-unit hit leaves 1. I couldn't confirm from code that x9 holds the unclamped damage at the first CAS, including on the retry path. A wrong guess there would change every hit at every setting, so it needs someone with a lift and a run in the game. Until then, please note it in the help or the description as a known limit. No tests or builds were run here. **Still yours:** - **The description:** - the setting applies only while Debug tools is on; - the Half and lethal-hit limit; - the "Rebased on current main" box should say main was merged in. - **A test request** for what nobody could check by reading. Dark Link: `hos_oot_is_player` checks only the vtable and the "Player" name, so he may be scaled and protected too. Also Ganon's final hits, drowning and cold water, heat rooms and lava. Each with Half, Double and Quadruple, then Normal after turning the tools off with Quadruple saved. - **WIP:** keep it WIP until there's a run in the game.
fix(gameplay): Damage taken is Normal while the Debug tools are off
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
Third-party parts / As their notes say (pull_request) Successful in 5s
b6d695b450
The row is hidden with the Debug tools off, but the host kept applying
the saved choice, so a hidden Quadruple went on scaling every hit. The
host now asks menu_damage_taken(), which answers Normal unless the tools
are on, as Movement speed and the other tools do; the saved choice is
kept for when they are on again. The help text and README say so, the
README paragraph is rewrapped to 120 columns, and the checks assert
that hidden means Normal.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
HumanBean deleted branch feat/damage-taken 2026-11-13 18:14:12 +00:00
Contributor

A heads-up from running the whole suite on main after this merged: tests/test_menu_i18n.py fails there (three runs on main f3fe1763, and it passed on fbd42024 before the merge).

FAIL: test_texts_are_the_menus
AssertionError: False is not true : kpt_BR: "Testing tools: no-clip flight, a free camera, teleport, Link's age, the inventory, the clock, heart protection and movement speed. Off puts flight, the free camera and heart protection away and Link back to his own speed." is no text of vk_menu.cpp (renamed? then translate it again)

The Debug tools help text in gpu/vk/vk_menu.cpp now ends "...Link back to his own speed and damage.", and the pt_BR entry in gpu/vk/menu_i18n.h still has the old English sentence as its key. It needs the new sentence as the key and the Portuguese brought up to date. I am not opening a pull request for it, since the Portuguese wording belongs to whoever translates; until it is fixed I name this test as failing on main in my own pull requests.

A heads-up from running the whole suite on main after this merged: `tests/test_menu_i18n.py` fails there (three runs on main f3fe1763, and it passed on fbd42024 before the merge). ``` FAIL: test_texts_are_the_menus AssertionError: False is not true : kpt_BR: "Testing tools: no-clip flight, a free camera, teleport, Link's age, the inventory, the clock, heart protection and movement speed. Off puts flight, the free camera and heart protection away and Link back to his own speed." is no text of vk_menu.cpp (renamed? then translate it again) ``` The Debug tools help text in `gpu/vk/vk_menu.cpp` now ends "...Link back to his own speed and damage.", and the pt_BR entry in `gpu/vk/menu_i18n.h` still has the old English sentence as its key. It needs the new sentence as the key and the Portuguese brought up to date. I am not opening a pull request for it, since the Portuguese wording belongs to whoever translates; until it is fixed I name this test as failing on main in my own pull requests.
First-time contributor

The merged change leaves a source-key mismatch in the menu translations: on unmodified main f3fe1763, python -I -B tests/run_all.py test_menu_i18n.py fails MenuI18nTests.test_texts_are_the_menus. The first failure is kpt_BR retaining the previous Testing tools help text as its English lookup key while vk_menu.cpp now uses the extended help text. Please refresh the affected lookup keys/translations together; the test was not weakened. This was independently reproduced on native Windows/Python 3.14.7 while checking unrelated build-tool work.

The merged change leaves a source-key mismatch in the menu translations: on unmodified main f3fe1763, python -I -B tests/run_all.py test_menu_i18n.py fails MenuI18nTests.test_texts_are_the_menus. The first failure is kpt_BR retaining the previous Testing tools help text as its English lookup key while vk_menu.cpp now uses the extended help text. Please refresh the affected lookup keys/translations together; the test was not weakened. This was independently reproduced on native Windows/Python 3.14.7 while checking unrelated build-tool work.
Author
Contributor

Thanks, monsterlag. The stale-key repair is isolated in #273: all 13 old Debug-tools translations omit the new damage-reset meaning, so the obsolete entries are removed and the complete English fallback stays. The unchanged translation checks now pass; the full suite is queued. The lethal-Half work remains separate in #269.

Thanks, monsterlag. The stale-key repair is isolated in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/273: all 13 old Debug-tools translations omit the new damage-reset meaning, so the obsolete entries are removed and the complete English fallback stays. The unchanged translation checks now pass; the full suite is queued. The lethal-Half work remains separate in #269.
Author
Contributor

Thanks, HumanBean, for fixing the Debug-tools gate in b6d695b4; I retained it unchanged.

The remaining lethal-Half fix is in #269 (head 8c3c77c). The actual lift was checked on aka with first-try and forced-retry CAS fixtures; x9 preserves the full hit. Gameplay now proves 2 units left + a 4-unit hit at Half → 0 and death, with nonlethal Half, Normal and Double unchanged. Saved Quadruple also takes Normal damage with tools Off and restores Quadruple with tools On. The PR has the point-by-point method, public screenshot attachments, wrapped README and tested-configurations rows. Current main was merged, not rebased.

Opened requests: T-ZJJT Dark Link, T-B8VV Ganon, T-53MS drowning/cold water, T-BHQP heat rooms/lava; each asks for Half/Double/Quadruple and Normal after tools Off with Quadruple saved. T-U5C9 covers Windows. They need the testing wanted label. These paths remain untested; #269 stays WIP for the unmet Windows build box and the checker's literal rebase wording. The translation-key repair is separately ready in #273.

Thanks, HumanBean, for fixing the Debug-tools gate in `b6d695b4`; I retained it unchanged. The remaining lethal-Half fix is in #269 (head `8c3c77c`). The actual lift was checked on aka with first-try and forced-retry CAS fixtures; x9 preserves the full hit. Gameplay now proves **2 units left + a 4-unit hit at Half → 0 and death**, with nonlethal Half, Normal and Double unchanged. Saved Quadruple also takes Normal damage with tools Off and restores Quadruple with tools On. The PR has the point-by-point method, public screenshot attachments, wrapped README and tested-configurations rows. Current main was merged, not rebased. Opened requests: **T-ZJJT** Dark Link, **T-B8VV** Ganon, **T-53MS** drowning/cold water, **T-BHQP** heat rooms/lava; each asks for Half/Double/Quadruple and Normal after tools Off with Quadruple saved. **T-U5C9** covers Windows. They need the `testing wanted` label. These paths remain untested; #269 stays WIP for the unmet Windows build box and the checker's literal rebase wording. The translation-key repair is separately ready in #273.
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
Third-party parts / As their notes say (pull_request) Successful in 5s
Some workflows are waiting to be reviewed.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!138
No description provided.