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