Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mp/src/game/server/neo/neo_detpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ CBaseGrenade* NEODeployedDetpack_Create(const Vector& position, const QAngle& an
pDet->SetTimer(FLT_MAX, FLT_MAX);
pDet->SetVelocity(velocity, angVelocity);
pDet->SetThrower(ToBaseCombatCharacter(pOwner));
if (pOwner) pDet->ChangeTeam(pOwner->GetTeamNumber());
pDet->m_takedamage = DAMAGE_EVENTS_ONLY;

return pDet;
Expand Down
1 change: 1 addition & 0 deletions mp/src/game/server/neo/neo_grenade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ CBaseGrenade *NEOFraggrenade_Create(const Vector &position, const QAngle &angles
pGrenade->SetTimer(timer, timer - NEO_FRAG_GRENADE_WARN_TIME);
pGrenade->SetVelocity(velocity, angVelocity);
pGrenade->SetThrower(ToBaseCombatCharacter(pOwner));
if (pOwner) pGrenade->ChangeTeam(pOwner->GetTeamNumber());
pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY;

return pGrenade;
Expand Down
1 change: 1 addition & 0 deletions mp/src/game/server/neo/neo_smokegrenade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ CBaseGrenade* NEOSmokegrenade_Create(const Vector& position, const QAngle& angle
pGrenade->SetTimer(FLT_MAX, FLT_MAX);
pGrenade->SetVelocity(velocity, angVelocity);
pGrenade->SetThrower(ToBaseCombatCharacter(pOwner));
if (pOwner) pGrenade->ChangeTeam(pOwner->GetTeamNumber());
pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY;

return pGrenade;
Expand Down
139 changes: 131 additions & 8 deletions mp/src/game/shared/neo/neo_gamerules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ammodef.h"

#include "takedamageinfo.h"
#include "basegrenade_shared.h"

#ifdef CLIENT_DLL
#include "c_neo_player.h"
Expand Down Expand Up @@ -67,6 +68,11 @@ ConVar neo_sv_mirror_teamdamage_immunity("neo_sv_mirror_teamdamage_immunity", "1
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);
ConVar neo_sv_suicide_prevent_cap_punish("neo_sv_suicide_prevent_cap_punish", "1", FCVAR_REPLICATED,
"If enabled, if a player suicides and is the only one alive in their team, "
"while the other team is holding the ghost, reward the ghost holder team "
"a rank up.",
true, 0.0f, true, 1.0f);
#endif

REGISTER_GAMERULES_CLASS( CNEORules );
Expand Down Expand Up @@ -479,6 +485,9 @@ void CNEORules::ResetMapSessionCommon()
}
m_flPrevThinkKick = 0.0f;
m_flPrevThinkMirrorDmg = 0.0f;
m_bTeamBeenAwardedDueToCapPrevent = false;
V_memset(m_arrayiEntPrevCap, 0, sizeof(m_arrayiEntPrevCap));
m_iEntPrevCapSize = 0;
#endif
}

Expand Down Expand Up @@ -1057,6 +1066,9 @@ void CNEORules::StartNextRound()
m_flIntermissionEndTime = 0;
m_flRestartGameTime = 0;
m_bCompleteReset = false;
m_bTeamBeenAwardedDueToCapPrevent = false;
V_memset(m_arrayiEntPrevCap, 0, sizeof(m_arrayiEntPrevCap));
m_iEntPrevCapSize = 0;
if (clearXP)
{
m_pRestoredInfos.Purge();
Expand Down Expand Up @@ -1712,7 +1724,14 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo
}
else if (iWinReason == NEO_VICTORY_TEAM_ELIMINATION)
{
V_sprintf_safe(victoryMsg, "Team %s wins by eliminating the other team!\n", (team == TEAM_JINRAI ? "Jinrai" : "NSF"));
if (m_bTeamBeenAwardedDueToCapPrevent)
{
V_sprintf_safe(victoryMsg, "Team %s wins and is awarded rank ups by ghost cap prevention!\n", (team == TEAM_JINRAI ? "Jinrai" : "NSF"));
}
else
{
V_sprintf_safe(victoryMsg, "Team %s wins by eliminating the other team!\n", (team == TEAM_JINRAI ? "Jinrai" : "NSF"));
}
}
else if (iWinReason == NEO_VICTORY_TIMEOUT_WIN_BY_NUMBERS)
{
Expand Down Expand Up @@ -1752,6 +1771,7 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo
soundParams.m_bEmitCloseCaption = false;

const int winningTeamNum = winningTeam->GetTeamNumber();
int iRankupCapPrev = 0;

for (int i = 1; i <= gpGlobals->maxClients; ++i)
{
Expand Down Expand Up @@ -1780,8 +1800,17 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo
int xpAward = 1; // Base reward for being on winning team
if (player->IsAlive())
{
++xpAward;
xpAward += static_cast<int>(player->IsCarryingGhost());
if (m_bTeamBeenAwardedDueToCapPrevent)
{
AwardRankUp(player);
xpAward = 0; // Already been rewarded rank-up XPs
++iRankupCapPrev;
}
else
{
++xpAward;
xpAward += static_cast<int>(player->IsCarryingGhost());
}
}
player->m_iXP.GetForModify() += xpAward;
}
Expand All @@ -1794,6 +1823,16 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo
}
}

