Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
npc_follow.c File Reference

Go to the source code of this file.

Enumerations

enum  NpcFollowStates { NPC_FOLLOW_STATE_RUN = 0 , NPC_FOLLOW_STATE_JUMP = 1 , NPC_FOLLOW_STATE_FALL = 2 , NPC_FOLLOW_STATE_IDLE = 10 }
 

Functions

void get_npc_pos (s32 npcID, f32 *outX, f32 *outY, f32 *outZ, s32 *outAirborne)
 
void npc_follow_init (Npc *npc, s32 targetNpcID, FollowAnims *anims, f32 walkSpeed, f32 runSpeed, s32 idleRadius, s32 walkRadius)
 
void npc_update_npc_tracking (Npc *npc)
 
void npc_follow_npc (Npc *npc)
 

Enumeration Type Documentation

◆ NpcFollowStates

Enumerator
NPC_FOLLOW_STATE_RUN 
NPC_FOLLOW_STATE_JUMP 
NPC_FOLLOW_STATE_FALL 
NPC_FOLLOW_STATE_IDLE 

Definition at line 3 of file npc_follow.c.

3 {
8};
@ NPC_FOLLOW_STATE_JUMP
Definition npc_follow.c:5
@ NPC_FOLLOW_STATE_IDLE
Definition npc_follow.c:7
@ NPC_FOLLOW_STATE_FALL
Definition npc_follow.c:6
@ NPC_FOLLOW_STATE_RUN
Definition npc_follow.c:4

Function Documentation

◆ get_npc_pos()

void get_npc_pos ( s32 npcID,
f32 * outX,
f32 * outY,
f32 * outZ,
s32 * outAirborne )

Definition at line 10 of file npc_follow.c.

10 {
12 Npc* npc;
13
14 *outX = 0.0f;
15 *outY = 0.0f;
16 *outZ = 0.0f;
17 *outAirborne = false;
18
19 if (npcID == NPC_SELF) {
21 *outY = playerStatus->pos.y;
22 *outZ = playerStatus->pos.z;
24 *outAirborne = true;
25 }
26 } else {
27 npc = get_npc_unsafe(npcID);
28 *outX = npc->pos.x;
29 *outY = npc->pos.y;
30 *outZ = npc->pos.z;
31 if (npc->flags & NPC_FLAG_JUMPING) {
32 *outAirborne = true;
33 }
34 }
35
36}
BSS s32 PopupMenu_SelectedIndex
@ PS_FLAG_FALLING
Definition enums.h:3070
@ PS_FLAG_JUMPING
Definition enums.h:3069
@ NPC_SELF
Definition enums.h:2512
@ NPC_FLAG_JUMPING
Definition enums.h:3043
Npc * get_npc_unsafe(s32 npcID)
Definition npc.c:992
s32 flags
Vec3f pos
PlayerStatus gPlayerStatus
Definition 77480.c:38

Referenced by npc_follow_npc(), and npc_update_npc_tracking().

◆ npc_follow_init()

void npc_follow_init ( Npc * npc,
s32 targetNpcID,
FollowAnims * anims,
f32 walkSpeed,
f32 runSpeed,
s32 idleRadius,
s32 walkRadius )

Definition at line 38 of file npc_follow.c.

