From da25c4c507cee680b35f861ee3953ece0e87c06e Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Wed, 9 Sep 2026 01:06:50 -0400 Subject: [PATCH] =?UTF-8?q?feat(D216):=20Game.SkipIntro=20=E2=80=94=20boot?= =?UTF-8?q?=20straight=20to=20the=20file-select=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New s32 portSkipIntro (port/src/video.c, configRegisterInt Game.SkipIntro), read once in src/game/lv.c at LEVELID_TITLE stage load, in the else branch of the existing GE_STARTMENU #ifdef PORT block. When set it performs the same state change every intro stage's own !is_first_time_on_main_menu branch does (front.c): is_first_time_on_main_menu=FALSE; prev_keypresses=TRUE; maybe_is_in_menu=TRUE; menu_update=MENU_FILE_SELECT. So it reuses the game's own post-intro route rather than inventing one; the legal screen + logo attract loop is skipped. GE_STARTMENU still wins when both are set. Default 0 => unchanged. Verified headless: default boot frame 90 = the TWYCROSS classification screen; SkipIntro=1 frame 90 = the SELECT FILE menu (folders/photos/cursor render, no crash across repeated boots). Screenshots eyeballed. findings.md D216 + index; QOL-INVENTORY row marked done. --- docs/dev/QOL-INVENTORY.md | 2 +- docs/dev/findings-index.csv | 1 + docs/dev/findings.md | 1 + port/src/video.c | 5 +++++ src/game/lv.c | 15 +++++++++++++++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/dev/QOL-INVENTORY.md b/docs/dev/QOL-INVENTORY.md index 5b2a5ae4..6fd886bd 100644 --- a/docs/dev/QOL-INVENTORY.md +++ b/docs/dev/QOL-INVENTORY.md @@ -26,7 +26,7 @@ ScreenShakeIntensity. Debug: FrameDump, InputLog. | Feature | PD location | Class | Effort | Notes / priority | |---|---|---|---|---| | **Key rebinding** (config-string binds per controller/action) | `input.c:69,627-675,1548` (`bindStrs`, `inputParseBindString`) | P | M | **High.** Explicit Phase 4 debt ("rebinding UI" — note PD has *no* UI either; config-file driven). Port-only in `port/src/input.c`. | -| **SkipIntro** | `main.c:139` (STAGE_TITLE → STAGE_CITRAINING) | P | S | **High.** GE analogue: stage-override path already exists (`boss.c:385-393`, `g_StageNum = LEVELID_TITLE` at :98); wire a flag + arg. 10-min check on what feeds `tokenFindLevel`. | +| **SkipIntro** _(DONE M-83, D216)_ | `Game.SkipIntro` -> `src/game/lv.c` | P | S | Boots to the SELECT FILE menu, skipping the legal screen + Nintendo/Rare/GoldenEye logo attract loop. Reuses the game's own post-intro route (sets `is_first_time_on_main_menu=FALSE` + `menu_update=MENU_FILE_SELECT`); one `#ifdef PORT` line in the existing GE_STARTMENU block. Verified headless (screenshots). | | **DisplayFPS** (+ interval) | config `Video.DisplayFPS(Interval)` | P | S | Cheap; useful for FPS-plan verification too. | | **Anisotropic filtering** (≤16×), MipmapFilter, TextureFilter2D | `gfx_opengl` texparam path | P | S–M | **High at 4K.** GE's `gfx_opengl.cpp` has mipmap nY but no aniso (`GL_TEXTURE_MAX_ANISOTROPY_EXT`). Port-only. | | Per-pad tuning: per-stick deadzones, stick scale, rumble scale, device index, swap sticks, C-button mapping | `input.c` padsCfg block | P | M | Medium. Matters for real-controller users; PD pattern is a config section per pad. | diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 9db80291..b915c98e 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -53,6 +53,7 @@ D179,FIXED,Packaged build crashes after the logos —,"Diagnosed, not fixed —" D180,PARTIAL,"Native-PC input pass (M-XX QoL run, qol/native-pc-input-menu, port-only).","LANDED, port-only, no #ifdef PORT." D181,PARTIAL,Game.ScreenShakeIntensity —,"LANDED behind config, default = original." D184,PARTIAL,F10 port-layer options overlay —,"LANDED, port-only, no #ifdef PORT in src/." +D216,PARTIAL,"Game.SkipIntro — boot straight to the file-select menu (M-83, port + one #ifdef PORT game-code line, PD parity, Phase 4 quick win).",LANDED + VERIFIED this session. D85,OPEN,room primary/secondary DL → texLoadFromGdl garbage,"OPEN (safety-netted; widen/pool fixes landed, geometry now renders)" D86 · D87,RESOLVED,modelInitRwData truncated ptr · attract-demo BE ramromfilestructure,resolved D88.4,RESOLVED,propDefs polymorphic record stream not byteswapped → setupDoor crash,resolved (d88_propdefs.py); layout audit M-20 → D132 diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 824ecd9c..8e7ba9e2 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -427,6 +427,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D180 | **Native-PC input pass (M-XX QoL run, `qol/native-pc-input-menu`, port-only).** Three parts, all config-gated with default = prior behaviour: **(WI-1) `Input.MouseCaptureMode`** (0 = legacy always-grab; 1 = Quake-style click-to-lock) in `port/src/input.c` + `port/src/video.c` — in mode 1 the OS cursor is free until a click lands in the game window (`SDL_MOUSEBUTTONDOWN` → `inputNotifyClick`), and ESC / focus-loss / entering any front-end menu (`current_menu != MENU_RUN_STAGE`) frees it again; re-entering a stage while still "armed" re-locks so unpausing needs no click. `reconcileGrab()` runs once per controller-0 poll. Mouse buttons are suppressed from the game while the cursor is free in-stage (no phantom fire). Controller paths untouched. **(WI-2) absolute-cursor menu tracking** — when capture mode is on and the cursor is free in a menu, the D165/D169 pointer P-controller takes its target from the real cursor's absolute window position mapped onto the live virtual front-end rect (`getPlayer_c_screen*`), giving true 1:1 tracking instead of the relative-delta estimator. Relative path unchanged for legacy/grabbed. **(B3) `Input.MouseAimSpeed` default 25 → 16** (aim mode still overshot at 25 per the backlog). | LANDED, port-only, no `#ifdef PORT`. Build 241/241, `-level_09` framediff 3/3 within threshold (unregressed), `GE_STARTMENU` menu boot crash-free. **Feel-checks owed** (headless can't drive the mouse): click-to-lock ergonomics, absolute menu tracking, the new aim-speed default. | | D181 | **`Game.ScreenShakeIntensity` — first route-(b) `src/` gameplay-cosmetic hook (M-XX QoL run).** `src/fr.c viShake()` gains a single `#ifdef PORT` line — `intensity *= portScreenShakeScale;` (extern `f32`, defined + `configRegisterFloat`'d 0.0–10.0 in `port/src/video.c`) — before the existing clamp. Default `1.0f` ⇒ exact no-op, every headless golden dump byte-unaffected; `0.0` disables explosion/effect screen shake, up to `10.0` exaggerates it. N64 build (no `-DPORT`) keeps the original line verbatim. Precedent per `docs/BACKLOG.md` "Fun features → screen-shake intensity slider" and AGENTS.md #2's documented-exception path. Same policy class as a future FOV hook. | LANDED behind config, default = original. Build green. | | D184 | **F10 port-layer options overlay — the approach-(C) surface from `OPTIONS-MENU-PLAN.md` (M-37).** New `port/src/optionsoverlay.c` + `port/include/optionsoverlay.h`: an immediate-mode overlay drawn as its own fast3d 2D DL, appended after the game DL in `gfx_pc.cpp gfx_run()` (`optionsOverlayEmit()` → NULL when closed ⇒ **zero bytes appended, golden dumps byte-identical**). F10 toggles (`video.c videoPumpEvents`, next to F12); while open, `input.c inputComputePad(0)` returns a neutral pad and routes arrows/enter/wheel/mouse-buttons to `optionsOverlayHandleInput()` (mirrors the WI-1/D180 swallow). ESC closes; `configSave()` on close. `config.c` gains `configForEachOption()` + a `configSetOptionMeta()` label/step/enum side table (no per-key knowledge in config.c). Live knobs (VSync/FpsCap/TextureFilter via `videoRequestLiveConfig()`; mouse + screen-shake via the registered pointer) apply immediately; MSAA tagged "(restart)". v1 rows per plan §3: VSync, FpsCap, MSAA, TextureFilter, MouseAimSpeed, MouseTurnSpeed, MouseInvertY, MouseCaptureMode, ScreenShakeIntensity. | **LANDED, port-only, no `#ifdef PORT` in `src/`.** Build links 241/241. **Runtime verification NOT run this session** — the headless env kills the GUI process after ~8 s and `gfx_opengl_dump_bound_fbo` writes 0-byte PPMs here, so framediff / 60 s soak / overlay-frame eyeball are all owed on an interactive machine. Overlay-closed no-op is code-evident (emit returns NULL before touching the DL buffer). | +| D216 | **`Game.SkipIntro` — boot straight to the file-select menu (M-83, port + one `#ifdef PORT` game-code line, PD parity, Phase 4 quick win).** New `s32 portSkipIntro` (`port/src/video.c`, `configRegisterInt("Game.SkipIntro", 0, 1)`), read once in `src/game/lv.c` at `LEVELID_TITLE` stage load — in the `else` of the existing `GE_STARTMENU` `#ifdef PORT` block. When set it does what every intro stage's own `!is_first_time_on_main_menu` branch does (`front.c`): `is_first_time_on_main_menu = FALSE; prev_keypresses = TRUE; maybe_is_in_menu = TRUE; menu_update = MENU_FILE_SELECT;` — i.e. it reuses the game's own post-intro route rather than inventing one, so the legal screen + Nintendo/Rare/GoldenEye logo attract loop is skipped. `GE_STARTMENU` still wins when both are set. Default 0 ⇒ untouched. | **LANDED + VERIFIED this session.** Build clean. Headless: default boot frame 90 = the TWYCROSS classification screen (10% non-black), `SkipIntro=1` frame 90 = the **SELECT FILE menu** (folders/photos/cursor all render, 89% non-black), no crash across repeated boots. Screenshots captured + eyeballed. **Owed:** nothing critical — a human boot to confirm the menu is fully interactive (cursor moves, a save slot opens). | | D85 | room primary/secondary DL → `texLoadFromGdl` garbage | OPEN (safety-netted; widen/pool fixes landed, geometry now renders) | | D86 · D87 | modelInitRwData truncated ptr · attract-demo BE `ramromfilestructure` | resolved | | D88.1–D88.3 · D88.5–D88.6 | `Usetup*Z` header + sub-table width/endian conversion (`d88_emit.py`) | resolved | diff --git a/port/src/video.c b/port/src/video.c index 9ebcb634..c0ce3add 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -80,9 +80,14 @@ static int cfgWinMax = 0; */ f32 portScreenShakeScale = 1.0f; +/* D216: Game.SkipIntro — read once in src/game/lv.c at title-stage load. + * 0 (default) = the legal screen + logo attract sequence plays as normal. */ +s32 portSkipIntro = 0; + PD_CONSTRUCTOR static void videoConfigInit(void) { configRegisterFloat("Game.ScreenShakeIntensity", &portScreenShakeScale, 0.0f, 10.0f); + configRegisterInt("Game.SkipIntro", &portSkipIntro, 0, 1); configRegisterInt("Video.VSync", &cfgVSync, 0, 1); configRegisterInt("Video.FpsCap", &cfgFpsCap, 0, 1000); configRegisterInt("Video.MSAA", &cfgMSAA, 1, 8); diff --git a/src/game/lv.c b/src/game/lv.c index 6f5d77d3..74118411 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -413,6 +413,21 @@ void lvlStageLoad(s32 stage) menu_update = (MENU)want; osSyncPrintf("GE_STARTMENU: menu=0x%x page=%d stage=%d diff=%d\n", want, page, selected_stage, diff); + } else { + /* D216: Game.SkipIntro — boot straight to the file-select + * menu, skipping the legal screen + Nintendo/Rare/GoldenEye + * logo attract loop. Reuses the game's own post-intro route: + * every intro stage already jumps to MENU_FILE_SELECT once + * is_first_time_on_main_menu is FALSE (front.c). Port config + * only; default 0 = unchanged. */ + extern s32 portSkipIntro; + if (portSkipIntro) { + is_first_time_on_main_menu = FALSE; + prev_keypresses = TRUE; + maybe_is_in_menu = TRUE; + menu_update = MENU_FILE_SELECT; + osSyncPrintf("Game.SkipIntro: booting to file-select\n"); + } } } #endif