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.
This commit is contained in:
MysterD 2020-09-02 23:50:27 -07:00
parent b9e84ad3b9
commit ab9e7c7bca
10 changed files with 167 additions and 15 deletions

View file

@ -3820,6 +3820,7 @@ const BehaviorScript bhvMario[] = {
OR_INT(oFlags, OBJ_FLAG_0100), OR_INT(oFlags, OBJ_FLAG_0100),
OR_INT(oUnk94, 0x0001), OR_INT(oUnk94, 0x0001),
SET_HITBOX(/*Radius*/ 37, /*Height*/ 160), SET_HITBOX(/*Radius*/ 37, /*Height*/ 160),
SET_INTERACT_TYPE(INTERACT_PLAYER),
BEGIN_LOOP(), BEGIN_LOOP(),
CALL_NATIVE(try_print_debug_mario_level_info), CALL_NATIVE(try_print_debug_mario_level_info),
CALL_NATIVE(bhv_mario_update), CALL_NATIVE(bhv_mario_update),
@ -3834,6 +3835,7 @@ const BehaviorScript bhvMario2[] = {
OR_INT(oFlags, OBJ_FLAG_0100), OR_INT(oFlags, OBJ_FLAG_0100),
OR_INT(oUnk94, 0x0001), OR_INT(oUnk94, 0x0001),
SET_HITBOX(/*Radius*/ 37, /*Height*/ 160), SET_HITBOX(/*Radius*/ 37, /*Height*/ 160),
SET_INTERACT_TYPE(INTERACT_PLAYER),
BEGIN_LOOP(), BEGIN_LOOP(),
CALL_NATIVE(bhv_mario2_update), CALL_NATIVE(bhv_mario2_update),
END_LOOP(), END_LOOP(),

View file

@ -277,7 +277,9 @@ struct MarioBodyState
/*0x0C*/ Vec3s torsoAngle; /*0x0C*/ Vec3s torsoAngle;
/*0x12*/ Vec3s headAngle; /*0x12*/ Vec3s headAngle;
/*0x18*/ Vec3f heldObjLastPosition; /// also known as HOLP /*0x18*/ Vec3f heldObjLastPosition; /// also known as HOLP
u8 padding[4]; /*????*/ Vec3f torsoPos;
/*????*/ Vec3f handFootPos[4];
//u8 padding[4];
}; };
struct OffsetSizePair struct OffsetSizePair
@ -365,6 +367,7 @@ struct MarioState
/*0xC4*/ f32 unkC4; /*0xC4*/ f32 unkC4;
/*0xC8*/ s16 currentRoom; /*0xC8*/ s16 currentRoom;
/*0xCA*/ struct Object* heldByObj; /*0xCA*/ struct Object* heldByObj;
/*????*/ u8 isSnoring;
}; };
#define PLAY_MODE_NORMAL 0 #define PLAY_MODE_NORMAL 0

View file