38 {
40 NpcFollowData* followData;
41 s32 i;
42
43 npc->blur.followData = followData = heap_malloc(sizeof(*followData));
44 ASSERT(followData != nullptr);
45
46 for (i = 0; i < ARRAY_COUNT(followData->moveHistory); i++) {
47 followData->moveHistory[i].pos.x = playerStatus->pos.x;
48 followData->moveHistory[i].pos.y = playerStatus->pos.y;
49 followData->moveHistory[i].pos.z = playerStatus->pos.z;
50 followData->moveHistory[i].isAirborne = false;
51 }
52 followData->lastPointIdx = 0;
53 followData->targetPointIdx = 0;
55 followData->targetNpcID = targetNpcID;
56 followData->anims = anims;
57 followData->walkSpeed = walkSpeed;
58 followData->runSpeed = runSpeed;
59 followData->idleRadius = idleRadius;
60 followData->walkRadius = walkRadius;
61 npc->curAnim = followData->anims->idle;
62 npc->jumpVel = 0.0f;
63 npc->flags |= NPC_FLAG_GRAVITY;
66}
NpcHistoryPoint moveHistory[40]
FollowAnims * anims
#define ASSERT(condition)
@ COLLIDER_FLAG_IGNORE_PLAYER
Definition enums.h:4281
@ NPC_FLAG_GRAVITY
Definition enums.h:3041
void * heap_malloc(s32 size)
Definition heap.c:34
#define ARRAY_COUNT(arr)
Definition macros.h:39
f32 jumpVel
AnimID curAnim
s32 collisionChannel
union Npc::@0 blur

◆ npc_update_npc_tracking()

void npc_update_npc_tracking ( Npc * npc)

Definition at line 68 of file npc_follow.c.

68 {
69 NpcFollowData* followData = npc->blur.followData;
70 f32 x, y, z;
72 s32 isAirborne;
74
75 get_npc_pos(followData->targetNpcID, &x, &y, &z, &airborne);
76 historyPoint = &followData->moveHistory[followData->lastPointIdx];
77 isAirborne = airborne != false;
78
79 if (historyPoint->isAirborne && isAirborne) {
80 return;
81 }
82
83 if (!isAirborne && dist2D(npc->pos.x, npc->pos.z, x, z) <= followData->idleRadius && !historyPoint->isAirborne) {
84 return;
85 }
86
87 historyPoint = &followData->moveHistory[followData->lastPointIdx];
88 if (historyPoint->pos.x != x || historyPoint->pos.y != y || historyPoint->pos.z != z) {
89 if (followData->targetPointIdx != followData->lastPointIdx + 1) {
90 followData->lastPointIdx++;
91 if (followData->lastPointIdx >= ARRAY_COUNT(followData->moveHistory)) {
92 followData->lastPointIdx = 0;
93 }
94 historyPoint = &followData->moveHistory[followData->lastPointIdx];
95 historyPoint->pos.x = x;
96 historyPoint->pos.y = y;
97 historyPoint->pos.z = z;
98 historyPoint->isAirborne = isAirborne;
99 }
100 }
101}
f32 dist2D(f32 ax, f32 ay, f32 bx, f32 by)
Definition 43F0.c:670
void get_npc_pos(s32 npcID, f32 *outX, f32 *outY, f32 *outZ, s32 *outAirborne)
Definition npc_follow.c:10

◆ npc_follow_npc()

void npc_follow_npc ( Npc * npc)

Definition at line 103 of file npc_follow.c.

