From 259c6b8ec4524dcd4a75da065106107f2165b202 Mon Sep 17 00:00:00 2001 From: Ash Ketchum Date: Wed, 15 Jul 2026 12:15:50 +0200 Subject: explore: pivot to noclip free-roam camera Ground-truth from render_overworld.py showed Kanto is ONE seamless world joined along shared edges with per-connection offsets -- so a map-by-map browser is the wrong model. Replaced it with noclip free-roam that leans on the engine's own player-walk + connection-crossing machinery (which already handles seams/offsets correctly): - wSlopNoclip flag (reused the dead wUnusedObtainedBadges byte at D356, 0 r/w, not unioned, in the saved region -> safe). - CollisionCheckOnLand: 5-byte hook at the top -> when noclip is set, return no-collision. Freed the room in ROM0 by deleting the unreferenced Func_0db5 (dead farcall LoadToggleableObjectData) in LoadMapHeader. - EXPLORE menu entry now just toggles wSlopNoclip and closes the menu, dropping you into the overworld to glide through walls/NPCs/ledges and walk across map connections exactly as the world data defines them. Verified: Pallet -> (walk north through the fences) -> seam-cross into Route 1 at y=35 -> continue north; toggle off restores collision (player blocked). --- engine/movie/oak_speech/init_player_data.asm | 2 +- engine/slop_menu.asm | 152 ++------------------------- home/overworld.asm | 9 +- ram/wram.asm | 2 +- 4 files changed, 17 insertions(+), 148 deletions(-) diff --git a/engine/movie/oak_speech/init_player_data.asm b/engine/movie/oak_speech/init_player_data.asm index 90fb8f0e..589f4b5f 100644 --- a/engine/movie/oak_speech/init_player_data.asm +++ b/engine/movie/oak_speech/init_player_data.asm @@ -39,7 +39,7 @@ DEF START_MONEY EQU $3000 ld hl, wObtainedBadges ld [hli], a - ASSERT wObtainedBadges + 1 == wUnusedObtainedBadges + ASSERT wObtainedBadges + 1 == wSlopNoclip ld [hl], a ld hl, wPlayerCoins diff --git a/engine/slop_menu.asm b/engine/slop_menu.asm index fcac7d10..e3b07fe6 100644 --- a/engine/slop_menu.asm +++ b/engine/slop_menu.asm @@ -221,149 +221,17 @@ WarpMapTable: ; (via wCurMapConnections + the connection headers). B restores the original ; map and returns to the SL0P menu. No warping yet - read-only browse. ; =========================================================================== +; =========================================================================== +; EXPLORE: toggle noclip free-roam. With noclip on, CollisionCheckOnLand is +; bypassed, so you glide through walls / NPCs / ledges and cross map connection +; seams exactly as the world data defines them. Closes the menu so you drop +; straight back into the overworld to roam. Pick it again to turn it off. +; =========================================================================== SlopExplore: -; save origin map + coords on the stack - ld a, [wYCoord] - ld b, a - ld a, [wXCoord] - ld c, a - push bc ; origin Y,X - ld a, [wCurrentTileBlockMapViewPointer] - ld b, a - ld a, [wCurrentTileBlockMapViewPointer + 1] - ld c, a - push bc ; origin view pointer - ld a, [wCurMap] - push af ; origin map -.render -; LoadMapData loads the map header + block map into wOverworldMap, but it renders -; using a STALE wCurrentTileBlockMapViewPointer -- the game only ever sets that -; from warp / connection / walk data, never from wXCoord/wYCoord. So load the -; map, then compute a centered view pointer ourselves and redraw the view. - call LoadMapData -; stride = wCurMapWidth + MAP_BORDER*2 ; bufRow = height/2 + 1 ; bufCol = width/2 -; wCurrentTileBlockMapViewPointer = wOverworldMap + bufRow*stride + bufCol - ld a, [wCurMapWidth] - add MAP_BORDER * 2 - ld c, a - ld b, 0 ; bc = row stride (blocks) - ld hl, 0 - ld a, [wCurMapHeight] - srl a - inc a ; bufRow (>= 1) -.viewRowLoop - add hl, bc - dec a - jr nz, .viewRowLoop - ld a, [wCurMapWidth] - srl a ; bufCol = width/2 - add l - ld l, a - jr nc, .viewNoCarry - inc h -.viewNoCarry - ld bc, wOverworldMap - add hl, bc - ld a, l - ld [wCurrentTileBlockMapViewPointer], a - ld a, h - ld [wCurrentTileBlockMapViewPointer + 1], a - xor a - ld [wYBlockCoord], a - ld [wXBlockCoord], a - call LoadCurrentMapView ; redraw wTileMap centered on the map - ldh a, [rLCDC] ; hide ALL sprites (player + actors) via LCDC - res B_LCDC_OBJS, a - ldh [rLCDC], a - ld a, $90 - ldh [hWY], a ; window off-screen -> the BG map is visible -; overlay direction markers at the screen edges for the legit connections - call LoadFontTilePatterns - ld a, [wCurMapConnections] - ld e, a - bit NORTH_F, e - jr z, .noN - hlcoord 9, 0 - ld [hl], $8D -.noN - bit SOUTH_F, e - jr z, .noS - hlcoord 9, 15 - ld [hl], $92 -.noS - bit WEST_F, e - jr z, .noW - hlcoord 0, 8 - ld [hl], $96 -.noW - bit EAST_F, e - jr z, .noE - hlcoord 18, 8 - ld [hl], $84 -.noE - ld b, HIGH(vBGMap0) - call CopyScreenTileBufferToVRAM ; push map + markers to VRAM -.input - call DelayFrame - call JoypadLowSensitivity - ldh a, [hJoyPressed] - ld d, a - bit B_PAD_B, d - jr nz, .exit - bit B_PAD_UP, d - jr z, .chkDown - ld a, [wCurMapConnections] - bit NORTH_F, a - jp z, .input - ld a, [wNorthConnectedMap] - ld [wCurMap], a - jp .render -.chkDown - bit B_PAD_DOWN, d - jr z, .chkLeft - ld a, [wCurMapConnections] - bit SOUTH_F, a - jp z, .input - ld a, [wSouthConnectedMap] - ld [wCurMap], a - jp .render -.chkLeft - bit B_PAD_LEFT, d - jr z, .chkRight - ld a, [wCurMapConnections] - bit WEST_F, a - jp z, .input - ld a, [wWestConnectedMap] - ld [wCurMap], a - jp .render -.chkRight - bit B_PAD_RIGHT, d - jp z, .input - ld a, [wCurMapConnections] - bit EAST_F, a - jp z, .input - ld a, [wEastConnectedMap] - ld [wCurMap], a - jp .render -.exit - pop af - ld [wCurMap], a ; restore origin map - pop bc ; restore origin view pointer - ld a, b - ld [wCurrentTileBlockMapViewPointer], a - ld a, c - ld [wCurrentTileBlockMapViewPointer + 1], a - pop bc ; restore origin Y,X - ld a, b - ld [wYCoord], a - and $1 - ld [wYBlockCoord], a - ld a, c - ld [wXCoord], a - and $1 - ld [wXBlockCoord], a - call LoadMapData ; reload origin, rendered from the restored pointer - and a ; carry clear -> back to SL0P menu + ld a, [wSlopNoclip] + xor $01 + ld [wSlopNoclip], a + scf ; carry set -> close menu, back to overworld ret ; =========================================================================== diff --git a/home/overworld.asm b/home/overworld.asm index a41a7ba8..95b94646 100644 --- a/home/overworld.asm +++ b/home/overworld.asm @@ -1218,6 +1218,9 @@ SignLoop:: ; function to check if the player will jump down a ledge and check if the tile ahead is passable (when not surfing) ; sets the carry flag if there is a collision, and unsets it if there isn't a collision CollisionCheckOnLand:: + ld a, [wSlopNoclip] ; SL0P noclip: walk through everything (carry clear) + and a + ret nz ld a, [wMovementFlags] bit BIT_LEDGE_OR_FISHING, a jr nz, .noCollision @@ -1797,10 +1800,8 @@ LoadPlayerSpriteGraphicsCommon:: ; function to load data from the map header LoadMapHeader:: farcall MarkTownVisitedAndLoadToggleableObjects - jr asm_0dbd - -Func_0db5:: ; unreferenced - farcall LoadToggleableObjectData +; SL0P: removed unreferenced Func_0db5 (farcall LoadToggleableObjectData) to make +; room in ROM0 for the noclip hook in CollisionCheckOnLand. asm_0dbd: ld a, [wCurMapTileset] ld [wUnusedCurMapTilesetCopy], a diff --git a/ram/wram.asm b/ram/wram.asm index 62ef1c44..e0488e62 100644 --- a/ram/wram.asm +++ b/ram/wram.asm @@ -1943,7 +1943,7 @@ wOptions:: db wObtainedBadges:: flag_array NUM_BADGES -wUnusedObtainedBadges:: db +wSlopNoclip:: db ; SL0P: nonzero = noclip free-roam (walk through walls) wLetterPrintingDelayFlags:: db -- cgit v1.3.1-sl0p