Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/architecture/gameplay/patch-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,10 +417,10 @@ WeaponSwitch, TransformGate, NativeAimDelta, etc.) are documented in the
| Use firmware language | `MelonPrimePatchUseFirmwareLanguage.*` | Registry: `OutOfGameFrame` | Adventure-aware; applied in menus; adapter passes `rom.isInAdventure` as 4th arg |
| FPS Camera Lock | `MelonPrimePatchFpsCameraLock.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | Public independent camera-behavior patch; distinct from the `LowLatencyMode` ImmediateSync/MoonLike instruction hook. |
| Show headshot online | `MelonPrimePatchShowHeadshotOnline.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | |
| Show enemy HP meter online | `MelonPrimePatchShowEnemyHpMeterOnline.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | |
| Show enemy target information online | `MelonPrimePatchShowEnemyHpMeterOnline.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | Legacy key/source name says HP meter; current UI exposes the broader target-information path. |
| Disable double-damage multiplier | `MelonPrimePatchDisableDoubleDamageMultiplier.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | Pairs with Damage-Notify-Purple |
| No picking up specific items | `MelonPrimePatchNoPickingUpSpecificItems.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | |
| Touch-screen aim only (battle) | `MelonPrimePatchTouchScreenAimOnly.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | Neutralizes the three in-match bottom-screen HUD hit-tests (Morph Ball, weapon quick slots, weapon menu) with `mov r0,#0` so the whole touch screen stays aim input; double-tap jump and the touch boost gesture are left alone |
| Touch-screen aim only (battle) | `MelonPrimePatchTouchScreenAimOnly.*` | Registry: `BattleRuntime \| ConfigReload` (`RF_OnLeave \| RF_OnStop`) | Requires Stylus Mode plus the parent setting; neutralizes three HUD hit-tests with `mov r0,#0`. Standard transform can temporarily restore/reapply them when `Metroid.Enable.touchScreenAimOnlySuspendForTransform` is enabled. See [Touch-Screen-Aim-Only](../../features/melonprime-settings/touch-screen-aim-only.md). |

---

Expand Down
12 changes: 8 additions & 4 deletions docs/development/hud/custom-hud-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ The always-on scoreboard still follows the regular HUD gameplay-state timing:
it is not tied to the native START/TAB scoreboard, and it is not forced on by
the HP gauge's individual Show setting. Its independent Show setting controls
only whether the scoreboard is drawn during normal gameplay.
The focused [Custom HUD scoreboard](../../features/hud/custom-hud-scoreboard.md)
page documents physical-slot versus result-order ownership and the four color
variants loaded for each hunter.