103 {
104 NpcFollowData* followData = npc->blur.followData;
105 f32 x, y, z;
108 f32 dist;
110 f32 targetX, targetY, targetZ;
111 f32 yaw;
112
113 get_npc_pos(followData->targetNpcID, &x, &y, &z, &airborne);
114
115 switch (followData->followState) {
117 historyPoint = &followData->moveHistory[followData->targetPointIdx];
118 targetX = historyPoint->pos.x;
119 targetY = historyPoint->pos.y;
120 targetZ = historyPoint->pos.z;
121 currentX = npc->pos.x;
122 currentY = npc->pos.y;
123 currentZ = npc->pos.z;
124 npc->moveSpeed = followData->walkSpeed;
125
126 dist = dist2D(npc->pos.x, npc->pos.z, x, z);
127 if (dist >= followData->walkRadius) {
128 npc->moveSpeed = followData->runSpeed;
129 }
130
131 npc->curAnim = followData->anims->run;
132 if (!(npc->flags & NPC_FLAG_GROUNDED)) {
133 npc->curAnim = followData->anims->fall;
134 }
135
136 while (true) {
137 dist = dist2D(currentX, currentZ, targetX, targetZ);
138 yaw = atan2(currentX, currentZ, targetX, targetZ);
139 if (dist > npc->moveSpeed) {
140 dist = dist2D(currentX, currentZ, x, z);
141 if (dist > followData->walkRadius) {
142 break;
143 }
144
145 if (dist > followData->idleRadius) {
146 npc->moveSpeed = dist - followData->idleRadius;
147 if (npc->moveSpeed > followData->walkSpeed) {
148 npc->moveSpeed = followData->walkSpeed;
149 } else {
150 npc->moveSpeed += 1.0;
151 }
152 break;
153 }
154 }
155
156 if (followData->targetPointIdx == followData->lastPointIdx) {
157 npc->moveSpeed = 0.0f;
158 yaw = npc->yaw;
159 npc->curAnim = followData->anims->idle;
160 break;
161 }
162
163 dist = dist2D(npc->pos.x, npc->pos.z, x, z);
164 if (dist <= followData->idleRadius) {
165 npc->moveSpeed = 0.0f;
166 yaw = npc->yaw;
167 npc->curAnim = followData->anims->idle;
169 break;
170 }
171
172 followData->targetPointIdx++;
173 if (followData->targetPointIdx >= ARRAY_COUNT(followData->moveHistory)) {
174 followData->targetPointIdx = 0;
175 }
176 historyPoint = &followData->moveHistory[followData->targetPointIdx];
177 targetX = historyPoint->pos.x;
178 targetZ = historyPoint->pos.z;
179 if (npc->flags & NPC_FLAG_GROUNDED) {
180 if (historyPoint->isAirborne) {
182 break;
183 }
184 }
185 }
186
187 if (!(npc->flags & NPC_FLAG_GROUNDED)) {
188 npc->moveSpeed *= 0.5f;
189 }
190 npc->yaw = yaw;
191 npc_move_heading(npc, npc->moveSpeed, yaw);
194 }
195 break;
197 if (followData->targetPointIdx != followData->lastPointIdx) {
198 followData->targetPointIdx++;
199 if (followData->targetPointIdx >= ARRAY_COUNT(followData->moveHistory)) {
200 followData->targetPointIdx = 0;
201 }
202 historyPoint = &followData->moveHistory[followData->targetPointIdx];
203 targetX = historyPoint->pos.x;
204 targetY = historyPoint->pos.y;
205 targetZ = historyPoint->pos.z;
206 npc->moveToPos.x = targetX;
207 npc->moveToPos.y = targetY;
208 npc->moveToPos.z = targetZ;
209 npc->duration = 0;
210 npc->jumpScale = 2.0f;
211 npc->moveSpeed = followData->runSpeed;
212 npc->planarFlyDist = dist2D(npc->pos.x, npc->pos.z, npc->moveToPos.x, npc->moveToPos.z);
213 npc->yaw = atan2(npc->pos.x, npc->pos.z, npc->moveToPos.x, npc->moveToPos.z);
214 dist = npc->planarFlyDist;
215 currentY = npc->moveToPos.y - npc->pos.y;
216 if (npc->planarFlyDist < currentY) {
217 dist = currentY;
218 }
219 if (dist < followData->idleRadius) {
220 npc->jumpVel = 0.0f;
221 npc->flags |= NPC_FLAG_GRAVITY;
222 npc->yaw = atan2(npc->pos.x, npc->pos.z, x, z);
223 followData->followState = NPC_FOLLOW_STATE_RUN;
224 return;
225 }
226 npc->duration = dist / npc->moveSpeed;
227 if (npc->duration < 10) {
228 npc->duration = 10;
229 }
230 npc->moveSpeed = npc->planarFlyDist / npc->duration;
231 npc->jumpVel = (currentY + (npc->jumpScale * npc->duration * npc->duration * 0.5f)) / npc->duration;
232 npc->curAnim = followData->anims->jump;
233 npc->flags &= ~NPC_FLAG_GRAVITY;
235 }
236 break;
238 npc->jumpVel -= npc->jumpScale;
239 npc->pos.y += npc->jumpVel;
240 if (npc->jumpVel <= 0.0f) {
241 npc->curAnim = followData->anims->fall;
242 }
243 npc_move_heading(npc, npc->moveSpeed, npc->yaw);
244 if (npc->jumpVel <= 0.0f) {
245 currentX = npc->pos.x;
246 dist = fabsf(npc->jumpVel) + 8.0;
247 currentY = npc->pos.y + dist;
248 currentZ = npc->pos.z;
250 dist <= fabsf(npc->jumpVel) + 8.0)
251 {
252 npc->curAnim = followData->anims->land;
253 npc->jumpVel = 0.0f;
254 npc->pos.y = currentY;
255 npc->flags |= NPC_FLAG_GRAVITY;
256 npc->yaw = atan2(currentX, currentZ, x, z);
257 followData->followState = NPC_FOLLOW_STATE_RUN;
258 }
259 }
260 break;
262 historyPoint = &followData->moveHistory[followData->targetPointIdx];
263 targetX = historyPoint->pos.x;
264 targetY = historyPoint->pos.y;
265 targetZ = historyPoint->pos.z;
266 currentX = npc->pos.x;
267 currentY = npc->pos.y;
268 currentZ = npc->pos.z;
269 dist = dist2D(npc->pos.x, npc->pos.z, x, z);
270 if (dist <= followData->idleRadius) {
271 break;
272 }
273
274 while (true) {
275 if (historyPoint->isAirborne) {
276 break;
277 }
278
279 yaw = atan2(npc->pos.x, npc->pos.z, targetX, targetZ);
280 if (fabsf(get_clamped_angle_diff(yaw, atan2(npc->pos.x, npc->pos.z, x, z))) < 90.0f) {
281 break;
282 }
283
284 if (followData->targetPointIdx == followData->lastPointIdx) {
285 break;
286 }
287
288 followData->targetPointIdx++;
289 if (followData->targetPointIdx >= ARRAY_COUNT(followData->moveHistory)) {
290 followData->targetPointIdx = 0;
291 }
292 historyPoint = &followData->moveHistory[followData->targetPointIdx];
293 targetX = historyPoint->pos.x;
294 targetZ = historyPoint->pos.z;
295 continue;
296 }
297
298 if (!historyPoint->isAirborne) {
299 followData->followState = NPC_FOLLOW_STATE_RUN;
300 return;
301 }
302
303 while (true) {
304 if (!historyPoint->isAirborne) {
305 break;
306 }
307
308 yaw = atan2(npc->pos.x, npc->pos.z, targetX, targetZ);
309 if (fabsf(get_clamped_angle_diff(yaw, atan2(npc->pos.x, npc->pos.z, x, z))) < 90.0f) {
310 break;
311 }
312
313 if (followData->targetPointIdx == followData->lastPointIdx) {
314 break;
315 }
316
317 followData->targetPointIdx++;
318 if (followData->targetPointIdx >= ARRAY_COUNT(followData->moveHistory)) {
319 followData->targetPointIdx = 0;
320 }
321 historyPoint = &followData->moveHistory[followData->targetPointIdx];
322 targetX = historyPoint->pos.x;
323 targetZ = historyPoint->pos.z;
324 }
326 break;
327 }
328}
#define npc_raycast_down_sides
#define atan2
@ NPC_FLAG_COLLDING_FORWARD_WITH_WORLD
Definition enums.h:3046
@ NPC_FLAG_GROUNDED
Definition enums.h:3044
f32 fabsf(f32 f)
f32 get_clamped_angle_diff(f32, f32)
Definition 43F0.c:606
void npc_move_heading(Npc *npc, f32 speed, f32 yaw)
Definition npc.c:983
f32 jumpScale
Vec3f moveToPos
f32 planarFlyDist
f32 moveSpeed
s16 duration