summaryrefslogtreecommitdiff
path: root/engine/overworld/sprite_collisions.asm
diff options
context:
space:
mode:
authordannye <33dannye@gmail.com>2020-11-04 00:06:44 -0600
committerdannye <33dannye@gmail.com>2020-11-04 00:06:44 -0600
commit5647ca687b92954dcf37a6ea6bfbc9a341c32de4 (patch)
treedde1937a1bfdb3a835f4155e1c2eb8f1aaf86f63 /engine/overworld/sprite_collisions.asm
parent53fcd05aa24693093d8af1dc8ec4fedd3957decc (diff)
Sync with pokered
Diffstat (limited to 'engine/overworld/sprite_collisions.asm')
-rw-r--r--engine/overworld/sprite_collisions.asm392
1 files changed, 392 insertions, 0 deletions
diff --git a/engine/overworld/sprite_collisions.asm b/engine/overworld/sprite_collisions.asm
new file mode 100644
index 00000000..db81977b
--- /dev/null
+++ b/engine/overworld/sprite_collisions.asm
@@ -0,0 +1,392 @@
+_UpdateSprites::
+ ld h, wSpriteStateData1 / $100
+ inc h
+ ld a, SPRITESTATEDATA2_IMAGEBASEOFFSET
+.spriteLoop
+ ld l, a
+ sub SPRITESTATEDATA2_IMAGEBASEOFFSET
+ ld c, a
+ ldh [hCurrentSpriteOffset], a
+ ld a, [hl]
+ and a
+ jr z, .skipSprite ; tests SPRITESTATEDATA2_IMAGEBASEOFFSET
+ push hl
+ push de
+ push bc
+ call .updateCurrentSprite
+ pop bc
+ pop de
+ pop hl
+.skipSprite
+ ld a, l
+ add $10 ; move to next sprite
+ cp SPRITESTATEDATA2_IMAGEBASEOFFSET ; test for overflow (back at beginning)
+ jr nz, .spriteLoop
+ ret
+.updateCurrentSprite
+ ldh a, [hCurrentSpriteOffset]
+ and a
+ jp z, UpdatePlayerSprite
+ cp $f0 ; pikachu
+ jp z, SpawnPikachu
+ ld a, [hl]
+
+UpdateNonPlayerSprite:
+ dec a
+ swap a
+ ldh [hTilePlayerStandingOn], a ; $10 * sprite#
+ ld a, [wNPCMovementScriptSpriteOffset] ; some sprite offset?
+ ld b, a
+ ldh a, [hCurrentSpriteOffset]
+ cp b
+ jr nz, .unequal
+ jp DoScriptedNPCMovement
+.unequal
+ jp UpdateNPCSprite
+
+; This detects if the current sprite (whose offset is at hCurrentSpriteOffset)
+; is going to collide with another sprite by looping over the other sprites.
+; The current sprite's offset will be labelled with i (e.g. i#SPRITESTATEDATA1_PICTUREID).
+; The loop sprite's offset will labelled with j (e.g. j#SPRITESTATEDATA1_PICTUREID).
+;
+; Note that the Y coordinate of the sprite (in [k#SPRITESTATEDATA1_YPIXELS])
+; is one of the following 9 values when the sprite is aligned with the grid:
+; $fc, $0c, $1c, $2c, ..., $7c.
+; The reason that 4 is added below to the coordinate is to make it align with a
+; multiple of $10 to make comparisons easier.
+DetectCollisionBetweenSprites:
+ ; nop
+
+ ld h, HIGH(wSpriteStateData1)
+ ldh a, [hCurrentSpriteOffset]
+ ld l, a
+
+ ld a, [hl] ; a = [i#SPRITESTATEDATA1_PICTUREID] (0 if slot is unused)
+ and a ; is this sprite slot slot used?
+ ret z ; return if not used
+
+ ld a, l
+ add 3
+ ld l, a
+
+ ld a, [hli] ; a = [i#SPRITESTATEDATA1_YSTEPVECTOR] (-1, 0, or 1)
+ call SetSpriteCollisionValues
+
+ ld a, [hli] ; a = [i#SPRITESTATEDATA1_YPIXELS]
+ add 4 ; align with multiple of $10
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving south or
+; subtract 7 from a if moving north.
+ add b
+ and $f0
+ or c
+
+ ldh [hFF90], a ; store Y coordinate adjusted for direction of movement
+
+ ld a, [hli] ; a = [i#SPRITESTATEDATA1_XSTEPVECTOR] (-1, 0, or 1)
+ call SetSpriteCollisionValues
+ ld a, [hl] ; a = [i#SPRITESTATEDATA1_XPIXELS]
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving east or
+; subtract 7 from a if moving west.
+ add b
+ and $f0
+ or c
+
+ ldh [hFF91], a ; store X coordinate adjusted for direction of movement
+
+ ld a, l
+ add 7
+ ld l, a
+
+ xor a
+ ld [hld], a ; zero [i#SPRITESTATEDATA1_0D] XXX what's this for?
+ ld [hld], a ; zero [i#SPRITESTATEDATA1_COLLISIONDATA]
+
+ ldh a, [hFF91]
+ ld [hld], a ; [i#SPRITESTATEDATA1_XADJUSTED]
+ ldh a, [hFF90]
+ ld [hl], a ; [i#SPRITESTATEDATA1_YADJUSTED]
+
+ xor a ; zero the loop counter
+
+.loop
+ ldh [hFF8F], a ; store loop counter
+ swap a
+ ld e, a
+ ldh a, [hCurrentSpriteOffset]
+ cp e ; does the loop sprite match the current sprite?
+ jp z, .next ; go to the next sprite if they match
+
+ ld d, h
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_PICTUREID] (0 if slot is unused)
+ and a ; is this sprite slot slot used?
+ jp z, .next ; go the next sprite if not used
+
+ inc e
+ inc e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_IMAGEINDEX] ($ff means the sprite is offscreen)
+ inc a
+ jp z, .next ; go the next sprite if offscreen
+
+ ldh a, [hCurrentSpriteOffset]
+ add 10
+ ld l, a
+
+ inc e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_YSTEPVECTOR]
+ call SetSpriteCollisionValues
+
+ inc e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_YPIXELS]
+ add 4 ; align with multiple of $10
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving south or
+; subtract 7 from a if moving north.
+ add b
+ and $f0
+ or c
+
+ sub [hl] ; subtract [i#SPRITESTATEDATA1_YADJUSTED] from [j#SPRITESTATEDATA1_YADJUSTED]
+
+; calculate the absolute value of the difference to get the distance
+ jr nc, .noCarry1
+ cpl
+ inc a
+.noCarry1
+ ldh [hFF90], a ; store the distance between the two sprites' adjusted Y values
+
+; Use the carry flag set by the above subtraction to determine which sprite's
+; Y coordinate is larger. This information is used later to set
+; [i#SPRITESTATEDATA1_COLLISIONDATA].
+; The following 5 lines set the lowest 2 bits of c, which are later shifted left by 2.
+; If sprite i's Y is larger, set lowest 2 bits of c to 10.
+; If sprite j's Y is larger or both are equal, set lowest 2 bits of c to 01.
+ push af
+ rl c
+ pop af
+ ccf
+ rl c
+
+; If sprite i's delta Y is 0, then b = 7, else b = 9.
+ ld b, 7
+ ld a, [hl] ; a = [i#SPRITESTATEDATA1_YADJUSTED]
+ and $f
+ jr z, .next1
+ ld b, 9
+
+.next1
+ ldh a, [hFF90] ; a = distance between adjusted Y coordinates
+ sub b
+ ldh [hFF92], a ; store distance adjusted using sprite i's direction
+ ld a, b
+ ldh [hFF90], a ; store 7 or 9 depending on sprite i's delta Y
+ jr c, .checkXDistance
+
+; If sprite j's delta Y is 0, then b = 7, else b = 9.
+ ld b, 7
+ dec e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_YSTEPVECTOR]
+ inc e
+ and a
+ jr z, .next2
+ ld b, 9
+
+.next2
+ ldh a, [hFF92] ; a = distance adjusted using sprite i's direction
+ sub b ; adjust distance using sprite j's direction
+ jr z, .checkXDistance
+ jr nc, .next ; go to next sprite if distance is still positive after both adjustments
+
+.checkXDistance
+ inc e
+ inc l
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_XSTEPVECTOR]
+
+ push bc
+
+ call SetSpriteCollisionValues
+ inc e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_XPIXELS]
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving east or
+; subtract 7 from a if moving west.
+ add b
+ and $f0
+ or c
+
+ pop bc
+
+ sub [hl] ; subtract [i#SPRITESTATEDATA1_XADJUSTED] from [j#SPRITESTATEDATA1_XADJUSTED]
+
+; calculate the absolute value of the difference to get the distance
+ jr nc, .noCarry2
+ cpl
+ inc a
+.noCarry2
+ ldh [hFF91], a ; store the distance between the two sprites' adjusted X values
+
+; Use the carry flag set by the above subtraction to determine which sprite's
+; X coordinate is larger. This information is used later to set
+; [i#SPRITESTATEDATA1_COLLISIONDATA].
+; The following 5 lines set the lowest 2 bits of c.
+; If sprite i's X is larger, set lowest 2 bits of c to 10.
+; If sprite j's X is larger or both are equal, set lowest 2 bits of c to 01.
+ push af
+ rl c
+ pop af
+ ccf
+ rl c
+
+; If sprite i's delta X is 0, then b = 7, else b = 9.
+ ld b, 7
+ ld a, [hl] ; a = [i#SPRITESTATEDATA1_XADJUSTED]
+ and $f
+ jr z, .next3
+ ld b, 9
+
+.next3
+ ldh a, [hFF91] ; a = distance between adjusted X coordinates
+ sub b
+ ldh [hFF92], a ; store distance adjusted using sprite i's direction
+ ld a, b
+ ldh [hFF91], a ; store 7 or 9 depending on sprite i's delta X
+ jr c, .collision
+
+; If sprite j's delta X is 0, then b = 7, else b = 9.
+ ld b, 7
+ dec e
+ ld a, [de] ; a = [j#SPRITESTATEDATA1_XSTEPVECTOR]
+ inc e
+ and a
+ jr z, .next4
+ ld b, 9
+
+.next4
+ ldh a, [hFF92] ; a = distance adjusted using sprite i's direction
+ sub b ; adjust distance using sprite j's direction
+ jr z, .collision
+ jr nc, .next ; go to next sprite if distance is still positive after both adjustments
+
+.collision
+ ld a, l
+ and $f0 ; collision with pikachu?
+ jr nz, .asm_4cd9
+ xor a
+ ld [wd434], a
+ ldh a, [hFF8F]
+ cp $f
+ jr nz, .asm_4cd9
+ call Func_4d0a
+ jr .asm_4cef
+.asm_4cd9
+ ldh a, [hFF91] ; a = 7 or 9 depending on sprite i's delta X
+ ld b, a
+ ldh a, [hFF90] ; a = 7 or 9 depending on sprite i's delta Y
+ inc l
+
+; If delta X isn't 0 and delta Y is 0, then b = %0011, else b = %1100.
+; (note that normally if delta X isn't 0, then delta Y must be 0 and vice versa)
+ cp b
+ jr c, .next5
+ ld b, %1100
+ jr .next6
+.next5
+ ld b, %0011
+
+.next6
+ ld a, c ; c has 2 bits set (one of bits 0-1 is set for the X axis and one of bits 2-3 for the Y axis)
+ and b ; we select either the bit in bits 0-1 or bits 2-3 based on the calculation immediately above
+ or [hl] ; or with existing collision direction bits in [i#SPRITESTATEDATA1_COLLISIONDATA]
+ ld [hl], a ; store new value
+ ld a, c ; useless code because a is overwritten before being used again
+
+; set bit in [i#SPRITESTATEDATA1_0E] or [i#SPRITESTATEDATA1_0F]
+; to indicate which sprite the collision occurred with
+ inc l
+ inc l
+.asm_4cef
+ ldh a, [hFF8F] ; a = loop counter
+ ld de, SpriteCollisionBitTable
+ add a
+ add e
+ ld e, a
+ jr nc, .noCarry3
+ inc d
+.noCarry3
+ ld a, [de]
+ or [hl]
+ ld [hli], a
+ inc de
+ ld a, [de]
+ or [hl]
+ ld [hl], a
+
+.next
+ ldh a, [hFF8F] ; a = loop counter
+ inc a
+ cp $10
+ jp nz, .loop
+ ret
+
+; takes delta X or delta Y in a
+; b = delta X/Y
+; c = 0 if delta X/Y is 0
+; c = 7 if delta X/Y is 1
+; c = 9 if delta X/Y is -1
+Func_4d0a:
+ ldh a, [hFF91]
+ ld b, a
+ ldh a, [hFF90]
+ inc l
+ cp b
+ jr c, .asm_4d17
+ ld b, %1100
+ jr .asm_4d19
+.asm_4d17
+ ld b, %11
+.asm_4d19
+ ld a, c
+ and b
+ ld [wd434], a
+ ld a, c
+ inc l
+ inc l
+ ret
+
+SetSpriteCollisionValues:
+ and a
+ ld b, 0
+ ld c, 0
+ jr z, .done
+ ld c, 9
+ cp -1
+ jr z, .ok
+ ld c, 7
+ ld a, 0
+.ok
+ ld b, a
+.done
+ ret
+
+SpriteCollisionBitTable:
+ db %00000000,%00000001
+ db %00000000,%00000010
+ db %00000000,%00000100
+ db %00000000,%00001000
+ db %00000000,%00010000
+ db %00000000,%00100000
+ db %00000000,%01000000
+ db %00000000,%10000000
+ db %00000001,%00000000
+ db %00000010,%00000000
+ db %00000100,%00000000
+ db %00001000,%00000000
+ db %00010000,%00000000
+ db %00100000,%00000000
+ db %01000000,%00000000
+ db %10000000,%00000000