diff --git a/src/d_player.h b/src/d_player.h index 5930cdede..2e7aeae1b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -741,6 +741,7 @@ struct player_t mobj_t *stumbleIndicator; mobj_t *sliptideZipIndicator; + mobj_t *whip; UINT8 instaShieldCooldown; UINT8 guardCooldown; diff --git a/src/g_game.c b/src/g_game.c index 37399c561..9f75d137a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2675,6 +2675,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) P_SetTarget(&players[player].follower, NULL); P_SetTarget(&players[player].awayview.mobj, NULL); P_SetTarget(&players[player].stumbleIndicator, NULL); + P_SetTarget(&players[player].whip, NULL); P_SetTarget(&players[player].ringShooter, NULL); P_SetTarget(&players[player].followmobj, NULL); diff --git a/src/k_collide.cpp b/src/k_collide.cpp index e8a088d8c..d2b886048 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -811,7 +811,26 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) //if (victim != attacker && !P_PlayerInPain(victimPlayer) && victimPlayer->flashing == 0) if (victim != attacker && victim->hitlag == 0) { - // BLOW THAT SHIT THE FUCK UP with guard + // If both players have a whip, hits are order-of-execution dependent and that sucks. + // Player expectation is a clash here. + if (victimPlayer->whip && !P_MobjWasRemoved(victimPlayer->whip)) + { + victimPlayer->whip->extravalue2 = 1; + shield->extravalue2 = 1; + + K_DoPowerClash(victim, attacker); + + victim->renderflags &= ~RF_DONTDRAW; + attacker->renderflags &= ~RF_DONTDRAW; + + angle_t thrangle = R_PointToAngle2(attacker->x, attacker->y, victim->x, victim->y); + P_Thrust(victim, thrangle, FRACUNIT*7); + P_Thrust(attacker, ANGLE_180 + thrangle, FRACUNIT*7); + + return false; + } + + // Instawhip _always_ loses to guard. if (K_PlayerGuard(victimPlayer)) //if (true) { @@ -824,11 +843,13 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) attacker->momx = attacker->momy = 0; P_Thrust(attacker, thrangle, FRACUNIT*7); + // A little extra juice, so successful reads are usually positive or zero on spheres. victimPlayer->spheres = std::min(victimPlayer->spheres + 10, 40); shield->renderflags &= ~RF_DONTDRAW; shield->flags |= MF_NOCLIPTHING; + // Attacker should be free to all reasonable followups. attacker->renderflags &= ~RF_DONTDRAW; attackerPlayer->spindashboost = 0; attackerPlayer->sneakertimer = 0; @@ -836,6 +857,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) attackerPlayer->guardCooldown = TICRATE*2; attackerPlayer->flashing = 0; + // Localized broly for a local event. mobj_t *broly = Obj_SpawnBrolyKi(victim, victimHitlag); broly->extravalue2 = 16*mapobjectscale; @@ -847,7 +869,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) K_AddHitLag(attacker, victimHitlag, true); K_AddHitLag(victim, attackerHitlag, false); - K_DoPowerClash(shield, victim); + K_DoPowerClash(shield, victim); // REJECTED attacker->hitlag = victimHitlag; // No, seriously, we do not care about K_AddHitLag's idea of a normal maximum shield->hitlag = attacker->hitlag; @@ -862,7 +884,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) // while still behaving as if it's a "real" hit. P_PlayRinglossSound(victim); P_PlayerRingBurst(victimPlayer, 5); - P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a pecial exception in P_DamageMobj for type==MT_INSTAWHIP + P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a special exception in P_DamageMobj for type==MT_INSTAWHIP angle_t thrangle = ANGLE_180 + R_PointToAngle2(victim->x, victim->y, shield->x, shield->y); P_Thrust(victim, thrangle, FRACUNIT*10); diff --git a/src/k_kart.c b/src/k_kart.c index f9a32344d..6cd6a0d76 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7925,6 +7925,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->guardCooldown) player->guardCooldown--; + if (player->whip && P_MobjWasRemoved(player->whip)) + player->whip = NULL; if (player->startboost > 0 && onground == true) { @@ -10619,6 +10621,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->guardCooldown = 50; S_StartSound(player->mo, sfx_iwhp); mobj_t *whip = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTAWHIP); + player->whip = whip; P_SetScale(whip, player->mo->scale); P_SetTarget(&whip->target, player->mo); K_MatchGenericExtraFlags(whip, player->mo); diff --git a/src/p_saveg.c b/src/p_saveg.c index 3b821e990..013d81830 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -74,7 +74,8 @@ typedef enum HOVERHYUDORO = 0x0020, STUMBLE = 0x0040, SLIPTIDEZIP = 0x0080, - RINGSHOOTER = 0x0100 + RINGSHOOTER = 0x0100, + WHIP = 0x0200, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -225,6 +226,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].sliptideZipIndicator) flags |= SLIPTIDEZIP; + if (players[i].whip) + flags |= WHIP; + if (players[i].ringShooter) flags |= RINGSHOOTER; @@ -251,6 +255,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & SLIPTIDEZIP) WRITEUINT32(save->p, players[i].sliptideZipIndicator->mobjnum); + if (flags & WHIP) + WRITEUINT32(save->p, players[i].whip->mobjnum); + if (flags & RINGSHOOTER) WRITEUINT32(save->p, players[i].ringShooter->mobjnum); @@ -638,6 +645,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & SLIPTIDEZIP) players[i].sliptideZipIndicator = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & WHIP) + players[i].whip = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & RINGSHOOTER) players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p); @@ -4987,6 +4997,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].sliptideZipIndicator, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "sliptideZipIndicator not found on player %d\n", i); } + if (players[i].whip) + { + temp = (UINT32)(size_t)players[i].whip; + players[i].whip = NULL; + if (!P_SetTarget(&players[i].whip, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "whip not found on player %d\n", i); + } if (players[i].ringShooter) { temp = (UINT32)(size_t)players[i].ringShooter;