diff --git a/src/game/interaction.c b/src/game/interaction.c index dadc0c8e2..a16f6d59b 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -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; } diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 4094ddd23..0d16c34d9 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -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) { diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index ab21f1223..a0b3634bc 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -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; diff --git a/src/game/mario_actions_moving.c b/src/game/mario_actions_moving.c index a2e9add52..c6605fded 100644 --- a/src/game/mario_actions_moving.c +++ b/src/game/mario_actions_moving.c @@ -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); diff --git a/src/game/object_collision.c b/src/game/object_collision.c index d7676b7eb..6604b5821 100644 --- a/src/game/object_collision.c +++ b/src/game/object_collision.c @@ -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; diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 8a77ce845..52ac3bb00 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -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; diff --git a/src/pc/network/network.h b/src/pc/network/network.h index dc3e8b64a..f0e045541 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -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); diff --git a/src/pc/network/packets/packet_save_file.c b/src/pc/network/packets/packet_save_file.c index 3b2dd1298..622790c82 100644 --- a/src/pc/network/packets/packet_save_file.c +++ b/src/pc/network/packets/packet_save_file.c @@ -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);