Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
MontyMoleAI.inc.c
Go to the documentation of this file.
1#include "common.h"
2#include "npc.h"
3#include "sprite/npc/MontyMole.h"
4
5// prerequisites
7
8// ensure state handlers conform to expected signature
9static AIStateHandler N(MontyMoleAI_Init);
10static AIStateHandler N(MontyMoleAI_Wander);
11static AIStateHandler N(MontyMoleAI_PreSurface);
12static AIStateHandler N(MontyMoleAI_Surface);
13static AIStateHandler N(MontyMoleAI_DrawRock);
14static AIStateHandler N(MontyMoleAI_ThrowRock);
15static AIStateHandler N(MontyMoleAI_PreBurrow);
16static AIStateHandler N(MontyMoleAI_Burrow);
17
19 AI_STATE_MOLE_INIT = 0, // choose random heading and duration for next state
20 AI_STATE_MOLE_WANDER = 1, // wander around 'underground'
21 AI_STATE_MOLE_PRE_SURFACE = 12, // delay before emerging from underground
22 AI_STATE_MOLE_SURFACE = 13, // emerge from underground
23 AI_STATE_MOLE_DRAW_ROCK = 14, // pull out a rock, can either attack or cancel
24 AI_STATE_MOLE_THROW_ROCK = 15, // throw the rock
26 AI_STATE_MOLE_PRE_BURROW = 20, // delay before burrowing back underground
27 AI_STATE_MOLE_BURROW = 21, // burrow underground
28};
29
30#define INTANGIBLE_MONTY_MOLE_NPC_FLAGS \
31 ENEMY_FLAG_SKIP_BATTLE \
32 | ENEMY_FLAG_IGNORE_TOUCH \
33 | ENEMY_FLAG_IGNORE_JUMP \
34 | ENEMY_FLAG_IGNORE_HAMMER \
35 | ENEMY_FLAG_IGNORE_PARTNER \
36 | ENEMY_FLAG_CANT_INTERACT
37
38static s32 N(MontyMoleAI_CanAttack)(Evt* script, EnemyDetectVolume* territory, f32 radius, f32 arg3) {
39 Camera* cam;
40 Enemy* enemy;
41 Npc* npc;
42 f32 angle;
43 s32 retVal;
44
45 enemy = script->owner1.enemy;
46 npc = get_npc_unsafe(enemy->npcID);
47 cam = &gCameras[gCurrentCamID];
48 retVal = basic_ai_check_player_dist(territory, enemy, radius * 1.1, arg3, 0) != 0;
49 // check npc facing angle for sight of player
50 angle = 270.0f;
51 if (clamp_angle(get_clamped_angle_diff(cam->curYaw, npc->yaw)) < 180.0) {
52 angle = 90.0f;
53 }
54 if (fabsf(get_clamped_angle_diff(angle, atan2(npc->pos.x, npc->pos.z, gPlayerStatusPtr->pos.x, gPlayerStatusPtr->pos.z))) > 60.0) {
55 retVal = FALSE;
56 }
57 // check for overlap with player
59 retVal = FALSE;
60 }
61 // check player elevation difference
62 if (fabsf(npc->pos.y - gPlayerStatusPtr->pos.y) >= 40.0f) {
63 retVal = FALSE;
64 }
65 // check for bow hiding
67 retVal = FALSE;
68 }
69 return retVal;
70}
71
72static void N(MontyMoleAI_Init)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
73 Enemy* enemy = script->owner1.enemy;
74 Npc* npc = get_npc_unsafe(enemy->npcID);
75
76 npc->duration = (aiSettings->moveTime / 2) + rand_int((aiSettings->moveTime / 2) + 1);
77 npc->yaw = clamp_angle((npc->yaw + rand_int(60)) - 30.0f);
78 if (enemy->territory->wander.moveSpeedOverride < 0) {
79 npc->moveSpeed = aiSettings->moveSpeed;
80 } else {
81 npc->moveSpeed = enemy->territory->wander.moveSpeedOverride / 32767.0;
82 }
85 script->functionTemp[1] = 0;
86 script->AI_TEMP_STATE = AI_STATE_MOLE_WANDER;
87}
88
89static void N(MontyMoleAI_Wander)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
90 Enemy* enemy = script->owner1.enemy;
91 Npc* npc = get_npc_unsafe(enemy->npcID);
92 Npc dummyNpc;
93 f32 hitDepth;
94
97 npc->pos.x, npc->pos.z,
99 npc->yaw = atan2(npc->pos.x, npc->pos.z, enemy->territory->wander.centerPos.x, enemy->territory->wander.centerPos.z);
100 }
101 dummyNpc.pos.x = npc->pos.x;
102 dummyNpc.pos.y = npc->pos.y + 1.0f;
103 dummyNpc.pos.z = npc->pos.z;
104 npc_move_heading(&dummyNpc, npc->moveSpeed + npc->collisionDiameter, npc->yaw);
105 hitDepth = 1000.0f;
106 if (npc_raycast_down_sides(0, &dummyNpc.pos.x, &dummyNpc.pos.y, &dummyNpc.pos.z, &hitDepth) && (hitDepth < 5.0f)) {
107 npc_move_heading(npc, npc->moveSpeed, npc->yaw);
108 }
110 script->AI_TEMP_STATE = AI_STATE_MOLE_INIT;
111 }
112 if (aiSettings->playerSearchInterval >= 0) {
113 if (script->functionTemp[1] <= 0) {
114 script->functionTemp[1] = aiSettings->playerSearchInterval;
115 if (N(MontyMoleAI_CanAttack)(script, territory, aiSettings->alertRadius, aiSettings->alertOffsetDist)) {
116 npc->duration = 0;
117 script->AI_TEMP_STATE = AI_STATE_MOLE_PRE_SURFACE;
118 return;
119 }
120 }
121 script->functionTemp[1]--;
122 }
123 if (!(npc->flags & (NPC_FLAG_FLYING | NPC_FLAG_GROUNDED))) {
124 npc->homePos.x = npc->pos.x;
125 npc->homePos.z = npc->pos.z;
126 }
127 npc->duration--;
128 if (npc->duration == 0) {
129 script->AI_TEMP_STATE = AI_STATE_MOLE_INIT;
130 }
131}
132
133static void N(MontyMoleAI_PreSurface)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
134 Enemy* enemy = script->owner1.enemy;
135 Npc* npc = get_npc_unsafe(enemy->npcID);
136
137 npc->flags &= ~NPC_FLAG_INVISIBLE;
139 npc->yaw = atan2(npc->pos.x, npc->pos.z, gPlayerStatusPtr->pos.x, gPlayerStatusPtr->pos.z);
140 npc->curAnim = ANIM_MontyMole_Anim10; // emerge from ground
141 npc->duration = 10;
142 script->AI_TEMP_STATE = AI_STATE_MOLE_SURFACE;
143}
144
145static void N(MontyMoleAI_Surface)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
146 Enemy* enemy = script->owner1.enemy;
147 Npc* npc = get_npc_unsafe(enemy->npcID);
148
149 npc->duration--;
150 if (npc->duration == 2) {
152 }
153 if (npc->duration <= 0) {
154 npc->curAnim = ANIM_MontyMole_Anim18; // get and throw rock
155 npc->duration = 10;
156 script->AI_TEMP_STATE = AI_STATE_MOLE_DRAW_ROCK;
157 }
158}
159
160static void N(MontyMoleAI_DrawRock)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
161 Enemy* enemy = script->owner1.enemy;
162 Npc* npc = get_npc_unsafe(enemy->npcID);
163 EffectInstance* emoteOut;
164
165 npc->duration--;
166 if ((npc->duration) <= 0) {
167 if (!N(MontyMoleAI_CanAttack)(script, territory, aiSettings->alertRadius * 1.1, aiSettings->alertOffsetDist)) {
168 fx_emote(EMOTE_QUESTION, npc, 0.0f, npc->collisionHeight, 1.0f, 2.0f, -20.0f, 15, &emoteOut);
169 npc->curAnim = ANIM_MontyMole_Anim01; // cancel attack
170 npc->duration = 30;
171 script->AI_TEMP_STATE = AI_STATE_MOLE_PRE_BURROW;
172 } else {
173 npc->curAnim = ANIM_MontyMole_Anim1B; // throw rock
174 npc->duration = 15;
175 script->AI_TEMP_STATE = AI_STATE_MOLE_THROW_ROCK;
176 }
177 }
178}
179
180static void N(MontyMoleAI_ThrowRock)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
181 Enemy* moleEnemy;
182 Enemy* rockEnemy;
183 Npc* moleNpc;
184
185 moleEnemy = script->owner1.enemy;
186 moleNpc = get_npc_unsafe(moleEnemy->npcID);
187 moleNpc->duration--;
188 if (moleNpc->duration == 13) {
189 rockEnemy = get_enemy(moleEnemy->npcID + 1);
190 rockEnemy->varTable[4] = moleEnemy->npcID;
191 rockEnemy->varTable[0] = 1;
192 }
193 if (moleNpc->duration < 8) {
194 if (dist2D(moleNpc->pos.x, moleNpc->pos.z, gPlayerStatusPtr->pos.x, gPlayerStatusPtr->pos.z) > 100.0) {
195 moleNpc->curAnim = ANIM_MontyMole_Anim15; // clap
196 }
197 }
198 if (moleNpc->duration <= 0) {
199 if (moleNpc->curAnim != ANIM_MontyMole_Anim15) {
200 moleNpc->curAnim = ANIM_MontyMole_Anim01;
201 }
202 moleNpc->duration = 15;
203 script->AI_TEMP_STATE = AI_STATE_MOLE_PRE_BURROW;
204 }
205}
206
207static void N(MontyMoleAI_PreBurrow)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
208 Enemy* enemy = script->owner1.enemy;
209 Npc* npc = get_npc_unsafe(enemy->npcID);
210
211 npc->duration--;
212 if (npc->duration <= 0) {
214 npc->duration = 11;
215 npc->curAnim = ANIM_MontyMole_Anim11; // retreat into ground
216 script->AI_TEMP_STATE = AI_STATE_MOLE_BURROW;
217 }
218}
219
220static void N(MontyMoleAI_Burrow)(Evt* script, MobileAISettings* aiSettings, EnemyDetectVolume* territory) {
221 Enemy* enemy = script->owner1.enemy;
222 Npc* npc = get_npc_unsafe(enemy->npcID);
223
224 npc->duration--;
225 if (npc->duration == 3) {
227 }
228 if (npc->duration <= 0) {
230 script->AI_TEMP_STATE = AI_STATE_MOLE_INIT;
231 }
232}
233
234API_CALLABLE(N(MontyMoleAI_Main)) {
235 Bytecode* args = script->ptrReadPos;
236 Enemy* enemy = script->owner1.enemy;
237 Npc* npc = get_npc_unsafe(enemy->npcID);
238 EnemyDetectVolume territory;
239 EnemyDetectVolume* territoryPtr = &territory;
240 MobileAISettings* aiSettings = (MobileAISettings*)evt_get_variable(script, *args++);
241
242 territory.skipPlayerDetectChance = 0;
243 territory.shape = enemy->territory->wander.detectShape;
244 territory.pointX = enemy->territory->wander.detectPos.x;
245 territory.pointZ = enemy->territory->wander.detectPos.z;
246 territory.sizeX = enemy->territory->wander.detectSize.x;
247 territory.sizeZ = enemy->territory->wander.detectSize.z;
248 territory.halfHeight = 65.0f;
249 territory.detectFlags = 0;
250
251 if (isInitialCall) {
252 script->AI_TEMP_STATE = AI_STATE_MOLE_INIT;
253 npc->duration = 0;
254 npc->flags &= ~NPC_FLAG_JUMPING;
256 }
257
258 if (enemy->aiFlags & AI_FLAG_SUSPEND) {
259 if (enemy->aiSuspendTime != 0) {
260 return ApiStatus_BLOCK;
261 }
262 enemy->aiFlags &= ~AI_FLAG_SUSPEND;
263 }
264
265 switch (script->AI_TEMP_STATE) {
267 N(MontyMoleAI_Init)(script, aiSettings, territoryPtr);
268 // fallthrough
270 N(MontyMoleAI_Wander)(script, aiSettings, territoryPtr);
271 return ApiStatus_BLOCK;
273 N(MontyMoleAI_PreSurface)(script, aiSettings, territoryPtr);
274 // fallthrough
276 N(MontyMoleAI_Surface)(script, aiSettings, territoryPtr);
277 if (script->AI_TEMP_STATE != AI_STATE_MOLE_DRAW_ROCK) {
278 return ApiStatus_BLOCK;
279 } // else fallthrough
281 N(MontyMoleAI_DrawRock)(script, aiSettings, territoryPtr);
282 if (script->AI_TEMP_STATE != AI_STATE_MOLE_THROW_ROCK) {
283 return ApiStatus_BLOCK;
284 } // else fallthrough
286 N(MontyMoleAI_ThrowRock)(script, aiSettings, territoryPtr);
287 if (script->AI_TEMP_STATE != AI_STATE_MOLE_UNUSED) {
288 return ApiStatus_BLOCK;
289 } // else fallthrough
291 N(MontyMoleAI_PreBurrow)(script, aiSettings, territoryPtr);
292 return ApiStatus_BLOCK;
294 N(MontyMoleAI_Burrow)(script, aiSettings, territoryPtr);
295 return ApiStatus_BLOCK;
296 }
297 return ApiStatus_BLOCK;
298}
#define INTANGIBLE_MONTY_MOLE_NPC_FLAGS
AiStateMontyMole
@ AI_STATE_MOLE_WANDER
@ AI_STATE_MOLE_DRAW_ROCK
@ AI_STATE_MOLE_INIT
@ AI_STATE_MOLE_BURROW
@ AI_STATE_MOLE_PRE_BURROW
@ AI_STATE_MOLE_SURFACE
@ AI_STATE_MOLE_PRE_SURFACE
@ AI_STATE_MOLE_THROW_ROCK
@ AI_STATE_MOLE_UNUSED
#define npc_raycast_down_sides
#define rand_int
#define clamp_angle
#define atan2
@ EMOTE_QUESTION
Definition enums.h:497
@ AI_FLAG_SUSPEND
Definition enums.h:4571
@ AI_FLAG_SKIP_EMOTE_AFTER_FLEE
Definition enums.h:4572
@ AI_FLAG_SKIP_IDLE_ANIM_AFTER_FLEE
Definition enums.h:4573
@ SOUND_BURROW_SURFACE
Definition enums.h:1117
@ SOUND_BURROW_DIG
Definition enums.h:1118
@ PARTNER_BOW
Definition enums.h:2894
@ NPC_FLAG_FLYING
Definition enums.h:3001
@ NPC_FLAG_COLLDING_FORWARD_WITH_WORLD
Definition enums.h:3012
@ NPC_FLAG_INVISIBLE
Definition enums.h:2999
@ NPC_FLAG_GROUNDED
Definition enums.h:3010
s32 Bytecode
Definition evt.h:7
#define ApiStatus_BLOCK
Definition evt.h:116
s32 evt_get_variable(Evt *script, Bytecode var)
Definition evt.c:1690
f32 fabsf(f32 f)
b32 is_point_outside_territory(s32 shape, f32 centerX, f32 centerZ, f32 pointX, f32 pointZ, f32 sizeX, f32 sizeZ)
Definition 23680.c:412
void ai_enemy_play_sound(Npc *npc, s32 arg1, s32 arg2)
Definition 23680.c:543
f32 dist2D(f32 ax, f32 ay, f32 bx, f32 by)
Definition 43F0.c:670
f32 get_xz_dist_to_player(f32, f32)
Definition 77480.c:960
f32 get_clamped_angle_diff(f32, f32)
Definition 43F0.c:606
enum TerritoryShape shape
Definition npc.h:201
s32 flags
Definition npc.h:295
VecXZi detectSize
Definition npc.h:216
enum TerritoryShape detectShape
Definition npc.h:217
s32 basic_ai_check_player_dist(EnemyDetectVolume *arg0, Enemy *arg1, f32 arg2, f32 arg3, b8 arg4)
Definition 23680.c:429
s16 npcID
Definition npc.h:300
Enemy * get_enemy(s32 npcID)
Looks for an enemy matching the specified npcID.
Definition npc.c:2540
VecXZi wanderSize
Definition npc.h:212
s8 aiSuspendTime
Definition npc.h:333
Npc * get_npc_unsafe(s32 npcID)
Definition npc.c:995
s16 detectFlags
Definition npc.h:207
s32 moveSpeedOverride
Definition npc.h:213
void AIStateHandler(Evt *script, MobileAISettings *settings, EnemyDetectVolume *territory)
Definition npc.h:292
enum TerritoryShape wanderShape
Definition npc.h:214
u32 aiFlags
Definition npc.h:332
void npc_move_heading(Npc *npc, f32 speed, f32 yaw)
Definition npc.c:986
s32 skipPlayerDetectChance
Definition npc.h:200
EnemyTerritoryWander wander
Definition npc.h:232
EnemyTerritory * territory
Definition npc.h:342
Definition npc.h:294
s16 collisionDiameter
s32 flags
AnimID curAnim
s16 collisionHeight
Vec3f pos
f32 moveSpeed
Vec3s homePos
s16 duration
PlayerStatus * gPlayerStatusPtr
PartnerStatus gPartnerStatus
Definition partners.c:42
Camera gCameras[4]
Definition cam_main.c:17
s16 gCurrentCamID
Definition cam_main.c:13