shithub: pokered

ref: cec6e6e2fb7886acc85200f921d3913fd2ea8a2b
dir: /engine/battle/animations.asm/

View raw version
; Draws a "frame block". Frame blocks are blocks of tiles that are put
; together to form frames in battle animations.
DrawFrameBlock:
	ld l,c
	ld h,b
	ld a,[hli]
	ld [wNumFBTiles],a
	ld a,[wFBDestAddr + 1]
	ld e,a
	ld a,[wFBDestAddr]
	ld d,a
	xor a
	ld [wFBTileCounter],a ; loop counter
.loop
	ld a,[wFBTileCounter]
	inc a
	ld [wFBTileCounter],a
	ld a,[wSubAnimTransform]
	dec a
	jr z,.flipHorizontalAndVertical   ; 1
	dec a
	jp z,.flipHorizontalTranslateDown ; 2
	dec a
	jr z,.flipBaseCoords              ; 3
.noTransformation
	ld a,[wBaseCoordY]
	add [hl]
	ld [de],a ; store Y
	inc hl
	inc de
	ld a,[wBaseCoordX]
	jr .finishCopying
.flipBaseCoords
	ld a,[wBaseCoordY]
	ld b,a
	ld a,136
	sub b ; flip Y base coordinate
	add [hl] ; Y offset
	ld [de],a ; store Y
	inc hl
	inc de
	ld a,[wBaseCoordX]
	ld b,a
	ld a,168
	sub b ; flip X base coordinate
.finishCopying ; finish copying values to OAM (when [wSubAnimTransform] not 1 or 2)
	add [hl] ; X offset
	ld [de],a ; store X
	inc hl
	inc de
	ld a,[hli]
	add a,$31 ; base tile ID for battle animations
	ld [de],a ; store tile ID
	inc de
	ld a,[hli]
	ld [de],a ; store flags
	inc de
	jp .nextTile
.flipHorizontalAndVertical
	ld a,[wBaseCoordY]
	add [hl] ; Y offset
	ld b,a
	ld a,136
	sub b ; flip Y coordinate
	ld [de],a ; store Y
	inc hl
	inc de
	ld a,[wBaseCoordX]
	add [hl] ; X offset
	ld b,a
	ld a,168
	sub b ; flip X coordinate
	ld [de],a ; store X
	inc hl
	inc de
	ld a,[hli]
	add a,$31 ; base tile ID for battle animations
	ld [de],a ; store tile ID
	inc de
; toggle horizontal and vertical flip
	ld a,[hli] ; flags
	and a
	ld b,OAM_VFLIP | OAM_HFLIP
	jr z,.storeFlags1
	cp a,OAM_HFLIP
	ld b,OAM_VFLIP
	jr z,.storeFlags1
	cp a,OAM_VFLIP
	ld b,OAM_HFLIP
	jr z,.storeFlags1
	ld b,0
.storeFlags1
	ld a,b
	ld [de],a
	inc de
	jp .nextTile
.flipHorizontalTranslateDown
	ld a,[wBaseCoordY]
	add [hl]
	add a,40 ; translate Y coordinate downwards
	ld [de],a ; store Y
	inc hl
	inc de
	ld a,[wBaseCoordX]
	add [hl]
	ld b,a
	ld a,168
	sub b ; flip X coordinate
	ld [de],a ; store X
	inc hl
	inc de
	ld a,[hli]
	add a,$31 ; base tile ID for battle animations
	ld [de],a ; store tile ID
	inc de
	ld a,[hli]
	bit 5,a ; is horizontal flip enabled?
	jr nz,.disableHorizontalFlip
.enableHorizontalFlip
	set 5,a
	jr .storeFlags2
.disableHorizontalFlip
	res 5,a
.storeFlags2
	ld [de],a
	inc de
.nextTile
	ld a,[wFBTileCounter]
	ld c,a
	ld a,[wNumFBTiles]
	cp c
	jp nz,.loop ; go back up if there are more tiles to draw
.afterDrawingTiles
	ld a,[wFBMode]
	cp a,2
	jr z,.advanceFrameBlockDestAddr; skip delay and don't clean OAM buffer
	ld a,[wSubAnimFrameDelay]
	ld c,a
	call DelayFrames
	ld a,[wFBMode]
	cp a,3
	jr z,.advanceFrameBlockDestAddr ; skip cleaning OAM buffer
	cp a,4
	jr z,.done ; skip cleaning OAM buffer and don't advance the frame block destination address
	ld a,[wAnimationID]
	cp a,GROWL
	jr z,.resetFrameBlockDestAddr
	call AnimationCleanOAM
.resetFrameBlockDestAddr
	ld hl,wOAMBuffer ; OAM buffer
	ld a,l
	ld [wFBDestAddr + 1],a
	ld a,h
	ld [wFBDestAddr],a ; set destination address to beginning of OAM buffer
	ret
.advanceFrameBlockDestAddr
	ld a,e
	ld [wFBDestAddr + 1],a
	ld a,d
	ld [wFBDestAddr],a
.done
	ret

PlayAnimation:
	xor a
	ld [$FF8B],a ; it looks like nothing reads this
	ld [wSubAnimTransform],a
	ld a,[wAnimationID] ; get animation number
	dec a
	ld l,a
	ld h,0
	add hl,hl
	ld de,AttackAnimationPointers  ; animation command stream pointers
	add hl,de
	ld a,[hli]
	ld h,[hl]
	ld l,a
.animationLoop
	ld a,[hli]
	cp a,$FF
	jr z,.AnimationOver
	cp a,$C0 ; is this subanimation or a special effect?
	jr c,.playSubanimation
.doSpecialEffect
	ld c,a
	ld de,SpecialEffectPointers
.searchSpecialEffectTableLoop
	ld a,[de]
	cp c
	jr z,.foundMatch
	inc de
	inc de
	inc de
	jr .searchSpecialEffectTableLoop
.foundMatch
	ld a,[hli]
	cp a,$FF ; is there a sound to play?
	jr z,.skipPlayingSound
	ld [wAnimSoundID],a ; store sound
	push hl
	push de
	call GetMoveSound
	call PlaySound
	pop de
	pop hl
.skipPlayingSound
	push hl
	inc de
	ld a,[de]
	ld l,a
	inc de
	ld a,[de]
	ld h,a
	ld de,.nextAnimationCommand
	push de
	jp [hl] ; jump to special effect function
.playSubanimation
	ld c,a
	and a,%00111111
	ld [wSubAnimFrameDelay],a
	xor a
	sla c
	rla
	sla c
	rla
	ld [wWhichBattleAnimTileset],a
	ld a,[hli] ; sound
	ld [wAnimSoundID],a ; store sound
	ld a,[hli] ; subanimation ID
	ld c,l
	ld b,h
	ld l,a
	ld h,0
	add hl,hl
	ld de,SubanimationPointers
	add hl,de
	ld a,l
	ld [wSubAnimAddrPtr],a
	ld a,h
	ld [wSubAnimAddrPtr + 1],a
	ld l,c
	ld h,b
	push hl
	ld a,[rOBP0]
	push af
	ld a,[wAnimPalette]
	ld [rOBP0],a
	call LoadAnimationTileset
	call LoadSubanimation
	call PlaySubanimation
	pop af
	ld [rOBP0],a
.nextAnimationCommand
	pop hl
	jr .animationLoop
.AnimationOver
	ret

LoadSubanimation:
	ld a,[wSubAnimAddrPtr + 1]
	ld h,a
	ld a,[wSubAnimAddrPtr]
	ld l,a
	ld a,[hli]
	ld e,a
	ld a,[hl]
	ld d,a ; de = address of subanimation
	ld a,[de]
	ld b,a
	and a,31
	ld [wSubAnimCounter],a ; number of frame blocks
	ld a,b
	and a,%11100000
	cp a,5 << 5 ; is subanimation type 5?
	jr nz,.isNotType5
.isType5
	call GetSubanimationTransform2
	jr .saveTransformation
.isNotType5
	call GetSubanimationTransform1
.saveTransformation
; place the upper 3 bits of a into bits 0-2 of a before storing
	srl a
	swap a
	ld [wSubAnimTransform],a
	cp a,4 ; is the animation reversed?
	ld hl,0
	jr nz,.storeSubentryAddr
; if the animation is reversed, then place the initial subentry address at the end of the list of subentries
	ld a,[wSubAnimCounter]
	dec a
	ld bc,3
.loop
	add hl,bc
	dec a
	jr nz,.loop
.storeSubentryAddr
	inc de
	add hl,de
	ld a,l
	ld [wSubAnimSubEntryAddr],a
	ld a,h
	ld [wSubAnimSubEntryAddr + 1],a
	ret

; called if the subanimation type is not 5
; sets the transform to 0 (i.e. no transform) if it's the player's turn
; sets the transform to the subanimation type if it's the enemy's turn
GetSubanimationTransform1:
	ld b,a
	ld a,[H_WHOSETURN]
	and a
	ld a,b
	ret nz
	xor a
	ret

; called if the subanimation type is 5
; sets the transform to 2 (i.e. horizontal and vertical flip) if it's the player's turn
; sets the transform to 0 (i.e. no transform) if it's the enemy's turn
GetSubanimationTransform2:
	ld a,[H_WHOSETURN]
	and a
	ld a,2 << 5
	ret z
	xor a
	ret

; loads tile patterns for battle animations
LoadAnimationTileset:
	ld a,[wWhichBattleAnimTileset]
	add a
	add a
	ld hl,AnimationTilesetPointers
	ld e,a
	ld d,0
	add hl,de
	ld a,[hli]
	ld [wTempTilesetNumTiles],a ; number of tiles
	ld a,[hli]
	ld e,a
	ld a,[hl]
	ld d,a ; de = address of tileset
	ld hl,vSprites + $310
	ld b, BANK(AnimationTileset1) ; ROM bank
	ld a,[wTempTilesetNumTiles]
	ld c,a ; number of tiles
	jp CopyVideoData ; load tileset

AnimationTilesetPointers:
	db 79 ; number of tiles
	dw AnimationTileset1
	db $FF

	db 79 ; number of tiles
	dw AnimationTileset2
	db $FF

	db 64 ; number of tiles
	dw AnimationTileset1
	db $FF

AnimationTileset1:
	INCBIN "gfx/attack_anim_1.2bpp"

AnimationTileset2:
	INCBIN "gfx/attack_anim_2.2bpp"

SlotMachineTiles2:
IF DEF(_RED)
	INCBIN "gfx/red/slotmachine2.2bpp"
ENDC
IF DEF(_BLUE)
	INCBIN "gfx/blue/slotmachine2.2bpp"
ENDC

MoveAnimation:
	push hl
	push de
	push bc
	push af
	call WaitForSoundToFinish
	call SetAnimationPalette
	ld a,[wAnimationID]
	and a
	jr z, .animationFinished

	; if throwing a Poké Ball, skip the regular animation code
	cp a,TOSS_ANIM
	jr nz, .moveAnimation
	ld de, .animationFinished
	push de
	jp TossBallAnimation

.moveAnimation
	; check if battle animations are disabled in the options
	ld a,[wOptions]
	bit 7,a
	jr nz, .animationsDisabled
	call ShareMoveAnimations
	call PlayAnimation
	jr .next4
.animationsDisabled
	ld c,30
	call DelayFrames
.next4
	call PlayApplyingAttackAnimation ; shake the screen or flash the pic in and out (to show damage)
.animationFinished
	call WaitForSoundToFinish
	xor a
	ld [wSubAnimSubEntryAddr],a
	ld [wUnusedD09B],a
	ld [wSubAnimTransform],a
	dec a
	ld [wAnimSoundID],a
	pop af
	pop bc
	pop de
	pop hl
	ret

