Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/behaviors/bobomb.inc.c
7861 views
1
// bobomb.c.inc
2
3
static struct ObjectHitbox sBobombHitbox = {
4
/* interactType: */ INTERACT_GRABBABLE,
5
/* downOffset: */ 0,
6
/* damageOrCoinValue: */ 0,
7
/* health: */ 0,
8
/* numLootCoins: */ 0,
9
/* radius: */ 65,
10
/* height: */ 113,
11
/* hurtboxRadius: */ 0,
12
/* hurtboxHeight: */ 0,
13
};
14
15
void bhv_bobomb_init(void) {
16
o->oGravity = 2.5;
17
o->oFriction = 0.8;
18
o->oBuoyancy = 1.3;
19
o->oInteractionSubtype = INT_SUBTYPE_KICKABLE;
20
}
21
22
void bobomb_spawn_coin(void) {
23
if (((o->oBehParams >> 8) & 0x1) == 0) {
24
obj_spawn_yellow_coins(o, 1);
25
o->oBehParams = 0x100;
26
set_object_respawn_info_bits(o, 1);
27
}
28
}
29
30
void bobomb_act_explode(void) {
31
struct Object *explosion;
32
33
if (configApplyBugFixes > 2)
34
cur_obj_become_intangible();
35
36
if (o->oTimer < 5)
37
cur_obj_scale(1.0 + (f32) o->oTimer / 5.0);
38
else {
39
explosion = spawn_object(o, MODEL_EXPLOSION, bhvExplosion);
40
explosion->oGraphYOffset += 100.0f;
41
42
bobomb_spawn_coin();
43
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
44
o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
45
}
46
}
47
48
49
void bobomb_check_interactions(void) {
50
obj_set_hitbox(o, &sBobombHitbox);
51
if ((o->oInteractStatus & INT_STATUS_INTERACTED) != 0)
52
{
53
if ((o->oInteractStatus & INT_STATUS_MARIO_KNOCKBACK_DMG) != 0)
54
{
55
o->oMoveAngleYaw = gMarioObject->header.gfx.angle[1];
56
o->oForwardVel = 25.0;
57
o->oVelY = 30.0;
58
o->oAction = BOBOMB_ACT_LAUNCHED;
59
}
60
61
if ((o->oInteractStatus & INT_STATUS_TOUCHED_BOB_OMB) != 0)
62
o->oAction = BOBOMB_ACT_EXPLODE;
63
64
o->oInteractStatus = 0;
65
}
66
67
if (obj_attack_collided_from_other_object(o) == 1)
68
o->oAction = BOBOMB_ACT_EXPLODE;
69
}
70
71
void bobomb_act_patrol(void) {
72
UNUSED s8 filler[4];
73
UNUSED s16 sp22;
74
s16 collisionFlags;
75
76
sp22 = o->header.gfx.animInfo.animFrame;
77
o->oForwardVel = 5.0;
78
79
collisionFlags = object_step();
80
if ((obj_return_home_if_safe(o, o->oHomeX, o->oHomeY, o->oHomeZ, 400) == 1)
81
&& (obj_check_if_facing_toward_angle(o->oMoveAngleYaw, o->oAngleToMario, 0x2000) == TRUE)) {
82
o->oBobombFuseLit = 1;
83
o->oAction = BOBOMB_ACT_CHASE_MARIO;
84
}
85
obj_check_floor_death(collisionFlags, sObjFloor);
86
}
87
88
void bobomb_act_chase_mario(void) {
89
UNUSED u8 filler[4];
90
s16 sp1a, collisionFlags;
91
92
sp1a = ++o->header.gfx.animInfo.animFrame;
93
o->oForwardVel = 20.0;
94
95
collisionFlags = object_step();
96
97
if (sp1a == 5 || sp1a == 16)
98
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
99
100
obj_turn_toward_object(o, gMarioObject, 16, 0x800);
101
obj_check_floor_death(collisionFlags, sObjFloor);
102
}
103
104
void bobomb_act_launched(void) {
105
s16 collisionFlags = 0;
106
107
if (configApplyBugFixes > 2)
108
cur_obj_become_intangible();
109
110
collisionFlags = object_step();
111
if ((collisionFlags & OBJ_COL_FLAG_GROUNDED) == OBJ_COL_FLAG_GROUNDED)
112
o->oAction = BOBOMB_ACT_EXPLODE; /* bit 0 */
113
}
114
115
void generic_bobomb_free_loop(void) {
116
switch (o->oAction) {
117
case BOBOMB_ACT_PATROL:
118
bobomb_act_patrol();
119
break;
120
121
case BOBOMB_ACT_LAUNCHED:
122
bobomb_act_launched();
123
break;
124
125
case BOBOMB_ACT_CHASE_MARIO:
126
bobomb_act_chase_mario();
127
break;
128
129
case BOBOMB_ACT_EXPLODE:
130
bobomb_act_explode();
131
break;
132
133
case BOBOMB_ACT_LAVA_DEATH:
134
if (obj_lava_death() == 1)
135
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
136
break;
137
138
case BOBOMB_ACT_DEATH_PLANE_DEATH:
139
o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
140
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
141
break;
142
}
143
144
bobomb_check_interactions();
145
146
if (o->oBobombFuseTimer >= 151)
147
o->oAction = 3;
148
}
149
150
void stationary_bobomb_free_loop(void) {
151
switch (o->oAction) {
152
case BOBOMB_ACT_LAUNCHED:
153
bobomb_act_launched();
154
break;
155
156
case BOBOMB_ACT_EXPLODE:
157
bobomb_act_explode();
158
break;
159
160
case BOBOMB_ACT_LAVA_DEATH:
161
if (obj_lava_death() == 1)
162
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
163
break;
164
165
case BOBOMB_ACT_DEATH_PLANE_DEATH:
166
o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
167
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
168
break;
169
}
170
171
bobomb_check_interactions();
172
173
if (o->oBobombFuseTimer >= 151)
174
o->oAction = 3;
175
}
176
177
void bobomb_free_loop(void) {
178
if (o->oBehParams2ndByte == BOBOMB_BP_STYPE_GENERIC)
179
generic_bobomb_free_loop();
180
else
181
stationary_bobomb_free_loop();
182
}
183
184
void bobomb_held_loop(void) {
185
o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
186
cur_obj_init_animation(1);
187
cur_obj_set_pos_relative(gMarioObject, 0, 60.0f, 100.0);
188
189
o->oBobombFuseLit = 1;
190
if (o->oBobombFuseTimer >= 151) {
191
//! Although the Bob-omb's action is set to explode when the fuse timer expires,
192
// bobomb_act_explode() will not execute until the bob-omb's held state changes.
193
// This allows the Bob-omb to be regrabbed indefinitely.
194
gMarioObject->oInteractStatus |= INT_STATUS_MARIO_DROP_OBJECT;
195
o->oAction = BOBOMB_ACT_EXPLODE;
196
if (configApplyBugFixes > 2)
197
bobomb_act_explode();
198
}
199
}
200
201
void bobomb_dropped_loop(void) {
202
cur_obj_get_dropped();
203
204
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
205
cur_obj_init_animation(0);
206
207
o->oHeldState = 0;
208
o->oAction = BOBOMB_ACT_PATROL;
209
}
210
211
void bobomb_thrown_loop(void) {
212
cur_obj_enable_rendering_2();
213
214
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
215
o->oHeldState = 0;
216
o->oFlags &= ~0x8; /* bit 3 */
217
o->oForwardVel = 25.0;
218
o->oVelY = 20.0;
219
o->oAction = BOBOMB_ACT_LAUNCHED;
220
}
221
222
// sp18 = blinkTimer
223
224
void curr_obj_random_blink(s32 *blinkTimer) {
225
if (*blinkTimer == 0) {
226
if ((s16)(random_float() * 100.0f) == 0) {
227
o->oAnimState = 1;
228
*blinkTimer = 1;
229
}
230
} else {
231
(*blinkTimer)++;
232
if (*blinkTimer >= 6)
233
o->oAnimState = 0;
234
if (*blinkTimer >= 11)
235
o->oAnimState = 1;
236
if (*blinkTimer >= 16) {
237
o->oAnimState = 0;
238
*blinkTimer = 0;
239
}
240
}
241
}
242
243
void bhv_bobomb_loop(void) {
244
s8 dustPeriodMinus1;
245
if (is_point_within_radius_of_mario(o->oPosX, o->oPosY, o->oPosZ, 4000) != 0) {
246
switch (o->oHeldState) {
247
case HELD_FREE:
248
bobomb_free_loop();
249
break;
250
251
case HELD_HELD:
252
bobomb_held_loop();
253
break;
254
255
case HELD_THROWN:
256
bobomb_thrown_loop();
257
break;
258
259
case HELD_DROPPED:
260
bobomb_dropped_loop();
261
break;
262
}
263
264
curr_obj_random_blink(&o->oBobombBlinkTimer);
265
266
if (o->oBobombFuseLit == 1) {
267
if (o->oBobombFuseTimer >= 121)
268
dustPeriodMinus1 = 1;
269
else
270
dustPeriodMinus1 = 7;
271
272
if ((dustPeriodMinus1 & o->oBobombFuseTimer)
273
== 0) /* oBobombFuseTimer % 2 or oBobombFuseTimer % 8 */
274
spawn_object(o, MODEL_SMOKE, bhvBobombFuseSmoke);
275
276
cur_obj_play_sound_1(SOUND_AIR_BOBOMB_LIT_FUSE);
277
278
o->oBobombFuseTimer++;
279
}
280
}
281
}
282
283
void bhv_bobomb_fuse_smoke_init(void) {
284
o->oPosX += (s32)(random_float() * 80.0f) - 40;
285
o->oPosY += (s32)(random_float() * 80.0f) + 60;
286
o->oPosZ += (s32)(random_float() * 80.0f) - 40;
287
cur_obj_scale(1.2f);
288
}
289
290
void bhv_bobomb_buddy_init(void) {
291
o->oGravity = 2.5;
292
o->oFriction = 0.8;
293
o->oBuoyancy = 1.3;
294
o->oInteractionSubtype = INT_SUBTYPE_NPC;
295
}
296
297
void bobomb_buddy_act_idle(void) {
298
UNUSED u8 filler[4];
299
s16 sp1a = o->header.gfx.animInfo.animFrame;
300
UNUSED s16 collisionFlags = 0;
301
302
o->oBobombBuddyPosXCopy = o->oPosX;
303
o->oBobombBuddyPosYCopy = o->oPosY;
304
o->oBobombBuddyPosZCopy = o->oPosZ;
305
306
collisionFlags = object_step();
307
308
if ((sp1a == 5) || (sp1a == 16))
309
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
310
311
if (o->oDistanceToMario < 1000.0f)
312
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oAngleToMario, 0x140);
313
314
if (o->oInteractStatus == INT_STATUS_INTERACTED)
315
o->oAction = BOBOMB_BUDDY_ACT_TURN_TO_TALK;
316
}
317
318
/**
319
* Function for the Bob-omb Buddy cannon guy.
320
* dialogFirstText is the first dialogID called when Bob-omb Buddy
321
* starts to talk to Mario to prepare the cannon(s) for him.
322
* Then the camera goes to the nearest cannon, to play the "prepare cannon" cutscene
323
* dialogSecondText is called after Bob-omb Buddy has the cannon(s) ready and
324
* then tells Mario that is "Ready for blastoff".
325
*/
326
void bobomb_buddy_cannon_dialog(s16 dialogFirstText, s16 dialogSecondText) {
327
struct Object *cannonClosed;
328
s16 buddyText, cutscene;
329
330
switch (o->oBobombBuddyCannonStatus) {
331
case BOBOMB_BUDDY_CANNON_UNOPENED:
332
buddyText = cutscene_object_with_dialog(CUTSCENE_DIALOG, o, dialogFirstText);
333
if (buddyText != 0) {
334
save_file_set_cannon_unlocked();
335
cannonClosed = cur_obj_nearest_object_with_behavior(bhvCannonClosed);
336
if (cannonClosed != 0)
337
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENING;
338
else
339
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_STOP_TALKING;
340
}
341
break;
342
343
case BOBOMB_BUDDY_CANNON_OPENING:
344
cannonClosed = cur_obj_nearest_object_with_behavior(bhvCannonClosed);
345
cutscene = cutscene_object(CUTSCENE_PREPARE_CANNON, cannonClosed);
346
if (cutscene == -1)
347
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENED;
348
break;
349
350
case BOBOMB_BUDDY_CANNON_OPENED:
351
buddyText = cutscene_object_with_dialog(CUTSCENE_DIALOG, o, dialogSecondText);
352
if (buddyText != 0)
353
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_STOP_TALKING;
354
break;
355
356
case BOBOMB_BUDDY_CANNON_STOP_TALKING:
357
set_mario_npc_dialog(MARIO_DIALOG_STOP);
358
359
o->activeFlags &= ~ACTIVE_FLAG_INITIATED_TIME_STOP;
360
o->oBobombBuddyHasTalkedToMario = BOBOMB_BUDDY_HAS_TALKED;
361
o->oInteractStatus = 0;
362
o->oAction = BOBOMB_BUDDY_ACT_IDLE;
363
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENED;
364
break;
365
}
366
}
367
368
void bobomb_buddy_act_talk(void) {
369
if (set_mario_npc_dialog(MARIO_DIALOG_LOOK_FRONT) == MARIO_DIALOG_STATUS_SPEAK) {
370
o->activeFlags |= ACTIVE_FLAG_INITIATED_TIME_STOP;
371
372
switch (o->oBobombBuddyRole) {
373
case BOBOMB_BUDDY_ROLE_ADVICE:
374
if (cutscene_object_with_dialog(CUTSCENE_DIALOG, o, o->oBehParams2ndByte)
375
!= BOBOMB_BUDDY_BP_STYPE_GENERIC) {
376
set_mario_npc_dialog(MARIO_DIALOG_STOP);
377
378
o->activeFlags &= ~ACTIVE_FLAG_INITIATED_TIME_STOP;
379
o->oBobombBuddyHasTalkedToMario = BOBOMB_BUDDY_HAS_TALKED;
380
o->oInteractStatus = 0;
381
o->oAction = BOBOMB_BUDDY_ACT_IDLE;
382
}
383
break;
384
385
case BOBOMB_BUDDY_ROLE_CANNON:
386
if (gCurrCourseNum == COURSE_BOB)
387
bobomb_buddy_cannon_dialog(DIALOG_004, DIALOG_105);
388
else
389
bobomb_buddy_cannon_dialog(DIALOG_047, DIALOG_106);
390
break;
391
}
392
}
393
}
394
395
void bobomb_buddy_act_turn_to_talk(void) {
396
s16 sp1e = o->header.gfx.animInfo.animFrame;
397
if ((sp1e == 5) || (sp1e == 16))
398
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
399
400
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oAngleToMario, 0x1000);
401
if ((s16) o->oMoveAngleYaw == (s16) o->oAngleToMario)
402
o->oAction = BOBOMB_BUDDY_ACT_TALK;
403
404
cur_obj_play_sound_2(SOUND_ACTION_READ_SIGN);
405
}
406
407
void bobomb_buddy_actions(void) {
408
switch (o->oAction) {
409
case BOBOMB_BUDDY_ACT_IDLE:
410
bobomb_buddy_act_idle();
411
break;
412
413
case BOBOMB_BUDDY_ACT_TURN_TO_TALK:
414
bobomb_buddy_act_turn_to_talk();
415
break;
416
417
case BOBOMB_BUDDY_ACT_TALK:
418
bobomb_buddy_act_talk();
419
break;
420
}
421
422
set_object_visibility(o, 3000);
423
}
424
425
void bhv_bobomb_buddy_loop(void) {
426
bobomb_buddy_actions();
427
428
curr_obj_random_blink(&o->oBobombBuddyBlinkTimer);
429
430
o->oInteractStatus = 0;
431
}
432
433