Alchemy
Loading...
Searching...
No Matches

tolbi_spring.c

Go to the documentation of this file.
1/// @file
2/// Tolbi's spring and the game rooms beside it: Lucky Medals or coins tossed
3/// into the spring win prizes, and the attendants run Lucky Dice and Lucky
4/// Wheels.
5
6#include "keystate.h"
10#include "unit/field/field.h"
11#include "unit/game/game.h"
12#include "unit/window/window.h"
14#include "unit/shop/shop.h"
15#include "battle.h"
16#include "debug.h"
17#include "field.h"
18#include "game.h"
19#include "graphics.h"
20#include "menu.h"
21#include "minigame.h"
22#include "sound.h"
23#include "system.h"
24#include "types.h"
25#include "field_event.h"
26#include "field_scene.h"
27#include "call.h"
28#include "scene_ids.h"
29#include "io_reg.h"
30#include "text/msg_ids.h"
31#include "global_cells.h"
32#include "edition.h"
33#include "field_service.h"
34#include "game_state.h"
35#include "inventory.h"
36#include "menu_list.h"
37#include "iwram_call.h"
38#include "ability_ids.h"
39#include "iowrite.h"
40#include "frame.h"
41#include "fixed_math.h"
42#include "item_ids.h"
43#include "map.h"
44#include "flags.h"
45#include "songs.h"
47
48static void TolbiSpring_RiseAndFadeIn(void);
49static void TolbiSpring_OfferLuckyWheels(s32 spoken);
53static s32 TolbiSpring_RunToss(s32 tossKind);
54
56
57/// The game room's actors, numbered in the order of its placement table from 8.
63 /// A soldier, gone after Colosso. "poor soldiers like us"
65};
66
67/// The spring's own actors, numbered the same way.
69 /// Asks for a medal. "Do you have a Lucky Medal?"
71 TOLBI_SPRING_SPRING = 80, ///< searched to toss a coin or medal
72};
73
74// FLAG_SCENE_TEMP_00 is set once the spring has risen and faded in.
75
76#define SPRING_CENTER_X PIXELS(120)
77#define SPRING_CENTER_Z PIXELS(71)
78
79/// The tossed coin or medal: its last four positions for the trail, its
80/// velocity and the frames the prize rings still pull it.
84 struct Vector3 trail[4]; ///< 16.16 fixed-point pixels
86 struct Vector3 velocity; ///< 16.16 fixed-point pixels per frame
88};
89
90/// One of the four obstacles moving in the spring during a toss.
92 struct Vector3 position; ///< 16.16 fixed-point pixels
99};
100
101static __inline__ void Io_SetBlendControl(s32 value) { REG_BLDCNT = value; }
102
103static __inline__ void Io_SetBlendAlpha(s32 value) { REG_BLDALPHA = value; }
104
107}
108
109const struct SceneRegion *TolbiSpring_GetRegions(void) { return NULL; }
110
112
113/// Selects the actors for the spring or its nearby games.
117 }
119}
120
121/// Asks whether the party has a Lucky Medal.
123 s32 question = MsgTolbiLuckyMedal;
124 Engine_EventSetMessage(question);
125 Engine_EventOpenMessage(actorId, 0);
126 if (Engine_EventChooseYesNo(0, 0) == 0) {
128 Engine_EventSetMessage(question + 1);
129 } else {
130 Engine_EventSetMessage(question + 2);
131 }
132 Engine_EventShowMessage(actorId, 0);
133}
134
135/// Asks whether this is the party's first Lucky Dice game.
137 s32 question = MsgTolbiYerFirstTime;
138 Engine_EventSetMessage(question);
139 Engine_EventOpenMessage(actorId, 0);
140 if (Engine_EventChooseYesNo(0, 0) == 0) {
142 Engine_EventSetMessage(question + 1);
143 } else {
144 Engine_EventSetMessage(question + 2);
145 }
146 Engine_EventShowMessage(actorId, 0);
147}
148
149/// Reveals the spring once, then positions the leader to toss into it.
152
156 }
158 Engine_ActorWalkToAndWait(leader, 120, 152);
162}
163
164/// Reads the Lucky Wheels rules sign.
167 Engine_EventSetMessage(MsgTolbiLuckyWheelsRulesPullLever);
170}
171
172/// Reads the Lucky Wheels prize sign.
175 Engine_EventSetMessage(MsgTolbiLuckyWheelsPrizesPrizesDetermined);
178}
179
182}
183
184/// Selects interactions for the spring or the game attendants.
187 return gTolbiSpringEvents2;
188 }
190}
191
192/// Prepares the spring or settles rewards from the nearby games.
194 s32 coinChange;
195 s8 *wonPrizes;
196 s32 prizeIndex;
197 s32 item;
198
201 Io_Write16(0x3f42, &REG_BLDCNT);
202 Io_Write16(0x80c, &REG_BLDALPHA);
210 if (gGameState.entrance == 1) {
213 Io_SetBlendControl(0x3f42);
214 Io_SetBlendAlpha(0x1000);
215 }
216 }
217 } else {
220 }
221 gOamCopyEnabled = 1;
223 if (gGameState.entrance == 10) {
226 }
227 // Lucky Dice returns here to report the change in the party's coins.
228 if (gGameState.entrance == 13 &&
238 coinChange = gGameState.coins - *(s32 *)gSceneState;
239 if (coinChange > 0) {
240 if (coinChange > 19999) {
242 } else if (coinChange > 4999) {
244 } else {
246 }
248 Engine_EventSetMessage(MsgTolbiYaWonNumber);
249 UiWork_PushValueSlot(coinChange, 5);
252 } else if (coinChange < 0) {
253 Engine_EventSetMessage(MsgTolbiYaLostNumber);
254 UiWork_PushValueSlot(-coinChange, 5);
256 }
258 }
259 // Lucky Wheels returns item categories for the attendant to award.
260 if (gGameState.entrance == 12 &&
262 wonPrizes = gGameState.wonPrizes;
267 if (wonPrizes[0] == -1) {
269 } else if (wonPrizes[0] != -2) {
270 Engine_EventSetMessage(MsgTolbiCongratulations);
272 if (*wonPrizes != -1) {
273 for (prizeIndex = 0; wonPrizes[prizeIndex] != -1; prizeIndex++) {
274 if (prizeIndex == 0) {
275 Engine_EventSetMessage(MsgTolbiWonItemGo);
276 } else {
277 Engine_EventSetMessage(MsgTolbiWonItemGo2);
278 }
279 item = TolbiSpring_SelectLuckyWheelsPrize(wonPrizes[prizeIndex]);
280 UiWork_PushValueSlot(item, 2);
282 Engine_ItemShowFound(item, 3);
283 Engine_PartyGiveItem(item, 0);
287 }
288 }
289 gGameState.wonPrizes[0] = -2;
290 Engine_EventSetMessage(MsgTolbiComeAgain);
292 }
294 }
295 }
296 return 0;
297}
298
299/// Displays one frame of the spring's opening or closing prize animation.
300static inline void TolbiSpring_ShowPrizeFrame(s32 sourceColumn, s32 sourceRow) {
301 Map_CopyCellsTo(sourceColumn, sourceRow, 70, 0, 3, 8);
304}
305
306/// Animates the spring's prize reveal, gives the item, and closes it.
307static void TolbiSpring_RevealPrize(s32 item) {
311 Engine_EventWait(100);
314
315 Map_CopyCellsTo(82, 20, 70, 0, 3, 8);
323
331 Map_CopyCellsTo(100, 29, 70, 0, 3, 8);
333
336 Engine_ItemShowFound(item, 3);
337 Engine_PartyGiveItem(item, 0);
339
355}
356
357/// Raises the spring's displayed layer and blends it into view.
358static void TolbiSpring_RiseAndFadeIn(void) {
359 u8 *springLayer = gMapWork[0];
360 const u16 *alphaSteps;
361 s32 stepIndex;
362
364 // This value belongs to layer 1; its individual field remains unnamed.
365 springLayer += offsetof(struct MapState, layers[1]) + 4;
366 for (stepIndex = 0; stepIndex < 16; stepIndex++) {
367 *(s32 *)(springLayer + 12) -= PIXELS(1);
369 }
370 alphaSteps = gTolbiSpring_AlphaSteps;
371 for (stepIndex = 0; stepIndex < 8; stepIndex++) {
372 Io_Write16(0x3f42, &REG_BLDCNT);
373 Io_Write16(*alphaSteps++, &REG_BLDALPHA);
375 }
376}
377
378/// Checks tickets and inventory space before sending the party to Lucky Wheels.
381 s32 room;
382
384 if (spoken == 0) {
385 s32 message = MsgTolbiStepRightTry;
386
387 Engine_EventSetMessage(message);
389 if (tickets == 0) {
391 return;
392 }
393 Engine_EventSetMessage(message + 2);
394 UiWork_PushValueSlot(tickets, 5);
396 if (Engine_EventChooseYesNo(0, 0) != 0) {
398 return;
399 }
401 if (room == 0) {
402 Engine_EventSetMessage(message + 4);
404 } else if (room <= 6) {
405 Engine_EventSetMessage(message + 5);
407 }
408 if (room <= 6 && Engine_EventChooseYesNo(0, 0) != 0) {
409 Engine_EventSetMessage(MsgTolbiGetRidItems);
411 return;
412 }
413 } else {
414 if (tickets == 0) {
415 Engine_EventSetMessage(MsgTolbiBadComeBack);
417 return;
418 }
419 Engine_EventSetMessage(MsgTolbiBadLikeUse);
421 if (Engine_EventChooseYesNo(0, 0) != 0) {
422 Engine_EventSetMessage(MsgTolbiComeAgain);
424 return;
425 }
426 }
427 Engine_EventSetMessage(MsgTolbiStepRightEnjoy);
431}
432
433/// Offers Lucky Dice and saves the coin total for the return visit.
434static void TolbiSpring_OfferLuckyDice(void) {
435 u32 cost;
436 u32 coins;
437
438 cost = Party_GetAverageLevel() * 10;
440 coins = gGameState.coins;
441 if (coins < cost) {
442 Engine_EventSetMessage(MsgTolbiYaDontEnough);
444 return;
445 }
446 *(u32 *)gSceneState = coins;
448 Engine_EventSetMessage(MsgTolbiBuddyWannaTry);
449 UiWork_PushValueSlot(cost, 5);
451 if (Engine_EventChooseYesNo(0, 0) == 0) {
457 Event_RequestDestination(0x1fd, 0);
459 } else {
462 }
464}
465
466#if LANGUAGE == LANGUAGE_ENGLISH || LANGUAGE == LANGUAGE_JAPANESE
467#define ASK_WIDTH 17
468#else
469#define ASK_WIDTH 19
470#endif
471#if LANGUAGE == LANGUAGE_JAPANESE
472#define NUMBER_X 56
473#define TOSS_X 22
474#define TOSS_WIDTH 7
475#elif LANGUAGE == LANGUAGE_ENGLISH
476#define NUMBER_X 72
477#define TOSS_X 20
478#define TOSS_WIDTH 9
479#else
480#define NUMBER_X 88
481#define TOSS_X 19
482#define TOSS_WIDTH 10
483#endif
484
485/// Chooses a coin or medal, runs its toss, and awards the resulting prize.
487 s32 choice = 0;
488 s32 text;
489 s32 cancel = -1;
490 s32 coins;
491 s32 medals;
492 struct UiWindow *window;
493 s32 result;
494 s32 totalWeight;
495 s32 prizeIndex;
496 s32 prizeRoll;
497 s32 message;
498 struct GameState *state;
499
500 Event_Begin();
502 message = MsgTolbiThrow;
503#if TBS_PATCH_1
504 state = &gGameState;
505prompt:
506 coins = state->coins;
508 Event_SetMessage(message);
509 Event_OpenMessage(-1, 0);
510 window = UiWindow_Create(0, 0, ASK_WIDTH, 4, 2);
511 {
512 text = MsgTolbiCoins;
513 UiText_DrawCharacterAtOffset(text, window, 0, 0);
515 UiText_DrawCharacterAtOffset(text + 1, window, 0, 8);
516 UiText_DrawNumberInWindow(medals, 6, window, NUMBER_X, 8);
517 }
518 choice = Menu_SelectCoinsOrMedals(choice);
519 UiWork_Finalize(window, 2);
521 if (choice == cancel) {
522 Event_End();
523 return 0;
524 }
525 if (choice == SPRING_TOSS_COIN) {
526 if (coins == 0) {
527 Event_SetMessage(message + 1);
528 Event_ShowMessage(-1, 0);
529 Task_Wait(1);
530 goto prompt;
531 }
532 } else if (choice == SPRING_TOSS_MEDAL) {
533 if (medals == 0) {
534 Event_SetMessage(message + 2);
535 Event_ShowMessage(-1, 0);
536 Task_Wait(1);
537 goto prompt;
538 }
540 Event_SetMessage(message + 4);
541 Event_OpenMessage(-1, 0);
542 if (Event_ChooseYesNo(0, 0) != 0) {
543 goto end;
544 }
545 }
546 }
547 window = UiWindow_Create(TOSS_X, 15, TOSS_WIDTH, 4, 2);
548 {
549 text = MsgTolbiToss;
550 UiText_DrawCharacterAtOffset(text, window, 0, 0);
551 UiText_DrawCharacterAtOffset(text + 1, window, 0, 8);
552 }
553 Task_Wait(5);
555 for (;;) {
556 if (gKeysPressed & KEY_A) {
558 result = 0;
559 break;
560 }
561 if (gKeysPressed & KEY_B) {
563 result = -1;
564 break;
565 }
566 Task_Wait(1);
567 }
568 UiWork_Finalize(window, 2);
569#else
570 /* FAKEMATCH: keep message in r10 and label in r9; as whole marker ids, the
571 * two swap registers. */
572 asm("" : "+r"(message));
573 for (;;) {
574 text = MsgTolbiCoins;
575 /* FAKEMATCH: keep the coin label in r9; otherwise the repeated marker takes
576 * fp and moves state into r9. */
577 asm("" : "+r"(text));
578 state = &gGameState;
579 coins = state->coins;
581 Event_SetMessage(message);
582 Event_OpenMessage(-1, 0);
583 window = UiWindow_Create(0, 0, ASK_WIDTH, 4, 2);
584 UiText_DrawCharacterAtOffset(text, window, 0, 0);
586 UiText_DrawCharacterAtOffset(MsgTolbiCounterMarker, window, 104, 0);
587 UiText_DrawCharacterAtOffset(text + 1, window, 0, 8);
588 UiText_DrawNumberInWindow(medals, 6, window, NUMBER_X, 8);
589 UiText_DrawCharacterAtOffset(MsgTolbiCounterMarker, window, 104, 8);
590 choice = Menu_SelectCoinsOrMedals(choice);
591 UiWork_Finalize(window, 2);
593 if (choice == cancel) {
594 Event_End();
595 return 0;
596 }
597 if (choice == SPRING_TOSS_COIN) {
598 if (coins == 0) {
599 Event_SetMessage(message + 1);
600 Event_ShowMessage(-1, 0);
601 Task_Wait(1);
602 continue;
603 }
604 } else if (choice == SPRING_TOSS_MEDAL) {
605 if (medals == 0) {
606 Event_SetMessage(message + 2);
607 Event_ShowMessage(-1, 0);
608 Task_Wait(1);
609 continue;
610 }
612 Event_SetMessage(message + 4);
613 Event_OpenMessage(-1, 0);
614 if (Event_ChooseYesNo(0, 0) != 0) {
615 Event_End();
616 return 0;
617 }
618 }
619 }
620 window = UiWindow_Create(TOSS_X, 15, TOSS_WIDTH, 4, 2);
621 result = MsgTolbiToss;
622 UiText_DrawCharacterAtOffset(result, window, 0, 0);
623 UiText_DrawCharacterAtOffset(result + 1, window, 0, 8);
624 Task_Wait(5);
626 for (;;) {
627 if (gKeysPressed & KEY_A) {
629 result = 0;
630 break;
631 }
632 if (gKeysPressed & KEY_B) {
634 result = -1;
635 break;
636 }
637 Task_Wait(1);
638 }
639 UiWork_Finalize(window, 2);
640 break;
641 }
642#endif
643 // Charge the chosen token only after the player confirms the toss.
644 if (result != cancel) {
645 if (choice == SPRING_TOSS_COIN) {
647 } else if (choice == SPRING_TOSS_MEDAL) {
649 }
650 result = TolbiSpring_RunToss(choice);
651 if (choice == SPRING_TOSS_COIN) {
652 if (result != 4) {
653#ifdef UBFIX
655#else
656 // UB: the original view indexes beyond its one-element array.
657 // Plain arrays change register allocation and add four bytes in tbs-en.
659 ->coins[result]);
660#endif
662#ifdef UBFIX
664#else
665 // UB: retain the same native one-element view for this second read.
667 ((struct { u16 coins[1]; } *)gTolbiSpring_CoinPrizes)
668 ->coins[result],
669 5);
670#endif
671 Event_SetMessage(MsgTolbiWonNumberCoin);
672 Event_ShowMessage(-1, 0);
673 } else {
675 Event_Wait(10);
676 }
677 } else {
678 // The prize ring limits the weighted pool of medal rewards.
679 totalWeight = 0;
680 prizeIndex = 0;
681 if (prizeIndex < result * 3 + 3) {
682 do {
683 u8 prizeWeight = ((u8 *)state)[prizeIndex + 285];
684 totalWeight += (s8)prizeWeight;
685 prizeIndex++;
686 } while (prizeIndex < result * 3 + 3);
687 }
688 prizeRoll = (u32)(Random_Next() * totalWeight) >> 16;
689 for (prizeIndex = 0; prizeIndex < 15; prizeIndex++) {
690 prizeRoll -= state->prizeWeights[prizeIndex];
691 if (prizeRoll < 0) {
692 break;
693 }
694 }
695 if (prizeIndex == 15) {
696 prizeIndex = 14;
697 }
698 // Reveal the selected item, then reduce its chance on later tosses.
699#ifdef UBFIX
701#else
702 // UB: use the whole item array in modern C; preserve the native view.
704 ((struct { s32 prize[1]; } *)gTolbiSpring_PrizeItems)
705 ->prize[prizeIndex]);
706#endif
707
708#if TBS_PATCH_1
709 /* FAKEMATCH: the pointer pair recomputes the prize-weight base in r0
710 * after the call; without it, 42 rows differ in tbs-en. */
711 state++;
712 state--;
713 {
714
715 /* FAKEMATCH: keeps the slot in r1 and state + 1 in r0; as a plain
716 * struct field, 42 rows differ in tbs-en. */
717 register s32 slot asm("r1") = prizeIndex + 284;
718 s8 *prizeWeight = (s8 *)state + 1;
719
720 if (prizeWeight[slot] > 1) {
721 prizeWeight[slot] /= 2;
722 }
723 }
724#else
725 {
726 /* FAKEMATCH: keeps state + 1 in r0 as the native does. */
727 register s8 *prizeWeight asm("r0");
728 s32 slot = prizeIndex + 284;
729
730 /* FAKEMATCH: the native recomputes state + 1 after the call. */
731 __asm__("" : "=r"(prizeWeight) : "0"(state));
732 prizeWeight++;
733 if (prizeWeight[slot] > 1) {
734 prizeWeight[slot] /= 2;
735 }
736 }
737#endif
738 }
739 }
740#if TBS_PATCH_1
741end:
742#endif
743 Event_End();
744 return 0;
745}
746
747/// A halfword holding the zero an actor's motion and sprite flags are cleared
748/// with.
749struct Half {
750 u16 v;
751};
752
753/// Chooses an item variant in the prize category, avoiding its last variant.
755 s32 previousVariant;
756 s32 variant;
757
758 if (category < 0) {
759 return 0;
760 }
761
762 if (category == 5) {
763 category = ((unsigned int)(Engine_RandomNext() * 5) >> 16);
764 }
765
766 previousVariant = gGameState.wonPrizes[8 + category];
767
768 variant = (previousVariant +
769 (s32)((unsigned int)(Engine_RandomNext() * 2) >> 16) + 4) %
770 3;
771
772 gGameState.wonPrizes[8 + category] = variant;
773
774 return gTolbiSpring_LuckyWheelsPrizeItems[category * 3 + variant];
775}
776
777/// Copies three coordinates and sets the actor's facing, child value and
778/// colors.
779static void TolbiSpring_SetActorAppearance(s32 actorId, const s32 *coordinate,
780 s32 facing, s32 colorVariant,
781 s32 childValue) {
782 struct FieldActor *actor = Object_GetById(actorId);
783 struct AnimationObject *sprite;
784 u32 entryCount;
785
786 if (actor != 0) {
787 struct Half zero;
788
789 actor->x.fixed = *coordinate++;
790 actor->y.fixed = *coordinate++;
791 actor->z.fixed = *coordinate;
792 actor->facing = facing;
793 zero.v = 0;
794 actor->motionFlags = zero.v;
795 actor->sprite->flags = zero.v;
797 childValue);
798 }
799 // BUG: the original still dereferences actor here when lookup returned NULL.
800 sprite = actor->sprite;
801 entryCount = sprite->count;
802 if (entryCount != 0) {
803 u32 invalidFrame = 255;
804 struct AnimationEntry **entries = sprite->entries;
805 u32 entriesLeft = entryCount;
806
807 do {
808 struct AnimationEntry *entry = *entries++;
809
810 if (entry->param != colorVariant) {
811 entry->param = colorVariant;
812 {
813 u32 frame = entry->frame;
814
815 // Invalidate the cached frame so the new colors are applied.
816 frame |= invalidFrame;
817 entry->frame = frame;
818 }
819 }
820 } while (--entriesLeft != 0);
821 }
822}
823
824/// Sets whether the specified actor draws its sprite.
825static void TolbiSpring_SetSpriteStorageKind(s32 actorId, s32 storageKind) {
826 struct FieldActor *actor = Object_GetById(actorId);
827
828 if (actor != 0) {
830
831 *spriteStorageKind = storageKind;
832 }
833}
834
836
838
839/// Unreferenced toss storage; removing it shifts the obstacle state.
840static s32 sTossUnknown[3] UNUSED;
841
842static struct SpringObstacle sObstacles[4];
843
847
848/// Advances the toss, obstacles, collisions and displayed trail each frame.
850 struct SpringTossMotion *toss;
851 struct SpringObstacle *obstacle;
852 s32 index;
853 s32 height;
854 s32 edgeInset;
855 s32 motionStep;
857 s32 cycleFrame;
858 s32 minX;
859 s32 maxX;
860 s32 minZ;
861 s32 maxZ;
862 /* FAKEMATCH(dead): the unused vector keeps twelve frame bytes; without it,
863 * 2 of 947 instructions differ in tbs-en. */
864 UNUSED struct Vector3 spare;
865
866 toss = &sToss;
867 // Retain the previous three positions for the visible toss trail.
868 index = 3;
869 do {
870 toss->trail[index].x = toss->trail[index - 1].x;
871 toss->trail[index].y = toss->trail[index - 1].y;
872 toss->trail[index].z = toss->trail[index - 1].z;
873 index--;
874 } while (index != 0);
875
876 // Delay motion until the leader's launch animation has played.
877 if (toss->launchFrame > 31) {
878 toss->trail[0].x += toss->velocity.x;
879 height = toss->trail[0].y + toss->velocity.y;
880 toss->trail[0].y = height;
881 toss->trail[0].z += toss->velocity.z;
882
883 if (height <= 0) {
884 toss->trail[0].y = 0;
885 if (toss->velocity.y != 0) {
886 toss->velocity.y = 0;
889 } else {
891 }
892 }
893
894 // Pull the floating token toward the prize rings, then let it slow down.
895 if (toss->attractionFrames > 0) {
896 s32 deltaX;
897 s32 deltaZ;
898 s32 distance;
899
900 deltaX = (SPRING_CENTER_X - toss->trail[0].x) >> 8;
901 deltaZ = (SPRING_CENTER_Z - toss->trail[0].z) >> 8;
902 // BUG: an exact center position leaves a zero divisor below.
903 distance = Iwram_Sqrt(deltaX * deltaX + deltaZ * deltaZ);
904 toss->velocity.x += ((PIXELS(1) / 10) * deltaX) / distance;
905 toss->velocity.z += ((PIXELS(1) / 10) * deltaZ) / distance;
906 toss->velocity.x = toss->velocity.x * 253 / 256;
907 toss->velocity.z = toss->velocity.z * 253 / 256;
908 toss->attractionFrames = toss->attractionFrames - 1;
909 } else {
910 toss->velocity.x = toss->velocity.x * 220 / 256;
911 toss->velocity.z = toss->velocity.z * 220 / 256;
912 if (toss->velocity.x > -PIXELS(1) / 64 &&
913 toss->velocity.x < PIXELS(1) / 64) {
914 toss->velocity.x = 0;
915 }
916 if (toss->velocity.z > -PIXELS(1) / 64 &&
917 toss->velocity.z < PIXELS(1) / 64) {
918 toss->velocity.z = 0;
919 }
920 if (toss->velocity.x == 0 && toss->velocity.z == 0) {
926 } else {
931 }
932 {
933 s32 deltaX = (SPRING_CENTER_X - toss->trail[0].x) >> 16;
934 s32 deltaZ = (SPRING_CENTER_Z - toss->trail[0].z) >> 16;
935 s32 distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
936
937 // Classify squared pixel distance into the five prize rings.
938 sTossEnded = 1;
939 if (distanceSquared <= 224) {
940 sTossResult = 0;
941 } else if (distanceSquared <= 624) {
942 sTossResult = 1;
943 } else if (distanceSquared <= 1088) {
944 sTossResult = 2;
945 } else if (distanceSquared <= 1680) {
946 sTossResult = 3;
947 } else {
948 sTossResult = 4;
949 }
950 }
951 }
952 }
953
954 // Narrow the bounds near the spring's sloping corners.
955 minX = PIXELS(48);
956 maxX = PIXELS(192);
957 minZ = PIXELS(24);
958 maxZ = PIXELS(120);
959 if (toss->trail[0].z < PIXELS(42)) {
960 edgeInset = (PIXELS(42) - toss->trail[0].z) * 42 / 18;
961 minX = PIXELS(48) + edgeInset;
962 if (minX > PIXELS(90)) {
963 minX = PIXELS(90);
964 }
965 maxX = PIXELS(192) - edgeInset;
966 if (maxX < PIXELS(150)) {
967 maxX = PIXELS(150);
968 }
969 }
970 if (toss->trail[0].z > PIXELS(102)) {
971 edgeInset = (toss->trail[0].z * 42 - PIXELS(102) * 42) / 18;
972 minX = PIXELS(48) + edgeInset;
973 if (minX > PIXELS(90)) {
974 minX = PIXELS(90);
975 }
976 maxX = PIXELS(192) - edgeInset;
977 if (maxX < PIXELS(150)) {
978 maxX = PIXELS(150);
979 }
980 }
981 if (toss->trail[0].x < PIXELS(90)) {
982 edgeInset = (PIXELS(90) - toss->trail[0].x) * 18 / 42;
983 minZ = PIXELS(24) + edgeInset;
984 if (minZ > PIXELS(42)) {
985 minZ = PIXELS(42);
986 }
987 maxZ = PIXELS(120) - edgeInset;
988 if (maxZ < PIXELS(102)) {
989 maxZ = PIXELS(102);
990 }
991 }
992 if (toss->trail[0].x > PIXELS(150)) {
993 edgeInset = (toss->trail[0].x * 18 - PIXELS(150) * 18) / 42;
994 minZ = PIXELS(24) + edgeInset;
995 if (minZ > PIXELS(42)) {
996 minZ = PIXELS(42);
997 }
998 maxZ = PIXELS(120) - edgeInset;
999 if (maxZ < PIXELS(102)) {
1000 maxZ = PIXELS(102);
1001 }
1002 }
1003
1004 // Reflect outward motion at the boundary, halving the reflected speed.
1005 if (toss->trail[0].x < minX) {
1006 toss->trail[0].x = minX;
1007 if (toss->velocity.x < 0) {
1008 toss->velocity.x = -toss->velocity.x / 2;
1009 }
1010 }
1011 if (toss->trail[0].x > maxX) {
1012 toss->trail[0].x = maxX;
1013 if (toss->velocity.x > 0) {
1014 toss->velocity.x = -toss->velocity.x / 2;
1015 }
1016 }
1017 if (toss->trail[0].z < minZ) {
1018 toss->trail[0].z = minZ;
1019 if (toss->velocity.z < 0) {
1020 toss->velocity.z = -toss->velocity.z / 2;
1021 }
1022 }
1023 if (toss->trail[0].z > maxZ) {
1024 toss->trail[0].z = maxZ;
1025 if (toss->velocity.z > 0) {
1026 toss->velocity.z = -toss->velocity.z / 2;
1027 }
1028 }
1029 } else {
1030 toss->velocity.y -= PIXELS(1) / 4;
1031 }
1032 }
1033
1034 // Two obstacles slide across X; two follow timed elliptical paths.
1035 index = 0;
1036 do {
1037 obstacle = &sObstacles[index];
1038
1039 if (obstacle->reactionFrames > 0) {
1040 obstacle->reactionFrames = obstacle->reactionFrames - 1;
1041 }
1042 if (obstacle->hitCooldownFrames > 0) {
1043 obstacle->hitCooldownFrames = obstacle->hitCooldownFrames - 1;
1044 }
1045
1046 if (index <= 1) {
1047 speedMode = obstacle->speedMode;
1048 motionStep = PIXELS(1);
1049 if (speedMode == 1) {
1050 motionStep = motionStep * 2;
1051 }
1052 if (speedMode == 2) {
1053 motionStep = motionStep * 3;
1054 }
1055 if (obstacle->reactionFrames > 0) {
1056 if (index == 0) {
1058 } else {
1060 }
1061 } else {
1062 if (index == 0) {
1064 } else {
1066 }
1067 if (obstacle->pauseOrPhase == 0) {
1068 if (obstacle->heading == 0) {
1069 obstacle->position.x = obstacle->position.x + motionStep;
1070 } else {
1071 obstacle->position.x = obstacle->position.x - motionStep;
1072 }
1073 if (obstacle->position.x <= PIXELS(64)) {
1074 obstacle->heading = 0;
1075 if (index == 1) {
1076 obstacle->pauseOrPhase = 30;
1077 }
1078 }
1079 if (obstacle->position.x > (PIXELS(176) - 1)) {
1080 obstacle->heading = 1;
1081 if (index == 1) {
1082 obstacle->pauseOrPhase = 30;
1083 }
1084 }
1085 } else {
1086 obstacle->pauseOrPhase = obstacle->pauseOrPhase - 1;
1087 }
1088 }
1089 } else if (index == 2) {
1090 speedMode = obstacle->speedMode;
1091 motionStep = -64;
1092 if (speedMode == 1) {
1093 motionStep = motionStep * 2;
1094 }
1095 if (speedMode == 2) {
1096 motionStep = motionStep * 3;
1097 }
1098 if (obstacle->reactionFrames > 0) {
1100 } else {
1102 obstacle->position.x =
1103 Engine_MathSin(obstacle->heading) * 48 + PIXELS(112);
1104 obstacle->position.z =
1105 Engine_MathCos(obstacle->heading) * 40 + PIXELS(72);
1106 cycleFrame = (u16)obstacle->heading + motionStep;
1107 obstacle->heading = cycleFrame;
1108 obstacle->pauseOrPhase = obstacle->pauseOrPhase + 1;
1109 }
1110 } else {
1111 cycleFrame = obstacle->pauseOrPhase & 511;
1112 speedMode = obstacle->speedMode;
1113 motionStep = 64;
1114 if (speedMode == 1) {
1115 motionStep = motionStep * 2;
1116 }
1117 if (speedMode == 2) {
1118 motionStep = motionStep * 3;
1119 }
1120 if (obstacle->reactionFrames > 0) {
1122 } else if (cycleFrame <= 383) {
1123 obstacle->position.x =
1124 Engine_MathSin(obstacle->heading) * 52 + PIXELS(112);
1125 obstacle->position.z =
1126 Engine_MathCos(obstacle->heading) * 24 + PIXELS(72);
1127 obstacle->heading = obstacle->heading + motionStep;
1129 } else {
1131 }
1132 obstacle->pauseOrPhase = obstacle->pauseOrPhase + 1;
1133 }
1134
1135 // A nearby obstacle redirects the token and shortens its attraction time.
1136 if (obstacle->hitCooldownFrames == 0 && toss->trail[0].y == 0) {
1137 s32 deltaX;
1138 s32 deltaZ;
1139 s32 distanceSquared;
1140
1141 deltaX = (obstacle->position.x - toss->trail[0].x) >> 16;
1142 deltaZ = (obstacle->position.z - toss->trail[0].z) >> 16;
1143 distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
1144 if (distanceSquared <= 119 && toss->attractionFrames > 30) {
1145 s32 speed;
1146 s32 nextMode;
1147
1148 speed = PIXELS(3);
1149 if (index <= 1) {
1150 if (obstacle->heading == 0) {
1151 if (toss->velocity.x < speed) {
1152 toss->velocity.x = speed;
1153 toss->attractionFrames = toss->attractionFrames - 100;
1154 }
1155 } else {
1156 if (toss->velocity.x > -speed) {
1157 toss->velocity.x = -speed;
1158 toss->attractionFrames = toss->attractionFrames - 100;
1159 }
1160 }
1161 } else {
1162 s32 distance;
1163
1164 // BUG: coincident positions leave a zero divisor below.
1165 distance = Iwram_Sqrt(distanceSquared);
1166 toss->velocity.x = -deltaX * speed / distance;
1167 toss->velocity.z = -deltaZ * speed / distance;
1168 toss->attractionFrames = toss->attractionFrames - 100;
1169 }
1171 nextMode = (obstacle->speedMode + 1) % 3;
1172 obstacle->reactionFrames = 36;
1173 obstacle->speedMode = nextMode;
1174 obstacle->hitCooldownFrames = 30;
1175 }
1176 }
1177
1178 // Apply the obstacle's position, orientation and speed-dependent colors.
1179 switch (index) {
1180 case 0:
1182 18, Vector3_Coordinates(&obstacle->position), 0,
1184 (obstacle->speedMode << 4) + 16);
1185 break;
1186 case 1:
1188 19, Vector3_Coordinates(&obstacle->position), 0,
1190 (obstacle->speedMode << 4) + 16);
1191 break;
1192 case 2:
1194 20, Vector3_Coordinates(&obstacle->position),
1195 0x8000 - obstacle->heading,
1197 (obstacle->speedMode << 4) + 16);
1198 break;
1199 case 3:
1201 21, Vector3_Coordinates(&obstacle->position),
1202 0xffff - obstacle->heading,
1204 (obstacle->speedMode << 4) + 16);
1205 break;
1206 }
1207
1208 index++;
1209 } while (index != 4);
1210
1211 // Draw the token, its ground-level shadow and the three trailing copies.
1212 toss->shadow.x = toss->trail[0].x;
1213 toss->shadow.y = 0;
1214 toss->shadow.z = toss->trail[0].z;
1215
1218 0, 16);
1220 16);
1222 0, 16);
1224 0, 16);
1226 0, 16);
1230 } else {
1232 0, 16);
1234 16);
1236 0, 16);
1238 0, 16);
1240 0, 16);
1244 }
1245
1246 if (toss->launchFrame != -1) {
1247 toss->launchFrame = toss->launchFrame + 1;
1248 }
1249}
1250
1251/// Resets four obstacles and schedules the spring simulation.
1253 struct SpringTossMotion *toss = &sToss;
1254 s32 obstacleIndex = 0;
1255 const u8 *pixelX;
1256 const u16 *headings;
1257 struct SpringObstacle *obstacle;
1258 const u8 *pixelZ;
1259
1260 pixelX = gTolbiSpring_ActorPixelX;
1261 obstacle = sObstacles;
1262 headings = gTolbiSpring_ActorHeadings;
1263 pixelZ = gTolbiSpring_ActorPixelZ;
1264
1265 do {
1266 obstacle->position.x = *pixelX << 16;
1267 obstacle->position.z = *pixelZ << 16;
1268 obstacle->position.y = 0;
1269 obstacle->heading = *headings;
1270 obstacle->pauseOrPhase = 0;
1271 obstacle->speedMode = 0;
1272 obstacle->reactionFrames = 0;
1273 obstacle->hitCooldownFrames = 0;
1274
1275 obstacleIndex++;
1276 pixelX++;
1277 pixelZ++;
1278 headings++;
1279 obstacle++;
1280 } while (obstacleIndex != 4);
1281
1282 toss->trail[0].x = -PIXELS(30);
1283 toss->trail[0].y = 0;
1284 toss->trail[0].z = PIXELS(100);
1285 toss->velocity.x = 0;
1286 toss->velocity.y = 0;
1287 toss->velocity.z = 0;
1288 toss->attractionFrames = 0;
1289
1292
1293 {
1294 s32 budget = 0xc83;
1295 void (*task)(void) = TolbiSpring_UpdateTossSimulation;
1296
1297 FieldScheduler_AddCallback(task, budget);
1298 }
1299}
1300
1301/// Launches the chosen coin or medal and waits for its final prize ring.
1302static s32 TolbiSpring_RunToss(s32 tossKind) {
1303 struct SpringTossMotion *toss;
1304
1305 toss = &sToss;
1306 toss->trail[0].y = 0;
1307 toss->trail[1].y = 0;
1308 toss->trail[2].y = 0;
1309 toss->trail[3].y = 0;
1310 sTossKind = tossKind;
1311 sTossEnded = 0;
1312 toss->launchFrame = -1;
1313 for (sTossFrame = 0;; sTossFrame++) {
1314 if (sTossFrame == 50) {
1316 }
1317 // Release the token on the leader's throw frame.
1318 if (sTossFrame == 16) {
1320 toss->launchFrame = 0;
1321 toss->velocity.x = 13 * PIXELS(1) / 10;
1322 toss->velocity.y = PIXELS(4);
1323 toss->velocity.z = -PIXELS(2);
1324 toss->trail[0].x = PIXELS(120);
1325 toss->trail[0].y = PIXELS(16);
1326 toss->trail[0].z = PIXELS(152);
1327 toss->attractionFrames = 300;
1334 } else {
1340 }
1341 }
1342 Engine_TaskWait(1);
1343 if (sTossEnded == 1) {
1344 break;
1345 }
1346 }
1347 return sTossResult;
1348}
1349
1351 {0, CONDITION_ALWAYS, 120, 0, 152, FACING_SOUTH, 0, -1, -1, -1, -1, 0},
1352 {1, CONDITION_ALWAYS, 120, 0, 168, FACING_NORTH, 0, -1, -1, -1, -1, 0},
1353 {2, CONDITION_ALWAYS, 120, 0, 152, FACING_SOUTH, 0, -1, -1, -1, -1, 0},
1354 {10, CONDITION_ALWAYS, 128, 0, 176, FACING_NORTH, 0, 8, 8, 248, 200, 0},
1355 {11, CONDITION_ALWAYS, 96, 0, 440, FACING_NORTH, 0, 8, 288, 248, 456, 0},
1356 {12, CONDITION_ALWAYS, 144, 0, 390, FACING_NORTH, 0, 8, 288, 248, 456, 0},
1357 {13, CONDITION_ALWAYS, 120, 0, 152, FACING_WEST, 0, 8, 8, 248, 200, 0},
1359};
1360
1362 SCENE_EXITS(189),
1363 SCENE_EXIT_IF(1, 135, 20, 0xffffffff),
1364 SCENE_EXIT(0, 137, 0),
1365 SCENE_EXIT_IF(1, 135, 13, 0xffffffff),
1366 SCENE_EXIT_IF(2, 135, 5, 0xffffffff),
1368};
1369
1370// clang-format off
1372 { 0x18e, CONDITION_ALWAYS, ACTOR_STAND,
1373 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1374 { 0x18e, CONDITION_ALWAYS, ACTOR_STAND,
1375 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1376 { 0x18e, CONDITION_ALWAYS, ACTOR_STAND,
1377 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1378 { 0x18e, CONDITION_ALWAYS, ACTOR_STAND,
1379 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1380 { 0x18e, CONDITION_ALWAYS, ACTOR_STAND,
1381 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1382 { 0x18f, CONDITION_ALWAYS, ACTOR_STAND,
1383 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1384 { 0x18f, CONDITION_ALWAYS, ACTOR_STAND,
1385 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1386 { 0x18f, CONDITION_ALWAYS, ACTOR_STAND,
1387 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1388 { 0x18f, CONDITION_ALWAYS, ACTOR_STAND,
1389 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1390 { 0x18f, CONDITION_ALWAYS, ACTOR_STAND,
1391 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1392 { 0x18c, CONDITION_ALWAYS, ACTOR_STAND,
1393 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1394 { 0x18c, CONDITION_ALWAYS, ACTOR_STAND,
1395 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1396 { 0x18d, CONDITION_ALWAYS, ACTOR_STAND,
1397 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1398 { 0x18d, CONDITION_ALWAYS, ACTOR_STAND,
1399 0, 0, 0, FACING_SOUTH, TALK_FACE_PARTY, 0 },
1400 { 0x112, CONDITION_ALWAYS, 7,
1402 { 0x112, CONDITION_ALWAYS, 7,
1404 { 0x112, CONDITION_ALWAYS, 7,
1406 { 0x112, CONDITION_ALWAYS, 7,
1411 PIXELS(24), 0, PIXELS(72), FACING_EAST, TALK_FACE_PARTY, 0 },
1414 { SCENE_TABLE_END },
1415};
1416
1430 { 0x16, CONDITION_ALWAYS, 7,
1432 { 0x16, CONDITION_ALWAYS, 7,
1434 { 0x16, CONDITION_ALWAYS, 7,
1438 { 0x16, CONDITION_ALWAYS, 7,
1440 { SCENE_TABLE_END },
1441};
1442
1444 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1445 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1447 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), TOLBI_SPRING_WHEELS_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringHmmIWonderIfTheseKids) },
1449 { EVENT_TALK, 10, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringReadTheSignIfYaDont) },
1451 { EVENT_TALK, 12, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringWhoaGettingTripleDigitsIsReal) },
1452 { EVENT_TALK, 13, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringIDontWantToStopPlaying) },
1453 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), TOLBI_SPRING_DICE_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringWinningOrLosingIsPureLuck) },
1454 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 10, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringTheRulesForLuckyDiceAint) },
1455 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 11, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringSometimesPeopleWhoHaveNeverPlayed) },
1456 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 12, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringThinkingWontHelpYouWinThis) },
1457 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 13, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringMyLuckHasRunOutFor) },
1460 { EVENT_TALK, 16, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringLuckyDiceThrowTheDiceOn) },
1461 { EVENT_TALK, 18, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringLuckyDiceIfTwoNumbersMatch) },
1462 { EVENT_TALK, TOLBI_SPRING_SOLDIER, FLAG_BABI_SAVED, MSGNO(MsgTolbiSpringYeahThatDiceGameLooksKinda) },
1463 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), TOLBI_SPRING_SOLDIER, FLAG_BABI_SAVED, MSGNO(MsgTolbiSpringILikePlayingLuckyDiceBut) },
1464 { EVENT_TALK, TOLBI_SPRING_SOLDIER, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringOhThereAreAlwaysScalpersAt) },
1465 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), TOLBI_SPRING_SOLDIER, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringTheresNoWayPoorSoldiersLike) },
1466 { SCENE_EVENTS_END },
1467};
1468
1470 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1471 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1472 { EVENT_TALK, 22, CONDITION_ALWAYS, MSGNO(MsgTolbiTossLuckyMedal) },
1473 { EVENT_TALK, 23, CONDITION_ALWAYS, MSGNO(MsgTolbiFaceAwayTolbi) },
1474 { EVENT_TALK, 26, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringTolbiSpringHasMysteriousPowersStand) },
1476 { EVENT_TALK, 28, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringTooBadIOnlyHaveOne) },
1477 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 26, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringIGetNervousWhenIToss) },
1478 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), TOLBI_SPRING_MEDAL_SEEKER, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringWhyWontAnyoneGiveMeA) },
1479 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), 28, CONDITION_ALWAYS, MSGNO(MsgTolbiSpringThisIsALuckyMedalSo) },
1481 { SCENE_EVENTS_END },
1482};
1483
1484/// BLDALPHA steps.
1486 0x90a, 0xa08, 0xb06, 0xc04, 0xd03, 0xe02, 0xf01, 0x1000,
1487};
1488
1490 23, 124, 81, 152, 37, 108, 171, 142,
1491 48, 160, 133, 97, 183, 186, 189,
1492};
1493
1495 20, 10, 2, 1, 0, 0,
1496};
1497
1499 250, 251, 252, 256, 257, 258, 262, 263,
1500 264, 183, 182, 181, 189, 186, 188,
1501};
1502
1504 0, 1, 9,
1505};
1506
1508 0, 3, 4,
1509};
1510
1512 80, 160, 80, 72,
1513};
1514
1516 32, 104, 68, 72,
1517};
1518
1521};
1522// clang-format on
Ability numbers, named from the English release.
@ ABILITY_MIND_READ
Definition ability_ids.h:16
struct FieldActor * Object_GetById(s32 actor)
Returns an actor, or NULL if it does not exist.
void ObjectTable_Snapshot(void)
Saves up to 32 actors (only the party's 8 on the world map) into gObjectSnapshots,...
Definition actors.c:488
Calls through a function pointer, by argument count.
The engine calls the debug menus import, and their palette swatch tiles.
The edition's language, for C to compare (LANGUAGE == LANGUAGE_GERMAN).
static s32 FieldScheduler_AddCallback(SchedulerCallback callback, s32 priority)
Registers a main-image scheduler callback.
Definition engine_call.h:36
void ObjectDispatch_SetAnimationSpeed(struct DispatchObject *rawObject, s32 value)
Sets the animation speed (timer step) of all of an object's sprites.
Definition objects.c:370
The scene script layer: the engine services a scene's events call, declared once (Engine_EventBegin,...
static s32 Event_OpenMessage(s32 speaker, s32 flags)
static void Actor_FaceDirection(s32 actor, s32 facing, s32 frames)
void Object_SetMode(struct FieldActor *object, s32 animation)
s32 Engine_EventChooseYesNo(s32 actor, s32 flags)
s32 Engine_PartyGiveItem(s32 item, s32 flags)
#define GameFlag_IsSet
s32 Engine_MathSin(s32 angle)
void Engine_ItemShowFound(s32 item, s32 height)
void Engine_ActorFaceDirection(s32 actor, s32 facing, s32 frames)
static void Map_CopyCellsTo(s32 srcX, s32 srcY, s32 destX, s32 destY, s32 width, s32 height)
void Engine_ActorWalkToAndWait(s32 actor, s32 x, s32 z)
void Engine_EventWaitForScreen(void)
void Engine_EventOpenScreen(void)
s32 Engine_EventOpenMessage(s32 speaker, s32 flags)
void Engine_GameFlagSet(s32 flag)
static void Event_ShowMessage(s32 speaker, s32 flags)
s32 Engine_RandomNext()
static void Event_PlayCue(s32 cue)
void Engine_ActorSetChildValue(s32 actor, s32 value)
s32 Engine_GameFlagIsSet(s32 flag)
u8 gSceneState[]
s32 Engine_MathCos(s32 angle)
static s32 Random_Next(void)
void Engine_EventWait(s32 frames)
@ TRANSITION_WINDOW
Definition field_obj.h:17
@ TRANSITION_BACKDROP_FADE
Definition field_obj.h:16
@ ACTOR_PRIORITY_UNDERFOOT
The depth sort treats it as 320 pixels higher on the screen.
Definition field_obj.h:155
#define SCENE_TRANSITION(style, variant)
Definition field_obj.h:20
The scene table formats: entrances, exits, placements and events, and the macros that write them.
#define SCENE_EVENTS_END
@ EVENT_EXIT
@ EVENT_TALK
@ FACING_STEP
Definition field_scene.h:36
#define SCENE_EXITS(scene)
Definition field_scene.h:72
#define SCENE_EXIT(exit, scene, entrance)
Definition field_scene.h:73
@ ACTOR_PARTY_LEADER
@ SCENE_TABLE_END
Definition field_scene.h:43
@ CONDITION_ALWAYS
Definition field_scene.h:44
@ TALK_FACE_PARTY
@ TALK_FACE_PARTY_AND_BACK
#define PSYNERGY_ON_ACTOR(ability)
#define EVENT_SCRIPT(script)
@ ACTOR_STAND
#define SEARCH(target)
#define SCENE_EXITS_END
Definition field_scene.h:77
@ FACING_EAST
Definition field_scene.h:26
@ FACING_NORTH
Definition field_scene.h:32
@ FACING_SOUTHWEST
Definition field_scene.h:29
@ FACING_NORTHWEST
Definition field_scene.h:31
@ FACING_SOUTH
Definition field_scene.h:28
@ FACING_WEST
Definition field_scene.h:30
#define PIXELS(value)
#define SCENE_EXIT_IF(exit, scene, entrance, flag)
Definition field_scene.h:75
@ SEARCH_UNNAMED
The scene service layer: one typed inline for each engine service a scene calls by its own name,...
static void Event_End(void)
static void Task_Wait(s32 frames)
static void Event_Begin(void)
static void Event_Wait(s32 frames)
static s32 Event_ChooseYesNo(s32 actor, s32 flags)
static void Event_SetMessage(s32 message)
16.16 fixed-point constants, sine and cosine, and the library division.
#define Q16_ONE
One in the engine's signed 16.16 fixed-point format.
Definition fixed_math.h:10
Game flags, named where their meaning is shown.
#define FLAG_COLOSSO_OVER
the Colosso finals have been fought
Definition flags.h:191
#define FLAG_BABI_SAVED
The party has saved Babi in Altmiller Cave with his Mystic Draught; Tolbi then holds the Colosso fina...
Definition flags.h:170
#define FLAG_SCENE_TEMP_00
Definition flags.h:713
#define FLAG_KEEP_PARTY_POSITION
Definition flags.h:93
The frame loop's globals: lag counts, the CPU load display and the VBlank flags.
u8 gOamCopyEnabled
The VBlank handler copies the OAM list only while it is set; scenes clear it to keep their own sprite...
GameState, the game's state: play time, coins, the scene and the leader's position,...
struct GameState gGameState
The work slots of gWorkSlot, read through their own names.
void * gMapWork[]
The screen blend's globals and the graphics routines.
s32 Menu_SelectCoinsOrMedals(s32 selection)
Selects icons 0x20 and 0x21 (Coins, Lucky Medals) centered on row 17.
Definition icon_menu.c:849
s32 PartyInventory_CountItem(s32 itemId)
Counts itemId across the party.
Definition inventory.c:526
s32 PartyInventory_CountFreeSlots(void)
Returns the free inventory slots of the whole party.
Definition inventory.c:122
s32 PartyInventory_Remove(s32 itemId)
Removes one itemId from whoever carries it first; always returns 0.
Definition inventory.c:410
gOverflowItem, an item slot the spoils code uses when items are handed out.
The hardware's I/O registers and their bits, as pret names them.
@ KEY_B
Definition io_reg.h:244
@ KEY_A
Definition io_reg.h:243
#define REG_BLDALPHA
Definition io_reg.h:142
#define REG_BLDCNT
Definition io_reg.h:119
Io_Write16, a 16-bit write to a register or memory.
#define Io_Write16(v, reg)
Writes a value to a 16-bit register or memory location.
Definition iowrite.h:14
Item numbers, named from the English release.
@ ITEM_GAME_TICKET
Definition item_ids.h:69
@ ITEM_LUCKY_MEDAL
Definition item_ids.h:70
The IWRAM call layer: the routines the game keeps resident in IWRAM, each at its fixed address,...
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
The keypad state the VBlank interrupt keeps.
volatile u32 gKeysPressed
Keys that went down this frame.
Maps: their cells, layers, animations and blend scripts, and the map state.
Menu lists: sprites, cursors, the party status window and paged selections.
The reel game's title lettering.
void Battle_ResetEffectCounter(void)
Clears the field's busy flag and, while Reveal is active, ends it: Reveal used with ACT_PACKED runs i...
Definition targets.c:261
s32 Party_AdjustCoins(s32 amount)
Adds amount to the party's coins, kept between 0 and 999999, and returns the new total.
Definition party.c:195
void Event_SetNextDestination(s32 scene, s32 entrance)
Sets the scene and entrance that the field goes to next (gGameState.nextScene and nextEntrance).
Definition requests.c:61
void Event_RequestDestination(u16 scene, u16 entrance)
Makes the event leave for entrance entrance of scene scene: sets gGameState.scene and entrance and ra...
Definition requests.c:79
void AudioCommand_WaitForStateByteClear(void)
Waits one frame at a time until the sound driver has finished the last command.
Definition run.c:198
Scene numbers, named after their places.
char SceneId_TolbiSpring1
char SceneId_TolbiSpring2
Song and sound effect ids, indices into gSound_SongTable.
#define SE_216
Definition songs.h:211
#define MUS_092
Definition songs.h:90
#define SE_RECOVERY
Definition songs.h:119
#define SE_MENU_CANCEL
Definition songs.h:106
#define SE_CAST
Definition songs.h:149
#define SE_148
Definition songs.h:141
#define SE_300
Definition songs.h:288
#define SE_301
Definition songs.h:289
#define MUS_093
Definition songs.h:91
#define SE_MENU_CONFIRM
Definition songs.h:105
#define SE_116
Definition songs.h:109
#define MUS_091
Definition songs.h:89
void Engine_ActorSetAnimation()
struct EventWork * gEventWork
void Engine_ActorSetPosition(u32 actor, s32 x, s32 z)
void Engine_EventEnd(void)
void Engine_AudioPlayCue()
void Engine_EventBegin(void)
void Engine_TaskWait()
void Engine_EventSetMessage()
void Engine_EventShowMessage()
#define NULL
Definition bilibin.h:6
#define offsetof(type, member)
Definition stddef.h:19
One animation layer of a sprite: its animation id, frames, running script and timer.
Definition animspr.h:37
u8 param
SpriteColorMode its pixels are drawn with; 0 when taken.
Definition animspr.h:45
u8 frame
frame shown, frameBase plus the facing offset; 255 none
Definition animspr.h:53
An animated sprite: its image and shadow, scale, rotation, size and up to four animation layers.
Definition animspr.h:108
struct AnimationEntry * entries[4]
its layers; empty ones null
Definition animspr.h:130
u8 count
layers in use, up to the last one present
Definition animspr.h:129
A field object as the object dispatcher's far calls take it.
s32 startTransition
SCENE_TRANSITION(style, variant).
Definition event_work.h:43
u16 message
the next message, advanced as each window opens
Definition event_work.h:47
A field actor: a person or thing on the field with a position, motion toward a target,...
Definition field_obj.h:30
u8 motionFlags
ActorMotionFlag bits; zero is free flight in 3D, and 3 is set on creation.
Definition field_obj.h:83
union FieldCoordinate x
16.16 fixed-point pixels
Definition field_obj.h:35
union FieldCoordinate y
16.16 fixed-point pixels
Definition field_obj.h:36
u8 priorityFlags
ActorPriorityFlag bits, read by the scrolling camera.
Definition field_obj.h:51
u8 spriteStorageKind
Zero hides sprites; one draws a single sprite, two draws a group.
Definition field_obj.h:78
union FieldCoordinate z
16.16 fixed-point pixels
Definition field_obj.h:37
u16 facing
1/65536 turns
Definition field_obj.h:34
s32 scaleX
16.16 fixed point, 0x10000 full size
Definition field_obj.h:39
The game's state: play time, coins, the scene and the leader's position, the active party and the opt...
Definition game_state.h:14
s8 prizeWeights[0x0f]
The slot machine's prize weights, three prizes to each of 4, 8, 16, 32 and 64 on a new game; not read...
Definition game_state.h:31
s16 entrance
Definition game_state.h:37
s8 wonPrizes[0x10]
Lucky Wheels: 0 to 7 the prize categories won, ended by -1 (-2 once awarded); 8 on the last variant g...
Definition game_state.h:34
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
A halfword holding the zero the gauge's slide and phase are cleared with.
The map work in heap slot 8 (struct MapScrollWork), seen as its tile animation channels,...
Definition map.h:49
A scene entrance: where and facing which way the party arrives, and the camera's bounds there (-1 for...
Definition field_scene.h:54
A scene event: its kind and detail, the trigger it answers, its condition and its value (a message or...
An actor a scene places: its sprite, the condition for placing it, how it behaves,...
Definition field_scene.h:81
A scene's exit region: the box that, while its condition holds, takes the party leader out through it...
One of the four obstacles moving in the spring during a toss.
struct Vector3 position
16.16 fixed-point pixels
The tossed coin or medal: its last four positions for the trail, its velocity and the frames the priz...
struct Vector3 velocity
16.16 fixed-point pixels per frame
struct Vector3 shadow
struct Vector3 trail[4]
16.16 fixed-point pixels
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
Three coordinates, x, y and z: a position, a velocity or a direction.
Definition vector3.h:11
s32 z
Definition vector3.h:24
s32 x
Definition vector3.h:22
s32 y
Definition vector3.h:23
The system's routines and globals: interrupts, BIOS sound bias, random numbers and sleep.
void UiText_DrawNumberInWindow(s32 value, s32 width, struct UiWindow *window, s32 x, s32 y)
Draws a number (width as UiText_FormatNumber) into a window's tilemap at pixel (x,...
Definition text_tiles.c:678
void UiText_DrawCharacterAtOffset(s32 message, const struct UiWindow *window, u32 offsetX, u32 offsetY)
Decodes a message and draws it as tiles in a window at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:552
static struct SpringObstacle sObstacles[4]
TolbiSpringActor
The spring's own actors, numbered the same way.
@ TOLBI_SPRING_MEDAL_SEEKER
Asks for a medal. "Do you have a Lucky Medal?".
@ TOLBI_SPRING_SPRING
searched to toss a coin or medal
static void TolbiSpring_SetActorAppearance(s32 actorId, const s32 *coordinate, s32 facing, s32 colorVariant, s32 childValue)
Copies three coordinates and sets the actor's facing, child value and colors.
const struct SceneEvent * TolbiSpring_GetEvents(void)
Selects interactions for the spring or the game attendants.
#define TOSS_X
const u32 * TolbiSpring_GetExits(void)
static void TolbiSpring_SetSpriteStorageKind(s32 actorId, s32 storageKind)
Sets whether the specified actor draws its sprite.
const u16 gTolbiSpring_AlphaSteps[]
BLDALPHA steps.
#define SPRING_CENTER_X
const u8 gTolbiSpring_ActorPixelZ[]
static s32 TolbiSpring_RunToss(s32 tossKind)
Launches the chosen coin or medal and waits for its final prize ring.
const u16 gTolbiSpring_ActorHeadings[]
TolbiSpringGameRoomActor
The game room's actors, numbered in the order of its placement table from 8.
@ TOLBI_SPRING_SOLDIER
A soldier, gone after Colosso. "poor soldiers like us".
@ TOLBI_SPRING_DICE_ATTENDANT
@ TOLBI_SPRING_WHEELS_PRIZE_SIGN
@ TOLBI_SPRING_WHEELS_RULES_SIGN
@ TOLBI_SPRING_WHEELS_ATTENDANT
static void TolbiSpring_ReadLuckyWheelsPrizes(void)
Reads the Lucky Wheels prize sign.
const struct SceneEvent gTolbiSpringEventsOther[]
const struct ScenePlacement * TolbiSpring_GetPlacements(void)
Selects the actors for the spring or its nearby games.
static s32 sTossFrame
const struct SceneEntrance gTolbiSpring_Entrances[]
const struct ScenePlacement gTolbiSpringPlacementsOther[]
static void TolbiSpring_AskAboutFirstDiceGame(s32 actorId)
Asks whether this is the party's first Lucky Dice game.
static __inline__ void Io_SetBlendAlpha(s32 value)
const u32 gTolbiSpring_LuckyWheelsPrizeItems[]
static void TolbiSpring_RevealPrize(s32 item)
Animates the spring's prize reveal, gives the item, and closes it.
static void TolbiSpring_TalkToWheelsAttendant(void)
const struct SceneEntrance * TolbiSpring_GetEntrances(void)
const struct SceneRegion * TolbiSpring_GetRegions(void)
const struct SceneEvent gTolbiSpringEvents2[]
const u8 gTolbiSpring_SliderFrames[]
SpringTossKind
@ SPRING_TOSS_MEDAL
@ SPRING_TOSS_COIN
static __inline__ void Io_SetBlendControl(s32 value)
static void TolbiSpring_AskAboutLuckyMedal(s32 actorId)
Asks whether the party has a Lucky Medal.
const u32 gTolbiSpring_Exits[]
static void TolbiSpring_UpdateTossSimulation(void)
Advances the toss, obstacles, collisions and displayed trail each frame.
static struct SpringTossMotion sToss
const u32 gTolbiSpring_PrizeItems[]
static s32 TolbiSpring_SelectLuckyWheelsPrize(s32 category)
Chooses an item variant in the prize category, avoiding its last variant.
static void TolbiSpring_TossCoinOrMedal(void)
Reveals the spring once, then positions the leader to toss into it.
static s32 sTossResult
const u8 gTolbiSpring_CircleFrames[]
#define ASK_WIDTH
#define SPRING_CENTER_Z
static void TolbiSpring_ShowPrizeFrame(s32 sourceColumn, s32 sourceRow)
Displays one frame of the spring's opening or closing prize animation.
#define NUMBER_X
static s32 sTossKind
static s32 sTossEnded
static void TolbiSpring_RiseAndFadeIn(void)
Raises the spring's displayed layer and blends it into view.
static void TolbiSpring_OfferLuckyWheels(s32 spoken)
Checks tickets and inventory space before sending the party to Lucky Wheels.
s32 TolbiSpring_Initialize(void)
Prepares the spring or settles rewards from the nearby games.
#define TOSS_WIDTH
const u8 gTolbiSpring_ActorPixelX[]
const struct ScenePlacement gTolbiSpringPlacements2[]
static s32 TolbiSpring_RunSpringGame(void)
Chooses a coin or medal, runs its toss, and awards the resulting prize.
const u16 gTolbiSpring_CoinPrizes[]
static void TolbiSpring_ReadLuckyWheelsRules(void)
Reads the Lucky Wheels rules sign.
static void TolbiSpring_OfferLuckyDice(void)
Offers Lucky Dice and saves the coin total for the return visit.
static void TolbiSpring_InitializeTossSimulation(void)
Resets four obstacles and schedules the spring simulation.
The integer types, TRUE, UNUSED and the fixed-address macros.
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
#define UNUSED
Definition types.h:22
unsigned char u8
Definition types.h:11
s32 fixed
16.16 fixed-point pixels
u32 Party_GetAverageLevel(void)
Returns the average level of the party members, rounded down (0 with no members).
Definition units.c:52
static const s32 * Vector3_Coordinates(const struct Vector3 *vector)
Returns the vector's three coordinates as an array.
Definition vector3.h:31
void UiWork_PushValueSlot(u32 value, u32 kind)
Adds a message argument of kind (MSG_ARG_) with value to the first free place of the argument table; ...
Definition messages.c:216
void UiWork_FinalizePendingCore(void)
Closes each message channel's window whose text has ended, then waits ten frames.
Definition messages.c:302
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
struct UiWindow * UiWindow_Create(s32 x, s32 y, s32 width, s32 height, s32 attrs)
Opens a window at tile (x, y) of width by height tiles in the first free window slot,...
Definition windows.c:279