Alchemy
Loading...
Searching...
No Matches

crossbone_isle_pillar_rooms.c

Go to the documentation of this file.
1/// @file
2/// Crossbone Isle's pillar rooms: pillars to push, jump across, Move and Carry,
3/// the switch that raises the water in room 3, and the pools Frost freezes in
4/// room 5.
5#include "script.h"
6#include "game_state.h"
7#include "map_scroll.h"
8#include "scene_ids.h"
9#include "unit/import.h"
12#include "unit/object/object.h"
14#include "unit/field/field.h"
16#include "field_obj.h"
17#include "types.h"
18#include "animspr.h"
19#include "field_event.h"
20#include "field_scene.h"
21#include "field_effect.h"
23#include "staged_actor.h"
24#include "ram_buffer.h"
25#include "map.h"
26#include "io_reg.h"
27#include "battle.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 "global_cells.h"
37#include "dma.h"
38#include "edition.h"
39#include "text/msg_ids.h"
40#include "call.h"
41#include "field_service.h"
42#include "irq.h"
43#include "ability_ids.h"
44#include "item_ids.h"
45#include "objcmd.h"
46#include "heap_state.h"
47#include "field_runtime.h"
48#include "object_effect.h"
49#include "fixed_math.h"
50#include "flags.h"
51#include "songs.h"
52#include "event.h"
53#include "macros.h"
54
55#define WATER_ACTOR 16
56/// Room 3: the water has been raised.
57#define WATER_RAISED_FLAG FLAG_SCENE_TEMP_00
58// Room 4 keeps FLAG_SCENE_TEMP_00 to 03 for its four pillars' footprints.
59// Room 5: FLAG_SCENE_TEMP_00 to 02 the three pools are frozen, 03 the leader
60// stood on the third when Frost began, 04 the sinking pillar has sunk.
61#define FIRST_FROZEN_POOL_FLAG FLAG_SCENE_TEMP_00
62#define LEADER_ON_THIRD_POOL_FLAG FLAG_SCENE_TEMP_03
63#define PILLAR_SUNK_FLAG FLAG_SCENE_TEMP_04
64
65/// A copied map cell, also read in the two flag pairs the pillar redraw keeps.
67 struct MapCell cell;
70 u32 lowFlags : 2; ///< bits 13..12 of MapCell.metatileAndFlags
71 u8 highFlags : 2; ///< bits 15..14, read by Map_GetCellHighFlags
75};
76
77STATIC_ASSERT(sizeof(union CrossboneIslePillarCell) == sizeof(struct MapCell));
78STATIC_ASSERT(offsetof(union CrossboneIslePillarCell, bits.collisionCode) == 2);
79STATIC_ASSERT(offsetof(union CrossboneIslePillarCell, bits.attributeB) == 3);
80
82#include "common/effect_spawn.h"
84
85#include "common/map_panel.h"
86
87/// Waits up to sixty frames for landing, then stops vertical movement on the
88/// ground.
89static void
91 s32 remainingFrames = 60;
92
93 for (;;) {
94 if (remainingFrames == 0) {
95 break;
96 }
98 if ((u32)actor->y.fixed == (u32)actor->terrainHeight) {
99 break;
100 }
101 remainingFrames--;
102 }
103
104 actor->velocityY = 0;
105 actor->targetY = ACTOR_NO_TARGET;
106 actor->y.fixed = actor->terrainHeight;
107}
108
109/// Waits until the actor reaches the ground or height limit, then stops its
110/// descent.
111static void
113 s32 limit) {
114 s32 remainingFrames = 60;
115
116 for (;;) {
117 if (remainingFrames == 0) {
118 break;
119 }
121 if (actor->y.fixed <= actor->terrainHeight) {
122 break;
123 }
124 if (actor->y.fixed <= limit) {
125 break;
126 }
127 remainingFrames--;
128 }
129
130 actor->velocityY = 0;
131 actor->targetY = ACTOR_NO_TARGET;
132}
133
134/// Finds the actor's footprint dimensions, grid position, and map-layer origin.
135static s32
137 struct StagedActorProbe *probe,
138 s32 *layerOriginX, s32 *layerOriginZ) {
139 struct MapScrollWork *map = gMapWork[0];
140 struct FieldActor *actor = Object_GetById(id);
141 u32 i;
142 s32 coordinate;
143 s32 farExtent;
144
145 for (i = 0; i <= 5; i++) {
146 if (actor->sprite->entries[0]->animId == gStagedActor_FootprintKinds[i]) {
147 probe->footprintIndex = i;
148 break;
149 }
150 probe->footprintIndex = 7;
151 }
152 if ((u32)probe->footprintIndex > 6) {
153 return 0;
154 }
155 // Bounds are pixel offsets; dimensions and origins below are 16-pixel cells.
156 probe->position.x = actor->x.fixed;
157 probe->position.y = actor->y.fixed;
158 probe->position.z = actor->z.fixed;
159 coordinate = gStagedActor_FootprintBounds[probe->footprintIndex * 4 + 1];
160 if (coordinate < 0) {
161 coordinate = -coordinate;
162 }
163 farExtent = gStagedActor_FootprintBounds[probe->footprintIndex * 4 + 3];
164 if (farExtent < 0) {
165 farExtent = -farExtent;
166 }
167 *depth = (coordinate + farExtent) >> 4;
168 coordinate = gStagedActor_FootprintBounds[probe->footprintIndex * 4];
169 if (coordinate < 0) {
170 coordinate = -coordinate;
171 }
172 farExtent = gStagedActor_FootprintBounds[probe->footprintIndex * 4 + 2];
173 if (farExtent < 0) {
174 farExtent = -farExtent;
175 }
176 *width = (coordinate + farExtent) >> 4;
177 // Move from the actor's center to the footprint's top-left cell.
178 probe->position.x +=
180 {
181 s32 offsetZ =
183
184 coordinate = probe->position.z;
185 coordinate += offsetZ;
186 coordinate >>= 20;
187 probe->position.z = coordinate;
188 }
189 probe->position.x >>= 20;
190 *layerOriginX = map->layers[1].offsetX >> 20;
191 *layerOriginZ = map->layers[1].offsetY >> 20;
192 return 1;
193}
194
195/// Draws the raised pillar's footprint and puts its actor under the leader.
197 struct FieldActor *actor = Object_GetById(actorId);
198 u32 width, depth;
199 s32 layerOriginX, layerOriginZ;
200 struct StagedActorProbe probe;
201 s32 cellX, cellZ;
202 u8 mask;
203
205 actorId, &width, &depth, &probe, &layerOriginX, &layerOriginZ) == 0) {
206 return 0;
207 }
208
209 cellX = probe.position.x;
210 cellZ = probe.position.z;
211 Map_CopyCellAttributes(2, 2, width, depth, cellX, cellZ);
212
213 Object_SetMode(actor, 4);
214 mask = 2;
215 actor->priorityFlags |= mask;
216
217 if (width > depth) {
218 Map_CopyCellsTo(70, 40, probe.position.x + 32, probe.position.z + 2, width,
219 depth);
220 } else {
221 Map_CopyCellsTo(68, 40, probe.position.x + 32, probe.position.z + 2, width,
222 depth);
223 }
224
225 return 1;
226}
227
228/// Redraws all seven pillars at the raised water level.
237}
238
239/// Restores the ground tiles and collision attributes under the actor's
240/// footprint.
242 struct FieldActor *actor = Object_GetById(actorId);
243 u32 width, depth;
244 s32 layerOriginX, layerOriginZ;
245 struct StagedActorProbe probe;
246
248 actorId, &width, &depth, &probe, &layerOriginX, &layerOriginZ) == 0) {
249 return 0;
250 }
251
252 {
253 s32 sourceCellX = layerOriginX + probe.position.x;
254 s32 sourceCellZ = layerOriginZ + probe.position.z;
255
256 Map_CopyCellAttributes(sourceCellX, sourceCellZ, width, depth,
257 probe.position.x, probe.position.z);
259 probe.position.z, width, depth, 255);
260 }
261
262 Object_SetMode(actor, 1);
264
265 return 1;
266}
267
268/// Reads one map cell through DMA, waiting until the transfer finishes.
269static void
271 union CrossboneIslePillarCell *dst) {
272 struct MapScrollWork *map = gMapWork[0];
273
274 if (map != 0) {
275 struct MapCell *cell = map->layers[layer].cells;
276
277 cell += x + (y << 7);
278 Dma_Set(cell, dst,
279 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) | sizeof(*cell) / sizeof(u32),
280 REG_DMA3);
281 {
282 volatile u32 *dma = REG_DMA3;
283
284 while (dma[2] & ((u32)DMA_ENABLE << 16))
285 ;
286 }
287 }
288}
289
290/// Copies the cell's flags and collision attributes without replacing its tile.
292 s32 layer, s32 x, s32 y, const union CrossboneIslePillarCell *source) {
293 struct MapScrollWork *map = gMapWork[0];
294
295 if (map != 0) {
296 // UB: the native bitfield view aliases MapCell storage. Byte writes keep
297 // its fields separate without that alias (tbs-en: 88 to 84 bytes).
298#ifdef UBFIX
299 u8 *cell = (u8 *)map->layers[layer].cells;
300
301 cell += sizeof(struct MapCell) * (x + (y << 7));
302 cell[1] = (source->bits.lowFlags << 4) | (cell[1] & ~0x30);
303 cell[1] = (source->bits.highFlags << 6) | (cell[1] & ~0xc0);
304 cell[2] = source->cell.collisionCode;
305 cell[3] = source->cell.attributeB;
306#else
307 struct CrossboneIslePillarCellBits *cell =
308 (struct CrossboneIslePillarCellBits *)map->layers[layer].cells;
309
310 cell += x + (y << 7);
311 cell->lowFlags = source->bits.lowFlags;
312 cell->highFlags = source->bits.highFlags;
313 cell->collisionCode = source->bits.collisionCode;
314 cell->attributeB = source->bits.attributeB;
315#endif
316 }
317}
318
319/// Finds the next terrain ledge and stops the falling actor at its projected
320/// height.
322 struct FieldActor *actor = Object_GetById(id);
323 s32 found;
324 struct FieldActor *temporaryActor;
325 s32 height;
326 s32 level;
327 s32 top;
328 u32 row;
329 u32 rows;
330 s32 z;
331 s32 y;
332 s32 x;
333 s32 kind;
334
335 found = 0;
336 temporaryActor = NULL;
337 Engine_ActorSetSpriteFlags(actor, found);
338 height = Map_GetTerrainHeight(2, actor->x.fixed, actor->z.fixed);
339 level = height / PIXELS(16);
340 rows = level;
341 if (level < 0) {
342 rows = -level;
343 }
344 rows++;
345 for (row = 0; row <= rows; row++) {
346 top = Map_GetTerrainHeight(actor->terrainId, actor->x.fixed,
347 (row << 20) + actor->z.fixed);
348 top /= PIXELS(16);
349 if (level < top) {
350 x = ((actor->x.fixed >> 20) << 20) + PIXELS(8);
351 if (far == 0) {
352 row += 2;
353 z = ((row + (actor->z.fixed >> 20)) << 20) + PIXELS(2);
354 y = top << 20;
355 kind = 223;
356 } else {
357 row += 3;
358 z = ((row + (actor->z.fixed >> 20)) << 20) - PIXELS(2);
359 y = top << 20;
360 kind = 253;
361 }
362 /* FAKEMATCH: a one-pass block around the object and height issues the
363 * height arithmetic first; as no block, 2 instructions differ in
364 * tbs-en. */
365 do {
366 temporaryActor = OverlayObject_PrepareObject(x, y, z, kind);
367 height = actor->z.fixed - z + y;
368 } while (0);
369 found = 1;
370 break;
371 }
372 }
374 actor->x.fixed = 0;
375 actor->y.fixed = 0;
376 actor->z.fixed = 0;
377 if (temporaryActor != 0) {
378 Engine_ObjectDispatchRelease(temporaryActor);
379 }
380 return found;
381}
382
383/// Shrinks the pillar and lowers its height for up to thirty-two frames.
385 struct FieldActor *actor = Object_GetById(index);
386 u32 elapsedFrames;
387
388 actor->motionFlags = 0;
389
390 elapsedFrames = 0;
391 for (;;) {
392 if (elapsedFrames > 31)
393 return;
395 actor->scaleY += -Q16_ONE / 10;
396 actor->y.fixed += -PIXELS(4) / 5;
397 elapsedFrames++;
398 if (actor->scaleY <= Q16_ONE / 10 - 1) {
399 actor->scaleY = Q16_ONE / 10;
400 return;
401 }
402 }
403}
404
405/// Spawns the unused particle near the leader once every four frames.
407
408 struct FieldActor *leader;
409 s32 cycleFrame;
410 s32 x;
411 s32 z;
413 s32 drawX;
414 s32 drawZ;
415 s32 drawY;
416 struct EffectOptions *settings;
417 struct EffectOptions options;
418
420 cycleFrame = gFrameCount & 3;
421 if (cycleFrame == 0) {
422 settings = &options;
423 settings->palette = 10;
424 settings->startScaleX = 7 * Q16_ONE / 10;
425 settings->startScaleY = 7 * Q16_ONE / 10;
426 drawX = Engine_RandomNext();
427 x = leader->x.fixed + ((((u32)((drawX << 4) + drawX) >> 16) - 8) << 16);
428 drawZ = Engine_RandomNext();
429 z = leader->z.fixed + ((((u32)((drawZ << 4) + drawZ) >> 16) - 8) << 16);
430 drawY = Engine_RandomNext();
431 velocityY = Math_Divide(
432 (((u32)((drawY << 2) + drawY) >> 16) << 16) + PIXELS(3), 10);
433 Effect_Spawn(x, leader->y.fixed, z, 0, velocityY, cycleFrame,
435 }
436}
437
438/// Jumps two cells forward when both the intervening and landing cells permit
439/// it.
441 struct FieldActor *leader;
442 u8 *flags;
443 s32 savedMotionFlags;
444 s32 jumpDirection;
445#if !TBS_PATCH_1
446 s32 step;
447#endif
448 struct FieldPosition *position;
449 struct FieldPosition destination;
450
452 flags = &leader->motionFlags;
453 savedMotionFlags = *flags;
454#if TBS_PATCH_1
455 jumpDirection = (leader->facing + 0x2000) & 0xc000;
456#endif
457 if (gGameState.movementMode != 0) {
458 return 0;
459 }
460 position = &destination;
461#if TBS_PATCH_1
462 position->x = (leader->x.fixed & -0x100000) + PIXELS(8);
463 position->y = leader->y.fixed;
464 position->z = (leader->z.fixed & -0x100000) + PIXELS(8);
465 Vector_AddPolarOffset(PIXELS(16), jumpDirection, position);
466#else
467 jumpDirection = leader->facing >> 12;
468 step = gStagedActor_DirectionSteps[jumpDirection];
469 position->x = leader->x.fixed + (step & 0xffff0000);
470 position->y = leader->y.fixed;
471 step <<= 16;
472 position->z = leader->z.fixed + step;
473#endif
474 // Require a free intervening cell before testing the two-cell landing.
475 if (FieldObject_CheckMovementCollision(leader, position) != 1 &&
476 StagedActor_FindAtTile(position, leader) == 0) {
477#if TBS_PATCH_1
478 position->x = (leader->x.fixed & -0x100000) + PIXELS(8);
479 position->y = leader->y.fixed;
480 position->z = (leader->z.fixed & -0x100000) + PIXELS(8);
481 Vector_AddPolarOffset(PIXELS(32), jumpDirection, position);
482#else
483 step = gStagedActor_DirectionSteps[jumpDirection];
484 position->x = leader->x.fixed + ((step & 0xffff0000) * 2);
485 position->y = leader->y.fixed;
486 step <<= 17;
487 position->z = leader->z.fixed + step;
488#endif
489 if (StagedActor_FindAtTile(position, leader) == 0 &&
490 FieldObject_CheckMovementCollision(leader, position) == 0) {
491 EV_BEGIN();
492 Object_SetMode(leader, 6);
495 Object_SetMode(leader, 7);
496 leader->speed = PIXELS(3);
497 leader->acceleration = PIXELS(2);
498 leader->velocityY = PIXELS(4);
499 *flags &= 126;
502 position->z >> 16);
503 Object_SetMode(leader, 6);
505 *flags = savedMotionFlags;
506 EV_END();
507 return 1;
508 }
509 }
510 return 0;
511}
512
513/// Moves the particle while damping its velocity, growing it, and advancing
514/// rotation.
515static void
517 s32 velocityX;
518 s32 velocityY;
519 s32 velocityZ;
520
521 object->effect.x += object->effect.velocity.x;
522 object->effect.y += object->effect.velocity.y;
523 object->effect.z += object->effect.velocity.z;
524 velocityX = object->effect.velocity.x;
525 velocityY = object->effect.velocity.y;
526 velocityZ = object->effect.velocity.z;
527 object->effect.velocity.x = velocityX - Math_Divide(velocityX, 10);
528 object->effect.velocity.y = velocityY - Math_Divide(velocityY, 3);
529 object->effect.velocity.z = velocityZ - Math_Divide(velocityZ, 10);
530 object->effect.scaleX += object->effect.scaleRateX;
531 object->effect.scaleY += object->effect.scaleRateY;
532 object->effect.sprite->rotation += object->effect.spin;
533}
534
535/// Applies the actor timer's low four bits to its child sprite value.
537 struct ScriptInterpreter *interpreter) {
538 struct FieldActor *actor = (struct FieldActor *)interpreter;
539 ObjectGroup_SetChildValue(actor, actor->timer & 15);
540 return 0;
541}
542
544 s16 scene = gGameState.scene;
545
546 if (scene == (s32)&SceneId_CrossboneIslePillarRooms1) {
548 }
549 if (scene == (s32)&SceneId_CrossboneIslePillarRooms2) {
551 }
552 if (scene == (s32)&SceneId_CrossboneIslePillarRooms3) {
554 }
555 if (scene == (s32)&SceneId_CrossboneIslePillarRooms4) {
557 }
558 if (scene == (s32)&SceneId_CrossboneIslePillarRooms5) {
560 }
562}
563
564const struct SceneRegion *Scene_GetRegions(void) { return 0; }
565
566/// Returns the shared exits for the pillar rooms.
568
570 s16 scene = gGameState.scene;
571
572 if (scene == (s32)&SceneId_CrossboneIslePillarRooms1) {
574 }
575 if (scene == (s32)&SceneId_CrossboneIslePillarRooms2) {
577 }
578 if (scene == (s32)&SceneId_CrossboneIslePillarRooms3) {
580 }
581 if (scene == (s32)&SceneId_CrossboneIslePillarRooms5) {
583 }
585}
586
587/// Walking into a pillar in room 1: jump two cells across it where the cells
588/// allow, otherwise push it.
589static void Event_JumpOrPushPillar(void) {
590 EV_BEGIN();
592 StagedActor_AdvancePair();
593 EV_END();
594}
595
596/// Walking into a pillar in rooms 2 and 3: push it if the cell beyond is clear.
597static void Event_PushPillar(void) {
598 struct StagedActorProbe out;
599
600 EV_BEGIN();
601 if (StagedActor_FindClearPosition(&out) != 0)
602 SceneActor_MoveAndRedraw(out);
603 EV_END();
604}
605
606/// A pillar slot's place and ground cell, without its flag; it holds one slot
607/// while two are swapped.
609 s32 x; ///< 16-pixel map cells
610 s32 y; ///< 16-pixel map cells
611 s32 z; ///< 16-pixel map cells
613};
614
615/// Where one of room 4's pillars stands, the ground cell saved under it and its
616/// footprint flag.
618 s32 x; ///< 16-pixel map cells
619 s32 y; ///< 16-pixel map cells
620 s32 z; ///< 16-pixel map cells
623};
624
625static struct PillarSlot sPillarSlots[4];
627static u8 sShakenScroll[12];
629
630/// Randomly substitutes the shifted background scroll during the water
631/// transition.
633 u32 line = REG_VCOUNT;
634 u32 *src = &((u32 *)gBgScroll)[1];
635 volatile u32 *reg = (volatile u32 *)0x04000014;
636
637 if (line == 227 || line <= 46) {
638 if ((u32)(Engine_RandomNext() * 100) >> 16 < sShakeChance) {
639 src = ((u32 *)sShakenScroll);
640 }
641 }
642 *reg = *src++;
643 reg = (volatile u32 *)0x04000018;
644 *reg++ = *src++;
645 *reg = *src;
646}
647
648/// Waits until the DMA channel clears its enable bit.
649static __inline__ void Dma_Wait(volatile u32 *dma) {
650 while (dma[2] & ((u32)DMA_ENABLE << 16))
651 ;
652}
653
654/// Copies the three background scroll pairs and shifts their vertical offsets
655/// by 192 pixels.
657 u32 *destination;
658 const u32 *source;
659 u16 *coordinates;
660
661 source = (const u32 *)&((u32 *)gBgScroll)[1];
662 destination = (u32 *)sShakenScroll;
663 *destination++ = *source++;
664 *destination++ = *source++;
665 *destination = *source;
666 coordinates = (u16 *)sShakenScroll;
667 coordinates[1] += 0xc0;
668 coordinates[3] += 0xc0;
669 coordinates[5] += 0xc0;
670}
671
672/// Reveals the alternate water tiles through a gradually randomized scroll
673/// transition.
675 s32 waterRaised;
676
678 if (waterRaised == 0) {
679 Map_DrawCellPanel(10, 19, 16, 5, waterRaised, 10, 31);
680 Map_DrawCellPanel(10, 51, 16, 5, 1, 10, 31);
681 Map_DrawCellPanel(42, 51, 16, 5, 2, 10, 31);
682 } else {
683 Map_DrawCellPanel(10, 19, 16, 5, 0, 10, 31);
684 Map_DrawCellPanel(10, 83, 16, 5, 1, 10, 31);
685 Map_DrawCellPanel(42, 83, 16, 5, 2, 10, 31);
686 }
687 // Increase the chance of showing the alternate scroll on each scanline.
688 sShakeChance = 0;
690 0xc80);
694 sShakeChance = 0;
695 do {
697 } while (++sShakeChance <= 100);
698 // Commit the water tiles, then remove both scroll callbacks.
701 Map_CopyCellsTo(0, 32, 32, 0, 32, 32);
702 Map_CopyCellsTo(32, 32, 64, 0, 32, 32);
703 } else {
704 Map_CopyCellsTo(0, 64, 32, 0, 32, 32);
705 Map_CopyCellsTo(32, 64, 64, 0, 32, 32);
706 }
708 Runtime_SetIrqHandler(1, 0, 0);
712 Engine_TaskWait(30);
713}
714
715/// Searching the switch in room 3 flips it. "Isaac flipped the switch." The
716/// water rises, or drains, and the pillars stand higher or lower with it.
717static void Event_FlipWaterSwitch(void) {
718 s32 waterRaised;
719 struct FieldActor *water;
720
721 EV_BEGIN();
722 Camera_SetSpeed(PIXELS(1), PIXELS(1) / 8);
723 Camera_MoveTo(PIXELS(281), -1, PIXELS(432), 1);
725 Engine_MessageShowCentered(MsgFieldFlippedSwitch, 1);
726 waterRaised = FLAG_CHECK(WATER_RAISED_FLAG);
727 // Raise or drain the water before updating the pillar collision footprints.
728 if (waterRaised == 0) {
729 SE(SE_232);
731 WAIT(30);
732 SE(SE_240);
734 Object_GetById(WATER_ACTOR)->motionFlags = waterRaised;
735 water = Actor_Get(WATER_ACTOR);
736 water->y.fixed = -PIXELS(32);
737 OBJ_POS_SET(WATER_ACTOR, 272, 416);
741 Map_CopyCellsTo(65, 40, 16, 27, 2, 4);
744 Map_CopyCellAttributes(24, 3, 1, 1, 24, 8);
746 } else {
747 SE(SE_232);
749 WAIT(30);
750 SE(SE_230);
752 water = Actor_Get(WATER_ACTOR);
753 water->y.fixed = -PIXELS(32);
754 OBJ_POS_SET(WATER_ACTOR, 272, 436);
756 Map_CopyCellsTo(65, 45, 16, 27, 2, 4);
766 Map_CopyCellAttributes(24, 4, 1, 1, 24, 8);
768 }
769 EV_END();
770}
771
772/// Matches a nearby actor's draw priority when it is behind the comparison
773/// actor.
774static s32
776 struct FieldActor *front) {
777 s32 result = 0;
778
779 if (front->x.fixed == actor->x.fixed && front->y.fixed == actor->y.fixed &&
780 front->z.fixed == actor->z.fixed) {
781 return result;
782 }
783 if (front->x.fixed - PIXELS(16) < actor->x.fixed &&
784 actor->x.fixed < front->x.fixed + PIXELS(16) &&
785 front->y.fixed / PIXELS(1) == actor->y.fixed / PIXELS(1) &&
786 front->z.fixed > actor->z.fixed &&
787 front->z.fixed - PIXELS(32) < actor->z.fixed) {
788 if (actor->sprite->image.priority < front->sprite->image.priority) {
790 actor->sprite->image.priority = front->sprite->image.priority;
791 actor->sprite->shadow.priority = front->sprite->shadow.priority;
792 }
793 result = 1;
794 }
795 return result;
796}
797
798/// Matches the comparison actor's draw priority when it overlaps the actor in
799/// front.
800static s32
802 struct FieldActor *actor) {
803 s32 result = 0;
804
805 if (actor->x.fixed == front->x.fixed && actor->y.fixed == front->y.fixed &&
806 actor->z.fixed == front->z.fixed) {
807 return result;
808 }
809 if (front->x.fixed - PIXELS(16) < actor->x.fixed &&
810 actor->x.fixed < front->x.fixed + PIXELS(16) &&
811 actor->y.fixed / PIXELS(1) == front->y.fixed / PIXELS(1) &&
812 front->z.fixed > actor->z.fixed &&
813 front->z.fixed - PIXELS(32) < actor->z.fixed) {
814 if (front->sprite->image.priority > actor->sprite->image.priority) {
816 actor->sprite->image.priority = front->sprite->image.priority;
817 actor->sprite->shadow.priority = front->sprite->shadow.priority;
818 }
819 result = 1;
820 }
821 return result;
822}
823
824// UB: calling this s32 function through a void update hook is incompatible.
825// Plain void changes 320 to 316 bytes in tbs-en.
826#ifdef UBFIX
827/// Keeps each moving pillar's priority and collision flags consistent with the
828/// leader.
829static void
831#else
832/// Keeps each moving pillar's priority and collision flags consistent with the
833/// leader.
834static s32
836#endif
837 struct FieldActor *actor = (struct FieldActor *)fieldObject;
838 struct FieldActor *leader = Object_GetById(0);
839 s32 hit;
840 u32 id;
841
842 if (((struct FieldWork *)gEventWork)->castStarted == 1) {
843 actor->sprite->image.priority = leader->sprite->image.priority;
844 actor->collisionFlags |= 1;
845#ifdef UBFIX
846 return;
847#else
848 return 0;
849#endif
850 }
852 for (id = 8; id <= 11; id++) {
854 Object_GetById(id));
855 }
856 if (hit != 0) {
857 for (id = 8; id <= 11; id++) {
859 }
860 }
861 if (actor->y.fixed < leader->y.fixed) {
863 actor->collisionFlags &= 254;
864 if (actor->sprite->image.priority < leader->sprite->image.priority) {
866 actor->sprite->image.priority = leader->sprite->image.priority;
867 actor->sprite->shadow.priority = leader->sprite->shadow.priority;
868 hit = 1;
869 }
870 } else {
872 actor->collisionFlags |= 1;
873 }
874 if (hit == 0) {
876 }
877#ifdef UBFIX
878 return;
879#else
880 return 0;
881#endif
882}
883
884/// Sorts the four pillars by height and depth, carrying their saved cell state
885/// with them.
887 u32 i;
888 u32 j;
889 struct FieldActor *firstActor;
890 struct FieldActor *secondActor;
891 struct FieldActor savedActor;
892#ifdef UBFIX
893 struct PillarPlacement savedPlacement;
894#else
895 // UB: the original 12-byte scratch receives a 16-byte DMA transfer.
896 // A complete placement record adds four native stack bytes.
897 s32 savedPlacement[3];
898#endif
899
900 for (i = 0; i <= 2; i++) {
901 firstActor = Object_GetById(i + 8);
902 for (j = i; j <= 3; j++) {
903 secondActor = Object_GetById(j + 8);
904 if (firstActor->y.fixed <= secondActor->y.fixed &&
905 firstActor->z.fixed < secondActor->z.fixed) {
906 continue;
907 }
908 Dma_Set(secondActor, &savedActor,
909 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
910 sizeof(savedActor) / sizeof(u32),
911 REG_DMA3);
913 Dma_Set(firstActor, secondActor,
914 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
915 sizeof(savedActor) / sizeof(u32),
916 REG_DMA3);
918 Dma_Set(&savedActor, firstActor,
919 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
920 sizeof(savedActor) / sizeof(u32),
921 REG_DMA3);
923 Dma_Set(&sPillarSlots[j], &savedPlacement,
924 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
925 sizeof(struct PillarPlacement) / sizeof(u32),
926 REG_DMA3);
929 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
930 sizeof(struct PillarPlacement) / sizeof(u32),
931 REG_DMA3);
933 Dma_Set(&savedPlacement, &sPillarSlots[i],
934 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
935 sizeof(struct PillarPlacement) / sizeof(u32),
936 REG_DMA3);
938 if (Engine_GameFlagIsSet(sPillarSlots[i].flag) &&
942 } else if (!Engine_GameFlagIsSet(sPillarSlots[i].flag) &&
946 }
947 }
948 }
949}
950
951/// Move or Carry on one of room 4's pillars, and after each push there: the
952/// pillars settle, and their footprints follow them.
953static void Event_MovePillar(void) {
954 u32 id;
955 struct FieldActor *actor;
956 u8 *flags;
957 u32 i;
958 u32 k;
959 struct FieldActor *other;
961
962 for (id = 8; id <= 11; id++) {
963 actor = Object_GetById(id);
964 actor->terrainId = 2;
965 i = id - 8;
966 if ((actor->x.fixed >> 20) == sPillarSlots[i].x &&
967 (actor->z.fixed >> 20) == sPillarSlots[i].z && actor->velocityY == 0) {
968 continue;
969 }
970 Dma_Set(&actor->x, &sProbePosition,
971 ((u32)(DMA_ENABLE | DMA_32BIT) << 16) |
972 sizeof(sProbePosition) / sizeof(u32),
973 REG_DMA3);
976 actor->motionFlags = 3;
977 }
978 flags = &actor->motionFlags;
980 0, sPillarSlots[i].x, sPillarSlots[i].z, &sPillarSlots[i].cell);
982 2, sPillarSlots[i].x, sPillarSlots[i].z, &sPillarSlots[i].cell);
983 // Falling pillars either reach a ledge, shrink away, or settle on ground.
984 if (*flags & 1) {
985 if (GetMapCellCollision(2, actor->x.fixed, actor->z.fixed) == 50) {
987 // Both u8 accesses are alias set 0, and the view is no struct reference
988 // (MEM_IN_STRUCT_P).
989 /* FAKEMATCH: the u8 view keeps these flag updates out of the record's
990 * alias set; as field stores, the loop is reallocated and 217
991 * instructions differ in tbs-en. */
995 } else if (GetMapCellCollision(2, actor->x.fixed, actor->z.fixed) == 51) {
998 actor->y.fixed = 0;
1001 actor->x.fixed = 0;
1002 actor->y.fixed = 0;
1003 actor->z.fixed = 0;
1005 } else {
1007 }
1008 *flags = 0;
1009 }
1010 // Save the new ground cell before applying the pillar's height attributes.
1012 0, actor->x.fixed >> 20, actor->z.fixed >> 20, &sPillarSlots[i].cell);
1013 if (actor->y.fixed >= 0) {
1014 CrossboneIslePillarRooms_ReadMapCell(0, 27, (actor->y.fixed >> 20) + 6,
1015 &cell);
1017 actor->z.fixed >> 20, &cell);
1018 cell.bits.highFlags = sPillarSlots[i].cell.bits.highFlags;
1020 2, *(s32 *)((u8 *)actor + offsetof(struct FieldActor, x)) >> 20,
1021 *(s32 *)((u8 *)actor + offsetof(struct FieldActor, z)) >> 20, &cell);
1022 }
1023 sPillarSlots[i].x = actor->x.fixed >> 20;
1024 sPillarSlots[i].y = actor->y.fixed >> 20;
1025 sPillarSlots[i].z = actor->z.fixed >> 20;
1026 // Mark pillars covered by this one, then restore their sort order.
1027 for (k = 0; k <= 3; k++) {
1028 if (k == i) {
1029 continue;
1030 }
1032 other = Object_GetById(k + 8);
1033 if ((actor->x.fixed >> 20) == (other->x.fixed >> 20) &&
1034 (actor->z.fixed >> 20) == (other->z.fixed >> 20) &&
1035 actor->y.fixed > other->y.fixed) {
1037 }
1038 }
1039 }
1041}
1042
1043/// Walking into a pillar in room 4: jump across if possible, otherwise push
1044/// it, and the pillars settle.
1046 EV_BEGIN();
1047#if TBS_PATCH_3
1049 Object_GetById(0)->terrainId, Object_GetById(0)->x.fixed,
1050 Object_GetById(0)->z.fixed);
1051#endif
1053 Object_GetById(0)->motionFlags &= 254;
1055 StagedActor_AdvancePair();
1060 }
1061 EV_END();
1062}
1063
1064/// Walking north at room 4's ladder: climb up.
1065static void Event_ClimbUp(void) { Field_ClimbUp(); }
1066
1067/// Walking south at room 4's ladder: push the pillar below, jump, or climb
1068/// down.
1069static void Event_PushOrClimbDown(void) {
1070 struct FieldActor *leader = Object_GetById(0);
1071 struct FieldPosition pos;
1072 struct FieldPosition *p = &pos;
1073
1074 p->x = leader->x.fixed;
1075 p->y = leader->y.fixed;
1076 p->z = leader->z.fixed + PIXELS(16);
1077 if (StagedActor_FindAtTile(p, leader) != NULL) {
1081 }
1082}
1083
1084/// Stepping on trigger 40 in room 4: the fade overlay follows the leader, and
1085/// the leader's rise counter starts.
1086static void Event_StartRising(void) {
1088 ((struct FadeOverlayWork *)WORK_SLOT(36))->actor = leader;
1089 leader->riseCounter = 1;
1090}
1091
1092/// Stepping on trigger 41 in room 4: the overlay lets the leader go, and the
1093/// rise counter stops.
1094static void Event_StopRising(void) {
1096
1097 ((struct FadeOverlayWork *)WORK_SLOT(36))->actor = NULL;
1098 leader->riseCounter = 0;
1099}
1100
1101// UB: calling this s32 function through a void update hook is incompatible.
1102// Plain void changes 54 to 52 bytes in tbs-en.
1103#ifdef UBFIX
1104/// Keeps the frozen pool's underfoot flag consistent with the leader's height
1105/// and depth.
1107 union FieldObject *fieldObject) {
1108#else
1109/// Puts the frozen pool under the leader when it is behind and below the
1110/// leader.
1112 union FieldObject *fieldObject) {
1113#endif
1114 struct FieldActor *actor = (struct FieldActor *)fieldObject;
1116
1117 if (actor->z.fixed > ref->z.fixed) {
1119 if (actor->y.fixed < ref->y.fixed)
1121 }
1122
1123#ifdef UBFIX
1124 return;
1125#else
1126 return 0;
1127#endif
1128}
1129
1130/// Remembers the frozen pool, installs its priority callback, and starts its
1131/// flicker script.
1133 struct FieldActor *actor;
1134
1135 actor = Object_GetById(id);
1136 EV_BEGIN();
1137#ifdef UBFIX
1139#else
1140 actor->update = (void (*)(
1142#endif
1143 Map_CopyCellAttributes(20, 14, 1, 1, actor->x.fixed >> 20,
1144 actor->z.fixed >> 20);
1147 EV_END();
1148}
1149
1150/// Frost on the first pool in room 5: it freezes into a pillar of ice.
1151static void Event_FreezeFirstPool(void) {
1153}
1154
1155/// Frost on the second pool in room 5.
1156static void Event_FreezeSecondPool(void) {
1158}
1159
1160/// Frost begins on the third pool: note whether the leader stands on it.
1162 struct FieldActor *leader;
1163 struct FieldActor *pillar;
1164 u8 *effects;
1165
1167 pillar = Object_GetById(13);
1168 effects = gEffectWork;
1169 if (pillar->x.fixed >> 20 == leader->x.fixed >> 20 &&
1170 pillar->z.fixed >> 20 == leader->z.fixed >> 20) {
1172 effects[53] = 1;
1173 } else {
1175 }
1176}
1177
1178/// Later in the same Frost: the third pool shrinks back, and the pillar beside
1179/// it (actor 14) rises 32 pixels.
1180static void Event_FrostRaisesPillar(void) {
1181 struct FieldActor *pillar;
1182
1183 pillar = Object_GetById(14);
1186 (Object_GetById(13)->sprite)->image.priority = 2;
1187 pillar->sprite->image.priority = 2;
1188 pillar->acceleration = PIXELS(2) / 5;
1189 pillar->speed = PIXELS(4) / 5;
1190 FieldObject_SetPosition(pillar, pillar->x.fixed, PIXELS(32), pillar->z.fixed);
1192 Map_CopyCellAttributes(20, 14, 1, 1, 22, 16);
1193}
1194
1195/// Emits growing, rotating particles during the third pool's Frost sequence.
1197 struct EffectOptions options;
1198 struct EffectOptions *settings;
1199 s32 velocityX;
1200 s32 velocityY;
1201 u32 draw;
1202 u32 mask;
1203
1204 if ((gFrameCount & 2) != 0)
1205 return;
1206 if ((gFrameCount & 7) == 0)
1208
1209 settings = &options;
1210 settings->palette = 10;
1211 settings->startScaleX = Q16_ONE / 2;
1212 settings->startScaleY = Q16_ONE / 2;
1213 settings->targetScaleX = 8 * Q16_ONE / 5;
1214 settings->targetScaleY = 8 * Q16_ONE / 5;
1215 draw = Engine_RandomNext();
1216 mask = 0x0ffff000;
1217 mask &= draw;
1218 settings->rotation = mask;
1220
1221 draw = Engine_RandomNext();
1222 velocityX = -(((draw * 5) >> 16) * PIXELS(1) + PIXELS(6));
1223 velocityX /= 2;
1224 draw = Engine_RandomNext();
1225 velocityY = -(((draw * 5) >> 16) * PIXELS(1) + PIXELS(5));
1226
1227 Effect_Spawn(PIXELS(324), PIXELS(48), PIXELS(228), velocityX, velocityY, 0,
1231 settings);
1232}
1233
1234// UB: calling this s32 function through a void update hook is incompatible.
1235// Plain void changes 172 to 168 bytes in tbs-en.
1236#ifdef UBFIX
1237/// Emits a randomized particle from the thawing pool every eight frames.
1238static void
1240#else
1241/// Emits a randomized particle from the thawing pool every eight frames.
1242static s32
1244#endif
1245 struct FieldActor *actor = (struct FieldActor *)fieldObject;
1246 struct EffectOptions options;
1247 struct EffectOptions *settings;
1248 u32 phase = gFrameCount & 7;
1249 s32 x;
1250 s32 y;
1251 s32 z;
1252 s32 scale;
1253
1254 if (phase != 0)
1255#ifdef UBFIX
1256 return;
1257#else
1258 return 0;
1259#endif
1260
1261 settings = &options;
1262 settings->palette = 7;
1263 settings->startScaleX = 7 * Q16_ONE / 10;
1264 settings->startScaleY = 7 * Q16_ONE / 10;
1265
1266 x = actor->x.fixed + (((((u32)Engine_RandomNext() * 17) >> 16) - 8) << 16);
1267 y = actor->y.fixed + ((((u32)Engine_RandomNext() * 17) >> 16) << 16);
1268 z = actor->z.fixed + (((((u32)Engine_RandomNext() * 17) >> 16) - 8) << 16);
1269 scale = Math_Divide(
1270 (((u32)Engine_RandomNext() * 5) >> 16) * PIXELS(1) + PIXELS(3), 10);
1271
1272 Effect_Spawn(x, y, z, 0, scale, phase,
1274#ifdef UBFIX
1275 return;
1276#else
1277 return 0;
1278#endif
1279}
1280
1281/// Frost ends on the third pool: unless the leader stood on it, it freezes,
1282/// and a frozen second pool thaws back to water.
1283static void Event_FreezeThirdPool(void) {
1284 s32 set;
1285
1287 if (set == 0) {
1289 EV_BEGIN();
1290 Engine_CameraSetSpeed(PIXELS(3) / 5, PIXELS(3) / 40);
1291 Engine_CameraMoveTo(PIXELS(312), -1, PIXELS(184), 1);
1293 WAIT(20);
1294 Map_CopyCellsTo(73, 10, 60, 10, 1, 2);
1295 WAIT(20);
1296 // The third pool freezes while the previously frozen second pool thaws.
1298 0xc80);
1299 WAIT(40);
1300 if (FLAG_CHECK(FIRST_FROZEN_POOL_FLAG + 1) != 0) {
1301#ifdef UBFIX
1303#else
1304 Object_GetById(12)->update = (void (*)(
1306#endif
1307 OBJ_MODE(12, 6);
1309 Object_GetById(12)->update = (void (*)(union FieldObject *))set;
1310 Map_CopyCellAttributes(17, 13, 1, 1, 18, 13);
1314 } else {
1315 WAIT(60);
1316 }
1318 WAIT(20);
1319 Map_CopyCellsTo(72, 10, 60, 10, 1, 2);
1320 WAIT(20);
1321 EV_END();
1322 }
1323}
1324
1325/// Move begins on room 5's sinking pillar: its cell is cleared.
1327 struct FieldActor *pillar = Object_GetById(10);
1328 s32 x;
1329 s32 z;
1330
1331 EV_BEGIN();
1332
1333 x = pillar->x.fixed >> 20;
1334 z = pillar->z.fixed >> 20;
1335
1337 EV_END();
1338}
1339
1340/// Move on room 5's sinking pillar, and after each push: once it reaches
1341/// column 16 it sinks two pixels into place for good.
1342static void Event_MoveSinkingPillar(void) {
1343 struct FieldActor *pillar;
1344 s32 alreadyLowered;
1345
1346 pillar = Object_GetById(10);
1347 EV_BEGIN();
1349 (pillar->z.fixed >> 20), 1, 1, 255);
1350 if ((pillar->x.fixed >> 20) == 16) {
1351 alreadyLowered = FLAG_CHECK(PILLAR_SUNK_FLAG);
1352 if (alreadyLowered == 0) {
1353 WAIT(10);
1355 pillar->motionFlags = alreadyLowered;
1356 pillar->terrainHeight = -PIXELS(2);
1357 pillar->y.fixed = -PIXELS(2);
1359 }
1360 }
1361 EV_END();
1362}
1363
1364/// Walking into room 5's sinking pillar: push it if the cell beyond is clear.
1365static void Event_PushSinkingPillar(void) {
1366 struct StagedActorProbe probe;
1367
1368 EV_BEGIN();
1369 if (StagedActor_FindClearPosition(&probe) != 0) {
1370 SceneActor_MoveAndRedraw(probe);
1371 } else {
1373 StagedActor_AdvancePair();
1375 }
1376 EV_END();
1377}
1378
1379const struct SceneEvent *Scene_GetEvents(void) {
1380 s16 scene = gGameState.scene;
1381
1382 if (scene == (s32)&SceneId_CrossboneIslePillarRooms1) {
1384 }
1385 if (scene == (s32)&SceneId_CrossboneIslePillarRooms2) {
1387 }
1388 if (scene == (s32)&SceneId_CrossboneIslePillarRooms3) {
1390 }
1391 if (scene == (s32)&SceneId_CrossboneIslePillarRooms4) {
1393 }
1394 if (scene == (s32)&SceneId_CrossboneIslePillarRooms5) {
1396 }
1398}
1399
1400/// Prepares the lowered actor's collision and draw priority below the leader.
1402 struct FieldActor *actor = Object_GetById(id);
1403
1404 actor->collisionFlags &= 254;
1406 actor->motionFlags = 0;
1408 actor->sprite->image.priority = 2;
1409}
1410
1411/// Restores a previously frozen pillar's mode, map cell, priority hook, and
1412/// script.
1414 struct FieldActor *actor;
1415
1416 actor = Object_GetById(actorId);
1417 if (FLAG_CHECK(FIRST_FROZEN_POOL_FLAG + actorId - 11) != 0) {
1418 Object_SetMode(actor, 5);
1419#ifdef UBFIX
1421#else
1422 actor->update = (void (*)(
1424#endif
1425 Map_CopyCellAttributes(20, 14, 1, 1, actor->x.fixed >> 20,
1426 actor->z.fixed >> 20);
1428 }
1429}
1430
1431/// Uses terrain layer 2 and installs the moving pillar's priority callback.
1433 struct FieldActor *actor = Object_GetById(id);
1434
1435 actor->terrainId = 2;
1436 actor->motionFlags = 0;
1437#ifdef UBFIX
1439#else
1440 actor->update = (void (*)(
1442#endif
1443}
1444
1445/// Matches the leader's draw priority to a nearby pillar in front.
1448 u32 selector;
1449 struct FieldActor *actor;
1450
1451 for (selector = 8; selector <= 11; selector++) {
1452 actor = Object_GetById(selector);
1453 if (hero->y.fixed / PIXELS(1) == actor->y.fixed / PIXELS(1) &&
1454 hero->z.fixed <= actor->z.fixed - PIXELS(8) &&
1455 hero->z.fixed > actor->z.fixed - PIXELS(24)) {
1456 if (hero->x.fixed - PIXELS(16) <= actor->x.fixed &&
1457 actor->x.fixed < hero->x.fixed + PIXELS(16)) {
1459 actor->sprite->image.priority);
1460 return;
1461 }
1462 } else {
1465 }
1466 }
1467}
1468
1469/// Restores each room's pillar footprints, water state, and persistent puzzle
1470/// state.
1472 struct FieldActor *actor;
1473 u32 i;
1474 u32 id;
1475 s32 x;
1476 s32 z;
1477
1480 FieldScene_RedrawActorFootprint(8);
1481 FieldScene_RedrawActorFootprint(9);
1482 FieldScene_RedrawActorFootprint(10);
1483 FieldScene_RedrawActorFootprint(11);
1484 FieldScene_RedrawActorFootprint(12);
1486 // Rebuild the water room and restore raised pillars when its flag persists.
1487 Map_CopyCells(32, 0, 64, 32, 0, 64);
1488 FieldScene_RedrawActorFootprint(8);
1489 FieldScene_RedrawActorFootprint(9);
1490 FieldScene_RedrawActorFootprint(10);
1491 FieldScene_RedrawActorFootprint(11);
1492 FieldScene_RedrawActorFootprint(12);
1493 FieldScene_RedrawActorFootprint(13);
1494 FieldScene_RedrawActorFootprint(14);
1495 FieldScene_RedrawActorFootprint(15);
1498 Map_CopyCellsTo(79, 34, 84, 24, 1, 2);
1499 Map_CopyCellsTo(0, 32, 32, 0, 32, 32);
1500 Map_CopyCellsTo(32, 32, 64, 0, 32, 32);
1502 Map_CopyCellAttributes(24, 3, 1, 1, 24, 8);
1503 }
1505 // Restore the four movable pillars and the leader's riding overlay.
1509 }
1513 }
1518 {
1519 s32 *entry = (s32 *)sPillarSlots;
1520
1521 for (i = 0; i < 4; i++) {
1522 entry[0] = 0;
1523 entry[1] = 0;
1524 entry[2] = 0;
1525 entry[4] = i + FLAG_SCENE_TEMP_00;
1526 entry += 5;
1527 }
1528 }
1530 WAIT(1);
1534 for (id = 8; id < 12; id++) {
1535 actor = Object_GetById(id);
1536 x = actor->x.fixed >> 20;
1537 if (x == 37) {
1538 z = actor->z.fixed >> 20;
1539 if (z == 9) {
1540 Map_CopyCellAttributes(27, 8, 1, 1, x, z);
1541 break;
1542 }
1543 }
1544 }
1545 }
1547 // Restore room 5's moved pillar, frozen pools, and raised pillar pair.
1548 OBJ_MODE(10, 2);
1550 FieldScene_RedrawActorFootprint(8);
1551 FieldScene_RedrawActorFootprint(9);
1552#if TBS_PATCH_1
1555#endif
1563 actor = Object_GetById(13);
1564 actor->update = 0;
1568 Object_GetById(13)->scaleX = 3 * Q16_ONE / 2;
1569 Object_GetById(13)->scaleY = 3 * Q16_ONE / 2;
1570 Object_GetById(13)->sprite->image.priority = 3;
1571 Object_GetById(14)->sprite->image.priority = 3;
1572 Map_CopyCellAttributes(26, 12, 1, 1, 22, 16);
1573 }
1574 }
1575 return 0;
1576}
1577
1579 SC_WAIT(1), SC_LOOP(22, SC_TOP), SC_WAIT(1),
1580 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1581};
1582
1584 SC_WAIT(1), SC_LOOP(44, SC_TOP), SC_WAIT(1),
1585 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1586};
1587
1589 SC_WAIT(1), SC_LOOP(126, SC_TOP), SC_WAIT(1),
1590 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1591};
1592
1593/// Pixel step by direction: X in the upper halfword, Z in the lower.
1594#define DIRECTION_STEP(dx, dz) (((dx) << 16) | ((dz) & 0xffff))
1595
1597 DIRECTION_STEP(16, 0), DIRECTION_STEP(16, 0), DIRECTION_STEP(16, 0),
1598 DIRECTION_STEP(0, 16), DIRECTION_STEP(0, 16), DIRECTION_STEP(0, 16),
1599 DIRECTION_STEP(0, 16), DIRECTION_STEP(-16, 0), DIRECTION_STEP(-16, 0),
1600 DIRECTION_STEP(-16, 0), DIRECTION_STEP(-16, 0), DIRECTION_STEP(0, -16),
1601 DIRECTION_STEP(0, -16), DIRECTION_STEP(0, -16), DIRECTION_STEP(0, -16),
1602 DIRECTION_STEP(16, 0),
1603};
1604
1605// clang-format off
1607 207, 205, 228, 229, 298, 297,
1608};
1609
1611 -32, -8, 32, 8,
1612 -8, -32, 8, 32,
1613 -32, -16, 32, 0,
1614 -8, -32, 8, 32,
1615 -32, -8, 32, 8,
1616 -8, -32, 8, 32,
1617};
1618
1623};
1624
1626 SC_SET(0x23, 2),
1628 SC_WAIT(2),
1629 SC_SET(0x23, 0),
1631 SC_WAIT(6),
1633};
1634
1636 MAP_CELL_STEP(76, 34, 1, 2, 4), MAP_CELL_STEP(77, 34, 1, 2, 4), MAP_CELL_STEP(78, 34, 1, 2, 4), MAP_CELL_STEP(79, 34, 1, 2, 4), MAP_CELL_STEPS_END,
1637};
1638
1640 MAP_CELL_STEP(79, 34, 1, 2, 4), MAP_CELL_STEP(78, 34, 1, 2, 4), MAP_CELL_STEP(77, 34, 1, 2, 4), MAP_CELL_STEP(76, 34, 1, 2, 4), MAP_CELL_STEPS_END,
1641};
1642
1644 MAP_CELL_STEP(67, 34, 2, 5, 6), MAP_CELL_STEP(69, 34, 2, 5, 6), MAP_CELL_STEP(71, 34, 2, 5, 6), MAP_CELL_STEP(73, 34, 2, 5, 6), MAP_CELL_STEPS_END,
1645};
1646
1648 MAP_CELL_STEP(71, 50, 2, 5, 6), MAP_CELL_STEP(69, 50, 2, 5, 6), MAP_CELL_STEP(67, 50, 2, 5, 6), MAP_CELL_STEP(65, 50, 2, 5, 6), MAP_CELL_STEPS_END,
1649};
1650
1652 MAP_CELL_STEP(75, 38, 2, 1, 4), MAP_CELL_STEP(77, 38, 2, 1, 6), MAP_CELL_STEP(79, 38, 2, 1, 8), MAP_CELL_STEP(65, 53, 2, 1, 1), MAP_CELL_STEPS_END,
1653};
1654
1656 { 0, CONDITION_ALWAYS, 120, 0, 152, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1657 { SCENE_TABLE_END },
1658};
1659
1661 { 1, CONDITION_ALWAYS, 712, 0, 760, FACING_NORTH, 0, 560, 368, 992, 792, 0 },
1662 { 2, CONDITION_ALWAYS, 600, 0, 760, FACING_NORTH, 0, 560, 368, 992, 792, 0 },
1663 { SCENE_TABLE_END },
1664};
1665
1667 { 1, CONDITION_ALWAYS, 216, 0, 360, FACING_NORTH, 0, 32, 32, 384, 400, 0 },
1668 { 2, CONDITION_ALWAYS, 104, 0, 360, FACING_NORTH, 0, 32, 32, 384, 400, 0 },
1669 { SCENE_TABLE_END },
1670};
1671
1673 { 1, CONDITION_ALWAYS, 184, 0, 472, FACING_NORTH, 0, -1, -1, -1, -1, 0 },
1674 { 2, CONDITION_ALWAYS, 64, 0, 472, FACING_NORTH, 0, -1, -1, -1, -1, 0 },
1675 { SCENE_TABLE_END },
1676};
1677
1679 { 1, CONDITION_ALWAYS, 744, 0, 520, FACING_NORTH, 0, 440, 16, 960, 544, 0 },
1680 { 2, CONDITION_ALWAYS, 632, 0, 520, FACING_NORTH, 0, 440, 16, 960, 544, 0 },
1681 { SCENE_TABLE_END },
1682};
1683
1685 { 1, CONDITION_ALWAYS, 280, 0, 488, FACING_NORTH, 0, 16, 32, 464, 528, 0 },
1686 { 2, CONDITION_ALWAYS, 168, 0, 488, FACING_NORTH, 0, 16, 32, 464, 528, 0 },
1687 { SCENE_TABLE_END },
1688};
1689
1691 SCENE_EXITS(115),
1692 SCENE_EXIT(1, 126, 3),
1693 SCENE_EXIT(2, 126, 4),
1694 SCENE_EXIT(0, 116, 0),
1695 SCENE_EXIT(1, 127, 3),
1696 SCENE_EXIT(2, 127, 4),
1697 SCENE_EXIT(0, 119, 0),
1698 SCENE_EXIT(1, 130, 3),
1699 SCENE_EXIT(2, 130, 4),
1700 SCENE_EXIT(0, 121, 0),
1701 SCENE_EXIT(1, 132, 3),
1702 SCENE_EXIT(2, 132, 4),
1703 SCENE_EXIT(0, 122, 0),
1704 SCENE_EXIT(1, 133, 3),
1705 SCENE_EXIT(2, 133, 4),
1707};
1708
1710 { SCENE_TABLE_END },
1711};
1712
1715 PIXELS(744), 0, PIXELS(488), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1717 PIXELS(792), 0, PIXELS(552), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1719 PIXELS(696), 0, PIXELS(616), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1721 PIXELS(776), 0, PIXELS(680), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1722 { SCENE_TABLE_END },
1723};
1724
1726 { 0x12a, CONDITION_ALWAYS, 7,
1727 PIXELS(144), 0, PIXELS(200), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1728 { 0x12a, CONDITION_ALWAYS, 7,
1729 PIXELS(256), 0, PIXELS(200), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1730 { 0x12a, CONDITION_ALWAYS, 7,
1731 PIXELS(224), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1732 { 0x129, CONDITION_ALWAYS, 7,
1733 PIXELS(280), 0, PIXELS(160), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1734 { 0x129, CONDITION_ALWAYS, 7,
1735 PIXELS(184), 0, PIXELS(240), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1736 { SCENE_TABLE_END },
1737};
1738
1740 { 0xcd, CONDITION_ALWAYS, 7,
1741 0, 0, 0, FACING_SOUTH, TALK_KEEP_FACING, 0 },
1742 { 0xcf, CONDITION_ALWAYS, 7,
1743 PIXELS(144), 0, PIXELS(296), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1744 { 0xcd, CONDITION_ALWAYS, 7,
1745 PIXELS(232), 0, PIXELS(256), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1746 { 0xcf, CONDITION_ALWAYS, 7,
1747 PIXELS(208), 0, PIXELS(216), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1748 { 0xcd, CONDITION_ALWAYS, 7,
1749 PIXELS(232), 0, PIXELS(176), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1750 { 0xcd, CONDITION_ALWAYS, 7,
1751 0, 0, 0, FACING_SOUTH, TALK_KEEP_FACING, 0 },
1752 { 0xcf, CONDITION_ALWAYS, 7,
1753 PIXELS(272), 0, PIXELS(168), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1754 { 0xcf, CONDITION_ALWAYS, 7,
1755 PIXELS(352), 0, PIXELS(168), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1756 { 0xd6, CONDITION_ALWAYS, 7,
1757 0, 0, 0, FACING_SOUTH, TALK_KEEP_FACING, 0 },
1758 { SCENE_TABLE_END },
1759};
1760
1762 { 0x100, CONDITION_ALWAYS, ACTOR_STAND,
1763 PIXELS(648), 0, PIXELS(184), FACING_NORTH, TALK_KEEP_FACING, 0 },
1764 { 0x100, CONDITION_ALWAYS, ACTOR_STAND,
1765 0, 0, 0, FACING_NORTH, TALK_KEEP_FACING, 0 },
1766 { 0x100, CONDITION_ALWAYS, ACTOR_STAND,
1767 PIXELS(680), 0, PIXELS(392), FACING_NORTH, TALK_KEEP_FACING, 0 },
1768 { 0x100, CONDITION_ALWAYS, ACTOR_STAND,
1769 0, 0, 0, FACING_NORTH, TALK_KEEP_FACING, 0 },
1770 { SCENE_TABLE_END },
1771};
1772
1774 { 0xcf, CONDITION_ALWAYS, 7,
1775 PIXELS(208), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1776 { 0xcf, CONDITION_ALWAYS, 7,
1777 PIXELS(224), 0, PIXELS(280), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1778 { 0x125, CONDITION_ALWAYS, ACTOR_STAND,
1779 PIXELS(232), 0, PIXELS(296), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1781 PIXELS(136), 0, PIXELS(216), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1783 PIXELS(296), 0, PIXELS(216), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1785 PIXELS(360), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1786 { 0x122, CONDITION_ALWAYS, ACTOR_STAND,
1787 PIXELS(360), 0, PIXELS(264), FACING_SOUTH, TALK_KEEP_FACING, 0 },
1788 { SCENE_TABLE_END },
1789};
1790
1792 { SCENE_EVENTS_END },
1793};
1794
1796 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1797 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1799 { SEARCH(SEARCH_CHEST), 100, 0xec2, FIND_ITEM(ITEM_ANTIDOTE) },
1800 { SEARCH(SEARCH_CHEST), 101, 0xec3, FIND_ITEM(ITEM_NUT) },
1801 { SEARCH(SEARCH_CHEST), 102, 0xec4, FIND_COINS(111) },
1802 { SEARCH(SEARCH_CHEST), 103, 0xec5, FIND_ITEM(ITEM_HARD_NUT) },
1807 { SCENE_EVENTS_END },
1808};
1809
1811 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1812 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1816 { SEARCH(SEARCH_CHEST), 102, 0xec8, FIND_COINS(222) },
1817 { SEARCH(SEARCH_CHEST), 103, 0xec9, FIND_ITEM(ITEM_ELIXIR) },
1818 { SCENE_EVENTS_END },
1819};
1820
1822 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1823 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1825 { 0x413, 100, 0xed2, FIND_COINS(555) },
1826 { 0xc413, 100, 0xed2, FIND_COINS(555) },
1827 { 0x413, 101, 0xed3, FIND_ITEM(ITEM_LUCKY_MEDAL) },
1828 { 0xe413, 101, 0xed3, FIND_ITEM(ITEM_LUCKY_MEDAL) },
1829 { SEARCH(SEARCH_CHEST), 102, 0xed4, FIND_ITEM(ITEM_POTION) },
1830 { SEARCH(SEARCH_CHEST), 103, 0xed5, FIND_ITEM(ITEM_NINJA_GARB) },
1832 { SCENE_EVENTS_END },
1833};
1834
1836 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1837 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1844 { SEARCH(SEARCH_CHEST), 100, 0xeda, FIND_COINS(777) },
1845 { SEARCH(SEARCH_CHEST), 101, 0xedb, FIND_ITEM(ITEM_LUCKY_MEDAL) },
1846 { 0x413, 102, 0xedc, FIND_ITEM(ITEM_PSY_CRYSTAL) },
1847 { SEARCH(SEARCH_CHEST), 103, 0xedd, FIND_ITEM(ITEM_WICKED_MACE) },
1856 { SCENE_EVENTS_END },
1857};
1858// clang-format on
1859
1865 {SEARCH(SEARCH_CHEST), 100, 0xede, FIND_COINS(888)},
1866 {SEARCH(SEARCH_CHEST), 101, 0xedf, FIND_ITEM(ITEM_POTION)},
1884};
Ability numbers, named from the English release.
@ ABILITY_MOVE
Definition ability_ids.h:15
@ ABILITY_FROST
Definition ability_ids.h:11
@ ABILITY_CARRY
Definition ability_ids.h:22
void ObjectGroup_SetChildValue(struct FieldActor *actor, s32 param)
Sets the param byte of every animated part of an actor's sprite.
void ObjectMotion_WaitForAnimationChange(u32 objectId)
Waits, at most 90 frames, for an actor's animation to change.
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)
s32 Scene_Initialize(void)
Sets up whichever scene is being entered and runs its arrival events.
s32 gStagedActor_DirectionSteps[]
Pixel step by direction: X in the upper halfword, Z in the lower.
s32 gStagedActor_FootprintBounds[]
#define DIRECTION_STEP(dx, dz)
Pixel step by direction: X in the upper halfword, Z in the lower.
u32 gStagedActor_FootprintKinds[]
Animated sprites: their layers, frames and colour modes, and the resource slots they draw from.
s32 gEffectScript0[]
const s32 * gEffectScripts[]
s32 gEffectScript1[]
s32 gEffectScript2[]
Calls through a function pointer, by argument count.
s32 Field_ClimbUp(void)
Climbing up (the player presses up at a climbable wall).
Definition climbing.c:196
s32 Field_ClimbDown(void)
Climbing down (the player presses down at a climbable wall).
Definition climbing.c:119
static __inline__ void Dma_Wait(volatile u32 *dma)
The engine calls a scene uses to create an overlay object and set its sprite flags.
void Engine_ActorSetSpriteFlags(struct FieldActor *, s32)
static s32 CrossboneIslePillarRooms_DrawRaisedFootprint(s32 actorId)
Draws the raised pillar's footprint and puts its actor under the leader.
static void Event_StartRising(void)
Stepping on trigger 40 in room 4: the fade overlay follows the leader, and the leader's rise counter ...
struct ScenePlacement gCrossboneIslePillarRoomsPlacements1[]
struct SceneEntrance gCrossboneIslePillarRoomsEntrances5[]
static void CrossboneIslePillarRooms_SetCellAttributes(s32 layer, s32 x, s32 y, const union CrossboneIslePillarCell *source)
Copies the cell's flags and collision attributes without replacing its tile.
struct SceneEvent gCrossboneIslePillarRoomsEventsOther[]
static void Event_JumpOrPushRoom4Pillar(void)
Walking into a pillar in room 4: jump across if possible, otherwise push it, and the pillars settle.
static void CrossboneIslePillarRooms_ReadMapCell(s32 layer, s32 x, s32 y, union CrossboneIslePillarCell *dst)
Reads one map cell through DMA, waiting until the transfer finishes.
static u8 sShakenScroll[12]
static void CrossboneIslePillarRooms_ShrinkPillar(s32 index)
Shrinks the pillar and lowers its height for up to thirty-two frames.
static void Event_JumpOrPushPillar(void)
Walking into a pillar in room 1: jump two cells across it where the cells allow, otherwise push it.
static UNUSED void CrossboneIslePillarRooms_SpawnUnusedLeaderParticle(void)
Spawns the unused particle near the leader once every four frames.
static void Event_PushPillar(void)
Walking into a pillar in rooms 2 and 3: push it if the cell beyond is clear.
static s32 CrossboneIslePillarRooms_SyncPriorityIfBehind(struct FieldActor *actor, struct FieldActor *front)
Matches a nearby actor's draw priority when it is behind the comparison actor.
static void Event_FlipWaterSwitch(void)
Searching the switch in room 3 flips it.
static void Event_FrostRaisesPillar(void)
Later in the same Frost: the third pool shrinks back, and the pillar beside it (actor 14) rises 32 pi...
static void CrossboneIslePillarRooms_PrepLoweredActor(s32 id)
Prepares the lowered actor's collision and draw priority below the leader.
static s32 CrossboneIslePillarRooms_UpdateFrozenPoolPriority(union FieldObject *fieldObject)
Puts the frozen pool under the leader when it is behind and below the leader.
static void CrossboneIslePillarRooms_DrawRaisedPillars(void)
Redraws all seven pillars at the raised water level.
static void Event_FreezeFirstPool(void)
Frost on the first pool in room 5: it freezes into a pillar of ice.
static void CrossboneIslePillarRooms_SpawnFrostParticle(void)
Emits growing, rotating particles during the third pool's Frost sequence.
static struct PillarSlot sPillarSlots[4]
static void CrossboneIslePillarRooms_RestoreFlaggedPillar(s32 actorId)
Restores a previously frozen pillar's mode, map cell, priority hook, and script.
static s32 CrossboneIslePillarRooms_SyncPriorityIfAhead(struct FieldActor *front, struct FieldActor *actor)
Matches the comparison actor's draw priority when it overlaps the actor in front.
static void CrossboneIslePillarRooms_FreezePool(s32 id)
Remembers the frozen pool, installs its priority callback, and starts its flicker script.
static s32 CrossboneIslePillarRooms_RestoreGroundFootprint(s32 actorId)
Restores the ground tiles and collision attributes under the actor's footprint.
static void Event_FreezeSecondPool(void)
Frost on the second pool in room 5.
#define WATER_ACTOR
static s32 CrossboneIslePillarRooms_SetChildValueFromTimer(struct ScriptInterpreter *interpreter)
Applies the actor timer's low four bits to its child sprite value.
struct SceneEntrance gCrossboneIslePillarRoomsEntrances4[]
static void CrossboneIslePillarRooms_PrepareWaterScroll(void)
Copies the three background scroll pairs and shifts their vertical offsets by 192 pixels.
#define WATER_RAISED_FLAG
Room 3: the water has been raised.
struct SceneEntrance gCrossboneIslePillarRoomsEntrances3[]
static void Event_StartMovingSinkingPillar(void)
Move begins on room 5's sinking pillar: its cell is cleared.
static struct FieldPosition sProbePosition
static void CrossboneIslePillarRooms_AnimateWaterLevel(void)
Reveals the alternate water tiles through a gradually randomized scroll transition.
static void Event_StopRising(void)
Stepping on trigger 41 in room 4: the overlay lets the leader go, and the rise counter stops.
static s32 CrossboneIslePillarRooms_UpdateActorPriority(union FieldObject *fieldObject)
Keeps each moving pillar's priority and collision flags consistent with the leader.
struct SceneEntrance gCrossboneIslePillarRoomsEntrances1[]
static void Event_ClimbUp(void)
Walking north at room 4's ladder: climb up.
#define LEADER_ON_THIRD_POOL_FLAG
u16 gCrossboneIslePillarRooms_ShiftSteps4[]
struct SceneEvent gCrossboneIslePillarRoomsEvents4[]
struct SceneEvent gCrossboneIslePillarRoomsEvents2[]
struct ScenePlacement gCrossboneIslePillarRoomsNoPlacements[]
static s32 CrossboneIslePillarRooms_SpawnThawParticle(union FieldObject *fieldObject)
Emits a randomized particle from the thawing pool every eight frames.
static void CrossboneIslePillarRooms_StopAtGroundOrHeight(struct FieldActor *actor, s32 limit)
Waits until the actor reaches the ground or height limit, then stops its descent.
static void Event_FrostBeginsOnThirdPool(void)
Frost begins on the third pool: note whether the leader stands on it.
#define PILLAR_SUNK_FLAG
struct SceneEvent gCrossboneIslePillarRoomsEvents3[]
static void CrossboneIslePillarRooms_SortPillarActors(void)
Sorts the four pillars by height and depth, carrying their saved cell state with them.
static void CrossboneIslePillarRooms_JitterBackgroundScroll(void)
Randomly substitutes the shifted background scroll during the water transition.
static void Event_MoveSinkingPillar(void)
Move on room 5's sinking pillar, and after each push: once it reaches column 16 it sinks two pixels i...
u16 gCrossboneIslePillarRooms_ShiftSteps5[]
static void Event_MovePillar(void)
Move or Carry on one of room 4's pillars, and after each push there: the pillars settle,...
static s32 CrossboneIslePillarRooms_QueryFootprint(s32 id, u32 *width, u32 *depth, struct StagedActorProbe *probe, s32 *layerOriginX, s32 *layerOriginZ)
Finds the actor's footprint dimensions, grid position, and map-layer origin.
static void CrossboneIslePillarRooms_SettleActorOnGround(struct FieldActor *actor)
Waits up to sixty frames for landing, then stops vertical movement on the ground.
struct ScenePlacement gCrossboneIslePillarRoomsPlacements5[]
struct SceneEntrance gCrossboneIslePillarRoomsEntrances2[]
u16 gCrossboneIslePillarRooms_ShiftSteps1[]
struct ScenePlacement gCrossboneIslePillarRoomsPlacements2[]
struct ScenePlacement gCrossboneIslePillarRoomsPlacementsOther[]
static void CrossboneIslePillarRooms_UpdateDampedParticle(union FieldObject *object)
Moves the particle while damping its velocity, growing it, and advancing rotation.
static void Event_PushOrClimbDown(void)
Walking south at room 4's ladder: push the pillar below, jump, or climb down.
struct SceneEvent gCrossboneIslePillarRoomsEvents1[]
static s32 CrossboneIslePillarRooms_TryJumpAcrossPillar(void)
Jumps two cells forward when both the intervening and landing cells permit it.
u16 gCrossboneIslePillarRooms_ShiftSteps3[]
struct SceneEntrance gCrossboneIslePillarRoomsEntrancesOther[]
u32 gCrossboneIslePillarRoomsExits[]
static void Event_PushSinkingPillar(void)
Walking into room 5's sinking pillar: push it if the cell beyond is clear.
struct SceneEvent gCrossboneIslePillarRoomsEvents5[]
static void CrossboneIslePillarRooms_UpdateLeaderPriority(void)
Matches the leader's draw priority to a nearby pillar in front.
s32 gCrossboneIslePillarRooms_ActionTable[]
static s32 sShakeChance
#define FIRST_FROZEN_POOL_FLAG
u16 gCrossboneIslePillarRooms_ShiftSteps2[]
struct ScenePlacement gCrossboneIslePillarRoomsPlacements3[]
static void Event_FreezeThirdPool(void)
Frost ends on the third pool: unless the leader stood on it, it freezes, and a frozen second pool tha...
static s32 CrossboneIslePillarRooms_LowerActorToLedge(s32 id, s32 far)
Finds the next terrain ledge and stops the falling actor at its projected height.
static void CrossboneIslePillarRooms_InitializePillarPriority(s32 id)
Uses terrain layer 2 and installs the moving pillar's priority callback.
The engine calls the debug menus import, and their palette swatch tiles.
DMA channel registers, and the DMA and CPU fills.
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
The edition's language, for C to compare (LANGUAGE == LANGUAGE_GERMAN).
static UNUSED void * OverlayObject_CreateConfiguredObject(s32 x, s32 y, s32 z, s32 type)
A still effect in palette 15, at sprite priority 1, drawn under the actors.
static s32 FieldScheduler_AddCallback(SchedulerCallback callback, s32 priority)
Registers a main-image scheduler callback.
Definition engine_call.h:36
static s32 FieldObject_CheckMovementCollision(struct FieldActor *actor, struct FieldPosition *position)
Definition engine_call.h:66
Scene commands: the all-caps macros the scene scripts are written in.
#define EV_BEGIN()
Definition event.h:30
#define OBJ_POS_SET(no, x, z)
Definition event.h:13
#define EV_END()
Definition event.h:31
#define WAIT(n)
Definition event.h:7
#define OBJ_MODE(no, m)
Definition event.h:18
#define SE(n)
Definition event.h:28
#define FLAG_SET(f)
Definition event.h:26
#define FLAG_CHECK(f)
Definition event.h:25
void BattleFx_StartFadeOverlay(void *object)
Starts the fade overlay on object (the party leader when NULL): loads its solid tiles,...
Field effects, their scripts and the flags that spawn them.
@ EFFECT_USE_PALETTE
options.palette, as its group child value
@ EFFECT_USE_UPDATE
options.update
@ EFFECT_USE_START_SCALE
options.startScaleX and startScaleY
@ EFFECT_USE_ROTATION
options.rotation, as the sprite's
@ EFFECT_SCALE_TO_TARGET
Grows from the start scale, or full size, to the target scale over the script's repeat count.
The scene script layer: the engine services a scene's events call, declared once (Engine_EventBegin,...
void Object_SetMode(struct FieldActor *object, s32 animation)
static void Map_AnimateCells(const u16 *steps, s32 destX, s32 destY)
void Engine_ActorSetSpritePriority(s32 actor, s32 priority)
static void Actor_SetBuiltinAction(s32 actor, s32 action)
void Engine_MessageShowCentered(s32 message, s32 flags)
void Engine_CameraSetSpeed(s32 speed, s32 acceleration)
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_ActorMoveToAndWait(s32 actor, s32 x, s32 z)
#define MAP_CELL_STEP(srcX, srcY, width, height, frames)
Definition field_event.h:59
void Engine_ObjectDispatchRelease(struct FieldActor *object)
static void Map_CopyCellsTo(s32 srcX, s32 srcY, s32 destX, s32 destY, s32 width, s32 height)
#define GameFlag_Clear
static s32 Math_Divide(s32 dividend, s32 divisor)
void Engine_GameFlagSet(s32 flag)
void Engine_GameFlagClear(s32 flag)
void Engine_CameraWaitForMove(void)
static void Actor_SetActionScript(s32 actor, const s32 *script)
The engine encodes either a built-in action or an action-script address.
void Engine_ActorWaitForMove(s32 actor)
static void Map_CopyCellAttributes(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
s32 Engine_RandomNext()
static void Map_CopyCells(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
void Engine_ActorSetChildValue(s32 actor, s32 value)
#define MAP_CELL_STEPS_END
Definition field_event.h:61
s32 Engine_GameFlagIsSet(s32 flag)
@ TASK_PRIORITY_SCENE
Definition field_event.h:40
void Engine_MapRedraw(void)
static void Camera_MoveTo(s32 fixed_x, s32 fixed_y, s32 fixed_z, s32 pan)
static struct FieldActor * Actor_Get(s32 actor)
Field objects and actors, and the scene transition styles.
@ TRANSITION_WINDOW
Definition field_obj.h:17
@ ACTOR_PRIORITY_UNDERFOOT
The depth sort treats it as 320 pixels higher on the screen.
Definition field_obj.h:155
@ ACTOR_PRIORITY_AUTOMATIC
The sprite takes its priority from the map cell under it.
Definition field_obj.h:153
@ ACTOR_FOLLOWS_TERRAIN
moves with the ground height under it
Definition field_obj.h:160
#define ACTOR_NO_TARGET
A target coordinate that is not set: the object has no target.
Definition field_obj.h:145
#define SCENE_TRANSITION(style, variant)
Definition field_obj.h:20
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_EXIT
#define PSYNERGY_PHASE_ON_ACTOR(ability, phase)
A Psynergy event on an actor at one phase of the cast, as PSYNERGY_IN_AREA is for an area.
#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
#define PSYNERGY_ON_ACTOR(ability)
#define EVENT_SCRIPT(script)
#define EVENT_TOUCH_WALKING
@ ACTOR_STAND
#define SEARCH(target)
#define SCENE_EXITS_END
Definition field_scene.h:77
#define TOUCH_WALKING_INTO(facing)
@ FACING_NORTH
Definition field_scene.h:32
@ FACING_SOUTH
Definition field_scene.h:28
#define PIXELS(value)
@ SEARCH_UNNAMED
@ SEARCH_CHEST
#define FIND_COINS(coins)
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)
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_SCENE_TEMP_03
Definition flags.h:716
#define FLAG_SCENE_TEMP_00
Definition flags.h:713
#define FLAG_SCENE_TEMP_02
Definition flags.h:715
#define FLAG_KEEP_PARTY_POSITION
Definition flags.h:93
#define FLAG_SCENE_TEMP_01
Definition flags.h:714
void Runtime_SetIrqHandler(u32 irq, s32 vcount, InterruptHandler handler)
Installs handler for interrupt irq and enables that interrupt, or with a null handler disables it and...
Definition frame.c:37
volatile u32 gFrameCount
Frames WaitFrames has finished.
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[]
void * gEffectWork
Slot 56: points to a scene-specific structure.
The screen blend's globals and the graphics routines.
The heap's work slots and HeapState.
#define WORK_SLOT(n)
Reads slots through their own addresses; adjacent slots are reached by offsets.
Definition heap_state.h:14
The main-image routines scene overlays import.
The hardware's I/O registers and their bits, as pret names them.
#define REG_VCOUNT
Definition io_reg.h:19
#define DMA_32BIT
Definition io_reg.h:210
#define REG_DMA3
Definition io_reg.h:153
#define DMA_ENABLE
Definition io_reg.h:217
The hardware's interrupt sources and the interrupt handler table.
Item numbers, named from the English release.
@ ITEM_LUCKY_PEPPER
Definition item_ids.h:55
@ ITEM_NINJA_GARB
Definition item_ids.h:29
@ ITEM_ELIXIR
Definition item_ids.h:48
@ ITEM_ANTIDOTE
Definition item_ids.h:47
@ ITEM_CLERICS_RING
Definition item_ids.h:87
@ ITEM_WICKED_MACE
Definition item_ids.h:19
@ ITEM_NUT
Definition item_ids.h:41
@ ITEM_SMOKE_BOMB
Definition item_ids.h:67
@ ITEM_MYSTERY_BLADE
Definition item_ids.h:16
@ ITEM_HARD_NUT
Definition item_ids.h:53
@ ITEM_POTION
Definition item_ids.h:43
@ ITEM_LUCKY_MEDAL
Definition item_ids.h:70
@ ITEM_PSY_CRYSTAL
Definition item_ids.h:46
Small statement macros both games use for positions, velocities and bounds.
Maps: their cells, layers, animations and blend scripts, and the map state.
static void Map_DrawCellPanel(s32 x, s32 y, s32 width, s32 height, s32 plane, s32 destX, s32 destY)
Draws the width by height cells at cell (x, y) of the map into screen block 5 from cell (destX,...
Definition map_panel.h:13
The map layers' scroll work and the background scroll values.
struct BgScroll gBgScroll[]
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 reel game's title lettering.
Field object scripts: one s32 command word, then its operands, as gScriptObject_CommandTable runs the...
#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_FOREVER
Definition objcmd.h:14
#define SC_WAIT(n)
Wait n frames.
Definition objcmd.h:16
#define SC_TOP
Script start.
Definition objcmd.h:13
#define SC_CALL(fn)
Definition objcmd.h:50
The fade overlay, the weather particles and the actor effect work.
static s32 StagedActor_FillGridAttributeRectangle(u32 layer, s32 x, s32 z, u32 width, u32 height, s32 value)
Sets the collision code of a width by height rectangle of cells, from cell (x, z),...
#define ACTOR_ACTION_IDLE
The work pointers in the gWorkSlot table, by name.
Scene numbers, named after their places.
char SceneId_CrossboneIslePillarRooms3
char SceneId_CrossboneIslePillarRooms2
char SceneId_CrossboneIslePillarRooms5
char SceneId_CrossboneIslePillarRooms4
char SceneId_CrossboneIslePillarRooms1
The field object script interpreter and the operands its commands read.
Song and sound effect ids, indices into gSound_SongTable.
#define SE_240
Definition songs.h:235
#define SE_159
Definition songs.h:154
#define SE_189
Definition songs.h:184
#define SE_136
Definition songs.h:129
#define SE_232
Definition songs.h:227
#define SE_231
Definition songs.h:226
#define SE_230
Definition songs.h:225
#define SE_JUMP
An actor jumps: ObjectMotion_Launch plays the high jump above 5 pixels a frame.
Definition songs.h:147
#define SE_289
Definition songs.h:282
struct EventWork * gEventWork
void Engine_AudioPlayCue()
void Engine_TaskWait()
#define NULL
Definition bilibin.h:6
The pushable actors' movement probes and footprint bounds.
#define STATIC_ASSERT(condition)
C89 permits repeated extern declarations of the same array.
#define offsetof(type, member)
Definition stddef.h:19
u8 highFlags
bits 15..14, read by Map_GetCellHighFlags
The options of a spawned field effect, each used when its EFFECT_USE_ flag (or EFFECT_SCALE_TO_TARGET...
s32 targetScaleY
16.16 fixed point, 0x10000 full size
s32 targetScaleX
16.16 fixed point, 0x10000 full size
s32 palette
passed to ObjectGroup_SetChildValue
s32 startScaleY
16.16 fixed point, 0x10000 full size
u16 rotation
1/65536 turns
s32 startScaleX
16.16 fixed point, 0x10000 full size
void(*) update(union FieldObject *object)
replaces the update hook
s32 startTransition
SCENE_TRANSITION(style, variant).
Definition event_work.h:43
The fade overlay draws one mark on each side of its tracked actor.
struct FieldActor * actor
the actor tracked; null draws nothing
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
u8 motionFlags
ActorMotionFlag bits; zero is free flight in 3D, and 3 is set on creation.
Definition field_obj.h:83
u8 riseCounter
not read by the engine; scene code's own counter
Definition field_obj.h:105
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
s32 terrainHeight
16.16 fixed-point pixels
Definition field_obj.h:38
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
s32 targetY
16.16 fixed-point pixels; ACTOR_NO_TARGET when unset
Definition field_obj.h:60
s32 scaleX
16.16 fixed point, 0x10000 full size
Definition field_obj.h:39
s32 scaleY
16.16 fixed point, 0x10000 full size
Definition field_obj.h:40
void(*) update(union FieldObject *object)
Called each frame before the object's script, while the object is live.
Definition field_obj.h:116
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
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).
u8 movementMode
1 while the leader is climbing, 0 otherwise
Definition game_state.h:57
One cell of a map layer: its metatile and flags, its collision code and an attribute indexing the col...
Definition map.h:13
u8 attributeB
Indexes the 4-byte collision entries (gMapCollision): terrain kind in the first byte's low four bits,...
Definition map.h:21
u8 collisionCode
The step code: 1 to 239 scene events, 240 and 241 encounter ground, 250 hazard, 252 to 254 for Field_...
Definition map.h:18
struct MapCell * cells
the layer's first cell in the map cell buffer
Definition map_scroll.h:21
s32 offsetY
16.16 fixed-point pixels
Definition map_scroll.h:16
The field map's work: the followed position, screen shake, tile animations, blend script,...
Definition map_scroll.h:27
struct MapLayerScroll layers[3]
BG3, BG2 and BG1, in that order.
Definition map_scroll.h:49
A pillar slot's place and ground cell, without its flag; it holds one slot while two are swapped.
union CrossboneIslePillarCell cell
s32 y
16-pixel map cells
s32 z
16-pixel map cells
s32 x
16-pixel map cells
Where one of room 4's pillars stands, the ground cell saved under it and its footprint flag.
s32 x
16-pixel map cells
s32 z
16-pixel map cells
union CrossboneIslePillarCell cell
s32 y
16-pixel map cells
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
Where a large thing ahead of the leader would roll to: its footprint, its actor, that position,...
struct FieldPosition position
16.16 pixels while moving; converted to cells by footprint redraws.
s32 footprintIndex
The large thing's row in gStagedActor_FootprintKinds and gStagedActor_FootprintBounds,...
The system's routines and globals: interrupts, BIOS sound bias, random numbers and sleep.
s32 GetMapCellCollision(s32 layer, s32 x, s32 y)
Returns the collision code of a layer's cell at (x, y) in 16.16 pixels.
Definition terrain.c:401
s32 Map_GetTerrainHeight(s32 layer, s32 x, s32 y)
Returns the ground height of a layer at (x, y) in 16.16 pixels: the height function of the cell's ter...
Definition terrain.c:343
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
A copied map cell, also read in the two flag pairs the pillar redraw keeps.
struct CrossboneIslePillarCell::CrossboneIslePillarCellBits bits
s32 fixed
16.16 fixed-point pixels
An object on the field, read as an actor or as an effect.
struct FieldActor actor