ShareMoveAnimations:
; some moves just reuse animations from status conditions
	ld a,[H_WHOSETURN]
	and a
	ret z

	; opponent’s turn

	ld a,[wAnimationID]

	cp a,AMNESIA
	ld b,CONF_ANIM
	jr z, .replaceAnim

	cp a,REST
	ld b,SLP_ANIM
	ret nz

.replaceAnim
	ld a,b
	ld [wAnimationID],a
	ret

PlayApplyingAttackAnimation:
; Generic animation that shows after the move's individual animation
; Different animation depending on whether the move has an additional effect and on whose turn it is
	ld a,[wAnimationType]
	and a
	ret z
	dec a
	add a
	ld c,a
	ld b,0
	ld hl,AnimationTypePointerTable
	add hl,bc
	ld a,[hli]
	ld h,[hl]
	ld l,a
	jp [hl]

AnimationTypePointerTable:
	dw ShakeScreenVertically ; enemy mon has used a damaging move without a side effect
	dw ShakeScreenHorizontallyHeavy ; enemy mon has used a damaging move with a side effect
	dw ShakeScreenHorizontallySlow ; enemy mon has used a non-damaging move
	dw BlinkEnemyMonSprite ; player mon has used a damaging move without a side effect
	dw ShakeScreenHorizontallyLight ; player mon has used a damaging move with a side effect
	dw ShakeScreenHorizontallySlow2 ; player mon has used a non-damaging move

ShakeScreenVertically:
	call PlayApplyingAttackSound
	ld b, 8
	jp AnimationShakeScreenVertically

ShakeScreenHorizontallyHeavy:
	call PlayApplyingAttackSound
	ld b, 8
	jp AnimationShakeScreenHorizontallyFast

ShakeScreenHorizontallySlow:
	lb bc, 6, 2
	jr AnimationShakeScreenHorizontallySlow

BlinkEnemyMonSprite:
	call PlayApplyingAttackSound
	jp AnimationBlinkEnemyMon

ShakeScreenHorizontallyLight:
	call PlayApplyingAttackSound
	ld b, 2
	jp AnimationShakeScreenHorizontallyFast

ShakeScreenHorizontallySlow2:
	lb bc, 3, 2

AnimationShakeScreenHorizontallySlow:
	push bc
	push bc
.loop1
	ld a, [rWX]
	inc a
	ld [rWX], a
	ld c, 2
	call DelayFrames
	dec b
	jr nz, .loop1
	pop bc
.loop2
	ld a, [rWX]
	dec a
	ld [rWX], a
	ld c, 2
	call DelayFrames
	dec b
	jr nz, .loop2
	pop bc
	dec c
	jr nz, AnimationShakeScreenHorizontallySlow
	ret

SetAnimationPalette:
	ld a, [wOnSGB]
	and a
	ld a, $e4
	jr z, .notSGB
	ld a, $f0
	ld [wAnimPalette], a
	ld b, $e4
	ld a, [wAnimationID]
	cp TRADE_BALL_DROP_ANIM
	jr c, .next
	cp TRADE_BALL_POOF_ANIM + 1
	jr nc, .next
	ld b, $f0
.next
	ld a, b
	ld [rOBP0], a
	ld a, $6c
	ld [rOBP1], a
	ret
.notSGB
	ld a, $e4
	ld [wAnimPalette], a
	ld [rOBP0], a
	ld a, $6c
	ld [rOBP1], a
	ret

PlaySubanimation:
	ld a,[wAnimSoundID]
	cp a,$FF
	jr z,.skipPlayingSound
	call GetMoveSound
	call PlaySound
.skipPlayingSound
	ld hl,wOAMBuffer ; base address of OAM buffer
	ld a,l
	ld [wFBDestAddr + 1],a
	ld a,h
	ld [wFBDestAddr],a
	ld a,[wSubAnimSubEntryAddr + 1]
	ld h,a
	ld a,[wSubAnimSubEntryAddr]
	ld l,a
.loop
	push hl
	ld c,[hl] ; frame block ID
	ld b,0
	ld hl,FrameBlockPointers
	add hl,bc
	add hl,bc
	ld a,[hli]
	ld c,a
	ld a,[hli]
	ld b,a
	pop hl
	inc hl
	push hl
	ld e,[hl] ; base coordinate ID
	ld d,0
	ld hl,FrameBlockBaseCoords  ; base coordinate table
	add hl,de
	add hl,de
	ld a,[hli]
	ld [wBaseCoordY],a
	ld a,[hl]
	ld [wBaseCoordX],a
	pop hl
	inc hl
	ld a,[hl] ; frame block mode
	ld [wFBMode],a
	call DrawFrameBlock
	call DoSpecialEffectByAnimationId ; run animation-specific function (if there is one)
	ld a,[wSubAnimCounter]
	dec a
	ld [wSubAnimCounter],a
	ret z
	ld a,[wSubAnimSubEntryAddr + 1]
	ld h,a
	ld a,[wSubAnimSubEntryAddr]
	ld l,a
	ld a,[wSubAnimTransform]
	cp a,4 ; is the animation reversed?
	ld bc,3
	jr nz,.nextSubanimationSubentry
	ld bc,-3
.nextSubanimationSubentry
	add hl,bc
	ld a,h
	ld [wSubAnimSubEntryAddr + 1],a
	ld a,l
	ld [wSubAnimSubEntryAddr],a
	jp .loop

AnimationCleanOAM:
	push hl
	push de
	push bc
	push af
	call DelayFrame
	call ClearSprites
	pop af
	pop bc
	pop de
	pop hl
	ret

; this runs after each frame block is drawn in a subanimation
; it runs a particular special effect based on the animation ID
DoSpecialEffectByAnimationId:
	push hl
	push de
	push bc
	ld a,[wAnimationID]
	ld hl,AnimationIdSpecialEffects
	ld de,3
	call IsInArray
	jr nc,.done
	inc hl
	ld a,[hli]
	ld h,[hl]
	ld l,a
	ld de,.done
	push de
	jp [hl]
.done
	pop bc
	pop de
	pop hl
	ret

; Format: Animation ID (1 byte), Address (2 bytes)
AnimationIdSpecialEffects:
	db MEGA_PUNCH
	dw AnimationFlashScreen

	db GUILLOTINE
	dw AnimationFlashScreen

	db MEGA_KICK
	dw AnimationFlashScreen

	db HEADBUTT
	dw AnimationFlashScreen

	db TAIL_WHIP
	dw TailWhipAnimationUnused

	db GROWL
	dw DoGrowlSpecialEffects

	db DISABLE
	dw AnimationFlashScreen

	db BLIZZARD
	dw DoBlizzardSpecialEffects

	db BUBBLEBEAM
	dw AnimationFlashScreen

	db HYPER_BEAM
	dw FlashScreenEveryFourFrameBlocks

	db THUNDERBOLT
	dw FlashScreenEveryEightFrameBlocks

	db REFLECT
	dw AnimationFlashScreen

	db SELFDESTRUCT
	dw DoExplodeSpecialEffects

	db SPORE
	dw AnimationFlashScreen

	db EXPLOSION
	dw DoExplodeSpecialEffects

	db ROCK_SLIDE
	dw DoRockSlideSpecialEffects

	db TRADE_BALL_DROP_ANIM
	dw TradeHidePokemon

	db TRADE_BALL_SHAKE_ANIM
	dw TradeShakePokeball

	db TRADE_BALL_TILT_ANIM
	dw TradeJumpPokeball

	db TOSS_ANIM
	dw DoBallTossSpecialEffects

	db SHAKE_ANIM
	dw DoBallShakeSpecialEffects

	db POOF_ANIM
	dw DoPoofSpecialEffects

	db GREATTOSS_ANIM
	dw DoBallTossSpecialEffects

	db ULTRATOSS_ANIM
	dw DoBallTossSpecialEffects

	db $FF ; terminator

DoBallTossSpecialEffects:
	ld a,[wcf91]
	cp a,3 ; is it a Master Ball or Ultra Ball?
	jr nc,.skipFlashingEffect
.flashingEffect ; do a flashing effect if it's Master Ball or Ultra Ball
	ld a,[rOBP0]
	xor a,%00111100 ; complement colors 1 and 2
	ld [rOBP0],a
.skipFlashingEffect
	ld a,[wSubAnimCounter]
	cp a,11 ; is it the beginning of the subanimation?
	jr nz,.skipPlayingSound
; if it is the beginning of the subanimation, play a sound
	ld a,SFX_BALL_TOSS
	call PlaySound
.skipPlayingSound
	ld a,[wIsInBattle]
	cp a,02 ; is it a trainer battle?
	jr z,.isTrainerBattle
	ld a,[wd11e]
	cp a,$10 ; is the enemy pokemon the Ghost Marowak?
	ret nz
; if the enemy pokemon is the Ghost Marowak, make it dodge during the last 3 frames
	ld a,[wSubAnimCounter]
	cp a,3
	jr z,.moveGhostMarowakLeft
	cp a,2
	jr z,.moveGhostMarowakLeft
	cp a,1
	ret nz
.moveGhostMarowakLeft
	coord hl, 17, 0
	ld de,20
	lb bc, 7, 7
.loop
	push hl
	push bc
	call AnimCopyRowRight ; move row of tiles left
	pop bc
	pop hl
	add hl,de
	dec b
	jr nz,.loop
	ld a,%00001000
	ld [rNR10],a ; Channel 1 sweep register
	ret
.isTrainerBattle ; if it's a trainer battle, shorten the animation by one frame
	ld a,[wSubAnimCounter]
	cp a,3
	ret nz
	dec a
	ld [wSubAnimCounter],a
	ret

DoBallShakeSpecialEffects:
	ld a,[wSubAnimCounter]
	cp a,4 ; is it the beginning of a shake?
	jr nz,.skipPlayingSound
; if it is the beginning of a shake, play a sound and wait 2/3 of a second
	ld a,SFX_TINK
	call PlaySound
	ld c,40
	call DelayFrames
.skipPlayingSound
	ld a,[wSubAnimCounter]
	dec a
	ret nz
; if it's the end of the ball shaking subanimation, check if more shakes are left and restart the subanimation
	ld a,[wNumShakes] ; number of shakes
	dec a ; decrement number of shakes
	ld [wNumShakes],a
	ret z
; if there are shakes left, restart the subanimation
	ld a,[wSubAnimSubEntryAddr]
	ld l,a
	ld a,[wSubAnimSubEntryAddr + 1]
	ld h,a
	ld de,-(4 * 3) ; 4 subentries and 3 bytes per subentry
	add hl,de
	ld a,l
	ld [wSubAnimSubEntryAddr],a
	ld a,h
	ld [wSubAnimSubEntryAddr + 1],a
	ld a,5 ; number of subentries in the ball shaking subanimation plus one
	ld [wSubAnimCounter],a
	ret

; plays a sound after the second frame of the poof animation
DoPoofSpecialEffects:
	ld a,[wSubAnimCounter]
	cp a,5
	ret nz
	ld a,SFX_BALL_POOF
	jp PlaySound

DoRockSlideSpecialEffects:
	ld a,[wSubAnimCounter]
	cp a,12
	ret nc
	cp a,8
	jr nc,.shakeScreen
	cp a,1
	jp z,AnimationFlashScreen ; if it's the end of the subanimation, flash the screen
	ret
; if the subaninmation counter is between 8 and 11, shake the screen horizontally and vertically
.shakeScreen
	ld b,1
	predef PredefShakeScreenHorizontally ; shake horizontally
	ld b,1
	predef_jump PredefShakeScreenVertically ; shake vertically

FlashScreenEveryEightFrameBlocks:
	ld a,[wSubAnimCounter]
	and a,7 ; is the subanimation counter exactly 8?
	call z,AnimationFlashScreen ; if so, flash the screen
	ret

; flashes the screen if the subanimation counter is divisible by 4
FlashScreenEveryFourFrameBlocks:
	ld a,[wSubAnimCounter]
	and a,3
	call z,AnimationFlashScreen
	ret

