Multiplayer engine: player objects (re)positioning logic [Commit 6] (#40)

This commit is contained in:
xCrystal
2024-03-03 17:21:01 +01:00
parent 9c641d3ca1
commit 340ef4147f
5 changed files with 82 additions and 47 deletions

View File

@@ -57,4 +57,5 @@ MapSetupCommands:
add_mapsetup BackupMapObjects ; 32
add_mapsetup LoadDisabledSpaces ; 33
add_mapsetup MapCallbackAtEndMapSetup ; 34
add_mapsetup RepositionMockedPlayerObject ; 35
add_mapsetup RepositionMockedPlayerObjects_Connection ; 35
add_mapsetup RepositionMockedPlayerObjects_NextPlayer ; 36

View File

@@ -93,7 +93,7 @@ MapSetupScript_Connection:
mapsetup RefreshPlayerCoords
mapsetup LoadBlockData
mapsetup LoadDisabledSpaces
mapsetup RepositionMockedPlayerObject
mapsetup RepositionMockedPlayerObjects_Connection
mapsetup LoadMapTileset
mapsetup SaveScreen
mapsetup LoadMapObjects
@@ -149,6 +149,7 @@ MapSetupScript_NextPlayer:
mapsetup GetMapScreenCoords
mapsetup LoadBlockData
mapsetup LoadDisabledSpaces
mapsetup RepositionMockedPlayerObjects_NextPlayer
mapsetup BufferScreen
mapsetup LoadMapGraphics
mapsetup LoadMapTimeOfDay

View File

@@ -248,7 +248,7 @@ The backing up of disabled spaces to WRAM is done individually per space the mom
When a map is entered during a level, if the map has been visited before during the current level, there will be backup disabled space and map object data for it. Its existence is looked up in *wDisabledSpacesBackups* and *wMapObjectsBackups* by searching for an entry matching the map group and map id of the map being entered (*LoadDisabledSpaces* map setup command and an extension to *CopyMapObjectEvents* all the way from the *LoadMapAttributes* map setup command).
Regarding map connections, note that, while a mocked player sprite during view map mode remains visible when crossing a connection by "respawning" it in the destination map (see *RepositionMockedPlayerObject*), the "respawning" of other map objects across map connections isn't supported. If you for example put an NPC too close to a map connection, it will disappear from the screen when crossing the connection, just like in Pokemon Crystal.
Regarding map connections, note that, while a mocked player sprite during view map mode remains visible when crossing a connection by "respawning" it in the destination map (see *RepositionMockedPlayerObjects_Connection*), the "respawning" of other map objects across map connections isn't supported. If you for example put an NPC too close to a map connection, it will disappear from the screen when crossing the connection, just like in Pokemon Crystal.
# Game navigation and progression

View File

@@ -854,26 +854,60 @@ QueueFollowerFirstStep:
scf
ret
PositionAllPlayerObjectsExceptCurrent:
; called by MAPSETUP_NEXTPLAYER
; calls RepositionMockedPlayerObject for each player according to wNumLevelPlayers,
; except for the player at wCurTurnPlayer.
ret
RepositionMockedPlayerObjects_NextPlayer:
; map setup command called by MAPSETUP_NEXTPLAYER after LoadBlockData,
; once the new map blocks have been loaded to wOverworldMapBlocks.
; position all players according to wNumLevelPlayers, except for the player at wCurTurnPlayer (which isn't being mocked).
ld a, [wNumLevelPlayers]
maskbits MAX_PLAYERS
cp 1
ret z ; return if single player mode
.loop
dec a
cp -1
ret z
ld hl, wCurTurnPlayer
cp [hl]
jr z, .loop
ld [wMockingWhichPlayer], a
push af
call RepositionMockedPlayerObject
pop af
jr .loop
RepositionMockedPlayerObject::
RepositionMockedPlayerObjects_Connection::
; 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
; During BOARDEVENT_VIEW_MAP_MODE:
; position all players according to wNumLevelPlayers.
; Otherwise:
; position all players according to wNumLevelPlayers, except for the player at wCurTurnPlayer (which isn't being mocked).
; this mode is the same as RepositionMockedPlayerObjects_NextPlayer
ldh a, [hCurBoardEvent]
cp BOARDEVENT_VIEW_MAP_MODE
ret nz
jr nz, RepositionMockedPlayerObjects_NextPlayer
ld a, [wNumLevelPlayers]
maskbits MAX_PLAYERS
.loop
dec a
cp -1
ret z
ld [wMockingWhichPlayer], a
push af
call RepositionMockedPlayerObject
pop af
jr .loop
; if map at wPlayer1MapGroup, wPlayer1MapNumber is not the current map,
; or a map connected to the current map, we are done.
ld hl, wPlayer1MapGroup
ld a, [hli]
RepositionMockedPlayerObject:
; if map at wPlayer*MapGroup, wPlayer*MapNumber is not the current map, do nothing.
; otherwise mock this player object.
ld a, [wMockingWhichPlayer]
ld hl, wPlayer1Location
ld bc, wPlayer2Location - wPlayer1Location
call AddNTimes
ld a, [hli] ; wPlayer*MapGroup
ld b, a
ld c, [hl] ; wPlayer1MapNumber
ld c, [hl] ; wPlayer*MapNumber
ld a, [wMapGroup]
cp b
jr nz, .next_map_1
@@ -917,10 +951,14 @@ RepositionMockedPlayerObject::
ld [wTempByteValue], a
ld hl, .got_sprite_coords
push hl
ld a, [wPlayer1XCoord]
ld d, a
ld a, [wPlayer1YCoord]
ld a, [wMockingWhichPlayer]
ld hl, wPlayer1YCoord
ld bc, wPlayer2YCoord - wPlayer1YCoord
call AddNTimes
ld a, [hli] ; wPlayer*YCoord
ld e, a
ld a, [hl] ; wPlayer*XCoord
ld d, a
jumptable_bc .Jumptable, wTempByteValue
.Jumptable:
@@ -931,27 +969,21 @@ RepositionMockedPlayerObject::
.got_sprite_coords
ret nc ; return if sprite is not in visible part of connected map
xor a ; PLAYER_1
ld [wMockingWhichPlayer], a
jr MockPlayerObject.loaded_player_mock_coords
MockPlayerObject_Multiplayer::
; input: a: value to write into wMockingWhichPlayer
maskbits MAX_PLAYERS
ld [wMockingWhichPlayer], a
; ld a, [wMockingWhichPlayer]
; hl is y,x coordinates: load them into wPlayer*MockYCoord and wPlayer*MockXCoord
push hl
ld a, [wMockingWhichPlayer]
ld hl, wPlayer1MockYCoord
ld bc, wPlayer2MockYCoord - wPlayer1MockYCoord
call AddNTimes
ld d, h
ld e, l
ld a, [wMockingWhichPlayer]
ld hl, wPlayer1YCoord
ld bc, wPlayer2YCoord - wPlayer1YCoord
call AddNTimes
jr MockPlayerObject
pop de
ld [hl], d
inc hl
ld [hl], e
jr MockPlayerObject.loaded_player_mock_coords
MockPlayerObject_ViewMapMode::
; called from .EnterViewMapMode either in board menu or branch space.
; mocks just PLAYER_1 to wMapObject{d:PLAYER_1_MOCK_OBJECT}.
farcall RefreshPlayerCoords ; refresh wPlayerObjectYCoord and wPlayerObjectXCoord (not strictly necessary)
xor a ; PLAYER_1
ld [wMockingWhichPlayer], a
@@ -1226,8 +1258,8 @@ GetWestConnectedSpriteCoords:
xor a
ret ; nc
; load into wPlayer1MockYCoord and wPlayer1MockXCoord the coordinates
; that correspond to wOverworldMapBlocks address at hl.
; load into h and l the y and x coordinates that correspond to wOverworldMapBlocks address at hl.
; return nc if either X or Y coord is too negative to be visible in the screen.
ConvertConnectedOverworldMapBlockAddressToXYCoords:
ld bc, -wOverworldMapBlocks + $10000
add hl, bc
@@ -1254,20 +1286,20 @@ ConvertConnectedOverworldMapBlockAddressToXYCoords:
; 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 [wPlayer1MockXCoord], a
ld l, a
ld a, d
sub 6
ld [wPlayer1MockYCoord], a
call CheckPlayerMockSpriteOutOfScreen
ld h, 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, [wPlayer1MockXCoord]
.CheckPlayerMockSpriteOutOfScreen:
ld a, l ; x coord
ld b, a
ld a, [wPlayer1MockYCoord]
ld a, h ; y coord
ld c, a
ld a, -5
cp b

View File

@@ -2164,10 +2164,11 @@ wBreedingCompatibility::
wMoveGrammar::
wApplyStatLevelMultipliersToEnemy::
wUsePPUp::
wMockingWhichPlayer::
db
wFailedToFlee:: db
wFailedToFlee::
wMockingWhichPlayer::
db
wNumFleeAttempts:: db
wMonTriedToEvolve:: db
@@ -2520,7 +2521,7 @@ wBikeStep:: dw
wKurtApricornQuantity:: db
wCurLevel:: db
wNumLevelPlayers:: db
wNumLevelPlayers:: db ; 1-4
wPlayer1Id:: db
wPlayer2Id:: db
wPlayer3Id:: db
@@ -2579,7 +2580,7 @@ wPlayer2Location:: player_location wPlayer2
wPlayer3Location:: player_location wPlayer3
wPlayer4Location:: player_location wPlayer4
; used in MockPlayerObject_* and RepositionMockedPlayerObject
; used in MockPlayerObject_* and RepositionMockedPlayerObjects_Connection
; player 1: used during a View Map mode session or in multiplayer
; player 2-4: used in multiplayer
wPlayer1MockYCoord:: db