Alchemy
Loading...
Searching...
No Matches

lamakan_desert.c

Go to the documentation of this file.
1/// @file
2/// Lamakan Desert, four maps of sand between Altin and the world map east.
3///
4/// Walking fills a heat gauge (gGameState.hazardSteps of hazardLimit), drawn as
5/// a bar at the top of the screen; each quarter it fills makes a party member
6/// complain, and a full gauge leaves the leader collapsed at the nearest
7/// recovery point. Reveal uncovers the hidden pits, the shimmering heat haze
8/// hides them again, and a pit drags the leader into a battle.
9
10#include "vram_tab.h"
11#include "unit/import.h"
14#include "unit/object/object.h"
16#include "unit/field/field.h"
18#include "field_obj.h"
19#include "types.h"
20#include "field_event.h"
21#include "field_scene.h"
23#include "field_runtime.h"
24#include "battle.h"
25#include "debug.h"
26#include "field.h"
27#include "game.h"
28#include "graphics.h"
29#include "menu.h"
30#include "minigame.h"
31#include "sound.h"
32#include "system.h"
33#include "text/msg_ids.h"
34#include "scene_ids.h"
35#include "iwram_call.h"
36#include "field_service.h"
37#include "field_effect.h"
38#include "game_state.h"
39#include "callback_scheduler.h"
40#include "resource.h"
41#include "mem.h"
42#include "dma.h"
43#include "io_write_queue.h"
44#include "menu_list.h"
45#include "heap_state.h"
46#include "ram_buffer.h"
47#include "vram_block.h"
48#include "io_reg.h"
49#include "ability_ids.h"
50#include "objcmd.h"
51#include "songs.h"
52#include "flags.h"
53#include "item_ids.h"
54
55static void HeatGauge_SetHeld(u16 held);
56
57enum {
58 /// FLAG_SCENE_TEMP_00: a collapse, a pit or the monster is under way.
60 /// FLAG_SCENE_TEMP_01: Reveal shows the pits.
62 FLAG_HEAT_QUARTER = 0x300, ///< the gauge has passed a quarter
65 /// Who has complained since the gauge last passed a mark: one per member,
66 /// in the order of their actors.
70};
71
72enum {
73 SAND_MONSTER = 8, ///< sprite 0xc3; talking to it starts a battle
74 FIRST_PIT = 9, ///< 9 to 12: the pits Reveal shows (sprite 0xc1)
76 SMOG = 13 ///< the Jupiter Djinni
77};
78
79/// The sand monster's fight and the pits' battles come from encounter zone 53.
80enum { LAMAKAN_ENCOUNTERS = 53 };
81
82#define NULL ((void *)0)
83
85#include "common/effect_spawn.h"
86
87/// Smog, Jupiter Djinni 3, joins the party.
88static void TalkToSmog(void) { Djinn_JoinParty(SMOG, 3, 3); }
89
91
92// The update a leader dragged into a pit runs: sand sounds every 8 frames
93// and a spray of sand every 16.
94// UB: calling this s32 function through a void update hook is incompatible.
95// Plain void changes 104 to 100 bytes in tbs-en.
96#ifdef UBFIX
97static void Leader_SpraySand(union FieldObject *fieldObject) {
98#else
99static s32 Leader_SpraySand(union FieldObject *fieldObject) {
100#endif
101 struct FieldActor *actor = (struct FieldActor *)fieldObject;
102 struct EffectOptions params;
103 s32 phase;
104
105 if ((gFrameCount & 7) == 0) {
107 }
108 phase = gFrameCount & 15;
109 if (phase != 0) {
110#ifdef UBFIX
111 return;
112#else
113 return 0;
114#endif
115 }
116 params.startScaleX = 0xcccc;
117 params.startScaleY = 0xcccc;
118 params.spin = ((u32)(Engine_RandomNext() << 12) >> 16) + 0xf800;
119 Effect_Spawn(actor->x.fixed, actor->y.fixed, actor->z.fixed, 0, phase, phase,
120 0x00880001, &params);
121#ifdef UBFIX
122 return;
123#else
124 return 0;
125#endif
126}
127
128/// Where the party can enter each of the four maps.
130 s16 scene = gGameState.scene;
131
132 if (scene == (s32)&SceneId_LamakanDesert1) {
134 }
135 if (scene == (s32)&SceneId_LamakanDesert2) {
137 }
138 if (scene == (s32)&SceneId_LamakanDesert3) {
140 }
141 if (scene == (s32)&SceneId_LamakanDesert4) {
143 }
145}
146
147const struct SceneRegion *LamakanDesert_GetRegions(void) { return 0; }
148
149/// All four desert maps share the packed exit table.
151
152/// The pits and the monster of each map. Arriving at the third map's
153/// entrance 5, the way back from the monster's battle, marks it beaten.
156 if (gGameState.entrance == 5) {
158#if TBS_PATCH_3
160#endif
161 }
162 }
165 }
168 }
171 }
173}
174
175/// Trigger 9: back out to the world map.
176static void LeaveForTheWorldMap(void) {
177 // Flags 0x8b2 and 0x8b3 are set together the first time; what reads them
178 // is not known.
184 }
185 }
189}
190
191/// The desert's triggers, the same on every map.
194}
195
196/// Reveal's end (phase 4), and every entry: the pits and Smog vanish under
197/// the sand again (cells 70, 68 or 69, 99 over the saved patches), the chests
198/// hide and the heat haze shimmers.
199// Native event return forms: see MATCHING.md.
200#ifdef UBFIX
201static void Reveal_HideSecrets(void) {
202#else
204#endif
205 s32 mapObjectId;
206
209 Map_CopyCells(70, 68, 4, 2, 22, 7);
210 Map_CopyCells(70, 68, 4, 2, 8, 10);
211 Map_CopyCells(70, 68, 4, 2, 23, 21);
212 Map_CopyCellAttributes(70, 68, 4, 1, 23, 23);
213 Map_CopyCells(70, 68, 4, 2, 16, 42);
214 Map_CopyCells(70, 68, 4, 2, 36, 44);
215 Map_CopyCells(70, 68, 4, 2, 14, 55);
216 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert2) {
217 Map_CopyCells(70, 68, 4, 2, 42, 5);
218 Map_CopyCells(70, 68, 4, 2, 20, 11);
219 Map_CopyCellAttributes(70, 68, 4, 1, 20, 13);
220 Map_CopyCells(70, 68, 4, 2, 14, 12);
221 Map_CopyCells(70, 68, 4, 2, 56, 18);
222 Map_CopyCells(70, 68, 4, 2, 7, 22);
223 Map_CopyCellAttributes(70, 68, 4, 1, 7, 24);
224 Map_CopyCells(70, 68, 4, 2, 44, 23);
225 Map_CopyCellAttributes(70, 68, 4, 1, 44, 25);
226 Map_CopyCells(70, 68, 4, 2, 38, 24);
227 Map_CopyCells(70, 68, 4, 2, 26, 28);
228 Map_CopyCells(70, 68, 4, 2, 17, 35);
229 Map_CopyCells(70, 68, 4, 2, 50, 36);
230 Map_CopyCells(70, 68, 4, 2, 34, 43);
231 Map_CopyCellAttributes(70, 68, 4, 1, 34, 45);
232 Map_CopyCells(70, 68, 4, 2, 6, 46);
233 Map_CopyCells(70, 68, 4, 2, 27, 55);
234 Map_CopyCells(70, 68, 4, 2, 43, 56);
235 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert3) {
236 Engine_MapCopyCellsLayered(69, 99, 4, 2, 8, 16);
237 Engine_MapCopyCellsLayered(69, 99, 4, 2, 6, 20);
238 Engine_MapCopyCellsLayered(69, 99, 4, 2, 10, 23);
239 Map_CopyCellAttributes(69, 99, 4, 2, 8, 14);
240 Map_CopyCellAttributes(69, 99, 4, 2, 6, 18);
241 Map_CopyCellAttributes(69, 99, 4, 1, 6, 20);
242 Engine_MapCopyCellAttributes(69, 99, 4, 2, 10, 21);
243 Map_CopyCells(0, 121, 5, 7, 8, 32);
244 Map_CopyCells(0, 121, 5, 7, 43, 32);
245 Map_CopyCells(6, 120, 3, 1, 9, 5);
246 Map_CopyCells(9, 120, 3, 1, 44, 5);
247 Map_CopyCellAttributes(9, 0, 3, 3, 9, 6);
248 }
251 Engine_ActorSetPosition(10, 0, 0);
252 Engine_ActorSetPosition(11, 0, 0);
253 Engine_ActorSetPosition(12, 0, 0);
255 // Hide the searchable map objects independently of the actor placements.
256 mapObjectId = 100;
257 do {
258 Engine_MapObjectSetPosition(mapObjectId, 0, 0);
259 mapObjectId = mapObjectId + 1;
260 } while (mapObjectId <= 107);
262 DisplayScroll_StartHBlankDma(0, 0x40000, 0x10000, 0x2000, 0x10000, 0x8000,
263 0x4000);
264 }
265}
266
267/// Reveal (phase 5): the saved patches of sand come back, the pits and the
268/// monster stand where they are, the chests show and the haze stops.
269#ifdef UBFIX
270static void Reveal_ShowSecrets(void) {
271#else
273#endif
274 s32 i;
275
278 // Restore each hidden patch from the spare cells saved on entry.
280 Map_CopyCells(64, 126, 4, 2, 22, 7);
281 Map_CopyCells(68, 126, 4, 2, 8, 10);
282 Map_CopyCells(72, 126, 4, 2, 23, 21);
283 Map_CopyCellAttributes(72, 126, 4, 2, 23, 22);
284 Map_CopyCells(76, 126, 4, 2, 16, 42);
285 Map_CopyCells(80, 126, 4, 2, 36, 44);
286 Map_CopyCells(84, 126, 4, 2, 14, 55);
287 Actor_SetPosition(9, PIXELS(400), PIXELS(364));
288 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert2) {
289 Map_CopyCells(64, 126, 4, 2, 42, 5);
290 Map_CopyCells(68, 126, 4, 2, 20, 11);
291 Map_CopyCellAttributes(68, 126, 4, 2, 20, 12);
292 Map_CopyCells(72, 126, 4, 2, 14, 12);
293 Map_CopyCells(76, 126, 4, 2, 56, 18);
294 Map_CopyCells(80, 126, 4, 2, 7, 22);
295 Map_CopyCellAttributes(80, 126, 4, 2, 7, 23);
296 Map_CopyCells(84, 126, 4, 2, 44, 23);
297 Map_CopyCellAttributes(84, 126, 4, 2, 44, 24);
298 Map_CopyCells(88, 126, 4, 2, 38, 24);
299 Map_CopyCells(92, 126, 4, 2, 26, 28);
300 Map_CopyCells(96, 126, 4, 2, 17, 35);
301 Map_CopyCells(100, 126, 4, 2, 50, 36);
302 Map_CopyCells(104, 126, 4, 2, 34, 43);
303 Map_CopyCellAttributes(104, 126, 4, 2, 34, 44);
304 Map_CopyCells(108, 126, 4, 2, 6, 46);
305 Map_CopyCells(112, 126, 4, 2, 27, 55);
306 Map_CopyCells(116, 126, 4, 2, 43, 56);
307 Actor_SetPosition(9, PIXELS(352), PIXELS(204));
308 Actor_SetPosition(10, PIXELS(736), PIXELS(396));
309 Actor_SetPosition(11, PIXELS(144), PIXELS(380));
310 Actor_SetPosition(12, PIXELS(576), PIXELS(716));
311 Actor_SetPosition(SMOG, PIXELS(648), PIXELS(408));
312 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert3) {
313 Map_CopyCells(64, 124, 4, 4, 8, 14);
314 Map_CopyCells(68, 124, 4, 4, 6, 18);
315 Map_CopyCellAttributes(68, 124, 4, 1, 6, 20);
316 Map_CopyCells(72, 124, 4, 4, 10, 21);
317 Map_CopyCells(10, 121, 5, 7, 8, 32);
318 Map_CopyCells(5, 121, 5, 7, 43, 32);
319 Map_CopyCells(0, 120, 3, 1, 9, 5);
320 Map_CopyCells(3, 120, 3, 1, 44, 5);
322 Actor_SetPosition(9, PIXELS(128), PIXELS(316));
323 Map_CopyCellAttributes(6, 0, 3, 3, 9, 6);
325 Map_CopyCells(0, 119, 3, 1, 9, 5);
326 }
327 }
328 for (i = 100; i <= 107; i++) {
329 MapObject_SetPosition(i, -1, -1);
330 }
334 }
335}
336
337/// Talking to the sand monster: its battle, returning to the third map's
338/// entrance 5.
339static void FightSandMonster(void) {
344}
345
346/// Raised trigger 1, 2 or 3 as the heat gauge passes a quarter, a half and
347/// three quarters: Garet, Ivan or Mia, picked at random (three tries for
348/// one who has not spoken yet), complains about the heat.
349static void ComplainAboutHeat(s32 mark) {
350 s32 tries;
351 s32 quarter;
352 s32 alreadySpoke;
353 s32 random;
354 s32 flag;
355 s32 member;
356
357 quarter = mark;
358 for (tries = 0; tries <= 2; tries++) {
359 random = Engine_RandomNext();
360 member = (u32)((random << 1) + random) >> 16; // 0 to 2
361 flag = member + FLAG_GARET_COMPLAINED;
362 alreadySpoke = Engine_GameFlagIsSet(flag);
363 if (alreadySpoke == 0) {
364 Engine_GameFlagSet(flag);
365 break;
366 }
367 }
369 // Three lines a mark, one for each member. They follow the Altin Mine
370 // sign three messages after Fuchin Temple's, and have no name of their own.
371 Engine_EventSetMessage((((quarter << 1) + quarter) + member) +
372 MSGNO(MsgFuchinTempleIDoNotThinkMasterHama));
373 Engine_EventShowMessage((member + 1), 0);
375}
376
377/// Every frame: raises triggers 1 to 3 as the gauge passes each quarter,
378/// and clears each mark and the complaints when the gauge drops back below
379/// it. Nothing is raised while Reveal is showing the pits.
381 struct EventWork *work = gEventWork;
383
385 return;
386 if (GameFlag_IsSet(FLAG_HEAT_THREE_QUARTERS) != 0 && percent <= 74) {
391 }
392 if (GameFlag_IsSet(FLAG_HEAT_HALF) != 0 && percent <= 49) {
397 }
398 if (GameFlag_IsSet(FLAG_HEAT_QUARTER) != 0 && percent <= 24) {
403 }
404 if (GameFlag_IsSet(FLAG_HEAT_QUARTER) == 0 && percent > 24) {
406 work->raisedTrigger = 1;
407 }
408 if (GameFlag_IsSet(FLAG_HEAT_HALF) == 0 && percent > 49) {
410 work->raisedTrigger = 2;
411 }
412 if (GameFlag_IsSet(FLAG_HEAT_THREE_QUARTERS) == 0 && percent > 74) {
414 work->raisedTrigger = 3;
415 }
416}
417
418/// The distance in whole pixels between two x, z positions.
419static s32 DistanceInPixels(s32 *firstPosition, s32 *secondPosition) {
420 s32 dx = (*secondPosition++ - *firstPosition++) >> 16;
421 s32 dz = (*secondPosition - firstPosition[1]) >> 16;
422 s32 squaredDeltaZ = dz * dz;
423 s32 squaredDeltaX = dx * dx;
424
425 return Iwram_Sqrt(squaredDeltaX + squaredDeltaZ);
426}
427
428/// A recovery point: where a collapsed leader comes to.
429struct SafePoint {
432};
433
434/// Trigger 20, raised when the heat gauge fills: the leader drops at the
435/// nearest recovery point of the map, lies collapsed while the gauge drains
436/// 5 steps a frame, then gets up.
437static void CollapseFromHeat(void) {
438 u8 *work;
439 s32 frames;
440 s32 best;
441 s32 count;
442 const s32 *points;
443 s32 left;
444 // Ordinary offset with a generic input/output boundary after its
445 // increment, or an input after point calculation, differs 10 in EN
446 // (228 native/built, 223 common). Repeated input operands retain 10.
447 // Generic offset/count outputs after the increment leave 26; a count
448 // output with offset input or the reverse leaves 22 (228 built).
449 // Reusing offset as the later frame countdown leaves 144 (222 built).
450 // Global allocation ranks point (5 refs/12 insns, 0.83), count (13/56,
451 // 0.70) and offset (7/34, 0.41) and hands out r5, r6 and r7 in that
452 // order; the native needs offset between the other two (8 or 9 refs).
453 /* FAKEMATCH: pins the byte offset to r6, where the game keeps it with the
454 * countdown in r7; unpinned, the countdown takes r6 and 22 to 112
455 * instructions differ in tbs-en. */
456 register s32 offset asm("r6");
457 const s32 *point;
458 s32 nearest;
459 s32 distance;
460 struct FieldActor *leader;
461 struct EffectOptions options;
462 struct EffectOptions puff;
463 s16 *progress;
464 u16 *hold;
465 s32 zero;
466
467 work = (u8 *)gEventWork;
468 frames = 60;
469 best = 0xf00000;
473 count = 3;
475 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert2) {
476 count = 5;
478 } else {
479 count = 2;
481 }
482 left = count;
483 // The selected table has 2, 3 or 5 points. The square root fits in 16 bits,
484 // below best, so the first iteration always sets nearest; ties use the last.
485 point = points;
486 if (count != 0) {
487 offset = 0;
488 do {
489 distance = DistanceInPixels(&Actor_Get(ACTOR_PARTY_LEADER)->x.fixed,
490 (s32 *)point);
491 if (distance <= best) {
492 best = distance;
493 nearest = left - count;
494 }
495 offset += 8;
496 point = (const s32 *)((const u8 *)points + offset);
497 } while (--count != 0);
498 }
499 nearest <<= 1;
500 // Move the collapsed leader to safety before draining the heat gauge.
501 Actor_SetSpeed(ACTOR_PARTY_LEADER, 0x20000, 0x10000);
502 {
503 struct FieldActor *actor = Actor_Get(ACTOR_PARTY_LEADER);
504 const struct SafePoint *spot = (const struct SafePoint *)&points[nearest];
505
506 FieldObject_SetPosition(actor, spot->x, 0, spot->z);
507 }
508 Actor_Get(ACTOR_PARTY_LEADER)->velocityY = 0x60000;
512 Actor_Get(ACTOR_PARTY_LEADER)->y.fixed);
514 {
515 struct FieldActor *actor = Actor_Get(ACTOR_PARTY_LEADER);
516
517 options.type = 214;
518 options.startScaleX = 0x8000;
519 options.startScaleY = 0xcccc;
520 options.targetScaleX = 0x10000;
521 options.targetScaleY = 0x13333;
522 Effect_Spawn(actor->x.fixed, actor->y.fixed, actor->z.fixed, 0, 0, 0,
525 &options);
526 }
529 hold = (u16 *)(work + 0xcba);
530 progress = &gGameState.hazardSteps;
531 zero = 0;
532 // Hold the gauge in view until both the minimum delay and cooling finish.
533 do {
534 s32 held = 600;
535
536 *hold = held;
537 frames--;
538 if (*progress != 0) {
539 *progress -= 5;
540 if (*progress <= 0) {
541 *progress = zero;
542 } else if (frames == 0) {
543 frames = 1;
544 }
545 }
546 Task_Wait(1);
547 } while (frames != 0);
548 {
549 struct FieldActor *actor = Actor_Get(ACTOR_PARTY_LEADER);
550
551 puff.type = 214;
552 puff.startScaleX = 0x8000;
553 puff.startScaleY = 0xcccc;
554 puff.targetScaleX = 0x8000;
555 puff.targetScaleY = 0x13333;
556 Effect_Spawn(
557 actor->x.fixed, actor->y.fixed, actor->z.fixed, 0, frames, frames,
559 &puff);
560 }
563 Actor_Get(ACTOR_PARTY_LEADER)->velocityY = 0x60000;
565 Event_Wait(10);
566 *(u16 *)(work + 0xcba) = frames;
568}
569
570/// Walking on sand (triggers 21 and 100 to 107): each moving frame adds a
571/// step of heat and kicks up dust, faster on the run.
572static void HeatGauge_CountStep(void) {
573 struct FieldActor *actor;
574 struct EventWork *event;
575 struct EffectOptions params;
576 s32 dx;
577 s32 z;
578 s32 phase;
580 event = gEventWork;
581 if (actor->targetX == 0x80000000) {
582 return;
583 }
585 if (event->touchedTrigger == 30) {
586 return;
587 }
588 if (actor->speed <= 0x10000) {
589 if ((gFrameCount & 15) != 0) {
590 return;
591 }
592 params.startScaleX = 0x8000;
593 params.startScaleY = 0x8000;
594 dx = 0;
595 params.spin = ((u32)(Engine_RandomNext() << 12) >> 16) + 0xf800;
596 if (actor->facing != 0 && actor->facing != 0x8000) {
597 z = actor->z.fixed;
598 dx = 0x20000 - ((((z >> 20) & 1) * 5) << 16);
599 } else {
600 z = actor->z.fixed;
601 }
602 Effect_Spawn(actor->x.fixed + dx, actor->y.fixed, z, 0, 0, 0, 0x880001,
603 &params);
604 } else {
605 phase = gFrameCount & 7;
606 if (phase != 0) {
607 return;
608 }
609 // Reads the frame counter again and drops the value, as the native does.
611 params.startScaleX = 0xcccc;
612 params.startScaleY = 0xcccc;
613 params.spin = ((u32)(Engine_RandomNext() << 12) >> 16) + 0xf800;
614 Effect_Spawn(actor->x.fixed, actor->y.fixed + 0x20000, actor->z.fixed, 0,
615 ((u32)(Engine_RandomNext() * 5) >> 16) * 0x1999, phase,
616 0x880001, &params);
617 }
618}
619
620/// Trigger 22, stepping near a hidden pit: the leader looks at the nearest
621/// pit, is pulled backwards into it and a battle begins.
622static void FallIntoPit(void) {
623 struct FieldActor *target;
624 struct FieldActor *actor;
625 struct FieldActor *record;
626 s32 id;
627 s32 best;
628 s32 min;
629 s32 dx;
630 s32 dz;
631
633 best = 9;
635 min = 0x100000;
636 // Select the closest live pit using Manhattan distance in whole pixels.
637 for (id = FIRST_PIT; id <= LAST_PIT; id++) {
638 struct FieldActor *other = Actor_Get(id);
639
640 if (other != 0) {
641 dx = (target->x.fixed - other->x.fixed) / 0x10000;
642 dz = (target->z.fixed - other->z.fixed) / 0x10000;
643 {
644 s32 ax = dx;
645
646 if (ax < 0) {
647 ax = -ax;
648 }
649 if (dz < 0) {
650 dz = -dz;
651 }
652 if (ax + dz < min) {
653 best = id;
654 min = ax + dz;
655 }
656 }
657 }
658 }
660 // Face the pit, then reverse the walking direction while being pulled in.
670 actor->facing = (record->facing + 0x8000) & -0x1000;
673 Actor_SetSpeed(ACTOR_PARTY_LEADER, 0x1999, 0xccc);
675#ifdef UBFIX
676 record->update = Leader_SpraySand;
677#else
678 record->update = (void (*)(union FieldObject *))Leader_SpraySand;
679#endif
680 record = Actor_Get(best);
681 if (record != 0) {
683 record->z.part.pixel);
684 }
686 Actor_ShowEmote(best, EMOTE_IN_FRONT | 4, 0);
691 record->update = NULL;
693}
694
695/// Trigger 30, on a slope of sand: the sand carries the leader south 1.5
696/// pixels a frame, and at a ledge the leader hops down 8 pixels.
697static void SlideDownDune(void) {
698 struct FieldActor *center;
699 s32 id;
700 struct FieldActor *actor;
701 struct FieldPosition probePosition;
702 struct EffectOptions params;
703 s32 hit;
704 s32 phase;
705 s32 x;
706
708 {
709 struct EventWork *event = gEventWork;
710
712 center = event->viewCenter;
713 }
714 actor = Object_GetById(id);
715 // First check for a ledge immediately below the sliding actor.
716 probePosition.x = actor->x.fixed;
717 probePosition.y = actor->y.fixed;
718 probePosition.z = actor->z.fixed + 0x18000;
719 hit = FieldObject_CheckMovementCollision(actor, &probePosition);
720 phase = gFrameCount & 4;
721 if (phase == 0) {
722 params.spin = ((u32)(Engine_RandomNext() << 12) >> 16) + 0xf800;
723 x = actor->x.fixed + (((u32)(Engine_RandomNext() * 12) >> 16) << 16) -
724 0x60000;
725 Effect_Spawn(x, actor->y.fixed, actor->z.fixed, 0, phase,
726 ((u32)(Engine_RandomNext() * 5) >> 16) * 0x1999 + 0x7ffd,
727 0x800000, &params);
728 }
729 if (hit < 0) {
730 // Let the fall complete before returning to ordinary field movement.
732 Engine_ObjectSetPosition(actor, actor->x.fixed, actor->y.fixed,
733 actor->z.fixed + 0x80000);
734 Object_SetMode(actor, 7);
736 do {
738 } while (actor->y.fixed != actor->terrainHeight);
739 Object_SetMode(actor, 6);
741 return;
742 }
743 // Probe ahead and on both diagonals before advancing actor and camera.
744 probePosition.x = actor->x.fixed;
745 probePosition.y = actor->y.fixed;
746 probePosition.z = actor->z.fixed + 0x80000;
747 hit = FieldObject_CheckMovementCollision(actor, &probePosition);
748 if (hit > 0) {
749 return;
750 }
751 probePosition.x = actor->x.fixed + 0x5b333;
752 probePosition.y = actor->y.fixed;
753 probePosition.z = actor->z.fixed + 0x5b333;
754 hit = FieldObject_CheckMovementCollision(actor, &probePosition);
755 if (hit > 0) {
756 return;
757 }
758 probePosition.x = actor->x.fixed - 0x5b333;
759 probePosition.y = actor->y.fixed;
760 probePosition.z = actor->z.fixed + 0x5b333;
761 hit = FieldObject_CheckMovementCollision(actor, &probePosition);
762 if (hit > 0) {
763 return;
764 }
765 center->z.fixed += 0x18000;
766 actor->z.fixed += 0x18000;
767}
768
769/// Trigger 40 on the third map, until the monster is beaten: the leader
770/// stops and backs away as the monster rises out of the sand.
771static void MeetSandMonster(void) {
772 s32 paused;
773 struct FieldActor *record;
774 struct FieldPosition backwardOffset;
775 s32 holdFrames;
776 u16 *holdTimer;
777 u8 *eventBytes;
778
779 eventBytes = (u8 *)gEventWork;
783 if (paused == 0) {
785 // Suspend heat accumulation while the leader notices the monster.
787 holdTimer = (u16 *)(eventBytes + 0xcba);
788 holdFrames = 0x258;
789 *holdTimer = holdFrames;
791 record->velocityX = paused;
793 record->velocityZ = paused;
795 record->targetX = -0x80000000;
797 record->targetZ = -0x80000000;
805 backwardOffset.x = paused;
806 // Step backward without turning away from the emerging monster.
807 backwardOffset.y = paused;
808 backwardOffset.z = paused;
810 FieldVector_AddPolarOffset(-0x100000, record->facing, &backwardOffset);
811 Actor_SetSpeed(ACTOR_PARTY_LEADER, 0x20000, 0x10000);
813 Actor_SetDestinationOffset(ACTOR_PARTY_LEADER, backwardOffset.x / 0x10000,
814 backwardOffset.z / 0x10000);
822 Actor_SetSpeed(SAND_MONSTER, 0x28000, 0x14000);
824 Actor_SetSpeed(SAND_MONSTER, 0x8000, 0x4000);
826 *holdTimer = holdFrames;
828 }
829 }
830}
831
832/// Entering the desert: the heat gauge (600 steps) starts on the first three
833/// maps, the patches Reveal uncovers are saved to the spare cells at 64, 126
834/// or 64, 124, and the secrets are hidden.
836 s32 v5;
837
838 gGameState.hazardLimit = 0x258;
840 gGameState.hazardDamage = 0x119;
842 } else {
847 Map_CopyCells(22, 7, 4, 2, 64, 126);
848 Map_CopyCells(8, 10, 4, 2, 68, 126);
849 Map_CopyCells(23, 21, 4, 2, 72, 126);
850 Map_CopyCells(16, 42, 4, 2, 76, 126);
851 Map_CopyCells(36, 44, 4, 2, 80, 126);
852 Map_CopyCells(14, 55, 4, 2, 84, 126);
853 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert2) {
854 Map_CopyCells(42, 5, 4, 2, 64, 126);
855 Map_CopyCells(20, 11, 4, 2, 68, 126);
856 Map_CopyCells(14, 12, 4, 2, 72, 126);
857 Map_CopyCells(56, 18, 4, 2, 76, 126);
858 Map_CopyCells(7, 22, 4, 2, 80, 126);
859 Map_CopyCells(44, 23, 4, 2, 84, 126);
860 Map_CopyCells(38, 24, 4, 2, 88, 126);
861 Map_CopyCells(26, 28, 4, 2, 92, 126);
862 Map_CopyCells(17, 35, 4, 2, 96, 126);
863 Map_CopyCells(50, 36, 4, 2, 100, 126);
864 Map_CopyCells(34, 43, 4, 2, 104, 126);
865 Map_CopyCells(6, 46, 4, 2, 108, 126);
866 Map_CopyCells(27, 55, 4, 2, 112, 126);
867 Map_CopyCells(43, 56, 4, 2, 116, 126);
868 } else if (gGameState.scene == (s32)&SceneId_LamakanDesert3) {
869 v5 = 124;
871 Map_CopyCells(8, 14, 4, 4, 64, v5);
872 Map_CopyCells(6, 18, 4, 4, 68, v5);
873 Map_CopyCells(10, 21, 4, 4, 72, v5);
874 }
876 }
877 return 0;
878}
879
880/// The monster sinks back to its spot; nothing calls this.
881static UNUSED void SandMonster_Return(void) {
882 Actor_SetSpeed(SAND_MONSTER, 0x8000, 0x4000);
886}
887
888/// Every frame: draws the heat gauge. It sits 8 pixels lower while held,
889/// slides away while flag 0x104 is set, its bar shrinks as the heat grows,
890/// and its colors turn redder (flickering greener past 60 of 120).
891static void HeatGauge_Draw(void) {
892 u8 *buffer;
893 s32 tile;
894 s16 *colors;
895 s16 *color;
896 u8 *display;
897 u32 *column;
898 u32 *word;
899 u8 *source;
900 u8 *destination;
901 struct MenuSprite *sprite;
902 s32 counter;
903 u32 i;
904 u32 fill;
905 u32 *palette;
906 u32 red;
907 u32 green;
908 u32 blue;
909 s32 level;
910 u32 attributes;
911 s32 x;
912 u32 offset;
913
915 if ((*(s16 *)&gLamakanDesert_SandPhase) != 0) {
916 gHeatGaugeSlide = 2;
917 } else if (Engine_GameFlagIsSet(0x104) != 0) {
918 if (gHeatGaugeSlide > 0) {
920 }
921 } else if (gHeatGaugeSlide <= 1) {
922 if (++gHeatGaugeSlide == 1) {
923 Dma_Set(gLamakanDesert_SandPalette, (void *)0x050003c0, 0x80000010,
924 REG_DMA3);
925 }
926 }
927 counter = gHeatGaugeSlide;
928 if (counter == 0) {
930 return;
931 }
932 display = *Ram_DisplayWork;
933 if (display != NULL) {
934 s16 *line = (s16 *)(display + display[0x539] * 644);
935 s32 shift = (s16)(counter << 3);
936
937 line = (s16 *)((u8 *)line + 38);
938 for (i = 0; i < 144; i++) {
939 *line = shift;
940 line += 2;
941 }
942 }
943
944 // Tint the six changing palette entries before composing the gauge tiles.
945 buffer = MemAlloc(0x900);
946 Dma_Set(gLamakanDesert_SandPalette, buffer, 0x80000010, REG_DMA3);
947 colors = (s16 *)(buffer + 12);
948 color = colors;
949 for (i = 6; i < 12; i++) {
950 u16 value = *color;
951
952 red = value & 31;
953 green = (value >> 5) & 31;
954 blue = ((value >> 10) & 31) - 20;
955 level = gHeatGaugeLevel;
956 red += level / 3;
957 blue = blue - level / 6 + 20;
958 if (level > 60 && (gFrameCount & 1)) {
959 green = green + (level << 6) / 120 - 32;
960 }
961 if (red > 31) {
962 red = 31;
963 }
964 if (green > 31) {
965 green = 31;
966 }
967 if (blue > 31) {
968 blue = 31;
969 }
970 *color++ = (blue << 10) | (green << 5) | red;
971 }
972 palette = (u32 *)0x050003cc;
973 word = (u32 *)(buffer + 12);
974 IoWriteQueue_Store32((u32)palette++, *word++);
975 IoWriteQueue_Store32((u32)palette++, *word++);
976 IoWriteQueue_Store32((u32)palette++, *word++);
977
979 if (gHeatGaugeLevel > 118) {
980 gHeatGaugeFlash = 119;
981 }
982 if (gHeatGaugeFlash != 0) {
984 gHeatGaugeFlash -= 8;
985 if (gHeatGaugeFlash <= 0) {
986 gHeatGaugeFlash = 0;
987 }
988 }
989
990 // Fill the remaining bar and overlay its nontransparent frame pixels.
991 Dma_Set(gLamakanDesert_SandTileBuffer, buffer, 0x84000240, REG_DMA3);
992 if (gHeatGaugeFlash <= 118) {
993 column = (u32 *)(buffer + 80);
994 fill = 0xeeeeeeee;
995 for (i = 12; i < 128 - gHeatGaugeLevel; i++) {
996 column[8] = fill;
997 *column++ = fill;
998 if ((i & 7) == 7) {
999 column += 8;
1000 }
1001 }
1002 column[0] = ((u32 *)buffer)[0];
1003 column[8] = ((u32 *)buffer)[8];
1004 }
1005 destination = buffer;
1006 source = buffer + 0x480;
1007 for (i = 0; i < 0x480; i++) {
1008 u8 value = *source++;
1009
1010 if (value != 0) {
1011 *destination = value;
1012 }
1013 destination++;
1014 }
1015 Engine_VramLoad((*(s16 *)&gLamakanDesert_SandVramSlot), 0x480, buffer);
1016
1017 // Submit four wide sprites and the narrow cap at the gauge's right end.
1018 attributes = 0x80008000;
1019 sprite = gLamakanDesert_SandWork;
1020 for (i = 0, offset = 0, x = 8; i < 5; i++) {
1021 u32 y = (gHeatGaugeSlide * 8 - 16) & 0x1ff;
1022
1023 if (i == 4) {
1024 attributes = 0x40000000;
1025 }
1026 sprite->next = NULL;
1027 sprite->oam.word.attr01 = (y << 16) | x | attributes;
1028 sprite->oam.word.attr23 = 0xe400 | tile;
1030 sprite++;
1031 tile += 8;
1032 offset += 12;
1033 x += 32;
1034 }
1035 MemFree(buffer);
1036}
1037
1038/// A halfword holding the zero the gauge's slide and phase are cleared with.
1039struct Half {
1041};
1042
1043/// Loads the gauge's tiles, claims VRAM for them and starts drawing it.
1045 s32 slot;
1046 u16 *slotAddress;
1047 struct Half zero;
1048
1051 slotAddress = &gLamakanDesert_SandVramSlot;
1052 slot = Resource_FindFreeEntry();
1053 *slotAddress = slot;
1054 Engine_VramLoad((s16)slot, 0x480, 0);
1055 zero.v = 0;
1056 gHeatGaugeSlide = zero.v;
1059}
1060
1061/// While held (1), the gauge stays on screen.
1062static void HeatGauge_SetHeld(u16 value) { gLamakanDesert_SandPhase = value; }
1063
1065 SC_WAIT(1), SC_LOOP(22, SC_TOP), SC_WAIT(1),
1066 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1067};
1068
1070 SC_WAIT(1), SC_LOOP(44, SC_TOP), SC_WAIT(1),
1071 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1072};
1073
1075 SC_WAIT(1), SC_LOOP(126, SC_TOP), SC_WAIT(1),
1076 SC_POS(0, 0, 0), SC_WAIT(1), SC_FREE,
1077};
1078
1079// clang-format off
1080/// Each map's recovery points, as x, z pairs.
1082 PIXELS(384), PIXELS(128),
1083 PIXELS(288), PIXELS(688),
1084 PIXELS(608), PIXELS(720),
1085};
1086
1088 PIXELS(704), PIXELS(96),
1089 PIXELS(448), PIXELS(464),
1090 PIXELS(832), PIXELS(592),
1091 PIXELS(128), PIXELS(752),
1092 PIXELS(464), PIXELS(896),
1093};
1094
1096 PIXELS(192), PIXELS(224),
1097 PIXELS(192), PIXELS(352),
1098};
1099
1104};
1105
1107 { 0, CONDITION_ALWAYS, 256, 0, 100, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1108 { 1, CONDITION_ALWAYS, 256, 0, 100, FACING_SOUTH, 0, 0, 0, 0, 0, 0 },
1109 { SCENE_TABLE_END },
1110};
1111
1113 { 0, CONDITION_ALWAYS, 256, 0, 100, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1114 { 1, CONDITION_ALWAYS, 984, 0, 952, FACING_WEST, 0, 0, 0, 1016, 1016, 0 },
1115 { 2, CONDITION_ALWAYS, 400, 0, 24, FACING_SOUTH, 0, 0, 0, 1016, 1016, 0 },
1116 { SCENE_TABLE_END },
1117};
1118
1120 { 0, CONDITION_ALWAYS, 256, 0, 100, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1121 { 1, CONDITION_ALWAYS, 912, 0, 1000, FACING_NORTH, 0, 0, 0, 1016, 1016, 0 },
1122 { 2, CONDITION_ALWAYS, 24, 0, 584, FACING_EAST, 0, 0, 0, 1016, 1016, 0 },
1123 { SCENE_TABLE_END },
1124};
1125
1127 { 0, CONDITION_ALWAYS, 256, 0, 100, FACING_SOUTH, 0, -1, -1, -1, -1, 0 },
1128 { 1, CONDITION_ALWAYS, 1511, 0, 104, FACING_WEST, 0, 0, 8, 1528, 504, 0 },
1129 { 2, CONDITION_ALWAYS, 727, 0, 104, FACING_SOUTH, 0, 0, 8, 1528, 504, 0 },
1130 { 3, CONDITION_ALWAYS, 599, 0, 296, FACING_SOUTH, 0, 0, 8, 1528, 504, 0 },
1131 { 4, CONDITION_ALWAYS, 167, 0, 120, FACING_SOUTH, 0, 0, 8, 1528, 504, 0 },
1132 { 5, CONDITION_ALWAYS, 167, 0, 136, FACING_NORTH, 0, 0, 8, 1528, 504, 0 },
1133 { SCENE_TABLE_END },
1134};
1135
1137 { 0, CONDITION_ALWAYS, 424, 0, 824, FACING_WEST, 0, -1, -1, -1, -1, 0 },
1138 { 1, CONDITION_ALWAYS, 424, 0, 968, FACING_NORTH, 0, 304, 560, 624, 1024, 0 },
1139 { 2, CONDITION_ALWAYS, 552, 0, 776, FACING_NORTH, 0, 304, 560, 624, 1024, 0 },
1140 { 3, CONDITION_ALWAYS, 440, 0, 312, FACING_NORTH, 0, 32, 16, 496, 320, 0 },
1141 { 4, CONDITION_ALWAYS, 136, 0, 296, FACING_NORTH, 0, 32, 16, 496, 320, 0 },
1142 { SCENE_TABLE_END },
1143};
1144
1146 SCENE_EXITS(89),
1147 SCENE_EXIT(1, 2, 18),
1148 SCENE_EXIT(2, 90, 1),
1149 SCENE_EXIT(0, 90, 0),
1150 SCENE_EXIT(1, 89, 2),
1151 SCENE_EXIT(2, 91, 1),
1152 SCENE_EXIT(0, 91, 0),
1153 SCENE_EXIT(1, 90, 2),
1154 SCENE_EXIT(2, 92, 2),
1155 SCENE_EXIT(3, 92, 1),
1156 SCENE_EXIT(4, 92, 3),
1157 SCENE_EXIT(0, 92, 0),
1158 SCENE_EXIT(1, 91, 2),
1159 SCENE_EXIT(2, 91, 3),
1160 SCENE_EXIT(3, 2, 53),
1161 SCENE_EXIT(4, 91, 4),
1163};
1164
1166 { SCENE_TABLE_END },
1167};
1168
1171 PIXELS(8), 0, PIXELS(8), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1173 PIXELS(400), 0, PIXELS(364), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1174 { SCENE_TABLE_END },
1175};
1176
1179 PIXELS(8), 0, PIXELS(8), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1181 PIXELS(352), 0, PIXELS(204), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1183 PIXELS(736), 0, PIXELS(396), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1185 PIXELS(144), 0, PIXELS(380), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1187 PIXELS(576), 0, PIXELS(716), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1188 { 0x5d, 0x6f, ACTOR_STAND,
1189 0, 0, 0, FACING_EAST, TALK_FACE_PARTY, 0 },
1190 { SCENE_TABLE_END },
1191};
1192
1195 PIXELS(168), 0, PIXELS(104), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1197 PIXELS(128), 0, PIXELS(316), FACING_SOUTH, TALK_FACE_PARTY, 0 },
1198 { SCENE_TABLE_END },
1199};
1200
1202 { EVENT_EXIT, 1, CONDITION_ALWAYS, 1 },
1203 { EVENT_EXIT, 2, CONDITION_ALWAYS, 2 },
1204 { EVENT_EXIT, 3, CONDITION_ALWAYS, 3 },
1205 { EVENT_EXIT, 4, CONDITION_ALWAYS, 4 },
1206 { EVENT_EXIT, 5, CONDITION_ALWAYS, 1 },
1207 { EVENT_EXIT, 6, CONDITION_ALWAYS, 2 },
1208 { EVENT_EXIT, 7, CONDITION_ALWAYS, 3 },
1209 { EVENT_EXIT, 8, CONDITION_ALWAYS, 4 },
1218 { SEARCH(SEARCH_CHEST), 100, 0xf7b, FIND_ITEM(ITEM_VULCAN_AXE) },
1219 { SEARCH(SEARCH_UNNAMED), 101, 0x350, FIND_PSYNERGY_STONE },
1221 { SEARCH(SEARCH_UNNAMED), 103, 0x351, FIND_PSYNERGY_STONE },
1222 { SEARCH(SEARCH_CHEST), 105, 0xf7d, FIND_COINS(777) },
1223 { SEARCH(SEARCH_CHEST), 106, 0xf7e, FIND_ITEM(ITEM_POTION) },
1224 { SEARCH(SEARCH_CHEST), 107, 0xf7f, FIND_ITEM(ITEM_MINT) },
1238 { SCENE_EVENTS_END },
1239};
1240// clang-format on
1241
1242/// The gauge: how full it is (0 to 120), how far it has slid onto the
1243/// screen, and a flash as it fills.
1245
1247
Ability numbers, named from the English release.
@ ABILITY_REVEAL
Definition ability_ids.h:19
void Object_SetActionById(u32 objectId, s32 steps)
Sets the animation timer steps of each of an actor's sprites (AnimationObjects_SetActiveTimerSteps).
struct FieldActor * Object_GetById(s32 actor)
Returns an actor, or NULL if it does not exist.
s32 gEffectScript0[]
const s32 * gEffectScripts[]
s32 gEffectScript1[]
s32 gEffectScript2[]
The task scheduler: tasks, their order and the scheduler's state.
The engine calls the debug menus import, and their palette swatch tiles.
void Djinn_JoinParty(s32 slot, s32 element, s32 djinn)
A Djinni joins the party: djinn (0 to 19) of element goes to the member with the fewest Djinn,...
Definition djinni.c:124
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
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 void DisplayScroll_StartHBlankDma(s32 mode, s32 freqX, s32 stepX, s32 ampX, s32 freqY, s32 stepY, s32 ampY)
Definition engine_call.h:95
u8 gLamakanDesert_SandEffectTiles[]
u16 gLamakanDesert_SandPhase
const u16 gLamakanDesert_SandPalette[16]
u16 gLamakanDesert_SandVramSlot
struct MenuSprite gLamakanDesert_SandWork[]
u8 gLamakanDesert_SandTileBuffer[]
Field effects, their scripts and the flags that spawn them.
@ EFFECT_USE_START_SCALE
options.startScaleX and startScaleY
@ EFFECT_SCALE_TO_TARGET
Grows from the start scale, or full size, to the target scale over the script's repeat count.
@ EFFECT_USE_TYPE
options.type, else object type 222
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)
@ ANIM_WALK
Definition field_event.h:44
@ ANIM_STAND
Definition field_event.h:43
void Engine_ActorFaceActor(s32 actor, s32 target, s32 frames)
void Engine_MapCopyCellAttributes(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
void Object_SetMode(struct FieldActor *object, s32 animation)
#define GameFlag_IsSet
static void Actor_WalkToAndWait(s32 actor, s32 x, s32 z)
#define EMOTE_IN_FRONT
Definition field_event.h:49
static void Actor_SetDestinationOffset(s32 actor, s32 dx, s32 dz)
void Engine_ActorSetDestination(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)
#define GameFlag_Clear
#define GameFlag_Set
static void Actor_ShowEmote(s32 actor, s32 emote, s32 frames)
void Engine_MapCopyCellsLayered(s32 srcX, s32 srcY, s32 destX, s32 destY, s32 width, s32 height)
void Engine_GameFlagSet(s32 flag)
void Engine_GameFlagClear(s32 flag)
void Engine_ActorWaitForMove(s32 actor)
static void Map_CopyCellAttributes(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
void Engine_ActorRunRepeatedMotion(s32 actor, s32 repeats)
s32 Engine_RandomNext()
static void Event_PlayCue(s32 cue)
static void Map_CopyCells(s32 srcX, s32 srcY, s32 width, s32 height, s32 destX, s32 destY)
void Engine_MapObjectSetPosition(s32 object, s32 fixed_x, s32 fixed_z)
s32 Engine_GameFlagIsSet(s32 flag)
void Engine_ActorStartRepeatedMotion(s32 actor, s32 repeats)
static void MapObject_SetPosition(s32 object, s32 fixed_x, s32 fixed_z)
static void Actor_SetSpeed(s32 actor, s32 speed, s32 acceleration)
static void Actor_SetPosition(s32 actor, s32 fixed_x, s32 fixed_z)
void Engine_EventWait(s32 frames)
static void Actor_SetAttachedEffect(s32 actor, s32 effect)
static struct FieldActor * Actor_Get(s32 actor)
void Marker_MoveLeaderAway(void)
Keeps the party leader off the markers: a leader within 16 pixels of a marker cell's centre is moved ...
Definition field_fx.c:43
Field objects and actors, and the scene transition styles.
@ TRANSITION_BACKDROP_FADE
Definition field_obj.h:16
#define SCENE_TRANSITION(style, variant)
Definition field_obj.h:20
@ ACTOR_FACES_MOTION
turns toward its velocity, 1/16 turn a frame
Definition field_obj.h:166
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
@ EVENT_TALK
@ EVENT_RAISED
#define SCENE_EXITS(scene)
Definition field_scene.h:72
#define PSYNERGY_IN_AREA(ability, phase)
#define FIND_PSYNERGY_STONE
#define SCENE_EXIT(exit, scene, entrance)
Definition field_scene.h:73
@ ACTOR_PARTY_LEADER
@ SCENE_TABLE_END
Definition field_scene.h:43
@ CONDITION_ALWAYS
Definition field_scene.h:44
@ TALK_FACE_PARTY
#define EVENT_SCRIPT(script)
@ ACTOR_STAND
#define SEARCH(target)
#define SCENE_EXITS_END
Definition field_scene.h:77
@ FACING_EAST
Definition field_scene.h:26
@ FACING_NORTH
Definition field_scene.h:32
@ FACING_SOUTH
Definition field_scene.h:28
@ FACING_WEST
Definition field_scene.h:30
#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 Task_Wait(s32 frames)
static void FieldVector_AddPolarOffset(s32 radius, s32 heading, struct FieldPosition *probe)
static void Actor_SetAnimation(s32 actor, s32 animation)
static void Event_Wait(s32 frames)
static void FieldObject_SetPosition(struct FieldActor *object, s32 fixed_x, s32 fixed_y, s32 fixed_z)
FixedPointPosition, a position in 16.16 fixed point.
Game flags, named where their meaning is shown.
#define FLAG_REACHED_KALAY
Set whenever Kalay is entered; Vale's villagers then talk of the party's journey.
Definition flags.h:441
#define FLAG_SCENE_TEMP_00
Definition flags.h:713
#define FLAG_HSU_RESCUED_WITHOUT_PARTY
Hsu was freed without the party: set with FLAG_HSU_RESCUED when the party comes back from Lama Temple...
Definition flags.h:139
#define FLAG_LAMAKAN_SAND_MONSTER_BEATEN
on the third map
Definition flags.h:268
#define FLAG_SCENE_TEMP_01
Definition flags.h:714
#define FLAG_HSU_RESCUED
Definition flags.h:114
volatile u32 gFrameCount
Frames WaitFrames has finished.
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 screen blend's globals and the graphics routines.
void * MemAlloc(s32 size)
Allocates size bytes (rounded up to words) with no slot, from IWRAM when they fit and EWRAM otherwise...
Definition heap.c:115
The heap's work slots and HeapState.
The main-image routines scene overlays import.
void IoWriteQueue_Store32(u32 address, u32 value)
Queues a word store.
Definition io_queue.c:23
The hardware's I/O registers and their bits, as pret names them.
#define REG_DMA3
Definition io_reg.h:153
The register write queue that VBlank carries out after each finished frame.
Item numbers, named from the English release.
@ ITEM_LUCKY_PEPPER
Definition item_ids.h:55
@ ITEM_MINT
Definition item_ids.h:54
@ ITEM_POTION
Definition item_ids.h:43
@ ITEM_VULCAN_AXE
Definition item_ids.h:17
The IWRAM call layer: the routines the game keeps resident in IWRAM, each at its fixed address,...
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
static s32 Reveal_ShowSecrets(void)
Reveal (phase 5): the saved patches of sand come back, the pits and the monster stand where they are,...
static void TalkToSmog(void)
Smog, Jupiter Djinni 3, joins the party.
static void FallIntoPit(void)
Trigger 22, stepping near a hidden pit: the leader looks at the nearest pit, is pulled backwards into...
const u32 * LamakanDesert_GetExits(void)
All four desert maps share the packed exit table.
const s32 gLamakanDesertRecoveryPoints2[]
static void ComplainAboutHeat(s32 mark)
Raised trigger 1, 2 or 3 as the heat gauge passes a quarter, a half and three quarters: Garet,...
s16 gHeatGaugeSlide
static void FightSandMonster(void)
Talking to the sand monster: its battle, returning to the third map's entrance 5.
static UNUSED void SandMonster_Return(void)
The monster sinks back to its spot; nothing calls this.
@ FLAG_HEAT_HALF
@ FLAG_HAZARDS_PAUSED
FLAG_SCENE_TEMP_00: a collapse, a pit or the monster is under way.
@ FLAG_IVAN_COMPLAINED
@ FLAG_HEAT_THREE_QUARTERS
@ FLAG_HEAT_QUARTER
the gauge has passed a quarter
@ FLAG_MIA_COMPLAINED
@ FLAG_GARET_COMPLAINED
Who has complained since the gauge last passed a mark: one per member, in the order of their actors.
@ FLAG_SECRETS_REVEALED
FLAG_SCENE_TEMP_01: Reveal shows the pits.
static void HeatGauge_CountStep(void)
Walking on sand (triggers 21 and 100 to 107): each moving frame adds a step of heat and kicks up dust...
static void CollapseFromHeat(void)
Trigger 20, raised when the heat gauge fills: the leader drops at the nearest recovery point of the m...
static s32 DistanceInPixels(s32 *firstPosition, s32 *secondPosition)
The distance in whole pixels between two x, z positions.
const struct SceneRegion * LamakanDesert_GetRegions(void)
@ LAMAKAN_ENCOUNTERS
struct SceneEntrance gLamakanDesertEntrancesOther[]
struct ScenePlacement gLamakanDesertPlacements3[]
static void HeatGauge_Draw(void)
Every frame: draws the heat gauge.
const struct SceneEvent * LamakanDesert_GetEvents(void)
The desert's triggers, the same on every map.
struct SceneEntrance gLamakanDesertEntrances3[]
static void HeatGauge_SetHeld(u16 held)
While held (1), the gauge stays on screen.
const struct SceneEntrance * LamakanDesert_GetEntrances(void)
Where the party can enter each of the four maps.
static void SlideDownDune(void)
Trigger 30, on a slope of sand: the sand carries the leader south 1.5 pixels a frame,...
s32 LamakanDesert_Initialize(s32 a0, s32 a1)
Entering the desert: the heat gauge (600 steps) starts on the first three maps, the patches Reveal un...
const s32 gLamakanDesertRecoveryPoints1[]
Each map's recovery points, as x, z pairs.
s16 gHeatGaugeLevel
The gauge: how full it is (0 to 120), how far it has slid onto the screen, and a flash as it fills.
u32 gLamakanDesertExits[]
struct SceneEntrance gLamakanDesertEntrances1[]
const struct ScenePlacement * LamakanDesert_GetPlacements(void)
The pits and the monster of each map.
struct ScenePlacement gLamakanDesertPlacements1[]
const s32 gLamakanDesertRecoveryPointsOther[]
struct SceneEvent gLamakanDesertEvents[]
struct SceneEntrance gLamakanDesertEntrances2[]
void HeatGauge_Start(void)
Loads the gauge's tiles, claims VRAM for them and starts drawing it.
static void HeatGauge_RaiseQuarterTriggers(void)
Every frame: raises triggers 1 to 3 as the gauge passes each quarter, and clears each mark and the co...
struct SceneEntrance gLamakanDesertEntrances4[]
static s32 Reveal_HideSecrets(void)
Reveal's end (phase 4), and every entry: the pits and Smog vanish under the sand again (cells 70,...
s16 gHeatGaugeFlash
static void MeetSandMonster(void)
Trigger 40 on the third map, until the monster is beaten: the leader stops and backs away as the mons...
@ SMOG
the Jupiter Djinni
@ SAND_MONSTER
sprite 0xc3; talking to it starts a battle
@ LAST_PIT
@ FIRST_PIT
9 to 12: the pits Reveal shows (sprite 0xc1)
struct ScenePlacement gLamakanDesertPlacementsOther[]
static s32 Leader_SpraySand(union FieldObject *fieldObject)
static void LeaveForTheWorldMap(void)
Trigger 9: back out to the world map.
struct ScenePlacement gLamakanDesertPlacements2[]
The heaps: allocating and freeing blocks.
void MemFree(void *buffer)
Menu lists: sprites, cursors, the party status window and paged selections.
The reel game's title lettering.
Field object scripts: one s32 command word, then its operands, as gScriptObject_CommandTable runs the...
#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_WAIT(n)
Wait n frames.
Definition objcmd.h:16
#define SC_TOP
Script start.
Definition objcmd.h:13
void FieldObject_WaitForMove(void *fieldObject)
Waits a frame at a time until a field object has arrived at its target, at most 600 frames.
static void SceneActor_WaitObjectBelowHeight(struct FieldActor *object, s32 height)
Waits until the object has come down to height (16.16), a second at most.
The work pointers in the gWorkSlot table, by name.
#define Ram_DisplayWork
Definition ram_buffer.h:51
void Runtime_PushSlotEntry(void *slotEntry, s32 slot)
Links a sprite entry into depth slot slot (clamped to 0..255) for this frame.
Definition render.c:79
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_SetNextDestination(s32 scene, s32 entrance)
Sets the scene and entrance that the field goes to next (gGameState.nextScene and nextEntrance).
Definition requests.c:61
void BattleFx_SetQueuedSoundAndPlay(s32 soundId)
Plays SE_298, then soundId (SE_289 for -1), and remembers soundId for BattleFx_PlayQueuedSound in the...
Definition requests.c:90
Scene numbers, named after their places.
char SceneId_LamakanDesert4
char SceneId_LamakanDesert1
char SceneId_LamakanDesert2
char SceneId_LamakanDesert3
Song and sound effect ids, indices into gSound_SongTable.
#define SE_118
Definition songs.h:111
#define SE_MAP_EXIT
Definition songs.h:116
#define SE_288
Definition songs.h:281
#define SE_148
Definition songs.h:141
#define SE_241
Definition songs.h:236
#define SE_JUMP
An actor jumps: ObjectMotion_Launch plays the high jump above 5 pixels a frame.
Definition songs.h:147
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_TaskWait()
void Engine_EventSetMessage()
void Engine_EventShowMessage()
#define NULL
Definition bilibin.h:6
The options of a spawned field effect, each used when its EFFECT_USE_ flag (or EFFECT_SCALE_TO_TARGET...
s16 type
the object type to create; 222 without the flag
s32 targetScaleY
16.16 fixed point, 0x10000 full size
s32 targetScaleX
16.16 fixed point, 0x10000 full size
s32 startScaleY
16.16 fixed point, 0x10000 full size
u16 spin
1/65536 turns per frame
s32 startScaleX
16.16 fixed point, 0x10000 full size
The running scene's event work: its placement and event tables, its actors, the triggers and exit it ...
Definition event_work.h:20
s16 raisedTrigger
a raised event for Scene_RunRaisedEvent
Definition event_work.h:35
s32 startTransition
SCENE_TRANSITION(style, variant).
Definition event_work.h:43
s16 touchedTrigger
the step event a touch or exit event answers
Definition event_work.h:26
A field actor: a person or thing on the field with a position, motion toward a target,...
Definition field_obj.h:30
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
u8 steeringFlags
ActorSteeringFlag bits.
Definition field_obj.h:92
union FieldCoordinate x
16.16 fixed-point pixels
Definition field_obj.h:35
s32 targetZ
16.16 fixed-point pixels; ACTOR_NO_TARGET when unset
Definition field_obj.h:61
s32 velocityX
16.16 fixed-point pixels per frame
Definition field_obj.h:52
union FieldCoordinate y
16.16 fixed-point pixels
Definition field_obj.h:36
s32 terrainHeight
16.16 fixed-point pixels
Definition field_obj.h:38
union FieldCoordinate z
16.16 fixed-point pixels
Definition field_obj.h:37
u16 facing
1/65536 turns
Definition field_obj.h:34
void(*) update(union FieldObject *object)
Called each frame before the object's script, while the object is live.
Definition field_obj.h:116
u16 work
The side of the player's next footprint (2 after a stop), or scene scratch.
Definition field_obj.h:112
s32 velocityZ
16.16 fixed-point pixels per frame
Definition field_obj.h:54
A position on the field.
s32 z
16.16 fixed-point pixels
s16 hazardLimit
Hazardous ground (the Lamakan Desert's heat): every hazardLimit steps the party takes hazardDamage (b...
Definition game_state.h:96
s16 hazardSteps
reset on entering a new scene group
Definition game_state.h:99
s16 hazardDamage
Definition game_state.h:98
s16 entrance
Definition game_state.h:37
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
s16 hazardMode
0 counts on hazard cells only; 1 counts every step
Definition game_state.h:97
A halfword holding the zero the gauge's slide and phase are cleared with.
A menu sprite: a list link and its OAM attributes, as two words or as fields.
Definition menu_list.h:24
struct MenuSprite * next
Definition menu_list.h:25
union MenuSprite::@002225022013150231017316170057321303227123371224 oam
A recovery point: where a collapsed leader comes to.
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...
u16 offset
bytes from the start of OBJ VRAM
Definition vram_tab.h:25
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.
Declares nothing of its own: it only includes the window, battle command, resource and VRAM table hea...
s32 VramCache_ReleaseVram(u32 no)
Gives back entry no's blocks but keeps the entry taken, with size 1, so a later VramCache_Load can re...
Definition vram_cache.c:122
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.
struct VramBlockCacheEntry gVramBlockCache[]
void DisplayScroll_DisarmHBlankDma(void)
Stops the wavy background: removes its two callbacks and stops DMA 0.