Skip to content

Commit

Permalink
Mirror team-damage + Kick player exceeding HP/damage threshold
Browse files Browse the repository at this point in the history
* Implement mirror team damage during a set duration after round starts
  post-freeze
* Victim is also have immunity during the duration if enabled
* New cvars
    * neo_sv_mirror_teamdamage_multiplier - Default: 2, 0 to disable
        * Disabled for debug mode
    * neo_sv_mirror_teamdamage_duration - Default: 7, 0 for the entire
      round
    * neo_sv_mirror_teamdamage_immunity - Default: 1, 0 for no immunity
      from team damage
* Kick players exeeding threshold
* New convars:
    * neo_sv_teamdamage_kick
    * neo_sv_teamdamage_kick_hp
    * neo_sv_teamdamage_kick_kills
* fixes NeotokyoRebuild#220
  • Loading branch information
nullsystem committed Sep 1, 2024
1 parent 4b5550b commit 0af1e0b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 2 deletions.
35 changes: 35 additions & 0 deletions mp/src/game/server/neo/neo_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "neo_model_manager.h"
#include "gib.h"
#include "world.h"

#include "weapon_ghost.h"
#include "weapon_supa7.h"
Expand Down Expand Up @@ -2468,11 +2469,45 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info)
++iDamage;
flDmgAccumlator -= 1.0f;
}

// Mirror team-damage
const bool bIsTeamDmg = (attackerIdx != entindex() && attacker->GetTeamNumber() == GetTeamNumber());
if (bIsTeamDmg)
{
const float flMirrorMult = NEORules()->MirrorDamageMultiplier();
if (flMirrorMult > 0.0f)
{
// Attacker own-team-inflicting - Mirror the damage
// NEO NOTE (nullsystem): DO NOT call OnTakeDamage directly as that'll crash the game, instead
// let the gamerule handle this situation by causing the attacker to suicide when health gets
// depliciated
const float flInflict = iDamage * flMirrorMult;
attacker->OnTakeDamage_Alive(CTakeDamageInfo(GetWorldEntity(), GetWorldEntity(),
flInflict, DMG_SLASH));
if (attacker->m_iHealth <= 0)
{
attacker->m_bKilledInflicted = true;
}

if (neo_sv_mirror_teamdamage_immunity.GetBool())
{
// Give immunity to the victim and don't go through the OnTakeDamage_Alive
return 0;
}
}
}