; used for Explosion and Selfdestruct
DoExplodeSpecialEffects:
	ld a,[wSubAnimCounter]
	cp a,1 ; is it the end of the subanimation?
	jr nz,FlashScreenEveryFourFrameBlocks
; if it's the end of the subanimation, make the attacking pokemon disappear
	coord hl, 1, 5
	jp AnimationHideMonPic ; make pokemon disappear

; flashes the screen when subanimation counter is 1 modulo 4
DoBlizzardSpecialEffects:
	ld a,[wSubAnimCounter]
	cp a,13
	jp z,AnimationFlashScreen
	cp a,9
	jp z,AnimationFlashScreen
	cp a,5
	jp z,AnimationFlashScreen
	cp a,1
	jp z,AnimationFlashScreen
	ret

; flashes the screen at 3 points in the subanimation
; unused
FlashScreenUnused:
	ld a,[wSubAnimCounter]
	cp a,14
	jp z,AnimationFlashScreen
	cp a,9
	jp z,AnimationFlashScreen
	cp a,2
	jp z,AnimationFlashScreen
	ret

; function to make the pokemon disappear at the beginning of the animation
TradeHidePokemon:
	ld a,[wSubAnimCounter]
	cp a,6
	ret nz
	ld a,2 * SCREEN_WIDTH + 7
	jp ClearMonPicFromTileMap ; make pokemon disappear

; function to make a shaking pokeball jump up at the end of the animation
TradeShakePokeball:
	ld a,[wSubAnimCounter]
	cp a,1
	ret nz
; if it's the end of the animation, make the ball jump up
	ld de,BallMoveDistances1
.loop
	ld hl,wOAMBuffer ; OAM buffer
	ld bc,4
.innerLoop
	ld a,[de]
	cp a,$ff
	jr z,.done
	add [hl] ; add to Y value of OAM entry
	ld [hl],a
	add hl,bc
	ld a,l
	cp a,4 * 4 ; there are 4 entries, each 4 bytes
	jr nz,.innerLoop
	inc de
	push bc
	call Delay3
	pop bc
	jr .loop
.done
	call AnimationCleanOAM
	ld a,SFX_TRADE_MACHINE
	jp PlaySound

BallMoveDistances1:
	db -12,-12,-8
	db $ff ; terminator

; function to make the pokeball jump up
TradeJumpPokeball:
	ld de,BallMoveDistances2
.loop
	ld hl,wOAMBuffer ; OAM buffer
	ld bc,4
.innerLoop
	ld a,[de]
	cp a,$ff
	jp z,ClearScreen
	add [hl]
	ld [hl],a
	add hl,bc
	ld a,l
	cp a,4 * 4 ; there are 4 entries, each 4 bytes
	jr nz,.innerLoop
	inc de
	push de
	ld a,[de]
	cp a,12
	jr z,.playSound
	cp a,$ff
	jr nz,.skipPlayingSound
.playSound ; play sound if next move distance is 12 or this is the last one
	ld a,SFX_BATTLE_18
	call PlaySound
.skipPlayingSound
	push bc
	ld c,5
	call DelayFrames
	pop bc
	ld a,[hSCX] ; background scroll X
	sub a,8 ; scroll to the left
	ld [hSCX],a
	pop de
	jr .loop

BallMoveDistances2:
	db 11,12,-12,-7,7,12,-8,8
	db $ff ; terminator

; this function copies the current musical note graphic
; so that there are two musical notes flying towards the defending pokemon
DoGrowlSpecialEffects:
	ld hl,wOAMBuffer ; OAM buffer
	ld de,wOAMBuffer + $10
	ld bc,$10
	call CopyData ; copy the musical note graphic
	ld a,[wSubAnimCounter]
	dec a
	call z,AnimationCleanOAM ; clean up at the end of the subanimation
	ret

; this is associated with Tail Whip, but Tail Whip doesn't use any subanimations
TailWhipAnimationUnused:
	ld a,1
	ld [wSubAnimCounter],a
	ld c,20
	jp DelayFrames

; Format: Special Effect ID (1 byte), Address (2 bytes)
SpecialEffectPointers:
	db SE_DARK_SCREEN_FLASH ; $FE
	dw AnimationFlashScreen
	db SE_DARK_SCREEN_PALETTE ; $FD
	dw AnimationDarkScreenPalette
	db SE_RESET_SCREEN_PALETTE ; $FC
	dw AnimationResetScreenPalette
	db SE_SHAKE_SCREEN ; $FB
	dw AnimationShakeScreen
	db SE_WATER_DROPLETS_EVERYWHERE ; $FA
	dw AnimationWaterDropletsEverywhere
	db SE_DARKEN_MON_PALETTE ; $F9
	dw AnimationDarkenMonPalette
	db SE_FLASH_SCREEN_LONG ; $F8
	dw AnimationFlashScreenLong
	db SE_SLIDE_MON_UP ; $F7
	dw AnimationSlideMonUp
	db SE_SLIDE_MON_DOWN ; $F6
	dw AnimationSlideMonDown
	db SE_FLASH_MON_PIC ; $F5
	dw AnimationFlashMonPic
	db SE_SLIDE_MON_OFF ; $F4
	dw AnimationSlideMonOff
	db SE_BLINK_MON ; $F3
	dw AnimationBlinkMon
	db SE_MOVE_MON_HORIZONTALLY ; $F2
	dw AnimationMoveMonHorizontally
	db SE_RESET_MON_POSITION ; $F1
	dw AnimationResetMonPosition
	db SE_LIGHT_SCREEN_PALETTE ; $F0
	dw AnimationLightScreenPalette
	db SE_HIDE_MON_PIC ; $EF
	dw AnimationHideMonPic
	db SE_SQUISH_MON_PIC ; $EE
	dw AnimationSquishMonPic
	db SE_SHOOT_BALLS_UPWARD ; $ED
	dw AnimationShootBallsUpward
	db SE_SHOOT_MANY_BALLS_UPWARD ; $EC
	dw AnimationShootManyBallsUpward
	db SE_BOUNCE_UP_AND_DOWN ; $EB
	dw AnimationBoundUpAndDown
	db SE_MINIMIZE_MON ; $EA
	dw AnimationMinimizeMon
	db SE_SLIDE_MON_DOWN_AND_HIDE ; $E9
	dw AnimationSlideMonDownAndHide
	db SE_TRANSFORM_MON ; $E8
	dw AnimationTransformMon
	db SE_LEAVES_FALLING ; $E7
	dw AnimationLeavesFalling
	db SE_PETALS_FALLING ; $E6
	dw AnimationPetalsFalling
	db SE_SLIDE_MON_HALF_OFF ; $E5
	dw AnimationSlideMonHalfOff
	db SE_SHAKE_ENEMY_HUD ; $E4
	dw AnimationShakeEnemyHUD
	db SE_SHAKE_ENEMY_HUD_2 ; unused--same pointer as SE_SHAKE_ENEMY_HUD ($E4)
	dw AnimationShakeEnemyHUD
	db SE_SPIRAL_BALLS_INWARD ; $E2
	dw AnimationSpiralBallsInward
	db SE_DELAY_ANIMATION_10 ; $E1
	dw AnimationDelay10
	db SE_FLASH_ENEMY_MON_PIC ; unused--same as SE_FLASH_MON_PIC ($F5), but for the enemy mon
	dw AnimationFlashEnemyMonPic
	db SE_HIDE_ENEMY_MON_PIC ; $DF
	dw AnimationHideEnemyMonPic
	db SE_BLINK_ENEMY_MON ; $DE
	dw AnimationBlinkEnemyMon
	db SE_SHOW_MON_PIC ; $DD
	dw AnimationShowMonPic
	db SE_SHOW_ENEMY_MON_PIC ; $DC
	dw AnimationShowEnemyMonPic
	db SE_SLIDE_ENEMY_MON_OFF ; $DB
	dw AnimationSlideEnemyMonOff
	db SE_SHAKE_BACK_AND_FORTH ; $DA
	dw AnimationShakeBackAndForth
	db SE_SUBSTITUTE_MON ; $D9
	dw AnimationSubstitute
	db SE_WAVY_SCREEN ; $D8
	dw AnimationWavyScreen
	db $FF

AnimationDelay10:
	ld c,10
	jp DelayFrames

; calls a function with the turn flipped from player to enemy or vice versa
; input - hl - address of function to call
CallWithTurnFlipped:
	ld a,[H_WHOSETURN]
	push af
	xor a,1
	ld [H_WHOSETURN],a
	ld de,.returnAddress
	push de
	jp [hl]
.returnAddress
	pop af
	ld [H_WHOSETURN],a
	ret

; flashes the screen for an extended period (48 frames)
AnimationFlashScreenLong:
	ld a,3 ; cycle through the palettes 3 times
	ld [wFlashScreenLongCounter],a
	ld a,[wOnSGB] ; running on SGB?
	and a
	ld hl,FlashScreenLongMonochrome
	jr z,.loop
	ld hl,FlashScreenLongSGB
.loop
	push hl
.innerLoop
	ld a,[hli]
	cp a,$01 ; is it the end of the palettes?
	jr z,.endOfPalettes
	ld [rBGP],a
	call FlashScreenLongDelay
	jr .innerLoop
.endOfPalettes
	ld a,[wFlashScreenLongCounter]
	dec a
	ld [wFlashScreenLongCounter],a
	pop hl
	jr nz,.loop
	ret

; BG palettes
FlashScreenLongMonochrome:
	db %11111001 ; 3, 3, 2, 1
	db %11111110 ; 3, 3, 3, 2
	db %11111111 ; 3, 3, 3, 3
	db %11111110 ; 3, 3, 3, 2
	db %11111001 ; 3, 3, 2, 1
	db %11100100 ; 3, 2, 1, 0
	db %10010000 ; 2, 1, 0, 0
	db %01000000 ; 1, 0, 0, 0
	db %00000000 ; 0, 0, 0, 0
	db %01000000 ; 1, 0, 0, 0
	db %10010000 ; 2, 1, 0, 0
	db %11100100 ; 3, 2, 1, 0
	db $01 ; terminator

; BG palettes
FlashScreenLongSGB:
	db %11111000 ; 3, 3, 2, 0
	db %11111100 ; 3, 3, 3, 0
	db %11111111 ; 3, 3, 3, 3
	db %11111100 ; 3, 3, 3, 0
	db %11111000 ; 3, 3, 2, 0
	db %11100100 ; 3, 2, 1, 0
	db %10010000 ; 2, 1, 0, 0
	db %01000000 ; 1, 0, 0, 0
	db %00000000 ; 0, 0, 0, 0
	db %01000000 ; 1, 0, 0, 0
	db %10010000 ; 2, 1, 0, 0
	db %11100100 ; 3, 2, 1, 0
	db $01 ; terminator

; causes a delay of 2 frames for the first cycle
; causes a delay of 1 frame for the second and third cycles
FlashScreenLongDelay:
	ld a,[wFlashScreenLongCounter]
	cp a,4 ; never true since [wFlashScreenLongCounter] starts at 3
	ld c,4
	jr z,.delayFrames
	cp a,3
	ld c,2
	jr z,.delayFrames
	cp a,2 ; nothing is done with this
	ld c,1
.delayFrames
	jp DelayFrames

AnimationFlashScreen:
	ld a,[rBGP]
	push af ; save initial palette
	ld a,%00011011 ; 0, 1, 2, 3 (inverted colors)
	ld [rBGP],a
	ld c,2
	call DelayFrames
	xor a ; white out background
	ld [rBGP],a
	ld c,2
	call DelayFrames
	pop af
	ld [rBGP],a ; restore initial palette
	ret

AnimationDarkScreenPalette:
; Changes the screen's palette to a dark palette.
	lb bc, $6f, $6f
	jr SetAnimationBGPalette

