pokeemerald/src/pokeball.c

1340 lines
40 KiB
C
Raw Normal View History

2017-12-02 19:39:07 +01:00
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
2018-11-14 01:01:50 +01:00
#include "decompress.h"
#include "graphics.h"
2017-12-02 19:39:07 +01:00
#include "main.h"
#include "m4a.h"
2018-11-14 01:01:50 +01:00
#include "pokeball.h"
#include "pokemon.h"
#include "sound.h"
#include "sprite.h"
#include "task.h"
#include "trig.h"
2017-12-02 20:38:26 +01:00
#include "util.h"
#include "data.h"
2018-11-14 01:01:50 +01:00
#include "constants/songs.h"
2017-12-02 19:39:07 +01:00
2017-12-02 21:27:00 +01:00
static void Task_DoPokeballSendOutAnim(u8 taskId);
static void SpriteCB_PlayerMonSendOut_1(struct Sprite *sprite);
static void SpriteCB_PlayerMonSendOut_2(struct Sprite *sprite);
static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite);
static void SpriteCB_BallThrow(struct Sprite *sprite);
static void SpriteCB_BallThrow_ReachMon(struct Sprite *sprite);
static void SpriteCB_BallThrow_StartShrinkMon(struct Sprite *sprite);
static void SpriteCB_BallThrow_ShrinkMon(struct Sprite *sprite);
static void SpriteCB_BallThrow_Close(struct Sprite *sprite);
static void SpriteCB_BallThrow_FallToGround(struct Sprite *sprite);
static void SpriteCB_BallThrow_StartShakes(struct Sprite *sprite);
static void SpriteCB_BallThrow_Shake(struct Sprite *sprite);
static void SpriteCB_BallThrow_StartCaptureMon(struct Sprite *sprite);
static void SpriteCB_BallThrow_CaptureMon(struct Sprite *sprite);
2017-12-02 21:27:00 +01:00
static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite);
static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite);
static void HandleBallAnimEnd(struct Sprite *sprite);
2021-01-23 05:22:37 +01:00
static void SpriteCB_PokeballReleaseMon(struct Sprite *sprite);
static void SpriteCB_ReleasedMonFlyOut(struct Sprite *sprite);
static void SpriteCB_TradePokeball(struct Sprite *sprite);
static void SpriteCB_TradePokeballSendOff(struct Sprite *sprite);
static void SpriteCB_TradePokeballEnd(struct Sprite *sprite);
static void SpriteCB_HealthboxSlideInDelayed(struct Sprite *sprite);
static void SpriteCB_HealthboxSlideIn(struct Sprite *sprite);
2017-12-02 21:27:00 +01:00
static void SpriteCB_HitAnimHealthoxEffect(struct Sprite *sprite);
2018-03-01 00:59:52 +01:00
static u16 GetBattlerPokeballItemId(u8 battlerId);
2017-12-02 21:27:00 +01:00
// rom const data
#define GFX_TAG_POKEBALL 55000
#define GFX_TAG_GREATBALL 55001
#define GFX_TAG_SAFARIBALL 55002
#define GFX_TAG_ULTRABALL 55003
#define GFX_TAG_MASTERBALL 55004
#define GFX_TAG_NETBALL 55005
#define GFX_TAG_DIVEBALL 55006
#define GFX_TAG_NESTBALL 55007
#define GFX_TAG_REPEATBALL 55008
#define GFX_TAG_TIMERBALL 55009
#define GFX_TAG_LUXURYBALL 55010
#define GFX_TAG_PREMIERBALL 55011
const struct CompressedSpriteSheet gBallSpriteSheets[POKEBALL_COUNT] =
{
[BALL_POKE] = {gBallGfx_Poke, 384, GFX_TAG_POKEBALL},
[BALL_GREAT] = {gBallGfx_Great, 384, GFX_TAG_GREATBALL},
[BALL_SAFARI] = {gBallGfx_Safari, 384, GFX_TAG_SAFARIBALL},
[BALL_ULTRA] = {gBallGfx_Ultra, 384, GFX_TAG_ULTRABALL},
[BALL_MASTER] = {gBallGfx_Master, 384, GFX_TAG_MASTERBALL},
[BALL_NET] = {gBallGfx_Net, 384, GFX_TAG_NETBALL},
[BALL_DIVE] = {gBallGfx_Dive, 384, GFX_TAG_DIVEBALL},
[BALL_NEST] = {gBallGfx_Nest, 384, GFX_TAG_NESTBALL},
[BALL_REPEAT] = {gBallGfx_Repeat, 384, GFX_TAG_REPEATBALL},
[BALL_TIMER] = {gBallGfx_Timer, 384, GFX_TAG_TIMERBALL},
[BALL_LUXURY] = {gBallGfx_Luxury, 384, GFX_TAG_LUXURYBALL},
[BALL_PREMIER] = {gBallGfx_Premier, 384, GFX_TAG_PREMIERBALL},
2017-12-02 21:27:00 +01:00
};
const struct CompressedSpritePalette gBallSpritePalettes[POKEBALL_COUNT] =
{
[BALL_POKE] = {gBallPal_Poke, GFX_TAG_POKEBALL},
[BALL_GREAT] = {gBallPal_Great, GFX_TAG_GREATBALL},
[BALL_SAFARI] = {gBallPal_Safari, GFX_TAG_SAFARIBALL},
[BALL_ULTRA] = {gBallPal_Ultra, GFX_TAG_ULTRABALL},
[BALL_MASTER] = {gBallPal_Master, GFX_TAG_MASTERBALL},
[BALL_NET] = {gBallPal_Net, GFX_TAG_NETBALL},
[BALL_DIVE] = {gBallPal_Dive, GFX_TAG_DIVEBALL},
[BALL_NEST] = {gBallPal_Nest, GFX_TAG_NESTBALL},
[BALL_REPEAT] = {gBallPal_Repeat, GFX_TAG_REPEATBALL},
[BALL_TIMER] = {gBallPal_Timer, GFX_TAG_TIMERBALL},
[BALL_LUXURY] = {gBallPal_Luxury, GFX_TAG_LUXURYBALL},
[BALL_PREMIER] = {gBallPal_Premier, GFX_TAG_PREMIERBALL},
2017-12-02 21:27:00 +01:00
};
static const struct OamData sBallOamData =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_DOUBLE,
.objMode = ST_OAM_OBJ_NORMAL,
2017-12-02 21:27:00 +01:00
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x16),
2017-12-02 21:27:00 +01:00
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x16),
2017-12-02 21:27:00 +01:00
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0,
};
static const union AnimCmd sBallAnimSeq3[] =
{
ANIMCMD_FRAME(0, 5),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sBallAnimSeq5[] =
{
ANIMCMD_FRAME(4, 1),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sBallAnimSeq4[] =
{
ANIMCMD_FRAME(8, 5),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sBallAnimSeq6[] =
{
ANIMCMD_FRAME(12, 1),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sBallAnimSeq0[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END,
};
static const union AnimCmd sBallAnimSeq1[] =
{
ANIMCMD_FRAME(4, 5),
ANIMCMD_FRAME(8, 5),
ANIMCMD_END,
};
static const union AnimCmd sBallAnimSeq2[] =
{
ANIMCMD_FRAME(4, 5),
ANIMCMD_FRAME(0, 5),
ANIMCMD_END,
};
static const union AnimCmd *const sBallAnimSequences[] =
{
sBallAnimSeq0,
sBallAnimSeq1,
sBallAnimSeq2,
// unused?
sBallAnimSeq3,
sBallAnimSeq4,
sBallAnimSeq5,
sBallAnimSeq6,
};
static const union AffineAnimCmd sAffineAnim_BallRotate_0[] =
2017-12-02 21:27:00 +01:00
{
AFFINEANIMCMD_FRAME(0, 0, 0, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd sAffineAnim_BallRotate_Right[] =
2017-12-02 21:27:00 +01:00
{
AFFINEANIMCMD_FRAME(0, 0, -3, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd sAffineAnim_BallRotate_Left[] =
2017-12-02 21:27:00 +01:00
{
AFFINEANIMCMD_FRAME(0, 0, 3, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd sAffineAnim_BallRotate_3[] =
2017-12-02 21:27:00 +01:00
{
AFFINEANIMCMD_FRAME(256, 256, 0, 0),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd sAffineAnim_BallRotate_4[] =
2017-12-02 21:27:00 +01:00
{
AFFINEANIMCMD_FRAME(0, 0, 25, 1),
AFFINEANIMCMD_JUMP(0),
};
static const union AffineAnimCmd *const sAffineAnim_BallRotate[] =
2017-12-02 21:27:00 +01:00
{
[BALL_AFFINE_ANIM_0] = sAffineAnim_BallRotate_0,
[BALL_ROTATE_RIGHT] = sAffineAnim_BallRotate_Right,
[BALL_ROTATE_LEFT] = sAffineAnim_BallRotate_Left,
[BALL_AFFINE_ANIM_3] = sAffineAnim_BallRotate_3,
[BALL_AFFINE_ANIM_4] = sAffineAnim_BallRotate_4,
2017-12-02 21:27:00 +01:00
};
const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] =
{
[BALL_POKE] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_POKEBALL,
.paletteTag = GFX_TAG_POKEBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_GREAT] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_GREATBALL,
.paletteTag = GFX_TAG_GREATBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_SAFARI] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_SAFARIBALL,
.paletteTag = GFX_TAG_SAFARIBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_ULTRA] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_ULTRABALL,
.paletteTag = GFX_TAG_ULTRABALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_MASTER] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_MASTERBALL,
.paletteTag = GFX_TAG_MASTERBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_NET] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_NETBALL,
.paletteTag = GFX_TAG_NETBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_DIVE] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_DIVEBALL,
.paletteTag = GFX_TAG_DIVEBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_NEST] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_NESTBALL,
.paletteTag = GFX_TAG_NESTBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_REPEAT] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_REPEATBALL,
.paletteTag = GFX_TAG_REPEATBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_TIMER] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_TIMERBALL,
.paletteTag = GFX_TAG_TIMERBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_LUXURY] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_LUXURYBALL,
.paletteTag = GFX_TAG_LUXURYBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
[BALL_PREMIER] =
2017-12-02 21:27:00 +01:00
{
.tileTag = GFX_TAG_PREMIERBALL,
.paletteTag = GFX_TAG_PREMIERBALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
2017-12-02 21:27:00 +01:00
},
};
2017-12-02 19:39:07 +01:00
2018-03-01 00:59:52 +01:00
#define tFrames data[0]
#define tPan data[1]
#define tThrowId data[2]
#define tBattler data[3]
#define tOpponentBattler data[4]
2017-12-02 19:39:07 +01:00
u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow)
{
u8 taskId;
gDoingBattleAnim = TRUE;
2018-02-06 02:46:59 +01:00
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive = 1;
2017-12-02 19:39:07 +01:00
taskId = CreateTask(Task_DoPokeballSendOutAnim, 5);
gTasks[taskId].tPan = pan;
gTasks[taskId].tThrowId = kindOfThrow;
2018-03-01 00:59:52 +01:00
gTasks[taskId].tBattler = gActiveBattler;
2017-12-02 19:39:07 +01:00
return 0;
}
2018-03-01 00:59:52 +01:00
#define sBattler data[6]
2017-12-02 19:39:07 +01:00
2017-12-02 21:27:00 +01:00
static void Task_DoPokeballSendOutAnim(u8 taskId)
2017-12-02 19:39:07 +01:00
{
u16 throwCaseId;
2018-03-01 00:59:52 +01:00
u8 battlerId;
2017-12-02 19:39:07 +01:00
u16 itemId, ballId;
u8 ballSpriteId;
bool8 notSendOut = FALSE;
if (gTasks[taskId].tFrames == 0)
{
gTasks[taskId].tFrames++;
return;
}
throwCaseId = gTasks[taskId].tThrowId;
2018-03-01 00:59:52 +01:00
battlerId = gTasks[taskId].tBattler;
2017-12-02 19:39:07 +01:00
2018-03-01 00:59:52 +01:00
if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
itemId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL);
2017-12-02 19:39:07 +01:00
else
2018-03-01 00:59:52 +01:00
itemId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL);
2017-12-02 19:39:07 +01:00
ballId = ItemIdToBallId(itemId);
LoadBallGfx(ballId);
ballSpriteId = CreateSprite(&gBallSpriteTemplates[ballId], 32, 80, 29);
gSprites[ballSpriteId].data[0] = 0x80;
gSprites[ballSpriteId].data[1] = 0;
gSprites[ballSpriteId].data[7] = throwCaseId;
switch (throwCaseId)
{
case POKEBALL_PLAYER_SENDOUT:
2018-03-01 00:59:52 +01:00
gBattlerTarget = battlerId;
2021-07-07 15:11:52 +02:00
gSprites[ballSpriteId].x = 24;
gSprites[ballSpriteId].y = 68;
2017-12-02 19:39:07 +01:00
gSprites[ballSpriteId].callback = SpriteCB_PlayerMonSendOut_1;
break;
case POKEBALL_OPPONENT_SENDOUT:
2021-07-07 15:11:52 +02:00
gSprites[ballSpriteId].x = GetBattlerSpriteCoord(battlerId, BATTLER_COORD_X);
gSprites[ballSpriteId].y = GetBattlerSpriteCoord(battlerId, BATTLER_COORD_Y) + 24;
2018-03-01 00:59:52 +01:00
gBattlerTarget = battlerId;
2017-12-02 20:38:26 +01:00
gSprites[ballSpriteId].data[0] = 0;
2017-12-02 19:39:07 +01:00
gSprites[ballSpriteId].callback = SpriteCB_OpponentMonSendOut;
break;
default:
2018-02-06 23:09:39 +01:00
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
2017-12-02 19:39:07 +01:00
notSendOut = TRUE;
break;
}
2018-03-01 00:59:52 +01:00
gSprites[ballSpriteId].sBattler = gBattlerTarget;
2017-12-02 19:39:07 +01:00
if (!notSendOut)
{
DestroyTask(taskId);
return;
}
// this will perform an unused ball throw animation
2017-12-02 20:38:26 +01:00
gSprites[ballSpriteId].data[0] = 0x22;
2018-10-06 23:04:53 +02:00
gSprites[ballSpriteId].data[2] = GetBattlerSpriteCoord(gBattlerTarget, BATTLER_COORD_X);
gSprites[ballSpriteId].data[4] = GetBattlerSpriteCoord(gBattlerTarget, BATTLER_COORD_Y) - 16;
2017-12-02 20:38:26 +01:00
gSprites[ballSpriteId].data[5] = -40;
2018-11-26 00:00:18 +01:00
InitAnimArcTranslation(&gSprites[ballSpriteId]);
2017-12-02 19:39:07 +01:00
gSprites[ballSpriteId].oam.affineParam = taskId;
2018-03-01 00:59:52 +01:00
gTasks[taskId].tOpponentBattler = gBattlerTarget;
2017-12-02 19:39:07 +01:00
gTasks[taskId].func = TaskDummy;
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_THROW);
2017-12-02 19:39:07 +01:00
}
// This sequence of functions is very similar to those that get run when
// a Pokéball gets thrown at a wild Pokémon, starting at SpriteCB_Ball_Arc.
// These do not seem to get run.
static void SpriteCB_BallThrow(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
2019-02-06 20:17:09 +01:00
if (TranslateAnimHorizontalArc(sprite))
2017-12-02 19:39:07 +01:00
{
u16 ballId;
u8 taskId = sprite->oam.affineParam;
2018-03-01 00:59:52 +01:00
u8 opponentBattler = gTasks[taskId].tOpponentBattler;
2017-12-02 19:39:07 +01:00
u8 noOfShakes = gTasks[taskId].tThrowId;
StartSpriteAnim(sprite, 1);
sprite->affineAnimPaused = 1;
2021-07-07 15:11:52 +02:00
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->x2 = 0;
sprite->y2 = 0;
2017-12-02 20:38:26 +01:00
sprite->data[5] = 0;
2018-03-01 00:59:52 +01:00
ballId = ItemIdToBallId(GetBattlerPokeballItemId(opponentBattler));
2022-05-21 21:21:50 +02:00
AnimateBallOpenParticles(sprite->x, sprite->y - 5, 1, 28, ballId);
2018-03-01 00:59:52 +01:00
sprite->data[0] = LaunchBallFadeMonTask(FALSE, opponentBattler, 14, ballId);
sprite->sBattler = opponentBattler;
2017-12-02 19:39:07 +01:00
sprite->data[7] = noOfShakes;
DestroyTask(taskId);
sprite->callback = SpriteCB_BallThrow_ReachMon;
2017-12-02 19:39:07 +01:00
}
}
#undef tFrames
#undef tPan
#undef tThrowId
2018-03-01 00:59:52 +01:00
#undef tBattler
#undef tOpponentBattler
2017-12-02 19:39:07 +01:00
static void SpriteCB_BallThrow_ReachMon(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
sprite->callback = SpriteCB_BallThrow_StartShrinkMon;
2017-12-02 19:39:07 +01:00
}
static void SpriteCB_BallThrow_StartShrinkMon(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
2017-12-02 20:38:26 +01:00
if (++sprite->data[5] == 10)
2017-12-02 19:39:07 +01:00
{
2017-12-02 20:38:26 +01:00
sprite->data[5] = 0;
sprite->callback = SpriteCB_BallThrow_ShrinkMon;
StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[sprite->sBattler]], BATTLER_AFFINE_RETURN);
2018-03-01 00:59:52 +01:00
AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]);
gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0;
2017-12-02 19:39:07 +01:00
}
}
static void SpriteCB_BallThrow_ShrinkMon(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
sprite->data[5]++;
if (sprite->data[5] == 11)
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_TRADE);
2018-03-01 00:59:52 +01:00
if (gSprites[gBattlerSpriteIds[sprite->sBattler]].affineAnimEnded)
2017-12-02 19:39:07 +01:00
{
StartSpriteAnim(sprite, 2);
2018-03-01 00:59:52 +01:00
gSprites[gBattlerSpriteIds[sprite->sBattler]].invisible = TRUE;
2017-12-02 19:39:07 +01:00
sprite->data[5] = 0;
sprite->callback = SpriteCB_BallThrow_Close;
2017-12-02 19:39:07 +01:00
}
else
{
2018-03-01 00:59:52 +01:00
gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] += 0x60;
2021-07-07 15:11:52 +02:00
gSprites[gBattlerSpriteIds[sprite->sBattler]].y2 = -gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] >> 8;
2017-12-02 19:39:07 +01:00
}
}
static void SpriteCB_BallThrow_Close(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
if (sprite->animEnded)
{
sprite->data[5]++;
if (sprite->data[5] == 1)
{
sprite->data[3] = 0;
sprite->data[4] = 32;
sprite->data[5] = 0;
2021-07-07 15:11:52 +02:00
sprite->y += Cos(0, 32);
sprite->y2 = -Cos(0, sprite->data[4]);
sprite->callback = SpriteCB_BallThrow_FallToGround;
2017-12-02 19:39:07 +01:00
}
}
}
static void SpriteCB_BallThrow_FallToGround(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
bool8 r5 = FALSE;
switch (sprite->data[3] & 0xFF)
{
case 0:
2021-07-07 15:11:52 +02:00
sprite->y2 = -Cos(sprite->data[5], sprite->data[4]);
2017-12-02 19:39:07 +01:00
sprite->data[5] += 4 + (sprite->data[3] >> 8);
if (sprite->data[5] >= 64)
{
sprite->data[4] -= 10;
sprite->data[3] += 0x101;
if (sprite->data[3] >> 8 == 4)
r5 = TRUE;
switch (sprite->data[3] >> 8)
{
case 1:
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_BOUNCE_1);
2017-12-02 19:39:07 +01:00
break;
case 2:
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_BOUNCE_2);
2017-12-02 19:39:07 +01:00
break;
case 3:
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_BOUNCE_3);
2017-12-02 19:39:07 +01:00
break;
default:
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_BOUNCE_4);
2017-12-02 19:39:07 +01:00
break;
}
}
break;
case 1:
2021-07-07 15:11:52 +02:00
sprite->y2 = -Cos(sprite->data[5], sprite->data[4]);
2017-12-02 19:39:07 +01:00
sprite->data[5] -= 4 + (sprite->data[3] >> 8);
if (sprite->data[5] <= 0)
{
sprite->data[5] = 0;
sprite->data[3] &= 0xFF00;
}
break;
}
if (r5)
{
sprite->data[3] = 0;
2021-07-07 15:11:52 +02:00
sprite->y += Cos(64, 32);
sprite->y2 = 0;
2017-12-02 19:39:07 +01:00
if (sprite->data[7] == 0)
{
sprite->callback = SpriteCB_ReleaseMonFromBall;
}
else
{
sprite->callback = SpriteCB_BallThrow_StartShakes;
2017-12-02 19:39:07 +01:00
sprite->data[4] = 1;
sprite->data[5] = 0;
}
}
}
static void SpriteCB_BallThrow_StartShakes(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
sprite->data[3]++;
if (sprite->data[3] == 31)
{
sprite->data[3] = 0;
sprite->affineAnimPaused = TRUE;
StartSpriteAffineAnim(sprite, 1);
sprite->callback = SpriteCB_BallThrow_Shake;
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL);
2017-12-02 19:39:07 +01:00
}
}
static void SpriteCB_BallThrow_Shake(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
switch (sprite->data[3] & 0xFF)
{
case 0:
case 2:
2021-07-07 15:11:52 +02:00
sprite->x2 += sprite->data[4];
2017-12-02 19:39:07 +01:00
sprite->data[5] += sprite->data[4];
sprite->affineAnimPaused = FALSE;
if (sprite->data[5] > 3 || sprite->data[5] < -3)
{
sprite->data[3]++;
sprite->data[5] = 0;
}
break;
case 1:
sprite->data[5]++;
if (sprite->data[5] == 1)
{
sprite->data[5] = 0;
sprite->data[4] = -sprite->data[4];
sprite->data[3]++;
sprite->affineAnimPaused = FALSE;
if (sprite->data[4] < 0)
ChangeSpriteAffineAnim(sprite, 2);
else
ChangeSpriteAffineAnim(sprite, 1);
}
else
{
sprite->affineAnimPaused = TRUE;
}
break;
case 3:
sprite->data[3] += 0x100;
if (sprite->data[3] >> 8 == sprite->data[7])
{
sprite->callback = SpriteCB_ReleaseMonFromBall;
}
else
{
if (sprite->data[7] == 4 && sprite->data[3] >> 8 == 3)
{
sprite->callback = SpriteCB_BallThrow_StartCaptureMon;
2017-12-02 19:39:07 +01:00
sprite->affineAnimPaused = TRUE;
}
else
{
sprite->data[3]++;
sprite->affineAnimPaused = TRUE;
}
}
break;
case 4:
default:
sprite->data[5]++;
if (sprite->data[5] == 31)
{
sprite->data[5] = 0;
sprite->data[3] &= 0xFF00;
StartSpriteAffineAnim(sprite, 3);
if (sprite->data[4] < 0)
StartSpriteAffineAnim(sprite, 2);
else
StartSpriteAffineAnim(sprite, 1);
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL);
2017-12-02 19:39:07 +01:00
}
break;
}
}
#define tCryTaskSpecies data[0]
#define tCryTaskPan data[1]
#define tCryTaskWantedCry data[2]
2021-11-07 18:58:11 +01:00
#define tCryTaskBattler data[3]
2017-12-02 19:39:07 +01:00
#define tCryTaskMonSpriteId data[4]
#define tCryTaskMonPtr1 data[5]
#define tCryTaskMonPtr2 data[6]
#define tCryTaskFrames data[10]
#define tCryTaskState data[15]
2017-12-02 21:27:00 +01:00
static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
2017-12-02 19:39:07 +01:00
{
u8 wantedCry = gTasks[taskId].tCryTaskWantedCry;
s8 pan = gTasks[taskId].tCryTaskPan;
u16 species = gTasks[taskId].tCryTaskSpecies;
2018-03-01 00:59:52 +01:00
u8 battlerId = gTasks[taskId].tCryTaskBattler;
2017-12-02 19:39:07 +01:00
u8 monSpriteId = gTasks[taskId].tCryTaskMonSpriteId;
2022-07-29 16:52:35 +02:00
struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
2017-12-02 19:39:07 +01:00
switch (gTasks[taskId].tCryTaskState)
{
case 0:
default:
if (gSprites[monSpriteId].affineAnimEnded)
gTasks[taskId].tCryTaskState = wantedCry + 1;
break;
case 1:
2021-11-07 19:54:44 +01:00
// Play single cry
2018-06-19 00:43:15 +02:00
if (ShouldPlayNormalMonCry(mon) == TRUE)
2021-11-07 19:54:44 +01:00
PlayCry_ByMode(species, pan, CRY_MODE_NORMAL);
2017-12-02 19:39:07 +01:00
else
2021-11-07 19:54:44 +01:00
PlayCry_ByMode(species, pan, CRY_MODE_WEAK);
2021-01-23 02:03:21 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
2017-12-02 19:39:07 +01:00
DestroyTask(taskId);
break;
case 2:
StopCryAndClearCrySongs();
gTasks[taskId].tCryTaskFrames = 3;
gTasks[taskId].tCryTaskState = 20;
break;
case 20:
if (gTasks[taskId].tCryTaskFrames == 0)
{
2021-11-07 19:54:44 +01:00
// Play first doubles cry
2018-06-19 00:43:15 +02:00
if (ShouldPlayNormalMonCry(mon) == TRUE)
2021-11-07 19:54:44 +01:00
PlayCry_ReleaseDouble(species, pan, CRY_MODE_DOUBLES);
2017-12-02 19:39:07 +01:00
else
2021-11-07 19:54:44 +01:00
PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK_DOUBLES);
2017-12-02 19:39:07 +01:00
2021-01-23 02:03:21 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
2017-12-02 19:39:07 +01:00
DestroyTask(taskId);
}
else
{
gTasks[taskId].tCryTaskFrames--;
}
break;
case 3:
gTasks[taskId].tCryTaskFrames = 6;
gTasks[taskId].tCryTaskState = 30;
break;
case 30:
if (gTasks[taskId].tCryTaskFrames != 0)
{
gTasks[taskId].tCryTaskFrames--;
break;
}
gTasks[taskId].tCryTaskState++;
// fall through
case 31:
if (!IsCryPlayingOrClearCrySongs())
{
StopCryAndClearCrySongs();
gTasks[taskId].tCryTaskFrames = 3;
gTasks[taskId].tCryTaskState++;
}
break;
case 32:
if (gTasks[taskId].tCryTaskFrames != 0)
{
gTasks[taskId].tCryTaskFrames--;
break;
}
2021-11-07 19:54:44 +01:00
// Play second doubles cry
2018-06-19 00:43:15 +02:00
if (ShouldPlayNormalMonCry(mon) == TRUE)
2021-11-07 19:54:44 +01:00
PlayCry_ReleaseDouble(species, pan, CRY_MODE_NORMAL);
2017-12-02 19:39:07 +01:00
else
2021-11-07 19:54:44 +01:00
PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK);
2017-12-02 19:39:07 +01:00
2021-01-23 02:03:21 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
2017-12-02 19:39:07 +01:00
DestroyTask(taskId);
break;
}
}
2017-12-02 21:27:00 +01:00
static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
2018-03-01 00:59:52 +01:00
u8 battlerId = sprite->sBattler;
2017-12-02 19:39:07 +01:00
u32 ballId;
StartSpriteAnim(sprite, 1);
2018-03-01 00:59:52 +01:00
ballId = ItemIdToBallId(GetBattlerPokeballItemId(battlerId));
2022-05-21 21:21:50 +02:00
AnimateBallOpenParticles(sprite->x, sprite->y - 5, 1, 28, ballId);
sprite->data[0] = LaunchBallFadeMonTask(TRUE, sprite->sBattler, 14, ballId);
2017-12-02 19:39:07 +01:00
sprite->callback = HandleBallAnimEnd;
if (gMain.inBattle)
{
struct Pokemon *mon;
u16 species;
s8 pan;
u16 wantedCryCase;
u8 taskId;
2018-03-01 00:59:52 +01:00
if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
2017-12-02 19:39:07 +01:00
{
2018-03-01 00:59:52 +01:00
mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]];
2017-12-02 19:39:07 +01:00
pan = 25;
}
else
{
2018-03-01 00:59:52 +01:00
mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]];
2017-12-02 19:39:07 +01:00
pan = -25;
}
species = GetMonData(mon, MON_DATA_SPECIES);
2018-03-01 00:59:52 +01:00
if ((battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))
2021-01-23 02:03:21 +01:00
&& IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive)
2017-12-02 19:39:07 +01:00
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (IsBGMPlaying())
m4aMPlayStop(&gMPlayInfo_BGM);
2017-12-02 19:39:07 +01:00
}
else
{
2021-10-09 17:33:37 +02:00
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 128);
2017-12-02 19:39:07 +01:00
}
}
2021-01-23 02:03:21 +01:00
if (!IsDoubleBattle() || !gBattleSpritesDataPtr->animationData->introAnimActive)
2017-12-02 19:39:07 +01:00
wantedCryCase = 0;
2018-03-01 00:59:52 +01:00
else if (battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))
2017-12-02 19:39:07 +01:00
wantedCryCase = 1;
else
wantedCryCase = 2;
2021-01-23 02:03:21 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = TRUE;
2017-12-02 19:39:07 +01:00
taskId = CreateTask(Task_PlayCryWhenReleasedFromBall, 3);
gTasks[taskId].tCryTaskSpecies = species;
gTasks[taskId].tCryTaskPan = pan;
gTasks[taskId].tCryTaskWantedCry = wantedCryCase;
2018-03-01 00:59:52 +01:00
gTasks[taskId].tCryTaskBattler = battlerId;
gTasks[taskId].tCryTaskMonSpriteId = gBattlerSpriteIds[sprite->sBattler];
2021-04-09 18:41:02 +02:00
gTasks[taskId].tCryTaskMonPtr1 = (u32)(mon) >> 16;
2017-12-02 19:39:07 +01:00
gTasks[taskId].tCryTaskMonPtr2 = (u32)(mon);
gTasks[taskId].tCryTaskState = 0;
}
StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[sprite->sBattler]], BATTLER_AFFINE_EMERGE);
2017-12-02 19:39:07 +01:00
2018-03-01 00:59:52 +01:00
if (GetBattlerSide(sprite->sBattler) == B_SIDE_OPPONENT)
2021-10-04 16:21:03 +02:00
gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_OpponentMonFromBall;
2017-12-02 19:39:07 +01:00
else
2021-10-04 16:21:03 +02:00
gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_PlayerMonFromBall;
2017-12-02 19:39:07 +01:00
2018-03-01 00:59:52 +01:00
AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]);
gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0x1000;
2017-12-02 19:39:07 +01:00
}
#undef tCryTaskSpecies
#undef tCryTaskPan
#undef tCryTaskWantedCry
2018-03-01 00:59:52 +01:00
#undef tCryTaskBattler
2017-12-02 19:39:07 +01:00
#undef tCryTaskMonSpriteId
#undef tCryTaskMonPtr1
#undef tCryTaskMonPtr2
#undef tCryTaskFrames
#undef tCryTaskState
static void SpriteCB_BallThrow_StartCaptureMon(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
sprite->animPaused = TRUE;
sprite->callback = SpriteCB_BallThrow_CaptureMon;
2017-12-02 19:39:07 +01:00
sprite->data[3] = 0;
sprite->data[4] = 0;
sprite->data[5] = 0;
}
2017-12-02 21:27:00 +01:00
static void HandleBallAnimEnd(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
bool8 affineAnimEnded = FALSE;
2018-03-01 00:59:52 +01:00
u8 battlerId = sprite->sBattler;
2017-12-02 19:39:07 +01:00
2018-03-01 00:59:52 +01:00
gSprites[gBattlerSpriteIds[battlerId]].invisible = FALSE;
2017-12-02 19:39:07 +01:00
if (sprite->animEnded)
sprite->invisible = TRUE;
2018-03-01 00:59:52 +01:00
if (gSprites[gBattlerSpriteIds[battlerId]].affineAnimEnded)
2017-12-02 19:39:07 +01:00
{
StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[battlerId]], BATTLER_AFFINE_NORMAL);
2017-12-02 19:39:07 +01:00
affineAnimEnded = TRUE;
}
else
{
2018-03-01 00:59:52 +01:00
gSprites[gBattlerSpriteIds[battlerId]].data[1] -= 288;
2021-07-07 15:11:52 +02:00
gSprites[gBattlerSpriteIds[battlerId]].y2 = gSprites[gBattlerSpriteIds[battlerId]].data[1] >> 8;
2017-12-02 19:39:07 +01:00
}
if (sprite->animEnded && affineAnimEnded)
{
2018-03-01 00:59:52 +01:00
s32 i, doneBattlers;
2017-12-02 19:39:07 +01:00
2021-07-07 15:11:52 +02:00
gSprites[gBattlerSpriteIds[battlerId]].y2 = 0;
2017-12-02 19:39:07 +01:00
gDoingBattleAnim = FALSE;
2018-03-01 00:59:52 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].ballAnimActive = 0;
2017-12-02 19:39:07 +01:00
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
2018-03-01 00:59:52 +01:00
for (doneBattlers = 0, i = 0; i < MAX_BATTLERS_COUNT; i++)
2017-12-02 19:39:07 +01:00
{
if (gBattleSpritesDataPtr->healthBoxesData[i].ballAnimActive == 0)
2018-03-01 00:59:52 +01:00
doneBattlers++;
2017-12-02 19:39:07 +01:00
}
2018-03-01 00:59:52 +01:00
if (doneBattlers == MAX_BATTLERS_COUNT)
2017-12-02 19:39:07 +01:00
{
for (i = 0; i < POKEBALL_COUNT; i++)
FreeBallGfx(i);
}
}
}
static void SpriteCB_BallThrow_CaptureMon(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
2018-03-01 00:59:52 +01:00
u8 battlerId = sprite->sBattler;
2017-12-02 19:39:07 +01:00
sprite->data[4]++;
if (sprite->data[4] == 40)
{
return;
}
else if (sprite->data[4] == 95)
{
gDoingBattleAnim = FALSE;
m4aMPlayAllStop();
2020-08-21 00:02:00 +02:00
PlaySE(MUS_EVOLVED);
2017-12-02 19:39:07 +01:00
}
else if (sprite->data[4] == 315)
{
2018-03-01 00:59:52 +01:00
FreeOamMatrix(gSprites[gBattlerSpriteIds[sprite->sBattler]].oam.matrixNum);
DestroySprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]);
2017-12-02 19:39:07 +01:00
DestroySpriteAndFreeResources(sprite);
if (gMain.inBattle)
2018-03-01 00:59:52 +01:00
gBattleSpritesDataPtr->healthBoxesData[battlerId].ballAnimActive = 0;
2017-12-02 19:39:07 +01:00
}
}
2017-12-02 21:27:00 +01:00
static void SpriteCB_PlayerMonSendOut_1(struct Sprite *sprite)
2017-12-02 19:39:07 +01:00
{
sprite->data[0] = 25;
2021-11-08 19:18:58 +01:00
sprite->data[2] = GetBattlerSpriteCoord(sprite->sBattler, BATTLER_COORD_X_2);
sprite->data[4] = GetBattlerSpriteCoord(sprite->sBattler, BATTLER_COORD_Y_PIC_OFFSET) + 24;
2017-12-02 19:39:07 +01:00
sprite->data[5] = -30;
2018-03-01 00:59:52 +01:00
sprite->oam.affineParam = sprite->sBattler;
2018-11-26 00:00:18 +01:00
InitAnimArcTranslation(sprite);
2017-12-02 19:39:07 +01:00
sprite->callback = SpriteCB_PlayerMonSendOut_2;
}
2017-12-02 20:38:26 +01:00
#define HIBYTE(x) (((x) >> 8) & 0xFF)
2017-12-02 21:27:00 +01:00
static void SpriteCB_PlayerMonSendOut_2(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
u32 r6;
u32 r7;
if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80)
{
s16 r4;
if ((sprite->oam.affineParam & 0xFF00) == 0)
{
r6 = sprite->data[1] & 1;
r7 = sprite->data[2] & 1;
sprite->data[1] = ((sprite->data[1] / 3) & ~1) | r6;
sprite->data[2] = ((sprite->data[2] / 3) & ~1) | r7;
StartSpriteAffineAnim(sprite, 4);
}
r4 = sprite->data[0];
2018-12-20 04:13:26 +01:00
AnimTranslateLinear(sprite);
2018-03-01 00:59:52 +01:00
sprite->data[7] += sprite->sBattler / 3;
2021-07-07 15:11:52 +02:00
sprite->y2 += Sin(HIBYTE(sprite->data[7]), sprite->data[5]);
2017-12-02 20:38:26 +01:00
sprite->oam.affineParam += 0x100;
if ((sprite->oam.affineParam >> 8) % 3 != 0)
sprite->data[0] = r4;
else
sprite->data[0] = r4 - 1;
if (HIBYTE(sprite->data[7]) >= 80)
{
r6 = sprite->data[1] & 1;
r7 = sprite->data[2] & 1;
sprite->data[1] = ((sprite->data[1] * 3) & ~1) | r6;
sprite->data[2] = ((sprite->data[2] * 3) & ~1) | r7;
}
}
else
{
2019-02-06 20:17:09 +01:00
if (TranslateAnimHorizontalArc(sprite))
2017-12-02 20:38:26 +01:00
{
2021-07-07 15:11:52 +02:00
sprite->x += sprite->x2;
sprite->y += sprite->y2;
sprite->y2 = 0;
sprite->x2 = 0;
2018-03-01 00:59:52 +01:00
sprite->sBattler = sprite->oam.affineParam & 0xFF;
2017-12-02 20:38:26 +01:00
sprite->data[0] = 0;
2021-01-23 02:03:21 +01:00
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
2018-03-01 00:59:52 +01:00
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT))
2017-12-02 20:38:26 +01:00
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
StartSpriteAffineAnim(sprite, 0);
}
}
}
2017-12-02 21:27:00 +01:00
static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
if (sprite->data[0]++ > 24)
{
sprite->data[0] = 0;
sprite->callback = SpriteCB_ReleaseMonFromBall;
}
}
2017-12-02 21:27:00 +01:00
static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
sprite->data[0]++;
if (sprite->data[0] > 15)
{
sprite->data[0] = 0;
2021-01-23 02:03:21 +01:00
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
2018-03-01 00:59:52 +01:00
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))
2017-12-02 20:38:26 +01:00
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
}
}
2018-03-01 00:59:52 +01:00
#undef sBattler
2017-12-02 20:38:26 +01:00
2022-05-21 21:21:50 +02:00
static u8 AnimateBallOpenParticlesForPokeball(u8 x, u8 y, u8 kindOfStars, u8 subpriority)
2017-12-02 20:38:26 +01:00
{
2022-05-21 21:21:50 +02:00
return AnimateBallOpenParticles(x, y, kindOfStars, subpriority, BALL_POKE);
2017-12-02 20:38:26 +01:00
}
2022-06-01 18:41:57 +02:00
static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 spritePalNum, u32 selectedPalettes)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
return LaunchBallFadeMonTask(unFadeLater, spritePalNum, selectedPalettes, BALL_POKE);
2017-12-02 20:38:26 +01:00
}
2022-06-01 18:41:57 +02:00
// Sprite data for the pokemon
#define sSpecies data[7]
// Sprite data for the pokeball
#define sMonSpriteId data[0]
#define sDelay data[1]
#define sMonPalNum data[2]
#define sFadePalsLo data[3]
#define sFadePalsHi data[4]
#define sFinalMonX data[5]
#define sFinalMonY data[6]
#define sTrigIdx data[7]
2021-01-23 05:22:37 +01:00
// Pokeball in Birch intro, and when receiving via trade
2022-06-01 18:41:57 +02:00
void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species)
2017-12-02 20:38:26 +01:00
{
u8 spriteId;
2022-06-01 18:41:57 +02:00
LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[BALL_POKE]);
LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[BALL_POKE]);
spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriortiy);
2017-12-02 20:38:26 +01:00
2022-06-01 18:41:57 +02:00
gSprites[spriteId].sMonSpriteId = monSpriteId;
gSprites[spriteId].sFinalMonX = gSprites[monSpriteId].x;
gSprites[spriteId].sFinalMonY = gSprites[monSpriteId].y;
2017-12-02 20:38:26 +01:00
2021-07-07 15:11:52 +02:00
gSprites[monSpriteId].x = x;
gSprites[monSpriteId].y = y;
2022-06-01 18:41:57 +02:00
gSprites[monSpriteId].sSpecies = species;
2017-12-02 20:38:26 +01:00
2022-06-01 18:41:57 +02:00
gSprites[spriteId].sDelay = delay;
gSprites[spriteId].sMonPalNum = monPalNum;
gSprites[spriteId].sFadePalsLo = fadePalettes;
gSprites[spriteId].sFadePalsHi = fadePalettes >> 16;
2017-12-02 20:38:26 +01:00
gSprites[spriteId].oam.priority = oamPriority;
2021-01-23 05:22:37 +01:00
gSprites[spriteId].callback = SpriteCB_PokeballReleaseMon;
2017-12-02 20:38:26 +01:00
gSprites[monSpriteId].invisible = TRUE;
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_PokeballReleaseMon(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
if (sprite->sDelay == 0)
2017-12-02 20:38:26 +01:00
{
2022-05-21 21:21:50 +02:00
u8 subpriority;
2022-06-01 18:41:57 +02:00
u8 spriteId = sprite->sMonSpriteId;
u8 monPalNum = sprite->sMonPalNum;
u32 selectedPalettes = (u16)sprite->sFadePalsLo | ((u16)sprite->sFadePalsHi << 16);
2017-12-02 20:38:26 +01:00
if (sprite->subpriority != 0)
2022-05-21 21:21:50 +02:00
subpriority = sprite->subpriority - 1;
2017-12-02 20:38:26 +01:00
else
2022-05-21 21:21:50 +02:00
subpriority = 0;
2017-12-02 20:38:26 +01:00
StartSpriteAnim(sprite, 1);
2022-05-21 21:21:50 +02:00
AnimateBallOpenParticlesForPokeball(sprite->x, sprite->y - 5, sprite->oam.priority, subpriority);
2022-06-01 18:41:57 +02:00
// sDelay re-used to store task id but never read
2022-07-25 20:59:14 +02:00
sprite->sDelay = LaunchBallFadeMonTaskForPokeball(TRUE, monPalNum, selectedPalettes);
2021-01-23 05:22:37 +01:00
sprite->callback = SpriteCB_ReleasedMonFlyOut;
2022-05-21 21:21:50 +02:00
gSprites[spriteId].invisible = FALSE;
StartSpriteAffineAnim(&gSprites[spriteId], BATTLER_AFFINE_EMERGE);
AnimateSprite(&gSprites[spriteId]);
gSprites[spriteId].data[1] = 0x1000;
2022-06-01 18:41:57 +02:00
sprite->sTrigIdx = 0;
2017-12-02 20:38:26 +01:00
}
else
{
2022-06-01 18:41:57 +02:00
sprite->sDelay--;
2017-12-02 20:38:26 +01:00
}
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_ReleasedMonFlyOut(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
bool8 emergeAnimFinished = FALSE;
bool8 atFinalPosition = FALSE;
u8 monSpriteId = sprite->sMonSpriteId;
u16 x, y;
2017-12-02 20:38:26 +01:00
if (sprite->animEnded)
sprite->invisible = TRUE;
2022-06-01 18:41:57 +02:00
2017-12-02 20:38:26 +01:00
if (gSprites[monSpriteId].affineAnimEnded)
{
StartSpriteAffineAnim(&gSprites[monSpriteId], BATTLER_AFFINE_NORMAL);
2022-06-01 18:41:57 +02:00
emergeAnimFinished = TRUE;
2017-12-02 20:38:26 +01:00
}
2022-06-01 18:41:57 +02:00
x = (sprite->sFinalMonX - sprite->x) * sprite->sTrigIdx / 128 + sprite->x;
y = (sprite->sFinalMonY - sprite->y) * sprite->sTrigIdx / 128 + sprite->y;
gSprites[monSpriteId].x = x;
gSprites[monSpriteId].y = y;
if (sprite->sTrigIdx < 128)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
s16 sine = -(gSineTable[(u8)sprite->sTrigIdx] / 8);
2017-12-02 20:38:26 +01:00
2022-06-01 18:41:57 +02:00
sprite->sTrigIdx += 4;
2021-07-07 15:11:52 +02:00
gSprites[monSpriteId].x2 = sine;
gSprites[monSpriteId].y2 = sine;
2017-12-02 20:38:26 +01:00
}
else
{
2022-06-01 18:41:57 +02:00
gSprites[monSpriteId].x = sprite->sFinalMonX;
gSprites[monSpriteId].y = sprite->sFinalMonY;
2021-07-07 15:11:52 +02:00
gSprites[monSpriteId].x2 = 0;
gSprites[monSpriteId].y2 = 0;
2022-06-01 18:41:57 +02:00
atFinalPosition = TRUE;
2017-12-02 20:38:26 +01:00
}
2022-06-01 18:41:57 +02:00
if (sprite->animEnded && emergeAnimFinished && atFinalPosition)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
if (gSprites[monSpriteId].sSpecies == SPECIES_EGG)
DoMonFrontSpriteAnimation(&gSprites[monSpriteId], gSprites[monSpriteId].sSpecies, TRUE, 0);
2017-12-02 20:38:26 +01:00
else
2022-06-01 18:41:57 +02:00
DoMonFrontSpriteAnimation(&gSprites[monSpriteId], gSprites[monSpriteId].sSpecies, FALSE, 0);
2017-12-02 20:38:26 +01:00
DestroySpriteAndFreeResources(sprite);
}
}
2022-06-01 18:41:57 +02:00
#undef sSpecies
#undef sFinalMonX
#undef sFinalMonY
#undef sTrigIdx
#define sTimer data[5]
u8 CreateTradePokeballSprite(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subPriority, u8 delay, u32 fadePalettes)
2017-12-02 20:38:26 +01:00
{
u8 spriteId;
2022-06-01 18:41:57 +02:00
LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[BALL_POKE]);
LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[BALL_POKE]);
spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subPriority);
gSprites[spriteId].sMonSpriteId = monSpriteId;
gSprites[spriteId].sDelay = delay;
gSprites[spriteId].sMonPalNum = monPalNum;
gSprites[spriteId].sFadePalsLo = fadePalettes;
gSprites[spriteId].sFadePalsHi = fadePalettes >> 16;
2017-12-02 20:38:26 +01:00
gSprites[spriteId].oam.priority = oamPriority;
2021-01-23 05:22:37 +01:00
gSprites[spriteId].callback = SpriteCB_TradePokeball;
2017-12-02 20:38:26 +01:00
return spriteId;
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_TradePokeball(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2022-06-01 18:41:57 +02:00
if (sprite->sDelay == 0)
2017-12-02 20:38:26 +01:00
{
2022-05-21 21:21:50 +02:00
u8 subpriority;
2022-06-01 18:41:57 +02:00
u8 monSpriteId = sprite->sMonSpriteId;
u8 monPalNum = sprite->sMonPalNum;
u32 selectedPalettes = (u16)sprite->sFadePalsLo | ((u16)sprite->sFadePalsHi << 16);
2017-12-02 20:38:26 +01:00
if (sprite->subpriority != 0)
2022-05-21 21:21:50 +02:00
subpriority = sprite->subpriority - 1;
2017-12-02 20:38:26 +01:00
else
2022-05-21 21:21:50 +02:00
subpriority = 0;
2017-12-02 20:38:26 +01:00
StartSpriteAnim(sprite, 1);
2022-05-21 21:21:50 +02:00
AnimateBallOpenParticlesForPokeball(sprite->x, sprite->y - 5, sprite->oam.priority, subpriority);
2022-06-01 18:41:57 +02:00
// sDelay re-used to store task id but never read
2022-07-25 20:59:14 +02:00
sprite->sDelay = LaunchBallFadeMonTaskForPokeball(TRUE, monPalNum, selectedPalettes);
2021-01-23 05:22:37 +01:00
sprite->callback = SpriteCB_TradePokeballSendOff;
#ifdef BUGFIX
// FIX: If this is used on a sprite that has previously had an affine animation, it will not
2021-11-17 21:47:47 +01:00
// play the shrink anim properly due to being paused. Works together with the fix to ResetSpriteAfterAnim.
2021-01-23 05:22:37 +01:00
gSprites[monSpriteId].affineAnimPaused = FALSE;
#endif // BUGFIX
StartSpriteAffineAnim(&gSprites[monSpriteId], BATTLER_AFFINE_RETURN);
2021-01-23 05:22:37 +01:00
AnimateSprite(&gSprites[monSpriteId]);
gSprites[monSpriteId].data[1] = 0;
2017-12-02 20:38:26 +01:00
}
else
{
2022-06-01 18:41:57 +02:00
sprite->sDelay--;
2017-12-02 20:38:26 +01:00
}
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_TradePokeballSendOff(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2021-01-23 05:22:37 +01:00
u8 monSpriteId;
2017-12-02 20:38:26 +01:00
2022-06-01 18:41:57 +02:00
sprite->sTimer++;
if (sprite->sTimer == 11)
2020-08-21 00:02:00 +02:00
PlaySE(SE_BALL_TRADE);
2022-06-01 18:41:57 +02:00
monSpriteId = sprite->sMonSpriteId;
2021-01-23 05:22:37 +01:00
if (gSprites[monSpriteId].affineAnimEnded)
2017-12-02 20:38:26 +01:00
{
StartSpriteAnim(sprite, 2);
2021-01-23 05:22:37 +01:00
gSprites[monSpriteId].invisible = TRUE;
2022-06-01 18:41:57 +02:00
sprite->sTimer = 0;
2021-01-23 05:22:37 +01:00
sprite->callback = SpriteCB_TradePokeballEnd;
2017-12-02 20:38:26 +01:00
}
else
{
2021-01-23 05:22:37 +01:00
gSprites[monSpriteId].data[1] += 96;
2021-07-07 15:11:52 +02:00
gSprites[monSpriteId].y2 = -gSprites[monSpriteId].data[1] >> 8;
2017-12-02 20:38:26 +01:00
}
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_TradePokeballEnd(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
if (sprite->animEnded)
sprite->callback = SpriteCallbackDummy;
}
2022-06-01 18:41:57 +02:00
#undef sMonSpriteId
#undef sDelay
#undef sMonPalNum
#undef sFadePalsLo
#undef sFadePalsHi
#undef sTimer
2018-12-30 17:58:42 +01:00
static void Unref_DestroySpriteAndFreeResources(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
DestroySpriteAndFreeResources(sprite);
}
2021-01-23 05:22:37 +01:00
#define sSpeedX data[0]
#define sSpeedY data[1]
#define sDelayTimer data[1]
2021-01-23 02:03:21 +01:00
void StartHealthboxSlideIn(u8 battlerId)
2017-12-02 20:38:26 +01:00
{
2018-03-01 00:59:52 +01:00
struct Sprite *healthboxSprite = &gSprites[gHealthboxSpriteIds[battlerId]];
2017-12-02 20:38:26 +01:00
2021-01-23 05:22:37 +01:00
healthboxSprite->sSpeedX = 5;
healthboxSprite->sSpeedY = 0;
2021-07-07 15:11:52 +02:00
healthboxSprite->x2 = 0x73;
healthboxSprite->y2 = 0;
2021-01-23 05:22:37 +01:00
healthboxSprite->callback = SpriteCB_HealthboxSlideIn;
2018-03-01 00:59:52 +01:00
if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
2017-12-02 20:38:26 +01:00
{
2021-01-23 05:22:37 +01:00
healthboxSprite->sSpeedX = -healthboxSprite->sSpeedX;
healthboxSprite->sSpeedY = -healthboxSprite->sSpeedY;
2021-07-07 15:11:52 +02:00
healthboxSprite->x2 = -healthboxSprite->x2;
healthboxSprite->y2 = -healthboxSprite->y2;
2017-12-02 20:38:26 +01:00
}
gSprites[healthboxSprite->data[5]].callback(&gSprites[healthboxSprite->data[5]]);
2018-03-01 00:59:52 +01:00
if (GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)
2021-01-23 05:22:37 +01:00
healthboxSprite->callback = SpriteCB_HealthboxSlideInDelayed;
2017-12-02 20:38:26 +01:00
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_HealthboxSlideInDelayed(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2021-01-23 05:22:37 +01:00
sprite->sDelayTimer++;
if (sprite->sDelayTimer == 20)
2017-12-02 20:38:26 +01:00
{
2021-01-23 05:22:37 +01:00
sprite->sDelayTimer = 0;
sprite->callback = SpriteCB_HealthboxSlideIn;
2017-12-02 20:38:26 +01:00
}
}
2021-01-23 05:22:37 +01:00
static void SpriteCB_HealthboxSlideIn(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
2021-07-07 15:11:52 +02:00
sprite->x2 -= sprite->sSpeedX;
sprite->y2 -= sprite->sSpeedY;
if (sprite->x2 == 0 && sprite->y2 == 0)
2017-12-02 20:38:26 +01:00
sprite->callback = SpriteCallbackDummy;
}
2021-01-23 05:22:37 +01:00
#undef sSpeedX
#undef sSpeedY
#undef sDelayTimer
2018-03-01 00:59:52 +01:00
void DoHitAnimHealthboxEffect(u8 battlerId)
2017-12-02 20:38:26 +01:00
{
u8 spriteId;
spriteId = CreateInvisibleSpriteWithCallback(SpriteCB_HitAnimHealthoxEffect);
gSprites[spriteId].data[0] = 1;
2018-03-01 00:59:52 +01:00
gSprites[spriteId].data[1] = gHealthboxSpriteIds[battlerId];
2017-12-02 20:38:26 +01:00
gSprites[spriteId].callback = SpriteCB_HitAnimHealthoxEffect;
}
2017-12-02 21:27:00 +01:00
static void SpriteCB_HitAnimHealthoxEffect(struct Sprite *sprite)
2017-12-02 20:38:26 +01:00
{
u8 r1 = sprite->data[1];
2021-07-07 15:11:52 +02:00
gSprites[r1].y2 = sprite->data[0];
2017-12-02 20:38:26 +01:00
sprite->data[0] = -sprite->data[0];
sprite->data[2]++;
if (sprite->data[2] == 21)
{
2021-07-07 15:11:52 +02:00
gSprites[r1].x2 = 0;
gSprites[r1].y2 = 0;
2017-12-02 20:38:26 +01:00
DestroySprite(sprite);
}
}
void LoadBallGfx(u8 ballId)
{
u16 var;
2017-12-02 21:27:00 +01:00
if (GetSpriteTileStartByTag(gBallSpriteSheets[ballId].tag) == 0xFFFF)
2017-12-02 20:38:26 +01:00
{
2018-12-17 23:00:08 +01:00
LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[ballId]);
LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[ballId]);
2017-12-02 20:38:26 +01:00
}
switch (ballId)
{
case BALL_DIVE:
case BALL_LUXURY:
case BALL_PREMIER:
break;
default:
2017-12-02 21:27:00 +01:00
var = GetSpriteTileStartByTag(gBallSpriteSheets[ballId].tag);
2021-03-30 21:07:04 +02:00
LZDecompressVram(gOpenPokeballGfx, (void *)(OBJ_VRAM0 + 0x100 + var * 32));
2017-12-02 20:38:26 +01:00
break;
}
}
void FreeBallGfx(u8 ballId)
{
2017-12-02 21:27:00 +01:00
FreeSpriteTilesByTag(gBallSpriteSheets[ballId].tag);
FreeSpritePaletteByTag(gBallSpritePalettes[ballId].tag);
2017-12-02 20:38:26 +01:00
}
2018-03-01 00:59:52 +01:00
static u16 GetBattlerPokeballItemId(u8 battlerId)
2017-12-02 20:38:26 +01:00
{
2018-03-01 00:59:52 +01:00
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL);
2017-12-02 20:38:26 +01:00
else
2018-03-01 00:59:52 +01:00
return GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL);
2017-12-02 20:38:26 +01:00
}