// Apply damages/hits numbers
if (iDamage > 0)
{
m_rfAttackersScores.GetForModify(attackerIdx) += iDamage;
m_rfAttackersAccumlator.Set(attackerIdx, flDmgAccumlator);
m_rfAttackersHits.GetForModify(attackerIdx) += 1;

if (bIsTeamDmg && neo_sv_teamdamage_kick.GetBool() && NEORules()->GetRoundStatus() == NeoRoundStatus::RoundLive)
{
attacker->m_iTeamDamageInflicted += iDamage;
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions mp/src/game/server/neo/neo_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ class CNEO_Player : public CHL2MP_Player
CNetworkVar(bool, m_bClientWantNeoName);

bool m_bIsPendingSpawnForThisRound;
bool m_bKilledInflicted = false; // Server-side var only
int m_iTeamDamageInflicted = 0;
int m_iTeamKillsInflicted = 0;
bool m_bIsPendingTKKick = false; // To not spam the kickid ConCommand

private:
bool m_bFirstDeathTick;
Expand Down
111 changes: 110 additions & 1 deletion mp/src/game/shared/neo/neo_gamerules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ ConVar neo_sv_build_integrity_check_allow_debug("neo_sv_build_integrity_check_al
"If enabled, when the server checks the client hashes, it'll also allow debug"
" builds which has a given special bit to bypass the check.",
true, 0.0f, true, 1.0f);

#ifdef DEBUG
static constexpr char TEAMDMG_MULTI[] = "0";
#else
static constexpr char TEAMDMG_MULTI[] = "2";
#endif
ConVar neo_sv_mirror_teamdamage_multiplier("neo_sv_mirror_teamdamage_multiplier", TEAMDMG_MULTI, FCVAR_REPLICATED, "The damage multiplier given to the friendly-firing individual. Set value to 0 to disable mirror team damage.", true, 0.0f, true, 100.0f);
ConVar neo_sv_mirror_teamdamage_duration("neo_sv_mirror_teamdamage_duration", "7", FCVAR_REPLICATED, "How long in seconds the mirror damage is active for the start of each round. Set to 0 for the entire round.", true, 0.0f, true, 10000.0f);
ConVar neo_sv_mirror_teamdamage_immunity("neo_sv_mirror_teamdamage_immunity", "1", FCVAR_REPLICATED, "If enabled, the victim will not take damage from a teammate during the mirror team damage duration.", true, 0.0f, true, 1.0f);

ConVar neo_sv_teamdamage_kick("neo_sv_teamdamage_kick", "0", FCVAR_REPLICATED, "If enabled, the friendly-firing individual will be kicked if damage is received during the neo_sv_mirror_teamdamage_duration, exceeds the neo_sv_teamdamage_kick_hp value, or executes a teammate.", true, 0.0f, true, 1.0f);
ConVar neo_sv_teamdamage_kick_hp("neo_sv_teamdamage_kick_hp", "900", FCVAR_REPLICATED, "The threshold for the amount of HP damage inflicted on teammates before the client is kicked.", true, 100.0f, false, 0.0f);
ConVar neo_sv_teamdamage_kick_kills("neo_sv_teamdamage_kick_kills", "6", FCVAR_REPLICATED, "The threshold for the amount of team kills before the client is kicked.", true, 1.0f, false, 0.0f);
#endif

REGISTER_GAMERULES_CLASS( CNEORules );
Expand Down Expand Up @@ -444,6 +457,20 @@ void CNEORules::ResetMapSessionCommon()
m_flNeoNextRoundStartTime = 0.0f;
#ifdef GAME_DLL
m_pRestoredInfos.Purge();

for (int i = 1; i <= gpGlobals->maxClients; i++)
{
auto *pPlayer = static_cast<CNEO_Player *>(UTIL_PlayerByIndex(i));
if (pPlayer)
{
pPlayer->m_iTeamDamageInflicted = 0;
pPlayer->m_iTeamKillsInflicted = 0;
pPlayer->m_bIsPendingTKKick = false;
pPlayer->m_bKilledInflicted = false;
}
}
m_flPrevThinkKick = 0.0f;
m_flPrevThinkMirrorDmg = 0.0f;
#endif
}

Expand Down Expand Up @@ -529,6 +556,51 @@ void CNEORules::Think(void)
BaseClass::Think();

#ifdef GAME_DLL
if (MirrorDamageMultiplier() > 0.0f &&
gpGlobals->curtime > (m_flPrevThinkMirrorDmg + 0.25f))
{
for (int i = 1; i <= gpGlobals->maxClients; ++i)
{
auto player = static_cast<CNEO_Player*>(UTIL_PlayerByIndex(i));
if (player && player->IsAlive() && player->m_bKilledInflicted && player->m_iHealth <= 0)
{
player->CommitSuicide(false, true);
}
}

m_flPrevThinkMirrorDmg = gpGlobals->curtime;
}

if (neo_sv_teamdamage_kick.GetBool() && m_nRoundStatus == NeoRoundStatus::RoundLive &&
gpGlobals->curtime > (m_flPrevThinkKick + 0.5f))
{
const int iThresKickHp = neo_sv_teamdamage_kick_hp.GetInt();
const int iThresKickKills = neo_sv_teamdamage_kick_kills.GetInt();

// Separate command from check so kick not affected by player index
int userIDsToKick[MAX_PLAYERS + 1] = {};
int userIDsToKickSize = 0;
for (int i = 1; i <= gpGlobals->maxClients; ++i)
{
auto player = static_cast<CNEO_Player*>(UTIL_PlayerByIndex(i));
if (player && (player->m_iTeamDamageInflicted >= iThresKickHp ||
player->m_iTeamKillsInflicted >= iThresKickKills) &&
!player->m_bIsPendingTKKick)
{
userIDsToKick[userIDsToKickSize++] = player->GetUserID();
player->m_bIsPendingTKKick = true;
}
}

for (int i = 0; i < userIDsToKickSize; ++i)
{
engine->ServerCommand(UTIL_VarArgs("kickid %d \"%s\"\n", userIDsToKick[i],
"Too much friendly-fire damage inflicted."));
}

m_flPrevThinkKick = gpGlobals->curtime;
}

if (IsRoundOver())
{
// If the next round was not scheduled yet
Expand Down Expand Up @@ -695,7 +767,7 @@ void CNEORules::AwardRankUp(CNEO_Player *pClient)
}

// Return remaining time in seconds. Zero means there is no time limit.
float CNEORules::GetRoundRemainingTime()
float CNEORules::GetRoundRemainingTime() const
{
if ((m_nRoundStatus != NeoRoundStatus::Warmup && neo_round_timelimit.GetFloat() == 0) ||
m_nRoundStatus == NeoRoundStatus::Idle)
Expand All @@ -707,6 +779,25 @@ float CNEORules::GetRoundRemainingTime()
return (m_flNeoRoundStartTime + roundTimeLimit) - gpGlobals->curtime;
}

float CNEORules::GetRoundAccumulatedTime() const
{
return gpGlobals->curtime - (m_flNeoRoundStartTime + mp_neo_preround_freeze_time.GetFloat());
}

#ifdef GAME_DLL
float CNEORules::MirrorDamageMultiplier() const
{
if (m_nRoundStatus != NeoRoundStatus::RoundLive)
{
return 0.0f;
}
const float flAccTime = GetRoundAccumulatedTime();
const float flMirrorMult = neo_sv_mirror_teamdamage_multiplier.GetFloat();
const float flMirrorDur = neo_sv_mirror_teamdamage_duration.GetFloat();
return (flMirrorDur == 0.0f || (0.0f <= flAccTime && flAccTime < flMirrorDur)) ? flMirrorMult : 0.0f;
}
#endif

void CNEORules::FireGameEvent(IGameEvent* event)
{
const char *type = event->GetName();
Expand Down Expand Up @@ -937,6 +1028,7 @@ void CNEORules::StartNextRound()
continue;
}

pPlayer->m_bKilledInflicted = false;
if (pPlayer->GetActiveWeapon())
{
pPlayer->GetActiveWeapon()->Holster();
Expand All @@ -958,11 +1050,16 @@ void CNEORules::StartNextRound()
{
pPlayer->Reset();
pPlayer->m_iXP.Set(0);
pPlayer->m_iTeamDamageInflicted = 0;
pPlayer->m_iTeamKillsInflicted = 0;
}
pPlayer->m_bIsPendingTKKick = false;

pPlayer->SetTestMessageVisible(false);
}

m_flPrevThinkKick = 0.0f;
m_flPrevThinkMirrorDmg = 0.0f;
m_flIntermissionEndTime = 0;
m_flRestartGameTime = 0;
m_bCompleteReset = false;
Expand Down Expand Up @@ -1762,6 +1859,12 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info)
if (attacker->GetTeamNumber() == victim->GetTeamNumber())
{
attacker->m_iXP.GetForModify() -= 1;
#ifdef GAME_DLL
if (neo_sv_teamdamage_kick.GetBool() && m_nRoundStatus == NeoRoundStatus::RoundLive)
{
++attacker->m_iTeamKillsInflicted;
}
#endif
}
// Enemy kill
else
Expand Down Expand Up @@ -2100,6 +2203,12 @@ bool CNEORules::FPlayerCanRespawn(CBasePlayer* pPlayer)
Assert(false);
}