AnimationDarkenMonPalette:
; Darkens the mon sprite's palette.
	lb bc, $f9, $f4
	jr SetAnimationBGPalette

AnimationUnusedPalette1:
	lb bc, $fe, $f8
	jr SetAnimationBGPalette

AnimationUnusedPalette2:
	lb bc, $ff, $ff
	jr SetAnimationBGPalette

AnimationResetScreenPalette:
; Restores the screen's palette to the normal palette.
	lb bc, $e4, $e4
	jr SetAnimationBGPalette

AnimationUnusedPalette3:
	lb bc, $00, $00
	jr SetAnimationBGPalette

AnimationLightScreenPalette:
; Changes the screen to use a palette with light colors.
	lb bc, $90, $90
	jr SetAnimationBGPalette

AnimationUnusedPalette4:
	lb bc, $40, $40

SetAnimationBGPalette:
	ld a, [wOnSGB]
	and a
	ld a, b
	jr z, .next
	ld a, c
.next
	ld [rBGP], a
	ret

	ld b, $5

AnimationShakeScreenVertically:
	predef_jump PredefShakeScreenVertically

AnimationShakeScreen:
; Shakes the screen for a while. Used in Earthquake/Fissure/etc. animations.
	ld b, $8

AnimationShakeScreenHorizontallyFast:
	predef_jump PredefShakeScreenHorizontally

AnimationWaterDropletsEverywhere:
; Draws water droplets all over the screen and makes them
; scroll. It's hard to describe, but it's the main animation
; in Surf/Mist/Toxic.
	xor a
	ld [wWhichBattleAnimTileset], a
	call LoadAnimationTileset
	ld d, 32
	ld a, -16
	ld [wBaseCoordX], a
	ld a, $71
	ld [wDropletTile], a
.loop
	ld a, 16
	ld [wBaseCoordY], a
	ld a, 0
	ld [wUnusedD08A], a
	call _AnimationWaterDroplets
	ld a, 24
	ld [wBaseCoordY], a
	ld a, 32
	ld [wUnusedD08A], a
	call _AnimationWaterDroplets
	dec d
	jr nz, .loop
	ret

_AnimationWaterDroplets:
	ld hl, wOAMBuffer
.loop
	ld a, [wBaseCoordY]
	ld [hli], a ; Y
	ld a, [wBaseCoordX]
	add 27
	ld [wBaseCoordX], a
	ld [hli], a ; X
	ld a, [wDropletTile]
	ld [hli], a ; tile
	xor a
	ld [hli], a ; attribute
	ld a, [wBaseCoordX]
	cp 144
	jr c, .loop
	sub 168
	ld [wBaseCoordX], a
	ld a, [wBaseCoordY]
	add 16
	ld [wBaseCoordY], a
	cp 112
	jr c, .loop
	call AnimationCleanOAM
	jp DelayFrame

AnimationSlideMonUp:
; Slides the mon's sprite upwards.
	ld c, 7
	ld a, [H_WHOSETURN]
	and a
	coord hl, 1, 6
	coord de, 1, 5
	ld a, $30
	jr z, .next
	coord hl, 12, 1
	coord de, 12, 0
	ld a, $ff
.next
	ld [wSlideMonUpBottomRowLeftTile], a
	jp _AnimationSlideMonUp

AnimationSlideMonDown:
; Slides the mon's sprite down out of the screen.
	xor a
	call GetTileIDList
.loop
	call GetMonSpriteTileMapPointerFromRowCount
	push bc
	push de
	call CopyPicTiles
	call Delay3
	call AnimationHideMonPic
	pop de
	pop bc
	dec b
	jr nz, .loop
	ret

AnimationSlideMonOff:
; Slides the mon's sprite off the screen horizontally.
	ld e, 8
	ld a, 3
	ld [wSlideMonDelay], a
	jp _AnimationSlideMonOff

AnimationSlideEnemyMonOff:
; Slides the enemy mon off the screen horizontally.
	ld hl, AnimationSlideMonOff
	jp CallWithTurnFlipped

_AnimationSlideMonUp:
	push de
	push hl
	push bc

; In each iteration, slide up all rows but the top one (which is overwritten).
	ld b, 6
.slideLoop
	push bc
	push de
	push hl
	ld bc, 7
	call CopyData
; Note that de and hl are popped in the same order they are pushed, swapping
; their values. When CopyData is called, hl points to a tile 1 row below
; the one de points to. To maintain this relationship, after swapping, we add 2
; rows to hl so that it is 1 row below again.
	pop de
	pop hl
	ld bc, SCREEN_WIDTH * 2
	add hl, bc
	pop bc
	dec b
	jr nz, .slideLoop

; Fill in the bottom row of the mon pic with the next row's tile IDs.
	ld a, [H_WHOSETURN]
	and a
	coord hl, 1, 11
	jr z, .next
	coord hl, 12, 6
.next
	ld a, [wSlideMonUpBottomRowLeftTile]
	inc a
	ld [wSlideMonUpBottomRowLeftTile], a
	ld c, 7
.fillBottomRowLoop
	ld [hli], a
	add 7
	dec c
	jr nz, .fillBottomRowLoop

	ld c, 2
	call DelayFrames
	pop bc
	pop hl
	pop de
	dec c
	jr nz, _AnimationSlideMonUp
	ret

ShakeEnemyHUD_WritePlayerMonPicOAM:
; Writes the OAM entries for a copy of the player mon's pic in OAM.
; The top 5 rows are reproduced in OAM, although only 2 are actually needed.
	ld a, $10
	ld [wBaseCoordX], a
	ld a, $30
	ld [wBaseCoordY], a
	ld hl, wOAMBuffer
	ld d, 0
	ld c, 7
.loop
	ld a, [wBaseCoordY]
	ld e, a
	ld b, 5
.innerLoop
	call BattleAnimWriteOAMEntry
	inc d
	dec b
	jr nz, .innerLoop
	dec c
	ret z
	inc d
	inc d
	ld a, [wBaseCoordX]
	add 8
	ld [wBaseCoordX], a
	jr .loop

BattleAnimWriteOAMEntry:
; Y coordinate = e (increased by 8 each call, before the write to OAM)
; X coordinate = [wBaseCoordX]
; tile = d
; attributes = 0
	ld a, e
	add 8
	ld e, a
	ld [hli], a
	ld a, [wBaseCoordX]
	ld [hli], a
	ld a, d
	ld [hli], a
	xor a
	ld [hli], a
	ret

AdjustOAMBlockXPos:
	ld l, e
	ld h, d

AdjustOAMBlockXPos2:
	ld de, 4
.loop
	ld a, [wCoordAdjustmentAmount]
	ld b, a
	ld a, [hl]
	add b
	cp 168
	jr c, .skipPuttingEntryOffScreen
; put off-screen if X >= 168
	dec hl
	ld a, 160
	ld [hli], a
.skipPuttingEntryOffScreen
	ld [hl], a
	add hl, de
	dec c
	jr nz, .loop
	ret

AdjustOAMBlockYPos:
	ld l, e
	ld h, d

AdjustOAMBlockYPos2:
	ld de, 4
.loop
	ld a, [wCoordAdjustmentAmount]
	ld b, a
	ld a, [hl]
	add b
	cp 112
	jr c, .skipSettingPreviousEntrysAttribute
	dec hl
	ld a, 160 ; bug, sets previous OAM entry's attribute
	ld [hli], a
.skipSettingPreviousEntrysAttribute
	ld [hl], a
	add hl, de
	dec c
	jr nz, .loop
	ret

AnimationBlinkEnemyMon:
; Make the enemy mon's sprite blink on and off for a second or two
	ld hl, AnimationBlinkMon
	jp CallWithTurnFlipped

AnimationBlinkMon:
; Make the mon's sprite blink on and off for a second or two.
	push af
	ld c, 6
.loop
	push bc
	call AnimationHideMonPic
	ld c, 5
	call DelayFrames
	call AnimationShowMonPic
	ld c, 5
	call DelayFrames
	pop bc
	dec c
	jr nz, .loop
	pop af
	ret

AnimationFlashMonPic:
; Flashes the mon's sprite on and off
	ld a, [wBattleMonSpecies]
	ld [wChangeMonPicPlayerTurnSpecies], a
	ld a, [wEnemyMonSpecies]
	ld [wChangeMonPicEnemyTurnSpecies], a
	jp ChangeMonPic

AnimationFlashEnemyMonPic:
; Flashes the enemy mon's sprite on and off
	ld hl, AnimationFlashMonPic
	jp CallWithTurnFlipped

AnimationShowMonPic:
	xor a
	call GetTileIDList
	call GetMonSpriteTileMapPointerFromRowCount
	call CopyPicTiles
	jp Delay3

AnimationShowEnemyMonPic:
; Shows the emenmy mon's front sprite. Used in animations like Seismic Toss
; to make the mon's sprite reappear after disappears offscreen.
	ld hl, AnimationShowMonPic
	jp CallWithTurnFlipped

AnimationShakeBackAndForth:
; Shakes the mon's sprite back and forth rapidly. This is used in Double Team.
; The mon's sprite disappears after this animation.
	ld a, [H_WHOSETURN]
	and a
	coord hl, 0, 5
	coord de, 2, 5
	jr z, .next
	coord hl, 11, 0
	coord de, 13, 0

.next
	xor a
	ld c, $10
.loop
	push af
	push bc
	push de
	push hl
	push hl
	push de
	push af
	push hl
	push hl
	call GetTileIDList
	pop hl
	call CopyPicTiles
	call Delay3
	pop hl
	lb bc, 7, 9
	call ClearScreenArea
	pop af
	call GetTileIDList
	pop hl
	call CopyPicTiles
	call Delay3
	pop hl
	lb bc, 7, 9
	call ClearScreenArea
	pop hl
	pop de
	pop bc
	pop af
	dec c
	jr nz, .loop
	ret

AnimationMoveMonHorizontally:
; Shifts the mon's sprite horizontally to a fixed location. Used by lots of
; animations like Tackle/Body Slam.
	call AnimationHideMonPic
	ld a, [H_WHOSETURN]
	and a
	coord hl, 2, 5
	jr z, .next
	coord hl, 11, 0
.next
	xor a
	push hl
	call GetTileIDList
	pop hl
	call CopyPicTiles
	ld c, 3
	jp DelayFrames

AnimationResetMonPosition:
; Resets the mon's sprites to be located at the normal coordinates.
	ld a, [H_WHOSETURN]
	and a
	ld a, 5 * SCREEN_WIDTH + 2
	jr z, .next
	ld a, 11
.next
	call ClearMonPicFromTileMap
	jp AnimationShowMonPic

AnimationSpiralBallsInward:
; Creates an effect that looks like energy balls spiralling into the
; player mon's sprite.  Used in Focus Energy, for example.
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	ld a, -40
	ld [wSpiralBallsBaseY], a
	ld a, 80
	ld [wSpiralBallsBaseX], a
	jr .next
.playerTurn
	xor a
	ld [wSpiralBallsBaseY], a
	ld [wSpiralBallsBaseX], a
.next
	ld d, $7a ; ball tile
	ld c, 3 ; number of balls
	xor a
	call InitMultipleObjectsOAM
	ld hl, SpiralBallAnimationCoordinates
.loop
	push hl
	ld c, 3
	ld de, wOAMBuffer
.innerLoop
	ld a, [hl]
	cp $ff
	jr z, .done
	ld a, [wSpiralBallsBaseY]
	add [hl]
	ld [de], a ; Y
	inc de
	inc hl
	ld a, [wSpiralBallsBaseX]
	add [hl]
	ld [de], a ; X
	inc hl
	inc de
	inc de
	inc de
	dec c
	jr nz, .innerLoop
	ld c, 5
	call DelayFrames
	pop hl
	inc hl
	inc hl
	jr .loop
