From df8cd2d0f3eed24a2ca5c2fb84e7fcd35783173b Mon Sep 17 00:00:00 2001 From: AZero13 <83477269+SiliconA-Z@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:47:51 -0400 Subject: [PATCH] Ice documentation Some better descriptions of variables and arguments and what they do. --- src/battle_anim_ice.c | 190 +++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 102 deletions(-) diff --git a/src/battle_anim_ice.c b/src/battle_anim_ice.c index 6b48215a7a..c612fba33e 100644 --- a/src/battle_anim_ice.c +++ b/src/battle_anim_ice.c @@ -13,11 +13,16 @@ #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; @@ -625,9 +630,9 @@ static void AnimIceBeamParticle(struct Sprite *sprite) // 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 -// arg 1: target y offset -// arg 2: ??? unknown boolean +// 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 0–1). static void AnimIceEffectParticle(struct Sprite *sprite) { if (gBattleAnimArgs[2] == 0) @@ -661,8 +666,8 @@ static void AnimFlickerIceEffectParticle(struct Sprite *sprite) // arg 1: initial y pixel offset // arg 2: target x offset // arg 3: target y offset -// arg 4: particle speed -// arg 5: multiple targets? (boolean) +// 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; @@ -710,8 +715,7 @@ static void AnimSwirlingSnowball(struct Sprite *sprite) sprite->x += sprite->x2; sprite->y += sprite->y2; - sprite->y2 = 0; - sprite->x2 = 0; + sprite->x2 = sprite->y2 = 0; for (i = 0; i < 8; i++) sprite->data[i] = tempDataHolder[i]; @@ -722,17 +726,16 @@ static void AnimSwirlingSnowball(struct Sprite *sprite) static void AnimSwirlingSnowball_Step1(struct Sprite *sprite) { - s16 tempVar; + s16 battlerSide; sprite->x += sprite->x2; sprite->y += sprite->y2; - sprite->y2 = 0; - sprite->x2 = 0; + sprite->x2 = sprite->y2 = 0; sprite->data[0] = 128; - tempVar = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20; + battlerSide = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20; - sprite->data[3] = Sin(sprite->data[0], tempVar); + sprite->data[3] = Sin(sprite->data[0], battlerSide); sprite->data[4] = Cos(sprite->data[0], 0xF); sprite->data[5] = 0; sprite->callback = AnimSwirlingSnowball_Step2; @@ -741,24 +744,22 @@ static void AnimSwirlingSnowball_Step1(struct Sprite *sprite) static void AnimSwirlingSnowball_Step2(struct Sprite *sprite) { - s16 tempVar; - tempVar = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20; + s16 battlerSide; + battlerSide = GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER ? 20 : -20; if (sprite->data[5] <= 31) { - sprite->x2 = Sin(sprite->data[0], tempVar) - sprite->data[3]; + 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] += 1; + sprite->data[5]++; } else { sprite->x += sprite->x2; sprite->y += sprite->y2; - sprite->y2 = 0; - sprite->x2 = 0; - sprite->data[4] = 0; - sprite->data[3] = 0; + sprite->x2 = sprite->y2 = 0; + sprite->data[3] = sprite->data[4] = 0; sprite->callback = AnimSwirlingSnowball_End; } } @@ -775,16 +776,19 @@ static void AnimSwirlingSnowball_End(struct Sprite *sprite) DestroyAnimSprite(sprite); } -// Moves particles towards the target mon and off the screen. Used to animate -// the large snowballs in Blizzard and the small snowballs in Powder Snow. +// 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 -// arg 5: wave amplitude -// arg 6: wave frequency -// arg 7: multiple targets? (boolean) +// 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; @@ -832,8 +836,7 @@ static void AnimMoveParticleBeyondTarget(struct Sprite *sprite) sprite->x += sprite->x2; sprite->y += sprite->y2; - sprite->y2 = 0; - sprite->x2 = 0; + sprite->x2 = sprite->y2 = 0; for (i = 0; i < 8; i++) sprite->data[i] = tempDataHolder[i]; @@ -862,10 +865,10 @@ static void AnimWiggleParticleTowardsTarget(struct Sprite *sprite) } } -// Animates the ice pilar wave used by Icy Wind. -// arg 0: initial x pixel offset -// arg 1: initial y pixel offset -// arg 2: ??? unknown boolean +// 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) @@ -894,18 +897,16 @@ static void AnimWaveFromCenterOfTarget(struct Sprite *sprite) } } -// Animates the fog that swirls around the mon in Mist and Smog. -// arg 0: initial x pixel offset +// 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: change in y pixels per rotation -// arg 3: duration -// arg 4: animate on opponent? (boolean) -// arg 5: ??? unknown boolean +// 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) { - s16 tempVar; - u8 battler; - if (gBattleAnimArgs[4] == 0) { if (gBattleAnimArgs[5] == 0) @@ -923,7 +924,7 @@ static void InitSwirlingFogAnim(struct Sprite *sprite) sprite->y += gBattleAnimArgs[1]; } - battler = gBattleAnimAttacker; + sprite->data[7] = gBattleAnimAttacker; } else { @@ -942,16 +943,14 @@ static void InitSwirlingFogAnim(struct Sprite *sprite) sprite->y += gBattleAnimArgs[1]; } - battler = gBattleAnimTarget; + sprite->data[7] = gBattleAnimTarget; } - sprite->data[7] = battler; if (gBattleAnimArgs[5] == 0 || !IsDoubleBattle()) - tempVar = 0x20; + sprite->data[6] = 0x20; else - tempVar = 0x40; + sprite->data[6] = 0x40; - sprite->data[6] = tempVar; if (GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER) sprite->y += 8; @@ -976,7 +975,7 @@ static void AnimSwirlingFogAnim(struct Sprite *sprite) sprite->x2 += Sin(sprite->data[5], sprite->data[6]); sprite->y2 += Cos(sprite->data[5], -6); - if ((u16)(sprite->data[5] - 64) <= 0x7F) + 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; @@ -996,6 +995,7 @@ void AnimTask_HazeScrollingFog(u8 taskId) 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); @@ -1080,15 +1080,12 @@ static void AnimTask_HazeScrollingFog_Step(u8 taskId) } } -// Throws the ball in Mist Ball. -// arg 0: initial x pixel offset -// arg 1: initial y pixel offset -// arg 2: targey x offset -// arg 3: target y offset -// arg 4: duration -// arg 5: ??? unknown (seems to vibrate target mon somehow) +// Mist Ball projectile: one frame sets start on the attacker, then TranslateAnimSpriteToTargetMonLocation +// runs the move to the target. Args 0–5 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; @@ -1182,15 +1179,14 @@ static void AnimTask_MistBallFog_Step(u8 taskId) } } -// Initializes gas clouds in the Poison Gas animation. -// arg 0: duration -// arg 1: ? target x offset -// arg 2: ? target y offset -// arg 3: ? swirl start x -// arg 4: ? swirl start y -// arg 5: ??? unknown -// arg 6: ??? unknown -// arg 7: ??? unknown boolean +// 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]; @@ -1240,14 +1236,11 @@ static void InitPoisonGasCloudAnim(struct Sprite *sprite) static void MovePoisonGasCloud(struct Sprite *sprite) { - int value; - switch (sprite->data[7] & 0xFF) { case 0: AnimTranslateLinear(sprite); - value = gSineTable[sprite->data[5]]; - sprite->x2 += value >> 4; + sprite->x2 += (16 * gSineTable[sprite->data[5]]) >> 8; if (sprite->data[6]) sprite->data[5] = (sprite->data[5] - 8) & 0xFF; else @@ -1271,21 +1264,18 @@ static void MovePoisonGasCloud(struct Sprite *sprite) sprite->data[5] = 80; sprite->y2 = 0; - value = gSineTable[sprite->data[5]]; - sprite->x2 = value >> 3; + sprite->x2 = (gSineTable[sprite->data[5]] * 32) >> 8; sprite->data[5] = (sprite->data[5] + 2) & 0xFF; InitAnimLinearTranslation(sprite); } break; case 1: AnimTranslateLinear(sprite); - value = gSineTable[sprite->data[5]]; - sprite->x2 += value >> 3; + sprite->x2 += (32 * gSineTable[sprite->data[5]]) >> 8; sprite->y2 += (gSineTable[sprite->data[5] + 0x40] * -3) >> 8; if (!IsContest()) { - u16 var0 = sprite->data[5] - 0x40; - if (var0 <= 0x7F) + 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; @@ -1294,8 +1284,7 @@ static void MovePoisonGasCloud(struct Sprite *sprite) } else { - u16 var0 = sprite->data[5] - 0x40; - if (var0 <= 0x7F) + if (sprite->data[5] > 63 && sprite->data[5] < 192) sprite->subpriority = 128; else sprite->subpriority = 140; @@ -1411,15 +1400,17 @@ static void AnimTask_Hail2(u8 taskId) #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; - s8 type = sHailCoordData[hailStructId].type; - if (type != HAILSTRUCTTYPE_FIXED_POSITION) + if (sHailCoordData[hailStructId].type != HAILSTRUCTTYPE_FIXED_POSITION) { id = GetBattlerAtPosition(sHailCoordData[hailStructId].bPosition); if (IsBattlerSpriteVisible(id)) @@ -1427,7 +1418,7 @@ static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, shouldSpawnImpactEffect = TRUE; battlerX = GetBattlerSpriteCoord(id, BATTLER_COORD_X_2); battlerY = GetBattlerSpriteCoord(id, BATTLER_COORD_Y_PIC_OFFSET); - switch (type) + switch (sHailCoordData[hailStructId].type) { case HAILSTRUCTTYPE_NEGATIVE_POS_MOD: battlerX -= GetBattlerSpriteCoordAttr(id, BATTLER_COORD_ATTR_WIDTH) / 6; @@ -1456,23 +1447,19 @@ static bool8 GenerateHailParticle(u8 hailStructId, u8 affineAnimNum, u8 taskId, { return FALSE; } - else - { - 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; - } + + 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) { - u8 spriteId; - sprite->x += 4; sprite->y += 8; @@ -1481,11 +1468,10 @@ static void AnimHailBegin(struct Sprite *sprite) if (sprite->sSpawnImpactEffect == 1 && sprite->sAffineAnimNum == 0) { - spriteId = CreateSprite(&gIceCrystalHitLargeSpriteTemplate, + sprite->data[0] = CreateSprite(&gIceCrystalHitLargeSpriteTemplate, sprite->sTargetX, sprite->sTargetY, sprite->subpriority); - sprite->data[0] = spriteId; - if (spriteId != MAX_SPRITES) + 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 @@ -1573,19 +1559,19 @@ static void AnimThrowIceBall(struct Sprite *sprite) // Initializes the particles that scatter at the end of the Ice Ball animation. static void InitIceBallParticle(struct Sprite *sprite) { - s16 randA, randB; + s16 xOffset, yOffset; sprite->oam.tileNum += 8; InitSpritePosToAnimTarget(sprite, TRUE); - randA = (Random2() & 0xFF) + 256; - randB = Random2() & 0x1FF; + xOffset = (Random2() & 0xFF) + 256; + yOffset = Random2() & 0x1FF; - if (randB > 0xFF) - randB = 256 - randB; + if (yOffset >= 256) + yOffset = 256 - yOffset; - sprite->data[1] = randA; - sprite->data[2] = randB; + sprite->data[1] = xOffset; + sprite->data[2] = yOffset; sprite->callback = AnimIceBallParticle; }