Current cached data inside `MelonPrimeHudRender.cpp` includes:
- weapon icon images
Expand Down Expand Up @@ -321,7 +324,7 @@ top-screen BG1-3 layers and flash the native visor. Fix (host-side, selective):
| OPT-HRT1 | HUD runtime state is cached by `NDS* + MainRAM + NumFrames + player offset + ROM group`; base/adventure/visible reads are cached eagerly, while ammo, owned weapons, bomb count, match status, rank, and time values are cached lazily only when their HUD elements need them | Repeated 120/240Hz draws of the same emulated frame reuse gameplay HUD state instead of re-reading RAM, while disabled elements do not populate unused cache fields | Per game frame |
| OPT-SCB1 | Scoreboard match metadata (Hunter ID, Hunter Rank/Stars, decoded player name, active roster, TeamIndex, game mode, and team flags) is cached once after match join and keyed by NDS/RAM/ROM group plus match serial; only live metrics and `ResultSlots` are read per emulated frame | Removes repeated static scoreboard RAM reads and name decoding from the per-frame path while preserving live score, standing, and display-order updates | Once per match / dynamic fields per game frame |
| OPT-SCB2 | Scoreboard/rank `QFont` and `QFontMetrics` values are cached by HUD config epoch, base-font generation/size, and HUD scale | Removes scoreboard font-size and metrics reconstruction from the high-refresh per-frame draw path while keeping settings and scale changes immediate | On relevant change |
| OPT-SCB3 | `ScoreboardRenderPlan` retains final localized/elided strings, text advances, cell geometry, colors, icon rectangles, and rank/metric roles. A compact `ScoreboardStructureKey` compares only match serial, mode/roster/order/team structure, visibility, and active slots; live cells use a semantic value key, so time changes are bucketed to the displayed second | Removes per-frame name/string comparison, repeated localization/elision, and full-layout rebuilding. A changed dynamic cell formats and measures once, then updates in place when it fits; overflow falls back to a structural rebuild | Structural change / changed dynamic cells per game frame |
| OPT-SCB3 | `ScoreboardRenderPlan` retains final localized/elided strings, text advances, cell geometry, colors, icon rectangles, hunter color variant, and rank/metric roles. A compact `ScoreboardStructureKey` compares match serial, mode/roster/order/team structure, visibility, result slots, and each physical slot's active state, team, and Hunter ID; live cells use a semantic value key, so time changes are bucketed to the displayed second | Removes per-frame name/string comparison, repeated localization/elision, and full-layout rebuilding while rebuilding when icon identity/variant ownership can change. A changed dynamic cell formats and measures once, then updates in place when it fits; overflow falls back to a structural rebuild | Structural change / changed dynamic cells per game frame |
| OPT-SCB4 | Scoreboard outline paths are stored on each retained text cell. Outline OFF bypasses path creation and lookup entirely; outline ON lazily builds one path per cell and clears it only when the displayed text/font plan changes | Removes the old 35-slot raster-cache machinery and avoids outline work when the feature is disabled while retaining the existing drawText behavior | Cell text/font change / outline-enabled draw |
| OPT-VFR1 | Software, OpenGL, Vulkan, DX12, and Metal presentation paths first probe only NDS identity plus emulated game frame. A new frame renders and commits the already-probed identity with its stamp after rendering; only a same-frame candidate constructs and compares the extended stamp containing config/font/state generations, menu language, output size, transform/origin, renderer generation, HUD toggle, and edit mode | Avoids extended-stamp pre-render validation and duplicate game-frame probing on new emulated frames while retaining exact same-frame image/texture reuse and invalidation. A rendered frame still commits one stamp after rendering for later same-frame validation | Per presentation callback |
| OPT-PERF1 | Developer-only HUD phase probe reports `HudStateRead`, scoreboard plan/raster, other painter, clear, hash, upload preparation, GPU upload, composite, and total-active timing with calls/sum/avg/p50/p95/max plus visual render/reuse, identity-probe, stamp-check/commit, plan rebuild, structure-check, dynamic-cell, time-visual-change, outline-path, hash, and upload counters | Makes 60/120/144/240 Hz high-refresh regressions measurable without per-frame log spam | Aggregate once per second when `MELONPRIME_PERF=1` |
Expand All @@ -332,9 +335,10 @@ The scoreboard keeps the existing cache boundaries: match-static values (Hunter
Stars/rank, license name, active roster, `TeamIndex`, mode, and team flags) are read
after match join, while time/kills/deaths/points/standings and `ResultSlots` remain
live per emulated frame. `ScoreboardRenderPlan` stores the final display strings and
device-independent geometry. `ScoreboardStructureKey` deliberately excludes names,
Hunter ID, Stars, and live metrics; the match serial is the invalidation boundary for
those match-static values. A live value update first compares a semantic value key
device-independent geometry. `ScoreboardStructureKey` excludes names, Stars, and
live metrics, but includes each slot's Hunter ID and active state because either can
change the selected p1-p4 hunter portrait. The match serial remains the invalidation
boundary for the other match-static values. A live value update first compares a semantic value key
(time is represented by visible seconds, including the `MAX` sentinel), then replaces
only the affected rank or metric cell. The changed-cell path calls formatting and
`horizontalAdvance()` once; a width overflow falls back to a full structural plan
Expand Down
106 changes: 86 additions & 20 deletions docs/development/ui/settings-and-edit-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ After schema changes, run `tools/ci/audits/audit-hud-key-parity.ps1` and
| `src/frontend/qt_sdl/InputConfig/MelonPrimeInputConfigCustomHudCode.inc` | Custom HUD TOML export/import tab (include fragment) |
| `src/frontend/qt_sdl/InputConfig/MelonPrimeInputConfigPreview.cpp` | live preview rendering, preview apply flow, snapshot/restore |
| `src/frontend/qt_sdl/InputConfig/MelonPrimeInputConfigInternal.h` | shared UI helpers, presets, color-sync helpers |
| `src/frontend/qt_sdl/MelonPrimeLocalization.h` | localization API (`Tr`, `LocalizeWidgetTree`, ...) — declarations only |
| `src/frontend/qt_sdl/MelonPrimeLocalization.cpp` | English/Japanese translation tables (`kTranslations`, `kObjectTextTranslations`) |
| `src/frontend/qt_sdl/MelonPrimeLocalization.h` | localization API (`Tr`, `LocalizeWidgetTree`, language selection) |
| `src/frontend/qt_sdl/MelonPrimeLocalization/` | language registry, translation catalog, dynamic text, widget localizer, splash text, and per-domain translation includes |