.done
	pop hl
	call AnimationCleanOAM
	jp AnimationFlashScreen

SpiralBallAnimationCoordinates:
; y, x pairs
; This is the sequence of screen coordinates that the spiralling
; balls are positioned at.
	db $38, $28
	db $40, $18
	db $50, $10
	db $60, $18
	db $68, $28
	db $60, $38
	db $50, $40
	db $40, $38
	db $40, $28
	db $46, $1E
	db $50, $18
	db $5B, $1E
	db $60, $28
	db $5B, $32
	db $50, $38
	db $46, $32
	db $48, $28
	db $50, $20
	db $58, $28
	db $50, $30
	db $50, $28
	db $FF ; list terminator

AnimationSquishMonPic:
; Squishes the mon's sprite horizontally making it
; disappear. Used by Teleport/Sky Attack animations.
	ld c, 4
.loop
	push bc
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	coord hl, 16, 0
	coord de, 14, 0
	jr .next
.playerTurn
	coord hl, 5, 5
	coord de, 3, 5
.next
	push de
	xor a ; left
	ld [wSquishMonCurrentDirection], a
	call _AnimationSquishMonPic
	pop hl
	ld a, 1 ; right
	ld [wSquishMonCurrentDirection], a
	call _AnimationSquishMonPic
	pop bc
	dec c
	jr nz, .loop
	call AnimationHideMonPic
	ld c, 2
	jp DelayFrame

_AnimationSquishMonPic:
	ld c, 7
.loop
	push bc
	push hl
	ld c, 3
	ld a, [wSquishMonCurrentDirection]
	cp 0
	jr nz, .right
	call AnimCopyRowLeft
	dec hl
	jr .next
.right
	call AnimCopyRowRight
	inc hl
.next
	ld [hl], " "
	pop hl
	ld de, SCREEN_WIDTH
	add hl, de
	pop bc
	dec c
	jr nz, .loop
	jp Delay3

AnimationShootBallsUpward:
; Shoots one pillar of "energy" balls upwards. Used in Teleport/Sky Attack
; animations.
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	lb bc, 0, 16 * 8
	jr .next
.playerTurn
	lb bc, 6 * 8, 5 * 8
.next
	ld a, b
	ld [wBaseCoordY], a
	ld a, c
	ld [wBaseCoordX], a
	lb bc, 5, 1
	call _AnimationShootBallsUpward
	jp AnimationCleanOAM

_AnimationShootBallsUpward:
	push bc
	xor a
	ld [wWhichBattleAnimTileset], a
	call LoadAnimationTileset
	pop bc
	ld d, $7a ; ball tile
	ld hl, wOAMBuffer
	push bc
	ld a, [wBaseCoordY]
	ld e, a
.initOAMLoop
	call BattleAnimWriteOAMEntry
	dec b
	jr nz, .initOAMLoop
	call DelayFrame
	pop bc
	ld a, b
	ld [wNumShootingBalls], a
.loop
	push bc
	ld hl, wOAMBuffer
.innerLoop
	ld a, [wBaseCoordY]
	add 8
	ld e, a
	ld a, [hl]
	cp e ; has the ball reached the top?
	jr z, .reachedTop
	add -4 ; ball hasn't reached the top. move it up 4 pixels
	ld [hl], a
	jr .next
.reachedTop
; remove the ball once it has reached the top
	ld [hl], 0 ; put it off-screen
	ld a, [wNumShootingBalls]
	dec a
	ld [wNumShootingBalls], a
.next
	ld de, 4
	add hl, de ; next OAM entry
	dec b
	jr nz, .innerLoop
	call DelayFrames
	pop bc
	ld a, [wNumShootingBalls]
	and a
	jr nz, .loop
	ret

AnimationShootManyBallsUpward:
; Shoots several pillars of "energy" balls upward.
	ld a, [H_WHOSETURN]
	and a
	ld hl, UpwardBallsAnimXCoordinatesPlayerTurn
	ld a, $50 ; y coordinate for "energy" ball pillar
	jr z, .player
	ld hl, UpwardBallsAnimXCoordinatesEnemyTurn
	ld a, $28 ; y coordinate for "energy" ball pillar
.player
	ld [wSavedY], a
.loop
	ld a, [wSavedY]
	ld [wBaseCoordY], a
	ld a, [hli]
	cp $ff
	jp z, AnimationCleanOAM
	ld [wBaseCoordX], a
	lb bc, 4, 1
	push hl
	call _AnimationShootBallsUpward
	pop hl
	jr .loop

UpwardBallsAnimXCoordinatesPlayerTurn:
; List of x coordinates for each pillar of "energy" balls in the
; AnimationShootManyBallsUpward animation. It's unused in the game.
	db $10, $40, $28, $18, $38, $30
	db $FF ; list terminator

UpwardBallsAnimXCoordinatesEnemyTurn:
; List of x coordinates for each pillar of "energy" balls in the
; AnimationShootManyBallsUpward animation. It's unused in the game.
	db $60, $90, $78, $68, $88, $80
	db $FF ; list terminator

AnimationMinimizeMon:
; Changes the mon's sprite to a mini black sprite. Used by the
; Minimize animation.
	ld hl, wTempPic
	push hl
	xor a
	ld bc, 7 * 7 * $10
	call FillMemory
	pop hl
	ld de, $194
	add hl, de
	ld de, MinimizedMonSprite
	ld c, MinimizedMonSpriteEnd - MinimizedMonSprite
.loop
	ld a, [de]
	ld [hli], a
	ld [hli], a
	inc de
	dec c
	jr nz, .loop
	call CopyTempPicToMonPic
	call Delay3
	jp AnimationShowMonPic

MinimizedMonSprite:
	INCBIN "gfx/minimized_mon_sprite.1bpp"
MinimizedMonSpriteEnd:

AnimationSlideMonDownAndHide:
; Slides the mon's sprite down and disappears. Used in Acid Armor.
	ld a, $1
	ld c, $2
.loop
	push bc
	push af
	call AnimationHideMonPic
	pop af
	push af
	call GetTileIDList
	call GetMonSpriteTileMapPointerFromRowCount
	call CopyPicTiles
	ld c, 8
	call DelayFrames
	pop af
	inc a
	pop bc
	dec c
	jr nz, .loop
	call AnimationHideMonPic
	ld hl, wTempPic
	ld bc, $0310
	xor a
	call FillMemory
	jp CopyTempPicToMonPic

_AnimationSlideMonOff:
; Slides the mon's sprite off the screen horizontally by e tiles and waits
; [wSlideMonDelay] V-blanks each time the pic is slid by one tile.
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	coord hl, 12, 0
	jr .next
.playerTurn
	coord hl, 0, 5
.next
	ld d, 8 ; d's value is unused
.slideLoop ; iterates once for each time the pic slides by one tile
	push hl
	ld b, 7
.rowLoop ; iterates once for each row
	ld c, 8
.tileLoop ; iterates once for each tile in the row
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn2
	call .EnemyNextTile
	jr .next2
.playerTurn2
	call .PlayerNextTile
.next2
	ld [hli], a
	dec c
	jr nz, .tileLoop
	push de
	ld de, SCREEN_WIDTH - 8
	add hl, de
	pop de
	dec b
	jr nz, .rowLoop
	ld a, [wSlideMonDelay]
	ld c, a
	call DelayFrames
	pop hl
	dec d
	dec e
	jr nz, .slideLoop
	ret

; Since mon pic tile numbers go from top to bottom, left to right in order,
; adding the height of the mon pic in tiles to a tile number gives the tile
; number of the tile one column to the right (and thus subtracting the height
; gives the reverse). If the next tile would be past the edge of the pic, the 2
; functions below catch it by checking if the tile number is within the valid
; range and if not, replacing it with a blank tile.

.PlayerNextTile
	ld a, [hl]
	add 7
; This is a bug. The lower right corner tile of the mon back pic is blanked
; while the mon is sliding off the screen. It should compare with the max tile
; plus one instead.
	cp $61
	ret c
	ld a, " "
	ret

.EnemyNextTile
	ld a, [hl]
	sub 7
; This has the same problem as above, but it has no visible effect because
; the lower right tile is in the first column to slide off the screen.
	cp $30
	ret c
	ld a, " "
	ret

AnimationSlideMonHalfOff:
; Slides the mon's sprite halfway off the screen. It's used in Softboiled.
	ld e, 4
	ld a, 4
	ld [wSlideMonDelay], a
	call _AnimationSlideMonOff
	jp Delay3

CopyTempPicToMonPic:
	ld a, [H_WHOSETURN]
	and a
	ld hl, vBackPic ; player turn
	jr z, .next
	ld hl, vFrontPic ; enemy turn
.next
	ld de, wTempPic
	ld bc, 7 * 7
	jp CopyVideoData

AnimationWavyScreen:
; used in Psywave/Psychic etc.
	ld hl, vBGMap0
	call BattleAnimCopyTileMapToVRAM
	call Delay3
	xor a
	ld [H_AUTOBGTRANSFERENABLED], a
	ld a, SCREEN_HEIGHT_PIXELS
	ld [hWY], a
	ld d, $80 ; terminator
	ld e, SCREEN_HEIGHT_PIXELS - 1
	ld c, $ff
	ld hl, WavyScreenLineOffsets
.loop
	push hl
.innerLoop
	call WavyScreen_SetSCX
	ld a, [rLY]
	cp e ; is it the last visible line in the frame?
	jr nz, .innerLoop ; keep going if not
	pop hl
	inc hl
	ld a, [hl]
	cp d ; have we reached the end?
	jr nz, .next
	ld hl, WavyScreenLineOffsets ; go back to the beginning if so
.next
	dec c
	jr nz, .loop
	xor a
	ld [hWY], a
	call SaveScreenTilesToBuffer2
	call ClearScreen
	ld a, 1
	ld [H_AUTOBGTRANSFERENABLED], a
	call Delay3
	call LoadScreenTilesFromBuffer2
	ld hl, vBGMap1
	call BattleAnimCopyTileMapToVRAM
	ret

WavyScreen_SetSCX:
	ld a, [rSTAT]
	and $3 ; is it H-blank?
	jr nz, WavyScreen_SetSCX ; wait until it's H-blank
	ld a, [hl]
	ld [rSCX], a
	inc hl
	ld a, [hl]
	cp d ; have we reached the end?
	ret nz
	ld hl, WavyScreenLineOffsets ; go back to the beginning if so
	ret

WavyScreenLineOffsets:
; Sequence of horizontal line pixel offsets for the wavy screen animation.
; This sequence vaguely resembles a sine wave.
	db 0, 0, 0, 0, 0,  1,  1,  1,  2,  2,  2,  2,  2,  1,  1,  1
	db 0, 0, 0, 0, 0, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, -1
	db $80 ; terminator

AnimationSubstitute:
; Changes the pokemon's sprite to the mini sprite
	ld hl, wTempPic
	xor a
	ld bc, $0310
	call FillMemory
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	ld hl, SlowbroSprite ; facing down sprite
	ld de, wTempPic + $120
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $10
	ld de, wTempPic + $120 + $70
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $20
	ld de, wTempPic + $120 + $10
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $30
	ld de, wTempPic + $120 + $10 + $70
	call CopySlowbroSpriteData
	jr .next
.playerTurn
	ld hl, SlowbroSprite + $40 ; facing up sprite
	ld de, wTempPic + $120 + $70
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $50
	ld de, wTempPic + $120 + $e0
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $60
	ld de, wTempPic + $120 + $80
	call CopySlowbroSpriteData
	ld hl, SlowbroSprite + $70
	ld de, wTempPic + $120 + $f0
	call CopySlowbroSpriteData
.next
	call CopyTempPicToMonPic
	jp AnimationShowMonPic

CopySlowbroSpriteData:
	ld bc, $0010
	ld a, BANK(SlowbroSprite)
	jp FarCopyData2

