Alchemy
Loading...
Searching...
No Matches

mercury_lighthouse_pillar.h

Go to the documentation of this file.
1#include "script.h"
2#include "battle.h"
3#include "debug.h"
4#include "field.h"
5#include "game.h"
6#include "graphics.h"
7#include "menu.h"
8#include "minigame.h"
9#include "sound.h"
10#include "system.h"
11#include "types.h"
12#include "mercury.h"
13#include "iwram_call.h"
14#include "field_event.h"
15#include "engine_call.h"
16#include "field_effect.h"
17#include "ram_buffer.h"
18#include "map.h"
19#include "field_runtime.h"
20#include "heap_state.h"
21#include "keystate.h"
22#include "fixed_math.h"
23#include "songs.h"
24
25// Mercury Lighthouse and its entrance, once the pool is woken: standing at the
26// pool's heart (touch trigger 11) gives the leader three lights, shown as a
27// column of light over them. Each hop into the pool (the floor's region, its
28// collision code) spends one, and a light comes to hold the leader up where
29// they land. A hop within the pool with no light left sinks the leader, who is
30// put back at the heart. Touch trigger 31 takes the lights away, and Retreat
31// removes the column.
32
33/// Raises the spark while its scale follows a half-sine pulse.
34static s32
36 struct FieldActor *effect = (struct FieldActor *)interpreter;
37 struct FieldActor *anchor = effect->linkedObject;
38 s16 step = ++effect->timer;
39 s32 scale;
40
41 if (step > 31) {
42 return 0;
43 }
44
45 scale = Engine_MathSin(step << 10);
46 effect->scaleX = scale;
47 effect->scaleY = scale;
48 effect->x.fixed = anchor->x.fixed;
49 effect->y.fixed += PIXELS(1);
50 effect->z.fixed = anchor->z.fixed;
51 return 1;
52}
53
54/// Grows the moving light over the first sixteen script frames.
56 struct ScriptInterpreter *interpreter) {
57 struct FieldActor *actor = (struct FieldActor *)interpreter;
58 s16 frame;
59
60 frame = ++actor->timer;
61 if (frame > 16) {
62 return 0;
63 }
64 actor->scaleX = (frame * 3) << 10;
65 actor->scaleY = (frame * 3) << 10;
66 return 1;
67}
68
69/// Expands the released light before its script removes it.
70static s32
72 struct FieldActor *actor = (struct FieldActor *)interpreter;
73 s16 frame;
74
75 frame = ++actor->timer;
76 if (frame > 16) {
77 return 0;
78 }
79 actor->scaleX = (frame << 11) + Q16_ONE;
80 actor->scaleY = (frame << 11) + Q16_ONE;
81 return 1;
82}
83
84/// Follows the linked actor and alternates the light's animation parameter.
85static s32
87 struct FieldActor *actor = (struct FieldActor *)interpreter;
88 struct FieldActor *leader;
91 s32 deltaX;
92 s32 deltaZ;
93 s32 distance;
94 s32 moveDistance;
95
96 actor->speed = PIXELS(2);
97 leader = actor->linkedObject;
98 actor->acceleration = PIXELS(1);
99 targetX = leader->x.fixed;
100 targetZ = leader->z.fixed;
101 actor->targetX = ACTOR_NO_TARGET;
102 actor->targetY = ACTOR_NO_TARGET;
103 actor->targetZ = ACTOR_NO_TARGET;
104
105 // Measure in whole pixels first; use fixed-point precision near the leader.
106 deltaX = (targetX - actor->x.fixed) / PIXELS(1);
107 deltaZ = (targetZ - actor->z.fixed) / PIXELS(1);
108 distance = (u16)Iwram_Sqrt(deltaX * deltaX + deltaZ * deltaZ);
109 deltaX = targetX - actor->x.fixed;
110 deltaZ = targetZ - actor->z.fixed;
111 distance <<= 16;
112 if (distance < PIXELS(64))
113 distance =
114 FixedSqrt(Iwram_MulQ16(deltaX, deltaX) + Iwram_MulQ16(deltaZ, deltaZ));
115 moveDistance = distance / 8;
116 if (moveDistance > actor->speed)
117 moveDistance = actor->speed;
118
119 // Snap within a quarter pixel; otherwise ease toward the leader.
120 if (distance < PIXELS(1) / 4) {
121 actor->x.fixed = targetX;
122 actor->z.fixed = targetZ;
123 } else {
124 if (distance > moveDistance) {
125 deltaX = Iwram_MulQ16(Iwram_RatioMulQ14(distance, deltaX), moveDistance);
126 deltaZ = Iwram_MulQ16(Iwram_RatioMulQ14(distance, deltaZ), moveDistance);
127 }
128 actor->x.fixed += deltaX;
129 actor->z.fixed += deltaZ;
130 }
131
132 // Alternate the light's draw parameter every two frames and redraw it.
133 // The sprite slot is read by its offset, the plain-C form of actor->sprite
134 // this site matches with (scene/README.md): through FieldActor's
135 // single/group union the movs r0, #1 and the sprite load swap.
136 {
137 struct AnimationObject *sprite =
138 *(struct AnimationObject **)((u8 *)actor +
139 offsetof(struct FieldActor, sprite));
140 struct AnimationEntry *entry = sprite->entries[0];
141
142 entry->param = ((gFrameCount >> 1) & 1) * 7;
143 sprite->dirty = 1;
144 }
145 return 1;
146}
147
148/// Places the two spark animations on the selected actor.
150 struct FieldActor *point;
151 struct FieldActor *spark;
152 struct AnimationObject *sprite;
153
155 spark =
156 Engine_ObjectCreate(26, point->x.fixed, point->y.fixed, point->z.fixed);
157 if (spark != NULL) {
158 spark->terrainHeight = point->terrainHeight;
159 sprite = spark->sprite;
161 spark->motionFlags = 0;
162 spark->timer = 0;
163 spark->linkedObject = point;
164 if (sprite != NULL) {
166 sprite->flags = 0;
167 sprite->image.priority = 1;
168 }
169 }
170 spark =
171 Engine_ObjectCreate(26, point->x.fixed, point->y.fixed, point->z.fixed);
172 if (spark != NULL) {
173 spark->terrainHeight = point->terrainHeight;
174 sprite = spark->sprite;
176 spark->motionFlags = 0;
177 spark->timer = 0;
178 spark->linkedObject = point;
179 spark->priorityFlags = 2;
180 if (sprite != NULL) {
182 sprite->flags = 0;
183 }
184 }
186}
187
188/// Touch trigger 11, the pool's heart: the column of light appears over the
189/// leader if it is not there, and grows a stage with each pair of sparks, half
190/// a second apart, up to three lights. The column's cell is where a sinking
191/// leader comes back to; flag 0x161 is set while the leader has lights.
193 struct MercuryLighthouseLights *state;
194 struct FieldActor *leader;
195 struct FieldActor *pillar;
196 struct FieldActor *obj;
197 s32 step;
198
199 state = *(struct MercuryLighthouseLights **)WORK_SLOT(35);
200 leader = ((struct FieldWork *)WORK_SLOT(HEAP_SLOT_EVENT))
202 if ((u32)state->levels > 2)
203 return;
205 pillar = state->pillar;
206 if (pillar == 0) {
207 obj = Engine_ObjectCreate(26, leader->x.fixed, leader->y.fixed + 0x180000,
208 leader->z.fixed);
209 if (obj != 0) {
210 struct AnimationObject *sprite = obj->sprite;
211
212 obj->terrainHeight = leader->terrainHeight;
214 obj->linkedObject = leader;
215 obj->motionFlags = 4;
216 // The height is written by its offset, the plain-C form of obj->y.fixed
217 // this site matches with (scene/README.md): as the field, the sprite's
218 // reload moves after the add and store.
219 *(s32 *)((u8 *)obj + offsetof(struct FieldActor, y)) += -PIXELS(1) / 2;
220 if (sprite != 0) {
221 sprite->flags = 0;
222 sprite->image.priority = 1;
223 }
224 obj->spriteStorageKind = 0;
225 state->pillar = obj;
226 pillar = obj;
227 } else {
228 pillar = state->pillar;
229 }
230 }
231 for (step = state->levels; step <= 2; step++) {
233 Engine_TaskWait(30);
234 pillar->spriteStorageKind = 1;
235 Object_SetMode(pillar, 5 - step);
236 }
237 state->levels = 3;
238 state->targetX = (pillar->x.fixed & -0x100000) + 0x80000;
239 state->targetZ = (pillar->z.fixed & -0x100000) + 0x80000;
240 Engine_GameFlagSet(0x161);
242}
243
244#define MAP_CELLS ((struct MapCell *)Ram_MapCellBuffer)
245
246/// Touch trigger 31: the leader loses their lights, and the column swells and
247/// fades away.
249 struct MercuryLighthouseLights *state =
250 *(struct MercuryLighthouseLights **)WORK_SLOT(35);
251 struct FieldActor *target;
252
253 if (state->levels == 0) {
254 return;
255 }
256
257 state->levels = 0;
259
260 target = state->pillar;
261 if (target != 0) {
262 target->timer = 0;
264 Object_SetMode(target, 7);
265 state->pillar = NULL;
266 }
267}
268
269/// Pushing against the pool's edge (triggers 11, 21 and 31): the leader hops
270/// two cells the way the pad points, unless the hop leads into the pool without
271/// a light or is blocked. A hop into the pool spends a light, which flies ahead
272/// to the landing cell; the column shrinks a stage, or goes with the last
273/// light. Landing in the pool with no light under them, the leader sinks until
274/// a key is pressed after half a second, and is put back at the heart.
276 struct FieldWork *work;
277 struct MercuryLighthouseLights *state;
278 struct FieldActor *actor;
279 struct FieldActor *source;
280 struct FieldActor *obj;
281 struct AnimationObject *sprite;
282 struct MapCell *from;
283 struct MapCell *to;
284 u8 saved;
285 u8 *flags;
286 s32 angle;
287 u32 keys;
288 s32 n;
289 s32 i;
290 u8 zero;
291 union FieldCoordinate pos[3];
292
293 state = *(struct MercuryLighthouseLights **)WORK_SLOT(35);
295 actor = work->actors[gGameState.selectedActor];
296 flags = &actor->motionFlags;
297 saved = *flags;
298 keys = gKeysHeld;
299 angle = gMercuryLighthouse_MoveAngles[(keys >> 4) & 15];
300 if ((s16)gMercuryLighthouse_MoveAngles[(keys >> 4) & 15] == -1)
301 return;
302 pos[0].fixed = (actor->x.fixed & -0x100000) + 0x80000;
303 pos[1].fixed = actor->terrainHeight;
304 pos[2].fixed = (actor->z.fixed & -0x100000) + 0x80000;
305 from = &MAP_CELLS[(pos[2].fixed / 0x100000 << 7) + pos[0].fixed / 0x100000];
306 Vector_AddPolarOffset(0x200000, angle, &pos[0].fixed);
307 to = &MAP_CELLS[(pos[2].fixed / 0x100000 << 7) + pos[0].fixed / 0x100000];
308 if (from->collisionCode != state->terrainCode &&
309 to->collisionCode == state->terrainCode && state->levels == 0)
310 return;
311
313 if (FieldObject_CheckMovementCollision(actor, (struct FieldPosition *)pos) !=
314 0)
315 return;
316 obj = state->stepEffect;
317 if (obj != NULL) {
318 obj->timer = 0;
320 Object_SetMode(obj, 7);
321 state->stepEffect = NULL;
322 }
323 if (to->collisionCode == state->terrainCode && state->levels != 0) {
324 source = state->pillar;
325 obj = Engine_ObjectCreate(26, source->x.fixed, source->y.fixed,
326 source->z.fixed);
327 if (obj != NULL) {
328 sprite = obj->sprite;
329 obj->terrainHeight = source->terrainHeight;
331 obj->motionFlags = 0;
332 obj->timer = 0;
333 obj->priorityFlags = 2;
334 obj->speed = 0x40000;
335 obj->acceleration = 0x20000;
336 Engine_ObjectSetPosition(obj, pos[0].fixed, pos[1].fixed, pos[2].fixed);
337 if (sprite != NULL) {
339 zero = 0;
340 sprite->flags = zero;
341 }
342 state->stepEffect = obj;
343 }
344 n = state->levels - 1;
345 state->levels = n;
346 if (n == 0) {
348 state->pillar = NULL;
350 } else if (state->pillar != NULL) {
351 Object_SetMode(state->pillar, 6 - n);
352 }
353 }
354 Object_SetMode(actor, 6);
357 Object_SetMode(actor, 7);
358 actor->speed = 0x30000;
359 actor->acceleration = 0x20000;
360 actor->velocityY = 0x40000;
361 *flags &= 0x7e;
363 Engine_ActorMoveToAndWait(0, pos[0].part.pixel, pos[2].part.pixel);
364 Object_SetMode(actor, 6);
366 if (to->collisionCode != state->terrainCode)
368 else
371 *flags = saved;
372 if (to->collisionCode == state->terrainCode && state->stepEffect == NULL) {
373 Object_SetMode(actor, 18);
375 for (i = 0;; i++) {
376 if ((i & 15) == 0)
377 OverlayObject_SpawnKind24AtObject(actor);
378 if (i > 31 && gKeysPressed != 0)
379 break;
381 }
384 actor->x.fixed = state->targetX;
385 actor->z.fixed = state->targetZ;
387 }
388 state->sparkTimer = 0;
390 work->walkingDistance += Iwram_MulQ16(work->walkingDistanceScale, 0x200000);
391}
392
393/// Touch trigger 21: while the leader stands there, a sparkle rises from them
394/// every 10 to 39 frames.
396 struct MercuryLighthouseLights *state;
397 struct FieldActor *point;
398
399 state = *(struct MercuryLighthouseLights **)WORK_SLOT(35);
400 point = ((struct FieldWork *)WORK_SLOT(HEAP_SLOT_EVENT))
402 if (state->sparkTimer != 0) {
403 state->sparkTimer--;
404 } else {
405 OverlayObject_SpawnKind24AtObject(point);
406 state->sparkTimer = ((u32)(Engine_RandomNext() * 30) >> 16) + 10;
407 }
408}
409
410/// Retreat (PSYNERGY_IN_AREA(ABILITY_RETREAT, 1)): remove the column of light.
412 struct MercuryLighthouseLights **published = Runtime_AllocateBlock(35, 4);
413 struct MercuryLighthouseLights *state;
414 struct FieldActor *pillar;
415
416 if (published == NULL)
417 return;
418 state = *published;
419 pillar = state->pillar;
420 if (pillar == NULL)
421 return;
423 state->pillar = NULL;
424}
s32 AnimationObjects_SelectAnimation(struct AnimationObject *sprite, s32 animation)
Starts animation flags & 0x7f on every layer of a sprite (bit 7 keeps each layer's place and timer),...
Definition animation.c:317
void Engine_ActorSetSpriteFlags(struct FieldActor *, s32)
struct FieldActor * Engine_ObjectCreate(s32, s32, s32, s32)
The engine calls the debug menus import, and their palette swatch tiles.
The engine call layer: one typed inline for each of the thirteen main-image routines below that the s...
static s32 FieldObject_CheckMovementCollision(struct FieldActor *actor, struct FieldPosition *position)
Definition engine_call.h:66
Field effects, their scripts and the flags that spawn them.
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)
void Object_SetMode(struct FieldActor *object, s32 animation)
void Engine_ActorMoveToAndWait(s32 actor, s32 x, s32 z)
s32 Engine_MathSin(s32 angle)
void Engine_ObjectDispatchRelease(struct FieldActor *object)
void Engine_ObjectSetScript(struct FieldActor *object, const s32 *script)
void Engine_GameFlagSet(s32 flag)
void Engine_GameFlagClear(s32 flag)
s32 Engine_RandomNext()
#define ACTOR_NO_TARGET
A target coordinate that is not set: the object has no target.
Definition field_obj.h:145
The field's runtime work: map cells, scene records and regions, map markers and FieldWork.
#define PIXELS(value)
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
volatile u32 gFrameCount
Frames WaitFrames has finished.
struct GameState gGameState
The screen blend's globals and the graphics routines.
void * Runtime_AllocateBlock(s32 kind, s32 size)
As Runtime_AllocateHeapBlock, but tries EWRAM first, for blocks that need room more than speed: the b...
Definition heap.c:80
The heap's work slots and HeapState.
@ HEAP_SLOT_EVENT
Definition heap_state.h:40
#define WORK_SLOT(n)
Reads slots through their own addresses; adjacent slots are reached by offsets.
Definition heap_state.h:14
The IWRAM call layer: the routines the game keeps resident in IWRAM, each at its fixed address,...
static s32 Iwram_RatioMulQ14(s32 divisor, s32 value)
All calls go through this layer.
Definition iwram_call.h:51
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
#define Iwram_MulQ16(left, right)
Definition iwram_call.h:127
The keypad state the VBlank interrupt keeps.
volatile u32 gKeysHeld
Keys down now.
volatile u32 gKeysPressed
Keys that went down this frame.
Maps: their cells, layers, animations and blend scripts, and the map state.
#define MAP_CELLS
Definition map_layers.c:794
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
s32 FixedSqrt(s32 value)
Square root of a 16.16 fixed-point value, in 16.16.
Definition math.c:149
Includes field_event.h with FIELD_STAGED_ACTOR_IMPORTS defined, so a scene gets the staged-actor impo...
s32 gMercuryLighthouse_PillarScript[]
s32 gMercuryLighthouse_ScaleCounterScript[]
s32 gMercuryLighthouse_RampScript[]
s32 gMercuryLighthouse_SparkScript[]
u16 gMercuryLighthouse_MoveAngles[]
The reel game's title lettering.
The work pointers in the gWorkSlot table, by name.
The field object script interpreter and the operands its commands read.
Song and sound effect ids, indices into gSound_SongTable.
#define SE_215
Definition songs.h:210
#define SE_288
Definition songs.h:281
#define SE_241
Definition songs.h:236
#define SE_130
Definition songs.h:123
#define SE_JUMP
An actor jumps: ObjectMotion_Launch plays the high jump above 5 pixels a frame.
Definition songs.h:147
struct EventWork * gEventWork
void Engine_EventEnd(void)
void Engine_AudioPlayCue()
void Engine_EventBegin(void)
void Engine_TaskWait()
#define NULL
Definition bilibin.h:6
static s32 OverlayObject_RampWords24And28Over16Frames(struct ScriptInterpreter *interpreter)
Grows the moving light over the first sixteen script frames.
void MercuryLighthouse_HopAcrossPool(void)
Pushing against the pool's edge (triggers 11, 21 and 31): the leader hops two cells the way the pad p...
static s32 MercuryLighthouse_FollowLeader(struct ScriptInterpreter *interpreter)
Follows the linked actor and alternates the light's animation parameter.
static void MercuryLighthouse_RemoveColumnOnRetreat(void)
Retreat (PSYNERGY_IN_AREA(ABILITY_RETREAT, 1)): remove the column of light.
static void MercuryLighthouse_SpawnSparkPair(void)
Places the two spark animations on the selected actor.
void MercuryLighthouse_LoseLights(void)
Touch trigger 31: the leader loses their lights, and the column swells and fades away.
static s32 OverlayObject_AdvanceScaleCounter(struct ScriptInterpreter *interpreter)
Expands the released light before its script removes it.
void MercuryLighthouse_SparkleAtLeader(void)
Touch trigger 21: while the leader stands there, a sparkle rises from them every 10 to 39 frames.
void MercuryLighthouse_ChargeLights(void)
Touch trigger 11, the pool's heart: the column of light appears over the leader if it is not there,...
static s32 SceneEffect_AdvanceAnchoredRiseFrame(struct ScriptInterpreter *interpreter)
Raises the spark while its scale follows a half-sine pulse.
#define offsetof(type, member)
Definition stddef.h:19
One animation layer of a sprite: its animation id, frames, running script and timer.
Definition animspr.h:37
u8 param
SpriteColorMode its pixels are drawn with; 0 when taken.
Definition animspr.h:45
An animated sprite: its image and shadow, scale, rotation, size and up to four animation layers.
Definition animspr.h:108
struct AnimationSpritePart image
Definition animspr.h:109
u8 flags
bits: 1 outlined in ObjectSystemWork's edge and fill colours | 0 shadow drawn (set when created)
Definition animspr.h:128
struct AnimationEntry * entries[4]
its layers; empty ones null
Definition animspr.h:130
u8 dirty
nonzero redraws its pixels into VRAM when next drawn
Definition animspr.h:125
u16 priority
2 when created
Definition animspr.h:100
struct FieldActor * actors[8]
the party's characters, ids 0 to 7
Definition event_work.h:23
A field actor: a person or thing on the field with a position, motion toward a target,...
Definition field_obj.h:30
s16 timer
The parameter its action script reads (a follow distance or an angle to face), the player's frames to...
Definition field_obj.h:109
s32 speed
speed limit, 16.16 fixed-point pixels per frame
Definition field_obj.h:55
s32 targetX
16.16 fixed-point pixels; ACTOR_NO_TARGET when unset
Definition field_obj.h:59
struct FieldActor * linkedObject
The object its action script follows or faces.
Definition field_obj.h:114
u8 motionFlags
ActorMotionFlag bits; zero is free flight in 3D, and 3 is set on creation.
Definition field_obj.h:83
union FieldCoordinate x
16.16 fixed-point pixels
Definition field_obj.h:35
s32 targetZ
16.16 fixed-point pixels; ACTOR_NO_TARGET when unset
Definition field_obj.h:61
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
u8 spriteStorageKind
Zero hides sprites; one draws a single sprite, two draws a group.
Definition field_obj.h:78
union FieldCoordinate z
16.16 fixed-point pixels
Definition field_obj.h:37
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
A position on the field.
Full event work allocated by Field_RunScene (heap slot 27).
s32 walkingDistanceScale
scales movement for PP and status updates
struct FieldActor * actors[66]
The actors by id: 0 to 7 the party's characters, 8 on the placed actors.
s32 walkingDistance
accumulated 16.16 distance awaiting an update
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
One cell of a map layer: its metatile and flags, its collision code and an attribute indexing the col...
Definition map.h:13
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
Published in the scene's light-controller slot while the pillar is active.
s32 terrainCode
the pool's collision code, the floor's region
s32 levels
The leader's lights, 0 to 3: the heart gives three and each hop into the pool spends one.
struct FieldActor * pillar
the column of light over the leader, or null
s32 targetX
16.16 fixed-point pixels: the centre of the column's cell, where a sinking leader is put back.
struct FieldActor * stepEffect
The light holding the leader up where they landed in the pool, or null.
s32 sparkTimer
Frames to the next sparkle at trigger 21, 10 to 39; a hop zeroes it.
The prefix that command handlers read from a field object's 0x70-byte slot.
Definition script.h:16
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
unsigned char u8
Definition types.h:11
A field coordinate, read whole or as its fraction and its whole pixels.
struct FieldCoordinate::@024321007066302143002254325121164243234242211074 part
s32 fixed
16.16 fixed-point pixels