### Tab structure

Expand All @@ -62,37 +62,97 @@ After schema changes, run `tools/ci/audits/audit-hud-key-parity.ps1` and
| Custom HUD Input/Output | `tabCustomHudCode` | TOML export/import of all Custom HUD settings (auto-discovers widgets in `tabCrosshair`) |

### Constructor/setup flow

Current constructor flow in `MelonPrimeInputConfig.cpp` is:
1. `MelonPrime::UiText::SetMenuLanguageMode(...)` + `setupMenuLanguageControl()`
2. `setupKeyBindings()`
3. `setupSensitivityAndToggles()` - calls `buildSettingBindings()` internally (non-HUD load/save binding table)
4. `setupInputMethodSection()`
5. `setupCollapsibleSections()`
6. `setupCustomHudWidgets()` - programmatic creation of all HUD parameter widgets
7. `setupPreviewConnections()`
8. `setupCustomHudCode()`
9. `MelonPrime::UiText::LocalizeWidgetTree(this)`
10. `snapshotVisualConfig()`

1. `MelonPrime::UiText::SetMenuLanguageSelection(...)` and
`setupMenuLanguageControl()`;
2. `setupKeyBindings()`;
3. `setupSensitivityAndToggles()` — this calls `buildSettingBindings()` for
non-HUD load/save ownership;
4. `setupInputMethodSection()`;
5. `setupSettingsOrganization()` — moves existing widgets into their current
task-oriented sections and reorders the Settings page;
6. `setupCollapsibleSections()`;
7. `setupCustomHudWidgets()`;
8. `setupPreviewConnections()`;
9. `setupCustomHudCode()`;
10. `MelonPrime::UiText::LocalizeWidgetTree(this)`;
11. `refreshSettingsPresentation()`;
12. platform-specific video/sync presentation;
13. `snapshotVisualConfig()`;
14. wheel guards and the Settings viewport resize event filter.

This ordering matters because preview wiring assumes widgets are already initialized.

### Localization
MelonPrime settings labels support English and Japanese through the `MelonPrime::UiText` API
(`MelonPrimeLocalization.h`); the translation tables live in `MelonPrimeLocalization.cpp`.

MelonPrime settings labels use the registered-language catalog behind the
`MelonPrime::UiText` API. `MelonPrimeLocalization.cpp` is only the unity entry;
the registry, catalogs, localizer, and domain-specific translation includes
live under `src/frontend/qt_sdl/MelonPrimeLocalization/`.

