pokecrystal-board/engine/overworld/player_object.asm

932 lines
14 KiB
NASM

BlankScreen:
call DisableSpriteUpdates
xor a
ldh [hBGMapMode], a
call ClearBGPalettes
call ClearSprites
hlcoord 0, 0
ld bc, wTilemapEnd - wTilemap
ld a, " "
call ByteFill
hlcoord 0, 0, wAttrmap
ld bc, wAttrmapEnd - wAttrmap
ld a, $7
call ByteFill
call WaitBGMap2
call SetPalettes
ret
SpawnPlayer:
ld a, -1
ld [wObjectFollow_Leader], a
ld [wObjectFollow_Follower], a
ld a, PLAYER
ld hl, PlayerObjectTemplate
call CopyPlayerObjectTemplate
ld b, PLAYER
call PlayerSpawn_ConvertCoords
ld a, PLAYER_OBJECT
call GetMapObject
ld hl, MAPOBJECT_PALETTE
add hl, bc
ln e, PAL_NPC_RED, OBJECTTYPE_SCRIPT
ld a, [wPlayerSpriteSetupFlags]
bit PLAYERSPRITESETUP_FEMALE_TO_MALE_F, a
jr nz, .ok
ld a, [wPlayerGender]
bit PLAYERGENDER_FEMALE_F, a
jr z, .ok
ln e, PAL_NPC_BLUE, OBJECTTYPE_SCRIPT
.ok
ld [hl], e
ld a, PLAYER_OBJECT
ldh [hMapObjectIndex], a
ld bc, wMapObjects
ld a, PLAYER_OBJECT
ldh [hObjectStructIndex], a
ld de, wObjectStructs
call CopyMapObjectToObjectStruct
ld a, PLAYER
ld [wCenteredObject], a
ret
PlayerObjectTemplate:
; A dummy map object used to initialize the player object.
; Shorter than the actual amount copied by two bytes.
; Said bytes seem to be unused.
object_event -4, -4, SPRITE_CHRIS, SPRITEMOVEDATA_PLAYER, 15, 15, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, 0, -1
CopyDECoordsToMapObject::
push de
ld a, b
call GetMapObject
pop de
ld hl, MAPOBJECT_X_COORD
add hl, bc
ld [hl], d
ld hl, MAPOBJECT_Y_COORD
add hl, bc
ld [hl], e
ret
PlayerSpawn_ConvertCoords:
push bc
ld a, [wXCoord]
add 4
ld d, a
ld a, [wYCoord]
add 4
ld e, a
pop bc
call CopyDECoordsToMapObject
ret
WriteObjectXY::
ld a, b
call CheckObjectVisibility
ret c
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
ldh a, [hMapObjectIndex]
ld b, a
call CopyDECoordsToMapObject
and a
ret
RefreshPlayerCoords::
ld a, [wXCoord]
add 4
ld d, a
ld hl, wPlayerMapX
sub [hl]
ld [hl], d
ld hl, wMapObjects + MAPOBJECT_X_COORD
ld [hl], d
ld hl, wPlayerLastMapX
ld [hl], d
ld d, a
ld a, [wYCoord]
add 4
ld e, a
ld hl, wPlayerMapY
sub [hl]
ld [hl], e
ld hl, wMapObjects + MAPOBJECT_Y_COORD
ld [hl], e
ld hl, wPlayerLastMapY
ld [hl], e
ld e, a
; the next three lines are useless
ld a, [wObjectFollow_Leader]
cp PLAYER
ret nz
ret
CopyObjectStruct::
call CheckObjectMask
and a
ret nz ; masked
ld hl, wObjectStructs + OBJECT_LENGTH * 1
ld a, 1
ld de, OBJECT_LENGTH
.loop
ldh [hObjectStructIndex], a
ld a, [hl]
and a
jr z, .done
add hl, de
ldh a, [hObjectStructIndex]
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
scf
ret ; overflow
.done
ld d, h
ld e, l
call CopyMapObjectToObjectStruct
ld hl, wVramState
bit 7, [hl]
ret z
ld hl, OBJECT_FLAGS2
add hl, de
set 5, [hl]
ret
CopyMapObjectToObjectStruct:
call .CopyMapObjectToTempObject
call CopyTempObjectToObjectStruct
ret
.CopyMapObjectToTempObject:
ldh a, [hObjectStructIndex]
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld [hl], a
ldh a, [hMapObjectIndex]
ld [wTempObjectCopyMapObjectIndex], a
ld hl, MAPOBJECT_SPRITE
add hl, bc
ld a, [hl]
ld [wTempObjectCopySprite], a
call GetSpriteVTile
ld [wTempObjectCopySpriteVTile], a
ld a, [hl]
call GetSpritePalette
ld [wTempObjectCopyPalette], a
ld hl, MAPOBJECT_PALETTE
add hl, bc
ld a, [hl]
and MAPOBJECT_PALETTE_MASK
jr z, .skip_color_override
swap a
and PALETTE_MASK
ld [wTempObjectCopyPalette], a
.skip_color_override
ld hl, MAPOBJECT_MOVEMENT
add hl, bc
ld a, [hl]
ld [wTempObjectCopyMovement], a
ld hl, MAPOBJECT_SIGHT_RANGE
add hl, bc
ld a, [hl]
ld [wTempObjectCopyRange], a
ld hl, MAPOBJECT_X_COORD
add hl, bc
ld a, [hl]
ld [wTempObjectCopyX], a
ld hl, MAPOBJECT_Y_COORD
add hl, bc
ld a, [hl]
ld [wTempObjectCopyY], a
ld hl, MAPOBJECT_RADIUS
add hl, bc
ld a, [hl]
ld [wTempObjectCopyRadius], a
ret
InitializeVisibleSprites:
ld bc, wMapObject1
ld a, 1
.loop
ldh [hMapObjectIndex], a
ld hl, MAPOBJECT_SPRITE
add hl, bc
ld a, [hl]
and a
jr z, .next
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp -1
jr nz, .next
ld a, [wXCoord]
ld d, a
ld a, [wYCoord]
ld e, a
ld hl, MAPOBJECT_X_COORD
add hl, bc
ld a, [hl]
add 1
sub d
jr c, .next
cp MAPOBJECT_SCREEN_WIDTH
jr nc, .next
ld hl, MAPOBJECT_Y_COORD
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .next
cp MAPOBJECT_SCREEN_HEIGHT
jr nc, .next
push bc
call CopyObjectStruct
pop bc
jp c, .ret
.next
ld hl, MAPOBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECTS
jr nz, .loop
ret
.ret
ret
CheckObjectEnteringVisibleRange::
nop
ld a, [wPlayerStepDirection]
cp STANDING
ret z
ld hl, .dw
rst JumpTable
ret
.dw
dw .Down
dw .Up
dw .Left
dw .Right
.Up:
ld a, [wYCoord]
sub 1
jr .Vertical
.Down:
ld a, [wYCoord]
add 9
.Vertical:
ld d, a
ld a, [wXCoord]
ld e, a
ld bc, wMapObject1
ld a, 1
.loop_v
ldh [hMapObjectIndex], a
ld hl, MAPOBJECT_SPRITE
add hl, bc
ld a, [hl]
and a
jr z, .next_v
ld hl, MAPOBJECT_Y_COORD
add hl, bc
ld a, d
cp [hl]
jr nz, .next_v
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp -1
jr nz, .next_v
ld hl, MAPOBJECT_X_COORD
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .next_v
cp MAPOBJECT_SCREEN_WIDTH
jr nc, .next_v
push de
push bc
call CopyObjectStruct
pop bc
pop de
.next_v
ld hl, MAPOBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECTS
jr nz, .loop_v
ret
.Left:
ld a, [wXCoord]
sub 1
jr .Horizontal
.Right:
ld a, [wXCoord]
add 10
.Horizontal:
ld e, a
ld a, [wYCoord]
ld d, a
ld bc, wMapObject1
ld a, 1
.loop_h
ldh [hMapObjectIndex], a
ld hl, MAPOBJECT_SPRITE
add hl, bc
ld a, [hl]
and a
jr z, .next_h
ld hl, MAPOBJECT_X_COORD
add hl, bc
ld a, e
cp [hl]
jr nz, .next_h
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp -1
jr nz, .next_h
ld hl, MAPOBJECT_Y_COORD
add hl, bc
ld a, [hl]
add 1
sub d
jr c, .next_h
cp MAPOBJECT_SCREEN_HEIGHT
jr nc, .next_h
push de
push bc
call CopyObjectStruct
pop bc
pop de
.next_h
ld hl, MAPOBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECTS
jr nz, .loop_h
ret
CopyTempObjectToObjectStruct:
ld a, [wTempObjectCopyMapObjectIndex]
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, de
ld [hl], a
ld a, [wTempObjectCopyMovement]
call CopySpriteMovementData
ld a, [wTempObjectCopyPalette]
ld hl, OBJECT_PALETTE
add hl, de
or [hl]
ld [hl], a
ld a, [wTempObjectCopyY]
call .InitYCoord
ld a, [wTempObjectCopyX]
call .InitXCoord
ld a, [wTempObjectCopySprite]
ld hl, OBJECT_SPRITE
add hl, de
ld [hl], a
ld a, [wTempObjectCopySpriteVTile]
ld hl, OBJECT_SPRITE_TILE
add hl, de
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, de
ld [hl], STEP_TYPE_RESET
ld hl, OBJECT_FACING
add hl, de
ld [hl], STANDING
ld a, [wTempObjectCopyRadius]
call .InitRadius
ld a, [wTempObjectCopyRange]
ld hl, OBJECT_RANGE
add hl, de
ld [hl], a
and a
ret
.InitYCoord:
ld hl, OBJECT_INIT_Y
add hl, de
ld [hl], a
ld hl, OBJECT_MAP_Y
add hl, de
ld [hl], a
ld hl, wYCoord
sub [hl]
and $f
swap a
ld hl, wPlayerBGMapOffsetY
sub [hl]
ld hl, OBJECT_SPRITE_Y
add hl, de
ld [hl], a
ret
.InitXCoord:
ld hl, OBJECT_INIT_X
add hl, de
ld [hl], a
ld hl, OBJECT_MAP_X
add hl, de
ld [hl], a
ld hl, wXCoord
sub [hl]
and $f
swap a
ld hl, wPlayerBGMapOffsetX
sub [hl]
ld hl, OBJECT_SPRITE_X
add hl, de
ld [hl], a
ret
.InitRadius:
ld h, a
inc a
and $f
ld l, a
ld a, h
add $10
and $f0
or l
ld hl, OBJECT_RADIUS
add hl, de
ld [hl], a
ret
TrainerOrTalkerWalkToPlayer:
ldh a, [hLastTalked]
call InitMovementBuffer
ld a, movement_step_sleep
call AppendToMovementBuffer
ld a, [wSeenTrainerOrTalkerDistance]
dec a
jr z, .TerminateStep
ldh a, [hLastTalked]
ld b, a
ld c, PLAYER
ld d, 1
call .GetPathToPlayer
call DecrementMovementBufferCount
.TerminateStep:
ld a, movement_step_end
call AppendToMovementBuffer
ret
.GetPathToPlayer:
push de
push bc
; get player object struct, load to de
ld a, c
call GetMapObject
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
call GetObjectStruct
ld d, b
ld e, c
; get last talked object struct, load to bc
pop bc
ld a, b
call GetMapObject
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
call GetObjectStruct
; get last talked coords, load to bc
ld hl, OBJECT_MAP_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld c, [hl]
ld b, a
; get player coords, load to de
ld hl, OBJECT_MAP_X
add hl, de
ld a, [hl]
ld hl, OBJECT_MAP_Y
add hl, de
ld e, [hl]
ld d, a
pop af
call ComputePathToWalkToPlayer
ret
SurfStartStep:
call InitMovementBuffer
call .GetMovementData
call AppendToMovementBuffer
ld a, movement_step_end
call AppendToMovementBuffer
ret
.GetMovementData:
ld a, [wPlayerDirection]
srl a
srl a
maskbits NUM_DIRECTIONS
ld e, a
ld d, 0
ld hl, .movement_data
add hl, de
ld a, [hl]
ret
.movement_data
slow_step DOWN
slow_step UP
slow_step LEFT
slow_step RIGHT
FollowNotExact::
push bc
ld a, c
call CheckObjectVisibility
ld d, b
ld e, c
pop bc
ret c
ld a, b
call CheckObjectVisibility
ret c
; object 2 is now in bc, object 1 is now in de
ld hl, OBJECT_MAP_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld c, [hl]
ld b, a
ld hl, OBJECT_MAP_X
add hl, de
ld a, [hl]
cp b
jr z, .same_x
jr c, .to_the_left
inc b
jr .continue
.to_the_left
dec b
jr .continue
.same_x
ld hl, OBJECT_MAP_Y
add hl, de
ld a, [hl]
cp c
jr z, .continue
jr c, .below
inc c
jr .continue
.below
dec c
.continue
ld hl, OBJECT_MAP_X
add hl, de
ld [hl], b
ld a, b
ld hl, wXCoord
sub [hl]
and $f
swap a
ld hl, wPlayerBGMapOffsetX
sub [hl]
ld hl, OBJECT_SPRITE_X
add hl, de
ld [hl], a
ld hl, OBJECT_MAP_Y
add hl, de
ld [hl], c
ld a, c
ld hl, wYCoord
sub [hl]
and $f
swap a
ld hl, wPlayerBGMapOffsetY
sub [hl]
ld hl, OBJECT_SPRITE_Y
add hl, de
ld [hl], a
ldh a, [hObjectStructIndex]
ld hl, OBJECT_RANGE
add hl, de
ld [hl], a
ld hl, OBJECT_MOVEMENT_TYPE
add hl, de
ld [hl], SPRITEMOVEDATA_FOLLOWNOTEXACT
ld hl, OBJECT_STEP_TYPE
add hl, de
ld [hl], STEP_TYPE_RESET
ret
GetRelativeFacing::
; Determines which way map object e would have to turn to face map object d. Returns carry if it's impossible for whatever reason.
ld a, d
call GetMapObject
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp NUM_OBJECT_STRUCTS
jr nc, .carry
ld d, a
ld a, e
call GetMapObject
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp NUM_OBJECT_STRUCTS
jr nc, .carry
ld e, a
call .GetFacing_e_relativeto_d
ret
.carry
scf
ret
.GetFacing_e_relativeto_d:
; Determines which way object e would have to turn to face object d. Returns carry if it's impossible.
; load the coordinates of object d into bc
ld a, d
call GetObjectStruct
ld hl, OBJECT_MAP_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld c, [hl]
ld b, a
push bc
; load the coordinates of object e into de
ld a, e
call GetObjectStruct
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
pop bc
; |x1 - x2|
ld a, b
sub d
jr z, .same_x_1
jr nc, .b_right_of_d_1
cpl
inc a
.b_right_of_d_1
; |y1 - y2|
ld h, a
ld a, c
sub e
jr z, .same_y_1
jr nc, .c_below_e_1
cpl
inc a
.c_below_e_1
; |y1 - y2| - |x1 - x2|
sub h
jr c, .same_y_1
.same_x_1
; compare the y coordinates
ld a, c
cp e
jr z, .same_x_and_y
jr c, .c_directly_below_e
; c directly above e
ld d, DOWN
and a
ret
.c_directly_below_e
ld d, UP
and a
ret
.same_y_1
ld a, b
cp d
jr z, .same_x_and_y
jr c, .b_directly_right_of_d
; b directly left of d
ld d, RIGHT
and a
ret
.b_directly_right_of_d
ld d, LEFT
and a
ret
.same_x_and_y
scf
ret
QueueFollowerFirstStep:
call .QueueFirstStep
jr c, .same
ld [wFollowMovementQueue], a
xor a
ld [wFollowerMovementQueueLength], a
ret
.same
ld a, -1
ld [wFollowerMovementQueueLength], a
ret
.QueueFirstStep:
ld a, [wObjectFollow_Leader]
call GetObjectStruct
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
ld a, [wObjectFollow_Follower]
call GetObjectStruct
ld hl, OBJECT_MAP_X
add hl, bc
ld a, d
cp [hl]
jr z, .check_y
jr c, .left
and a
ld a, movement_step + RIGHT
ret
.left
and a
ld a, movement_step + LEFT
ret
.check_y
ld hl, OBJECT_MAP_Y
add hl, bc
ld a, e
cp [hl]
jr z, .same_xy
jr c, .up
and a
ld a, movement_step + DOWN
ret
.up
and a
ld a, movement_step + UP
ret
.same_xy
scf
ret
MockPlayerObject::
; refresh wPlayerObjectYCoord and wPlayerObjectXCoord
farcall RefreshPlayerCoords
; copy default sprite object to the last object struct
ld hl, .DefaultPlayerObject
ld de, wMapObject{d:LAST_OBJECT}
ld bc, OBJECT_EVENT_SIZE + 1
call CopyBytes
; adjust sprite id and palette number
ld hl, .PlayerObjectFields
.loop
ld a, [wPlayerGender]
cp [hl]
inc hl
jr nz, .next1
ld a, [wPlayerState]
cp [hl]
inc hl
jr nz, .next2
; found a match
ld a, [hli] ; sprite
ld [wMapObject{d:LAST_OBJECT}Sprite], a
ld a, [hl] ; palette | objecttype
ld [wMapObject{d:LAST_OBJECT}Palette], a ; also wMapObject{d:LAST_OBJECT}Type
jr .copy_player_coords
.next1
inc hl
.next2
inc hl
inc hl
ld a, [hl]
cp -1
jr nz, .loop
.copy_player_coords
; copy player's coordinates
ld hl, wPlayerObjectYCoord
ld de, wMapObject{d:LAST_OBJECT}YCoord
ld a, [hli]
ld [de], a
inc de
ld a, [hl] ; wPlayerObjectXCoord
ld [de], a ; wMapObject{d:LAST_OBJECT}XCoord
; set facing direction
ld a, [wPlayerDirection]
srl a
srl a
maskbits NUM_DIRECTIONS
ld b, SPRITEMOVEDATA_STANDING_DOWN
add b
ld [wMapObject{d:LAST_OBJECT}Movement], a
; display mocked player object
; it will go to the last wMapObjects slot and to whichever wObjectStructs slot
; wObjectStructs[n][MAPOBJECT_OBJECT_STRUCT_ID] links both structs
ld a, NUM_OBJECTS - 1
call UnmaskCopyMapObjectStruct
ret
.DefaultPlayerObject:
db -1 ; MAPOBJECT_OBJECT_STRUCT_ID
object_event 0, 0, SPRITE_CHRIS, SPRITEMOVEDATA_STANDING_DOWN, 0, 0, -1, -1, PAL_NPC_RED, OBJECTTYPE_SCRIPT, 0, ObjectEvent, -1
.PlayerObjectFields:
; [wPlayerGender], [wPlayerState], sprite id, palette
db 0, PLAYER_NORMAL, SPRITE_CHRIS, PAL_NPC_RED << 4 | OBJECTTYPE_SCRIPT
db 1 << PLAYERGENDER_FEMALE_F, PLAYER_NORMAL, SPRITE_KRIS, PAL_NPC_BLUE << 4 | OBJECTTYPE_SCRIPT
db 0, PLAYER_SURF, SPRITE_SURF, PAL_NPC_RED << 4 | OBJECTTYPE_SCRIPT
db 1 << PLAYERGENDER_FEMALE_F, PLAYER_SURF, SPRITE_SURF, PAL_NPC_BLUE << 4 | OBJECTTYPE_SCRIPT
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