Files
Commonwealth-Online-Public/docs/pause-menu-vanilla-settings-plan.md
T
andrew 8acdb4f555 Extract reusable settings UI
Move the Commonwealth Online settings screen into a shared module with its own data, styles, and controller API. The pause menu now mounts the reusable component, keeps the prompt bar behavior consistent, and animates the panel open/close with a subtler transition.
2026-07-05 21:30:52 +12:00

11 KiB
Raw Blame History

Pause Menu: Vanilla Game Settings Integration Plan

Status: Planned (not implemented)
Date: 2026-07-04
Related prototype: ui/views/CommonwealthOnline/pause-menu/
Related inject pattern: plugin/src/F4TMainMenuInject.cpp

This document records the agreed approach for opening Fallout 4s built-in settings from the Commonwealth Online custom pause UI, without showing QUICKSAVE, LOAD, QUIT, and the rest of the vanilla pause list.


Goal

From the custom pause menu SETTINGS → GAME SETTINGS → OPEN FALLOUT 4 SETTINGS:

  1. Show only the vanilla settings panel (right column: Audio, Display, etc.).
  2. Hide the entire left pause column (QUICKSAVE, SAVE, LOAD, INSTALLED, MODS, HELP, SETTINGS, QUIT).
  3. When the player presses B / Escape and backs out of settings, restore the custom PrismaUI pause menu.

Multiplayer options stay in the custom HTML MULTIPLAYER SETTINGS tab. This plan is only for the full vanilla game settings UI.


Why not simpler approaches

PrismaUI always on top

PrismaUI_F4 composites HTML in the D3D11 Present hook, after the full game frame (including all Scaleform menus). While a PrismaUI view is shown, it sits above PauseMenu and any settings UI.

So you cannot truly interleave:

PauseMenu (left list) < custom UI < Settings panel

You only get:

all Scaleform < PrismaUI   (while the view is visible)

Settings is not a standalone menu

In Fallout 4, settings is the pause menu with SETTINGS selected: left list + right settings panel in the same StartMenu / PauseMenu UI. Hiding PrismaUI and “opening settings” alone still shows QUICKSAVE, LOAD, QUIT, etc.

Render-to-texture inside SWF

Possible in principle (custom IMenu, Ultralight → texture → Scaleform image) so custom UI participates in the menu stack. That is a large research project. Editing MainMenu / PauseMenu ActionScript is fragile (crashes already seen on main menu AS changes), which is why Multiplayer uses list inject + PrismaUI instead.

Decision: Do not use RTT/SWF hosting for this feature. Use runtime GFx + menu lifecycle, same class of work as main-menu inject.


Chosen approach

Treat vanilla settings as a modal sub-flow owned by C++:

CustomPause  →  VanillaSettingsOnly  →  CustomPause
Phase What the player sees PrismaUI PauseMenu
Custom pause CO map / left nav / settings tabs Shown + focused Closed, or open but fully covered / unused
Vanilla settings only Right-hand settings panel only Hidden + unfocused Open; left column hidden; navigated into settings
Back from settings Custom pause again Shown + focused Closed or reset; left column restored if needed

This is the opposite of Multiplayer inject: remove / hide pause rows and chrome instead of inserting a row.


Technical background

Shared StartMenu UI

  • RE::PauseMenu and RE::MainMenu both inherit RE::StartMenuBase.

  • Title and pause share the same list pattern the inject already uses:

    • MainPanel_mc.List_mc
    • MainPanel_mc.List_mc.entryList
  • Pause mode is detectable via the movie variable PauseMode (main-menu inject skips inject when PauseMode is true).

  • StartMenuBase exposes useful targets, including mainPanel, settingsCategoryList, and settingsList.

Existing inject pattern to mirror

F4TMainMenuInject.cpp:

  • Resolves entryList on the live GFx movie.
  • Inserts a row (text + logical index).
  • Calls listWidget.Invoke("InvalidateData").
  • Hooks InitMainList so the list is re-applied after the game rebuilds it.
  • Opens the browser by handling item press / returning to MAIN_STATE.

Pause settings work should follow the same style: runtime GFx only, no SWF decompile, scoped to a dedicated flow flag.


Implementation plan

1. Bridge from HTML

Prototype already stubs:

  • window.CommonwealthOnlineUI.openGameSettings

Wire this to F4SE (same pattern as server browser interop): JS invoke → plugin enters VanillaSettingsOnly.

2. Enter VanillaSettingsOnly

On openGameSettings:

  1. Set a plugin flag, e.g. g_vanillaSettingsFromCustomPause = true.
  2. Ensure PauseMenu is open (open it if the custom pause flow did not already leave it open for pause state).
  3. Resolve pause-mode MainPanel_mc.List_mc.entryList.
  4. Hide the entire left column via GFx SetVisible(false) on MainPanel_mc / list (preferred for “only settings visible”). Optionally also strip entryList to only SETTINGS as a safety net so nothing in the left list remains activatable if visibility fails.
  5. Open settings programmatically:
    • Discover SETTINGS logical index in pause mode by logging entryList while paused (indices differ from title screen; do not reuse kLoadIndex / kMultiplayerIndex).
    • Select SETTINGS and invoke the same open path the list uses (mirror OpenMultiplayerMenu / item-press handling), or drive currentState / settings panel if a stable GFx path is found.
  6. Hide and unfocus the PrismaUI pause view so input goes to vanilla settings.

Player should see only the settings panel (and game blur / pause chrome that is not the left list).

3. Stay in settings