if (m_bTeamBeenAwardedDueToCapPrevent && iWinReason != NEO_VICTORY_GHOST_CAPTURE)
{
UTIL_ClientPrintAll(HUD_PRINTTALK, "Last player of %s1 suicided vs. ghost carrier; awarding capture to team %s2.",
(team == TEAM_JINRAI ? "NSF" : "Jinrai"), (team == TEAM_JINRAI ? "Jinrai" : "NSF"));
char szHudChatPrint[42];
V_sprintf_safe(szHudChatPrint, "Awarding capture rank-up to %d player%s.",
iRankupCapPrev, iRankupCapPrev == 1 ? "" : "s");
UTIL_ClientPrintAll(HUD_PRINTTALK, szHudChatPrint);
}

if (gotMatchWinner)
{
GoToIntermission();
Expand Down Expand Up @@ -1830,24 +1869,91 @@ static CNEO_Player* FetchAssists(CNEO_Player* attacker, CNEO_Player* victim)
return NULL;
}

#ifdef GAME_DLL
void CNEORules::CheckIfCapPrevent(CNEO_Player *capPreventerPlayer)
{
// If this is the only player alive left before the suicide/disconnect and the other team was holding
// the ghost, reward the other team an XP to the next rank as a ghost cap was prevented.
const bool bShouldCheck = (neo_sv_suicide_prevent_cap_punish.GetBool()
&& m_nRoundStatus == NeoRoundStatus::RoundLive
&& !m_bTeamBeenAwardedDueToCapPrevent);
if (!bShouldCheck)
{
return;
}

bool bOtherTeamPlayingGhost = false;
int iTallyAlive[TEAM__TOTAL] = {};
const int iPreventerTeam = capPreventerPlayer->GetTeamNumber();
// Sanity check: Make sure it's only Jinrai/NSF players
const bool bValidTeam = iPreventerTeam == TEAM_JINRAI || iPreventerTeam == TEAM_NSF;
Assert(bValidTeam);
if (!bValidTeam)
{
return;
}

const int iCapPreventerEntIdx = capPreventerPlayer->entindex();

// Sanity check: Prevent duplication just in-case
bool bContainsEntIdx = false;
for (int i = 0; !bContainsEntIdx && i < m_iEntPrevCapSize; ++i)
{
bContainsEntIdx = (m_arrayiEntPrevCap[i] == iCapPreventerEntIdx);
}
if (!bContainsEntIdx) m_arrayiEntPrevCap[m_iEntPrevCapSize++] = iCapPreventerEntIdx;

for (int i = 1; i <= gpGlobals->maxClients; ++i)
{
auto *player = static_cast<CNEO_Player*>(UTIL_PlayerByIndex(i));
if (!player || player->entindex() == iCapPreventerEntIdx)
{
continue;
}

const int iPlayerTeam = player->GetTeamNumber();
iTallyAlive[iPlayerTeam] += player->IsAlive();
if (iPlayerTeam != iPreventerTeam && player->IsCarryingGhost())
{
bOtherTeamPlayingGhost = true;
}
}

const int iOppositeTeam = (iPreventerTeam == TEAM_JINRAI) ? TEAM_NSF : TEAM_JINRAI;
m_bTeamBeenAwardedDueToCapPrevent = (bOtherTeamPlayingGhost &&
iTallyAlive[iPreventerTeam] == 0 && iTallyAlive[iOppositeTeam] > 0);
}
#endif

