Alchemy
Loading...
Searching...
No Matches

colosso_wall.c

Go to the documentation of this file.
1/// @file
2/// Colosso's second finals course: Isaac races another warrior through the
3/// Mini-Maze, the shifting floor, the Log-Rolling Stage, the Wall and the Board
4/// Walk to the arena.
5
6#include "script.h"
7#include "game_state.h"
8#include "io_write_queue.h"
9#include "scene_ids.h"
10#include "vram_tab.h"
11#include "unit/import.h"
16#include "unit/object/object.h"
18#include "unit/field/field.h"
19#include "unit/game/game.h"
20#include "unit/window/window.h"
22#include "unit/menu/menu.h"
23#include "unit/sound/sound.h"
26#include "battle.h"
27#include "keystate.h"
28#include "debug.h"
29#include "field.h"
30#include "game.h"
31#include "graphics.h"
32#include "menu.h"
33#include "minigame.h"
34#include "sound.h"
35#include "system.h"
36#include "fx_scene.h"
37#include "mem.h"
38#include "resource.h"
39#include "types.h"
40#include "field_event.h"
41#include "field_scene.h"
42#include "io_reg.h"
43#include "callback_scheduler.h"
44#include "resource_ids.h"
45#include "field_service.h"
46#include "text/msg_ids.h"
47#include "battle_unit.h"
48#include "ability_ids.h"
49#include "edition.h"
50#include "item_ids.h"
51#include "objcmd.h"
52#include "field_obj.h"
53#include "global_cells.h"
54#include "heap_state.h"
55#include "field_runtime.h"
56#include "flags.h"
57#include "songs.h"
58#include "macros.h"
59
60#define COLOSSO_WALL_FIRST_PLATFORM 22
61#define COLOSSO_WALL_LAST_PLATFORM 25
62#define COLOSSO_WALL_PLATFORM_CYCLE_FRAMES 120
63#define COLOSSO_WALL_PLATFORM_CROSSING_FRAME 75
64#define COLOSSO_WALL_DEMONSTRATION_TIMEOUT_FRAMES 600
65#define COLOSSO_WALL_LOG_TRAVEL_PIXELS 64
66
67// UB: westward log travel passes -64, which cannot be shifted left as signed C.
68// Both callers supply only -64, 0 or 64, so fixed-point multiplication fits.
69#ifdef UBFIX
70#define COLOSSO_WALL_OFFSET_FIXED(value) ((value) * 0x10000)
71#else
72#define COLOSSO_WALL_OFFSET_FIXED(value) ((value) << 16)
73#endif
74
75#if LANGUAGE == LANGUAGE_JAPANESE || LANGUAGE == LANGUAGE_ENGLISH
76#define COLOSSO_WALL_ALTERNATE_PORTRAIT_PALETTE_OFFSET 64
77#else
78#define COLOSSO_WALL_ALTERNATE_PORTRAIT_PALETTE_OFFSET 160
79#endif
80
81#if LANGUAGE == LANGUAGE_JAPANESE
82#define COLOSSO_WALL_INTRO_SCALE 256
83#else
84#define COLOSSO_WALL_INTRO_SCALE 384
85#endif
86
87#if LANGUAGE == LANGUAGE_FRENCH
88#define COLOSSO_WALL_PULSE_FIRST_SCALE 512
89#define COLOSSO_WALL_PULSE_HIGH_SCALE 512
90#define COLOSSO_WALL_PULSE_LOW_SCALE 384
91#elif LANGUAGE == LANGUAGE_SPANISH || LANGUAGE == LANGUAGE_ITALIAN
92#define COLOSSO_WALL_PULSE_FIRST_SCALE 384
93#define COLOSSO_WALL_PULSE_HIGH_SCALE 384
94#define COLOSSO_WALL_PULSE_LOW_SCALE 256
95#else
96#define COLOSSO_WALL_PULSE_FIRST_SCALE 512
97#define COLOSSO_WALL_PULSE_HIGH_SCALE 384
98#define COLOSSO_WALL_PULSE_LOW_SCALE 256
99#endif
100
101#ifdef UBFIX
102#define COLOSSO_WALL_SHOW_MESSAGE Engine_EventShowMessage
103#define COLOSSO_WALL_MOVE_ACTOR_PAIR ColossoWall_MovePusherAndLog
104#define COLOSSO_WALL_START_MODE_TASK ColossoWall_StartLetteringAnimation
105#define COLOSSO_WALL_APPLY_MATCHING_SLOT_VALUE Scene_GiveAndEquipItem
106#else
107#define COLOSSO_WALL_SHOW_MESSAGE ((s32 (*)())Engine_EventShowMessage)
108#define COLOSSO_WALL_MOVE_ACTOR_PAIR ((s32 (*)())ColossoWall_MovePusherAndLog)
109#define COLOSSO_WALL_START_MODE_TASK \
110 ((s32 (*)())ColossoWall_StartLetteringAnimation)
111#define COLOSSO_WALL_APPLY_MATCHING_SLOT_VALUE \
112 ((s32 (*)())Scene_GiveAndEquipItem)
113#endif
114
115// Forward declarations.
116static void ColossoWall_IntroduceSecondFinals(s32 attendantId);
119static void SceneState_EmptyHook(void);
120static void ColossoWall_WaitForRivalFinish(void);
121static s32 ColossoWall_ApproachFinishAndTakeItem(s32 firstItem, s32 secondItem);
122static void ColossoWall_StartRivalPath(s32 pathResource);
123static void ColossoWall_PushAdjacentObstacle(void);
124
125/// The second course's actors, by the guide's and attendants' explanations: the
126/// Mini-Maze, the shifting floor, the Log-Rolling Stage, the Wall and the Board
127/// Walk lead to the arena. The lifts are actors 22 to 25
128/// (COLOSSO_WALL_FIRST_PLATFORM to COLOSSO_WALL_LAST_PLATFORM).
129enum {
130 OPPONENT = 8, ///< the warrior racing Isaac to the arena
138 FALLING_OBSTACLE = 30, ///< knocked down by Force on FORCE_TARGET
140 RIDING_LOG_NORTH = 32, ///< the Log-Rolling Stage's two logs
142 GUIDE = 34, ///< explains the second course
148 PRIZE_A = 40, ///< the arena's Chain Mail and Cotton Shirt
150};
151
152#define WORK_SLOT_STAGE 59
153
154/// The rival's recorded-path playback state, held in the scene state; status
155/// 9 marks the rival finished.
162};
163
164/// Sets the party leader's walking speed and acceleration.
165static inline void InitializeActorZero(void) {
167}
168
169/// Sets the specified actor's walking speed and acceleration.
170static inline void InitializeSelectedActor(s32 actorId) {
171 Engine_ActorSetSpeed(actorId, PIXELS(1), PIXELS(1) / 2);
172}
173
176}
177
178const struct SceneRegion *Scene_GetRegions(void) { return 0; }
179
180/// Returns the packed course and battle-result exits.
181const u32 *Scene_GetExits(void) { return gColossoWallExits; }
182
183/// Returns the course's competitors, obstacles and attendants.
186}
187
188/// Move on the first two rocks does nothing.
189static void Event_IgnoreMove(void) {}
190
191/// Pushes the obstacle immediately ahead of the selected actor.
193
194/// Updates the first movable obstacle's collision after Move.
195static void Event_UpdateMovedRockCell(void) {
196 struct FieldActor *obstacle;
197 s32 column;
198 s32 row;
199
200 obstacle = Object_GetById(11);
201 if ((obstacle->x.fixed >> 20) == 36) {
202 Engine_GameFlagSet(0x335);
203 column = 0x23;
204 row = 0x4D;
205 Map_CopyCellAttributes(0x23, 0x4E, 1, 1, column, row);
206 } else {
208 column = 0x23;
209 row = 0x4D;
210 Map_CopyCellAttributes(0x22, 0x4D, 1, 1, column, row);
211 }
212}
213
214/// Pushes an obstacle, then restores the first obstacle's collision cell.
218}
219
220/// Saves the movable obstacles' columns and rebuilds their collision cells.
221static void Event_SaveMovedRockColumns(void) {
222
223 struct FieldActor *obstacle;
224 s32 column;
225
226 Map_CopyCellAttributes(100, 11, 12, 4, 14, 11);
227
228 obstacle = Object_GetById(12);
229 column = obstacle->x.fixed >> 20;
230 FieldGameFlag_SetByte(832, column);
231 Map_CopyCellAttributes(71, 16, 1, 1, column, 16);
232
233 obstacle = Object_GetById(13);
234 column = obstacle->x.fixed >> 20;
235 FieldGameFlag_SetByte(840, column);
236 Map_CopyCellAttributes(71, 16, 1, 1, column, 16);
237
238 obstacle = Object_GetById(14);
239 column = obstacle->x.fixed >> 20;
240 FieldGameFlag_SetByte(848, column);
241 Map_CopyCellAttributes(71, 16, 1, 1, column, 16);
242}
243
244/// Enables the child effect for Force on the course obstacle.
245static void Event_PrepareForce(void) {
246 u8 *effectWork = gEffectWork;
247
248 ((struct BattleFxScene *)effectWork)->childMode = 1;
249}
250
251/// Force on the target: the obstacle spins down into place and the way past it
252/// opens (0x330).
253static void Event_KnockDownObstacle(void) {
254 struct FieldActor *obstacle;
255 s32 fallFrames;
256 s32 rotationStep;
257 s32 settleFrames;
258 s32 settleRotationStep;
259 struct AnimationObject *sprite;
260
262 sprite = obstacle->sprite;
263 GameFlag_Set(0x330);
264 obstacle->acceleration = 0x1999;
265 obstacle->speed = 0x13333;
267 // Spin through the fall, then settle the obstacle at its final height.
268 FieldObject_SetPosition(obstacle, PIXELS(336), PIXELS(10), PIXELS(264));
269 for (rotationStep = 0, fallFrames = 9; fallFrames >= 0; fallFrames--) {
270 sprite->rotation -= rotationStep;
272 rotationStep += 36;
273 }
274 FieldObject_SetPosition(obstacle, PIXELS(330), -PIXELS(16), PIXELS(264));
275 for (settleRotationStep = 0x168, settleFrames = 21; settleFrames >= 0;
276 settleFrames--) {
277 sprite->rotation -= settleRotationStep;
279 settleRotationStep += 36;
280 }
281 FieldObject_WaitForMove(obstacle);
284 {
285 s32 rotation = 0;
286
287 sprite->rotation = rotation;
288 }
290 obstacle->x.fixed = PIXELS(336);
291 obstacle->y.fixed = -PIXELS(8);
292 obstacle->z.fixed = PIXELS(264);
293 obstacle->velocityY = 0;
294 obstacle->velocityX = 0;
295 // Keep both the course and its saved collision strip in the new state.
296 Map_CopyCellAttributes(19, 16, 1, 1, 20, 16);
297 Map_CopyCellAttributes(20, 80, 1, 1, 21, 80);
298}
299
300/// Frost on the western target: it freezes and its crossing opens (0x331).
301static void Event_FreezeWestTarget(void) {
302 struct FieldActor *obstacle;
303
304 Engine_GameFlagSet(0x331);
306 obstacle->motionFlags = 0;
307 {
308
309 Map_CopyCellAttributes(46, 17, 1, 1, 44, 17);
310 }
311}
312
313/// Frost on the eastern target: it freezes and its crossing opens (0x332).
314static void Event_FreezeEastTarget(void) {
315 struct FieldActor *obstacle;
316
317 Engine_GameFlagSet(0x332);
319 obstacle->motionFlags = 0;
320 {
321
322 Map_CopyCellAttributes(46, 17, 1, 1, 50, 17);
323 }
324}
325
326/// Growth on the plant: it grows into a strip that can be climbed (0x333).
327static void Event_GrowPlant(void) {
328 Engine_GameFlagSet(0x333);
329 {
330
331 Map_CopyCellAttributes(32, 37, 1, 4, 32, 77);
332 }
333}
334
335/// Runs the rising-object sequence for the actor touching the obstacle.
336static void Event_RunRisingSequence(void) {
338}
339
341
342/// Moves a riding log and its pusher to aligned cell centers.
343static void ColossoWall_MovePusherAndLog(s32 logId, s32 offsetX, s32 offsetZ) {
344
345 struct FieldActor *pusher;
346 struct FieldActor *log;
347 s32 destinationX;
348 s32 destinationZ;
349
351 log = Object_GetById(logId);
353 {
354 destinationX =
355 ((pusher->x.fixed + COLOSSO_WALL_OFFSET_FIXED(offsetX)) & 0xFFF00000) +
356 0x80000;
357 destinationZ =
358 ((pusher->z.fixed + COLOSSO_WALL_OFFSET_FIXED(offsetZ)) & 0xFFF00000) +
359 0x80000;
360
361 pusher->speed = 0x10000;
362 pusher->acceleration = 0x8000;
363 Engine_ObjectSetPosition(pusher, destinationX, pusher->y.fixed,
364 destinationZ);
365 }
366 Object_SetMode(pusher, 27);
367 {
368 destinationX =
369 ((log->x.fixed + COLOSSO_WALL_OFFSET_FIXED(offsetX)) & 0xFFF00000) +
370 0x80000;
371 destinationZ =
372 ((log->z.fixed + COLOSSO_WALL_OFFSET_FIXED(offsetZ)) & 0xFFF00000) +
373 0x80000;
374
375 log->speed = 0x10000;
376 log->acceleration = 0x8000;
377 Engine_ObjectSetPosition(log, destinationX, log->y.fixed, destinationZ);
378 }
379 if (offsetX < 0 || offsetZ < 0) {
380 Object_SetMode(log, 4);
381 } else {
382 Object_SetMode(log, 3);
383 }
387 Object_SetMode(log, 2);
389}
390
391/// Moves the aligned riding log in the direction the player holds.
392static void Event_RideLog(void) {
394 s32 column = pusher->x.fixed >> 20;
395 s32 offsetX = 0;
396 s32 logId = RIDING_LOG_NORTH;
397
398 if (pusher->z.fixed >> 20 > 12) {
399 logId = RIDING_LOG_SOUTH;
400 }
401 if (Object_GetById(logId)->x.fixed >> 20 != column) {
402 return;
403 }
404 if (column > 51) {
405 if (gKeysHeld & KEY_LEFT) {
407 }
408 } else if (gKeysHeld & KEY_RIGHT) {
410 }
411 if (offsetX != 0) {
412 ColossoWall_MovePusherAndLog(logId, offsetX, 0);
413 // Replace both log footprints after the moving pair has stopped.
414 Map_CopyCellAttributes(120, 10, 5, 6, 48, 10);
415 column = Object_GetById(RIDING_LOG_NORTH)->x.fixed >> 20;
416 Map_CopyCellAttributes(52, 28, 1, 3, column, 10);
417 column = Object_GetById(RIDING_LOG_SOUTH)->x.fixed >> 20;
418 Map_CopyCellAttributes(52, 28, 1, 3, column, 13);
419 }
420}
421
422/// Rolls a log to half-cell coordinates while its pusher follows halfway.
423static void ColossoWall_RollLogToCell(s32 logId, s32 halfCellX, s32 halfCellZ) {
425 struct FieldActor *pusherActor = Object_GetById(pusher);
426 struct FieldActor *log = Object_GetById(logId);
427 s32 heading = (pusherActor->facing + 0x1000) & 0xe000;
428 s32 alongX = (log->x.fixed >> 20) != halfCellX / 2;
429 s32 offsetX;
430 s32 offsetZ;
431
432 halfCellX <<= 19;
433 halfCellZ <<= 19;
434 if (alongX) {
435 offsetX = (halfCellX - log->x.fixed) / 2;
436 offsetZ = 0;
437 } else {
438 offsetX = 0;
439 offsetZ = (halfCellZ - log->z.fixed) / 2;
440 }
442 Engine_ActorSetAnimation(pusher, 8);
444 log->speed = 0x8000;
445 log->acceleration = 0x3333;
446 Object_SetMode(log, gColossoWall_RollLogScript[heading / 0x4000]);
447 Engine_ObjectSetPosition(log, halfCellX, 0, halfCellZ);
449 Engine_ActorSetAnimation(pusher, 2);
450 // Follow halfway behind the rolling log while its finish effect runs.
453 HEAP_SLOT_EVENT, sizeof(struct FieldWork)))
454 ->viewCenter,
455 (s32)log);
456 Engine_ActorSetSpeed(pusher, 0x8000, 0x3333);
457 Object_SetMode(pusherActor, 2);
458 Engine_ObjectSetPosition(pusherActor, pusherActor->x.fixed + offsetX, 0,
459 pusherActor->z.fixed + offsetZ);
461 FieldObject_WaitForMove(pusherActor);
462 Object_SetMode(pusherActor, 1);
466 Object_SetMode(log, 1);
469}
470
471/// Rebuilds the three rolling logs' collision rectangles at their positions.
473 s32 cell;
474
475 Map_CopyCellAttributes(100, 11, 12, 4, 14, 11);
476
477 cell = Object_GetById(ROLLING_LOG_WEST)->x.fixed >> 20;
478 Map_CopyCellAttributes(13, 28, 1, 4, cell, 11);
479
480 cell = Object_GetById(ROLLING_LOG_EAST)->x.fixed >> 20;
481 Map_CopyCellAttributes(13, 28, 1, 4, cell, 11);
482
484 Map_CopyCellAttributes(13, 28, 4, 1, 18, cell);
485}
486
487static void Event_RollWestLogWest(void) {
490}
491
492static void Event_RollWestLogEast(void) {
495}
496
497static void Event_RollEastLogWest(void) {
500}
501
502static void Event_RollEastLogEast(void) {
505}
506
507/// Rolls the center log north and rebuilds the log collision rectangles.
511}
512
513/// Rolls the center log south and rebuilds the log collision rectangles.
517}
518
519/// Carries the selected actor on the moving platforms and alternates their
520/// routes.
522 s32 platformId;
523 struct FieldActor *rider;
524 struct FieldActor *platform;
525 s32 platformX;
526 s32 riderX;
527 s32 deltaX;
528 s32 platformZ;
529 s32 riderZ;
530 s32 deltaZ;
531 struct FieldActor *platformActor;
532
534 // Carry a rider only while both horizontal distances are less than ten
535 // pixels.
536 for (platformId = COLOSSO_WALL_FIRST_PLATFORM;
537 platformId <= COLOSSO_WALL_LAST_PLATFORM; platformId++) {
538 platform = Object_GetById(platformId);
539 platform->paused = 0;
540 platformX = platform->x.fixed;
541 riderX = rider->x.fixed;
542 deltaX = platformX - riderX;
543 if (deltaX >= 0) {
544 if (deltaX > 0x9ffff) {
545 continue;
546 }
547 } else {
548 riderX -= platformX;
549 if (riderX > 0x9ffff) {
550 continue;
551 }
552 }
553 platformZ = platform->z.fixed;
554 riderZ = rider->z.fixed;
555 deltaZ = platformZ - riderZ;
556 if (deltaZ >= 0) {
557 if (deltaZ > 0x9ffff) {
558 continue;
559 }
560 } else {
561 riderZ -= platformZ;
562 if (riderZ > 0x9ffff) {
563 continue;
564 }
565 }
566 if (GameFlag_IsSet(0x104) != 0) {
567 riderZ = platform->z.fixed;
568 } else {
569 riderZ = rider->z.fixed;
570 deltaZ = platform->velocityZ;
571 riderZ += deltaZ;
572 }
573 rider->z.fixed = riderZ;
574 }
575 // The final platform's destination selects the collision tiles for the row.
576 if (gColossoWall_SpectatorTimer != 0 && platform->targetX == 0x80000000) {
578 Map_CopyCellAttributes(58, 28, 7, 1, 58, 13);
579 } else {
580 Map_CopyCellAttributes(58, 10, 1, 1, 58, 11);
581 }
582 } else {
583 Map_CopyCellAttributes(57, 11, 1, 1, 58, 11);
584 Map_CopyCellAttributes(58, 14, 7, 1, 58, 13);
585 }
586 // Start the next alternating route when the two-second cycle wraps.
590 platformActor = Object_GetById(22);
591 FieldObject_SetPosition(platformActor, PIXELS(936), 0, PIXELS(184));
592 platformActor = Object_GetById(23);
593 FieldObject_SetPosition(platformActor, PIXELS(968), 0, PIXELS(248));
594 platformActor = Object_GetById(24);
595 FieldObject_SetPosition(platformActor, PIXELS(1000), 0, PIXELS(184));
596 platformActor = Object_GetById(25);
597 FieldObject_SetPosition(platformActor, PIXELS(1032), 0, PIXELS(248));
599 } else {
600 platformActor = Object_GetById(22);
601 FieldObject_SetPosition(platformActor, PIXELS(936), 0, PIXELS(216));
602 platformActor = Object_GetById(23);
603 FieldObject_SetPosition(platformActor, PIXELS(968), 0, PIXELS(216));
604 platformActor = Object_GetById(24);
605 FieldObject_SetPosition(platformActor, PIXELS(1000), 0, PIXELS(216));
606 platformActor = Object_GetById(25);
607 FieldObject_SetPosition(platformActor, PIXELS(1032), 0, PIXELS(216));
609 }
610 }
613 if (GameFlag_IsSet(0x104) == 0) {
615 }
616 }
617}
618
619/// Stops the moving-platform task and restores its starting row.
624 Actor_SetPosition(22, PIXELS(936), PIXELS(216));
625 Actor_SetPosition(23, PIXELS(968), PIXELS(216));
626 Actor_SetPosition(24, PIXELS(1000), PIXELS(216));
627 Actor_SetPosition(25, PIXELS(1032), PIXELS(216));
629}
630
631/// Starts the moving-platform task and releases its pause flag.
635}
636
637/// Waits for the demonstration's crossing phase, with a ten-second timeout.
639 s32 elapsedFrames;
640
641 Engine_TaskWait(10);
642
643 elapsedFrames = 0;
644 while (gColossoWall_SpectatorPhase != 0 ||
647 elapsedFrames++;
648 if (elapsedFrames >= COLOSSO_WALL_DEMONSTRATION_TIMEOUT_FRAMES) {
649 return;
650 }
651 }
652}
653
654/// Halt on the lift operator: the lifts stop and leave a crossing (0x334).
655static void Event_HaltLifts(void) {
658
661 }
662
663 Engine_TaskWait(30);
665
667
668 Map_CopyCellAttributes(58, 28, 7, 1, 58, 13);
669 Map_CopyCellAttributes(57, 11, 1, 1, 58, 11);
670}
671
672/// Reaching the arena: the leader takes a prize (the Chain Mail if first, the
673/// Cotton Shirt if second), and he and his opponent face off and fight.
674/// FLAG_SCENE_TEMP_11 says the arena was already reached; the arena's touch
675/// works while FLAG_SCENE_TEMP_10 is clear.
676static void Event_ReachArena(void) {
677 s32 rivalFinishedFirst;
678 s32 refreshCount;
679 s32 resultBase;
680
681 resultBase = 2;
684 rivalFinishedFirst = ColossoWall_ApproachFinishAndTakeItem(77, 89);
686 refreshCount = 9;
687 do {
689 refreshCount = (refreshCount - 1);
690 } while (refreshCount >= 0);
691 // Bring the competitors together before selecting the battle's result bias.
693 Actor_WalkTo(OPPONENT, 88, 256);
709 /* FAKEMATCH: resultBase, set to 2 at the top, keeps the sum unfolded; as
710 * 3 - rivalFinishedFirst, 10 instructions differ in tbs-en. */
712 resultBase - rivalFinishedFirst + 1);
717}
718
719/// Returns the course's obstacle interactions and attendant dialogue.
720const struct SceneEvent *Scene_GetEvents(void) { return gColossoWallEvents; }
721
722/// Back from the battle: the two warriors stand in the arena as the result is
723/// shown.
725
732 Actor_FaceActor(OPPONENT, 0x4000, 0);
734 if (result < 0) {
737 } else {
740 }
742 Camera_MoveTo(PIXELS(104), 0, PIXELS(192), 0);
745}
746
747/// Restores obstacle progress and sets up the selected course entrance.
749 struct FieldActor *actor;
750 s32 x;
751 s32 obstacleId;
752 s32 column;
753 s32 platformId;
754 s32 columnOffset;
755 s32 row;
756 s32 zero;
757
759 // Restore the collision templates before applying saved puzzle progress.
761 Map_CopyCellAttributes(14, 11, 12, 4, 100, 11);
762 Map_CopyCellAttributes(48, 10, 5, 6, 120, 10);
763 for (obstacleId = 26; obstacleId <= 30; obstacleId++) {
764 actor = Object_GetById(obstacleId);
765 Object_SetMode(actor, 4);
766 actor->motionFlags = 0;
767 actor->y.fixed = 0;
768 actor->priorityFlags = 2;
769 }
771 if (Engine_GameFlagIsSet(0x330)) {
773 actor->x.fixed = PIXELS(336);
774 actor->y.fixed = -PIXELS(8);
775 actor->z.fixed = PIXELS(264);
776 Map_CopyCellAttributes(19, 16, 1, 1, 20, 16);
777 Map_CopyCellAttributes(20, 80, 1, 1, 21, 80);
778 } else {
780 Object_SetMode(actor, 3);
781 actor->y.fixed = PIXELS(16);
782 }
784 zero = 0;
785 if (Engine_GameFlagIsSet(0x335)) {
786 Map_CopyCellAttributes(35, 78, 1, 1, 35, 77);
787 }
788 if (Engine_GameFlagIsSet(0x333)) {
790 Map_CopyCellAttributes(32, 37, 1, 4, 32, 77);
791 }
792 if (Engine_GameFlagIsSet(0x331)) {
796 Map_CopyCellAttributes(46, 17, 1, 1, 44, 17);
797 }
798 if (Engine_GameFlagIsSet(0x332)) {
802 Map_CopyCellAttributes(46, 17, 1, 1, 50, 17);
803 }
805 column = actor->x.fixed >> 20;
806 actor->motionFlags = zero;
807 actor->priorityFlags = 2;
808 Map_CopyCellAttributes(52, 28, 1, 3, column, 10);
810 column = actor->x.fixed >> 20;
811 actor->motionFlags = zero;
812 actor->priorityFlags = 2;
813 Map_CopyCellAttributes(52, 28, 1, 3, column, 13);
814
815 // Each movable obstacle stores its last occupied column in the scene flags.
816 x = FieldGameFlag_GetByte(0x340);
817 if (x == 0) {
818 x = 73;
819 }
820 actor = Object_GetById(12);
821 actor->x.fixed = (x << 20) + 0x80000;
822 actor->motionFlags = zero;
823 actor->priorityFlags = 2;
824 Map_CopyCellAttributes(71, 16, 1, 1, x, 16);
825 x = FieldGameFlag_GetByte(0x348);
826 if (x == 0) {
827 x = 76;
828 }
829 actor = Object_GetById(13);
830 actor->x.fixed = (x << 20) + 0x80000;
831 actor->motionFlags = zero;
832 actor->priorityFlags = 2;
833 Map_CopyCellAttributes(71, 16, 1, 1, x, 16);
834 x = FieldGameFlag_GetByte(0x350);
835 if (x == 0) {
836 x = 79;
837 }
838 actor = Object_GetById(14);
839 actor->x.fixed = (x << 20) + 0x80000;
840 actor->motionFlags = zero;
841 actor->priorityFlags = 2;
842 Map_CopyCellAttributes(71, 16, 1, 1, x, 16);
843
845 // A Halted controller leaves stationary platforms; otherwise resume the task.
847 if (Engine_GameFlagIsSet(0x334)) {
848 for (platformId = COLOSSO_WALL_FIRST_PLATFORM, row = 13, columnOffset = 58;
849 platformId <= COLOSSO_WALL_LAST_PLATFORM;
850 platformId++, columnOffset += 2) {
851 actor = Object_GetById(platformId);
852 actor->priorityFlags = 2;
853 Object_SetMode(actor, 4);
854 Map_CopyCellAttributes(56, 13, 1, 1, columnOffset, row);
855 }
858 } else {
859 for (platformId = COLOSSO_WALL_FIRST_PLATFORM;
860 platformId <= COLOSSO_WALL_LAST_PLATFORM; platformId++) {
861 actor = Object_GetById(platformId);
862 actor->priorityFlags = 2;
863 Object_SetMode(actor, 4);
864 actor->speed = 0x8000;
865 actor->acceleration = 0x3333;
866 }
869 }
875 // Entrances distinguish the race, solo practice, briefing and battle results.
876 switch (gGameState.entrance) {
877 case 1:
878 FieldScene_BuildDescriptorAndInstallTask(0, 8, 5, 0x680000, 0x1000000, 40,
879 41);
880 Map_CopyCellsLayered(127, 0, 1, 2, 79, 6);
892 }
897 break;
898 case 2:
906 }
907 break;
908 case 3:
912 }
913 break;
914 case 4:
917 break;
918 case 5:
921 break;
922 }
923 return 0;
924}
925
926/// Introduces the second finals course, then gathers the party behind Isaac.
927static void ColossoWall_IntroduceSecondFinals(s32 attendantId) {
928 s32 attendantZ;
929 s32 attendantX;
930 struct FieldActor *actor;
931
932 actor = Object_GetById(attendantId);
933 attendantX = actor->x.part.pixel;
934 attendantZ = actor->z.part.pixel;
936 Actor_SetSpeed(attendantId, PIXELS(1), PIXELS(1) / 2);
938 Actor_SetSpeed(1, PIXELS(1), PIXELS(1) / 2);
939 Actor_SetSpeed(2, PIXELS(1), PIXELS(1) / 2);
940 Actor_SetSpeed(3, PIXELS(1), PIXELS(1) / 2);
942 ((attendantZ << 16) - 0x300000));
943 Engine_ActorSetPosition(1, (attendantX << 16) - 0x100000,
944 (attendantZ << 16) - 0x280000);
945 Engine_ActorSetPosition(2, (attendantX << 16) + 0x100000,
946 (attendantZ << 16) - 0x280000);
947 Engine_ActorSetPosition(3, (attendantX << 16),
948 ((attendantZ << 16) - 0x200000));
949 Engine_ActorSetPosition(attendantId, (attendantX << 16),
950 ((attendantZ << 16) - 0x500000));
952 actor->facing = FACING_NORTH;
956 Engine_EventSetMessage(MsgColossoSiteSecondFinals);
957 Engine_ActorSetAnimationAndWait(attendantId, 3);
958 Engine_EventShowMessage(attendantId, 0);
959 Engine_ActorRunRepeatedMotion(attendantId, 2);
960 Engine_EventShowMessage(attendantId, 0);
961 Engine_ActorRunRepeatedMotion(attendantId, 2);
962 Engine_EventShowMessage(attendantId, 0);
963 Engine_ActorRunRepeatedMotion(attendantId, 2);
964 Engine_EventShowMessage(attendantId, 0);
965 // Gather the companions into Isaac before the attendant returns to his post.
973 if (actor != 0) {
974 Engine_ActorSetDestination(1, actor->x.part.pixel, actor->z.part.pixel);
975 }
978 if (actor != 0) {
979 Engine_ActorSetDestination(2, actor->x.part.pixel, actor->z.part.pixel);
980 }
983 if (actor != 0) {
984 Engine_ActorSetDestination(3, actor->x.part.pixel, actor->z.part.pixel);
985 }
986 Engine_ActorWalkToAndWait(attendantId, (attendantX - 16), (attendantZ - 64));
990 Engine_ActorWalkToAndWait(attendantId, (attendantX - 16), (attendantZ - 16));
991 Engine_ActorWalkToAndWait(attendantId, attendantX, attendantZ);
992 Engine_ActorFaceDirection(attendantId, FACING_NORTH, 10);
994}
995
996/// The Mini-Maze's attendant: the first time, a warrior runs the maze; then the
997/// objective.
998static void Event_TalkToMiniMazeAttendant(s32 attendant) {
999 s32 result;
1000 struct FieldActor *actor;
1001
1002 if (gGameState.entrance == 2) {
1003 Colosso_AskDoneCheering(attendant);
1004 } else {
1006 result = Colosso_GreetAtStage(attendant, 1);
1007 if (result == 0) {
1008 Engine_EventSetMessage(MsgColossoStageDubbedMini);
1009 Engine_CameraSetSpeed(0x30000, 0x6000);
1010 Engine_CameraMoveTo(PIXELS(1224), -1, PIXELS(184), 1);
1012 Engine_EventShowMessage(attendant, 0);
1018 Engine_EventShowMessage(attendant, 0);
1023 actor->velocityY = 0x40000;
1026 Engine_EventWait(30);
1027 Engine_EventShowMessage(attendant, 0);
1030 Colosso_RepeatStageNumber(attendant, 1);
1031 } else if (result == 1) {
1032 Engine_EventSetMessage(MsgColossoMainThingStage);
1033 Engine_EventShowMessage(attendant, 0);
1034 }
1035 Colosso_OfferCheeringSpot(result, attendant, 1);
1037 }
1038}
1039
1040/// The shifting floor's attendant: the first time, a warrior rides the lifts;
1041/// then, that the lifts' operator will cheer for Isaac.
1043 s32 result;
1044
1045 if (gGameState.entrance == 2) {
1046 Colosso_AskDoneCheering(attendant);
1047 } else {
1049 result = Colosso_GreetAtStage(attendant, 2);
1050 if (result == 0) {
1051 Engine_EventSetMessage(MsgColossoShiftingFloorStage);
1053 Camera_SetSpeed(0x30000, 0x6000);
1054 Camera_MoveTo(PIXELS(984), -1, PIXELS(232), 1);
1056 // UB: the void target is called through an incompatible return type.
1057 /* FAKEMATCH: called through a cast to the s32 call type the bytes show,
1058 * its result discarded; as a direct call, 2 instructions differ in
1059 * tbs-en. */
1060 COLOSSO_WALL_SHOW_MESSAGE(attendant, 0);
1062 Event_ShowMessage(attendant, 0);
1064 Engine_EventWait(15);
1070 Camera_MoveTo(-1, -1, -1, 0);
1071 Event_ShowMessage(attendant, 0);
1074 Colosso_RepeatStageNumber(attendant, 2);
1075 } else {
1076 if (result == 1) {
1077 Engine_EventSetMessage(MsgColossoOperatorLiftsWillCheerFor);
1078 Event_ShowMessage(attendant, 0);
1079 }
1080 }
1081 Colosso_OfferCheeringSpot(result, attendant, 2);
1083 }
1084}
1085
1086/// The Log-Rolling Stage's attendant: a warrior rides a log across; then the
1087/// objective.
1088static void Event_TalkToLogRollingAttendant(s32 attendant) {
1089 s32 result;
1090
1091 if (gGameState.entrance == 2) {
1092 Colosso_AskDoneCheering(attendant);
1093 } else {
1095 result = Colosso_GreetAtStage(attendant, 3);
1096 if (result == 0) {
1097 Engine_EventSetMessage(MsgColossoLogRollingStage);
1098 Camera_SetSpeed(0x30000, 0x6000);
1099 Camera_MoveTo(PIXELS(752), -1, PIXELS(192), 1);
1101 Engine_EventWait(60);
1102 Camera_SetSpeed(0x10000, 0x2000);
1103 Camera_MoveTo(PIXELS(752), -1, PIXELS(224), 1);
1105 Event_ShowMessage(attendant, 0);
1107 Engine_EventWait(10);
1111 Event_ShowMessage(attendant, 0);
1113 Engine_EventWait(10);
1114 // UB: the void target is called through an incompatible return type.
1115 /* FAKEMATCH: called through a cast to the s32 call type the bytes show,
1116 * its result discarded; as a direct call, 2 instructions differ in
1117 * tbs-en. */
1119 Camera_MoveTo(PIXELS(752), -1, PIXELS(216), 1);
1121 Engine_EventWait(10);
1124 Engine_EventWait(10);
1126 Event_ShowMessage(attendant, 0);
1130 Colosso_RepeatStageNumber(attendant, 3);
1131 } else if (result == 1) {
1132 Engine_EventSetMessage(MsgColossoHereYourObjectiveRideLogs);
1133 Event_ShowMessage(attendant, 0);
1134 }
1135 Colosso_OfferCheeringSpot(result, attendant, 3);
1137 }
1138}
1139
1140/// The Wall's attendant: a warrior climbs it; then the objective.
1141static void Event_TalkToWallAttendant(s32 attendant) {
1142 s32 z;
1143 struct FieldActor *actor;
1144 s32 result;
1145
1146 if (gGameState.entrance == 2) {
1147 Colosso_AskDoneCheering(attendant);
1148 return;
1149 }
1151 result = Colosso_GreetAtStage(attendant, 4);
1152 if (result == 0) {
1153 Engine_EventSetMessage(MsgColossoFansJustCallWall);
1154 Engine_CameraSetSpeed(0x30000, 0x6000);
1155 Camera_MoveTo(PIXELS(536), -1, PIXELS(240), 1);
1157 Engine_EventWait(45);
1158 Camera_SetSpeed(0x10000, 0x2000);
1159 Camera_MoveTo(PIXELS(536), -1, PIXELS(192), 1);
1160 z = 264;
1162 Engine_EventShowMessage(attendant, 0);
1167 Field_ClimbUp();
1168 Camera_SetSpeed(0x4000, 0x800);
1169 Camera_MoveTo(PIXELS(536), -1, PIXELS(160), 1);
1170 Actor_SetSpeed(ACTOR_PARTY_LEADER, 0x8000, 0x4000);
1173 Engine_ObjectSetPosition(actor, actor->x.fixed, actor->y.fixed + 0x400000,
1174 actor->z.fixed);
1176 Field_ClimbUp();
1177 Camera_MoveTo(-1, -1, -1, 0);
1178 Event_ShowMessage(attendant, 0);
1183 Engine_CameraMoveTo(PIXELS(536), -1, PIXELS(160), 1);
1184 Engine_EventShowMessage(attendant, 0);
1187 Colosso_RepeatStageNumber(attendant, 4);
1188 } else if (result == 1) {
1189 Engine_EventSetMessage(MsgColossoScalingWallQuickly);
1190 Engine_EventShowMessage(attendant, 0);
1191 }
1192 Colosso_OfferCheeringSpot(result, attendant, 4);
1194}
1195
1196/// Moves a demonstration obstacle to pixel coordinates while its pusher
1197/// follows.
1198static void ColossoWall_PushBlockToCell(s32 obstacleId, s32 column, s32 row) {
1199 s32 pusher = gGameState.selectedActor;
1200 struct FieldActor *pusherActor = Object_GetById(pusher);
1201 struct FieldActor *obstacle = Object_GetById(obstacleId);
1202 s32 alongX = (obstacle->x.fixed >> 20) != column / 2;
1203 s32 offsetX;
1204 s32 offsetZ;
1205
1206 column <<= 16;
1207 row <<= 16;
1208 if (alongX) {
1209 offsetX = (column - obstacle->x.fixed) / 2;
1210 offsetZ = 0;
1211 } else {
1212 offsetX = 0;
1213 offsetZ = (row - obstacle->z.fixed) / 2;
1214 }
1215 Engine_ActorSetAnimation(pusher, 8);
1217 obstacle->speed = 0x8000;
1218 obstacle->acceleration = 0x3333;
1220 Object_SetMode(obstacle, 3);
1221 Engine_ObjectSetPosition(obstacle, column, 0, row);
1223 Engine_ActorSetAnimation(pusher, 2);
1224 Engine_ActorSetSpeed(pusher, 0x8000, 0x3333);
1225 Object_SetMode(pusherActor, 2);
1226 Engine_ObjectSetPosition(pusherActor, pusherActor->x.fixed + offsetX, 0,
1227 pusherActor->z.fixed + offsetZ);
1228 FieldObject_WaitForMove(pusherActor);
1229 Object_SetMode(pusherActor, 1);
1230 FieldObject_WaitForMove(obstacle);
1231 Object_SetMode(obstacle, 1);
1234 Engine_EventWait(15);
1235}
1236
1237/// The Board Walk's attendant: a warrior rolls a log into a path; then the
1238/// objective.
1239static void Event_TalkToBoardWalkAttendant(s32 attendant) {
1240 s32 result;
1241
1242 if (gGameState.entrance == 2) {
1243 Colosso_AskDoneCheering(attendant);
1244 return;
1245 }
1247 result = Colosso_GreetAtStage(attendant, 5);
1248 if (result == 0) {
1249 Engine_EventSetMessage(MsgColossoPlaceCalledBoard);
1250 Engine_CameraSetSpeed(0x20000, 0x4000);
1251 Camera_MoveTo(PIXELS(328), -1, PIXELS(264), 1);
1253 Engine_EventWait(30);
1254 Camera_SetSpeed(0x18000, 0x3000);
1255 Camera_MoveTo(PIXELS(312), -1, PIXELS(176), 1);
1257 Engine_EventShowMessage(attendant, 0);
1262 Engine_EventShowMessage(attendant, 0);
1269 Engine_EventShowMessage(attendant, 0);
1273 Colosso_RepeatStageNumber(attendant, 5);
1274 } else if (result == 1) {
1275 Engine_EventSetMessage(MsgColossoFocusRollingLogs);
1276 Engine_EventShowMessage(attendant, 0);
1277 }
1278 Colosso_OfferCheeringSpot(result, attendant, 5);
1280}
1281
1282#include "common/colosso_solo.h"
1283#include "common/item_equip.h"
1284#include "common/colosso_task.h"
1285
1286/// Chooses the lettering animation script and resets its task state.
1287static void ColossoWall_StartLetteringAnimation(u32 mode, u32 tileVariant) {
1288 s32 script;
1289
1291 gColosso_LetteringParam = (tileVariant << 4);
1292
1293 {
1294 s32 priority = 0xc80;
1295 FieldScheduler_AddCallback(Colosso_UpdateLettering, priority);
1296 }
1297
1299 if (mode == 2) {
1301 }
1302 if (mode == 4) {
1304 }
1305 if (mode == 3) {
1306 if (tileVariant != 0) {
1308 } else {
1310 }
1311 }
1312
1314 gColosso_LetteringScript = (s16 *)script;
1318}
1319
1320/// Shows the course-opening lettering and plays its timed audio cues.
1322 if (mode == 0) {
1326 Engine_EventWait(30);
1328 Colosso_LoadLettering(0);
1330 Engine_EventWait(120);
1332 return;
1333 }
1334
1339 {
1340 s16 *openingScript = gColossoWall_ModeRecordDefault;
1341 *(s16 *)((u8 *)openingScript + 30) = (mode * 60);
1342 }
1343 Engine_EventWait(30);
1344 Engine_AudioPlayCue(mode + 0x5a);
1345 Colosso_LoadLettering(mode);
1347 Engine_EventWait(120);
1348
1349 // Finish the opening cue before the final lettering pulses and start signal.
1350 while (AudioCommand_GetStateByte() != 0) {
1351 Engine_TaskWait(1);
1352 }
1353
1355 Colosso_LoadLettering(5);
1358 Engine_EventWait(60);
1361 Engine_EventWait(60);
1362 Colosso_LoadLettering(6);
1365 Engine_EventWait(60);
1366 Colosso_LoadLettering(7);
1371 Engine_GameFlagSet(0x123);
1372}
1373
1374/// Shows the win-or-loss lettering and the appropriate competitor's reaction.
1376
1377 s32 reactingActor;
1378
1382 gColossoWall_ModeRecordThree[13] = result * 60;
1384 (result < 0 ? -result : result) * 60;
1385 if (result < 0) {
1386 Engine_EventWait(30);
1388 Colosso_LoadLettering(8);
1389 // UB: the void target is called through an incompatible return type.
1390 /* FAKEMATCH: called through a cast to the s32 call type the bytes show, its
1391 * result discarded; as a direct call, 2 instructions differ in tbs-en. */
1393 Engine_EventWait(-result * 60 + 60);
1394 reactingActor = 0;
1395 } else {
1396 Engine_EventWait(30);
1397 Engine_AudioPlayCue(result + 90);
1398 Colosso_LoadLettering(4);
1399 // UB: the void target is called through an incompatible return type.
1400 /* FAKEMATCH: called through the s32 cast like the call above; as a direct
1401 * call, 2 instructions differ in tbs-en. */
1403 Engine_EventWait(result * 60 + 60);
1404 reactingActor = 8;
1405 }
1406 Engine_ActorShowEmote(reactingActor, 0x105, 0);
1407 while (AudioCommand_GetStateByte() != 0) {
1408 Engine_TaskWait(1);
1409 }
1411 Engine_EventWait(30);
1415}
1416
1417#include "common/colosso_marker.h"
1419
1420/// Writes eight uppercase hexadecimal digits followed by a terminator.
1421static UNUSED void Text_WriteU32AsHex(u8 *destination, u32 value) {
1422 s32 digit;
1423
1424 destination += 8;
1425 *destination = 0;
1426 destination--;
1427 for (digit = 7; digit >= 0; digit--) {
1428 *destination = gHexDigits[value & 15];
1429 value >>= 4;
1430 destination--;
1431 }
1432}
1433
1434/// Retains the empty hook called before the finish sequence.
1435static void SceneState_EmptyHook(void) {}
1436
1438 ((struct StageControl *)gSceneState)->status = 9;
1439}
1440
1442 s16 *status = &((struct StageControl *)gSceneState)->status;
1443
1444 while (*status != 9) {
1445 Engine_TaskWait(1);
1446 }
1447}
1448
1449/// Occasionally spawns a falling particle around the linked finish effect.
1450static void ColossoWall_SpawnRandomSceneEffect(union FieldObject *fieldObject) {
1451 struct FieldActor *source = (struct FieldActor *)fieldObject;
1452 s32 position[3];
1453 u32 chance;
1454
1455 if (source->velocityY >= -255 && source->velocityY <= 255) {
1456 source->motionFlags = 0;
1457 }
1458 chance = Engine_RandomNext();
1459 if (chance * 100 >> 16 <= 9) {
1460 struct FieldActor *particle;
1461 s32 radius;
1462 s32 angle;
1463
1464 SET_VECTOR(position, source->x.fixed, source->y.fixed, source->z.fixed);
1466 angle = Engine_RandomNext();
1467 Vector_AddPolarOffset(radius << 4, angle, position);
1468 {
1469 s32 x = position[0];
1470 s32 y = position[1];
1471 s32 z = position[2];
1472
1473 particle = Engine_ObjectCreate(285, x, y, z);
1474 }
1475 if (particle != 0) {
1476 particle->motionFlags = 0;
1477 Engine_ActorSetSpriteFlags(particle, 0);
1479 Object_SetMode(particle, 1);
1480 Object_SetMode(particle, 0);
1481 }
1482 }
1483}
1484
1485/// Raises the linked finish effect, starts its script and clears the link.
1486static s32
1488 struct FieldActor *source = (struct FieldActor *)interpreter;
1489 struct FieldActor *effect = Object_GetById(source->timer);
1490
1491 Engine_ObjectSetPosition(effect, source->x.fixed,
1492 source->y.fixed + PIXELS(36), source->z.fixed);
1493 effect->motionFlags = 0;
1496 source->timer = 0;
1497 return 0;
1498}
1499
1500/// Approaches the finish item on the unused side and shows the item message.
1502 s32 secondItem) {
1503 u8 *raceWork = *(u8 **)((u8 *)gWorkSlot + WORK_SLOT_STAGE * 4);
1504 u8 *gameState;
1505 struct FieldActor *competitor;
1506 s32 finishAlreadyReached;
1507 s32 x;
1508 s32 z;
1509 u16 *itemActor;
1510 s16 *timer;
1511
1512 finishAlreadyReached = Engine_GameFlagIsSet(FLAG_SCENE_TEMP_11);
1513
1514 gameState = (u8 *)&gGameState;
1515 competitor = Object_GetById(((struct GameState *)gameState)->selectedActor);
1516
1517 if (*(s32 *)(raceWork + 232) < competitor->x.fixed) {
1518 x = *(s32 *)(raceWork + 232) + 0xc0000;
1519 } else {
1520 x = *(s32 *)(raceWork + 232) - 0xc0000;
1521 }
1522
1523 if (finishAlreadyReached != 0) {
1524 z = *(s32 *)(raceWork + 236) + 0x100000;
1525 itemActor = (u16 *)(raceWork + 228);
1526 } else {
1527 z = *(s32 *)(raceWork + 236) - 0x100000;
1528 itemActor = (u16 *)(raceWork + 226);
1529 }
1530
1531 timer = (s16 *)&competitor->timer;
1532 *timer = *itemActor;
1533 competitor->acceleration = 0x4000;
1534 competitor->speed = 0x10000;
1535
1536 Engine_ObjectSetPosition(competitor, x, 0, z);
1539
1540 while (*timer != 0) {
1541 Engine_TaskWait(1);
1542 }
1543
1544 // Assign the item on the side this competitor reached first.
1545 if (finishAlreadyReached == 0) {
1546 // UB: the void target is called through an incompatible return type.
1547 /* FAKEMATCH: called through a cast to the s32 call type the bytes show; as
1548 * a call by its void prototype, the argument moves swap and 2 instructions
1549 * differ in tbs-en. */
1551 UiWork_PushValueSlot(firstItem, 2);
1552 } else {
1553 // UB: the void target is called through an incompatible return type.
1554 /* FAKEMATCH: the same cast as above; as a call by its prototype, 2
1555 * instructions differ in tbs-en. */
1557 UiWork_PushValueSlot(secondItem, 2);
1558 }
1559
1560 gameState = (u8 *)&gGameState;
1561 UiWork_PushValueSlot(((struct GameState *)gameState)->selectedActor, 1);
1562 Engine_MessageShowCentered(MsgColossoIsaacGotItem, 3);
1564
1565 return finishAlreadyReached;
1566}
1567
1569
1570/// Allocates the race work, records both competitors and installs the gauge
1571/// task.
1573 s32 raceMode, s32 finishX,
1574 s32 finishZ, s32 firstItem,
1575 s32 secondItem) {
1576 u8 *raceWork;
1577 struct FieldActor *leader;
1578 struct FieldActor *rival;
1579 void *graphics;
1580 s32 vramSlot;
1581
1582 raceWork = Runtime_AllocateBlock(59, 0x7170);
1583 graphics = Runtime_BumpAllocateAlternatePool(512);
1584
1585 *(u16 *)(raceWork + 222) = leaderId;
1586 *(u16 *)(raceWork + 224) = rivalId;
1587 *(u16 *)(raceWork + 226) = firstItem;
1588 *(u16 *)(raceWork + 228) = secondItem;
1589 *(u16 *)(raceWork + 230) = raceMode;
1590 *(s32 *)(raceWork + 232) = finishX;
1591 *(s32 *)(raceWork + 236) = finishZ;
1592
1593 leader = Object_GetById(leaderId);
1594 rival = Object_GetById(rivalId);
1595
1596 // Mirror the recorded rival start when this is a fresh race entrance.
1598 rival->x.fixed = (finishX << 1) - leader->x.fixed;
1599 rival->z.fixed = leader->z.fixed;
1600 }
1601
1602 *(u16 *)(raceWork + 218) = 0;
1603 *(u16 *)(raceWork + 220) = 0;
1604
1605 // Upload the gauge graphics and keep only its VRAM slot after freeing
1606 // scratch.
1608
1609 vramSlot = Resource_FindFreeEntry();
1610 *(u16 *)(raceWork + 216) = vramSlot;
1611 Engine_VramLoad((s16)vramSlot, 512, graphics);
1612
1613 FieldScheduler_AddCallback(Colosso_DrawGauge, 0xc76);
1614
1615 Runtime_BumpFree(graphics);
1616}
1617
1618/// Decodes the rival's recorded path and starts its playback task.
1619static void ColossoWall_StartRivalPath(s32 pathResource) {
1620 u8 *raceWork = *(u8 **)((u8 *)gWorkSlot + WORK_SLOT_STAGE * 4);
1621 struct StageControl *pathState = (struct StageControl *)gSceneState;
1622
1624 raceWork + 240);
1626 pathState->status = 1;
1627 pathState->f2 = 1;
1628 pathState->f4 = *(u16 *)(raceWork + 224);
1629 pathState->f8 = 0;
1630 pathState->f6 = 0;
1631 }
1632 FieldScheduler_AddCallback(Colosso_UpdatePathRival, 0xc85);
1633}
1634
1636#include "common/object_findslot.h"
1637
1638/// Pushes the adjacent obstacle only when the destination and space above are
1639/// clear.
1641
1642 struct FieldActor *pusher;
1643 struct FieldActor *obstacle;
1644 struct FieldActor *blocker;
1645 u32 packedOffset;
1646 u32 direction;
1647 struct FieldPosition destination;
1648 s32 zero;
1649 s32 actorId;
1650
1651 actorId = gGameState.selectedActor;
1652 pusher = Object_GetById(actorId);
1653
1654 direction = pusher->facing >> 12;
1655
1656 packedOffset = gColossoWall_DirectionSteps[direction];
1657 destination.x = pusher->x.fixed + (packedOffset & 0xffff0000);
1658 destination.y = pusher->y.fixed;
1659 packedOffset <<= 16;
1660 destination.z = pusher->z.fixed + packedOffset;
1661
1662 obstacle = SceneData_FindSlotAtPosition(&destination, pusher);
1663 if (obstacle == 0) {
1664 return;
1665 }
1666
1667 // Require a free destination cell and free space immediately above the block.
1668 packedOffset = gColossoWall_DirectionSteps[direction];
1669 destination.x = obstacle->x.fixed + (packedOffset & 0xffff0000);
1670 destination.y = obstacle->y.fixed;
1671 packedOffset <<= 16;
1672 destination.z = obstacle->z.fixed + packedOffset;
1673
1674 blocker = SceneData_FindSlotAtPosition(&destination, obstacle);
1675 if (blocker != 0 && (blocker->collisionFlags & 1) != 0) {
1676 return;
1677 }
1678
1679 destination.x = obstacle->x.fixed;
1680 destination.y = obstacle->y.fixed + 0x100000;
1681 destination.z = obstacle->z.fixed;
1682
1683 blocker = SceneData_FindSlotAtPosition(&destination, obstacle);
1684 if (blocker != 0 && (blocker->collisionFlags & 1) != 0) {
1685 return;
1686 }
1687
1688 obstacle->terrainId = 2;
1689 zero = 0;
1690
1691 packedOffset = gColossoWall_DirectionSteps[direction];
1692 destination.x = obstacle->x.fixed + (packedOffset & 0xffff0000);
1693 destination.y = obstacle->y.fixed;
1694 packedOffset <<= 16;
1695 destination.z = obstacle->z.fixed + packedOffset;
1696
1697 if (FieldObject_CheckMovementCollision(obstacle, &destination) > 0) {
1698 return;
1699 }
1700
1701 // Start the push only after all occupancy and terrain checks have passed.
1702 Object_SetMode(pusher, 8);
1703 Engine_TaskWait(15);
1704
1705 obstacle->speed = 0x3333;
1706 obstacle->acceleration = 0x3333;
1707 Engine_ObjectSetPosition(obstacle, destination.x, destination.y,
1708 destination.z);
1709
1710 pusher->speed = 0x3333;
1711 pusher->acceleration = 0x3333;
1712 Engine_ObjectSetPosition(pusher, destination.x, destination.y, destination.z);
1713
1715 FieldObject_WaitForMove(obstacle);
1717
1718 obstacle->x.fixed = destination.x;
1719 obstacle->z.fixed = destination.z;
1720 obstacle->velocityX = zero;
1721 obstacle->velocityZ = zero;
1722
1723 Object_SetMode(pusher, 1);
1724}
1725
1727
1728const u8 gColossoWall_RollLogScript[] = {2, 2, 3, 3};
1729
1730// clang-format off
1732 0, 32, 32, 32, 96, 64, 64,
1734};
1735
1737 0x1000, 512, 1, 0x2000, 0, 1, 0x1000, 256,
1738 6, 0x2000, 16, 6, 0x7fff, 30, 0x2000, 0,
1739 30, 0x7fff, 30, -1,
1740};
1741
1743 0x1000, 128, 1, 0x2000, 0, 1, 0x1000, 256,
1744 6, 0x2000, 16, 6, 0x7fff, 60, 0x1000, 384,
1745 6, 0x7fff, 6, 0x1000, 240, 6, 0x7fff, 6,
1746 0x1000, 368, 6, 0x7fff, 6, 0x1000, 224, 6,
1747 0x7fff, 6, 0x2000, 0, 30, 0x1000, 352, 6,
1748 0x7fff, 6, 0x1000, 208, 6, 0x7fff, 6, 0x1000,
1749 336, 6, 0x7fff, 6, 0x1000, 192, 6, 0x7fff,
1750 6, 0x1000, 320, 6, 0x7fff, 6, -1,
1751};
1752
1753char gHexDigits[20] = "0123456789ABCDEF";
1754
1756 SC_ADD(SW_Y, -0x4000),
1757 SC_WAIT(1),
1758 SC_LOOP(60, SC_TOP),
1759 SC_FREE,
1760};
1761
1764 SC_LABEL(1),
1765 SC_WAIT(10),
1766 SC_SET(SW_SCALE_X, 0x16000),
1767 SC_SET(SW_SCALE_Y, 0x16000),
1768 SC_WAIT(2),
1769 SC_SET(SW_SCALE_X, 0x18000),
1770 SC_SET(SW_SCALE_Y, 0x18000),
1771 SC_WAIT(2),
1772 SC_SET(SW_SCALE_X, 0x12000),
1773 SC_SET(SW_SCALE_Y, 0x12000),
1774 SC_WAIT(2),
1775 SC_SET(SW_SCALE_X, 0x10000),
1776 SC_SET(SW_SCALE_Y, 0x10000),
1777 SC_WAIT(2),
1778 SC_SET(SW_SCALE_X, 0x16000),
1779 SC_SET(SW_SCALE_Y, 0x16000),
1780 SC_WAIT(2),
1781 SC_SET(SW_SCALE_X, 0x18000),
1782 SC_SET(SW_SCALE_Y, 0x18000),
1783 SC_WAIT(2),
1784 SC_SET(SW_SCALE_X, 0x12000),
1785 SC_SET(SW_SCALE_Y, 0x12000),
1786 SC_WAIT(2),
1787 SC_SET(SW_SCALE_X, 0x10000),
1788 SC_SET(SW_SCALE_Y, 0x10000),
1789 SC_WAIT(2),
1790 SC_WAIT(50),
1791 SC_LOOP(2, SC_TO(1)),
1792 SC_POS(0, 0, 0),
1793 SC_SET(SW_HOOK, 0),
1794 SC_END,
1795};
1796
1797#define DIRECTION_STEP(x, z) ((u32)(x) << 16 | (u16)(z))
1798
1800 DIRECTION_STEP(16, 0),
1801 DIRECTION_STEP(16, 0),
1802 DIRECTION_STEP(16, 0),
1803 DIRECTION_STEP(0, 16),
1804 DIRECTION_STEP(0, 16),
1805 DIRECTION_STEP(0, 16),
1806 DIRECTION_STEP(0, 16),
1807 DIRECTION_STEP(-16, 0),
1808 DIRECTION_STEP(-16, 0),
1809 DIRECTION_STEP(-16, 0),
1810 DIRECTION_STEP(-16, 0),
1811 DIRECTION_STEP(0, -16),
1812 DIRECTION_STEP(0, -16),
1813 DIRECTION_STEP(0, -16),
1814 DIRECTION_STEP(0, -16),
1815 DIRECTION_STEP(16, 0),
1816};
1817
1819 { 1, CONDITION_ALWAYS, 1272, 0, 168, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1820 { 2, CONDITION_ALWAYS, 1224, 0, 488, FACING_NORTH, 0, -1, -1, -1, -1, 0 },
1821 { 10, CONDITION_ALWAYS, 384, 0, 264, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1822 { SCENE_TABLE_END },
1823};
1824
1826 SCENE_EXITS(144),
1827 SCENE_EXIT(10, 144, 1),
1828 SCENE_EXIT(11, 140, 68),
1829 SCENE_EXIT(4, 140, 31),
1830 SCENE_EXIT(5, 138, 98),
1832};
1833
1836 PIXELS(1272), 0, PIXELS(168), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1838 0, 0, 0, FACING_NORTH, TALK_FACE_PARTY, 0 },
1840 0, 0, 0, FACING_NORTH, TALK_FACE_PARTY, 0 },
1842 0, 0, 0, FACING_NORTH, TALK_FACE_PARTY, 0 },
1844 0, 0, 0, FACING_EAST, TALK_FACE_PARTY, 0 },
1846 PIXELS(264), 0, PIXELS(168), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1857 { 0xcd, CONDITION_ALWAYS, 7,
1858 PIXELS(264), 0, PIXELS(208), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1859 { 0xcd, CONDITION_ALWAYS, 7,
1860 PIXELS(392), 0, PIXELS(208), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1861 { 0xcf, CONDITION_ALWAYS, 7,
1862 0, 0, 0, FACING_SOUTH, TALK_KEEP_FACING, 0 },
1863 { 0x103, CONDITION_ALWAYS, 7,
1864 PIXELS(360), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1866 PIXELS(520), 0, PIXELS(258), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1868 PIXELS(712), 0, PIXELS(280), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1870 PIXELS(808), 0, PIXELS(280), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1871 { 0x122, CONDITION_ALWAYS, 7,
1872 PIXELS(936), 0, PIXELS(216), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1873 { 0x122, CONDITION_ALWAYS, 7,
1875 { 0x122, CONDITION_ALWAYS, 7,
1877 { 0x122, CONDITION_ALWAYS, 7,
1879 { 0x122, CONDITION_ALWAYS, 7,
1881 { 0x122, CONDITION_ALWAYS, 7,
1883 { 0x122, CONDITION_ALWAYS, 7,
1885 { 0x122, CONDITION_ALWAYS, 7,
1887 { 0x103, CONDITION_ALWAYS, 7,
1888 PIXELS(360), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1890 PIXELS(1072), 0, PIXELS(280), FACING_WEST, TALK_KEEP_FACING, 0 },
1891 { 0xed, CONDITION_ALWAYS, 7,
1892 PIXELS(840), 0, PIXELS(184), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1893 { 0xed, CONDITION_ALWAYS, 7,
1894 PIXELS(840), 0, PIXELS(232), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1896 PIXELS(1224), 0, PIXELS(488), FACING_SOUTH, TALK_FACE_PARTY_AND_BACK, 0 },
1898 PIXELS(1208), 0, PIXELS(376), FACING_SOUTH, TALK_FACE_PARTY_AND_BACK, 0 },
1908 PIXELS(104), 0, PIXELS(240), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1910 PIXELS(104), 0, PIXELS(272), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1911 { SCENE_TABLE_END },
1912};
1913
1915
1917
1930 { SEARCH(SEARCH_CHEST), 101, 0x338, FIND_ITEM(ITEM_NUT) },
1931 { 0x8413, 102, 0x339, FIND_ITEM(ITEM_SMOKE_BOMB) },
1932 { SEARCH(SEARCH_CHEST), 104, 0x33b, FIND_ITEM(ITEM_NUT) },
1951 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), GUIDE, CONDITION_ALWAYS, MSGNO(MsgColossoRiverThisYearsFinalsAreIncredibleThis) },
1952 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), MINI_MAZE_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgColossoWallICantBelieveAnybodyEverGets) },
1953 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), SHIFTING_FLOOR_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgColossoWallHowDoTheyMakeThatFloor) },
1954 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), LOG_ROLLING_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgColossoWallImAmazedPeopleCanActuallyCross) },
1955 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), WALL_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgColossoWallBoyThatBitOverThereMust) },
1956 { PSYNERGY_ON_ACTOR(ABILITY_MIND_READ), BOARD_WALK_ATTENDANT, CONDITION_ALWAYS, MSGNO(MsgColossoWallIHeardBabiGotTheLog) },
1958 { SCENE_EVENTS_END },
1959};
1960
1962
1964 0x4000, -188, 2048, 0x1000, COLOSSO_WALL_INTRO_SCALE, 1, 0x2000, 16,
1965 1, 0x7fff, 14, 0x3000, 0, 20, 0x7fff, 60,
1966 0x3000, 2048, 20, 0x7fff, 60, -1,
1967};
1968
1970 0x1000, COLOSSO_WALL_PULSE_FIRST_SCALE, 1, 0x2000, 0, 1, 0x1000, COLOSSO_WALL_PULSE_LOW_SCALE,
1971 6, 0x2000, 16, 6, 0x7fff, 6, 0x1000, COLOSSO_WALL_PULSE_HIGH_SCALE,
1972 6, 0x7fff, 6, 0x1000, COLOSSO_WALL_PULSE_LOW_SCALE, 6, 0x7fff, 6,
1973 0x1000, COLOSSO_WALL_PULSE_HIGH_SCALE, 6, 0x7fff, 6, 0x1000, COLOSSO_WALL_PULSE_LOW_SCALE, 6,
1974 0x7fff, 6, 0x2000, 0, 30, 0x1000, COLOSSO_WALL_PULSE_HIGH_SCALE, 6,
1975 0x7fff, 6, 0x1000, COLOSSO_WALL_PULSE_LOW_SCALE, 6, 0x7fff, 6, 0x1000,
1976 COLOSSO_WALL_PULSE_HIGH_SCALE, 6, 0x7fff, 6, 0x1000, COLOSSO_WALL_PULSE_LOW_SCALE, 6, 0x7fff,
1977 6, 0x1000, COLOSSO_WALL_PULSE_HIGH_SCALE, 6, 0x7fff, 6, -1,
1978
1979};
1980
1982 0x1000, 128, 1, 0x2000, 0, 1, 0x1000, 256,
1983 6, 0x2000, 16, 6, 0x7fff, 60, 0x1000, 384,
1984 6, 0x7fff, 6, 0x1000, 272, 6, 0x7fff, 6,
1985 0x1000, 400, 6, 0x7fff, 6, 0x1000, 288, 6,
1986 0x7fff, 6, 0x2000, 0, 30, 0x1000, 416, 6,
1987 0x7fff, 6, 0x1000, 304, 6, 0x7fff, 6, 0x1000,
1988 432, 6, 0x7fff, 6, 0x1000, 320, 6, 0x7fff,
1989 6, 0x1000, 448, 6, 0x7fff, 6, -1,
1990};
1991// clang-format on
1992
1994
1996 SC_ARG(2),
1997 SC_SYNC,
1998 SC_ARG(1),
1999 SC_WAIT(6),
2001 SC_WAIT(3),
2002 SC_ARG(10),
2003 SC_WAIT(150),
2005 SC_ARG(1),
2006 SC_END,
2007};
2008
2010 SC_ARG(2),
2011 SC_SYNC,
2012 SC_ARG(1),
2013 SC_WAIT(6),
2015 SC_WAIT(3),
2016 SC_ARG(28),
2017 SC_WAIT(150),
2019 SC_ARG(1),
2020 SC_END,
2021};
Ability numbers, named from the English release.
@ ABILITY_FORCE
Definition ability_ids.h:17
@ ABILITY_MIND_READ
Definition ability_ids.h:16
@ ABILITY_HALT
Definition ability_ids.h:20
@ ABILITY_MOVE
Definition ability_ids.h:15
@ ABILITY_FROST
Definition ability_ids.h:11
@ ABILITY_GROWTH
Definition ability_ids.h:10
void Object_RefreshSelectorById(u32 objectId)
Waits, at most 300 frames, for an actor's script to reach its end (command 0x10).
void Object_LinkObjectAndSetCallback(u32 objectId, u32 linkedObjectId)
Makes an actor follow another (the linked action script): writes its linkedObject.
struct FieldActor * Object_GetById(s32 actor)
Returns an actor, or NULL if it does not exist.
const struct ScenePlacement * Scene_GetPlacements(void)
const struct SceneEntrance * Scene_GetEntrances(void)
const struct SceneEvent * Scene_GetEvents(void)
const u32 * Scene_GetExits(void)
Both scenes share one exit table.
const struct SceneRegion * Scene_GetRegions(void)
#define DIRECTION_STEP(dx, dz)
Pixel step by direction: X in the upper halfword, Z in the lower.
BattleUnit, a party member's or enemy's battle state, with the elements, abilities and actions it use...
static void Event_CheckAhead(void)
Checks the blocked direction before allowing another movement step.
Definition bilibin.c:331
static void Event_GrowPlant(void)
Growth on the entrance section's target: the plant grows and its cell's collision changes; 0x305 keep...
The task scheduler: tasks, their order and the scheduler's state.
#define GARET
Jerald.
#define MIA
Mary.
#define IVAN
Ivan.
s32 Field_ClimbUp(void)
Climbing up (the player presses up at a climbable wall).
Definition climbing.c:196
s16 gColosso_MarkerSlot
const u8 gColosso_LetteringPaletteOffsets[]
static void Event_IgnoreMove(void)
Move on this rock needs nothing more.
static void Event_ReachArena(void)
Reaching the arena: the leader takes a prize (the Claymore if first, the Hunter's Sword if second),...
s16 gColosso_LetteringSlot
@ PRIZE_A
the arena's Claymore and Hunter's Sword
@ LOG_ROLLING_ATTENDANT
@ OPPONENT
the warrior racing Isaac to the arena
Definition colosso_log.c:93
@ GUIDE
explains the third course
@ GROWTH_PLANT
@ PRIZE_B
static void Event_TalkToLogRollingAttendant(s32 actor)
The log-rolling area's attendant: the first time, a marker traces the log's route; then the objective...
s32 gColosso_RivalFinishScript[]
static void Event_PushRock(void)
Walking into a rock pushes it.
u8 gColosso_GaugeGraphics[]
char gHexDigits[20]
static UNUSED void Text_WriteU32AsHex(u8 *buf, u32 value)
Writes eight hexadecimal digits and a terminating null.
The Colosso finals' three courses (Colosso Log, River and Wall): a helper for the race gauge that no ...
s16 gColosso_ModeMoveTarget
s16 * gColosso_LetteringScript
s16 gColosso_LetteringTimer
s32 gColosso_LetteringPosition
s16 gColosso_ModeMoveDuration
s16 gColosso_LetteringParam
s16 gColosso_LetteringMode
static s32 ColossoWall_ApproachFinishAndTakeItem(s32 firstItem, s32 secondItem)
Approaches the finish item on the unused side and shows the item message.
static void Event_TalkToWallAttendant(s32 attendant)
The Wall's attendant: a warrior climbs it; then the objective.
static void ColossoWall_StageBattleResult(s32 result)
Back from the battle: the two warriors stand in the arena as the result is shown.
static void ColossoWall_ShowRoundStartLettering(s32 mode)
Shows the course-opening lettering and plays its timed audio cues.
static s32 ColossoWall_RaiseLinkedSceneEffect(struct ScriptInterpreter *interpreter)
Raises the linked finish effect, starts its script and clears the link.
static void ColossoWall_StartRivalPath(s32 pathResource)
Decodes the rival's recorded path and starts its playback task.
static void ColossoWall_ShowBattleResultLettering(s32 result)
Shows the win-or-loss lettering and the appropriate competitor's reaction.
static void Event_FreezeWestTarget(void)
Frost on the western target: it freezes and its crossing opens (0x331).
s32 gColossoWall_ApproachScript[]
#define COLOSSO_WALL_OFFSET_FIXED(value)
const u8 gColossoWall_RollLogScript[]
static void Event_PrepareForce(void)
Enables the child effect for Force on the course obstacle.
static void ColossoWall_StartMovingPlatforms(void)
Starts the moving-platform task and releases its pause flag.
#define COLOSSO_WALL_PLATFORM_CROSSING_FRAME
struct ScenePlacement gColossoWallPlacements[]
struct SceneEntrance gColossoWallEntrances[]
s16 gColossoWall_ModeRecordDefault[]
@ FALLING_OBSTACLE
knocked down by Force on FORCE_TARGET
@ FROST_TARGET_EAST
@ SHIFTING_FLOOR_ATTENDANT
@ FROST_TARGET_WEST
@ WALL_ATTENDANT
@ ROLLING_LOG_CENTER
@ BOARD_WALK_ATTENDANT
@ MINI_MAZE_ATTENDANT
@ RIDING_LOG_NORTH
the Log-Rolling Stage's two logs
@ ROLLING_LOG_WEST
@ ROLLING_LOG_EAST
@ FORCE_TARGET
@ RIDING_LOG_SOUTH
@ LIFT_OPERATOR
static void InitializeSelectedActor(s32 actorId)
Sets the specified actor's walking speed and acceleration.
static UNUSED void ColossoWall_RollCenterLogSouth(void)
Rolls the center log south and rebuilds the log collision rectangles.
#define COLOSSO_WALL_INTRO_SCALE
static void Event_KnockDownObstacle(void)
Force on the target: the obstacle spins down into place and the way past it opens (0x330).
static void InitializeActorZero(void)
Sets the party leader's walking speed and acceleration.
static void ColossoWall_WaitForRivalFinish(void)
static void Event_RollWestLogWest(void)
static void Event_PushRockAndUpdateCells(void)
Pushes an obstacle, then restores the first obstacle's collision cell.
static void Event_FreezeEastTarget(void)
Frost on the eastern target: it freezes and its crossing opens (0x332).
u32 gColossoWall_DirectionSteps[]
static void SceneState_EmptyHook(void)
Retains the empty hook called before the finish sequence.
const s16 gColossoWall_ModeRecordThreeAlt[]
s16 gColossoWall_ModeRecordFour[]
s32 gColossoWall_SpectatorTimer
static void Event_RollEastLogEast(void)
static void ColossoWall_PushAdjacentObstacle(void)
Pushes the adjacent obstacle only when the destination and space above are clear.
#define COLOSSO_WALL_LAST_PLATFORM
#define COLOSSO_WALL_SHOW_MESSAGE
static void Event_TalkToMiniMazeAttendant(s32 attendant)
The Mini-Maze's attendant: the first time, a warrior runs the maze; then the objective.
#define COLOSSO_WALL_PULSE_FIRST_SCALE
static void ColossoWall_StartLetteringAnimation(u32 mode, u32 tileVariant)
Chooses the lettering animation script and resets its task state.
static void Event_UpdateMovedRockCell(void)
Updates the first movable obstacle's collision after Move.
static void Event_TalkToShiftingFloorAttendant(s32 attendant)
The shifting floor's attendant: the first time, a warrior rides the lifts; then, that the lifts' oper...
#define COLOSSO_WALL_DEMONSTRATION_TIMEOUT_FRAMES
#define COLOSSO_WALL_FIRST_PLATFORM
#define COLOSSO_WALL_START_MODE_TASK
static UNUSED void ColossoWall_RollCenterLogNorth(void)
Rolls the center log north and rebuilds the log collision rectangles.
#define COLOSSO_WALL_PULSE_HIGH_SCALE
s32 gColossoWall_SpectatorPhase
static void ColossoWall_UpdateRollingLogCollision(void)
Rebuilds the three rolling logs' collision rectangles at their positions.
#define WORK_SLOT_STAGE
const s16 gColossoWall_ModeRecordTwo[]
#define COLOSSO_WALL_PULSE_LOW_SCALE
static void Event_RunRisingSequence(void)
Runs the rising-object sequence for the actor touching the obstacle.
static void ColossoWall_WaitForPlatformCrossingPhase(void)
Waits for the demonstration's crossing phase, with a ten-second timeout.
static void Event_SaveMovedRockColumns(void)
Saves the movable obstacles' columns and rebuilds their collision cells.
u32 gColossoWallExits[]
void FieldScene_BuildDescriptorAndInstallTask(s32 leaderId, s32 rivalId, s32 raceMode, s32 finishX, s32 finishZ, s32 firstItem, s32 secondItem)
Allocates the race work, records both competitors and installs the gauge task.
static void ColossoWall_RollLogToCell(s32 logId, s32 halfCellX, s32 halfCellZ)
Rolls a log to half-cell coordinates while its pusher follows halfway.
static void ColossoWall_ResetMovingPlatforms(void)
Stops the moving-platform task and restores its starting row.
#define COLOSSO_WALL_PLATFORM_CYCLE_FRAMES
static void Event_RollEastLogWest(void)
static void Event_RollWestLogEast(void)
struct SceneEvent gColossoWallEvents[]
static UNUSED void ColossoWall_MarkRivalFinished(void)
static void ColossoWall_MovePusherAndLog(s32 logId, s32 offsetX, s32 offsetZ)
Moves a riding log and its pusher to aligned cell centers.
s32 ColossoWall_Initialize(void)
Restores obstacle progress and sets up the selected course entrance.
static void ColossoWall_IntroduceSecondFinals(s32 attendantId)
Introduces the second finals course, then gathers the party behind Isaac.
static void ColossoWall_PushBlockToCell(s32 obstacleId, s32 column, s32 row)
Moves a demonstration obstacle to pixel coordinates while its pusher follows.
static void ColossoWall_UpdateMovingPlatforms(void)
Carries the selected actor on the moving platforms and alternates their routes.
static void Event_HaltLifts(void)
Halt on the lift operator: the lifts stop and leave a crossing (0x334).
#define COLOSSO_WALL_MOVE_ACTOR_PAIR
static void Event_RideLog(void)
Moves the aligned riding log in the direction the player holds.
static void ColossoWall_SpawnRandomSceneEffect(union FieldObject *fieldObject)
Occasionally spawns a falling particle around the linked finish effect.
#define COLOSSO_WALL_APPLY_MATCHING_SLOT_VALUE
static void Event_TalkToBoardWalkAttendant(s32 attendant)
The Board Walk's attendant: a warrior rolls a log into a path; then the objective.
s32 gColossoWall_RaiseScript[]
#define COLOSSO_WALL_ALTERNATE_PORTRAIT_PALETTE_OFFSET
#define COLOSSO_WALL_LOG_TRAVEL_PIXELS
s32 gColossoWall_SparkScript[]
s16 gColossoWall_ModeRecordThree[]
void Engine_ActorSetSpriteFlags(struct FieldActor *, s32)
struct FieldActor * Engine_ObjectCreate(s32, s32, s32, s32)
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
static s32 FieldResource_DecodeType01(const void *source, void *destination)
Decodes type-01 compressed data.
Definition engine_call.h:90
static s32 FieldObject_CheckMovementCollision(struct FieldActor *actor, struct FieldPosition *position)
Definition engine_call.h:66
static s32 FieldGameFlag_GetByte(s32 flag)
Definition engine_call.h:71
static void FieldBattleFx_RunRisingObjectSequence(s32 actor, s32 mode, s32 action)
Definition engine_call.h:60
static void FieldGameFlag_SetByte(s32 flag, s32 value)
Definition engine_call.h:75
void ObjectDispatch_StartCamera(struct DispatchObject *object, s32 linkedObjectHandle)
Starts an object on the camera script (SC_CAMERA).
Definition objects.c:480
void ObjectDispatch_WaitForValue16(void *rawObject)
Waits until the object's script reaches SC_END (0x10), at most 300 frames.
Definition objects.c:495
The scene script layer: the engine services a scene's events call, declared once (Engine_EventBegin,...
void Engine_ObjectSetPosition(struct FieldActor *object, s32 fixed_x, s32 fixed_y, s32 fixed_z)
s32 Engine_LeaderCheckAhead(void)
static void Actor_FaceDirection(s32 actor, s32 facing, s32 frames)
void Engine_ActorSetAnimationAndWait(s32 actor, s32 animation)
void Object_SetMode(struct FieldActor *object, s32 animation)
void Engine_MessageShowCentered(s32 message, s32 flags)
#define GameFlag_IsSet
void Engine_CameraSetSpeed(s32 speed, s32 acceleration)
static void Actor_WalkToAndWait(s32 actor, s32 x, s32 z)
static void Camera_SetSpeed(s32 speed, s32 acceleration)
void Engine_CameraMoveTo(s32 fixed_x, s32 fixed_y, s32 fixed_z, s32 pan)
s32 Engine_TaskRemoveCallback(void(*callback)(void))
void Engine_ActorSetSpeed(s32 actor, s32 speed, s32 acceleration)
void Engine_ActorFaceDirection(s32 actor, s32 facing, s32 frames)
void Engine_EventCloseScreen(void)
void Engine_ActorSetDestination(s32 actor, s32 x, s32 z)
void Engine_ActorWalkToAndWait(s32 actor, s32 x, s32 z)
static void Actor_FaceActor(s32 actor, s32 target, s32 frames)
s32 Engine_VramLoad(s32 block, s32 size, const void *data)
static void Actor_WalkTo(s32 actor, s32 x, s32 z)
void Engine_EventWaitForScreen(void)
#define GameFlag_Set
void Engine_ActorDestroy(s32 actor)
void Engine_ObjectSetScript(struct FieldActor *object, const s32 *script)
void Engine_EventOpenScreen(void)
void Engine_GameFlagSet(s32 flag)
void Engine_GameFlagClear(s32 flag)
void Engine_CameraWaitForMove(void)
void Engine_ActorWaitForMove(s32 actor)
void Engine_CameraFollowActor(s32 actor, s32 keep_position)
static void Map_CopyCellAttributes(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
void Engine_ActorRunRepeatedMotion(s32 actor, s32 repeats)
static void Event_ShowMessage(s32 speaker, s32 flags)
void Engine_ActorSetAttachedEffect(s32 actor, s32 effect)
s32 Engine_RandomNext()
void Engine_ActorSetChildValue(s32 actor, s32 value)
#define STORE_CONST(place)
Definition field_event.h:29
s32 Engine_GameFlagIsSet(s32 flag)
u8 gSceneState[]
static void Actor_SetSpeed(s32 actor, s32 speed, s32 acceleration)
static void Camera_MoveTo(s32 fixed_x, s32 fixed_y, s32 fixed_z, s32 pan)
static void Actor_SetPosition(s32 actor, s32 fixed_x, s32 fixed_z)
static void Map_CopyCellsLayered(s32 srcX, s32 srcY, s32 destX, s32 destY, s32 width, s32 height)
void Engine_ActorFaceEachOther(s32 actor, s32 other, s32 frames)
void Engine_EventWait(s32 frames)
Field objects and actors, and the scene transition styles.
The field's runtime work: map cells, scene records and regions, map markers and FieldWork.
The scene table formats: entrances, exits, placements and events, and the macros that write them.
#define SCENE_EVENTS_END
#define FIND_ITEM(item)
@ EVENT_TOUCH
@ EVENT_TALK
@ EVENT_RAISED
#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_KEEP_FACING
@ 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
#define TOUCH_WALKING_INTO(facing)
#define PLACEMENT_SHARE_PREVIOUS_SPRITE
@ FACING_EAST
Definition field_scene.h:26
@ FACING_NORTH
Definition field_scene.h:32
@ FACING_SOUTH
Definition field_scene.h:28
@ FACING_WEST
Definition field_scene.h:30
#define TOUCH_WALKING
#define PIXELS(value)
@ SEARCH_CHEST
The scene service layer: one typed inline for each engine service a scene calls by its own name,...
static void FieldObject_SetPosition(struct FieldActor *object, s32 fixed_x, s32 fixed_y, s32 fixed_z)
Game flags, named where their meaning is shown.
#define FLAG_KEEP_MUSIC
The next scene keeps the music that is playing; scene.c clears FLAG_KEEP_MUSIC as it does,...
Definition flags.h:35
#define FLAG_RETREAT_BLOCKED
Retreat is blocked, from Psynergy or an item.
Definition flags.h:55
#define FLAG_SCENE_TEMP_11
Definition flags.h:730
#define FLAG_KEEP_PARTY_POSITION
Definition flags.h:93
#define FLAG_SCENE_TEMP_10
Definition flags.h:729
BattleFxScene, the work of a battle or field Psynergy effect scene, and its arc objects.
GameState, the game's state: play time, coins, the scene and the leader's position,...
#define Battle_SetStart(mode)
Sets the battle start type.
Definition game_state.h:164
struct GameState gGameState
The work slots of gWorkSlot, read through their own names.
void * gEffectWork
Slot 56: points to a scene-specific structure.
The screen blend's globals and the graphics routines.
void BattleEffect_PauseObject(s32 actorId)
Pauses object id, keeping its per-frame update and, if it is animated, its first animation's param in...
Definition halt.c:173
void * Runtime_BumpAllocateAlternatePool(s32 size)
As MemAlloc, but tries EWRAM first.
Definition heap.c:140
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
The heap's work slots and HeapState.
@ HEAP_SLOT_EVENT
Definition heap_state.h:40
The main-image routines scene overlays import.
The hardware's I/O registers and their bits, as pret names them.
@ KEY_LEFT
Definition io_reg.h:248
@ KEY_RIGHT
Definition io_reg.h:247
The register write queue that VBlank carries out after each finished frame.
Colosso Log, River and Wall, and Goma Cave's entrance: giving a character an item and putting it on.
Item numbers, named from the English release.
@ ITEM_NUT
Definition item_ids.h:41
@ ITEM_SMOKE_BOMB
Definition item_ids.h:67
The keypad state the VBlank interrupt keeps.
volatile u32 gKeysHeld
Keys down now.
Small statement macros both games use for positions, velocities and bounds.
#define SET_VECTOR(v, x, y, z)
Sets the x, y and z of a three-part vector: a position about to be projected, a per-frame step,...
Definition macros.h:12
void Vector_AddPolarOffset(s32 radius, s32 angle, void *position)
Moves the point position by radius in direction angle on the ground plane: x by the cosine,...
Definition math.c:23
The heaps: allocating and freeing blocks.
void Runtime_BumpFree(void *buffer)
The reel game's title lettering.
Field object scripts: one s32 command word, then its operands, as gScriptObject_CommandTable runs the...
#define SC_LABEL(n)
Branch destination.
Definition objcmd.h:11
#define SC_SET(w, v)
Write a work value.
Definition objcmd.h:37
#define SC_LOOP(n, to)
Repeat up to n times.
Definition objcmd.h:28
#define SC_POS(x, y, z)
Set position.
Definition objcmd.h:18
#define SC_FREE
Release.
Definition objcmd.h:43
#define SC_SYNC
Wait for arrival.
Definition objcmd.h:17
#define SC_TO(n)
Definition objcmd.h:12
#define SC_ADD(w, v)
Definition objcmd.h:38
#define SC_ARG(v)
Definition objcmd.h:44
#define SC_WAIT(n)
Wait n frames.
Definition objcmd.h:16
#define SC_TOP
Script start.
Definition objcmd.h:13
#define SC_END
Return to the main script.
Definition objcmd.h:32
@ SW_SCALE_Y
Definition objcmd.h:63
@ SW_FACING
Definition objcmd.h:57
@ SW_Y
Definition objcmd.h:59
@ SW_SCALE_X
Definition objcmd.h:62
@ SW_HOOK
Definition objcmd.h:84
#define SC_CALL(fn)
Definition objcmd.h:50
void FieldObject_WaitForMove(void *fieldObject)
Waits a frame at a time until a field object has arrived at its target, at most 600 frames.
void Field_RequestBattleFromEncounterTable(s32 zoneNo, s32 slotNo)
Sets up a battle with enemy group slot of encounter table zone.
Definition requests.c:14
void Event_SetDefeatDestination(s32 scene, s32 entrance)
Sets where the party wakes up after losing a battle (gGameState.defeatScene and defeatEntrance).
Definition requests.c:70
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
Resource numbers, named where their contents are known.
char ResourceId_RivalPathB
void Audio_PlayCueFromEventWork(void)
Starts the current scene's field music, chosen by Scene_ResolveInteractionResult.
Definition scene.c:355
Scene numbers, named after their places.
char SceneId_ColossoWall
s32 Scheduler_SetTaskFlags(SchedulerCallback callback, u32 mask)
Stores mask as the flags of the task running callback; bit 0 keeps it running while the overlay tasks...
Definition scheduler.c:220
The field object script interpreter and the operands its commands read.
Song and sound effect ids, indices into gSound_SongTable.
#define SE_213
Definition songs.h:208
#define SE_240
Definition songs.h:235
#define SE_239
Definition songs.h:234
#define SE_247
Definition songs.h:242
#define MUS_086
Definition songs.h:85
#define MUS_089
Definition songs.h:88
#define MUS_TREASURE_FOUND
Definition songs.h:82
#define SE_288
Definition songs.h:281
#define SE_226
Definition songs.h:221
#define MUS_017
Definition songs.h:27
#define SE_227
Definition songs.h:222
#define MUS_019
Definition songs.h:29
#define SE_237
Definition songs.h:232
#define SE_238
Definition songs.h:233
#define SE_236
Definition songs.h:231
#define SE_289
Definition songs.h:282
s32 AudioCommand_GetStateByte(void)
Returns the remaining delay before BGM can be restored after a fanfare.
Definition sound.c:261
void Engine_ActorSetAnimation()
struct EventWork * gEventWork
void Engine_EventRequestExit()
void Engine_ActorSetPosition(u32 actor, s32 x, s32 z)
void Engine_EventEnd(void)
void Engine_AudioPlayCue()
void Engine_EventBegin(void)
void Engine_ActorShowEmote()
void Engine_TaskWait()
void Engine_EventSetMessage()
void Engine_EventShowMessage()
Colosso Log, River and Wall, and Crossbone Isle's platform rooms: an actor drawn as an item's menu ic...
void Colosso_ShowItemIcon(s32 id, s32 item)
Replaces the picture of actor id, when it has a sprite of its own, with the icon of item: the icon's ...
void Colosso_FadeInCompetitor(s32 id, s32 x, s32 z)
Isaac (id) appears at pixel (x, z) on the stage, facing south: he fades in through the backgrounds wh...
void Colosso_WalkActorToAndStand(s32 actorId, s32 x, s32 z)
Walks the actor at 1.25 pixels a frame to pixel (x, z), waits until it arrives and stands it there.
void Colosso_RestoreCompetitor(s32 id)
Puts Isaac (id) back where Colosso_FadeInCompetitor found him: he stands (or turns south,...
void Colosso_OfferCheeringSpot(s32 mode, s32 guide, s32 stage)
The guide at a stage, with the mode Colosso_GreetAtStage returned: offer a friend's cheering at the s...
void Colosso_RepeatStageNumber(s32 speaker, s32 competitor)
A stage's guide, after describing the stage, names it again: the copy of that line after it in the te...
void Colosso_AskDoneCheering(s32 id)
A stage's guide, talking to a friend who looks around alone, asks whether they are done cheering.
static void Colosso_TakePlaceForMatch(void)
Raised event 99: the nearest actor with animation 165 level with or below the friend on the map calls...
void Colosso_PlaceFriendsAtCheeringSpots(void)
Puts Garet, Ivan and Mia back at the cells where they chose to cheer, at the centre of each cell.
void Colosso_ClearCheeringSpots(void)
Forgets where Garet, Ivan and Mia cheer from.
s32 Colosso_GreetAtStage(s32 speaker, s32 competitor)
A stage's guide names the stage and decides what to do next.
void Colosso_ControlCharacterAlone(s32 id)
Makes the character the party's only member, the one the player moves and the camera follows,...
void Colosso_RaiseTakePlaceOnPsynergy(void)
A scene callback while a friend looks around the course alone (entrance 2): when the Psynergy request...
static void Colosso_AskReadyToRace(s32 actorId)
The guide at the course's start.
void * gWorkSlot[]
An animated sprite: its image and shadow, scale, rotation, size and up to four animation layers.
Definition animspr.h:108
u16 rotation
1/65536 turns
Definition animspr.h:118
The work of a Psynergy effect scene: its caster and target, where it takes place, the map state it re...
Definition fx_scene.h:31
s8 childMode
set by some scenes: Frost and Force leave the target be
Definition fx_scene.h:53
A field object as the object dispatcher's far calls take it.
The running scene's event work: its placement and event tables, its actors, the triggers and exit it ...
Definition event_work.h:20
s32 startTransition
SCENE_TRANSITION(style, variant).
Definition event_work.h:43
A field actor: a person or thing on the field with a position, motion toward a target,...
Definition field_obj.h:30
s16 timer
The parameter its action script reads (a follow distance or an angle to face), the player's frames to...
Definition field_obj.h:109
s32 speed
speed limit, 16.16 fixed-point pixels per frame
Definition field_obj.h:55
s32 targetX
16.16 fixed-point pixels; ACTOR_NO_TARGET when unset
Definition field_obj.h:59
u16 radius
Collision radius in pixels: 16, or half the sprite metadata's boxY, when created; the overlap checks ...
Definition field_obj.h:43
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
s32 velocityX
16.16 fixed-point pixels per frame
Definition field_obj.h:52
u8 paused
nonzero skips its script and motion, not its update
Definition field_obj.h:93
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
s32 velocityY
16.16 fixed-point pixels per frame
Definition field_obj.h:53
union FieldCoordinate z
16.16 fixed-point pixels
Definition field_obj.h:37
u16 facing
1/65536 turns
Definition field_obj.h:34
s32 acceleration
Added toward the target each frame, or taken off the speed when there is none, in 16....
Definition field_obj.h:58
u8 collisionFlags
bits: 7 refuses to move into a solid object | 4 nearer for Psynergy targets | 3 never a talk or Psyne...
Definition field_obj.h:91
s32 velocityZ
16.16 fixed-point pixels per frame
Definition field_obj.h:54
u8 terrainId
The map layer (0 to 3) its ground height is read from, which the scrolling camera updates from the ce...
Definition field_obj.h:47
A position on the field.
s32 z
16.16 fixed-point pixels
Full event work allocated by Field_RunScene (heap slot 27).
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
s16 entrance
Definition game_state.h:37
u8 movementMode
1 while the leader is climbing, 0 otherwise
Definition game_state.h:57
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
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...
The prefix that command handlers read from a field object's 0x70-byte slot.
Definition script.h:16
The rival's recorded-path playback state, held in the scene state; status 9 marks the rival finished.
void * Resource_GetTableEntry(s32 index)
Returns the ROM address of resource index from the resource directory.
Definition system.c:150
The system's routines and globals: interrupts, BIOS sound bias, random numbers and sleep.
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 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
struct FieldCoordinate::@024321007066302143002254325121164243234242211074 part
s32 fixed
16.16 fixed-point pixels
An object on the field, read as an actor or as an effect.
void Party_RestoreHpPp(s32 cureConditions)
Restores every party member's HP and PP; with cureConditions 1 it also cures poison and haunting.
Definition units.c:662
s32 Resource_FindFreeEntry(void)
Returns the number of the first free cache entry, or VRAM_CACHE_ENTRY_COUNT when all are taken.
Definition vram_cache.c:211
The sprite VRAM block cache and its owners.
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