boo.inc.c (23455B)
1 // boo.inc.c 2 3 #define SPAWN_CASTLE_BOO_STAR_REQUIREMENT 12 4 5 static struct ObjectHitbox sBooGivingStarHitbox = { 6 /* interactType: */ 0, 7 /* downOffset: */ 0, 8 /* damageOrCoinValue: */ 3, 9 /* health: */ 3, 10 /* numLootCoins: */ 0, 11 /* radius: */ 140, 12 /* height: */ 80, 13 /* hurtboxRadius: */ 40, 14 /* hurtboxHeight: */ 60, 15 }; 16 17 // Relative positions 18 static s16 sCourtyardBooTripletPositions[][3] = { 19 { 0, 50, 0 }, 20 { 210, 110, 210 }, 21 { -210, 70, -210 }, 22 }; 23 24 static void boo_stop(void) { 25 o->oForwardVel = 0.0f; 26 o->oVelY = 0.0f; 27 o->oGravity = 0.0f; 28 } 29 30 void bhv_boo_init(void) { 31 o->oBooInitialMoveYaw = o->oMoveAngleYaw; 32 } 33 34 static s32 boo_should_be_stopped(void) { 35 if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo) || cur_obj_has_behavior(bhvMerryGoRoundBoo)) { 36 if (!gMarioOnMerryGoRound) { 37 return TRUE; 38 } else { 39 return FALSE; 40 } 41 } else { 42 if (o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) { 43 return TRUE; 44 } 45 46 if (o->oRoom == 10 && (gTimeStopState & TIME_STOP_MARIO_OPENED_DOOR)) { 47 return TRUE; 48 } 49 } 50 51 return FALSE; 52 } 53 54 static s32 boo_should_be_active(void) { 55 f32 activationRadius; 56 57 if (cur_obj_has_behavior(bhvBalconyBigBoo)) { 58 activationRadius = 5000.0f; 59 } else { 60 activationRadius = 1500.0f; 61 } 62 63 if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo) || cur_obj_has_behavior(bhvMerryGoRoundBoo)) { 64 if (gMarioOnMerryGoRound == TRUE) { 65 return TRUE; 66 } else { 67 return FALSE; 68 } 69 } else if (o->oRoom == -1) { 70 if (o->oDistanceToMario < activationRadius) { 71 return TRUE; 72 } 73 } else if (!boo_should_be_stopped()) { 74 if (o->oDistanceToMario < activationRadius 75 && (o->oRoom == gMarioCurrentRoom || gMarioCurrentRoom == 0)) { 76 return TRUE; 77 } 78 } 79 80 return FALSE; 81 } 82 83 void bhv_courtyard_boo_triplet_init(void) { 84 s32 i; 85 86 if (gHudDisplay.stars < SPAWN_CASTLE_BOO_STAR_REQUIREMENT) { 87 obj_mark_for_deletion(o); 88 } else { 89 for (i = 0; i < 3; i++) { 90 struct Object *boo = spawn_object_relative( 91 BOO_BP_GENERIC, sCourtyardBooTripletPositions[i][0], sCourtyardBooTripletPositions[i][1], 92 sCourtyardBooTripletPositions[i][2], o, MODEL_BOO, bhvGhostHuntBoo); 93 94 boo->oMoveAngleYaw = random_u16(); 95 } 96 } 97 } 98 99 static void boo_approach_target_opacity_and_update_scale(void) { 100 f32 scale; 101 102 if (o->oBooTargetOpacity != o->oOpacity) { 103 if (o->oBooTargetOpacity > o->oOpacity) { 104 o->oOpacity += 20; 105 106 if (o->oBooTargetOpacity < o->oOpacity) { 107 o->oOpacity = o->oBooTargetOpacity; 108 } 109 } else { 110 o->oOpacity -= 20; 111 112 if (o->oBooTargetOpacity > o->oOpacity) { 113 o->oOpacity = o->oBooTargetOpacity; 114 } 115 } 116 } 117 118 scale = (o->oOpacity / 255.0f * 0.4 + 0.6) * o->oBooBaseScale; 119 obj_scale(o, scale); // why no cur_obj_scale? was cur_obj_scale written later? 120 } 121 122 static void boo_oscillate(s32 ignoreOpacity) { 123 o->oFaceAnglePitch = sins(o->oBooOscillationTimer) * 0x400; 124 125 if (o->oOpacity == 255 || ignoreOpacity == TRUE) { 126 o->header.gfx.scale[0] = sins(o->oBooOscillationTimer) * 0.08 + o->oBooBaseScale; 127 o->header.gfx.scale[1] = -sins(o->oBooOscillationTimer) * 0.08 + o->oBooBaseScale; 128 o->header.gfx.scale[2] = o->header.gfx.scale[0]; 129 o->oGravity = sins(o->oBooOscillationTimer) * o->oBooBaseScale; 130 o->oBooOscillationTimer += 0x400; 131 } 132 } 133 134 static s32 boo_vanish_or_appear(void) { 135 s16 relativeAngleToMario = abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw); 136 s16 relativeMarioFaceAngle = abs_angle_diff(o->oMoveAngleYaw, gMarioObject->oFaceAngleYaw); 137 // magic? 138 s16 relativeAngleToMarioThreshhold = 0x1568; 139 s16 relativeMarioFaceAngleThreshhold = 0x6B58; 140 s32 doneAppearing = FALSE; 141 142 o->oVelY = 0.0f; 143 144 if (relativeAngleToMario > relativeAngleToMarioThreshhold 145 || relativeMarioFaceAngle < relativeMarioFaceAngleThreshhold) { 146 if (o->oOpacity == 40) { 147 o->oBooTargetOpacity = 255; 148 cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG); 149 } 150 151 if (o->oOpacity > 180) { 152 doneAppearing = TRUE; 153 } 154 } else if (o->oOpacity == 255) { 155 o->oBooTargetOpacity = 40; 156 } 157 158 return doneAppearing; 159 } 160 161 static void boo_set_move_yaw_for_during_hit(s32 hurt) { 162 cur_obj_become_intangible(); 163 164 o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; 165 o->oBooMoveYawBeforeHit = (f32) o->oMoveAngleYaw; 166 167 if (hurt) { 168 o->oBooMoveYawDuringHit = gMarioObject->oMoveAngleYaw; 169 } else if (coss((s16) o->oMoveAngleYaw - (s16) o->oAngleToMario) < 0.0f) { 170 o->oBooMoveYawDuringHit = o->oMoveAngleYaw; 171 } else { 172 o->oBooMoveYawDuringHit = (s16)(o->oMoveAngleYaw + 0x8000); 173 } 174 } 175 176 static void boo_move_during_hit(s32 roll, f32 fVel) { 177 // Boos seem to have been supposed to oscillate up then down then back again 178 // when hit. However it seems the programmers forgot to scale the cosine, 179 // so the Y velocity goes from 1 to -1 and back to 1 over 32 frames. 180 // This is such a small change that the Y position only changes by 5 units. 181 // It's completely unnoticable in-game. 182 s32 oscillationVel = o->oTimer * 0x800 + 0x800; 183 184 o->oForwardVel = fVel; 185 o->oVelY = coss(oscillationVel); 186 o->oMoveAngleYaw = o->oBooMoveYawDuringHit; 187 188 if (roll) { 189 o->oFaceAngleYaw += sBooHitRotations[o->oTimer]; 190 o->oFaceAngleRoll += sBooHitRotations[o->oTimer]; 191 } 192 } 193 194 static void big_boo_shake_after_hit(void) { 195 // Oscillate yaw 196 s32 oscillationVel = o->oTimer * 0x2000 - 0x3E000; 197 o->oFaceAngleYaw += coss(oscillationVel) * 0x400; 198 } 199 200 static void boo_reset_after_hit(void) { 201 o->oMoveAngleYaw = o->oBooMoveYawBeforeHit; 202 o->oFlags |= OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; 203 o->oInteractStatus = 0; 204 } 205 206 // called iff boo/big boo/cage boo is in action 2, which only occurs if it was non-attack-ly interacted with/bounced on? 207 static s32 boo_update_after_bounced_on(f32 a0) { 208 boo_stop(); 209 210 if (o->oTimer == 0) { 211 boo_set_move_yaw_for_during_hit(FALSE); 212 } 213 214 if (o->oTimer < 32) { 215 boo_move_during_hit(FALSE, sBooHitRotations[o->oTimer] / 5000.0f * a0); 216 } else { 217 cur_obj_become_tangible(); 218 boo_reset_after_hit(); 219 o->oAction = 1; 220 221 return TRUE; 222 } 223 224 return FALSE; 225 } 226 227 // called iff big boo nonlethally hit 228 static s32 big_boo_update_during_nonlethal_hit(f32 a0) { 229 boo_stop(); 230 231 if (o->oTimer == 0) { 232 boo_set_move_yaw_for_during_hit(TRUE); 233 } 234 235 if (o->oTimer < 32) { 236 boo_move_during_hit(TRUE, sBooHitRotations[o->oTimer] / 5000.0f * a0); 237 } else if (o->oTimer < 48) { 238 big_boo_shake_after_hit(); 239 } else { 240 cur_obj_become_tangible(); 241 boo_reset_after_hit(); 242 o->oAction = 1; 243 244 return TRUE; 245 } 246 247 return FALSE; 248 } 249 250 // called every frame once mario lethally hits the boo until the boo is deleted, 251 // returns whether death is complete 252 static s32 boo_update_during_death(void) { 253 if (o->oTimer == 0) { 254 o->oForwardVel = 40.0f; 255 o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw; 256 o->oBooDeathStatus = BOO_DEATH_STATUS_DYING; 257 o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; 258 } else { 259 if (o->oTimer == 5) { 260 o->oBooTargetOpacity = 0; 261 } 262 263 if (o->oTimer > 30 || o->oMoveFlags & OBJ_MOVE_HIT_WALL) { 264 spawn_mist_particles(); 265 o->oBooDeathStatus = BOO_DEATH_STATUS_DEAD; 266 267 if (o->oBooParentBigBoo != NULL) { 268 struct Object *parentBigBoo = o->oBooParentBigBoo; 269 270 #ifndef VERSION_JP 271 if (!cur_obj_has_behavior(bhvBoo)) { 272 #endif 273 parentBigBoo->oBigBooNumMinionBoosKilled++; 274 #ifndef VERSION_JP 275 } 276 #endif 277 } 278 279 return TRUE; 280 } 281 } 282 283 o->oVelY = 5.0f; 284 o->oFaceAngleRoll += 0x800; 285 o->oFaceAngleYaw += 0x800; 286 287 return FALSE; 288 } 289 290 static s32 obj_has_attack_type(u32 attackType) { 291 if ((o->oInteractStatus & INT_STATUS_ATTACK_MASK) == attackType) { 292 return TRUE; 293 } else { 294 return FALSE; 295 } 296 } 297 298 static s32 boo_get_attack_status(void) { 299 s32 attackStatus = BOO_NOT_ATTACKED; 300 301 if (o->oInteractStatus & INT_STATUS_INTERACTED) { 302 if ((o->oInteractStatus & INT_STATUS_WAS_ATTACKED) 303 && !obj_has_attack_type(ATTACK_FROM_ABOVE)) { 304 cur_obj_become_intangible(); 305 306 o->oInteractStatus = 0; 307 308 cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_SHORT); 309 310 attackStatus = BOO_ATTACKED; 311 } else { 312 cur_obj_play_sound_2(SOUND_OBJ_BOO_BOUNCE_TOP); 313 314 o->oInteractStatus = 0; 315 316 attackStatus = BOO_BOUNCED_ON; 317 } 318 } 319 320 return attackStatus; 321 } 322 323 // boo idle/chasing movement? 324 static void boo_chase_mario(f32 a0, s16 turnSpeed, f32 velMultiplier) { 325 f32 dy; 326 s16 targetYaw; 327 328 if (boo_vanish_or_appear()) { 329 o->oInteractType = INTERACT_BOUNCE_TOP; 330 331 if (cur_obj_lateral_dist_from_mario_to_home() > 1500.0f) { 332 targetYaw = cur_obj_angle_to_home(); 333 } else { 334 targetYaw = o->oAngleToMario; 335 } 336 337 cur_obj_rotate_yaw_toward(targetYaw, turnSpeed); 338 o->oVelY = 0.0f; 339 340 if (!mario_is_in_air_action()) { 341 dy = o->oPosY - gMarioObject->oPosY; 342 if (a0 < dy && dy < 500.0f) { 343 o->oVelY = increment_velocity_toward_range( 344 o->oPosY, gMarioObject->oPosY + 50.0f, 10.0f, 2.0f); 345 } 346 } 347 348 cur_obj_set_vel_from_mario_vel(10.0f - o->oBooNegatedAggressiveness, velMultiplier); 349 350 if (o->oForwardVel != 0.0f) { 351 boo_oscillate(FALSE); 352 } 353 } else { 354 o->oInteractType = 0; 355 // why is boo_stop not used here 356 o->oForwardVel = 0.0f; 357 o->oVelY = 0.0f; 358 o->oGravity = 0.0f; 359 } 360 } 361 362 static void boo_act_0(void) { 363 o->activeFlags |= ACTIVE_FLAG_MOVE_THROUGH_GRATE; 364 365 if (o->oBhvParams2ndByte == BOO_BP_MERRY_GO_ROUND) { 366 o->oRoom = 10; 367 } 368 369 cur_obj_set_pos_to_home(); 370 o->oMoveAngleYaw = o->oBooInitialMoveYaw; 371 boo_stop(); 372 373 o->oBooParentBigBoo = cur_obj_nearest_object_with_behavior(bhvGhostHuntBigBoo); 374 o->oBooBaseScale = 1.0f; 375 o->oBooTargetOpacity = 255; 376 377 if (boo_should_be_active()) { 378 // Condition is met if the object is bhvBalconyBigBoo or bhvMerryGoRoundBoo 379 if (o->oBhvParams2ndByte == BOO_BP_MERRY_GO_ROUND) { 380 o->oBooParentBigBoo = NULL; 381 o->oAction = 5; 382 } else { 383 o->oAction = 1; 384 } 385 } 386 } 387 388 static void boo_act_5(void) { 389 if (o->oTimer < 30) { 390 o->oVelY = 0.0f; 391 o->oForwardVel = 13.0f; 392 boo_oscillate(FALSE); 393 o->oWallHitboxRadius = 0.0f; 394 } else { 395 o->oAction = 1; 396 o->oWallHitboxRadius = 30.0f; 397 } 398 } 399 400 static void boo_act_1(void) { 401 s32 attackStatus; 402 403 if (o->oTimer == 0) { 404 o->oBooNegatedAggressiveness = -random_float() * 5.0f; 405 o->oBooTurningSpeed = (s32)(random_float() * 128.0f); 406 } 407 408 boo_chase_mario(-100.0f, o->oBooTurningSpeed + 0x180, 0.5f); 409 410 attackStatus = boo_get_attack_status(); 411 412 if (boo_should_be_stopped()) { 413 o->oAction = 0; 414 } 415 416 if (attackStatus == BOO_BOUNCED_ON) { 417 o->oAction = 2; 418 } 419 420 if (attackStatus == BOO_ATTACKED) { 421 o->oAction = 3; 422 } 423 424 if (attackStatus == BOO_ATTACKED) { 425 create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); 426 } 427 } 428 429 static void boo_act_2(void) { 430 if (boo_update_after_bounced_on(20.0f)) { 431 o->oAction = 1; 432 } 433 } 434 435 static void boo_act_3(void) { 436 if (boo_update_during_death()) { 437 if (o->oBhvParams2ndByte != BOO_BP_GHOST_HUNT) { 438 obj_mark_for_deletion(o); 439 } else { 440 o->oAction = 4; 441 cur_obj_disable(); 442 } 443 } 444 } 445 446 // Called when a Go on a Ghost Hunt boo dies 447 static void boo_act_4(void) { 448 s32 dialogID; 449 450 // If there are no remaining "minion" boos, show the dialog of the Big Boo 451 if (cur_obj_nearest_object_with_behavior(bhvGhostHuntBoo) == NULL) { 452 dialogID = DIALOG_108; 453 } else { 454 dialogID = DIALOG_107; 455 } 456 457 if (cur_obj_update_dialog(MARIO_DIALOG_LOOK_UP, DIALOG_FLAG_TEXT_DEFAULT, dialogID, 0)) { 458 create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); 459 obj_mark_for_deletion(o); 460 461 if (dialogID == DIALOG_108) { // If the Big Boo should spawn, play the jingle 462 play_puzzle_jingle(); 463 } 464 } 465 } 466 467 static void (*sBooActions[])(void) = { 468 boo_act_0, 469 boo_act_1, 470 boo_act_2, 471 boo_act_3, 472 boo_act_4, 473 boo_act_5, 474 }; 475 476 void bhv_boo_loop(void) { 477 //PARTIAL_UPDATE 478 479 cur_obj_update_floor_and_walls(); 480 cur_obj_call_action_function(sBooActions); 481 cur_obj_move_standard(78); 482 boo_approach_target_opacity_and_update_scale(); 483 484 if (obj_has_behavior(o->parentObj, bhvMerryGoRoundBooManager) 485 && o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { 486 o->parentObj->oMerryGoRoundBooManagerNumBoosKilled++; 487 } 488 489 o->oInteractStatus = 0; 490 } 491 492 static void big_boo_act_0(void) { 493 if (cur_obj_has_behavior(bhvBalconyBigBoo)) { 494 obj_set_secondary_camera_focus(); 495 // number of killed boos set > 5 so that boo always loads 496 // redundant? this is also done in behavior_data.s 497 o->oBigBooNumMinionBoosKilled = 10; 498 } 499 500 o->oBooParentBigBoo = NULL; 501 502 if (boo_should_be_active() 503 #ifndef VERSION_JP 504 && o->oBigBooNumMinionBoosKilled >= gDebugInfo[DEBUG_PAGE_ENEMYINFO][0] + 5 505 #else 506 && o->oBigBooNumMinionBoosKilled >= 5 507 #endif 508 ) { 509 o->oAction = 1; 510 511 cur_obj_set_pos_to_home(); 512 o->oMoveAngleYaw = o->oBooInitialMoveYaw; 513 514 cur_obj_unhide(); 515 516 o->oBooTargetOpacity = 255; 517 o->oBooBaseScale = 3.0f; 518 o->oHealth = 3; 519 520 cur_obj_scale(3.0f); 521 cur_obj_become_tangible(); 522 } else { 523 cur_obj_hide(); 524 cur_obj_become_intangible(); 525 boo_stop(); 526 } 527 } 528 529 static void big_boo_act_1(void) { 530 s32 attackStatus; 531 s16 turnSpeed; 532 f32 velMultiplier; 533 534 if (o->oHealth == 3) { 535 turnSpeed = 0x180; velMultiplier = 0.5f; 536 } else if (o->oHealth == 2) { 537 turnSpeed = 0x240; velMultiplier = 0.6f; 538 } else { 539 turnSpeed = 0x300; velMultiplier = 0.8f; 540 } 541 542 boo_chase_mario(-100.0f, turnSpeed, velMultiplier); 543 544 attackStatus = boo_get_attack_status(); 545 546 // redundant; this check is in boo_should_be_stopped 547 if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo)) { 548 if (!gMarioOnMerryGoRound) { 549 o->oAction = 0; 550 } 551 } else if (boo_should_be_stopped()) { 552 o->oAction = 0; 553 } 554 555 if (attackStatus == BOO_BOUNCED_ON) { 556 o->oAction = 2; 557 } 558 559 if (attackStatus == BOO_ATTACKED) { 560 o->oAction = 3; 561 } 562 563 if (attackStatus == BOO_ATTACKED) { 564 create_sound_spawner(SOUND_OBJ_THWOMP); 565 } 566 } 567 568 static void big_boo_act_2(void) { 569 if (boo_update_after_bounced_on(20.0f)) { 570 o->oAction = 1; 571 } 572 } 573 574 static void big_boo_spawn_ghost_hunt_star(void) { 575 spawn_default_star(980.0f, 1100.0f, 250.0f); 576 } 577 578 static void big_boo_spawn_balcony_star(void) { 579 spawn_default_star(700.0f, 3200.0f, 1900.0f); 580 } 581 582 static void big_boo_spawn_merry_go_round_star(void) { 583 struct Object *merryGoRound; 584 585 spawn_default_star(-1600.0f, -2100.0f, 205.0f); 586 587 merryGoRound = cur_obj_nearest_object_with_behavior(bhvMerryGoRound); 588 589 if (merryGoRound != NULL) { 590 merryGoRound->oMerryGoRoundStopped = TRUE; 591 } 592 } 593 594 static void big_boo_act_3(void) { 595 if (o->oTimer == 0) { 596 o->oHealth--; 597 } 598 599 if (o->oHealth == 0) { 600 if (boo_update_during_death()) { 601 cur_obj_disable(); 602 603 o->oAction = 4; 604 605 obj_set_angle(o, 0, 0, 0); 606 607 if (o->oBhvParams2ndByte == BIG_BOO_BP_GHOST_HUNT) { 608 big_boo_spawn_ghost_hunt_star(); 609 } else if (o->oBhvParams2ndByte == BIG_BOO_BP_MERRY_GO_ROUND) { 610 big_boo_spawn_merry_go_round_star(); 611 } else { // BIG_BOO_BP_BALCONY 612 big_boo_spawn_balcony_star(); 613 } 614 } 615 } else { 616 if (o->oTimer == 0) { 617 spawn_mist_particles(); 618 o->oBooBaseScale -= 0.5; 619 } 620 621 if (big_boo_update_during_nonlethal_hit(40.0f)) { 622 o->oAction = 1; 623 } 624 } 625 } 626 627 static void big_boo_act_4(void) { 628 #ifndef VERSION_JP 629 boo_stop(); 630 #endif 631 632 if (o->oBhvParams2ndByte == BIG_BOO_BP_GHOST_HUNT) { 633 obj_set_pos(o, 973, 0, 626); 634 635 if (o->oTimer > 60 && o->oDistanceToMario < 600.0f) { 636 obj_set_pos(o, 973, 0, 717); 637 638 spawn_object_relative(0, 0, 0, 0, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase); 639 spawn_object_relative(1, 0, 0, -200, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase); 640 spawn_object_relative(2, 0, 0, 200, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase); 641 642 obj_mark_for_deletion(o); 643 } 644 } else { 645 obj_mark_for_deletion(o); 646 } 647 } 648 649 static void (*sBooGivingStarActions[])(void) = { 650 big_boo_act_0, 651 big_boo_act_1, 652 big_boo_act_2, 653 big_boo_act_3, 654 big_boo_act_4, 655 }; 656 657 void bhv_big_boo_loop(void) { 658 //PARTIAL_UPDATE 659 660 obj_set_hitbox(o, &sBooGivingStarHitbox); 661 662 o->oGraphYOffset = o->oBooBaseScale * 60.0f; 663 664 cur_obj_update_floor_and_walls(); 665 cur_obj_call_action_function(sBooGivingStarActions); 666 cur_obj_move_standard(78); 667 668 boo_approach_target_opacity_and_update_scale(); 669 670 o->oInteractStatus = 0; 671 } 672 673 static void boo_with_cage_act_0(void) { 674 o->oBooParentBigBoo = NULL; 675 o->oBooTargetOpacity = 255; 676 o->oBooBaseScale = 2.0f; 677 678 cur_obj_scale(2.0f); 679 cur_obj_become_tangible(); 680 681 if (boo_should_be_active()) { 682 o->oAction = 1; 683 } 684 } 685 686 static void boo_with_cage_act_1(void) { 687 s32 attackStatus; 688 689 boo_chase_mario(100.0f, 0x200, 0.5f); 690 691 attackStatus = boo_get_attack_status(); 692 693 if (boo_should_be_stopped()) { 694 o->oAction = 0; 695 } 696 697 if (attackStatus == BOO_BOUNCED_ON) { 698 o->oAction = 2; 699 } 700 701 if (attackStatus == BOO_ATTACKED) { 702 o->oAction = 3; 703 } 704 } 705 706 static void boo_with_cage_act_2(void) { 707 if (boo_update_after_bounced_on(20.0f)) { 708 o->oAction = 1; 709 } 710 } 711 712 static void boo_with_cage_act_3(void) { 713 if (boo_update_during_death()) { 714 obj_mark_for_deletion(o); 715 } 716 } 717 718 void bhv_boo_with_cage_init(void) { 719 if (gHudDisplay.stars < SPAWN_CASTLE_BOO_STAR_REQUIREMENT) { 720 obj_mark_for_deletion(o); 721 } else { 722 struct Object *cage = spawn_object(o, MODEL_HAUNTED_CAGE, bhvBooCage); 723 cage->oBhvParams = o->oBhvParams; 724 } 725 } 726 727 static void (*sBooWithCageActions[])(void) = { 728 boo_with_cage_act_0, 729 boo_with_cage_act_1, 730 boo_with_cage_act_2, 731 boo_with_cage_act_3, 732 }; 733 734 void bhv_boo_with_cage_loop(void) { 735 //PARTIAL_UPDATE 736 737 cur_obj_update_floor_and_walls(); 738 cur_obj_call_action_function(sBooWithCageActions); 739 cur_obj_move_standard(78); 740 741 boo_approach_target_opacity_and_update_scale(); 742 743 o->oInteractStatus = 0; 744 } 745 746 void bhv_merry_go_round_boo_manager_loop(void) { 747 switch (o->oAction) { 748 case 0: 749 if (o->oDistanceToMario < 1000.0f) { 750 if (o->oMerryGoRoundBooManagerNumBoosKilled < 5) { 751 if (o->oMerryGoRoundBooManagerNumBoosSpawned != 5) { 752 if (o->oMerryGoRoundBooManagerNumBoosSpawned 753 - o->oMerryGoRoundBooManagerNumBoosKilled < 2) { 754 spawn_object(o, MODEL_BOO, bhvMerryGoRoundBoo); 755 o->oMerryGoRoundBooManagerNumBoosSpawned++; 756 } 757 } 758 759 o->oAction++; 760 } 761 762 if (o->oMerryGoRoundBooManagerNumBoosKilled >= 5) { 763 struct Object *bigBoo = spawn_object(o, MODEL_BOO, bhvMerryGoRoundBigBoo); 764 obj_copy_behavior_params(bigBoo, o); 765 766 o->oAction = 2; 767 768 #ifndef VERSION_JP 769 play_puzzle_jingle(); 770 #else 771 play_sound(SOUND_GENERAL2_RIGHT_ANSWER, gGlobalSoundSource); 772 #endif 773 } 774 } 775 776 break; 777 778 case 1: 779 if (o->oTimer > 60) { 780 o->oAction = 0; 781 } 782 783 break; 784 785 case 2: 786 break; 787 } 788 } 789 790 void obj_set_secondary_camera_focus(void) { 791 gSecondCameraFocus = o; 792 } 793 794 void bhv_animated_texture_loop(void) { 795 cur_obj_set_pos_to_home_with_debug(); 796 } 797 798 void bhv_boo_in_castle_loop(void) { 799 s16 targetAngle; 800 801 o->oBooBaseScale = 2.0f; 802 803 if (o->oAction == 0) { 804 cur_obj_hide(); 805 806 if (gHudDisplay.stars < SPAWN_CASTLE_BOO_STAR_REQUIREMENT) { 807 obj_mark_for_deletion(o); 808 } 809 810 if (gMarioCurrentRoom == 1) { 811 o->oAction++; 812 } 813 } else if (o->oAction == 1) { 814 cur_obj_unhide(); 815 816 o->oOpacity = 180; 817 818 if (o->oTimer == 0) { 819 cur_obj_scale(o->oBooBaseScale); 820 } 821 822 if (o->oDistanceToMario < 1000.0f) { 823 o->oAction++; 824 cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG); 825 } 826 827 o->oForwardVel = 0.0f; 828 targetAngle = o->oAngleToMario; 829 } else { 830 cur_obj_forward_vel_approach_upward(32.0f, 1.0f); 831 832 o->oHomeX = -1000.0f; 833 o->oHomeZ = -9000.0f; 834 835 targetAngle = cur_obj_angle_to_home(); 836 837 if (o->oPosZ < -5000.0f) { 838 if (o->oOpacity > 0) { 839 o->oOpacity -= 20; 840 } else { 841 o->oOpacity = 0; 842 } 843 } 844 845 if (o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) { 846 o->oAction = 1; 847 } 848 } 849 850 o->oVelY = 0.0f; 851 852 targetAngle = cur_obj_angle_to_home(); 853 854 cur_obj_rotate_yaw_toward(targetAngle, 0x5A8); 855 boo_oscillate(TRUE); 856 cur_obj_move_using_fvel_and_gravity(); 857 } 858 859 void bhv_boo_staircase(void) { 860 f32 targetY; 861 862 switch (o->oBhvParams2ndByte) { 863 case 1: 864 targetY = 0.0f; 865 break; 866 case 0: 867 targetY = -206.0f; 868 break; 869 case 2: 870 targetY = -413.0f; 871 break; 872 } 873 874 switch (o->oAction) { 875 case 0: 876 o->oPosY = o->oHomeY - 620.0f; 877 o->oAction++; 878 // fallthrough 879 case 1: 880 o->oPosY += 8.0f; 881 cur_obj_play_sound_1(SOUND_ENV_ELEVATOR2); 882 883 if (o->oPosY > targetY) { 884 o->oPosY = targetY; 885 o->oAction++; 886 } 887 888 break; 889 890 case 2: 891 if (o->oTimer == 0) { 892 cur_obj_play_sound_2(SOUND_GENERAL_UNKNOWN4_LOWPRIO); 893 } 894 895 if (jiggle_bbh_stair(o->oTimer)) { 896 o->oAction++; 897 } 898 899 break; 900 901 case 3: 902 if (o->oTimer == 0 && o->oBhvParams2ndByte == 1) { 903 play_puzzle_jingle(); 904 } 905 906 break; 907 } 908 }