pokered/engine/overworld/movement.asm

882 lines
21 KiB
NASM
Raw Normal View History

2014-05-26 02:45:59 +00:00
UpdatePlayerSprite: ; 4e31 (1:4e31)
ld a, [wSpriteStateData2]
and a
jr z, .checkIfTextBoxInFrontOfSprite
2014-05-26 02:45:59 +00:00
cp $ff
jr z, .disableSprite
2014-05-26 02:45:59 +00:00
dec a
ld [wSpriteStateData2], 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
aCoord 8, 9
2015-07-15 11:27:59 +00:00
ld [hTilePlayerStandingOn], a
2014-05-26 02:45:59 +00:00
cp $60
jr c, .lowerLeftTileIsMapTile
.disableSprite
2014-05-26 02:45:59 +00:00
ld a, $ff
ld [wSpriteStateData1 + 2], a
2014-05-26 02:45:59 +00:00
ret
.lowerLeftTileIsMapTile
call DetectCollisionBetweenSprites
2015-07-15 11:27:59 +00:00
ld h, wSpriteStateData1 / $100
ld a, [wWalkCounter]
2014-05-26 02:45:59 +00:00
and a
2015-07-15 11:27:59 +00:00
jr nz, .moving
2015-07-20 00:52:26 +00:00
ld a, [wPlayerMovingDirection]
2015-07-15 11:27:59 +00:00
; check if down
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_DOWN, a
2015-07-15 11:27:59 +00:00
jr z, .checkIfUp
xor a ; ld a, SPRITE_FACING_DOWN
2015-07-15 11:27:59 +00:00
jr .next
.checkIfUp
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_UP, a
2015-07-15 11:27:59 +00:00
jr z, .checkIfLeft
ld a, SPRITE_FACING_UP
2015-07-15 11:27:59 +00:00
jr .next
.checkIfLeft
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_LEFT, a
2015-07-15 11:27:59 +00:00
jr z, .checkIfRight
ld a, SPRITE_FACING_LEFT
2015-07-15 11:27:59 +00:00
jr .next
.checkIfRight
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_RIGHT, a
2015-07-15 11:27:59 +00:00
jr z, .notMoving
ld a, SPRITE_FACING_RIGHT
2015-07-15 11:27:59 +00:00
jr .next
.notMoving
; zero the animation counters
2014-05-26 02:45:59 +00:00
xor a
ld [wSpriteStateData1 + 7], a
ld [wSpriteStateData1 + 8], a
2015-07-15 11:27:59 +00:00
jr .calcImageIndex
.next
ld [wSpriteStateData1 + 9], a ; facing direction
2015-02-09 02:40:08 +00:00
ld a, [wFontLoaded]
2014-05-26 02:45:59 +00:00
bit 0, a
2015-07-15 11:27:59 +00:00
jr nz, .notMoving
.moving
ld a, [wd736]
2015-07-15 11:27:59 +00:00
bit 7, a ; is the player sprite spinning due to a spin tile?
jr nz, .skipSpriteAnim
2014-05-26 02:45:59 +00:00
ld a, [H_CURRENTSPRITEOFFSET]
add $7
ld l, a
ld a, [hl]
inc a
ld [hl], a
2015-07-15 11:27:59 +00:00
cp 4
jr nz, .calcImageIndex
2014-05-26 02:45:59 +00:00
xor a
ld [hl], a
inc hl
ld a, [hl]
inc a
and $3
ld [hl], a
2015-07-15 11:27:59 +00:00
.calcImageIndex
ld a, [wSpriteStateData1 + 8]
2014-05-26 02:45:59 +00:00
ld b, a
ld a, [wSpriteStateData1 + 9]
2014-05-26 02:45:59 +00:00
add b
ld [wSpriteStateData1 + 2], a
2015-07-15 11:27:59 +00:00
.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.
ld a, [hTilePlayerStandingOn]
2014-05-26 02:45:59 +00:00
ld c, a
ld a, [W_GRASSTILE]
cp c
ld a, $0
2015-07-15 11:27:59 +00:00
jr nz, .next2
2014-05-26 02:45:59 +00:00
ld a, $80
2015-07-15 11:27:59 +00:00
.next2
ld [wSpriteStateData2 + $07], a
2014-05-26 02:45:59 +00:00
ret
2015-07-15 11:27:59 +00:00
UnusedReadSpriteDataFunction: ; 4ec7 (1:4ec7)
2014-05-26 02:45:59 +00:00
push bc
push af
2015-07-15 11:27:59 +00:00
ld a, [H_CURRENTSPRITEOFFSET]
2014-05-26 02:45:59 +00:00
ld c, a
pop af
add c
ld l, a
pop bc
ret
2015-07-15 11:27:59 +00:00
UpdateNPCSprite: ; 4ed1 (1:4ed1)
2014-05-26 02:45:59 +00:00
ld a, [H_CURRENTSPRITEOFFSET]
swap a
dec a
add a
ld hl, W_MAPSPRITEDATA
2014-05-26 02:45:59 +00:00
add l
ld l, a
ld a, [hl] ; read movement byte 2
ld [wCurSpriteMovement2], a
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
ld l, a
inc l
ld a, [hl] ; c1x1
and a
jp z, InitializeSpriteStatus
call CheckSpriteAvailability
ret c ; if sprite is invisible, on tile >=$60, in grass or player is currently walking
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
ld l, a
inc l
ld a, [hl] ; c1x1
2015-08-30 00:15:05 +00:00
bit 7, a ; is the face player flag set?
jp nz, MakeNPCFacePlayer
2014-05-26 02:45:59 +00:00
ld b, a
2015-02-09 02:40:08 +00:00
ld a, [wFontLoaded]
2014-05-26 02:45:59 +00:00
bit 0, a
jp nz, notYetMoving
ld a, b
cp $2
jp z, UpdateSpriteMovementDelay ; c1x1 == 2
cp $3
jp z, UpdateSpriteInWalkingAnimation ; c1x1 == 3
ld a, [wWalkCounter]
2014-05-26 02:45:59 +00:00
and a
ret nz ; don't do anything yet if player is currently moving (redundant, already tested in CheckSpriteAvailability)
call InitializeSpriteScreenPosition
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $6
ld l, a
ld a, [hl] ; c2x6: movement byte 1
inc a
2015-07-15 11:27:59 +00:00
jr z, .randomMovement ; value $FF
2014-05-26 02:45:59 +00:00
inc a
2015-07-15 11:27:59 +00:00
jr z, .randomMovement ; value $FE
; scripted movement
2014-05-26 02:45:59 +00:00
dec a
2015-07-15 11:27:59 +00:00
ld [hl], a ; increment movement byte 1 (movement data index)
2014-05-26 02:45:59 +00:00
dec a
push hl
ld hl, wNPCNumScriptedSteps
dec [hl] ; decrement wNPCNumScriptedSteps
2014-05-26 02:45:59 +00:00
pop hl
2015-07-15 11:27:59 +00:00
ld de, wNPCMovementDirections
call LoadDEPlusA ; a = [wNPCMovementDirections + movement byte 1]
2014-05-26 02:45:59 +00:00
cp $e0
jp z, ChangeFacingDirection
cp $ff
2015-07-15 11:27:59 +00:00
jr nz, .next
; reached end of wNPCMovementDirections list
ld [hl], a ; store $ff in movement byte 1, disabling scripted movement
ld hl, wd730
2014-05-26 02:45:59 +00:00
res 0, [hl]
xor a
ld [wSimulatedJoypadStatesIndex], a
ld [wWastedByteCD3A], a
2014-05-26 02:45:59 +00:00
ret
2015-07-15 11:27:59 +00:00
.next
2014-05-26 02:45:59 +00:00
cp $fe
2015-07-15 11:27:59 +00:00
jr nz, .determineDirection
; current NPC movement data is $fe. this seems buggy
2014-05-26 02:45:59 +00:00
ld [hl], $1 ; set movement byte 1 to $1
2015-07-15 11:27:59 +00:00
ld de, wNPCMovementDirections
call LoadDEPlusA ; a = [wNPCMovementDirections + $fe] (?)
jr .determineDirection
.randomMovement
2015-07-15 20:58:21 +00:00
call GetTileSpriteStandsOn
2014-05-26 02:45:59 +00:00
call Random
2015-07-15 11:27:59 +00:00
.determineDirection
2014-05-26 02:45:59 +00:00
ld b, a
ld a, [wCurSpriteMovement2]
cp $d0
jr z, .moveDown ; movement byte 2 = $d0 forces down
cp $d1
jr z, .moveUp ; movement byte 2 = $d1 forces up
cp $d2
jr z, .moveLeft ; movement byte 2 = $d2 forces left
cp $d3
jr z, .moveRight ; movement byte 2 = $d3 forces right
ld a, b
cp $40 ; a < $40: down (or left)
jr nc, .notDown
ld a, [wCurSpriteMovement2]
cp $2
jr z, .moveLeft ; movement byte 2 = $2 only allows left or right
.moveDown
2015-07-15 11:27:59 +00:00
ld de, 2*SCREEN_WIDTH
2014-05-26 02:45:59 +00:00
add hl, de ; move tile pointer two rows down
2015-07-19 03:49:52 +00:00
lb de, 1, 0
lb bc, 4, SPRITE_FACING_DOWN
2014-05-26 02:45:59 +00:00
jr TryWalking
.notDown
cp $80 ; $40 <= a < $80: up (or right)
jr nc, .notUp
ld a, [wCurSpriteMovement2]
cp $2
jr z, .moveRight ; movement byte 2 = $2 only allows left or right
.moveUp
2015-07-15 11:27:59 +00:00
ld de, -2*SCREEN_WIDTH
2014-05-26 02:45:59 +00:00
add hl, de ; move tile pointer two rows up
2015-07-19 03:49:52 +00:00
lb de, -1, 0
lb bc, 8, SPRITE_FACING_UP
2014-05-26 02:45:59 +00:00
jr TryWalking
.notUp
cp $c0 ; $80 <= a < $c0: left (or up)
jr nc, .notLeft
ld a, [wCurSpriteMovement2]
cp $1
jr z, .moveUp ; movement byte 2 = $1 only allows up or down
.moveLeft
dec hl
dec hl ; move tile pointer two columns left
2015-07-19 03:49:52 +00:00
lb de, 0, -1
lb bc, 2, SPRITE_FACING_LEFT
2014-05-26 02:45:59 +00:00
jr TryWalking
.notLeft ; $c0 <= a: right (or down)
ld a, [wCurSpriteMovement2]
cp $1
jr z, .moveDown ; movement byte 2 = $1 only allows up or down
.moveRight
inc hl
inc hl ; move tile pointer two columns right
2015-07-19 03:49:52 +00:00
lb de, 0, 1
lb bc, 1, SPRITE_FACING_RIGHT
2014-05-26 02:45:59 +00:00
jr TryWalking
; changes facing direction by zeroing the movement delta and calling TryWalking
ChangeFacingDirection: ; 4fc8 (1:4fc8)
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)
2015-07-15 11:27:59 +00:00
; hl: pointer to tile the sprite would walk onto
2014-05-26 02:45:59 +00:00
; set carry on failure, clears carry on success
TryWalking: ; 4fcb (1:4fcb)
push hl
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $9
ld l, a
ld [hl], c ; c1x9 (update facing direction)
ld a, [H_CURRENTSPRITEOFFSET]
add $3
ld l, a
ld [hl], d ; c1x3 (update Y movement delta)
inc l
inc l
ld [hl], e ; c1x5 (update X movement delta)
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, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $4
ld l, a
ld a, [hl] ; c2x4: Y position
add d
ld [hli], a ; update Y position
ld a, [hl] ; c2x5: X position
add e
ld [hl], a ; update X position
ld a, [H_CURRENTSPRITEOFFSET]
ld l, a
ld [hl], $10 ; c2x0=16: walk animation counter
dec h
inc l
ld [hl], $3 ; c1x1: set movement status to walking
jp UpdateSpriteImage
; update the walking animation parameters for a sprite that is currently walking
UpdateSpriteInWalkingAnimation: ; 4ffe (1:4ffe)
ld a, [H_CURRENTSPRITEOFFSET]
add $7
ld l, a
ld a, [hl] ; c1x7 (counter until next walk animation frame)
inc a
ld [hl], a ; c1x7 += 1
cp $4
jr nz, .noNextAnimationFrame
xor a
ld [hl], a ; c1x7 = 0
inc l
ld a, [hl] ; c1x8 (walk animation frame)
inc a
and $3
ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step)
.noNextAnimationFrame
ld a, [H_CURRENTSPRITEOFFSET]
add $3
ld l, a
ld a, [hli] ; c1x3 (movement Y delta)
ld b, a
ld a, [hl] ; c1x4 (screen Y position)
add b
ld [hli], a ; update screen Y position
ld a, [hli] ; c1x5 (movement X delta)
ld b, a
ld a, [hl] ; c1x6 (screen X position)
add b
ld [hl], a ; update screen X position
ld a, [H_CURRENTSPRITEOFFSET]
ld l, a
inc h
ld a, [hl] ; c2x0 (walk animantion counter)
dec a
ld [hl], a ; update walk animantion counter
ret nz
ld a, $6 ; walking finished, update state
add l
ld l, a
ld a, [hl] ; c2x6 (movement byte 1)
cp $fe
jr nc, .initNextMovementCounter ; values $fe and $ff
ld a, [H_CURRENTSPRITEOFFSET]
inc a
ld l, a
dec h
ld [hl], $1 ; c1x1 = 1 (movement status ready)
ret
.initNextMovementCounter
call Random
ld a, [H_CURRENTSPRITEOFFSET]
add $8
ld l, a
ld a, [hRandomAdd]
and $7f
ld [hl], a ; c2x8: set next movement delay to a random value in [0,$7f]
dec h ; note that value 0 actually makes the delay $100 (bug?)
ld a, [H_CURRENTSPRITEOFFSET]
inc a
ld l, a
ld [hl], $2 ; c1x1 = 2 (movement status)
inc l
inc l
xor a
ld b, [hl] ; c1x3 (movement Y delta)
ld [hli], a ; reset movement Y delta
inc l
ld c, [hl] ; c1x5 (movement X delta)
ld [hl], a ; reset movement X delta
ret
; update delay value (c2x8) for sprites in the delayed state (c1x1)
UpdateSpriteMovementDelay: ; 5057 (1:5057)
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $6
ld l, a
ld a, [hl] ; c2x6: movement byte 1
inc l
inc l
cp $fe
jr nc, .tickMoveCounter ; values $fe or $ff
ld [hl], $0
jr .moving
.tickMoveCounter
dec [hl] ; c2x8: frame counter until next movement
jr nz, notYetMoving
.moving
dec h
ld a, [H_CURRENTSPRITEOFFSET]
inc a
ld l, a
ld [hl], $1 ; c1x1 = 1 (mark as ready to move)
notYetMoving: ; 5073 (1:5073)
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $8
ld l, a
ld [hl], $0 ; c1x8 = 0 (walk animation frame)
jp UpdateSpriteImage
2015-08-30 00:15:05 +00:00
MakeNPCFacePlayer: ; 507f (1:507f)
; 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]
2014-05-26 02:45:59 +00:00
bit 5, a
jr nz, notYetMoving
2015-08-30 00:15:05 +00:00
2014-05-26 02:45:59 +00:00
res 7, [hl]
2015-07-20 00:52:26 +00:00
ld a, [wPlayerDirection]
bit PLAYER_DIR_BIT_UP, a
2014-05-26 02:45:59 +00:00
jr z, .notFacingDown
2015-07-20 00:52:26 +00:00
ld c, SPRITE_FACING_DOWN
2014-05-26 02:45:59 +00:00
jr .facingDirectionDetermined
.notFacingDown
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_DOWN, a
2014-05-26 02:45:59 +00:00
jr z, .notFacingUp
2015-07-20 00:52:26 +00:00
ld c, SPRITE_FACING_UP
2014-05-26 02:45:59 +00:00
jr .facingDirectionDetermined
.notFacingUp
2015-07-20 00:52:26 +00:00
bit PLAYER_DIR_BIT_LEFT, a
2014-05-26 02:45:59 +00:00
jr z, .notFacingRight
2015-07-20 00:52:26 +00:00
ld c, SPRITE_FACING_RIGHT
2014-05-26 02:45:59 +00:00
jr .facingDirectionDetermined
.notFacingRight
2015-07-20 00:52:26 +00:00
ld c, SPRITE_FACING_LEFT
2014-05-26 02:45:59 +00:00
.facingDirectionDetermined
ld a, [H_CURRENTSPRITEOFFSET]
add $9
ld l, a
ld [hl], c ; c1x9: set facing direction
jr notYetMoving
InitializeSpriteStatus: ; 50ad (1:50ad)
ld [hl], $1 ; $c1x1: set movement status to ready
inc l
ld [hl], $ff ; $c1x2: set sprite image to $ff (invisible/off screen)
inc h
ld a, [H_CURRENTSPRITEOFFSET]
add $2
ld l, a
ld a, $8
ld [hli], a ; $c2x2: set Y displacement to 8
ld [hl], a ; $c2x3: set X displacement to 8
ret
; calculates the spprite's scrren position form its map position and the player position
InitializeSpriteScreenPosition: ; 50bd (1:50bd)
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $4
ld l, a
ld a, [W_YCOORD]
2014-05-26 02:45:59 +00:00
ld b, a
ld a, [hl] ; c2x4 (Y position + 4)
sub b ; relative to player position
swap a ; * 16
sub $4 ; - 4
dec h
ld [hli], a ; c1x4 (screen Y position)
inc h
ld a, [W_XCOORD]
2014-05-26 02:45:59 +00:00
ld b, a
ld a, [hli] ; c2x6 (X position + 4)
sub b ; relative to player position
swap a ; * 16
dec h
ld [hl], a ; c1x6 (screen X position)
ret
; tests if sprite is off screen or otherwise unable to do anything
CheckSpriteAvailability: ; 50dc (1:50dc)
predef IsObjectHidden
2014-05-26 02:45:59 +00:00
ld a, [$ffe5]
and a
jp nz, .spriteInvisible
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $6
ld l, a
ld a, [hl] ; c2x6: movement byte 1
cp $fe
2015-07-15 11:27:59 +00:00
jr c, .skipXVisibilityTest ; movement byte 1 < $fe (i.e. the sprite's movement is scripted)
2014-05-26 02:45:59 +00:00
ld a, [H_CURRENTSPRITEOFFSET]
add $4
ld l, a
ld b, [hl] ; c2x4: Y pos (+4)
ld a, [W_YCOORD]
2014-05-26 02:45:59 +00:00
cp b
jr z, .skipYVisibilityTest
jr nc, .spriteInvisible ; above screen region
add $8 ; screen is 9 tiles high
cp b
jr c, .spriteInvisible ; below screen region
.skipYVisibilityTest
inc l
ld b, [hl] ; c2x5: X pos (+4)
ld a, [W_XCOORD]
2014-05-26 02:45:59 +00:00
cp b
jr z, .skipXVisibilityTest
jr nc, .spriteInvisible ; left of screen region
add $9 ; screen is 10 tiles wide
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
2015-07-15 20:58:21 +00:00
call GetTileSpriteStandsOn
2014-05-26 02:45:59 +00:00
ld d, $60
ld a, [hli]
cp d
jr nc, .spriteInvisible ; standing on tile with ID >=$60 (bottom left tile)
ld a, [hld]
cp d
jr nc, .spriteInvisible ; standing on tile with ID >=$60 (bottom right tile)
ld bc, -20
2014-05-26 02:45:59 +00:00
add hl, bc ; go back one row of tiles
ld a, [hli]
cp d
jr nc, .spriteInvisible ; standing on tile with ID >=$60 (top left tile)
ld a, [hl]
cp d
jr c, .spriteVisible ; standing on tile with ID >=$60 (top right tile)
.spriteInvisible
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $2
ld l, a
ld [hl], $ff ; c1x2
scf
jr .done
.spriteVisible
ld c, a
ld a, [wWalkCounter]
2014-05-26 02:45:59 +00:00
and a
jr nz, .done ; if player is currently walking, we're done
call UpdateSpriteImage
inc h
ld a, [H_CURRENTSPRITEOFFSET]
add $7
ld l, a
ld a, [W_GRASSTILE]
cp c
ld a, $0
jr nz, .notInGrass
ld a, $80
.notInGrass
ld [hl], a ; c2x7
and a
.done
ret
UpdateSpriteImage: ; 5157 (1:5157)
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $8
ld l, a
ld a, [hli] ; c1x8: walk animation frame
ld b, a
ld a, [hl] ; c1x9: facing direction
add b
ld b, a
ld a, [$ff93] ; current sprite offset
add b
ld b, a
ld a, [H_CURRENTSPRITEOFFSET]
add $2
ld l, a
ld [hl], b ; c1x2: sprite to display
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: ; 516e (1:516e)
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $6
ld l, a
ld a, [hl] ; c2x6 (movement byte 1)
cp $fe
2015-07-15 11:27:59 +00:00
jr nc, .notScripted ; values $fe and $ff
; always allow walking if the movement is scripted
2014-05-26 02:45:59 +00:00
and a
ret
2015-07-15 11:27:59 +00:00
.notScripted
2014-05-26 02:45:59 +00:00
ld a, [W_TILESETCOLLISIONPTR]
ld l, a
ld a, [W_TILESETCOLLISIONPTR+1]
ld h, a
.tilePassableLoop
ld a, [hli]
cp $ff
jr z, .impassable
cp c
jr nz, .tilePassableLoop
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $6
ld l, a
ld a, [hl] ; $c2x6 (movement byte 1)
inc a
jr z, .impassable ; if $ff, no movement allowed (however, changing direction is)
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $4
ld l, a
ld a, [hli] ; c1x4 (screen Y pos)
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] ; c1x6 (screen X pos)
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
2014-05-26 02:45:59 +00:00
pop bc
pop de
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $c
ld l, a
ld a, [hl] ; c1xc (directions in which sprite collision would occur)
2014-05-26 02:45:59 +00:00
and b ; check against chosen direction (1,2,4 or 8)
jr nz, .impassable ; collision between sprites, don't go there
2014-05-26 02:45:59 +00:00
ld h, $c2
ld a, [H_CURRENTSPRITEOFFSET]
add $2
ld l, a
ld a, [hli] ; c2x2 (sprite Y displacement, 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
cp $5
jr c, .impassable ; if c2x2+d < 5, don't go ;bug: this tests probably were supposed to prevent sprites
jr .checkHorizontal ; from walking out too far, but this line makes sprites get stuck
.upwards ; whenever they walked upwards 5 steps
sub $1 ; on the other hand, the amount a sprite can walk out to the
jr c, .impassable ; if d2x2 == 0, don't go ; right of bottom is not limited (until the counter overflows)
.checkHorizontal
ld d, a
ld a, [hl] ; c2x3 (sprite X displacement, 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 d2x3 == 0, don't go
.passable
ld [hld], a ; update c2x3
ld [hl], d ; update c2x2
and a ; clear carry (marking success)
ret
.impassable
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
inc a
ld l, a
ld [hl], $2 ; c1x1 = 2 (set movement status to delayed)
inc l
inc l
xor a
ld [hli], a ; c1x3 = 0 (clear Y movement delta)
inc l
ld [hl], a ; c1x5 = 0 (clear X movement delta)
inc h
ld a, [H_CURRENTSPRITEOFFSET]
add $8
ld l, a
call Random
ld a, [hRandomAdd]
and $7f
ld [hl], a ; c2x8: set next movement delay 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 stancs on
; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to
; hl: output pointer
2015-07-15 20:58:21 +00:00
GetTileSpriteStandsOn: ; 5207 (1:5207)
2014-05-26 02:45:59 +00:00
ld h, $c1
ld a, [H_CURRENTSPRITEOFFSET]
add $4
ld l, a
ld a, [hli] ; c1x4: screen Y position
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] ; c1x6: screen Y position
srl a
srl a
2015-07-14 07:16:19 +00:00
srl a ; screen X tile
add SCREEN_WIDTH ; screen X tile + 20
2014-05-26 02:45:59 +00:00
ld d, $0
ld e, a
2015-07-18 20:52:03 +00:00
coord hl, 0, 0
2014-05-26 02:45:59 +00:00
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: ; 522f (1:522f)
add e
ld e, a
jr nc, .noCarry
inc d
.noCarry
ld a, [de]
ret
2015-07-15 11:27:59 +00:00
DoScriptedNPCMovement: ; 5236 (1:5236)
2015-07-15 20:58:21 +00:00
; 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]
2014-05-26 02:45:59 +00:00
bit 7, a
ret z
ld hl, wd72e
2014-05-26 02:45:59 +00:00
bit 7, [hl]
set 7, [hl]
2015-07-15 11:27:59 +00:00
jp z, InitScriptedNPCMovement
ld hl, wNPCMovementDirections2
ld a, [wNPCMovementDirections2Index]
2014-05-26 02:45:59 +00:00
add l
ld l, a
2015-07-15 11:27:59 +00:00
jr nc, .noCarry
2014-05-26 02:45:59 +00:00
inc h
2015-07-15 11:27:59 +00:00
.noCarry
2014-05-26 02:45:59 +00:00
ld a, [hl]
2015-07-15 11:27:59 +00:00
; check if moving up
2015-07-15 20:58:21 +00:00
cp NPC_MOVEMENT_UP
2015-07-15 11:27:59 +00:00
jr nz, .checkIfMovingDown
call GetSpriteScreenYPointer
ld c, SPRITE_FACING_UP
ld a, -2
jr .move
.checkIfMovingDown
2015-07-15 20:58:21 +00:00
cp NPC_MOVEMENT_DOWN
2015-07-15 11:27:59 +00:00
jr nz, .checkIfMovingLeft
call GetSpriteScreenYPointer
ld c, SPRITE_FACING_DOWN
ld a, 2
jr .move
.checkIfMovingLeft
2015-07-15 20:58:21 +00:00
cp NPC_MOVEMENT_LEFT
2015-07-15 11:27:59 +00:00
jr nz, .checkIfMovingRight
call GetSpriteScreenXPointer
ld c, SPRITE_FACING_LEFT
ld a, -2
jr .move
.checkIfMovingRight
2015-07-15 20:58:21 +00:00
cp NPC_MOVEMENT_RIGHT
2015-07-15 11:27:59 +00:00
jr nz, .noMatch
call GetSpriteScreenXPointer
ld c, SPRITE_FACING_RIGHT
ld a, 2
jr .move
.noMatch
2014-05-26 02:45:59 +00:00
cp $ff
ret
2015-07-15 11:27:59 +00:00
.move
2014-05-26 02:45:59 +00:00
ld b, a
ld a, [hl]
add b
ld [hl], a
ld a, [H_CURRENTSPRITEOFFSET]
add $9
ld l, a
ld a, c
2015-07-15 11:27:59 +00:00
ld [hl], a ; facing direction
call AnimScriptedNPCMovement
2015-07-15 20:58:21 +00:00
ld hl, wScriptedNPCWalkCounter
2014-05-26 02:45:59 +00:00
dec [hl]
ret nz
2015-07-15 20:58:21 +00:00
ld a, 8
ld [wScriptedNPCWalkCounter], a
ld hl, wNPCMovementDirections2Index
2014-05-26 02:45:59 +00:00
inc [hl]
ret
2015-07-15 11:27:59 +00:00
InitScriptedNPCMovement: ; 52a6 (1:52a6)
2014-05-26 02:45:59 +00:00
xor a
ld [wNPCMovementDirections2Index], a
2015-07-15 20:58:21 +00:00
ld a, 8
ld [wScriptedNPCWalkCounter], a
2015-07-15 11:27:59 +00:00
jp AnimScriptedNPCMovement
2014-05-26 02:45:59 +00:00
2015-07-15 11:27:59 +00:00
GetSpriteScreenYPointer: ; 52b2 (1:52b2)
2014-05-26 02:45:59 +00:00
ld a, $4
ld b, a
2015-07-15 11:27:59 +00:00
jr GetSpriteScreenXYPointerCommon
2014-05-26 02:45:59 +00:00
2015-07-15 11:27:59 +00:00
GetSpriteScreenXPointer: ; 52b7 (1:52b7)
2014-05-26 02:45:59 +00:00
ld a, $6
ld b, a
2015-07-15 11:27:59 +00:00
GetSpriteScreenXYPointerCommon: ; 52ba (1:52ba)
2014-05-26 02:45:59 +00:00
ld hl, wSpriteStateData1
ld a, [H_CURRENTSPRITEOFFSET]
add l
add b
ld l, a
ret
2015-07-15 11:27:59 +00:00
AnimScriptedNPCMovement: ; 52c3 (1:52c3)
2014-05-26 02:45:59 +00:00
ld hl, wSpriteStateData2
ld a, [H_CURRENTSPRITEOFFSET]
add $e
ld l, a
2015-07-15 11:27:59 +00:00
ld a, [hl] ; VRAM slot
2014-05-26 02:45:59 +00:00
dec a
swap a
ld b, a
ld hl, wSpriteStateData1
ld a, [H_CURRENTSPRITEOFFSET]
add $9
ld l, a
2015-07-15 11:27:59 +00:00
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
2014-05-26 02:45:59 +00:00
ret
2015-07-15 11:27:59 +00:00
.anim
2014-05-26 02:45:59 +00:00
add b
ld b, a
2015-07-15 11:27:59 +00:00
ld [hSpriteVRAMSlotAndFacing], a
call AdvanceScriptedNPCAnimFrameCounter
2014-05-26 02:45:59 +00:00
ld hl, wSpriteStateData1
ld a, [H_CURRENTSPRITEOFFSET]
add $2
ld l, a
2015-07-15 11:27:59 +00:00
ld a, [hSpriteVRAMSlotAndFacing]
2014-05-26 02:45:59 +00:00
ld b, a
2015-07-15 11:27:59 +00:00
ld a, [hSpriteAnimFrameCounter]
2014-05-26 02:45:59 +00:00
add b
ld [hl], a
ret
2015-07-15 11:27:59 +00:00
AdvanceScriptedNPCAnimFrameCounter: ; 5301 (1:5301)
2014-05-26 02:45:59 +00:00
ld a, [H_CURRENTSPRITEOFFSET]
add $7
ld l, a
2015-07-15 11:27:59 +00:00
ld a, [hl] ; intra-animation frame counter
2014-05-26 02:45:59 +00:00
inc a
ld [hl], a
2015-07-15 11:27:59 +00:00
cp 4
2014-05-26 02:45:59 +00:00
ret nz
xor a
2015-07-15 11:27:59 +00:00
ld [hl], a ; reset intra-animation frame counter
2014-05-26 02:45:59 +00:00
inc l
2015-07-15 11:27:59 +00:00
ld a, [hl] ; animation frame counter
2014-05-26 02:45:59 +00:00
inc a
and $3
ld [hl], a
2015-07-15 11:27:59 +00:00
ld [hSpriteAnimFrameCounter], a
2014-05-26 02:45:59 +00:00
ret