From ab9e7c7bca2bdf70aa02ba5945f02e74918c3f25 Mon Sep 17 00:00:00 2001 From: MysterD Date: Wed, 2 Sep 2020 23:50:27 -0700 Subject: [PATCH] Added player-to-player interactions Players are now solid to each other and can push each other Players can now attack each other (but they do not deal damage) Players can bounce on top of each other, and they get squished temporarily Players are given slightly offset positions when they transition between areas and levels, so they're no longer on top of one another. --- data/behavior_data.c | 2 + include/types.h | 5 +- src/game/interaction.c | 73 +++++++++++++++++++++++++- src/game/interaction.h | 2 +- src/game/mario.c | 20 ++++++- src/game/mario_actions_stationary.c | 5 ++ src/game/mario_misc.c | 13 ++++- src/game/object_collision.c | 52 ++++++++++++++++++ src/game/object_list_processor.c | 1 + src/pc/network/packets/packet_player.c | 9 ---- 10 files changed, 167 insertions(+), 15 deletions(-) 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) {