// Do not let anyone who tried to team-kill during mirror damage + live round to respawn
if (static_cast<CNEO_Player *>(pPlayer)->m_bKilledInflicted)
{
return false;
}

// Did we make it in time to spawn for this round?
if (GetRemainingPreRoundFreezeTime(false) + mp_neo_latespawn_max_time.GetFloat() > 0)
{
Expand Down
13 changes: 12 additions & 1 deletion mp/src/game/shared/neo/neo_gamerules.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ class NEOViewVectors : public HL2MPViewVectors
#ifdef GAME_DLL
class CNEOGhostCapturePoint;
class CNEO_Player;

extern ConVar neo_sv_mirror_teamdamage_multiplier;
extern ConVar neo_sv_mirror_teamdamage_duration;
extern ConVar neo_sv_mirror_teamdamage_immunity;
extern ConVar neo_sv_teamdamage_kick;
#else
class C_NEO_Player;
#endif
Expand Down Expand Up @@ -176,7 +181,11 @@ class CNEORules : public CHL2MPRules, public CGameEventListener

virtual bool CheckGameOver(void) OVERRIDE;

float GetRoundRemainingTime();
float GetRoundRemainingTime() const;
float GetRoundAccumulatedTime() const;
#ifdef GAME_DLL
float MirrorDamageMultiplier() const;
#endif

virtual void PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) OVERRIDE;

Expand Down Expand Up @@ -262,6 +271,8 @@ class CNEORules : public CHL2MPRules, public CGameEventListener

#ifdef GAME_DLL
CUtlVector<int> m_pGhostCaps;
float m_flPrevThinkKick = 0.0f;
float m_flPrevThinkMirrorDmg = 0.0f;
#endif
CNetworkVar(int, m_nRoundStatus); // NEO TODO (Rain): probably don't need to network this
CNetworkVar(int, m_iRoundNumber);
Expand Down

0 comments on commit 0af1e0b

Please sign in to comment.