#include "global.h" #include "battle_anim.h" #include "gpu_regs.h" #include "trig.h" #include "constants/rgb.h" static void AnimMegahornHorn(struct Sprite *); static void AnimLeechLifeNeedle(struct Sprite *); static void AnimTranslateWebThread(struct Sprite *); static void AnimTranslateWebThread_Step(struct Sprite *); static void AnimStringWrap(struct Sprite *); static void AnimStringWrap_Step(struct Sprite *); static void AnimSpiderWeb(struct Sprite *); static void AnimSpiderWeb_Step(struct Sprite *); static void AnimSpiderWeb_End(struct Sprite *); static void AnimTranslateStinger(struct Sprite *); static void AnimMissileArc(struct Sprite *); static void AnimMissileArc_Step(struct Sprite *); static void AnimTailGlowOrb(struct Sprite *); static const union AffineAnimCmd sAffineAnim_MegahornHorn_0[] = { AFFINEANIMCMD_FRAME(0x100, 0x100, 30, 0), AFFINEANIMCMD_END, }; static const union AffineAnimCmd sAffineAnim_MegahornHorn_1[] = { AFFINEANIMCMD_FRAME(0x100, 0x100, -99, 0), AFFINEANIMCMD_END, }; static const union AffineAnimCmd sAffineAnim_MegahornHorn_2[] = { AFFINEANIMCMD_FRAME(0x100, 0x100, 94, 0), AFFINEANIMCMD_END, }; static const union AffineAnimCmd *const sAffineAnims_MegahornHorn[] = { sAffineAnim_MegahornHorn_0, sAffineAnim_MegahornHorn_1, sAffineAnim_MegahornHorn_2, }; const struct SpriteTemplate gMegahornHornSpriteTemplate = { .tileTag = ANIM_TAG_HORN_HIT_2, .paletteTag = ANIM_TAG_HORN_HIT_2, .oam = &gOamData_AffineDouble_ObjNormal_32x16, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_MegahornHorn, .callback = AnimMegahornHorn, }; static const union AffineAnimCmd sAffineAnim_LeechLifeNeedle_0[] = { AFFINEANIMCMD_FRAME(0x0, 0x0, -33, 1), AFFINEANIMCMD_END, }; static const union AffineAnimCmd sAffineAnim_LeechLifeNeedle_1[] = { AFFINEANIMCMD_FRAME(0x0, 0x0, 96, 1), AFFINEANIMCMD_END, }; static const union AffineAnimCmd sAffineAnim_LeechLifeNeedle_2[] = { AFFINEANIMCMD_FRAME(0x0, 0x0, -96, 1), AFFINEANIMCMD_END, }; static const union AffineAnimCmd *const sAffineAnims_LeechLifeNeedle[] = { sAffineAnim_LeechLifeNeedle_0, sAffineAnim_LeechLifeNeedle_1, sAffineAnim_LeechLifeNeedle_2, }; const struct SpriteTemplate gLeechLifeNeedleSpriteTemplate = { .tileTag = ANIM_TAG_NEEDLE, .paletteTag = ANIM_TAG_NEEDLE, .oam = &gOamData_AffineNormal_ObjNormal_16x16, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_LeechLifeNeedle, .callback = AnimLeechLifeNeedle, }; const struct SpriteTemplate gWebThreadSpriteTemplate = { .tileTag = ANIM_TAG_WEB_THREAD, .paletteTag = ANIM_TAG_WEB_THREAD, .oam = &gOamData_AffineOff_ObjNormal_8x8, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = AnimTranslateWebThread, }; const struct SpriteTemplate gStringWrapSpriteTemplate = { .tileTag = ANIM_TAG_STRING, .paletteTag = ANIM_TAG_STRING, .oam = &gOamData_AffineOff_ObjNormal_64x32, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = AnimStringWrap, }; static const union AffineAnimCmd sAffineAnim_SpiderWeb[] = { AFFINEANIMCMD_FRAME(0x10, 0x10, 0, 0), AFFINEANIMCMD_FRAME(0x6, 0x6, 0, 1), AFFINEANIMCMD_JUMP(1), }; static const union AffineAnimCmd *const sAffineAnims_SpiderWeb[] = { sAffineAnim_SpiderWeb, }; const struct SpriteTemplate gSpiderWebSpriteTemplate = { .tileTag = ANIM_TAG_SPIDER_WEB, .paletteTag = ANIM_TAG_SPIDER_WEB, .oam = &gOamData_AffineDouble_ObjBlend_64x64, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_SpiderWeb, .callback = AnimSpiderWeb, }; const struct SpriteTemplate gLinearStingerSpriteTemplate = { .tileTag = ANIM_TAG_NEEDLE, .paletteTag = ANIM_TAG_NEEDLE, .oam = &gOamData_AffineNormal_ObjNormal_16x16, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = AnimTranslateStinger, }; const struct SpriteTemplate gPinMissileSpriteTemplate = { .tileTag = ANIM_TAG_NEEDLE, .paletteTag = ANIM_TAG_NEEDLE, .oam = &gOamData_AffineNormal_ObjNormal_16x16, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = AnimMissileArc, }; const struct SpriteTemplate gIcicleSpearSpriteTemplate = { .tileTag = ANIM_TAG_ICICLE_SPEAR, .paletteTag = ANIM_TAG_ICICLE_SPEAR, .oam = &gOamData_AffineNormal_ObjNormal_32x32, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = AnimMissileArc, }; static const union AffineAnimCmd sAffineAnim_TailGlowOrb[] = { AFFINEANIMCMD_FRAME(0x10, 0x10, 0, 0), AFFINEANIMCMD_FRAME(0x8, 0x8, 0, 18), AFFINEANIMCMD_LOOP(0), AFFINEANIMCMD_FRAME(0xFFFB, 0xFFFB, 0, 8), AFFINEANIMCMD_FRAME(0x5, 0x5, 0, 8), AFFINEANIMCMD_LOOP(5), AFFINEANIMCMD_END, }; static const union AffineAnimCmd *const sAffineAnims_TailGlowOrb[] = { sAffineAnim_TailGlowOrb, }; const struct SpriteTemplate gTailGlowOrbSpriteTemplate = { .tileTag = ANIM_TAG_CIRCLE_OF_LIGHT, .paletteTag = ANIM_TAG_CIRCLE_OF_LIGHT, .oam = &gOamData_AffineNormal_ObjBlend_64x64, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_TailGlowOrb, .callback = AnimTailGlowOrb, }; static void AnimMegahornHorn(struct Sprite *sprite) { if (IsContest()) { StartSpriteAffineAnim(sprite, 2); gBattleAnimArgs[2] = -gBattleAnimArgs[2]; gBattleAnimArgs[0] = -gBattleAnimArgs[0]; } else if (!GetBattlerSide(gBattleAnimTarget)) { StartSpriteAffineAnim(sprite, 1); gBattleAnimArgs[1] = -gBattleAnimArgs[1]; gBattleAnimArgs[2] = -gBattleAnimArgs[2]; gBattleAnimArgs[3] = -gBattleAnimArgs[3]; gBattleAnimArgs[0] = -gBattleAnimArgs[0]; } sprite->x = GetBattlerSpriteCoord2(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[0]; sprite->y = GetBattlerSpriteCoord2(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[1]; sprite->data[0] = gBattleAnimArgs[4]; sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[2]; sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[3]; sprite->callback = StartAnimLinearTranslation; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); } static void AnimLeechLifeNeedle(struct Sprite *sprite) { if (IsContest()) { gBattleAnimArgs[0] = -gBattleAnimArgs[0]; StartSpriteAffineAnim(sprite, 2); } else if (!GetBattlerSide(gBattleAnimTarget)) { gBattleAnimArgs[1] = -gBattleAnimArgs[1]; gBattleAnimArgs[0] = -gBattleAnimArgs[0]; } sprite->x = GetBattlerSpriteCoord2(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[0]; sprite->y = GetBattlerSpriteCoord2(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[1]; sprite->data[0] = gBattleAnimArgs[2]; sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2); sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET); sprite->callback = StartAnimLinearTranslation; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); } // Creates a single web thread that travels from attacker to target. // Used by MOVE_STRING_SHOT and MOVE_SPIDER_WEB in their first move phase. // arg 0: x // arg 1: y // arg 2: controls the left-to-right movement // arg 3: amplitude // arg 4: if targets both opponents static void AnimTranslateWebThread(struct Sprite *sprite) { if (IsContest()) gBattleAnimArgs[2] /= 2; InitSpritePosToAnimAttacker(sprite, TRUE); sprite->data[0] = gBattleAnimArgs[2]; sprite->data[1] = sprite->x; sprite->data[3] = sprite->y; if (!gBattleAnimArgs[4]) { sprite->data[2] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2); sprite->data[4] = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET); } else { SetAverageBattlerPositions(gBattleAnimTarget, 1, &sprite->data[2], &sprite->data[4]); } InitAnimLinearTranslationWithSpeed(sprite); sprite->data[5] = gBattleAnimArgs[3]; sprite->callback = AnimTranslateWebThread_Step; } static void AnimTranslateWebThread_Step(struct Sprite *sprite) { if (AnimTranslateLinear(sprite)) { DestroyAnimSprite(sprite); return; } sprite->x2 += Sin(sprite->data[6], sprite->data[5]); sprite->data[6] = (sprite->data[6] + 13) & 0xFF; } // Second stage of String Shot static void AnimStringWrap(struct Sprite *sprite) { SetAverageBattlerPositions(gBattleAnimTarget, 0, &sprite->x, &sprite->y); if (GetBattlerSide(gBattleAnimAttacker)) sprite->x -= gBattleAnimArgs[0]; else sprite->x += gBattleAnimArgs[0]; sprite->y += gBattleAnimArgs[1]; if (!GetBattlerSide(gBattleAnimTarget)) sprite->y += 8; sprite->callback = AnimStringWrap_Step; } static void AnimStringWrap_Step(struct Sprite *sprite) { if (++sprite->data[0] == 3) { sprite->data[0] = 0; sprite->invisible ^= 1; } if (++sprite->data[1] == 51) { DestroyAnimSprite(sprite); } } static void AnimSpiderWeb(struct Sprite *sprite) { SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT2_ALL | BLDCNT_EFFECT_BLEND); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(16, 0)); sprite->data[0] = 16; sprite->callback = AnimSpiderWeb_Step; } static void AnimSpiderWeb_Step(struct Sprite *sprite) { if (sprite->data[2] < 20) { sprite->data[2]++; } else if (sprite->data[1]++ & 1) { sprite->data[0]--; SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(sprite->data[0], 16 - sprite->data[0])); if (sprite->data[0] == 0) { sprite->invisible = TRUE; sprite->callback = AnimSpiderWeb_End; } } } static void AnimSpiderWeb_End(struct Sprite *sprite) { SetGpuReg(REG_OFFSET_BLDCNT, 0); SetGpuReg(REG_OFFSET_BLDALPHA, 0); DestroyAnimSprite(sprite); } // Translates a stinger sprite linearly to a destination location. The sprite is // initially rotated so that it appears to be traveling in a straight line. // arg 0: initial x pixel offset // arg 1: initial y pixel offset // arg 2: target x pixel offset // arg 3: target y pixel offset // arg 4: duration static void AnimTranslateStinger(struct Sprite *sprite) { s16 lVarX, lVarY; u16 rot; if (IsContest()) { gBattleAnimArgs[2] = -gBattleAnimArgs[2]; } else { if (GetBattlerSide(gBattleAnimAttacker)) { gBattleAnimArgs[2] = -gBattleAnimArgs[2]; gBattleAnimArgs[1] = -gBattleAnimArgs[1]; gBattleAnimArgs[3] = -gBattleAnimArgs[3]; } } if (!IsContest() && GetBattlerSide(gBattleAnimAttacker) == GetBattlerSide(gBattleAnimTarget)) { if (GetBattlerPosition(gBattleAnimTarget) == B_POSITION_PLAYER_LEFT || GetBattlerPosition(gBattleAnimTarget) == B_POSITION_OPPONENT_LEFT) { gBattleAnimArgs[2] *= -1; gBattleAnimArgs[0] *= -1; } } InitSpritePosToAnimAttacker(sprite, 1); lVarX = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2) + gBattleAnimArgs[2]; lVarY = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + gBattleAnimArgs[3]; rot = ArcTan2Neg(lVarX - sprite->x, lVarY - sprite->y); rot += 0xC000; TrySetSpriteRotScale(sprite, FALSE, 0x100, 0x100, rot); sprite->data[0] = gBattleAnimArgs[4]; sprite->data[2] = lVarX; sprite->data[4] = lVarY; sprite->callback = StartAnimLinearTranslation; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); } // Rotates sprite and moves it in an arc, so that it appears like a missle or arrow traveling. // arg 0: initial x pixel offset // arg 1: initial y pixel offset // arg 2: target x pixel offset // arg 3: target y pixel offset // arg 4: duration // arg 5: wave amplitude static void AnimMissileArc(struct Sprite *sprite) { InitSpritePosToAnimAttacker(sprite, 1); if (GetBattlerSide(gBattleAnimAttacker)) gBattleAnimArgs[2] = -gBattleAnimArgs[2]; sprite->data[0] = gBattleAnimArgs[4]; 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 = AnimMissileArc_Step; sprite->invisible = TRUE; } static void AnimMissileArc_Step(struct Sprite *sprite) { sprite->invisible = FALSE; if (TranslateAnimHorizontalArc(sprite)) { DestroyAnimSprite(sprite); } else { s16 tempData[8]; u16 *data = sprite->data; u16 x1 = sprite->x; s16 x2 = sprite->x2; u16 y1 = sprite->y; s16 y2 = sprite->y2; int i; for (i = 0; i < 8; i++) tempData[i] = data[i]; x2 += x1; y2 += y1; if (!TranslateAnimHorizontalArc(sprite)) { u16 rotation = ArcTan2Neg(sprite->x + sprite->x2 - x2, sprite->y + sprite->y2 - y2); rotation += 0xC000; TrySetSpriteRotScale(sprite, FALSE, 0x100, 0x100, rotation); for (i = 0; i < 8; i++) data[i] = tempData[i]; } } } static void AnimTailGlowOrb(struct Sprite *sprite) { if (gBattleAnimArgs[0] == ANIM_ATTACKER) { sprite->x = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2); sprite->y = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET) + 18; } else { sprite->x = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2); sprite->y = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET) + 18; } StoreSpriteCallbackInData6(sprite, DestroySpriteAndMatrix); sprite->callback = RunStoredCallbackWhenAffineAnimEnds; }