mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-04-25 19:42:20 +00:00
Player interaction enhancements
Made water punch do an attack Increased knockback significantly Different types of attacks will have different knockback severity and damage (Stomp > kick > the rest) Fixed rapid attack spam Adjust radius for bouncing on players and popping bubbles Made player interactions a synchronized server flag: None - Players are not solid Solid (Default) - Player can run into each other and stun each other PVP - Same as solid except friendly fire is on Note: Not currently configurable due to a lack of a UI.
This commit is contained in:
parent
91981d679d
commit
51940d6a82
8 changed files with 98 additions and 28 deletions
|
|
@ -187,6 +187,14 @@ u32 determine_interaction(struct MarioState *m, struct Object *o) {
|
|||
u32 interaction = 0;
|
||||
u32 action = m->action;
|
||||
|
||||
// hack: make water punch actually do something
|
||||
if (m->action == ACT_WATER_PUNCH && o->oInteractType & INTERACT_PLAYER) {
|
||||
s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
|
||||
if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
|
||||
return INT_PUNCH;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
|
@ -611,7 +619,9 @@ u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) {
|
|||
}
|
||||
}
|
||||
|
||||
f32 sign = 1.0f;
|
||||
if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) {
|
||||
sign = -1.0f;
|
||||
m->forwardVel *= -1.0f;
|
||||
bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex];
|
||||
} else {
|
||||
|
|
@ -619,6 +629,16 @@ u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) {
|
|||
bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex];
|
||||
}
|
||||
|
||||
// set knockback very high when dealing with player attacks
|
||||
if (m->interactObj != NULL && (m->interactObj->oInteractType & INTERACT_PLAYER) && terrainIndex != 2) {
|
||||
f32 mag = m->interactObj->oDamageOrCoinValue * 25.0f * sign;
|
||||
m->forwardVel = mag;
|
||||
if (sign > 0 && terrainIndex == 1) { mag *= -1.0f; }
|
||||
m->vel[0] = mag * sins(angleToObject);
|
||||
m->vel[1] = abs(mag);
|
||||
m->vel[2] = mag * coss(angleToObject);
|
||||
}
|
||||
|
||||
return bonkAction;
|
||||
}
|
||||
|
||||
|
|
@ -706,6 +726,13 @@ u32 take_damage_from_interact_object(struct MarioState *m) {
|
|||
damage = 0;
|
||||
}
|
||||
|
||||
// disable player-to-player damage if the server says so
|
||||
if (m->interactObj != NULL && m->interactObj->oInteractType & INTERACT_PLAYER) {
|
||||
if (gServerSettings.playerInteractions != PLAYER_INTERACTIONS_PVP) {
|
||||
damage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m->hurtCounter += 4 * damage;
|
||||
|
||||
queue_rumble_data_mario(m, 5, 80);
|
||||
|
|
@ -1113,7 +1140,7 @@ 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) {
|
||||
static u8 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;
|
||||
|
|
@ -1130,17 +1157,38 @@ static void resolve_player_collision(struct MarioState* m, struct MarioState* m2
|
|||
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;
|
||||
if (marioDist >= radius) { return; }
|
||||
|
||||
// bounce
|
||||
u32 interaction = determine_interaction(m, m2->marioObj);
|
||||
if (interaction & INT_HIT_FROM_ABOVE) {
|
||||
if (m2->playerIndex == 0) {
|
||||
m2->squishTimer = max(m2->squishTimer, 4);
|
||||
}
|
||||
bounce_off_object(m, m2->marioObj, 30.0f);
|
||||
queue_rumble_data_mario(m, 5, 80);
|
||||
// don't do further interactions if we've hopped on top
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//! 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;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u8 determine_player_damage_value(u32 interaction) {
|
||||
if (interaction & INT_GROUND_POUND_OR_TWIRL) { return 3; }
|
||||
if (interaction & INT_KICK) { return 2; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object* o) {
|
||||
if (gServerSettings.playerInteractions == PLAYER_INTERACTIONS_NONE) { return FALSE; }
|
||||
|
||||
struct MarioState* m2 = NULL;
|
||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||
if (o == gMarioStates[i].marioObj) {
|
||||
|
|
@ -1149,27 +1197,27 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object
|
|||
}
|
||||
}
|
||||
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);
|
||||
// don't do further interactions if we've hopped on top
|
||||
if (resolve_player_collision(m, m2)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u32 interaction = determine_interaction(m, o);
|
||||
|
||||
// attacked
|
||||
if (!inCutscene && m2->invincTimer <= 0 && (interaction & INT_ANY_ATTACK)) {
|
||||
u8 isInCutscene = ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) || ((m2->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE);
|
||||
u8 isInvulnerable = (m2->action & ACT_FLAG_INVULNERABLE) || m2->invincTimer != 0 || m2->hurtCounter != 0 || isInCutscene;
|
||||
if ((interaction & INT_ANY_ATTACK) && !isInvulnerable) {
|
||||
if (m->action == ACT_GROUND_POUND) {
|
||||
m2->squishTimer = max(m2->squishTimer, 20);
|
||||
}
|
||||
if (m2->playerIndex == 0) {
|
||||
m2->interactObj = m->marioObj;
|
||||
if (interaction & INT_KICK) {
|
||||
set_mario_action(m2, ACT_FREEFALL, 0);
|
||||
}
|
||||
m->marioObj->oDamageOrCoinValue = determine_player_damage_value(interaction);
|
||||
}
|
||||
m2->invincTimer = max(m2->invincTimer, 3);
|
||||
take_damage_and_knock_back(m2, m->marioObj);
|
||||
|
|
@ -1177,8 +1225,6 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
resolve_player_collision(m, m2);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1093,7 +1093,9 @@ u32 common_air_knockback_step(struct MarioState *m, u32 landAction, u32 hardFall
|
|||
f32 speed) {
|
||||
u32 stepResult;
|
||||
|
||||
mario_set_forward_vel(m, speed);
|
||||
if (m->interactObj == NULL || !(m->interactObj->oInteractType & INTERACT_PLAYER)) {
|
||||
mario_set_forward_vel(m, speed);
|
||||
}
|
||||
|
||||
stepResult = perform_air_step(m, 0);
|
||||
switch (stepResult) {
|
||||
|
|
|
|||
|
|
@ -928,7 +928,7 @@ s32 act_bubbled(struct MarioState* m) {
|
|||
}
|
||||
|
||||
// pop bubble
|
||||
if (m->playerIndex == 0 && distanceToPlayer < 200 && is_player_active(targetMarioState) && m->numLives != -1) {
|
||||
if (m->playerIndex == 0 && distanceToPlayer < 120 && is_player_active(targetMarioState) && m->numLives != -1) {
|
||||
m->hurtCounter = 0;
|
||||
m->healCounter = 31;
|
||||
m->health = 0x100;
|
||||
|
|
|
|||
|
|
@ -1615,11 +1615,13 @@ s32 common_ground_knockback_action(struct MarioState *m, s32 animation, s32 arg2
|
|||
#endif
|
||||
}
|
||||
|
||||
if (m->forwardVel > 32.0f) {
|
||||
m->forwardVel = 32.0f;
|
||||
}
|
||||
if (m->forwardVel < -32.0f) {
|
||||
m->forwardVel = -32.0f;
|
||||
if (m->interactObj == NULL || !(m->interactObj->oInteractType & INTERACT_PLAYER)) {
|
||||
if (m->forwardVel > 32.0f) {
|
||||
m->forwardVel = 32.0f;
|
||||
}
|
||||
if (m->forwardVel < -32.0f) {
|
||||
m->forwardVel = -32.0f;
|
||||
}
|
||||
}
|
||||
|
||||
val04 = set_mario_animation(m, animation);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ int detect_object_hurtbox_overlap(struct Object *a, struct Object *b) {
|
|||
f32 sp28 = a->hurtboxRadius + b->hurtboxRadius;
|
||||
f32 sp24 = sqrtf(sp34 * sp34 + sp2C * sp2C);
|
||||
|
||||
// two-player hack
|
||||
if (a == gMarioObject) { b->oInteractionSubtype |= INT_SUBTYPE_DELAY_INVINCIBILITY; }
|
||||
if (a == gMario2Object) { b->oInteractionSubtype |= INT_SUBTYPE_DELAY_INVINCIBILITY_MARIO2; }
|
||||
|
||||
|
|
@ -128,6 +129,7 @@ int detect_object_hurtbox_overlap(struct Object *a, struct Object *b) {
|
|||
if (sp20 < sp38) {
|
||||
return 0;
|
||||
}
|
||||
// two-player hack
|
||||
if (a == gMarioObject) { b->oInteractionSubtype &= ~INT_SUBTYPE_DELAY_INVINCIBILITY; }
|
||||
if (a == gMario2Object) { b->oInteractionSubtype &= ~INT_SUBTYPE_DELAY_INVINCIBILITY_MARIO2; }
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ struct sockaddr_in txAddr;
|
|||
u8 networkLoadingLevel = 0;
|
||||
bool networkLevelLoaded = false;
|
||||
|
||||
struct ServerSettings gServerSettings = {
|
||||
.playerInteractions = PLAYER_INTERACTIONS_SOLID,
|
||||
};
|
||||
|
||||
void network_init(enum NetworkType inNetworkType, char* ip, char* port) {
|
||||
networkType = inNetworkType;
|
||||
|
||||
|
|
|
|||
|
|
@ -63,12 +63,24 @@ struct SyncObject {
|
|||
void* extraFields[MAX_SYNC_OBJECT_FIELDS];
|
||||
};
|
||||
|
||||
enum PlayerInteractions {
|
||||
PLAYER_INTERACTIONS_NONE,
|
||||
PLAYER_INTERACTIONS_SOLID,
|
||||
PLAYER_INTERACTIONS_PVP,
|
||||
};
|
||||
|
||||
struct ServerSettings {
|
||||
enum PlayerInteractions playerInteractions;
|
||||
};
|
||||
|
||||
extern struct MarioState gMarioStates[];
|
||||
extern u8 gInsidePainting;
|
||||
extern s16 sCurrPlayMode;
|
||||
extern enum NetworkType networkType;
|
||||
extern struct SyncObject syncObjects[];
|
||||
extern bool networkLevelLoaded;
|
||||
extern struct ServerSettings gServerSettings;
|
||||
|
||||
|
||||
void network_init(enum NetworkType inNetworkType, char* ip, char* port);
|
||||
void network_on_init_level(void);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ void network_send_save_file(void) {
|
|||
struct Packet p;
|
||||
packet_init(&p, PACKET_SAVE_FILE, true);
|
||||
packet_write(&p, &gCurrSaveFileNum, sizeof(s16));
|
||||
packet_write(&p, &gServerSettings.playerInteractions, sizeof(u8));
|
||||
packet_write(&p, eeprom, sizeof(u8) * 512);
|
||||
network_send(&p);
|
||||
}
|
||||
|
|
@ -52,6 +53,7 @@ void network_receive_save_file(struct Packet* p) {
|
|||
|
||||
// find all reserved objects
|
||||
packet_read(p, &gCurrSaveFileNum, sizeof(s16));
|
||||
packet_read(p, &gServerSettings.playerInteractions, sizeof(u8));
|
||||
packet_read(p, eeprom, sizeof(u8) * 512);
|
||||
|
||||
save_file_load_all(TRUE);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue