diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index 14a1f178..b30b2932 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -68,4 +68,6 @@ zr_human_win_overlay_material "" //Material override for human's win overlay pa zr_human_win_overlay_size 100 //Size of human's win overlay particle zr_zombie_win_overlay_particle "" //Screenspace particle to display when zombie win zr_zombie_win_overlay_material "" //Material override for zombie's win overlay particle -zr_zombie_win_overlay_size 100 //Size of zombie's win overlay particle \ No newline at end of file +zr_zombie_win_overlay_size 100 //Size of zombie's win overlay particle +zr_zombie_mute 0 // Prevent humans from hearing zombies. Zombies can still talk, but will only he heard by other zombies. +zr_zombie_mute_bypass "abj" // A list of admin flags that can talk to humans as a zombie diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp index 025f045a..88cca380 100644 --- a/src/adminsystem.cpp +++ b/src/adminsystem.cpp @@ -1302,6 +1302,19 @@ bool CAdminSystem::FindAndRemoveInfraction(ZEPlayer *player, CInfractionBase::EI return false; } +bool CAdminSystem::HasInfraction(ZEPlayer *player, CInfractionBase::EInfractionType type) +{ + FOR_EACH_VEC(m_vecInfractions, i) + { + if (m_vecInfractions[i]->GetSteamId64() == player->GetSteamId64() && m_vecInfractions[i]->GetType() == type) + { + return true; + } + } + + return false; +} + CAdmin *CAdminSystem::FindAdmin(uint64 iSteamID) { FOR_EACH_VEC(m_vecAdmins, i) diff --git a/src/adminsystem.h b/src/adminsystem.h index 287f9627..d2d371ce 100644 --- a/src/adminsystem.h +++ b/src/adminsystem.h @@ -148,6 +148,12 @@ class CAdminSystem void SaveInfractions(); bool ApplyInfractions(ZEPlayer *player); bool FindAndRemoveInfraction(ZEPlayer *player, CInfractionBase::EInfractionType type); + + /// @brief Determines if the player has an active infraction of the specified type + /// @param player + /// @param type + /// @return true if an active infraction is found + bool HasInfraction(ZEPlayer *player, CInfractionBase::EInfractionType type); CAdmin *FindAdmin(uint64 iSteamID); uint64 ParseFlags(const char* pszFlags); diff --git a/src/detours.cpp b/src/detours.cpp index 9e431fd0..542a919e 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -95,7 +95,7 @@ void FASTCALL Detour_CBaseEntity_TakeDamageOld(Z_CBaseEntity *pThis, CTakeDamage inputInfo->m_flDamage, inputInfo->m_bitsDamageType); #endif - + // Block all player damage if desired if (g_bBlockAllDamage && pThis->IsPawn()) return; @@ -155,7 +155,7 @@ void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, Z_CBaseEntity* pOthe Vector vecAbsDir; matrix3x4_t mat = pPush->m_CBodyComponent()->m_pSceneNode()->EntityToWorldTransform(); - + Vector pushDir = pPush->m_vecPushDirEntitySpace(); // i had issues with vectorrotate on linux so i did it here @@ -187,12 +187,20 @@ void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, Z_CBaseEntity* pOthe pOther->m_fFlags(flags); } -bool FASTCALL Detour_IsHearingClient(void* serverClient, int index) +bool FASTCALL Detour_IsHearingClient(CServerSideClient* serverClient, int index) { ZEPlayer* player = g_playerManager->GetPlayer(index); if (player && player->IsMuted()) return false; + if (serverClient) + { + CCSPlayerController* client = CCSPlayerController::FromSlot(serverClient->GetPlayerSlot()); + + if (player->IsMutedToTeam(client->m_iTeamNum())) + return false; + } + return IsHearingClient(serverClient, index); } @@ -256,7 +264,7 @@ void SayChatMessageWithTimer(IRecipientFilter &filter, const char *pText, CCSPla { if (pCurrentWord[j] >= '0' && pCurrentWord[j] <= '9') continue; - + if (pCurrentWord[j] == 's') { pCurrentWord[j] = '\0'; @@ -490,7 +498,7 @@ bool InitDetours(CGameConfig *gameConfig) if (!CCSPlayer_WeaponServices_CanUse.CreateDetour(gameConfig)) success = false; CCSPlayer_WeaponServices_CanUse.EnableDetour(); - + if (!CEntityIdentity_AcceptInput.CreateDetour(gameConfig)) success = false; CEntityIdentity_AcceptInput.EnableDetour(); diff --git a/src/detours.h b/src/detours.h index 7e984d8d..6e95ece9 100644 --- a/src/detours.h +++ b/src/detours.h @@ -46,7 +46,7 @@ void FlushAllDetours(); void FASTCALL Detour_UTIL_SayTextFilter(IRecipientFilter &, const char *, CCSPlayerController *, uint64); void FASTCALL Detour_UTIL_SayText2Filter(IRecipientFilter &, CCSPlayerController *, uint64, const char *, const char *, const char *, const char *, const char *); -bool FASTCALL Detour_IsHearingClient(void*, int); +bool FASTCALL Detour_IsHearingClient(CServerSideClient*, int); void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, Z_CBaseEntity* pOther); void FASTCALL Detour_CGameRules_Constructor(CGameRules *pThis); void FASTCALL Detour_CBaseEntity_TakeDamageOld(Z_CBaseEntity *pThis, CTakeDamageInfo *inputInfo); diff --git a/src/playermanager.h b/src/playermanager.h index a1744669..03e4c230 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -70,7 +70,7 @@ class ZEPlayerHandle void operator=(const ZEPlayerHandle &other) { m_Index = other.m_Index; } void operator=(ZEPlayer *pZEPlayer) { Set(pZEPlayer); } void Set(ZEPlayer *pZEPlayer); - + ZEPlayer *Get() const; private: @@ -89,12 +89,13 @@ class ZEPlayer { public: ZEPlayer(CPlayerSlot slot, bool m_bFakeClient = false): m_slot(slot), m_bFakeClient(m_bFakeClient), m_Handle(slot) - { + { m_bAuthenticated = false; m_iAdminFlags = 0; m_SteamID = nullptr; m_bGagged = false; m_bMuted = false; + m_iBlockVoiceToTeam = CS_TEAM_NONE; m_iHideDistance = 0; m_bConnected = false; m_iTotalDamage = 0; @@ -130,7 +131,7 @@ class ZEPlayer const CSteamID* GetSteamId() { return m_SteamID; } bool IsAdminFlagSet(uint64 iFlag); bool IsFlooding(); - + void SetAuthenticated() { m_bAuthenticated = true; } void SetConnected() { m_bConnected = true; } void SetUnauthenticatedSteamId(const CSteamID* steamID) { m_UnauthenticatedSteamID = steamID; } @@ -158,8 +159,10 @@ class ZEPlayer void SetFlashLight(CBarnLight *pLight) { m_hFlashLight.Set(pLight); } void SetBeaconParticle(CParticleSystem *pParticle) { m_hBeaconParticle.Set(pParticle); } void SetPlayerState(uint32 iPlayerState) { m_iPlayerState = iPlayerState; } + void SetMutedToTeam(int team) { m_iBlockVoiceToTeam = team; } bool IsMuted() { return m_bMuted; } + bool IsMutedToTeam(int team) { return m_iBlockVoiceToTeam == team; } bool IsGagged() { return m_bGagged; } bool ShouldBlockTransmit(int index) { return m_shouldTransmit.Get(index); } int GetHideDistance(); @@ -180,7 +183,7 @@ class ZEPlayer CParticleSystem *GetBeaconParticle() { return m_hBeaconParticle.Get(); } ZEPlayerHandle GetHandle() { return m_Handle; } uint32 GetPlayerState() { return m_iPlayerState; } - + void OnAuthenticated(); void CheckAdmin(); void CheckInfractions(); @@ -195,6 +198,7 @@ class ZEPlayer CPlayerSlot m_slot; bool m_bFakeClient; bool m_bMuted; + int m_iBlockVoiceToTeam; bool m_bGagged; uint64 m_iAdminFlags; int m_iHideDistance; @@ -253,7 +257,7 @@ class CPlayerManager uint64 GetStopSoundMask() { return m_nUsingStopSound; } uint64 GetSilenceSoundMask() { return m_nUsingSilenceSound; } uint64 GetStopDecalsMask() { return m_nUsingStopDecals; } - + void SetPlayerStopSound(int slot, bool set); void SetPlayerSilenceSound(int slot, bool set); void SetPlayerStopDecals(int slot, bool set); @@ -274,4 +278,4 @@ class CPlayerManager uint64 m_nUsingStopDecals; }; -extern CPlayerManager *g_playerManager; \ No newline at end of file +extern CPlayerManager *g_playerManager; diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index f5d1bbf4..6a0c9bb6 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -78,6 +78,9 @@ static std::string g_szZombieWinOverlayParticle; static std::string g_szZombieWinOverlayMaterial; static float g_flZombieWinOverlaySize; +static bool g_bMuteZombies; +static std::string g_szBypassZombieMutes; + FAKE_BOOL_CVAR(zr_enable, "Whether to enable ZR features", g_bEnableZR, false, false) FAKE_FLOAT_CVAR(zr_ztele_max_distance, "Maximum distance players are allowed to move after starting ztele", g_flMaxZteleDistance, 150.0f, false) FAKE_BOOL_CVAR(zr_ztele_allow_humans, "Whether to allow humans to use ztele", g_bZteleHuman, false, false) @@ -96,6 +99,8 @@ FAKE_FLOAT_CVAR(zr_human_win_overlay_size, "Size of human's win overlay particle FAKE_STRING_CVAR(zr_zombie_win_overlay_particle, "Screenspace particle to display when zombie win", g_szZombieWinOverlayParticle, false) FAKE_STRING_CVAR(zr_zombie_win_overlay_material, "Material override for zombie's win overlay particle", g_szZombieWinOverlayMaterial, false) FAKE_FLOAT_CVAR(zr_zombie_win_overlay_size, "Size of zombie's win overlay particle", g_flZombieWinOverlaySize, 5.0f, false) +FAKE_BOOL_CVAR(zr_zombie_mute, "Prevent humans from hearing zombies.", g_bMuteZombies, false, false) +FAKE_STRING_CVAR(zr_zombie_mute_bypass, "A list of admin flags that can talk to humans as a zombie", g_szBypassZombieMutes, false) void ZR_Precache(IEntityResourceManifest* pResourceManifest) { @@ -103,7 +108,7 @@ void ZR_Precache(IEntityResourceManifest* pResourceManifest) pResourceManifest->AddResource(g_szHumanWinOverlayParticle.c_str()); pResourceManifest->AddResource(g_szZombieWinOverlayParticle.c_str()); - + pResourceManifest->AddResource(g_szHumanWinOverlayMaterial.c_str()); pResourceManifest->AddResource(g_szZombieWinOverlayMaterial.c_str()); } @@ -197,7 +202,7 @@ void CZRPlayerClassManager::LoadPlayerClass() Message("Human Classes:\n"); else Message("Zombie Classes:\n"); - + for (KeyValues* pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey()) { bool bEnabled = pSubKey->GetBool("enabled", false); @@ -215,7 +220,7 @@ void CZRPlayerClassManager::LoadPlayerClass() // if (!bEnabled) // continue; - + if (!pSubKey->FindKey("team_default")) { Warning("%s has unspecified keyvalue: team_default\n", pszClassName); @@ -293,10 +298,10 @@ void CZRPlayerClassManager::LoadPlayerClass() if (bTeamDefault) m_vecHumanDefaultClass.AddToTail(pHumanClass); - + pHumanClass->PrintInfo(); } - else + else { ZRZombieClass *pZombieClass; if (pszBase) @@ -319,7 +324,7 @@ void CZRPlayerClassManager::LoadPlayerClass() m_ZombieClassMap.Insert(hash_32_fnv1a_const(pSubKey->GetName()), pZombieClass); if (pSubKey->GetBool("team_default", false)) m_vecZombieDefaultClass.AddToTail(pZombieClass); - + pZombieClass->PrintInfo(); } } @@ -388,7 +393,7 @@ void CZRPlayerClassManager::ApplyPreferredOrDefaultHumanClass(CCSPlayerPawn *pPa Warning("Missing default human class or valid preferences!\n"); return; } - + ApplyHumanClass(humanClass, pPawn); } @@ -428,7 +433,7 @@ void CZRPlayerClassManager::ApplyPreferredOrDefaultZombieClass(CCSPlayerPawn *pP Warning("Missing default zombie class or valid preferences!\n"); return; } - + ApplyZombieClass(zombieClass, pPawn); } @@ -500,11 +505,11 @@ void CZRRegenTimer::Tick() { continue; } - + if (pTimer->m_flLastExecute == -1) pTimer->m_flLastExecute = g_flUniversalTime; - // Timer execute + // Timer execute if (pTimer->m_flLastExecute + pTimer->m_flInterval <= g_flUniversalTime) { pTimer->Execute(); @@ -586,6 +591,28 @@ ZRWeapon* ZRWeaponConfig::FindWeapon(const char *pszWeaponName) return nullptr; } +void ZR_UpdateMute(CCSPlayerController* pController) +{ + if (!g_bMuteZombies) + return; + + ZEPlayer* player = g_playerManager->GetPlayer(pController->GetPlayerSlot()); + uint64 bypassFlags = g_pAdminSystem->ParseFlags(g_szBypassZombieMutes.c_str()); + + // Prevent zombies from speaking to humans + // Zombies can speak to each other and hear humans. + // Allow admins to bypass zombie muting to speak to humans if they need to. + if (pController->m_iTeamNum() == CS_TEAM_T) + { + if (player->IsAdminFlagSet(bypassFlags)) + return; + + return player->SetMutedToTeam(CS_TEAM_CT); + } + + player->SetMutedToTeam(CS_TEAM_NONE); +} + void ZR_RespawnAll() { for (int i = 0; i < gpGlobals->maxClients; i++) @@ -627,6 +654,7 @@ void ZR_OnRoundPrestart(IGameEvent* pEvent) continue; pController->SwitchTeam(CS_TEAM_CT); + ZR_UpdateMute(pController); } } @@ -695,7 +723,7 @@ void ZR_ApplyKnockback(CCSPlayerPawn *pHuman, CCSPlayerPawn *pVictim, int iDamag if (!pWeapon) return; float flWeaponKnockbackScale = pWeapon->flKnockback; - + Vector vecKnockback; AngleVectors(pHuman->m_angEyeAngles(), &vecKnockback); vecKnockback *= (iDamage * g_flKnockbackScale * flWeaponKnockbackScale); @@ -743,7 +771,7 @@ void ZR_StripAndGiveKnife(CCSPlayerPawn *pPawn) CCSPlayer_ItemServices *pItemServices = pPawn->m_pItemServices(); CPlayer_WeaponServices* pWeaponServices = pPawn->m_pWeaponServices(); - // it can sometimes be null when player joined on the very first round? + // it can sometimes be null when player joined on the very first round? if (!pItemServices || !pWeaponServices) return; @@ -779,6 +807,8 @@ void ZR_Cure(CCSPlayerController *pTargetController) if (!pTargetPawn) return; + ZR_UpdateMute(pTargetController); + g_pZRPlayerClassManager->ApplyPreferredOrDefaultHumanClass(pTargetPawn); } @@ -797,7 +827,8 @@ void ZR_Infect(CCSPlayerController *pAttackerController, CCSPlayerController *pV return; ZR_StripAndGiveKnife(pVictimPawn); - + ZR_UpdateMute(pVictimController); + g_pZRPlayerClassManager->ApplyPreferredOrDefaultZombieClass(pVictimPawn); } @@ -810,6 +841,8 @@ void ZR_InfectMotherZombie(CCSPlayerController *pVictimController) return; ZR_StripAndGiveKnife(pVictimPawn); + ZR_UpdateMute(pVictimController); + ZRZombieClass *pClass = g_pZRPlayerClassManager->GetZombieClass("MotherZombie"); if (pClass) g_pZRPlayerClassManager->ApplyZombieClass(pClass, pVictimPawn); @@ -938,7 +971,7 @@ void ZR_InitialInfection() ZEPlayer* pPlayer = g_playerManager->GetPlayer(i); if (!pPlayer || vecIsMZ[i]) continue; - + pPlayer->SetImmunity(pPlayer->GetImmunity() - g_iMZImmunityReduction); } @@ -1164,7 +1197,7 @@ bool ZR_IsTeamAlive(int iTeamNum) { if (!pPawn->IsAlive()) continue; - + if (pPawn->m_iTeamNum() == iTeamNum) return true; } @@ -1245,7 +1278,7 @@ void ZR_EndRoundAndAddTeamScore(int iTeamNum) ZR_CreateOverlay(g_szHumanWinOverlayParticle.c_str(), 1.0f, g_flHumanWinOverlaySize, 1.0f, flRestartDelay, Color(255, 255, 255), g_szHumanWinOverlayMaterial.c_str()); } else if (iTeamNum == CS_TEAM_T) - { + { if (!g_hTeamT.Get()) { Panic("Cannot find CTeam for T!\n"); @@ -1391,7 +1424,7 @@ CON_COMMAND_CHAT(zclass, "find and select your Z:R class") if (sCurrentClass[0] != '\0') { ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Your current %s class is: %s. Available classes:", sTeamName, sCurrentClass); - } + } else { ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Available %s classes:", sTeamName); @@ -1528,4 +1561,4 @@ CON_COMMAND_CHAT_FLAGS(revive, "revive a player", ADMFLAG_GENERIC) } PrintMultiAdminAction(nType, pszCommandPlayerName, "revived", "", ZR_PREFIX); -} \ No newline at end of file +}