While g_vanillaSettingsFromCustomPause:

  • If InitMainList (or equivalent) rebuilds the pause list, re-hide the left column (and re-strip if stripping).
  • Do not run title-screen Multiplayer inject on this menu.
  • Do not show the custom pause UI.

4. Exit back to custom pause

Detect leave-settings / back:

  • PauseMenu currentState returns from settings toward main pause list, and/or
  • Settings panel closes, and/or
  • B / Escape after settings (prefer detecting state, not only the key).

On exit:

  1. Restore left column visibility (and full entryList if stripped).
  2. Close PauseMenu if it was only opened for this flow, or hide it immediately so the player never sees QUICKSAVE / LOAD / QUIT.
  3. Clear g_vanillaSettingsFromCustomPause.
  4. Show + focus the PrismaUI pause view (settings tab / game settings pane as appropriate).
  5. Ensure Escape handling returns to custom UI behavior (JS keydown / plugin focus), same idea as the server browser.

Important: Vanilla often backs from settings to the pause list, then B again closes pause. For this flow, the first back from settings should go straight to custom UI, with no flash of the full pause list. Hide/close PauseMenu as soon as settings is left.

5. Abort / edge cases

  • Player force-closes menus, loads, or quits: clear the flag, restore list visibility, do not leave PauseMenu permanently stripped.
  • Custom pause closed while settings open: still clear flag and restore vanilla pause to a normal state.
  • Resolution / safe area: left-column hide is clip-based, so it should not need a transparent “hole” rect (unlike a PrismaUI mask approach).

State machine (summary)

                    openGameSettings()
  [CustomPause] ──────────────────────────► [VanillaSettingsOnly]
       ▲                                            │
       │         settings back / Escape             │
       └────────────────────────────────────────────┘
              restore list, close/hide PauseMenu,
              Show+Focus PrismaUI pause view

Alternatives considered (not chosen for v1)

Approach Why not for v1
Transparent PrismaUI hole over settings panel Possible, but alignment and input focus are harder; left list still exists underneath
Hide PrismaUI only (full pause menu) Shows QUICKSAVE / LOAD / QUIT — fails the goal
Custom IMenu + Ultralight render-to-texture Correct for true stack interleaving; too large for this feature
Patch PauseMenu.swf ActionScript Crash risk; project already avoided this on main menu
Reimplement all FO4 settings in HTML Huge scope; vanilla settings already work

Discovery checklist (before coding)

Run in-game with F4SE logging while PauseMenu is open:

  1. Confirm PauseMode == true on the pause movie.
  2. Dump MainPanel_mc.List_mc.entryList entries (text, index) and note SETTINGS index.
  3. Confirm SetVisible(false) on the left panel clip hides the whole column without breaking the settings panel.
  4. Find a reliable way to open settings (item press invoke vs currentState / menuObj members).
  5. Find a reliable “left settings” signal for returning to custom UI.
  6. Confirm B / Escape order: settings → (must not show list) → custom UI.

Suggested implementation order

  1. Pause menu PrismaUI view wired like the server browser (show/hide/focus), without vanilla settings yet.
  2. openGameSettings bridge → open PauseMenu, hide PrismaUI (accept full pause list temporarily).
  3. Hide left column + auto-open settings.
  4. Exit detection → restore custom pause with no list flash.
  5. InitMainList re-apply while flag is set.
  6. Polish: abort paths, logging, controller + keyboard.

Files likely involved

Area Path
HTML / bridge stub ui/views/CommonwealthOnline/pause-menu/script.js
Reusable settings UI ui/views/CommonwealthOnline/settings/ (CO_Settings.create)
Pause PrismaUI (new, similar to browser) plugin/src/F4TPrismaUI.cpp (or dedicated pause module)
Pause list hide / settings open (new) e.g. plugin/src/F4TPauseMenuSettings.cpp — mirror F4TMainMenuInject.cpp
Types RE::PauseMenu, RE::StartMenuBase in CommonLibF4
Prototype UI copy ui/views/CommonwealthOnline/pause-menu/ (embeds ../settings/)

Success criteria

  • From custom pause, player can open full vanilla game settings.
  • Left pause column never visible during that flow.
  • No QUICKSAVE / LOAD / QUIT (or other pause actions) reachable during that flow.
  • B / Escape from settings returns to custom pause, not a bare vanilla pause list.
  • Normal title-screen Multiplayer inject unchanged.
  • No permanent PauseMenu SWF edits.

Manual test checklist (when implemented)

  • Server / game running; custom pause opens in-game via PrismaUI.
  • SETTINGS → GAME SETTINGS → OPEN FALLOUT 4 SETTINGS.
  • Only settings panel visible; left column absent.
  • Change a setting (e.g. audio slider); confirm it applies.
  • B / Escape returns to custom pause (settings tab), no flash of vanilla list.
  • Open settings again; still correct.
  • Quit flow / reload; pause menu not permanently broken if opened without custom UI (if vanilla pause remains reachable at all).
  • Controller and keyboard both work in vanilla settings and after return.

References

  • Main menu inject (add row): plugin/src/F4TMainMenuInject.cpp
  • PrismaUI view lifecycle: ThirdParty/framework-F4-Conversion/docs/view-lifecycle.md
  • PrismaUI architecture (Present overlay): ThirdParty/framework-F4-Conversion/README.md
  • Pause menu prototype settings UI: ui/views/CommonwealthOnline/pause-menu/
  • Menu flags / depth (Scaleform stack): RE::UI_MENU_FLAGS, RE::UI_DEPTH_PRIORITY