Alchemy
Loading...
Searching...
No Matches

transition.c

Go to the documentation of this file.
1/// @file
2/// Window transitions and dissolves between scenes.
3/// Part of object.c, the field object, compiled only through it.
4
6
7/// Each frame, before drawing: turns windows 0 and 1 on, writes the first four
8/// values of the shown page into WININ, WINOUT, WIN0H and WIN1H, and has DMA 0
9/// copy the rest into WIN0H and WIN1H one word each HBlank, so that every line
10/// of the screen gets its own window edges.
11///
12/// A scheduler callback (priority 0x480) that DisplayTransition_Start,
13/// DisplayTransition_Finish and DisplayTransition_InitializeBattleEffectState
14/// add for a window transition, and DisplayTransition_UpdateScanlineTable
15/// removes when it ends.
18 u16 *scanlineData = work->pages[work->page];
19 volatile u16 *dma0 = REG_DMA0;
20
21 // Stop the previous transfer before priming the first line's registers.
23 dma0[5] &= ~DMA_ENABLE;
24 (void)dma0[5];
26 Io_Write16(*scanlineData++, &REG_WININ);
27 Io_Write16(*scanlineData++, &REG_WINOUT);
28 Io_Write16(*scanlineData++, &REG_WIN0H);
29 Io_Write16(*scanlineData++, &REG_WIN1H);
32 // HBlank repeats one word: both horizontal window bounds for the next line.
33 Dma_Set(scanlineData, (void *)&REG_WIN0H, DMA_HBLANK_WORD_PER_LINE,
34 (volatile u32 *)dma0);
35}
36
37/// A run of screen lines in a transition shape: one window edge pair for every
38/// line, or, when that is 0, edges moving from left x0 and right y0 to left
39/// x1 and right y1 down the run.
41 u16 cnt; ///< lines
43 u8 x0; ///< screen pixels
44 u8 x1; ///< screen pixels
45 u8 y0; ///< screen pixels
46 u8 y1; ///< screen pixels
47};
48
49/// One step of the shape window transition: the WININ and WINOUT values,
50/// swapped as the transition opens or closes, and three runs of lines.
55};
56
58
59/// Each frame of a window transition: advances its value toward its end (and
60/// at the end turns the windows off, or moves to the next mode), then builds
61/// the next page of window edges, WININ and WINOUT first and then a
62/// (left << 8 | right) pair for each of the 160 lines. Bit 5 of the value says
63/// whether the transition opens or closes; its low 5 bits are its progress.
64/// Modes: 0 random bars, 1 a shape from gDisplayTransition_Shapes, 2 random
65/// horizontal noise, 3 a circle at the screen's centre, 4 alternate lines
66/// wiping in from the left and the right, 7 and 9 an ellipse wider than tall
67/// around the party leader (7 flickers), 8 a wobbling ellipse around a point,
68/// 77 an ellipse a little taller than wide, centred 8 pixels lower on the
69/// leader.
70///
71/// A scheduler callback (priority 0xc80), added with
72/// BattleFx_StartWindowHBlankDma; it removes both and stops DMA 0 when a
73/// transition with no next mode ends. Writes the hidden page of the
74/// transition state (heap slot 31) and then flips state->page.
76 struct DisplayTransitionState *state;
77 struct TransitionSpan *span;
78 struct TransitionShape *shape;
79 struct MapScrollWork *map;
80 u16 *scanlineData;
81#ifndef UBFIX
82 // UB: viewX is a scalar, so indexing its address cannot reach viewY.
83 // Direct named members grow the English function from 0x90c to 0x914
84 // bytes. Modern code reads the actual members below.
85 s32 *view7;
86 s32 *view8;
87 s32 *view9;
88 s32 *view77;
89#endif
90 struct ObjectRuntime *obj;
91 u32 i;
92 s32 k;
93 s32 lineCount;
94 s32 value;
95 s32 radiusSquaredQ16;
96 s32 delta;
97 u32 horizontalExtent;
98 s32 left;
99 s32 right;
100 s32 centerX;
101 s32 centerY;
102
103 s32 cameraY;
104 s32 cameraX;
105 s32 cameraY8;
106 u32 leftEdge;
107 s32 oppositeEdges;
108
109 map = gMapWork[0];
110#ifdef UBFIX
111 state = gDisplayTransition;
112#else
113 // UB: this indexes past gMapWork into the separate transition-work cell.
114 // The named cell grows 0x90c to 0x910 native bytes in English.
115 state = gMapWork[23];
116#endif
117
118 // Finish or advance the timed phase before generating the hidden page.
119 if (state->duration != 0) {
120 if (state->step >= state->duration) {
121 state->duration = 0;
122 if (state->nextMode == 0) {
123 // A transition that closed (value 64) also turns backgrounds 1 to 3
124 // and the sprites off, so the screen stays covered; one that opened
125 // just turns the windows off.
126 if (state->end == 64) {
129 } else {
131 }
136 {
137 volatile u16 *dma0 = REG_DMA0;
138
140 dma0[5] &= ~DMA_ENABLE;
141 dma0[5];
142 }
143 return;
144 }
145 state->mode = state->nextMode;
146 } else {
147 state->value =
148 state->start +
149 Iwram_SignedDivide((state->end - state->start) * ++state->step,
150 state->duration);
151 }
152 }
153
154 scanlineData = state->pages[1 ^ state->page];
155 UiWindow_FillFromSceneFar(scanlineData + 2);
156
157 // Each mode writes WININ and WINOUT first. 0x7f7f shows every layer inside
158 // both windows and 1 only background 0 outside them; a mode swaps the two
159 // to cover the screen inside the windows instead. Several modes fold the
160 // progress when bit 5 is set, so that closing runs their table backwards.
161 switch (state->mode) {
162 case 0:
163 *scanlineData++ = 0x7f7f;
164 *scanlineData++ = 1;
165 value = state->value;
166 if (value & 32) {
167 value = 32 - (value & 31);
168 } else {
169 value &= 31;
170 }
171 // Each line shows one bar of the table's width at a random place on the
172 // 240-pixel line.
173 value = gDisplayTransition_NoiseWidths[value];
174 for (i = 0; i < 160; i++) {
175 leftEdge = (Random16() * (241 - value)) >> 16;
176 scanlineData[0] = ((leftEdge << 8) | (leftEdge + value));
177 scanlineData += 2;
178 }
179 break;
180
181 case 1:
182 value = state->value;
183 shape = &gDisplayTransition_Shapes[value & 31];
184 if (value & 32) {
185 *scanlineData++ = shape->first;
186 *scanlineData++ = shape->second;
187 } else {
188 *scanlineData++ = shape->second;
189 *scanlineData++ = shape->first;
190 }
191 // Three runs of lines, each one fixed edge pair or a slope between two.
192 for (i = 0; i < 3; i++) {
193 span = &shape->spans[i];
194 lineCount = span->cnt;
195 right = span->val;
196 if (lineCount != 0) {
197 if (right != 0) {
198 for (k = 0; k < lineCount; k++) {
199 scanlineData[0] = right;
200 scanlineData += 2;
201 }
202 } else {
203 // These shared temporaries hold the left and right endpoints here.
204 // Four span-local endpoints shrink 0x90c to 0x8bc bytes in English;
205 // keep this reuse until a plain form matches.
206 radiusSquaredQ16 = span->x0;
207 left = span->y0;
208 delta = span->x1;
209 right = span->y1;
210 for (k = 0; k < lineCount; k++) {
211 scanlineData[0] = (((radiusSquaredQ16 +
212 (delta - radiusSquaredQ16) * k / lineCount)
213 << 8) +
214 (left + (right - left) * k / lineCount));
215 scanlineData += 2;
216 }
217 }
218 }
219 }
220 break;
221
222 case 2:
223 value = state->value - 1;
224 if (value & 32) {
225 *scanlineData++ = 1;
226 *scanlineData++ = 0x7f7f;
227 } else {
228 *scanlineData++ = 0x7f7f;
229 *scanlineData++ = 1;
230 }
231 // Each line's window runs from the left edge to 16 pixels per step of
232 // progress plus up to 15 at random, at most 255.
233 value &= 31;
234 for (i = 0; i < 160; i++) {
235 horizontalExtent = (value << 4) + ((16 * Random16()) >> 16);
236 if (horizontalExtent > 255) {
237 horizontalExtent = 255;
238 }
239 scanlineData[0] = horizontalExtent;
240 scanlineData += 2;
241 }
242 break;
243
244 case 3: {
245#ifdef UBFIX
246 // Closing writes four header halfwords, then 160 pairs: its last write
247 // crosses the 322-halfword page. Keep that write into the following page
248 // or solid tile, using bytes of the whole allocated state rather than an
249 // out-of-bounds page pointer or a u16 alias of the u32 solid tile.
250 u8 *pageBytes = (u8 *)state + (1 ^ state->page) * sizeof(state->pages[0]);
251 const u16 header[4] = {0x7f7f, 1, 1, 0x7f7f};
252 u32 headerSize = 2 * sizeof(u16);
253 u16 edges;
254#endif
255 value = state->value;
256#ifdef UBFIX
257 if ((u32)value > 32) {
258 headerSize = sizeof(header);
259 value = 64 - value;
260 }
261 memcpy(pageBytes, header, headerSize);
262 pageBytes += headerSize;
263#else
264 *scanlineData++ = 0x7f7f;
265 *scanlineData++ = 1;
266 if ((u32)value > 32) {
267 *scanlineData++ = 1;
268 *scanlineData++ = 0x7f7f;
269 value = 64 - value;
270 }
271#endif
272 // A circle about the screen's centre (120, 80) whose radius grows 5
273 // pixels a step, to 160 at 32; each line's edges are clamped to the
274 // screen.
275 value *= 5;
276 radiusSquaredQ16 = (value * value) << 16;
277 for (i = 0; i < 160; i++) {
278 value = i - 80;
279 horizontalExtent =
280 Iwram_Sqrt(radiusSquaredQ16 - ((value * value) << 16)) >> 8;
281 left = 120 - horizontalExtent;
282 right = horizontalExtent + 120;
283 if (left < 0) {
284 left = 0;
285 }
286 if (right < 0) {
287 right = 0;
288 }
289 if (left > 240) {
290 left = 240;
291 }
292 if (right > 240) {
293 right = 240;
294 }
295#ifdef UBFIX
296 edges = (left << 8) + right;
297 memcpy(pageBytes, &edges, sizeof(edges));
298 pageBytes += 2 * sizeof(u16);
299#else
300 scanlineData[0] = ((left << 8) + right);
301 scanlineData += 2;
302#endif
303 }
304 break;
305 }
306
307 case 4:
308 value = state->value;
309 if (value & 32) {
310 *scanlineData++ = 1;
311 *scanlineData++ = 0x7f7f;
312 } else {
313 *scanlineData++ = 0x7f7f;
314 *scanlineData++ = 1;
315 }
316 // Even lines' windows grow from the left edge and odd lines' from the
317 // right, by up to 240 pixels over 32 steps.
318 value = (u32)(240 * (value & 31)) >> 5;
319 for (i = 0; i < 160; i += 2) {
320 oppositeEdges = ((240 - value) << 8) + 240;
321 scanlineData[0] = value;
322 scanlineData[2] = oppositeEdges;
323 scanlineData += 4;
324 }
325 break;
326
327 case 7: {
328 s32 ax;
329
330#ifdef UBFIX
331 ax = map->viewX & 0xffff0000;
332 cameraY = map->viewY & 0xffff0000;
333#else
334 view7 = &map->viewX;
335 ax = view7[0] & 0xffff0000;
336 cameraY = view7[1] & 0xffff0000;
337#endif
338 // Centred on the leader's place on screen, 16 pixels above its feet.
340 centerX = (obj->x - ax) / 0x10000;
341 centerY = ((obj->z - obj->y) - cameraY) / 0x10000 - 16;
342 // The frame counter is read here, where mode 8 forms its index, and the
343 // value is not used.
345 // Mode 7 writes the two masks and folds the progress the other way round
346 // from mode 9.
347 *scanlineData++ = state->active;
348 *scanlineData++ = state->mask;
349 value = state->value;
350 if ((value & 32) == 0) {
351 value = 32 - (value & 31);
352 } else {
353 value &= 31;
354 }
355 // On odd frames the ellipse has no size: the flicker.
356 if (gFrameCount & 1) {
357 value = 0;
358 }
359 // Half-widths of sqrt(r * r - 1.5 * dy * dy) with r 5 pixels a step: an
360 // ellipse 1.22 times as wide as it is tall.
361 value *= 5;
362 radiusSquaredQ16 = (value * value) << 16;
363 for (i = 0; i < 160; i++) {
364 value = i - centerY;
365 horizontalExtent =
366 Iwram_Sqrt(radiusSquaredQ16 - ((value * value * 3) << 15)) >> 8;
367 left = centerX - horizontalExtent;
368 right = centerX + horizontalExtent;
369 if (left < 0) {
370 left = 0;
371 }
372 if (right < 0) {
373 right = 0;
374 }
375 if (left > 240) {
376 left = 240;
377 }
378 if (right > 240) {
379 right = 240;
380 }
381 scanlineData[0] = ((left << 8) + right);
382 scanlineData += 2;
383 }
384 break;
385 }
386 case 8:
387#ifdef UBFIX
388 cameraX = map->viewX & 0xffff0000;
389 cameraY8 = map->viewY & 0xffff0000;
390#else
391 view8 = &map->viewX;
392 cameraX = view8[0] & 0xffff0000;
393 cameraY8 = view8[1] & 0xffff0000;
394#endif
395 // Centred on state->centreX and centreY; each line's edges move out by
396 // gDisplayTransition_EdgeOffsets, indexed two entries a line and one a
397 // frame, so the outline ripples.
398 centerX = (state->centreX - cameraX) / 0x10000;
399 centerY = (state->centreY - cameraY8) / 0x10000 - 16;
400 k = -centerY * 2 + gFrameCount;
401 *scanlineData++ = state->mask;
402 *scanlineData++ = state->active;
403 value = state->value;
404 if (value & 32) {
405 value = 32 - (value & 31);
406 } else {
407 value &= 31;
408 }
409 value *= 5;
410 radiusSquaredQ16 = (value * value) << 16;
411 for (i = 0; i < 160; i++) {
412 value = i - centerY;
413 horizontalExtent =
414 Iwram_Sqrt(radiusSquaredQ16 - ((value * value * 3) << 15)) >> 8;
415 left = centerX - horizontalExtent;
416 right = centerX + horizontalExtent;
417 if (left < right) {
418 delta = gDisplayTransition_EdgeOffsets[k & 31];
419 left -= delta;
420 right += delta;
421 // An offset that closes the line leaves it with no window.
422 if (left >= right) {
423 left = 240;
424 right = 240;
425 }
426 }
427 if (left < 0) {
428 left = 0;
429 }
430 if (right < 0) {
431 right = 0;
432 }
433 if (left > 240) {
434 left = 240;
435 }
436 if (right > 240) {
437 right = 240;
438 }
439 scanlineData[0] = ((left << 8) + right);
440 scanlineData += 2;
441 k += 2;
442 }
443 break;
444
445 case 9: {
446 s32 ax;
447
448#ifdef UBFIX
449 ax = map->viewX & 0xffff0000;
450 cameraY = map->viewY & 0xffff0000;
451#else
452 view9 = &map->viewX;
453 ax = view9[0] & 0xffff0000;
454 cameraY = view9[1] & 0xffff0000;
455#endif
457 centerX = (obj->x - ax) / 0x10000;
458 centerY = ((obj->z - obj->y) - cameraY) / 0x10000 - 16;
459 // As in mode 7, the frame counter is read and not used.
461 *scanlineData++ = state->mask;
462 *scanlineData++ = state->active;
463 value = state->value;
464 if (value & 32) {
465 value = 32 - (value & 31);
466 } else {
467 value &= 31;
468 }
469 value *= 5;
470 radiusSquaredQ16 = (value * value) << 16;
471 for (i = 0; i < 160; i++) {
472 value = i - centerY;
473 horizontalExtent =
474 Iwram_Sqrt(radiusSquaredQ16 - ((value * value * 3) << 15)) >> 8;
475 left = centerX - horizontalExtent;
476 right = centerX + horizontalExtent;
477 if (left < 0) {
478 left = 0;
479 }
480 if (right < 0) {
481 right = 0;
482 }
483 if (left > 240) {
484 left = 240;
485 }
486 if (right > 240) {
487 right = 240;
488 }
489 scanlineData[0] = ((left << 8) + right);
490 scanlineData += 2;
491 }
492 break;
493 }
494 case 77: {
495 s32 ax;
496
497#ifdef UBFIX
498 ax = map->viewX & 0xffff0000;
499 cameraY = map->viewY & 0xffff0000;
500#else
501 view77 = &map->viewX;
502 ax = view77[0] & 0xffff0000;
503 cameraY = view77[1] & 0xffff0000;
504#endif
506 centerX = (obj->x - ax) / 0x10000;
507 // Centred 8 pixels above the leader's feet, not 16 as in modes 7 and 9.
508 centerY = ((obj->z - obj->y) - cameraY) / 0x10000 - 8;
509 // As in mode 7, the frame counter is read and not used.
511 *scanlineData++ = state->mask;
512 *scanlineData++ = state->active;
513 value = state->value;
514 if (value & 32) {
515 value = 32 - (value & 31);
516 } else {
517 value &= 31;
518 }
519 // sqrt(r * r - 0.75 * dy * dy): 1.15 times as tall as it is wide.
520 value *= 5;
521 radiusSquaredQ16 = (value * value) << 16;
522 for (i = 0; i < 160; i++) {
523 value = i - centerY;
524 horizontalExtent =
525 Iwram_Sqrt(radiusSquaredQ16 - ((value * value * 3) << 14)) >> 8;
526 left = centerX - horizontalExtent;
527 right = centerX + horizontalExtent;
528 if (left < 0) {
529 left = 0;
530 }
531 if (right < 0) {
532 right = 0;
533 }
534 if (left > 240) {
535 left = 240;
536 }
537 if (right > 240) {
538 right = 240;
539 }
540 scanlineData[0] = ((left << 8) + right);
541 scanlineData += 2;
542 }
543 break;
544 }
545 }
546
547 // Publish only after every span of the next page is ready.
548 state->page ^= 1;
549}
550
551/// Starts window transition mode at value 0, with no duration: the caller
552/// moves it.
553///
554/// Called by Reveal's effect (mode 8) and, through
555/// DisplayTransition_InitializeBattleEffectStateFar, by three scenes (Altmiller
556/// Cave, Tret Tree's branch and Babi's palace). Allocates and clears the
557/// transition state in heap slot 31 and adds both window callbacks; mask
558/// 0x3f3f shows every layer in both windows.
560 struct DisplayTransitionState *state =
561 Runtime_AllocateBlock(31, sizeof(*state));
562
563 Dma_Fill32(0, state, sizeof(*state));
564 state->mode = mode;
565 state->value = 0;
566 state->mask = 0x3f3f;
567 state->active = 1;
570}
571
572/// Suspends the window transition's two callbacks.
573///
574/// XXX: nothing in ☀️ names it and it has no far-call entry, yet it is not
575/// marked UNUSED; a caller through a table or ⚓️'s twin would settle it.
579}
580
581/// Allocates the display transition state (heap block 31) and clears it.
582///
583/// Called by DisplayTransition_Start and DisplayTransition_Finish for every
584/// kind but the two fades.
586 struct DisplayTransitionState *state;
587
588 state = Runtime_AllocateBlock(31, sizeof(*state));
589 Dma_Fill32(0, state, sizeof(*state));
590 return state;
591}
592
593/// Fades the screen in over frames frames. mode's high byte chooses how: 0
594/// from black, 1 from the backdrop colour, 2 a window transition (its low
595/// byte the mode), 3 a dissolve, 4 opening from the centre line (low byte 1
596/// flickers).
597///
598/// Called by Field_RunScene to fade a scene in with work->transition and
599/// work->transitionFrames, and by Event_StartScreenTransition for scene
600/// scripts. Kinds 2 to 4 add a scheduler callback (and kind 4 the VCount
601/// handler, IRQ 1) that runs the transition; every kind ORs the map's display
602/// bits into DISPCNT through the I/O write queue.
604 struct DisplayTransitionWindow *display;
605 s32 variant;
606 s32 transitionKind;
607
608 transitionKind = (mode >> 8) & 0xff;
609 display = gMapWork[0];
610 variant = mode & 0xff;
611 switch (transitionKind) {
612 case 0:
613 Fade_ToBlack(0);
614 Fade_FromBlack(frames);
615 WaitFrames(1);
616 break;
617 case 1:
620 WaitFrames(1);
622 0x20000);
624 return;
625 // The window, dissolve and centre-line kinds open from value 0 (closed) to
626 // 32 (open), or for the centre line from 80 to 0, over frames frames.
627 case 2: {
628 struct DisplayTransitionState *state =
630 state->mode = variant;
631 state->value = 0;
632 state->mask = 63;
633 state->active = 1;
636 WaitFrames(1);
638 0x20000);
639 state->start = 0;
640 state->end = 32;
641 state->duration = frames;
642 state->step = 0;
643 return;
644 }
645 case 3: {
646 struct DisplayTransitionState *state =
648 state->mode = variant;
649 state->value = 32;
651 WaitFrames(1);
654 0x20000);
655 state->start = 0;
656 state->end = 32;
657 state->duration = frames;
658 state->step = 0;
659 return;
660 }
661 case 4: {
662 struct DisplayTransitionState *state;
663
664 display = gMapWork[0];
666 display->firstLine = 80;
667 display->secondLine = 80;
668 WaitFrames(1);
669 if (variant == 0) {
672 state->start = 80;
673 state->end = 0;
674 state->duration = frames;
675 state->step = 0;
676 } else {
679 state->start = 80;
680 state->end = 0;
681 state->duration = frames;
682 state->step = 0;
683 }
684 break;
685 }
686 }
688 0x20000);
689}
690
691/// Fades the screen out over frames frames, as DisplayTransition_Start fades
692/// it in.
693///
694/// Called by Field_RunScene as a scene ends, by Field_StartEncounterTransition
695/// before a battle (kinds 0x401 and 0x207), and by Event_FinishScreenTransition
696/// for scene scripts. The window and dissolve kinds run from value 32 to 64,
697/// the centre line from 0 to 80.
699 s32 variant;
700 s32 transitionKind;
701
702 transitionKind = (mode >> 8) & 0xff;
703 variant = mode & 0xff;
704 switch (transitionKind) {
705 case 0:
707 Fade_ToBlack(frames);
708 break;
709 case 1:
712 break;
713 case 2: {
714 struct DisplayTransitionState *state =
716 state->mode = variant;
717 state->value = 32;
718 state->mask = 63;
719 state->active = 1;
722 WaitFrames(1);
723 state->start = 32;
724 state->end = 64;
725 state->duration = frames;
726 state->step = 0;
727 break;
728 }
729 case 3: {
730 struct DisplayTransitionState *state =
732 state->mode = variant;
733 state->value = 32;
735 WaitFrames(1);
737 state->start = 32;
738 state->end = 64;
739 state->duration = frames;
740 state->step = 0;
741 break;
742 }
743 case 4: {
744 struct DisplayTransitionState *state =
746 if (variant == 0) {
749 state->start = 0;
750 state->end = 80;
751 state->duration = frames;
752 state->step = 0;
753 } else {
756 state->start = 0;
757 state->end = 80;
758 state->duration = frames;
759 state->step = 0;
760 }
761 break;
762 }
763 }
764}
765
766/// Clears the map's display-control bits for backgrounds 3, 2 and 1
767/// (0x0800, 0x0400 and 0x0200) for each argument that is set.
768///
769/// Reached only through its unnamed far-call veneer. DisplayTransition_Start
770/// ORs these bits into DISPCNT.
771void DisplayState_ClearFlags(s32 clearBg3, s32 clearBg2, s32 clearBg1) {
772 struct DisplayTransitionWindow *state;
773
774 state = gMapWork[0];
775 if (state != NULL) {
776 if (clearBg1 != 0) {
777 state->dispcnt &= 0xFDFF;
778 }
779 if (clearBg2 != 0) {
780 state->dispcnt &= 0xFBFF;
781 }
782 if (clearBg3 != 0) {
783 state->dispcnt &= 0xF7FF;
784 }
785 }
786}
787
788/// Each frame of the centre-line transition: moves the value toward its end
789/// and sets the two lines where the screen switches between shown and blank.
790///
791/// A scheduler callback added by DisplayTransition_Start and
792/// DisplayTransition_Finish for kind 4, variant 0; when its run ends it removes
793/// itself and the VCount handler.
796 struct DisplayTransitionWindow *display =
798 s8 *duration = &state->duration;
799 u32 edgeDistance;
800
801 if (*duration != 0) {
802 s8 *step = &state->step;
803
804 if (*step >= *duration) {
805 *duration = 0;
807 Runtime_SetIrqHandler(1, 0, 0);
808 return;
809 } else {
810 s32 delta = state->end - state->start;
811 s32 value;
812
813 (*step)++;
814 value = Iwram_SignedDivide(delta * *step, *duration);
815 state->value = state->start + value;
816 }
817 }
818
819 // At 80 or more the lines are put below the screen's 160, so no switch
820 // happens.
821 edgeDistance = state->value;
822 if (edgeDistance > 79) {
823 display->firstLine = 200;
824 display->secondLine = 250;
825 } else {
826 display->firstLine = edgeDistance;
827 display->secondLine = 159 - edgeDistance;
828 }
829}
830
831/// As DisplayTransition_Update, but the lines flicker every other frame.
832///
833/// The kind 4 callback for a non-zero variant.
836 struct DisplayTransitionWindow *window =
838 s8 *duration = &state->duration;
839 u16 edgeDistance;
840
841 if (*duration != 0) {
842 s8 *step = &state->step;
843
844 if (*step >= *duration) {
845 *duration = 0;
847 Runtime_SetIrqHandler(1, 0, 0);
848 return;
849 } else {
850 s32 delta = state->end - state->start;
851 s32 edgeOffset;
852
853 (*step)++;
854 edgeOffset = Iwram_SignedDivide(delta * *step, *duration);
855 state->value = state->start + edgeOffset;
856 }
857 }
858
859 edgeDistance = state->value;
860 if (edgeDistance > 79) {
861 window->firstLine = 200;
862 window->secondLine = 250;
863 } else if (edgeDistance != 0 && (gFrameCount & 1)) {
864 window->firstLine = edgeDistance + 80;
865 window->secondLine = 80 - edgeDistance;
866 } else {
867 window->firstLine = 0;
868 window->secondLine = 159;
869 }
870}
871
872/// The VCount interrupt of the centre-line transition: switches the display
873/// to mode 2 (nothing shown) at the first line and back to mode 0 at the
874/// second.
875///
876/// Installed as IRQ handler 1 by DisplayTransition_Start and
877/// DisplayTransition_Finish for kind 4. Phases: 0 waits for a line on screen,
878/// 1 for whichever edge line comes first, 2 and 3 for the other one; 9 is done.
880 u32 scanline;
881 struct DisplayTransitionWindow *state;
882
883 scanline = REG_VCOUNT;
884 state = gMapWork[0];
885
886 // Keep the same VCount sample across phase changes. A for/switch retry loop
887 // shrinks the English function from 0xd2 to 0xba bytes.
888again:
889 switch (state->irqPhase) {
890 case 3:
891 if (scanline >= state->irqFirstLine) {
893 state->irqPhase = 9;
894 }
895 break;
896 case 2:
897 if (scanline >= state->irqSecondLine) {
898 REG_DISPCNT &= 0xfff8;
899 state->irqPhase = 9;
900 }
901 break;
902 case 1:
903 if (scanline >= state->irqFirstLine) {
905 state->irqPhase++;
906 goto again;
907 }
908 if (scanline >= state->irqSecondLine) {
909 REG_DISPCNT &= 0xfff8;
910 state->irqPhase = 3;
911 goto again;
912 }
913 break;
914 case 0:
915 if (scanline <= 158) {
916 state->irqPhase = 1;
917 goto again;
918 }
919 break;
920 }
921}
922
923/// Each frame of the dissolve: moves the value toward its end and sets two
924/// more of the solid tile's 64 pixels, in the order of
925/// gDisplayTransition_DitherTable, to colour 15 (or 0 when closing).
926///
927/// The dissolve's scheduler callback (kind 3 of DisplayTransition_Start and
928/// DisplayTransition_Finish, and DisplayTransition_InitializeState). Queues the
929/// changed tile into character block 0 each frame.
932 s32 ditherIndex;
933 s32 colorIndex;
934 u32 i;
935
936 if (state->duration != 0) {
937 if (state->step >= state->duration) {
938 volatile u16 *dma0;
939 state->duration = 0;
941 dma0 = REG_DMA0;
943 dma0[5] &= ~DMA_ENABLE;
944 (void)dma0[5];
945 return;
946 } else {
947 s32 delta = state->end - state->start;
948 state->step++;
949 state->value = state->start +
950 Iwram_SignedDivide(delta * state->step, state->duration);
951 }
952 }
953
954 ditherIndex = state->value - 1;
955 state->ditherToggle ^= 1;
956 colorIndex = 0;
957 if (ditherIndex & 32)
958 colorIndex = 15;
959 ditherIndex = (ditherIndex & 31) * 2;
960 for (i = 0; i <= 1; i++) {
961 u8 pixelIndex = gDisplayTransition_DitherTable[ditherIndex & 63];
962 u8 *tileByte = &state->paletteNibbles[pixelIndex >> 1];
963 if (pixelIndex & 1)
964 *tileByte = (*tileByte & 0x0f) | (colorIndex << 4);
965 else
966 *tileByte = (*tileByte & 0xf0) | colorIndex;
967 ditherIndex++;
968 }
969
971 (DMA_ENABLE | DMA_32BIT) << 16 | 0x8);
972}
973
974/// Fills background screen block 4 with tile 0 in palette 15 and, unless color
975/// is -1, makes tile 0 solid color.
976///
977/// Sets up the dissolve for DisplayTransition_Start (colour 15),
978/// DisplayTransition_Finish and DisplayTransition_InitializeState (colour 0).
981 Dma_Fill32(0xf000f000, BG_SCREEN_BLOCK(4), 0x500);
982 if (color != -1) {
983 u32 pattern = 0;
984 s32 remainingWords;
985 u32 *tile;
986 for (remainingWords = 7; remainingWords >= 0; --remainingWords)
987 pattern = (pattern << 4) | color;
988 tile = work->solidTile;
989 for (remainingWords = 7; remainingWords >= 0; --remainingWords)
990 *tile++ = pattern;
992 (DMA_ENABLE | DMA_32BIT) << 16 | 0x8, REG_DMA3);
993 }
994}
995
996/// Starts a dissolve of the given mode and waits 120 frames.
997///
998/// It sets no duration, so the dissolve stays at value 0 until a caller moves
999/// it. Reached only through its unnamed far-call veneer.
1001 struct DisplayTransitionState *state;
1002
1003 state = Runtime_AllocateBlock(0x1f, sizeof(*state));
1004 Dma_Fill32(0, state, sizeof(*state));
1006 state->mode = value;
1007 state->value = 0;
1009 WaitFrames(0x78);
1010}
void * ObjectTable_Get(u32 actorIndex)
Returns the actor in slot index (0 to 65), or NULL.
Definition actors.c:462
void(*) SchedulerCallback(void)
void BattleFx_ApplyColorToTargetBuffer(s32 mode, s32 half)
Sets the colours the buffer moves toward (see BattleFx_BuildBuffer); does nothing without a colour bu...
Definition color.c:398
void BattleFx_SetPrimaryBufferValue(u32 color)
Sets the buffer's source backdrop colour (BG colour 0 of its palette copy).
Definition color.c:419
void BattleFx_StartBufferInterpolation(s32 frames)
Starts moving the shown colours to the target colours over frames frames: sets work->duration,...
Definition color.c:431
void BattleFx_ApplyColorToSourceBuffer(s32 mode, s32 half)
Sets the colours the buffer shows now (see BattleFx_BuildBuffer).
Definition color.c:409
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
#define Dma_Fill32(value, destination, size)
Fills size bytes at destination with the word value by DMA3.
Definition dma.h:30
void Fade_ToBlack(s32 frames)
Fades out to black.
Definition fade.c:76
void Fade_FromBlack(s32 frames)
Fades in from black.
Definition fade.c:79
void UiWindow_FillFromSceneFar(void *dst)
void Runtime_SetIrqHandler(u32 irq, s32 vcount, InterruptHandler handler)
Installs handler for interrupt irq and enables that interrupt, or with a null handler disables it and...
Definition frame.c:37
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
volatile u32 gFrameCount
Frames WaitFrames has finished.
const s8 gDisplayTransition_EdgeOffsets[32]
The wobble of the circle edge of window transition mode 8.
Definition fxdata.c:126
const u8 gDisplayTransition_DitherTable[64]
0 to 63 shuffled: the order in which the dissolve sets the pixels.
Definition fxdata.c:132
const u8 gDisplayTransition_NoiseWidths[34]
The width of the bars of window transition mode 0, by progress.
Definition fxdata.c:120
struct GameState gGameState
void * gMapWork[]
struct DisplayTransitionState * gDisplayTransition
Slot 31.
void * Runtime_AllocateBlock(s32 kind, s32 size)
As Runtime_AllocateHeapBlock, but tries EWRAM first, for blocks that need room more than speed: the b...
Definition heap.c:80
#define DMA_HBLANK_WORD_PER_LINE
Definition io_reg.h:230
#define REG_WIN1H
Definition io_reg.h:75
#define REG_WININ
Definition io_reg.h:81
#define BG_SCREEN_BLOCK(n)
Definition io_reg.h:167
#define BG_CHAR_BLOCK(n)
Definition io_reg.h:166
#define REG_DMA0
Definition io_reg.h:151
#define REG_WIN0H
Definition io_reg.h:74
#define DMA_START_TIMING_DRQ_REPEAT
A DMA control's start timing (bits 12 and 13), cartridge request (bit 11) and repeat (bit 9): the bit...
Definition io_reg.h:220
@ DISPCNT_OBJ_ON
Definition io_reg.h:44
@ DISPCNT_WIN0_ON
Definition io_reg.h:45
@ DISPCNT_BG3_ON
Definition io_reg.h:43
@ DISPCNT_MODE_2
BG2 and BG3 affine.
Definition io_reg.h:38
@ DISPCNT_WIN1_ON
Definition io_reg.h:46
@ DISPCNT_BG2_ON
Definition io_reg.h:42
@ DISPCNT_BG1_ON
Definition io_reg.h:41
#define REG_VCOUNT
Definition io_reg.h:19
#define DMA_32BIT
Definition io_reg.h:210
#define REG_DISPCNT
Definition io_reg.h:9
#define PLTT_BACKDROP
Definition io_reg.h:155
#define REG_DMA3
Definition io_reg.h:153
#define REG_WINOUT
Definition io_reg.h:82
#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 DMA_ENABLE
Definition io_reg.h:217
#define REG_WIN1V
Definition io_reg.h:77
#define REG_WIN0V
Definition io_reg.h:76
#define IO_WRITE_QUEUE_PUSH(address, value, control)
Disables interrupts to add one register write or DMA to the queue; a write to a full queue is dropped...
#define Io_Write16(v, reg)
Writes a value to a 16-bit register or memory location.
Definition iowrite.h:14
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
static s32 Iwram_SignedDivide(s32 dividend, s32 divisor)
Definition iwram_call.h:73
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
void DisplayTransition_InitializeBattleEffectState(s32 mode)
Starts window transition mode at value 0, with no duration: the caller moves it.
Definition transition.c:559
void DisplayState_ClearFlags(s32 clearBg3, s32 clearBg2, s32 clearBg1)
Clears the map's display-control bits for backgrounds 3, 2 and 1 (0x0800, 0x0400 and 0x0200) for each...
Definition transition.c:771
void DisplayTransition_FillTilemapAndSolidTile(s32 color)
Fills background screen block 4 with tile 0 in palette 15 and, unless color is -1,...
Definition transition.c:979
void DisplayTransition_UpdateScanline(void)
The VCount interrupt of the centre-line transition: switches the display to mode 2 (nothing shown) at...
Definition transition.c:879
void DisplayTransition_Start(s32 mode, s32 frames)
Fades the screen in over frames frames.
Definition transition.c:603
void DisplayTransition_Finish(s32 mode, s32 frames)
Fades the screen out over frames frames, as DisplayTransition_Start fades it in.
Definition transition.c:698
void DisplayTransition_UpdateScanlineTable(void)
Each frame of a window transition: advances its value toward its end (and at the end turns the window...
Definition transition.c:75
void BattleFx_StartWindowHBlankDma(void)
Each frame, before drawing: turns windows 0 and 1 on, writes the first four values of the shown page ...
Definition transition.c:16
void DisplayTransition_SuspendCallbacks(void)
Suspends the window transition's two callbacks.
Definition transition.c:576
void DisplayTransition_InitializeState(s32 value)
Starts a dissolve of the given mode and waits 120 frames.
void DisplayTransition_Update(void)
Each frame of the centre-line transition: moves the value toward its end and sets the two lines where...
Definition transition.c:794
struct DisplayTransitionState * DisplayTransition_AllocateAndClearState(void)
Allocates the display transition state (heap block 31) and clears it.
Definition transition.c:585
void DisplayTransition_UpdateFrame(void)
Each frame of the dissolve: moves the value toward its end and sets two more of the solid tile's 64 p...
Definition transition.c:930
void DisplayTransition_UpdateFromCentre(void)
As DisplayTransition_Update, but the lines flicker every other frame.
Definition transition.c:834
struct TransitionShape gDisplayTransition_Shapes[]
What every part of object.c shares: its includes and the macros and types more than one part uses.
#define Ram_DisplayWork
Definition ram_buffer.h:51
#define Ram_MapWork
Definition ram_buffer.h:48
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_SuspendTasks(SchedulerCallback callback)
Suspends the task running callback, or every task when callback is null, by setting bit 0 of its orde...
Definition scheduler.c:172
s32 Scheduler_RemoveCallback(SchedulerCallback callback)
Unschedules the task running callback: clears its routine and gives it order 0x7fff,...
Definition scheduler.c:146
#define NULL
Definition bilibin.h:6
The display transition state as the centre-line transition and the dissolve read it: the dissolve's t...
u8 ditherToggle
DisplayTransitionState's page: the dissolve flips it each frame and never reads it.
s8 start
the value's first step
u16 value
The progress, moved from start to end over duration frames: bit 5 says whether the dissolve fills or ...
s8 duration
frames; 0 when the value is not moving
s8 end
the value's last step
u8 paletteNibbles[0x22]
DisplayTransitionState's solidTile, as 4-bit pixels two a byte: the dissolve sets two more each frame...
The display transition state (heap block 31): two pages of window edges for every line,...
u16 value
The progress, moved from start to end over duration frames: bit 5 says whether the transition opens o...
u8 page
the pages entry shown; the next is built in the other
s16 active
WININ, or WINOUT by mode: 1 when a transition starts.
s8 nextMode
The mode taken when the value reaches its end; 0 ends the transition.
s8 end
the value's last step; 64 hides BG1 to BG3 and sprites
s8 start
the value's first step
u16 mode
The window transition's shape: modes are listed at DisplayTransition_UpdateScanlineTable.
s16 mask
WINOUT, or WININ by mode: 0x3f3f or 63 when a transition starts.
u16 pages[2][322]
Window edges, two pages: WININ and WINOUT, then a WIN0H and WIN1H pair, each (left << 8 | right),...
u32 solidTile[8]
The dissolve's solid tile, one 8 by 8 tile of 4-bit pixels.
s32 centreX
16.16 fixed-point pixels: the centre of the ellipse around a point (mode 8), set by the Reveal effect...
s8 duration
frames; 0 when the value is not moving
s32 centreY
as centreX: the target's z less its y
The map work's display fields that transitions use: its display control bits and the lines where the ...
u16 irqSecondLine
scanlines; secondLine as the VCount interrupt reads it
u16 firstLine
scanlines: where the screen is hidden, 200 when it is not
u16 irqFirstLine
scanlines; firstLine as the VCount interrupt reads it
u16 secondLine
scanlines: where the screen is shown again, 250 when it is not hidden
u16 irqPhase
The VCount interrupt's phase: 0 waiting for a line above 159, 1 neither line passed,...
u16 dispcnt
Display control bits ORed into DISPCNT; DisplayState_ClearFlags clears those of backgrounds 1 to 3.
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
The field map's work: the followed position, screen shake, tile animations, blend script,...
Definition map_scroll.h:27
s32 viewY
the view's top left, 16.16 fixed-point pixels
Definition map_scroll.h:40
One of the 64 field objects in the object pool (heap slot 5): its script, position,...
s32 z
16.16 fixed-point pixels
s32 x
16.16 fixed-point pixels
s32 y
16.16 fixed-point pixels
One step of the shape window transition: the WININ and WINOUT values, swapped as the transition opens...
Definition transition.c:51
struct TransitionSpan spans[3]
Definition transition.c:54
A run of screen lines in a transition shape: one window edge pair for every line, or,...
Definition transition.c:40
u8 x1
screen pixels
Definition transition.c:44
u8 y0
screen pixels
Definition transition.c:45
u16 cnt
lines
Definition transition.c:41
u8 y1
screen pixels
Definition transition.c:46
u8 x0
screen pixels
Definition transition.c:43
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
void * memcpy(void *destination, const void *source, unsigned long size)