pokeemerald/src/battle_anim_ice.c
AZero13 df8cd2d0f3 Ice documentation
Some better descriptions of variables and arguments and what they do.
2026-03-29 17:12:08 -04:00

1602 lines
49 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "global.h"
#include "battle_anim.h"
#include "bg.h"
#include "field_weather.h"
#include "gpu_regs.h"
#include "graphics.h"
#include "main.h"
#include "palette.h"
#include "random.h"
#include "sprite.h"
#include "task.h"
#include "trig.h"
#include "constants/battle_anim.h"
#include "constants/rgb.h"
// Ice-type (and related) move battle animations.
enum {
HAILSTRUCTTYPE_NEGATIVE_POS_MOD = 0,
HAILSTRUCTTYPE_POSITIVE_POS_MOD = 1,
HAILSTRUCTTYPE_FIXED_POSITION = 2,
};
// Hail: per-slot spawn rule. type + bPosition pick a battler or fixed screen coords;
// x, y are fallbacks when that battler has no visible sprite.
struct HailStruct {
s32 x:10;
s32 y:10;
s32 bPosition:8;
s32 type:4;
};
static void AnimUnusedIceCrystalThrow(struct Sprite *);
static void AnimUnusedIceCrystalThrow_Step(struct Sprite *);
static void AnimIcePunchSwirlingParticle(struct Sprite *);
static void AnimIceBeamParticle(struct Sprite *);
static void AnimIceEffectParticle(struct Sprite *);
static void AnimFlickerIceEffectParticle(struct Sprite *);
static void AnimSwirlingSnowball(struct Sprite *);
static void AnimSwirlingSnowball_Step1(struct Sprite *);
static void AnimSwirlingSnowball_Step2(struct Sprite *);
static void AnimSwirlingSnowball_End(struct Sprite *);
static void AnimMoveParticleBeyondTarget(struct Sprite *);
static void AnimWiggleParticleTowardsTarget(struct Sprite *);
static void AnimWaveFromCenterOfTarget(struct Sprite *);
static void InitSwirlingFogAnim(struct Sprite *);
static void AnimSwirlingFogAnim(struct Sprite *);
static void AnimThrowMistBall(struct Sprite *);
static void InitPoisonGasCloudAnim(struct Sprite *);
static void MovePoisonGasCloud(struct Sprite *);
static void AnimHailBegin(struct Sprite *);
static void AnimHailContinue(struct Sprite *);
static void InitIceBallAnim(struct Sprite *);
static void AnimThrowIceBall(struct Sprite *);
static void InitIceBallParticle(struct Sprite *);
static void AnimIceBallParticle(struct Sprite *);
static void AnimTask_HazeScrollingFog_Step(u8);
static void AnimTask_MistBallFog_Step(u8);
static void AnimTask_Hail2(u8);
static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, u8 c);
static const union AnimCmd sAnim_Unused[] =
{
ANIMCMD_FRAME(0, 5, .hFlip = TRUE),
ANIMCMD_FRAME(1, 5, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_Unused[] =
{
sAnim_Unused,
};
// Unused
static const struct SpriteTemplate sUnusedIceCrystalThrowSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjNormal_8x8,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimUnusedIceCrystalThrow,
};
static const union AnimCmd sAnim_IceCrystalLargeChunk[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_IceCrystalLarge[] =
{
ANIMCMD_FRAME(4, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_IceCrystalSmall[] =
{
ANIMCMD_FRAME(6, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_Snowball[] =
{
ANIMCMD_FRAME(7, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_BlizzardIceCrystal[] =
{
ANIMCMD_FRAME(8, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_SmallBubblePair[] =
{
ANIMCMD_FRAME(12, 6),
ANIMCMD_FRAME(13, 6),
ANIMCMD_JUMP(0),
};
// Unused, contains just the top left corner of the large ice crystal
static const union AnimCmd *const sAnims_IceCrystalLargeChunk[] =
{
sAnim_IceCrystalLargeChunk,
};
static const union AnimCmd *const sAnims_IceCrystalLarge[] =
{
sAnim_IceCrystalLarge,
};
static const union AnimCmd *const sAnims_IceCrystalSmall[] =
{
sAnim_IceCrystalSmall,
};
static const union AnimCmd *const sAnims_Snowball[] =
{
sAnim_Snowball,
};
static const union AnimCmd *const sAnims_BlizzardIceCrystal[] =
{
sAnim_BlizzardIceCrystal,
};
const union AnimCmd *const gAnims_SmallBubblePair[] =
{
sAnim_SmallBubblePair,
};
static const union AffineAnimCmd sAffineAnim_IceCrystalSpiralInwardLarge[] =
{
AFFINEANIMCMD_FRAME(0x0, 0x0, 40, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd *const sAffineAnims_IceCrystalSpiralInwardLarge[] =
{
sAffineAnim_IceCrystalSpiralInwardLarge,
};
const struct SpriteTemplate gIceCrystalSpiralInwardLarge =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineDouble_ObjBlend_8x16,
.anims = sAnims_IceCrystalLarge,
.images = NULL,
.affineAnims = sAffineAnims_IceCrystalSpiralInwardLarge,
.callback = AnimIcePunchSwirlingParticle,
};
const struct SpriteTemplate gIceCrystalSpiralInwardSmall =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjBlend_8x8,
.anims = sAnims_IceCrystalSmall,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimIcePunchSwirlingParticle,
};
static const union AffineAnimCmd sAffineAnim_IceBeamInnerCrystal[] =
{
AFFINEANIMCMD_FRAME(0x0, 0x0, 10, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd *const sAffineAnims_IceBeamInnerCrystal[] =
{
sAffineAnim_IceBeamInnerCrystal,
};
const struct SpriteTemplate gIceBeamInnerCrystalSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineNormal_ObjBlend_8x16,
.anims = sAnims_IceCrystalLarge,
.images = NULL,
.affineAnims = sAffineAnims_IceBeamInnerCrystal,
.callback = AnimIceBeamParticle,
};
const struct SpriteTemplate gIceBeamOuterCrystalSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjBlend_8x8,
.anims = sAnims_IceCrystalSmall,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimIceBeamParticle,
};
static const union AffineAnimCmd sAffineAnim_IceCrystalHit[] =
{
AFFINEANIMCMD_FRAME(0xCE, 0xCE, 0, 0),
AFFINEANIMCMD_FRAME(0x5, 0x5, 0, 10),
AFFINEANIMCMD_FRAME(0x0, 0x0, 0, 6),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd *const sAffineAnims_IceCrystalHit[] =
{
sAffineAnim_IceCrystalHit,
};
const struct SpriteTemplate gIceCrystalHitLargeSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineNormal_ObjBlend_8x16,
.anims = sAnims_IceCrystalLarge,
.images = NULL,
.affineAnims = sAffineAnims_IceCrystalHit,
.callback = AnimIceEffectParticle,
};
const struct SpriteTemplate gIceCrystalHitSmallSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineNormal_ObjBlend_8x8,
.anims = sAnims_IceCrystalSmall,
.images = NULL,
.affineAnims = sAffineAnims_IceCrystalHit,
.callback = AnimIceEffectParticle,
};
const struct SpriteTemplate gSwirlingSnowballSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjNormal_8x8,
.anims = sAnims_Snowball,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimSwirlingSnowball,
};
const struct SpriteTemplate gBlizzardIceCrystalSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjNormal_16x16,
.anims = sAnims_BlizzardIceCrystal,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimMoveParticleBeyondTarget,
};
const struct SpriteTemplate gPowderSnowSnowballSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjNormal_8x8,
.anims = sAnims_Snowball,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimMoveParticleBeyondTarget,
};
static const union AnimCmd sAnim_IceGroundSpike[] =
{
ANIMCMD_FRAME(0, 5),
ANIMCMD_FRAME(2, 5),
ANIMCMD_FRAME(4, 5),
ANIMCMD_FRAME(6, 5),
ANIMCMD_FRAME(4, 5),
ANIMCMD_FRAME(2, 5),
ANIMCMD_FRAME(0, 5),
ANIMCMD_END,
};
static const union AnimCmd *const sAnims_IceGroundSpike[] =
{
sAnim_IceGroundSpike,
};
const struct SpriteTemplate gIceGroundSpikeSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_SPIKES,
.paletteTag = ANIM_TAG_ICE_SPIKES,
.oam = &gOamData_AffineOff_ObjBlend_8x16,
.anims = sAnims_IceGroundSpike,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimWaveFromCenterOfTarget,
};
static const union AnimCmd sAnim_Cloud[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(8, 8),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_Cloud[] =
{
sAnim_Cloud,
};
const struct SpriteTemplate gMistCloudSpriteTemplate =
{
.tileTag = ANIM_TAG_MIST_CLOUD,
.paletteTag = ANIM_TAG_MIST_CLOUD,
.oam = &gOamData_AffineOff_ObjBlend_32x16,
.anims = sAnims_Cloud,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = InitSwirlingFogAnim,
};
const struct SpriteTemplate gSmogCloudSpriteTemplate =
{
.tileTag = ANIM_TAG_PURPLE_GAS_CLOUD,
.paletteTag = ANIM_TAG_PURPLE_GAS_CLOUD,
.oam = &gOamData_AffineOff_ObjBlend_32x16,
.anims = sAnims_Cloud,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = InitSwirlingFogAnim,
};
static const u8 sHazeBlendAmounts[] =
{
0, 1, 2, 2, 2, 2, 3, 4, 4, 4, 5, 6, 6, 6, 6, 7, 8, 8, 8, 9,
};
const struct SpriteTemplate gMistBallSpriteTemplate =
{
.tileTag = ANIM_TAG_SMALL_BUBBLES,
.paletteTag = ANIM_TAG_SMALL_BUBBLES,
.oam = &gOamData_AffineOff_ObjNormal_16x16,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = AnimThrowMistBall,
};
static const u8 sMistBlendAmounts[] =
{
0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5,
};
const struct SpriteTemplate gPoisonGasCloudSpriteTemplate =
{
.tileTag = ANIM_TAG_PURPLE_GAS_CLOUD,
.paletteTag = ANIM_TAG_PURPLE_GAS_CLOUD,
.oam = &gOamData_AffineOff_ObjBlend_32x16,
.anims = sAnims_Cloud,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = InitPoisonGasCloudAnim,
};
static const struct HailStruct sHailCoordData[] =
{
{.x = 100, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_FIXED_POSITION},
{.x = 85, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 242, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 66, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 182, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 60, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_FIXED_POSITION},
{.x = 214, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 113, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 210, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 38, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
};
static const union AffineAnimCmd sAffineAnim_HailParticle_0[] =
{
AFFINEANIMCMD_FRAME(0x100, 0x100, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_HailParticle_1[] =
{
AFFINEANIMCMD_FRAME(0xF0, 0xF0, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_HailParticle_2[] =
{
AFFINEANIMCMD_FRAME(0xE0, 0xE0, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_WeatherBallIceDown[] =
{
AFFINEANIMCMD_FRAME(0x150, 0x150, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd *const sAffineAnims_HailParticle[] =
{
sAffineAnim_HailParticle_0,
sAffineAnim_HailParticle_1,
sAffineAnim_HailParticle_2,
};
static const union AffineAnimCmd *const sAffineAnims_WeatherBallIceDown[] =
{
sAffineAnim_WeatherBallIceDown,
};
const struct SpriteTemplate gHailParticleSpriteTemplate =
{
.tileTag = ANIM_TAG_HAIL,
.paletteTag = ANIM_TAG_HAIL,
.oam = &gOamData_AffineNormal_ObjNormal_16x16,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = sAffineAnims_HailParticle,
.callback = AnimHailBegin,
};
const struct SpriteTemplate gWeatherBallIceDownSpriteTemplate =
{
.tileTag = ANIM_TAG_HAIL,
.paletteTag = ANIM_TAG_HAIL,
.oam = &gOamData_AffineNormal_ObjNormal_16x16,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = sAffineAnims_WeatherBallIceDown,
.callback = AnimWeatherBallDown,
};
static const union AnimCmd sAnim_IceBallChunk_0[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_IceBallChunk_1[] =
{
ANIMCMD_FRAME(16, 4),
ANIMCMD_FRAME(32, 4),
ANIMCMD_FRAME(48, 4),
ANIMCMD_FRAME(64, 4),
ANIMCMD_END,
};
static const union AnimCmd *const sAnims_IceBallChunk[] =
{
sAnim_IceBallChunk_0,
sAnim_IceBallChunk_1,
};
static const union AffineAnimCmd sAffineAnim_IceBallChunk_0[] =
{
AFFINEANIMCMD_FRAME(0xE0, 0xE0, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_IceBallChunk_1[] =
{
AFFINEANIMCMD_FRAME(0x118, 0x118, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_IceBallChunk_2[] =
{
AFFINEANIMCMD_FRAME(0x150, 0x150, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_IceBallChunk_3[] =
{
AFFINEANIMCMD_FRAME(0x180, 0x180, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_IceBallChunk_4[] =
{
AFFINEANIMCMD_FRAME(0x1C0, 0x1C0, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd *const sAffineAnims_IceBallChunk[] =
{
sAffineAnim_IceBallChunk_0,
sAffineAnim_IceBallChunk_1,
sAffineAnim_IceBallChunk_2,
sAffineAnim_IceBallChunk_3,
sAffineAnim_IceBallChunk_4,
};
const struct SpriteTemplate gIceBallChunkSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CHUNK,
.paletteTag = ANIM_TAG_ICE_CHUNK,
.oam = &gOamData_AffineDouble_ObjNormal_32x32,
.anims = sAnims_IceBallChunk,
.images = NULL,
.affineAnims = sAffineAnims_IceBallChunk,
.callback = InitIceBallAnim,
};
const struct SpriteTemplate gIceBallImpactShardSpriteTemplate =
{
.tileTag = ANIM_TAG_ICE_CRYSTALS,
.paletteTag = ANIM_TAG_ICE_CRYSTALS,
.oam = &gOamData_AffineOff_ObjNormal_8x8,
.anims = sAnims_IceCrystalSmall,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = InitIceBallParticle,
};
// Unused
static void AnimUnusedIceCrystalThrow(struct Sprite *sprite)
{
s16 targetX, targetY, attackerX, attackerY;
sprite->oam.tileNum += 7;
targetX = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
targetY = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET);
attackerX = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2);
attackerY = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET);
sprite->data[0] = gBattleAnimArgs[4];
sprite->data[1] = gBattleAnimArgs[0] + attackerX;
sprite->data[2] = gBattleAnimArgs[2] + targetX;
sprite->data[3] = gBattleAnimArgs[1] + attackerY;
sprite->data[4] = gBattleAnimArgs[3] + targetY;
ConvertPosDataToTranslateLinearData(sprite);
for (;(targetX >= -32 && targetX <= DISPLAY_WIDTH + 32) && (targetY >= -32 && targetY <= DISPLAY_HEIGHT + 32);
targetX += sprite->data[1], targetY += sprite->data[2])
;
sprite->data[1] = -sprite->data[1];
sprite->data[2] = -sprite->data[2];
for (;(attackerX >= -32 && attackerX <= DISPLAY_WIDTH + 32) && (attackerY >= -32 && attackerY <= DISPLAY_HEIGHT + 32);
attackerX += sprite->data[1], attackerY += sprite->data[2])
;
sprite->x = attackerX;
sprite->y = attackerY;
sprite->data[0] = gBattleAnimArgs[4];
sprite->data[1] = attackerX;
sprite->data[2] = targetX;
sprite->data[3] = attackerY;
sprite->data[4] = targetY;
ConvertPosDataToTranslateLinearData(sprite);
sprite->data[3] = gBattleAnimArgs[5];
sprite->data[4] = gBattleAnimArgs[6];
sprite->callback = AnimUnusedIceCrystalThrow_Step;
}
static void AnimUnusedIceCrystalThrow_Step(struct Sprite *sprite)
{
if (sprite->data[0] != 0)
{
sprite->data[5] += sprite->data[1];
sprite->data[6] += sprite->data[2];
sprite->x2 = sprite->data[5];
sprite->y2 = sprite->data[6];
sprite->x2 += Sin(sprite->data[7], sprite->data[3]);
sprite->y2 += Sin(sprite->data[7], sprite->data[3]);
sprite->data[7] = (sprite->data[7] + sprite->data[4]) & 0xFF;
sprite->data[0]--;
}
else
{
DestroyAnimSprite(sprite);
}
}
// Animates the swirling ice crystals in Ice Punch.
// arg 0: initial position angle around circle (0-256)
static void AnimIcePunchSwirlingParticle(struct Sprite *sprite)
{
sprite->data[0] = gBattleAnimArgs[0];
sprite->data[1] = 60;
sprite->data[2] = 9;
sprite->data[3] = 30;
sprite->data[4] = -512;
StoreSpriteCallbackInData6(sprite, DestroyAnimSprite);
sprite->callback = TranslateSpriteInGrowingCircle;
sprite->callback(sprite);
}
// Animates the ice particles in Ice Beam.
// arg 0: initial x pixel offset
// arg 1: initial y pixel offset
// arg 2: target x offset
// arg 3: target y offset
// arg 4: duration
static void AnimIceBeamParticle(struct Sprite *sprite)
{
InitSpritePosToAnimAttacker(sprite, TRUE);
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
sprite->data[2] -= gBattleAnimArgs[2];
else
sprite->data[2] += gBattleAnimArgs[2];
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[3];
sprite->data[0] = gBattleAnimArgs[4];
StoreSpriteCallbackInData6(sprite, DestroyAnimSprite);
sprite->callback = StartAnimLinearTranslation;
}
// Animates the ice crystals at the end of Ice Punch, Ice Beam, Tri Attack,
// Weather Ball (Hail), Blizzard, and Powder Snow.
// arg 0: target x offset (dual-target branch only; may be negated)
// arg 1: target y offset (dual-target branch only)
// arg 2: 0 = single target; non-zero = dual targets (average positions + args 01).
static void AnimIceEffectParticle(struct Sprite *sprite)
{
if (gBattleAnimArgs[2] == 0)
{
InitSpritePosToAnimTarget(sprite, TRUE);
}
else
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &sprite->x, &sprite->y);
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
gBattleAnimArgs[0] = -gBattleAnimArgs[0];
sprite->x += gBattleAnimArgs[0];
sprite->y += gBattleAnimArgs[1];
}
StoreSpriteCallbackInData6(sprite, AnimFlickerIceEffectParticle);
sprite->callback = RunStoredCallbackWhenAffineAnimEnds;
}
static void AnimFlickerIceEffectParticle(struct Sprite *sprite)
{
sprite->invisible ^= 1;
sprite->data[0] += 1;
if (sprite->data[0] == 20)
DestroySpriteAndMatrix(sprite);
}
// Animates the small snowballs that swirl around the target in Blizzard and Icy Wind.
// arg 0: initial x pixel offset
// arg 1: initial y pixel offset
// arg 2: target x offset
// arg 3: target y offset
// arg 4: particle speed (sub-pixel / fixed-point rate for InitAnimFastLinearTranslationWithSpeed)
// arg 5: 0 = single target, non-zero = dual targets (average positions)
static void AnimSwirlingSnowball(struct Sprite *sprite)
{
int i;
s16 tempDataHolder[8];
InitSpritePosToAnimAttacker(sprite, TRUE);
sprite->data[0] = gBattleAnimArgs[4];
sprite->data[1] = sprite->x;
sprite->data[3] = sprite->y;
if (!gBattleAnimArgs[5])
{
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[3];
}
else
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &sprite->data[2], &sprite->data[4]);
}
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
sprite->data[2] -= gBattleAnimArgs[2];
else
sprite->data[2] += gBattleAnimArgs[2];
for (i = 0; i < 8; i++)
tempDataHolder[i] = sprite->data[i];
InitAnimFastLinearTranslationWithSpeed(sprite);
sprite->data[1] ^= 1;
sprite->data[2] ^= 1;
while (1)
{
sprite->data[0] = 1;
AnimFastTranslateLinear(sprite);
if (sprite->x + sprite->x2 > DISPLAY_WIDTH + 16
|| sprite->x + sprite->x2 < -16
|| sprite->y + sprite->y2 > DISPLAY_HEIGHT
|| sprite->y + sprite->y2 < -16)
break;
}
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->x2 = sprite->y2 = 0;
for (i = 0; i < 8; i++)
sprite->data[i] = tempDataHolder[i];
sprite->callback = InitAnimFastLinearTranslationWithSpeedAndPos;
StoreSpriteCallbackInData6(sprite, AnimSwirlingSnowball_Step1);
}
static void AnimSwirlingSnowball_Step1(struct Sprite *sprite)
{
s16 battlerSide;
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->x2 = sprite->y2 = 0;
sprite->data[0] = 128;
battlerSide = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20;
sprite->data[3] = Sin(sprite->data[0], battlerSide);
sprite->data[4] = Cos(sprite->data[0], 0xF);
sprite->data[5] = 0;
sprite->callback = AnimSwirlingSnowball_Step2;
sprite->callback(sprite);
}
static void AnimSwirlingSnowball_Step2(struct Sprite *sprite)
{
s16 battlerSide;
battlerSide = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20;
if (sprite->data[5] <= 31)
{
sprite->x2 = Sin(sprite->data[0], battlerSide) - sprite->data[3];
sprite->y2 = Cos(sprite->data[0], 15) - sprite->data[4];
sprite->data[0] = (sprite->data[0] + 16) & 0xFF;
sprite->data[5]++;
}
else
{
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->x2 = sprite->y2 = 0;
sprite->data[3] = sprite->data[4] = 0;
sprite->callback = AnimSwirlingSnowball_End;
}
}
static void AnimSwirlingSnowball_End(struct Sprite *sprite)
{
sprite->data[0] = 1;
AnimFastTranslateLinear(sprite);
if (sprite->x + sprite->x2 > 256
|| sprite->x + sprite->x2 < -16
|| sprite->y + sprite->y2 > 256
|| sprite->y + sprite->y2 < -16)
DestroyAnimSprite(sprite);
}
// Phase 1: straight move from attacker toward the target, stepping in a loop until the
// sprite leaves the screen (simulates starting from off-screen). Phase 2: same linear move
// toward the target, but each frame adds vertical wobble (Sin) so the path wiggles.
// gBlizzardIceCrystalSpriteTemplate (large ice shards) and gPowderSnowSnowballSpriteTemplate
// (small snowballs) — not the gSwirlingSnowball path.
// arg 0: initial x pixel offset
// arg 1: initial y pixel offset
// arg 2: target x offset
// arg 3: target y offset
// arg 4: speed (sub-pixel / fixed-point rate for InitAnimFastLinearTranslationWithSpeed)
// arg 5: amplitude passed to Sin() for vertical wobble on y2 during the wiggle phase
// arg 6: angle added to phase each frame (data[7] += arg6)
// arg 7: 0 = single target, non-zero = dual targets (average positions)
static void AnimMoveParticleBeyondTarget(struct Sprite *sprite)
{
int i;
s16 tempDataHolder[8];
InitSpritePosToAnimAttacker(sprite, TRUE);
sprite->data[0] = gBattleAnimArgs[4];
sprite->data[1] = sprite->x;
sprite->data[3] = sprite->y;
if (!gBattleAnimArgs[7])
{
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET);
}
else
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &sprite->data[2], &sprite->data[4]);
}
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
sprite->data[2] -= gBattleAnimArgs[2];
else
sprite->data[2] += gBattleAnimArgs[2];
sprite->data[4] += gBattleAnimArgs[3];
InitAnimFastLinearTranslationWithSpeed(sprite);
for (i = 0; i < 8; i++)
tempDataHolder[i] = sprite->data[i];
sprite->data[1] ^= 1;
sprite->data[2] ^= 1;
while (1)
{
sprite->data[0] = 1;
AnimFastTranslateLinear(sprite);
if (sprite->x + sprite->x2 > DISPLAY_WIDTH + 16
|| sprite->x + sprite->x2 < -16
|| sprite->y + sprite->y2 > DISPLAY_HEIGHT
|| sprite->y + sprite->y2 < -16)
break;
}
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->x2 = sprite->y2 = 0;
for (i = 0; i < 8; i++)
sprite->data[i] = tempDataHolder[i];
sprite->data[5] = gBattleAnimArgs[5];
sprite->data[6] = gBattleAnimArgs[6];
sprite->callback = AnimWiggleParticleTowardsTarget;
}
// Moves particles in a sine wave towards the target.
static void AnimWiggleParticleTowardsTarget(struct Sprite *sprite)
{
AnimFastTranslateLinear(sprite);
if (sprite->data[0] == 0)
sprite->data[0] = 1;
sprite->y2 += Sin(sprite->data[7], sprite->data[5]);
sprite->data[7] = (sprite->data[7] + sprite->data[6]) & 0xFF;
if (sprite->data[0] == 1)
{
if (sprite->x + sprite->x2 > DISPLAY_WIDTH + 16
|| sprite->x + sprite->x2 < -16
|| sprite->y + sprite->y2 > DISPLAY_HEIGHT
|| sprite->y + sprite->y2 < -16)
DestroyAnimSprite(sprite);
}
}
// Rising ground ice spikes near the end of the Icy Wind move animation.
// arg 0: x offset (dual-target branch only; flipped for opponent attacker)
// arg 1: y offset (dual-target branch only)
// arg 2: 0 = single target (InitSpritePosToAnimTarget), non-zero = dual (average + offsets)
static void AnimWaveFromCenterOfTarget(struct Sprite *sprite)
{
if (sprite->data[0] == 0)
{
if (gBattleAnimArgs[2] == 0)
{
InitSpritePosToAnimTarget(sprite, FALSE);
}
else
{
SetAverageBattlerPositions(gBattleAnimTarget, FALSE, &sprite->x, &sprite->y);
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
gBattleAnimArgs[0] = -gBattleAnimArgs[0];
sprite->x += gBattleAnimArgs[0];
sprite->y += gBattleAnimArgs[1];
}
sprite->data[0]++;
}
else
{
if (sprite->animEnded)
DestroyAnimSprite(sprite);
}
}
// Mist / Smog: fog puffs. First moves in a straight line (InitAnimLinearTranslation) while
// the callback adds a sine/cos wobble on top; then continues with the wobble callback.
// arg 0: initial x pixel offset (from attacker or from target; which side is set by arg 4)
// arg 1: initial y pixel offset
// arg 2: Y delta added to start Y to get the straight-move destination (end Y of that segment)
// arg 3: number of frames for that straight move (counts down each frame)
// arg 4: 0 = position from attacker, non-zero = position from target (defender)
// arg 5: 0 = single target, non-zero = dual targets (average positions; doubles wobble width)
static void InitSwirlingFogAnim(struct Sprite *sprite)
{
if (gBattleAnimArgs[4] == 0)
{
if (gBattleAnimArgs[5] == 0)
{
InitSpritePosToAnimAttacker(sprite, FALSE);
}
else
{
SetAverageBattlerPositions(gBattleAnimAttacker, FALSE, &sprite->x, &sprite->y);
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
sprite->x -= gBattleAnimArgs[0];
else
sprite->x += gBattleAnimArgs[0];
sprite->y += gBattleAnimArgs[1];
}
sprite->data[7] = gBattleAnimAttacker;
}
else
{
if (gBattleAnimArgs[5] == 0)
{
InitSpritePosToAnimTarget(sprite, FALSE);
}
else
{
SetAverageBattlerPositions(gBattleAnimTarget, FALSE, &sprite->x, &sprite->y);
if (GetBattlerSide(gBattleAnimTarget) != B_SIDE_PLAYER)
sprite->x -= gBattleAnimArgs[0];
else
sprite->x += gBattleAnimArgs[0];
sprite->y += gBattleAnimArgs[1];
}
sprite->data[7] = gBattleAnimTarget;
}
if (gBattleAnimArgs[5] == 0 || !IsDoubleBattle())
sprite->data[6] = 0x20;
else
sprite->data[6] = 0x40;
if (GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER)
sprite->y += 8;
sprite->data[0] = gBattleAnimArgs[3];
sprite->data[1] = sprite->x;
sprite->data[2] = sprite->x;
sprite->data[3] = sprite->y;
sprite->data[4] = sprite->y + gBattleAnimArgs[2];
InitAnimLinearTranslation(sprite);
sprite->data[5] = 64;
sprite->callback = AnimSwirlingFogAnim;
sprite->callback(sprite);
}
// Animates swirling fog initialized by InitSwirlingFogAnim.
static void AnimSwirlingFogAnim(struct Sprite *sprite)
{
if (!AnimTranslateLinear(sprite))
{
sprite->x2 += Sin(sprite->data[5], sprite->data[6]);
sprite->y2 += Cos(sprite->data[5], -6);
if (sprite->data[5] > 63 && sprite->data[5] < 192)
sprite->oam.priority = GetBattlerSpriteBGPriority(sprite->data[7]);
else
sprite->oam.priority = GetBattlerSpriteBGPriority(sprite->data[7]) + 1;
sprite->data[5] = (sprite->data[5] + 3) & 0xFF;
}
else
{
DestroyAnimSprite(sprite);
}
}
// Adds moving foggy overlay. Used by Haze.
void AnimTask_HazeScrollingFog(u8 taskId)
{
struct BattleAnimBgData animBg;
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(0, 16));
SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 1);
SetAnimBgAttribute(1, BG_ANIM_SCREEN_SIZE, 0);
if (!IsContest())
SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 1);
gBattle_BG1_X = 0;
gBattle_BG1_Y = 0;
SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X);
SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y);
GetBattleAnimBg1Data(&animBg);
LoadBgTiles(animBg.bgId, gWeatherFogHorizontalTiles, 0x800, animBg.tilesOffset);
AnimLoadCompressedBgTilemapHandleContest(&animBg, gBattleAnimFogTilemap, FALSE);
LoadPalette(&gFogPalette, BG_PLTT_ID(animBg.paletteId), PLTT_SIZE_4BPP);
gTasks[taskId].func = AnimTask_HazeScrollingFog_Step;
}
static void AnimTask_HazeScrollingFog_Step(u8 taskId)
{
struct BattleAnimBgData animBg;
gBattle_BG1_X += -1;
gBattle_BG1_Y += 0;
switch (gTasks[taskId].data[12])
{
case 0:
if (++gTasks[taskId].data[10] == 4)
{
gTasks[taskId].data[10] = 0;
gTasks[taskId].data[9]++;
gTasks[taskId].data[11] = sHazeBlendAmounts[gTasks[taskId].data[9]];
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 16 - gTasks[taskId].data[11]));
if (gTasks[taskId].data[11] == 9)
{
gTasks[taskId].data[12]++;
gTasks[taskId].data[11] = 0;
}
}
break;
case 1:
if (++gTasks[taskId].data[11] == 0x51)
{
gTasks[taskId].data[11] = 9;
gTasks[taskId].data[12]++;
}
break;
case 2:
if (++gTasks[taskId].data[10] == 4)
{
gTasks[taskId].data[10] = 0;
gTasks[taskId].data[11]--;
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 16 - gTasks[taskId].data[11]));
if (gTasks[taskId].data[11] == 0)
{
gTasks[taskId].data[12]++;
gTasks[taskId].data[11] = 0;
}
}
break;
case 3:
GetBattleAnimBg1Data(&animBg);
ClearBattleAnimBg(1);
ClearBattleAnimBg(2);
gTasks[taskId].data[12]++;
// fall through
case 4:
if (!IsContest())
SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 0);
gBattle_BG1_X = 0;
gBattle_BG1_Y = 0;
SetGpuReg(REG_OFFSET_BLDCNT, 0);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(0, 0));
SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 1);
DestroyAnimVisualTask(taskId);
break;
}
}
// Mist Ball projectile: one frame sets start on the attacker, then TranslateAnimSpriteToTargetMonLocation
// runs the move to the target. Args 05 are consumed there. Arg 5 packs two flags in one u16: low byte
// picks which target Y coordinate is used; high byte picks whether attacker coords use mon pic offsets.
static void AnimThrowMistBall(struct Sprite *sprite)
{
// First two lines are redundant with the code in TranslateAnimSpriteToTargetMonLocation.
sprite->x = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2);
sprite->y = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET);
sprite->callback = TranslateAnimSpriteToTargetMonLocation;
}
// Displays misty background in Mist Ball.
void AnimTask_MistBallFog(u8 taskId)
{
struct BattleAnimBgData animBg;
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(0, 16));
SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 1);
SetAnimBgAttribute(1, BG_ANIM_SCREEN_SIZE, 0);
if (!IsContest())
SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 1);
gBattle_BG1_X = 0;
gBattle_BG1_Y = 0;
SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X);
SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y);
GetBattleAnimBg1Data(&animBg);
LoadBgTiles(animBg.bgId, gWeatherFogHorizontalTiles, 0x800, animBg.tilesOffset);
AnimLoadCompressedBgTilemapHandleContest(&animBg, gBattleAnimFogTilemap, FALSE);
LoadPalette(&gFogPalette, BG_PLTT_ID(animBg.paletteId), PLTT_SIZE_4BPP);
gTasks[taskId].data[15] = -1;
gTasks[taskId].func = AnimTask_MistBallFog_Step;
}
static void AnimTask_MistBallFog_Step(u8 taskId)
{
struct BattleAnimBgData animBg;
gBattle_BG1_X += gTasks[taskId].data[15];
gBattle_BG1_Y += 0;
switch (gTasks[taskId].data[12])
{
case 0:
gTasks[taskId].data[9] += 1;
gTasks[taskId].data[11] = sMistBlendAmounts[gTasks[taskId].data[9]];
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 17 - gTasks[taskId].data[11]));
if (gTasks[taskId].data[11] == 5)
{
gTasks[taskId].data[12]++;
gTasks[taskId].data[11] = 0;
}
break;
case 1:
if (++gTasks[taskId].data[11] == 0x51)
{
gTasks[taskId].data[11] = 5;
gTasks[taskId].data[12]++;
}
break;
case 2:
if (++gTasks[taskId].data[10] == 4)
{
gTasks[taskId].data[10] = 0;
gTasks[taskId].data[11] -= 1;
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 16 - gTasks[taskId].data[11]));
if (gTasks[taskId].data[11] == 0)
{
gTasks[taskId].data[12]++;
gTasks[taskId].data[11] = 0;
}
}
break;
case 3:
GetBattleAnimBg1Data(&animBg);
ClearBattleAnimBg(1);
ClearBattleAnimBg(2);
gTasks[taskId].data[12]++;
// fall through
case 4:
if (!IsContest())
SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 0);
gBattle_BG1_X = 0;
gBattle_BG1_Y = 0;
SetGpuReg(REG_OFFSET_BLDCNT, 0);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(0, 0));
SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 1);
DestroyAnimVisualTask(taskId);
break;
}
}
// Poison Gas purple cloud: straight approach with wobble, then scripted phases in MovePoisonGasCloud.
// arg 0: timer for first phase (decrements each frame until phase change)
// arg 1: x offset from attacker start
// arg 2: y offset from attacker start
// arg 3: x offset added to target coords for the first InitAnimLinearTranslation endpoint
// arg 4: y offset added to target coords for that endpoint
// arg 5, arg 6: unused (values in the Poison Gas script are ignored here)
// arg 7: 0 = target endpoint uses BATTLER_COORD_X / BATTLER_COORD_Y; non-zero uses X_2 / Y pic offset
static void InitPoisonGasCloudAnim(struct Sprite *sprite)
{
sprite->data[0] = gBattleAnimArgs[0];
if (GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2) < GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2))
sprite->data[7] = 0x8000;
if (GET_BATTLER_SIDE2(gBattleAnimTarget) == B_SIDE_PLAYER)
{
gBattleAnimArgs[1] = -gBattleAnimArgs[1];
gBattleAnimArgs[3] = -gBattleAnimArgs[3];
if ((sprite->data[7] & 0x8000) && GET_BATTLER_SIDE2(gBattleAnimAttacker) == B_SIDE_PLAYER)
sprite->subpriority = gSprites[GetAnimBattlerSpriteId(ANIM_TARGET)].subpriority + 1;
sprite->data[6] = 1;
}
sprite->x = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2);
sprite->y = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET);
if (gBattleAnimArgs[7])
{
sprite->data[1] = sprite->x + gBattleAnimArgs[1];
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[3];
sprite->data[3] = sprite->y + gBattleAnimArgs[2];
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[4];
sprite->data[7] |= GetBattlerSpriteBGPriority(gBattleAnimTarget) << 8;
}
else
{
sprite->data[1] = sprite->x + gBattleAnimArgs[1];
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X) + gBattleAnimArgs[3];
sprite->data[3] = sprite->y + gBattleAnimArgs[2];
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y) + gBattleAnimArgs[4];
sprite->data[7] |= GetBattlerSpriteBGPriority(gBattleAnimTarget) << 8;
}
if (IsContest())
{
sprite->data[6] = 1;
sprite->subpriority = 0x80;
}
InitAnimLinearTranslation(sprite);
sprite->callback = MovePoisonGasCloud;
}
static void MovePoisonGasCloud(struct Sprite *sprite)
{
switch (sprite->data[7] & 0xFF)
{
case 0:
AnimTranslateLinear(sprite);
sprite->x2 += (16 * gSineTable[sprite->data[5]]) >> 8;
if (sprite->data[6])
sprite->data[5] = (sprite->data[5] - 8) & 0xFF;
else
sprite->data[5] = (sprite->data[5] + 8) & 0xFF;
if (sprite->data[0] <= 0)
{
sprite->data[0] = 80;
sprite->x = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X);
sprite->data[1] = sprite->x;
sprite->data[2] = sprite->x;
sprite->y += sprite->y2;
sprite->data[3] = sprite->y;
sprite->data[4] = sprite->y + 29;
sprite->data[7]++;
if (IsContest())
sprite->data[5] = 80;
else if (GET_BATTLER_SIDE2(gBattleAnimTarget) != B_SIDE_PLAYER)
sprite->data[5] = 204;
else
sprite->data[5] = 80;
sprite->y2 = 0;
sprite->x2 = (gSineTable[sprite->data[5]] * 32) >> 8;
sprite->data[5] = (sprite->data[5] + 2) & 0xFF;
InitAnimLinearTranslation(sprite);
}
break;
case 1:
AnimTranslateLinear(sprite);
sprite->x2 += (32 * gSineTable[sprite->data[5]]) >> 8;
sprite->y2 += (gSineTable[sprite->data[5] + 0x40] * -3) >> 8;
if (!IsContest())
{
if (sprite->data[5] > 63 && sprite->data[5] < 192)
sprite->oam.priority = sprite->data[7] >> 8;
else
sprite->oam.priority = (sprite->data[7] >> 8) + 1;
sprite->data[5] = (sprite->data[5] + 4) & 0xFF;
}
else
{
if (sprite->data[5] > 63 && sprite->data[5] < 192)
sprite->subpriority = 128;
else
sprite->subpriority = 140;
sprite->data[5] = (sprite->data[5] - 4) & 0xFF;
}
if (sprite->data[0] <= 0)
{
sprite->data[0] = 0x300;
sprite->data[1] = sprite->x += sprite->x2;
sprite->data[3] = sprite->y += sprite->y2;
sprite->data[4] = sprite->y + 4;
if (IsContest())
sprite->data[2] = -16;
else if (GET_BATTLER_SIDE2(gBattleAnimTarget) != B_SIDE_PLAYER)
sprite->data[2] = DISPLAY_WIDTH + 16;
else
sprite->data[2] = -16;
sprite->data[7]++;
sprite->x2 = sprite->y2 = 0;
InitAnimLinearTranslationWithSpeed(sprite);
}
break;
case 2:
if (AnimTranslateLinear(sprite))
{
if (sprite->oam.affineMode & 1)
{
FreeOamMatrix(sprite->oam.matrixNum);
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
}
DestroySprite(sprite);
gAnimVisualTaskCount--;
}
break;
}
}
#define tState data[0]
#define tSpriteCount data[1]
#define tHailAffineAnimNum data[2]
#define tHailStructId data[3]
#define tInitialDelayTimer data[4]
#define tHailSpawnTimer data[5]
void AnimTask_Hail(u8 taskId)
{
struct Task *task = &gTasks[taskId];
task->func = AnimTask_Hail2;
}
static void AnimTask_Hail2(u8 taskId)
{
struct Task *task = &gTasks[taskId];
switch (task->tState)
{
case 0:
if (++task->tInitialDelayTimer > 2)
{
task->tInitialDelayTimer = 0;
task->tHailSpawnTimer = 0;
task->tHailAffineAnimNum = 0;
task->tState++;
}
break;
case 1:
if (task->tHailSpawnTimer == 0)
{
if (GenerateHailParticle(task->tHailStructId, task->tHailAffineAnimNum, taskId, 1))
task->tSpriteCount++;
if (++task->tHailAffineAnimNum == (int)ARRAY_COUNT(sAffineAnims_HailParticle))
{
if (++task->tHailStructId == (int)ARRAY_COUNT(sHailCoordData))
task->tState++;
else
task->tState--;
}
else
{
task->tHailSpawnTimer = 1;
}
}
else
{
task->tHailSpawnTimer--;
}
break;
case 2:
if (task->tSpriteCount == 0)
DestroyAnimVisualTask(taskId);
break;
}
}
#undef tState
#undef tSpriteCount
#undef tHailAffineAnimNum
#undef tHailStructId
#undef tInitialDelayTimer
#undef tHailSpawnTimer
// Hail falling particle sprite vars
#define sSpawnImpactEffect data[0]
#define sTargetX data[3]
#define sTargetY data[4]
#define sAffineAnimNum data[5]
#define sOwnerTaskId data[6]
#define sOwnerTaskSpriteCountField data[7]
// hailStructId: index into sHailCoordData. affineAnimNum: hail affine variant.
// taskId: owner AnimTask_Hail2. c: gTasks[taskId].data[] index (e.g. tSpriteCount) for sprite bookkeeping.
// Returns TRUE if sprite created.
static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, u8 c)
{
u8 id;
s16 battlerX, battlerY;
s16 spriteX;
bool8 shouldSpawnImpactEffect = FALSE;
if (sHailCoordData[hailStructId].type != HAILSTRUCTTYPE_FIXED_POSITION)
{
id = GetBattlerAtPosition(sHailCoordData[hailStructId].bPosition);
if (IsBattlerSpriteVisible(id))
{
shouldSpawnImpactEffect = TRUE;
battlerX = GetBattlerSpriteCoord(id, BATTLER_COORD_X_2);
battlerY = GetBattlerSpriteCoord(id, BATTLER_COORD_Y_PIC_OFFSET);
switch (sHailCoordData[hailStructId].type)
{
case HAILSTRUCTTYPE_NEGATIVE_POS_MOD:
battlerX -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6;
battlerY -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6;
break;
case HAILSTRUCTTYPE_POSITIVE_POS_MOD:
battlerX += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6;
battlerY += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6;
break;
}
}
else
{
battlerX = sHailCoordData[hailStructId].x;
battlerY = sHailCoordData[hailStructId].y;
}
}
else
{
battlerX = sHailCoordData[hailStructId].x;
battlerY = sHailCoordData[hailStructId].y;
}
spriteX = battlerX - ((battlerY + 8) / 2);
id = CreateSprite(&gHailParticleSpriteTemplate, spriteX, -8, 18);
if (id == MAX_SPRITES)
{
return FALSE;
}
StartSpriteAffineAnim(&gSprites[id], affineAnimNum);
gSprites[id].sSpawnImpactEffect = shouldSpawnImpactEffect;
gSprites[id].sTargetX = battlerX;
gSprites[id].sTargetY = battlerY;
gSprites[id].sAffineAnimNum = affineAnimNum;
gSprites[id].sOwnerTaskId = taskId;
gSprites[id].sOwnerTaskSpriteCountField = c;
return TRUE;
}
static void AnimHailBegin(struct Sprite *sprite)
{
sprite->x += 4;
sprite->y += 8;
if (sprite->x < sprite->sTargetX && sprite->y < sprite->sTargetY)
return;
if (sprite->sSpawnImpactEffect == 1 && sprite->sAffineAnimNum == 0)
{
sprite->data[0] = CreateSprite(&gIceCrystalHitLargeSpriteTemplate,
sprite->sTargetX, sprite->sTargetY, sprite->subpriority);
if (sprite->data[0] != MAX_SPRITES)
{
// The sprite template we're using is shared amongst a few other
// places, which make the sprite flicker. That's not what we want
// here, though. Override the callback.
gSprites[sprite->data[0]].callback = AnimHailContinue;
gSprites[sprite->data[0]].sOwnerTaskId = sprite->sOwnerTaskId;
gSprites[sprite->data[0]].sOwnerTaskSpriteCountField = sprite->sOwnerTaskSpriteCountField;
}
FreeOamMatrix(sprite->oam.matrixNum);
DestroySprite(sprite);
}
else
{
gTasks[sprite->sOwnerTaskId].data[sprite->sOwnerTaskSpriteCountField]--;
FreeOamMatrix(sprite->oam.matrixNum);
DestroySprite(sprite);
}
}
#undef sSpawnImpactEffect
#undef sTargetX
#undef sTargetY
#undef sAffineAnimNum
// Hail impact VFX sprite vars
#define sTimer data[0]
static void AnimHailContinue(struct Sprite *sprite)
{
if (++sprite->sTimer == 20)
{
gTasks[sprite->sOwnerTaskId].data[sprite->sOwnerTaskSpriteCountField]--;
FreeOamMatrix(sprite->oam.matrixNum);
DestroySprite(sprite);
}
}
#undef sTimer
#undef sOwnerTaskId
#undef sOwnerTaskSpriteCountField
// Initializes the animation for Ice Ball.
// arg 0: initial x pixel offset
// arg 1: initial y pixel offset
// arg 2: target x offset
// arg 3: target y offset
// arg 4: duration
// arg 5: arc height (negative)
static void InitIceBallAnim(struct Sprite *sprite)
{
u8 animNum = gAnimDisableStructPtr->rolloutTimerStartValue - gAnimDisableStructPtr->rolloutTimer - 1;
if (animNum > 4)
animNum = 4;
StartSpriteAffineAnim(sprite, animNum);
InitSpritePosToAnimAttacker(sprite, TRUE);
sprite->data[0] = gBattleAnimArgs[4];
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
gBattleAnimArgs[2] = -gBattleAnimArgs[2];
sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[2];
sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[3];
sprite->data[5] = gBattleAnimArgs[5];
InitAnimArcTranslation(sprite);
sprite->callback = AnimThrowIceBall;
}
// Throws the ball of ice in Ice Ball.
static void AnimThrowIceBall(struct Sprite *sprite)
{
if (!TranslateAnimHorizontalArc(sprite))
return;
StartSpriteAnim(sprite, 1);
sprite->callback = RunStoredCallbackWhenAnimEnds;
StoreSpriteCallbackInData6(sprite, DestroyAnimSprite);
}
// Initializes the particles that scatter at the end of the Ice Ball animation.
static void InitIceBallParticle(struct Sprite *sprite)
{
s16 xOffset, yOffset;
sprite->oam.tileNum += 8;
InitSpritePosToAnimTarget(sprite, TRUE);
xOffset = (Random2() & 0xFF) + 256;
yOffset = Random2() & 0x1FF;
if (yOffset >= 256)
yOffset = 256 - yOffset;
sprite->data[1] = xOffset;
sprite->data[2] = yOffset;
sprite->callback = AnimIceBallParticle;
}
// Animates the particles created by InitIceBallParticle.
static void AnimIceBallParticle(struct Sprite *sprite)
{
sprite->data[3] += sprite->data[1];
sprite->data[4] += sprite->data[2];
if (sprite->data[1] & 1)
sprite->x2 = -(sprite->data[3] >> 8);
else
sprite->x2 = sprite->data[3] >> 8;
sprite->y2 = sprite->data[4] >> 8;
if (++sprite->data[0] == 21)
DestroyAnimSprite(sprite);
}
void AnimTask_GetIceBallCounter(u8 taskId)
{
u8 arg = gBattleAnimArgs[0];
gBattleAnimArgs[arg] = gAnimDisableStructPtr->rolloutTimerStartValue - gAnimDisableStructPtr->rolloutTimer - 1;
DestroyAnimVisualTask(taskId);
}