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.
252 lines
11 KiB
Markdown
252 lines
11 KiB
Markdown
# 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 4’s 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:
|
||
|
||
```text
|
||
PauseMenu (left list) < custom UI < Settings panel
|
||
```
|
||
|
||
You only get:
|
||
|
||
```text
|
||
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++:
|
||
|
||
```text
|
||
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)
|
||
|
||
```text
|
||
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`
|