Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
BulletBillAI.inc.c
Go to the documentation of this file.
1#include "common.h"
2#include "effects.h"
3
4#define FIRST_BULLET_NPCID 50
5#define LAST_BULLET_NPCID 59
6
13
20
21#define AI_VAR_BULLET_BLASTER varTable[1] // npcID of bullet's blaster
22#define AI_VAR_BULLET_RANGE varTable[2] // limiting x position of bullets
23
24#define AI_VAR_BLASTER_BULLET varTable[0] // npcID of blaster's last bullet
25#define AI_VAR_BLASTER_RANGE varTable[1] // limiting x position of bullets
26
27static s32 N(BillBlasterAI_GetIdleBulletNpcID)(void) {
28 s32 i;
29
30 for (i = FIRST_BULLET_NPCID; i <= LAST_BULLET_NPCID; i++) {
31 if (get_enemy(i)->VAR_PROJECTILE_HITBOX_STATE == PROJECTILE_HITBOX_STATE_NONE) {
32 return i;
33 }
34 }
35
36 return -1;
37}
38
39API_CALLABLE(N(BulletBillAI_Main)) {
40 Enemy* enemy = script->owner1.enemy;
41 Npc* npc = get_npc_unsafe(enemy->npcID);
42 MobileAISettings* aiSettings = (MobileAISettings*)evt_get_variable(script, *script->ptrReadPos);
43 s32 hitDetected = FALSE;
44 s32 done = FALSE;
45 Npc* blasterNpc;
46 f32 nextX, nextZ;
47 f32 deltaY;
48
49 if (isInitialCall || enemy->VAR_PROJECTILE_HITBOX_STATE == PROJECTILE_HITBOX_STATE_DONE) {
50 script->AI_TEMP_STATE = AI_STATE_BULLET_INIT;
51 npc->duration = 0;
55 enemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_NONE;
56 enemy->AI_VAR_BULLET_BLASTER = -1;
57 }
58
59 if (enemy->aiFlags & AI_FLAG_SUSPEND) {
60 if (enemy->aiSuspendTime != 0) {
61 return ApiStatus_BLOCK;
62 }
63 enemy->aiFlags &= ~AI_FLAG_SUSPEND;
64 }
65
66 switch (script->AI_TEMP_STATE) {
68 npc->pos.x = NPC_DISPOSE_POS_X;
69 npc->pos.y = NPC_DISPOSE_POS_Y;
70 npc->pos.z = NPC_DISPOSE_POS_Z;
71 npc->rot.y = 0.0f;
72 npc->duration = 0;
74 npc->flags &= ~NPC_FLAG_DONT_UPDATE_SHADOW_Y;
76 enemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_NONE;
77 script->AI_TEMP_STATE = AI_STATE_BULLET_READY;
78 // fallthrough
80 if (enemy->VAR_PROJECTILE_HITBOX_STATE != PROJECTILE_HITBOX_STATE_PRE) {
81 break;
82 }
83 enemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_ACTIVE;
84 blasterNpc = get_npc_unsafe(get_enemy(enemy->AI_VAR_BULLET_BLASTER)->npcID);
85 npc->flags &= ~NPC_FLAG_INVISIBLE;
86 npc->pos.x = blasterNpc->pos.x;
87 npc->pos.y = blasterNpc->pos.y + 11.0;
88 npc->pos.z = blasterNpc->pos.z + 1.0;
89 npc->yaw = blasterNpc->yaw;
90 npc->moveSpeed = aiSettings->chaseSpeed;
92 add_vec2D_polar(&npc->pos.x, &npc->pos.z, 25.0f, npc->yaw);
93 if (npc->yaw < 180.0f) {
94 npc->renderYaw = 180.0f;
95 } else {
96 npc->renderYaw = 0.0f;
97 }
99 npc->duration = 300;
102 script->AI_TEMP_STATE = AI_STATE_BULLET_FIRED;
103 // fallthrough
105 deltaY = (npc->pos.y - gPlayerStatusPtr->pos.y);
106 if ((deltaY > 190.0) || (deltaY < -120.0)) {
107 done = TRUE;
108 break;
109 }
110
111 nextX = npc->pos.x;
112 nextZ = npc->pos.z;
113 add_vec2D_polar(&nextX, &nextZ, npc->moveSpeed, npc->yaw);
114 if (npc->yaw < 180.0f) {
115 if (enemy->AI_VAR_BULLET_RANGE <= nextX) {
116 hitDetected = TRUE;
117 }
118 } else {
119 if (nextX <= enemy->AI_VAR_BULLET_RANGE) {
120 hitDetected = TRUE;
121 }
122 }
123
124 if (hitDetected) {
127 fx_ring_blast(0, npc->pos.x, npc->pos.y + 5.0f, npc->pos.z + 1.0f, 0.05f, 20);
128 fx_smoke_burst(0, npc->pos.x, npc->pos.y + 5.0f, npc->pos.z + 0.0f, 1.2f, 25);
129 npc->duration = 1;
130 script->AI_TEMP_STATE = AI_STATE_BULLET_HIT;
131 } else {
132 npc_move_heading(npc, npc->moveSpeed, npc->yaw);
133 }
134
135 if (script->AI_TEMP_STATE != AI_STATE_BULLET_HIT) {
136 break;
137 }
138 // fallthrough
140 npc->duration--;
141 if (npc->duration <= 0) {
142 done = TRUE;
143 }
144 break;
145 }
146 if (done != 0) {
147 enemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_DONE;
148 }
149
150 return ApiStatus_BLOCK;
151}
152
153API_CALLABLE(N(BillBlasterAI_Main)) {
154 Bytecode* args = script->ptrReadPos;
155 Enemy* enemy = script->owner1.enemy;
156 Npc* npc = get_npc_unsafe(enemy->npcID);
157 MobileAISettings* aiSettings = (MobileAISettings*)evt_get_variable(script, *args++);
158 s32 bulletNpcID;
159 Enemy* bulletEnemy;
160 f32 deltaY;
161
162 if (isInitialCall) {
163 script->AI_TEMP_STATE = AI_STATE_BLASTER_INIT;
164 npc->duration = 30;
168 }
169
170 deltaY = npc->pos.y - gPlayerStatusPtr->pos.y;
171 if ((deltaY > 190.0) || (deltaY < -80.0)) {
172 return ApiStatus_BLOCK;
173 }
174
175 if (enemy->aiFlags & AI_FLAG_SUSPEND) {
177 if (enemy->aiSuspendTime != 0) {
178 return ApiStatus_BLOCK;
179 }
180 enemy->aiFlags &= ~AI_FLAG_SUSPEND;
181 }
182
183 switch (script->AI_TEMP_STATE) {
185 npc->duration--;
186 if (npc->duration <= 0) {
187 script->AI_TEMP_STATE = AI_STATE_BLASTER_PREPARE;
188 }
189 break;
191 bulletNpcID = N(BillBlasterAI_GetIdleBulletNpcID)();
192 enemy->AI_VAR_BLASTER_BULLET = bulletNpcID;
193 if (bulletNpcID > 0) {
194 bulletEnemy = get_enemy(bulletNpcID);
195 bulletEnemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_INIT;
196 bulletEnemy->AI_VAR_BULLET_BLASTER = enemy->npcID;
197 bulletEnemy->AI_VAR_BULLET_RANGE = enemy->AI_VAR_BLASTER_RANGE;
199 npc->duration = 10;
200 script->AI_TEMP_STATE = AI_STATE_BLASTER_FIRE;
201 } else {
202 npc->duration = (rand_int(1000) % 60) + 40;
203 script->AI_TEMP_STATE =AI_STATE_BLASTER_INIT;
204 }
205 break;
207 npc->duration--;
208 if (npc->duration > 0) {
209 break;
210 }
212 bulletEnemy = get_enemy(enemy->AI_VAR_BLASTER_BULLET);
213 bulletEnemy->VAR_PROJECTILE_HITBOX_STATE = PROJECTILE_HITBOX_STATE_PRE;
215 npc->duration = 5;
216 script->AI_TEMP_STATE = AI_STATE_BLASTER_COOLDOWN;
217 // fallthrough
219 npc->duration--;
220 if (npc->duration <= 0) {
221 npc->duration = (rand_int(1000) % 60) + 40;
222 script->AI_TEMP_STATE = AI_STATE_BLASTER_INIT;
223 }
224 break;
225 }
226 return ApiStatus_BLOCK;
227}
AiStateBillBlaster
@ AI_STATE_BLASTER_COOLDOWN
@ AI_STATE_BLASTER_FIRE
@ AI_STATE_BLASTER_INIT
@ AI_STATE_BLASTER_PREPARE
#define AI_VAR_BULLET_RANGE
#define LAST_BULLET_NPCID
#define FIRST_BULLET_NPCID
AiStateBulletBill
@ AI_STATE_BULLET_INIT
@ AI_STATE_BULLET_FIRED
@ AI_STATE_BULLET_READY
@ AI_STATE_BULLET_HIT
#define rand_int
@ ENEMY_ANIM_INDEX_MELEE_PRE
Definition enums.h:3434
@ ENEMY_ANIM_INDEX_IDLE
Definition enums.h:3426
@ ENEMY_ANIM_INDEX_HIT
Definition enums.h:3433
@ ENEMY_ANIM_INDEX_RUN
Definition enums.h:3428
@ ENEMY_ANIM_INDEX_CHASE
Definition enums.h:3429
@ AI_FLAG_SUSPEND
Definition enums.h:4571
@ AI_FLAG_SKIP_EMOTE_AFTER_FLEE
Definition enums.h:4572
@ ENEMY_FLAG_ACTIVE_WHILE_OFFSCREEN
Definition enums.h:4542
@ PROJECTILE_HITBOX_STATE_ACTIVE
Definition enums.h:4654
@ PROJECTILE_HITBOX_STATE_NONE
Definition enums.h:4651
@ PROJECTILE_HITBOX_STATE_INIT
Definition enums.h:4652
@ PROJECTILE_HITBOX_STATE_DONE
Definition enums.h:4656
@ PROJECTILE_HITBOX_STATE_PRE
Definition enums.h:4653
@ SOUND_SEQ_BULLET_BILL_EXPLODE
Definition enums.h:1723
@ SOUND_BLASTER_FIRE
Definition enums.h:1121
@ SOUND_PARAM_MORE_QUIET
Definition enums.h:1746
@ NPC_FLAG_DIRTY_SHADOW
Definition enums.h:3014
@ NPC_FLAG_INVISIBLE
Definition enums.h:2999
@ NPC_FLAG_IGNORE_CAMERA_FOR_YAW
Definition enums.h:3016
@ NPC_FLAG_DONT_UPDATE_SHADOW_Y
Definition enums.h:3008
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
void ai_enemy_play_sound(Npc *npc, s32 arg1, s32 arg2)
Definition 23680.c:543
void add_vec2D_polar(f32 *x, f32 *y, f32 r, f32 theta)
Definition 43F0.c:685
f32 chaseSpeed
Definition npc.h:99
s32 flags
Definition npc.h:295
s16 npcID
Definition npc.h:300
Enemy * get_enemy(s32 npcID)
Looks for an enemy matching the specified npcID.
Definition npc.c:2540
void enable_npc_shadow(Npc *npc)
Definition npc.c:1027
s32 * animList
Definition npc.h:341
void disable_npc_shadow(Npc *npc)
Definition npc.c:1037
s8 aiSuspendTime
Definition npc.h:333
Npc * get_npc_unsafe(s32 npcID)
Definition npc.c:995
u32 aiFlags
Definition npc.h:332
void npc_move_heading(Npc *npc, f32 speed, f32 yaw)
Definition npc.c:986
Definition npc.h:294
#define NPC_DISPOSE_POS_Z
Definition macros.h:161
#define NPC_DISPOSE_POS_X
Definition macros.h:159
#define NPC_DISPOSE_POS_Y
Definition macros.h:160
s32 flags
f32 renderYaw
AnimID curAnim
Vec3f rot
Vec3f pos
f32 moveSpeed
s16 duration
PlayerStatus * gPlayerStatusPtr