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