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:
parent
b9e84ad3b9
commit
ab9e7c7bca
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue