diff options
| author | Ash Ketchum <no-reply@sl0p.foo> | 2026-07-15 15:23:39 +0200 |
|---|---|---|
| committer | Ash Ketchum <no-reply@sl0p.foo> | 2026-07-15 15:23:39 +0200 |
| commit | eeb61bd9aa3ad514d685b82a635a8b7d4511339a (patch) | |
| tree | aa963e1dbb7d0bc503fc46756313b2b61a0ae9f0 /engine | |
| parent | EXPLORE: animate the approach to map edges instead of hard-jumping (diff) | |
| download | pokeyellow-eeb61bd9aa3ad514d685b82a635a8b7d4511339a.tar.gz pokeyellow-eeb61bd9aa3ad514d685b82a635a8b7d4511339a.tar.xz pokeyellow-eeb61bd9aa3ad514d685b82a635a8b7d4511339a.zip | |
SL0P menu: add SAVER screensaver (autopilot EXPLORE)
New menu entry that auto-drives the EXPLORE camera across the overworld:
- persistent-direction wander (DFS-ish): keep heading, turn only when blocked
or occasionally at a junction, never an immediate U-turn. SlopSaverPickDir
synthesises a d-pad direction into the normal EXPLORE dispatch, so it inherits
smooth scrolling and smooth map-edge crossing.
- arrows hidden; player/Pikachu never drawn (clear+freeze OAM right after every
LoadMapData, before the first frame renders - also fixes a brief player flash
at crossings in manual EXPLORE).
- any button wakes it back to the menu.
State byte wSlopSaverDir reuses wUnusedCreditsByte (0=off, else current dir).
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/slop_menu.asm | 193 |
1 files changed, 191 insertions, 2 deletions
diff --git a/engine/slop_menu.asm b/engine/slop_menu.asm index 36654111..09650150 100644 --- a/engine/slop_menu.asm +++ b/engine/slop_menu.asm @@ -8,7 +8,7 @@ ; To add an entry: add a "NAME" line to SlopMenuText, a `dw Handler` to ; SlopMenuHandlers, and bump SLOP_MENU_COUNT. -DEF SLOP_MENU_COUNT EQU 13 +DEF SLOP_MENU_COUNT EQU 14 SECTION "Slop Menu", ROMX @@ -121,7 +121,8 @@ SlopMenuText: next "ITEMS" next "NOWILD" next "REPEL" - next "EXPLORE@" + next "EXPLORE" + next "SAVER@" SlopOnText: db "ON@" SlopMenuHandlers: @@ -138,6 +139,7 @@ SlopMenuHandlers: dw SlopToggleNoWild dw SlopRepel dw SlopExplore + dw SlopSaver ; =========================================================================== @@ -230,7 +232,23 @@ DEF SLOP_PAGE_Y EQU 8 ; coords moved per vertical page DEF SLOP_SCROLL_PX EQU 8 DEF SLOP_SCROLL_FRAMES EQU 16 / SLOP_SCROLL_PX +SlopSaver: +; SCREENSAVER: auto-scroll the overworld. Seed an initial direction; the rest is +; identical to EXPLORE (shared body), driven by SlopSaverPickDir in the loop. + ld a, PAD_DOWN + ld [wSlopSaverDir], a + jr SlopExploreShared SlopExplore: + xor a + ld [wSlopSaverDir], a +SlopExploreShared: +; wait for the opening button to be released so it isn't read as EXPLORE input +.waitRelease + call DelayFrame + call Joypad ; VBlank only reads raw input; Joypad updates hJoyHeld + ldh a, [hJoyHeld] + and a + jr nz, .waitRelease ; save origin (map, coords, block coords, view pointer) to restore on exit ld a, [wYCoord] ld b, a @@ -251,12 +269,21 @@ SlopExplore: push af call LoadMapData ; reload current map + tileset (menu clobbered VRAM) .renderView +; LoadMapData (entry/cross) re-enables sprites and reloads the player + Pikachu +; follower. Freeze and clear OAM now, before the first frame renders, so they are +; never drawn during EXPLORE/screensaver. + ld a, $ff + ld [wUpdateSpritesEnabled], a + call ClearSprites call SlopExploreRenderBG ; fresh render to vBGMap0, hardware scroll reset .renderArrows call LoadFontTilePatterns ; arrow glyphs ($ed/$ee) into VRAM for the OBJ layer ; draw a direction-arrow sprite for each direction the camera can currently move: ; either there's more map that way, or the map has a connection that way. call ClearSprites + ld a, [wSlopSaverDir] + and a + jp nz, .arrowsDone ; screensaver hides the direction arrows ld hl, wShadowOAM ; up: movable unless at the top edge (wYCoord == 0) with no north connection ld a, [wYCoord] @@ -337,9 +364,20 @@ SlopExplore: ldh [hWY], a .input call DelayFrame + ld a, [wSlopSaverDir] + and a + jr nz, .saverInput call JoypadLowSensitivity ldh a, [hJoyPressed] ld d, a + jr .dispatch +.saverInput + call Joypad ; refresh hJoyHeld (VBlank only reads raw input) + ldh a, [hJoyHeld] ; any real button wakes/exits the screensaver + and a + jp nz, .exit + call SlopSaverPickDir ; d = auto-picked scroll direction +.dispatch bit B_PAD_B, d jp nz, .exit bit B_PAD_A, d @@ -825,6 +863,157 @@ SlopScrollUp: jr nz, .loop ret +; =========================================================================== +; SCREENSAVER direction logic: a persistent-direction wander. Keep drifting one +; way; only turn when the current direction gets blocked, or occasionally at a +; junction. Avoids immediate U-turns so it glides smoothly (DFS-ish) across the +; overworld instead of jittering. The chosen direction is fed into the normal +; EXPLORE dispatch as if the d-pad were pressed, so it inherits smooth scrolling +; and smooth map-edge crossing for free. +; =========================================================================== +SlopDirBits: + db PAD_UP, PAD_DOWN, PAD_LEFT, PAD_RIGHT + +; -> a = bitmask of currently-movable directions (same rule as the arrows) +SlopValidDirMask: + ld c, 0 + ld a, [wYCoord] + and a + jr nz, .up + ld a, [wNorthConnectedMap] + inc a + jr z, .noUp +.up + set B_PAD_UP, c +.noUp + ld a, [wCurrentMapHeight2] + dec a + ld b, a + ld a, [wYCoord] + cp b + jr nz, .down + ld a, [wSouthConnectedMap] + inc a + jr z, .noDown +.down + set B_PAD_DOWN, c +.noDown + ld a, [wXCoord] + and a + jr nz, .left + ld a, [wWestConnectedMap] + inc a + jr z, .noLeft +.left + set B_PAD_LEFT, c +.noLeft + ld a, [wCurrentMapWidth2] + dec a + ld b, a + ld a, [wXCoord] + cp b + jr nz, .right + ld a, [wEastConnectedMap] + inc a + jr z, .noRight +.right + set B_PAD_RIGHT, c +.noRight + ld a, c + ret + +; a = nonzero mask -> a = one set bit of it, chosen pseudo-randomly +SlopRandomBit: + ld b, a ; b = mask + ldh a, [hRandomSub] + and $03 + ld c, a ; random start index 0..3 + ld d, 4 +.loop + ld a, c + and $03 + ld hl, SlopDirBits + add l + ld l, a + ld a, 0 + adc h + ld h, a + ld a, [hl] + and b + ret nz ; this direction is in the mask -> use it + inc c + dec d + jr nz, .loop + ld a, b ; fallback (a nonzero mask always hits above) + ret + +; a = dir bit -> a = the opposite direction bit +SlopRevDir: + cp PAD_UP + jr nz, .notUp + ld a, PAD_DOWN + ret +.notUp + cp PAD_DOWN + jr nz, .notDown + ld a, PAD_UP + ret +.notDown + cp PAD_LEFT + jr nz, .notLeft + ld a, PAD_RIGHT + ret +.notLeft + ld a, PAD_LEFT ; was RIGHT + ret + +; a = dir bit -> a = mask of the two perpendicular directions +SlopPerpMask: + and PAD_UP | PAD_DOWN + ld a, PAD_LEFT | PAD_RIGHT ; vertical dir -> horizontal perpendicular + ret nz + ld a, PAD_UP | PAD_DOWN + ret + +; pick the next screensaver direction; store in wSlopSaverDir, return it in d +SlopSaverPickDir: + call SlopValidDirMask + ld e, a ; e = valid-direction mask + and a + jr z, .keepCurrent ; nothing valid (shouldn't happen) + ld a, [wSlopSaverDir] + ld b, a ; b = current direction + and e + jr z, .blocked ; current direction no longer valid -> turn +; current direction still valid: mostly keep going, occasionally turn + ldh a, [hRandomAdd] + and $07 + jr nz, .keepCurrent ; 7/8: carry straight on + ld a, b + call SlopPerpMask + and e ; valid perpendicular directions + jr z, .keepCurrent ; none -> keep going + call SlopRandomBit + jr .store +.blocked + ld a, b + call SlopRevDir + cpl + and e ; valid directions excluding a U-turn + jr z, .onlyReverse + call SlopRandomBit + jr .store +.onlyReverse + ld a, e ; only a U-turn is available + call SlopRandomBit + jr .store +.keepCurrent + ld a, [wSlopSaverDir] +.store + ld [wSlopSaverDir], a + ld d, a + ret + ; (dy, dx) coord offsets scanned outward from the centre for a walkable tile SlopWarpOffsets: db 0, 0 |
