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:
MysterD 2020-09-06 10:05:58 -07:00
parent 91981d679d
commit 51940d6a82
8 changed files with 98 additions and 28 deletions

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);