Alchemy
Loading...
Searching...
No Matches

kirin.c

Go to the documentation of this file.
1/// @file
2/// The Kirin summon.
3/// Part of effect.c, the battle effect object, compiled only through it.
4
6
7#define KIRIN_OBJECTS 9 ///< objects in the row, work->objects[0] to [8]
8/// One 48x32 fire blob picture; the fire sheet holds four.
9#define KIRIN_BLOB_BYTES (48 * 32)
10
11/// Over a scrolling forest a row of objects rides in on a wave trailing fire,
12/// then sweeps across the targets, each throwing a burst of motes as the row
13/// passes it.
14///
15/// Abilities: Kirin (scene kind 3).
16///
17/// The scene runs in two stages. In the sweep (120 frames; A or B ends it
18/// after frame 16), KIRIN_OBJECTS objects ride in on a sine wave while the
19/// backdrop scrolls, a ring of motes opens around them and a trail of fire
20/// follows. In the rise (96 frames) the row slides across the targets; each
21/// target the row passes throws a burst of 32 motes and plays its hit
22/// reaction, and the fire sheet slides off behind it. The battle shows the
23/// damage at the end. The scene leaves gProjection.centerY at 240.
24///
25/// Particles: work->particles 0 to 47 are the fire trail (x in pixels, y
26/// 16.16, variant the frames before it starts); PARTICLES 32i to 32i + 31
27/// are target i's burst motes, variant their frames left.
29 static struct SpriteScale sweepScale = {{{0x10000, 0x10000}}};
30 static struct SpriteScale riseScale = {{{0x10000, 0x10000}}};
31 void **cursor;
32 struct SpritePlacement place;
33 struct EffectPosition unit;
34 u8 jitterRow[16];
35 u8 hitRow[14];
36 struct SpritePlacement risePlace;
37 void *canvas;
38 struct BattleEffectWork *work;
39 DrawRectangle blit;
40 u8 *sheet;
41 s32 baseX;
42 s32 grow;
43 s32 savedX;
44 struct BattleBackgroundView *control;
45 s32 shift;
46 s32 wave;
47 s32 frame;
48 s32 i;
49
50 cursor = &gBattleFxWork[1];
51 canvas = cursor[0];
54 work->effect = effect;
59 PLTT_BACKDROP = 0;
60 BG_PLTT_COLOR(0, 1) = 0;
64 // Setup: wipe the battle away, swap in the forest backdrop behind a window
65 // 202 pixels wide, and wipe back.
68 gProjection.centerY = 240;
72 REG_WIN0H = WIN_RANGE(0, 202);
73 WaitFrames(1);
79
80 baseX = 0;
81 grow = 0;
82 savedX = gBgScroll[1].x;
84 shift = 0;
86 // The scene owns BG1's scroll until the rise stage.
87 control->busy = 1;
88
89 i = 0;
90 do {
91 struct EffectStep *fire = &work->particles[i];
92
93 // 64 fire particles start at random places, speeds and delays; only the
94 // first 48 are ever drawn.
95 fire->x = (Random16() & 31) + 16;
96 fire->y = ((Random16() & 31) + 48) << 16;
97 fire->velocityY = ((Random16() & 31) - 16) << 16;
98 fire->variant = Random16() % 48 + 2;
99 i++;
100 } while (i != 64);
101
105 // The canvas sits behind the row until frame 28.
108
109 // The sweep stage: 120 frames, which A or B ends after frame 16.
110 for (frame = 0; frame != 120; frame++) {
111 wave = 0;
112 if (frame == 0) {
114 }
115 if (frame == 26) {
117 }
118 if (frame == 40) {
120 }
121 if (frame == 72) {
123 }
124 if (frame == 104) {
126 }
127 if ((gKeysRepeat & (KEY_A | KEY_B)) != 0 && frame > 16) {
128 break;
129 }
130
131 // Speed the forest's scroll up over frames 24 to 55, to at most 24
132 // pixels a frame.
133 if (frame >= 24 && frame <= 55) {
134 shift++;
135 }
136 if (shift > 24) {
137 shift = 24;
138 }
139 // DEAD: always true, as the loop ends at frame 120; the ROM keeps the test.
140 if (frame <= 135) {
141 gBgScroll[1].x -= shift;
142 grow += shift;
143 }
144
145 // From frame 8 the row bobs on a sine wave (32 pixels high, 96 after
146 // frame 104) and shakes the screen every 32 frames; after frame 103 it
147 // lifts away to the left.
148 // DEAD: always true, as the loop ends at frame 120; the ROM keeps the test.
149 if (frame <= 149) {
150 struct SpriteScale pair = sweepScale;
151 s32 lift;
152
153 lift = 0;
154 if (frame > 103) {
155 lift = frame * 16 - 1664;
156 }
157 if (frame >= 8 && frame <= 31) {
158 baseX = baseX + shift - 8;
159 }
160 if (frame > 7) {
161 s32 amp;
162 s32 ang;
163
164 amp = 96;
165 if (frame <= 104) {
166 amp = 32;
167 }
168 // Folding the angle into the first half turn keeps the sine
169 // positive: the row hops rather than sways.
170 ang = (frame * 1024 - 0x2000) & 0xFFFF;
171 if (ang > 0x8000) {
172 ang -= 0x8000;
173 }
174 wave = (Trig_Sin(ang) * amp) >> 16;
175 if ((frame & 31) == 8) {
176 work->shakeFrames = 4;
177 }
178 }
179 place.groundHeight = 0;
180 place.height = 255 << 16;
181 i = 0;
182 do {
183 place.x = ((baseX + gKirin_Columns[i] - lift) << 16) + (224 << 16);
184 place.y = ((gKirin_Rows[i] - wave) << 16) + (144 << 15);
185 Object_ApplyProjectedPlacementFar(work->objects[i], &place, &pair, 0);
186 i++;
187 } while (i != KIRIN_OBJECTS);
188 }
189
190 // Over the first 27 frames a ring of motes opens around the row, eight
191 // more motes each frame.
192 if (frame <= 26) {
193 s32 size = grow + 4;
194 s32 count = frame * 8;
195
196 if (size > 10) {
197 size = 10;
198 }
199 if (count > 64) {
200 count = 64;
201 }
202 for (i = 0; i != count; i++) {
203 s32 ang = i << 10;
204 s32 x = ((Trig_Sin(ang) * (grow * 2 + 8)) >> 16) + grow + 96;
205 s32 y = ((Trig_Cos(ang) * (grow * 12 + 48)) >> 16) + 64;
206
207 EFFECT_DRAW_MOTE(blit, canvas, sheet, x, y, size);
208 }
209 }
210
211 if (frame == 24) {
213 }
214 if (frame == 28) {
217 }
218
219 // From frame 18 the 48 fire particles trail the row; each waits out its
220 // delay first, and one that leaves the canvas restarts behind the row.
221 if (frame > 17) {
222 i = 0;
223 do {
224 struct EffectStep *fire = &work->particles[i];
225
226 if (fire->variant == 0) {
227 s32 size = i % 3 + 1;
228
229 blit(canvas, sheet + gBattleFx_MoteCells[size - 1], fire->x,
230 Q_16_16_TO_INT(fire->y) - size, size, size * 2);
231 fire->x += 2;
232 fire->y += fire->velocityY;
233 fire->velocityY = fire->velocityY * 48 / 64;
234 } else {
235 fire->variant--;
236 }
237 if (fire->x > 128 || fire->variant == 1) {
238 fire->x = (Random16() & 31) + baseX + 172;
239 fire->y = ((Random16() & 31) - wave + 56) << 16;
240 fire->velocityY = ((Random16() & 31) - 16) << 15;
241 }
242 i++;
243 } while (i != 48);
244 }
245
246 // From frame 32 a column of six random fire blobs slides up to 40
247 // pixels left.
248 if (frame > 31) {
249 s32 rise = (frame - 32) / 2;
250 s32 y;
251
252 if (rise > 40) {
253 rise = 40;
254 }
255 i = 0;
256 y = 0;
257 do {
258 blit(canvas, work->sheet + (Random16() & 3) * KIRIN_BLOB_BYTES,
259 120 - rise, y, 48, 32);
260 i++;
261 y += 18;
262 } while (i != 6);
263 }
264
265 // While the shake timer runs, jitter the forest's vertical scroll;
266 // otherwise hold it at 32.
267 if (work->shakeFrames > 0) {
268 work->shakeFrames--;
269 gBgScroll[1].y = (Random16() & 7) + 28;
270 } else {
271 gBgScroll[1].y = 32;
272 }
274 }
275
276 // The rise stage: restore the backdrop and display, put the objects behind
277 // the background, and clear the hit marks and the burst motes.
278 gBgScroll[1].x = savedX;
279 control->busy = 0;
281 REG_WIN0H = WIN_RANGE(0, 240);
282
283 i = 0;
284 do {
285 struct AnimationObject *object = work->objects[i];
286
287 object->image.priority = 3;
288 i++;
289 } while (i != KIRIN_OBJECTS);
290
291 {
292 s32 rowX;
293 u8 *hit;
294 u8 *jitter;
295
296 rowX = 224;
297 hit = hitRow;
298 i = 0;
299 do {
300 hit[i] = 0;
301 i++;
302 } while (i != 14);
303 jitter = jitterRow;
304 i = 0;
305 do {
306 jitter[i] = Random16() & 31;
307 i++;
308 } while (i != 16);
309 // 320 burst motes are cleared, though only six targets' 192 are drawn.
310 i = 0;
311 do {
312 PARTICLES[i].variant = 0;
313 i++;
314 } while (i != 160 << 1);
315
319 REG_BLDALPHA = BLEND_ALPHA(16, 16);
320
321 frame = 0;
322 do {
323 // The row slides 16 pixels left a frame on a sine 64 pixels high for
324 // the first nine frames and 32 after.
325 if (frame <= 23) {
326 struct SpriteScale riseScaleCopy = riseScale;
327 s32 ang;
328 s32 rise;
329
330 rowX -= 16;
331 if (frame <= 8) {
332 ang = frame * 2048 + 0x4000;
333 if (ang > 0x8000) {
334 ang -= 0x8000;
335 }
336 rise = ((Trig_Sin(ang) * 64)) >> 16;
337 } else {
338 ang = frame * 2048 + 0x4000;
339 if (ang > 0x8000) {
340 ang -= 0x8000;
341 }
342 rise = ((Trig_Sin(ang) * 32)) >> 16;
343 }
344 risePlace.groundHeight = 0;
345 risePlace.height = 255 << 16;
346 i = 0;
347 do {
348 risePlace.x = (rowX + gKirin_Columns[i]) << 16;
349 risePlace.y = ((gKirin_Rows[i] - rise) << 16) + (144 << 15);
350 Object_ApplyProjectedPlacementFar(work->objects[i], &risePlace,
351 &riseScaleCopy, 0);
352 i++;
353 } while (i != KIRIN_OBJECTS);
354 }
355
356 // Frame 8: Camera_ApplyShake shakes the screen for 8 frames.
357 if (frame == 8) {
358 work->shakeFrames = frame;
360 }
361 if (frame == 11) {
363 }
364 if (frame == 46) {
366 }
367
369 // A target the row passes for the first time throws a burst of 32
370 // motes from 16 pixels above its projected point (x halved for the wide
371 // canvas), each already four frames along its path, and plays its hit
372 // reaction.
373 if (hit[i] == 0) {
375 if (unit.x > rowX) {
376 s32 k;
377
378 hit[i] = 1;
379 k = 0;
380 do {
381 struct EffectStep *burst = &PARTICLES[i * 32 + k];
382 s32 up;
383
384 burst->x = unit.x << 15;
385 burst->y = (unit.y - 16) << 16;
386 burst->velocityX = ((Random16() & 255) - 128) << 10;
387 up = (Random16() & 255) - 192;
388 burst->velocityY = up << 11;
389 burst->x += burst->velocityX * 4;
390 burst->y += up << 13;
391 burst->variant = (Random16() & 15) + 8;
392 k++;
393 } while (k != 32);
396 }
397 }
398 }
399
400 i = 0;
401 do {
402 // Each burst mote is drawn and moved until its life runs out.
403 struct EffectStep *burst = &PARTICLES[i];
404
405 if (burst->variant > 0) {
406 s32 size = 3;
407
408 EFFECT_DRAW_MOTE(blit, canvas, sheet, Q_16_16_TO_INT(burst->x),
409 Q_16_16_TO_INT(burst->y), size);
410 burst->x += burst->velocityX;
411 burst->y += burst->velocityY;
412 burst->variant--;
413 }
414 i++;
415 } while (i != 192);
416
417 if (frame == 48) {
419 }
420 // From frame 41 the fire sheet, 16 jittered rows, slides off to the
421 // left 12 pixels a frame over a canvas that keeps everything; from
422 // frame 65 the canvas fades again and the smear dies away.
423 if (frame > 40) {
424 s32 slide = (frame - 40) * 12;
425 s32 y;
426
428 i = 0;
429 y = -8;
430 do {
431 blit(canvas, work->sheet + (Random16() & 3) * KIRIN_BLOB_BYTES,
432 jitter[i] - slide + 120, y, 48, 32);
433 i++;
434 y += 8;
435 } while (i != 16);
436 }
437 if (frame > 64) {
439 }
440 // Frame 58: every target turns black in its damage pose.
441 if (frame == 58) {
444 POSE_DAMAGE, -1, 0);
445 }
446 }
447
448 Camera_ApplyShake(8, 8);
449 EFFECT_END_FRAME(work);
450 frame++;
451 } while (frame != 96);
452 }
453
454 // The finish: the battle shows the damage, then the objects and the
455 // canvas are released.
457 i = 0;
458 do {
460 i++;
461 } while (i != KIRIN_OBJECTS);
463}
464
465#undef KIRIN_OBJECTS
466#undef KIRIN_BLOB_BYTES
@ SPRITE_COLOR_BLACK
solid colour 1, black
Definition animspr.h:31
void * gBattleFxWork[]
@ POSE_DAMAGE
taking damage
Definition battle.h:176
@ COMPOSITE_ADDITIVE
BattleEffectDrawRectangle DrawRectangle
#define EFFECT_FOR_EACH_TARGET(i, effect)
Loops i over the effect's targets, effect->actors[0] to effect->actors[effect->count - 1]: the loops ...
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_WipeCanvas(s32 mode, s32 value)
Wipes the whole canvas to one colour with a ragged edge: each row (or column) lags a random 0 to 63 p...
Definition canvas.c:27
void BattleFx_ConfigureEffectDisplay(void)
Puts the battle screen back into the display state every effect starts from and ends with: no blendin...
Definition display.c:16
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
void BattleFx_SetupBlendedDisplay(void)
Shows the BG1 battle background behind a cleared canvas: clears the canvas buffer and the 16 KiB of V...
Definition dispatch.c:217
void BattleFx_SelectLivingTargets(struct BattleEffectArgument *effect)
Shows the battle objects of every unit with HP left on the targets' side: the enemies (units 128 to 1...
Definition dispatch.c:184
What every part of the battle effect object shares: its includes, macros and frame-loop helpers.
#define EFFECT_SHOW_WIDE_CANVAS(top)
Shows the canvas: the effect display mode, BG2 stretched to twice its width (the 128-pixel canvas sho...
#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,...
#define EFFECT_SET_TRANSFER(work, mode, value)
Sets how the canvas transfer treats what is left in the canvas after each copy: mode with value (see ...
@ CANVAS_COPY
copy as drawn
@ 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 EFFECT_CALLBACK_ORDER
The scheduler order of every per-frame effect callback (Scheduler_AddOrUpdateCallback sorts the frame...
#define EFFECT_FINISH_CANVAS_ONE_BLITTER()
The same for an effect that drew through one blitter.
#define EFFECT_SHOW_DAMAGE(hitSound)
Lets the battle show the damage: resumes its paused playback, which plays hitSound (an SE_ id; 0 keep...
#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_WIN0_LAYERS
Window 0, the battle view, shows BG0 to BG2, sprites and blending.
#define EFFECT_END_FRAME(work)
The usual end of an effect frame: counts down the targets' flash timers (ObjectGroup_TickMemberTimers...
u8 gKirin_Rows[]
u16 gBattleFx_MoteCells[]
Mote cells: particle sheets hold ten, cell n (from 0) n + 1 pixels wide and twice that tall.
u8 gKirin_Columns[]
void BattleFx_SpawnObjects(s32 entryCount, s32 source, u32 priority)
Creates the effect's animation objects in gBattleFxWork's objects.
Definition eruption.c:220
void BattleMotion_ApplyVariantMotionFar(s32 id, s32 variant)
void BattleBackground_LoadFar(s32 mode, s32 resource, s32 level)
void Object_ApplyProjectedPlacementFar(struct AnimationObject *sprite, const struct SpritePlacement *position, const struct SpriteScale *scale, s32 facing)
Far call to Render_ApplyProjectedPlacement.
void ResourceObject_ReleaseFar(struct AnimationObject *sprite)
void AudioCommand_PlayFar(s32 id)
s32 Trig_Cos(s32 angle)
s32 Trig_Sin(s32 angle)
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
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_BATTLE_BACKGROUND
Definition heap_state.h:45
@ HEAP_SLOT_BATTLE_SHEET
Definition heap_state.h:44
@ HEAP_SLOT_BLITTER
Definition heap_state.h:46
@ HEAP_SLOT_BATTLE_CANVAS
Definition heap_state.h:43
#define BGCNT_CHAR_BASE(block)
Definition io_reg.h:25
#define REG_WININ
Definition io_reg.h:81
#define BGCNT_256_COLORS
Definition io_reg.h:27
#define BGCNT_PRIORITY(n)
Definition io_reg.h:24
#define REG_WIN0H
Definition io_reg.h:74
#define WININ_WIN1_BG0
Definition io_reg.h:93
#define WININ_WIN1_CLR
Definition io_reg.h:98
@ KEY_B
Definition io_reg.h:244
@ KEY_A
Definition io_reg.h:243
#define PLTT_BACKDROP
Definition io_reg.h:155
#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 BGCNT_SCREEN_BASE(block)
Definition io_reg.h:28
#define REG_BG2CNT
Definition io_reg.h:21
#define REG_BLDALPHA
Definition io_reg.h:142
#define WIN_RANGE(start, end)
A REG_WINxH or REG_WINxV value: the window covers start up to, but not including, end.
Definition io_reg.h:80
#define WININ_WIN1_BG1
Definition io_reg.h:94
#define BG_PLTT_COLOR(palette, color)
Definition io_reg.h:158
#define WININ_WIN1_BG2
Definition io_reg.h:95
volatile u32 gKeysRepeat
Pressed keys, repeating while held: after 19 frames, then every 6.
#define KIRIN_BLOB_BYTES
One 48x32 fire blob picture; the fire sheet holds four.
Definition kirin.c:9
#define KIRIN_OBJECTS
objects in the row, work->objects[0] to [8]
Definition kirin.c:7
void BattleFx_RunKirin(struct BattleEffectArgument *effect)
Over a scrolling forest a row of objects rides in on a wave trailing fire, then sweeps across the tar...
Definition kirin.c:28
struct BgScroll gBgScroll[]
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
void EffectPosition_ProjectBattler(s32 id, struct EffectPosition *position)
The screen position of a battler, 16 pixels higher.
Definition positions.c:100
struct Projection gProjection
char ResourceId_ForestBackdrop
char ResourceId_ParticleSpritesA
char ResourceId_FireBlobSheet
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
#define SE_137
Definition songs.h:130
#define SE_136
Definition songs.h:129
#define SE_CAST
Definition songs.h:149
#define SE_141
Definition songs.h:134
#define SE_HEAVY_IMPACT
Definition songs.h:127
#define SE_145
Definition songs.h:138
void * gWorkSlot[]
An animated sprite: its image and shadow, scale, rotation, size and up to four animation layers.
Definition animspr.h:108
struct AnimationSpritePart image
Definition animspr.h:109
u16 priority
2 when created
Definition animspr.h:100
The battle background's work slot as the background code reads it: its display mode (2 draws it as an...
s32 busy
while set, BattleCamera_SetRange leaves BG1's scroll
The command shared by an effect's caller, dispatcher and handlers.
s16 actors[8]
the targets' unit ids, count of them
The running battle effect's work: its canvas image, 64 particles, the display registers it stages and...
s32 shakeFrames
shake frames left; Camera_ApplyShake counts down
struct AnimationObject * objects[16]
the effect's animation objects
s32 transferMode
How the canvas transfer callbacks treat the canvas after each copy (enum CanvasTransferMode in effect...
struct BattleEffectArgument * effect
the command being run
struct EffectStep particles[64]
free while variant is -1
u16 y
pixels
Definition map_scroll.h:65
A point projected onto the screen, with its depth.
Definition effect_step.h:46
s32 y
screen pixels
Definition effect_step.h:59
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 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 centerY
screen pixels
Definition project.h:17
A sprite's screen position and its two vertical offsets, in 16.16 pixels.
s32 x
screen x of the sprite's centre
s32 y
screen y at the ground; sets the depth slot
s32 groundHeight
how far the shadow is raised above y
s32 height
how far the image is raised above y
Horizontal and vertical sprite scale; 0x10000 is the original size.
signed int s32
Definition types.h:14
unsigned char u8
Definition types.h:11
#define EFFECT_CLIP_BOUNDS
Clips overflow on all four sides.
Definition effect.h:224
#define Q_16_16_TO_INT(v)
Definition effect.h:231
#define PARTICLES
The map cell buffer (gMapCellBuffer), which the effects borrow, has two views here: Ram_MapCellBuffer...
Definition effect.h:221