@ -55,6 +55,7 @@ u32 interact_warp(struct MarioState *, u32, struct Object *);
u32 interact_warp_door(struct MarioState *, u32, struct Object *); u32 interact_warp_door(struct MarioState *, u32, struct Object *);
u32 interact_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_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_igloo_barrier(struct MarioState *, u32, struct Object *);
u32 interact_tornado(struct MarioState *, u32, struct Object *); u32 interact_tornado(struct MarioState *, u32, struct Object *);
u32 interact_whirlpool(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_CAP, interact_cap },
{ INTERACT_GRABBABLE, interact_grabbable }, { INTERACT_GRABBABLE, interact_grabbable },
{ INTERACT_TEXT, interact_text }, { INTERACT_TEXT, interact_text },
{ INTERACT_PLAYER, interact_player },
}; };
static u32 sForwardKnockbackActions[][3] = { static u32 sForwardKnockbackActions[][3] = {
@ -1109,6 +1111,75 @@ u32 interact_cannon_base(struct MarioState *m, UNUSED u32 interactType, struct O
return FALSE; 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) { u32 interact_igloo_barrier(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
//! Sets used object without changing action (LOTS of interesting glitches, //! Sets used object without changing action (LOTS of interesting glitches,
// but unfortunately the igloo barrier is the only object with this interaction // 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) { if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) {
s32 i; s32 i;
for (i = 0; i < 31; i++) { for (i = 0; i < 32; i++) {
u32 interactType = sInteractionHandlers[i].interactType; u32 interactType = sInteractionHandlers[i].interactType;
if (m->collidedObjInteractTypes & interactType) { if (m->collidedObjInteractTypes & interactType) {
struct Object *object = mario_get_collided_object(m, interactType); struct Object *object = mario_get_collided_object(m, interactType);

View file

@ -36,7 +36,7 @@
#define INTERACT_SNUFIT_BULLET /* 0x10000000 */ (1 << 28) #define INTERACT_SNUFIT_BULLET /* 0x10000000 */ (1 << 28)
#define INTERACT_SHOCK /* 0x20000000 */ (1 << 29) #define INTERACT_SHOCK /* 0x20000000 */ (1 << 29)
#define INTERACT_IGLOO_BARRIER /* 0x40000000 */ (1 << 30) #define INTERACT_IGLOO_BARRIER /* 0x40000000 */ (1 << 30)
#define INTERACT_UNKNOWN_31 /* 0x80000000 */ (1 << 31) #define INTERACT_PLAYER /* 0x80000000 */ (1 << 31)
// INTERACT_WARP // INTERACT_WARP

View file

@ -1833,6 +1833,15 @@ s32 execute_mario_action(UNUSED struct Object *o) {
mario_handle_special_floors(gMarioState); mario_handle_special_floors(gMarioState);
mario_process_interactions(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 Mario is OOB, stop executing actions.
if (gMarioState->floor == NULL) { if (gMarioState->floor == NULL) {
return 0; return 0;
@ -1984,7 +1993,16 @@ void init_mario(void) {
vec3s_set(gMarioState->angleVel, 0, 0, 0); vec3s_set(gMarioState->angleVel, 0, 0, 0);
vec3s_to_vec3f(gMarioState->pos, gMarioSpawnInfo->startPos); vec3s_to_vec3f(gMarioState->pos, gMarioSpawnInfo->startPos);
vec3f_set(gMarioState->vel, 0, 0, 0); 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 = gMarioState->floorHeight =
find_floor(gMarioState->pos[0], gMarioState->pos[1], gMarioState->pos[2], &gMarioState->floor); find_floor(gMarioState->pos[0], gMarioState->pos[1], gMarioState->pos[2], &gMarioState->floor);

View file

@ -290,10 +290,12 @@ s32 act_sleeping(struct MarioState *m) {
if (sp24 == 2) { if (sp24 == 2) {
play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject);
m->isSnoring = TRUE;
} }
if (sp24 == 20) { if (sp24 == 20) {
play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject);
m->isSnoring = TRUE;
} }
if (m->playerIndex == 0 && is_anim_at_end(m)) { 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); sp24 = set_mario_animation(m, MARIO_ANIM_SLEEP_LYING);
#ifndef VERSION_JP #ifndef VERSION_JP
play_sound_if_no_flag(m, SOUND_MARIO_SNORING3, MARIO_ACTION_SOUND_PLAYED); play_sound_if_no_flag(m, SOUND_MARIO_SNORING3, MARIO_ACTION_SOUND_PLAYED);
m->isSnoring = TRUE;
#else #else
if (sp24 == 2) { if (sp24 == 2) {
play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject);
m->isSnoring = TRUE;
} }
if (sp24 == 25) { if (sp24 == 25) {
play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject);
m->isSnoring = TRUE;
} }
#endif #endif
break; break;

View file

@ -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 * 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 GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node;
struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter]; struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter];
s32 action = bodyState->action; 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[0] = bodyState->torsoAngle[1];
rotNode->rotation[1] = bodyState->torsoAngle[2]; rotNode->rotation[1] = bodyState->torsoAngle[2];
rotNode->rotation[2] = bodyState->torsoAngle[0]; rotNode->rotation[2] = bodyState->torsoAngle[0];
// update torso position in bodyState
get_pos_from_transform_mtx(bodyState->torsoPos, *curTransform, *gCurGraphNodeCamera->matrixPtr);
} }
return NULL; 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 * ! 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. * (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; static s16 sMarioAttackAnimCounter = 0;
struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node;
struct GraphNodeScale *scaleNode = (struct GraphNodeScale *) node->next; 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)] gMarioAttackScaleAnimation[(asGenerated->parameter & 0x03) * 6 + (bodyState->punchState & 0x3F)]
/ 10.0f; / 10.0f;
} }
// update hand/foot position in bodyState
get_pos_from_transform_mtx(bodyState->handFootPos[(asGenerated->parameter & 0x03)],
*curTransform,
*gCurGraphNodeCamera->matrixPtr);
} }
return NULL; return NULL;
} }

View file

@ -22,6 +22,52 @@ struct Object *debug_print_obj_collision(struct Object *a) {
return NULL; 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) { int detect_object_hitbox_overlap(struct Object *a, struct Object *b) {
f32 sp3C = a->oPosY - a->hitboxDownOffset; f32 sp3C = a->oPosY - a->hitboxDownOffset;
f32 sp38 = b->oPosY - b->hitboxDownOffset; f32 sp38 = b->oPosY - b->hitboxDownOffset;
@ -135,6 +181,12 @@ void check_player_object_collision(void) {
(struct Object *) &gObjectLists[OBJ_LIST_DESTRUCTIVE]); (struct Object *) &gObjectLists[OBJ_LIST_DESTRUCTIVE]);
sp18 = (struct Object *) sp18->header.next; 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) { void check_pushable_object_collision(void) {

View file

@ -19,6 +19,7 @@
#include "platform_displacement.h" #include "platform_displacement.h"
#include "profiler.h" #include "profiler.h"
#include "spawn_object.h" #include "spawn_object.h"
#include "engine/math_util.h"
/** /**
* Flags controlling what debug info is displayed. * Flags controlling what debug info is displayed.

View file

@ -99,15 +99,6 @@ void network_receive_player(struct Packet* p) {
gMarioStates[1].marioBodyState->punchState = (0 << 6) | 4; 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) { void network_update_player(void) {