pokecrystal-board/engine/overworld/map_objects.asm
2024-03-09 16:40:21 +01:00

3511 lines
59 KiB
NASM

INCLUDE "data/sprites/facings.asm"
INCLUDE "data/sprites/map_objects.asm"
DeleteMapObject::
push bc
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
push af
ld h, b
ld l, c
ld bc, OBJECT_LENGTH
xor a
call ByteFill
pop af
cp -1
jr z, .ok
bit 7, a
jr nz, .ok
call GetMapObject
ld hl, OBJECT_SPRITE
add hl, bc
ld [hl], -1
.ok
pop bc
ret
HandleObjectStep:
call CheckObjectStillVisible
ret c
call HandleStepType
call HandleObjectAction
ret
CheckObjectStillVisible:
ld hl, OBJECT_FLAGS2
add hl, bc
res OBJ_FLAGS2_6, [hl]
ld a, [wXCoord]
ld e, a
ld hl, OBJECT_MAP_X
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .ok
cp MAPOBJECT_SCREEN_WIDTH
jr nc, .ok
ld a, [wYCoord]
ld e, a
ld hl, OBJECT_MAP_Y
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .ok
cp MAPOBJECT_SCREEN_HEIGHT
jr nc, .ok
jr .yes
.ok
ld hl, OBJECT_FLAGS2
add hl, bc
set OBJ_FLAGS2_6, [hl]
ld a, [wXCoord]
ld e, a
ld hl, OBJECT_INIT_X
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .ok2
cp MAPOBJECT_SCREEN_WIDTH
jr nc, .ok2
ld a, [wYCoord]
ld e, a
ld hl, OBJECT_INIT_Y
add hl, bc
ld a, [hl]
add 1
sub e
jr c, .ok2
cp MAPOBJECT_SCREEN_HEIGHT
jr nc, .ok2
.yes
and a
ret
.ok2
ld hl, OBJECT_FLAGS1
add hl, bc
bit WONT_DELETE_F, [hl]
jr nz, .yes2
call DeleteMapObject
scf
ret
.yes2
ld hl, OBJECT_FLAGS2
add hl, bc
set OBJ_FLAGS2_6, [hl]
and a
ret
HandleStepType:
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld a, [hl]
and a
jr z, .zero
ld hl, OBJECT_FLAGS2
add hl, bc
bit FROZEN_F, [hl]
jr nz, .frozen
cp STEP_TYPE_FROM_MOVEMENT
jr z, .one
jr .ok3
.zero
call StepFunction_Reset
ld hl, OBJECT_FLAGS2
add hl, bc
bit FROZEN_F, [hl]
jr nz, .frozen
.one
call StepFunction_FromMovement
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld a, [hl]
and a
ret z
cp STEP_TYPE_FROM_MOVEMENT
ret z
.ok3
ld hl, StepTypesJumptable
rst JumpTable
ret
.frozen
ret
HandleObjectAction:
ld hl, OBJECT_FLAGS1
add hl, bc
bit INVISIBLE_F, [hl]
jr nz, SetFacingStanding
ld hl, OBJECT_FLAGS2
add hl, bc
bit OBJ_FLAGS2_6, [hl]
jr nz, SetFacingStanding
bit FROZEN_F, [hl]
jr nz, _CallFrozenObjectAction
; use first column (normal)
ld de, ObjectActionPairPointers
jr CallObjectAction
HandleFrozenObjectAction:
ld hl, OBJECT_FLAGS1
add hl, bc
bit INVISIBLE_F, [hl]
jr nz, SetFacingStanding
_CallFrozenObjectAction:
; use second column (frozen)
ld de, ObjectActionPairPointers + 2
jr CallObjectAction ; pointless
CallObjectAction:
ld hl, OBJECT_ACTION
add hl, bc
ld a, [hl]
ld l, a
ld h, 0
add hl, hl
add hl, hl
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
call _hl_
ret
INCLUDE "engine/overworld/map_object_action.asm"
CopyCoordsTileToLastCoordsTile:
ld hl, OBJECT_MAP_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_LAST_MAP_X
add hl, bc
ld [hl], a
ld hl, OBJECT_MAP_Y
add hl, bc
ld a, [hl]
ld hl, OBJECT_LAST_MAP_Y
add hl, bc
ld [hl], a
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld a, [hl]
ld hl, OBJECT_LAST_TILE
add hl, bc
ld [hl], a
call SetTallGrassFlags
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld a, [hl]
call UselessAndA
ret
CopyLastCoordsToCoords:
ld hl, OBJECT_LAST_MAP_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_MAP_X
add hl, bc
ld [hl], a
ld hl, OBJECT_LAST_MAP_Y
add hl, bc
ld a, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld [hl], a
ret
UpdateTallGrassFlags:
ld hl, OBJECT_FLAGS2
add hl, bc
bit OVERHEAD_F, [hl]
jr z, .ok
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld a, [hl]
call SetTallGrassFlags
.ok
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld a, [hl]
call UselessAndA
ret c ; never happens
ld hl, OBJECT_LAST_TILE
add hl, bc
ld a, [hl]
call UselessAndA
ret
SetTallGrassFlags:
call CheckSuperTallGrassTile
jr z, .set
call CheckGrassTile
jr c, .reset
.set
ld hl, OBJECT_FLAGS2
add hl, bc
set OVERHEAD_F, [hl]
ret
.reset
ld hl, OBJECT_FLAGS2
add hl, bc
res OVERHEAD_F, [hl]
ret
UselessAndA:
and a
ret
EndSpriteMovement:
xor a
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], a
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
ld [hli], a
ld [hli], a ; OBJECT_STEP_INDEX
ld [hli], a ; OBJECT_1D
ld [hl], a ; OBJECT_1E
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ret
InitStep:
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], a
ld hl, OBJECT_FLAGS1
add hl, bc
bit FIXED_FACING_F, [hl]
jr nz, GetNextTile
add a
add a
and %00001100
ld hl, OBJECT_DIRECTION
add hl, bc
ld [hl], a
; fallthrough
GetNextTile:
call GetStepVector
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld a, d
call GetStepVectorSign
ld hl, OBJECT_LAST_MAP_X
add hl, bc
add [hl]
ld hl, OBJECT_MAP_X
add hl, bc
ld [hl], a
ld d, a
ld a, e
call GetStepVectorSign
ld hl, OBJECT_LAST_MAP_Y
add hl, bc
add [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld [hl], a
ld e, a
push bc
call GetCoordTileCollision
pop bc
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld [hl], a
ret
AddStepVector:
call GetStepVector
ld hl, OBJECT_SPRITE_X
add hl, bc
ld a, [hl]
add d
ld [hl], a
ld hl, OBJECT_SPRITE_Y
add hl, bc
ld a, [hl]
add e
ld [hl], a
ret
GetStepVector:
; Return (x, y, duration, speed) in (d, e, a, h).
ld hl, OBJECT_WALKING
add hl, bc
ld a, [hl]
and %00001111
add a
add a
ld l, a
ld h, 0
ld de, StepVectors
add hl, de
ld d, [hl]
inc hl
ld e, [hl]
inc hl
ld a, [hli]
ld h, [hl]
ret
StepVectors:
; x, y, duration, speed
; slow
db 0, 1, 16, 1
db 0, -1, 16, 1
db -1, 0, 16, 1
db 1, 0, 16, 1
; normal
db 0, 2, 8, 2
db 0, -2, 8, 2
db -2, 0, 8, 2
db 2, 0, 8, 2
; fast
db 0, 4, 4, 4
db 0, -4, 4, 4
db -4, 0, 4, 4
db 4, 0, 4, 4
GetStepVectorSign:
add a
ret z ; 0 or 128 (-128)
ld a, 1
ret nc ; +1 to +127
ld a, -1
ret ; -127 to -1
UpdatePlayerStep:
ld hl, OBJECT_WALKING
add hl, bc
ld a, [hl]
and %00000011
ld [wPlayerStepDirection], a
call AddStepVector
ld a, [wPlayerStepVectorX]
add d
ld [wPlayerStepVectorX], a
ld a, [wPlayerStepVectorY]
add e
ld [wPlayerStepVectorY], a
ld hl, wPlayerStepFlags
set PLAYERSTEP_CONTINUE_F, [hl]
ret
GetMapObjectField: ; unreferenced
push bc
ld e, a
ld d, 0
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
call GetMapObject
add hl, de
ld a, [hl]
pop bc
ret
RestoreDefaultMovement:
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
cp -1
jr z, .ok
push bc
call GetMapObject
ld hl, MAPOBJECT_MOVEMENT
add hl, bc
ld a, [hl]
pop bc
ret
.ok
ld a, SPRITEMOVEDATA_STANDING_DOWN
ret
ObjectMovement_ZeroAnonJumptableIndex: ; unreferenced
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
ld [hl], 0
ret
ObjectMovement_IncAnonJumptableIndex:
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
inc [hl]
ret
ObjectMovement_DecAnonJumptableIndex:
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
dec [hl]
ret
ObjectMovement_AnonJumptable:
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
ld a, [hl]
pop hl
rst JumpTable
ret
ObjectStep_ZeroAnonJumptableIndex:
ld hl, OBJECT_STEP_INDEX
add hl, bc
ld [hl], 0
ret
ObjectStep_IncAnonJumptableIndex:
ld hl, OBJECT_STEP_INDEX
add hl, bc
inc [hl]
ret
ObjectStep_AnonJumptable:
ld hl, OBJECT_STEP_INDEX
add hl, bc
ld a, [hl]
pop hl
rst JumpTable
ret
ObjectStep_GetAnonJumptableIndex: ; unreferenced
ld hl, OBJECT_STEP_INDEX
add hl, bc
ld a, [hl]
ret
ObjectStep_SetAnonJumptableIndex: ; unreferenced
ld hl, OBJECT_STEP_INDEX
add hl, bc
ld [hl], a
ret
StepFunction_Reset:
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
push bc
call GetCoordTileCollision
pop bc
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld [hl], a
call CopyCoordsTileToLastCoordsTile
call EndSpriteMovement
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_FromMovement:
call ObjectStep_ZeroAnonJumptableIndex
call GetSpriteMovementFunction
ld a, [hl]
ld hl, .Pointers
rst JumpTable
ret
.Pointers:
; entries correspond to SPRITEMOVEFN_* constants (see constants/map_object_constants.asm)
table_width 2, StepFunction_FromMovement.Pointers
dw MovementFunction_Null ; 00
dw MovementFunction_RandomWalkY ; 01
dw MovementFunction_RandomWalkX ; 02
dw MovementFunction_RandomWalkXY ; 03
dw MovementFunction_RandomSpinSlow ; 04
dw MovementFunction_RandomSpinFast ; 05
dw MovementFunction_Standing ; 06
dw MovementFunction_ObeyDPad ; 07
dw MovementFunction_Indexed1 ; 08
dw MovementFunction_Indexed2 ; 09
dw MovementFunction_0a ; 0a
dw MovementFunction_0b ; 0b
dw MovementFunction_0c ; 0c
dw MovementFunction_0d ; 0d
dw MovementFunction_0e ; 0e
dw MovementFunction_Follow ; 0f
dw MovementFunction_Script ; 10
dw MovementFunction_Strength ; 11
dw MovementFunction_FollowNotExact ; 12
dw MovementFunction_Shadow ; 13
dw MovementFunction_Emote ; 14
dw MovementFunction_BigStanding ; 15
dw MovementFunction_Bouncing ; 16
dw MovementFunction_ScreenShake ; 17
dw MovementFunction_SpinClockwise ; 18
dw MovementFunction_SpinCounterclockwise ; 19
dw MovementFunction_BoulderDust ; 1a
dw MovementFunction_ShakingGrass ; 1b
assert_table_length NUM_SPRITEMOVEFN
MovementFunction_Null:
ret
MovementFunction_RandomWalkY:
call Random
ldh a, [hRandomAdd]
and %00000001
jp _RandomWalkContinue
MovementFunction_RandomWalkX:
call Random
ldh a, [hRandomAdd]
and %00000001
or %00000010
jp _RandomWalkContinue
MovementFunction_RandomWalkXY:
call Random
ldh a, [hRandomAdd]
and %00000011
jp _RandomWalkContinue
MovementFunction_RandomSpinSlow:
call Random
ldh a, [hRandomAdd]
and %00001100
ld hl, OBJECT_DIRECTION
add hl, bc
ld [hl], a
jp RandomStepDuration_Slow
MovementFunction_RandomSpinFast:
ld hl, OBJECT_DIRECTION
add hl, bc
ld a, [hl]
and %00001100
ld d, a
call Random
ldh a, [hRandomAdd]
and %00001100
cp d
jr nz, .keep
xor %00001100
.keep
ld [hl], a
jp RandomStepDuration_Fast
MovementFunction_Standing:
call CopyLastCoordsToCoords
call EndSpriteMovement
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STAND
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_RESTORE
ret
MovementFunction_ObeyDPad:
ld hl, GetPlayerNextMovementIndex
jp HandleMovementData
MovementFunction_Indexed1:
ld hl, GetIndexedMovementIndex1
jp HandleMovementData
MovementFunction_Indexed2:
ld hl, GetIndexedMovementIndex2
jp HandleMovementData
MovementFunction_0a:
jp _GetMovementObject
MovementFunction_0b:
jp _GetMovementObject
MovementFunction_0c:
jp _GetMovementObject
MovementFunction_0d:
ld hl, GetPlayerNextMovementIndex
jp HandleMovementData
MovementFunction_0e:
jp _GetMovementObject
MovementFunction_Follow:
ld hl, GetFollowerNextMovementIndex
jp HandleMovementData
MovementFunction_Script:
ld hl, GetMovementIndex
jp HandleMovementData
MovementFunction_Strength:
call ObjectMovement_AnonJumptable
.anon_dw
dw .start
dw .stop
.start:
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld a, [hl]
call CheckPitTile
jr z, .on_pit
ld hl, OBJECT_FLAGS2
add hl, bc
bit OBJ_FLAGS2_2, [hl]
res OBJ_FLAGS2_2, [hl]
jr z, .ok
ld hl, OBJECT_RANGE
add hl, bc
ld a, [hl]
and %00000011
or 0
call InitStep
call CanObjectMoveInDirection
jr c, .ok2
ld de, SFX_STRENGTH
call PlaySFX
call SpawnStrengthBoulderDust
call UpdateTallGrassFlags
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_STRENGTH_BOULDER
ret
.ok2
call CopyLastCoordsToCoords
.ok
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ret
.on_pit
call ObjectMovement_IncAnonJumptableIndex
.stop:
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ret
MovementFunction_FollowNotExact:
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
ld hl, OBJECT_RANGE
add hl, bc
ld a, [hl]
push bc
call GetObjectStruct
ld hl, OBJECT_WALKING
add hl, bc
ld a, [hl]
cp STANDING
jr z, .standing
ld hl, OBJECT_LAST_MAP_X
add hl, bc
ld a, [hl]
cp d
jr z, .equal
jr c, .less
ld a, 3
jr .done
.less
ld a, 2
jr .done
.equal
ld hl, OBJECT_LAST_MAP_Y
add hl, bc
ld a, [hl]
cp e
jr z, .standing
jr c, .less2
ld a, 0
jr .done
.less2
ld a, 1
.done
ld d, a
ld hl, OBJECT_WALKING
add hl, bc
ld a, [hl]
and %00001100
or d
pop bc
jp NormalStep
.standing
pop bc
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STAND
ret
MovementFunction_BigStanding:
call EndSpriteMovement
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_BIG_DOLL_SYM
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_STANDING
ret
MovementFunction_Bouncing:
call EndSpriteMovement
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_BOUNCE
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_STANDING
ret
MovementFunction_SpinCounterclockwise:
call ObjectMovement_AnonJumptable
.anon_dw
dw _MovementSpinInit
dw _MovementSpinRepeat
dw _MovementSpinTurnLeft
MovementFunction_SpinClockwise:
call ObjectMovement_AnonJumptable
.anon_dw
dw _MovementSpinInit
dw _MovementSpinRepeat
dw _MovementSpinTurnRight
_MovementSpinInit:
call EndSpriteMovement
call ObjectMovement_IncAnonJumptableIndex
; fallthrough
_MovementSpinRepeat:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STAND
ld hl, OBJECT_RANGE
add hl, bc
ld a, [hl]
ld a, $10
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_SLEEP
call ObjectMovement_IncAnonJumptableIndex
ret
_MovementSpinTurnLeft:
ld de, .facings_counterclockwise
call _MovementSpinNextFacing
jr MovementFunction_SpinCounterclockwise
.facings_counterclockwise:
db OW_RIGHT
db OW_LEFT
db OW_DOWN
db OW_UP
_MovementSpinTurnRight:
ld de, .facings_clockwise
call _MovementSpinNextFacing
jr MovementFunction_SpinClockwise
.facings_clockwise:
db OW_LEFT
db OW_RIGHT
db OW_UP
db OW_DOWN
_MovementSpinNextFacing:
ld hl, OBJECT_DIRECTION
add hl, bc
ld a, [hl]
and %00001100
rrca
rrca
push hl
ld l, a
ld h, 0
add hl, de
ld a, [hl]
pop hl
ld [hl], a
call ObjectMovement_DecAnonJumptableIndex
ret
MovementFunction_Shadow:
call InitMovementField1dField1e
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SHADOW
ld hl, OBJECT_STEP_DURATION
add hl, de
ld a, [hl]
inc a
add a
add 0
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld hl, OBJECT_WALKING
add hl, de
ld a, [hl]
maskbits NUM_DIRECTIONS
ld d, 1 * TILE_WIDTH + 6
cp DOWN
jr z, .ok
cp UP
jr z, .ok
ld d, 1 * TILE_WIDTH + 4
.ok
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], d
ld hl, OBJECT_SPRITE_X_OFFSET
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_TRACKING_OBJECT
ret
MovementFunction_Emote:
call EndSpriteMovement
call InitMovementField1dField1e
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_EMOTE
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 0
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], -2 * TILE_WIDTH
ld hl, OBJECT_SPRITE_X_OFFSET
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_TRACKING_OBJECT
ret
MovementFunction_BoulderDust:
call EndSpriteMovement
call InitMovementField1dField1e
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_BOULDER_DUST
ld hl, OBJECT_STEP_DURATION
add hl, de
ld a, [hl]
inc a
add a
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld hl, OBJECT_WALKING
add hl, de
ld a, [hl]
and %00000011
ld e, a
ld d, 0
ld hl, .dust_coords
add hl, de
add hl, de
ld d, [hl]
inc hl
ld e, [hl]
ld hl, OBJECT_SPRITE_X_OFFSET
add hl, bc
ld [hl], d
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], e
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_TRACKING_OBJECT
ret
.dust_coords:
; x, y
db 0, -4
db 0, 8
db 6, 2
db -6, 2
MovementFunction_ShakingGrass:
call EndSpriteMovement
call InitMovementField1dField1e
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_GRASS_SHAKE
ld hl, OBJECT_STEP_DURATION
add hl, de
ld a, [hl]
add -1
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_TRACKING_OBJECT
ret
InitMovementField1dField1e:
ld hl, OBJECT_RANGE
add hl, bc
ld a, [hl]
push bc
call GetObjectStruct
ld d, b
ld e, c
pop bc
ld hl, OBJECT_1D
add hl, bc
ld [hl], e
inc hl ; OBJECT_1E
ld [hl], d
ret
MovementFunction_ScreenShake:
call EndSpriteMovement
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_00
ld hl, OBJECT_RANGE
add hl, bc
ld a, [hl]
call .GetDurationAndField1e
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], e
ld hl, OBJECT_1E
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_SCREENSHAKE
ret
.GetDurationAndField1e:
ld d, a
and %00111111
ld e, a
ld a, d
rlca
rlca
and %00000011
ld d, a
inc d
ld a, 1
.loop
dec d
ret z
add a
jr .loop
_RandomWalkContinue:
call InitStep
call CanObjectMoveInDirection
jr c, .new_duration
call UpdateTallGrassFlags
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STEP
ld hl, wCenteredObject
ldh a, [hMapObjectIndex]
cp [hl]
jr z, .centered
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_CONTINUE_WALK
ret
.centered
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_PLAYER_WALK
ret
.new_duration:
call EndSpriteMovement
call CopyLastCoordsToCoords
; fallthrough
RandomStepDuration_Slow:
call Random
ldh a, [hRandomAdd]
and %01111111
jr _SetRandomStepDuration
RandomStepDuration_Fast:
call Random
ldh a, [hRandomAdd]
and %00011111
_SetRandomStepDuration:
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], a
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STAND
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_SLEEP
ret
StepTypesJumptable:
; entries correspond to STEP_TYPE_* constants (see constants/map_object_constants.asm)
table_width 2, StepTypesJumptable
dw StepFunction_Reset ; 00
dw StepFunction_FromMovement ; 01
dw StepFunction_NPCWalk ; 02
dw StepFunction_Sleep ; 03
dw StepFunction_Standing ; 04
dw StepFunction_Restore ; 05
dw StepFunction_PlayerWalk ; 06
dw StepFunction_ContinueWalk ; 07
dw StepFunction_NPCJump ; 08
dw StepFunction_PlayerJump ; 09
dw StepFunction_Turn ; 0a
dw StepFunction_Bump ; 0b
dw StepFunction_TeleportFrom ; 0c
dw StepFunction_TeleportTo ; 0d
dw StepFunction_Skyfall ; 0e
dw StepFunction_StrengthBoulder ; 0f
dw StepFunction_GotBite ; 10
dw StepFunction_RockSmash ; 11
dw StepFunction_DigTo ; 12
dw StepFunction_TrackingObject ; 13
dw StepFunction_14 ; 14
dw StepFunction_ScreenShake ; 15
dw StepFunction_16 ; 16
dw StepFunction_17 ; 17
dw StepFunction_Delete ; 18
dw StepFunction_SkyfallTop ; 19
assert_table_length NUM_STEP_TYPES
WaitStep_InPlace:
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_NPCJump:
call ObjectStep_AnonJumptable
.anon_dw
dw .Jump
dw .Land
.Jump:
call AddStepVector
call UpdateJumpPosition
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call CopyCoordsTileToLastCoordsTile
call GetNextTile
ld hl, OBJECT_FLAGS2
add hl, bc
res OVERHEAD_F, [hl]
call ObjectStep_IncAnonJumptableIndex
ret
.Land:
call AddStepVector
call UpdateJumpPosition
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_PlayerJump:
call ObjectStep_AnonJumptable
.anon_dw
dw .initjump
dw .stepjump
dw .initland
dw .stepland
.initjump
ld hl, wPlayerStepFlags
set PLAYERSTEP_START_F, [hl]
call ObjectStep_IncAnonJumptableIndex
.stepjump
call UpdateJumpPosition
call UpdatePlayerStep
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_FLAGS2
add hl, bc
res OVERHEAD_F, [hl]
ld hl, wPlayerStepFlags
set PLAYERSTEP_STOP_F, [hl]
set PLAYERSTEP_MIDAIR_F, [hl]
call ObjectStep_IncAnonJumptableIndex
ret
.initland
call GetNextTile
ld hl, wPlayerStepFlags
set PLAYERSTEP_START_F, [hl]
call ObjectStep_IncAnonJumptableIndex
.stepland
call UpdateJumpPosition
call UpdatePlayerStep
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, wPlayerStepFlags
set PLAYERSTEP_STOP_F, [hl]
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_TeleportFrom:
call ObjectStep_AnonJumptable
.anon_dw
dw .InitSpin
dw .DoSpin
dw .InitSpinRise
dw .DoSpinRise
.InitSpin:
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
.DoSpin:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SPIN
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call ObjectStep_IncAnonJumptableIndex
ret
.InitSpinRise:
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
ld [hl], $10
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
ld hl, OBJECT_FLAGS2
add hl, bc
res OVERHEAD_F, [hl]
call ObjectStep_IncAnonJumptableIndex
.DoSpinRise:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SPIN
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
inc [hl]
ld a, [hl]
ld d, $60
call Sine
ld a, h
sub $60
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_TeleportTo:
call ObjectStep_AnonJumptable
.anon_dw
dw .InitWait
dw .DoWait
dw .InitDescent
dw .DoDescent
dw .InitFinalSpin
dw .DoFinalSpin
dw .FinishStep
.InitWait:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_00
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
ret
.DoWait:
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call ObjectStep_IncAnonJumptableIndex
.InitDescent:
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
ret
.DoDescent:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SPIN
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
inc [hl]
ld a, [hl]
ld d, $60
call Sine
ld a, h
sub $60
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call ObjectStep_IncAnonJumptableIndex
.InitFinalSpin:
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
ret
.DoFinalSpin:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SPIN
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
.FinishStep:
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_Skyfall:
call ObjectStep_AnonJumptable
.anon_dw
dw .Init
dw .Step
dw .Fall
dw .Finish
.Init:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_00
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
.Step:
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_STEP
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
.Fall:
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
inc [hl]
ld a, [hl]
ld d, $60
call Sine
ld a, h
sub $60
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call ObjectStep_IncAnonJumptableIndex
.Finish:
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_GotBite:
call ObjectStep_AnonJumptable
.anon_dw
dw .Init
dw .Run
.Init:
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 8
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], 0
call ObjectStep_IncAnonJumptableIndex
.Run:
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld a, [hl]
xor 1
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_RockSmash:
call .Step
jp WaitStep_InPlace
.Step:
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld a, [hl]
and %00000001
ld a, OBJECT_ACTION_STAND
jr z, .yes
ld a, OBJECT_ACTION_00
.yes
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], a
ret
StepFunction_DigTo:
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld a, [hl]
and %00000001
ld a, OBJECT_ACTION_SPIN
jr z, .yes
ld a, OBJECT_ACTION_SPIN_FLICKER
.yes
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], a
jp WaitStep_InPlace
StepFunction_Sleep:
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_Delete:
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
jp DeleteMapObject
StepFunction_Bump:
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_Restore:
call ObjectStep_AnonJumptable
.anon_dw
dw .Reset
dw StepFunction_Standing
.Reset:
call RestoreDefaultMovement
call GetInitialFacing
ld hl, OBJECT_DIRECTION
add hl, bc
ld [hl], a
call ObjectStep_IncAnonJumptableIndex
; fallthrough
StepFunction_Standing:
call Stubbed_UpdateYOffset
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ret
StepFunction_NPCWalk:
call Stubbed_UpdateYOffset
call AddStepVector
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_ContinueWalk:
call AddStepVector
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call CopyCoordsTileToLastCoordsTile
jp RandomStepDuration_Slow
StepFunction_PlayerWalk:
call ObjectStep_AnonJumptable
.anon_dw
dw .init
dw .step
.init
ld hl, wPlayerStepFlags
set PLAYERSTEP_START_F, [hl]
call ObjectStep_IncAnonJumptableIndex
.step
call UpdatePlayerStep
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, wPlayerStepFlags
set PLAYERSTEP_STOP_F, [hl]
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_Turn:
call ObjectStep_AnonJumptable
.anon_dw
dw .init1
dw .step1
dw .init2
dw .step2
.init1
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld a, [hl]
ld [hl], 4
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 1
call ObjectStep_IncAnonJumptableIndex
.step1
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
call ObjectStep_IncAnonJumptableIndex
.init2
ld hl, OBJECT_1D ; new facing
add hl, bc
ld a, [hl]
ld hl, OBJECT_DIRECTION
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 3
call ObjectStep_IncAnonJumptableIndex
.step2
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_StrengthBoulder:
call AddStepVector
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
push bc
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
ld b, a
farcall CopyDECoordsToMapObject
pop bc
ld hl, OBJECT_FLAGS2
add hl, bc
res OBJ_FLAGS2_2, [hl]
call CopyCoordsTileToLastCoordsTile
ld hl, OBJECT_WALKING
add hl, bc
ld [hl], STANDING
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
StepFunction_TrackingObject:
ld hl, OBJECT_1D
add hl, bc
ld e, [hl]
inc hl
ld d, [hl]
ld hl, OBJECT_SPRITE
add hl, de
ld a, [hl]
and a
jr z, .nope
ld hl, OBJECT_SPRITE_X
add hl, de
ld a, [hl]
ld hl, OBJECT_SPRITE_X
add hl, bc
ld [hl], a
ld hl, OBJECT_SPRITE_Y
add hl, de
ld a, [hl]
ld hl, OBJECT_SPRITE_Y
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld a, [hl]
and a
ret z
dec [hl]
ret nz
.nope
jp DeleteMapObject
StepFunction_14:
StepFunction_ScreenShake:
call ObjectStep_AnonJumptable
.anon_dw
dw .Init
dw .Run
.Init:
xor a
ld hl, OBJECT_1D
add hl, bc
ld [hl], a
call ObjectStep_IncAnonJumptableIndex
.Run:
ld hl, OBJECT_1D
add hl, bc
ld d, [hl]
ld a, [wPlayerStepVectorY]
sub d
ld [wPlayerStepVectorY], a
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
jr z, .ok
ld a, [hl]
call .GetSign
ld hl, OBJECT_1D
add hl, bc
ld [hl], a
ld d, a
ld a, [wPlayerStepVectorY]
add d
ld [wPlayerStepVectorY], a
ret
.ok
call DeleteMapObject
ret
.GetSign:
ld hl, OBJECT_1E
add hl, bc
and 1
ld a, [hl]
ret z
cpl
inc a
ret
StepFunction_16:
call ObjectStep_AnonJumptable
; jumptable was dummied out here
StepFunction_17:
call ObjectStep_AnonJumptable
.anon_dw
dw .null
dw .null
dw .null
.null
StepFunction_SkyfallTop:
call ObjectStep_AnonJumptable
.anon_dw
dw .Init
dw .Run
.Init:
ld hl, OBJECT_ACTION
add hl, bc
ld [hl], OBJECT_ACTION_SKYFALL
ld hl, OBJECT_STEP_DURATION
add hl, bc
ld [hl], 16
call ObjectStep_IncAnonJumptableIndex
.Run:
ld hl, OBJECT_STEP_DURATION
add hl, bc
dec [hl]
ret nz
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], $60
ld hl, OBJECT_STEP_FRAME
add hl, bc
ld [hl], 0
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_FROM_MOVEMENT
ret
Stubbed_UpdateYOffset:
; dummied out
ret
ld hl, OBJECT_1D
add hl, bc
inc [hl]
ld a, [hl]
srl a
srl a
and %00000111
ld l, a
ld h, 0
ld de, .y_offsets
add hl, de
ld a, [hl]
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], a
ret
.y_offsets:
db 0, -1, -2, -3, -4, -3, -2, -1
UpdateJumpPosition:
call GetStepVector
ld a, h
ld hl, OBJECT_JUMP_HEIGHT
add hl, bc
ld e, [hl]
add e
ld [hl], a
nop
srl e
ld d, 0
ld hl, .y_offsets
add hl, de
ld a, [hl]
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld [hl], a
ret
.y_offsets:
db -4, -6, -8, -10, -11, -12, -12, -12
db -11, -10, -9, -8, -6, -4, 0, 0
GetPlayerNextMovementIndex:
; copy [wPlayerNextMovement] to [wPlayerMovement]
ld a, [wPlayerNextMovement]
ld hl, wPlayerMovement
ld [hl], a
; load [wPlayerNextMovement] with movement_step_sleep
ld a, movement_step_sleep
ld [wPlayerNextMovement], a
; recover the previous value of [wPlayerNextMovement]
ld a, [hl]
ret
GetMovementIndex:
ld hl, wMovementDataBank
call _GetMovementIndex
ret
GetIndexedMovementIndex1:
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
ld e, [hl]
inc [hl]
ld d, 0
ld hl, wMovementObject
ld a, [hli]
ld h, [hl]
ld l, a
add hl, de
ld a, [hl]
ret
GetIndexedMovementIndex2:
ld hl, OBJECT_MOVEMENT_INDEX
add hl, bc
ld e, [hl]
inc [hl]
ld d, 0
ld hl, wIndexedMovement2Pointer
ld a, [hli]
ld h, [hl]
ld l, a
add hl, de
ld a, [hl]
ret
_GetMovementObject:
ld hl, GetMovementObject
jp HandleMovementData
GetMovementObject:
ld a, [wMovementObject]
ret
HandleMovementData:
call .StorePointer
.loop
xor a
ld [wContinueReadingMovement], a
call JumpMovementPointer
call DoMovementFunction
ld a, [wContinueReadingMovement]
and a
jr nz, .loop
ret
.StorePointer:
ld a, l
ld [wMovementPointer], a
ld a, h
ld [wMovementPointer + 1], a
ret
JumpMovementPointer:
ld hl, wMovementPointer
ld a, [hli]
ld h, [hl]
ld l, a
jp hl
ContinueReadingMovement:
ld a, TRUE
ld [wContinueReadingMovement], a
ret
DoMovementFunction:
push af
call ApplyMovementToFollower
pop af
ld hl, MovementPointers
rst JumpTable
ret
INCLUDE "engine/overworld/movement.asm"
ApplyMovementToFollower:
ld e, a
ld a, [wObjectFollow_Follower]
cp -1
ret z
ld a, [wObjectFollow_Leader]
ld d, a
ldh a, [hMapObjectIndex]
cp d
ret nz
ld a, e
cp movement_step_sleep
ret z
cp movement_step_end
ret z
cp movement_step_4b
ret z
cp movement_step_bump
ret z
cp movement_slow_step
ret c
push af
ld hl, wFollowerMovementQueueLength
inc [hl]
ld e, [hl]
ld d, 0
ld hl, wFollowMovementQueue
add hl, de
pop af
ld [hl], a
ret
GetFollowerNextMovementIndex:
ld hl, wFollowerMovementQueueLength
ld a, [hl]
and a
jr z, .done
cp -1
jr z, .done
dec [hl]
ld e, a
ld d, 0
ld hl, wFollowMovementQueue
add hl, de
inc e
ld a, -1
.loop
ld d, [hl]
ld [hld], a
ld a, d
dec e
jr nz, .loop
ret
.done
call .CancelFollowIfLeaderMissing
ret c
ld a, movement_step_sleep
ret
.CancelFollowIfLeaderMissing:
ld a, [wObjectFollow_Leader]
cp -1
jr z, .nope
push bc
call GetObjectStruct
ld hl, OBJECT_SPRITE
add hl, bc
ld a, [hl]
pop bc
and a
jr z, .nope
and a
ret
.nope
ld a, -1
ld [wObjectFollow_Follower], a
ld a, movement_step_end
scf
ret
SpawnShadow:
push bc
ld de, .ShadowObject
call CopyTempObjectData
call InitTempObject
pop bc
ret
.ShadowObject:
; vtile, palette, movement
db $00, PAL_OW_GREY, SPRITEMOVEDATA_SHADOW
SpawnStrengthBoulderDust:
push bc
ld de, .BoulderDustObject
call CopyTempObjectData
call InitTempObject
pop bc
ret
.BoulderDustObject:
; vtile, palette, movement
db $00, PAL_OW_GREY, SPRITEMOVEDATA_BOULDERDUST
SpawnEmote:
push bc
ld de, .EmoteObject
call CopyTempObjectData
call InitTempObject
pop bc
ret
.EmoteObject:
; vtile, palette, movement
db $00, PAL_OW_GREY, SPRITEMOVEDATA_EMOTE
ShakeGrass:
push bc
ld de, .GrassObject
call CopyTempObjectData
call InitTempObject
pop bc
ret
.GrassObject:
; vtile, palette, movement
db $00, PAL_OW_TREE, SPRITEMOVEDATA_GRASS
ShakeScreen:
push bc
push af
ld de, .ScreenShakeObject
call CopyTempObjectData
pop af
ld [wTempObjectCopyRange], a
call InitTempObject
pop bc
ret
.ScreenShakeObject:
; vtile, palette, movement
db $00, PAL_OW_GREY, SPRITEMOVEDATA_SCREENSHAKE
DespawnEmote:
push bc
ldh a, [hMapObjectIndex]
ld c, a
call .DeleteEmote
pop bc
ret
.DeleteEmote:
ld de, wObjectStructs
ld a, NUM_OBJECT_STRUCTS
.loop
push af
ld hl, OBJECT_FLAGS1
add hl, de
bit EMOTE_OBJECT_F, [hl]
jr z, .next
ld hl, OBJECT_SPRITE
add hl, de
ld a, [hl]
and a
jr z, .next
push bc
xor a
ld bc, OBJECT_LENGTH
call ByteFill
pop bc
.next
ld hl, OBJECT_LENGTH
add hl, de
ld d, h
ld e, l
pop af
dec a
jr nz, .loop
ret
InitTempObject:
call FindFirstEmptyObjectStruct
ret nc
ld d, h
ld e, l
farcall CopyTempObjectToObjectStruct
ret
CopyTempObjectData:
; load into wTempObjectCopy:
; -1, -1, [de], [de + 1], [de + 2], [hMapObjectIndex], [NextMapX], [NextMapY], -1
; This spawns the object at the same place as whichever object is loaded into bc.
ld hl, wTempObjectCopyMapObjectIndex
ld [hl], -1
inc hl
ld [hl], -1
inc hl
ld a, [de]
inc de
ld [hli], a
ld a, [de]
inc de
ld [hli], a
ld a, [de]
ld [hli], a
ldh a, [hMapObjectIndex]
ld [hli], a
push hl
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
pop hl
ld [hl], d
inc hl
ld [hl], e
inc hl
ld [hl], -1
ret
UpdateAllObjectsFrozen::
ld a, [wStateFlags]
bit 0, a
ret z
ld bc, wObjectStructs
xor a
.loop
ldh [hMapObjectIndex], a
call DoesObjectHaveASprite
jr z, .ok
call UpdateObjectFrozen
.ok
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
ret
RespawnPlayerAndOpponent:
; called at battle start
call HideAllObjects
ld a, PLAYER
call RespawnObject
ld a, [wBattleScriptFlags]
bit 7, a
jr z, .skip_opponent
ldh a, [hLastTalked]
and a
jr z, .skip_opponent
call RespawnObject
.skip_opponent
call _UpdateActiveSprites
ret
RespawnPlayer:
call HideAllObjects
ld a, PLAYER
call RespawnObject
call _UpdateActiveSprites
ret
RespawnObject:
cp NUM_OBJECTS
ret nc
call GetMapObject
ld hl, MAPOBJECT_OBJECT_STRUCT_ID
add hl, bc
ld a, [hl]
cp -1
ret z
cp NUM_OBJECT_STRUCTS
ret nc
call GetObjectStruct
call DoesObjectHaveASprite
ret z
call UpdateRespawnedObjectFrozen
ret
HideAllObjects:
xor a
ld bc, wObjectStructs
.loop
ldh [hMapObjectIndex], a
call SetFacing_Standing
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
ret
UpdateObjectFrozen:
push bc
call CheckObjectCoveredByTextbox
pop bc
jr c, SetFacing_Standing
call CheckObjectOnScreen
jr c, SetFacing_Standing
call UpdateObjectTile
call HandleFrozenObjectAction
xor a
ret
UpdateRespawnedObjectFrozen:
call CheckObjectOnScreen
jr c, SetFacing_Standing
call HandleFrozenObjectAction
xor a
ret
SetFacing_Standing:
ld hl, OBJECT_FACING
add hl, bc
ld [hl], STANDING
scf
ret
UpdateObjectTile:
push bc
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
call GetCoordTileCollision
pop bc
ld hl, OBJECT_TILE_COLLISION
add hl, bc
ld [hl], a
call UpdateTallGrassFlags
ret
CheckObjectOnScreen:
ld hl, OBJECT_MAP_X
add hl, bc
ld d, [hl]
ld hl, OBJECT_MAP_Y
add hl, bc
ld e, [hl]
inc d
inc e
ld a, [wXCoord]
cp d
jr z, .equal_x
jr nc, .nope
add MAPOBJECT_SCREEN_WIDTH - 1
cp d
jr c, .nope
.equal_x
ld a, [wYCoord]
cp e
jr z, .equal_y
jr nc, .nope
add MAPOBJECT_SCREEN_HEIGHT - 1
cp e
jr c, .nope
.equal_y
xor a
ret
.nope
scf
ret
CheckObjectCoveredByTextbox:
; Check whether the object fits in the screen width.
ld a, [wPlayerBGMapOffsetX]
ld d, a
ld hl, OBJECT_SPRITE_X_OFFSET
add hl, bc
ld a, [hl]
ld hl, OBJECT_SPRITE_X
add hl, bc
add [hl]
add d
cp $f0
jr nc, .ok1
cp SCREEN_WIDTH_PX
jp nc, .disappear
.ok1
; Account for objects currently moving left/right.
and %00000111
ld d, 2
cp TILE_WIDTH / 2
jr c, .ok2
ld d, 3
.ok2
; Convert pixels to tiles.
ld a, [hl]
srl a
srl a
srl a
cp SCREEN_WIDTH
jr c, .ok3
sub BG_MAP_WIDTH
.ok3
ldh [hCurSpriteXCoord], a
; Check whether the object fits in the screen height.
ld a, [wPlayerBGMapOffsetY]
ld e, a
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
ld a, [hl]
ld hl, OBJECT_SPRITE_Y
add hl, bc
add [hl]
add e
cp $f0
jr nc, .ok4
cp SCREEN_HEIGHT_PX
jr nc, .disappear
.ok4
; Account for objects currently moving up/down.
and %00000111
ld e, 2
cp TILE_WIDTH / 2
jr c, .ok5
ld e, 3
.ok5
; Convert pixels to tiles.
ld a, [hl]
srl a
srl a
srl a
cp SCREEN_HEIGHT
jr c, .ok6
sub BG_MAP_HEIGHT
.ok6
ldh [hCurSpriteYCoord], a
; Account for big objects that are twice as wide and high.
ld hl, OBJECT_PALETTE
add hl, bc
bit BIG_OBJECT_F, [hl]
jr z, .ok7
ld a, d
add 2
ld d, a
ld a, e
add 2
ld e, a
.ok7
ld a, d
ldh [hCurSpriteXPixel], a
.loop
ldh a, [hCurSpriteXPixel]
ld d, a
ldh a, [hCurSpriteYCoord]
add e
dec a
ld b, a
.next
ldh a, [hCurSpriteXCoord]
add d
dec a
ld c, a
push bc
call ScrollAwareCoord2Tile
pop bc
; NPCs disappear if standing on tiles FIRST_REGULAR_TEXT_CHAR or above,
; since those IDs are for text characters and textbox frames.
; - if 1bpp text, a single textbox tile overlapping with the sprite makes it disappear.
; - if 2bpp text, sprite only disappears if ALL overlapping tiles are textbox tiles.
ld a, [hl]
cp FIRST_REGULAR_TEXT_CHAR
jr c, .object_not_in_textbox
;.object_in_textbox
ld a, [wText2bpp]
and a
jr z, .disappear
jr .ok8
.object_not_in_textbox
ld a, [wText2bpp]
and a
jr nz, .not_disappear
.ok8
dec d
jr nz, .next
.ok9
; while sprites are centered to tiles in the X axis exactly on top of two adjacent horizonal tiles,
; in the Y axis they occupy 4-8-4 pixels of adjacent vertical tiles instead.
; this is why we loop here one more time (thrice for regular-sized sprites) than horizontally.
dec e
ld a, e
cp $ff
jr nz, .loop
; if we managed make it here without returning early, there are only two options:
; - if 1bpp text, the sprite is wholly outside of a textbox
; - if 2bpp text, the sprite is wholly inside a textbox
ld a, [wText2bpp]
and a
jr z, .not_disappear
.disappear
scf
ret
.not_disappear
and a
ret
HandleNPCStep::
call ResetStepVector
call DoStepsForAllObjects
ret
ResetStepVector:
xor a
ld [wPlayerStepVectorX], a
ld [wPlayerStepVectorY], a
ld [wPlayerStepFlags], a
ld a, STANDING
ld [wPlayerStepDirection], a
ret
DoStepsForAllObjects:
ld bc, wObjectStructs
xor a
.loop
ldh [hMapObjectIndex], a
call DoesObjectHaveASprite
jr z, .next
call HandleObjectStep
.next
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
ldh a, [hMapObjectIndex]
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
ret
RefreshPlayerSprite:
ld a, movement_step_sleep
ld [wPlayerNextMovement], a
ld [wPlayerMovement], a
xor a
ld [wPlayerTurningDirection], a
ld [wPlayerStepFrame], a
call TryResetPlayerAction
farcall CheckWarpFacingDown
call c, SpawnInFacingDown
call SpawnInCustomFacing
ret
TryResetPlayerAction:
ld hl, wPlayerSpriteSetupFlags
bit PLAYERSPRITESETUP_RESET_ACTION_F, [hl]
jr nz, .ok
ret
.ok
ld a, OBJECT_ACTION_00
ld [wPlayerAction], a
ret
SpawnInCustomFacing:
ld hl, wPlayerSpriteSetupFlags
bit PLAYERSPRITESETUP_CUSTOM_FACING_F, [hl]
ret z
ld a, [wPlayerSpriteSetupFlags]
and PLAYERSPRITESETUP_FACING_MASK
add a
add a
jr _ContinueSpawnFacing
SpawnInFacingDown:
ld a, DOWN
_ContinueSpawnFacing:
ld bc, wPlayerStruct
call SetSpriteDirection
ret
_SetPlayerPalette:
ld a, d
and 1 << 7
ret z
ld bc, 0 ; debug?
ld hl, OBJECT_DIRECTION
add hl, bc
ld a, [hl]
or d
ld [hl], a
ld a, d
swap a
and PALETTE_MASK
ld d, a
ld bc, wPlayerStruct
ld hl, OBJECT_PALETTE
add hl, bc
ld a, [hl]
and ~PALETTE_MASK
or d
ld [hl], a
ret
StartFollow::
push bc
ld a, b
call SetLeaderIfVisible
pop bc
ret c
ld a, c
call SetFollowerIfVisible
farcall QueueFollowerFirstStep
ret
SetLeaderIfVisible:
call CheckObjectVisibility
ret c
ldh a, [hObjectStructIndex]
ld [wObjectFollow_Leader], a
ret
StopFollow::
call ResetLeader
call ResetFollower
ret
ResetLeader:
ld a, -1
ld [wObjectFollow_Leader], a
ret
SetFollowerIfVisible:
push af
call ResetFollower
pop af
call CheckObjectVisibility
ret c
ld hl, OBJECT_MOVEMENT_TYPE
add hl, bc
ld [hl], SPRITEMOVEDATA_FOLLOWING
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_RESET
ldh a, [hObjectStructIndex]
ld [wObjectFollow_Follower], a
ret
ResetFollower:
ld a, [wObjectFollow_Follower]
cp -1
ret z
call GetObjectStruct
call ResetObject
ld a, -1
ld [wObjectFollow_Follower], a
ret
FreezeAllOtherObjects::
ld a, c
call CheckObjectVisibility
ret c
push bc
call FreezeAllObjects
pop bc
ld hl, OBJECT_FLAGS2
add hl, bc
res FROZEN_F, [hl]
xor a
ret
FreezeObject: ; unreferenced
call CheckObjectVisibility
ret c
ld hl, OBJECT_FLAGS2
add hl, bc
set FROZEN_F, [hl]
xor a
ret
FreezeAllObjects:
ld bc, wObjectStructs
xor a
.loop
push af
call DoesObjectHaveASprite
jr z, .next
ld hl, OBJECT_FLAGS2
add hl, bc
set FROZEN_F, [hl]
.next
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
pop af
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
ret
_UnfreezeFollowerObject::
ld a, [wObjectFollow_Leader]
cp -1
ret z
push bc
call GetObjectStruct
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
pop bc
cp c
ret nz
ld a, [wObjectFollow_Follower]
cp -1
ret z
call GetObjectStruct
ld hl, OBJECT_FLAGS2
add hl, bc
res FROZEN_F, [hl]
ret
UnfreezeAllObjects::
push bc
ld bc, wObjectStructs
xor a
.loop
push af
call DoesObjectHaveASprite
jr z, .next
ld hl, OBJECT_FLAGS2
add hl, bc
res FROZEN_F, [hl]
.next
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
pop af
inc a
cp NUM_OBJECT_STRUCTS
jr nz, .loop
pop bc
ret
UnfreezeObject: ; unreferenced
call CheckObjectVisibility
ret c
ld hl, OBJECT_FLAGS2
add hl, bc
res FROZEN_F, [hl]
ret
ResetObject:
ld hl, OBJECT_MAP_OBJECT_INDEX
add hl, bc
ld a, [hl]
cp -1
jp z, .set_standing
push bc
call GetMapObject
ld hl, MAPOBJECT_MOVEMENT
add hl, bc
ld a, [hl]
pop bc
ld hl, OBJECT_MOVEMENT_TYPE
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_RESET
ret
.set_standing:
call GetSpriteDirection
rrca
rrca
ld e, a
ld d, 0
ld hl, .standing_movefns
add hl, de
ld a, [hl]
ld hl, OBJECT_MOVEMENT_TYPE
add hl, bc
ld [hl], a
ld hl, OBJECT_STEP_TYPE
add hl, bc
ld [hl], STEP_TYPE_RESET
ret
.standing_movefns:
db SPRITEMOVEDATA_STANDING_DOWN
db SPRITEMOVEDATA_STANDING_UP
db SPRITEMOVEDATA_STANDING_LEFT
db SPRITEMOVEDATA_STANDING_RIGHT
; Used to make the last talked object remain hidden.
; This is done by setting its event flag to special value ALWAYS_HIDDEN.
; Otherwise when data is read from wMapObjectsBackups after entering the map,
; it would appear by default even if it had been made disappear.
SetObjectToRemainHidden:
ldh a, [hLastTalked]
call GetMapObject
ld hl, MAPOBJECT_EVENT_FLAG
add hl, bc
ld de, ALWAYS_HIDDEN
ld [hl], e
inc hl
ld [hl], d
ret
_UpdateActiveSpritesAfterOffset::
ld a, [wStateFlags]
bit 0, a
ret z
jr _UpdateActiveSprites.go
_UpdateActiveSprites::
ld a, [wStateFlags]
bit 0, a
ret z
xor a
ldh [hUsedSpriteIndex], a
.go
ldh a, [hOAMUpdate]
push af
ld a, 1
ldh [hOAMUpdate], a
call InitSprites
call .fill
pop af
ldh [hOAMUpdate], a
ret
.fill
ld a, [wStateFlags]
bit 1, a
ld b, NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH
jr z, .ok
ld b, (NUM_SPRITE_OAM_STRUCTS - 12) * SPRITEOAMSTRUCT_LENGTH
.ok
ldh a, [hUsedSpriteIndex]
cp b
ret nc
ld l, a
ld h, HIGH(wShadowOAM)
ld de, SPRITEOAMSTRUCT_LENGTH
ld a, b
ld c, SCREEN_HEIGHT_PX + 2 * TILE_WIDTH
.loop
ld [hl], c ; y
add hl, de
cp l
jr nz, .loop
ret
ApplyBGMapAnchorToObjects:
push hl
push de
push bc
ld a, [wPlayerBGMapOffsetX]
ld d, a
ld a, [wPlayerBGMapOffsetY]
ld e, a
ld bc, wObjectStructs
ld a, NUM_OBJECT_STRUCTS
.loop
push af
call DoesObjectHaveASprite
jr z, .skip
ld hl, OBJECT_SPRITE_X
add hl, bc
ld a, [hl]
add d
ld [hl], a
ld hl, OBJECT_SPRITE_Y
add hl, bc
ld a, [hl]
add e
ld [hl], a
.skip
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
pop af
dec a
jr nz, .loop
xor a
ld [wPlayerBGMapOffsetX], a
ld [wPlayerBGMapOffsetY], a
pop bc
pop de
pop hl
ret
DEF PRIORITY_LOW EQU $10
DEF PRIORITY_NORM EQU $20
DEF PRIORITY_HIGH EQU $30
InitSprites:
call InitSecondarySprites
call .DeterminePriorities
ld c, PRIORITY_HIGH
call .InitSpritesByPriority
ld c, PRIORITY_NORM
call .InitSpritesByPriority
ld c, PRIORITY_LOW
call .InitSpritesByPriority
ret
.DeterminePriorities:
xor a
ld hl, wObjectPriorities
ld bc, NUM_OBJECT_STRUCTS
call ByteFill
ld d, 0
ld bc, wObjectStructs
ld hl, wObjectPriorities
.loop
push hl
call DoesObjectHaveASprite
jr z, .skip
ld hl, OBJECT_FACING
add hl, bc
ld a, [hl]
cp STANDING
jr z, .skip
; Define the sprite priority.
ld e, PRIORITY_LOW
ld hl, OBJECT_FLAGS2
add hl, bc
bit LOW_PRIORITY_F, [hl]
jr nz, .add
ld e, PRIORITY_NORM
bit HIGH_PRIORITY_F, [hl]
jr z, .add
ld e, PRIORITY_HIGH
jr .add
.skip
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
pop hl
jr .next
.add
ld hl, OBJECT_LENGTH
add hl, bc
ld b, h
ld c, l
pop hl
ld a, d
or e
ld [hli], a
.next
inc d
ld a, d
cp NUM_OBJECT_STRUCTS
jr nz, .loop
ret
.InitSpritesByPriority:
ld hl, wObjectPriorities
.next_sprite
ld a, [hli]
ld d, a
and $f0
ret z
cp c
jr nz, .next_sprite
push bc
push hl
ld a, d
and $f
call .GetObjectStructPointer
call .InitSprite
pop hl
pop bc
jr .next_sprite
.InitSprite:
ld hl, OBJECT_SPRITE_TILE
add hl, bc
ld a, [hl]
and ~(1 << 7)
ldh [hCurSpriteTile], a
xor a
bit 7, [hl]
jr nz, .not_vram1
or VRAM_BANK_1
.not_vram1
ld hl, OBJECT_FLAGS2
add hl, bc
ld e, [hl]
bit OBJ_FLAGS2_7, e
jr z, .not_priority
or PRIORITY
.not_priority
bit USE_OBP1_F, e
jr z, .not_obp_num
or OBP_NUM
.not_obp_num
ld hl, OBJECT_PALETTE
add hl, bc
ld d, a
ld a, [hl]
and PALETTE_MASK
or d
ld d, a
xor a
bit OVERHEAD_F, e
jr z, .not_overhead
or PRIORITY
.not_overhead
ldh [hCurSpriteOAMFlags], a
ld hl, OBJECT_SPRITE_X
add hl, bc
ld a, [hl]
ld hl, OBJECT_SPRITE_X_OFFSET
add hl, bc
add [hl]
add 8
ld e, a
ld a, [wPlayerBGMapOffsetX]
add e
ldh [hCurSpriteXPixel], a
ld hl, OBJECT_SPRITE_Y
add hl, bc
ld a, [hl]
ld hl, OBJECT_SPRITE_Y_OFFSET
add hl, bc
add [hl]
add 12
ld e, a
ld a, [wPlayerBGMapOffsetY]
add e
ldh [hCurSpriteYPixel], a
ld hl, OBJECT_FACING
add hl, bc
ld a, [hl]
cp STANDING
jp z, .done
cp NUM_FACINGS
jp nc, .done
ld l, a
ld h, 0
add hl, hl
ld bc, Facings
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ldh a, [hUsedSpriteIndex]
ld c, a
ld b, HIGH(wShadowOAM)
ld a, [hli]
ldh [hUsedSpriteTile], a
add c
cp LOW(wShadowOAMEnd)
jr nc, .full
.addsprite
ldh a, [hCurSpriteYPixel]
add [hl]
inc hl
ld [bc], a ; y
inc c
ldh a, [hCurSpriteXPixel]
add [hl]
inc hl
ld [bc], a ; x
inc c
ld e, [hl]
inc hl
ldh a, [hCurSpriteTile]
bit ABSOLUTE_TILE_ID_F, e
jr z, .nope1
xor a
.nope1
add [hl]
inc hl
ld [bc], a ; tile id
inc c
ld a, e
bit RELATIVE_ATTRIBUTES_F, a
jr z, .nope2
ldh a, [hCurSpriteOAMFlags]
or e
.nope2
and OBP_NUM | X_FLIP | Y_FLIP | PRIORITY
or d
ld [bc], a ; attributes
inc c
ldh a, [hUsedSpriteTile]
dec a
ldh [hUsedSpriteTile], a
jr nz, .addsprite
ld a, c
ldh [hUsedSpriteIndex], a
.done
xor a
ret
.full
scf
ret
.GetObjectStructPointer:
ld c, a
ld b, 0
ld hl, .Addresses
add hl, bc
add hl, bc
ld c, [hl]
inc hl
ld b, [hl]
ret
.Addresses:
dw wPlayerStruct
dw wObject1Struct
dw wObject2Struct
dw wObject3Struct
dw wObject4Struct
dw wObject5Struct
dw wObject6Struct
dw wObject7Struct
dw wObject8Struct
dw wObject9Struct
dw wObject10Struct
dw wObject11Struct
dw wObject12Struct
_UpdateSecondarySprites::
; this is a shorter _UpdateActiveSprites for when only secondary sprites
; have changed since the last sprites update, but NOT expanded or shrinked,
; which would require to displace primary (NPC) sprites in OAM.
; if it is detected that the size of secondary sprites has increased in the end,
; fall back to calling _UpdateActiveSprites to avoid corruption.
ld a, [wStateFlags]
bit 0, a
ret z
ld a, [hUsedSpriteIndex]
push af
xor a
ldh [hUsedSpriteIndex], a
.go
ldh a, [hOAMUpdate]
push af
ld a, 1
ldh [hOAMUpdate], a
call InitSecondarySprites
pop af
ldh [hOAMUpdate], a
ldh a, [hUsedSpriteIndex]
ld c, a
pop af
cp c
ret nc
jp _UpdateActiveSprites
InitSecondarySprites:
ld a, [wDisplaySecondarySprites]
bit SECONDARYSPRITES_BOARD_MENU_F, a
call nz, InitBoardMenuSprites
bit SECONDARYSPRITES_DIE_ROLL_F, a
call nz, InitDieRollSprites
bit SECONDARYSPRITES_SPACES_LEFT_F, a
call nz, InitSpacesLeftNumberSprites
bit SECONDARYSPRITES_BRANCH_SPACE_F, a
call nz, InitBranchArrowsSprites
bit SECONDARYSPRITES_VIEW_MAP_MODE_F, a
call nz, InitViewMapModeSprites
bit SECONDARYSPRITES_TALKER_EVENT_F, a
call nz, InitTalkerEventSprites
bit SECONDARYSPRITES_GAIN_OR_LOSE_COINS_F, a
call nz, InitGainOrLoseCoinsSprites
ret
InitBoardMenuSprites:
push af
ld hl, BoardMenuOAM
ld a, [wBoardMenuCursorPosition]
ld bc, BOARD_MENU_ITEM_SIZE * SPRITEOAMSTRUCT_LENGTH
call AddNTimes
; find the beginning of free space in OAM, and assure there's space for a BOARD_MENU_ITEM_SIZE object
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (BOARD_MENU_ITEM_SIZE * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
; copy the sprite data (BOARD_MENU_ITEM_SIZE objects) of that item to the available space in OAM
ld e, a
ld d, HIGH(wShadowOAM)
ld bc, BOARD_MENU_ITEM_SIZE * SPRITEOAMSTRUCT_LENGTH
call CopyBytes
ldh a, [hUsedSpriteIndex]
add (BOARD_MENU_ITEM_SIZE * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.oam_full
pop af
ret
InitDieRollSprites:
push af
ld a, [wDieRoll]
and a
jr z, .zero_or_oam_full
dec a
ld hl, DieRollOAM
ld bc, DIE_SIZE * SPRITEOAMSTRUCT_LENGTH
call AddNTimes
; find the beginning of free space in OAM, and assure there's space for a DIE_SIZE object
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (DIE_SIZE * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .zero_or_oam_full
; copy the sprite data (DIE_SIZE objects) of that item to the available space in OAM
ld e, a
ld d, HIGH(wShadowOAM)
ld bc, DIE_SIZE * SPRITEOAMSTRUCT_LENGTH
call CopyBytes
ldh a, [hUsedSpriteIndex]
add (DIE_SIZE * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.zero_or_oam_full
pop af
ret
InitSpacesLeftNumberSprites:
push af
ld hl, SpacesLeftNumberOAM
ld a, [wSpacesLeft]
dec a
ld bc, DIE_NUMBER_SIZE * SPRITEOAMSTRUCT_LENGTH
call AddNTimes
; find the beginning of free space in OAM, and assure there's space for a DIE_NUMBER_SIZE object
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (DIE_NUMBER_SIZE * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
; copy the sprite data (DIE_NUMBER_SIZE objects) of that item to the available space in OAM
ld e, a
ld d, HIGH(wShadowOAM)
ld bc, DIE_NUMBER_SIZE * SPRITEOAMSTRUCT_LENGTH
call CopyBytes
ldh a, [hUsedSpriteIndex]
add (DIE_NUMBER_SIZE * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.oam_full
pop af
ret
InitBranchArrowsSprites:
push af
;; arrows
; find the beginning of free space in OAM, and assure there's space for 4 objects
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (NUM_DIRECTIONS * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
ld hl, BranchArrowsOAM
ld de, wTempSpaceBranchStruct
ld c, NUM_DIRECTIONS
.loop
ld a, [de]
cp BRANCH_DIRECTION_INVALID
jr z, .next1 ; skip this arrow if this direction is not valid
cp BRANCH_DIRECTION_UNAVAILABLE
ld b, PAL_OW_GREY ; draw grey arrow if this direction is unavailable
jr z, .got_pal
call GetSecondarySpritesPal
ld b, a
.got_pal
; draw this arrow and advance hUsedSpriteIndex
; preserve loop variables d, e, c
push de
push bc
ldh a, [hUsedSpriteIndex]
ld e, a
ld d, HIGH(wShadowOAM)
; copy all bytes minus the attributes one. the palette matches the
; player's color palette, or is PAL_OW_GREY if direction is unavailable
push bc
ld bc, SPRITEOAMSTRUCT_LENGTH - 1
call CopyBytes
pop bc
ld a, b ; palette
ld [de], a
inc de
ld a, e
ldh [hUsedSpriteIndex], a
pop bc
pop de
jr .next2
.next1
inc hl ;
inc hl ;
inc hl ;
.next2
inc hl ; next object in BranchArrowsOAM
inc de
dec c
jr nz, .loop
;; legend
; find the beginning of free space in OAM, and assure there's space for 8 objects
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (8 * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
; copy the sprite data of the legend to the available space in OAM.
; the palette byte overrides that data as it matches the player's color palette.
ld e, a
ld d, HIGH(wShadowOAM)
call GetSecondarySpritesPal
ld b, a
ld c, 8 ; number of objects
ld hl, BranchLegendOAM
.loop2
push bc
ld bc, SPRITEOAMSTRUCT_LENGTH - 1
call CopyBytes
pop bc
ld a, b ; palette
ld [de], a
inc hl
inc de
dec c
jr nz, .loop2
ldh a, [hUsedSpriteIndex]
add (8 * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.oam_full
pop af
ret
InitViewMapModeSprites:
push af
;; arrows
; find the beginning of free space in OAM, and assure there's space for 4 objects
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (NUM_DIRECTIONS * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
ld hl, ViewMapModeArrowsOAM
ld de, wTileDown
ld c, NUM_DIRECTIONS
.loop
ld a, [de]
cp $ff
jr z, .next1 ; skip this arrow if this direction is not valid
call GetSecondarySpritesPal
ld b, a
; draw this arrow and advance hUsedSpriteIndex
; preserve loop variables d, e, c
push de
push bc
ldh a, [hUsedSpriteIndex]
ld e, a
ld d, HIGH(wShadowOAM)
; copy all bytes minus the attributes one.
; the palette matches the player's color palette.
push bc
ld bc, SPRITEOAMSTRUCT_LENGTH - 1
call CopyBytes
pop bc
ld a, b ; palette
ld [de], a
inc de
ld a, e
ldh [hUsedSpriteIndex], a
pop bc
pop de
jr .next2
.next1
inc hl ;
inc hl ;
inc hl ;
.next2
inc hl ; next object in ViewMapModeArrowsOAM
inc de
dec c
jr nz, .loop
;; legend
; find the beginning of free space in OAM, and assure there's space for 8 objects
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (8 * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
; copy the sprite data of the legend to the available space in OAM.
; the palette byte overrides that data as it matches the player's color palette.
ld e, a
ld d, HIGH(wShadowOAM)
call GetSecondarySpritesPal
ld b, a
ld c, 8 ; number of objects
ld hl, ViewMapModeLegendOAM
.loop2
push bc
ld bc, SPRITEOAMSTRUCT_LENGTH - 1
call CopyBytes
pop bc
ld a, b ; palette
ld [de], a
inc hl
inc de
dec c
jr nz, .loop2
ldh a, [hUsedSpriteIndex]
add (8 * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.oam_full
pop af
ret
InitTalkerEventSprites:
push af
; find the beginning of free space in OAM, and assure there's space for 8 objects
ldh a, [hUsedSpriteIndex]
cp (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (8 * SPRITEOAMSTRUCT_LENGTH) + 1
jr nc, .oam_full
; copy the sprite data of the legend to the available space in OAM.
; the palette byte overrides that data as it matches the player's color palette.
ld e, a
ld d, HIGH(wShadowOAM)
call GetSecondarySpritesPal
ld b, a
ld c, 8 ; number of objects
ld hl, TalkerEventLegendOAM
.loop2
push bc
ld bc, SPRITEOAMSTRUCT_LENGTH - 1
call CopyBytes
pop bc
ld a, b ; palette
ld [de], a
inc hl
inc de
dec c
jr nz, .loop2
ldh a, [hUsedSpriteIndex]
add (8 * SPRITEOAMSTRUCT_LENGTH)
ldh [hUsedSpriteIndex], a
.oam_full
pop af
ret
InitGainOrLoseCoinsSprites:
push af
; print string at wStringBuffer1 as OAM
; get string length
ld hl, wStringBuffer1
ld c, 0
.get_length_loop
ld a, [hli]
cp "@"
jr z, .got_length
inc c
ld a, c
cp $2 + MAX_DELTA_COINS_DIGITS + 1
jr nz, .get_length_loop
.got_length
ld e, c
; find the beginning of free space in OAM, and assure there's space for c objects
ld a, (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) + 1
sla c ;
sla c ; c *= SPRITEOAMSTRUCT_LENGTH
sub c
ld b, a ; b = (NUM_SPRITE_OAM_STRUCTS * SPRITEOAMSTRUCT_LENGTH) - (c * SPRITEOAMSTRUCT_LENGTH) + 1
ldh a, [hUsedSpriteIndex]
cp b
jr nc, .oam_full
; align OAM in the X axis according to the string length (e)
ld a, 10 * TILE_WIDTH
ld c, TILE_WIDTH / 2
.x_coord_loop
sub c
dec e
jr nz, .x_coord_loop
; b = SPRITEOAMSTRUCT_XCOORD
ld b, a
; c = SPRITEOAMSTRUCT_YCOORD
ld c, 8 * TILE_WIDTH
; de = address within wShadowOAM
ld a, [hUsedSpriteIndex]
ld e, a
ld d, HIGH(wShadowOAM)
ld hl, wStringBuffer1
.print_oam_loop
ld a, c
ld [de], a ; SPRITEOAMSTRUCT_YCOORD
inc de
ld a, b
ld [de], a ; SPRITEOAMSTRUCT_XCOORD
inc de
add 1 * TILE_WIDTH
ld b, a
ld a, [hli]
ld [de], a ; SPRITEOAMSTRUCT_TILE_ID
inc de
ld a, PAL_OW_MISC
ld [de], a ; SPRITEOAMSTRUCT_ATTRIBUTES
inc de
ld a, [hl]
cp "@"
jr nz, .print_oam_loop
ld a, e
ld [hUsedSpriteIndex], a
.oam_full
pop af
ret
GetSecondarySpritesPal:
push hl
push de
push bc
ld a, [wPlayerCharacter]
ld e, PLAYERDATA_OW_PAL
call GetPlayerField
pop bc
pop de
pop hl
ret
INCLUDE "data/sprites/secondary_sprites.asm"