void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info)
{
BaseClass::PlayerKilled(pVictim, info);

auto attacker = dynamic_cast<CNEO_Player*>(info.GetAttacker());
auto victim = dynamic_cast<CNEO_Player*>(pVictim);
auto grenade = dynamic_cast<CBaseGrenade *>(info.GetInflictor());

if (!attacker || !pVictim)
if (!victim)
{
return;
}

// Suicide
if (attacker == victim)
// Suicide or suicide by environment (non-grenade as grenade is likely from a player)
if (attacker == victim || (!attacker && !grenade))
{
attacker->m_iXP.GetForModify() -= 1;
victim->m_iXP.GetForModify() -= 1;
#ifdef GAME_DLL
CheckIfCapPrevent(victim);
#endif
}
else
#ifdef GAME_DLL
else if (!attacker && grenade && grenade->GetTeamNumber() == victim->GetTeamNumber())
{
// Death by own team's grenade, but the player is already disconnected. Check for cap prevent.
CheckIfCapPrevent(victim);
}
#endif
else if (attacker)
{
// Team kill
if (attacker->GetTeamNumber() == victim->GetTeamNumber())
Expand All @@ -1858,6 +1964,20 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info)
{
++attacker->m_iTeamKillsInflicted;
}

for (int i = 0; i < m_iEntPrevCapSize; ++i)
{
if (m_arrayiEntPrevCap[i] == attacker->entindex())
{
// Posthumous teamkill to prevent ghost cap scenario:
// Player-A throws nade at Player-B, Player-A suicides right after,
// Player-B gets killed from the nade - This dodges the general case
// as Player-A is not the final player, but it was Player-A's intention
// to prevent the ghost cap.
CheckIfCapPrevent(victim);
break;
}
}
#endif
}
// Enemy kill
Expand Down Expand Up @@ -2159,6 +2279,9 @@ void CNEORules::ClientDisconnected(edict_t* pClient)
}
}
}

// Check if this is done to prevent ghost cap
CheckIfCapPrevent(pNeoPlayer);
}

BaseClass::ClientDisconnected(pClient);
Expand Down
6 changes: 6 additions & 0 deletions mp/src/game/shared/neo/neo_gamerules.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
float MirrorDamageMultiplier() const;
#endif

#ifdef GAME_DLL
void CheckIfCapPrevent(CNEO_Player *capPreventerPlayer);
#endif
virtual void PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) OVERRIDE;

// IGameEventListener interface:
Expand Down Expand Up @@ -279,6 +282,9 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
CWeaponGhost *m_pGhost = nullptr;
float m_flPrevThinkKick = 0.0f;
float m_flPrevThinkMirrorDmg = 0.0f;
bool m_bTeamBeenAwardedDueToCapPrevent = false;
int m_arrayiEntPrevCap[MAX_PLAYERS + 1]; // This is to check for cap-prevention workaround attempts
int m_iEntPrevCapSize = 0;
#endif
CNetworkVar(int, m_nRoundStatus); // NEO TODO (Rain): probably don't need to network this
CNetworkVar(int, m_iRoundNumber);
Expand Down