diff --git a/data/behavior_data.c b/data/behavior_data.c index 095e78829..a52c1812d 100644 --- a/data/behavior_data.c +++ b/data/behavior_data.c @@ -3820,6 +3820,7 @@ const BehaviorScript bhvMario[] = { OR_INT(oFlags, OBJ_FLAG_0100), OR_INT(oUnk94, 0x0001), SET_HITBOX(/*Radius*/ 37, /*Height*/ 160), + SET_INTERACT_TYPE(INTERACT_PLAYER), BEGIN_LOOP(), CALL_NATIVE(try_print_debug_mario_level_info), CALL_NATIVE(bhv_mario_update), @@ -3834,6 +3835,7 @@ const BehaviorScript bhvMario2[] = { OR_INT(oFlags, OBJ_FLAG_0100), OR_INT(oUnk94, 0x0001), SET_HITBOX(/*Radius*/ 37, /*Height*/ 160), + SET_INTERACT_TYPE(INTERACT_PLAYER), BEGIN_LOOP(), CALL_NATIVE(bhv_mario2_update), END_LOOP(), diff --git a/include/types.h b/include/types.h index 6de3e7b69..c91a6d45f 100644 --- a/include/types.h +++ b/include/types.h @@ -277,7 +277,9 @@ struct MarioBodyState /*0x0C*/ Vec3s torsoAngle; /*0x12*/ Vec3s headAngle; /*0x18*/ Vec3f heldObjLastPosition; /// also known as HOLP - u8 padding[4]; + /*????*/ Vec3f torsoPos; + /*????*/ Vec3f handFootPos[4]; + //u8 padding[4]; }; struct OffsetSizePair @@ -365,6 +367,7 @@ struct MarioState /*0xC4*/ f32 unkC4; /*0xC8*/ s16 currentRoom; /*0xCA*/ struct Object* heldByObj; + /*????*/ u8 isSnoring; }; #define PLAY_MODE_NORMAL 0 diff --git a/src/game/interaction.c b/src/game/interaction.c index 0cf8ac3b8..bd7083786 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -55,6 +55,7 @@ u32 interact_warp(struct MarioState *, u32, struct Object *); u32 interact_warp_door(struct MarioState *, u32, struct Object *); u32 interact_door(struct MarioState *, u32, struct Object *); u32 interact_cannon_base(struct MarioState *, u32, struct Object *); +u32 interact_player(struct MarioState*, u32, struct Object*); u32 interact_igloo_barrier(struct MarioState *, u32, struct Object *); u32 interact_tornado(struct MarioState *, u32, struct Object *); u32 interact_whirlpool(struct MarioState *, u32, struct Object *); @@ -114,6 +115,7 @@ static struct InteractionHandler sInteractionHandlers[] = { { INTERACT_CAP, interact_cap }, { INTERACT_GRABBABLE, interact_grabbable }, { INTERACT_TEXT, interact_text }, + { INTERACT_PLAYER, interact_player }, }; static u32 sForwardKnockbackActions[][3] = { @@ -1109,6 +1111,75 @@ u32 interact_cannon_base(struct MarioState *m, UNUSED u32 interactType, struct O return FALSE; } +static void resolve_player_collision(struct MarioState* m, struct MarioState* m2) { + // move player outside of other player + f32 extentY = m->marioObj->hitboxHeight; + f32 radius = m->marioObj->hitboxRadius * 2.0f; + + f32* localTorso = m->marioBodyState->torsoPos; + f32* remoteTorso = m2->marioBodyState->torsoPos; + + f32 marioRelY = localTorso[1] - remoteTorso[1]; + if (marioRelY < 0) { marioRelY = -marioRelY; } + if (marioRelY >= extentY) { return FALSE; } + + + f32 marioRelX = localTorso[0] - remoteTorso[0]; + f32 marioRelZ = localTorso[2] - remoteTorso[2]; + f32 marioDist = sqrtf(sqr(marioRelX) + sqr(marioRelZ)); + + if (marioDist < radius) { + //! If this function pushes Mario out of bounds, it will trigger Mario's + // oob failsafe + m->pos[0] += (radius - marioDist) / radius * marioRelX; + m->pos[2] += (radius - marioDist) / radius * marioRelZ; + m->marioBodyState->torsoPos[0] += (radius - marioDist) / radius * marioRelX; + m->marioBodyState->torsoPos[2] += (radius - marioDist) / radius * marioRelZ; + } +} + +u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object* o) { + struct MarioState* m2 = NULL; + for (int i = 0; i < MAX_PLAYERS; i++) { + if (o == gMarioStates[i].marioObj) { + m2 = &gMarioStates[i]; + break; + } + } + if (m2 == NULL) { return FALSE; } + u8 inCutscene = ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) || ((m2->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE); + + u32 interaction = determine_interaction(m, o); + + // bounce + if (interaction & INT_HIT_FROM_ABOVE) { + if (m2->playerIndex == 0) { + m2->squishTimer = max(m2->squishTimer, 4); + } + bounce_off_object(m, o, 30.0f); + queue_rumble_data_mario(m, 5, 80); + return FALSE; + } + + // attacked + if (!inCutscene && m2->invincTimer <= 0 && (interaction & INT_ANY_ATTACK)) { + if (m->action == ACT_GROUND_POUND) { + m2->squishTimer = max(m2->squishTimer, 20); + } + if (m2->playerIndex == 0) { + m2->interactObj = m->marioObj; + } + m2->invincTimer = max(m2->invincTimer, 3); + take_damage_and_knock_back(m2, m->marioObj); + bounce_back_from_attack(m, interaction); + return FALSE; + } + + resolve_player_collision(m, m2); + + return FALSE; +} + u32 interact_igloo_barrier(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { //! Sets used object without changing action (LOTS of interesting glitches, // but unfortunately the igloo barrier is the only object with this interaction @@ -1797,7 +1868,7 @@ void mario_process_interactions(struct MarioState *m) { if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) { s32 i; - for (i = 0; i < 31; i++) { + for (i = 0; i < 32; i++) { u32 interactType = sInteractionHandlers[i].interactType; if (m->collidedObjInteractTypes & interactType) { struct Object *object = mario_get_collided_object(m, interactType); diff --git a/src/game/interaction.h b/src/game/interaction.h index d8d82a541..3c9cdd078 100644 --- a/src/game/interaction.h +++ b/src/game/interaction.h @@ -36,7 +36,7 @@ #define INTERACT_SNUFIT_BULLET /* 0x10000000 */ (1 << 28) #define INTERACT_SHOCK /* 0x20000000 */ (1 << 29) #define INTERACT_IGLOO_BARRIER /* 0x40000000 */ (1 << 30) -#define INTERACT_UNKNOWN_31 /* 0x80000000 */ (1 << 31) +#define INTERACT_PLAYER /* 0x80000000 */ (1 << 31) // INTERACT_WARP diff --git a/src/game/mario.c b/src/game/mario.c index 510a05f6f..6f720bf04 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1833,6 +1833,15 @@ s32 execute_mario_action(UNUSED struct Object *o) { mario_handle_special_floors(gMarioState); mario_process_interactions(gMarioState); + // HACK: mute snoring even when we skip the waking up action + if (gMarioState->isSnoring && gMarioState->action != ACT_SLEEPING) { + func_803205E8(SOUND_MARIO_SNORING1, gMarioState->marioObj->header.gfx.cameraToObject); + func_803205E8(SOUND_MARIO_SNORING2, gMarioState->marioObj->header.gfx.cameraToObject); +#ifndef VERSION_JP + func_803205E8(SOUND_MARIO_SNORING3, gMarioState->marioObj->header.gfx.cameraToObject); +#endif + } + // If Mario is OOB, stop executing actions. if (gMarioState->floor == NULL) { return 0; @@ -1984,7 +1993,16 @@ void init_mario(void) { vec3s_set(gMarioState->angleVel, 0, 0, 0); vec3s_to_vec3f(gMarioState->pos, gMarioSpawnInfo->startPos); vec3f_set(gMarioState->vel, 0, 0, 0); - if (!isLocal) { gMarioState->pos[0] -= 150; } + + // two-player hack + if ((networkType == NT_CLIENT && isLocal) || (networkType == NT_SERVER && !isLocal)) { + gMarioState->pos[0] += 50.0f * coss(gMarioState->faceAngle[1]); + gMarioState->pos[2] += 50.0f * sins(gMarioState->faceAngle[1]); + } else { + gMarioState->pos[0] -= 50.0f * coss(gMarioState->faceAngle[1]); + gMarioState->pos[2] -= 50.0f * sins(gMarioState->faceAngle[1]); + } + gMarioState->floorHeight = find_floor(gMarioState->pos[0], gMarioState->pos[1], gMarioState->pos[2], &gMarioState->floor); diff --git a/src/game/mario_actions_stationary.c b/src/game/mario_actions_stationary.c index c1446b0ac..7ce9fb727 100644 --- a/src/game/mario_actions_stationary.c +++ b/src/game/mario_actions_stationary.c @@ -290,10 +290,12 @@ s32 act_sleeping(struct MarioState *m) { if (sp24 == 2) { play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); + m->isSnoring = TRUE; } if (sp24 == 20) { play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); + m->isSnoring = TRUE; } if (m->playerIndex == 0 && is_anim_at_end(m)) { @@ -318,13 +320,16 @@ s32 act_sleeping(struct MarioState *m) { sp24 = set_mario_animation(m, MARIO_ANIM_SLEEP_LYING); #ifndef VERSION_JP play_sound_if_no_flag(m, SOUND_MARIO_SNORING3, MARIO_ACTION_SOUND_PLAYED); + m->isSnoring = TRUE; #else if (sp24 == 2) { play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); + m->isSnoring = TRUE; } if (sp24 == 25) { play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); + m->isSnoring = TRUE; } #endif break; diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index d7495c111..fec501d80 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -378,7 +378,8 @@ Gfx *geo_switch_mario_eyes(s32 callContext, struct GraphNode *node, UNUSED Mat4 /** * Makes Mario's upper body tilt depending on the rotation stored in his bodyState */ -Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { +Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, Mat4 *mtx) { + Mat4* curTransform = mtx; struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter]; s32 action = bodyState->action; @@ -393,6 +394,9 @@ Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, UNUSED Mat4 * rotNode->rotation[0] = bodyState->torsoAngle[1]; rotNode->rotation[1] = bodyState->torsoAngle[2]; rotNode->rotation[2] = bodyState->torsoAngle[0]; + + // update torso position in bodyState + get_pos_from_transform_mtx(bodyState->torsoPos, *curTransform, *gCurGraphNodeCamera->matrixPtr); } return NULL; } @@ -457,7 +461,8 @@ Gfx *geo_switch_mario_hand(s32 callContext, struct GraphNode *node, UNUSED Mat4 * ! Since the animation gets updated in GEO_CONTEXT_RENDER, drawing Mario multiple times * (such as in the mirror room) results in a faster and desynced punch / kick animation. */ -Gfx *geo_mario_hand_foot_scaler(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { +Gfx *geo_mario_hand_foot_scaler(s32 callContext, struct GraphNode *node, Mat4 *mtx) { + Mat4* curTransform = mtx; static s16 sMarioAttackAnimCounter = 0; struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; struct GraphNodeScale *scaleNode = (struct GraphNodeScale *) node->next; @@ -474,6 +479,10 @@ Gfx *geo_mario_hand_foot_scaler(s32 callContext, struct GraphNode *node, UNUSED gMarioAttackScaleAnimation[(asGenerated->parameter & 0x03) * 6 + (bodyState->punchState & 0x3F)] / 10.0f; } + // update hand/foot position in bodyState + get_pos_from_transform_mtx(bodyState->handFootPos[(asGenerated->parameter & 0x03)], + *curTransform, + *gCurGraphNodeCamera->matrixPtr); } return NULL; } diff --git a/src/game/object_collision.c b/src/game/object_collision.c index 0608c5b4a..900a0a5ae 100644 --- a/src/game/object_collision.c +++ b/src/game/object_collision.c @@ -22,6 +22,52 @@ struct Object *debug_print_obj_collision(struct Object *a) { return NULL; } +int detect_player_hitbox_overlap(struct MarioState* local, struct MarioState* remote) { + if (local->marioObj == NULL || local->marioObj->oIntangibleTimer != 0) { return; } + if (remote->marioObj == NULL || remote->marioObj->oIntangibleTimer != 0) { return; } + + struct Object* a = local->marioObj; + f32* aTorso = local->marioBodyState->torsoPos; + + struct Object* b = remote->marioObj; + f32* bTorso = remote->marioBodyState->torsoPos; + + f32 sp3C = aTorso[1] - a->hitboxDownOffset; + f32 sp38 = bTorso[1] - b->hitboxDownOffset; + f32 dx = aTorso[0] - bTorso[0]; + UNUSED f32 sp30 = sp3C - sp38; + f32 dz = aTorso[2] - bTorso[2]; + f32 collisionRadius = (a->hitboxRadius + b->hitboxRadius) * 1.25f; + f32 distance = sqrtf(dx * dx + dz * dz); + + if (collisionRadius > distance) { + f32 sp20 = a->hitboxHeight + sp3C; + f32 sp1C = b->hitboxHeight + sp38; + + if (sp3C > sp1C) { + return 0; + } + if (sp20 < sp38) { + return 0; + } + if (a->numCollidedObjs >= 4) { + return 0; + } + if (b->numCollidedObjs >= 4) { + return 0; + } + a->collidedObjs[a->numCollidedObjs] = b; + b->collidedObjs[b->numCollidedObjs] = a; + a->collidedObjInteractTypes |= b->oInteractType; + b->collidedObjInteractTypes |= a->oInteractType; + a->numCollidedObjs++; + b->numCollidedObjs++; + return 1; + } + + //! no return value +} + int detect_object_hitbox_overlap(struct Object *a, struct Object *b) { f32 sp3C = a->oPosY - a->hitboxDownOffset; f32 sp38 = b->oPosY - b->hitboxDownOffset; @@ -135,6 +181,12 @@ void check_player_object_collision(void) { (struct Object *) &gObjectLists[OBJ_LIST_DESTRUCTIVE]); sp18 = (struct Object *) sp18->header.next; } + + // two-player hack ? + extern struct MarioState gMarioStates[]; + for (int i = 1; i < MAX_PLAYERS; i++) { + detect_player_hitbox_overlap(&gMarioStates[0], &gMarioStates[i]); + } } void check_pushable_object_collision(void) { diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 1d2e41f62..440ccc291 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -19,6 +19,7 @@ #include "platform_displacement.h" #include "profiler.h" #include "spawn_object.h" +#include "engine/math_util.h" /** * Flags controlling what debug info is displayed. diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index 0b90db65c..df68cc424 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -99,15 +99,6 @@ void network_receive_player(struct Packet* p) { gMarioStates[1].marioBodyState->punchState = (0 << 6) | 4; } } - - // mute snoring - if (oldAction == ACT_SLEEPING && gMarioStates[1].action != ACT_SLEEPING) { - func_803205E8(SOUND_MARIO_SNORING1, gMarioStates[1].marioObj->header.gfx.cameraToObject); - func_803205E8(SOUND_MARIO_SNORING2, gMarioStates[1].marioObj->header.gfx.cameraToObject); -#ifndef VERSION_JP - func_803205E8(SOUND_MARIO_SNORING3, gMarioStates[1].marioObj->header.gfx.cameraToObject); -#endif - } } void network_update_player(void) {