Alchemy
Loading...
Searching...
No Matches

casting_impact.c

Go to the documentation of this file.
1/// @file
2/// The casting impact scene and its image helpers.
3/// Part of effect.c, the battle effect object, compiled only through it.
4
6
7// The casting impacts: their phases and particle use are described in the
8// folder README, under "Effect families".
9
10/// Moves each RGB555 channel of background colours 0 to 63 one level towards
11/// the resource palette. Colour 0 always goes towards black, whatever the
12/// resource holds.
13///
14/// Called once a frame, so a palette arrives in at most 31 frames: by the
15/// casting impacts, Ragnarok, the Titan Blade and Ground (hail.c). Reads
16/// and rewrites background palette RAM.
18 u16 target[64];
19 u16 *color = (u16 *)BG_PLTT;
20 s32 i;
21
22 Iwram_CopyWords(target, Resource_GetTableEntry(resourceId), 128);
23 target[0] = 0;
24 for (i = 0; i != 64; i++, color++) {
25 s32 red = *color & 0x1f;
26 s32 green = (*color >> 5) & 0x1f;
27 s32 blue = (*color >> 10) & 0x1f;
28 s32 targetRed = target[i] & 0x1f;
29 s32 targetGreen = (target[i] >> 5) & 0x1f;
30 s32 targetBlue = (target[i] >> 10) & 0x1f;
31
32 if (red < targetRed)
33 red++;
34 else if (red > targetRed)
35 red--;
36 if (green < targetGreen)
37 green++;
38 else if (green > targetGreen)
39 green--;
40 if (blue < targetBlue)
41 blue++;
42 else if (blue > targetBlue)
43 blue--;
44 target[i] = RGB(red, green, blue);
45 }
46 Iwram_CopyWords((u16 *)BG_PLTT, target, 128);
47}
48
49/// Loads one of the impact's pictures (Resource_LoadAndDecompress).
50static inline void CastingImpact_LoadResource(const void *id, void *dest,
51 s32 skip, s32 copy) {
52 /* FAKEMATCH: inline scope keeps each resource call's arguments local for
53 * BattleFx_RunCastingImpact; with its 24 loads called directly, it differs
54 * throughout in tbs-en. */
55 Resource_LoadAndDecompress((s32)id, dest, skip, copy);
56}
57
58/// Packs a casting impact's picture rows (Graphics_PackTileRows).
59static inline void CastingImpact_PackRows(void *src, void *dest, s32 width,
60 s32 height) {
61 /* FAKEMATCH: keep packing arguments within their own inline scope;
62 * called directly, 202 instructions differ in tbs-en. */
63 Graphics_PackTileRows(src, dest, width, height);
64}
65
66/// Draws one casting impact image with the given blitter.
67static inline void CastingImpact_DrawImage(void *canvas, const void *pixels,
68 s32 x, s32 y, s32 width, s32 height,
69 DrawRectangle *draw) {
70 /* FAKEMATCH: inline scope reloads each image address at its drawing
71 * call; called directly, 938 instructions differ in tbs-en. */
72 (*draw)(canvas, pixels, x, y, width, height);
73}
74
75/// Draws a casting-impact image whose height is twice its width.
76static inline void CastingImpact_DrawTallImage(void *canvas, const void *pixels,
77 s32 x, s32 y, s32 width,
78 DrawRectangle *draw) {
79 /* FAKEMATCH: derive height inside the inline scope so the dimension stores
80 * reuse one register; called directly, 128 instructions differ in tbs-en. */
81 (*draw)(canvas, pixels, x, y, width, width * 2);
82}
83
84/// Draws only the unscrolled height of a casting-impact image.
85static inline void CastingImpact_DrawScrolledImage(void *canvas,
86 const void *pixels, s32 x,
87 s32 y, s32 width, s32 height,
88 s32 scroll,
89 DrawRectangle *draw) {
90 /* FAKEMATCH: subtract the scrolled part inside the drawing scope;
91 * called directly, 60 instructions differ in tbs-en. */
92 (*draw)(canvas, pixels, x, y, width, height - scroll);
93}
94
95/// Draws a casting-impact image cropped to 91 pixels high.
96static inline void CastingImpact_DrawCroppedImage(void *canvas,
97 const void *pixels, s32 x,
98 s32 y, s32 width,
99 DrawRectangle *draw) {
100 /* FAKEMATCH: forwarding inline; the cropped height stays inside its scope.
101 * As direct blitter calls with 91, 16 instructions differ in tbs-en. */
102 (*draw)(canvas, pixels, x, y, width, 91);
103}
104
105/// Clears the prelude image, stops its callbacks and releases its blitters
106/// before the next effect takes over.
107///
108/// Called by BattleFx_RunCastingImpact for the impacts that hand off after
109/// the gathering (15, 17, 24, 26 and Severe Blow); Meltdown's end does the
110/// same inline. Clears the canvas and its VRAM (character block 1) and
111/// work->effect->prelude, removes Palette_StepFadeTransfer and the canvas
112/// transfer, and frees both blitters.
113static inline void CastingImpact_EndPrelude(struct BattleEffectWork *work,
114 void *canvas) {
117 work->effect->prelude = 0;
121}
122
123/// The casting impacts, by kind - 100: each is named for the first ability
124/// whose gBattle_ActionFlags entry selects it; the four no ability selects
125/// keep their numbers.
126///
127/// The trailing comments give each impact's own animation, drawn over the
128/// target after the orb and sparks. Most animations use the 64 particles at
129/// Ram_MapCellBuffer + 0x4000, which start at the target; their pictures are in
130/// Ram_MapCellBuffer. "Smoke" is puffs of smoke rising from the target; "hands
131/// off" impacts stop after gathering and hand the canvas to another effect.
138 CASTING_IMPACT_IMPACT_SCORCH, ///< flash bursts rise from the target
139 CASTING_IMPACT_IMPACT_SQUALL, ///< two bolts strike among glints
140 CASTING_IMPACT_IMPACT_SAP, ///< motes fly back to the caster
141 CASTING_IMPACT_IMPACT_TERRA_STRIKE, ///< a rock spire grows and sinks
144 CASTING_IMPACT_IMPACT_MORTAL_DANGER, ///< also Death Size; a large picture
145 ///< rises and breaks
146 CASTING_IMPACT_IMPACT_BAD_OMEN, ///< skulls rise from the target
147 CASTING_IMPACT_IMPACT_LIFE_NOURISH, ///< motes fly back to the caster
148 CASTING_IMPACT_IMPACT_AQUA_SOCK, ///< a blue flame column falls onto the
149 ///< target
151 CASTING_IMPACT_IMPACT_FROST_BITE, ///< ice chips converge on the target
153 CASTING_IMPACT_IMPACT_LIFE_LEECH, ///< motes fly back to the caster
154 CASTING_IMPACT_IMPACT_PSY_LEECH, ///< motes fly back to the caster
155 CASTING_IMPACT_IMPACT_BROIL, ///< ember streaks spread sideways
156 CASTING_IMPACT_IMPACT_MELTDOWN, ///< nothing; hands to the stat reduction
157 CASTING_IMPACT_IMPACT_HEAT_MIRAGE, ///< also Heat Flash; smoke
158 CASTING_IMPACT_IMPACT_BARRAGE, ///< flash bursts rise from the target
162 CASTING_IMPACT_IMPACT_STUN_VOLTAGE, ///< two bolts strike among glints
165 CASTING_IMPACT_IMPACT_CYCLONE_SLASH, ///< a tornado stands over the target
166 CASTING_IMPACT_IMPACT_PSYPHON_SEAL, ///< a rune opens over the target
167 CASTING_IMPACT_IMPACT_ASURA, ///< a beast scrolls across and breaks
168 CASTING_IMPACT_IMPACT_FORCIBLE_ARM, ///< a flame falls diagonally
169 CASTING_IMPACT_IMPACT_TRUNCHEON_FIST, ///< a flame falls diagonally
171};
172
173/// The impact orb, 20x48 pictures packed at half width.
174#define CASTING_IMPACT_ORB_CELLS 0x5100
175/// The Djinni pictures, 20x40, in Ram_MapCellBuffer after the impact's own.
176#define CASTING_IMPACT_DJINNI_CELLS 0x3c56
177#define CASTING_IMPACT_GATHER_FRAMES 32 ///< frames the sparks gather
178#define CASTING_IMPACT_SPARKS 64 ///< sparks in work->particles
179/// The impact pictures' particles, in Ram_MapCellBuffer after the Djinni.
180#define CASTING_IMPACT_IMPACT_PARTICLES \
181 ((struct EffectStep *)(Ram_MapCellBuffer + 0x4000))
182#define CASTING_IMPACT_IMPACT_PARTICLE_COUNT 64
183
184/// gCastingImpact_ImageX has seven pictures for each side.
185#define CASTING_IMPACT_IMAGE_X(side, image) \
186 gCastingImpact_ImageX[(side)][(image)]
187
188/// Gathers sparks around the caster, launches the Djinni and draws the
189/// impact on the first target.
190///
191/// Called only by BattleFx_DispatchByIdRange (positions.c), for effect kinds
192/// 100 to 135. The phases, the hand-offs and the particle ranges are in the
193/// folder README under "Effect families". Opens the canvas layer (tiled for
194/// Terra Strike, Mortal Danger and Asura), writes the background palette,
195/// BG2's scale and reference and REG_BLDALPHA, holds the caster still while
196/// the sparks gather, and adds BattleFx_ArmBg2AffineHBlankDma for the waving
197/// impacts; it releases all of them before it returns, or hands them on.
198///
199/// mode: the impact, an enum CastingImpact (the kind less 100). The tests on
200/// mode 100 never hold, as the dispatcher passes 0 to 35.
202 void **slots;
203 void **slot;
204 s32 alive;
205 s32 cellOffset;
206 struct EffectPosition casterPos;
207 struct EffectPosition targetPos;
208 struct FixedPointPosition djinnPos;
209 s32 phase;
210 s32 i;
211 s32 *scanline;
212 s32 n;
213 s32 djinnX;
214 DrawRectangle *slotDraw;
215 struct BattleEffectWork *work;
216 void *canvas;
217 s32 frame;
218 struct BattleCamera *camera;
219 u8 *sprites;
220 s32 scrollPos;
221 s32 scrollSpeed;
222 s32 duration;
223 struct EffectPosition *targetScreen;
224 struct EffectPosition *casterScreen;
225 DrawRectangle *drawPair;
226 s32 savedVelocityX;
227 s32 savedVelocityY;
228 s32 savedVelocityZ;
229 s32 savedAcceleration;
230 s32 savedVerticalStrength;
231 struct MotionObject *targetActor;
232 struct FixedPointPosition *motion;
233 s32 kindFromTwo;
234 s32 kindFromFour;
235 struct FixedPointPosition *position;
236 struct MotionObject *casterActor;
237 struct EffectStep *particle;
238 s32 pairX;
239 s32 stripX;
240 struct FixedPointPosition velocity;
241 struct EffectPosition sparkScreen;
242 BlitterPair blitters;
243 struct EffectPosition projected;
244
246 slot = slots;
247 work = (struct BattleEffectWork *)*slot++;
248 canvas = *slot;
249 camera = WORK_SLOT(HEAP_SLOT_CAMERA);
250 sprites = (u8 *)EFFECT_SLOT(slots, HEAP_SLOT_BATTLE_SHEET);
251 work->effect = effect;
256 } else {
258 }
259 REG_BLDALPHA = BLEND_ALPHA(16, 16);
260
261 // Setup: the pictures.
262 // work->sheet: six 48x72 frames over the caster
263 // work->sheet + CASTING_IMPACT_ORB_CELLS: the impact orb
264 // Ram_MapCellBuffer: the impact's own pictures
265 // Ram_MapCellBuffer + CASTING_IMPACT_DJINNI_CELLS: the element's Djinni
266 // The orb is decoded into the map buffer first and packed from there,
267 // before the impact's own pictures replace it.
271 0);
272 /* FAKEMATCH: local dimensions preserve the packing argument order; as
273 * literal 40 and 288, 4 instructions differ in tbs-en. */
274 {
275 s32 width = 40;
276 s32 height = 288;
278 work->sheet + CASTING_IMPACT_ORB_CELLS, width,
279 height);
280 }
281 if (mode == CASTING_IMPACT_IMPACT_SCORCH ||
284 1, 0);
285 } else if (mode == CASTING_IMPACT_IMPACT_BAD_OMEN) {
287 } else if (mode == CASTING_IMPACT_IMPACT_SQUALL ||
290 0);
292 Ram_MapCellBuffer + 0xc56, 1, 0);
293 } else if (mode == CASTING_IMPACT_IMPACT_PSYPHON_SEAL) {
295 } else if (mode == CASTING_IMPACT_IMPACT_TERRA_STRIKE) {
297 1);
298 } else if (mode == CASTING_IMPACT_IMPACT_AQUA_SOCK) {
300 0);
301 } else if (mode == CASTING_IMPACT_IMPACT_CYCLONE_SLASH) {
303 0);
304 } else if (mode == CASTING_IMPACT_IMPACT_FROST_BITE) {
306 0);
307 } else if (mode == CASTING_IMPACT_IMPACT_BROIL) {
309 1, 0);
310 } else if (mode == CASTING_IMPACT_IMPACT_FORCIBLE_ARM ||
313 0);
314 } else if (mode != CASTING_IMPACT_IMPACT_MORTAL_DANGER &&
317 }
318 // The Djinni of the impact's element.
319 switch (mode) {
332 1, 1);
333 break;
343 1, 1);
344 break;
357 1, 1);
358 break;
370 1, 1);
371 break;
372 case 100:
375 1, 1);
376 break;
377 }
378 // Trails: each transfer keeps 3/4 of every canvas level for Bad Omen,
379 // half for the others.
381 if (mode == CASTING_IMPACT_IMPACT_BAD_OMEN) {
382 work->transferValue = 75;
383 } else {
384 work->transferValue = 50;
385 }
388 /* FAKEMATCH: retain each output pointer while preparing its call; as
389 * pointers set in statements before the calls, 38 instructions differ in
390 * tbs-en. */
392 targetScreen = &targetPos);
394 casterScreen = &casterPos);
395 BattleFx_FetchRectangleBlitters(work->effect->side, drawPair = blitters);
396 // The background palette fade over 24 frames (Palette_StepFadeTransfer,
397 // added by BattleFx_BeginCanvasLayer).
398 work->fadeFrames = 24;
399 work->fadeStep = 0;
400 casterActor = GetBattleObjectSlotFar(work->effect->actor)->object;
401
402 // Gathering sparks: x is the radius (32 to 95), and the three velocities
403 // are the spark's roll, pitch and yaw.
404 {
405 for (i = 0; i != CASTING_IMPACT_SPARKS; i++) {
406 particle = &work->particles[i];
407 SET_POSITION(particle, (Random16() & 63) + 32, 0, 0);
408 SET_VELOCITY(particle, Random16() & 0xffff, Random16() & 0xffff,
409 Random16() & 0xffff);
410 }
411 }
412 // Stop the acting unit, saving its motion for after the gathering, and
413 // place the Djinni 80 pixels overhead.
414 ObjectDispatch_SetAnimationSpeedFar((struct DispatchObject *)casterActor, 0);
415 position = &djinnPos;
416 SET_POSITION(position, casterActor->x, casterActor->y + 0x500000,
417 casterActor->z);
418 savedVelocityX = casterActor->velocityX;
419 savedVelocityY = casterActor->velocityY;
420 savedVelocityZ = casterActor->velocityZ;
421 savedAcceleration = casterActor->acceleration;
422 savedVerticalStrength = casterActor->verticalMotionStrength;
423 SET_VELOCITY(casterActor, 0, 0, 0);
424 casterActor->acceleration = 0;
425 casterActor->verticalMotionStrength = 0;
427 casterScreen->x = casterScreen->x / 2;
429
430 // Phase 1, the gathering (CASTING_IMPACT_GATHER_FRAMES): four more sparks
431 // each frame, their radii shrinking by 4 towards the caster; a spark goes
432 // out once its radius passes 0. The Djinni impacts (0 to 7) show the
433 // Djinni once fewer than 64 sparks are left.
434 frame = 0;
435 do {
436 n = 0;
437 for (i = 0; i != CASTING_IMPACT_SPARKS; i++) {
438 struct EffectStep *step = &work->particles[i];
439 if (step->x >= 0) {
440 if (frame >= i / 4) {
441 {
442 s32 size = 5;
447 EffectPosition_ProjectPoint(step, &sparkScreen);
448 sparkScreen.x = sparkScreen.x / 2 + casterScreen->x;
449 // Djinni kinds circle the caster's chest; the rest circle lower,
450 // Severe Blow lowest.
451 if (mode <= 7)
452 sparkScreen.y = sparkScreen.y + casterScreen->y - 8;
453 else if (mode == CASTING_IMPACT_IMPACT_SEVERE_BLOW)
454 sparkScreen.y = sparkScreen.y + casterScreen->y + 44;
455 else
456 sparkScreen.y = sparkScreen.y + casterScreen->y + 12;
457 // The depth is clamped to 0 to 120 here but not read.
458 CLAMP(sparkScreen.depth, -60, 60);
459 sparkScreen.depth += 60;
460 EFFECT_DRAW_MOTE(blitters[1], canvas, sprites, sparkScreen.x,
461 sparkScreen.y, size);
462 }
463 step->x -= 4;
464 }
465 n++;
466 }
467 }
468 if (mode <= 7) {
469 alive = n;
470 if (alive <= 63) {
472 EffectPosition_ProjectPoint(&djinnPos, &sparkScreen);
473 sparkScreen.x /= 2;
474 djinnX = sparkScreen.x;
476 djinnX - 10, sparkScreen.y - 4, 20, 40);
477 }
478 }
480 frame++;
481 } while (frame != CASTING_IMPACT_GATHER_FRAMES);
482 /* FAKEMATCH: restart the pointer's lifetime after gathering so the
483 * projection above uses the stack position directly; without it, 114
484 * instructions differ in tbs-en. */
485 position = &djinnPos;
486
487 // Phase 2: place BG2 for the impacts that need it, at its unstretched
488 // scale with the impact over the target.
490 REG_BG2PA = Q_8_8(1);
491 if (work->effect->side == 0) {
492 // frame is 32 here, the gathering's length.
493 REG_BG2X = (frame - targetScreen->x) << 8;
494 } else {
495 REG_BG2X = (96 - targetScreen->x) << 8;
496 }
497 } else {
498 if (mode == CASTING_IMPACT_IMPACT_ASURA) {
499 // Scroll the image horizontally; scrollPos is in 16.16 pixels.
500 REG_BG2PA = Q_8_8(1);
501 if (work->effect->side == 0) {
502 scrollPos = -0x800000;
503 scrollSpeed = 0xc0000;
504 } else {
505 scrollPos = 0x80000;
506 scrollSpeed = -0xc0000;
507 }
508 REG_BG2X = (scrollPos >> 16) << 8;
509 }
510 }
512 REG_BG2PA = Q_8_8(1);
513 REG_BG2X = (64 - targetScreen->x) << 8;
515 work->transferValue = 0;
518 REG_BLDCNT = 0;
519 }
521 REG_BG2PA = Q_8_8(1);
522 if (work->effect->side == 0) {
523 REG_BG2X = (32 - targetScreen->x) << 8;
524 } else {
525 REG_BG2X = (96 - targetScreen->x) << 8;
526 }
527 }
528 // Kinds 15, 17, 24 and 26 clear the canvas and hand off to another effect
529 // without giving the caster its motion back: 15 to projectile volley 9,
530 // 24 to Mystic Flame's orbs and 26 to projectile volley 8; 17 ends here.
531 if (mode == CASTING_IMPACT_IMPACT_15 || mode == CASTING_IMPACT_IMPACT_17 ||
533 CastingImpact_EndPrelude(work, canvas);
534 ObjectDispatch_SelectAnimationFar(casterActor, 3);
535 if (mode == CASTING_IMPACT_IMPACT_15) {
537 }
538 if (mode == CASTING_IMPACT_IMPACT_24) {
540 }
541 if (mode != CASTING_IMPACT_IMPACT_26) {
542 return;
543 }
545 return;
546 }
547 ObjectDispatch_SetAnimationSpeedFar((struct DispatchObject *)casterActor, 16);
548 SET_VELOCITY(casterActor, savedVelocityX, savedVelocityY, savedVelocityZ);
549 casterActor->acceleration = savedAcceleration;
550 casterActor->verticalMotionStrength = savedVerticalStrength;
551 // Kind 35 (Severe Blow), with the caster's motion back, clears the canvas
552 // and hands off to BattleFx_RunSparkGroups, mode 2, with variant 3.
554 CastingImpact_EndPrelude(work, canvas);
555 effect->variant = 3;
556 BattleFx_RunSparkGroups(effect, 2);
557 return;
558 }
559
560 // The Djinni's step a frame, to reach 30 pixels above the target in six
561 // frames.
562 targetActor = GetBattleObjectSlotFar(work->effect->actors[0])->object;
563 motion = &velocity;
564 SET_POSITION(motion, (targetActor->x - position->x) / 6,
565 (targetActor->y - position->y + 0x1e0000) / 6,
566 (targetActor->z - position->z) / 6);
567 for (i = 0; i != CASTING_IMPACT_SPARKS; i++) {
568 particle = &work->particles[i];
569 particle->variant = 0;
570 }
571 // 32 sparks scatter from half the target's height, variant their lifetime;
572 // Aqua Sock throws its own later, and Psyphon Seal's rise more slowly.
574 s32 height = BattleUnit_GetEffectHeightFar(work->effect->actors[0]) / 2;
575 for (i = 0; i != 32; i++) {
576 particle = &work->particles[i];
577 SET_POSITION(particle, targetActor->x, height, targetActor->z);
579 SET_VELOCITY(particle, ((Random16() & 255) - 127) << 12,
580 ((Random16() & 255) - 64) << 10,
581 ((Random16() & 255) - 127) << 12);
582 } else {
583 SET_VELOCITY(particle, ((Random16() & 255) - 127) << 12,
584 ((Random16() & 255) - 64) << 12,
585 ((Random16() & 255) - 127) << 12);
586 }
587 particle->variant = i / 2 + 32;
588 }
589 }
590 // Mortal Danger and Asura replace the caster's overlay and the impact's
591 // pictures.
595 1, 0);
596 REG_BLDALPHA = BLEND_ALPHA(16, 14);
597 }
598 if (mode == CASTING_IMPACT_IMPACT_ASURA) {
600 1, 1);
602 1, 0);
603 REG_BLDALPHA = BLEND_ALPHA(16, 14);
604 }
605 // The 64 particles of the impact's own animation start at the target, 20
606 // pixels up (on the ground for Bad Omen), flying outwards. The returning
607 // motes start theirs on frame 12; Mortal Danger and Asura use none.
608 if (mode != CASTING_IMPACT_IMPACT_SAP &&
614 s32 height = 0;
616 height = 0x140000;
617 for (i = 0; i != CASTING_IMPACT_IMPACT_PARTICLE_COUNT; i++) {
618 particle = &CASTING_IMPACT_IMPACT_PARTICLES[i];
619 SET_POSITION(particle, targetActor->x, height, targetActor->z);
620 if (mode == CASTING_IMPACT_IMPACT_SCORCH ||
622 SET_VELOCITY(particle, ((Random16() & 255) - 127) << 11,
623 (Random16() & 255) << 11,
624 ((Random16() & 255) - 127) << 11);
625 } else if (mode == CASTING_IMPACT_IMPACT_ACID_BATH) {
626 SET_VELOCITY(particle, ((Random16() & 255) - 127) << 11,
627 (Random16() & 127) << 10,
628 ((Random16() & 255) - 127) << 11);
629 } else {
630 SET_VELOCITY(particle, ((Random16() & 255) - 127) << 10,
631 (Random16() & 127) << 10,
632 ((Random16() & 255) - 127) << 10);
633 }
634 particle->variant = 0;
635 }
636 }
637 // Mist, Fever, Bad Omen, Heat Mirage, Blinding Smog and Murk wave BG2 by
638 // scanline.
639 kindFromTwo = mode - 2;
640 if ((kindFromTwo >= 0 && kindFromTwo <= 1) ||
647 }
648
649 // The impact lasts 20, 32, 40, 48, 64 or 80 frames, by mode.
650 kindFromFour = mode - 4;
651 if ((kindFromFour >= 0 && kindFromFour <= 2) ||
656 mode == CASTING_IMPACT_IMPACT_TRUNCHEON_FIST || mode == 100) {
657 duration = 32;
658 } else if ((mode >= 0 && mode <= 3) ||
667 duration = 48;
668 } else if (mode == CASTING_IMPACT_IMPACT_MELTDOWN) {
669 duration = 20;
670 } else if (mode == CASTING_IMPACT_IMPACT_MORTAL_DANGER ||
673 duration = 40;
674 } else if (mode == CASTING_IMPACT_IMPACT_BLINDING_SMOG ||
676 duration = 64;
677 } else {
678 duration = 80;
679 }
680 // Phase 3, the impact, for duration frames. Each frame: the wave table,
681 // the caster's overlay, the palette step, the orb and the scattering
682 // sparks, the impact's own animation, the Djinni's flight, then the cues
683 // for the target.
684 frame = 0;
685 while (frame != duration) {
686 // BG2 wave table: BG2X for each scanline.
688 if (mode != CASTING_IMPACT_IMPACT_ASURA) {
689 s32 base;
690 scanline = work->bg2xLines;
691 i = 0;
692 base = 0x40000;
693 phase = frame << 12;
694 for (; i != 160; i++) {
695 // 1 - sin pixels, in BG2X's 20.8 format.
696 s32 offset = (base - ((Trig_Sin(phase) * 4))) >> 10;
697 *scanline++ = offset;
698 phase += 0x800;
699 }
700 }
701 }
702 // Follow the caster for the first three frames, anchoring 16 pixels
703 // lower.
704 if (frame <= 2) {
706 casterScreen->x = casterScreen->x / 2;
707 casterScreen->y += 16;
708 }
709 // The caster's overlay: six pictures, two frames each.
715 if (frame <= 11) {
716 if (work->effect->side == 0) {
717 blitters[0](canvas, work->sheet + frame / 2 * (48 * 72),
718 casterScreen->x - 32, casterScreen->y - 40, 48, 72);
719 } else {
720 blitters[0](canvas, work->sheet + frame / 2 * (48 * 72),
721 casterScreen->x, casterScreen->y - 40, 48, 72);
722 }
723 }
724 }
725 // Step the background palette towards the impact's palette; impacts not
726 // listed keep the faded one.
727 switch (mode) {
730 break;
733 break;
736 break;
739 break;
743 break;
748 break;
751 break;
754 break;
758 break;
762 break;
767 break;
770 break;
774 break;
775 }
776 // The orb on frames 4 to 15, and the 32 scattering sparks from frame 4.
780 if (frame >= 4 && frame <= 15) {
781 drawPair[1](canvas,
782 work->sheet + (frame - 4) / 2 * (20 * 48) +
784 targetScreen->x / 2 - 8, casterScreen->y - 24, 20, 48);
785 }
787 // Each spark is visited twice a frame, so it moves and ages twice; even
788 // sparks draw brightest-wins, odd ones additively.
789 if (frame > 3) {
790 for (i = 0; i != 128; i++) {
791 s32 index = i / 2;
792 struct EffectStep *step = &work->particles[index];
793 s32 size = step->variant;
794 if (size > 0) {
795 EffectPosition_ProjectPoint(step, &projected);
796 size >>= 4;
797 size++;
798 projected.x /= 2;
799 EFFECT_DRAW_MOTE(blitters[index & 1], canvas, sprites, projected.x,
800 projected.y, size);
801 EffectStep_AdvanceWithGravity3D(step, 60, -0x1000);
802 step->variant--;
803 }
804 }
805 }
806 }
807 // The impact's own animation.
808 if (mode == CASTING_IMPACT_IMPACT_SAP ||
812 // Motes burst from the target on frame 12 and fly back into the caster,
813 // who glows white from frame 50 to 79. Mote i steers towards the
814 // caster's middle from frame i + 23, by a 256th of the distance more
815 // each frame, and goes out once over the caster; only the first 32 are
816 // drawn.
817 if (frame == 50) {
819 -1, 0);
820 }
821 if (frame == 79) {
822 ObjectGroup_UpdateMembers(work->effect->actor, 0, -1, -1, 0);
823 }
824 if (frame == 12) {
827 i++, particle++) {
828 SET_POSITION(particle, targetActor->x, 0x140000, targetActor->z);
829 SET_VELOCITY(particle, ((Random16() & 255) - 128) << 10,
830 ((Random16() & 255) - 128) << 10,
831 ((Random16() & 255) - 128) << 10);
832 particle->variant = 0;
833 }
834 }
835 if (frame > 11) {
837 struct MotionObject *caster;
838 s32 height;
839 caster = GetBattleObjectSlotFar(work->effect->actor)->object;
841 for (i = 0; i != 32; i++, step++) {
842 if (step->variant >= 0) {
843 s32 size = (i & 1) + 6;
844 EffectPosition_ProjectPoint(step, &projected);
845 projected.x >>= 1;
846 blitters[0](canvas, sprites + gBattleFx_MoteCells[size - 1],
847 projected.x - (u32)size / 2, projected.y - size, size,
848 size * 2);
850 if (frame > i + 22) {
851 s32 dx = (caster->x - step->x) >> 8;
852 s32 dy = (caster->y + height - step->y) >> 8;
853 s32 dz = (caster->z - step->z) >> 8;
854 step->velocityX += dx;
855 step->velocityY += dy;
856 step->velocityZ += dz;
857 if ((dx >= -0xfff && dx <= 0xfff) &&
858 (dz >= -0xfff && dz <= 0xfff))
859 step->variant = -1;
860 }
861 }
862 }
863 }
864 } else if (mode == CASTING_IMPACT_IMPACT_MELTDOWN) {
865 // Draw nothing; finish with BattleFx_RunImpair.
866 } else if (mode == CASTING_IMPACT_IMPACT_SQUALL ||
868 // Two 24x104 bolts strike the target from frame 6 to 19, three frames
869 // apart in their four-picture cycle; from frame 8 to 23 three glints a
870 // frame flash at random points around it, each randomly flipped. Each
871 // glint takes a blitter of its own, so the pair is fetched again after.
872 if (frame >= 6 && frame <= 19) {
873 phase = frame;
874 i = 0;
875 do {
876 blitters[0](canvas,
877 ((phase / 2) & 3) * LIGHTNING_BOLT_CELL_BYTES +
878 (Ram_MapCellBuffer + 0xc56),
879 targetScreen->x / 2 - 8, 0, 24, 104);
880 i++;
881 phase += 3;
882 } while (i != 2);
883 }
884 if (frame >= 8 && frame <= 23) {
885 for (i = 0; i != 3; i++) {
886 s32 image = i & 3;
887 s32 angle = Random16() & 0xffff;
888 s32 imageX;
889 s32 imageY;
890 s32 drawFlags;
891 imageX = Trig_Sin(angle);
892 imageX <<= 3;
893 imageX >>= 16;
894 imageX += targetScreen->x / 2;
895 imageX -= gBattleFx_GlintCellWidths[image] / 2;
896 imageY = Trig_Cos(angle);
897 imageY <<= 5;
898 imageY >>= 16;
899 imageY -= gBattleFx_GlintCellHeights[image] / 2;
901 drawFlags = Random16();
903 gCastingImpact_GlintDrawFlags[drawFlags & 3] |
907 canvas, Ram_MapCellBuffer + gBattleFx_GlintCells[image], imageX,
908 imageY + 56, gBattleFx_GlintCellWidths[image],
912 }
913 }
914 } else if (mode == CASTING_IMPACT_IMPACT_AQUA_SOCK) {
915 // Over the first 24 frames a flame column falls onto the target: a
916 // 17x104 picture tiled down the screen, scrolling 16 pixels a frame,
917 // its head falling 32 pixels a frame until it lands, and the 34x65 flame
918 // at its foot. The screen shakes from frame 8, and from frame 2 up to
919 // four more sparks a frame rise from the target into free slots.
920 s32 fall;
921 s32 scroll;
923 if (frame >= 0 && frame <= 23) {
924 DrawRectangle *draw;
925 s32 width;
926 s32 tileHeight;
927 s32 columnX;
928 s32 columnY;
929 s32 top;
930 s32 originX;
931
932 originX = targetScreen->x / 2;
933 fall = frame * 32 - 232;
934 scroll = frame * 16 - 48;
935 if (fall > 0)
936 fall = 0;
937 while (scroll > 104)
938 scroll -= 104;
941 draw = (DrawRectangle *)gWorkSlot;
943 columnX = originX - 8;
944 columnY = fall + scroll;
945 top = columnY - 104;
946 width = 17;
947 tileHeight = 104;
948 CastingImpact_DrawImage(canvas, Ram_MapCellBuffer, columnX, top, width,
949 tileHeight, draw);
951 columnY, width, tileHeight, scroll,
952 draw);
953 (*draw)(canvas, Ram_MapCellBuffer + (17 * 104), originX - width,
954 fall + 47, width * 2, 65);
956 if (frame == 8) {
957 work->shakeFrames = frame;
958 }
959 if (frame > 1) {
960 s32 emitted = 0;
961 struct EffectStep *step;
962 for (i = 0; i != CASTING_IMPACT_SPARKS; i++) {
963 if ((step = &work->particles[i])->variant == 0) {
964 SET_POSITION(step, targetActor->x, 0x140000, targetActor->z);
965 SET_VELOCITY(step, ((Random16() & 255) - 127) << 12,
966 ((Random16() & 255) - 64) << 10,
967 ((Random16() & 255) - 127) << 12);
968 step->variant = i / 2 + 32;
969 emitted++;
970 if (emitted == 4)
971 break;
972 }
973 }
974 }
975 }
977 } else if (mode == CASTING_IMPACT_IMPACT_PSYPHON_SEAL) {
978 s32 height;
980 // From frame 4 to 23 the rune's two halves are drawn brightest-wins,
981 // the second mirrored, each with a blitter of its own.
982 if (frame >= 4 && frame <= 23) {
983 pairX = targetScreen->x / 2;
984 height = 48;
987 slotDraw = (DrawRectangle *)gWorkSlot;
988 CastingImpact_DrawImage(canvas, Ram_MapCellBuffer, pairX - 24, 48, 24,
989 height, slotDraw + HEAP_SLOT_BLITTER_ALTERNATE);
994 CastingImpact_DrawImage(canvas, Ram_MapCellBuffer, pairX, 48, 24,
995 height, slotDraw + HEAP_SLOT_BLITTER_ALTERNATE);
997 }
999 } else if (mode == CASTING_IMPACT_IMPACT_CYCLONE_SLASH) {
1000 // From frame 6 a tornado of three stacked 40x32 strips stands over the
1001 // target, cycling three pictures of two frames each; from frame 16 it
1002 // fades.
1003 s32 image;
1004 if (frame > 15) {
1005 REG_BLDALPHA = BLEND_ALPHA(32 - frame, 16);
1006 }
1007 if (frame > 5) {
1008 stripX = targetScreen->x / 2 - 20;
1009 image = frame / 2 % 3;
1010 cellOffset = image * 2560;
1011 blitters[0](canvas, Ram_MapCellBuffer + 0xc56 + cellOffset, stripX, 16,
1012 40, 32);
1013 blitters[0](canvas, image * (40 * 32) + (Ram_MapCellBuffer + 0x2a56),
1014 stripX, 48, 40, 32);
1015 blitters[0](canvas, cellOffset + (Ram_MapCellBuffer + 0x1156), stripX,
1016 80, 40, 32);
1017 }
1018 } else if (mode == CASTING_IMPACT_IMPACT_SCORCH ||
1020 // Six 32x64 frames per particle, two ticks each.
1022 for (i = 0; i != 16; i++, step++) {
1023 // 16 flash bursts rise, two starting each frame from frame 4.
1024 if (frame >= i / 2 + 4) {
1025 s32 age = step->variant;
1026 if (age <= 11) {
1027 s32 image = age / 2;
1028 EffectPosition_ProjectPoint(step, &projected);
1029 projected.x /= 2;
1030 blitters[0](canvas, Ram_MapCellBuffer + (image << 11),
1031 projected.x - 16, projected.y - 32, 32, 64);
1032 EffectStep_AdvanceWithGravity3D(step, 60, 0x1000);
1033 step->variant++;
1034 }
1035 }
1036 }
1037 } else if (mode == CASTING_IMPACT_IMPACT_FLINT) {
1038 // Draw nothing.
1039 } else if (mode == CASTING_IMPACT_IMPACT_MORTAL_DANGER) {
1040 s32 imageY;
1041 s32 sway;
1042 // The 57x98 picture shows until frame 7 and the 99x69 one from frame 4;
1043 // the canvas flashes on frames 4 and 5, and from frame 6 five pieces
1044 // follow, two frames each.
1045 // The sine is taken and dropped; only the cosine sways the pictures,
1046 // up to 4 pixels.
1047 Trig_Sin(frame << 9);
1048 sway = Trig_Cos(frame << 9);
1049 sway <<= 2;
1050 sway >>= 16;
1051 imageY = (targetScreen->y >> 16) + sway + 16;
1052 if (frame <= 3) {
1053 blitters[0](canvas, work->sheet,
1055 imageY + gCastingImpact_ImageY[0], 57, 98);
1056 } else {
1057 if (frame <= 7) {
1058 blitters[0](canvas, work->sheet,
1060 imageY + gCastingImpact_ImageY[0], 57, 98);
1061 }
1062 blitters[0](canvas, work->sheet + (57 * 98),
1064 imageY + gCastingImpact_ImageY[1], 99, 69);
1065 if (frame == 4 || frame == 5) {
1066 Iwram_FillWords(canvas, CANVAS_BYTES, 0x3f3f3f3f);
1067 }
1068 if (frame == 6 || frame == 7) {
1069 blitters[0](canvas, work->sheet + (57 * 98 + 99 * 69),
1071 imageY + gCastingImpact_ImageY[2], 128, 91);
1072 }
1073 if (frame == 8 || frame == 9) {
1075 canvas, Ram_MapCellBuffer,
1077 imageY + gCastingImpact_ImageY[3], 128, &blitters[0]);
1078 }
1079 if (frame == 10 || frame == 11) {
1080 blitters[0](canvas, Ram_MapCellBuffer + (128 * 91),
1082 imageY + gCastingImpact_ImageY[4], 128, 59);
1083 }
1084 if (frame == 12 || frame == 13) {
1085 blitters[0](canvas, Ram_MapCellBuffer + (128 * 91 + 128 * 59),
1087 imageY + gCastingImpact_ImageY[5], 122, 29);
1088 }
1089 if (frame == 14 || frame == 15) {
1090 blitters[0](canvas,
1091 Ram_MapCellBuffer + (128 * 91 + 128 * 59 + 122 * 29),
1093 imageY + gCastingImpact_ImageY[6], 76, 25);
1094 }
1095 }
1096 } else if (mode == CASTING_IMPACT_IMPACT_ASURA) {
1097 // The background scrolls, slowing from frame 7 until it stops, while an
1098 // 80x104 beast appears and breaks: its first picture shows until frame
1099 // 7 and its second from frame 4, the canvas flashes on frames 4 and 5,
1100 // and from frame 6 five pieces follow, two frames each. It fades from
1101 // frame 16.
1102 scrollPos += scrollSpeed;
1103 if (frame > 6) {
1104 scrollSpeed = (scrollSpeed * 48) / 64;
1105 }
1106 REG_BG2X = (scrollPos >> 16) << 8;
1107 {
1108 s32 fade;
1109 fade = frame - 16;
1110 if (fade >= 0 && fade <= 15) {
1111 REG_BLDALPHA = BLEND_ALPHA(16 - fade, 16);
1112 }
1113 }
1114 if (frame == 4 || frame == 5) {
1115 Iwram_FillWords(canvas, CANVAS_BYTES, 0x3f3f3f3f);
1116 }
1117 if (frame <= 3) {
1118 if (work->effect->side == 1) {
1119 blitters[0](canvas, work->imageStorage, 0, 24, 80, 104);
1120 } else {
1121 blitters[0](canvas, work->imageStorage, 48, 24, 80, 104);
1122 }
1123 } else {
1124 if (frame <= 7) {
1125 if (work->effect->side == 1) {
1126 blitters[0](canvas, work->imageStorage, 0, 24, 80, 104);
1127 } else {
1128 blitters[0](canvas, work->imageStorage, 48, 24, 80, 104);
1129 }
1130 }
1131 if (work->effect->side == 1) {
1132 blitters[0](canvas, work->imageStorage + 0x1e00, 16, 16, 80, 104);
1133 } else {
1134 blitters[0](canvas, work->imageStorage + 0x1e00, 32, 16, 80, 104);
1135 }
1136 if (frame == 6 || frame == 7) {
1137 blitters[0](canvas, work->imageStorage + 0x3e80, 0, 16, 128, 91);
1138 }
1139 if (frame == 8 || frame == 9) {
1141 &blitters[0]);
1142 }
1143 if (frame == 10 || frame == 11) {
1144 blitters[0](canvas, Ram_MapCellBuffer + (128 * 91), 0, 16, 128, 59);
1145 }
1146 if (frame == 12 || frame == 13) {
1147 blitters[0](canvas, Ram_MapCellBuffer + (128 * 91 + 128 * 59), 0, 16,
1148 128, 29);
1149 }
1150 if (frame == 14 || frame == 15) {
1151 blitters[0](canvas,
1152 Ram_MapCellBuffer + (128 * 91 + 128 * 59 + 128 * 29), 0,
1153 16, 128, 26);
1154 }
1155 }
1156 } else if (mode == CASTING_IMPACT_IMPACT_BROIL) {
1157 // Twelve ember streaks, one starting each frame from frame 6, each six
1158 // pictures of two frames. Odd streaks step right of the target and even
1159 // ones left, 3 pixels further each pair; the first, and each whose
1160 // (i - 1) % 4 is 2 or 3, draws additively.
1161 for (i = 0; i != 12; i++) {
1162 if (frame >= i + 6 && frame < i + 18) {
1163 s32 image = (frame - i - 6) / 2;
1164 s32 imageX =
1165 targetScreen->x / 2 - gBattleFx_PuffArcCellWidths[image] / 2;
1166 s32 mirrored;
1167 if (i & 1)
1168 imageX += (1 + i) / 2 * 3;
1169 else
1170 imageX -= (i + 1) / 2 * 3;
1171 mirrored = 1;
1172 if (i != 0) {
1173 mirrored = 0;
1174 if (((i - 1) & 3) > 1)
1175 mirrored = 1;
1176 }
1177 blitters[mirrored](canvas,
1179 imageX, gBattleFx_PuffArcCellBiasY[image] + 48,
1182 }
1183 }
1184 } else if (mode == CASTING_IMPACT_IMPACT_FROST_BITE) {
1185 // 64 ice chips close in on the target in straight lines from 32 to 159
1186 // pixels out, two more each frame, 6 pixels a frame. The target
1187 // flinches, with a sound, as each eighth chip and the last arrive.
1188 if (frame == 0) {
1189 for (i = 0; i != CASTING_IMPACT_IMPACT_PARTICLE_COUNT; i++) {
1190 particle = &CASTING_IMPACT_IMPACT_PARTICLES[i];
1191 SET_POSITION(particle, (Random16() & 127) + 32, 0, 0);
1192 SET_VELOCITY(particle, Random16() & 0xffff, Random16() & 0xffff,
1193 Random16() & 0xffff);
1194 }
1196 }
1197 {
1199 struct EffectPosition *origin;
1200 /* FAKEMATCH: a zero offset known only after gcse keeps the origin copy
1201 * after the projection address; as origin = targetScreen, 64
1202 * instructions differ in tbs-en. */
1203 s32 bias = 0;
1204 for (i = 0; i != CASTING_IMPACT_IMPACT_PARTICLE_COUNT; i++, step++) {
1205 if (step->x >= 0 && frame >= i / 2) {
1206 s32 image = i & 3;
1210 EffectPosition_ProjectPoint(step, &projected);
1211 /* FAKEMATCH: the zero bias above; see there. */
1212 origin = (struct EffectPosition *)((u8 *)targetScreen + bias);
1213 projected.x = projected.x / 2 + origin->x / 2;
1214 projected.y += origin->y + 32;
1215 blitters[1](canvas,
1217 projected.x - 4, projected.y - 4, 8, 8);
1218 step->x -= 6;
1219 if (step->x < 0 && ((i & 7) == 0 || i == 63)) {
1223 }
1224 }
1225 }
1226 }
1227 } else if (mode == CASTING_IMPACT_IMPACT_TERRA_STRIKE) {
1228 s32 height;
1229 // The rock spire rises 16 pixels a frame from frame 5 to its full 96,
1230 // and from frame 26 sinks 4 a frame.
1231 if (frame >= 5 && frame <= 49) {
1232 if (frame > 25) {
1233 height = 196 - (frame << 2);
1234 } else {
1235 height = (frame << 4) - 64;
1236 }
1237 if (height > 96) {
1238 height = 96;
1239 }
1240 CastingImpact_DrawImage(canvas, Ram_MapCellBuffer, 48, 104 - height, 32,
1241 height, &blitters[0]);
1242 }
1243 } else if (mode == CASTING_IMPACT_IMPACT_FORCIBLE_ARM ||
1245 // A 32x64 flame falls diagonally onto the target over six frames.
1246 s32 offset;
1247 s32 imageX;
1248 s32 imageY;
1249 if (frame <= 5) {
1250 /* FAKEMATCH: calculate each center before the shared centered draw;
1251 * as targetScreen->x / 2 in each arm, 147 instructions differ in
1252 * tbs-en. */
1253 if (work->effect->side == 0) {
1254 imageX = targetScreen->x;
1255 imageX += (u32)imageX >> 31;
1256 imageX >>= 1;
1257 offset = (6 - frame) * 3;
1258 imageX += offset * 2;
1259 imageY = targetScreen->y - offset * 4 + 24;
1260 } else {
1261 imageX = targetScreen->x;
1262 imageX += (u32)imageX >> 31;
1263 imageX >>= 1;
1264 offset = (6 - frame) * 3;
1265 imageX -= offset * 2;
1266 imageY = targetScreen->y - offset * 4 + 24;
1267 }
1269 imageY - 32, 32, &blitters[1]);
1270 }
1271 } else if (mode == CASTING_IMPACT_IMPACT_BAD_OMEN) {
1272 // 16 skulls of three 24x24 pictures rise from the target, each at one of
1273 // four rates by its number; from frame 48 they fade.
1274 if (frame > 47) {
1275 REG_BLDALPHA = BLEND_ALPHA(64 - frame, 16);
1276 }
1277 {
1279 for (i = 0; i != 16; i++, step++) {
1280 s32 image = i % 3;
1281 EffectPosition_ProjectPoint(step, &projected);
1282 projected.x /= 2;
1283 blitters[i & 1](canvas, Ram_MapCellBuffer + image * (24 * 24),
1284 projected.x - 12, projected.y - 12, 24, 24);
1285 EffectStep_AdvanceWithGravity3D(step, 60, 1 << ((i & 3) + 11));
1286 step->variant++;
1287 }
1288 }
1289 } else if (mode != 100) {
1290 // The smoke impacts: six 24x48 pictures a puff, four frames each.
1292 for (i = 0; i != 16; i++, step++) {
1293 // One puff starts each frame from frame 4; Acid Bath's rise slower.
1294 if (frame >= i + 4) {
1295 s32 age = step->variant;
1296 if (age <= 23) {
1297 s32 image = age / 4;
1298 EffectPosition_ProjectPoint(step, &projected);
1299 projected.x /= 2;
1300 blitters[i & 1](canvas, Ram_MapCellBuffer + image * (24 * 48),
1301 projected.x - 12, projected.y - 24, 24, 48);
1303 EffectStep_AdvanceWithGravity3D(step, 60, 0x400);
1304 else
1305 EffectStep_AdvanceWithGravity3D(step, 60, 0x1000);
1306 step->variant++;
1307 }
1308 }
1309 }
1310 }
1311 // Djinni kinds 0-7: fly to the target in six frames.
1312 if (mode <= 7) {
1313 if (frame <= 5) {
1314 EffectPosition_ProjectPoint(position, &projected);
1315 projected.x /= 2;
1316 djinnX = projected.x;
1317 drawPair[1](canvas, Ram_MapCellBuffer + CASTING_IMPACT_DJINNI_CELLS,
1318 djinnX - 10, projected.y - 4, 20, 40);
1319 position->x += motion->x;
1320 position->y += motion->y;
1321 position->z += motion->z;
1322 }
1323 }
1324 // The cues: the damage shows on frame 3 with no sound of its own (a cue
1325 // of -1 is never played), the heavy impact sounds on frame 4, frame 6
1326 // gives some impacts a reaction and a shake, and the target flashes on
1327 // frames 6 and 14.
1328 if (frame == 3) {
1330 }
1331 if (frame == 4) {
1333 }
1334 if (frame == 6) {
1335 if (mode == CASTING_IMPACT_IMPACT_FLINT ||
1337 mode == CASTING_IMPACT_IMPACT_SAP ||
1342 mode == CASTING_IMPACT_IMPACT_TRUNCHEON_FIST || mode == 100) {
1344 work->shakeFrames = 8;
1345 } else if (mode == CASTING_IMPACT_IMPACT_BROIL ||
1349 work->shakeFrames = 2;
1350 } else if (mode == CASTING_IMPACT_IMPACT_CYCLONE_SLASH ||
1353 work->shakeFrames = 8;
1354 }
1355 }
1356 if (frame == 6) {
1358 POSE_DAMAGE, 0, 4);
1359 }
1360 if (frame == 14) {
1362 POSE_DAMAGE, 0, 4);
1363 }
1364 Camera_ApplyShake(8, 8);
1365 EFFECT_END_FRAME(work);
1366 frame++;
1367 }
1368 // Phase 4: Meltdown hands the cleared canvas to the stat reduction
1369 // (Impair), as CastingImpact_EndPrelude does; the others stop the wave
1370 // callback and release the canvas.
1371 if (mode == CASTING_IMPACT_IMPACT_MELTDOWN) {
1374 work->effect->prelude = 0;
1378 BattleFx_RunImpair(effect);
1379 } else {
1380 if (mode == CASTING_IMPACT_IMPACT_MIST ||
1387 }
1389 }
1390}
1391
1392#undef CASTING_IMPACT_ORB_CELLS
1393#undef CASTING_IMPACT_DJINNI_CELLS
1394#undef CASTING_IMPACT_GATHER_FRAMES
1395#undef CASTING_IMPACT_SPARKS
1396#undef CASTING_IMPACT_IMPACT_PARTICLES
1397#undef CASTING_IMPACT_IMPACT_PARTICLE_COUNT
1398#undef CASTING_IMPACT_IMAGE_X
@ SPRITE_COLOR_WHITE
solid colour 15, white
Definition animspr.h:26
@ POSE_DAMAGE
taking damage
Definition battle.h:176
@ COMPOSITE_FLIP_X
@ COMPOSITE_MAX
BattleEffectDrawRectangle DrawRectangle
void BattleFx_ArmBg2AffineHBlankDma(void)
Arms DMA 0 to copy one word of gBattleFxWork's bg2xLines table into BG2X at each horizontal blank,...
void Palette_StepFadeTransfer(void)
Darkens the battle backdrop while the canvas is open: for fadeFrames frames, writes the backdrop's co...
Definition canvas.c:243
void BattleFx_ProcessPendingGraphicsTransfer(void)
Copies the canvas to VRAM once the effect has finished drawing a frame (transferPending): the 128x128...
Definition canvas.c:103
void BattleFx_BeginCanvasLayer(s32 bgControl)
Opens the canvas layer: starts the background palette fade, makes BG2 the canvas, alpha-blended over ...
Definition canvas.c:314
void BattleFx_BeginTiledCanvas(s32 bgControl)
Opens the canvas layer with BG2 a wrapping 256x256 map: each row of the map is the canvas's 16 tiles ...
Definition canvas.c:463
#define CASTING_IMPACT_GATHER_FRAMES
frames the sparks gather
#define CASTING_IMPACT_IMPACT_PARTICLES
The impact pictures' particles, in Ram_MapCellBuffer after the Djinni.
static void CastingImpact_EndPrelude(struct BattleEffectWork *work, void *canvas)
Clears the prelude image, stops its callbacks and releases its blitters before the next effect takes ...
void BattleFx_StepPaletteToResource(s32 resourceId)
Moves each RGB555 channel of background colours 0 to 63 one level towards the resource palette.
void BattleFx_RunCastingImpact(struct BattleEffectArgument *effect, s32 mode)
Gathers sparks around the caster, launches the Djinni and draws the impact on the first target.
#define CASTING_IMPACT_IMPACT_PARTICLE_COUNT
static void CastingImpact_DrawImage(void *canvas, const void *pixels, s32 x, s32 y, s32 width, s32 height, DrawRectangle *draw)
Draws one casting impact image with the given blitter.
static void CastingImpact_DrawScrolledImage(void *canvas, const void *pixels, s32 x, s32 y, s32 width, s32 height, s32 scroll, DrawRectangle *draw)
Draws only the unscrolled height of a casting-impact image.
#define CASTING_IMPACT_DJINNI_CELLS
The Djinni pictures, 20x40, in Ram_MapCellBuffer after the impact's own.
static void CastingImpact_PackRows(void *src, void *dest, s32 width, s32 height)
Packs a casting impact's picture rows (Graphics_PackTileRows).
static void CastingImpact_DrawTallImage(void *canvas, const void *pixels, s32 x, s32 y, s32 width, DrawRectangle *draw)
Draws a casting-impact image whose height is twice its width.
static void CastingImpact_DrawCroppedImage(void *canvas, const void *pixels, s32 x, s32 y, s32 width, DrawRectangle *draw)
Draws a casting-impact image cropped to 91 pixels high.
#define CASTING_IMPACT_SPARKS
sparks in work->particles
#define CASTING_IMPACT_ORB_CELLS
The impact orb, 20x48 pictures packed at half width.
#define CASTING_IMPACT_IMAGE_X(side, image)
gCastingImpact_ImageX has seven pictures for each side.
CastingImpact
The casting impacts, by kind - 100: each is named for the first ability whose gBattle_ActionFlags ent...
@ CASTING_IMPACT_IMPACT_CYCLONE_SLASH
a tornado stands over the target
@ CASTING_IMPACT_IMPACT_MURK
smoke
@ CASTING_IMPACT_IMPACT_SCORCH
flash bursts rise from the target
@ CASTING_IMPACT_IMPACT_FLINT
nothing
@ CASTING_IMPACT_IMPACT_SAP
motes fly back to the caster
@ CASTING_IMPACT_IMPACT_POISON_CLOUD
smoke
@ CASTING_IMPACT_IMPACT_LIFE_LEECH
motes fly back to the caster
@ CASTING_IMPACT_IMPACT_BARRAGE
flash bursts rise from the target
@ CASTING_IMPACT_IMPACT_ASURA
a beast scrolls across and breaks
@ CASTING_IMPACT_IMPACT_PSY_LEECH
motes fly back to the caster
@ CASTING_IMPACT_IMPACT_TRUNCHEON_FIST
a flame falls diagonally
@ CASTING_IMPACT_IMPACT_MELTDOWN
nothing; hands to the stat reduction
@ CASTING_IMPACT_IMPACT_BROIL
ember streaks spread sideways
@ CASTING_IMPACT_IMPACT_TERRA_STRIKE
a rock spire grows and sinks
@ CASTING_IMPACT_IMPACT_15
hands off
@ CASTING_IMPACT_IMPACT_DEADLY_POISON
smoke
@ CASTING_IMPACT_IMPACT_SEVERE_BLOW
hands off
@ CASTING_IMPACT_IMPACT_26
hands off
@ CASTING_IMPACT_IMPACT_HEAT_MIRAGE
also Heat Flash; smoke
@ CASTING_IMPACT_IMPACT_STUN_VOLTAGE
two bolts strike among glints
@ CASTING_IMPACT_IMPACT_MIST
smoke
@ CASTING_IMPACT_IMPACT_SMOG
smoke
@ CASTING_IMPACT_IMPACT_LIFE_NOURISH
motes fly back to the caster
@ CASTING_IMPACT_IMPACT_FORCIBLE_ARM
a flame falls diagonally
@ CASTING_IMPACT_IMPACT_24
hands off
@ CASTING_IMPACT_IMPACT_ACID_BATH
smoke
@ CASTING_IMPACT_IMPACT_MORTAL_DANGER
also Death Size; a large picture rises and breaks
@ CASTING_IMPACT_IMPACT_FROST_BITE
ice chips converge on the target
@ CASTING_IMPACT_IMPACT_BAD_OMEN
skulls rise from the target
@ CASTING_IMPACT_IMPACT_AQUA_SOCK
a blue flame column falls onto the target
@ CASTING_IMPACT_IMPACT_BANE
smoke
@ CASTING_IMPACT_IMPACT_BLINDING_SMOG
smoke
@ CASTING_IMPACT_IMPACT_SQUALL
two bolts strike among glints
@ CASTING_IMPACT_IMPACT_17
hands off
@ CASTING_IMPACT_IMPACT_FEVER
smoke
@ CASTING_IMPACT_IMPACT_PSYPHON_SEAL
a rune opens over the target
static void CastingImpact_LoadResource(const void *id, void *dest, s32 skip, s32 copy)
Loads one of the impact's pictures (Resource_LoadAndDecompress).
void Resource_LoadAndDecompress(s32 id, void *dst, s32 skipPalette, s32 copyPalette)
Decompresses a resource whose first 128 bytes may be a palette.
void ObjectGroup_UpdateMembers(s32 actorId, s32 palette, s32 animation, s32 timerSlot, s32 timerFrames)
Updates every animation group of one battler's object.
Definition dispatch.c:254
What every part of the battle effect object shares: its includes, macros and frame-loop helpers.
#define EFFECT_RELEASE_BLITTERS()
Frees the two blitters, the alternate first.
#define EFFECT_DRAW_MOTE(draw, canvas, sheet, x, y, size)
Particle sheets hold ten mote cells, 1x2 to 10x20 pixels.
#define EFFECT_PRESENT_FRAME(work)
Ends a frame of a canvas effect: marks the canvas ready for the transfer callback,...
@ CANVAS_COPY_AND_CLEAR
copy, then fill the canvas with transferValue
@ CANVAS_COPY_AND_FADE
copy, keeping 50% or 75% (transferValue) of each pixel in the canvas for the next frame
#define CANVAS_BYTES
The canvas: 128x128 pixels, one byte each, in its heap slot and in VRAM at BG_CHAR_BLOCK(1).
#define EFFECT_CALLBACK_ORDER
The scheduler order of every per-frame effect callback (Scheduler_AddOrUpdateCallback sorts the frame...
#define EFFECT_SLOT(slots, slot)
A work slot read through a pointer to the effect's own slot, gWorkSlot[HEAP_SLOT_BATTLE_EFFECT].
#define EFFECT_FINISH_CANVAS()
The usual end of a canvas effect: stops the transfer callback before freeing what it reads,...
#define EFFECT_SHOW_DAMAGE(hitSound)
Lets the battle show the damage: resumes its paused playback, which plays hitSound (an SE_ id; 0 keep...
#define LIGHTNING_BOLT_CELL_BYTES
Closing bolts and lightning columns use the same 24x120 picture cells.
#define EFFECT_LOAD_BLITTER(slot, flags, mode)
Builds a blitter for the 128x128 canvas (1 << 7 pixels each way) in work slot slot: BattleFx_BuildBli...
#define EFFECT_END_FRAME(work)
The usual end of an effect frame: counts down the targets' flash timers (ObjectGroup_TickMemberTimers...
#define EFFECT_VIEW_FROM_CAMERA(camera)
Starts a frame's 3D projection from the battle camera: resets the transform, then looks from camera->...
u16 gBattleFx_PuffArcCells[]
u8 gBattleFx_PuffArcCellHeights[]
u8 gBattleFx_GlintCellHeights[]
u16 gCastingImpact_IceChipCells[]
The four ice chip cells, 8x8.
u8 gBattleFx_PuffArcCellBiasY[]
u16 gBattleFx_MoteCells[]
Mote cells: particle sheets hold ten, cell n (from 0) n + 1 pixels wide and twice that tall.
u8 gBattleFx_PuffArcCellWidths[]
u16 gBattleFx_GlintCells[]
u8 gBattleFx_GlintCellWidths[]
u8 gCastingImpact_ImageY[]
u8 gCastingImpact_GlintDrawFlags[]
The flips a glint may take, picked at random.
struct BattleObj * GetBattleObjectSlotFar(s32 objectId)
Far call to BattleObjGet.
s32 BattleUnit_GetEffectHeightFar(s32 id)
void BattleMotion_ApplyVariantMotionFar(s32 id, s32 variant)
void ObjectDispatch_SelectAnimationFar(void *raw, s32 no)
void ObjectDispatch_SetAnimationSpeedFar(struct DispatchObject *obj, s32 val)
void AudioCommand_PlayFar(s32 id)
s32 Trig_Cos(s32 angle)
s32 Trig_Sin(s32 angle)
void Camera_ApplyShake(s32 rangeX, u32 rangeY)
Shakes the battle view while gBattleFxWork's shakeFrames lasts, moving BG1 and the projection centre ...
Definition hail.c:364
@ HEAP_SLOT_BATTLE_EFFECT
Definition heap_state.h:42
@ HEAP_SLOT_BLITTER_ALTERNATE
Definition heap_state.h:47
@ HEAP_SLOT_BATTLE_SHEET
Definition heap_state.h:44
@ HEAP_SLOT_CAMERA
Definition heap_state.h:38
#define WORK_SLOT(n)
Reads slots through their own addresses; adjacent slots are reached by offsets.
Definition heap_state.h:14
#define Q_8_8(n)
The BG2/BG3 affine matrix entries are signed 8.8 fixed point: texels stepped per screen pixel.
Definition io_reg.h:59
#define RGB(r, g, b)
A 15-bit colour from its red, green and blue levels (0 to 31 each), as the palette stores it: blue in...
Definition io_reg.h:164
#define BG_CHAR_BLOCK(n)
Definition io_reg.h:166
#define REG_BG2X
Definition io_reg.h:65
#define BLEND_ALPHA(top, bottom)
A REG_BLDALPHA value: top and bottom are the two targets' weights, out of 16.
Definition io_reg.h:145
#define REG_BLDALPHA
Definition io_reg.h:142
#define BG_PLTT
Definition io_reg.h:157
#define REG_BG2PA
Definition io_reg.h:61
#define REG_BLDCNT
Definition io_reg.h:119
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
static s32 Iwram_FillWords(void *destination, s32 size, u32 value)
Definition iwram_call.h:59
static s32 Iwram_ClearWords(void *destination, s32 size)
Definition iwram_call.h:55
#define SET_VELOCITY(p, vx, vy, vz)
Definition macros.h:18
#define SET_POSITION(p, px, py, pz)
Sets the position or the velocity of a particle, a piece of debris or an object in one statement.
Definition macros.h:16
#define CLAMP(v, low, high)
Keeps v within low to high, the low bound tested first.
Definition macros.h:23
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
void Runtime_ReleaseHeapBlock(s32 slot)
void BattleFx_FetchRectangleBlitters(s32 flip, BlitterPair out)
Builds the two usual routines that draw onto the 128x128 canvas, both clipped on both axes.
void BattleFx_RunSparkGroups(struct BattleEffectArgument *effect, s32 mode)
One to three bursts, 8 frames apart: each flashes, rings itself with twelve smaller flashes that fade...
Definition plasma.c:234
void EffectPosition_ProjectScaledBattler(s32 id, struct EffectPosition *position)
The screen position of a battler, scaled for the battle view, 16 pixels higher.
Definition positions.c:88
void EffectStep_AdvanceWithGravity3D(struct EffectStep *step, s32 damping, s32 gravity)
Moves a particle one frame in three dimensions: adds its velocity, then gravity to its vertical speed...
Definition positions.c:29
s32 EffectPosition_ProjectPoint(const void *point, struct EffectPosition *position)
Projects a point in the current transform onto the screen, 16 pixels higher, and returns the projecti...
Definition positions.c:64
void BattleFx_RunProjectileVolley(struct BattleEffectArgument *effect, s32 mode)
Projectile volley: shots fly from the user to each target and burst into a flash and sparks when they...
#define Ram_MapCellBuffer
Definition ram_buffer.h:10
char ResourceId_CounterRevealSheet
char ResourceId_VenusDjinnSmallSheet
char ResourceId_PinkBurstSheet
char ResourceId_MarsDjinnSmallSheet
char ResourceId_CyanPalette
char ResourceId_MercuryDjinnSmallSheet
char ResourceId_TornadoSheet
char ResourceId_YellowOrbSheet
char ResourceId_LimePalette
char ResourceId_FireStreakSheet
char ResourceId_MarsDjinnSheet
char ResourceId_IceChipSheet
char ResourceId_BlastSheet
char ResourceId_SkullSheet
char ResourceId_BlueFlameSheet
char ResourceId_LightningBoltSheet
char ResourceId_RedCrescentSheetA
char ResourceId_SmokeSheet
char ResourceId_BlueBeastSheet
char ResourceId_RockSpireSheet
char ResourceId_FlameSheetA
char ResourceId_ParticleSpritesA
char ResourceId_IceBlockSheet
char ResourceId_JupiterDjinnSmallSheet
char ResourceId_FlashBurstSheet
char ResourceId_RuneSheet
char ResourceId_EmberStreakSheet
char ResourceId_RedCrescentSheetB
char ResourceId_TanPalette
void Graphics_PackTileRows(u8 *src, u8 *dst, s32 stride, s32 rowCount)
Packs 288 rows of 40 bytes to half width, keeping every second byte (the bytes of the odd columns win...
s32 Scheduler_AddOrUpdateCallback(SchedulerCallback callback, s32 order)
Schedules callback with order, or gives a task already running it the new order, and re-sorts the tab...
Definition scheduler.c:102
s32 Scheduler_RemoveCallback(SchedulerCallback callback)
Unschedules the task running callback: clears its routine and gives it order 0x7fff,...
Definition scheduler.c:146
#define SE_133
Definition songs.h:126
#define SE_212
Definition songs.h:207
#define SE_HEAVY_IMPACT
Definition songs.h:127
void * gWorkSlot[]
void BattleFx_RunImpair(struct BattleEffectArgument *effect)
A shield appears over each target and breaks into shards that fade through darker copies,...
The battle camera: its eye, the focus it turns about, and its pitch, yaw and distance.
The command shared by an effect's caller, dispatcher and handlers.
s32 actor
the user's unit id
s32 side
1 when the targets are the party, 0 for enemies
s16 actors[8]
the targets' unit ids, count of them
s32 variant
the ability's size, from 0 (Quake, then Earthquake)
s32 prelude
1 plays a prelude: in some effects a Djinni appears
The running battle effect's work: its canvas image, 64 particles, the display registers it stages and...
s32 fadeFrames
backdrop fade frames left (Palette_StepFadeTransfer)
s32 frame
frame counter some effects keep, from 0
s32 fadeStep
CANVAS_FADE_STEPs the backdrop has darkened so far.
s32 shakeFrames
shake frames left; Camera_ApplyShake counts down
s32 transferMode
How the canvas transfer callbacks treat the canvas after each copy (enum CanvasTransferMode in effect...
s32 transferValue
The transfer mode's argument: the fill value, the share kept (50 or 75 percent) or the darken or brig...
struct BattleEffectArgument * effect
the command being run
struct EffectStep particles[64]
free while variant is -1
struct MotionObject * object
NULL once released.
A field object as the object dispatcher's far calls take it.
A point projected onto the screen, with its depth.
Definition effect_step.h:46
s32 y
screen pixels
Definition effect_step.h:59
s32 depth
projected depth, larger further away
Definition effect_step.h:60
s32 x
screen pixels
Definition effect_step.h:58
A moving point of an effect: its position, its velocity and a value each effect uses its own way (a s...
Definition effect_step.h:13
s32 velocityX
16.16 fixed-point pixels per frame
Definition effect_step.h:28
s32 z
16.16 fixed-point pixels
Definition effect_step.h:26
s32 variant
each effect's own: a size, a countdown, -1 for idle
Definition effect_step.h:31
s32 x
16.16 fixed-point pixels
Definition effect_step.h:24
s32 y
16.16 fixed-point pixels
Definition effect_step.h:25
s32 velocityY
16.16 fixed-point pixels per frame
Definition effect_step.h:29
s32 velocityZ
16.16 fixed-point pixels per frame
Definition effect_step.h:30
A position in three coordinates, each in 16.16 fixed point.
s32 z
16.16 fixed point
s32 x
16.16 fixed point
s32 y
16.16 fixed point
An effect object that moves and draws one sprite: its position, scale, velocity and target,...
Definition motobj.h:20
s32 velocityZ
16.16 fixed-point pixels per frame
Definition motobj.h:33
s32 velocityX
16.16 fixed-point pixels per frame
Definition motobj.h:31
s32 z
16.16 fixed-point pixels
Definition motobj.h:25
s32 y
16.16 fixed-point pixels
Definition motobj.h:24
u16 height
half the sprite's box height; FieldActor's radius
Definition motobj.h:29
s32 velocityY
16.16 fixed-point pixels per frame
Definition motobj.h:32
u16 angle
1/65536 turns
Definition motobj.h:22
s32 acceleration
Horizontal speed gained each frame towards the target, or lost braking without one,...
Definition motobj.h:37
s32 x
16.16 fixed-point pixels
Definition motobj.h:23
s32 verticalMotionStrength
Gravity: taken from velocityY each frame above the ground with motionFlags bit 1, in 16....
Definition motobj.h:48
void * Resource_GetTableEntry(s32 index)
Returns the ROM address of resource index from the resource directory.
Definition system.c:150
void BattleFx_RunMysticFlame(struct BattleEffectArgument *effect)
Glowing orbs.
void SceneTransform_ApplyYaw(s32 angle)
Rotates gTransform about the Y axis by angle.
Definition transform.c:113
void SceneTransform_ApplyPitch(s32 angle)
Rotates gTransform about the X axis by angle.
Definition transform.c:97
void Render_ResetTransformState(void)
Allocates the one-level save area (heap slot HEAP_SLOT_TRANSFORM_STACK), clears gTransformStackDepth ...
Definition transform.c:19
void SceneTransform_ApplyRoll(s32 angle)
Rotates gTransform about the Z axis by angle.
Definition transform.c:129
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
#define EFFECT_CLIP_BOUNDS
Clips overflow on all four sides.
Definition effect.h:224
DrawRectangle BlitterPair[2]
A blitter pair: [0] built in HEAP_SLOT_BLITTER and [1] in HEAP_SLOT_BLITTER_ALTERNATE (BattleFx_Fetch...
Definition effect.h:244