Current rules:
- OS locale decides the UI language with `QLocale::system().language()`.
- On Japanese OS, the Settings tab shows a `Menu Language` selector (`Metroid.UI.MenuLanguage`, `0=Japanese`, `1=English`), defaulting to Japanese.
- On non-Japanese OS, the selector is hidden and MelonPrime UI text stays English regardless of the saved language value.
- `Metroid.UI.MenuLanguage=-1` selects the system/default language.
- The Menu Language selector is always visible and contains System default plus
every ID returned by `AllSelectableMenuLanguages()`; it is not limited to
English/Japanese or gated by a Japanese OS locale.
- The language row has a minimum width of 260 px and maximum width of 420 px so
long native language names remain usable without forcing the Settings page
horizontally wider.
- English source strings remain the default and are the lookup keys for short text in `MelonPrime::UiText::kTranslations`.
- Long or HTML-rich description labels should use stable object names and Japanese entries in `MelonPrime::UiText::kObjectTextTranslations`.
- Japanese strings are hand-authored; do not use machine-translated text.
- Long or HTML-rich description labels should use stable object names and
explicit entries in the object-text catalog for every registered language.
- Translations are authored entries; do not rely on fallback for a requested
all-language UI change.
- Config keys, TOML keys, object names, and enum/storage values remain English and must not be translated.
- Static Qt Designer text and generated widget text are localized by `MelonPrime::UiText::LocalizeWidgetTree(this)` after all setup is complete.
- Dynamic labels, toggle text, dialog titles, status messages, and in-game overlay text should call `MelonPrime::UiText::Tr(...)` at the point where the text is assigned or drawn.
- The in-game DS-space overlay is tight; prefer concise Japanese labels and update text measurement code when changing drawn sample text.

When adding or renaming visible MelonPrime UI text, update both the English call site and the Japanese entry in `MelonPrimeLocalization.cpp`. For C++-created description labels, set a stable object name before `LocalizeWidgetTree(this)` runs. The main call sites are `MelonPrimeInputConfig.cpp`, `MelonPrimeInputConfigCustomHudCode.inc`, `MelonPrimeHudConfigOnScreenEdit.cpp`, and the edit-mode `.inc` fragments (`Defs`, `Draw`, `Input`).
When adding or renaming visible MelonPrime UI text, update the English call site
and every registered language in the relevant translation include. For
C++-created description labels, set a stable object name before
`LocalizeWidgetTree(this)` runs. The main call sites are
`MelonPrimeInputConfig.cpp`, `MelonPrimeInputConfigCustomHudCode.inc`,
`MelonPrimeHudConfigOnScreenEdit.cpp`, and the edit-mode `.inc` fragments.

### Settings tab organization and responsive presentation

`setupSettingsOrganization()` presents the Settings page in this visible
order, independent of the historical order in the `.ui` XML:

1. Language
2. Sensitivity
3. Input Settings
4. Input Method
5. Gameplay Toggles
6. Game Feature Improvements
7. Disable Features
8. Low HP Warning
9. In-Game Apply
10. In-Game Aspect Ratio
11. Screen Sync
12. Cursor Clip Settings
13. Video Quality
14. Volume
15. License Apply
16. Bug Fixes
17. Developer Only

Touch/stylus controls, Touch-Screen-Aim-Only, top-screen touch, stylus cursor
policy, Morph Ball Boost swipe controls, and Joy2Key compatibility are grouped
under Input Settings. Sensitivity now contains aim tuning only. Moving a widget
does not change its object name, binding, config key, or parent/child state
logic.

`refreshSettingsPresentation()` disables the horizontal scrollbar and wraps
every full checkbox caption with `QTextLayout` when the viewport size or menu
language changes. The unwrapped text remains in a widget property and in
`accessibleName`, so wrapping is presentation-only and localization can safely
replace the full caption. Wheel events over a non-focused combo box/spin box
scroll the containing Settings page instead of accidentally changing the
control value; focused controls retain their normal wheel behaviour.

### Collapsible sections
The settings UI uses toggle buttons and section widgets wired up in `setupCollapsibleSections()`. Toggle state is persisted under `Metroid.UI.Section*` config keys.
Expand Down Expand Up @@ -179,6 +239,8 @@ It currently saves:
Important side effects in `saveConfig()`:
- if `Metroid.Visual.ClipCursorToBottomScreenWhenNotInGame` changed, windows schedule `panel->updateClipIfNeeded()` via `QTimer::singleShot`
- if `Metroid.Visual.InGameTopScreenOnly` changed, windows schedule `onScreenLayoutChanged()` via Qt queued connection
- stylus cursor visibility/confinement/centre-pin settings refresh the active
panel cursor policy through `refreshStylusCursorSettings()`
- `MelonPrime::CustomHud_InvalidateConfigCache(core->HudConfigState())` is called at the end so the per-instance runtime HUD reads updated values on the next frame

