diff --git a/.gitignore b/.gitignore index 99c530f41..70f09bd65 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ used_space.png # macos files .DS_STORE + +# vscode settings +.vscode diff --git a/data/maps/setup_script_pointers.asm b/data/maps/setup_script_pointers.asm index c257f054e..66c6a5cb4 100644 --- a/data/maps/setup_script_pointers.asm +++ b/data/maps/setup_script_pointers.asm @@ -57,3 +57,4 @@ MapSetupCommands: add_mapsetup BackupMapObjects ; 32 add_mapsetup LoadDisabledSpaces ; 33 add_mapsetup MapCallbackAtEndMapSetup ; 34 + add_mapsetup RepositionMockedPlayerObject ; 35 diff --git a/data/maps/setup_scripts.asm b/data/maps/setup_scripts.asm index 116328609..8293a5ecf 100644 --- a/data/maps/setup_scripts.asm +++ b/data/maps/setup_scripts.asm @@ -122,6 +122,7 @@ MapSetupScript_Connection: mapsetup RefreshPlayerCoords mapsetup LoadBlockData mapsetup LoadDisabledSpaces + mapsetup RepositionMockedPlayerObject mapsetup LoadMapTileset mapsetup SaveScreen mapsetup LoadMapObjects diff --git a/docs/develop/index.md b/docs/develop/index.md index 1dff360fb..80a117d2f 100755 --- a/docs/develop/index.md +++ b/docs/develop/index.md @@ -102,7 +102,10 @@ - **wTempSpaceStruct**: Temporary scope. Same structure as *wCurSpaceStruct* - **wTempSpaceBranchStruct**: Temporary scope. The structure is four bytes for next space for each direction (R/L/U/D; -1 if unavailable direction) followed by four bytes for required techniques for each direction (R/L/U/D) - **wViewMapModeRange**, **wViewMapModeDisplacementY**, **wViewMapModeDisplacementX**: Temporary scope during a Vew Map mode session. - - **wBeforeViewMapYCoord**, **wBeforeViewMapXCoord**, **wBeforeViewMapMapGroup**, **wBeforeViewMapMapNumber**: Temporary scope during a Vew Map mode session. Used to preserve previous player state. + - **wBeforeViewMapYCoord**, **wBeforeViewMapXCoord**, **wBeforeViewMapMapGroup**, **wBeforeViewMapMapNumber**, **wBeforeViewMapDirection**: Temporary scope during a Vew Map mode session. Used to preserve player state before entering View Map mode. + +- Additional addresses for View Map mode, that share memory region with *wCurBattleMon* and *wCurMoveNum*, which are not used outside of battle: + - **wPlayerMockYCoord**, **wPlayerMockXCoord**: Used to handle the player mock sprite through map connections during View Map mode. - Addresses for talker events: - *wSeenTrainer** addresses have been repurposed as **wSeenTrainerOrTalker*** diff --git a/engine/board/menu.asm b/engine/board/menu.asm index bf3a10601..a233c188e 100755 --- a/engine/board/menu.asm +++ b/engine/board/menu.asm @@ -118,6 +118,8 @@ BoardMenuScript:: ld [wBeforeViewMapXCoord], a ld a, [wYCoord] ld [wBeforeViewMapYCoord], a + ld a, [wPlayerDirection] + ld [wBeforeViewMapDirection], a xor a ld [wViewMapModeDisplacementY], a ld [wViewMapModeDisplacementX], a diff --git a/engine/board/spaces.asm b/engine/board/spaces.asm index 0bba68ec0..3c3faca7a 100755 --- a/engine/board/spaces.asm +++ b/engine/board/spaces.asm @@ -291,6 +291,8 @@ PromptPlayerToChooseBranchDirection: ld [wBeforeViewMapXCoord], a ld a, [wYCoord] ld [wBeforeViewMapYCoord], a + ld a, [wPlayerDirection] + ld [wBeforeViewMapDirection], a xor a ld [wViewMapModeDisplacementY], a ld [wViewMapModeDisplacementX], a diff --git a/engine/overworld/player_object.asm b/engine/overworld/player_object.asm index 5b5f461c9..44262b31c 100644 --- a/engine/overworld/player_object.asm +++ b/engine/overworld/player_object.asm @@ -856,8 +856,90 @@ QueueFollowerFirstStep: scf ret +RepositionMockedPlayerObject:: +; map setup command called by map setup script MAPSETUP_CONNECTION after LoadBlockData, +; once the new map blocks have been loaded to wOverworldMapBlocks. +; Only applies during BOARDEVENT_VIEW_MAP_MODE + ldh a, [hCurBoardEvent] + cp BOARDEVENT_VIEW_MAP_MODE + ret nz + +; if map at wBeforeViewMapMapGroup, wBeforeViewMapMapNumber is not the current map, +; or a map connected to the current map, we are done. + ld hl, wBeforeViewMapMapGroup + ld a, [hli] + ld b, a + ld c, [hl] ; wBeforeViewMapMapNumber + ld a, [wMapGroup] + cp b + jr nz, .next_map_1 + ld a, [wMapNumber] + cp c + jr z, MockPlayerObject +.next_map_1 + ld a, [wNorthConnectedMapGroup] + cp b + jr nz, .next_map_2 + ld a, [wNorthConnectedMapNumber] + cp c + ld a, 0 ; north connected map + jr z, .is_connected_map +.next_map_2 + ld a, [wSouthConnectedMapGroup] + cp b + jr nz, .next_map_3 + ld a, [wSouthConnectedMapNumber] + cp c + ld a, 1 ; south connected map + jr z, .is_connected_map +.next_map_3 + ld a, [wWestConnectedMapGroup] + cp b + jr nz, .next_map_4 + ld a, [wWestConnectedMapNumber] + cp c + ld a, 2 ; west connected map + jr z, .is_connected_map +.next_map_4 + ld a, [wEastConnectedMapGroup] + cp b + ret nz + ld a, [wEastConnectedMapNumber] + cp c + ld a, 3 ; east connected map + ret nz + +.is_connected_map + ld [wTempByteValue], a + ld hl, .got_sprite_coords + push hl + ld a, [wBeforeViewMapXCoord] + ld d, a + ld a, [wBeforeViewMapYCoord] + ld e, a + jumptable_bc .Jumptable, wTempByteValue + +.Jumptable: + dw GetNorthConnectedSpriteCoords + dw GetSouthConnectedSpriteCoords + dw GetWestConnectedSpriteCoords + dw GetEastConnectedSpriteCoords + +.got_sprite_coords + ret nc ; return if sprite is not in visible part of connected map + jr MockPlayerObject.loaded_player_mock_coords + MockPlayerObject:: -; refresh wPlayerObjectYCoord and wPlayerObjectXCoord + ld hl, wBeforeViewMapYCoord + ld de, wPlayerMockYCoord + ld a, [hli] + ld [de], a + inc de + ld a, [hl] + ld [de], a + +.loaded_player_mock_coords +; refresh wPlayerObjectYCoord and wPlayerObjectXCoord (not strictly necessary) farcall RefreshPlayerCoords ; copy default sprite object to the last object struct ld hl, .DefaultPlayerObject @@ -893,15 +975,17 @@ MockPlayerObject:: .copy_player_coords ; copy player's coordinates - ld hl, wPlayerObjectYCoord + ld hl, wPlayerMockYCoord ld de, wMapObject{d:LAST_OBJECT}YCoord ld a, [hli] + add 4 ld [de], a inc de - ld a, [hl] ; wPlayerObjectXCoord + ld a, [hl] ; wPlayerMockXCoord + add 4 ld [de], a ; wMapObject{d:LAST_OBJECT}XCoord ; set facing direction - ld a, [wPlayerDirection] + ld a, [wBeforeViewMapDirection] srl a srl a maskbits NUM_DIRECTIONS @@ -929,3 +1013,207 @@ MockPlayerObject:: db 0, PLAYER_BIKE, SPRITE_CHRIS_BIKE, PAL_NPC_RED << 4 | OBJECTTYPE_SCRIPT db 1 << PLAYERGENDER_FEMALE_F, PLAYER_BIKE, SPRITE_KRIS_BIKE, PAL_NPC_BLUE << 4 | OBJECTTYPE_SCRIPT db -1 + +GetSouthConnectedSpriteCoords: +; ycoord / 2 <= 2 + ld a, e + srl a + cp 3 + ret nc +; [wSouthConnectionStripLocation] + ld hl, wSouthConnectionStripLocation + ld a, [hli] + ld h, [hl] + ld l, a +; + (xcoord / 2) + srl d + ld b, 0 + ld c, d + add hl, bc +; + ([wMapWidth] + 6) * ycoord / 2 + ld a, [wMapWidth] + add 6 + ld c, a + ld b, 0 + srl e + ld a, e + and a + jr z, .done +.loop + add hl, bc + dec a + jr nz, .loop +.done + call ConvertConnectedOverworldMapBlockAddressToXYCoords + ret + +GetNorthConnectedSpriteCoords: +; wNorthConnectedMapHeight >= 3 +; ycoord / 2 >= ([wNorthConnectedMapHeight] - 3) + ld a, [wNorthConnectedMapHeight] + sub 3 + jr c, .nope + ld c, a + ld a, e + srl a + sub c + jr c, .nope + ld e, a ; e = ycoord / 2 - ([wNorthConnectedMapHeight] - 3) +; [wNorthConnectionStripLocation] + ld hl, wNorthConnectionStripLocation + ld a, [hli] + ld h, [hl] + ld l, a +; + (xcoord / 2) + srl d + ld b, 0 + ld c, d + add hl, bc +; + ([wMapWidth] + 6) * {ycoord / 2 - ([wNorthConnectedMapHeight] - 3)} --> + ([wMapWidth] + 6) * e + ld a, [wMapWidth] + add 6 + ld c, a + ld b, 0 + ld a, e + and a + jr z, .done +.loop + add hl, bc + dec a + jr nz, .loop +.done + call ConvertConnectedOverworldMapBlockAddressToXYCoords + ret +.nope + xor a + ret ; nc + +GetEastConnectedSpriteCoords: +; xcoord / 2 <= 2 + ld a, d + srl a + cp 3 + ret nc +; [wEastConnectionStripLocation] + ld hl, wEastConnectionStripLocation + ld a, [hli] + ld h, [hl] + ld l, a +; + (xcoord / 2) + srl d + ld b, 0 + ld c, d + add hl, bc +; + ([wMapWidth] + 6) * ycoord / 2 + ld a, [wMapWidth] + add 6 + ld c, a + ld b, 0 + srl e + ld a, e + and a + jr z, .done +.loop + add hl, bc + dec a + jr nz, .loop +.done + call ConvertConnectedOverworldMapBlockAddressToXYCoords + ret + +GetWestConnectedSpriteCoords: +; wWestConnectedMapWidth >= 3 +; xcoord / 2 >= ([wWestConnectedMapWidth] - 3) + ld a, [wWestConnectedMapWidth] + sub 3 + jr c, .nope + ld c, a + ld a, d + srl a + sub c + jr c, .nope + ld d, a ; d = xcoord / 2 - ([wWestConnectedMapWidth] - 3) +; [wWestConnectionStripLocation] + ld hl, wWestConnectionStripLocation + ld a, [hli] + ld h, [hl] + ld l, a +; + xcoord / 2 - ([wWestConnectedMapWidth] - 3) --> + d + ld c, d + ld b, 0 + add hl, bc +; + ([wMapWidth] + 6) * ycoord / 2 + ld a, [wMapWidth] + add 6 + ld c, a + ld b, 0 + srl e + ld a, e + and a + jr z, .done +.loop + add hl, bc + dec a + jr nz, .loop +.done + call ConvertConnectedOverworldMapBlockAddressToXYCoords + ret +.nope + xor a + ret ; nc + +; load into wPlayerMockYCoord and wPlayerMockXCoord the coordinates +; that correspond to wOverworldMapBlocks address at hl. +ConvertConnectedOverworldMapBlockAddressToXYCoords: + ld bc, -wOverworldMapBlocks + $10000 + add hl, bc + ld a, [wMapWidth] + add 6 + xor $ff + inc a + ld c, a + ld b, $ff + ld d, -2 ; +.calc_y_coord_loop + inc d ; + inc d ; each block in wOverworldMapBlocks occupies two half-blocks + add hl, bc + ld a, h + cp $ff + jr nz, .calc_y_coord_loop +; for X, we want the value of l in the second-to-last iteration of the previous loop, +; so undo the last iteration in l by adding [wMapWidth]+6 to it + ld a, [wMapWidth] + add 6 + add l + add a ; each block in wOverworldMapBlocks occupies two half-blocks +; substract 6 tiles from y, substract 6 tiles from x. +; the '6's correspond to the 3 extra blocks in each margin of wOverworldMapBlocks. + sub 6 + ld [wPlayerMockXCoord], a + ld a, d + sub 6 + ld [wPlayerMockYCoord], a + call CheckPlayerMockSpriteOutOfScreen + ret ; c or nc + +; return nc if either X or Y coord is too negative to be visible in the screen. +; this corresponds to half-block coords -5 and -6, which are visible by wOverworldMapBlocks, +; but not by the map sprite engine when it adds 4 to X and to Y to obtain wPlayerObject coords. +CheckPlayerMockSpriteOutOfScreen: + ld a, [wPlayerMockXCoord] + ld b, a + ld a, [wPlayerMockYCoord] + ld c, a + ld a, -5 + cp b + ret z ; nc + cp c + ret z ; nc + dec a ; -6 + cp b + ret z ; nc + cp c + ret z ; nc + scf + ret diff --git a/engine/overworld/scripting.asm b/engine/overworld/scripting.asm index 0d8f595d2..7a56e3c51 100644 --- a/engine/overworld/scripting.asm +++ b/engine/overworld/scripting.asm @@ -1248,10 +1248,12 @@ Script_reloadmapafterviewmapmode: ld hl, wDisplaySecondarySprites set SECONDARYSPRITES_SPACES_LEFT_F, [hl] set SECONDARYSPRITES_BRANCH_SPACE_F, [hl] +; load the saved facing direction to wPlayerSpriteSetupFlags ld hl, wPlayerSpriteSetupFlags -; get the facing direction from the mocked object's facing direction - ld a, [wMapObject{d:LAST_OBJECT}Movement] - sub SPRITEMOVEDATA_STANDING_DOWN + ld a, [wBeforeViewMapDirection] + srl a + srl a + maskbits NUM_DIRECTIONS ld [hl], a set PLAYERSPRITESETUP_CUSTOM_FACING_F, [hl] ld a, BOARDEVENT_RESUME_BRANCH diff --git a/macros/code.asm b/macros/code.asm index bb3b904ba..4e20e8c67 100644 --- a/macros/code.asm +++ b/macros/code.asm @@ -27,6 +27,23 @@ endc jp hl ENDM +MACRO jumptable_bc +if STRIN("\2", "h") == 1 + ldh a, [\2] +else + ld a, [\2] +endc + ld c, a + ld b, 0 + ld hl, \1 + add hl, bc + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + jp hl +ENDM + MACRO maskbits ; masks just enough bits to cover values 0 to \1 - 1 ; \2 is an optional shift amount diff --git a/ram/wram.asm b/ram/wram.asm index 138bb99a6..5f6f467c2 100644 --- a/ram/wram.asm +++ b/ram/wram.asm @@ -1715,6 +1715,7 @@ wViewMapModeDisplacementX:: db ; coords and map backup to know where to spawn after returning from View Map mode wBeforeViewMapYCoord:: db wBeforeViewMapXCoord:: db +wBeforeViewMapDirection:: db wBeforeViewMapMapGroup:: db wBeforeViewMapMapNumber:: db ENDU @@ -1724,12 +1725,20 @@ wStartMenuLastCursorPosition:: wBoardMenuLastCursorPosition:: db +UNION + wCurBattleMon:: ; index of the player's mon currently in battle (0-5) db wCurMoveNum:: db +NEXTU +; temp variables used within MockPlayerObject during View Map mode +wPlayerMockYCoord:: db +wPlayerMockXCoord:: db +ENDU + wLastPocket:: db wPCItemsCursor:: db