mirror of
https://github.com/thornAvery/kep-hack.git
synced 2025-09-16 18:30:50 +12:00

Rhyperior files are dummies and will need to be properly edited with sprites, Rhydon evo data, etc, but it (and the other files included) show that the trainer and pokemon sprite indexes have been separated, which allows us to add the other KEP mons
893 lines
23 KiB
NASM
893 lines
23 KiB
NASM
DEF MAP_TILESET_SIZE EQU $60
|
|
|
|
UpdatePlayerSprite:
|
|
ld a, [wSpritePlayerStateData2WalkAnimationCounter]
|
|
and a
|
|
jr z, .checkIfTextBoxInFrontOfSprite
|
|
cp $ff
|
|
jr z, .disableSprite
|
|
dec a
|
|
ld [wSpritePlayerStateData2WalkAnimationCounter], a
|
|
jr .disableSprite
|
|
; check if a text box is in front of the sprite by checking if the lower left
|
|
; background tile the sprite is standing on is greater than $5F, which is
|
|
; the maximum number for map tiles
|
|
.checkIfTextBoxInFrontOfSprite
|
|
lda_coord 8, 9
|
|
ldh [hTilePlayerStandingOn], a
|
|
cp MAP_TILESET_SIZE
|
|
jr c, .lowerLeftTileIsMapTile
|
|
.disableSprite
|
|
ld a, $ff
|
|
ld [wSpritePlayerStateData1ImageIndex], a
|
|
ret
|
|
.lowerLeftTileIsMapTile
|
|
call DetectCollisionBetweenSprites
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ld a, [wWalkCounter]
|
|
and a
|
|
jr nz, .moving
|
|
ld a, [wPlayerMovingDirection]
|
|
; check if down
|
|
bit PLAYER_DIR_BIT_DOWN, a
|
|
jr z, .checkIfUp
|
|
xor a ; ld a, SPRITE_FACING_DOWN
|
|
jr .next
|
|
.checkIfUp
|
|
bit PLAYER_DIR_BIT_UP, a
|
|
jr z, .checkIfLeft
|
|
ld a, SPRITE_FACING_UP
|
|
jr .next
|
|
.checkIfLeft
|
|
bit PLAYER_DIR_BIT_LEFT, a
|
|
jr z, .checkIfRight
|
|
ld a, SPRITE_FACING_LEFT
|
|
jr .next
|
|
.checkIfRight
|
|
bit PLAYER_DIR_BIT_RIGHT, a
|
|
jr z, .notMoving
|
|
ld a, SPRITE_FACING_RIGHT
|
|
jr .next
|
|
.notMoving
|
|
; zero the animation counters
|
|
xor a
|
|
ld [wSpritePlayerStateData1IntraAnimFrameCounter], a
|
|
ld [wSpritePlayerStateData1AnimFrameCounter], a
|
|
jr .calcImageIndex
|
|
.next
|
|
ld [wSpritePlayerStateData1FacingDirection], a
|
|
ld a, [wFontLoaded]
|
|
bit 0, a
|
|
jr nz, .notMoving
|
|
.moving
|
|
ld a, [wd736]
|
|
bit 7, a ; is the player sprite spinning due to a spin tile?
|
|
jr nz, .skipSpriteAnim
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $7
|
|
ld l, a
|
|
ld a, [hl]
|
|
inc a
|
|
ld [hl], a
|
|
cp 4
|
|
jr nz, .calcImageIndex
|
|
xor a
|
|
ld [hl], a
|
|
inc hl
|
|
ld a, [hl]
|
|
inc a
|
|
and $3
|
|
ld [hl], a
|
|
.calcImageIndex
|
|
ld a, [wSpritePlayerStateData1AnimFrameCounter]
|
|
ld b, a
|
|
ld a, [wSpritePlayerStateData1FacingDirection]
|
|
add b
|
|
ld [wSpritePlayerStateData1ImageIndex], a
|
|
.skipSpriteAnim
|
|
; If the player is standing on a grass tile, make the player's sprite have
|
|
; lower priority than the background so that it's partially obscured by the
|
|
; grass. Only the lower half of the sprite is permitted to have the priority
|
|
; bit set by later logic.
|
|
ldh a, [hTilePlayerStandingOn]
|
|
ld c, a
|
|
ld a, [wGrassTile]
|
|
cp c
|
|
ld a, 0
|
|
jr nz, .next2
|
|
ld a, OAM_BEHIND_BG
|
|
.next2
|
|
ld [wSpritePlayerStateData2GrassPriority], a
|
|
ret
|
|
|
|
UnusedReadSpriteDataFunction:
|
|
push bc
|
|
push af
|
|
ldh a, [hCurrentSpriteOffset]
|
|
ld c, a
|
|
pop af
|
|
add c
|
|
ld l, a
|
|
pop bc
|
|
ret
|
|
|
|
UpdateNPCSprite:
|
|
ldh a, [hCurrentSpriteOffset]
|
|
swap a
|
|
dec a
|
|
add a
|
|
ld hl, wMapSpriteData
|
|
add l
|
|
ld l, a
|
|
jr nc, .nc
|
|
inc h
|
|
.nc
|
|
ld a, [hl] ; read movement byte 2
|
|
ld [wCurSpriteMovement2], a
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
ld l, a
|
|
inc l
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
|
|
and a
|
|
jp z, InitializeSpriteStatus
|
|
call CheckSpriteAvailability
|
|
ret c ; if sprite is invisible, on tile >=MAP_TILESET_SIZE, in grass or player is currently walking
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
ld l, a
|
|
inc l
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
|
|
bit 7, a ; is the face player flag set?
|
|
jp nz, MakeNPCFacePlayer
|
|
ld b, a
|
|
ld a, [wFontLoaded]
|
|
bit 0, a
|
|
jp nz, notYetMoving
|
|
ld a, b
|
|
cp $2
|
|
jp z, UpdateSpriteMovementDelay ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] == 2
|
|
cp $3
|
|
jp z, UpdateSpriteInWalkingAnimation ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] == 3
|
|
ld a, [wWalkCounter]
|
|
and a
|
|
ret nz ; don't do anything yet if player is currently moving (redundant, already tested in CheckSpriteAvailability)
|
|
call InitializeSpriteScreenPosition
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $6
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
inc a
|
|
jr z, .randomMovement ; value STAY
|
|
inc a
|
|
jr z, .randomMovement ; value WALK
|
|
; scripted movement
|
|
dec a
|
|
ld [hl], a ; increment movement byte 1 (movement data index)
|
|
dec a
|
|
push hl
|
|
ld hl, wNPCNumScriptedSteps
|
|
dec [hl] ; decrement wNPCNumScriptedSteps
|
|
pop hl
|
|
ld de, wNPCMovementDirections
|
|
call LoadDEPlusA ; a = [wNPCMovementDirections + movement byte 1]
|
|
cp NPC_CHANGE_FACING
|
|
jp z, ChangeFacingDirection
|
|
cp STAY
|
|
jr nz, .next
|
|
; reached end of wNPCMovementDirections list
|
|
ld [hl], a ; store $ff in movement byte 1, disabling scripted movement
|
|
ld hl, wd730
|
|
res 0, [hl]
|
|
xor a
|
|
ld [wSimulatedJoypadStatesIndex], a
|
|
ld [wWastedByteCD3A], a
|
|
ret
|
|
.next
|
|
cp WALK
|
|
jr nz, .determineDirection
|
|
; current NPC movement data is WALK ($fe). this seems buggy
|
|
ld [hl], $1 ; set movement byte 1 to $1
|
|
ld de, wNPCMovementDirections
|
|
call LoadDEPlusA ; a = [wNPCMovementDirections + $fe] (?)
|
|
jr .determineDirection
|
|
.randomMovement
|
|
call GetTileSpriteStandsOn
|
|
call Random
|
|
.determineDirection
|
|
ld b, a
|
|
ld a, [wCurSpriteMovement2]
|
|
cp DOWN
|
|
jr z, .moveDown
|
|
cp UP
|
|
jr z, .moveUp
|
|
cp LEFT
|
|
jr z, .moveLeft
|
|
cp RIGHT
|
|
jr z, .moveRight
|
|
ld a, b
|
|
cp NPC_MOVEMENT_UP ; NPC_MOVEMENT_DOWN <= a < NPC_MOVEMENT_UP: down (or left)
|
|
jr nc, .notDown
|
|
ld a, [wCurSpriteMovement2]
|
|
cp LEFT_RIGHT
|
|
jr z, .moveLeft
|
|
.moveDown
|
|
ld de, 2*SCREEN_WIDTH
|
|
add hl, de ; move tile pointer two rows down
|
|
lb de, 1, 0
|
|
lb bc, 4, SPRITE_FACING_DOWN
|
|
jr TryWalking
|
|
.notDown
|
|
cp NPC_MOVEMENT_LEFT ; NPC_MOVEMENT_UP <= a < NPC_MOVEMENT_LEFT: up (or right)
|
|
jr nc, .notUp
|
|
ld a, [wCurSpriteMovement2]
|
|
cp LEFT_RIGHT
|
|
jr z, .moveRight
|
|
.moveUp
|
|
ld de, -2*SCREEN_WIDTH
|
|
add hl, de ; move tile pointer two rows up
|
|
lb de, -1, 0
|
|
lb bc, 8, SPRITE_FACING_UP
|
|
jr TryWalking
|
|
.notUp
|
|
cp NPC_MOVEMENT_RIGHT ; NPC_MOVEMENT_LEFT <= a < NPC_MOVEMENT_RIGHT: left (or up)
|
|
jr nc, .notLeft
|
|
ld a, [wCurSpriteMovement2]
|
|
cp UP_DOWN
|
|
jr z, .moveUp
|
|
.moveLeft
|
|
dec hl
|
|
dec hl ; move tile pointer two columns left
|
|
lb de, 0, -1
|
|
lb bc, 2, SPRITE_FACING_LEFT
|
|
jr TryWalking
|
|
.notLeft ; NPC_MOVEMENT_RIGHT <= a: right (or down)
|
|
ld a, [wCurSpriteMovement2]
|
|
cp UP_DOWN
|
|
jr z, .moveDown
|
|
.moveRight
|
|
inc hl
|
|
inc hl ; move tile pointer two columns right
|
|
lb de, 0, 1
|
|
lb bc, 1, SPRITE_FACING_RIGHT
|
|
jr TryWalking
|
|
|
|
; changes facing direction by zeroing the movement delta and calling TryWalking
|
|
ChangeFacingDirection:
|
|
ld de, $0
|
|
; fall through
|
|
|
|
; b: direction (1,2,4 or 8)
|
|
; c: new facing direction (0,4,8 or $c)
|
|
; d: Y movement delta (-1, 0 or 1)
|
|
; e: X movement delta (-1, 0 or 1)
|
|
; hl: pointer to tile the sprite would walk onto
|
|
; set carry on failure, clears carry on success
|
|
TryWalking:
|
|
push hl
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $9
|
|
ld l, a
|
|
ld [hl], c ; x#SPRITESTATEDATA1_FACINGDIRECTION
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $3
|
|
ld l, a
|
|
ld [hl], d ; x#SPRITESTATEDATA1_YSTEPVECTOR
|
|
inc l
|
|
inc l
|
|
ld [hl], e ; x#SPRITESTATEDATA1_XSTEPVECTOR
|
|
pop hl
|
|
push de
|
|
ld c, [hl] ; read tile to walk onto
|
|
call CanWalkOntoTile
|
|
pop de
|
|
ret c ; cannot walk there (reinitialization of delay values already done)
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $4
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MAPY
|
|
add d
|
|
ld [hli], a ; update Y position
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MAPX
|
|
add e
|
|
ld [hl], a ; update X position
|
|
ldh a, [hCurrentSpriteOffset]
|
|
ld l, a
|
|
ld [hl], $10 ; [x#SPRITESTATEDATA2_WALKANIMATIONCOUNTER] = 16
|
|
dec h
|
|
inc l
|
|
ld [hl], $3 ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
|
|
jp UpdateSpriteImage
|
|
|
|
; update the walking animation parameters for a sprite that is currently walking
|
|
UpdateSpriteInWalkingAnimation:
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $7
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER
|
|
inc a
|
|
ld [hl], a ; [x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER]++
|
|
cp $4
|
|
jr nz, .noNextAnimationFrame
|
|
xor a
|
|
ld [hl], a ; [x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER] = 0
|
|
inc l
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_ANIMFRAMECOUNTER
|
|
inc a
|
|
and $3
|
|
ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step)
|
|
.noNextAnimationFrame
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $3
|
|
ld l, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA1_YSTEPVECTOR
|
|
ld b, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_YPIXELS
|
|
add b
|
|
ld [hli], a ; update [x#SPRITESTATEDATA1_YPIXELS]
|
|
ld a, [hli] ; x#SPRITESTATEDATA1_XSTEPVECTOR
|
|
ld b, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
|
|
add b
|
|
ld [hl], a ; update [x#SPRITESTATEDATA1_XPIXELS]
|
|
ldh a, [hCurrentSpriteOffset]
|
|
ld l, a
|
|
inc h
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_WALKANIMATIONCOUNTER
|
|
dec a
|
|
ld [hl], a ; update walk animation counter
|
|
ret nz
|
|
ld a, $6 ; walking finished, update state
|
|
add l
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
cp WALK
|
|
jr nc, .initNextMovementCounter ; values WALK or STAY
|
|
ldh a, [hCurrentSpriteOffset]
|
|
inc a
|
|
ld l, a
|
|
dec h
|
|
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 1 (movement status ready)
|
|
ret
|
|
.initNextMovementCounter
|
|
call Random
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $8
|
|
ld l, a
|
|
ldh a, [hRandomAdd]
|
|
and $7f
|
|
ld [hl], a ; x#SPRITESTATEDATA2_MOVEMENTDELAY:
|
|
; set next movement delay to a random value in [0,$7f]
|
|
; note that value 0 actually makes the delay $100 (bug?)
|
|
dec h ; HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
inc a
|
|
ld l, a
|
|
ld [hl], $2 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 2 (movement status)
|
|
inc l
|
|
inc l
|
|
xor a
|
|
ld b, [hl] ; x#SPRITESTATEDATA1_YSTEPVECTOR
|
|
ld [hli], a ; [x#SPRITESTATEDATA1_YSTEPVECTOR] = 0
|
|
inc l
|
|
ld c, [hl] ; x#SPRITESTATEDATA1_XSTEPVECTOR
|
|
ld [hl], a ; [x#SPRITESTATEDATA1_XSTEPVECTOR] = 0
|
|
ret
|
|
|
|
; update [x#SPRITESTATEDATA2_MOVEMENTDELAY] for sprites in the delayed state (x#SPRITESTATEDATA1_MOVEMENTSTATUS)
|
|
UpdateSpriteMovementDelay:
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $6
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
inc l
|
|
inc l
|
|
cp WALK
|
|
jr nc, .tickMoveCounter ; values WALK or STAY
|
|
ld [hl], $0
|
|
jr .moving
|
|
.tickMoveCounter
|
|
dec [hl] ; x#SPRITESTATEDATA2_MOVEMENTDELAY
|
|
jr nz, notYetMoving
|
|
.moving
|
|
dec h
|
|
ldh a, [hCurrentSpriteOffset]
|
|
inc a
|
|
ld l, a
|
|
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 1 (mark as ready to move)
|
|
notYetMoving:
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_ANIMFRAMECOUNTER
|
|
ld l, a
|
|
ld [hl], $0 ; [x#SPRITESTATEDATA1_ANIMFRAMECOUNTER] = 0 (walk animation frame)
|
|
jp UpdateSpriteImage
|
|
|
|
MakeNPCFacePlayer:
|
|
; Make an NPC face the player if the player has spoken to him or her.
|
|
|
|
; Check if the behaviour of the NPC facing the player when spoken to is
|
|
; disabled. This is only done when rubbing the S.S. Anne captain's back.
|
|
ld a, [wd72d]
|
|
bit 5, a
|
|
jr nz, notYetMoving
|
|
res 7, [hl]
|
|
ld a, [wPlayerDirection]
|
|
bit PLAYER_DIR_BIT_UP, a
|
|
jr z, .notFacingDown
|
|
ld c, SPRITE_FACING_DOWN
|
|
jr .facingDirectionDetermined
|
|
.notFacingDown
|
|
bit PLAYER_DIR_BIT_DOWN, a
|
|
jr z, .notFacingUp
|
|
ld c, SPRITE_FACING_UP
|
|
jr .facingDirectionDetermined
|
|
.notFacingUp
|
|
bit PLAYER_DIR_BIT_LEFT, a
|
|
jr z, .notFacingRight
|
|
ld c, SPRITE_FACING_RIGHT
|
|
jr .facingDirectionDetermined
|
|
.notFacingRight
|
|
ld c, SPRITE_FACING_LEFT
|
|
.facingDirectionDetermined
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $9
|
|
ld l, a
|
|
ld [hl], c ; [x#SPRITESTATEDATA1_FACINGDIRECTION]: set facing direction
|
|
jr notYetMoving
|
|
|
|
InitializeSpriteStatus:
|
|
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = ready
|
|
inc l
|
|
ld [hl], $ff ; [x#SPRITESTATEDATA1_IMAGEINDEX] = invisible/off screen
|
|
inc h ; HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $2
|
|
ld l, a
|
|
ld a, $8
|
|
ld [hli], a ; [x#SPRITESTATEDATA2_YDISPLACEMENT] = 8
|
|
ld [hl], a ; [x#SPRITESTATEDATA2_XDISPLACEMENT] = 8
|
|
ret
|
|
|
|
; calculates the sprite's screen position from its map position and the player position
|
|
InitializeSpriteScreenPosition:
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_MAPY
|
|
ld l, a
|
|
ld a, [wYCoord]
|
|
ld b, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MAPY
|
|
sub b ; relative to player position
|
|
swap a ; * 16
|
|
sub $4 ; - 4
|
|
dec h
|
|
ld [hli], a ; [x#SPRITESTATEDATA1_YPIXELS]
|
|
inc h
|
|
ld a, [wXCoord]
|
|
ld b, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA2_MAPX
|
|
sub b ; relative to player position
|
|
swap a ; * 16
|
|
dec h
|
|
ld [hl], a ; [x#SPRITESTATEDATA1_XPIXELS]
|
|
ret
|
|
|
|
; tests if sprite is off screen or otherwise unable to do anything
|
|
CheckSpriteAvailability:
|
|
predef IsObjectHidden
|
|
ldh a, [hIsHiddenMissableObject]
|
|
and a
|
|
jp nz, .spriteInvisible
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
cp WALK
|
|
jr c, .skipXVisibilityTest ; movement byte 1 < WALK (i.e. the sprite's movement is scripted)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_MAPY
|
|
ld l, a
|
|
ld b, [hl] ; x#SPRITESTATEDATA2_MAPY
|
|
ld a, [wYCoord]
|
|
cp b
|
|
jr z, .skipYVisibilityTest
|
|
jr nc, .spriteInvisible ; above screen region
|
|
add SCREEN_HEIGHT / 2 - 1
|
|
cp b
|
|
jr c, .spriteInvisible ; below screen region
|
|
.skipYVisibilityTest
|
|
inc l
|
|
ld b, [hl] ; x#SPRITESTATEDATA2_MAPX
|
|
ld a, [wXCoord]
|
|
cp b
|
|
jr z, .skipXVisibilityTest
|
|
jr nc, .spriteInvisible ; left of screen region
|
|
add SCREEN_WIDTH / 2 - 1
|
|
cp b
|
|
jr c, .spriteInvisible ; right of screen region
|
|
.skipXVisibilityTest
|
|
; make the sprite invisible if a text box is in front of it
|
|
; $5F is the maximum number for map tiles
|
|
call GetTileSpriteStandsOn
|
|
ld d, MAP_TILESET_SIZE
|
|
ld a, [hli]
|
|
cp d
|
|
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom left tile)
|
|
ld a, [hld]
|
|
cp d
|
|
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom right tile)
|
|
ld bc, -SCREEN_WIDTH
|
|
add hl, bc ; go back one row of tiles
|
|
ld a, [hli]
|
|
cp d
|
|
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (top left tile)
|
|
ld a, [hl]
|
|
cp d
|
|
jr c, .spriteVisible ; standing on tile with ID >=MAP_TILESET_SIZE (top right tile)
|
|
.spriteInvisible
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_IMAGEINDEX
|
|
ld l, a
|
|
ld [hl], $ff ; x#SPRITESTATEDATA1_IMAGEINDEX
|
|
scf
|
|
jr .done
|
|
.spriteVisible
|
|
ld c, a
|
|
ld a, [wWalkCounter]
|
|
and a
|
|
jr nz, .done ; if player is currently walking, we're done
|
|
call UpdateSpriteImage
|
|
inc h
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $7
|
|
ld l, a
|
|
ld a, [wGrassTile]
|
|
cp c
|
|
ld a, 0
|
|
jr nz, .notInGrass
|
|
ld a, OAM_BEHIND_BG
|
|
.notInGrass
|
|
ld [hl], a ; x#SPRITESTATEDATA2_GRASSPRIORITY
|
|
and a
|
|
.done
|
|
ret
|
|
|
|
UpdateSpriteImage:
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $8
|
|
ld l, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA1_ANIMFRAMECOUNTER
|
|
ld b, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_FACINGDIRECTION
|
|
add b
|
|
ld b, a
|
|
ldh a, [hTilePlayerStandingOn]
|
|
add b
|
|
ld b, a
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $2
|
|
ld l, a
|
|
ld [hl], b ; x#SPRITESTATEDATA1_IMAGEINDEX
|
|
ret
|
|
|
|
; tests if sprite can walk the specified direction
|
|
; b: direction (1,2,4 or 8)
|
|
; c: ID of tile the sprite would walk onto
|
|
; d: Y movement delta (-1, 0 or 1)
|
|
; e: X movement delta (-1, 0 or 1)
|
|
; set carry on failure, clears carry on success
|
|
CanWalkOntoTile:
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
cp WALK
|
|
jr nc, .notScripted ; values WALK or STAY
|
|
; always allow walking if the movement is scripted
|
|
and a
|
|
ret
|
|
.notScripted
|
|
ld a, [wTilesetCollisionPtr]
|
|
ld l, a
|
|
ld a, [wTilesetCollisionPtr+1]
|
|
ld h, a
|
|
.tilePassableLoop
|
|
ld a, [hli]
|
|
cp $ff
|
|
jr z, .impassable
|
|
cp c
|
|
jr nz, .tilePassableLoop
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $6
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
|
|
inc a
|
|
jr z, .impassable ; if $ff, no movement allowed (however, changing direction is)
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_YPIXELS
|
|
ld l, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
|
|
add $4 ; align to blocks (Y pos is always 4 pixels off)
|
|
add d ; add Y delta
|
|
cp $80 ; if value is >$80, the destination is off screen (either $81 or $FF underflow)
|
|
jr nc, .impassable ; don't walk off screen
|
|
inc l
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
|
|
add e ; add X delta
|
|
cp $90 ; if value is >$90, the destination is off screen (either $91 or $FF underflow)
|
|
jr nc, .impassable ; don't walk off screen
|
|
push de
|
|
push bc
|
|
call DetectCollisionBetweenSprites
|
|
pop bc
|
|
pop de
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $c
|
|
ld l, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_COLLISIONDATA (directions in which sprite collision would occur)
|
|
and b ; check against chosen direction (1,2,4 or 8)
|
|
jr nz, .impassable ; collision between sprites, don't go there
|
|
ld h, HIGH(wSpriteStateData2)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_YDISPLACEMENT
|
|
ld l, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA2_YDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
|
|
bit 7, d ; check if going upwards (d=$ff)
|
|
jr nz, .upwards
|
|
add d
|
|
; bug: these tests against $5 probably were supposed to prevent
|
|
; sprites from walking out too far, but this line makes sprites get
|
|
; stuck whenever they walked upwards 5 steps
|
|
; on the other hand, the amount a sprite can walk out to the
|
|
; right of bottom is not limited (until the counter overflows)
|
|
cp $5
|
|
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT]+d < 5, don't go
|
|
jr .checkHorizontal
|
|
.upwards
|
|
sub $1
|
|
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT] == 0, don't go
|
|
.checkHorizontal
|
|
ld d, a
|
|
ld a, [hl] ; x#SPRITESTATEDATA2_XDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
|
|
bit 7, e ; check if going left (e=$ff)
|
|
jr nz, .left
|
|
add e
|
|
cp $5 ; compare, but no conditional jump like in the vertical check above (bug?)
|
|
jr .passable
|
|
.left
|
|
sub $1
|
|
jr c, .impassable ; if [x#SPRITESTATEDATA2_XDISPLACEMENT] == 0, don't go
|
|
.passable
|
|
ld [hld], a ; update x#SPRITESTATEDATA2_XDISPLACEMENT
|
|
ld [hl], d ; update x#SPRITESTATEDATA2_YDISPLACEMENT
|
|
and a ; clear carry (marking success)
|
|
ret
|
|
.impassable
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
inc a
|
|
ld l, a
|
|
ld [hl], $2 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 2 (delayed)
|
|
inc l
|
|
inc l
|
|
xor a
|
|
ld [hli], a ; [x#SPRITESTATEDATA1_YSTEPVECTOR] = 0
|
|
inc l
|
|
ld [hl], a ; [x#SPRITESTATEDATA1_XSTEPVECTOR] = 0
|
|
inc h
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $8
|
|
ld l, a
|
|
call Random
|
|
ldh a, [hRandomAdd]
|
|
and $7f
|
|
ld [hl], a ; x#SPRITESTATEDATA2_MOVEMENTDELAY: set to a random value in [0,$7f] (again with delay $100 if value is 0)
|
|
scf ; set carry (marking failure to walk)
|
|
ret
|
|
|
|
; calculates the tile pointer pointing to the tile the current sprite stands on
|
|
; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to
|
|
; hl: output pointer
|
|
GetTileSpriteStandsOn:
|
|
ld h, HIGH(wSpriteStateData1)
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_YPIXELS
|
|
ld l, a
|
|
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
|
|
add $4 ; align to 2*2 tile blocks (Y position is always off 4 pixels to the top)
|
|
and $f0 ; in case object is currently moving
|
|
srl a ; screen Y tile * 4
|
|
ld c, a
|
|
ld b, $0
|
|
inc l
|
|
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
|
|
srl a
|
|
srl a
|
|
srl a ; screen X tile
|
|
add SCREEN_WIDTH ; screen X tile + 20
|
|
ld d, $0
|
|
ld e, a
|
|
hlcoord 0, 0
|
|
add hl, bc
|
|
add hl, bc
|
|
add hl, bc
|
|
add hl, bc
|
|
add hl, bc
|
|
add hl, de ; wTileMap + 20*(screen Y tile + 1) + screen X tile
|
|
ret
|
|
|
|
; loads [de+a] into a
|
|
LoadDEPlusA:
|
|
add e
|
|
ld e, a
|
|
jr nc, .noCarry
|
|
inc d
|
|
.noCarry
|
|
ld a, [de]
|
|
ret
|
|
|
|
DoScriptedNPCMovement:
|
|
; This is an alternative method of scripting an NPC's movement and is only used
|
|
; a few times in the game. It is used when the NPC and player must walk together
|
|
; in sync, such as when the player is following the NPC somewhere. An NPC can't
|
|
; be moved in sync with the player using the other method.
|
|
ld a, [wd730]
|
|
bit 7, a
|
|
ret z
|
|
ld hl, wd72e
|
|
bit 7, [hl]
|
|
set 7, [hl]
|
|
jp z, InitScriptedNPCMovement
|
|
ld hl, wNPCMovementDirections2
|
|
ld a, [wNPCMovementDirections2Index]
|
|
add l
|
|
ld l, a
|
|
jr nc, .noCarry
|
|
inc h
|
|
.noCarry
|
|
ld a, [hl]
|
|
; check if moving up
|
|
cp NPC_MOVEMENT_UP
|
|
jr nz, .checkIfMovingDown
|
|
call GetSpriteScreenYPointer
|
|
ld c, SPRITE_FACING_UP
|
|
ld a, -2
|
|
jr .move
|
|
.checkIfMovingDown
|
|
cp NPC_MOVEMENT_DOWN
|
|
jr nz, .checkIfMovingLeft
|
|
call GetSpriteScreenYPointer
|
|
ld c, SPRITE_FACING_DOWN
|
|
ld a, 2
|
|
jr .move
|
|
.checkIfMovingLeft
|
|
cp NPC_MOVEMENT_LEFT
|
|
jr nz, .checkIfMovingRight
|
|
call GetSpriteScreenXPointer
|
|
ld c, SPRITE_FACING_LEFT
|
|
ld a, -2
|
|
jr .move
|
|
.checkIfMovingRight
|
|
cp NPC_MOVEMENT_RIGHT
|
|
jr nz, .noMatch
|
|
call GetSpriteScreenXPointer
|
|
ld c, SPRITE_FACING_RIGHT
|
|
ld a, 2
|
|
jr .move
|
|
.noMatch
|
|
cp $ff
|
|
ret
|
|
.move
|
|
ld b, a
|
|
ld a, [hl]
|
|
add b
|
|
ld [hl], a
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $9
|
|
ld l, a
|
|
ld a, c
|
|
ld [hl], a ; facing direction
|
|
call AnimScriptedNPCMovement
|
|
ld hl, wScriptedNPCWalkCounter
|
|
dec [hl]
|
|
ret nz
|
|
ld a, 8
|
|
ld [wScriptedNPCWalkCounter], a
|
|
ld hl, wNPCMovementDirections2Index
|
|
inc [hl]
|
|
ret
|
|
|
|
InitScriptedNPCMovement:
|
|
xor a
|
|
ld [wNPCMovementDirections2Index], a
|
|
ld a, 8
|
|
ld [wScriptedNPCWalkCounter], a
|
|
jp AnimScriptedNPCMovement
|
|
|
|
GetSpriteScreenYPointer:
|
|
ld a, SPRITESTATEDATA1_YPIXELS
|
|
ld b, a
|
|
jr GetSpriteScreenXYPointerCommon
|
|
|
|
GetSpriteScreenXPointer:
|
|
ld a, SPRITESTATEDATA1_XPIXELS
|
|
ld b, a
|
|
|
|
GetSpriteScreenXYPointerCommon:
|
|
ld hl, wSpriteStateData1
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add l
|
|
add b
|
|
ld l, a
|
|
ret
|
|
|
|
AnimScriptedNPCMovement:
|
|
ld hl, wSpriteStateData2
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA2_IMAGEBASEOFFSET
|
|
ld l, a
|
|
ld a, [hl] ; VRAM slot
|
|
dec a
|
|
swap a
|
|
ld b, a
|
|
ld hl, wSpriteStateData1
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_FACINGDIRECTION
|
|
ld l, a
|
|
ld a, [hl] ; facing direction
|
|
cp SPRITE_FACING_DOWN
|
|
jr z, .anim
|
|
cp SPRITE_FACING_UP
|
|
jr z, .anim
|
|
cp SPRITE_FACING_LEFT
|
|
jr z, .anim
|
|
cp SPRITE_FACING_RIGHT
|
|
jr z, .anim
|
|
ret
|
|
.anim
|
|
add b
|
|
ld b, a
|
|
ldh [hSpriteVRAMSlotAndFacing], a
|
|
call AdvanceScriptedNPCAnimFrameCounter
|
|
ld hl, wSpriteStateData1
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add SPRITESTATEDATA1_IMAGEINDEX
|
|
ld l, a
|
|
ldh a, [hSpriteVRAMSlotAndFacing]
|
|
ld b, a
|
|
ldh a, [hSpriteAnimFrameCounter]
|
|
add b
|
|
ld [hl], a
|
|
ret
|
|
|
|
AdvanceScriptedNPCAnimFrameCounter:
|
|
ldh a, [hCurrentSpriteOffset]
|
|
add $7
|
|
ld l, a
|
|
ld a, [hl] ; intra-animation frame counter
|
|
inc a
|
|
ld [hl], a
|
|
cp 4
|
|
ret nz
|
|
xor a
|
|
ld [hl], a ; reset intra-animation frame counter
|
|
inc l
|
|
ld a, [hl] ; animation frame counter
|
|
inc a
|
|
and $3
|
|
ld [hl], a
|
|
ldh [hSpriteAnimFrameCounter], a
|
|
ret
|