obj_behaviors_2.c (29805B)
1 #include <PR/ultratypes.h> 2 3 #include "sm64.h" 4 #include "actors/common0.h" 5 #include "actors/group11.h" 6 #include "actors/group17.h" 7 #include "audio/external.h" 8 #include "behavior_actions.h" 9 #include "behavior_data.h" 10 #include "camera.h" 11 #include "dialog_ids.h" 12 #include "engine/behavior_script.h" 13 #include "engine/math_util.h" 14 #include "engine/surface_collision.h" 15 #include "engine/surface_load.h" 16 #include "game_init.h" 17 #include "geo_misc.h" 18 #include "ingame_menu.h" 19 #include "interaction.h" 20 #include "level_table.h" 21 #include "level_update.h" 22 #include "levels/bitdw/header.h" 23 #include "levels/bitfs/header.h" 24 #include "levels/bits/header.h" 25 #include "levels/bob/header.h" 26 #include "levels/ccm/header.h" 27 #include "levels/hmc/header.h" 28 #include "levels/jrb/header.h" 29 #include "levels/lll/header.h" 30 #include "levels/rr/header.h" 31 #include "levels/ssl/header.h" 32 #include "levels/thi/header.h" 33 #include "levels/ttc/header.h" 34 #include "levels/vcutm/header.h" 35 #include "mario.h" 36 #include "mario_actions_cutscene.h" 37 #include "memory.h" 38 #include "obj_behaviors.h" 39 #include "obj_behaviors_2.h" 40 #include "object_constants.h" 41 #include "object_helpers.h" 42 #include "object_list_processor.h" 43 #include "platform_displacement.h" 44 #include "rendering_graph_node.h" 45 #include "save_file.h" 46 #include "seq_ids.h" 47 #include "spawn_sound.h" 48 49 #define POS_OP_SAVE_POSITION 0 50 #define POS_OP_COMPUTE_VELOCITY 1 51 #define POS_OP_RESTORE_POSITION 2 52 53 #define o gCurrentObject 54 55 /* BSS (declared to force order) */ 56 extern s32 sNumActiveFirePiranhaPlants; 57 extern s32 sNumKilledFirePiranhaPlants; 58 extern f32 sObjSavedPosX; 59 extern f32 sObjSavedPosY; 60 extern f32 sObjSavedPosZ; 61 extern struct Object *sMontyMoleHoleList; 62 extern s32 sMontyMoleKillStreak; 63 extern f32 sMontyMoleLastKilledPosX; 64 extern f32 sMontyMoleLastKilledPosY; 65 extern f32 sMontyMoleLastKilledPosZ; 66 extern struct Object *sMasterTreadmill; 67 68 /** 69 * The treadmill that plays sounds and controls the others on random setting. 70 */ 71 struct Object *sMasterTreadmill; 72 73 f32 sObjSavedPosX; 74 f32 sObjSavedPosY; 75 f32 sObjSavedPosZ; 76 77 void wiggler_jumped_on_attack_handler(void); 78 void huge_goomba_weakly_attacked(void); 79 80 static s32 obj_is_rendering_enabled(void) { 81 if (o->header.gfx.node.flags & GRAPH_RENDER_ACTIVE) { 82 return TRUE; 83 } else { 84 return FALSE; 85 } 86 } 87 88 static s16 obj_get_pitch_from_vel(void) { 89 return -atan2s(o->oForwardVel, o->oVelY); 90 } 91 92 /** 93 * Show dialog proposing a race. 94 * If the player accepts the race, then leave time stop enabled and Mario in the 95 * text action so that the racing object can wait before starting the race. 96 * If the player declines the race, then disable time stop and allow Mario to 97 * move again. 98 */ 99 static s32 obj_update_race_proposition_dialog(s16 dialogID) { 100 s32 dialogResponse = 101 cur_obj_update_dialog_with_cutscene(MARIO_DIALOG_LOOK_UP, 102 (DIALOG_FLAG_TURN_TO_MARIO | DIALOG_FLAG_TIME_STOP_ENABLED), CUTSCENE_RACE_DIALOG, dialogID); 103 104 if (dialogResponse == DIALOG_RESPONSE_NO) { 105 set_mario_npc_dialog(MARIO_DIALOG_STOP); 106 disable_time_stop_including_mario(); 107 } 108 109 return dialogResponse; 110 } 111 112 static void obj_set_dist_from_home(f32 distFromHome) { 113 o->oPosX = o->oHomeX + distFromHome * coss(o->oMoveAngleYaw); 114 o->oPosZ = o->oHomeZ + distFromHome * sins(o->oMoveAngleYaw); 115 } 116 117 static s32 obj_is_near_to_and_facing_mario(f32 maxDist, s16 maxAngleDiff) { 118 if (o->oDistanceToMario < maxDist 119 && abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario) < maxAngleDiff) { 120 return TRUE; 121 } 122 return FALSE; 123 } 124 125 //! Although having no return value, this function 126 //! must be u32 to match other functions on -O2. 127 static BAD_RETURN(u32) obj_perform_position_op(s32 op) { 128 switch (op) { 129 case POS_OP_SAVE_POSITION: 130 sObjSavedPosX = o->oPosX; 131 sObjSavedPosY = o->oPosY; 132 sObjSavedPosZ = o->oPosZ; 133 break; 134 135 case POS_OP_COMPUTE_VELOCITY: 136 o->oVelX = o->oPosX - sObjSavedPosX; 137 o->oVelY = o->oPosY - sObjSavedPosY; 138 o->oVelZ = o->oPosZ - sObjSavedPosZ; 139 break; 140 141 case POS_OP_RESTORE_POSITION: 142 o->oPosX = sObjSavedPosX; 143 o->oPosY = sObjSavedPosY; 144 o->oPosZ = sObjSavedPosZ; 145 break; 146 } 147 } 148 149 static void platform_on_track_update_pos_or_spawn_ball(s32 ballIndex, f32 x, f32 y, f32 z) { 150 struct Object *trackBall; 151 struct Waypoint *initialPrevWaypoint; 152 struct Waypoint *nextWaypoint; 153 struct Waypoint *prevWaypoint; 154 UNUSED u8 filler[4]; 155 f32 amountToMove; 156 f32 dx; 157 f32 dy; 158 f32 dz; 159 f32 distToNextWaypoint; 160 161 if (ballIndex == 0 || ((u16)(o->oBhvParams >> 16) & 0x0080)) { 162 initialPrevWaypoint = o->oPlatformOnTrackPrevWaypoint; 163 nextWaypoint = initialPrevWaypoint; 164 165 if (ballIndex != 0) { 166 amountToMove = 300.0f * ballIndex; 167 } else { 168 obj_perform_position_op(POS_OP_SAVE_POSITION); 169 o->oPlatformOnTrackPrevWaypointFlags = 0; 170 amountToMove = o->oForwardVel; 171 } 172 173 do { 174 prevWaypoint = nextWaypoint; 175 176 nextWaypoint++; 177 if (nextWaypoint->flags == WAYPOINT_FLAGS_END) { 178 if (ballIndex == 0) { 179 o->oPlatformOnTrackPrevWaypointFlags = WAYPOINT_FLAGS_END; 180 } 181 182 if (((u16)(o->oBhvParams >> 16) & PLATFORM_ON_TRACK_BP_RETURN_TO_START)) { 183 nextWaypoint = o->oPlatformOnTrackStartWaypoint; 184 } else { 185 return; 186 } 187 } 188 189 dx = nextWaypoint->pos[0] - x; 190 dy = nextWaypoint->pos[1] - y; 191 dz = nextWaypoint->pos[2] - z; 192 193 distToNextWaypoint = sqrtf(dx * dx + dy * dy + dz * dz); 194 195 // Move directly to the next waypoint, even if it's farther away 196 // than amountToMove 197 amountToMove -= distToNextWaypoint; 198 x += dx; 199 y += dy; 200 z += dz; 201 } while (amountToMove > 0.0f); 202 203 // If we moved farther than amountToMove, move in the opposite direction 204 // No risk of near-zero division: If distToNextWaypoint is close to 205 // zero, then that means we didn't cross a waypoint this frame (since 206 // otherwise distToNextWaypoint would equal the distance between two 207 // waypoints, which should never be that small). But this implies that 208 // amountToMove - distToNextWaypoint <= 0, and amountToMove is at least 209 // 0.1 (from platform on track behavior). 210 distToNextWaypoint = amountToMove / distToNextWaypoint; 211 x += dx * distToNextWaypoint; 212 y += dy * distToNextWaypoint; 213 z += dz * distToNextWaypoint; 214 215 if (ballIndex != 0) { 216 trackBall = spawn_object_relative(o->oPlatformOnTrackBaseBallIndex + ballIndex, 0, 0, 0, o, 217 MODEL_TRAJECTORY_MARKER_BALL, bhvTrackBall); 218 219 if (trackBall != NULL) { 220 trackBall->oPosX = x; 221 trackBall->oPosY = y; 222 trackBall->oPosZ = z; 223 } 224 } else { 225 if (prevWaypoint != initialPrevWaypoint) { 226 if (o->oPlatformOnTrackPrevWaypointFlags == 0) { 227 o->oPlatformOnTrackPrevWaypointFlags = initialPrevWaypoint->flags; 228 } 229 o->oPlatformOnTrackPrevWaypoint = prevWaypoint; 230 } 231 232 o->oPosX = x; 233 o->oPosY = y; 234 o->oPosZ = z; 235 236 obj_perform_position_op(POS_OP_COMPUTE_VELOCITY); 237 238 o->oPlatformOnTrackPitch = 239 atan2s(sqrtf(o->oVelX * o->oVelX + o->oVelZ * o->oVelZ), -o->oVelY); 240 o->oPlatformOnTrackYaw = atan2s(o->oVelZ, o->oVelX); 241 } 242 } 243 } 244 245 static void cur_obj_spin_all_dimensions(f32 arg0, f32 arg1) { 246 f32 val24; 247 f32 val20; 248 f32 val1C; 249 f32 c; 250 f32 s; 251 f32 val10; 252 f32 val0C; 253 f32 val08; 254 f32 val04; 255 f32 val00; 256 257 if (o->oForwardVel == 0.0f) { 258 val24 = val20 = val1C = 0.0f; 259 260 if (o->oMoveFlags & OBJ_MOVE_IN_AIR) { 261 val20 = 50.0f; 262 } else { 263 if (o->oFaceAnglePitch < 0) { 264 val1C = -arg0; 265 } else if (o->oFaceAnglePitch > 0) { 266 val1C = arg0; 267 } 268 269 if (o->oFaceAngleRoll < 0) { 270 val24 = -arg1; 271 } else if (o->oFaceAngleRoll > 0) { 272 val24 = arg1; 273 } 274 } 275 276 c = coss(o->oFaceAnglePitch); 277 s = sins(o->oFaceAnglePitch); 278 val08 = val1C * c + val20 * s; 279 val0C = val20 * c - val1C * s; 280 281 c = coss(o->oFaceAngleRoll); 282 s = sins(o->oFaceAngleRoll); 283 val04 = val24 * c + val0C * s; 284 val0C = val0C * c - val24 * s; 285 286 c = coss(o->oFaceAngleYaw); 287 s = sins(o->oFaceAngleYaw); 288 val10 = val04 * c - val08 * s; 289 val08 = val08 * c + val04 * s; 290 291 val04 = val24 * c - val1C * s; 292 val00 = val1C * c + val24 * s; 293 294 o->oPosX = o->oHomeX - val04 + val10; 295 o->oGraphYOffset = val20 - val0C; 296 o->oPosZ = o->oHomeZ + val00 - val08; 297 } 298 } 299 300 static void obj_rotate_yaw_and_bounce_off_walls(s16 targetYaw, s16 turnAmount) { 301 if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) { 302 targetYaw = cur_obj_reflect_move_angle_off_wall(); 303 } 304 cur_obj_rotate_yaw_toward(targetYaw, turnAmount); 305 } 306 307 static s16 obj_get_pitch_to_home(f32 latDistToHome) { 308 return atan2s(latDistToHome, o->oPosY - o->oHomeY); 309 } 310 311 static void obj_compute_vel_from_move_pitch(f32 speed) { 312 o->oForwardVel = speed * coss(o->oMoveAnglePitch); 313 o->oVelY = speed * -sins(o->oMoveAnglePitch); 314 } 315 316 static s32 clamp_s16(s16 *value, s16 minimum, s16 maximum) { 317 if (*value <= minimum) { 318 *value = minimum; 319 } else if (*value >= maximum) { 320 *value = maximum; 321 } else { 322 return FALSE; 323 } 324 325 return TRUE; 326 } 327 328 static s32 clamp_f32(f32 *value, f32 minimum, f32 maximum) { 329 if (*value <= minimum) { 330 *value = minimum; 331 } else if (*value >= maximum) { 332 *value = maximum; 333 } else { 334 return FALSE; 335 } 336 337 return TRUE; 338 } 339 340 static void cur_obj_init_anim_extend(s32 arg0) { 341 cur_obj_init_animation_with_sound(arg0); 342 cur_obj_extend_animation_if_at_end(); 343 } 344 345 static s32 cur_obj_init_anim_and_check_if_end(s32 arg0) { 346 cur_obj_init_animation_with_sound(arg0); 347 return cur_obj_check_if_near_animation_end(); 348 } 349 350 static s32 cur_obj_init_anim_check_frame(s32 arg0, s32 arg1) { 351 cur_obj_init_animation_with_sound(arg0); 352 return cur_obj_check_anim_frame(arg1); 353 } 354 355 static s32 cur_obj_set_anim_if_at_end(s32 arg0) { 356 if (cur_obj_check_if_at_animation_end()) { 357 cur_obj_init_animation_with_sound(arg0); 358 return TRUE; 359 } 360 return FALSE; 361 } 362 363 static s32 cur_obj_play_sound_at_anim_range(s8 arg0, s8 arg1, u32 sound) { 364 s32 val04; 365 366 if ((val04 = o->header.gfx.animInfo.animAccel / 0x10000) <= 0) { 367 val04 = 1; 368 } 369 370 if (cur_obj_check_anim_frame_in_range(arg0, val04) || cur_obj_check_anim_frame_in_range(arg1, val04)) { 371 cur_obj_play_sound_2(sound); 372 return TRUE; 373 } 374 375 return FALSE; 376 } 377 378 static s16 obj_turn_pitch_toward_mario(f32 targetOffsetY, s16 turnAmount) { 379 s16 targetPitch; 380 381 o->oPosY -= targetOffsetY; 382 targetPitch = obj_turn_toward_object(o, gMarioObject, O_MOVE_ANGLE_PITCH_INDEX, turnAmount); 383 o->oPosY += targetOffsetY; 384 385 return targetPitch; 386 } 387 388 static s32 approach_f32_ptr(f32 *px, f32 target, f32 delta) { 389 if (*px > target) { 390 delta = -delta; 391 } 392 393 *px += delta; 394 395 if ((*px - target) * delta >= 0) { 396 *px = target; 397 return TRUE; 398 } 399 return FALSE; 400 } 401 402 static s32 obj_forward_vel_approach(f32 target, f32 delta) { 403 return approach_f32_ptr(&o->oForwardVel, target, delta); 404 } 405 406 static s32 obj_y_vel_approach(f32 target, f32 delta) { 407 return approach_f32_ptr(&o->oVelY, target, delta); 408 } 409 410 static s32 obj_move_pitch_approach(s16 target, s16 delta) { 411 o->oMoveAnglePitch = approach_s16_symmetric(o->oMoveAnglePitch, target, delta); 412 413 if ((s16) o->oMoveAnglePitch == target) { 414 return TRUE; 415 } 416 417 return FALSE; 418 } 419 420 static s32 obj_face_pitch_approach(s16 targetPitch, s16 deltaPitch) { 421 o->oFaceAnglePitch = approach_s16_symmetric(o->oFaceAnglePitch, targetPitch, deltaPitch); 422 423 if ((s16) o->oFaceAnglePitch == targetPitch) { 424 return TRUE; 425 } 426 427 return FALSE; 428 } 429 430 static s32 obj_face_yaw_approach(s16 targetYaw, s16 deltaYaw) { 431 o->oFaceAngleYaw = approach_s16_symmetric(o->oFaceAngleYaw, targetYaw, deltaYaw); 432 433 if ((s16) o->oFaceAngleYaw == targetYaw) { 434 return TRUE; 435 } 436 437 return FALSE; 438 } 439 440 static s32 obj_face_roll_approach(s16 targetRoll, s16 deltaRoll) { 441 o->oFaceAngleRoll = approach_s16_symmetric(o->oFaceAngleRoll, targetRoll, deltaRoll); 442 443 if ((s16) o->oFaceAngleRoll == targetRoll) { 444 return TRUE; 445 } 446 447 return FALSE; 448 } 449 450 static s32 obj_smooth_turn(s16 *angleVel, s32 *angle, s16 targetAngle, f32 targetSpeedProportion, 451 s16 accel, s16 minSpeed, s16 maxSpeed) { 452 s16 currentSpeed; 453 s16 currentAngle = (s16)(*angle); 454 455 *angleVel = 456 approach_s16_symmetric(*angleVel, (targetAngle - currentAngle) * targetSpeedProportion, accel); 457 458 currentSpeed = absi(*angleVel); 459 clamp_s16(¤tSpeed, minSpeed, maxSpeed); 460 461 *angle = approach_s16_symmetric(*angle, targetAngle, currentSpeed); 462 return (s16)(*angle) == targetAngle; 463 } 464 465 static void obj_roll_to_match_yaw_turn(s16 targetYaw, s16 maxRoll, s16 rollSpeed) { 466 s16 targetRoll = o->oMoveAngleYaw - targetYaw; 467 clamp_s16(&targetRoll, -maxRoll, maxRoll); 468 obj_face_roll_approach(targetRoll, rollSpeed); 469 } 470 471 static s16 random_linear_offset(s16 base, s16 range) { 472 return base + (s16)(range * random_float()); 473 } 474 475 static s16 random_mod_offset(s16 base, s16 step, s16 mod) { 476 return base + step * (random_u16() % mod); 477 } 478 479 static s16 obj_random_fixed_turn(s16 delta) { 480 return o->oMoveAngleYaw + (s16) random_sign() * delta; 481 } 482 483 /** 484 * Begin by increasing the object's scale by *scaleVel, and slowly decreasing 485 * scaleVel. Once the object starts to shrink, wait a bit, and then begin to 486 * scale the object toward endScale. The first time it reaches below 487 * shootFireScale during this time, return 1. 488 * Return -1 once it's reached endScale. 489 */ 490 static s32 obj_grow_then_shrink(f32 *scaleVel, f32 shootFireScale, f32 endScale) { 491 if (o->oTimer < 2) { 492 o->header.gfx.scale[0] += *scaleVel; 493 494 if ((*scaleVel -= 0.01f) > -0.03f) { 495 o->oTimer = 0; 496 } 497 } else if (o->oTimer > 10) { 498 if (approach_f32_ptr(&o->header.gfx.scale[0], endScale, 0.05f)) { 499 return -1; 500 } else if (*scaleVel != 0.0f && o->header.gfx.scale[0] < shootFireScale) { 501 *scaleVel = 0.0f; 502 return 1; 503 } 504 } 505 506 return 0; 507 } 508 509 static s32 oscillate_toward(s32 *value, f32 *vel, s32 target, f32 velCloseToZero, f32 accel, 510 f32 slowdown) { 511 s32 startValue = *value; 512 *value += (s32) *vel; 513 514 if (*value == target 515 || ((*value - target) * (startValue - target) < 0 && *vel > -velCloseToZero 516 && *vel < velCloseToZero)) { 517 *value = target; 518 *vel = 0.0f; 519 return TRUE; 520 } else { 521 if (*value >= target) { 522 accel = -accel; 523 } 524 if (*vel * accel < 0.0f) { 525 accel *= slowdown; 526 } 527 528 *vel += accel; 529 } 530 531 return FALSE; 532 } 533 534 static void obj_update_blinking(s32 *blinkTimer, s16 baseCycleLength, s16 cycleLengthRange, 535 s16 blinkLength) { 536 if (*blinkTimer != 0) { 537 (*blinkTimer)--; 538 } else { 539 *blinkTimer = random_linear_offset(baseCycleLength, cycleLengthRange); 540 } 541 542 if (*blinkTimer > blinkLength) { 543 o->oAnimState = 0; 544 } else { 545 o->oAnimState = 1; 546 } 547 } 548 549 static s32 obj_resolve_object_collisions(s32 *targetYaw) { 550 struct Object *otherObject; 551 f32 dx; 552 f32 dz; 553 s16 angle; 554 f32 radius; 555 f32 otherRadius; 556 f32 relativeRadius; 557 f32 newCenterX; 558 f32 newCenterZ; 559 560 if (o->numCollidedObjs != 0) { 561 otherObject = o->collidedObjs[0]; 562 if (otherObject != gMarioObject) { 563 //! If one object moves after collisions are detected and this code 564 // runs, the objects can move toward each other (transport cloning) 565 566 dx = otherObject->oPosX - o->oPosX; 567 dz = otherObject->oPosZ - o->oPosZ; 568 angle = atan2s(dx, dz); //! This should be atan2s(dz, dx) 569 570 radius = o->hitboxRadius; 571 otherRadius = otherObject->hitboxRadius; 572 relativeRadius = radius / (radius + otherRadius); 573 574 newCenterX = o->oPosX + dx * relativeRadius; 575 newCenterZ = o->oPosZ + dz * relativeRadius; 576 577 o->oPosX = newCenterX - radius * coss(angle); 578 o->oPosZ = newCenterZ - radius * sins(angle); 579 580 otherObject->oPosX = newCenterX + otherRadius * coss(angle); 581 otherObject->oPosZ = newCenterZ + otherRadius * sins(angle); 582 583 if (targetYaw != NULL && abs_angle_diff(o->oMoveAngleYaw, angle) < 0x4000) { 584 // Bounce off object (or it would, if the above atan2s bug 585 // were fixed) 586 *targetYaw = (s16)(angle - o->oMoveAngleYaw + angle + 0x8000); 587 return TRUE; 588 } 589 } 590 } 591 592 return FALSE; 593 } 594 595 static s32 obj_bounce_off_walls_edges_objects(s32 *targetYaw) { 596 if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) { 597 *targetYaw = cur_obj_reflect_move_angle_off_wall(); 598 } else if (o->oMoveFlags & OBJ_MOVE_HIT_EDGE) { 599 *targetYaw = (s16)(o->oMoveAngleYaw + 0x8000); 600 } else if (!obj_resolve_object_collisions(targetYaw)) { 601 return FALSE; 602 } 603 604 return TRUE; 605 } 606 607 static s32 obj_resolve_collisions_and_turn(s16 targetYaw, s16 turnSpeed) { 608 obj_resolve_object_collisions(NULL); 609 610 if (cur_obj_rotate_yaw_toward(targetYaw, turnSpeed)) { 611 return FALSE; 612 } else { 613 return TRUE; 614 } 615 } 616 617 static void obj_die_if_health_non_positive(void) { 618 if (o->oHealth <= 0) { 619 if (o->oDeathSound == 0) { 620 spawn_mist_particles_with_sound(SOUND_OBJ_DEFAULT_DEATH); 621 } else if (o->oDeathSound > 0) { 622 spawn_mist_particles_with_sound(o->oDeathSound); 623 } else { 624 spawn_mist_particles(); 625 } 626 627 if (o->oNumLootCoins < 0) { 628 spawn_object(o, MODEL_BLUE_COIN, bhvSpawnedBlueCoin); 629 } else { 630 obj_spawn_loot_yellow_coins(o, o->oNumLootCoins, 20.0f); 631 } 632 // This doesn't do anything 633 obj_spawn_loot_yellow_coins(o, o->oNumLootCoins, 20.0f); 634 635 if (o->oHealth < 0) { 636 cur_obj_hide(); 637 cur_obj_become_intangible(); 638 } else { 639 obj_mark_for_deletion(o); 640 } 641 } 642 } 643 644 UNUSED static void obj_unused_die(void) { 645 o->oHealth = 0; 646 obj_die_if_health_non_positive(); 647 } 648 649 static void obj_set_knockback_action(s32 attackType) { 650 switch (attackType) { 651 case ATTACK_KICK_OR_TRIP: 652 case ATTACK_FAST_ATTACK: 653 o->oAction = OBJ_ACT_VERTICAL_KNOCKBACK; 654 o->oForwardVel = 20.0f; 655 o->oVelY = 50.0f; 656 break; 657 658 default: 659 o->oAction = OBJ_ACT_HORIZONTAL_KNOCKBACK; 660 o->oForwardVel = 50.0f; 661 o->oVelY = 30.0f; 662 break; 663 } 664 665 o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; 666 o->oMoveAngleYaw = obj_angle_to_object(gMarioObject, o); 667 } 668 669 static void obj_set_squished_action(void) { 670 cur_obj_play_sound_2(SOUND_OBJ_STOMPED); 671 o->oAction = OBJ_ACT_SQUISHED; 672 } 673 674 static s32 obj_die_if_above_lava_and_health_non_positive(void) { 675 if (o->oMoveFlags & OBJ_MOVE_UNDERWATER_ON_GROUND) { 676 if (o->oGravity + o->oBuoyancy > 0.0f 677 || find_water_level(o->oPosX, o->oPosZ) - o->oPosY < 150.0f) { 678 return FALSE; 679 } 680 } else if (!(o->oMoveFlags & OBJ_MOVE_ABOVE_LAVA)) { 681 if (o->oMoveFlags & OBJ_MOVE_ENTERED_WATER) { 682 if (o->oWallHitboxRadius < 200.0f) { 683 cur_obj_play_sound_2(SOUND_OBJ_DIVING_INTO_WATER); 684 } else { 685 cur_obj_play_sound_2(SOUND_OBJ_DIVING_IN_WATER); 686 } 687 } 688 return FALSE; 689 } 690 691 obj_die_if_health_non_positive(); 692 return TRUE; 693 } 694 695 static s32 obj_handle_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioAction, 696 u8 *attackHandlers) { 697 s32 attackType; 698 699 obj_set_hitbox(o, hitbox); 700 701 //! Die immediately if above lava 702 if (obj_die_if_above_lava_and_health_non_positive()) { 703 return 1; 704 } else if (o->oInteractStatus & INT_STATUS_INTERACTED) { 705 if (o->oInteractStatus & INT_STATUS_ATTACKED_MARIO) { 706 if (o->oAction != attackedMarioAction) { 707 o->oAction = attackedMarioAction; 708 o->oTimer = 0; 709 } 710 } else { 711 attackType = o->oInteractStatus & INT_STATUS_ATTACK_MASK; 712 713 switch (attackHandlers[attackType - 1]) { 714 case ATTACK_HANDLER_NOP: 715 break; 716 717 case ATTACK_HANDLER_DIE_IF_HEALTH_NON_POSITIVE: 718 obj_die_if_health_non_positive(); 719 break; 720 721 case ATTACK_HANDLER_KNOCKBACK: 722 obj_set_knockback_action(attackType); 723 break; 724 725 case ATTACK_HANDLER_SQUISHED: 726 obj_set_squished_action(); 727 break; 728 729 case ATTACK_HANDLER_SPECIAL_KOOPA_LOSE_SHELL: 730 shelled_koopa_attack_handler(attackType); 731 break; 732 733 case ATTACK_HANDLER_SET_SPEED_TO_ZERO: 734 obj_set_speed_to_zero(); 735 break; 736 737 case ATTACK_HANDLER_SPECIAL_WIGGLER_JUMPED_ON: 738 wiggler_jumped_on_attack_handler(); 739 break; 740 741 case ATTACK_HANDLER_SPECIAL_HUGE_GOOMBA_WEAKLY_ATTACKED: 742 huge_goomba_weakly_attacked(); 743 break; 744 745 case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN: 746 o->oNumLootCoins = -1; 747 obj_set_squished_action(); 748 break; 749 } 750 751 o->oInteractStatus = 0; 752 return attackType; 753 } 754 } 755 756 o->oInteractStatus = 0; 757 return 0; 758 } 759 760 static void obj_act_knockback(UNUSED f32 baseScale) { 761 cur_obj_update_floor_and_walls(); 762 763 if (o->header.gfx.animInfo.curAnim != NULL) { 764 cur_obj_extend_animation_if_at_end(); 765 } 766 767 //! Dies immediately if above lava 768 if ((o->oMoveFlags 769 & (OBJ_MOVE_MASK_ON_GROUND | OBJ_MOVE_MASK_IN_WATER | OBJ_MOVE_HIT_WALL | OBJ_MOVE_ABOVE_LAVA)) 770 || (o->oAction == OBJ_ACT_VERTICAL_KNOCKBACK && o->oTimer >= 9)) { 771 obj_die_if_health_non_positive(); 772 } 773 774 cur_obj_move_standard(-78); 775 } 776 777 static void obj_act_squished(f32 baseScale) { 778 f32 targetScaleY = baseScale * 0.3f; 779 780 cur_obj_update_floor_and_walls(); 781 782 if (o->header.gfx.animInfo.curAnim != NULL) { 783 cur_obj_extend_animation_if_at_end(); 784 } 785 786 if (approach_f32_ptr(&o->header.gfx.scale[1], targetScaleY, baseScale * 0.14f)) { 787 o->header.gfx.scale[0] = o->header.gfx.scale[2] = baseScale * 2.0f - o->header.gfx.scale[1]; 788 789 if (o->oTimer > 15) { 790 obj_die_if_health_non_positive(); 791 } 792 } 793 794 o->oForwardVel = 0.0f; 795 cur_obj_move_standard(-78); 796 } 797 798 static s32 obj_update_standard_actions(f32 scale) { 799 if (o->oAction < 100) { 800 return TRUE; 801 } else { 802 cur_obj_become_intangible(); 803 804 switch (o->oAction) { 805 case OBJ_ACT_HORIZONTAL_KNOCKBACK: 806 case OBJ_ACT_VERTICAL_KNOCKBACK: 807 obj_act_knockback(scale); 808 break; 809 810 case OBJ_ACT_SQUISHED: 811 obj_act_squished(scale); 812 break; 813 } 814 815 return FALSE; 816 } 817 } 818 819 static s32 obj_check_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioAction) { 820 s32 attackType; 821 822 obj_set_hitbox(o, hitbox); 823 824 //! Dies immediately if above lava 825 if (obj_die_if_above_lava_and_health_non_positive()) { 826 return 1; 827 } else if (o->oInteractStatus & INT_STATUS_INTERACTED) { 828 if (o->oInteractStatus & INT_STATUS_ATTACKED_MARIO) { 829 if (o->oAction != attackedMarioAction) { 830 o->oAction = attackedMarioAction; 831 o->oTimer = 0; 832 } 833 } else { 834 attackType = o->oInteractStatus & INT_STATUS_ATTACK_MASK; 835 obj_die_if_health_non_positive(); 836 o->oInteractStatus = 0; 837 return attackType; 838 } 839 } 840 841 o->oInteractStatus = 0; 842 return 0; 843 } 844 845 static s32 obj_move_for_one_second(s32 endAction) { 846 cur_obj_update_floor_and_walls(); 847 cur_obj_extend_animation_if_at_end(); 848 849 if (o->oTimer > 30) { 850 o->oAction = endAction; 851 return TRUE; 852 } 853 854 cur_obj_move_standard(-78); 855 return FALSE; 856 } 857 858 /** 859 * If we are far from home (> threshold away), then set oAngleToMario to the 860 * angle to home and oDistanceToMario to 25000. 861 * If we are close to home, but Mario is far from us (> threshold away), then 862 * keep oAngleToMario the same and set oDistanceToMario to 20000. 863 * If we are close to both home and Mario, then keep both oAngleToMario and 864 * oDistanceToMario the same. 865 * 866 * The point of this function is to avoid having to write extra code to get 867 * the object to return to home. When Mario is far away and the object is far 868 * from home, it could theoretically re-use the "approach Mario" logic to approach 869 * its home instead. 870 * However, most objects that use this function handle the far-from-home case 871 * separately anyway. 872 * This function causes seemingly erroneous behavior in some objects that try to 873 * attack Mario (e.g. fly guy shooting fire or lunging), especially when combined 874 * with partial updates. 875 */ 876 static void treat_far_home_as_mario(f32 threshold) { 877 f32 dx = o->oHomeX - o->oPosX; 878 f32 dy = o->oHomeY - o->oPosY; 879 f32 dz = o->oHomeZ - o->oPosZ; 880 f32 distance = sqrtf(dx * dx + dy * dy + dz * dz); 881 882 if (distance > threshold) { 883 o->oAngleToMario = atan2s(dz, dx); 884 o->oDistanceToMario = 25000.0f; 885 } else { 886 dx = o->oHomeX - gMarioObject->oPosX; 887 dy = o->oHomeY - gMarioObject->oPosY; 888 dz = o->oHomeZ - gMarioObject->oPosZ; 889 distance = sqrtf(dx * dx + dy * dy + dz * dz); 890 891 if (distance > threshold) { 892 o->oDistanceToMario = 20000.0f; 893 } 894 } 895 } 896 897 #include "behaviors/koopa.inc.c" // TODO: Text arg field name 898 #include "behaviors/pokey.inc.c" 899 #include "behaviors/swoop.inc.c" 900 #include "behaviors/fly_guy.inc.c" 901 #include "behaviors/goomba.inc.c" 902 #include "behaviors/chain_chomp.inc.c" // TODO: chain_chomp_sub_act_lunge documentation 903 #include "behaviors/wiggler.inc.c" // TODO 904 #include "behaviors/spiny.inc.c" 905 #include "behaviors/enemy_lakitu.inc.c" // TODO 906 #include "behaviors/cloud.inc.c" 907 #include "behaviors/camera_lakitu.inc.c" // TODO: 104 label, follow cam documentation 908 #include "behaviors/monty_mole.inc.c" // TODO 909 #include "behaviors/platform_on_track.inc.c" 910 #include "behaviors/seesaw_platform.inc.c" 911 #include "behaviors/ferris_wheel.inc.c" 912 #include "behaviors/water_bomb.inc.c" // TODO: Shadow position 913 #include "behaviors/ttc_rotating_solid.inc.c" 914 #include "behaviors/ttc_pendulum.inc.c" 915 #include "behaviors/ttc_treadmill.inc.c" // TODO 916 #include "behaviors/ttc_moving_bar.inc.c" 917 #include "behaviors/ttc_cog.inc.c" 918 #include "behaviors/ttc_pit_block.inc.c" 919 #include "behaviors/ttc_elevator.inc.c" 920 #include "behaviors/ttc_2d_rotator.inc.c" 921 #include "behaviors/ttc_spinner.inc.c" 922 #include "behaviors/mr_blizzard.inc.c" 923 #include "behaviors/sliding_platform_2.inc.c" 924 #include "behaviors/rotating_octagonal_plat.inc.c" 925 #include "behaviors/animated_floor_switch.inc.c" 926 #include "behaviors/activated_bf_plat.inc.c" 927 #include "behaviors/recovery_heart.inc.c" 928 #include "behaviors/water_bomb_cannon.inc.c" 929 #include "behaviors/unagi.inc.c" 930 #include "behaviors/dorrie.inc.c" 931 #include "behaviors/haunted_chair.inc.c" 932 #include "behaviors/mad_piano.inc.c" 933 #include "behaviors/flying_bookend_switch.inc.c" 934 935 /** 936 * Used by bowser, fly guy, piranha plant, and fire spitters. 937 */ 938 void obj_spit_fire(s16 relativePosX, s16 relativePosY, s16 relativePosZ, f32 scale, s32 model, 939 f32 startSpeed, f32 endSpeed, s16 movePitch) { 940 struct Object *obj = spawn_object_relative_with_scale(1, relativePosX, relativePosY, relativePosZ, 941 scale, o, model, bhvSmallPiranhaFlame); 942 943 if (obj != NULL) { 944 obj->oSmallPiranhaFlameStartSpeed = startSpeed; 945 obj->oSmallPiranhaFlameEndSpeed = endSpeed; 946 obj->oSmallPiranhaFlameModel = model; 947 obj->oMoveAnglePitch = movePitch; 948 } 949 } 950 951 #include "behaviors/fire_piranha_plant.inc.c" 952 #include "behaviors/fire_spitter.inc.c" 953 #include "behaviors/flame.inc.c" 954 #include "behaviors/snufit.inc.c" 955 #include "behaviors/horizontal_grindel.inc.c" 956 #include "behaviors/eyerok.inc.c" 957 #include "behaviors/klepto.inc.c" 958 #include "behaviors/bird.inc.c" 959 #include "behaviors/racing_penguin.inc.c" 960 #include "behaviors/coffin.inc.c" 961 #include "behaviors/clam.inc.c" 962 #include "behaviors/skeeter.inc.c" 963 #include "behaviors/swing_platform.inc.c" 964 #include "behaviors/donut_platform.inc.c" 965 #include "behaviors/ddd_pole.inc.c" 966 #include "behaviors/reds_star_marker.inc.c" 967 #include "behaviors/triplet_butterfly.inc.c" 968 #include "behaviors/bubba.inc.c"