HideSubstituteShowMonAnim:
	ld a, [H_WHOSETURN]
	and a
	ld hl, wPlayerMonMinimized
	ld a, [wPlayerBattleStatus2]
	jr z, .next1
	ld hl, wEnemyMonMinimized
	ld a, [wEnemyBattleStatus2]
.next1
	push hl
; if the substitute broke, slide it down, else slide it offscreen horizontally
	bit HasSubstituteUp, a
	jr nz, .substituteStillUp
	call AnimationSlideMonDown
	jr .next2
.substituteStillUp
	call AnimationSlideMonOff
.next2
	pop hl
	ld a, [hl]
	and a
	jp nz, AnimationMinimizeMon
	call AnimationFlashMonPic
	jp AnimationShowMonPic

ReshowSubstituteAnim:
	call AnimationSlideMonOff
	call AnimationSubstitute
	jp AnimationShowMonPic

AnimationBoundUpAndDown:
; Bounces the mon's sprite up and down several times. It is used
; by Splash's animation.
	ld c, 5
.loop
	push bc
	call AnimationSlideMonDown
	pop bc
	dec c
	jr nz, .loop
	jp AnimationShowMonPic

AnimationTransformMon:
; Redraws this mon's sprite as the back/front sprite of the opposing mon.
; Used in Transform.
	ld a, [wEnemyMonSpecies]
	ld [wChangeMonPicPlayerTurnSpecies], a
	ld a, [wBattleMonSpecies]
	ld [wChangeMonPicEnemyTurnSpecies], a

ChangeMonPic:
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	ld a, [wChangeMonPicEnemyTurnSpecies]
	ld [wcf91], a
	ld [wd0b5], a
	xor a
	ld [wSpriteFlipped], a
	call GetMonHeader
	coord hl, 12, 0
	call LoadFrontSpriteByMonIndex
	jr .done
.playerTurn
	ld a, [wBattleMonSpecies2]
	push af
	ld a, [wChangeMonPicPlayerTurnSpecies]
	ld [wBattleMonSpecies2], a
	ld [wd0b5], a
	call GetMonHeader
	predef LoadMonBackPic
	xor a
	call GetTileIDList
	call GetMonSpriteTileMapPointerFromRowCount
	call CopyPicTiles
	pop af
	ld [wBattleMonSpecies2], a
.done
	ld b, SET_PAL_BATTLE
	jp RunPaletteCommand

AnimationHideEnemyMonPic:
; Hides the enemy mon's sprite
	xor a
	ld [H_AUTOBGTRANSFERENABLED], a
	ld hl, AnimationHideMonPic
	call CallWithTurnFlipped
	ld a, $1
	ld [H_AUTOBGTRANSFERENABLED], a
	jp Delay3

InitMultipleObjectsOAM:
; Writes c OAM entries with tile d.
; Sets their Y coordinates to sequential multiples of 8, starting from 0.
; Sets their X coordinates to 0.
; Loads animation tileset a.
	push bc
	push de
	ld [wWhichBattleAnimTileset], a
	call LoadAnimationTileset
	pop de
	pop bc
	xor a
	ld e, a
	ld [wBaseCoordX], a
	ld hl, wOAMBuffer
.loop
	call BattleAnimWriteOAMEntry
	dec c
	jr nz, .loop
	ret

AnimationHideMonPic:
; Hides the mon's sprite.
	ld a, [H_WHOSETURN]
	and a
	jr z, .playerTurn
	ld a, 12
	jr ClearMonPicFromTileMap
.playerTurn
	ld a, 5 * SCREEN_WIDTH + 1

ClearMonPicFromTileMap:
	push hl
	push de
	push bc
	ld e, a
	ld d, 0
	coord hl, 0, 0
	add hl, de
	lb bc, 7, 7
	call ClearScreenArea
	pop bc
	pop de
	pop hl
	ret

; puts the tile map destination address of a mon sprite in hl, given the row count in b
; The usual row count is 7, but it may be smaller when sliding a mon sprite in/out,
; in order to show only a portion of the mon sprite.
GetMonSpriteTileMapPointerFromRowCount:
	push de
	ld a, [H_WHOSETURN]
	and a
	jr nz, .enemyTurn
	ld a, 20 * 5 + 1
	jr .next
.enemyTurn
	ld a, 12
.next
	coord hl, 0, 0
	ld e, a
	ld d, 0
	add hl, de
	ld a, 7
	sub b
	and a
	jr z, .done
	ld de, 20
.loop
	add hl, de
	dec a
	jr nz, .loop
.done
	pop de
	ret

; Input:
; a = tile ID list index
; Output:
; de = tile ID list pointer
; b = number of rows
; c = number of columns
GetTileIDList:
	ld hl, TileIDListPointerTable
	ld e, a
	ld d, 0
	add hl, de
	add hl, de
	add hl, de
	ld a, [hli]
	ld e, a
	ld a, [hli]
	ld d, a
	ld a, [hli]
	ld b, a
	and $f
	ld c, a
	ld a, b
	swap a
	and $f
	ld b, a
	ret

AnimCopyRowLeft:
; copy a row of c tiles 1 tile left
	ld a, [hld]
	ld [hli], a
	inc hl
	dec c
	jr nz, AnimCopyRowLeft
	ret

AnimCopyRowRight:
; copy a row of c tiles 1 tile right
	ld a, [hli]
	ld [hld], a
	dec hl
	dec c
	jr nz, AnimCopyRowRight
	ret

; get the sound of the move id in b
GetMoveSoundB:
	ld a, b
	call GetMoveSound
	ld b, a
	ret

GetMoveSound:
	ld hl,MoveSoundTable
	ld e,a
	ld d,0
	add hl,de
	add hl,de
	add hl,de
	ld a,[hli]
	ld b,a
	call IsCryMove
	jr nc,.NotCryMove
	ld a,[H_WHOSETURN]
	and a
	jr nz,.next
	ld a,[wBattleMonSpecies] ; get number of current monster
	jr .Continue
.next
	ld a,[wEnemyMonSpecies]
.Continue
	push hl
	call GetCryData
	ld b,a
	pop hl
	ld a,[wFrequencyModifier]
	add [hl]
	ld [wFrequencyModifier],a
	inc hl
	ld a,[wTempoModifier]
	add [hl]
	ld [wTempoModifier],a
	jr .done
.NotCryMove
	ld a,[hli]
	ld [wFrequencyModifier],a
	ld a,[hli]
	ld [wTempoModifier],a
.done
	ld a,b
	ret

IsCryMove:
; set carry if the move animation involves playing a monster cry
	ld a,[wAnimationID]
	cp a,GROWL
	jr z,.CryMove
	cp a,ROAR
	jr z,.CryMove
	and a ; clear carry
	ret
.CryMove
	scf
	ret

