ref: 3e572b6f48b81e5340980c13b1b37b2907942713
dir: /engine/battle/animations.asm/
; 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 $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 $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 OAM_HFLIP ld b, OAM_VFLIP jr z, .storeFlags1 cp 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 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 $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 2 jr z, .advanceFrameBlockDestAddr; skip delay and don't clean OAM buffer ld a, [wSubAnimFrameDelay] ld c, a call DelayFrames ld a, [wFBMode] cp 3 jr z, .advanceFrameBlockDestAddr ; skip cleaning OAM buffer cp 4 jr z, .done ; skip cleaning OAM buffer and don't advance the frame block destination address ld a, [wAnimationID] cp 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 ldh [hROMBankTemp], 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 $FF jr z, .AnimationOver cp $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 $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 %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 ldh a, [rOBP0] push af ld a, [wAnimPalette] ldh [rOBP0], a call LoadAnimationTileset call LoadSubanimation call PlaySubanimation pop af ldh [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 31 ld [wSubAnimCounter], a ; number of frame blocks ld a, b and %11100000 cp 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 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 ldh a, [hWhoseTurn] 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: ldh a, [hWhoseTurn] 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 tile $31 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/battle/attack_anim_1.2bpp" AnimationTileset2: INCBIN "gfx/battle/attack_anim_2.2bpp" SlotMachineTiles2: IF DEF(_RED) INCBIN "gfx/slots/red_slots_2.2bpp" ENDC IF DEF(_BLUE) INCBIN "gfx/slots/blue_slots_2.2bpp" ENDC SlotMachineTiles2End: 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 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 ldh a, [hWhoseTurn] and a ret z ; opponent's turn ld a, [wAnimationID] cp AMNESIA ld b, CONF_ANIM jr z, .replaceAnim cp 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 ldh a, [rWX] inc a ldh [rWX], a ld c, 2 call DelayFrames dec b jr nz, .loop1 pop bc .loop2 ldh a, [rWX] dec a ldh [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 ldh [rOBP0], a ld a, $6c ldh [rOBP1], a ret .notSGB ld a, $e4 ld [wAnimPalette], a ldh [rOBP0], a ld a, $6c ldh [rOBP1], a ret PlaySubanimation: ld a, [wAnimSoundID] cp $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 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 INCLUDE "data/moves/animation_special_effects.asm" DoBallTossSpecialEffects: ld a, [wcf91] cp 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 ldh a, [rOBP0] xor %00111100 ; complement colors 1 and 2 ldh [rOBP0], a .skipFlashingEffect ld a, [wSubAnimCounter] cp 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 02 ; is it a trainer battle? jr z, .isTrainerBattle ld a, [wd11e] cp $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 3 jr z, .moveGhostMarowakLeft cp 2 jr z, .moveGhostMarowakLeft cp 1 ret nz .moveGhostMarowakLeft hlcoord 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 ldh [rNR10], a ; Channel 1 sweep register ret .isTrainerBattle ; if it's a trainer battle, shorten the animation by one frame ld a, [wSubAnimCounter] cp 3 ret nz dec a ld [wSubAnimCounter], a ret DoBallShakeSpecialEffects: ld a, [wSubAnimCounter] cp 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 5 ret nz ld a, SFX_BALL_POOF jp PlaySound DoRockSlideSpecialEffects: ld a, [wSubAnimCounter] cp 12 ret nc cp 8 jr nc, .shakeScreen cp 1 jp z, AnimationFlashScreen ; if it's the end of the subanimation, flash the screen ret ; if the subanimation 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 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 3 call z, AnimationFlashScreen ret ; used for Explosion and Selfdestruct DoExplodeSpecialEffects: ld a, [wSubAnimCounter] cp 1 ; is it the end of the subanimation? jr nz, FlashScreenEveryFourFrameBlocks ; if it's the end of the subanimation, make the attacking pokemon disappear hlcoord 1, 5 jp AnimationHideMonPic ; make pokemon disappear ; flashes the screen when subanimation counter is 1 modulo 4 DoBlizzardSpecialEffects: ld a, [wSubAnimCounter] cp 13 jp z, AnimationFlashScreen cp 9 jp z, AnimationFlashScreen cp 5 jp z, AnimationFlashScreen cp 1 jp z, AnimationFlashScreen ret ; flashes the screen at 3 points in the subanimation ; unused FlashScreenUnused: ld a, [wSubAnimCounter] cp 14 jp z, AnimationFlashScreen cp 9 jp z, AnimationFlashScreen cp 2 jp z, AnimationFlashScreen ret ; function to make the pokemon disappear at the beginning of the animation TradeHidePokemon: ld a, [wSubAnimCounter] cp 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 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 $ff jr z, .done add [hl] ; add to Y value of OAM entry ld [hl], a add hl, bc ld a, l cp 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 $ff jp z, ClearScreen add [hl] ld [hl], a add hl, bc ld a, l cp 4 * 4 ; there are 4 entries, each 4 bytes jr nz, .innerLoop inc de push de ld a, [de] cp 12 jr z, .playSound cp $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 ldh a, [hSCX] ; background scroll X sub 8 ; scroll to the left ldh [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 INCLUDE "data/moves/animation_special_effect_pointers.asm" 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: ldh a, [hWhoseTurn] push af xor 1 ldh [hWhoseTurn], a ld de, .returnAddress push de jp hl .returnAddress pop af ldh [hWhoseTurn], 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 $01 ; is it the end of the palettes? jr z, .endOfPalettes ldh [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 4 ; never true since [wFlashScreenLongCounter] starts at 3 ld c, 4 jr z, .delayFrames cp 3 ld c, 2 jr z, .delayFrames cp 2 ; nothing is done with this ld c, 1 .delayFrames jp DelayFrames AnimationFlashScreen: ldh a, [rBGP] push af ; save initial palette ld a, %00011011 ; 0, 1, 2, 3 (inverted colors) ldh [rBGP], a ld c, 2 call DelayFrames xor a ; white out background ldh [rBGP], a ld c, 2 call DelayFrames pop af ldh [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 ldh [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 ldh a, [hWhoseTurn] and a hlcoord 1, 6 decoord 1, 5 ld a, $30 jr z, .next hlcoord 12, 1 decoord 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. ldh a, [hWhoseTurn] and a hlcoord 1, 11 jr z, .next hlcoord 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 enemy 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. ldh a, [hWhoseTurn] and a hlcoord 0, 5 decoord 2, 5 jr z, .next hlcoord 11, 0 decoord 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 ldh a, [hWhoseTurn] and a hlcoord 2, 5 jr z, .next hlcoord 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. ldh a, [hWhoseTurn] 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. ldh a, [hWhoseTurn] 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 ldh a, [hWhoseTurn] and a jr z, .playerTurn hlcoord 16, 0 decoord 14, 0 jr .next .playerTurn hlcoord 5, 5 decoord 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. ldh a, [hWhoseTurn] 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. ldh a, [hWhoseTurn] 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/battle/minimize.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, $310 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. ldh a, [hWhoseTurn] and a jr z, .playerTurn hlcoord 12, 0 jr .next .playerTurn hlcoord 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 ldh a, [hWhoseTurn] 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: ldh a, [hWhoseTurn] 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 ldh [hAutoBGTransferEnabled], a ld a, SCREEN_HEIGHT_PX ldh [hWY], a ld d, $80 ; terminator ld e, SCREEN_HEIGHT_PX - 1 ld c, $ff ld hl, WavyScreenLineOffsets .loop push hl .innerLoop call WavyScreen_SetSCX ldh 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 ldh [hWY], a call SaveScreenTilesToBuffer2 call ClearScreen ld a, 1 ldh [hAutoBGTransferEnabled], a call Delay3 call LoadScreenTilesFromBuffer2 ld hl, vBGMap1 call BattleAnimCopyTileMapToVRAM ret WavyScreen_SetSCX: ldh a, [rSTAT] and $3 ; is it H-blank? jr nz, WavyScreen_SetSCX ; wait until it's H-blank ld a, [hl] ldh [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, $310 call FillMemory ldh a, [hWhoseTurn] 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, $10 ld a, BANK(SlowbroSprite) jp FarCopyData2 HideSubstituteShowMonAnim: ldh a, [hWhoseTurn] 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 HAS_SUBSTITUTE_UP, 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: ldh a, [hWhoseTurn] and a jr z, .playerTurn ld a, [wChangeMonPicEnemyTurnSpecies] ld [wcf91], a ld [wd0b5], a xor a ld [wSpriteFlipped], a call GetMonHeader hlcoord 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 ldh [hAutoBGTransferEnabled], a ld hl, AnimationHideMonPic call CallWithTurnFlipped ld a, $1 ldh [hAutoBGTransferEnabled], 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. ldh a, [hWhoseTurn] 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 hlcoord 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 ldh a, [hWhoseTurn] and a jr nz, .enemyTurn ld a, 20 * 5 + 1 jr .next .enemyTurn ld a, 12 .next hlcoord 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 ldh a, [hWhoseTurn] 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 GROWL jr z, .CryMove cp ROAR jr z, .CryMove and a ; clear carry ret .CryMove scf ret INCLUDE "data/moves/sfx.asm" CopyPicTiles: ldh a, [hWhoseTurn] 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 ldh [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 ldh [hAutoBGTransferEnabled], a ; fall through ; b = number of rows ; c = number of columns CopyTileIDs: push hl .rowLoop push bc push hl ldh 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 ldh [hAutoBGTransferEnabled], a pop hl ret TileIDListPointerTable: dw Unknown_79b24 dn 7, 7 dw Unknown_79b55 dn 5, 7 dw Unknown_79b78 dn 3, 7 dw GengarIntroTiles1 dn 7, 7 dw GengarIntroTiles2 dn 7, 7 dw GengarIntroTiles3 dn 7, 7 dw Unknown_79c20 dn 8, 6 dw Unknown_79c50 dn 3, 12 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 GengarIntroTiles1: 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 GengarIntroTiles2: 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 GengarIntroTiles3: 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. ldh a, [rOBP0] push af ld a, [wAnimPalette] ldh [rOBP0], a ld d, $37 ; leaf tile ld a, 3 ; number of leaves ld [wNumFallingObjects], a call AnimationFallingObjects pop af ldh [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 ldh [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_PX ldh [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 ldh [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_PX ldh [hWY], a ld hl, vBGMap1 call BattleAnimCopyTileMapToVRAM xor a ldh [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 ldh [hBaseTileID], a ld a, b push hl call GetTileIDList pop hl jp CopyTileIDs ShakeEnemyHUD_ShakeBG: ldh a, [hSCX] ld [wTempSCX], a .loop ld a, [wTempSCX] add d ldh [hSCX], a ld c, 2 call DelayFrames ld a, [wTempSCX] sub d ldh [hSCX], a ld c, 2 call DelayFrames dec e jr nz, .loop ld a, [wTempSCX] ldh [hSCX], a ret BattleAnimCopyTileMapToVRAM: ld a, h ldh [hAutoBGTransferDest + 1], a ld a, l ldh [hAutoBGTransferDest], a jp Delay3 TossBallAnimation: ld a, [wIsInBattle] cp 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 $F0 swap a ld c, a ; lower nybble: number of shakes ; store these for later ld a, b and $F ld [wNumShakes], a ld hl, .PokeBallAnimations ; choose which toss animation to use ld a, [wcf91] cp POKE_BALL ld b, TOSS_ANIM jr z, .done cp 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