aboutsummaryrefslogtreecommitdiffstats
path: root/engine
diff options
context:
space:
mode:
authorAsh Ketchum <no-reply@sl0p.foo>2026-07-15 15:23:39 +0200
committerAsh Ketchum <no-reply@sl0p.foo>2026-07-15 15:23:39 +0200
commiteeb61bd9aa3ad514d685b82a635a8b7d4511339a (patch)
treeaa963e1dbb7d0bc503fc46756313b2b61a0ae9f0 /engine
parentEXPLORE: animate the approach to map edges instead of hard-jumping (diff)
downloadpokeyellow-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.asm193
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