MoveSoundTable:
	; ID, pitch mod, tempo mod
	db SFX_POUND,             $00,$80 ; POUND
	db SFX_BATTLE_0C,         $10,$80 ; KARATE_CHOP
	db SFX_DOUBLESLAP,        $00,$80 ; DOUBLESLAP
	db SFX_BATTLE_0B,         $01,$80 ; COMET_PUNCH
	db SFX_BATTLE_0D,         $00,$40 ; MEGA_PUNCH
	db SFX_SILPH_SCOPE,       $00,$ff ; PAY_DAY
	db SFX_BATTLE_0D,         $10,$60 ; FIRE_PUNCH
	db SFX_BATTLE_0D,         $20,$80 ; ICE_PUNCH
	db SFX_BATTLE_0D,         $00,$a0 ; THUNDERPUNCH
	db SFX_DAMAGE,            $00,$80 ; SCRATCH
	db SFX_BATTLE_0F,         $20,$40 ; VICEGRIP
	db SFX_BATTLE_0F,         $00,$80 ; GUILLOTINE
	db SFX_BATTLE_0E,         $00,$a0 ; RAZOR_WIND
	db SFX_NOT_VERY_EFFECTIVE,$10,$c0 ; SWORDS_DANCE
	db SFX_NOT_VERY_EFFECTIVE,$00,$a0 ; CUT
	db SFX_BATTLE_12,         $00,$c0 ; GUST
	db SFX_BATTLE_12,         $10,$a0 ; WING_ATTACK
	db SFX_BATTLE_13,         $00,$e0 ; WHIRLWIND
	db SFX_NOT_VERY_EFFECTIVE,$20,$c0 ; FLY
	db SFX_BATTLE_14,         $00,$80 ; BIND
	db SFX_BATTLE_22,         $00,$80 ; SLAM
	db SFX_VINE_WHIP,         $01,$80 ; VINE_WHIP
	db SFX_BATTLE_20,         $00,$80 ; STOMP
	db SFX_BATTLE_17,         $f0,$40 ; DOUBLE_KICK
	db SFX_SUPER_EFFECTIVE,   $00,$80 ; MEGA_KICK
	db SFX_BATTLE_17,         $00,$80 ; JUMP_KICK
	db SFX_BATTLE_21,         $10,$80 ; ROLLING_KICK
	db SFX_BATTLE_1B,         $01,$a0 ; SAND_ATTACK
	db SFX_BATTLE_18,         $00,$80 ; HEADBUTT
	db SFX_BATTLE_1E,         $00,$60 ; HORN_ATTACK
	db SFX_BATTLE_1E,         $01,$40 ; FURY_ATTACK
	db SFX_HORN_DRILL,        $00,$a0 ; HORN_DRILL
	db SFX_SUPER_EFFECTIVE,   $10,$a0 ; TACKLE
	db SFX_BATTLE_20,         $00,$c0 ; BODY_SLAM
	db SFX_BATTLE_14,         $10,$60 ; WRAP
	db SFX_SUPER_EFFECTIVE,   $00,$a0 ; TAKE_DOWN
	db SFX_BATTLE_22,         $11,$c0 ; THRASH
	db SFX_SUPER_EFFECTIVE,   $20,$c0 ; DOUBLE_EDGE
	db SFX_BATTLE_21,         $00,$80 ; TAIL_WHIP
	db SFX_BATTLE_1B,         $00,$80 ; POISON_STING
	db SFX_BATTLE_1B,         $20,$c0 ; TWINEEDLE
	db SFX_BATTLE_19,         $00,$80 ; PIN_MISSILE
	db SFX_BATTLE_31,         $ff,$40 ; LEER
	db SFX_BATTLE_1E,         $00,$80 ; BITE
	db SFX_BATTLE_0B,         $00,$c0 ; GROWL
	db SFX_BATTLE_0B,         $00,$40 ; ROAR
	db SFX_BATTLE_35,         $00,$80 ; SING
	db SFX_BATTLE_27,         $40,$60 ; SUPERSONIC
	db SFX_BATTLE_27,         $00,$80 ; SONICBOOM
	db SFX_BATTLE_27,         $ff,$40 ; DISABLE
	db SFX_BATTLE_2A,         $80,$c0 ; ACID
	db SFX_BATTLE_19,         $10,$a0 ; EMBER
	db SFX_BATTLE_19,         $21,$e0 ; FLAMETHROWER
	db SFX_BATTLE_29,         $00,$80 ; MIST
	db SFX_BATTLE_24,         $20,$60 ; WATER_GUN
	db SFX_BATTLE_2A,         $00,$80 ; HYDRO_PUMP
	db SFX_BATTLE_2C,         $00,$80 ; SURF
	db SFX_BATTLE_28,         $40,$80 ; ICE_BEAM
	db SFX_BATTLE_29,         $f0,$e0 ; BLIZZARD
	db SFX_PSYBEAM,           $00,$80 ; PSYBEAM
	db SFX_BATTLE_2A,         $f0,$60 ; BUBBLEBEAM
	db SFX_BATTLE_28,         $00,$80 ; AURORA_BEAM
	db SFX_BATTLE_36,         $00,$80 ; HYPER_BEAM
	db SFX_PECK,$01,          $a0 ; PECK
	db SFX_BATTLE_13,         $f0,$20 ; DRILL_PECK
	db SFX_BATTLE_23,         $01,$c0 ; SUBMISSION
	db SFX_BATTLE_23,         $00,$80 ; LOW_KICK
	db SFX_SUPER_EFFECTIVE,   $00,$e0 ; COUNTER
	db SFX_BATTLE_26,         $01,$60 ; SEISMIC_TOSS
	db SFX_BATTLE_26,         $20,$40 ; STRENGTH
	db SFX_BATTLE_24,         $00,$80 ; ABSORB
	db SFX_BATTLE_24,         $40,$c0 ; MEGA_DRAIN
	db SFX_BATTLE_1B,         $03,$60 ; LEECH_SEED
	db SFX_BATTLE_25,         $11,$e0 ; GROWTH
	db SFX_BATTLE_12,         $20,$e0 ; RAZOR_LEAF
	db SFX_BATTLE_2E,         $00,$80 ; SOLARBEAM
	db SFX_BATTLE_1C,         $00,$80 ; POISONPOWDER
	db SFX_BATTLE_1C,         $11,$a0 ; STUN_SPORE
	db SFX_BATTLE_1C,         $01,$c0 ; SLEEP_POWDER
	db SFX_BATTLE_13,         $14,$c0 ; PETAL_DANCE
	db SFX_BATTLE_1B,         $02,$a0 ; STRING_SHOT
	db SFX_BATTLE_29,         $f0,$80 ; DRAGON_RAGE
	db SFX_BATTLE_29,         $20,$c0 ; FIRE_SPIN
	db SFX_BATTLE_2F,         $00,$20 ; THUNDERSHOCK
	db SFX_BATTLE_2F,         $20,$80 ; THUNDERBOLT
	db SFX_BATTLE_2E,         $12,$60 ; THUNDER_WAVE
	db SFX_BATTLE_26,         $00,$80 ; THUNDER
	db SFX_BATTLE_14,         $01,$e0 ; ROCK_THROW
	db SFX_BATTLE_29,         $0f,$e0 ; EARTHQUAKE
	db SFX_BATTLE_29,         $11,$20 ; FISSURE
	db SFX_DAMAGE,            $10,$40 ; DIG
	db SFX_BATTLE_0F,         $10,$c0 ; TOXIC
	db SFX_BATTLE_14,         $00,$20 ; CONFUSION
	db SFX_PSYCHIC_M,         $00,$80 ; PSYCHIC_M
	db SFX_BATTLE_35,         $11,$18 ; HYPNOSIS
	db SFX_BATTLE_09,         $20,$c0 ; MEDITATE
	db SFX_FAINT_FALL,        $20,$c0 ; AGILITY
	db SFX_BATTLE_25,         $00,$10 ; QUICK_ATTACK
	db SFX_BATTLE_26,         $f0,$20 ; RAGE
	db SFX_BATTLE_33,         $f0,$c0 ; TELEPORT
	db SFX_NOT_VERY_EFFECTIVE,$f0,$e0 ; NIGHT_SHADE
	db SFX_BATTLE_09,         $f0,$40 ; MIMIC
	db SFX_BATTLE_31,         $00,$80 ; SCREECH
	db SFX_BATTLE_33,         $80,$40 ; DOUBLE_TEAM
	db SFX_BATTLE_33,         $00,$80 ; RECOVER
	db SFX_BATTLE_14,         $11,$20 ; HARDEN
	db SFX_BATTLE_14,         $22,$10 ; MINIMIZE
	db SFX_BATTLE_1B,         $f1,$ff ; SMOKESCREEN
	db SFX_BATTLE_13,         $f1,$ff ; CONFUSE_RAY
	db SFX_BATTLE_14,         $33,$30 ; WITHDRAW
	db SFX_BATTLE_32,         $40,$c0 ; DEFENSE_CURL
	db SFX_BATTLE_0E,         $20,$20 ; BARRIER
	db SFX_BATTLE_0E,         $f0,$10 ; LIGHT_SCREEN
	db SFX_BATTLE_0F,         $f8,$10 ; HAZE
	db SFX_NOT_VERY_EFFECTIVE,$f0,$10 ; REFLECT
	db SFX_BATTLE_25,         $00,$80 ; FOCUS_ENERGY
	db SFX_BATTLE_18,         $00,$c0 ; BIDE
	db SFX_BATTLE_32,         $c0,$ff ; METRONOME
	db SFX_BATTLE_09,         $f2,$20 ; MIRROR_MOVE
	db SFX_BATTLE_34,         $00,$80 ; SELFDESTRUCT
	db SFX_BATTLE_34,         $00,$40 ; EGG_BOMB
	db SFX_BATTLE_09,         $00,$40 ; LICK
	db SFX_NOT_VERY_EFFECTIVE,$10,$ff ; SMOG
	db SFX_BATTLE_2A,         $20,$20 ; SLUDGE
	db SFX_BATTLE_32,         $00,$80 ; BONE_CLUB
	db SFX_BATTLE_29,         $1f,$20 ; FIRE_BLAST
	db SFX_BATTLE_25,         $2f,$80 ; WATERFALL
	db SFX_BATTLE_0F,         $1f,$ff ; CLAMP
	db SFX_BATTLE_2B,         $1f,$60 ; SWIFT
	db SFX_BATTLE_26,         $1e,$20 ; SKULL_BASH
	db SFX_BATTLE_26,         $1f,$18 ; SPIKE_CANNON
	db SFX_BATTLE_14,         $0f,$80 ; CONSTRICT
	db SFX_BATTLE_09,         $f8,$10 ; AMNESIA
	db SFX_FAINT_FALL,        $18,$20 ; KINESIS
	db SFX_BATTLE_32,         $08,$40 ; SOFTBOILED
	db SFX_BATTLE_17,         $01,$e0 ; HI_JUMP_KICK
	db SFX_NOT_VERY_EFFECTIVE,$09,$ff ; GLARE
	db SFX_BATTLE_35,         $42,$01 ; DREAM_EATER
	db SFX_BATTLE_1C,         $00,$ff ; POISON_GAS
	db SFX_BATTLE_32,         $08,$e0 ; BARRAGE
	db SFX_BATTLE_24,         $00,$80 ; LEECH_LIFE
	db SFX_BATTLE_09,         $88,$10 ; LOVELY_KISS
	db SFX_BATTLE_25,         $48,$ff ; SKY_ATTACK
	db SFX_FAINT_FALL,        $ff,$ff ; TRANSFORM
	db SFX_BATTLE_24,         $ff,$10 ; BUBBLE
	db SFX_FAINT_FALL,        $ff,$04 ; DIZZY_PUNCH
	db SFX_BATTLE_1C,         $01,$ff ; SPORE
	db SFX_BATTLE_13,         $f8,$ff ; FLASH
	db SFX_BATTLE_0C,         $f0,$f0 ; PSYWAVE
	db SFX_BATTLE_0F,         $08,$10 ; SPLASH
	db SFX_BATTLE_0D,         $f0,$ff ; ACID_ARMOR
	db SFX_SUPER_EFFECTIVE,   $f0,$ff ; CRABHAMMER
	db SFX_BATTLE_34,         $10,$ff ; EXPLOSION
	db SFX_BATTLE_0E,         $f0,$20 ; FURY_SWIPES
	db SFX_BATTLE_2B,         $f0,$60 ; BONEMERANG
	db SFX_BATTLE_21,         $12,$10 ; REST
	db SFX_BATTLE_36,         $f0,$20 ; ROCK_SLIDE
	db SFX_BATTLE_1E,         $12,$ff ; HYPER_FANG
	db SFX_BATTLE_31,         $80,$04 ; SHARPEN
	db SFX_BATTLE_33,         $f0,$10 ; CONVERSION
	db SFX_BATTLE_29,         $f8,$ff ; TRI_ATTACK
	db SFX_BATTLE_26,         $f0,$ff ; SUPER_FANG
	db SFX_NOT_VERY_EFFECTIVE,$01,$ff ; SLASH
	db SFX_BATTLE_2C,         $d8,$04 ; SUBSTITUTE
	db SFX_BATTLE_0B,         $00,$80 ; STRUGGLE
	db SFX_BATTLE_0B,         $00,$80

CopyPicTiles:
	ld a, [H_WHOSETURN]
	and a
	ld a, $31 ; base tile ID of player mon sprite
	jr z, .next
; enemy turn
	xor a ; base tile ID of enemy mon sprite
.next
	ld [hBaseTileID], a
	jr CopyTileIDs_NoBGTransfer

; copy the tiles used when a mon is being sent out of or into a pokeball
CopyDownscaledMonTiles:
	call GetPredefRegisters
	ld a, [wDownscaledMonSize]
	and a
	jr nz, .smallerSize
	ld de, DownscaledMonTiles_5x5
	jr CopyTileIDs_NoBGTransfer
.smallerSize
	ld de, DownscaledMonTiles_3x3
; fall through

CopyTileIDs_NoBGTransfer:
	xor a
	ld [H_AUTOBGTRANSFERENABLED], a
; fall through

; b = number of rows
; c = number of columns
CopyTileIDs:
	push hl
.rowLoop
	push bc
	push hl
	ld a, [hBaseTileID]
	ld b, a
.columnLoop
	ld a, [de]
	add b
	inc de
	ld [hli], a
	dec c
	jr nz, .columnLoop
	pop hl
	ld bc, 20
	add hl, bc
	pop bc
	dec b
	jr nz, .rowLoop
	ld a, $1
	ld [H_AUTOBGTRANSFERENABLED], a
	pop hl
	ret

TileIDListPointerTable:
	dw Unknown_79b24
	db $77
	dw Unknown_79b55
	db $57
	dw Unknown_79b78
	db $37
	dw Unknown_79b8d
	db $77
	dw Unknown_79bbe
	db $77
	dw Unknown_79bef
	db $77
	dw Unknown_79c20
	db $86
	dw Unknown_79c50
	db $3C

DownscaledMonTiles_5x5:
	db $31,$38,$46,$54,$5B
	db $32,$39,$47,$55,$5C
	db $34,$3B,$49,$57,$5E
	db $36,$3D,$4B,$59,$60
	db $37,$3E,$4C,$5A,$61

DownscaledMonTiles_3x3:
	db $31,$46,$5B
	db $34,$49,$5E
	db $37,$4C,$61

Unknown_79b24:
	db $00,$07,$0E,$15,$1C,$23,$2A
	db $01,$08,$0F,$16,$1D,$24,$2B
	db $02,$09,$10,$17,$1E,$25,$2C
	db $03,$0A,$11,$18,$1F,$26,$2D
	db $04,$0B,$12,$19,$20,$27,$2E
	db $05,$0C,$13,$1A,$21,$28,$2F
	db $06,$0D,$14,$1B,$22,$29,$30

Unknown_79b55:
	db $00,$07,$0E,$15,$1C,$23,$2A
	db $01,$08,$0F,$16,$1D,$24,$2B
	db $03,$0A,$11,$18,$1F,$26,$2D
	db $04,$0B,$12,$19,$20,$27,$2E
	db $05,$0C,$13,$1A,$21,$28,$2F

Unknown_79b78:
	db $00,$07,$0E,$15,$1C,$23,$2A
	db $02,$09,$10,$17,$1E,$25,$2C
	db $04,$0B,$12,$19,$20,$27,$2E

Unknown_79b8d:
	db $00,$00,$00,$00,$00,$00,$00
	db $00,$00,$00,$00,$00,$19,$00
	db $02,$06,$0B,$10,$14,$1A,$00
	db $00,$07,$0C,$11,$15,$1B,$00
	db $03,$08,$0D,$12,$16,$1C,$00
	db $04,$09,$0E,$13,$17,$1D,$1F
	db $05,$0A,$0F,$01,$18,$1E,$20

