interaction.c (61913B)
1 #include <PR/ultratypes.h> 2 3 #include "area.h" 4 #include "actors/common1.h" 5 #include "audio/external.h" 6 #include "behavior_actions.h" 7 #include "behavior_data.h" 8 #include "camera.h" 9 #include "course_table.h" 10 #include "dialog_ids.h" 11 #include "engine/math_util.h" 12 #include "engine/surface_collision.h" 13 #include "game_init.h" 14 #include "interaction.h" 15 #include "level_update.h" 16 #include "mario.h" 17 #include "mario_step.h" 18 #include "memory.h" 19 #include "obj_behaviors.h" 20 #include "object_helpers.h" 21 #include "save_file.h" 22 #include "seq_ids.h" 23 #include "sm64.h" 24 #include "sound_init.h" 25 #include "rumble_init.h" 26 27 #define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01 28 #define INT_PUNCH (1 << 1) // 0x02 29 #define INT_KICK (1 << 2) // 0x04 30 #define INT_TRIP (1 << 3) // 0x08 31 #define INT_SLIDE_KICK (1 << 4) // 0x10 32 #define INT_FAST_ATTACK_OR_SHELL (1 << 5) // 0x20 33 #define INT_HIT_FROM_ABOVE (1 << 6) // 0x40 34 #define INT_HIT_FROM_BELOW (1 << 7) // 0x80 35 36 #define INT_ATTACK_NOT_FROM_BELOW \ 37 (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ 38 | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE) 39 40 #define INT_ANY_ATTACK \ 41 (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ 42 | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW) 43 44 #define INT_ATTACK_NOT_WEAK_FROM_ABOVE \ 45 (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW) 46 47 u8 sDelayInvincTimer; 48 s16 sInvulnerable; 49 u32 interact_coin(struct MarioState *, u32, struct Object *); 50 u32 interact_water_ring(struct MarioState *, u32, struct Object *); 51 u32 interact_star_or_key(struct MarioState *, u32, struct Object *); 52 u32 interact_bbh_entrance(struct MarioState *, u32, struct Object *); 53 u32 interact_warp(struct MarioState *, u32, struct Object *); 54 u32 interact_warp_door(struct MarioState *, u32, struct Object *); 55 u32 interact_door(struct MarioState *, u32, struct Object *); 56 u32 interact_cannon_base(struct MarioState *, u32, struct Object *); 57 u32 interact_igloo_barrier(struct MarioState *, u32, struct Object *); 58 u32 interact_tornado(struct MarioState *, u32, struct Object *); 59 u32 interact_whirlpool(struct MarioState *, u32, struct Object *); 60 u32 interact_strong_wind(struct MarioState *, u32, struct Object *); 61 u32 interact_flame(struct MarioState *, u32, struct Object *); 62 u32 interact_snufit_bullet(struct MarioState *, u32, struct Object *); 63 u32 interact_clam_or_bubba(struct MarioState *, u32, struct Object *); 64 u32 interact_bully(struct MarioState *, u32, struct Object *); 65 u32 interact_shock(struct MarioState *, u32, struct Object *); 66 u32 interact_mr_blizzard(struct MarioState *, u32, struct Object *); 67 u32 interact_hit_from_below(struct MarioState *, u32, struct Object *); 68 u32 interact_bounce_top(struct MarioState *, u32, struct Object *); 69 u32 interact_unknown_08(struct MarioState *, u32, struct Object *); 70 u32 interact_damage(struct MarioState *, u32, struct Object *); 71 u32 interact_breakable(struct MarioState *, u32, struct Object *); 72 u32 interact_koopa_shell(struct MarioState *, u32, struct Object *); 73 u32 interact_pole(struct MarioState *, u32, struct Object *); 74 u32 interact_hoot(struct MarioState *, u32, struct Object *); 75 u32 interact_cap(struct MarioState *, u32, struct Object *); 76 u32 interact_grabbable(struct MarioState *, u32, struct Object *); 77 u32 interact_text(struct MarioState *, u32, struct Object *); 78 79 struct InteractionHandler { 80 u32 interactType; 81 u32 (*handler)(struct MarioState *, u32, struct Object *); 82 }; 83 84 static struct InteractionHandler sInteractionHandlers[] = { 85 { INTERACT_COIN, interact_coin }, 86 { INTERACT_WATER_RING, interact_water_ring }, 87 { INTERACT_STAR_OR_KEY, interact_star_or_key }, 88 { INTERACT_BBH_ENTRANCE, interact_bbh_entrance }, 89 { INTERACT_WARP, interact_warp }, 90 { INTERACT_WARP_DOOR, interact_warp_door }, 91 { INTERACT_DOOR, interact_door }, 92 { INTERACT_CANNON_BASE, interact_cannon_base }, 93 { INTERACT_IGLOO_BARRIER, interact_igloo_barrier }, 94 { INTERACT_TORNADO, interact_tornado }, 95 { INTERACT_WHIRLPOOL, interact_whirlpool }, 96 { INTERACT_STRONG_WIND, interact_strong_wind }, 97 { INTERACT_FLAME, interact_flame }, 98 { INTERACT_SNUFIT_BULLET, interact_snufit_bullet }, 99 { INTERACT_CLAM_OR_BUBBA, interact_clam_or_bubba }, 100 { INTERACT_BULLY, interact_bully }, 101 { INTERACT_SHOCK, interact_shock }, 102 { INTERACT_BOUNCE_TOP2, interact_bounce_top }, 103 { INTERACT_MR_BLIZZARD, interact_mr_blizzard }, 104 { INTERACT_HIT_FROM_BELOW, interact_hit_from_below }, 105 { INTERACT_BOUNCE_TOP, interact_bounce_top }, 106 { INTERACT_DAMAGE, interact_damage }, 107 { INTERACT_POLE, interact_pole }, 108 { INTERACT_HOOT, interact_hoot }, 109 { INTERACT_BREAKABLE, interact_breakable }, 110 { INTERACT_KOOPA, interact_bounce_top }, 111 { INTERACT_KOOPA_SHELL, interact_koopa_shell }, 112 { INTERACT_UNKNOWN_08, interact_unknown_08 }, 113 { INTERACT_CAP, interact_cap }, 114 { INTERACT_GRABBABLE, interact_grabbable }, 115 { INTERACT_TEXT, interact_text }, 116 }; 117 118 static u32 sForwardKnockbackActions[][3] = { 119 { ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB }, 120 { ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB }, 121 { ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB }, 122 }; 123 124 static u32 sBackwardKnockbackActions[][3] = { 125 { ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB }, 126 { ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB }, 127 { ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB }, 128 }; 129 130 static u8 sDisplayingDoorText = FALSE; 131 static u8 sJustTeleported = FALSE; 132 static u8 sPSSSlideStarted = FALSE; 133 134 /** 135 * Returns the type of cap Mario is wearing. 136 */ 137 u32 get_mario_cap_flag(struct Object *capObject) { 138 const BehaviorScript *script = virtual_to_segmented(0x13, capObject->behavior); 139 140 if (script == bhvNormalCap) { 141 return MARIO_NORMAL_CAP; 142 } else if (script == bhvMetalCap) { 143 return MARIO_METAL_CAP; 144 } else if (script == bhvWingCap) { 145 return MARIO_WING_CAP; 146 } else if (script == bhvVanishCap) { 147 return MARIO_VANISH_CAP; 148 } 149 150 return 0; 151 } 152 153 /** 154 * Returns true if the passed in object has a moving angle yaw 155 * in the angular range given towards Mario. 156 */ 157 u32 object_facing_mario(struct MarioState *m, struct Object *o, s16 angleRange) { 158 f32 dx = m->pos[0] - o->oPosX; 159 f32 dz = m->pos[2] - o->oPosZ; 160 161 s16 angleToMario = atan2s(dz, dx); 162 s16 dAngle = angleToMario - o->oMoveAngleYaw; 163 164 if (-angleRange <= dAngle && dAngle <= angleRange) { 165 return TRUE; 166 } 167 168 return FALSE; 169 } 170 171 s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o) { 172 f32 dx = o->oPosX - m->pos[0]; 173 f32 dz = o->oPosZ - m->pos[2]; 174 175 return atan2s(dz, dx); 176 } 177 178 /** 179 * Determines Mario's interaction with a given object depending on their proximity, 180 * action, speed, and position. 181 */ 182 u32 determine_interaction(struct MarioState *m, struct Object *o) { 183 u32 interaction = 0; 184 u32 action = m->action; 185 186 if (action & ACT_FLAG_ATTACKING) { 187 if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) { 188 s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; 189 190 if (m->flags & MARIO_PUNCHING) { 191 // 120 degrees total, or 60 each way 192 if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { 193 interaction = INT_PUNCH; 194 } 195 } 196 if (m->flags & MARIO_KICKING) { 197 // 120 degrees total, or 60 each way 198 if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { 199 interaction = INT_KICK; 200 } 201 } 202 if (m->flags & MARIO_TRIPPING) { 203 // 180 degrees total, or 90 each way 204 if (-0x4000 <= dYawToObject && dYawToObject <= 0x4000) { 205 interaction = INT_TRIP; 206 } 207 } 208 } else if (action == ACT_GROUND_POUND || action == ACT_TWIRLING) { 209 if (m->vel[1] < 0.0f) { 210 interaction = INT_GROUND_POUND_OR_TWIRL; 211 } 212 } else if (action == ACT_GROUND_POUND_LAND || action == ACT_TWIRL_LAND) { 213 // Neither ground pounding nor twirling change Mario's vertical speed on landing., 214 // so the speed check is nearly always true (perhaps not if you land while going upwards?) 215 // Additionally, actionState it set on each first thing in their action, so this is 216 // only true prior to the very first frame (i.e. active 1 frame prior to it run). 217 if (m->vel[1] < 0.0f && m->actionState == 0) { 218 interaction = INT_GROUND_POUND_OR_TWIRL; 219 } 220 } else if (action == ACT_SLIDE_KICK || action == ACT_SLIDE_KICK_SLIDE) { 221 interaction = INT_SLIDE_KICK; 222 } else if (action & ACT_FLAG_RIDING_SHELL) { 223 interaction = INT_FAST_ATTACK_OR_SHELL; 224 } else if (m->forwardVel <= -26.0f || 26.0f <= m->forwardVel) { 225 interaction = INT_FAST_ATTACK_OR_SHELL; 226 } 227 } 228 229 // Prior to this, the interaction type could be overwritten. This requires, however, 230 // that the interaction not be set prior. This specifically overrides turning a ground 231 // pound into just a bounce. 232 if (interaction == 0 && (action & ACT_FLAG_AIR)) { 233 if (m->vel[1] < 0.0f) { 234 if (m->pos[1] > o->oPosY) { 235 interaction = INT_HIT_FROM_ABOVE; 236 } 237 } else { 238 if (m->pos[1] < o->oPosY) { 239 interaction = INT_HIT_FROM_BELOW; 240 } 241 } 242 } 243 244 return interaction; 245 } 246 247 /** 248 * Sets the interaction types for INT_STATUS_INTERACTED, INT_STATUS_WAS_ATTACKED 249 */ 250 u32 attack_object(struct Object *o, s32 interaction) { 251 u32 attackType = 0; 252 253 switch (interaction) { 254 case INT_GROUND_POUND_OR_TWIRL: 255 attackType = ATTACK_GROUND_POUND_OR_TWIRL; 256 break; 257 case INT_PUNCH: 258 attackType = ATTACK_PUNCH; 259 break; 260 case INT_KICK: 261 case INT_TRIP: 262 attackType = ATTACK_KICK_OR_TRIP; 263 break; 264 case INT_SLIDE_KICK: 265 case INT_FAST_ATTACK_OR_SHELL: 266 attackType = ATTACK_FAST_ATTACK; 267 break; 268 case INT_HIT_FROM_ABOVE: 269 attackType = ATTACK_FROM_ABOVE; 270 break; 271 case INT_HIT_FROM_BELOW: 272 attackType = ATTACK_FROM_BELOW; 273 break; 274 } 275 276 o->oInteractStatus = attackType + (INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED); 277 return attackType; 278 } 279 280 void mario_stop_riding_object(struct MarioState *m) { 281 if (m->riddenObj != NULL) { 282 m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING; 283 stop_shell_music(); 284 m->riddenObj = NULL; 285 } 286 } 287 288 void mario_grab_used_object(struct MarioState *m) { 289 if (m->heldObj == NULL) { 290 m->heldObj = m->usedObj; 291 obj_set_held_state(m->heldObj, bhvCarrySomething3); 292 } 293 } 294 295 void mario_drop_held_object(struct MarioState *m) { 296 if (m->heldObj != NULL) { 297 if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { 298 stop_shell_music(); 299 } 300 301 obj_set_held_state(m->heldObj, bhvCarrySomething4); 302 303 // ! When dropping an object instead of throwing it, it will be put at Mario's 304 // y-positon instead of the HOLP's y-position. This fact is often exploited when 305 // cloning objects. 306 m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0]; 307 m->heldObj->oPosY = m->pos[1]; 308 m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2]; 309 310 m->heldObj->oMoveAngleYaw = m->faceAngle[1]; 311 312 m->heldObj = NULL; 313 } 314 } 315 316 void mario_throw_held_object(struct MarioState *m) { 317 if (m->heldObj != NULL) { 318 if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { 319 stop_shell_music(); 320 } 321 322 obj_set_held_state(m->heldObj, bhvCarrySomething5); 323 324 m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]); 325 m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1]; 326 m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]); 327 328 m->heldObj->oMoveAngleYaw = m->faceAngle[1]; 329 330 m->heldObj = NULL; 331 } 332 } 333 334 void mario_stop_riding_and_holding(struct MarioState *m) { 335 mario_drop_held_object(m); 336 mario_stop_riding_object(m); 337 338 if (m->action == ACT_RIDING_HOOT) { 339 m->usedObj->oInteractStatus = FALSE; 340 m->usedObj->oHootMarioReleaseTime = gGlobalTimer; 341 } 342 } 343 344 u32 does_mario_have_normal_cap_on_head(struct MarioState *m) { 345 return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); 346 } 347 348 void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) { 349 struct Object *capObject; 350 351 if (does_mario_have_normal_cap_on_head(m)) { 352 save_file_set_cap_pos(m->pos[0], m->pos[1], m->pos[2]); 353 354 m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); 355 356 capObject = spawn_object(m->marioObj, MODEL_MARIOS_CAP, bhvNormalCap); 357 358 capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f; 359 capObject->oForwardVel = capSpeed; 360 capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400); 361 362 if (m->forwardVel < 0.0f) { 363 capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000); 364 } 365 } 366 } 367 368 u32 mario_lose_cap_to_enemy(u32 arg) { 369 u32 wasWearingCap = FALSE; 370 371 if (does_mario_have_normal_cap_on_head(gMarioState)) { 372 save_file_set_flags(arg == 1 ? SAVE_FLAG_CAP_ON_KLEPTO : SAVE_FLAG_CAP_ON_UKIKI); 373 gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); 374 wasWearingCap = TRUE; 375 } 376 377 return wasWearingCap; 378 } 379 380 void mario_retrieve_cap(void) { 381 mario_drop_held_object(gMarioState); 382 save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI); 383 gMarioState->flags &= ~MARIO_CAP_ON_HEAD; 384 gMarioState->flags |= MARIO_NORMAL_CAP | MARIO_CAP_IN_HAND; 385 } 386 387 u32 able_to_grab_object(struct MarioState *m, UNUSED struct Object *o) { 388 u32 action = m->action; 389 390 if (action == ACT_DIVE_SLIDE || action == ACT_DIVE) { 391 if (!(o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { 392 return TRUE; 393 } 394 } else if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING) { 395 if (m->actionArg < 2) { 396 return TRUE; 397 } 398 } 399 400 return FALSE; 401 } 402 403 struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType) { 404 s32 i; 405 struct Object *object; 406 407 for (i = 0; i < m->marioObj->numCollidedObjs; i++) { 408 object = m->marioObj->collidedObjs[i]; 409 410 if (object->oInteractType == interactType) { 411 return object; 412 } 413 } 414 415 return NULL; 416 } 417 418 u32 mario_check_object_grab(struct MarioState *m) { 419 u32 result = FALSE; 420 const BehaviorScript *script; 421 422 if (m->input & INPUT_INTERACT_OBJ_GRABBABLE) { 423 script = virtual_to_segmented(0x13, m->interactObj->behavior); 424 425 if (script == bhvBowser) { 426 s16 facingDYaw = m->faceAngle[1] - m->interactObj->oMoveAngleYaw; 427 if (facingDYaw >= -0x5555 && facingDYaw <= 0x5555) { 428 m->faceAngle[1] = m->interactObj->oMoveAngleYaw; 429 m->usedObj = m->interactObj; 430 result = set_mario_action(m, ACT_PICKING_UP_BOWSER, 0); 431 } 432 } else { 433 s16 facingDYaw = mario_obj_angle_to_object(m, m->interactObj) - m->faceAngle[1]; 434 if (facingDYaw >= -0x2AAA && facingDYaw <= 0x2AAA) { 435 m->usedObj = m->interactObj; 436 437 if (!(m->action & ACT_FLAG_AIR)) { 438 set_mario_action( 439 m, (m->action & ACT_FLAG_DIVING) ? ACT_DIVE_PICKING_UP : ACT_PICKING_UP, 0); 440 } 441 442 result = TRUE; 443 } 444 } 445 } 446 447 return result; 448 } 449 450 u32 bully_knock_back_mario(struct MarioState *mario) { 451 struct BullyCollisionData marioData; 452 struct BullyCollisionData bullyData; 453 s16 newMarioYaw; 454 s16 newBullyYaw; 455 s16 marioDYaw; 456 UNUSED s16 bullyDYaw; 457 458 u32 bonkAction = 0; 459 460 struct Object *bully = mario->interactObj; 461 462 //! Conversion ratios multiply to more than 1 (could allow unbounded speed 463 // with bonk cancel - but this isn't important for regular bully battery) 464 f32 bullyToMarioRatio = bully->hitboxRadius * 3 / 53; 465 f32 marioToBullyRatio = 53.0f / bully->hitboxRadius; 466 467 init_bully_collision_data(&marioData, mario->pos[0], mario->pos[2], mario->forwardVel, 468 mario->faceAngle[1], bullyToMarioRatio, 52.0f); 469 470 init_bully_collision_data(&bullyData, bully->oPosX, bully->oPosZ, bully->oForwardVel, 471 bully->oMoveAngleYaw, marioToBullyRatio, bully->hitboxRadius + 2.0f); 472 473 if (mario->forwardVel != 0.0f) { 474 transfer_bully_speed(&marioData, &bullyData); 475 } else { 476 transfer_bully_speed(&bullyData, &marioData); 477 } 478 479 newMarioYaw = atan2s(marioData.velZ, marioData.velX); 480 newBullyYaw = atan2s(bullyData.velZ, bullyData.velX); 481 482 marioDYaw = newMarioYaw - mario->faceAngle[1]; 483 bullyDYaw = newBullyYaw - bully->oMoveAngleYaw; 484 485 mario->faceAngle[1] = newMarioYaw; 486 mario->forwardVel = sqrtf(marioData.velX * marioData.velX + marioData.velZ * marioData.velZ); 487 mario->pos[0] = marioData.posX; 488 mario->pos[2] = marioData.posZ; 489 490 bully->oMoveAngleYaw = newBullyYaw; 491 bully->oForwardVel = sqrtf(bullyData.velX * bullyData.velX + bullyData.velZ * bullyData.velZ); 492 bully->oPosX = bullyData.posX; 493 bully->oPosZ = bullyData.posZ; 494 495 if (marioDYaw < -0x4000 || marioDYaw > 0x4000) { 496 mario->faceAngle[1] += 0x8000; 497 mario->forwardVel *= -1.0f; 498 499 if (mario->action & ACT_FLAG_AIR) { 500 bonkAction = ACT_BACKWARD_AIR_KB; 501 } else { 502 bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; 503 } 504 } else { 505 if (mario->action & ACT_FLAG_AIR) { 506 bonkAction = ACT_FORWARD_AIR_KB; 507 } else { 508 bonkAction = ACT_SOFT_FORWARD_GROUND_KB; 509 } 510 } 511 512 return bonkAction; 513 } 514 515 void bounce_off_object(struct MarioState *m, struct Object *o, f32 velY) { 516 m->pos[1] = o->oPosY + o->hitboxHeight; 517 m->vel[1] = velY; 518 519 m->flags &= ~MARIO_UNKNOWN_08; 520 521 play_sound(SOUND_ACTION_BOUNCE_OFF_OBJECT, m->marioObj->header.gfx.cameraToObject); 522 } 523 524 void hit_object_from_below(struct MarioState *m, UNUSED struct Object *o) { 525 m->vel[1] = 0.0f; 526 set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW); 527 } 528 529 UNUSED static u32 unused_determine_knockback_action(struct MarioState *m) { 530 u32 bonkAction; 531 s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); 532 s16 facingDYaw = angleToObject - m->faceAngle[1]; 533 534 if (m->forwardVel < 16.0f) { 535 m->forwardVel = 16.0f; 536 } 537 538 m->faceAngle[1] = angleToObject; 539 540 if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { 541 m->forwardVel *= -1.0f; 542 if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { 543 bonkAction = ACT_BACKWARD_AIR_KB; 544 } else { 545 bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; 546 } 547 } else { 548 m->faceAngle[1] += 0x8000; 549 if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { 550 bonkAction = ACT_FORWARD_AIR_KB; 551 } else { 552 bonkAction = ACT_SOFT_FORWARD_GROUND_KB; 553 } 554 } 555 556 return bonkAction; 557 } 558 559 u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) { 560 u32 bonkAction; 561 562 s16 terrainIndex = 0; // 1 = air, 2 = water, 0 = default 563 s16 strengthIndex = 0; 564 565 s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); 566 s16 facingDYaw = angleToObject - m->faceAngle[1]; 567 s16 remainingHealth = m->health - 0x40 * m->hurtCounter; 568 569 if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) { 570 terrainIndex = 2; 571 } else if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { 572 terrainIndex = 1; 573 } 574 575 if (remainingHealth < 0x100) { 576 strengthIndex = 2; 577 } else if (m->interactObj->oDamageOrCoinValue >= 4) { 578 strengthIndex = 2; 579 } else if (m->interactObj->oDamageOrCoinValue >= 2) { 580 strengthIndex = 1; 581 } 582 583 m->faceAngle[1] = angleToObject; 584 585 if (terrainIndex == 2) { 586 if (m->forwardVel < 28.0f) { 587 mario_set_forward_vel(m, 28.0f); 588 } 589 590 if (m->pos[1] >= m->interactObj->oPosY) { 591 if (m->vel[1] < 20.0f) { 592 m->vel[1] = 20.0f; 593 } 594 } else { 595 if (m->vel[1] > 0.0f) { 596 m->vel[1] = 0.0f; 597 } 598 } 599 } else { 600 if (m->forwardVel < 16.0f) { 601 mario_set_forward_vel(m, 16.0f); 602 } 603 } 604 605 if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) { 606 m->forwardVel *= -1.0f; 607 bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex]; 608 } else { 609 m->faceAngle[1] += 0x8000; 610 bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex]; 611 } 612 613 return bonkAction; 614 } 615 616 void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 padding) { 617 f32 minDistance = o->hitboxRadius + m->marioObj->hitboxRadius + padding; 618 619 f32 offsetX = m->pos[0] - o->oPosX; 620 f32 offsetZ = m->pos[2] - o->oPosZ; 621 f32 distance = sqrtf(offsetX * offsetX + offsetZ * offsetZ); 622 623 if (distance < minDistance) { 624 struct Surface *floor; 625 s16 pushAngle; 626 f32 newMarioX; 627 f32 newMarioZ; 628 629 if (distance == 0.0f) { 630 pushAngle = m->faceAngle[1]; 631 } else { 632 pushAngle = atan2s(offsetZ, offsetX); 633 } 634 635 newMarioX = o->oPosX + minDistance * sins(pushAngle); 636 newMarioZ = o->oPosZ + minDistance * coss(pushAngle); 637 638 f32_find_wall_collision(&newMarioX, &m->pos[1], &newMarioZ, 60.0f, 50.0f); 639 640 find_floor(newMarioX, m->pos[1], newMarioZ, &floor); 641 if (floor != NULL) { 642 //! Doesn't update Mario's referenced floor (allows oob death when 643 // an object pushes you into a steep slope while in a ground action) 644 m->pos[0] = newMarioX; 645 m->pos[2] = newMarioZ; 646 } 647 } 648 } 649 650 void bounce_back_from_attack(struct MarioState *m, u32 interaction) { 651 if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP)) { 652 if (m->action == ACT_PUNCHING) { 653 m->action = ACT_MOVE_PUNCHING; 654 } 655 656 if (m->action & ACT_FLAG_AIR) { 657 mario_set_forward_vel(m, -16.0f); 658 } else { 659 mario_set_forward_vel(m, -48.0f); 660 } 661 662 set_camera_shake_from_hit(SHAKE_ATTACK); 663 m->particleFlags |= PARTICLE_TRIANGLE; 664 } 665 666 if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP | INT_FAST_ATTACK_OR_SHELL)) { 667 play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); 668 } 669 } 670 671 u32 should_push_or_pull_door(struct MarioState *m, struct Object *o) { 672 f32 dx = o->oPosX - m->pos[0]; 673 f32 dz = o->oPosZ - m->pos[2]; 674 675 s16 dYaw = o->oMoveAngleYaw - atan2s(dz, dx); 676 677 return (dYaw >= -0x4000 && dYaw <= 0x4000) ? 0x00000001 : 0x00000002; 678 } 679 680 u32 take_damage_from_interact_object(struct MarioState *m) { 681 s32 shake; 682 s32 damage = m->interactObj->oDamageOrCoinValue; 683 684 if (damage >= 4) { 685 shake = SHAKE_LARGE_DAMAGE; 686 } else if (damage >= 2) { 687 shake = SHAKE_MED_DAMAGE; 688 } else { 689 shake = SHAKE_SMALL_DAMAGE; 690 } 691 692 if (!(m->flags & MARIO_CAP_ON_HEAD)) { 693 damage += (damage + 1) / 2; 694 } 695 696 if (m->flags & MARIO_METAL_CAP) { 697 damage = 0; 698 } 699 700 m->hurtCounter += 4 * damage; 701 702 #if ENABLE_RUMBLE 703 queue_rumble_data(5, 80); 704 #endif 705 set_camera_shake_from_hit(shake); 706 707 return damage; 708 } 709 710 u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) { 711 u32 damage; 712 713 if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) 714 && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 715 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; 716 m->interactObj = o; 717 718 damage = take_damage_from_interact_object(m); 719 720 if (o->oInteractionSubtype & INT_SUBTYPE_BIG_KNOCKBACK) { 721 m->forwardVel = 40.0f; 722 } 723 724 if (o->oDamageOrCoinValue > 0) { 725 play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); 726 } 727 728 update_mario_sound_and_camera(m); 729 return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), 730 damage); 731 } 732 733 return FALSE; 734 } 735 736 void reset_mario_pitch(struct MarioState *m) { 737 if (m->action == ACT_WATER_JUMP || m->action == ACT_SHOT_FROM_CANNON || m->action == ACT_FLYING) { 738 set_camera_mode(m->area->camera, m->area->camera->defMode, 1); 739 m->faceAngle[0] = 0; 740 } 741 } 742 743 u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 744 m->numCoins += o->oDamageOrCoinValue; 745 m->healCounter += 4 * o->oDamageOrCoinValue; 746 747 o->oInteractStatus = INT_STATUS_INTERACTED; 748 749 if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) 750 && m->numCoins - o->oDamageOrCoinValue < 100 && m->numCoins >= 100) { 751 bhv_spawn_star_no_level_exit(STAR_INDEX_100_COINS); 752 } 753 754 #if ENABLE_RUMBLE 755 if (o->oDamageOrCoinValue >= 2) { 756 queue_rumble_data(5, 80); 757 } 758 #endif 759 760 return FALSE; 761 } 762 763 u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 764 m->healCounter += 4 * o->oDamageOrCoinValue; 765 o->oInteractStatus = INT_STATUS_INTERACTED; 766 return FALSE; 767 } 768 769 u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 770 u32 starIndex; 771 u32 starGrabAction = ACT_STAR_DANCE_EXIT; 772 u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0; 773 u32 grandStar = (o->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0; 774 775 if (m->health >= 0x100) { 776 mario_stop_riding_and_holding(m); 777 #if ENABLE_RUMBLE 778 queue_rumble_data(5, 80); 779 #endif 780 781 if (!noExit) { 782 m->hurtCounter = 0; 783 m->healCounter = 0; 784 if (m->capTimer > 1) { 785 m->capTimer = 1; 786 } 787 } 788 789 if (noExit) { 790 starGrabAction = ACT_STAR_DANCE_NO_EXIT; 791 } 792 793 if (m->action & ACT_FLAG_SWIMMING) { 794 starGrabAction = ACT_STAR_DANCE_WATER; 795 } 796 797 if (m->action & ACT_FLAG_METAL_WATER) { 798 starGrabAction = ACT_STAR_DANCE_WATER; 799 } 800 801 if (m->action & ACT_FLAG_AIR) { 802 starGrabAction = ACT_FALL_AFTER_STAR_GRAB; 803 } 804 805 spawn_object(o, MODEL_NONE, bhvStarKeyCollectionPuffSpawner); 806 807 o->oInteractStatus = INT_STATUS_INTERACTED; 808 m->interactObj = o; 809 m->usedObj = o; 810 811 starIndex = (o->oBhvParams >> 24) & 0x1F; 812 save_file_collect_star_or_key(m->numCoins, starIndex); 813 814 m->numStars = 815 save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); 816 817 if (!noExit) { 818 drop_queued_background_music(); 819 fadeout_level_music(126); 820 } 821 822 play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject); 823 #ifndef VERSION_JP 824 update_mario_sound_and_camera(m); 825 #endif 826 827 if (grandStar) { 828 return set_mario_action(m, ACT_JUMBO_STAR_CUTSCENE, 0); 829 } 830 831 return set_mario_action(m, starGrabAction, noExit + 2 * grandStar); 832 } 833 834 return FALSE; 835 } 836 837 u32 interact_bbh_entrance(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 838 if (m->action != ACT_BBH_ENTER_SPIN && m->action != ACT_BBH_ENTER_JUMP) { 839 mario_stop_riding_and_holding(m); 840 841 o->oInteractStatus = INT_STATUS_INTERACTED; 842 m->interactObj = o; 843 m->usedObj = o; 844 845 if (m->action & ACT_FLAG_AIR) { 846 return set_mario_action(m, ACT_BBH_ENTER_SPIN, 0); 847 } 848 849 return set_mario_action(m, ACT_BBH_ENTER_JUMP, 0); 850 } 851 852 return FALSE; 853 } 854 855 u32 interact_warp(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 856 u32 action; 857 858 if (o->oInteractionSubtype & INT_SUBTYPE_FADING_WARP) { 859 action = m->action; 860 861 if (action == ACT_TELEPORT_FADE_IN) { 862 sJustTeleported = TRUE; 863 864 } else if (!sJustTeleported) { 865 if (action == ACT_IDLE || action == ACT_PANTING || action == ACT_STANDING_AGAINST_WALL 866 || action == ACT_CROUCHING) { 867 m->interactObj = o; 868 m->usedObj = o; 869 870 sJustTeleported = TRUE; 871 return set_mario_action(m, ACT_TELEPORT_FADE_OUT, 0); 872 } 873 } 874 } else { 875 if (m->action != ACT_EMERGE_FROM_PIPE) { 876 o->oInteractStatus = INT_STATUS_INTERACTED; 877 m->interactObj = o; 878 m->usedObj = o; 879 880 #if ENABLE_RUMBLE 881 if (o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8)) { 882 play_sound(SOUND_MENU_ENTER_PIPE, m->marioObj->header.gfx.cameraToObject); 883 queue_rumble_data(15, 80); 884 } else { 885 play_sound(SOUND_MENU_ENTER_HOLE, m->marioObj->header.gfx.cameraToObject); 886 queue_rumble_data(12, 80); 887 } 888 #else 889 play_sound(o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8) 890 ? SOUND_MENU_ENTER_PIPE 891 : SOUND_MENU_ENTER_HOLE, 892 m->marioObj->header.gfx.cameraToObject); 893 #endif 894 895 mario_stop_riding_object(m); 896 return set_mario_action(m, ACT_DISAPPEARED, (WARP_OP_WARP_OBJECT << 16) + 2); 897 } 898 } 899 900 return FALSE; 901 } 902 903 u32 interact_warp_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 904 u32 doorAction = 0; 905 u32 saveFlags = save_file_get_flags(); 906 s16 warpDoorId = o->oBhvParams >> 24; 907 u32 actionArg; 908 909 if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) { 910 if (warpDoorId == 1 && !(saveFlags & SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) { 911 if (!(saveFlags & SAVE_FLAG_HAVE_KEY_2)) { 912 if (!sDisplayingDoorText) { 913 set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 914 (saveFlags & SAVE_FLAG_HAVE_KEY_1) ? DIALOG_023 : DIALOG_022); 915 } 916 sDisplayingDoorText = TRUE; 917 918 return FALSE; 919 } 920 921 doorAction = ACT_UNLOCKING_KEY_DOOR; 922 } 923 924 if (warpDoorId == 2 && !(saveFlags & SAVE_FLAG_UNLOCKED_BASEMENT_DOOR)) { 925 if (!(saveFlags & SAVE_FLAG_HAVE_KEY_1)) { 926 if (!sDisplayingDoorText) { 927 // Moat door skip was intended confirmed 928 set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 929 (saveFlags & SAVE_FLAG_HAVE_KEY_2) ? DIALOG_023 : DIALOG_022); 930 } 931 sDisplayingDoorText = TRUE; 932 933 return FALSE; 934 } 935 936 doorAction = ACT_UNLOCKING_KEY_DOOR; 937 } 938 939 if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) { 940 actionArg = should_push_or_pull_door(m, o) + 0x00000004; 941 942 if (doorAction == 0) { 943 if (actionArg & 0x00000001) { 944 doorAction = ACT_PULLING_DOOR; 945 } else { 946 doorAction = ACT_PUSHING_DOOR; 947 } 948 } 949 950 m->interactObj = o; 951 m->usedObj = o; 952 return set_mario_action(m, doorAction, actionArg); 953 } 954 } 955 956 return FALSE; 957 } 958 959 u32 get_door_save_file_flag(struct Object *door) { 960 u32 saveFileFlag = 0; 961 s16 requiredNumStars = door->oBhvParams >> 24; 962 963 s16 isCCMDoor = door->oPosX < 0.0f; 964 s16 isPSSDoor = door->oPosY > 500.0f; 965 966 switch (requiredNumStars) { 967 case 1: 968 if (isPSSDoor) { 969 saveFileFlag = SAVE_FLAG_UNLOCKED_PSS_DOOR; 970 } else { 971 saveFileFlag = SAVE_FLAG_UNLOCKED_WF_DOOR; 972 } 973 break; 974 975 case 3: 976 if (isCCMDoor) { 977 saveFileFlag = SAVE_FLAG_UNLOCKED_CCM_DOOR; 978 } else { 979 saveFileFlag = SAVE_FLAG_UNLOCKED_JRB_DOOR; 980 } 981 break; 982 983 case 8: 984 saveFileFlag = SAVE_FLAG_UNLOCKED_BITDW_DOOR; 985 break; 986 987 case 30: 988 saveFileFlag = SAVE_FLAG_UNLOCKED_BITFS_DOOR; 989 break; 990 991 case 50: 992 saveFileFlag = SAVE_FLAG_UNLOCKED_50_STAR_DOOR; 993 break; 994 } 995 996 return saveFileFlag; 997 } 998 999 u32 interact_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1000 s16 requiredNumStars = o->oBhvParams >> 24; 1001 s16 numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); 1002 1003 if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) { 1004 if (numStars >= requiredNumStars) { 1005 u32 actionArg = should_push_or_pull_door(m, o); 1006 u32 enterDoorAction; 1007 u32 doorSaveFileFlag; 1008 1009 if (actionArg & 0x00000001) { 1010 enterDoorAction = ACT_PULLING_DOOR; 1011 } else { 1012 enterDoorAction = ACT_PUSHING_DOOR; 1013 } 1014 1015 doorSaveFileFlag = get_door_save_file_flag(o); 1016 m->interactObj = o; 1017 m->usedObj = o; 1018 1019 if (o->oInteractionSubtype & INT_SUBTYPE_STAR_DOOR) { 1020 enterDoorAction = ACT_ENTERING_STAR_DOOR; 1021 } 1022 1023 if (doorSaveFileFlag != 0 && !(save_file_get_flags() & doorSaveFileFlag)) { 1024 enterDoorAction = ACT_UNLOCKING_STAR_DOOR; 1025 } 1026 1027 return set_mario_action(m, enterDoorAction, actionArg); 1028 } else if (!sDisplayingDoorText) { 1029 u32 text = DIALOG_022 << 16; 1030 1031 switch (requiredNumStars) { 1032 case 1: 1033 text = DIALOG_024 << 16; 1034 break; 1035 case 3: 1036 text = DIALOG_025 << 16; 1037 break; 1038 case 8: 1039 text = DIALOG_026 << 16; 1040 break; 1041 case 30: 1042 text = DIALOG_027 << 16; 1043 break; 1044 case 50: 1045 text = DIALOG_028 << 16; 1046 break; 1047 case 70: 1048 text = DIALOG_029 << 16; 1049 break; 1050 } 1051 1052 text += requiredNumStars - numStars; 1053 1054 sDisplayingDoorText = TRUE; 1055 return set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, text); 1056 } 1057 } else if (m->action == ACT_IDLE && sDisplayingDoorText == TRUE && requiredNumStars == 70) { 1058 m->interactObj = o; 1059 m->usedObj = o; 1060 return set_mario_action(m, ACT_ENTERING_STAR_DOOR, should_push_or_pull_door(m, o)); 1061 } 1062 1063 return FALSE; 1064 } 1065 1066 u32 interact_cannon_base(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1067 if (m->action != ACT_IN_CANNON) { 1068 mario_stop_riding_and_holding(m); 1069 o->oInteractStatus = INT_STATUS_INTERACTED; 1070 m->interactObj = o; 1071 m->usedObj = o; 1072 return set_mario_action(m, ACT_IN_CANNON, 0); 1073 } 1074 1075 return FALSE; 1076 } 1077 1078 u32 interact_igloo_barrier(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1079 //! Sets used object without changing action (LOTS of interesting glitches, 1080 // but unfortunately the igloo barrier is the only object with this interaction 1081 // type) 1082 m->interactObj = o; 1083 m->usedObj = o; 1084 push_mario_out_of_object(m, o, 5.0f); 1085 return FALSE; 1086 } 1087 1088 u32 interact_tornado(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1089 struct Object *marioObj = m->marioObj; 1090 1091 if (m->action != ACT_TORNADO_TWIRLING && m->action != ACT_SQUISHED) { 1092 mario_stop_riding_and_holding(m); 1093 mario_set_forward_vel(m, 0.0f); 1094 update_mario_sound_and_camera(m); 1095 1096 o->oInteractStatus = INT_STATUS_INTERACTED; 1097 m->interactObj = o; 1098 m->usedObj = o; 1099 1100 marioObj->oMarioTornadoYawVel = 0x400; 1101 marioObj->oMarioTornadoPosY = m->pos[1] - o->oPosY; 1102 1103 play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); 1104 #if ENABLE_RUMBLE 1105 queue_rumble_data(30, 60); 1106 #endif 1107 return set_mario_action(m, ACT_TORNADO_TWIRLING, m->action == ACT_TWIRLING); 1108 } 1109 1110 return FALSE; 1111 } 1112 1113 u32 interact_whirlpool(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1114 struct Object *marioObj = m->marioObj; 1115 1116 if (m->action != ACT_CAUGHT_IN_WHIRLPOOL) { 1117 mario_stop_riding_and_holding(m); 1118 o->oInteractStatus = INT_STATUS_INTERACTED; 1119 m->interactObj = o; 1120 m->usedObj = o; 1121 1122 m->forwardVel = 0.0f; 1123 1124 marioObj->oMarioWhirlpoolPosY = m->pos[1] - o->oPosY; 1125 1126 play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); 1127 #if ENABLE_RUMBLE 1128 queue_rumble_data(30, 60); 1129 #endif 1130 return set_mario_action(m, ACT_CAUGHT_IN_WHIRLPOOL, 0); 1131 } 1132 1133 return FALSE; 1134 } 1135 1136 u32 interact_strong_wind(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1137 UNUSED struct Object *marioObj = m->marioObj; 1138 1139 if (m->action != ACT_GETTING_BLOWN) { 1140 mario_stop_riding_and_holding(m); 1141 o->oInteractStatus = INT_STATUS_INTERACTED; 1142 m->interactObj = o; 1143 m->usedObj = o; 1144 1145 m->faceAngle[1] = o->oMoveAngleYaw + 0x8000; 1146 m->gettingBlownGravity = 0.4f; 1147 m->forwardVel = -24.0f; 1148 m->vel[1] = 12.0f; 1149 1150 play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); 1151 update_mario_sound_and_camera(m); 1152 return set_mario_action(m, ACT_GETTING_BLOWN, 0); 1153 } 1154 1155 return FALSE; 1156 } 1157 1158 u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1159 u32 burningAction = ACT_BURNING_JUMP; 1160 1161 if (!sInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP) 1162 && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1163 #if ENABLE_RUMBLE 1164 queue_rumble_data(5, 80); 1165 #endif 1166 o->oInteractStatus = INT_STATUS_INTERACTED; 1167 m->interactObj = o; 1168 1169 if ((m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) 1170 || m->waterLevel - m->pos[1] > 50.0f) { 1171 play_sound(SOUND_GENERAL_FLAME_OUT, m->marioObj->header.gfx.cameraToObject); 1172 } else { 1173 m->marioObj->oMarioBurnTimer = 0; 1174 update_mario_sound_and_camera(m); 1175 play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject); 1176 1177 if ((m->action & ACT_FLAG_AIR) && m->vel[1] <= 0.0f) { 1178 burningAction = ACT_BURNING_FALL; 1179 } 1180 1181 return drop_and_set_mario_action(m, burningAction, 1); 1182 } 1183 } 1184 1185 return FALSE; 1186 } 1187 1188 u32 interact_snufit_bullet(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1189 if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)) { 1190 if (m->flags & MARIO_METAL_CAP) { 1191 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED; 1192 play_sound(SOUND_ACTION_UNKNOWN458, m->marioObj->header.gfx.cameraToObject); 1193 } else { 1194 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; 1195 m->interactObj = o; 1196 take_damage_from_interact_object(m); 1197 1198 play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); 1199 update_mario_sound_and_camera(m); 1200 1201 return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), 1202 o->oDamageOrCoinValue); 1203 } 1204 } 1205 1206 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1207 sDelayInvincTimer = TRUE; 1208 } 1209 1210 return FALSE; 1211 } 1212 1213 u32 interact_clam_or_bubba(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1214 if (o->oInteractionSubtype & INT_SUBTYPE_EATS_MARIO) { 1215 o->oInteractStatus = INT_STATUS_INTERACTED; 1216 m->interactObj = o; 1217 return set_mario_action(m, ACT_EATEN_BY_BUBBA, 0); 1218 } else if (take_damage_and_knock_back(m, o)) { 1219 return TRUE; 1220 } 1221 1222 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1223 sDelayInvincTimer = TRUE; 1224 } 1225 1226 return TRUE; 1227 } 1228 1229 u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1230 UNUSED u8 filler[4]; 1231 1232 u32 interaction; 1233 if (m->flags & MARIO_METAL_CAP) { 1234 interaction = INT_FAST_ATTACK_OR_SHELL; 1235 } else { 1236 interaction = determine_interaction(m, o); 1237 } 1238 1239 m->interactObj = o; 1240 1241 if (interaction & INT_ATTACK_NOT_FROM_BELOW) { 1242 #if ENABLE_RUMBLE 1243 queue_rumble_data(5, 80); 1244 #endif 1245 push_mario_out_of_object(m, o, 5.0f); 1246 1247 m->forwardVel = -16.0f; 1248 o->oMoveAngleYaw = m->faceAngle[1]; 1249 o->oForwardVel = 3392.0f / o->hitboxRadius; 1250 1251 attack_object(o, interaction); 1252 bounce_back_from_attack(m, interaction); 1253 return TRUE; 1254 } 1255 1256 else if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) 1257 && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1258 o->oInteractStatus = INT_STATUS_INTERACTED; 1259 m->invincTimer = 2; 1260 1261 update_mario_sound_and_camera(m); 1262 play_sound(SOUND_MARIO_EEUH, m->marioObj->header.gfx.cameraToObject); 1263 play_sound(SOUND_OBJ_BULLY_METAL, m->marioObj->header.gfx.cameraToObject); 1264 1265 push_mario_out_of_object(m, o, 5.0f); 1266 drop_and_set_mario_action(m, bully_knock_back_mario(m), 0); 1267 #if ENABLE_RUMBLE 1268 queue_rumble_data(5, 80); 1269 #endif 1270 return TRUE; 1271 } 1272 1273 return FALSE; 1274 } 1275 1276 u32 interact_shock(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1277 if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) 1278 && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1279 u32 actionArg = (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) == 0; 1280 1281 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; 1282 m->interactObj = o; 1283 1284 take_damage_from_interact_object(m); 1285 play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); 1286 #if ENABLE_RUMBLE 1287 queue_rumble_data(70, 60); 1288 #endif 1289 1290 if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) { 1291 return drop_and_set_mario_action(m, ACT_WATER_SHOCKED, 0); 1292 } else { 1293 update_mario_sound_and_camera(m); 1294 return drop_and_set_mario_action(m, ACT_SHOCKED, actionArg); 1295 } 1296 } 1297 1298 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1299 sDelayInvincTimer = TRUE; 1300 } 1301 1302 return FALSE; 1303 } 1304 1305 UNUSED static u32 interact_stub(UNUSED struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1306 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1307 sDelayInvincTimer = TRUE; 1308 } 1309 return FALSE; 1310 } 1311 1312 u32 interact_mr_blizzard(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1313 if (take_damage_and_knock_back(m, o)) { 1314 return TRUE; 1315 } 1316 1317 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1318 sDelayInvincTimer = TRUE; 1319 } 1320 1321 return FALSE; 1322 } 1323 1324 u32 interact_hit_from_below(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1325 UNUSED u8 filler[4]; 1326 1327 u32 interaction; 1328 if (m->flags & MARIO_METAL_CAP) { 1329 interaction = INT_FAST_ATTACK_OR_SHELL; 1330 } else { 1331 interaction = determine_interaction(m, o); 1332 } 1333 1334 if (interaction & INT_ANY_ATTACK) { 1335 #if ENABLE_RUMBLE 1336 queue_rumble_data(5, 80); 1337 #endif 1338 attack_object(o, interaction); 1339 bounce_back_from_attack(m, interaction); 1340 1341 if (interaction & INT_HIT_FROM_BELOW) { 1342 hit_object_from_below(m, o); 1343 } 1344 1345 if (interaction & INT_HIT_FROM_ABOVE) { 1346 if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) { 1347 bounce_off_object(m, o, 80.0f); 1348 reset_mario_pitch(m); 1349 #ifndef VERSION_JP 1350 play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject); 1351 #endif 1352 return drop_and_set_mario_action(m, ACT_TWIRLING, 0); 1353 } else { 1354 bounce_off_object(m, o, 30.0f); 1355 } 1356 } 1357 } else if (take_damage_and_knock_back(m, o)) { 1358 return TRUE; 1359 } 1360 1361 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1362 sDelayInvincTimer = TRUE; 1363 } 1364 1365 return FALSE; 1366 } 1367 1368 u32 interact_bounce_top(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1369 u32 interaction; 1370 if (m->flags & MARIO_METAL_CAP) { 1371 interaction = INT_FAST_ATTACK_OR_SHELL; 1372 } else { 1373 interaction = determine_interaction(m, o); 1374 } 1375 1376 if (interaction & INT_ATTACK_NOT_FROM_BELOW) { 1377 #if ENABLE_RUMBLE 1378 queue_rumble_data(5, 80); 1379 #endif 1380 attack_object(o, interaction); 1381 bounce_back_from_attack(m, interaction); 1382 1383 if (interaction & INT_HIT_FROM_ABOVE) { 1384 if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) { 1385 bounce_off_object(m, o, 80.0f); 1386 reset_mario_pitch(m); 1387 #ifndef VERSION_JP 1388 play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject); 1389 #endif 1390 return drop_and_set_mario_action(m, ACT_TWIRLING, 0); 1391 } else { 1392 bounce_off_object(m, o, 30.0f); 1393 } 1394 } 1395 } else if (take_damage_and_knock_back(m, o)) { 1396 return TRUE; 1397 } 1398 1399 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1400 sDelayInvincTimer = TRUE; 1401 } 1402 1403 return FALSE; 1404 } 1405 1406 u32 interact_unknown_08(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1407 u32 interaction = determine_interaction(m, o); 1408 1409 if (interaction & INT_PUNCH) { 1410 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED | ATTACK_PUNCH; 1411 bounce_back_from_attack(m, interaction); 1412 } else if (take_damage_and_knock_back(m, o)) { 1413 return TRUE; 1414 } 1415 1416 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1417 sDelayInvincTimer = TRUE; 1418 } 1419 1420 return FALSE; 1421 } 1422 1423 u32 interact_damage(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1424 if (take_damage_and_knock_back(m, o)) { 1425 return TRUE; 1426 } 1427 1428 if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { 1429 sDelayInvincTimer = TRUE; 1430 } 1431 1432 return FALSE; 1433 } 1434 1435 u32 interact_breakable(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1436 u32 interaction = determine_interaction(m, o); 1437 1438 if (interaction & INT_ATTACK_NOT_WEAK_FROM_ABOVE) { 1439 attack_object(o, interaction); 1440 bounce_back_from_attack(m, interaction); 1441 1442 m->interactObj = o; 1443 1444 switch (interaction) { 1445 case INT_HIT_FROM_ABOVE: 1446 bounce_off_object(m, o, 30.0f); //! Not in the 0x8F mask 1447 break; 1448 1449 case INT_HIT_FROM_BELOW: 1450 hit_object_from_below(m, o); 1451 break; 1452 } 1453 1454 return TRUE; 1455 } 1456 1457 return FALSE; 1458 } 1459 1460 u32 interact_koopa_shell(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1461 if (!(m->action & ACT_FLAG_RIDING_SHELL)) { 1462 u32 interaction = determine_interaction(m, o); 1463 1464 if (interaction == INT_HIT_FROM_ABOVE || m->action == ACT_WALKING 1465 || m->action == ACT_HOLD_WALKING) { 1466 m->interactObj = o; 1467 m->usedObj = o; 1468 m->riddenObj = o; 1469 1470 attack_object(o, interaction); 1471 update_mario_sound_and_camera(m); 1472 play_shell_music(); 1473 mario_drop_held_object(m); 1474 1475 //! Puts Mario in ground action even when in air, making it easy to 1476 // escape air actions into crouch slide (shell cancel) 1477 return set_mario_action(m, ACT_RIDING_SHELL_GROUND, 0); 1478 } 1479 1480 push_mario_out_of_object(m, o, 2.0f); 1481 } 1482 1483 return FALSE; 1484 } 1485 1486 u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1487 if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable) 1488 && (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { 1489 if (object_facing_mario(m, o, 0x2AAA)) { 1490 mario_stop_riding_and_holding(m); 1491 o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_GRABBED_MARIO; 1492 1493 m->faceAngle[1] = o->oMoveAngleYaw; 1494 m->interactObj = o; 1495 m->usedObj = o; 1496 1497 update_mario_sound_and_camera(m); 1498 play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); 1499 #if ENABLE_RUMBLE 1500 queue_rumble_data(5, 80); 1501 #endif 1502 return set_mario_action(m, ACT_GRABBED, 0); 1503 } 1504 } 1505 1506 push_mario_out_of_object(m, o, -5.0f); 1507 return FALSE; 1508 } 1509 1510 u32 interact_pole(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1511 s32 actionId = m->action & ACT_ID_MASK; 1512 if (actionId >= 0x080 && actionId < 0x0A0) { 1513 if (!(m->prevAction & ACT_FLAG_ON_POLE) || m->usedObj != o) { 1514 #if defined(VERSION_SH) || defined(VERSION_CN) 1515 f32 velConv = m->forwardVel; // conserve the velocity. 1516 struct Object *marioObj = m->marioObj; 1517 u32 lowSpeed; 1518 #else 1519 u32 lowSpeed = (m->forwardVel <= 10.0f); 1520 struct Object *marioObj = m->marioObj; 1521 #endif 1522 1523 mario_stop_riding_and_holding(m); 1524 1525 #if defined(VERSION_SH) || defined(VERSION_CN) 1526 lowSpeed = (velConv <= 10.0f); 1527 #endif 1528 1529 m->interactObj = o; 1530 m->usedObj = o; 1531 m->vel[1] = 0.0f; 1532 m->forwardVel = 0.0f; 1533 1534 marioObj->oMarioPoleUnk108 = 0; 1535 marioObj->oMarioPoleYawVel = 0; 1536 marioObj->oMarioPolePos = m->pos[1] - o->oPosY; 1537 1538 if (lowSpeed) { 1539 return set_mario_action(m, ACT_GRAB_POLE_SLOW, 0); 1540 } 1541 1542 //! @bug Using m->forwardVel here is assumed to be 0.0f due to the set from earlier. 1543 // This is fixed in the Shindou version. 1544 #if defined(VERSION_SH) || defined(VERSION_CN) 1545 marioObj->oMarioPoleYawVel = (s32)(velConv * 0x100 + 0x1000); 1546 #else 1547 marioObj->oMarioPoleYawVel = (s32)(m->forwardVel * 0x100 + 0x1000); 1548 #endif 1549 reset_mario_pitch(m); 1550 #if ENABLE_RUMBLE 1551 queue_rumble_data(5, 80); 1552 #endif 1553 return set_mario_action(m, ACT_GRAB_POLE_FAST, 0); 1554 } 1555 } 1556 1557 return FALSE; 1558 } 1559 1560 u32 interact_hoot(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1561 s32 actionId = m->action & ACT_ID_MASK; 1562 1563 //! Can pause to advance the global timer without falling too far, allowing 1564 // you to regrab after letting go. 1565 if (actionId >= 0x080 && actionId < 0x098 1566 && (gGlobalTimer - m->usedObj->oHootMarioReleaseTime > 30)) { 1567 mario_stop_riding_and_holding(m); 1568 1569 o->oInteractStatus = TRUE; //! Note: Not a flag, treated as a TRUE/FALSE statement 1570 m->interactObj = o; 1571 m->usedObj = o; 1572 1573 #if ENABLE_RUMBLE 1574 queue_rumble_data(5, 80); 1575 #endif 1576 update_mario_sound_and_camera(m); 1577 return set_mario_action(m, ACT_RIDING_HOOT, 0); 1578 } 1579 1580 return FALSE; 1581 } 1582 1583 u32 interact_cap(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1584 u32 capFlag = get_mario_cap_flag(o); 1585 u16 capMusic = 0; 1586 u16 capTime = 0; 1587 1588 if (m->action != ACT_GETTING_BLOWN && capFlag != 0) { 1589 m->interactObj = o; 1590 o->oInteractStatus = INT_STATUS_INTERACTED; 1591 1592 m->flags &= ~MARIO_CAP_ON_HEAD & ~MARIO_CAP_IN_HAND; 1593 m->flags |= capFlag; 1594 1595 switch (capFlag) { 1596 case MARIO_VANISH_CAP: 1597 capTime = 600; 1598 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP); 1599 break; 1600 1601 case MARIO_METAL_CAP: 1602 capTime = 600; 1603 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_METAL_CAP); 1604 break; 1605 1606 case MARIO_WING_CAP: 1607 capTime = 1800; 1608 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP); 1609 break; 1610 } 1611 1612 if (capTime > m->capTimer) { 1613 m->capTimer = capTime; 1614 } 1615 1616 if ((m->action & ACT_FLAG_IDLE) || m->action == ACT_WALKING) { 1617 m->flags |= MARIO_CAP_IN_HAND; 1618 set_mario_action(m, ACT_PUTTING_ON_CAP, 0); 1619 } else { 1620 m->flags |= MARIO_CAP_ON_HEAD; 1621 } 1622 1623 play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject); 1624 play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); 1625 1626 if (capMusic != 0) { 1627 play_cap_music(capMusic); 1628 } 1629 1630 return TRUE; 1631 } 1632 1633 return FALSE; 1634 } 1635 1636 u32 interact_grabbable(struct MarioState *m, u32 interactType, struct Object *o) { 1637 const BehaviorScript *script = virtual_to_segmented(0x13, o->behavior); 1638 1639 if (o->oInteractionSubtype & INT_SUBTYPE_KICKABLE) { 1640 u32 interaction = determine_interaction(m, o); 1641 if (interaction & (INT_KICK | INT_TRIP)) { 1642 attack_object(o, interaction); 1643 bounce_back_from_attack(m, interaction); 1644 return FALSE; 1645 } 1646 } 1647 1648 if ((o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { 1649 if (check_object_grab_mario(m, interactType, o)) { 1650 return TRUE; 1651 } 1652 } 1653 1654 if (able_to_grab_object(m, o)) { 1655 if (!(o->oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE)) { 1656 m->interactObj = o; 1657 m->input |= INPUT_INTERACT_OBJ_GRABBABLE; 1658 return TRUE; 1659 } 1660 } 1661 1662 if (script != bhvBowser) { 1663 push_mario_out_of_object(m, o, -5.0f); 1664 } 1665 1666 return FALSE; 1667 } 1668 1669 u32 mario_can_talk(struct MarioState *m, u32 arg) { 1670 s16 val6; 1671 1672 if ((m->action & ACT_FLAG_IDLE) != 0x00000000) { 1673 return TRUE; 1674 } 1675 1676 if (m->action == ACT_WALKING) { 1677 if (arg) { 1678 return TRUE; 1679 } 1680 1681 val6 = m->marioObj->header.gfx.animInfo.animID; 1682 1683 if (val6 == 0x0080 || val6 == 0x007F || val6 == 0x006C) { 1684 return TRUE; 1685 } 1686 } 1687 1688 return FALSE; 1689 } 1690 1691 #ifdef VERSION_JP 1692 #define READ_MASK (INPUT_B_PRESSED) 1693 #else 1694 #define READ_MASK (INPUT_B_PRESSED | INPUT_A_PRESSED) 1695 #endif 1696 1697 #ifdef VERSION_JP 1698 #define SIGN_RANGE 0x38E3 1699 #else 1700 #define SIGN_RANGE 0x4000 1701 #endif 1702 1703 u32 check_read_sign(struct MarioState *m, struct Object *o) { 1704 if ((m->input & READ_MASK) && mario_can_talk(m, 0) && object_facing_mario(m, o, SIGN_RANGE)) { 1705 s16 facingDYaw = (s16)(o->oMoveAngleYaw + 0x8000) - m->faceAngle[1]; 1706 if (facingDYaw >= -SIGN_RANGE && facingDYaw <= SIGN_RANGE) { 1707 f32 targetX = o->oPosX + 105.0f * sins(o->oMoveAngleYaw); 1708 f32 targetZ = o->oPosZ + 105.0f * coss(o->oMoveAngleYaw); 1709 1710 m->marioObj->oMarioReadingSignDYaw = facingDYaw; 1711 m->marioObj->oMarioReadingSignDPosX = targetX - m->pos[0]; 1712 m->marioObj->oMarioReadingSignDPosZ = targetZ - m->pos[2]; 1713 1714 m->interactObj = o; 1715 m->usedObj = o; 1716 return set_mario_action(m, ACT_READING_SIGN, 0); 1717 } 1718 } 1719 1720 return FALSE; 1721 } 1722 1723 u32 check_npc_talk(struct MarioState *m, struct Object *o) { 1724 if ((m->input & READ_MASK) && mario_can_talk(m, 1)) { 1725 s16 facingDYaw = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; 1726 if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { 1727 o->oInteractStatus = INT_STATUS_INTERACTED; 1728 1729 m->interactObj = o; 1730 m->usedObj = o; 1731 1732 push_mario_out_of_object(m, o, -10.0f); 1733 return set_mario_action(m, ACT_WAITING_FOR_DIALOG, 0); 1734 } 1735 } 1736 1737 push_mario_out_of_object(m, o, -10.0f); 1738 return FALSE; 1739 } 1740 1741 u32 interact_text(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { 1742 u32 interact = FALSE; 1743 1744 if (o->oInteractionSubtype & INT_SUBTYPE_SIGN) { 1745 interact = check_read_sign(m, o); 1746 } else if (o->oInteractionSubtype & INT_SUBTYPE_NPC) { 1747 interact = check_npc_talk(m, o); 1748 } else { 1749 push_mario_out_of_object(m, o, 2.0f); 1750 } 1751 1752 return interact; 1753 } 1754 1755 void check_kick_or_punch_wall(struct MarioState *m) { 1756 if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) { 1757 Vec3f detector; 1758 detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]); 1759 detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]); 1760 detector[1] = m->pos[1]; 1761 1762 if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) { 1763 if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) { 1764 if (m->action == ACT_PUNCHING) { 1765 m->action = ACT_MOVE_PUNCHING; 1766 } 1767 1768 mario_set_forward_vel(m, -48.0f); 1769 play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); 1770 m->particleFlags |= PARTICLE_TRIANGLE; 1771 } else if (m->action & ACT_FLAG_AIR) { 1772 mario_set_forward_vel(m, -16.0f); 1773 play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); 1774 m->particleFlags |= PARTICLE_TRIANGLE; 1775 } 1776 } 1777 } 1778 } 1779 1780 void mario_process_interactions(struct MarioState *m) { 1781 sDelayInvincTimer = FALSE; 1782 sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0; 1783 1784 if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) { 1785 s32 i; 1786 for (i = 0; i < ARRAY_COUNT(sInteractionHandlers); i++) { 1787 u32 interactType = sInteractionHandlers[i].interactType; 1788 if (m->collidedObjInteractTypes & interactType) { 1789 struct Object *object = mario_get_collided_object(m, interactType); 1790 1791 m->collidedObjInteractTypes &= ~interactType; 1792 1793 if (!(object->oInteractStatus & INT_STATUS_INTERACTED)) { 1794 if (sInteractionHandlers[i].handler(m, interactType, object)) { 1795 break; 1796 } 1797 } 1798 } 1799 } 1800 } 1801 1802 if (m->invincTimer > 0 && !sDelayInvincTimer) { 1803 m->invincTimer--; 1804 } 1805 1806 //! If the kick/punch flags are set and an object collision changes Mario's 1807 // action, he will get the kick/punch wall speed anyway. 1808 check_kick_or_punch_wall(m); 1809 m->flags &= ~MARIO_PUNCHING & ~MARIO_KICKING & ~MARIO_TRIPPING; 1810 1811 if (!(m->marioObj->collidedObjInteractTypes & (INTERACT_WARP_DOOR | INTERACT_DOOR))) { 1812 sDisplayingDoorText = FALSE; 1813 } 1814 if (!(m->marioObj->collidedObjInteractTypes & INTERACT_WARP)) { 1815 sJustTeleported = FALSE; 1816 } 1817 } 1818 1819 void check_death_barrier(struct MarioState *m) { 1820 if (m->pos[1] < m->floorHeight + 2048.0f) { 1821 if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) { 1822 play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); 1823 } 1824 } 1825 } 1826 1827 void check_lava_boost(struct MarioState *m) { 1828 if (!(m->action & ACT_FLAG_RIDING_SHELL) && m->pos[1] < m->floorHeight + 10.0f) { 1829 if (!(m->flags & MARIO_METAL_CAP)) { 1830 m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; 1831 } 1832 1833 update_mario_sound_and_camera(m); 1834 drop_and_set_mario_action(m, ACT_LAVA_BOOST, 0); 1835 } 1836 } 1837 1838 void pss_begin_slide(UNUSED struct MarioState *m) { 1839 if (!(gHudDisplay.flags & HUD_DISPLAY_FLAG_TIMER)) { 1840 level_control_timer(TIMER_CONTROL_SHOW); 1841 level_control_timer(TIMER_CONTROL_START); 1842 sPSSSlideStarted = TRUE; 1843 } 1844 } 1845 1846 void pss_end_slide(struct MarioState *m) { 1847 //! This flag isn't set on death or level entry, allowing double star spawn 1848 if (sPSSSlideStarted) { 1849 u16 slideTime = level_control_timer(TIMER_CONTROL_STOP); 1850 if (slideTime < 630) { 1851 m->marioObj->oBhvParams = (STAR_INDEX_ACT_2 << 24); 1852 spawn_default_star(-6358.0f, -4300.0f, 4700.0f); 1853 } 1854 sPSSSlideStarted = FALSE; 1855 } 1856 } 1857 1858 void mario_handle_special_floors(struct MarioState *m) { 1859 if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) { 1860 return; 1861 } 1862 1863 if (m->floor != NULL) { 1864 s32 floorType = m->floor->type; 1865 1866 switch (floorType) { 1867 case SURFACE_DEATH_PLANE: 1868 case SURFACE_VERTICAL_WIND: 1869 check_death_barrier(m); 1870 break; 1871 1872 case SURFACE_WARP: 1873 level_trigger_warp(m, WARP_OP_WARP_FLOOR); 1874 break; 1875 1876 case SURFACE_TIMER_START: 1877 pss_begin_slide(m); 1878 break; 1879 1880 case SURFACE_TIMER_END: 1881 pss_end_slide(m); 1882 break; 1883 } 1884 1885 if (!(m->action & ACT_FLAG_AIR) && !(m->action & ACT_FLAG_SWIMMING)) { 1886 switch (floorType) { 1887 case SURFACE_BURNING: 1888 check_lava_boost(m); 1889 break; 1890 } 1891 } 1892 } 1893 }