// HEAVILY EDITED === Specific interaction functions removed #include #include "../include/PR/ultratypes.h" #include "../shim.h" #include "area.h" //#include "actors/common1.h" //#include "audio/external.h" //#include "behavior_actions.h" //#include "behavior_data.h" #include "camera.h" //#include "course_table.h" //#include "dialog_ids.h" #include "../engine/math_util.h" #include "../engine/surface_collision.h" //#include "game_init.h" #include "interaction.h" #include "level_update.h" #include "mario.h" #include "mario_step.h" #include "memory.h" //#include "obj_behaviors.h" //#include "object_helpers.h" #include "save_file.h" //#include "seq_ids.h" #include "../include/sm64.h" //#include "sound_init.h" //#include "thread6.h" #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" #define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01 #define INT_PUNCH (1 << 1) // 0x02 #define INT_KICK (1 << 2) // 0x04 #define INT_TRIP (1 << 3) // 0x08 #define INT_SLIDE_KICK (1 << 4) // 0x10 #define INT_FAST_ATTACK_OR_SHELL (1 << 5) // 0x20 #define INT_HIT_FROM_ABOVE (1 << 6) // 0x40 #define INT_HIT_FROM_BELOW (1 << 7) // 0x80 #define INT_ATTACK_NOT_FROM_BELOW \ (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE) #define INT_ANY_ATTACK \ (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW) #define INT_ATTACK_NOT_WEAK_FROM_ABOVE \ (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW) #define sDelayInvincTimer (g_state->msDelayInvincTimer) #define sInvulnerable (g_state->msInvulnerable) // u8 sDelayInvincTimer; // s16 sInvulnerable; struct InteractionHandler { u32 interactType; u32 (*handler)(struct MarioState *, u32, struct Object *); }; static struct InteractionHandler sInteractionHandlers[] = { }; static u32 sForwardKnockbackActions[][3] = { { ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB }, { ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB }, { ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB }, }; static u32 sBackwardKnockbackActions[][3] = { { ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB }, { ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB }, { ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB }, }; static u8 sDisplayingDoorText = FALSE; static u8 sJustTeleported = FALSE; static u8 sPssSlideStarted = FALSE; /** * Returns the type of cap Mario is wearing. */ u32 get_mario_cap_flag(struct Object *capObject) { return MARIO_NORMAL_CAP; // const BehaviorScript *script = virtual_to_segmented(0x13, capObject->behavior); // if (script == bhvNormalCap) { // return MARIO_NORMAL_CAP; // } else if (script == bhvMetalCap) { // return MARIO_METAL_CAP; // } else if (script == bhvWingCap) { // return MARIO_WING_CAP; // } else if (script == bhvVanishCap) { // return MARIO_VANISH_CAP; // } return 0; } /** * Returns true if the passed in object has a moving angle yaw * in the angular range given towards Mario. */ u32 object_facing_mario(struct MarioState *m, struct Object *o, s16 angleRange) { f32 dx = m->pos[0] - o->oPosX; f32 dz = m->pos[2] - o->oPosZ; s16 angleToMario = atan2s(dz, dx); s16 dAngle = angleToMario - o->oMoveAngleYaw; if (-angleRange <= dAngle && dAngle <= angleRange) { return TRUE; } return FALSE; } s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o) { f32 dx = o->oPosX - m->pos[0]; f32 dz = o->oPosZ - m->pos[2]; return atan2s(dz, dx); } /** * Determines Mario's interaction with a given object depending on their proximity, * action, speed, and position. */ u32 determine_interaction(struct MarioState *m, struct Object *o) { u32 interaction = 0; u32 action = m->action; if (action & ACT_FLAG_ATTACKING) { if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) { s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; if (m->flags & MARIO_PUNCHING) { // 120 degrees total, or 60 each way if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { interaction = INT_PUNCH; } } if (m->flags & MARIO_KICKING) { // 120 degrees total, or 60 each way if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { interaction = INT_KICK; } } if (m->flags & MARIO_TRIPPING) { // 180 degrees total, or 90 each way if (-0x4000 <= dYawToObject && dYawToObject <= 0x4000) { interaction = INT_TRIP; } } } else if (action == ACT_GROUND_POUND || action == ACT_TWIRLING) { if (m->vel[1] < 0.0f) { interaction = INT_GROUND_POUND_OR_TWIRL; } } else if (action == ACT_GROUND_POUND_LAND || action == ACT_TWIRL_LAND) { // Neither ground pounding nor twirling change Mario's vertical speed on landing., // so the speed check is nearly always true (perhaps not if you land while going upwards?) // Additionally, actionState it set on each first thing in their action, so this is // only true prior to the very first frame (i.e. active 1 frame prior to it run). if (m->vel[1] < 0.0f && m->actionState == 0) { interaction = INT_GROUND_POUND_OR_TWIRL; } } else if (action == ACT_SLIDE_KICK || action == ACT_SLIDE_KICK_SLIDE) { interaction = INT_SLIDE_KICK; } else if (action & ACT_FLAG_RIDING_SHELL) { interaction = INT_FAST_ATTACK_OR_SHELL; } else if (m->forwardVel <= -26.0f || 26.0f <= m->forwardVel) { interaction = INT_FAST_ATTACK_OR_SHELL; } } // Prior to this, the interaction type could be overwritten. This requires, however, // that the interaction not be set prior. This specifically overrides turning a ground // pound into just a bounce. if (interaction == 0 && (action & ACT_FLAG_AIR)) { if (m->vel[1] < 0.0f) { if (m->pos[1] > o->oPosY) { interaction = INT_HIT_FROM_ABOVE; } } else { if (m->pos[1] < o->oPosY) { interaction = INT_HIT_FROM_BELOW; } } } return interaction; } /** * Sets the interaction types for INT_STATUS_INTERACTED, INT_STATUS_WAS_ATTACKED */ u32 attack_object(struct Object *o, s32 interaction) { u32 attackType = 0; switch (interaction) { case INT_GROUND_POUND_OR_TWIRL: attackType = ATTACK_GROUND_POUND_OR_TWIRL; break; case INT_PUNCH: attackType = ATTACK_PUNCH; break; case INT_KICK: case INT_TRIP: attackType = ATTACK_KICK_OR_TRIP; break; case INT_SLIDE_KICK: case INT_FAST_ATTACK_OR_SHELL: attackType = ATTACK_FAST_ATTACK; break; case INT_HIT_FROM_ABOVE: attackType = ATTACK_FROM_ABOVE; break; case INT_HIT_FROM_BELOW: attackType = ATTACK_FROM_BELOW; break; } o->oInteractStatus = attackType + (INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED); return attackType; } void mario_stop_riding_object(struct MarioState *m) { if (m->riddenObj != NULL) { m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING; stop_shell_music(); m->riddenObj = NULL; } } void mario_grab_used_object(struct MarioState *m) { if (m->heldObj == NULL) { m->heldObj = m->usedObj; // obj_set_held_state(m->heldObj, bhvCarrySomething3); } } void mario_drop_held_object(struct MarioState *m) { if (m->heldObj != NULL) { // if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { // stop_shell_music(); // } // obj_set_held_state(m->heldObj, bhvCarrySomething4); // ! When dropping an object instead of throwing it, it will be put at Mario's // y-positon instead of the HOLP's y-position. This fact is often exploited when // cloning objects. m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0]; m->heldObj->oPosY = m->pos[1]; m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2]; m->heldObj->oMoveAngleYaw = m->faceAngle[1]; m->heldObj = NULL; } } void mario_throw_held_object(struct MarioState *m) { if (m->heldObj != NULL) { // if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { // stop_shell_music(); // } // obj_set_held_state(m->heldObj, bhvCarrySomething5); m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]); m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1]; m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]); m->heldObj->oMoveAngleYaw = m->faceAngle[1]; m->heldObj = NULL; } } void mario_stop_riding_and_holding(struct MarioState *m) { mario_drop_held_object(m); mario_stop_riding_object(m); if (m->action == ACT_RIDING_HOOT) { m->usedObj->oInteractStatus = 0; m->usedObj->oHootMarioReleaseTime = gGlobalTimer; } } u32 does_mario_have_normal_cap_on_head(struct MarioState *m) { return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); } void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) { // struct Object *capObject; // if (does_mario_have_normal_cap_on_head(m)) { // save_file_set_cap_pos(m->pos[0], m->pos[1], m->pos[2]); // m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); // capObject = spawn_object(m->marioObj, MODEL_MARIOS_CAP, bhvNormalCap); // capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f; // capObject->oForwardVel = capSpeed; // capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400); // if (m->forwardVel < 0.0f) { // capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000); // } // } } u32 mario_lose_cap_to_enemy(u32 arg) { u32 wasWearingCap = FALSE; if (does_mario_have_normal_cap_on_head(gMarioState)) { save_file_set_flags(arg == 1 ? SAVE_FLAG_CAP_ON_KLEPTO : SAVE_FLAG_CAP_ON_UKIKI); gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); wasWearingCap = TRUE; } return wasWearingCap; } void mario_retrieve_cap(void) { mario_drop_held_object(gMarioState); save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI); gMarioState->flags &= ~MARIO_CAP_ON_HEAD; gMarioState->flags |= MARIO_NORMAL_CAP | MARIO_CAP_IN_HAND; } u32 able_to_grab_object(struct MarioState *m, UNUSED struct Object *o) { u32 action = m->action; if (action == ACT_DIVE_SLIDE || action == ACT_DIVE) { if (!(o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { return TRUE; } } else if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING) { if (m->actionArg < 2) { return TRUE; } } return FALSE; } struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType) { s32 i; struct Object *object; for (i = 0; i < m->marioObj->numCollidedObjs; i++) { object = m->marioObj->collidedObjs[i]; if (object->oInteractType == interactType) { return object; } } return NULL; } u32 mario_check_object_grab(struct MarioState *m) { u32 result = FALSE; // const BehaviorScript *script; if (m->input & INPUT_INTERACT_OBJ_GRABBABLE) { // script = virtual_to_segmented(0x13, m->interactObj->behavior); // if (script == bhvBowser) { // s16 facingDYaw = m->faceAngle[1] - m->interactObj->oMoveAngleYaw; // if (facingDYaw >= -0x5555 && facingDYaw <= 0x5555) { // m->faceAngle[1] = m->interactObj->oMoveAngleYaw; // m->usedObj = m->interactObj; // result = set_mario_action(m, ACT_PICKING_UP_BOWSER, 0); // } // } else { s16 facingDYaw = mario_obj_angle_to_object(m, m->interactObj) - m->faceAngle[1]; if (facingDYaw >= -0x2AAA && facingDYaw <= 0x2AAA) { m->usedObj = m->interactObj; if (!(m->action & ACT_FLAG_AIR)) { set_mario_action( m, (m->action & ACT_FLAG_DIVING) ? ACT_DIVE_PICKING_UP : ACT_PICKING_UP, 0); } result = TRUE; } // } } return result; } u32 bully_knock_back_mario(struct MarioState *mario) { struct BullyCollisionData marioData; struct BullyCollisionData bullyData; s16 newMarioYaw; s16 newBullyYaw; s16 marioDYaw; UNUSED s16 bullyDYaw; u32 bonkAction = 0; struct Object *bully = mario->interactObj; //! Conversion ratios multiply to more than 1 (could allow unbounded speed // with bonk cancel - but this isn't important for regular bully battery) f32 bullyToMarioRatio = bully->hitboxRadius * 3 / 53; f32 marioToBullyRatio = 53.0f / bully->hitboxRadius; init_bully_collision_data(&marioData, mario->pos[0], mario->pos[2], mario->forwardVel, mario->faceAngle[1], bullyToMarioRatio, 52.0f); init_bully_collision_data(&bullyData, bully->oPosX, bully->oPosZ, bully->oForwardVel, bully->oMoveAngleYaw, marioToBullyRatio, bully->hitboxRadius + 2.0f); if (mario->forwardVel != 0.0f) { transfer_bully_speed(&marioData, &bullyData); } else { transfer_bully_speed(&bullyData, &marioData); } newMarioYaw = atan2s(marioData.velZ, marioData.velX); newBullyYaw = atan2s(bullyData.velZ, bullyData.velX); marioDYaw = newMarioYaw - mario->faceAngle[1]; bullyDYaw = newBullyYaw - bully->oMoveAngleYaw; mario->faceAngle[1] = newMarioYaw; mario->forwardVel = sqrtf(marioData.velX * marioData.velX + marioData.velZ * marioData.velZ); mario->pos[0] = marioData.posX; mario->pos[2] = marioData.posZ; bully->oMoveAngleYaw = newBullyYaw; bully->oForwardVel = sqrtf(bullyData.velX * bullyData.velX + bullyData.velZ * bullyData.velZ); bully->oPosX = bullyData.posX; bully->oPosZ = bullyData.posZ; if (marioDYaw < -0x4000 || marioDYaw > 0x4000) { mario->faceAngle[1] += 0x8000; mario->forwardVel *= -1.0f; if (mario->action & ACT_FLAG_AIR) { bonkAction = ACT_BACKWARD_AIR_KB; } else { bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; } } else { if (mario->action & ACT_FLAG_AIR) { bonkAction = ACT_FORWARD_AIR_KB; } else { bonkAction = ACT_SOFT_FORWARD_GROUND_KB; } } return bonkAction; } void bounce_off_object(struct MarioState *m, struct Object *o, f32 velY) { m->pos[1] = o->oPosY + o->hitboxHeight; m->vel[1] = velY; m->flags &= ~MARIO_UNKNOWN_08; play_sound(SOUND_ACTION_BOUNCE_OFF_OBJECT, m->marioObj->header.gfx.cameraToObject); } void hit_object_from_below(struct MarioState *m, UNUSED struct Object *o) { m->vel[1] = 0.0f; set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW); } // static u32 unused_determine_knockback_action(struct MarioState *m) { // u32 bonkAction; // s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); // s16 facingDYaw = angleToObject - m->faceAngle[1]; // // if (m->forwardVel < 16.0f) { // m->forwardVel = 16.0f; // } // // m->faceAngle[1] = angleToObject; // // if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { // m->forwardVel *= -1.0f; // if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { // bonkAction = ACT_BACKWARD_AIR_KB; // } else { // bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; // } // } else { // m->faceAngle[1] += 0x8000; // if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { // bonkAction = ACT_FORWARD_AIR_KB; // } else { // bonkAction = ACT_SOFT_FORWARD_GROUND_KB; // } // } // // return bonkAction; // } u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) { u32 bonkAction; s16 terrainIndex = 0; // 1 = air, 2 = water, 0 = default s16 strengthIndex = 0; s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); s16 facingDYaw = angleToObject - m->faceAngle[1]; s16 remainingHealth = m->health - 0x40 * m->hurtCounter; if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) { terrainIndex = 2; } else if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { terrainIndex = 1; } if (remainingHealth < 0x100) { strengthIndex = 2; } else if (m->interactObj->oDamageOrCoinValue >= 4) { strengthIndex = 2; } else if (m->interactObj->oDamageOrCoinValue >= 2) { strengthIndex = 1; } m->faceAngle[1] = angleToObject; if (terrainIndex == 2) { if (m->forwardVel < 28.0f) { mario_set_forward_vel(m, 28.0f); } if (m->pos[1] >= m->interactObj->oPosY) { if (m->vel[1] < 20.0f) { m->vel[1] = 20.0f; } } else { if (m->vel[1] > 0.0f) { m->vel[1] = 0.0f; } } } else { if (m->forwardVel < 16.0f) { mario_set_forward_vel(m, 16.0f); } } if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) { m->forwardVel *= -1.0f; bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex]; } else { m->faceAngle[1] += 0x8000; bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex]; } return bonkAction; } void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 padding) { f32 minDistance = o->hitboxRadius + m->marioObj->hitboxRadius + padding; f32 offsetX = m->pos[0] - o->oPosX; f32 offsetZ = m->pos[2] - o->oPosZ; f32 distance = sqrtf(offsetX * offsetX + offsetZ * offsetZ); if (distance < minDistance) { struct Surface *floor; s16 pushAngle; f32 newMarioX; f32 newMarioZ; if (distance == 0.0f) { pushAngle = m->faceAngle[1]; } else { pushAngle = atan2s(offsetZ, offsetX); } newMarioX = o->oPosX + minDistance * sins(pushAngle); newMarioZ = o->oPosZ + minDistance * coss(pushAngle); f32_find_wall_collision(&newMarioX, &m->pos[1], &newMarioZ, 60.0f, 50.0f); find_floor(newMarioX, m->pos[1], newMarioZ, &floor); if (floor != NULL) { //! Doesn't update Mario's referenced floor (allows oob death when // an object pushes you into a steep slope while in a ground action) m->pos[0] = newMarioX; m->pos[2] = newMarioZ; } } } void bounce_back_from_attack(struct MarioState *m, u32 interaction) { if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP)) { if (m->action == ACT_PUNCHING) { m->action = ACT_MOVE_PUNCHING; } if (m->action & ACT_FLAG_AIR) { mario_set_forward_vel(m, -16.0f); } else { mario_set_forward_vel(m, -48.0f); } set_camera_shake_from_hit(SHAKE_ATTACK); m->particleFlags |= PARTICLE_TRIANGLE; } if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP | INT_FAST_ATTACK_OR_SHELL)) { play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); } } u32 should_push_or_pull_door(struct MarioState *m, struct Object *o) { f32 dx = o->oPosX - m->pos[0]; f32 dz = o->oPosZ - m->pos[2]; s16 dYaw = o->oMoveAngleYaw - atan2s(dz, dx); return (dYaw >= -0x4000 && dYaw <= 0x4000) ? 0x00000001 : 0x00000002; } u32 take_damage_from_interact_object(struct MarioState *m) { // s32 shake; s32 damage = m->interactObj->oDamageOrCoinValue; // if (damage >= 4) { // shake = SHAKE_LARGE_DAMAGE; // } else if (damage >= 2) { // shake = SHAKE_MED_DAMAGE; // } else { // shake = SHAKE_SMALL_DAMAGE; // } if (!(m->flags & MARIO_CAP_ON_HEAD)) { damage += (damage + 1) / 2; } if (m->flags & MARIO_METAL_CAP) { damage = 0; } m->hurtCounter += 4 * damage; #ifdef VERSION_SH queue_rumble_data(5, 80); #endif // set_camera_shake_from_hit(shake); return damage; } u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) { u32 damage; if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; m->interactObj = o; damage = take_damage_from_interact_object(m); if (o->oInteractionSubtype & INT_SUBTYPE_BIG_KNOCKBACK) { m->forwardVel = 40.0f; } if (o->oDamageOrCoinValue > 0) { play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); } update_mario_sound_and_camera(m); return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), damage); } return FALSE; } void reset_mario_pitch(struct MarioState *m) { if (m->action == ACT_WATER_JUMP || m->action == ACT_SHOT_FROM_CANNON || m->action == ACT_FLYING) { set_camera_mode(m->area->camera, m->area->camera->defMode, 1); m->faceAngle[0] = 0; } } u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable) && (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { if (object_facing_mario(m, o, 0x2AAA)) { mario_stop_riding_and_holding(m); o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_GRABBED_MARIO; m->faceAngle[1] = o->oMoveAngleYaw; m->interactObj = o; m->usedObj = o; update_mario_sound_and_camera(m); play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); #ifdef VERSION_SH queue_rumble_data(5, 80); #endif return set_mario_action(m, ACT_GRABBED, 0); } } push_mario_out_of_object(m, o, -5.0f); return FALSE; } u32 mario_can_talk(struct MarioState *m, u32 arg) { s16 val6; if ((m->action & ACT_FLAG_IDLE) != 0x00000000) { return TRUE; } if (m->action == ACT_WALKING) { if (arg) { return TRUE; } val6 = m->marioObj->header.gfx.animInfo.animID; if (val6 == 0x0080 || val6 == 0x007F || val6 == 0x006C) { return TRUE; } } return FALSE; } #ifdef VERSION_JP #define READ_MASK (INPUT_B_PRESSED) #else #define READ_MASK (INPUT_B_PRESSED | INPUT_A_PRESSED) #endif #ifdef VERSION_JP #define SIGN_RANGE 0x38E3 #else #define SIGN_RANGE 0x4000 #endif u32 check_read_sign(struct MarioState *m, struct Object *o) { if ((m->input & READ_MASK) && mario_can_talk(m, 0) && object_facing_mario(m, o, SIGN_RANGE)) { s16 facingDYaw = (s16)(o->oMoveAngleYaw + 0x8000) - m->faceAngle[1]; if (facingDYaw >= -SIGN_RANGE && facingDYaw <= SIGN_RANGE) { f32 targetX = o->oPosX + 105.0f * sins(o->oMoveAngleYaw); f32 targetZ = o->oPosZ + 105.0f * coss(o->oMoveAngleYaw); m->marioObj->oMarioReadingSignDYaw = facingDYaw; m->marioObj->oMarioReadingSignDPosX = targetX - m->pos[0]; m->marioObj->oMarioReadingSignDPosZ = targetZ - m->pos[2]; m->interactObj = o; m->usedObj = o; return set_mario_action(m, ACT_READING_SIGN, 0); } } return FALSE; } u32 check_npc_talk(struct MarioState *m, struct Object *o) { if ((m->input & READ_MASK) && mario_can_talk(m, 1)) { s16 facingDYaw = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { o->oInteractStatus = INT_STATUS_INTERACTED; m->interactObj = o; m->usedObj = o; push_mario_out_of_object(m, o, -10.0f); return set_mario_action(m, ACT_WAITING_FOR_DIALOG, 0); } } push_mario_out_of_object(m, o, -10.0f); return FALSE; } void check_kick_or_punch_wall(struct MarioState *m) { if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) { Vec3f detector; detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]); detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]); detector[1] = m->pos[1]; if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) { if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) { if (m->action == ACT_PUNCHING) { m->action = ACT_MOVE_PUNCHING; } mario_set_forward_vel(m, -48.0f); play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); m->particleFlags |= PARTICLE_TRIANGLE; } else if (m->action & ACT_FLAG_AIR) { mario_set_forward_vel(m, -16.0f); play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); m->particleFlags |= PARTICLE_TRIANGLE; } } } } void mario_process_interactions(struct MarioState *m) { sDelayInvincTimer = FALSE; sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0; if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) { s32 i; for (i = 0; i < 31; i++) { u32 interactType = sInteractionHandlers[i].interactType; if (m->collidedObjInteractTypes & interactType) { struct Object *object = mario_get_collided_object(m, interactType); m->collidedObjInteractTypes &= ~interactType; if (!(object->oInteractStatus & INT_STATUS_INTERACTED)) { if (sInteractionHandlers[i].handler(m, interactType, object)) { break; } } } } } if (m->invincTimer > 0 && !sDelayInvincTimer) { m->invincTimer -= 1; } //! If the kick/punch flags are set and an object collision changes Mario's // action, he will get the kick/punch wall speed anyway. check_kick_or_punch_wall(m); m->flags &= ~MARIO_PUNCHING & ~MARIO_KICKING & ~MARIO_TRIPPING; if (!(m->marioObj->collidedObjInteractTypes & (INTERACT_WARP_DOOR | INTERACT_DOOR))) { sDisplayingDoorText = FALSE; } if (!(m->marioObj->collidedObjInteractTypes & INTERACT_WARP)) { sJustTeleported = FALSE; } } void check_death_barrier(struct MarioState *m) { if (m->pos[1] < m->floorHeight + 2048.0f) { if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) { play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); } } } void check_lava_boost(struct MarioState *m) { if (!(m->action & ACT_FLAG_RIDING_SHELL) && m->pos[1] < m->floorHeight + 10.0f) { if (!(m->flags & MARIO_METAL_CAP)) { m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; } update_mario_sound_and_camera(m); drop_and_set_mario_action(m, ACT_LAVA_BOOST, 0); } } void pss_begin_slide(UNUSED struct MarioState *m) { // if (!(gHudDisplay.flags & HUD_DISPLAY_FLAG_TIMER)) { // level_control_timer(TIMER_CONTROL_SHOW); // level_control_timer(TIMER_CONTROL_START); // sPssSlideStarted = TRUE; // } } void pss_end_slide(struct MarioState *m) { //! This flag isn't set on death or level entry, allowing double star spawn if (sPssSlideStarted) { u16 slideTime = level_control_timer(TIMER_CONTROL_STOP); if (slideTime < 630) { m->marioObj->oBehParams = (1 << 24); spawn_default_star(-6358.0f, -4300.0f, 4700.0f); } sPssSlideStarted = FALSE; } } void mario_handle_special_floors(struct MarioState *m) { if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) { return; } if (m->floor != NULL) { s32 floorType = m->floor->type; switch (floorType) { case SURFACE_DEATH_PLANE: case SURFACE_VERTICAL_WIND: check_death_barrier(m); break; case SURFACE_WARP: // level_trigger_warp(m, WARP_OP_WARP_FLOOR); break; case SURFACE_TIMER_START: pss_begin_slide(m); break; case SURFACE_TIMER_END: pss_end_slide(m); break; } if (!(m->action & ACT_FLAG_AIR) && !(m->action & ACT_FLAG_SWIMMING)) { switch (floorType) { case SURFACE_BURNING: check_lava_boost(m); break; } } } }