Unknown_79bbe:
	db $00,$00,$00,$30,$00,$37,$00
	db $00,$00,$2B,$31,$34,$38,$3D
	db $21,$26,$2C,$01,$35,$39,$3E
	db $22,$27,$2D,$32,$36,$01,$00
	db $23,$28,$2E,$33,$01,$3A,$00
	db $24,$29,$2F,$01,$01,$3B,$00
	db $25,$2A,$01,$01,$01,$3C,$00

Unknown_79bef:
	db $00,$00,$00,$00,$00,$00,$00
	db $00,$00,$47,$4D,$00,$00,$00
	db $00,$00,$48,$4E,$52,$56,$5B
	db $3F,$43,$49,$4F,$53,$57,$5C
	db $40,$44,$4A,$50,$54,$58,$00
	db $41,$45,$4B,$51,$4C,$59,$5D
	db $42,$46,$4C,$4C,$55,$5A,$5E

Unknown_79c20:
	db $31,$32,$32,$32,$32,$33
	db $34,$35,$36,$36,$37,$38
	db $34,$39,$3A,$3A,$3B,$38
	db $3C,$3D,$3E,$3E,$3F,$40
	db $41,$42,$43,$43,$44,$45
	db $46,$47,$43,$48,$49,$4A
	db $41,$43,$4B,$4C,$4D,$4E
	db $4F,$50,$50,$50,$51,$52

Unknown_79c50:
	db $43,$55,$56,$53,$53,$53,$53,$53,$53,$53,$53,$53
	db $43,$57,$58,$54,$54,$54,$54,$54,$54,$54,$54,$54
	db $43,$59,$5A,$43,$43,$43,$43,$43,$43,$43,$43,$43

AnimationLeavesFalling:
; Makes leaves float down from the top of the screen. This is used
; in Razor Leaf's animation.
	ld a, [rOBP0]
	push af
	ld a, [wAnimPalette]
	ld [rOBP0], a
	ld d, $37 ; leaf tile
	ld a, 3 ; number of leaves
	ld [wNumFallingObjects], a
	call AnimationFallingObjects
	pop af
	ld [rOBP0], a
	ret

AnimationPetalsFalling:
; Makes lots of petals fall down from the top of the screen. It's used in
; the animation for Petal Dance.
	ld d, $71 ; petal tile
	ld a, 20 ; number of petals
	ld [wNumFallingObjects], a
	call AnimationFallingObjects
	jp ClearSprites

AnimationFallingObjects:
	ld c, a
	ld a, 1
	call InitMultipleObjectsOAM
	call FallingObjects_InitXCoords
	call FallingObjects_InitMovementData
	ld hl, wOAMBuffer
	ld [hl], 0
.loop
	ld hl, wFallingObjectsMovementData
	ld de, 0
	ld a, [wNumFallingObjects]
	ld c, a
.innerLoop
	push bc
	push hl
	push de
	ld a, [hl]
	ld [wFallingObjectMovementByte], a
	call FallingObjects_UpdateMovementByte
	call FallingObjects_UpdateOAMEntry
	pop de
	ld hl, 4
	add hl, de
	ld e, l
	ld d, h
	pop hl
	ld a, [wFallingObjectMovementByte]
	ld [hli], a
	pop bc
	dec c
	jr nz, .innerLoop
	call Delay3
	ld hl, wOAMBuffer
	ld a, [hl] ; Y
	cp 104 ; has the top falling object reached 104 yet?
	jr nz, .loop ; keep moving the falling objects down until it does
	ret

FallingObjects_UpdateOAMEntry:
; Increases Y by 2 pixels and adjusts X and X flip based on the falling object's
; movement byte.
	ld hl, wOAMBuffer
	add hl, de
	ld a, [hl]
	inc a
	inc a
	cp 112
	jr c, .next
	ld a, 160 ; if Y >= 112, put it off-screen
.next
	ld [hli], a ; Y
	ld a, [wFallingObjectMovementByte]
	ld b, a
	ld de, FallingObjects_DeltaXs
	and $7f
	add e
	jr nc, .noCarry
	inc d
.noCarry
	ld e, a
	ld a, b
	and $80
	jr nz, .movingLeft
; moving right
	ld a, [de]
	add [hl]
	ld [hli], a ; X
	inc hl
	xor a ; no horizontal flip
	jr .next2
.movingLeft
	ld a, [de]
	ld b, a
	ld a, [hl]
	sub b
	ld [hli], a ; X
	inc hl
	ld a, (1 << OAM_X_FLIP)
.next2
	ld [hl], a ; attribute
	ret

FallingObjects_DeltaXs:
	db 0, 1, 3, 5, 7, 9, 11, 13, 15

FallingObjects_UpdateMovementByte:
	ld a, [wFallingObjectMovementByte]
	inc a
	ld b, a
	and $7f
	cp 9 ; have we reached the end of the delta-Xs?
	ld a, b
	jr nz, .next
; We've reached the end of the delta-Xs, so wrap to the start and change
; direction from right to left or vice versa.
	and $80
	xor $80
.next
	ld [wFallingObjectMovementByte], a
	ret

FallingObjects_InitXCoords:
	ld hl, wOAMBuffer + $01
	ld de, FallingObjects_InitialXCoords
	ld a, [wNumFallingObjects]
	ld c, a
.loop
	ld a, [de]
	ld [hli], a
	inc hl
	inc hl
	inc hl
	inc de
	dec c
	jr nz, .loop
	ret

FallingObjects_InitialXCoords:
	db $38,$40,$50,$60,$70,$88,$90,$56,$67,$4A,$77,$84,$98,$32,$22,$5C,$6C,$7D,$8E,$99

FallingObjects_InitMovementData:
	ld hl, wFallingObjectsMovementData
	ld de, FallingObjects_InitialMovementData
	ld a, [wNumFallingObjects]
	ld c, a
.loop
	ld a, [de]
	ld [hli], a
	inc de
	dec c
	jr nz, .loop
	ret

FallingObjects_InitialMovementData:
	db $00,$84,$06,$81,$02,$88,$01,$83,$05,$89,$09,$80,$07,$87,$03,$82,$04,$85,$08,$86

AnimationShakeEnemyHUD:
; Shakes the enemy HUD.

; Make a copy of the back pic's tile patterns in sprite tile pattern VRAM.
	ld de, vBackPic
	ld hl, vSprites
	ld bc, 7 * 7
	call CopyVideoData

	xor a
	ld [hSCX], a

; Copy wTileMap to BG map 0. The regular BG (not the window) is set to use
; map 0 and can be scrolled with SCX, which allows a shaking effect.
	ld hl, vBGMap0
	call BattleAnimCopyTileMapToVRAM

; Now that the regular BG is showing the same thing the window was, move the
; window off the screen so that we can modify its contents below.
	ld a, SCREEN_HEIGHT_PIXELS
	ld [hWY], a

; Copy wTileMap to VRAM such that the row below the enemy HUD (in wTileMap) is
; lined up with row 0 of the window.
	ld hl, vBGMap1 - $20 * 7
	call BattleAnimCopyTileMapToVRAM

; Move the window so that the row below the enemy HUD (in BG map 0) lines up
; with the top row of the window on the screen. This makes it so that the window
; covers everything below the enemy HD with a copy that looks just like what
; was there before.
	ld a, 7 * 8
	ld [hWY], a

; Write OAM entries so that the copy of the back pic from the top of this
; function shows up on screen. We need this because the back pic's Y coordinates
; range overlaps with that of the enemy HUD and we don't want to shake the top
; of the back pic when we shake the enemy HUD. The OAM copy won't be affected
; by SCX.
	call ShakeEnemyHUD_WritePlayerMonPicOAM

	ld hl, vBGMap0
	call BattleAnimCopyTileMapToVRAM

; Remove the back pic from the BG map.
	call AnimationHideMonPic
	call Delay3

; Use SCX to shake the regular BG. The window and the back pic OAM copy are
; not affected.
	lb de, 2, 8
	call ShakeEnemyHUD_ShakeBG

; Restore the original graphics.
	call AnimationShowMonPic
	call ClearSprites
	ld a, SCREEN_HEIGHT_PIXELS
	ld [hWY], a
	ld hl, vBGMap1
	call BattleAnimCopyTileMapToVRAM
	xor a
	ld [hWY], a
	call SaveScreenTilesToBuffer1
	ld hl, vBGMap0
	call BattleAnimCopyTileMapToVRAM
	call ClearScreen
	call Delay3
	call LoadScreenTilesFromBuffer1
	ld hl, vBGMap1
	jp BattleAnimCopyTileMapToVRAM

; b = tile ID list index
; c = base tile ID
CopyTileIDsFromList:
	call GetPredefRegisters
	ld a, c
	ld [hBaseTileID], a
	ld a, b
	push hl
	call GetTileIDList
	pop hl
	jp CopyTileIDs

ShakeEnemyHUD_ShakeBG:
	ld a, [hSCX]
	ld [wTempSCX], a
.loop
	ld a, [wTempSCX]
	add d
	ld [hSCX], a
	ld c, 2
	call DelayFrames
	ld a, [wTempSCX]
	sub d
	ld [hSCX], a
	ld c, 2
	call DelayFrames
	dec e
	jr nz, .loop
	ld a, [wTempSCX]
	ld [hSCX], a
	ret

BattleAnimCopyTileMapToVRAM:
	ld a, h
	ld [H_AUTOBGTRANSFERDEST + 1], a
	ld a, l
	ld [H_AUTOBGTRANSFERDEST], a
	jp Delay3

TossBallAnimation:
	ld a,[wIsInBattle]
	cp a,2
	jr z,.BlockBall ; if in trainer battle, play different animation
	ld a,[wPokeBallAnimData]
	ld b,a

	; upper nybble: how many animations (from PokeBallAnimations) to play
	; this will be 4 for successful capture, 6 for breakout
	and a,$F0
	swap a
	ld c,a

	; lower nybble: number of shakes
	; store these for later
	ld a,b
	and a,$F
	ld [wNumShakes],a

	ld hl,.PokeBallAnimations
	; choose which toss animation to use
	ld a,[wcf91]
	cp a,POKE_BALL
	ld b,TOSS_ANIM
	jr z,.done
	cp a,GREAT_BALL
	ld b,GREATTOSS_ANIM
	jr z,.done
	ld b,ULTRATOSS_ANIM
.done
	ld a,b
.PlayNextAnimation
	ld [wAnimationID],a
	push bc
	push hl
	call PlayAnimation
	pop hl
	ld a,[hli]
	pop bc
	dec c
	jr nz,.PlayNextAnimation
	ret

.PokeBallAnimations:
; sequence of animations that make up the Poké Ball toss
	db POOF_ANIM,HIDEPIC_ANIM,SHAKE_ANIM,POOF_ANIM,SHOWPIC_ANIM

.BlockBall
	ld a,TOSS_ANIM
	ld [wAnimationID],a
	call PlayAnimation
	ld a,SFX_FAINT_THUD
	call PlaySound
	ld a,BLOCKBALL_ANIM
	ld [wAnimationID],a
	jp PlayAnimation

PlayApplyingAttackSound:
; play a different sound depending if move is not very effective, neutral, or super-effective
; don't play any sound at all if move is ineffective
	call WaitForSoundToFinish
	ld a, [wDamageMultipliers]
	and $7f
	ret z
	cp 10
	ld a, $20
	ld b, $30
	ld c, SFX_DAMAGE
	jr z, .playSound
	ld a, $e0
	ld b, $ff
	ld c, SFX_SUPER_EFFECTIVE
	jr nc, .playSound
	ld a, $50
	ld b, $1
	ld c, SFX_NOT_VERY_EFFECTIVE
.playSound
	ld [wFrequencyModifier], a
	ld a, b
	ld [wTempoModifier], a
	ld a, c
	jp PlaySound