HUD settings are saved from both the settings dialog (`saveConfig()`) and the in-game edit mode (`CustomHud_ExitEditMode(hudConfig, true, cfg)` -> `Config::Save()`). Both write the same config keys. When the dialog reopens, `setupCustomHudWidgets()` reads the latest config values, ensuring sync.
Expand All @@ -187,6 +249,10 @@ HUD settings are saved from both the settings dialog (`saveConfig()`) and the in

The **Reset sensitivity values** button resets every bound setting whose widget currently belongs to `sectionSensitivity`. It does not maintain a second list of numeric defaults. Instead, it resolves values from `Config.cpp` through the `Config::Table::GetDefaultInt/GetDefaultBool/GetDefaultDouble/GetDefaultString` accessors. `CheckBoolInverted` rows invert the positive stored default when updating a negative **Disable...** checkbox.

Morph Ball Boost controls no longer belong to `sectionSensitivity`, so this
button intentionally does not reset them. Reset those Input Settings values by
editing them explicitly or through a broader configuration reset.

`Low-Latency Aim Mode` remains outside the generic binding table because its save path uses combo item data and a public/developer-mode gate; the reset handler restores it explicitly through the same compiled integer default.

The handler blocks widget signals while applying defaults and does not call `SetInt`, `SetBool`, `SetDouble`, `Config::Save()`, or runtime notification functions. The new values are committed only by the dialog's existing Save/OK path. Cancelling the dialog therefore preserves the previously saved sensitivity configuration.
Expand Down
2 changes: 2 additions & 0 deletions docs/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ the complete route rather than one isolated behavior.
- [Custom HUD adventure camera scenes](hud/custom-hud-adventure-camera-scene.md)
- [Custom HUD adventure Scan Visor](hud/custom-hud-adventure-scan-visor.md)
- [Custom HUD Enemy Target](hud/custom-hud-enemy-target.md)
- [Custom HUD scoreboard](hud/custom-hud-scoreboard.md)
- [Zoom input methods](input/zoom-input-methods.md)
- [macOS compute renderer restriction](rendering/macos-compute-renderer-restriction.md)
- [Vulkan backend](rendering/vulkan-backend.md)
- [DirectX 12 backend](rendering/dx12-backend.md)
- [High-resolution vertex coordinates](rendering/high-resolution-coordinates.md)

Completed feature/refactor evidence is indexed under
[docs/archive/features/](../archive/features/).
1 change: 1 addition & 0 deletions docs/features/hud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ reference.
- [Adventure camera scenes](custom-hud-adventure-camera-scene.md)
- [Adventure Scan Visor](custom-hud-adventure-scan-visor.md)
- [Enemy Target](custom-hud-enemy-target.md)
- [Scoreboard and per-player hunter colors](custom-hud-scoreboard.md)
- [Helmet spawn flash](custom-hud-helmet-spawn-flash.md)

The focused pages own behavior that needs its own runtime or rendering
Expand Down
2 changes: 1 addition & 1 deletion docs/features/hud/custom-hud-enemy-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Settings: **HUD ENEMY TARGET** section in the Custom HUD dialog, and element **E
the in-game layout editor. Keys are `Metroid.Visual.HudEnemyTarget*`; `HudEnemyTargetShow`
defaults to `true`.

The dialog section opens with a note row pointing at **Show Enemy HP Meter Online**
The dialog section opens with a note row pointing at **Show Enemy Target Information Online**
(`Metroid.GameFeature.ShowEnemyHpMeterOnline`, under GAME FEATURE IMPROVEMENTS), which online
play additionally needs — see [Nintendo WFC](#nintendo-wfc). It is a wrapped `HWType::Label`
row, not a tooltip, so it is readable without hovering. Nothing is disabled when that fix is
Expand Down
Loading
Loading