diff --git a/README.md b/README.md index 93cd795d2..e5b55c0cf 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | mp_freezetime_duck | 1 | 0 | 1 | Allow players to duck during freezetime.
`0` disabled
`1` enabled | | mp_freezetime_jump | 1 | 0 | 1 | Allow players to jump during freezetime.
`0` disabled
`1` enabled | | mp_defuser_allocation | 0 | 0 | 2 | Give defuser on player spawn.
`0` disabled
`1` Random players.
`2` All players. | +| mp_location_area_info | 0 | 0 | 3 | Enable location area info.
`0` disabled
`1` show location below HUD radar.
`2` show location in HUD chat. `NOT RECOMMENDED!` [:speech_balloon:](## "Not all client builds are compatible")
`3` both displayed. `NOT RECOMMENDED!` [:speech_balloon:](## "Not all client builds are compatible")

`NOTE`: Navigation `maps/.nav` file required and should contain place names
`NOTE`: If option `2` or `3` is enabled, be sure to enable `mp_chat_loc_fallback 1` | ## How to install zBot for CS 1.6? diff --git a/dist/game.cfg b/dist/game.cfg index 91009dd00..e6510f345 100644 --- a/dist/game.cfg +++ b/dist/game.cfg @@ -575,3 +575,15 @@ mp_freezetime_jump "1" // // Default value: "0" mp_defuser_allocation "0" + +// Enable location area info +// 0 - disabled (default behavior) +// 1 - show location below HUD radar +// 2 - show location in HUD chat (NOT RECOMMENDED! Not all client builds are compatible) +// 3 - both displayed (NOT RECOMMENDED! Not all client builds are compatible) +// +// NOTE: Navigation maps/.nav file required and should contain place names +// NOTE: If option 2 or 3 is enabled, be sure to enable mp_chat_loc_fallback 1 +// +// Default value: "0" +mp_location_area_info "0" diff --git a/regamedll/dlls/API/CAPI_Impl.cpp b/regamedll/dlls/API/CAPI_Impl.cpp index 5bb6b3940..41175c838 100644 --- a/regamedll/dlls/API/CAPI_Impl.cpp +++ b/regamedll/dlls/API/CAPI_Impl.cpp @@ -335,6 +335,7 @@ GAMEHOOK_REGISTRY(CSGameRules_SendDeathMessage); GAMEHOOK_REGISTRY(CBasePlayer_PlayerDeathThink); GAMEHOOK_REGISTRY(CBasePlayer_Observer_Think); +GAMEHOOK_REGISTRY(CBasePlayer_RemoveAllItems); int CReGameApi::GetMajorVersion() { return REGAMEDLL_API_VERSION_MAJOR; diff --git a/regamedll/dlls/API/CAPI_Impl.h b/regamedll/dlls/API/CAPI_Impl.h index 0548a841f..2635be5b9 100644 --- a/regamedll/dlls/API/CAPI_Impl.h +++ b/regamedll/dlls/API/CAPI_Impl.h @@ -745,6 +745,10 @@ typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBase typedef IHookChainClassImpl CReGameHook_CBasePlayer_Observer_Think; typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBasePlayer_Observer_Think; +// CBasePlayer::RemoveAllItems hook +typedef IHookChainClassImpl CReGameHook_CBasePlayer_RemoveAllItems; +typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBasePlayer_RemoveAllItems; + class CReGameHookchains: public IReGameHookchains { public: // CBasePlayer virtual @@ -905,6 +909,7 @@ class CReGameHookchains: public IReGameHookchains { CReGameHookRegistry_CBasePlayer_PlayerDeathThink m_CBasePlayer_PlayerDeathThink; CReGameHookRegistry_CBasePlayer_Observer_Think m_CBasePlayer_Observer_Think; + CReGameHookRegistry_CBasePlayer_RemoveAllItems m_CBasePlayer_RemoveAllItems; public: virtual IReGameHookRegistry_CBasePlayer_Spawn *CBasePlayer_Spawn(); @@ -1064,6 +1069,7 @@ class CReGameHookchains: public IReGameHookchains { virtual IReGameHookRegistry_CBasePlayer_PlayerDeathThink *CBasePlayer_PlayerDeathThink(); virtual IReGameHookRegistry_CBasePlayer_Observer_Think *CBasePlayer_Observer_Think(); + virtual IReGameHookRegistry_CBasePlayer_RemoveAllItems *CBasePlayer_RemoveAllItems(); }; extern CReGameHookchains g_ReGameHookchains; diff --git a/regamedll/dlls/bot/cs_bot.cpp b/regamedll/dlls/bot/cs_bot.cpp index 838387a95..dd85448c6 100644 --- a/regamedll/dlls/bot/cs_bot.cpp +++ b/regamedll/dlls/bot/cs_bot.cpp @@ -43,10 +43,7 @@ int GetBotFollowCount(CBasePlayer *pLeader) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -685,10 +682,7 @@ CBasePlayer *CCSBot::GetImportantEnemy(bool checkVisibility) const { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) diff --git a/regamedll/dlls/bot/cs_bot_chatter.cpp b/regamedll/dlls/bot/cs_bot_chatter.cpp index 31bd03bbd..1476d8d58 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.cpp +++ b/regamedll/dlls/bot/cs_bot_chatter.cpp @@ -65,10 +65,7 @@ void BotMeme::Transmit(CCSBot *pSender) const { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -540,8 +537,13 @@ bool BotPhraseManager::Initialize(const char *filename, int bankIndex) else if (!Q_stricmp("UNDEFINED", token)) placeCriteria = UNDEFINED_PLACE; else + { placeCriteria = TheBotPhrases->NameToID(token); + if (!TheBotPhrases->IsValid() && placeCriteria == UNDEFINED_PLACE) + placeCriteria = TheNavAreaGrid.NameToID(token); + } + continue; } @@ -1520,10 +1522,7 @@ BotStatement *BotChatterInterface::GetActiveStatement() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) diff --git a/regamedll/dlls/bot/cs_bot_chatter.h b/regamedll/dlls/bot/cs_bot_chatter.h index f8d7f45b2..4a4591cf6 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.h +++ b/regamedll/dlls/bot/cs_bot_chatter.h @@ -255,6 +255,8 @@ class BotPhraseManager Place NameToID(const char *name) const; const char *IDToName(Place id) const; + bool IsValid() const { return !m_placeList.empty(); } + // given a name, return the associated phrase collection const BotPhrase *GetPhrase(const char *name) const; diff --git a/regamedll/dlls/bot/cs_bot_init.cpp b/regamedll/dlls/bot/cs_bot_init.cpp index 4ed27ac55..a6503e901 100644 --- a/regamedll/dlls/bot/cs_bot_init.cpp +++ b/regamedll/dlls/bot/cs_bot_init.cpp @@ -62,6 +62,8 @@ cvar_t cv_bot_deathmatch = { "bot_deathmatch", "0", FCVAR_SERVER, 0. cvar_t cv_bot_quota_mode = { "bot_quota_mode", "normal", FCVAR_SERVER, 0.0f, nullptr }; cvar_t cv_bot_join_delay = { "bot_join_delay", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t cv_bot_freeze = { "bot_freeze", "0", 0, 0.0f, nullptr }; +cvar_t cv_bot_mimic = { "bot_mimic", "0", 0, 0.0f, nullptr }; +cvar_t cv_bot_mimic_yaw_offset = { "bot_mimic_yaw_offset", "0", 0, 0.0f, nullptr }; #else // Migrated to bot_quota_mode, use "match" cvar_t cv_bot_quota_match = { "bot_quota_match", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -131,6 +133,8 @@ void Bot_RegisterCVars() CVAR_REGISTER(&cv_bot_quota_mode); CVAR_REGISTER(&cv_bot_join_delay); CVAR_REGISTER(&cv_bot_freeze); + CVAR_REGISTER(&cv_bot_mimic); + CVAR_REGISTER(&cv_bot_mimic_yaw_offset); #endif } diff --git a/regamedll/dlls/bot/cs_bot_init.h b/regamedll/dlls/bot/cs_bot_init.h index 4f62b511b..8687e3f7f 100644 --- a/regamedll/dlls/bot/cs_bot_init.h +++ b/regamedll/dlls/bot/cs_bot_init.h @@ -62,6 +62,8 @@ extern cvar_t cv_bot_deathmatch; extern cvar_t cv_bot_quota_mode; extern cvar_t cv_bot_join_delay; extern cvar_t cv_bot_freeze; +extern cvar_t cv_bot_mimic; +extern cvar_t cv_bot_mimic_yaw_offset; #else extern cvar_t cv_bot_quota_match; #endif diff --git a/regamedll/dlls/bot/cs_bot_manager.cpp b/regamedll/dlls/bot/cs_bot_manager.cpp index 78ce0cf5d..b4391be7f 100644 --- a/regamedll/dlls/bot/cs_bot_manager.cpp +++ b/regamedll/dlls/bot/cs_bot_manager.cpp @@ -337,6 +337,10 @@ void CCSBotManager::ClientDisconnect(CBasePlayer *pPlayer) pPlayer = GetClassPtr((CBasePlayer *)pevTemp); AddEntityHashValue(pPlayer->pev, STRING(pPlayer->pev->classname), CLASSNAME); pPlayer->pev->flags = FL_DORMANT; + +#ifdef REGAMEDLL_FIXES + pPlayer->has_disconnected = true; +#endif } void PrintAllEntities() @@ -396,16 +400,19 @@ void CCSBotManager::ServerCommand(const char *pcmd) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; const char *name = STRING(pPlayer->pev->netname); if (FStrEq(name, "")) continue; +#ifdef REGAMEDLL_FIXES + if (pPlayer->pev->deadflag != DEAD_NO) + continue; +#endif + if (pPlayer->IsBot()) { CCSBot *pBot = static_cast(pPlayer); @@ -422,13 +429,17 @@ void CCSBotManager::ServerCommand(const char *pcmd) else kickThemAll = false; +#ifdef REGAMEDLL_ADD + bool fillMode = FStrEq(cv_bot_quota_mode.string, "fill"); +#else + bool fillMode = false; +#endif + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; const char *name = STRING(pPlayer->pev->netname); @@ -443,7 +454,11 @@ void CCSBotManager::ServerCommand(const char *pcmd) // adjust bot quota so kicked bot is not immediately added back in int newQuota = cv_bot_quota.value - 1; SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", name)); - CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, int(cv_bot_quota.value))); + + if (kickThemAll || !fillMode) + { + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, int(cv_bot_quota.value))); + } } } } @@ -665,6 +680,9 @@ void CCSBotManager::ServerCommand(const char *pcmd) CBaseEntity *pEntity = nullptr; while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) { + if (FNullEnt(pEntity->edict())) + break; + if (!pEntity->IsPlayer()) continue; @@ -745,6 +763,23 @@ void CCSBotManager::ServerCommand(const char *pcmd) BOOL CCSBotManager::ClientCommand(CBasePlayer *pPlayer, const char *pcmd) { +#ifdef REGAMEDLL_ADD + if (pPlayer->IsBot()) + return FALSE; + + if (cv_bot_mimic.value == pPlayer->entindex()) + { + // Bots mimic our client commands + ForEachPlayer([pPlayer, pcmd](CBasePlayer *bot) + { + if (pPlayer != bot && bot->IsBot()) + bot->ClientCommand(pcmd, CMD_ARGV_(1)); + + return true; + }); + } +#endif + return FALSE; } @@ -787,7 +822,8 @@ bool CCSBotManager::BotAddCommand(BotProfileTeamType team, bool isFromConsole) // decrease the bot quota if (!isFromConsole) { - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1); + int newQuota = cv_bot_quota.value - 1; + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, (int)cv_bot_quota.value)); } #endif @@ -821,7 +857,8 @@ bool CCSBotManager::BotAddCommand(BotProfileTeamType team, bool isFromConsole) if (isFromConsole) { // increase the bot quota to account for manually added bot - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value + 1); + int newQuota = cv_bot_quota.value + 1; + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, gpGlobals->maxClients)); } } #ifdef REGAMEDLL_FIXES @@ -830,7 +867,8 @@ bool CCSBotManager::BotAddCommand(BotProfileTeamType team, bool isFromConsole) // decrease the bot quota if (!isFromConsole) { - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1); + int newQuota = cv_bot_quota.value - 1; + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, (int)cv_bot_quota.value)); } } #endif @@ -860,10 +898,17 @@ void CCSBotManager::MaintainBotQuota() int desiredBotCount = int(cv_bot_quota.value); int occupiedBotSlots = UTIL_BotsInGame(); + bool isRoundInDeathmatch = false; + +#ifdef REGAMEDLL_ADD + if (round_infinite.value > 0) + isRoundInDeathmatch = true; // is no round end gameplay +#endif + // isRoundInProgress is true if the round has progressed far enough that new players will join as dead. bool isRoundInProgress = CSGameRules()->IsGameStarted() && !TheCSBots()->IsRoundOver() && - (CSGameRules()->GetRoundElapsedTime() >= CSGameRules()->GetRoundRespawnTime()); + (CSGameRules()->GetRoundRespawnTime() != -1 && CSGameRules()->GetRoundElapsedTime() >= CSGameRules()->GetRoundRespawnTime()) && !isRoundInDeathmatch; #ifdef REGAMEDLL_ADD if (FStrEq(cv_bot_quota_mode.string, "fill")) @@ -872,7 +917,7 @@ void CCSBotManager::MaintainBotQuota() // unless the round is already in progress, in which case we play with what we've been dealt if (!isRoundInProgress) { - desiredBotCount = Q_max(0, desiredBotCount - humanPlayersInGame + spectatorPlayersInGame); + desiredBotCount = Q_max(0, desiredBotCount - humanPlayersInGame); } else { @@ -919,13 +964,15 @@ void CCSBotManager::MaintainBotQuota() if (cv_bot_auto_vacate.value > 0.0) desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - (humanPlayersInGame + 1)); else - desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - humanPlayersInGame + spectatorPlayersInGame); + desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - humanPlayersInGame); #ifdef REGAMEDLL_FIXES // Try to balance teams, if we are in the first specified seconds of a round and bots can join either team. - if (occupiedBotSlots > 0 && desiredBotCount == occupiedBotSlots && CSGameRules()->IsGameStarted()) + if (occupiedBotSlots > 0 && desiredBotCount == occupiedBotSlots && (CSGameRules()->IsGameStarted() || isRoundInDeathmatch)) { - if (CSGameRules()->GetRoundElapsedTime() < CSGameRules()->GetRoundRespawnTime()) // new bots can still spawn during this time + if (isRoundInDeathmatch || + (CSGameRules()->GetRoundRespawnTime() == -1 || // means no time limit + CSGameRules()->GetRoundElapsedTime() < CSGameRules()->GetRoundRespawnTime())) // new bots can still spawn during this time { if (autoteambalance.value > 0.0f) { @@ -1042,7 +1089,8 @@ void CCSBotManager::MaintainBotQuota() UTIL_KickBotFromTeam(TERRORIST); } - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1.0f); + int newQuota = cv_bot_quota.value - 1; + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, (int)cv_bot_quota.value)); } } @@ -1580,7 +1628,8 @@ void CCSBotManager::OnFreeEntPrivateData(CBaseEntity *pEntity) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || pPlayer->IsDormant()) + + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->IsBot()) diff --git a/regamedll/dlls/bot/cs_bot_pathfind.cpp b/regamedll/dlls/bot/cs_bot_pathfind.cpp index 00386be7b..f418b56cb 100644 --- a/regamedll/dlls/bot/cs_bot_pathfind.cpp +++ b/regamedll/dlls/bot/cs_bot_pathfind.cpp @@ -1118,10 +1118,7 @@ bool CCSBot::IsFriendInTheWay(const Vector *goalPos) const { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (!pPlayer->IsAlive()) diff --git a/regamedll/dlls/bot/cs_bot_statemachine.cpp b/regamedll/dlls/bot/cs_bot_statemachine.cpp index 325252641..7bb31eddc 100644 --- a/regamedll/dlls/bot/cs_bot_statemachine.cpp +++ b/regamedll/dlls/bot/cs_bot_statemachine.cpp @@ -299,6 +299,12 @@ void CCSBot::Attack(CBasePlayer *victim) if (cv_bot_zombie.value != 0.0f) return; +#ifdef REGAMEDLL_ADD + // If mimicing the player, don't attack state + if (cv_bot_mimic.value) + return; +#endif + // cannot attack if we are reloading if (IsActiveWeaponReloading()) return; diff --git a/regamedll/dlls/bot/cs_bot_vision.cpp b/regamedll/dlls/bot/cs_bot_vision.cpp index 8fc811ed2..f7b16acc5 100644 --- a/regamedll/dlls/bot/cs_bot_vision.cpp +++ b/regamedll/dlls/bot/cs_bot_vision.cpp @@ -61,6 +61,12 @@ void CCSBot::UpdateLookAngles() float stiffness; float damping; +#ifdef REGAMEDLL_ADD + // If mimicing the player, don't modify the view angles + if (cv_bot_mimic.value > 0) + return; +#endif + // springs are stiffer when attacking, so we can track and move between targets better if (IsAttacking()) { @@ -253,7 +259,7 @@ bool CCSBot::IsVisible(CBasePlayer *pPlayer, bool testFOV, unsigned char *visPar if ((pPlayer->pev->flags & FL_NOTARGET) || (pPlayer->pev->effects & EF_NODRAW)) return false; #endif - + Vector spot = pPlayer->pev->origin; unsigned char testVisParts = NONE; @@ -701,10 +707,7 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; // is it a player? diff --git a/regamedll/dlls/career_tasks.cpp b/regamedll/dlls/career_tasks.cpp index 200c67558..972ccca9b 100644 --- a/regamedll/dlls/career_tasks.cpp +++ b/regamedll/dlls/career_tasks.cpp @@ -545,7 +545,11 @@ void CCareerTaskManager::HandleDeath(int team, CBasePlayer *pAttacker) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && pPlayer->m_iTeam == enemyTeam && pPlayer->IsAlive()) + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (pPlayer->m_iTeam == enemyTeam && pPlayer->IsAlive()) numEnemies++; } diff --git a/regamedll/dlls/client.cpp b/regamedll/dlls/client.cpp index 2a58443d7..7d3fb659e 100644 --- a/regamedll/dlls/client.cpp +++ b/regamedll/dlls/client.cpp @@ -438,6 +438,9 @@ NOXREF int CountTeams() if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->m_iTeam == UNASSIGNED) @@ -499,7 +502,8 @@ int CountTeamPlayers(int iTeam) if (pEntity->IsDormant()) continue; - if (GetClassPtr((CBasePlayer *)pEntity->pev)->m_iTeam == iTeam) + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); + if (pPlayer->m_iTeam == iTeam) { nCount++; } @@ -534,6 +538,9 @@ void ProcessKickVote(CBasePlayer *pVotingPlayer, CBasePlayer *pKickPlayer) if (FNullEnt(pTempEntity->edict())) break; + if (pTempEntity->IsDormant()) + continue; + pTempPlayer = GetClassPtr((CBasePlayer *)pTempEntity->pev); if (!pTempPlayer || pTempPlayer->m_iTeam == UNASSIGNED) @@ -571,6 +578,9 @@ void ProcessKickVote(CBasePlayer *pVotingPlayer, CBasePlayer *pKickPlayer) if (FNullEnt(pTempEntity->edict())) break; + if (pTempEntity->IsDormant()) + continue; + pTempPlayer = GetClassPtr((CBasePlayer *)pTempEntity->pev); if (!pTempPlayer || pTempPlayer->m_iTeam == UNASSIGNED) @@ -829,14 +839,18 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) return; const char *placeName = nullptr; - char *pszFormat = nullptr; + const char *pszFormat = nullptr; char *pszConsoleFormat = nullptr; bool consoleUsesPlaceName = false; // team only if (teamonly) { - if (AreRunningCZero() && (pPlayer->m_iTeam == CT || pPlayer->m_iTeam == TERRORIST)) + if (( +#ifdef REGAMEDLL_ADD + location_area_info.value >= 2 || +#endif + AreRunningCZero()) && (pPlayer->m_iTeam == CT || pPlayer->m_iTeam == TERRORIST)) { // search the place name where is located the player Place playerPlace = TheNavAreaGrid.GetPlace(&pPlayer->pev->origin); @@ -850,8 +864,17 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) break; } } + + if (!placeName) + placeName = TheNavAreaGrid.IDToName(playerPlace); } + bool bUseLocFallback = false; +#ifdef REGAMEDLL_ADD + if (chat_loc_fallback.value) + bUseLocFallback = true; +#endif + if (pPlayer->m_iTeam == CT) { if (bSenderDead) @@ -861,7 +884,7 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) } else if (placeName) { - pszFormat = "#Cstrike_Chat_CT_Loc"; + pszFormat = bUseLocFallback ? "\x1(Counter-Terrorist) \x3%s1\x1 @ \x4%s3\x1 : %s2" : "#Cstrike_Chat_CT_Loc"; pszConsoleFormat = "*(Counter-Terrorist) %s @ %s : %s"; consoleUsesPlaceName = true; } @@ -880,7 +903,7 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) } else if (placeName) { - pszFormat = "#Cstrike_Chat_T_Loc"; + pszFormat = bUseLocFallback ? "\x1(Terrorist) \x3%s1\x1 @ \x4%s3\x1 : %s2" : "#Cstrike_Chat_T_Loc"; pszConsoleFormat = "(Terrorist) %s @ %s : %s"; consoleUsesPlaceName = true; } @@ -963,6 +986,9 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) if (pReceiver->edict() == pEntity) continue; + if (pReceiver->IsDormant()) + continue; + // Not a client ? (should never be true) if (!pReceiver->IsNetClient()) continue; @@ -2330,6 +2356,9 @@ CBaseEntity *EntityFromUserID(int userID) if (FNullEnt(pTempEntity->edict())) break; + if (pTempEntity->IsDormant()) + continue; + CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pTempEntity->pev); if (pTempPlayer->m_iTeam != UNASSIGNED && userID == GETPLAYERUSERID(pTempEntity->edict())) @@ -2350,6 +2379,9 @@ NOXREF int CountPlayersInServer() if (FNullEnt(pTempEntity->edict())) break; + if (pTempEntity->IsDormant()) + continue; + CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pTempEntity->pev); if (pTempPlayer->m_iTeam != UNASSIGNED) @@ -3330,7 +3362,11 @@ void EXT_FUNC InternalCommand(edict_t *pEntity, const char *pcmd, const char *pa for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (pObserver && pObserver->IsObservingPlayer(pPlayer)) + + if (!UTIL_IsValidPlayer(pObserver)) + continue; + + if (pObserver->IsObservingPlayer(pPlayer)) { EMIT_SOUND(ENT(pObserver->pev), CHAN_ITEM, "items/nvg_off.wav", RANDOM_FLOAT(0.92, 1), ATTN_NORM); @@ -3355,7 +3391,11 @@ void EXT_FUNC InternalCommand(edict_t *pEntity, const char *pcmd, const char *pa for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (pObserver && pObserver->IsObservingPlayer(pPlayer)) + + if (!UTIL_IsValidPlayer(pObserver)) + continue; + + if (pObserver->IsObservingPlayer(pPlayer)) { EMIT_SOUND(ENT(pObserver->pev), CHAN_ITEM, "items/nvg_on.wav", RANDOM_FLOAT(0.92, 1), ATTN_NORM); @@ -3723,6 +3763,9 @@ void EXT_FUNC ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) #ifdef REGAMEDLL_ADD CSGameRules()->ServerActivate(); + + if (location_area_info.value) + LoadNavigationMap(); #endif } diff --git a/regamedll/dlls/cmdhandler.cpp b/regamedll/dlls/cmdhandler.cpp index d715de641..73bfe7cfd 100644 --- a/regamedll/dlls/cmdhandler.cpp +++ b/regamedll/dlls/cmdhandler.cpp @@ -37,7 +37,10 @@ void SV_Continue_f() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && !pPlayer->IsBot()) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) { // at the end of the round is showed window with the proposal surrender or continue // now of this time HUD is completely hidden @@ -96,7 +99,7 @@ void SV_Career_EndRound_f() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->IsBot() && pPlayer->m_iTeam == pLocalPlayer->m_iTeam) diff --git a/regamedll/dlls/combat.cpp b/regamedll/dlls/combat.cpp index 4cbc485dc..17fe84e27 100644 --- a/regamedll/dlls/combat.cpp +++ b/regamedll/dlls/combat.cpp @@ -9,7 +9,11 @@ void PlayerBlind(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAt for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (pObserver && pObserver->IsObservingPlayer(pPlayer)) + + if (!UTIL_IsValidPlayer(pObserver)) + continue; + + if (pObserver->IsObservingPlayer(pPlayer)) { UTIL_ScreenFade(pObserver, color, fadeTime, fadeHold, alpha, 0); } diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 4ab375d8b..8d781724d 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -178,6 +178,8 @@ cvar_t legacy_vehicle_block = { "mp_legacy_vehicle_block", "1", 0, cvar_t dying_time = { "mp_dying_time", "3.0", 0, 3.0f, nullptr }; cvar_t defuser_allocation = { "mp_defuser_allocation", "0", 0, 0.0f, nullptr }; +cvar_t location_area_info = { "mp_location_area_info", "0", 0, 0.0f, nullptr }; +cvar_t chat_loc_fallback = { "mp_chat_loc_fallback", "1", 1, 0.0f, nullptr }; void GameDLL_Version_f() { @@ -441,6 +443,8 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&freezetime_duck); CVAR_REGISTER(&freezetime_jump); CVAR_REGISTER(&defuser_allocation); + CVAR_REGISTER(&location_area_info); + CVAR_REGISTER(&chat_loc_fallback); // print version CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index 706e1c2af..dcd712f5e 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -201,6 +201,8 @@ extern cvar_t assist_damage_threshold; extern cvar_t freezetime_duck; extern cvar_t freezetime_jump; extern cvar_t defuser_allocation; +extern cvar_t location_area_info; +extern cvar_t chat_loc_fallback; #endif diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index e7dc35085..cc6e9ec8f 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -252,7 +252,8 @@ enum KillRarity KILLRARITY_ASSISTEDFLASH = 0x020, // Assister helped with a flash KILLRARITY_DOMINATION_BEGAN = 0x040, // Killer player began dominating the victim (NOTE: this flag is set once) KILLRARITY_DOMINATION = 0x080, // Continues domination by the killer - KILLRARITY_REVENGE = 0x100 // Revenge by the killer + KILLRARITY_REVENGE = 0x100, // Revenge by the killer + KILLRARITY_INAIR = 0x200 // Killer was in the air (skill to deal with high inaccuracy) }; enum @@ -741,7 +742,7 @@ class CHalfLifeMultiplay: public CGameRules VFUNC bool HasRoundTimeExpired(); VFUNC bool IsBombPlanted(); - void SendDeathMessage(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); + VFUNC void SendDeathMessage(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); int GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bFlashAssist); CBasePlayer *CheckAssistsToKill(CBaseEntity *pKiller, CBasePlayer *pVictim, bool &bFlashAssist); diff --git a/regamedll/dlls/hostage/hostage.cpp b/regamedll/dlls/hostage/hostage.cpp index c9ff79725..7ef34b222 100644 --- a/regamedll/dlls/hostage/hostage.cpp +++ b/regamedll/dlls/hostage/hostage.cpp @@ -1242,7 +1242,7 @@ void CHostage::SendHostagePositionMsg() if (!pEntity->IsPlayer()) continue; - if (pEntity->pev->flags == FL_DORMANT) + if (pEntity->IsDormant()) continue; CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); @@ -1271,7 +1271,7 @@ void CHostage::SendHostageEventMsg() if (!pEntity->IsPlayer()) continue; - if (pEntity->pev->flags == FL_DORMANT) + if (pEntity->IsDormant()) continue; CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); diff --git a/regamedll/dlls/hostage/hostage_improv.cpp b/regamedll/dlls/hostage/hostage_improv.cpp index a70d2c76a..9cdba7777 100644 --- a/regamedll/dlls/hostage/hostage_improv.cpp +++ b/regamedll/dlls/hostage/hostage_improv.cpp @@ -358,10 +358,7 @@ bool CHostageImprov::IsFriendInTheWay(const Vector &goalPos) const { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (!pPlayer->IsAlive() || pPlayer->m_iTeam == TERRORIST) @@ -675,10 +672,7 @@ void CHostageImprov::UpdateVision() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index e0d27dfb2..7caa946b8 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -34,7 +34,10 @@ bool IsBotSpeaking() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || !pPlayer->IsBot()) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) continue; CCSBot *pBot = static_cast(pPlayer); @@ -267,6 +270,10 @@ void CHalfLifeMultiplay::EndRoundMessage(const char *sentence, ScenarioEventEndR } UTIL_LogPrintf("World triggered \"Round_End\"\n"); + +#ifdef REGAMEDLL_ADD + FireTargets("game_round_end", nullptr, nullptr, USE_TOGGLE, 0.0); +#endif } void CHalfLifeMultiplay::ReadMultiplayCvars() @@ -648,6 +655,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(CleanUpMap)() UTIL_RestartOther("env_beam"); UTIL_RestartOther("env_laser"); UTIL_RestartOther("trigger_auto"); + UTIL_RestartOther("trigger_multiple"); #endif // Remove grenades and C4 @@ -699,7 +707,7 @@ CBasePlayer *EXT_FUNC CHalfLifeMultiplay::__API_HOOK(GiveC4)() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->edict())) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->pev->deadflag != DEAD_NO || pPlayer->m_iTeam != TERRORIST) @@ -1074,10 +1082,8 @@ bool EXT_FUNC CHalfLifeMultiplay::NeededPlayersCheck() if (IsCareer()) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(gpGlobals->maxClients); - if (!pPlayer || !pPlayer->IsBot()) - { + if (!UTIL_IsValidPlayer(pPlayer) || !pPlayer->IsBot()) return true; - } } return OnRoundEnd_Intercept(WINSTATUS_DRAW, ROUND_GAME_COMMENCE, IsCareer() ? 0 : 3); @@ -1810,10 +1816,11 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(RestartRound)() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && !FNullEnt(pPlayer->pev)) - { - pPlayer->Reset(); - } + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + pPlayer->Reset(); } if (TheBots) @@ -1981,7 +1988,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(RestartRound)() if (FNullEnt(pEntity->edict())) break; - if (pEntity->pev->flags == FL_DORMANT) + if (pEntity->IsDormant()) continue; CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); @@ -2092,12 +2099,17 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(RestartRound)() BOOL CHalfLifeMultiplay::IsThereABomber() { - CBasePlayer *pPlayer = nullptr; - while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) { - if (FNullEnt(pPlayer->edict())) + if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); + if (pPlayer->m_iTeam != CT && pPlayer->IsBombGuy()) { // There you are. @@ -2512,7 +2524,10 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(Think)() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && !pPlayer->IsBot()) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) { MESSAGE_BEGIN(MSG_ONE, gmsgCZCareerHUD, nullptr, pPlayer->pev); WRITE_STRING("ROUND"); @@ -2722,9 +2737,9 @@ bool CHalfLifeMultiplay::CheckFragLimit() // check if any player is over the frag limit for (int i = 1; i <= gpGlobals->maxClients; i++) { - auto pPlayer = UTIL_PlayerByIndex(i); + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || pPlayer->has_disconnected) + if (!UTIL_IsValidPlayer(pPlayer) || pPlayer->has_disconnected) continue; if (pPlayer->pev->frags >= fraglimit.value) @@ -2817,9 +2832,15 @@ void EXT_FUNC CHalfLifeMultiplay::OnRoundFreezeEnd() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *plr = UTIL_PlayerByIndex(i); - if (!plr || plr->pev->flags == FL_DORMANT) + + if (!UTIL_IsValidPlayer(plr)) continue; +#ifndef REGAMEDLL_FIXES + if (plr->pev->flags == FL_DORMANT) + continue; +#endif + if (plr->m_iJoiningState == JOINED) { if (plr->m_iTeam == CT && !bCTPlayed) @@ -2852,6 +2873,10 @@ void EXT_FUNC CHalfLifeMultiplay::OnRoundFreezeEnd() { TheCareerTasks->HandleEvent(EVENT_ROUND_START); } + +#ifdef REGAMEDLL_ADD + FireTargets("game_round_freeze_end", nullptr, nullptr, USE_TOGGLE, 0.0); +#endif } void CHalfLifeMultiplay::CheckFreezePeriodExpired() @@ -3191,7 +3216,7 @@ void CHalfLifeMultiplay::MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->m_iTeam == iTeam) @@ -3229,7 +3254,7 @@ void CHalfLifeMultiplay::CareerRestart() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (!pPlayer->IsBot()) @@ -3430,13 +3455,9 @@ void CHalfLifeMultiplay::InitHUD(CBasePlayer *pl) { // FIXME: Probably don't need to cast this just to read m_iDeaths CBasePlayer *plr = UTIL_PlayerByIndex(i); - if (!plr) + if (!UTIL_IsValidPlayer(plr)) continue; -#ifdef REGAMEDLL_FIXES - if (plr->IsDormant()) - continue; -#endif MESSAGE_BEGIN(MSG_ONE, gmsgScoreInfo, nullptr, pl->edict()); WRITE_BYTE(i); // client number WRITE_SHORT(int(plr->pev->frags)); @@ -3475,13 +3496,9 @@ void CHalfLifeMultiplay::InitHUD(CBasePlayer *pl) for (i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *plr = UTIL_PlayerByIndex(i); - if (!plr) - continue; -#ifdef REGAMEDLL_FIXES - if (plr->IsDormant()) + if (!UTIL_IsValidPlayer(plr)) continue; -#endif MESSAGE_BEGIN(MSG_ONE, gmsgTeamInfo, nullptr, pl->edict()); WRITE_BYTE(plr->entindex()); @@ -3493,7 +3510,7 @@ void CHalfLifeMultiplay::InitHUD(CBasePlayer *pl) if (pl->entindex() != i) { #ifndef REGAMEDLL_FIXES - if (plr->pev->flags == FL_DORMANT) + if (plr->IsDormant()) continue; #endif if (plr->pev->deadflag == DEAD_NO @@ -3654,6 +3671,9 @@ void CHalfLifeMultiplay::ClientDisconnected(edict_t *pClient) if (!pObserver->pev || pObserver == pPlayer) continue; + if (pObserver->IsDormant()) + continue; + // If a spectator was chasing this player, move him/her onto the next player if (pObserver->m_hObserverTarget == pPlayer) { @@ -4121,6 +4141,16 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, iDeathMessageFlags |= PLAYERDEATH_KILLRARITY; } +#ifdef REGAMEDLL_ADD + iDeathMessageFlags &= UTIL_ReadFlags(deathmsg_flags.string); // leave only allowed bitsums for extra info + + // Send the victim's death position only + // 1. if it is not a free for all mode + // 2. if the attacker is a player and they are not teammates + if (IsFreeForAll() || !pKiller || PlayerRelationship(pKiller, pVictim) == GR_TEAMMATE) + iDeathMessageFlags &= ~PLAYERDEATH_POSITION; // do not send a position +#endif + SendDeathMessage(pKiller, pVictim, pAssister, pevInflictor, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); // Updates the stats of who has killed whom @@ -4622,10 +4652,11 @@ int CountPlayers() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - { - nCount++; - } + + if (!UTIL_IsValidPlayer(pPlayer) || pPlayer->IsBot()) + continue; + + nCount++; } return nCount; @@ -4727,6 +4758,9 @@ void CHalfLifeMultiplay::ResetAllMapVotes() if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->m_iTeam != UNASSIGNED) { @@ -4830,6 +4864,9 @@ void CHalfLifeMultiplay::ProcessMapVote(CBasePlayer *pPlayer, int iVote) if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->m_iTeam != UNASSIGNED) @@ -5209,7 +5246,7 @@ CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBaseEntity *pKiller, CBaseP continue; // dealt no damage CBasePlayer *pAttackerPlayer = UTIL_PlayerByIndex(i); - if (!pAttackerPlayer || pAttackerPlayer->IsDormant()) + if (!UTIL_IsValidPlayer(pAttackerPlayer)) continue; // ignore idle clients CCSPlayer *pCSAttackerPlayer = pAttackerPlayer->CSPlayer(); @@ -5271,20 +5308,19 @@ int CHalfLifeMultiplay::GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVict if (pVictim->m_bHeadshotKilled) iRarity |= KILLRARITY_HEADSHOT; - // The killer player was blind CBasePlayer *pKillerPlayer = static_cast(pKiller); - if (pKillerPlayer && pKillerPlayer->IsPlayer()) + if (pKillerPlayer && pKillerPlayer->IsPlayer() && pKillerPlayer != pVictim) { WeaponClassType weaponClass = AliasToWeaponClass(killerWeaponName); - if (pKillerPlayer != pVictim - && weaponClass != WEAPONCLASS_NONE - && weaponClass != WEAPONCLASS_KNIFE - && weaponClass != WEAPONCLASS_GRENADE) + if (weaponClass != WEAPONCLASS_NONE && + weaponClass != WEAPONCLASS_KNIFE && + weaponClass != WEAPONCLASS_GRENADE) { // The killer player kills the victim through the walls if (pVictim->GetDmgPenetrationLevel() > 0) iRarity |= KILLRARITY_PENETRATED; + // The killer player was blind if (pKillerPlayer->IsFullyBlind()) iRarity |= KILLRARITY_KILLER_BLIND; @@ -5296,6 +5332,10 @@ int CHalfLifeMultiplay::GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVict const Vector inEyePos = pKillerPlayer->EyePosition(); if (TheCSBots()->IsLineBlockedBySmoke(&inEyePos, &pVictim->pev->origin)) iRarity |= KILLRARITY_THRUSMOKE; + + // The killer player kills the victim while in air + if (!(pKillerPlayer->pev->flags & FL_ONGROUND)) + iRarity |= KILLRARITY_INAIR; } // Calculate # of unanswered kills between killer & victim @@ -5308,6 +5348,10 @@ int CHalfLifeMultiplay::GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVict int iKillsUnanswered = pVictim->CSPlayer()->m_iNumKilledByUnanswered[iAttackerEntityIndex - 1] + 1; if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION || pKillerPlayer->CSPlayer()->IsPlayerDominated(pVictim->entindex() - 1)) { + // Sets the beginning of domination over the victim until he takes revenge + if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION) + iRarity |= KILLRARITY_DOMINATION_BEGAN; + // this is the Nth unanswered kill between killer and victim, killer is now dominating victim iRarity |= KILLRARITY_DOMINATION; @@ -5343,32 +5387,13 @@ LINK_HOOK_CLASS_VOID_CUSTOM_CHAIN(CHalfLifeMultiplay, CSGameRules, SendDeathMess // void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(SendDeathMessage)(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill) { - CBasePlayer *pKillerPlayer = (pKiller && pKiller->IsPlayer()) ? static_cast(pKiller) : nullptr; - - // Only the player can dominate the victim - if ((iRarityOfKill & KILLRARITY_DOMINATION) && pKillerPlayer && pVictim != pKillerPlayer) - { - // Sets the beginning of domination over the victim until he takes revenge - int iKillsUnanswered = pVictim->CSPlayer()->m_iNumKilledByUnanswered[pKillerPlayer->entindex() - 1] + 1; - if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION) - iRarityOfKill |= KILLRARITY_DOMINATION_BEGAN; - } - MESSAGE_BEGIN(MSG_ALL, gmsgDeathMsg); WRITE_BYTE((pKiller && pKiller->IsPlayer()) ? pKiller->entindex() : 0); // the killer WRITE_BYTE(pVictim->entindex()); // the victim - WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot + WRITE_BYTE((iRarityOfKill & KILLRARITY_HEADSHOT)); // is killed headshot WRITE_STRING(killerWeaponName); // what they were killed by (should this be a string?) #ifdef REGAMEDLL_ADD - iDeathMessageFlags &= UTIL_ReadFlags(deathmsg_flags.string); // leave only allowed bitsums for extra info - - // Send the victim's death position only - // 1. if it is not a free for all mode - // 2. if the attacker is a player and they are not teammates - if (IsFreeForAll() || !pKillerPlayer || PlayerRelationship(pKillerPlayer, pVictim) == GR_TEAMMATE) - iDeathMessageFlags &= ~PLAYERDEATH_POSITION; // do not send a position - if (iDeathMessageFlags > 0) { WRITE_LONG(iDeathMessageFlags); @@ -5384,7 +5409,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(SendDeathMessage)(CBaseEntity *pKil // Writes the index of the teammate who assisted in the kill if (iDeathMessageFlags & PLAYERDEATH_ASSISTANT) - WRITE_BYTE(pAssister->entindex()); + WRITE_BYTE((pAssister && pAssister->IsPlayer()) ? pAssister->entindex() : 0); // Writes the rarity classification of the kill if (iDeathMessageFlags & PLAYERDEATH_KILLRARITY) @@ -5405,7 +5430,7 @@ void CHalfLifeMultiplay::GiveDefuserToRandomPlayer() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->edict())) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (!pPlayer->IsAlive() || pPlayer->m_iTeam != CT) @@ -5439,9 +5464,9 @@ void CHalfLifeMultiplay::GiveDefuserToRandomPlayer() for (int i = 0; i < iDefusersToGive && i < candidates.Count(); ++i) { CBasePlayer *pPlayer = candidates[i]; - assert(pPlayer && pPlayer->m_iTeam == CT && pPlayer->IsAlive()); + DbgAssert(pPlayer && pPlayer->m_iTeam == CT && pPlayer->IsAlive()); pPlayer->GiveDefuser(); - ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "#Got_defuser"); + pPlayer->HintMessage("#Got_defuser", FALSE, TRUE); } } diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index 06c550890..38605ebaa 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -332,7 +332,10 @@ CBasePlayer *CBasePlayer::GetNextRadioRecipient(CBasePlayer *pStartPlayer) continue; CBasePlayer *pTarget = CBasePlayer::Instance(pPlayer->m_hObserverTarget->pev); - if (pTarget && pTarget->m_iTeam == m_iTeam) + if (!pTarget || pTarget->IsDormant()) + continue; + + if (pTarget->m_iTeam == m_iTeam) { bSend = true; } @@ -365,6 +368,9 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Radio)(const char *msg_id, const char *msg if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + bool bSend = false; CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); @@ -382,7 +388,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Radio)(const char *msg_id, const char *msg continue; // is this player on our team? (even dead players hear our radio calls) - if (pPlayer->m_iTeam == m_iTeam) + if (g_pGameRules->PlayerRelationship(this, pPlayer) == GR_TEAMMATE) bSend = true; } // this means we're a spectator @@ -396,7 +402,11 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Radio)(const char *msg_id, const char *msg if (FNullEnt(pPlayer->m_hObserverTarget)) continue; - if (pPlayer->m_hObserverTarget && pPlayer->m_hObserverTarget->m_iTeam == m_iTeam) + CBasePlayer *pTarget = CBasePlayer::Instance(pPlayer->m_hObserverTarget->pev); + if (!pTarget || pTarget->IsDormant()) + continue; + + if (g_pGameRules->PlayerRelationship(this, pTarget) == GR_TEAMMATE) { bSend = true; } @@ -411,11 +421,15 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Radio)(const char *msg_id, const char *msg MESSAGE_END(); // radio message icon - if (msg_verbose) + if (msg_verbose && msg_verbose[0] != 0) { // search the place name where is located the player const char *placeName = nullptr; - if (AreRunningCZero() && TheBotPhrases) + if (( +#ifdef REGAMEDLL_ADD + location_area_info.value >= 2 || +#endif + AreRunningCZero()) && TheBotPhrases) { Place playerPlace = TheNavAreaGrid.GetPlace(&pev->origin); const BotPhraseList *placeList = TheBotPhrases->GetPlaceList(); @@ -427,11 +441,25 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Radio)(const char *msg_id, const char *msg break; } } + + if (!placeName) + placeName = TheNavAreaGrid.IDToName(playerPlace); + } + + if (placeName && placeName[0]) + { + bool bUseLocFallback = false; +#ifdef REGAMEDLL_ADD + if (chat_loc_fallback.value) + bUseLocFallback = true; +#endif + + ClientPrint(pEntity->pev, HUD_PRINTRADIO, NumAsString(entindex()), bUseLocFallback ? "\x3%s1\x1 @ \x4%s2\x1 (RADIO): %s3" : "#Game_radio_location", STRING(pev->netname), placeName, msg_verbose); } - if (placeName) - ClientPrint(pEntity->pev, HUD_PRINTRADIO, NumAsString(entindex()), "#Game_radio_location", STRING(pev->netname), placeName, msg_verbose); else + { ClientPrint(pEntity->pev, HUD_PRINTRADIO, NumAsString(entindex()), "#Game_radio", STRING(pev->netname), msg_verbose); + } } // icon over the head for teammates @@ -1011,7 +1039,10 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || pPlayer->m_hObserverTarget != this) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (pPlayer->m_hObserverTarget != this) continue; MESSAGE_BEGIN(MSG_ONE, gmsgSpecHealth, nullptr, pPlayer->edict()); @@ -1074,6 +1105,9 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->m_iTeam == m_iTeam) @@ -1260,7 +1294,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->m_hObserverTarget == this) @@ -1715,7 +1749,9 @@ void EXT_FUNC CBasePlayer::__API_HOOK(GiveDefaultItems)() #endif } -void CBasePlayer::RemoveAllItems(BOOL removeSuit) +LINK_HOOK_CLASS_VOID_CHAIN(CBasePlayer, RemoveAllItems, (BOOL removeSuit), removeSuit) + +void EXT_FUNC CBasePlayer::__API_HOOK(RemoveAllItems)(BOOL removeSuit) { int i; @@ -1856,6 +1892,9 @@ void CBasePlayer::SetProgressBarTime(int time) if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->GetObserverMode() == OBS_IN_EYE && pPlayer->pev->iuser2 == playerIndex) @@ -1896,6 +1935,9 @@ void CBasePlayer::SetProgressBarTime2(int time, float timeElapsed) if (FNullEnt(pEntity->edict())) break; + if (pEntity->IsDormant()) + continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); if (pPlayer->GetObserverMode() == OBS_IN_EYE && pPlayer->pev->iuser2 == playerIndex) @@ -2168,7 +2210,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; bool killedByHumanPlayer = (!pPlayer->IsBot() && pPlayer->pev == pevAttacker && pPlayer->m_iTeam != m_iTeam); @@ -2199,7 +2241,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) { CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (!pObserver) + if (!UTIL_IsValidPlayer(pObserver)) continue; if (pObserver->IsObservingPlayer(this)) @@ -4423,7 +4465,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(AddPointsToTeam)(int score, BOOL bAllowNeg { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && i != index) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (i != index) { if (g_pGameRules->PlayerRelationship(this, pPlayer) == GR_TEAMMATE) { @@ -5810,7 +5855,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Spawn)() { CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (pObserver && pObserver->IsObservingPlayer(this)) + if (!UTIL_IsValidPlayer(pObserver)) + continue; + + if (pObserver->IsObservingPlayer(this)) { MESSAGE_BEGIN(MSG_ONE, gmsgNVGToggle, nullptr, pObserver->pev); WRITE_BYTE(0); @@ -5939,8 +5987,10 @@ void CBasePlayer::SetScoreboardAttributes(CBasePlayer *destination) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && !FNullEnt(pPlayer->edict())) - SetScoreboardAttributes(pPlayer); + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + SetScoreboardAttributes(pPlayer); } } @@ -6489,10 +6539,8 @@ void CBasePlayer::ForceClientDllUpdate() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->edict())) - continue; - if (pPlayer->IsDormant()) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pev->deadflag == DEAD_NO) @@ -7643,14 +7691,14 @@ void EXT_FUNC CBasePlayer::__API_HOOK(UpdateClientData)() { CBaseEntity *pEntity = UTIL_PlayerByIndex(i); - if (!pEntity || i == entindex()) + if (!UTIL_IsValidPlayer(pEntity)) continue; - CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); - - if (pPlayer->pev->flags == FL_DORMANT) + if (i == entindex()) continue; + CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); + if (pPlayer->pev->deadflag != DEAD_NO) continue; @@ -7684,16 +7732,11 @@ void EXT_FUNC CBasePlayer::__API_HOOK(UpdateClientData)() { CBaseEntity *pEntity = UTIL_PlayerByIndex(playerIndex); - if (!pEntity) + if (!UTIL_IsValidPlayer(pEntity)) continue; CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); -#ifdef REGAMEDLL_FIXES - if (pPlayer->IsDormant()) - continue; -#endif // REGAMEDLL_FIXES - #ifdef REGAMEDLL_FIXES if (scoreboard_showhealth.value != -1.0f) #endif @@ -8217,24 +8260,21 @@ CBaseEntity *EXT_FUNC CBasePlayer::__API_HOOK(DropPlayerItem)(const char *pszIte if (FNullEnt(pEntity->edict())) break; - if (!pEntity->IsPlayer()) + if (!pEntity->IsPlayer() || pEntity->IsDormant()) continue; - if (pEntity->pev->flags != FL_DORMANT) - { - CBasePlayer *pOther = GetClassPtr((CBasePlayer *)pEntity->pev); + CBasePlayer *pOther = GetClassPtr((CBasePlayer *)pEntity->pev); - if (pOther->pev->deadflag == DEAD_NO && pOther->m_iTeam == TERRORIST) - { - ClientPrint(pOther->pev, HUD_PRINTCENTER, "#Game_bomb_drop", STRING(pev->netname)); - - MESSAGE_BEGIN(MSG_ONE, gmsgBombDrop, nullptr, pOther->pev); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z); - WRITE_BYTE(BOMB_FLAG_DROPPED); - MESSAGE_END(); - } + if (pOther->pev->deadflag == DEAD_NO && pOther->m_iTeam == TERRORIST) + { + ClientPrint(pOther->pev, HUD_PRINTCENTER, "#Game_bomb_drop", STRING(pev->netname)); + + MESSAGE_BEGIN(MSG_ONE, gmsgBombDrop, nullptr, pOther->pev); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + WRITE_BYTE(BOMB_FLAG_DROPPED); + MESSAGE_END(); } } } @@ -10081,9 +10121,13 @@ void CBasePlayer::UpdateLocation(bool forceUpdate) if (!forceUpdate && m_flLastUpdateTime >= gpGlobals->time + 2.0f) return; - const char *placeName = ""; + const char *placeName = nullptr; - if (pev->deadflag == DEAD_NO && AreBotsAllowed()) + if (pev->deadflag == DEAD_NO && ( +#ifdef REGAMEDLL_ADD + (location_area_info.value == 1 || location_area_info.value == 3) || +#endif + AreBotsAllowed())) { // search the place name where is located the player Place playerPlace = TheNavAreaGrid.GetPlace(&pev->origin); @@ -10096,9 +10140,12 @@ void CBasePlayer::UpdateLocation(bool forceUpdate) break; } } + + if (!placeName) + placeName = TheNavAreaGrid.IDToName(playerPlace); } - if (!placeName[0] || (m_lastLocation[0] && !Q_strcmp(placeName, &m_lastLocation[1]))) + if (!placeName || !placeName[0] || (m_lastLocation[0] && !Q_strcmp(placeName, &m_lastLocation[1]))) { return; } @@ -10110,7 +10157,7 @@ void CBasePlayer::UpdateLocation(bool forceUpdate) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->m_iTeam == m_iTeam || pPlayer->m_iTeam == SPECTATOR) @@ -10658,6 +10705,11 @@ bool CBasePlayer::Kill() // have the player kill himself pev->health = 0.0f; + +#ifdef REGAMEDLL_API + CSPlayer()->ResetAllStats(); // reset damage stats on killed himself or team change +#endif + Killed(pev, GIB_NEVER); if (CSGameRules()->m_pVIP == this) @@ -10665,3 +10717,12 @@ bool CBasePlayer::Kill() return true; } + +const usercmd_t *CBasePlayer::GetLastUserCommand() const +{ +#ifdef REGAMEDLL_API + return CSPlayer()->GetLastUserCommand(); +#else + return nullptr; +#endif +} diff --git a/regamedll/dlls/player.h b/regamedll/dlls/player.h index 8908e44c9..d3d37d3ea 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -448,6 +448,7 @@ class CBasePlayer: public CBaseMonster { edict_t *EntSelectSpawnPoint_OrigFunc(); void PlayerDeathThink_OrigFunc(); void Observer_Think_OrigFunc(); + void RemoveAllItems_OrigFunc(BOOL removeSuit); CCSPlayer *CSPlayer() const; #endif // REGAMEDLL_API @@ -499,6 +500,7 @@ class CBasePlayer: public CBaseMonster { void SetClientUserInfoModel(char *infobuffer, char *szNewModel); void SetClientUserInfoModel_api(char *infobuffer, char *szNewModel); void SetNewPlayerModel(const char *modelName); + const usercmd_t *GetLastUserCommand() const; BOOL SwitchWeapon(CBasePlayerItem *pWeapon); void CheckPowerups(); bool CanAffordPrimary(); @@ -985,6 +987,19 @@ inline CBasePlayer *UTIL_PlayerByIndex(int playerIndex) return GET_PRIVATE(INDEXENT(playerIndex)); } +// return true if the given player is valid +inline bool UTIL_IsValidPlayer(CBaseEntity *pPlayer) +{ + return pPlayer && !FNullEnt(pPlayer->pev) && !pPlayer->IsDormant(); +} + +#else + +inline bool UTIL_IsValidPlayer(CBaseEntity *pPlayer) +{ + return pPlayer && !FNullEnt(pPlayer->pev); +} + #endif inline CBasePlayer *UTIL_PlayerByIndexSafe(int playerIndex) diff --git a/regamedll/dlls/triggers.cpp b/regamedll/dlls/triggers.cpp index c4af258c4..82bf6907a 100644 --- a/regamedll/dlls/triggers.cpp +++ b/regamedll/dlls/triggers.cpp @@ -996,6 +996,14 @@ void CTriggerMultiple::Spawn() } } +#ifdef REGAMEDLL_FIXES +void CTriggerMultiple::Restart() +{ + pev->nextthink = -1; + Spawn(); +} +#endif + LINK_ENTITY_TO_CLASS(trigger_once, CTriggerOnce, CCSTriggerOnce) void CTriggerOnce::Spawn() @@ -1763,8 +1771,30 @@ void CBaseTrigger::TeleportTouch(CBaseEntity *pOther) if (pOther->IsPlayer()) { +#ifdef REGAMEDLL_ADD + // If a landmark was specified, offset the player relative to the landmark + if (m_iszLandmarkName) + { + edict_t *pentLandmark = FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(m_iszLandmarkName)); + + if (!FNullEnt(pentLandmark)) + { + Vector diff = pevToucher->origin - VARS(pentLandmark)->origin; + tmp += diff; + tmp.z--; // offset by +1 because -1 will run out of this scope. + } + else + { + // fallback, shouldn't happen but anyway. + tmp.z -= pOther->pev->mins.z; + } + } + else +#endif // make origin adjustments in case the teleportee is a player. (origin in center, not at feet) - tmp.z -= pOther->pev->mins.z; + { + tmp.z -= pOther->pev->mins.z; + } } tmp.z++; @@ -1817,6 +1847,26 @@ void CTriggerTeleport::Spawn() SetTouch(&CTriggerTeleport::TeleportTouch); } +void CTriggerTeleport::KeyValue(KeyValueData *pkvd) +{ +#ifdef REGAMEDLL_ADD + if (FStrEq(pkvd->szKeyName, "landmark")) + { + if (Q_strlen(pkvd->szValue) > 0) + { + m_iszLandmarkName = ALLOC_STRING(pkvd->szValue); + } + + // If empty, handle it in the teleport touch instead + pkvd->fHandled = TRUE; + } + else +#endif + { + CBaseTrigger::KeyValue(pkvd); + } +} + LINK_ENTITY_TO_CLASS(info_teleport_destination, CPointEntity, CCSPointEntity) LINK_ENTITY_TO_CLASS(func_buyzone, CBuyZone, CCSBuyZone) @@ -1979,7 +2029,7 @@ void CEscapeZone::EscapeTouch(CBaseEntity *pOther) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->m_iTeam == pEscapee->m_iTeam) diff --git a/regamedll/dlls/triggers.h b/regamedll/dlls/triggers.h index c16f969c8..90a7daef3 100644 --- a/regamedll/dlls/triggers.h +++ b/regamedll/dlls/triggers.h @@ -204,6 +204,11 @@ class CBaseTrigger: public CBaseToggle void EXPORT CounterUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); void EXPORT ToggleUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); void InitTrigger(); + +#ifdef REGAMEDLL_ADD + // For trigger_teleport TriggerTouch + int m_iszLandmarkName = 0; +#endif }; #define SF_TRIGGER_HURT_TARGETONCE BIT(0) // Only fire hurt target once @@ -278,6 +283,10 @@ class CTriggerMultiple: public CBaseTrigger { public: virtual void Spawn(); + +#ifdef REGAMEDLL_FIXES + virtual void Restart(); +#endif }; // Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching @@ -393,6 +402,7 @@ class CTriggerTeleport: public CBaseTrigger { public: virtual void Spawn(); + virtual void KeyValue(KeyValueData *pkvd); }; class CBuyZone: public CBaseTrigger diff --git a/regamedll/dlls/tutor.cpp b/regamedll/dlls/tutor.cpp index 5f2dbeca0..0f83d4812 100644 --- a/regamedll/dlls/tutor.cpp +++ b/regamedll/dlls/tutor.cpp @@ -75,7 +75,10 @@ void MonitorTutorStatus() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && !pPlayer->IsBot()) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) numHumans++; } diff --git a/regamedll/dlls/tutor_cs_tutor.cpp b/regamedll/dlls/tutor_cs_tutor.cpp index f8ca0aef7..18e5d2b67 100644 --- a/regamedll/dlls/tutor_cs_tutor.cpp +++ b/regamedll/dlls/tutor_cs_tutor.cpp @@ -2040,7 +2040,11 @@ void CCSTutor::GetNumPlayersAliveOnTeams(int &numT, int &numCT) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || !pPlayer->IsAlive()) + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsAlive()) continue; switch (pPlayer->m_iTeam) @@ -2132,7 +2136,11 @@ void CCSTutor::CheckForBombViewable() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer && pPlayer->m_bHasC4) + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (pPlayer->m_bHasC4) { pBombCarrier = pPlayer; break; @@ -2819,7 +2827,7 @@ void CCSTutor::ConstructRecentDeathsList(TeamName team, char *buf, int buflen, T { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; // ignore alive players diff --git a/regamedll/dlls/util.cpp b/regamedll/dlls/util.cpp index 7c3c3db6c..7841b006b 100644 --- a/regamedll/dlls/util.cpp +++ b/regamedll/dlls/util.cpp @@ -506,7 +506,11 @@ void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, fl for (i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || !(pPlayer->pev->flags & FL_ONGROUND)) + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!(pPlayer->pev->flags & FL_ONGROUND)) continue; localAmplitude = 0; @@ -552,7 +556,10 @@ void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity) { - if (!pEntity || !pEntity->IsNetClient()) + if (!UTIL_IsValidPlayer(pEntity)) + return; + + if (!pEntity->IsNetClient()) return; MESSAGE_BEGIN(MSG_ONE, gmsgFade, nullptr, pEntity->edict()); @@ -634,10 +641,11 @@ void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - { - UTIL_HudMessage(pPlayer, textparms, pMessage); - } + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + UTIL_HudMessage(pPlayer, textparms, pMessage); } } @@ -843,8 +851,11 @@ void UTIL_ShowMessageAll(const char *pString, bool isHint) for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - UTIL_ShowMessage(pString, pPlayer, isHint); + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + UTIL_ShowMessage(pString, pPlayer, isHint); } } @@ -1749,10 +1760,11 @@ int UTIL_GetNumPlayers() for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - { - nNumPlayers++; - } + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + nNumPlayers++; } return nNumPlayers; @@ -1837,7 +1849,10 @@ int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, i { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || !pPlayer->IsInWorld()) + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsInWorld()) continue; if (bOnlyAlive && !pPlayer->IsAlive()) diff --git a/regamedll/dlls/weapons.cpp b/regamedll/dlls/weapons.cpp index 2c035c746..454b96f04 100644 --- a/regamedll/dlls/weapons.cpp +++ b/regamedll/dlls/weapons.cpp @@ -79,7 +79,7 @@ LINK_HOOK_VOID_CHAIN2(ClearMultiDamage) // Resets the global multi damage accumulator void EXT_FUNC __API_HOOK(ClearMultiDamage)() { - gMultiDamage.pEntity = nullptr; + gMultiDamage.hEntity = nullptr; gMultiDamage.amount = 0; gMultiDamage.type = 0; } @@ -89,11 +89,15 @@ LINK_HOOK_VOID_CHAIN(ApplyMultiDamage, (entvars_t *pevInflictor, entvars_t *pevA // Inflicts contents of global multi damage register on gMultiDamage.pEntity void EXT_FUNC __API_HOOK(ApplyMultiDamage)(entvars_t *pevInflictor, entvars_t *pevAttacker) { - if (!gMultiDamage.pEntity) + EntityHandle hEnt = gMultiDamage.hEntity; + if (!hEnt) return; - gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type); - gMultiDamage.pEntity->ResetDmgPenetrationLevel(); + hEnt->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type); + + // check again, the entity may be removed after taking damage + if (hEnt) + hEnt->ResetDmgPenetrationLevel(); } LINK_HOOK_VOID_CHAIN(AddMultiDamage, (entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType), pevInflictor, pEntity, flDamage, bitsDamageType) @@ -105,11 +109,17 @@ void EXT_FUNC __API_HOOK(AddMultiDamage)(entvars_t *pevInflictor, CBaseEntity *p gMultiDamage.type |= bitsDamageType; - if (pEntity != gMultiDamage.pEntity) + if (pEntity != gMultiDamage.hEntity) { - // UNDONE: wrong attacker! - ApplyMultiDamage(pevInflictor, pevInflictor); - gMultiDamage.pEntity = pEntity; +#ifdef REGAMEDLL_FIXES + if (gMultiDamage.hEntity) // avoid api calls with null default pEntity +#endif + { + // UNDONE: wrong attacker! + ApplyMultiDamage(pevInflictor, pevInflictor); + } + + gMultiDamage.hEntity = pEntity; gMultiDamage.amount = 0; } @@ -1454,9 +1464,12 @@ void CBasePlayerWeapon::ReloadSound() CBasePlayer *pPlayer = nullptr; while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) { - if (pPlayer->IsDormant()) + if (FNullEnt(pPlayer->edict())) break; + if (pPlayer->IsDormant()) + continue; + if (pPlayer == m_pPlayer) continue; @@ -1974,6 +1987,7 @@ void CWeaponBox::Touch(CBaseEntity *pOther) if (!m_rgpPlayerItems[i]) continue; + CBasePlayerItem *pPrev = NULL; CBasePlayerItem *pItem = m_rgpPlayerItems[i]; // have at least one weapon in this slot @@ -2036,7 +2050,7 @@ void CWeaponBox::Touch(CBaseEntity *pOther) if (!pEntity->IsPlayer()) continue; - if (pEntity->pev->flags == FL_DORMANT) + if (pEntity->IsDormant()) continue; CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); @@ -2070,13 +2084,13 @@ void CWeaponBox::Touch(CBaseEntity *pOther) } else if (i == GRENADE_SLOT) { - CBasePlayerWeapon *pGrenade = static_cast(m_rgpPlayerItems[i]); + CBasePlayerWeapon *pGrenade = static_cast(pItem); if (pGrenade && pGrenade->IsWeapon()) { int playerGrenades = pPlayer->m_rgAmmo[pGrenade->m_iPrimaryAmmoType]; #ifdef REGAMEDLL_FIXES - CBasePlayerItem *pNext = m_rgpPlayerItems[i]->m_pNext; + CBasePlayerItem *pNext = pItem->m_pNext; // Determine the max ammo capacity for the picked-up grenade int iMaxPickupAmmo = pGrenade->iMaxAmmo1(); @@ -2094,7 +2108,11 @@ void CWeaponBox::Touch(CBaseEntity *pOther) playerGrenades, pGrenade->pszAmmo1(), iMaxPickupAmmo, &givenItem)) { // unlink this weapon from the box - m_rgpPlayerItems[i] = pItem = pNext; + if (pPrev) + pPrev->m_pNext = pItem = pNext; + else + m_rgpPlayerItems[i] = pItem = pNext; + continue; } #else @@ -2143,7 +2161,8 @@ void CWeaponBox::Touch(CBaseEntity *pOther) } else { - auto pNext = m_rgpPlayerItems[i]->m_pNext; + CBasePlayerItem *pNext = pItem->m_pNext; + if (pPlayer->AddPlayerItem(pItem)) { pItem->AttachToPlayer(pPlayer); @@ -2155,12 +2174,17 @@ void CWeaponBox::Touch(CBaseEntity *pOther) } // unlink this weapon from the box - m_rgpPlayerItems[i] = pItem = pNext; + if (pPrev) + pPrev->m_pNext = pNext; + else + m_rgpPlayerItems[i] = pItem = pNext; + continue; } bRemove = false; - pItem = m_rgpPlayerItems[i]->m_pNext; + pPrev = pItem; + pItem = pItem->m_pNext; } } diff --git a/regamedll/dlls/weapons.h b/regamedll/dlls/weapons.h index c9cd58c36..9266d8434 100644 --- a/regamedll/dlls/weapons.h +++ b/regamedll/dlls/weapons.h @@ -133,7 +133,7 @@ struct AmmoInfo struct MULTIDAMAGE { - CBaseEntity *pEntity; + EntityHandle hEntity; float amount; int type; }; diff --git a/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd b/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd index 0e6838732..79fe8333a 100644 --- a/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd +++ b/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd @@ -1934,6 +1934,7 @@ bombradius(integer) : "Bomb Radius" : 500 ] +@PointClass base(Targetname) iconsprite("sprites/CS/info_target.spr") = info_landmark : "Transition/Relative teleport Landmark" [] @PointClass base(Targetname) iconsprite("sprites/CS/info_target.spr") = info_null : "info_null (spotlight target)" [] @PointClass iconsprite("sprites/CS/info_player_deathmatch.spr") base(PlayerClass) = info_player_deathmatch : "Terrorist start" [] @PointClass iconsprite("sprites/CS/info_player_start.spr") base(PlayerClass) = info_player_start : "Counter-terrorist start" [] @@ -2264,6 +2265,7 @@ @SolidClass base(Trigger) = trigger_teleport : "Trigger teleport" [ + landmark(string) : "Landmark name" spawnflags(flags) = [ 256: "Keep angles" : 0 diff --git a/regamedll/game_shared/bot/bot.cpp b/regamedll/game_shared/bot/bot.cpp index e5849b9b4..0f19e81f4 100644 --- a/regamedll/game_shared/bot/bot.cpp +++ b/regamedll/game_shared/bot/bot.cpp @@ -261,13 +261,6 @@ void CBot::ExecuteCommand() // Adjust msec to command time interval adjustedMSec = ThrottledMsec(); - // player model is "munged" - pev->angles = pev->v_angle; - pev->angles.x /= -3.0f; - - // save the command time - m_flPreviousCommandTime = gpGlobals->time; - if (IsCrouching()) { m_buttonFlags |= IN_DUCK; @@ -282,8 +275,69 @@ void CBot::ExecuteCommand() } #endif + // Run mimic command + usercmd_t botCmd; + if (!RunMimicCommand(botCmd)) + { + botCmd.forwardmove = m_forwardSpeed; + botCmd.sidemove = m_strafeSpeed; + botCmd.upmove = m_verticalSpeed; + botCmd.buttons = m_buttonFlags; + botCmd.impulse = 0; + botCmd.viewangles = pev->v_angle; + } + + // player model is "munged" + pev->angles = pev->v_angle; + pev->angles.x /= -3.0f; + + // save the command time + m_flPreviousCommandTime = gpGlobals->time; + // Run the command - PLAYER_RUN_MOVE(edict(), pev->v_angle, m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0, adjustedMSec); + PLAYER_RUN_MOVE(edict(), botCmd.viewangles, botCmd.forwardmove, botCmd.sidemove, botCmd.upmove, botCmd.buttons, 0, adjustedMSec); +} + +bool CBot::RunMimicCommand(usercmd_t &botCmd) +{ +#ifdef REGAMEDLL_ADD + if (cv_bot_mimic.value <= 0) + return false; + + if (cv_bot_mimic.value > gpGlobals->maxClients) + return false; + + CBasePlayer *pPlayer = UTIL_PlayerByIndex(cv_bot_mimic.value); + if (!pPlayer) + return false; + + if (!UTIL_IsValidPlayer(pPlayer)) + return false; + + if (!pPlayer->IsAlive()) + return false; + + if (pPlayer->IsBot()) + return false; + + const usercmd_t *ucmd = pPlayer->GetLastUserCommand(); + if (!ucmd) + return false; + + botCmd = *ucmd; + botCmd.viewangles[YAW] += cv_bot_mimic_yaw_offset.value; + + float mult = 8.0f; + botCmd.forwardmove *= mult; + botCmd.sidemove *= mult; + botCmd.upmove *= mult; + + pev->fixangle = 0; + + return true; +#else + return false; +#endif } void CBot::ResetCommand() @@ -352,10 +406,8 @@ int CBot::GetEnemiesRemaining() const for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -380,10 +432,8 @@ int CBot::GetFriendsRemaining() const for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -491,9 +541,15 @@ void ActiveGrenade::OnEntityGone() m_entity = nullptr; } +void ActiveGrenade::CheckOnEntityGone() +{ + if (m_dieTimestamp == 0 && !m_entity.IsValid()) + OnEntityGone(); +} + bool ActiveGrenade::IsValid() const { - if (!m_entity) + if (!m_entity.IsValid()) { if (gpGlobals->time > m_dieTimestamp) return false; @@ -502,7 +558,7 @@ bool ActiveGrenade::IsValid() const return true; } -const Vector *ActiveGrenade::GetPosition() const +const Vector *ActiveGrenade::GetPosition() { return &m_entity->pev->origin; } diff --git a/regamedll/game_shared/bot/bot.h b/regamedll/game_shared/bot/bot.h index 6ad3cda11..17cec5a98 100644 --- a/regamedll/game_shared/bot/bot.h +++ b/regamedll/game_shared/bot/bot.h @@ -259,6 +259,8 @@ class CBot: public CBasePlayer void ResetCommand(); byte ThrottledMsec() const; + bool RunMimicCommand(usercmd_t &botCmd); + // returns current movement speed (for walk/run) float GetMoveSpeed(); diff --git a/regamedll/game_shared/bot/bot_manager.cpp b/regamedll/game_shared/bot/bot_manager.cpp index 285a26a36..7ccd32814 100644 --- a/regamedll/game_shared/bot/bot_manager.cpp +++ b/regamedll/game_shared/bot/bot_manager.cpp @@ -147,6 +147,8 @@ void CBotManager::StartFrame() ActiveGrenade *ag = (*iter); // lazy validation + ag->CheckOnEntityGone(); + if (!ag->IsValid()) { delete ag; @@ -194,7 +196,7 @@ void CBotManager::StartFrame() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->IsBot() && IsEntityValid(pPlayer)) @@ -203,6 +205,8 @@ void CBotManager::StartFrame() pBot->BotThink(); } } + + ValidateActiveGrenades(); } // Return the filename for this map's "nav map" file @@ -225,10 +229,7 @@ void CBotManager::__API_HOOK(OnEvent)(GameEventType event, CBaseEntity* pEntity, { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -278,13 +279,16 @@ void CBotManager::RemoveGrenade(CGrenade *grenade) } // Destroy any invalid active grenades -NOXREF void CBotManager::ValidateActiveGrenades() +void CBotManager::ValidateActiveGrenades() { auto iter = m_activeGrenadeList.begin(); while (iter != m_activeGrenadeList.end()) { ActiveGrenade *ag = (*iter); + // lazy validation + ag->CheckOnEntityGone(); + if (!ag->IsValid()) { delete ag; @@ -314,6 +318,8 @@ bool CBotManager::IsInsideSmokeCloud(const Vector *pos) ActiveGrenade *ag = (*iter); // lazy validation + ag->CheckOnEntityGone(); + if (!ag->IsValid()) { delete ag; @@ -358,6 +364,8 @@ bool CBotManager::IsLineBlockedBySmoke(const Vector *from, const Vector *to) ActiveGrenade *ag = (*iter); // lazy validation + ag->CheckOnEntityGone(); + if (!ag->IsValid()) { delete ag; diff --git a/regamedll/game_shared/bot/bot_manager.h b/regamedll/game_shared/bot/bot_manager.h index dfcfd8edc..a985fc1d7 100644 --- a/regamedll/game_shared/bot/bot_manager.h +++ b/regamedll/game_shared/bot/bot_manager.h @@ -42,16 +42,17 @@ class ActiveGrenade ActiveGrenade(int weaponID, CGrenade *grenadeEntity); void OnEntityGone(); + void CheckOnEntityGone(); bool IsValid() const; - bool IsEntity(CGrenade *grenade) const { return (grenade == m_entity) ? true : false; } + bool IsEntity(CGrenade *grenade) const { return grenade == m_entity; } int GetID() const { return m_id; } const Vector *GetDetonationPosition() const { return &m_detonationPosition; } - const Vector *GetPosition() const; + const Vector *GetPosition(); private: int m_id; - CGrenade *m_entity; + EntityHandle m_entity; Vector m_detonationPosition; float m_dieTimestamp; }; diff --git a/regamedll/game_shared/bot/bot_util.cpp b/regamedll/game_shared/bot/bot_util.cpp index 9b5e2013e..449b7e373 100644 --- a/regamedll/game_shared/bot/bot_util.cpp +++ b/regamedll/game_shared/bot/bot_util.cpp @@ -11,10 +11,7 @@ bool UTIL_IsNameTaken(const char *name, bool ignoreHumans) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -46,14 +43,12 @@ bool UTIL_IsNameTaken(const char *name, bool ignoreHumans) int UTIL_ClientsInGame() { int iCount = 0; + for (int iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -68,14 +63,12 @@ int UTIL_ClientsInGame() int UTIL_ActivePlayersInGame() { int iCount = 0; + for (int iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -102,10 +95,7 @@ int UTIL_HumansInGame(bool ignoreSpectators) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -138,12 +128,9 @@ int UTIL_SpectatorsInGame() for (int iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { - CBasePlayer* pPlayer = UTIL_PlayerByIndex(iIndex); - - if (!pPlayer) - continue; + CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -167,14 +154,12 @@ int UTIL_SpectatorsInGame() int UTIL_HumansOnTeam(int teamID, bool isAlive) { int iCount = 0; + for (int iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -203,10 +188,7 @@ int UTIL_BotsInGame() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -230,10 +212,7 @@ bool UTIL_KickBotFromTeam(TeamName kickTeam) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; const char *name = STRING(pPlayer->pev->netname); @@ -256,10 +235,7 @@ bool UTIL_KickBotFromTeam(TeamName kickTeam) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; const char *name = STRING(pPlayer->pev->netname); @@ -283,19 +259,17 @@ bool UTIL_KickBotFromTeam(TeamName kickTeam) bool UTIL_IsTeamAllBots(int team) { int botCount = 0; + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (pPlayer->m_iTeam != team) continue; - if (FNullEnt(pPlayer->pev)) - continue; - if (FStrEq(STRING(pPlayer->pev->netname), "")) continue; @@ -403,10 +377,7 @@ bool UTIL_IsVisibleToTeam(const Vector &spot, int team, float maxRange) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) @@ -443,10 +414,7 @@ CBasePlayer *UTIL_GetLocalPlayer() { CBasePlayer *pPlayer = UTIL_PlayerByIndex(iIndex); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) + if (!UTIL_IsValidPlayer(pPlayer)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) diff --git a/regamedll/game_shared/bot/bot_util.h b/regamedll/game_shared/bot/bot_util.h index e7fbd71b0..8dfa352de 100644 --- a/regamedll/game_shared/bot/bot_util.h +++ b/regamedll/game_shared/bot/bot_util.h @@ -141,7 +141,7 @@ inline bool IsIntersecting2D(const Vector &startA, const Vector &endA, const Vec // Iterate over all active players in the game, invoking functor on each. // If functor returns false, stop iteration and return false. template -bool ForEachPlayer(Functor &func) +bool ForEachPlayer(Functor func) { for (int i = 1; i <= gpGlobals->maxClients; i++) { diff --git a/regamedll/game_shared/bot/nav_area.cpp b/regamedll/game_shared/bot/nav_area.cpp index 1ff3ef8c5..8e4baf0da 100644 --- a/regamedll/game_shared/bot/nav_area.cpp +++ b/regamedll/game_shared/bot/nav_area.cpp @@ -3815,6 +3815,9 @@ void EditNavAreas(NavEditCmdType cmd) if (area->GetPlace()) { const char *name = TheBotPhrases->IDToName(area->GetPlace()); + if (!TheBotPhrases->IsValid() && !name) + name = TheNavAreaGrid.IDToName(area->GetPlace()); + if (name) Q_strcpy(locName, name); else @@ -4428,6 +4431,7 @@ inline bool IsAreaVisible(const Vector *pos, const CNavArea *area) // Determine the set of "approach areas". // An approach area is an area representing a place where players // move into/out of our local neighborhood of areas. +// @todo Optimize by search from eye outward and modifying pathfinder to treat all links as bi-directional void CNavArea::ComputeApproachAreas() { m_approachCount = 0; @@ -4449,95 +4453,132 @@ void CNavArea::ComputeApproachAreas() enum { MAX_PATH_LENGTH = 256 }; CNavArea *path[MAX_PATH_LENGTH]; - // In order to enumerate all of the approach areas, we need to - // run the algorithm many times, once for each "far away" area - // and keep the union of the approach area sets - for (auto farArea : goodSizedAreaList) + enum SearchType { - BlockedIDCount = 0; + FROM_EYE, ///< start search from our eyepoint outward to farArea + TO_EYE, ///< start search from farArea beack towards our eye + SEARCH_FINISHED + }; - // if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak - if (IsAreaVisible(&eye, farArea)) - continue; + // In order to *completely* enumerate all of the approach areas, we + // need to search from our eyepoint outward, as well as from outwards + // towards our eyepoint + for (int searchType = FROM_EYE; searchType != SEARCH_FINISHED; searchType++) + { + // In order to enumerate all of the approach areas, we need to + // run the algorithm many times, once for each "far away" area + // and keep the union of the approach area sets + for (auto farArea : goodSizedAreaList) + { + BlockedIDCount = 0; - // make first path to far away area - ApproachAreaCost cost; - if (NavAreaBuildPath(this, farArea, nullptr, cost) == false) - continue; + // if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak + if (IsAreaVisible(&eye, farArea)) + continue; - // - // Keep building paths to farArea and blocking them off until we - // cant path there any more. - // As areas are blocked off, all exits will be enumerated. - // - while (m_approachCount < MAX_APPROACH_AREAS) - { - // find number of areas on path - int count = 0; - CNavArea *area; - for (area = farArea; area; area = area->GetParent()) - count++; - - if (count > MAX_PATH_LENGTH) - count = MAX_PATH_LENGTH; - - // build path in correct order - from eye outwards - int i = count; - for (area = farArea; i && area; area = area->GetParent()) - { - path[--i] = area; - } + ApproachAreaCost cost; - // traverse path to find first area we cannot see (skip the first area) - for (i = 1; i < count; i++) + // + // Keep building paths to farArea and blocking them off until we + // cant path there any more. + // As areas are blocked off, all exits will be enumerated. + // + while (m_approachCount < MAX_APPROACH_AREAS) { - // if we see this area, continue on - if (IsAreaVisible(&eye, path[i])) - continue; + CNavArea *from, *to; - // we can't see this area. - // mark this area as "blocked" and unusable by subsequent approach paths - if (BlockedIDCount == MAX_BLOCKED_AREAS) + if (searchType == FROM_EYE) { - CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id); - return; + // find another path *to* 'farArea' + // we must pathfind from us in order to pick up one-way paths OUT OF our area + from = this; + to = farArea; + } + else // TO_EYE + { + // find another path *from* 'farArea' + // we must pathfind to us in order to pick up one-way paths INTO our area + from = farArea; + to = this; } - // if the area to be blocked is actually farArea, block the one just prior - // (blocking farArea will cause all subsequent pathfinds to fail) - int block = (path[i] == farArea) ? i - 1 : i; + // build the actual path + if (NavAreaBuildPath(from, to, NULL, cost) == false) + break; - BlockedID[BlockedIDCount++] = path[block]->GetID(); + // find number of areas on path + int count = 0; + CNavArea *area; + for (area = to; area; area = area->GetParent()) + count++; - if (block == 0) + if (count > MAX_PATH_LENGTH) + count = MAX_PATH_LENGTH; + + // if the path is only two areas long, there can be no approach points + if (count <= 2) break; - // store new approach area if not already in set - int a; - for (a = 0; a < m_approachCount; a++) - if (m_approach[a].here.area == path[block - 1]) - break; + // build path starting from eye + int i = 0; - if (a == m_approachCount) + if (searchType == FROM_EYE) { - m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr; - m_approach[m_approachCount].here.area = path[block - 1]; - m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow(); - m_approach[m_approachCount].next.area = path[block]; - m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow(); - m_approachCount++; + for(area = to; i < count && area; area = area->GetParent()) + { + path[count - i - 1] = area; + ++i; + } + } + else // TO_EYE + { + for(area = to; i < count && area; area = area->GetParent()) + path[i++] = area; } - // we are done with this path - break; - } + // traverse path to find first area we cannot see (skip the first area) + for (i = 1; i < count; i++) + { + // if we see this area, continue on + if (IsAreaVisible(&eye, path[i])) + continue; - // find another path to 'farArea' - ApproachAreaCost cost; - if (NavAreaBuildPath(this, farArea, nullptr, cost) == false) - { - // can't find a path to 'farArea' means all exits have been already tested and blocked - break; + // we can't see this area. + // mark this area as "blocked" and unusable by subsequent approach paths + if (BlockedIDCount == MAX_BLOCKED_AREAS) + { + CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id); + return; + } + + // if the area to be blocked is actually farArea, block the one just prior + // (blocking farArea will cause all subsequent pathfinds to fail) + int block = (path[i] == farArea) ? i - 1 : i; + + if (block == 0) + continue; + + BlockedID[BlockedIDCount++] = path[block]->GetID(); + + // store new approach area if not already in set + int a; + for (a = 0; a < m_approachCount; a++) + if (m_approach[a].here.area == path[block-1]) + break; + + if (a == m_approachCount) + { + m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr; + m_approach[m_approachCount].here.area = path[block - 1]; + m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow(); + m_approach[m_approachCount].next.area = path[block]; + m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow(); + m_approachCount++; + } + + // we are done with this path + break; + } } } } @@ -4810,3 +4851,133 @@ Place CNavAreaGrid::GetPlace(const Vector *pos) const return UNDEFINED_PLACE; } + +static const char *g_pszDefaultPlaceNames[] = +{ + "BombsiteA", + "BombsiteB", + "BombsiteC", + "Hostages", + "HostageRescueZone", + "VipRescueZone", + "CTSpawn", + "TSpawn", + "Bridge", + "Middle", + "House", + "Apartment", + "Apartments", + "Market", + "Sewers", + "Tunnel", + "Ducts", + "Village", + "Roof", + "Upstairs", + "Downstairs", + "Basement", + "Crawlspace", + "Kitchen", + "Inside", + "Outside", + "Tower", + "WineCellar", + "Garage", + "Courtyard", + "Water", + "FrontDoor", + "BackDoor", + "SideDoor", + "BackWay", + "FrontYard", + "BackYard", + "SideYard", + "Lobby", + "Vault", + "Elevator", + "DoubleDoors", + "SecurityDoors", + "LongHall", + "SideHall", + "FrontHall", + "BackHall", + "MainHall", + "FarSide", + "Windows", + "Window", + "Attic", + "StorageRoom", + "ProjectorRoom", + "MeetingRoom", + "ConferenceRoom", + "ComputerRoom", + "BigOffice", + "LittleOffice", + "Dumpster", + "Airplane", + "Underground", + "Bunker", + "Mines", + "Front", + "Back", + "Rear", + "Side", + "Ramp", + "Underpass", + "Overpass", + "Stairs", + "Ladder", + "Gate", + "GateHouse", + "LoadingDock", + "GuardHouse", + "Entrance", + "VendingMachines", + "Loft", + "Balcony", + "Alley", + "BackAlley", + "SideAlley", + "FrontRoom", + "BackRoom", + "SideRoom", + "Crates", + "Truck", + "Bedroom", + "FamilyRoom", + "Bathroom", + "LivingRoom", + "Den", + "Office", + "Atrium", + "Entryway", + "Foyer", + "Stairwell", + "Fence", + "Deck", + "Porch", + "Patio", + "Wall" +}; + +// Return fallback place name for given place id +const char *CNavAreaGrid::IDToName(Place place) const +{ + if (place <= 0 || place > ARRAYSIZE(g_pszDefaultPlaceNames)) + return nullptr; + + return g_pszDefaultPlaceNames[place - 1]; +} + +// Return place id for given place name +Place CNavAreaGrid::NameToID(const char *name) const +{ + for (unsigned int place = 0; place < ARRAYSIZE(g_pszDefaultPlaceNames); place++) + { + const char *placeName = g_pszDefaultPlaceNames[place]; + if (!Q_stricmp(placeName, name)) + return place + 1; + } + + return UNDEFINED_PLACE; +} diff --git a/regamedll/game_shared/bot/nav_area.h b/regamedll/game_shared/bot/nav_area.h index d1ed024ec..c4cf8a1ff 100644 --- a/regamedll/game_shared/bot/nav_area.h +++ b/regamedll/game_shared/bot/nav_area.h @@ -505,6 +505,8 @@ class CNavAreaGrid bool IsValid() const; Place GetPlace(const Vector *pos) const; // return radio chatter place for given coordinate + Place NameToID(const char *name) const; + const char *IDToName(Place id) const; private: const float m_cellSize; diff --git a/regamedll/game_shared/bot/nav_file.cpp b/regamedll/game_shared/bot/nav_file.cpp index ad04f52c9..c9f206fd9 100644 --- a/regamedll/game_shared/bot/nav_file.cpp +++ b/regamedll/game_shared/bot/nav_file.cpp @@ -85,7 +85,10 @@ void PlaceDirectory::Save(int fd) // store entries for (auto &id : m_directory) { - auto placeName = TheBotPhrases->IDToName(id); + const char *placeName = TheBotPhrases->IDToName(id); + + if (!TheBotPhrases->IsValid() && !placeName) + placeName = TheNavAreaGrid.IDToName(id); // store string length followed by string itself unsigned short len = (unsigned short)Q_strlen(placeName) + 1; @@ -110,7 +113,11 @@ void PlaceDirectory::Load(SteamFile *file) file->Read(&len, sizeof(unsigned short)); file->Read(placeName, len); - AddPlace(TheBotPhrases->NameToID(placeName)); + Place place = TheBotPhrases->NameToID(placeName); + if (!TheBotPhrases->IsValid() && place == UNDEFINED_PLACE) + place = TheNavAreaGrid.NameToID(placeName); + + AddPlace(place); } } @@ -652,7 +659,12 @@ void LoadLocationFile(const char *filename) for (int i = 0; i < dirSize; i++) { locData = SharedParse(locData); - directory.push_back(TheBotPhrases->NameToID(SharedGetToken())); + + Place place = TheBotPhrases->NameToID(SharedGetToken()); + if (!TheBotPhrases->IsValid() && place == UNDEFINED_PLACE) + place = TheNavAreaGrid.NameToID(SharedGetToken()); + + directory.push_back(place); } // read places for each nav area diff --git a/regamedll/pm_shared/pm_shared.cpp b/regamedll/pm_shared/pm_shared.cpp index ed6819769..b40495569 100644 --- a/regamedll/pm_shared/pm_shared.cpp +++ b/regamedll/pm_shared/pm_shared.cpp @@ -3300,6 +3300,11 @@ void EXT_FUNC __API_HOOK(PM_Move)(struct playermove_s *ppmove, int server) { pmove->friction = 1.0f; } + +#ifdef REGAMEDLL_API + // save the last usercmd + pmoveplayer->SetLastUserCommand(pmove->cmd); +#endif } NOXREF int PM_GetVisEntInfo(int ent) diff --git a/regamedll/public/regamedll/API/CSPlayer.h b/regamedll/public/regamedll/API/CSPlayer.h index 0b8672332..f2bfe028b 100644 --- a/regamedll/public/regamedll/API/CSPlayer.h +++ b/regamedll/public/regamedll/API/CSPlayer.h @@ -141,6 +141,16 @@ class CCSPlayer: public CCSMonster { EProtectionState GetProtectionState() const; bool CheckActivityInGame(); + const usercmd_t *GetLastUserCommand() const + { + return &m_LastCmd; + } + + void SetLastUserCommand(const usercmd_t &ucmd) + { + m_LastCmd = ucmd; + } + public: char m_szModel[32]; bool m_bForceShowMenu; @@ -175,6 +185,7 @@ class CCSPlayer: public CCSMonster { bool m_bPlayerDominated[MAX_CLIENTS]; // [0-31] array of state per other player whether player is dominating other players int m_iGibDamageThreshold; // negative health to reach to gib player + usercmd_t m_LastCmd; }; // Inlines diff --git a/regamedll/public/regamedll/regamedll_api.h b/regamedll/public/regamedll/regamedll_api.h index 4aeb12545..d655ee656 100644 --- a/regamedll/public/regamedll/regamedll_api.h +++ b/regamedll/public/regamedll/regamedll_api.h @@ -38,7 +38,7 @@ #include #define REGAMEDLL_API_VERSION_MAJOR 5 -#define REGAMEDLL_API_VERSION_MINOR 26 +#define REGAMEDLL_API_VERSION_MINOR 27 // CBasePlayer::Spawn hook typedef IHookChainClass IReGameHook_CBasePlayer_Spawn; @@ -624,6 +624,10 @@ typedef IHookChainRegistryClass IReGameHookRegistry_CBa typedef IHookChainClass IReGameHook_CBasePlayer_Observer_Think; typedef IHookChainRegistryClass IReGameHookRegistry_CBasePlayer_Observer_Think; +// CBasePlayer::RemoveAllItems hook +typedef IHookChainClass IReGameHook_CBasePlayer_RemoveAllItems; +typedef IHookChainRegistryClass IReGameHookRegistry_CBasePlayer_RemoveAllItems; + class IReGameHookchains { public: virtual ~IReGameHookchains() {} @@ -785,6 +789,7 @@ class IReGameHookchains { virtual IReGameHookRegistry_CBasePlayer_PlayerDeathThink *CBasePlayer_PlayerDeathThink() = 0; virtual IReGameHookRegistry_CBasePlayer_Observer_Think *CBasePlayer_Observer_Think() = 0; + virtual IReGameHookRegistry_CBasePlayer_RemoveAllItems *CBasePlayer_RemoveAllItems() = 0; }; struct ReGameFuncs_t { diff --git a/regamedll/version/version.h b/regamedll/version/version.h index 0f32ed96e..fc64686b4 100644 --- a/regamedll/version/version.h +++ b/regamedll/version/version.h @@ -6,5 +6,5 @@ #pragma once #define VERSION_MAJOR 5 -#define VERSION_MINOR 26 +#define VERSION_MINOR 27 #define VERSION_MAINTENANCE 0