You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
693 lines
14 KiB
693 lines
14 KiB
#include "bot/bot_common.h" |
|
|
|
/* |
|
* Globals initialization |
|
*/ |
|
short s_iBeamSprite = 0; |
|
float cosTable[ COS_TABLE_SIZE ]; |
|
|
|
bool UTIL_IsNameTaken (const char *name, bool ignoreHumans) |
|
{ |
|
for (int i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBaseEntity *player = UTIL_PlayerByIndex (i); |
|
|
|
if (player == NULL) |
|
continue; |
|
|
|
if (FNullEnt (player->pev)) |
|
continue; |
|
|
|
if (FStrEq (STRING (player->pev->netname), "")) |
|
continue; |
|
|
|
if (player->IsPlayer () && (((CBasePlayer *)player)->IsBot () == TRUE)) |
|
{ |
|
// bots can have prefixes so we need to check the name |
|
// against the profile name instead. |
|
CBot *bot = static_cast<CBot *>(player); |
|
if (FStrEq (name, bot->GetProfile ()->GetName ())) |
|
{ |
|
return true; |
|
} |
|
} |
|
else |
|
{ |
|
if (!ignoreHumans) |
|
{ |
|
if (FStrEq (name, STRING (player->pev->netname))) |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
int UTIL_ClientsInGame () |
|
{ |
|
int iCount = 0; |
|
for (int iIndex = 1; iIndex <= gpGlobals->maxClients; ++iIndex) |
|
{ |
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex (iIndex); |
|
|
|
if (pPlayer == NULL) |
|
continue; |
|
|
|
if (FNullEnt (pPlayer->pev)) |
|
continue; |
|
|
|
if (FStrEq (STRING (pPlayer->pev->netname), "")) |
|
continue; |
|
|
|
++iCount; |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
int UTIL_ActivePlayersInGame() |
|
{ |
|
int iCount = 0; |
|
for (int iIndex = 1; iIndex <= gpGlobals->maxClients; ++iIndex) |
|
{ |
|
CBaseEntity *entity = UTIL_PlayerByIndex(iIndex); |
|
|
|
if (entity == NULL) |
|
continue; |
|
|
|
if (FNullEnt(entity->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(entity->pev->netname), "")) |
|
continue; |
|
|
|
CBasePlayer *player = static_cast<CBasePlayer *>(entity); |
|
|
|
++iCount; |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
int UTIL_HumansInGame(bool ignoreSpectators) |
|
{ |
|
int iCount = 0; |
|
|
|
for (int iIndex = 1; iIndex <= gpGlobals->maxClients; ++iIndex) |
|
{ |
|
CBaseEntity *entity = UTIL_PlayerByIndex(iIndex); |
|
|
|
if (entity == NULL) |
|
continue; |
|
|
|
if (FNullEnt(entity->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(entity->pev->netname), "")) |
|
continue; |
|
|
|
CBasePlayer *player = static_cast<CBasePlayer *>(entity); |
|
|
|
if (player->IsBot()) |
|
continue; |
|
|
|
//if (ignoreSpectators && player->m_iTeam != TERRORIST && player->m_iTeam != CT) |
|
// continue; |
|
|
|
//if (ignoreSpectators && player->m_iJoiningState != JOINED) |
|
// continue; |
|
|
|
++iCount; |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
int UTIL_HumansOnTeam(int teamID, bool isAlive) |
|
{ |
|
int iCount = 0; |
|
for (int iIndex = 1; iIndex <= gpGlobals->maxClients; ++iIndex) |
|
{ |
|
CBaseEntity *entity = UTIL_PlayerByIndex(iIndex); |
|
|
|
if (entity == NULL) |
|
continue; |
|
|
|
if (FNullEnt(entity->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(entity->pev->netname), "")) |
|
continue; |
|
|
|
CBasePlayer *player = static_cast<CBasePlayer *>(entity); |
|
|
|
if (player->IsBot()) |
|
continue; |
|
|
|
//if (player->m_iTeam != teamID) |
|
// continue; |
|
|
|
if (isAlive && !player->IsAlive()) |
|
continue; |
|
|
|
++iCount; |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
int UTIL_BotsInGame() |
|
{ |
|
int iCount = 0; |
|
|
|
for (int iIndex = 1; iIndex <= gpGlobals->maxClients; ++iIndex) |
|
{ |
|
CBasePlayer *pPlayer = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(iIndex)); |
|
|
|
if (pPlayer == NULL) |
|
continue; |
|
|
|
if (FNullEnt(pPlayer->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(pPlayer->pev->netname), "")) |
|
continue; |
|
|
|
if (!pPlayer->IsBot()) |
|
continue; |
|
|
|
++iCount; |
|
} |
|
|
|
return iCount; |
|
} |
|
/* |
|
bool UTIL_KickBotFromTeam(TeamName kickTeam) |
|
{ |
|
int i; |
|
|
|
// try to kick a dead bot first |
|
for (i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (player == NULL) |
|
continue; |
|
|
|
if (FNullEnt(player->pev)) |
|
continue; |
|
|
|
const char *name = STRING(player->pev->netname); |
|
if (FStrEq(name, "")) |
|
continue; |
|
|
|
if (!player->IsBot()) |
|
continue; |
|
|
|
if (!player->IsAlive() && player->m_iTeam == kickTeam) |
|
{ |
|
// its a bot on the right team - kick it |
|
SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", STRING(player->pev->netname))); |
|
return true; |
|
} |
|
} |
|
|
|
// no dead bots, kick any bot on the given team |
|
for (i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (player == NULL) |
|
continue; |
|
|
|
if (FNullEnt(player->pev)) |
|
continue; |
|
|
|
const char *name = STRING(player->pev->netname); |
|
if (FStrEq(name, "")) |
|
continue; |
|
|
|
if (!player->IsBot()) |
|
continue; |
|
|
|
if (player->m_iTeam == kickTeam) |
|
{ |
|
// its a bot on the right team - kick it |
|
SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", STRING(player->pev->netname))); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
*/ |
|
bool UTIL_IsTeamAllBots(int team) |
|
{ |
|
int botCount = 0; |
|
for (int i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (player == NULL) |
|
continue; |
|
|
|
//if (player->m_iTeam != team) |
|
// continue; |
|
|
|
if (FNullEnt(player->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(player->pev->netname), "")) |
|
continue; |
|
|
|
if (!(player->pev->flags & FL_FAKECLIENT)) |
|
return false; |
|
|
|
++botCount; |
|
} |
|
|
|
return (botCount) ? true : false; |
|
} |
|
|
|
// Return the closest active player to the given position. |
|
// If 'distance' is non-NULL, the distance to the closest player is returned in it. |
|
|
|
/*extern*/ CBasePlayer *UTIL_GetClosestPlayer(const Vector *pos, float *distance) |
|
{ |
|
CBasePlayer *closePlayer = NULL; |
|
float closeDistSq = 1.0e12f; // 999999999999.9f |
|
|
|
for (int i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (!IsEntityValid(player)) |
|
continue; |
|
|
|
if (!player->IsAlive()) |
|
continue; |
|
|
|
float distSq = (player->pev->origin - *pos).LengthSquared(); |
|
if (distSq < closeDistSq) |
|
{ |
|
closeDistSq = distSq; |
|
closePlayer = player; |
|
} |
|
} |
|
|
|
if (distance) |
|
*distance = sqrt(closeDistSq); |
|
|
|
return closePlayer; |
|
} |
|
|
|
// Return the closest active player on the given team to the given position. |
|
// If 'distance' is non-NULL, the distance to the closest player is returned in it. |
|
|
|
/*extern*/ CBasePlayer *UTIL_GetClosestPlayer(const Vector *pos, int team, float *distance) |
|
{ |
|
CBasePlayer *closePlayer = NULL; |
|
float closeDistSq = 1.0e12f; // 999999999999.9f |
|
|
|
for (int i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (!IsEntityValid(player)) |
|
continue; |
|
|
|
if (!player->IsAlive()) |
|
continue; |
|
|
|
// if (player->m_iTeam != team) |
|
// continue; |
|
|
|
float distSq = (player->pev->origin - *pos).LengthSquared(); |
|
if (distSq < closeDistSq) |
|
{ |
|
closeDistSq = distSq; |
|
closePlayer = player; |
|
} |
|
} |
|
|
|
if (distance) |
|
*distance = sqrt(closeDistSq); |
|
|
|
return closePlayer; |
|
} |
|
|
|
const char *UTIL_GetBotPrefix() |
|
{ |
|
return cv_bot_prefix.string; |
|
} |
|
|
|
void UTIL_ConstructBotNetName(char *name, int nameLength, const BotProfile *profile) |
|
{ |
|
if (profile == NULL) |
|
{ |
|
name[0] = '\0'; |
|
return; |
|
} |
|
|
|
// if there is no bot prefix just use the profile name. |
|
if ((UTIL_GetBotPrefix() == NULL) || (Q_strlen(UTIL_GetBotPrefix()) == 0)) |
|
{ |
|
Q_strncpy(name, profile->GetName(), nameLength); |
|
return; |
|
} |
|
|
|
Q_snprintf(name, nameLength, "%s %s", UTIL_GetBotPrefix(), profile->GetName()); |
|
} |
|
|
|
bool UTIL_IsVisibleToTeam(const Vector &spot, int team, float maxRange) |
|
{ |
|
for (int i = 1; i <= gpGlobals->maxClients; ++i) |
|
{ |
|
CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex(i)); |
|
|
|
if (player == NULL) |
|
continue; |
|
|
|
if (FNullEnt(player->pev)) |
|
continue; |
|
|
|
if (FStrEq(STRING(player->pev->netname), "")) |
|
continue; |
|
|
|
if (!player->IsAlive()) |
|
continue; |
|
|
|
// if (player->m_iTeam != team) |
|
// continue; |
|
|
|
if (maxRange > 0.0f && (spot - player->Center()).IsLengthGreaterThan(maxRange)) |
|
continue; |
|
|
|
TraceResult result; |
|
UTIL_TraceLine(player->EyePosition(), spot, ignore_monsters, ignore_glass, ENT(player->pev), &result); |
|
|
|
if (result.flFraction == 1.0f) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
CBasePlayer *UTIL_GetLocalPlayer() |
|
{ |
|
if (!IS_DEDICATED_SERVER()) |
|
return static_cast<CBasePlayer *>(UTIL_PlayerByIndex(1)); |
|
|
|
return NULL; |
|
} |
|
|
|
NOXREF Vector UTIL_ComputeOrigin(entvars_t *pevVars) |
|
{ |
|
if (pevVars->origin.x == 0.0f && pevVars->origin.y == 0.0f && pevVars->origin.z == 0.0f) |
|
return (pevVars->absmax + pevVars->absmin) * 0.5f; |
|
else |
|
return pevVars->origin; |
|
} |
|
|
|
NOXREF Vector UTIL_ComputeOrigin(CBaseEntity *pEntity) |
|
{ |
|
return UTIL_ComputeOrigin(pEntity->pev); |
|
} |
|
|
|
NOXREF Vector UTIL_ComputeOrigin(edict_t *pentEdict) |
|
{ |
|
return UTIL_ComputeOrigin(VARS(pentEdict)); |
|
} |
|
|
|
NOXREF void UTIL_DrawBeamFromEnt(int iIndex, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue) |
|
{ |
|
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecEnd); |
|
WRITE_BYTE(TE_BEAMENTPOINT); |
|
WRITE_SHORT(iIndex); |
|
WRITE_COORD(vecEnd.x); |
|
WRITE_COORD(vecEnd.y); |
|
WRITE_COORD(vecEnd.z); |
|
WRITE_SHORT(s_iBeamSprite); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(iLifetime); |
|
WRITE_BYTE(10); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(bRed); |
|
WRITE_BYTE(bGreen); |
|
WRITE_BYTE(bBlue); |
|
WRITE_BYTE(255); |
|
WRITE_BYTE(0); |
|
MESSAGE_END(); |
|
} |
|
|
|
void UTIL_DrawBeamPoints(Vector vecStart, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue) |
|
{ |
|
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecStart); |
|
WRITE_BYTE(TE_BEAMPOINTS); |
|
WRITE_COORD(vecStart.x); |
|
WRITE_COORD(vecStart.y); |
|
WRITE_COORD(vecStart.z); |
|
WRITE_COORD(vecEnd.x); |
|
WRITE_COORD(vecEnd.y); |
|
WRITE_COORD(vecEnd.z); |
|
WRITE_SHORT(s_iBeamSprite); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(iLifetime); |
|
WRITE_BYTE(10); |
|
WRITE_BYTE(0); |
|
WRITE_BYTE(bRed); |
|
WRITE_BYTE(bGreen); |
|
WRITE_BYTE(bBlue); |
|
WRITE_BYTE(255); |
|
WRITE_BYTE(0); |
|
MESSAGE_END(); |
|
} |
|
|
|
void CONSOLE_ECHO(char *pszMsg, ...) |
|
{ |
|
va_list argptr; |
|
static char szStr[1024]; |
|
|
|
va_start(argptr, pszMsg); |
|
vsprintf(szStr, pszMsg, argptr); |
|
va_end(argptr); |
|
|
|
SERVER_PRINT(szStr); |
|
} |
|
|
|
void CONSOLE_ECHO_LOGGED(char *pszMsg, ...) |
|
{ |
|
va_list argptr; |
|
static char szStr[1024]; |
|
|
|
va_start(argptr, pszMsg); |
|
vsprintf(szStr, pszMsg, argptr); |
|
va_end(argptr); |
|
|
|
SERVER_PRINT(szStr); |
|
UTIL_LogPrintf(szStr); |
|
} |
|
|
|
void BotPrecache() |
|
{ |
|
s_iBeamSprite = PRECACHE_MODEL("sprites/smoke.spr"); |
|
|
|
PRECACHE_SOUND("buttons/bell1.wav"); |
|
PRECACHE_SOUND("buttons/blip1.wav"); |
|
PRECACHE_SOUND("buttons/blip2.wav"); |
|
PRECACHE_SOUND("buttons/button11.wav"); |
|
PRECACHE_SOUND("buttons/latchunlocked2.wav"); |
|
PRECACHE_SOUND("buttons/lightswitch2.wav"); |
|
PRECACHE_SOUND("ambience/quail1.wav"); |
|
|
|
PRECACHE_SOUND("events/tutor_msg.wav"); |
|
PRECACHE_SOUND("events/enemy_died.wav"); |
|
PRECACHE_SOUND("events/friend_died.wav"); |
|
PRECACHE_SOUND("events/task_complete.wav"); |
|
} |
|
|
|
void InitBotTrig() |
|
{ |
|
for (int i = 0; i < COS_TABLE_SIZE; ++i) |
|
{ |
|
float angle = 2.0f * M_PI * (float)i / (float)(COS_TABLE_SIZE - 1); |
|
cosTable[i] = cos(angle); |
|
} |
|
} |
|
|
|
float BotCOS(float angle) |
|
{ |
|
angle = NormalizeAnglePositive(angle); |
|
int i = angle * ((COS_TABLE_SIZE - 1) / 360.0f); |
|
return cosTable[ i ]; |
|
} |
|
|
|
float BotSIN(float angle) |
|
{ |
|
angle = NormalizeAnglePositive(angle - 90); |
|
int i = angle * ((COS_TABLE_SIZE - 1) / 360.0f); |
|
return cosTable[ i ]; |
|
} |
|
|
|
// Determine if this event is audible, and if so, return its audible range and priority |
|
|
|
bool IsGameEventAudible(GameEventType event, CBaseEntity *entity, CBaseEntity *other, float *range, PriorityType *priority, bool *isHostile) |
|
{ |
|
#if 0 |
|
CBasePlayer *player = static_cast<CBasePlayer *>(entity); |
|
|
|
if (entity == NULL || !player->IsPlayer()) |
|
player = NULL; |
|
|
|
const float ShortRange = 1000.0f; |
|
const float NormalRange = 2000.0f; |
|
|
|
switch (event) |
|
{ |
|
// TODO: Check weapon type (knives are pretty quiet) |
|
// TODO: Use actual volume, account for silencers, etc. |
|
case EVENT_WEAPON_FIRED: |
|
{ |
|
if (player->m_pActiveItem == NULL) |
|
return false; |
|
|
|
switch (player->m_pActiveItem->m_iId) |
|
{ |
|
// silent "firing" |
|
case WEAPON_HEGRENADE: |
|
case WEAPON_SMOKEGRENADE: |
|
case WEAPON_FLASHBANG: |
|
case WEAPON_SHIELDGUN: |
|
case WEAPON_C4: |
|
return false; |
|
// quiet |
|
case WEAPON_KNIFE: |
|
case WEAPON_TMP: |
|
*range = ShortRange; |
|
break; |
|
// M4A1 - check for silencer |
|
case WEAPON_M4A1: |
|
{ |
|
CBasePlayerWeapon *pWeapon = static_cast<CBasePlayerWeapon *>(player->m_pActiveItem); |
|
if (pWeapon->m_iWeaponState & WPNSTATE_M4A1_SILENCED) |
|
*range = ShortRange; |
|
else |
|
*range = NormalRange; |
|
break; |
|
} |
|
// USP - check for silencer |
|
case WEAPON_USP: |
|
{ |
|
CBasePlayerWeapon *pWeapon = static_cast<CBasePlayerWeapon *>(player->m_pActiveItem); |
|
if (pWeapon->m_iWeaponState & WPNSTATE_USP_SILENCED) |
|
*range = ShortRange; |
|
else |
|
*range = NormalRange; |
|
break; |
|
} |
|
// loud |
|
case WEAPON_AWP: |
|
*range = 99999.0f; |
|
break; |
|
// normal |
|
default: |
|
*range = NormalRange; |
|
break; |
|
} |
|
|
|
*priority = PRIORITY_HIGH; |
|
*isHostile = true; |
|
return true; |
|
} |
|
case EVENT_HE_GRENADE_EXPLODED: |
|
*range = 99999.0f; |
|
*priority = PRIORITY_HIGH; |
|
*isHostile = true; |
|
return true; |
|
|
|
case EVENT_FLASHBANG_GRENADE_EXPLODED: |
|
*range = 1000.0f; |
|
*priority = PRIORITY_LOW; |
|
*isHostile = true; |
|
return true; |
|
|
|
case EVENT_SMOKE_GRENADE_EXPLODED: |
|
*range = 1000.0f; |
|
*priority = PRIORITY_LOW; |
|
*isHostile = true; |
|
return true; |
|
|
|
case EVENT_GRENADE_BOUNCED: |
|
*range = 500.0f; |
|
*priority = PRIORITY_LOW; |
|
*isHostile = true; |
|
return true; |
|
|
|
case EVENT_BREAK_GLASS: |
|
case EVENT_BREAK_WOOD: |
|
case EVENT_BREAK_METAL: |
|
case EVENT_BREAK_FLESH: |
|
case EVENT_BREAK_CONCRETE: |
|
*range = 1100.0f; |
|
*priority = PRIORITY_MEDIUM; |
|
*isHostile = true; |
|
return true; |
|
|
|
case EVENT_DOOR: |
|
*range = 1100.0f; |
|
*priority = PRIORITY_MEDIUM; |
|
*isHostile = false; |
|
return true; |
|
|
|
case EVENT_WEAPON_FIRED_ON_EMPTY: |
|
case EVENT_PLAYER_FOOTSTEP: |
|
case EVENT_WEAPON_RELOADED: |
|
case EVENT_WEAPON_ZOOMED: |
|
case EVENT_PLAYER_LANDED_FROM_HEIGHT: |
|
*range = 1100.0f; |
|
*priority = PRIORITY_LOW; |
|
*isHostile = false; |
|
return true; |
|
|
|
case EVENT_HOSTAGE_USED: |
|
case EVENT_HOSTAGE_CALLED_FOR_HELP: |
|
*range = 1200.0f; |
|
*priority = PRIORITY_MEDIUM; |
|
*isHostile = false; |
|
return true; |
|
} |
|
|
|
return false; |
|
#else |
|
return true; |
|
#endif |
|
} |
|
|
|
void HintMessageToAllPlayers(const char *message) |
|
{ |
|
hudtextparms_t textParms; |
|
|
|
textParms.x = -1.0f; |
|
textParms.y = -1.0f; |
|
textParms.effect = 0; |
|
|
|
textParms.r1 = 100; |
|
textParms.g1 = 255; |
|
textParms.b1 = 100; |
|
|
|
textParms.r2 = 255; |
|
textParms.g2 = 255; |
|
textParms.b2 = 255; |
|
|
|
textParms.fadeinTime = 1.0f; |
|
textParms.fadeoutTime = 5.0f; |
|
textParms.holdTime = 5.0f; |
|
textParms.fxTime = 0.0f; |
|
|
|
textParms.channel = 0; |
|
|
|
UTIL_HudMessageAll(textParms, message); |
|
}
|
|
|