Alchemy
Loading...
Searching...
No Matches

update.c

Go to the documentation of this file.
1#include "unit/field/update.h"
3#include "types.h"
4#include "irq.h"
5#include "iwram_call.h"
6#include "mulq16.h"
7#include "script_motion.h"
8#include "macros.h"
9
10#define OBJECT_SLOTS 8 ///< script-driven objects updated each frame
11#define TARGET_UNSET ((s32)0x80000000) ///< no movement target
12#define TURN_MAX 0x1000 ///< the most an object turns in one frame
13
14/// Calls a resident IWRAM routine at its absolute address, which this code
15/// still reaches when it runs from its copy in RAM.
16#define IWRAM_ENTRY(type, offset) ((type)(IwramIrqMain + (offset)))
17#define IwramSqrt IWRAM_ENTRY(s32 (*)(s32 value), Iwram_SqrtOffset)
18#define IwramRatioMulQ14 \
19 IWRAM_ENTRY(s32 (*)(s32 divisor, s32 value), Iwram_RatioMulQ14Offset)
20
21extern struct ScriptMotionObject *gObjectSlots;
22
23// Far-call stubs copied to RAM together with this code (updfar.s).
27
28/// Advances the eight script-driven objects by one frame. For each live one:
29/// run its hook, then its script (unless it waits or is paused); steer it
30/// towards its target, or brake it when it has none; apply gravity and
31/// bounce off the ground; detect arrival at the target; and turn it to face
32/// the way it moves. Copied to RAM and run from there. Writes each object's
33/// position, velocity and facing, and unsets its target on arrival. Only
34/// Object_RunUpdateAllFromHeap copies and runs it, and nothing calls that;
35/// the field runs Object_UpdateAllThumb instead.
36void Object_UpdateAll(void) {
37 struct ScriptMotionObject *object;
38 s32 y;
39 s32 arrived;
40 struct ScriptMotionObject *nextObject;
41 s32 nextSlot;
42 s32 slot;
43 s32 x;
44 s32 z;
45 s32 deltaX;
46 s32 deltaZ;
49 s32 distance;
50 s32 speedRatio;
51 s32 groundY;
52 s32 turnDelta;
53 u16 travelAngle;
54
55 for (slot = 0, object = gObjectSlots; slot < OBJECT_SLOTS;
56 slot = nextSlot, object = nextObject) {
57 nextSlot = slot + 1;
58 nextObject = object + 1;
59 if (object->script == NULL)
60 continue;
61 if (object->hook != NULL)
62 object->hook((union FieldObject *)object);
63 if (object->script == NULL)
64 continue;
65 if (object->paused != 0)
66 continue;
67
68 // Count down a wait, or run commands until one yields for this frame.
69 // Words above 63 are operands, which the cursor steps over.
70 if (object->wait != 0) {
71 object->wait--;
72 } else {
73 for (;;) {
74 s32 cursor = (s16)object->scriptCursor;
75#ifdef UBFIX
76 u32 command = object->script[cursor];
77#else
78 u32 command = object->script->word[cursor];
79#endif
80
81 if (command <= 63) {
82 if (gScriptObject_CommandTable[command](
83 (struct ScriptInterpreter *)object) == 0)
84 break;
85 } else {
86 object->scriptCursor++;
87 }
88 }
89 if (object->script == NULL)
90 continue;
91 }
92
93 x = object->x;
94 y = object->y;
95 z = object->z;
96// UB: frozen objects skip the initialization below, but still test arrival.
97#ifdef UBFIX
98 arrived = 0;
99#endif
100 if (object->frozen == 0) {
101 arrived = 0;
102 if (object->targetX != TARGET_UNSET) {
103 // Accelerate towards the target. The first distance is in whole
104 // pixels, to stay in range; under 256 pixels it is measured again in
105 // 16.16.
106 deltaX = (object->targetX - x) / 65536;
107 deltaZ = (object->targetZ - z) / 65536;
108 distance = IwramSqrt(deltaX * deltaX + deltaZ * deltaZ) << 16;
109 if (distance <= 0xffffff) {
110 deltaX = object->targetX - x;
111 deltaZ = object->targetZ - z;
112 distance = IwramSqrt(MulQ16(deltaX, deltaX) + MulQ16(deltaZ, deltaZ))
113 << 8;
114 }
115 if (distance == 0) {
116 x = object->targetX;
117 z = object->targetZ;
118 } else {
119 speedRatio = IwramRatioMulQ14(distance, object->acceleration);
120 velocityX = object->velocityX + MulQ16(deltaX, speedRatio);
121 object->velocityX = velocityX;
122 // Limit the combined horizontal velocity after accelerating both
123 // axes.
124 velocityZ = object->velocityZ + MulQ16(deltaZ, speedRatio);
125 object->velocityZ = velocityZ;
126 distance = IwramSqrt(MulQ16(velocityX, velocityX) +
128 << 8;
129 if (distance > object->speedLimit) {
130 // Too fast: scale the velocity back to the speed limit.
131 speedRatio = IwramRatioMulQ14(distance, object->speedLimit);
132 object->velocityX = MulQ16(velocityX, speedRatio);
133 object->velocityZ = MulQ16(velocityZ, speedRatio);
134 }
135 }
136 } else {
137 // No target: brake by the acceleration until the object stops.
138 // XXX: this branch calls the far-call stubs where the steering above
139 // calls the IWRAM routines by address; both reach the same routines,
140 // and only the bytes show the difference, not why.
141 deltaX = object->velocityX;
142 deltaZ = object->velocityZ;
143 distance =
144 IwramSqrtFarArm(MulQ16(deltaX, deltaX) + MulQ16(deltaZ, deltaZ))
145 << 8;
146 if (distance != 0) {
147 if (distance - object->acceleration < 0) {
148 object->velocityX = 0;
149 object->velocityZ = 0;
150 } else {
151 speedRatio = IwramRatioMulQ14FarArm(
152 distance, distance - object->acceleration);
153 object->velocityX = MulQ16(deltaX, speedRatio);
154 object->velocityZ = MulQ16(deltaZ, speedRatio);
155 }
156 } else {
157 object->velocityX = 0;
158 object->velocityZ = 0;
159 }
160 }
161
162 // Gravity, and a bounce that loses energy on landing.
163 if (object->motionFlags & 2) {
164 groundY = object->terrainHeight;
165 if (y > groundY)
166 object->velocityY -= object->gravity;
168 y = groundY;
169 object->velocityY =
170 -MulQ16(object->velocityY, object->vertical.bounce);
171 if ((object->velocityY < 0 ? -object->velocityY
172 : object->velocityY) <= object->gravity)
173 object->velocityY = 0;
174 }
175 }
176 }
177
178 // Integrate velocity even when the acceleration and gravity step is frozen.
179 x += object->velocityX;
180 y += object->velocityY;
181 z += object->velocityZ;
182
183 // Arrived: the watched axis reached the target, or crossed it this frame
184 // (the sign of the offset changed).
185 switch (object->arrivalAxis) {
186 case ARRIVAL_AXIS_X:
187 if (x == object->targetX ||
188 ((object->x - object->targetX) ^ (x - object->targetX)) < 0)
189 arrived = 1;
190 break;
191 case ARRIVAL_AXIS_Y:
192 if (y == object->targetY ||
193 ((object->y - object->targetY) ^ (y - object->targetY)) < 0)
194 arrived = 1;
195 break;
196 case ARRIVAL_AXIS_Z:
197 if (z == object->targetZ ||
198 ((object->z - object->targetZ) ^ (z - object->targetZ)) < 0)
199 arrived = 1;
200 break;
201 }
202 // Snapping stops the object on the target; it takes the target's height
203 // too only when its motion flags are all clear.
204 if (arrived) {
205 if (object->snapToTarget) {
206 x = object->targetX;
207 object->velocityX = 0;
208 z = object->targetZ;
209 object->velocityZ = 0;
210 if (object->motionFlags == 0) {
211 y = object->targetY;
212 object->velocityY = 0;
213 }
214 }
215 object->targetX = TARGET_UNSET;
216 object->targetY = TARGET_UNSET;
217 object->targetZ = TARGET_UNSET;
218 object->arrivalAxis = 0;
219 }
220 SET_POSITION(object, x, y, z);
221
222 // Turn to face the direction of travel.
223 if (object->steeringFlags & 1) {
224 x = object->velocityX;
225 z = object->velocityZ;
226 if (x != 0 || z != 0) {
227 travelAngle = ArcTan2FarArm(z, x);
228 turnDelta = (s16)(travelAngle - object->facing);
229 if (turnDelta > TURN_MAX)
230 turnDelta = TURN_MAX;
231 if (turnDelta < -TURN_MAX)
232 turnDelta = -TURN_MAX;
233 object->facing += turnDelta;
234 }
235 }
236 }
237}
#define TARGET_UNSET
A movement target coordinate that is not set.
The hardware's interrupt sources and the interrupt handler table.
The IWRAM call layer: the routines the game keeps resident in IWRAM, each at its fixed address,...
Small statement macros both games use for positions, velocities and bounds.
#define SET_POSITION(p, px, py, pz)
Sets the position or the velocity of a particle, a piece of debris or an object in one statement.
Definition macros.h:16
MulQ16, the 16.16 fixed-point multiply.
static __inline__ s32 MulQ16(s32 left, s32 right)
Multiplies 16.16 fixed-point values, returning the middle 32 product bits.
Definition mulq16.h:18
const ScriptCommand gScriptObject_CommandTable[47]
The handler for each script command number; 0x24 onwards act as SC_END.
ScriptMotionObject, an object moved by a field script.
#define NULL
Definition bilibin.h:6
The prefix that command handlers read from a field object's 0x70-byte slot.
Definition script.h:16
A field object as the motion code sees it: its script, position, motion toward a target,...
s32 x
16.16 fixed-point pixels
s32 velocityY
16.16 fixed-point pixels per frame
void(*) hook(union FieldObject *object)
Called each frame before its script.
s32 velocityX
16.16 fixed-point pixels per frame
const ScriptMotionWords * script
null when the object is free
s32 y
16.16 fixed-point pixels
s32 z
16.16 fixed-point pixels
s32 velocityZ
16.16 fixed-point pixels per frame
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
An object on the field, read as an actor or as an effect.
s32 ArcTan2FarArm(s32 z, s32 x)
struct ScriptMotionObject * gObjectSlots
s32 IwramSqrtFarArm(s32 value)
#define IwramRatioMulQ14
Definition update.c:18
void Object_UpdateAll(void)
Advances the eight script-driven objects by one frame.
Definition update.c:36
s32 IwramRatioMulQ14FarArm(s32 divisor, s32 value)
#define IwramSqrt
Definition update.c:17
#define TURN_MAX
the most an object turns in one frame
Definition update.c:12
#define OBJECT_SLOTS
script-driven objects updated each frame
Definition update.c:10
@ ARRIVAL_AXIS_Y
Definition update.h:11
@ ARRIVAL_AXIS_Z
Definition update.h:12
@ ARRIVAL_AXIS_X
Definition update.h:10