Identified task and sprite fields related to battle weather animations

Named task and sprite fields related to the animations for Hail, Rain Dance, and Sandstorm.
This commit is contained in:
DavidJCobb
2025-04-28 05:39:52 -04:00
parent 66c07f9c33
commit 35b33a46e9
3 changed files with 172 additions and 89 deletions
+90 -48
View File
@@ -13,11 +13,16 @@
#include "constants/battle_anim.h" #include "constants/battle_anim.h"
#include "constants/rgb.h" #include "constants/rgb.h"
enum {
HAILSTRUCTTYPE_NEGATIVE_POS_MOD = 0,
HAILSTRUCTTYPE_POSITIVE_POS_MOD = 1,
HAILSTRUCTTYPE_FIXED_POSITION = 2,
};
struct HailStruct { struct HailStruct {
s32 x:10; s32 x:10;
s32 y:10; s32 y:10;
s32 bPosition:8; s32 bPosition:8;
s32 unk3:4; s32 type:4;
}; };
static void AnimUnusedIceCrystalThrow(struct Sprite *); static void AnimUnusedIceCrystalThrow(struct Sprite *);
@@ -371,16 +376,16 @@ const struct SpriteTemplate gPoisonGasCloudSpriteTemplate =
static const struct HailStruct sHailCoordData[] = static const struct HailStruct sHailCoordData[] =
{ {
{.x = 100, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .unk3 = 2}, {.x = 100, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_FIXED_POSITION},
{.x = 85, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .unk3 = 0}, {.x = 85, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 242, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .unk3 = 1}, {.x = 242, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 66, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .unk3 = 1}, {.x = 66, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 182, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .unk3 = 0}, {.x = 182, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 60, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .unk3 = 2}, {.x = 60, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_FIXED_POSITION},
{.x = 214, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .unk3 = 0}, {.x = 214, .y = 120, .bPosition = B_POSITION_OPPONENT_LEFT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
{.x = 113, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .unk3 = 1}, {.x = 113, .y = 120, .bPosition = B_POSITION_PLAYER_LEFT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 210, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .unk3 = 1}, {.x = 210, .y = 120, .bPosition = B_POSITION_OPPONENT_RIGHT, .type = HAILSTRUCTTYPE_POSITIVE_POS_MOD},
{.x = 38, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .unk3 = 0}, {.x = 38, .y = 120, .bPosition = B_POSITION_PLAYER_RIGHT, .type = HAILSTRUCTTYPE_NEGATIVE_POS_MOD},
}; };
static const union AffineAnimCmd sAffineAnim_HailParticle_0[] = static const union AffineAnimCmd sAffineAnim_HailParticle_0[] =
@@ -1332,6 +1337,13 @@ static void MovePoisonGasCloud(struct Sprite *sprite)
} }
} }
#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) void AnimTask_Hail(u8 taskId)
{ {
struct Task *task = &gTasks[taskId]; struct Task *task = &gTasks[taskId];
@@ -1342,71 +1354,86 @@ void AnimTask_Hail(u8 taskId)
static void AnimTask_Hail2(u8 taskId) static void AnimTask_Hail2(u8 taskId)
{ {
struct Task *task = &gTasks[taskId]; struct Task *task = &gTasks[taskId];
switch (task->data[0]) switch (task->tState)
{ {
case 0: case 0:
if (++task->data[4] > 2) if (++task->tInitialDelayTimer > 2)
{ {
task->data[4] = 0; task->tInitialDelayTimer = 0;
task->data[5] = 0; task->tHailSpawnTimer = 0;
task->data[2] = 0; task->tHailAffineAnimNum = 0;
task->data[0]++; task->tState++;
} }
break; break;
case 1: case 1:
if (task->data[5] == 0) if (task->tHailSpawnTimer == 0)
{ {
if (GenerateHailParticle(task->data[3], task->data[2], taskId, 1)) if (GenerateHailParticle(task->tHailStructId, task->tHailAffineAnimNum, taskId, 1))
task->data[1]++; task->tSpriteCount++;
if (++task->data[2] == 3) if (++task->tHailAffineAnimNum == 3) // i.e. ARRAY_COUNT(sAffineAnims_HailParticle)
{ {
if (++task->data[3] == 10) if (++task->tHailStructId == 10) // i.e. ARRAY_COUNT(sHailCoordData)
task->data[0]++; task->tState++;
else else
task->data[0]--; task->tState--;
} }
else else
{ {
task->data[5] = 1; task->tHailSpawnTimer = 1;
} }
} }
else else
{ {
task->data[5]--; task->tHailSpawnTimer--;
} }
break; break;
case 2: case 2:
if (task->data[1] == 0) if (task->tSpriteCount == 0)
DestroyAnimVisualTask(taskId); DestroyAnimVisualTask(taskId);
break; 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]
static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, u8 c) static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, u8 c)
{ {
u8 id; u8 id;
s16 battlerX, battlerY; s16 battlerX, battlerY;
s16 spriteX; s16 spriteX;
bool8 possibleBool = FALSE; bool8 shouldSpawnImpactEffect = FALSE;
s8 unk = sHailCoordData[hailStructId].unk3; s8 type = sHailCoordData[hailStructId].type;
if (unk != 2) if (type != HAILSTRUCTTYPE_FIXED_POSITION)
{ {
id = GetBattlerAtPosition(sHailCoordData[hailStructId].bPosition); id = GetBattlerAtPosition(sHailCoordData[hailStructId].bPosition);
if (IsBattlerSpriteVisible(id)) if (IsBattlerSpriteVisible(id))
{ {
possibleBool = TRUE; shouldSpawnImpactEffect = TRUE;
battlerX = GetBattlerSpriteCoord(id, BATTLER_COORD_X_2); battlerX = GetBattlerSpriteCoord(id, BATTLER_COORD_X_2);
battlerY = GetBattlerSpriteCoord(id, BATTLER_COORD_Y_PIC_OFFSET); battlerY = GetBattlerSpriteCoord(id, BATTLER_COORD_Y_PIC_OFFSET);
switch (unk) switch (type)
{ {
case 0: case HAILSTRUCTTYPE_NEGATIVE_POS_MOD:
battlerX -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6; battlerX -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6;
battlerY -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6; battlerY -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6;
break; break;
case 1: case HAILSTRUCTTYPE_POSITIVE_POS_MOD:
battlerX += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6; battlerX += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6;
battlerY += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6; battlerY += GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_HEIGHT) / 6;
break; break;
@@ -1432,12 +1459,12 @@ static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId,
else else
{ {
StartSpriteAffineAnim(&gSprites[id], affineAnimNum); StartSpriteAffineAnim(&gSprites[id], affineAnimNum);
gSprites[id].data[0] = possibleBool; gSprites[id].sSpawnImpactEffect = shouldSpawnImpactEffect;
gSprites[id].data[3] = battlerX; gSprites[id].sTargetX = battlerX;
gSprites[id].data[4] = battlerY; gSprites[id].sTargetY = battlerY;
gSprites[id].data[5] = affineAnimNum; gSprites[id].sAffineAnimNum = affineAnimNum;
gSprites[id].data[6] = taskId; gSprites[id].sOwnerTaskId = taskId;
gSprites[id].data[7] = c; gSprites[id].sOwnerTaskSpriteCountField = c;
return TRUE; return TRUE;
} }
} }
@@ -1449,20 +1476,23 @@ static void AnimHailBegin(struct Sprite *sprite)
sprite->x += 4; sprite->x += 4;
sprite->y += 8; sprite->y += 8;
if (sprite->x < sprite->data[3] && sprite->y < sprite->data[4]) if (sprite->x < sprite->sTargetX && sprite->y < sprite->sTargetY)
return; return;
if (sprite->data[0] == 1 && sprite->data[5] == 0) if (sprite->sSpawnImpactEffect == 1 && sprite->sAffineAnimNum == 0)
{ {
spriteId = CreateSprite(&gIceCrystalHitLargeSpriteTemplate, spriteId = CreateSprite(&gIceCrystalHitLargeSpriteTemplate,
sprite->data[3], sprite->data[4], sprite->subpriority); sprite->sTargetX, sprite->sTargetY, sprite->subpriority);
sprite->data[0] = spriteId; sprite->data[0] = spriteId;
if (spriteId != MAX_SPRITES) if (spriteId != 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]].callback = AnimHailContinue;
gSprites[sprite->data[0]].data[6] = sprite->data[6]; gSprites[sprite->data[0]].sOwnerTaskId = sprite->sOwnerTaskId;
gSprites[sprite->data[0]].data[7] = sprite->data[7]; gSprites[sprite->data[0]].sOwnerTaskSpriteCountField = sprite->sOwnerTaskSpriteCountField;
} }
FreeOamMatrix(sprite->oam.matrixNum); FreeOamMatrix(sprite->oam.matrixNum);
@@ -1470,22 +1500,34 @@ static void AnimHailBegin(struct Sprite *sprite)
} }
else else
{ {
gTasks[sprite->data[6]].data[sprite->data[7]]--; gTasks[sprite->sOwnerTaskId].data[sprite->sOwnerTaskSpriteCountField]--;
FreeOamMatrix(sprite->oam.matrixNum); FreeOamMatrix(sprite->oam.matrixNum);
DestroySprite(sprite); 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) static void AnimHailContinue(struct Sprite *sprite)
{ {
if (++sprite->data[0] == 20) if (++sprite->sTimer == 20)
{ {
gTasks[sprite->data[6]].data[sprite->data[7]]--; gTasks[sprite->sOwnerTaskId].data[sprite->sOwnerTaskSpriteCountField]--;
FreeOamMatrix(sprite->oam.matrixNum); FreeOamMatrix(sprite->oam.matrixNum);
DestroySprite(sprite); DestroySprite(sprite);
} }
} }
#undef sTimer
#undef sOwnerTaskId
#undef sOwnerTaskSpriteCountField
// Initializes the animation for Ice Ball. // Initializes the animation for Ice Ball.
// arg 0: initial x pixel offset // arg 0: initial x pixel offset
// arg 1: initial y pixel offset // arg 1: initial y pixel offset
+61 -34
View File
@@ -388,6 +388,11 @@ static void AnimParticleInVortex_Step(struct Sprite *sprite)
} }
} }
#define tBlendTimer data[10]
#define tBlend data[11]
#define tFullAlphaTimer data[11] // not a typo; this data field is used for multiple purposes
#define tState data[12]
void AnimTask_LoadSandstormBackground(u8 taskId) void AnimTask_LoadSandstormBackground(u8 taskId)
{ {
int var0; int var0;
@@ -430,45 +435,45 @@ static void AnimTask_LoadSandstormBackground_Step(u8 taskId)
gBattle_BG1_Y += -1; gBattle_BG1_Y += -1;
switch (gTasks[taskId].data[12]) switch (gTasks[taskId].tState)
{ {
case 0: case 0:
if (++gTasks[taskId].data[10] == 4) if (++gTasks[taskId].tBlendTimer == 4)
{ {
gTasks[taskId].data[10] = 0; gTasks[taskId].tBlendTimer = 0;
gTasks[taskId].data[11]++; gTasks[taskId].tBlend++;
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 16 - gTasks[taskId].data[11])); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].tBlend, 16 - gTasks[taskId].tBlend));
if (gTasks[taskId].data[11] == 7) if (gTasks[taskId].tBlend == 7)
{ {
gTasks[taskId].data[12]++; gTasks[taskId].tState++;
gTasks[taskId].data[11] = 0; gTasks[taskId].tFullAlphaTimer = 0;
} }
} }
break; break;
case 1: case 1:
if (++gTasks[taskId].data[11] == 101) if (++gTasks[taskId].tFullAlphaTimer == 101)
{ {
gTasks[taskId].data[11] = 7; gTasks[taskId].tBlend = 7;
gTasks[taskId].data[12]++; gTasks[taskId].tState++;
} }
break; break;
case 2: case 2:
if (++gTasks[taskId].data[10] == 4) if (++gTasks[taskId].tBlendTimer == 4)
{ {
gTasks[taskId].data[10] = 0; gTasks[taskId].tBlendTimer = 0;
gTasks[taskId].data[11]--; gTasks[taskId].tBlend--;
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].data[11], 16 - gTasks[taskId].data[11])); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gTasks[taskId].tBlend, 16 - gTasks[taskId].tBlend));
if (gTasks[taskId].data[11] == 0) if (gTasks[taskId].tBlend == 0)
{ {
gTasks[taskId].data[12]++; gTasks[taskId].tState++;
gTasks[taskId].data[11] = 0; gTasks[taskId].tFullAlphaTimer = 0;
} }
} }
break; break;
case 3: case 3:
GetBattleAnimBg1Data(&animBg); GetBattleAnimBg1Data(&animBg);
ClearBattleAnimBg(animBg.bgId); ClearBattleAnimBg(animBg.bgId);
gTasks[taskId].data[12]++; gTasks[taskId].tState++;
break; break;
case 4: case 4:
if (!IsContest()) if (!IsContest())
@@ -484,21 +489,36 @@ static void AnimTask_LoadSandstormBackground_Step(u8 taskId)
} }
} }
#undef tBlendTimer
#undef tBlend
#undef tFullAlphaTimer
#undef tState
// Animates the sprites that fly diagonally across the screen // Animates the sprites that fly diagonally across the screen
// in Sandstorm and Heat Wave. // in Sandstorm and Heat Wave.
// arg 0: initial y pixel offset
// arg 1: projectile speed #define sState data[0]
// arg 2: y pixel drop #define sVelocityX data[1] // 256ths of a pixel // init'd from gBattleAnimArgs[1]
// arg 3: ??? unknown (possibly a color bit) #define sVelocityY data[2] // 256ths of a pixel // init'd from gBattleAnimArgs[2]
#define sFractionalX data[3] // 256ths of a pixel
#define sFractionalY data[4] // 256ths of a pixel
#define sMirroredX data[5] // init'd from gBattleAnimArgs[3]
// The fields named "velocity" are arguably more like "acceleration,"
// and the fields named "fractional" are arguably more like "velocity."
//
// ...is what I WOULD say if the "fractional" fields weren't AND'd with
// 0xFF after every frame.
static void AnimFlyingSandCrescent(struct Sprite *sprite) static void AnimFlyingSandCrescent(struct Sprite *sprite)
{ {
if (sprite->data[0] == 0) if (sprite->sState == 0)
{ {
if (gBattleAnimArgs[3] != 0 && GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER) if (gBattleAnimArgs[3] != 0 && GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
{ {
sprite->x = DISPLAY_WIDTH + 64; sprite->x = DISPLAY_WIDTH + 64;
gBattleAnimArgs[1] = -gBattleAnimArgs[1]; gBattleAnimArgs[1] = -gBattleAnimArgs[1];
sprite->data[5] = 1; sprite->sMirroredX = 1;
sprite->oam.matrixNum = ST_OAM_HFLIP; sprite->oam.matrixNum = ST_OAM_HFLIP;
} }
else else
@@ -508,18 +528,18 @@ static void AnimFlyingSandCrescent(struct Sprite *sprite)
sprite->y = gBattleAnimArgs[0]; sprite->y = gBattleAnimArgs[0];
SetSubspriteTables(sprite, sFlyingSandSubspriteTable); SetSubspriteTables(sprite, sFlyingSandSubspriteTable);
sprite->data[1] = gBattleAnimArgs[1]; sprite->sVelocityX = gBattleAnimArgs[1];
sprite->data[2] = gBattleAnimArgs[2]; sprite->sVelocityY = gBattleAnimArgs[2];
sprite->data[0]++; sprite->sState++;
} }
else else
{ {
sprite->data[3] += sprite->data[1]; sprite->sFractionalX += sprite->sVelocityX;
sprite->data[4] += sprite->data[2]; sprite->sFractionalY += sprite->sVelocityY;
sprite->x2 += (sprite->data[3] >> 8); sprite->x2 += (sprite->sFractionalX >> 8);
sprite->y2 += (sprite->data[4] >> 8); sprite->y2 += (sprite->sFractionalY >> 8);
sprite->data[3] &= 0xFF; sprite->sFractionalX &= 0xFF;
sprite->data[4] &= 0xFF; sprite->sFractionalY &= 0xFF;
if (sprite->data[5] == 0) if (sprite->data[5] == 0)
{ {
@@ -535,6 +555,13 @@ static void AnimFlyingSandCrescent(struct Sprite *sprite)
} }
} }
#undef sState
#undef sVelocityX
#undef sVelocityY
#undef sFractionalX
#undef sFractionalY
#undef sMirroredX
// Animates the rising rocks in Ancient Power. // Animates the rising rocks in Ancient Power.
// arg 0: initial x pixel offset // arg 0: initial x pixel offset
// arg 1: initial y pixel offset // arg 1: initial y pixel offset
+21 -7
View File
@@ -473,27 +473,37 @@ const struct SpriteTemplate gWeatherBallWaterDownSpriteTemplate =
.callback = AnimWeatherBallDown, .callback = AnimWeatherBallDown,
}; };
#define tRaindropSpawnTimer data[0]
#define tRaindropUnused data[1]
#define tRaindropSpawnInterval data[2]
#define tRaindropSpawnDuration data[3] // number of frames over which we spawn raindrops
void AnimTask_CreateRaindrops(u8 taskId) void AnimTask_CreateRaindrops(u8 taskId)
{ {
u8 x, y; u8 x, y;
if (gTasks[taskId].data[0] == 0) if (gTasks[taskId].tRaindropSpawnTimer == 0)
{ {
gTasks[taskId].data[1] = gBattleAnimArgs[0]; gTasks[taskId].tRaindropUnused = gBattleAnimArgs[0];
gTasks[taskId].data[2] = gBattleAnimArgs[1]; gTasks[taskId].tRaindropSpawnInterval = gBattleAnimArgs[1];
gTasks[taskId].data[3] = gBattleAnimArgs[2]; gTasks[taskId].tRaindropSpawnDuration = gBattleAnimArgs[2];
} }
gTasks[taskId].data[0]++; gTasks[taskId].tRaindropSpawnTimer++;
if (gTasks[taskId].data[0] % gTasks[taskId].data[2] == 1) if (gTasks[taskId].tRaindropSpawnTimer % gTasks[taskId].tRaindropSpawnInterval == 1)
{ {
x = Random2() % DISPLAY_WIDTH; x = Random2() % DISPLAY_WIDTH;
y = Random2() % (DISPLAY_HEIGHT / 2); y = Random2() % (DISPLAY_HEIGHT / 2);
CreateSprite(&gRainDropSpriteTemplate, x, y, 4); CreateSprite(&gRainDropSpriteTemplate, x, y, 4);
} }
if (gTasks[taskId].data[0] == gTasks[taskId].data[3]) if (gTasks[taskId].tRaindropSpawnTimer == gTasks[taskId].tRaindropSpawnDuration)
DestroyAnimVisualTask(taskId); DestroyAnimVisualTask(taskId);
} }
#undef tRaindropSpawnTimer
#undef tRaindropUnused
#undef tRaindropSpawnInterval
#undef tRaindropSpawnDuration
static void AnimRainDrop(struct Sprite *sprite) static void AnimRainDrop(struct Sprite *sprite)
{ {
sprite->callback = AnimRainDrop_Step; sprite->callback = AnimRainDrop_Step;
@@ -503,6 +513,10 @@ static void AnimRainDrop_Step(struct Sprite *sprite)
{ {
if (++sprite->data[0] <= 13) if (++sprite->data[0] <= 13)
{ {
//
// Make the raindrop fall, but only until it reaches the
// impact/splash frames of its animation.
//
sprite->x2++; sprite->x2++;
sprite->y2 += 4; sprite->y2 += 4;
} }