yapb-noob-edition/source/basecode.cpp

6049 lines
186 KiB
C++
Raw Normal View History

//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
2014-07-30 14:17:46 +04:00
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// http://yapb.jeefo.net/license
2014-07-30 14:17:46 +04:00
//
#include <core.h>
ConVar yb_debug ("yb_debug", "0");
ConVar yb_debug_goal ("yb_debug_goal", "-1");
ConVar yb_user_follow_percent ("yb_user_follow_percent", "20");
ConVar yb_user_max_followers ("yb_user_max_followers", "1");
2014-07-30 14:17:46 +04:00
ConVar yb_jasonmode ("yb_jasonmode", "0");
ConVar yb_communication_type ("yb_communication_type", "2");
ConVar yb_economics_rounds ("yb_economics_rounds", "1");
ConVar yb_walking_allowed ("yb_walking_allowed", "1");
ConVar yb_camping_allowed ("yb_camping_allowed", "1");
2014-07-30 14:17:46 +04:00
ConVar yb_tkpunish ("yb_tkpunish", "1");
ConVar yb_freeze_bots ("yb_freeze_bots", "0");
ConVar yb_spraypaints ("yb_spraypaints", "1");
ConVar yb_botbuy ("yb_botbuy", "1");
ConVar yb_chatter_path ("yb_chatter_path", "sound/radio/bot");
ConVar yb_restricted_weapons ("yb_restricted_weapons", "");
2014-07-30 14:17:46 +04:00
// game console variables
ConVar mp_c4timer ("mp_c4timer", NULL, VT_NOREGISTER);
ConVar mp_buytime ("mp_buytime", NULL, VT_NOREGISTER);
ConVar mp_footsteps ("mp_footsteps", NULL, VT_NOREGISTER);
ConVar sv_gravity ("sv_gravity", NULL, VT_NOREGISTER);
int Bot::GetMessageQueue (void)
{
// this function get the current message from the bots message queue
int message = m_messageQueue[m_actMessageIndex++];
m_actMessageIndex &= 0x1f; // wraparound
return message;
}
void Bot::PushMessageQueue (int message)
{
// this function put a message into the bot message queue
if (message == GSM_SAY)
{
// notify other bots of the spoken text otherwise, bots won't respond to other bots (network messages aren't sent from bots)
int entityIndex = GetIndex ();
for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
Bot *otherBot = bots.GetBot (i);
2014-07-30 14:17:46 +04:00
if (otherBot != NULL && otherBot->pev != pev)
{
2014-08-06 00:03:50 +04:00
if (m_notKilled == IsAlive (otherBot->GetEntity ()))
2014-07-30 14:17:46 +04:00
{
otherBot->m_sayTextBuffer.entityIndex = entityIndex;
strcpy (otherBot->m_sayTextBuffer.sayText, m_tempStrings);
}
otherBot->m_sayTextBuffer.timeNextChat = GetWorldTime () + otherBot->m_sayTextBuffer.chatDelay;
}
}
}
m_messageQueue[m_pushMessageIndex++] = message;
m_pushMessageIndex &= 0x1f; // wraparound
}
int Bot::InFieldOfView (const Vector &destination)
{
float entityAngle = AngleMod (destination.ToYaw ()); // find yaw angle from source to destination...
float viewAngle = AngleMod (pev->v_angle.y); // get bot's current view angle...
// return the absolute value of angle to destination entity
// zero degrees means straight ahead, 45 degrees to the left or
// 45 degrees to the right is the limit of the normal view angle
int absoluteAngle = abs (static_cast <int> (viewAngle) - static_cast <int> (entityAngle));
if (absoluteAngle > 180)
absoluteAngle = 360 - absoluteAngle;
return absoluteAngle;
}
bool Bot::IsInViewCone (const Vector &origin)
{
// this function returns true if the spatial vector location origin is located inside
// the field of view cone of the bot entity, false otherwise. It is assumed that entities
// have a human-like field of view, that is, about 90 degrees.
return ::IsInViewCone (origin, GetEntity ());
}
bool Bot::ItemIsVisible (const Vector &destination, char *itemName)
{
TraceResult tr;
// trace a line from bot's eyes to destination..
TraceLine (EyePosition (), destination, true, GetEntity (), &tr);
// check if line of sight to object is not blocked (i.e. visible)
if (tr.flFraction != 1.0f)
2014-07-30 14:17:46 +04:00
{
// check for standard items
if (strcmp (STRING (tr.pHit->v.classname), itemName) == 0)
return true;
if (tr.flFraction > 0.98f && (yb_csdm_mode.GetBool () && strncmp (STRING (tr.pHit->v.classname), "csdmw_", 6) == 0))
2014-07-30 14:17:46 +04:00
return true;
return false;
}
return true;
}
bool Bot::EntityIsVisible (const Vector &dest, bool fromBody)
{
TraceResult tr;
// trace a line from bot's eyes to destination...
TraceLine (fromBody ? pev->origin - Vector (0.0f, 0.0f, 1.0f) : EyePosition (), dest, true, true, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
// check if line of sight to object is not blocked (i.e. visible)
return tr.flFraction >= 1.0f;
2014-07-30 14:17:46 +04:00
}
2015-06-11 16:53:21 +03:00
void Bot::CheckGrenadeThrow (void)
{
// check if throwing a grenade is a good thing to do...
if (m_lastEnemy == NULL || yb_ignore_enemies.GetBool () || yb_jasonmode.GetBool () || m_grenadeCheckTime > GetWorldTime () || m_isUsingGrenade || GetTaskId () == TASK_PLANTBOMB || GetTaskId () == TASK_DEFUSEBOMB || m_isReloading || !IsAlive (m_lastEnemy))
2015-06-11 16:53:21 +03:00
{
m_states &= ~(STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG);
return;
}
// check again in some seconds
2015-06-20 11:45:59 +03:00
m_grenadeCheckTime = GetWorldTime () + 0.5f;
2015-06-11 16:53:21 +03:00
// check if we have grenades to throw
int grenadeToThrow = CheckGrenades ();
// if we don't have grenades no need to check it this round again
if (grenadeToThrow == -1)
{
m_grenadeCheckTime = GetWorldTime () + 15.0f; // changed since, conzero can drop grens from dead players
2015-06-11 16:53:21 +03:00
m_states &= ~(STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG);
return;
}
// care about different types of grenades
if ((grenadeToThrow == WEAPON_EXPLOSIVE || grenadeToThrow == WEAPON_SMOKE) && Random.Long (0, 100) < 45 && !(m_states & (STATE_SEEING_ENEMY | STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG)))
{
float distance = (m_lastEnemy->v.origin - pev->origin).GetLength ();
// is enemy to high to throw
if ((m_lastEnemy->v.origin.z > (pev->origin.z + 650.0)) || !(m_lastEnemy->v.flags & (FL_ONGROUND | FL_DUCKING)))
distance = 99999.0f; // just some crazy value
2015-06-11 16:53:21 +03:00
// enemy is within a good throwing distance ?
if (distance > (grenadeToThrow == WEAPON_SMOKE ? 400 : 600) && distance <= 1000)
{
// care about different types of grenades
if (grenadeToThrow == WEAPON_EXPLOSIVE)
{
bool allowThrowing = true;
// check for teammates
if (GetNearbyFriendsNearPosition (m_lastEnemy->v.origin, 256.0f) > 0)
2015-06-11 16:53:21 +03:00
allowThrowing = false;
if (allowThrowing && m_seeEnemyTime + 2.0 < GetWorldTime ())
{
const Vector &enemyPredict = ((m_lastEnemy->v.velocity * 0.5f).Get2D () + m_lastEnemy->v.origin);
2015-06-11 16:53:21 +03:00
float searchRadius = m_lastEnemy->v.velocity.GetLength2D ();
// check the search radius
if (searchRadius < 128.0f)
searchRadius = 128.0f;
2015-06-11 16:53:21 +03:00
Array <int> predictedPoints;
2015-06-11 16:53:21 +03:00
// search waypoints
waypoints.FindInRadius (predictedPoints, searchRadius, enemyPredict, 5);
2015-06-11 16:53:21 +03:00
FOR_EACH_AE (predictedPoints, it)
2015-06-11 16:53:21 +03:00
{
allowThrowing = true;
// check the throwing
m_throw = waypoints.GetPath (predictedPoints[it])->origin;
2015-06-11 16:53:21 +03:00
Vector src = CheckThrow (EyePosition (), m_throw);
if (src.GetLengthSquared () < 100.0f)
2015-06-11 16:53:21 +03:00
src = CheckToss (EyePosition (), m_throw);
if (src.IsZero ())
2015-06-11 16:53:21 +03:00
allowThrowing = false;
else
break;
}
}
// start explosive grenade throwing?
if (allowThrowing)
m_states |= STATE_THROW_HE;
else
m_states &= ~STATE_THROW_HE;
}
else if (grenadeToThrow == WEAPON_SMOKE)
{
// start smoke grenade throwing?
if ((m_states & STATE_SEEING_ENEMY) && GetShootingConeDeviation (m_enemy, &pev->origin) >= 0.70f && m_seeEnemyTime + 2.0f < GetWorldTime ())
2015-06-11 16:53:21 +03:00
m_states &= ~STATE_THROW_SG;
else
m_states |= STATE_THROW_SG;
}
}
}
else if (IsAlive (m_lastEnemy) && grenadeToThrow == WEAPON_FLASHBANG && (m_lastEnemy->v.origin - pev->origin).GetLength () < 800.0f && !(m_aimFlags & AIM_ENEMY) && Random.Long (0, 100) < 50)
2015-06-11 16:53:21 +03:00
{
bool allowThrowing = true;
Array <int> inRadius;
waypoints.FindInRadius (inRadius, 256.0f, m_lastEnemy->v.origin + (m_lastEnemy->v.velocity * 0.5f).Get2D ());
2015-06-11 16:53:21 +03:00
FOR_EACH_AE (inRadius, i)
2015-06-11 16:53:21 +03:00
{
2015-07-24 15:10:51 +03:00
Path *path = waypoints.GetPath (i);
2015-06-11 16:53:21 +03:00
int friendCount = GetNearbyFriendsNearPosition (path->origin, 256.0f);
if (friendCount != 0 || !(m_difficulty == 4 && friendCount != 0))
2015-06-11 16:53:21 +03:00
continue;
m_throw = path->origin;
Vector src = CheckThrow (EyePosition (), m_throw);
if (src.GetLengthSquared () < 100.0f)
2015-06-11 16:53:21 +03:00
src = CheckToss (EyePosition (), m_throw);
if (src.IsZero ())
2015-06-11 16:53:21 +03:00
continue;
allowThrowing = true;
break;
}
// start concussion grenade throwing?
if (allowThrowing && m_seeEnemyTime + 2.0f < GetWorldTime ())
2015-06-11 16:53:21 +03:00
m_states |= STATE_THROW_FB;
else
m_states &= ~STATE_THROW_FB;
}
if (m_states & STATE_THROW_HE)
PushTask (TASK_THROWHEGRENADE, TASKPRI_THROWGRENADE, -1, GetWorldTime () + 3.0f, false);
2015-06-11 16:53:21 +03:00
else if (m_states & STATE_THROW_FB)
PushTask (TASK_THROWFLASHBANG, TASKPRI_THROWGRENADE, -1, GetWorldTime () + 3.0f, false);
2015-06-11 16:53:21 +03:00
else if (m_states & STATE_THROW_SG)
PushTask (TASK_THROWSMOKE, TASKPRI_THROWGRENADE, -1, GetWorldTime () + 3.0f, false);
2015-06-11 16:53:21 +03:00
// delay next grenade throw
if (m_states & (STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG))
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
}
2014-07-30 14:17:46 +04:00
void Bot::AvoidGrenades (void)
{
// checks if bot 'sees' a grenade, and avoid it
2015-07-24 15:10:51 +03:00
if (!bots.HasActiveGrenades ())
2015-06-11 00:18:49 +03:00
return;
2014-07-30 14:17:46 +04:00
// check if old pointers to grenade is invalid
2015-06-11 14:19:52 +03:00
if (IsEntityNull (m_avoidGrenade))
2014-07-30 14:17:46 +04:00
{
m_avoidGrenade = NULL;
m_needAvoidGrenade = 0;
}
2015-06-11 14:19:52 +03:00
else if ((m_avoidGrenade->v.flags & FL_ONGROUND) || (m_avoidGrenade->v.effects & EF_NODRAW))
2014-07-30 14:17:46 +04:00
{
m_avoidGrenade = NULL;
m_needAvoidGrenade = 0;
}
2015-07-24 15:10:51 +03:00
Array <entity_t> activeGrenades = bots.GetActiveGrenades ();
2014-07-30 14:17:46 +04:00
// find all grenades on the map
FOR_EACH_AE (activeGrenades, it)
2014-07-30 14:17:46 +04:00
{
2015-06-11 14:19:52 +03:00
edict_t *ent = activeGrenades[it];
2014-07-30 14:17:46 +04:00
if (ent->v.effects & EF_NODRAW)
continue;
2014-07-30 14:17:46 +04:00
// check if visible to the bot
if (!EntityIsVisible (ent->v.origin) && InFieldOfView (ent->v.origin - EyePosition ()) > pev->fov * 0.5f)
2014-07-30 14:17:46 +04:00
continue;
2015-07-11 19:54:46 +03:00
if (m_turnAwayFromFlashbang < GetWorldTime () && m_personality == PERSONALITY_RUSHER && m_difficulty == 4 && strcmp (STRING (ent->v.model) + 9, "flashbang.mdl") == 0)
2014-07-30 14:17:46 +04:00
{
2015-06-11 14:19:52 +03:00
// don't look at flash bang
2015-06-04 11:52:48 +03:00
if (!(m_states & STATE_SEEING_ENEMY))
2014-07-30 14:17:46 +04:00
{
2015-07-11 19:54:46 +03:00
pev->v_angle.y = AngleNormalize ((GetEntityOrigin (ent) - EyePosition ()).ToAngles ().y + 180.0f);
2014-07-30 14:17:46 +04:00
m_canChooseAimDirection = false;
2015-07-11 19:54:46 +03:00
m_turnAwayFromFlashbang = GetWorldTime () + Random.Float (1.0f, 2.0f);
2014-07-30 14:17:46 +04:00
}
}
2015-06-04 11:52:48 +03:00
else if (strcmp (STRING (ent->v.model) + 9, "hegrenade.mdl") == 0)
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_avoidGrenade))
2014-07-30 14:17:46 +04:00
return;
2014-08-06 00:03:50 +04:00
if (GetTeam (ent->v.owner) == m_team && ent->v.owner != GetEntity ())
2014-07-30 14:17:46 +04:00
return;
if ((ent->v.flags & FL_ONGROUND) == 0)
{
float distance = (ent->v.origin - pev->origin).GetLength ();
float distanceMoved = ((ent->v.origin + ent->v.velocity * m_frameInterval) - pev->origin).GetLength ();
if (distanceMoved < distance && distance < 500.0f)
2014-07-30 14:17:46 +04:00
{
MakeVectors (pev->v_angle);
2015-06-11 14:19:52 +03:00
const Vector &dirToPoint = (pev->origin - ent->v.origin).Normalize2D ();
const Vector &rightSide = g_pGlobals->v_right.Normalize2D ();
2014-07-30 14:17:46 +04:00
if ((dirToPoint | rightSide) > 0.0f)
2014-07-30 14:17:46 +04:00
m_needAvoidGrenade = -1;
else
m_needAvoidGrenade = 1;
m_avoidGrenade = ent;
}
}
}
}
}
bool Bot::IsBehindSmokeClouds (edict_t *ent)
{
2015-07-24 15:10:51 +03:00
if (!bots.HasActiveGrenades ())
2015-06-11 00:18:49 +03:00
return false;
2015-06-11 14:19:52 +03:00
const Vector &betweenUs = (ent->v.origin - pev->origin).Normalize ();
2015-07-24 15:10:51 +03:00
Array <entity_t> activeGrenades = bots.GetActiveGrenades ();
2014-07-30 14:17:46 +04:00
2015-06-11 14:19:52 +03:00
// find all grenades on the map
FOR_EACH_AE (activeGrenades, it)
2014-07-30 14:17:46 +04:00
{
edict_t *grenade = activeGrenades[it];
if (grenade->v.effects & EF_NODRAW)
continue;
2015-06-11 14:19:52 +03:00
2014-07-30 14:17:46 +04:00
// if grenade is invisible don't care for it
if (!(grenade->v.flags & (FL_ONGROUND | FL_PARTIALGROUND)) || strcmp (STRING (grenade->v.model) + 9, "smokegrenade.mdl"))
2014-07-30 14:17:46 +04:00
continue;
// check if visible to the bot
if (!EntityIsVisible (ent->v.origin) && InFieldOfView (ent->v.origin - EyePosition ()) > pev->fov / 3.0f)
2014-07-30 14:17:46 +04:00
continue;
const Vector &entityOrigin = GetEntityOrigin (grenade);
2015-06-11 14:19:52 +03:00
const Vector &betweenNade = (entityOrigin - pev->origin).Normalize ();
const Vector &betweenResult = ((betweenNade.Get2D () * 150.0f + entityOrigin) - pev->origin).Normalize ();
2014-07-30 14:17:46 +04:00
if ((betweenNade | betweenUs) > (betweenNade | betweenResult))
return true;
}
return false;
}
int Bot::GetBestWeaponCarried (void)
{
// this function returns the best weapon of this bot (based on personality prefs)
int *ptr = g_weaponPrefs[m_personality];
int weaponIndex = 0;
int weapons = pev->weapons;
WeaponSelect *weaponTab = &g_weaponSelect[0];
// take the shield in account
if (HasShield ())
weapons |= (1 << WEAPON_SHIELD);
for (int i = 0; i < NUM_WEAPONS; i++)
{
if (weapons & (1 << weaponTab[*ptr].id))
weaponIndex = i;
ptr++;
}
return weaponIndex;
}
int Bot::GetBestSecondaryWeaponCarried (void)
{
// this function returns the best secondary weapon of this bot (based on personality prefs)
int *ptr = g_weaponPrefs[m_personality];
int weaponIndex = 0;
int weapons = pev->weapons;
// take the shield in account
if (HasShield ())
weapons |= (1 << WEAPON_SHIELD);
WeaponSelect *weaponTab = &g_weaponSelect[0];
for (int i = 0; i < NUM_WEAPONS; i++)
{
int id = weaponTab[*ptr].id;
if ((weapons & (1 << weaponTab[*ptr].id)) && (id == WEAPON_USP || id == WEAPON_GLOCK || id == WEAPON_DEAGLE || id == WEAPON_P228 || id == WEAPON_ELITE || id == WEAPON_FIVESEVEN))
{
weaponIndex = i;
break;
}
ptr++;
}
return weaponIndex;
}
bool Bot::RateGroundWeapon (edict_t *ent)
{
// this function compares weapons on the ground to the one the bot is using
int hasWeapon = 0;
int groundIndex = 0;
int *ptr = g_weaponPrefs[m_personality];
WeaponSelect *weaponTab = &g_weaponSelect[0];
for (int i = 0; i < NUM_WEAPONS; i++)
{
if (strcmp (weaponTab[*ptr].modelName, STRING (ent->v.model) + 9) == 0)
{
groundIndex = i;
break;
}
ptr++;
}
if (groundIndex < 7)
hasWeapon = GetBestSecondaryWeaponCarried ();
else
hasWeapon = GetBestWeaponCarried ();
return groundIndex > hasWeapon;
}
void Bot::VerifyBreakable (edict_t *touch)
{
2015-07-17 19:23:31 +03:00
if (!IsShootableBreakable (touch))
return;
m_breakableEntity = FindBreakable ();
if (IsEntityNull (m_breakableEntity))
return;
m_campButtons = pev->button & IN_DUCK;
PushTask (TASK_SHOOTBREAKABLE, TASKPRI_SHOOTBREAKABLE, -1, 0.0f, false);
}
2014-07-30 14:17:46 +04:00
edict_t *Bot::FindBreakable (void)
{
// this function checks if bot is blocked by a shoot able breakable in his moving direction
TraceResult tr;
TraceLine (pev->origin, pev->origin + (m_destOrigin - pev->origin).Normalize () * 64.0f, false, false, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
if (tr.flFraction != 1.0f)
2014-07-30 14:17:46 +04:00
{
edict_t *ent = tr.pHit;
// check if this isn't a triggered (bomb) breakable and if it takes damage. if true, shoot the crap!
if (IsShootableBreakable (ent))
{
m_breakable = tr.vecEndPos;
return ent;
}
}
TraceLine (EyePosition (), EyePosition () + (m_destOrigin - EyePosition ()).Normalize () * 64.0f, false, false, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
if (tr.flFraction != 1.0f)
2014-07-30 14:17:46 +04:00
{
edict_t *ent = tr.pHit;
if (IsShootableBreakable (ent))
{
m_breakable = tr.vecEndPos;
return ent;
}
}
m_breakableEntity = NULL;
m_breakable.Zero ();
2014-07-30 14:17:46 +04:00
return NULL;
}
2015-06-04 11:52:48 +03:00
void Bot::SetIdealReactionTimes (bool actual)
{
float min = 0.0f;
float max = 0.01f;
switch (m_difficulty)
{
case 0:
min = 0.8f; max = 1.0f;
break;
case 1:
min = 0.4f; max = 0.6f;
break;
case 2:
min = 0.2f; max = 0.4f;
break;
case 3:
min = 0.0f; max = 0.1f;
break;
case 4:
default:
min = 0.0f;max = 0.01f;
break;
}
if (actual)
{
m_idealReactionTime = min;
m_actualReactionTime = min;
return;
}
m_idealReactionTime = Random.Float (min, max);
2015-06-04 11:52:48 +03:00
}
2014-07-30 14:17:46 +04:00
void Bot::FindItem (void)
{
// this function finds Items to collect or use in the near of a bot
// don't try to pickup anything while on ladder or trying to escape from bomb...
if (IsOnLadder () || GetTaskId () == TASK_ESCAPEFROMBOMB || yb_jasonmode.GetBool ())
2014-07-30 14:17:46 +04:00
{
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
return;
}
edict_t *ent = NULL, *pickupItem = NULL;
Bot *bot = NULL;
float distance, minDistance = 341.0f;
2014-07-30 14:17:46 +04:00
2015-06-11 14:19:52 +03:00
const float searchRadius = 340.0f;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_pickupItem))
2014-07-30 14:17:46 +04:00
{
bool itemExists = false;
pickupItem = m_pickupItem;
2015-06-11 14:19:52 +03:00
while (!IsEntityNull (ent = FIND_ENTITY_IN_SPHERE (ent, pev->origin, searchRadius)))
2014-07-30 14:17:46 +04:00
{
if ((ent->v.effects & EF_NODRAW) || IsValidPlayer (ent->v.owner))
2015-06-11 14:19:52 +03:00
continue; // someone owns this weapon or it hasn't re spawned yet
2014-07-30 14:17:46 +04:00
if (ent == pickupItem)
{
if (ItemIsVisible (GetEntityOrigin (ent), const_cast <char *> (STRING (ent->v.classname))))
itemExists = true;
2015-06-11 14:19:52 +03:00
2014-07-30 14:17:46 +04:00
break;
}
}
if (itemExists)
return;
2015-06-11 14:19:52 +03:00
2014-07-30 14:17:46 +04:00
else
{
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
}
}
ent = NULL;
pickupItem = NULL;
PickupType pickupType = PICKUP_NONE;
Vector pickupOrigin;
Vector entityOrigin;
2014-07-30 14:17:46 +04:00
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
2015-06-11 14:19:52 +03:00
while (!IsEntityNull (ent = FIND_ENTITY_IN_SPHERE (ent, pev->origin, searchRadius)))
2014-07-30 14:17:46 +04:00
{
bool allowPickup = false; // assume can't use it until known otherwise
2014-07-30 14:17:46 +04:00
if ((ent->v.effects & EF_NODRAW) || ent == m_itemIgnore)
continue; // someone owns this weapon or it hasn't respawned yet
entityOrigin = GetEntityOrigin (ent);
// check if line of sight to object is not blocked (i.e. visible)
if (ItemIsVisible (entityOrigin, const_cast <char *> (STRING (ent->v.classname))))
{
if (strncmp ("hostage_entity", STRING (ent->v.classname), 14) == 0)
{
allowPickup = true;
pickupType = PICKUP_HOSTAGE;
}
else if (strncmp ("weaponbox", STRING (ent->v.classname), 9) == 0 && strcmp (STRING (ent->v.model) + 9, "backpack.mdl") == 0)
{
allowPickup = true;
pickupType = PICKUP_DROPPED_C4;
}
else if ((strncmp ("weaponbox", STRING (ent->v.classname), 9) == 0 || strncmp ("armoury_entity", STRING (ent->v.classname), 14) == 0 || strncmp ("csdm", STRING (ent->v.classname), 4) == 0) && !m_isUsingGrenade)
{
allowPickup = true;
pickupType = PICKUP_WEAPON;
}
else if (strncmp ("weapon_shield", STRING (ent->v.classname), 13) == 0 && !m_isUsingGrenade)
{
allowPickup = true;
pickupType = PICKUP_SHIELD;
}
2014-08-06 00:03:50 +04:00
else if (strncmp ("item_thighpack", STRING (ent->v.classname), 14) == 0 && m_team == TEAM_CF && !m_hasDefuser)
2014-07-30 14:17:46 +04:00
{
allowPickup = true;
pickupType = PICKUP_DEFUSEKIT;
}
else if (strncmp ("grenade", STRING (ent->v.classname), 7) == 0 && strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0)
{
allowPickup = true;
pickupType = PICKUP_PLANTED_C4;
}
}
if (allowPickup) // if the bot found something it can pickup...
{
distance = (entityOrigin - pev->origin).GetLength ();
// see if it's the closest item so far...
if (distance < minDistance)
{
if (pickupType == PICKUP_WEAPON) // found weapon on ground?
{
int weaponCarried = GetBestWeaponCarried ();
int secondaryWeaponCarried = GetBestSecondaryWeaponCarried ();
if (secondaryWeaponCarried < 7 && (m_ammo[g_weaponSelect[secondaryWeaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[secondaryWeaponCarried].id].ammo1Max) && strcmp (STRING (ent->v.model) + 9, "w_357ammobox.mdl") == 0)
allowPickup = false;
else if (!m_isVIP && weaponCarried >= 7 && (m_ammo[g_weaponSelect[weaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[weaponCarried].id].ammo1Max) && strncmp (STRING (ent->v.model) + 9, "w_", 2) == 0)
{
if (strcmp (STRING (ent->v.model) + 9, "w_9mmarclip.mdl") == 0 && !(weaponCarried == WEAPON_FAMAS || weaponCarried == WEAPON_AK47 || weaponCarried == WEAPON_M4A1 || weaponCarried == WEAPON_GALIL || weaponCarried == WEAPON_AUG || weaponCarried == WEAPON_SG552))
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "w_shotbox.mdl") == 0 && !(weaponCarried == WEAPON_M3 || weaponCarried == WEAPON_XM1014))
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "w_9mmclip.mdl") == 0 && !(weaponCarried == WEAPON_MP5 || weaponCarried == WEAPON_TMP || weaponCarried == WEAPON_P90 || weaponCarried == WEAPON_MAC10 || weaponCarried == WEAPON_UMP45))
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "w_crossbow_clip.mdl") == 0 && !(weaponCarried == WEAPON_AWP || weaponCarried == WEAPON_G3SG1 || weaponCarried == WEAPON_SCOUT || weaponCarried == WEAPON_SG550))
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "w_chainammo.mdl") == 0 && weaponCarried == WEAPON_M249)
allowPickup = false;
}
else if (m_isVIP || !RateGroundWeapon (ent))
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "medkit.mdl") == 0 && pev->health >= 100.0f)
2014-07-30 14:17:46 +04:00
allowPickup = false;
else if ((strcmp (STRING (ent->v.model) + 9, "kevlar.mdl") == 0 || strcmp (STRING (ent->v.model) + 9, "battery.mdl") == 0) && pev->armorvalue >= 100.0f) // armor vest
2014-07-30 14:17:46 +04:00
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "flashbang.mdl") == 0 && (pev->weapons & (1 << WEAPON_FLASHBANG))) // concussion grenade
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "hegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_EXPLOSIVE))) // explosive grenade
allowPickup = false;
else if (strcmp (STRING (ent->v.model) + 9, "smokegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_SMOKE))) // smoke grenade
allowPickup = false;
}
else if (pickupType == PICKUP_SHIELD) // found a shield on ground?
{
if ((pev->weapons & (1 << WEAPON_ELITE)) || HasShield () || m_isVIP || (HasPrimaryWeapon () && !RateGroundWeapon (ent)))
allowPickup = false;
}
2014-08-06 00:03:50 +04:00
else if (m_team == TEAM_TF) // terrorist team specific
2014-07-30 14:17:46 +04:00
{
if (pickupType == PICKUP_DROPPED_C4)
{
allowPickup = true;
2015-06-11 14:19:52 +03:00
m_destOrigin = entityOrigin; // ensure we reached dropped bomb
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_FoundC4); // play info about that
DeleteSearchNodes ();
}
else if (pickupType == PICKUP_HOSTAGE)
{
m_itemIgnore = ent;
allowPickup = false;
if (!m_defendHostage && m_difficulty >= 3 && Random.Long (0, 100) < 30 && m_timeCamping + 15.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
int index = FindDefendWaypoint (entityOrigin);
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (30.0f, 60.0f), true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (3.0f, 6.0f), true); // push move command
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2014-07-30 14:17:46 +04:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
2015-06-04 11:52:48 +03:00
m_defendHostage = true;
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_GoingToGuardHostages); // play info about that
return;
}
}
else if (pickupType == PICKUP_PLANTED_C4)
{
allowPickup = false;
2015-07-22 23:04:43 +03:00
if (!m_defendedBomb)
2014-07-30 14:17:46 +04:00
{
m_defendedBomb = true;
int index = FindDefendWaypoint (entityOrigin);
2015-07-24 15:10:51 +03:00
Path *path = waypoints.GetPath (index);
2014-07-30 14:17:46 +04:00
float bombTimer = mp_c4timer.GetFloat ();
float timeMidBlowup = g_timeBombPlanted + (bombTimer * 0.5f + bombTimer * 0.25f) - waypoints.GetTravelTime (pev->maxspeed, pev->origin, path->origin);
2014-07-30 14:17:46 +04:00
if (timeMidBlowup > GetWorldTime ())
{
RemoveCertainTask (TASK_MOVETOPOSITION); // remove any move tasks
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, timeMidBlowup, true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeMidBlowup, true); // push move command
2014-07-30 14:17:46 +04:00
if (path->vis.crouch <= path->vis.stand)
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
if (Random.Long (0, 100) < 90)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_DefendingBombSite);
}
else
RadioMessage (Radio_ShesGonnaBlow); // issue an additional radio message
}
}
}
2014-08-06 00:03:50 +04:00
else if (m_team == TEAM_CF)
2014-07-30 14:17:46 +04:00
{
if (pickupType == PICKUP_HOSTAGE)
{
if (IsEntityNull (ent) || ent->v.health <= 0)
2014-07-30 14:17:46 +04:00
allowPickup = false; // never pickup dead hostage
else for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
if ((bot = bots.GetBot (i)) != NULL && IsAlive (bot->GetEntity ()))
2014-07-30 14:17:46 +04:00
{
for (int j = 0; j < MAX_HOSTAGES; j++)
{
if (bot->m_hostages[j] == ent)
{
allowPickup = false;
break;
}
}
}
}
}
else if (pickupType == PICKUP_PLANTED_C4 && !OutOfBombTimer ())
{
if (m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY))
{
allowPickup = false;
return;
}
if (Random.Long (0, 100) < 90)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_FoundBombPlace);
2015-07-24 15:10:51 +03:00
allowPickup = !IsBombDefusing (waypoints.GetBombPosition ()) || m_hasProgressBar;
2014-07-30 14:17:46 +04:00
pickupType = PICKUP_PLANTED_C4;
if (!m_defendedBomb && !allowPickup)
{
m_defendedBomb = true;
int index = FindDefendWaypoint (entityOrigin);
2015-07-24 15:10:51 +03:00
Path *path = waypoints.GetPath (index);
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
float timeToExplode = g_timeBombPlanted + mp_c4timer.GetFloat () - waypoints.GetTravelTime (pev->maxspeed, pev->origin, path->origin);
2014-07-30 14:17:46 +04:00
RemoveCertainTask (TASK_MOVETOPOSITION); // remove any move tasks
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, timeToExplode, true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeToExplode, true); // push move command
2014-07-30 14:17:46 +04:00
if (path->vis.crouch <= path->vis.stand)
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
if (Random.Long (0, 100) < 90)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_DefendingBombSite);
}
}
else if (pickupType == PICKUP_DROPPED_C4)
{
m_itemIgnore = ent;
allowPickup = false;
if (!m_defendedBomb && m_difficulty >= 2 && Random.Long (0, 100) < 80)
2014-07-30 14:17:46 +04:00
{
int index = FindDefendWaypoint (entityOrigin);
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (30.0f, 70.0f), true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (10.0f, 30.0f), true); // push move command
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2014-07-30 14:17:46 +04:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
2015-06-04 11:52:48 +03:00
m_defendedBomb = true;
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_GoingToGuardDoppedBomb); // play info about that
return;
}
}
}
// if condition valid
if (allowPickup)
{
minDistance = distance; // update the minimum distance
pickupOrigin = entityOrigin; // remember location of entity
pickupItem = ent; // remember this entity
m_pickupType = pickupType;
}
else
pickupType = PICKUP_NONE;
}
}
} // end of the while loop
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (pickupItem))
2014-07-30 14:17:46 +04:00
{
for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
if ((bot = bots.GetBot (i)) != NULL && IsAlive (bot->GetEntity ()) && bot->m_pickupItem == pickupItem)
2014-07-30 14:17:46 +04:00
{
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
return;
}
}
2015-06-20 11:45:59 +03:00
if (pickupOrigin.z > EyePosition ().z + (m_pickupType == PICKUP_HOSTAGE ? 40.0f : 15.0f) || IsDeadlyDrop (pickupOrigin)) // check if item is too high to reach, check if getting the item would hurt bot
2014-07-30 14:17:46 +04:00
{
2015-06-20 11:45:59 +03:00
m_itemIgnore = m_pickupItem;
2014-07-30 14:17:46 +04:00
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
return;
}
m_pickupItem = pickupItem; // save pointer of picking up entity
}
}
void Bot::GetCampDirection (Vector *dest)
{
// this function check if view on last enemy position is blocked - replace with better vector then
// mostly used for getting a good camping direction vector if not camping on a camp waypoint
TraceResult tr;
2015-06-11 14:19:52 +03:00
const Vector &src = EyePosition ();
2014-07-30 14:17:46 +04:00
TraceLine (src, *dest, true, GetEntity (), &tr);
// check if the trace hit something...
if (tr.flFraction < 1.0f)
2014-07-30 14:17:46 +04:00
{
float length = (tr.vecEndPos - src).GetLengthSquared ();
if (length > 10000.0f)
2014-07-30 14:17:46 +04:00
return;
float minDistance = 99999.0f;
float maxDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
int enemyIndex = -1, tempIndex = -1;
// find nearest waypoint to bot and position
for (int i = 0; i < g_numWaypoints; i++)
{
2015-07-24 15:10:51 +03:00
float distance = (waypoints.GetPath (i)->origin - pev->origin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
if (distance < minDistance)
{
minDistance = distance;
tempIndex = i;
}
2015-07-24 15:10:51 +03:00
distance = (waypoints.GetPath (i)->origin - *dest).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
if (distance < maxDistance)
{
maxDistance = distance;
enemyIndex = i;
}
}
if (tempIndex == -1 || enemyIndex == -1)
return;
minDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
int lookAtWaypoint = -1;
2015-07-24 15:10:51 +03:00
Path *path = waypoints.GetPath (tempIndex);
2014-07-30 14:17:46 +04:00
for (int i = 0; i < MAX_PATH_INDEX; i++)
{
if (path->index[i] == -1)
continue;
float distance = static_cast <float> (waypoints.GetPathDistance (path->index[i], enemyIndex));
2014-07-30 14:17:46 +04:00
if (distance < minDistance)
{
minDistance = distance;
lookAtWaypoint = path->index[i];
}
}
if (lookAtWaypoint != -1 && lookAtWaypoint < g_numWaypoints)
2015-07-24 15:10:51 +03:00
*dest = waypoints.GetPath (lookAtWaypoint)->origin;
2014-07-30 14:17:46 +04:00
}
}
void Bot::SwitchChatterIcon (bool show)
{
// this function depending on show boolen, shows/remove chatter, icon, on the head of bot.
2015-06-04 11:52:48 +03:00
if (g_gameVersion == CSV_OLD || yb_communication_type.GetInt () != 2)
2014-07-30 14:17:46 +04:00
return;
for (int i = 0; i < GetMaxClients (); i++)
{
if (!(g_clients[i].flags & CF_USED) || (g_clients[i].ent->v.flags & FL_FAKECLIENT) || g_clients[i].team != m_team)
2014-07-30 14:17:46 +04:00
continue;
2015-07-24 14:39:11 +03:00
MESSAGE_BEGIN (MSG_ONE, netmsg.GetId (NETMSG_BOTVOICE), NULL, g_clients[i].ent); // begin message
2014-07-30 14:17:46 +04:00
WRITE_BYTE (show); // switch on/off
WRITE_BYTE (GetIndex ());
MESSAGE_END ();
}
}
void Bot::InstantChatterMessage (int type)
{
// this function sends instant chatter messages.
if (yb_communication_type.GetInt () != 2 || g_chatterFactory[type].IsEmpty () || g_gameVersion == CSV_OLD || !g_sendAudioFinished)
return;
2014-08-06 00:03:50 +04:00
if (m_notKilled)
2014-07-30 14:17:46 +04:00
SwitchChatterIcon (true);
static float reportTime = GetWorldTime ();
// delay only reportteam
if (type == Radio_ReportTeam)
{
if (reportTime >= GetWorldTime ())
return;
reportTime = GetWorldTime () + Random.Float (30.0f, 80.0f);
2014-07-30 14:17:46 +04:00
}
String defaultSound = g_chatterFactory[type].GetRandomElement ().name;
String painSound = g_chatterFactory[Chatter_DiePain].GetRandomElement ().name;
for (int i = 0; i < GetMaxClients (); i++)
{
2014-09-17 20:36:42 +04:00
edict_t *ent = EntityOfIndex (i);
2014-07-30 14:17:46 +04:00
2014-08-06 00:03:50 +04:00
if (!IsValidPlayer (ent) || IsValidBot (ent) || GetTeam (ent) != m_team)
2014-07-30 14:17:46 +04:00
continue;
g_sendAudioFinished = false;
2015-07-24 14:39:11 +03:00
MESSAGE_BEGIN (MSG_ONE, netmsg.GetId (NETMSG_SENDAUDIO), NULL, ent); // begin message
2014-07-30 14:17:46 +04:00
WRITE_BYTE (GetIndex ());
if (pev->deadflag & DEAD_DYING)
2015-06-04 11:52:48 +03:00
WRITE_STRING (FormatBuffer ("%s/%s.wav", yb_chatter_path.GetString (), painSound.GetBuffer ()));
2014-07-30 14:17:46 +04:00
else if (!(pev->deadflag & DEAD_DEAD))
2015-06-04 11:52:48 +03:00
WRITE_STRING (FormatBuffer ("%s/%s.wav", yb_chatter_path.GetString (), defaultSound.GetBuffer ()));
2014-07-30 14:17:46 +04:00
WRITE_SHORT (m_voicePitch);
MESSAGE_END ();
g_sendAudioFinished = true;
}
}
void Bot::RadioMessage (int message)
{
// this function inserts the radio message into the message queue
if (yb_communication_type.GetInt () == 0 || m_numFriendsLeft == 0)
2014-07-30 14:17:46 +04:00
return;
if (g_chatterFactory[message].IsEmpty () || g_gameVersion == CSV_OLD || yb_communication_type.GetInt () != 2)
g_radioInsteadVoice = true; // use radio instead voice
m_radioSelect = message;
PushMessageQueue (GSM_RADIO);
}
void Bot::ChatterMessage (int message)
{
// this function inserts the voice message into the message queue (mostly same as above)
if (g_gameVersion == CSV_OLD || yb_communication_type.GetInt () != 2 || g_chatterFactory[message].IsEmpty () || m_numFriendsLeft == 0)
2014-07-30 14:17:46 +04:00
return;
bool shouldExecute = false;
if (m_chatterTimes[message] < GetWorldTime () || m_chatterTimes[message] == 99999.0f)
2014-07-30 14:17:46 +04:00
{
if (m_chatterTimes[message] != 99999.0f)
m_chatterTimes[message] = GetWorldTime () + g_chatterFactory[message][0].repeatTime;
2014-07-30 14:17:46 +04:00
shouldExecute = true;
}
if (!shouldExecute)
return;
m_radioSelect = message;
PushMessageQueue (GSM_RADIO);
}
void Bot::CheckMessageQueue (void)
{
// this function checks and executes pending messages
// no new message?
if (m_actMessageIndex == m_pushMessageIndex)
return;
// get message from stack
int currentQueueMessage = GetMessageQueue ();
// nothing to do?
if (currentQueueMessage == GSM_IDLE || (currentQueueMessage == GSM_RADIO && yb_csdm_mode.GetInt () == 2))
return;
switch (currentQueueMessage)
{
case GSM_BUY_STUFF: // general buy message
// buy weapon
if (m_nextBuyTime > GetWorldTime ())
{
// keep sending message
PushMessageQueue (GSM_BUY_STUFF);
return;
}
if (!m_inBuyZone || yb_csdm_mode.GetBool ())
{
m_buyPending = true;
m_buyingFinished = true;
break;
}
m_buyPending = false;
2015-07-11 19:54:46 +03:00
m_nextBuyTime = GetWorldTime () + Random.Float (0.5f, 1.3f);
2014-07-30 14:17:46 +04:00
// if bot buying is off then no need to buy
if (!yb_botbuy.GetBool ())
m_buyState = 6;
// if fun-mode no need to buy
if (yb_jasonmode.GetBool ())
{
m_buyState = 6;
SelectWeaponByName ("weapon_knife");
}
// prevent vip from buying
if (IsPlayerVIP (GetEntity ()))
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
m_isVIP = true;
m_buyState = 6;
m_pathType = 0;
2014-07-30 14:17:46 +04:00
}
// prevent terrorists from buying on es maps
2014-08-06 00:03:50 +04:00
if ((g_mapType & MAP_ES) && m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
m_buyState = 6;
// prevent teams from buying on fun maps
if (g_mapType & (MAP_KA | MAP_FY))
{
m_buyState = 6;
if (g_mapType & MAP_KA)
yb_jasonmode.SetInt (1);
}
if (m_buyState > 5)
{
m_buyingFinished = true;
return;
}
PushMessageQueue (GSM_IDLE);
2015-06-20 11:45:59 +03:00
PurchaseWeapons ();
2014-07-30 14:17:46 +04:00
break;
case GSM_RADIO: // general radio message issued
// if last bot radio command (global) happened just a second ago, delay response
if (g_lastRadioTime[m_team] + 1.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
// if same message like previous just do a yes/no
if (m_radioSelect != Radio_Affirmative && m_radioSelect != Radio_Negative)
{
if (m_radioSelect == g_lastRadio[m_team] && g_lastRadioTime[m_team] + 1.5f > GetWorldTime ())
2014-07-30 14:17:46 +04:00
m_radioSelect = -1;
else
{
if (m_radioSelect != Radio_ReportingIn)
2014-08-06 00:03:50 +04:00
g_lastRadio[m_team] = m_radioSelect;
2014-07-30 14:17:46 +04:00
else
2014-08-06 00:03:50 +04:00
g_lastRadio[m_team] = -1;
2014-07-30 14:17:46 +04:00
for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
Bot *bot = bots.GetBot (i);
2014-07-30 14:17:46 +04:00
if (bot != NULL)
{
2014-08-06 00:03:50 +04:00
if (pev != bot->pev && GetTeam (bot->GetEntity ()) == m_team)
2014-07-30 14:17:46 +04:00
{
bot->m_radioOrder = m_radioSelect;
bot->m_radioEntity = GetEntity ();
}
}
}
}
}
if (m_radioSelect == Radio_ReportingIn)
{
switch (GetTaskId ())
{
case TASK_NORMAL:
if (GetTask ()->data != -1 && Random.Long (0, 100) < 70)
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
Path *path = waypoints.GetPath (GetTask ()->data);
2014-07-30 14:17:46 +04:00
if (path->flags & FLAG_GOAL)
{
2014-09-17 20:36:42 +04:00
if ((g_mapType & MAP_DE) && m_team == TEAM_TF && m_hasC4)
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_GoingToPlantBomb);
else
InstantChatterMessage (Chatter_Nothing);
}
else if (path->flags & FLAG_RESCUE)
InstantChatterMessage (Chatter_RescuingHostages);
else if ((path->flags & FLAG_CAMP) && Random.Long (0, 100) > 15)
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_GoingToCamp);
else
InstantChatterMessage (Chatter_HearSomething);
}
else if (Random.Long (0, 100) < 40)
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_ReportingIn);
2014-07-30 14:17:46 +04:00
break;
case TASK_MOVETOPOSITION:
InstantChatterMessage (Chatter_GoingToCamp);
break;
case TASK_CAMP:
if (Random.Long (0, 100) < 40)
2014-07-30 14:17:46 +04:00
{
2014-08-06 00:03:50 +04:00
if (g_bombPlanted && m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_GuardDroppedC4);
2014-08-06 00:03:50 +04:00
else if (m_inVIPZone && m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_GuardingVipSafety);
else
InstantChatterMessage (Chatter_Camp);
}
break;
case TASK_PLANTBOMB:
InstantChatterMessage (Chatter_PlantingC4);
break;
case TASK_DEFUSEBOMB:
InstantChatterMessage (Chatter_DefusingC4);
break;
case TASK_ATTACK:
InstantChatterMessage (Chatter_InCombat);
break;
case TASK_HIDE:
case TASK_SEEKCOVER:
InstantChatterMessage (Chatter_SeeksEnemy);
break;
default:
InstantChatterMessage (Chatter_Nothing);
break;
}
}
if (m_radioSelect != Radio_ReportingIn || g_radioInsteadVoice || yb_communication_type.GetInt () != 2 || g_chatterFactory[m_radioSelect].IsEmpty () || g_gameVersion == CSV_OLD)
2014-07-30 14:17:46 +04:00
{
if (m_radioSelect < Radio_GoGoGo)
FakeClientCommand (GetEntity (), "radio1");
else if (m_radioSelect < Radio_Affirmative)
{
m_radioSelect -= Radio_GoGoGo - 1;
FakeClientCommand (GetEntity (), "radio2");
}
else
{
m_radioSelect -= Radio_Affirmative - 1;
FakeClientCommand (GetEntity (), "radio3");
}
// select correct menu item for this radio message
FakeClientCommand (GetEntity (), "menuselect %d", m_radioSelect);
}
else if (m_radioSelect != -1 && m_radioSelect != Radio_ReportingIn)
InstantChatterMessage (m_radioSelect);
g_radioInsteadVoice = false; // reset radio to voice
2014-08-06 00:03:50 +04:00
g_lastRadioTime[m_team] = GetWorldTime (); // store last radio usage
2014-07-30 14:17:46 +04:00
}
else
PushMessageQueue (GSM_RADIO);
break;
// team independent saytext
case GSM_SAY:
SayText (m_tempStrings);
break;
// team dependent saytext
case GSM_SAY_TEAM:
TeamSayText (m_tempStrings);
break;
default:
return;
}
}
bool Bot::IsRestricted (int weaponIndex)
{
// this function checks for weapon restrictions.
if (IsNullString (yb_restricted_weapons.GetString ()))
2015-07-11 19:54:46 +03:00
return IsRestrictedAMX (weaponIndex); // no banned weapons
2014-07-30 14:17:46 +04:00
Array <String> bannedWeapons = String (yb_restricted_weapons.GetString ()).Split (';');
FOR_EACH_AE (bannedWeapons, i)
2014-07-30 14:17:46 +04:00
{
const char *banned = STRING (GetWeaponReturn (true, NULL, weaponIndex));
// check is this weapon is banned
if (strncmp (bannedWeapons[i].GetBuffer (), banned, bannedWeapons[i].GetLength ()) == 0)
return true;
}
return IsRestrictedAMX (weaponIndex);
}
bool Bot::IsRestrictedAMX (int weaponIndex)
{
// this function checks restriction set by AMX Mod, this function code is courtesy of KWo.
// check for weapon restrictions
if ((1 << weaponIndex) & (WEAPON_PRIMARY | WEAPON_SECONDARY | WEAPON_SHIELD))
{
2015-06-04 11:52:48 +03:00
const char *restrictedWeapons = CVAR_GET_STRING ("amx_restrweapons");
2014-07-30 14:17:46 +04:00
if (IsNullString (restrictedWeapons))
return false;
int indices[] = {4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11};
// find the weapon index
int index = indices[weaponIndex - 1];
// validate index range
if (index < 0 || index >= static_cast <int> (strlen (restrictedWeapons)))
return false;
return restrictedWeapons[index] != '0';
}
else // check for equipment restrictions
{
2015-06-04 11:52:48 +03:00
const char *restrictedEquipment = CVAR_GET_STRING ("amx_restrequipammo");
2014-07-30 14:17:46 +04:00
if (IsNullString (restrictedEquipment))
return false;
int indices[] = {-1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5};
// find the weapon index
int index = indices[weaponIndex - 1];
// validate index range
if (index < 0 || index >= static_cast <int> (strlen (restrictedEquipment)))
return false;
return restrictedEquipment[index] != '0';
}
}
bool Bot::IsMorePowerfulWeaponCanBeBought (void)
{
// this function determines currently owned primary weapon, and checks if bot has
// enough money to buy more powerful weapon.
// if bot is not rich enough or non-standard weapon mode enabled return false
2015-06-04 11:52:48 +03:00
if (g_weaponSelect[25].teamStandard != 1 || m_moneyAmount < 4000)
2014-07-30 14:17:46 +04:00
return false;
2015-06-04 11:52:48 +03:00
if (!IsNullString (yb_restricted_weapons.GetString ()))
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
Array <String> bannedWeapons = String (yb_restricted_weapons.GetString ()).Split (';');
// check if its banned
FOR_EACH_AE (bannedWeapons, i)
2015-06-04 11:52:48 +03:00
{
if (m_currentWeapon == GetWeaponReturn (false, bannedWeapons[i].GetBuffer ()))
return true;
}
2014-07-30 14:17:46 +04:00
}
if (m_currentWeapon == WEAPON_SCOUT && m_moneyAmount > 5000)
return true;
else if (m_currentWeapon == WEAPON_MP5 && m_moneyAmount > 6000)
return true;
else if ((m_currentWeapon == WEAPON_M3 || m_currentWeapon == WEAPON_XM1014) && m_moneyAmount > 4000)
return true;
return false;
}
int Bot::PickBestFromRandom (int *random, int count)
{
// this function taken from gina bot, it's picks best available weapon from random choice
float buyFactor = (m_moneyAmount - 3000.0f) / (16000.0f - 3000.0f) * 3.0f;
if (buyFactor < 1.0f)
buyFactor = 1.0f;
return random[static_cast <int> (static_cast <float> (count - 1) * log10f (Random.Float (1, powf (10.0f, buyFactor))) / buyFactor + 0.5f)];
}
2015-06-20 11:45:59 +03:00
void Bot::PurchaseWeapons (void)
2014-07-30 14:17:46 +04:00
{
// this function does all the work in selecting correct buy menus for most weapons/items
WeaponSelect *selectedWeapon = NULL;
2015-07-11 19:54:46 +03:00
m_nextBuyTime = GetWorldTime () + Random.Float (0.3f, 0.5f);
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
int count = 0, foundWeapons = 0;
2014-07-30 14:17:46 +04:00
int choices[NUM_WEAPONS];
// select the priority tab for this personality
int *ptr = g_weaponPrefs[m_personality] + NUM_WEAPONS;
bool isPistolMode = g_weaponSelect[25].teamStandard == -1 && g_weaponSelect[3].teamStandard == 2;
2015-07-24 15:10:51 +03:00
bool teamEcoValid = bots.EconomicsValid (m_team);
2014-07-30 14:17:46 +04:00
switch (m_buyState)
{
case 0: // if no primary weapon and bot has some money, buy a primary weapon
2015-06-04 11:52:48 +03:00
if ((!HasShield () && !HasPrimaryWeapon () && teamEcoValid) || (teamEcoValid && IsMorePowerfulWeaponCanBeBought ()))
2014-07-30 14:17:46 +04:00
{
do
{
bool ignoreWeapon = false;
ptr--;
InternalAssert (*ptr > -1);
InternalAssert (*ptr < NUM_WEAPONS);
selectedWeapon = &g_weaponSelect[*ptr];
count++;
if (selectedWeapon->buyGroup == 1)
continue;
// weapon available for every team?
2014-08-06 00:03:50 +04:00
if ((g_mapType & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team)
2014-07-30 14:17:46 +04:00
continue;
// ignore weapon if this weapon not supported by currently running cs version...
if (g_gameVersion == CSV_OLD && selectedWeapon->buySelect == -1)
continue;
// ignore weapon if this weapon is not targeted to out team....
2014-08-06 00:03:50 +04:00
if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team)
2014-07-30 14:17:46 +04:00
continue;
// ignore weapon if this weapon is restricted
if (IsRestricted (selectedWeapon->id))
continue;
2015-06-04 11:52:48 +03:00
int economicsPrice = 0;
2014-07-30 14:17:46 +04:00
// filter out weapons with bot economics (thanks for idea to nicebot project)
switch (m_personality)
{
case PERSONALITY_RUSHER:
economicsPrice = g_botBuyEconomyTable[8];
break;
case PERSONALITY_CAREFUL:
economicsPrice = g_botBuyEconomyTable[7];
break;
case PERSONALITY_NORMAL:
economicsPrice = g_botBuyEconomyTable[9];
break;
}
2014-08-06 00:03:50 +04:00
if (m_team == TEAM_CF)
2014-07-30 14:17:46 +04:00
{
switch (selectedWeapon->id)
{
case WEAPON_TMP:
case WEAPON_UMP45:
case WEAPON_P90:
case WEAPON_MP5:
if (m_moneyAmount > g_botBuyEconomyTable[1] + economicsPrice)
ignoreWeapon = true;
break;
}
if (selectedWeapon->id == WEAPON_SHIELD && m_moneyAmount > g_botBuyEconomyTable[10])
ignoreWeapon = true;
}
2014-08-06 00:03:50 +04:00
else if (m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
{
switch (selectedWeapon->id)
{
case WEAPON_UMP45:
case WEAPON_MAC10:
case WEAPON_P90:
case WEAPON_MP5:
case WEAPON_SCOUT:
if (m_moneyAmount > g_botBuyEconomyTable[2] + economicsPrice)
ignoreWeapon = true;
break;
}
}
switch (selectedWeapon->id)
{
case WEAPON_XM1014:
case WEAPON_M3:
if (m_moneyAmount < g_botBuyEconomyTable[3] || m_moneyAmount > g_botBuyEconomyTable[4])
ignoreWeapon = true;
break;
}
switch (selectedWeapon->id)
{
case WEAPON_SG550:
case WEAPON_G3SG1:
case WEAPON_AWP:
2014-07-30 14:17:46 +04:00
case WEAPON_M249:
if (m_moneyAmount < g_botBuyEconomyTable[5] || m_moneyAmount > g_botBuyEconomyTable[6])
ignoreWeapon = true;
break;
}
if (ignoreWeapon && g_weaponSelect[25].teamStandard == 1 && yb_economics_rounds.GetBool ())
continue;
int moneySave = Random.Long (900, 1100);
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (bots.GetLastWinner () == m_team)
2014-07-30 14:17:46 +04:00
moneySave = 0;
if (selectedWeapon->price <= (m_moneyAmount - moneySave))
choices[foundWeapons++] = *ptr;
} while (count < NUM_WEAPONS && foundWeapons < 4);
// found a desired weapon?
if (foundWeapons > 0)
{
int chosenWeapon;
// choose randomly from the best ones...
if (foundWeapons > 1)
chosenWeapon = PickBestFromRandom (choices, foundWeapons);
2014-07-30 14:17:46 +04:00
else
chosenWeapon = choices[foundWeapons - 1];
selectedWeapon = &g_weaponSelect[chosenWeapon];
}
else
selectedWeapon = NULL;
if (selectedWeapon != NULL)
{
FakeClientCommand (GetEntity (), "buy;menuselect %d", selectedWeapon->buyGroup);
if (g_gameVersion == CSV_OLD)
FakeClientCommand(GetEntity (), "menuselect %d", selectedWeapon->buySelect);
else // SteamCS buy menu is different from the old one
{
2014-08-06 00:03:50 +04:00
if (m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
FakeClientCommand(GetEntity (), "menuselect %d", selectedWeapon->newBuySelectT);
else
FakeClientCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectCT);
}
}
}
else if (HasPrimaryWeapon () && !HasShield ())
{
m_reloadState = RELOAD_PRIMARY;
break;
}
else if ((HasSecondaryWeapon () && !HasShield ()) || HasShield())
{
m_reloadState = RELOAD_SECONDARY;
break;
}
case 1: // if armor is damaged and bot has some money, buy some armor
if (pev->armorvalue < Random.Long (50, 80) && (isPistolMode || (teamEcoValid && HasPrimaryWeapon ())))
2014-07-30 14:17:46 +04:00
{
// if bot is rich, buy kevlar + helmet, else buy a single kevlar
if (m_moneyAmount > 1500 && !IsRestricted (WEAPON_ARMORHELM))
FakeClientCommand (GetEntity (), "buyequip;menuselect 2");
else if (!IsRestricted (WEAPON_ARMOR))
FakeClientCommand (GetEntity (), "buyequip;menuselect 1");
}
break;
case 2: // if bot has still some money, buy a better secondary weapon
if (isPistolMode || (HasPrimaryWeapon () && (pev->weapons & ((1 << WEAPON_USP) | (1 << WEAPON_GLOCK))) && m_moneyAmount > Random.Long (7500, 9000)))
2014-07-30 14:17:46 +04:00
{
do
{
ptr--;
InternalAssert (*ptr > -1);
InternalAssert (*ptr < NUM_WEAPONS);
selectedWeapon = &g_weaponSelect[*ptr];
count++;
if (selectedWeapon->buyGroup != 1)
continue;
// ignore weapon if this weapon is restricted
if (IsRestricted (selectedWeapon->id))
continue;
// weapon available for every team?
2014-08-06 00:03:50 +04:00
if ((g_mapType & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team)
2014-07-30 14:17:46 +04:00
continue;
if (g_gameVersion == CSV_OLD && selectedWeapon->buySelect == -1)
continue;
2014-08-06 00:03:50 +04:00
if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team)
2014-07-30 14:17:46 +04:00
continue;
if (selectedWeapon->price <= (m_moneyAmount - Random.Long (100, 200)))
2014-07-30 14:17:46 +04:00
choices[foundWeapons++] = *ptr;
} while (count < NUM_WEAPONS && foundWeapons < 4);
// found a desired weapon?
if (foundWeapons > 0)
{
int chosenWeapon;
// choose randomly from the best ones...
if (foundWeapons > 1)
chosenWeapon = PickBestFromRandom (choices, foundWeapons);
2014-07-30 14:17:46 +04:00
else
chosenWeapon = choices[foundWeapons - 1];
selectedWeapon = &g_weaponSelect[chosenWeapon];
}
else
selectedWeapon = NULL;
if (selectedWeapon != NULL)
{
FakeClientCommand (GetEntity (), "buy;menuselect %d", selectedWeapon->buyGroup);
if (g_gameVersion == CSV_OLD)
FakeClientCommand (GetEntity (), "menuselect %d", selectedWeapon->buySelect);
2014-07-30 14:17:46 +04:00
else // steam cs buy menu is different from old one
{
if (GetTeam (GetEntity ()) == TEAM_TF)
FakeClientCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectT);
2014-07-30 14:17:46 +04:00
else
FakeClientCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectCT);
2014-07-30 14:17:46 +04:00
}
}
}
break;
case 3: // if bot has still some money, choose if bot should buy a grenade or not
if (Random.Long (1, 100) < g_grenadeBuyPrecent[0] && m_moneyAmount >= 400 && !IsRestricted (WEAPON_EXPLOSIVE))
2014-07-30 14:17:46 +04:00
{
// buy a he grenade
FakeClientCommand (GetEntity (), "buyequip");
FakeClientCommand (GetEntity (), "menuselect 4");
}
if (Random.Long (1, 100) < g_grenadeBuyPrecent[1] && m_moneyAmount >= 300 && teamEcoValid && !IsRestricted (WEAPON_FLASHBANG))
2014-07-30 14:17:46 +04:00
{
// buy a concussion grenade, i.e., 'flashbang'
FakeClientCommand (GetEntity (), "buyequip");
FakeClientCommand (GetEntity (), "menuselect 3");
}
if (Random.Long (1, 100) < g_grenadeBuyPrecent[2] && m_moneyAmount >= 400 && teamEcoValid && !IsRestricted (WEAPON_SMOKE))
2014-07-30 14:17:46 +04:00
{
// buy a smoke grenade
FakeClientCommand (GetEntity (), "buyequip");
FakeClientCommand (GetEntity (), "menuselect 5");
}
break;
case 4: // if bot is CT and we're on a bomb map, randomly buy the defuse kit
if ((g_mapType & MAP_DE) && m_team == TEAM_CF && Random.Long (1, 100) < 80 && m_moneyAmount > 200 && !IsRestricted (WEAPON_DEFUSER))
2014-07-30 14:17:46 +04:00
{
if (g_gameVersion == CSV_OLD)
FakeClientCommand (GetEntity (), "buyequip;menuselect 6");
else
FakeClientCommand (GetEntity (), "defuser"); // use alias in SteamCS
}
break;
case 5: // buy enough primary & secondary ammo (do not check for money here)
for (int i = 0; i <= 5; i++)
FakeClientCommand (GetEntity (), "buyammo%d", Random.Long (1, 2)); // simulate human
2014-07-30 14:17:46 +04:00
// buy enough secondary ammo
if (HasPrimaryWeapon ())
FakeClientCommand (GetEntity (), "buy;menuselect 7");
// buy enough primary ammo
FakeClientCommand (GetEntity (), "buy;menuselect 6");
// try to reload secondary weapon
if (m_reloadState != RELOAD_PRIMARY)
m_reloadState = RELOAD_SECONDARY;
break;
}
m_buyState++;
PushMessageQueue (GSM_BUY_STUFF);
}
TaskItem *ClampDesire (TaskItem *first, float min, float max)
{
// this function iven some values min and max, clamp the inputs to be inside the [min, max] range.
if (first->desire < min)
first->desire = min;
else if (first->desire > max)
first->desire = max;
return first;
}
TaskItem *MaxDesire (TaskItem *first, TaskItem *second)
{
// this function returns the behavior having the higher activation level.
if (first->desire > second->desire)
return first;
return second;
}
TaskItem *SubsumeDesire (TaskItem *first, TaskItem *second)
{
// this function returns the first behavior if its activation level is anything higher than zero.
if (first->desire > 0)
return first;
return second;
}
TaskItem *ThresholdDesire (TaskItem *first, float threshold, float desire)
{
// this function returns the input behavior if it's activation level exceeds the threshold, or some default
// behavior otherwise.
if (first->desire < threshold)
first->desire = desire;
return first;
}
float HysteresisDesire (float cur, float min, float max, float old)
{
// this function clamp the inputs to be the last known value outside the [min, max] range.
if (cur <= min || cur >= max)
old = cur;
return old;
}
2015-07-17 19:23:31 +03:00
void Bot::UpdateEmotions (void)
2014-07-30 14:17:46 +04:00
{
// slowly increase/decrease dynamic emotions back to their base level
2015-07-17 19:23:31 +03:00
if (m_nextEmotionUpdate > GetWorldTime ())
return;
if (m_difficulty == 4)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_agressionLevel *= 2;
m_fearLevel *= 0.5f;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_agressionLevel > m_baseAgressionLevel)
m_agressionLevel -= 0.10f;
else
m_agressionLevel += 0.10f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_fearLevel > m_baseFearLevel)
m_fearLevel -= 0.05f;
else
m_fearLevel += 0.05f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_agressionLevel < 0.0f)
m_agressionLevel = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_nextEmotionUpdate = GetWorldTime () + 1.0f;
}
void Bot::SetConditions (void)
{
// this function carried out each frame. does all of the sensing, calculates emotions and finally sets the desired
// action after applying all of the Filters
m_aimFlags = 0;
UpdateEmotions ();
2014-07-30 14:17:46 +04:00
// does bot see an enemy?
if (LookupEnemy ())
m_states |= STATE_SEEING_ENEMY;
else
{
m_states &= ~STATE_SEEING_ENEMY;
m_enemy = NULL;
}
// did bot just kill an enemy?
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_lastVictim))
2014-07-30 14:17:46 +04:00
{
2014-08-06 00:03:50 +04:00
if (GetTeam (m_lastVictim) != m_team)
2014-07-30 14:17:46 +04:00
{
// add some aggression because we just killed somebody
2015-07-17 19:23:31 +03:00
m_agressionLevel += 0.1f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_agressionLevel > 1.0f)
m_agressionLevel = 1.0f;
2014-07-30 14:17:46 +04:00
if (Random.Long (1, 100) < 10)
2014-07-30 14:17:46 +04:00
ChatMessage (CHAT_KILLING);
if (Random.Long (1, 100) < 10)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_EnemyDown);
else
{
if ((m_lastVictim->v.weapons & (1 << WEAPON_AWP)) || (m_lastVictim->v.weapons & (1 << WEAPON_SCOUT)) || (m_lastVictim->v.weapons & (1 << WEAPON_G3SG1)) || (m_lastVictim->v.weapons & (1 << WEAPON_SG550)))
ChatterMessage (Chatter_SniperKilled);
else
{
switch (GetNearbyEnemiesNearPosition (pev->origin, 99999.0f))
2014-07-30 14:17:46 +04:00
{
case 0:
if (Random.Long (0, 100) < 50)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_NoEnemiesLeft);
else
ChatterMessage (Chatter_EnemyDown);
break;
case 1:
ChatterMessage (Chatter_OneEnemyLeft);
break;
case 2:
ChatterMessage (Chatter_TwoEnemiesLeft);
break;
case 3:
ChatterMessage (Chatter_ThreeEnemiesLeft);
break;
default:
ChatterMessage (Chatter_EnemyDown);
}
}
}
// if no more enemies found AND bomb planted, switch to knife to get to bombplace faster
if (m_team == TEAM_CF && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted)
2014-07-30 14:17:46 +04:00
{
SelectWeaponByName ("weapon_knife");
m_plantedBombWptIndex = FindPlantedBomb ();
2015-06-04 11:52:48 +03:00
if (IsPointOccupied (m_plantedBombWptIndex))
2014-07-30 14:17:46 +04:00
InstantChatterMessage (Chatter_BombSiteSecured);
}
}
else
{
ChatMessage (CHAT_TEAMKILL, true);
ChatterMessage (Chatter_TeamKill);
}
m_lastVictim = NULL;
}
// check if our current enemy is still valid
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_lastEnemy))
2014-07-30 14:17:46 +04:00
{
if (!IsAlive (m_lastEnemy) && m_shootAtDeadTime < GetWorldTime ())
{
m_lastEnemyOrigin.Zero ();
2014-07-30 14:17:46 +04:00
m_lastEnemy = NULL;
}
}
else
{
m_lastEnemyOrigin.Zero ();
2014-07-30 14:17:46 +04:00
m_lastEnemy = NULL;
}
// don't listen if seeing enemy, just checked for sounds or being blinded (because its inhuman)
if (!yb_ignore_enemies.GetBool () && m_soundUpdateTime < GetWorldTime () && m_blindTime < GetWorldTime () && m_seeEnemyTime + 1.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
ReactOnSound ();
2015-06-20 11:45:59 +03:00
m_soundUpdateTime = GetWorldTime () + 0.25f;
2014-07-30 14:17:46 +04:00
}
else if (m_heardSoundTime < GetWorldTime ())
m_states &= ~STATE_HEARING_ENEMY;
if (IsEntityNull (m_enemy) && !IsEntityNull (m_lastEnemy) && !m_lastEnemyOrigin.IsZero ())
2014-07-30 14:17:46 +04:00
{
2015-07-01 01:06:37 +03:00
m_aimFlags |= AIM_PREDICT_PATH;
2014-07-30 14:17:46 +04:00
2015-07-01 01:06:37 +03:00
if (EntityIsVisible (m_lastEnemyOrigin))
m_aimFlags |= AIM_LAST_ENEMY;
2014-07-30 14:17:46 +04:00
}
2015-06-11 16:53:21 +03:00
CheckGrenadeThrow ();
2014-07-30 14:17:46 +04:00
// check if there are items needing to be used/collected
2015-06-04 11:52:48 +03:00
if (m_itemCheckTime < GetWorldTime () || !IsEntityNull (m_pickupItem))
2014-07-30 14:17:46 +04:00
{
2015-06-20 11:45:59 +03:00
m_itemCheckTime = GetWorldTime () + 0.4f;
2014-07-30 14:17:46 +04:00
FindItem ();
}
2015-07-17 19:23:31 +03:00
ApplyTaskFilters ();
}
void Bot::ApplyTaskFilters (void)
{
// initialize & calculate the desire for all actions based on distances, emotions and other stuff
GetTask ();
2014-07-30 14:17:46 +04:00
float tempFear = m_fearLevel;
float tempAgression = m_agressionLevel;
// decrease fear if teammates near
int friendlyNum = 0;
if (!m_lastEnemyOrigin.IsZero ())
friendlyNum = GetNearbyFriendsNearPosition (pev->origin, 500.0f) - GetNearbyEnemiesNearPosition (m_lastEnemyOrigin, 500.0f);
2014-07-30 14:17:46 +04:00
if (friendlyNum > 0)
tempFear = tempFear * 0.5f;
2014-07-30 14:17:46 +04:00
// increase/decrease fear/aggression if bot uses a sniping weapon to be more careful
if (UsesSniper ())
{
tempFear = tempFear * 1.5f;
tempAgression = tempAgression * 0.5f;
2014-07-30 14:17:46 +04:00
}
// bot found some item to use?
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_pickupItem) && GetTaskId () != TASK_ESCAPEFROMBOMB)
2014-07-30 14:17:46 +04:00
{
m_states |= STATE_PICKUP_ITEM;
if (m_pickupType == PICKUP_BUTTON)
g_taskFilters[TASK_PICKUPITEM].desire = 50.0f; // always pickup button
2014-07-30 14:17:46 +04:00
else
{
float distance = (500.0f - (GetEntityOrigin (m_pickupItem) - pev->origin).GetLength ()) * 0.2f;
2014-07-30 14:17:46 +04:00
if (distance > 50.0f)
distance = 50.0f;
2014-07-30 14:17:46 +04:00
g_taskFilters[TASK_PICKUPITEM].desire = distance;
}
}
else
{
m_states &= ~STATE_PICKUP_ITEM;
g_taskFilters[TASK_PICKUPITEM].desire = 0.0f;
2014-07-30 14:17:46 +04:00
}
// calculate desire to attack
if ((m_states & STATE_SEEING_ENEMY) && ReactOnEnemy ())
2014-07-30 14:17:46 +04:00
g_taskFilters[TASK_ATTACK].desire = TASKPRI_ATTACK;
else
g_taskFilters[TASK_ATTACK].desire = 0.0f;
2014-07-30 14:17:46 +04:00
// calculate desires to seek cover or hunt
if (IsValidPlayer (m_lastEnemy) && !m_lastEnemyOrigin.IsZero ())
2014-07-30 14:17:46 +04:00
{
float distance = (m_lastEnemyOrigin - pev->origin).GetLength ();
// retreat level depends on bot health
float retreatLevel = (100.0f - (pev->health > 100.0f ? 100.0f : pev->health)) * tempFear;
2014-07-30 14:17:46 +04:00
float timeSeen = m_seeEnemyTime - GetWorldTime ();
float timeHeard = m_heardSoundTime - GetWorldTime ();
float ratio = 0.0f;
2014-07-30 14:17:46 +04:00
if (timeSeen > timeHeard)
{
timeSeen += 10.0f;
ratio = timeSeen * 0.1f;
2014-07-30 14:17:46 +04:00
}
else
{
timeHeard += 10.0f;
ratio = timeHeard * 0.1f;
2014-07-30 14:17:46 +04:00
}
if (g_bombPlanted || m_isStuck)
ratio /= 3.0f; // reduce the seek cover desire if bomb is planted
2014-07-30 14:17:46 +04:00
else if (m_isVIP || m_isReloading)
ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading
2014-07-30 14:17:46 +04:00
if (distance > 500.0f)
2014-07-30 14:17:46 +04:00
g_taskFilters[TASK_SEEKCOVER].desire = retreatLevel * ratio;
// if half of the round is over, allow hunting
// FIXME: it probably should be also team/map dependant
2015-07-24 15:10:51 +03:00
if (GetTaskId () != TASK_ESCAPEFROMBOMB && IsEntityNull (m_enemy) && g_timeRoundMid < GetWorldTime () && !m_isUsingGrenade && m_currentWaypointIndex != waypoints.FindNearest (m_lastEnemyOrigin) && m_personality != PERSONALITY_CAREFUL)
2014-07-30 14:17:46 +04:00
{
float desireLevel = 4096.0f - ((1.0f - tempAgression) * distance);
desireLevel = (100.0f * desireLevel) / 4096.0;
2014-07-30 14:17:46 +04:00
desireLevel -= retreatLevel;
if (desireLevel > 89.0f)
desireLevel = 89.0f;
2014-07-30 14:17:46 +04:00
g_taskFilters[TASK_HUNTENEMY].desire = desireLevel;
}
else
g_taskFilters[TASK_HUNTENEMY].desire = 0.0f;
2014-07-30 14:17:46 +04:00
}
else
{
g_taskFilters[TASK_SEEKCOVER].desire = 0.0f;
g_taskFilters[TASK_HUNTENEMY].desire = 0.0f;
2014-07-30 14:17:46 +04:00
}
2015-06-11 16:53:21 +03:00
// blinded behavior
2014-07-30 14:17:46 +04:00
if (m_blindTime > GetWorldTime ())
g_taskFilters[TASK_BLINDED].desire = TASKPRI_BLINDED;
else
g_taskFilters[TASK_BLINDED].desire = 0.0f;
2014-07-30 14:17:46 +04:00
// now we've initialized all the desires go through the hard work
// of filtering all actions against each other to pick the most
// rewarding one to the bot.
// FIXME: instead of going through all of the actions it might be
// better to use some kind of decision tree to sort out impossible
// actions.
// most of the values were found out by trial-and-error and a helper
// utility i wrote so there could still be some weird behaviors, it's
// hard to check them all out.
m_oldCombatDesire = HysteresisDesire (g_taskFilters[TASK_ATTACK].desire, 40.0f, 90.0f, m_oldCombatDesire);
2014-07-30 14:17:46 +04:00
g_taskFilters[TASK_ATTACK].desire = m_oldCombatDesire;
TaskItem *taskOffensive = &g_taskFilters[TASK_ATTACK];
TaskItem *taskPickup = &g_taskFilters[TASK_PICKUPITEM];
// calc survive (cover/hide)
TaskItem *taskSurvive = ThresholdDesire (&g_taskFilters[TASK_SEEKCOVER], 40.0f, 0.0f);
2014-07-30 14:17:46 +04:00
taskSurvive = SubsumeDesire (&g_taskFilters[TASK_HIDE], taskSurvive);
TaskItem *def = ThresholdDesire (&g_taskFilters[TASK_HUNTENEMY], 41.0f, 0.0f); // don't allow hunting if desires 60<
2014-07-30 14:17:46 +04:00
taskOffensive = SubsumeDesire (taskOffensive, taskPickup); // if offensive task, don't allow picking up stuff
TaskItem *taskSub = MaxDesire (taskOffensive, def); // default normal & careful tasks against offensive actions
TaskItem *final = SubsumeDesire (&g_taskFilters[TASK_BLINDED], MaxDesire (taskSurvive, taskSub)); // reason about fleeing instead
if (!m_tasks.IsEmpty ())
2015-06-04 11:52:48 +03:00
{
2014-07-30 14:17:46 +04:00
final = MaxDesire (final, GetTask ());
PushTask (final->id, final->desire, final->data, final->time, final->resume); // push the final behavior in our task stack to carry out
2015-06-04 11:52:48 +03:00
}
2014-07-30 14:17:46 +04:00
}
void Bot::ResetTasks (void)
{
// this function resets bot tasks stack, by removing all entries from the stack.
m_tasks.RemoveAll ();
}
void Bot::PushTask (TaskID id, float desire, int data, float time, bool resume)
2014-07-30 14:17:46 +04:00
{
if (!m_tasks.IsEmpty ())
{
TaskItem &item = m_tasks.Last ();
if (item.id == id)
{
item.desire = desire;
return;
}
}
TaskItem item;
item.id = id;
item.desire = desire;
item.data = data;
item.time = time;
item.resume = resume;
m_tasks.Push (item);
DeleteSearchNodes ();
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
2014-07-30 14:17:46 +04:00
// leader bot?
if (m_isLeader && GetTaskId () == TASK_SEEKCOVER)
CommandTeam (); // reorganize team if fleeing
if (GetTaskId () == TASK_CAMP)
SelectBestWeapon ();
// this is best place to handle some voice commands report team some info
if (Random.Long (0, 100) < 95)
2014-07-30 14:17:46 +04:00
{
switch (GetTaskId ())
{
case TASK_BLINDED:
InstantChatterMessage (Chatter_GotBlinded);
break;
case TASK_PLANTBOMB:
InstantChatterMessage (Chatter_PlantingC4);
break;
}
}
if (Random.Long (0, 100) < 80 && GetTaskId () == TASK_CAMP)
2014-07-30 14:17:46 +04:00
{
if ((g_mapType & MAP_DE) && g_bombPlanted)
ChatterMessage (Chatter_GuardDroppedC4);
else
ChatterMessage (Chatter_GoingToCamp);
}
if (yb_debug_goal.GetInt () != -1)
m_chosenGoalIndex = yb_debug_goal.GetInt ();
else
m_chosenGoalIndex = GetTask ()->data;
if (Random.Long (0, 100) < 80 && GetTaskId () == TASK_CAMP && m_team == TEAM_TF && m_inVIPZone)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_GoingToGuardVIPSafety);
}
TaskItem *Bot::GetTask (void)
{
if (m_tasks.IsEmpty ())
{
m_tasks.RemoveAll ();
TaskItem task;
task.id = TASK_NORMAL;
task.desire = TASKPRI_NORMAL;
task.data = -1;
task.time = 0.0f;
2014-07-30 14:17:46 +04:00
task.resume = true;
m_tasks.Push (task);
}
return &m_tasks.Last ();
}
2015-07-05 18:53:58 +03:00
void Bot::RemoveCertainTask (TaskID id)
2014-07-30 14:17:46 +04:00
{
// this function removes one task from the bot task stack.
if (m_tasks.IsEmpty () || (!m_tasks.IsEmpty () && GetTaskId () == TASK_NORMAL))
return; // since normal task can be only once on the stack, don't remove it...
if (GetTaskId () == id)
{
DeleteSearchNodes ();
m_tasks.Pop ();
return;
}
FOR_EACH_AE (m_tasks, i)
2014-07-30 14:17:46 +04:00
{
if (m_tasks[i].id == id)
m_tasks.RemoveAt (i);
}
DeleteSearchNodes ();
}
void Bot::TaskComplete (void)
{
// this function called whenever a task is completed.
if (m_tasks.IsEmpty ())
return;
do
{
m_tasks.Pop ();
} while (!m_tasks.IsEmpty () && !m_tasks.Last ().resume);
DeleteSearchNodes ();
}
bool Bot::EnemyIsThreat (void)
{
2015-06-04 11:52:48 +03:00
if (IsEntityNull (m_enemy) || GetTaskId () == TASK_SEEKCOVER)
2014-07-30 14:17:46 +04:00
return false;
float distance = (m_enemy->v.origin - pev->origin).GetLength ();
// if bot is camping, he should be firing anyway and not leaving his position
if (GetTaskId () == TASK_CAMP)
return false;
// if enemy is near or facing us directly
if (distance < 256.0f || IsInViewCone (m_enemy->v.origin))
return true;
return false;
}
bool Bot::ReactOnEnemy (void)
{
// the purpose of this function is check if task has to be interrupted because an enemy is near (run attack actions then)
if (!EnemyIsThreat ())
return false;
if (m_enemyReachableTimer < GetWorldTime ())
{
2015-07-24 15:10:51 +03:00
int i = waypoints.FindNearest (pev->origin);
int enemyIndex = waypoints.FindNearest (m_enemy->v.origin);
2014-07-30 14:17:46 +04:00
float lineDist = (m_enemy->v.origin - pev->origin).GetLength ();
float pathDist = static_cast <float> (waypoints.GetPathDistance (i, enemyIndex));
2014-07-30 14:17:46 +04:00
if (pathDist - lineDist > 112.0f)
2014-07-30 14:17:46 +04:00
m_isEnemyReachable = false;
else
m_isEnemyReachable = true;
m_enemyReachableTimer = GetWorldTime () + 1.0f;
2014-07-30 14:17:46 +04:00
}
if (m_isEnemyReachable)
{
m_navTimeset = GetWorldTime (); // override existing movement by attack movement
return true;
}
return false;
}
bool Bot::LastEnemyShootable (void)
{
2015-06-04 11:52:48 +03:00
// don't allow shooting through walls
if (!(m_aimFlags & AIM_LAST_ENEMY) || !m_lastEnemyOrigin.IsZero () || IsEntityNull (m_lastEnemy))
2014-07-30 14:17:46 +04:00
return false;
return GetShootingConeDeviation (GetEntity (), &m_lastEnemyOrigin) >= 0.90f && IsShootableThruObstacle (m_lastEnemyOrigin);
2014-07-30 14:17:46 +04:00
}
void Bot::CheckRadioCommands (void)
{
// this function handling radio and reactings to it
float distance = (m_radioEntity->v.origin - pev->origin).GetLength ();
// don't allow bot listen you if bot is busy
2014-09-17 20:36:42 +04:00
if ((GetTaskId () == TASK_DEFUSEBOMB || GetTaskId () == TASK_PLANTBOMB || HasHostage () || m_hasC4) && m_radioOrder != Radio_ReportTeam)
2014-07-30 14:17:46 +04:00
{
m_radioOrder = 0;
return;
}
switch (m_radioOrder)
{
case Radio_CoverMe:
case Radio_FollowMe:
case Radio_StickTogether:
case Chatter_GoingToPlantBomb:
case Chatter_CoverMe:
// check if line of sight to object is not blocked (i.e. visible)
if ((EntityIsVisible (m_radioEntity->v.origin)) || (m_radioOrder == Radio_StickTogether))
{
if (IsEntityNull (m_targetEntity) && IsEntityNull (m_enemy) && Random.Long (0, 100) < (m_personality == PERSONALITY_CAREFUL ? 80 : 20))
2014-07-30 14:17:46 +04:00
{
int numFollowers = 0;
// Check if no more followers are allowed
for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
Bot *bot = bots.GetBot (i);
2014-07-30 14:17:46 +04:00
if (bot != NULL)
{
if (IsAlive (bot->GetEntity ()))
{
if (bot->m_targetEntity == m_radioEntity)
numFollowers++;
}
}
}
int allowedFollowers = yb_user_max_followers.GetInt ();
if (m_radioEntity->v.weapons & (1 << WEAPON_C4))
allowedFollowers = 1;
if (numFollowers < allowedFollowers)
{
RadioMessage (Radio_Affirmative);
m_targetEntity = m_radioEntity;
// don't pause/camp/follow anymore
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2014-07-30 14:17:46 +04:00
if (taskID == TASK_PAUSE || taskID == TASK_CAMP)
GetTask ()->time = GetWorldTime ();
PushTask (TASK_FOLLOWUSER, TASKPRI_FOLLOWUSER, -1, 0.0f, true);
2014-07-30 14:17:46 +04:00
}
else if (numFollowers > allowedFollowers)
{
for (int i = 0; (i < GetMaxClients () && numFollowers > allowedFollowers); i++)
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
Bot *bot = bots.GetBot (i);
2014-07-30 14:17:46 +04:00
if (bot != NULL)
{
if (IsAlive (bot->GetEntity ()))
{
if (bot->m_targetEntity == m_radioEntity)
{
bot->m_targetEntity = NULL;
numFollowers--;
}
}
}
}
}
2015-07-17 20:44:47 +03:00
else if (m_radioOrder != Chatter_GoingToPlantBomb && Random.Long (0, 100) < 65)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Negative);
}
2015-07-17 20:44:47 +03:00
else if (m_radioOrder != Chatter_GoingToPlantBomb && Random.Long (0, 100) < 65)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Negative);
}
break;
case Radio_HoldPosition:
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_targetEntity))
2014-07-30 14:17:46 +04:00
{
if (m_targetEntity == m_radioEntity)
{
m_targetEntity = NULL;
RadioMessage (Radio_Affirmative);
m_campButtons = 0;
PushTask (TASK_PAUSE, TASKPRI_PAUSE, -1, GetWorldTime () + Random.Float (30.0f, 60.0f), false);
2014-07-30 14:17:46 +04:00
}
}
break;
case Chatter_NewRound:
ChatterMessage (Chatter_You_Heard_The_Man);
break;
case Radio_TakingFire:
2015-06-04 11:52:48 +03:00
if (IsEntityNull (m_targetEntity))
2014-07-30 14:17:46 +04:00
{
if (IsEntityNull (m_enemy) && m_seeEnemyTime + 4.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
// decrease fear levels to lower probability of bot seeking cover again
m_fearLevel -= 0.2f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
if (Random.Long (0, 100) < 45 && yb_communication_type.GetInt () == 2)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_OnMyWay);
else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2)
RadioMessage (Radio_Affirmative);
2015-06-04 11:52:48 +03:00
TryHeadTowardRadioEntity ();
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else if (Random.Long (0, 100) < 70)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Negative);
}
break;
case Radio_YouTakePoint:
if (EntityIsVisible (m_radioEntity->v.origin) && m_isLeader)
RadioMessage (Radio_Affirmative);
break;
case Radio_EnemySpotted:
case Radio_NeedBackup:
case Chatter_ScaredEmotion:
case Chatter_Pinned_Down:
if (((IsEntityNull (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 2048.0f || !m_moveToC4) && Random.Long (0, 100) > 50 && m_seeEnemyTime + 4.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
m_fearLevel -= 0.1f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
if (Random.Long (0, 100) < 45 && yb_communication_type.GetInt () == 2)
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_OnMyWay);
else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2)
RadioMessage (Radio_Affirmative);
2015-06-04 11:52:48 +03:00
TryHeadTowardRadioEntity ();
2014-07-30 14:17:46 +04:00
}
else if (Random.Long (0, 100) < 80 && m_radioOrder == Radio_NeedBackup)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Negative);
break;
case Radio_GoGoGo:
if (m_radioEntity == m_targetEntity)
{
if (Random.Long (0, 100) < 45 && yb_communication_type.GetInt () == 2)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Affirmative);
else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2)
RadioMessage (Radio_Affirmative);
m_targetEntity = NULL;
m_fearLevel -= 0.2f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
}
2015-06-04 11:52:48 +03:00
else if ((IsEntityNull (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 2048)
2014-07-30 14:17:46 +04:00
{
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2014-07-30 14:17:46 +04:00
if (taskID == TASK_PAUSE || taskID == TASK_CAMP)
{
m_fearLevel -= 0.2f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Affirmative);
// don't pause/camp anymore
GetTask ()->time = GetWorldTime ();
m_targetEntity = NULL;
MakeVectors (m_radioEntity->v.v_angle);
m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * Random.Float (1024.0f, 2048.0f);
2014-07-30 14:17:46 +04:00
DeleteSearchNodes ();
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true);
2014-07-30 14:17:46 +04:00
}
}
2015-06-04 11:52:48 +03:00
else if (!IsEntityNull (m_doubleJumpEntity))
2014-07-30 14:17:46 +04:00
{
RadioMessage (Radio_Affirmative);
ResetDoubleJumpState ();
}
else
RadioMessage (Radio_Negative);
2015-07-17 19:23:31 +03:00
2014-07-30 14:17:46 +04:00
break;
case Radio_ShesGonnaBlow:
if (IsEntityNull (m_enemy) && distance < 2048.0f && g_bombPlanted && m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
{
RadioMessage (Radio_Affirmative);
if (GetTaskId () == TASK_CAMP)
RemoveCertainTask (TASK_CAMP);
m_targetEntity = NULL;
PushTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, -1, 0.0f, true);
2014-07-30 14:17:46 +04:00
}
else
RadioMessage (Radio_Negative);
break;
case Radio_RegroupTeam:
// if no more enemies found AND bomb planted, switch to knife to get to bombplace faster
if ((m_team == TEAM_CF) && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted && GetTaskId () != TASK_DEFUSEBOMB)
2014-07-30 14:17:46 +04:00
{
SelectWeaponByName ("weapon_knife");
DeleteSearchNodes ();
2015-07-24 15:10:51 +03:00
MoveToVector (waypoints.GetBombPosition ());
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_Affirmative);
}
break;
case Radio_StormTheFront:
if (((IsEntityNull (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f) && Random.Long (0, 100) > 50)
2014-07-30 14:17:46 +04:00
{
RadioMessage (Radio_Affirmative);
// don't pause/camp anymore
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2014-07-30 14:17:46 +04:00
if (taskID == TASK_PAUSE || taskID == TASK_CAMP)
GetTask ()->time = GetWorldTime ();
m_targetEntity = NULL;
MakeVectors (m_radioEntity->v.v_angle);
m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * Random.Float (1024.0f, 2048.0f);
2014-07-30 14:17:46 +04:00
DeleteSearchNodes ();
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true);
2014-07-30 14:17:46 +04:00
m_fearLevel -= 0.3f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel < 0.0f)
m_fearLevel = 0.0f;
2014-07-30 14:17:46 +04:00
m_agressionLevel += 0.3f;
2014-07-30 14:17:46 +04:00
if (m_agressionLevel > 1.0f)
m_agressionLevel = 1.0f;
2014-07-30 14:17:46 +04:00
}
break;
case Radio_Fallback:
if ((IsEntityNull (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f)
2014-07-30 14:17:46 +04:00
{
m_fearLevel += 0.5f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel > 1.0f)
m_fearLevel = 1.0f;
2014-07-30 14:17:46 +04:00
m_agressionLevel -= 0.5f;
2014-07-30 14:17:46 +04:00
if (m_agressionLevel < 0.0f)
m_agressionLevel = 0.0f;
2014-07-30 14:17:46 +04:00
if (GetTaskId () == TASK_CAMP)
GetTask ()->time += Random.Float (10.0f, 15.0f);
2014-07-30 14:17:46 +04:00
else
{
// don't pause/camp anymore
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2014-07-30 14:17:46 +04:00
if (taskID == TASK_PAUSE)
GetTask ()->time = GetWorldTime ();
m_targetEntity = NULL;
m_seeEnemyTime = GetWorldTime ();
// if bot has no enemy
if (m_lastEnemyOrigin.IsZero ())
2014-07-30 14:17:46 +04:00
{
float nearestDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
// take nearest enemy to ordering player
for (int i = 0; i < GetMaxClients (); i++)
{
2014-08-06 00:03:50 +04:00
if (!(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || g_clients[i].team == m_team)
2014-07-30 14:17:46 +04:00
continue;
edict_t *enemy = g_clients[i].ent;
2015-06-04 11:52:48 +03:00
float curDist = (m_radioEntity->v.origin - enemy->v.origin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (curDist < nearestDistance)
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
nearestDistance = curDist;
2014-07-30 14:17:46 +04:00
m_lastEnemy = enemy;
m_lastEnemyOrigin = enemy->v.origin;
}
}
}
DeleteSearchNodes ();
}
}
break;
case Radio_ReportTeam:
if (Random.Long (0, 100) < 30)
RadioMessage ((GetNearbyEnemiesNearPosition (pev->origin, 400.0f) == 0 && yb_communication_type.GetInt () != 2) ? Radio_SectorClear : Radio_ReportingIn);
2014-07-30 14:17:46 +04:00
break;
case Radio_SectorClear:
// is bomb planted and it's a ct
if (g_bombPlanted)
{
int bombPoint = -1;
// check if it's a ct command
2014-08-06 00:03:50 +04:00
if (GetTeam (m_radioEntity) == TEAM_CF && m_team == TEAM_CF && IsValidBot (m_radioEntity))
2014-07-30 14:17:46 +04:00
{
if (g_timeNextBombUpdate < GetWorldTime ())
{
float minDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
// find nearest bomb waypoint to player
2015-07-24 15:10:51 +03:00
FOR_EACH_AE (waypoints.m_goalPoints, i)
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
distance = (waypoints.GetPath (waypoints.m_goalPoints[i])->origin - m_radioEntity->v.origin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
if (distance < minDistance)
{
minDistance = distance;
2015-07-24 15:10:51 +03:00
bombPoint = waypoints.m_goalPoints[i];
2014-07-30 14:17:46 +04:00
}
}
// mark this waypoint as restricted point
2015-07-24 15:10:51 +03:00
if (bombPoint != -1 && !waypoints.IsGoalVisited (bombPoint))
2014-07-30 14:17:46 +04:00
{
// does this bot want to defuse?
if (GetTaskId () == TASK_NORMAL)
{
// is he approaching this goal?
if (GetTask ()->data == bombPoint)
{
GetTask ()->data = -1;
RadioMessage (Radio_Affirmative);
}
}
2015-07-24 15:10:51 +03:00
waypoints.SetGoalVisited (bombPoint);
2014-07-30 14:17:46 +04:00
}
g_timeNextBombUpdate = GetWorldTime () + 0.5f;
2014-07-30 14:17:46 +04:00
}
}
}
break;
case Radio_GetInPosition:
if ((IsEntityNull (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f)
2014-07-30 14:17:46 +04:00
{
RadioMessage (Radio_Affirmative);
if (GetTaskId () == TASK_CAMP)
GetTask ()->time = GetWorldTime () + Random.Float (30.0f, 60.0f);
2014-07-30 14:17:46 +04:00
else
{
// don't pause anymore
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2014-07-30 14:17:46 +04:00
if (taskID == TASK_PAUSE)
GetTask ()->time = GetWorldTime ();
m_targetEntity = NULL;
m_seeEnemyTime = GetWorldTime ();
2015-06-04 11:52:48 +03:00
// if bot has no enemy
if (m_lastEnemyOrigin.IsZero ())
2014-07-30 14:17:46 +04:00
{
float nearestDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
// take nearest enemy to ordering player
2014-07-30 14:17:46 +04:00
for (int i = 0; i < GetMaxClients (); i++)
{
2014-08-06 00:03:50 +04:00
if (!(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || g_clients[i].team == m_team)
2014-07-30 14:17:46 +04:00
continue;
edict_t *enemy = g_clients[i].ent;
2015-06-04 11:52:48 +03:00
float dist = (m_radioEntity->v.origin - enemy->v.origin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (dist < nearestDistance)
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
nearestDistance = dist;
2014-07-30 14:17:46 +04:00
m_lastEnemy = enemy;
m_lastEnemyOrigin = enemy->v.origin;
}
}
}
DeleteSearchNodes ();
int index = FindDefendWaypoint (m_radioEntity->v.origin);
// push camp task on to stack
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (30.0f, 60.0f), true);
2014-07-30 14:17:46 +04:00
// push move command
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (30.0f, 60.0f), true);
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2014-07-30 14:17:46 +04:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
}
}
break;
}
m_radioOrder = 0; // radio command has been handled, reset
}
2015-06-04 11:52:48 +03:00
void Bot::TryHeadTowardRadioEntity (void)
{
2015-07-05 18:53:58 +03:00
TaskID taskID = GetTaskId ();
2015-06-04 11:52:48 +03:00
if (taskID == TASK_MOVETOPOSITION || m_headedTime + 15.0f < GetWorldTime () || !IsAlive (m_radioEntity) || m_hasC4)
return;
if ((IsValidBot (m_radioEntity) && Random.Long (0, 100) < 25 && m_personality == PERSONALITY_NORMAL) || !(m_radioEntity->v.flags & FL_FAKECLIENT))
2015-06-04 11:52:48 +03:00
{
if (taskID == TASK_PAUSE || taskID == TASK_CAMP)
GetTask ()->time = GetWorldTime ();
m_headedTime = GetWorldTime ();
m_position = m_radioEntity->v.origin;
DeleteSearchNodes ();
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true);
2015-06-04 11:52:48 +03:00
}
}
2014-07-30 14:17:46 +04:00
void Bot::SelectLeaderEachTeam (int team)
{
if (g_mapType & MAP_AS)
{
if (m_isVIP && !g_leaderChoosen[TEAM_CF])
{
// vip bot is the leader
m_isLeader = true;
if (Random.Long (1, 100) < 50)
2014-07-30 14:17:46 +04:00
{
RadioMessage (Radio_FollowMe);
m_campButtons = 0;
}
g_leaderChoosen[TEAM_CF] = true;
}
else if ((team == TEAM_TF) && !g_leaderChoosen[TEAM_TF])
{
2015-07-24 15:10:51 +03:00
Bot *botLeader = bots.GetHighestFragsBot(team);
2014-07-30 14:17:46 +04:00
if (botLeader != NULL && IsAlive (botLeader->GetEntity()))
{
botLeader->m_isLeader = true;
if (Random.Long (1, 100) < 45)
2014-07-30 14:17:46 +04:00
botLeader->RadioMessage (Radio_FollowMe);
}
g_leaderChoosen[TEAM_TF] = true;
}
}
else if (g_mapType & MAP_DE)
{
if (team == TEAM_TF && !g_leaderChoosen[TEAM_TF])
{
2014-09-17 20:36:42 +04:00
if (m_hasC4)
2014-07-30 14:17:46 +04:00
{
// bot carrying the bomb is the leader
m_isLeader = true;
// terrorist carrying a bomb needs to have some company
if (Random.Long (1, 100) < 80)
2014-07-30 14:17:46 +04:00
{
if (yb_communication_type.GetInt () == 2)
ChatterMessage (Chatter_GoingToPlantBomb);
else
ChatterMessage (Radio_FollowMe);
m_campButtons = 0;
}
g_leaderChoosen[TEAM_TF] = true;
}
}
else if (!g_leaderChoosen[TEAM_CF])
{
2015-07-24 15:10:51 +03:00
Bot *botLeader = bots.GetHighestFragsBot(team);
2014-07-30 14:17:46 +04:00
if (botLeader != NULL)
{
botLeader->m_isLeader = true;
if (Random.Long (1, 100) < 30)
2014-07-30 14:17:46 +04:00
botLeader->RadioMessage (Radio_FollowMe);
}
g_leaderChoosen[TEAM_CF] = true;
}
}
else if (g_mapType & (MAP_ES | MAP_KA | MAP_FY))
{
2015-07-24 15:10:51 +03:00
Bot *botLeader = bots.GetHighestFragsBot (team);
2014-07-30 14:17:46 +04:00
if (botLeader != NULL)
{
botLeader->m_isLeader = true;
if (Random.Long (1, 100) < 30)
2014-07-30 14:17:46 +04:00
botLeader->RadioMessage (Radio_FollowMe);
}
}
else
{
2015-07-24 15:10:51 +03:00
Bot *botLeader = bots.GetHighestFragsBot(team);
2014-07-30 14:17:46 +04:00
if (botLeader != NULL)
{
botLeader->m_isLeader = true;
if (Random.Long (1, 100) < (team == TEAM_TF ? 30 : 40))
2014-07-30 14:17:46 +04:00
botLeader->RadioMessage (Radio_FollowMe);
}
}
}
void Bot::ChooseAimDirection (void)
{
unsigned int flags = m_aimFlags;
2014-07-30 14:17:46 +04:00
// don't allow bot to look at danger positions under certain circumstances
if (!(flags & (AIM_GRENADE | AIM_ENEMY | AIM_ENTITY)))
{
if (IsOnLadder () || IsInWater () || (m_waypointFlags & FLAG_LADDER) || (m_currentTravelFlags & PATHFLAG_JUMP))
{
flags &= ~(AIM_LAST_ENEMY | AIM_PREDICT_PATH);
2015-06-08 22:55:57 +03:00
m_canChooseAimDirection = false;
2014-07-30 14:17:46 +04:00
}
}
if (flags & AIM_OVERRIDE)
m_lookAt = m_camp;
else if (flags & AIM_GRENADE)
m_lookAt = m_throw + Vector (0.0f, 0.0f, 1.0f * m_grenade.z);
2014-07-30 14:17:46 +04:00
else if (flags & AIM_ENEMY)
FocusEnemy ();
else if (flags & AIM_ENTITY)
m_lookAt = m_entity;
else if (flags & AIM_LAST_ENEMY)
{
m_lookAt = m_lastEnemyOrigin;
// did bot just see enemy and is quite aggressive?
if (m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
// feel free to fire if shootable
if (!UsesSniper () && LastEnemyShootable ())
m_wantsToFire = true;
}
}
else if (flags & AIM_PREDICT_PATH)
2014-07-30 14:17:46 +04:00
{
bool changePredictedEnemy = true;
2014-07-30 14:17:46 +04:00
if (m_trackingEdict == m_lastEnemy)
{
if (m_timeNextTracking < GetWorldTime ())
changePredictedEnemy = IsAlive (m_lastEnemy);
2014-07-30 14:17:46 +04:00
}
if (changePredictedEnemy)
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
m_lookAt = waypoints.GetPath (GetAimingWaypoint (m_lastEnemyOrigin))->origin;
m_camp = m_lookAt;
2014-07-30 14:17:46 +04:00
m_timeNextTracking = GetWorldTime () + 2.0f;
m_trackingEdict = m_lastEnemy;
2014-07-30 14:17:46 +04:00
}
else
m_lookAt = m_camp;
2014-07-30 14:17:46 +04:00
}
else if (flags & AIM_CAMP)
m_lookAt = m_camp;
else if (flags & AIM_NAVPOINT)
2014-07-30 14:17:46 +04:00
{
m_lookAt = m_destOrigin;
2015-06-08 22:55:57 +03:00
if (m_canChooseAimDirection && m_currentWaypointIndex != -1 && !(m_currentPath->flags & FLAG_LADDER))
2014-07-30 14:17:46 +04:00
{
int index = m_currentWaypointIndex;
2014-08-06 00:03:50 +04:00
if (m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
{
if ((g_experienceData + (index * g_numWaypoints) + index)->team0DangerIndex != -1)
2015-07-24 15:10:51 +03:00
m_lookAt = waypoints.GetPath ((g_experienceData + (index * g_numWaypoints) + index)->team0DangerIndex)->origin;
2014-07-30 14:17:46 +04:00
}
else
{
if ((g_experienceData + (index * g_numWaypoints) + index)->team1DangerIndex != -1)
2015-07-24 15:10:51 +03:00
m_lookAt = waypoints.GetPath ((g_experienceData + (index * g_numWaypoints) + index)->team1DangerIndex)->origin;
2014-07-30 14:17:46 +04:00
}
}
}
if (m_lookAt.IsZero ())
2014-07-30 14:17:46 +04:00
m_lookAt = m_destOrigin;
}
void Bot::Think (void)
{
2015-07-22 23:04:43 +03:00
if (m_thinkFps <= GetWorldTime ())
{
2015-07-22 23:04:43 +03:00
// execute delayed think
ThinkDelayed ();
// skip some frames
2015-07-22 23:04:43 +03:00
m_thinkFps = GetWorldTime () + m_thinkInterval;
}
else
UpdateLookAngles ();
}
void Bot::ThinkDelayed (void)
2014-07-30 14:17:46 +04:00
{
pev->button = 0;
pev->flags |= FL_FAKECLIENT; // restore fake client bit, if it were removed by some evil action =)
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
m_moveAngles.Zero ();
2014-07-30 14:17:46 +04:00
m_canChooseAimDirection = true;
m_notKilled = IsAlive (GetEntity ());
2014-08-06 00:03:50 +04:00
m_team = GetTeam (GetEntity ());
2014-07-30 14:17:46 +04:00
if (m_team == TEAM_TF && (g_mapType & MAP_DE))
2014-09-17 20:36:42 +04:00
m_hasC4 = !!(pev->weapons & (1 << WEAPON_C4));
2014-07-30 14:17:46 +04:00
// is bot movement enabled
bool botMovement = false;
if (m_notStarted) // if the bot hasn't selected stuff to start the game yet, go do that...
StartGame (); // select team & class
else if (!m_notKilled)
{
// no movement allowed in
if (m_voteKickIndex != m_lastVoteKick && yb_tkpunish.GetBool ()) // we got a teamkiller? vote him away...
2014-07-30 14:17:46 +04:00
{
FakeClientCommand (GetEntity (), "vote %d", m_voteKickIndex);
m_lastVoteKick = m_voteKickIndex;
// if bot tk punishment is enabled slay the tk
2014-09-17 20:36:42 +04:00
if (yb_tkpunish.GetInt () != 2 || IsValidBot (EntityOfIndex (m_voteKickIndex)))
2014-07-30 14:17:46 +04:00
return;
edict_t *killer = EntityOfIndex (m_lastVoteKick);
killer->v.frags++;
MDLL_ClientKill (killer);
2014-07-30 14:17:46 +04:00
}
else if (m_voteMap != 0) // host wants the bots to vote for a map?
{
FakeClientCommand (GetEntity (), "votemap %d", m_voteMap);
m_voteMap = 0;
}
extern ConVar yb_chat;
if (yb_chat.GetBool () && !RepliesToPlayer () && m_lastChatTime + 10.0 < GetWorldTime () && g_lastChatTime + 5.0f < GetWorldTime ()) // bot chatting turned on?
2014-07-30 14:17:46 +04:00
{
// say a text every now and then
if (Random.Long (1, 1500) < 2)
2014-07-30 14:17:46 +04:00
{
m_lastChatTime = GetWorldTime ();
g_lastChatTime = GetWorldTime ();
char *pickedPhrase = const_cast <char *> (g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer ());
bool sayBufferExists = false;
// search for last messages, sayed
FOR_EACH_AE (m_sayTextBuffer.lastUsedSentences, i)
2014-07-30 14:17:46 +04:00
{
if (strncmp (m_sayTextBuffer.lastUsedSentences[i].GetBuffer (), pickedPhrase, m_sayTextBuffer.lastUsedSentences[i].GetLength ()) == 0)
sayBufferExists = true;
}
if (!sayBufferExists)
{
PrepareChatMessage (pickedPhrase);
PushMessageQueue (GSM_SAY);
// add to ignore list
m_sayTextBuffer.lastUsedSentences.Push (pickedPhrase);
}
// clear the used line buffer every now and then
if (m_sayTextBuffer.lastUsedSentences.GetElementNumber () > Random.Long (4, 6))
2014-07-30 14:17:46 +04:00
m_sayTextBuffer.lastUsedSentences.RemoveAll ();
}
}
}
else if (m_notKilled && m_buyingFinished && !(pev->maxspeed < 10.0f && GetTaskId () != TASK_PLANTBOMB && GetTaskId () != TASK_DEFUSEBOMB) && !yb_freeze_bots.GetBool ())
2014-07-30 14:17:46 +04:00
botMovement = true;
#ifdef XASH_CSDM
if (m_notKilled)
botMovement = true;
#endif
2014-07-30 14:17:46 +04:00
CheckMessageQueue (); // check for pending messages
// remove voice icon
if (g_lastRadioTime[g_clients[IndexOfEntity (GetEntity ()) - 1].realTeam] + Random.Float (0.8f, 2.1f) < GetWorldTime ())
SwitchChatterIcon (false); // hide icon
if (botMovement)
2014-07-30 14:17:46 +04:00
BotAI (); // execute main code
RunPlayerMovement (); // run the player movement
2014-07-30 14:17:46 +04:00
}
void Bot::PeriodicThink (void)
2014-07-30 14:17:46 +04:00
{
if (m_timePeriodicUpdate > GetWorldTime ())
return;
2015-07-17 19:23:31 +03:00
// this function is called from main think function
2014-07-30 14:17:46 +04:00
m_numFriendsLeft = GetNearbyFriendsNearPosition (pev->origin, 99999.0f);
m_numEnemiesLeft = GetNearbyEnemiesNearPosition (pev->origin, 99999.0f);
2015-07-24 15:10:51 +03:00
if (g_bombPlanted && m_team == TEAM_CF && (pev->origin - waypoints.GetBombPosition ()).GetLength () < 700 && !IsBombDefusing (waypoints.GetBombPosition ()) && !m_hasProgressBar && GetTaskId () != TASK_ESCAPEFROMBOMB)
2014-07-30 14:17:46 +04:00
ResetTasks ();
2015-07-17 19:23:31 +03:00
CheckSpawnTimeConditions ();
// clear enemy far away
if (!m_lastEnemyOrigin.IsZero () && !IsEntityNull (m_lastEnemy) && (pev->origin - m_lastEnemyOrigin).GetLength () >= 1600.0f)
{
2015-07-19 13:45:45 +03:00
m_lastEnemy = NULL;
m_lastEnemyOrigin.Zero ();
}
m_timePeriodicUpdate = GetWorldTime () + 0.5f;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Normal (void)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// user forced a waypoint as a goal?
if (yb_debug_goal.GetInt () != -1 && GetTask ()->data != yb_debug_goal.GetInt ())
{
DeleteSearchNodes ();
GetTask ()->data = yb_debug_goal.GetInt ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bots rushing with knife, when have no enemy (thanks for idea to nicebot project)
if (m_currentWeapon == WEAPON_KNIFE && (IsEntityNull (m_lastEnemy) || !IsAlive (m_lastEnemy)) && IsEntityNull (m_enemy) && m_knifeAttackTime < GetWorldTime () && !HasShield () && GetNearbyFriendsNearPosition (pev->origin, 96) == 0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (Random.Long (0, 100) < 40)
pev->button |= IN_ATTACK;
else
pev->button |= IN_ATTACK2;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_knifeAttackTime = GetWorldTime () + Random.Float (2.5f, 6.0f);
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_reloadState == RELOAD_NONE && GetAmmo () != 0 && GetAmmoInClip () < 5 && g_weaponDefs[m_currentWeapon].ammo1 != -1)
m_reloadState = RELOAD_PRIMARY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if bomb planted and it's a CT calculate new path to bomb point if he's not already heading for
2015-07-24 15:10:51 +03:00
if (g_bombPlanted && m_team == TEAM_CF && GetTask ()->data != -1 && !(waypoints.GetPath (GetTask ()->data)->flags & FLAG_GOAL) && GetTaskId () != TASK_ESCAPEFROMBOMB)
2015-07-17 19:23:31 +03:00
{
DeleteSearchNodes ();
GetTask ()->data = -1;
}
2014-07-30 14:17:46 +04:00
if (!g_bombPlanted && m_currentWaypointIndex != -1 && (m_currentPath->flags & FLAG_GOAL) && Random.Long (0, 100) < 80 && GetNearbyEnemiesNearPosition (pev->origin, 650.0f) == 0)
2015-07-17 19:23:31 +03:00
RadioMessage (Radio_SectorClear);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// reached the destination (goal) waypoint?
if (DoWaypointNav ())
{
TaskComplete ();
m_prevGoalIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// spray logo sometimes if allowed to do so
if (m_timeLogoSpray < GetWorldTime () && yb_spraypaints.GetBool () && Random.Long (1, 100) < 60 && m_moveSpeed > GetWalkSpeed () && IsEntityNull (m_pickupItem))
PushTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, -1, GetWorldTime () + 1.0f, false);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// reached waypoint is a camp waypoint
if ((m_currentPath->flags & FLAG_CAMP) && !yb_csdm_mode.GetBool () && yb_camping_allowed.GetBool ())
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// check if bot has got a primary weapon and hasn't camped before
if (HasPrimaryWeapon () && m_timeCamping + 10.0f < GetWorldTime () && !HasHostage ())
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
bool campingAllowed = true;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// Check if it's not allowed for this team to camp here
if (m_team == TEAM_TF)
{
if (m_currentPath->flags & FLAG_CF_ONLY)
2014-07-30 14:17:46 +04:00
campingAllowed = false;
2015-07-17 19:23:31 +03:00
}
else
{
if (m_currentPath->flags & FLAG_TF_ONLY)
2014-07-30 14:17:46 +04:00
campingAllowed = false;
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// don't allow vip on as_ maps to camp + don't allow terrorist carrying c4 to camp
if (IsPlayerVIP (GetEntity ()) || ((g_mapType & MAP_DE) && m_team == TEAM_TF && !g_bombPlanted && m_hasC4))
campingAllowed = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// check if another bot is already camping here
if (IsPointOccupied (m_currentWaypointIndex))
campingAllowed = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (campingAllowed)
{
// crouched camping here?
if (m_currentPath->flags & FLAG_CROUCH)
m_campButtons = IN_DUCK;
else
m_campButtons = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
SelectBestWeapon ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!(m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) && !m_reloadState)
m_reloadState = RELOAD_PRIMARY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
MakeVectors (pev->v_angle);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (20.0f, 40.0f), true);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_camp = Vector (m_currentPath->campStartX, m_currentPath->campStartY, 0.0f);
m_aimFlags |= AIM_CAMP;
m_campDirection = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// tell the world we're camping
if (Random.Long (0, 100) < 80)
RadioMessage (Radio_InPosition);
m_moveToGoal = false;
m_checkTerrain = false;
m_moveSpeed = 0;
m_strafeSpeed = 0;
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
}
else
{
// some goal waypoints are map dependant so check it out...
if (g_mapType & MAP_CS)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// CT Bot has some hostages following?
if (HasHostage () && m_team == TEAM_CF)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// and reached a Rescue Point?
if (m_currentPath->flags & FLAG_RESCUE)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
for (int i = 0; i < MAX_HOSTAGES; i++)
m_hostages[i] = NULL; // clear array of hostage pointers
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else if (m_team == TEAM_TF && Random.Long (0, 100) < 80)
{
int index = FindDefendWaypoint (m_currentPath->origin);
2014-07-30 14:17:46 +04:00
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (60.0f, 120.0f), true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (5.0f, 10.0f), true); // push move command
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2015-07-17 19:23:31 +03:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
ChatterMessage (Chatter_GoingToGuardVIPSafety); // play info about that
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else if ((g_mapType & MAP_DE) && ((m_currentPath->flags & FLAG_GOAL) || m_inBombZone) && m_seeEnemyTime + 1.5f < GetWorldTime ())
{
// is it a terrorist carrying the bomb?
if (m_hasC4)
2014-07-30 14:17:46 +04:00
{
if ((m_states & STATE_SEEING_ENEMY) && GetNearbyFriendsNearPosition (pev->origin, 768.0f) == 0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// request an help also
RadioMessage (Radio_NeedBackup);
InstantChatterMessage (Chatter_ScaredEmotion);
2014-07-30 14:17:46 +04:00
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (4.0f, 8.0f), true);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
PushTask (TASK_PLANTBOMB, TASKPRI_PLANTBOMB, -1, 0.0f, false);
2015-07-17 19:23:31 +03:00
}
else if (m_team == TEAM_CF)
{
if (!g_bombPlanted && GetNearbyFriendsNearPosition (pev->origin, 360.0f) < 3 && Random.Long (0, 100) < 85 && m_personality != PERSONALITY_RUSHER)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
int index = FindDefendWaypoint (m_currentPath->origin);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (25.0, 40.0), true); // push camp task on to stack
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (5.0f, 11.0f), true); // push move command
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2015-07-17 19:23:31 +03:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
ChatterMessage (Chatter_DefendingBombSite); // play info about that
2014-07-30 14:17:46 +04:00
}
}
}
}
2015-07-17 19:23:31 +03:00
}
// no more nodes to follow - search new ones (or we have a bomb)
else if (!GoalIsValid ())
{
m_moveSpeed = pev->maxspeed;
DeleteSearchNodes ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
int destIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// did we already decide about a goal before?
if (GetTask ()->data != -1)
destIndex = GetTask ()->data;
else
destIndex = FindGoal ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = destIndex;
m_chosenGoalIndex = destIndex;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// remember index
GetTask ()->data = destIndex;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// do pathfinding if it's not the current waypoint
if (destIndex != m_currentWaypointIndex)
FindPath (m_currentWaypointIndex, destIndex, ((g_bombPlanted && m_team == TEAM_CF) || yb_debug_goal.GetInt () != -1) ? 0 : m_pathType);
}
else
{
if (!(pev->flags & FL_DUCKING) && m_minSpeed != pev->maxspeed)
m_moveSpeed = m_minSpeed;
}
2014-07-30 14:17:46 +04:00
if ((yb_walking_allowed.GetBool () && mp_footsteps.GetBool ()) && m_difficulty >= 2 && !(m_aimFlags & AIM_ENEMY) && (m_heardSoundTime + 13.0f >= GetWorldTime () || (m_states & (STATE_HEARING_ENEMY | STATE_SUSPECT_ENEMY))) && GetNearbyEnemiesNearPosition (pev->origin, 1024.0f) >= 1 && !yb_jasonmode.GetBool () && !g_bombPlanted)
2015-07-17 19:23:31 +03:00
m_moveSpeed = GetWalkSpeed ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot hasn't seen anything in a long time and is asking his teammates to report in
if (m_seeEnemyTime + Random.Float (45.0f, 80.0f) < GetWorldTime () && Random.Long (0, 100) < 30 && g_timeRoundStart + 20.0f < GetWorldTime () && m_askCheckTime + Random.Float (20.0f, 30.0f) < GetWorldTime ())
2015-07-17 19:23:31 +03:00
{
m_askCheckTime = GetWorldTime ();
RadioMessage (Radio_ReportTeam);
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Spray (void)
{
m_aimFlags |= AIM_ENTITY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot didn't spray this round?
if (m_timeLogoSpray < GetWorldTime () && GetTask ()->time > GetWorldTime ())
{
MakeVectors (pev->v_angle);
Vector sprayOrigin = EyePosition () + g_pGlobals->v_forward * 128.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
TraceResult tr;
TraceLine (EyePosition (), sprayOrigin, true, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// no wall in front?
if (tr.flFraction >= 1.0f)
sprayOrigin.z -= 128.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_entity = sprayOrigin;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (GetTask ()->time - 0.5f < GetWorldTime ())
{
// emit spraycan sound
EMIT_SOUND_DYN2 (GetEntity (), CHAN_VOICE, "player/sprayer.wav", 1.0, ATTN_NORM, 0, 100.0f);
TraceLine (EyePosition (), EyePosition () + g_pGlobals->v_forward * 128.0f, true, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// paint the actual logo decal
DecalTrace (pev, &tr, m_logotypeIndex);
m_timeLogoSpray = GetWorldTime () + Random.Float (30.0f, 45.0f);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_moveToGoal = false;
m_checkTerrain = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_navTimeset = GetWorldTime ();
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_HuntEnemy (void)
{
m_aimFlags |= AIM_NAVPOINT;
m_checkTerrain = true;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if we've got new enemy...
if (!IsEntityNull (m_enemy) || IsEntityNull (m_lastEnemy))
{
// forget about it...
TaskComplete ();
m_prevGoalIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_lastEnemy = NULL;
m_lastEnemyOrigin.Zero ();
2015-07-17 19:23:31 +03:00
}
else if (GetTeam (m_lastEnemy) == m_team)
{
// don't hunt down our teammate...
RemoveCertainTask (TASK_HUNTENEMY);
m_prevGoalIndex = -1;
}
else if (DoWaypointNav ()) // reached last enemy pos?
{
// forget about it...
TaskComplete ();
m_prevGoalIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_lastEnemy = NULL;
m_lastEnemyOrigin.Zero ();
2015-07-17 19:23:31 +03:00
}
else if (!GoalIsValid ()) // do we need to calculate a new path?
{
DeleteSearchNodes ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
int destIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// is there a remembered index?
if (GetTask ()->data != -1 && GetTask ()->data < g_numWaypoints)
destIndex = GetTask ()->data;
else // no. we need to find a new one
2015-07-24 15:10:51 +03:00
destIndex = waypoints.FindNearest (m_lastEnemyOrigin);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// remember index
m_prevGoalIndex = destIndex;
GetTask ()->data = destIndex;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (destIndex != m_currentWaypointIndex)
FindPath (m_currentWaypointIndex, destIndex, m_pathType);
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bots skill higher than 60?
if (yb_walking_allowed.GetBool () && mp_footsteps.GetBool () && m_difficulty >= 1 && !yb_jasonmode.GetBool ())
{
// then make him move slow if near enemy
if (!(m_currentTravelFlags & PATHFLAG_JUMP))
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (m_currentWaypointIndex != -1)
2014-07-30 14:17:46 +04:00
{
if (m_currentPath->radius < 32 && !IsOnLadder () && !IsInWater () && m_seeEnemyTime + 4.0f > GetWorldTime () && m_difficulty < 2)
2015-07-17 19:23:31 +03:00
pev->button |= IN_DUCK;
2014-07-30 14:17:46 +04:00
}
if ((m_lastEnemyOrigin - pev->origin).GetLength () < 512.0f)
2015-07-17 19:23:31 +03:00
m_moveSpeed = GetWalkSpeed ();
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_SeekCover (void)
{
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsEntityNull (m_lastEnemy) || !IsAlive (m_lastEnemy))
{
TaskComplete ();
m_prevGoalIndex = -1;
}
else if (DoWaypointNav ()) // reached final cover waypoint?
{
// yep. activate hide behaviour
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = -1;
m_pathType = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// start hide task
PushTask (TASK_HIDE, TASKPRI_HIDE, -1, GetWorldTime () + Random.Float (5.0f, 15.0f), false);
2015-07-17 19:23:31 +03:00
Vector dest = m_lastEnemyOrigin;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// get a valid look direction
GetCampDirection (&dest);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_aimFlags |= AIM_CAMP;
m_camp = dest;
m_campDirection = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// chosen waypoint is a camp waypoint?
if (m_currentPath->flags & FLAG_CAMP)
{
// use the existing camp wpt prefs
if (m_currentPath->flags & FLAG_CROUCH)
m_campButtons = IN_DUCK;
else
m_campButtons = 0;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// choose a crouch or stand pos
if (m_currentPath->vis.crouch <= m_currentPath->vis.stand)
m_campButtons = IN_DUCK;
2014-07-30 14:17:46 +04:00
else
2015-07-17 19:23:31 +03:00
m_campButtons = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// enter look direction from previously calculated positions
m_currentPath->campStartX = dest.x;
m_currentPath->campStartY = dest.y;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_currentPath->campStartX = dest.x;
m_currentPath->campEndY = dest.y;
2014-07-30 14:17:46 +04:00
}
if (m_reloadState == RELOAD_NONE && GetAmmoInClip () < 8 && GetAmmo () != 0)
2015-07-17 19:23:31 +03:00
m_reloadState = RELOAD_PRIMARY;
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
m_moveToGoal = false;
2015-07-17 19:23:31 +03:00
m_checkTerrain = true;
}
else if (!GoalIsValid ()) // we didn't choose a cover waypoint yet or lost it due to an attack?
{
DeleteSearchNodes ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
int destIndex = -1;
2015-06-20 11:45:59 +03:00
2015-07-17 19:23:31 +03:00
if (GetTask ()->data != -1)
destIndex = GetTask ()->data;
2014-07-30 14:17:46 +04:00
else
{
2015-07-17 19:23:31 +03:00
destIndex = FindCoverWaypoint (1024.0f);
if (destIndex == -1)
2015-07-24 15:10:51 +03:00
destIndex = waypoints.FindNearest (pev->origin, 512.0f);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
m_campDirection = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = destIndex;
GetTask ()->data = destIndex;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (destIndex != m_currentWaypointIndex)
FindPath (m_currentWaypointIndex, destIndex, 0);
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Attack (void)
{
m_moveToGoal = false;
m_checkTerrain = false;
if (!IsEntityNull (m_enemy))
{
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsOnLadder ())
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
pev->button |= IN_JUMP;
DeleteSearchNodes ();
}
CombatFight ();
}
else
{
TaskComplete ();
m_destOrigin = m_lastEnemyOrigin;
}
m_navTimeset = GetWorldTime ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Pause (void)
{
m_moveToGoal = false;
m_checkTerrain = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_navTimeset = GetWorldTime ();
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// is bot blinded and above average difficulty?
if (m_viewDistance < 500.0f && m_difficulty >= 2)
2015-07-17 19:23:31 +03:00
{
// go mad!
m_moveSpeed = -fabsf ((m_viewDistance - 500.0f) * 0.5f);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_moveSpeed < -pev->maxspeed)
m_moveSpeed = -pev->maxspeed;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
MakeVectors (pev->v_angle);
m_camp = EyePosition () + g_pGlobals->v_forward * 500.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_aimFlags |= AIM_OVERRIDE;
m_wantsToFire = true;
}
else
pev->button |= m_campButtons;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// stop camping if time over or gets hurt by something else than bullets
if (GetTask ()->time < GetWorldTime () || m_lastDamageType > 0)
TaskComplete ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Blinded (void)
{
m_moveToGoal = false;
m_checkTerrain = false;
m_navTimeset = GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if bot remembers last enemy position
if (m_difficulty >= 2 && !m_lastEnemyOrigin.IsZero () && IsValidPlayer (m_lastEnemy) && !UsesSniper ())
2015-07-17 19:23:31 +03:00
{
m_lookAt = m_lastEnemyOrigin; // face last enemy
m_wantsToFire = true; // and shoot it
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_moveSpeed = m_blindMoveSpeed;
m_strafeSpeed = m_blindSidemoveSpeed;
pev->button |= m_blindButton;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_blindTime < GetWorldTime ())
TaskComplete ();
}
2015-06-04 11:52:48 +03:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Camp (void)
{
if (!yb_camping_allowed.GetBool ())
{
TaskComplete ();
return;
}
2015-07-17 19:23:31 +03:00
m_aimFlags |= AIM_CAMP;
m_checkTerrain = false;
m_moveToGoal = false;
2014-07-30 14:17:46 +04:00
if (m_team == TEAM_CF && g_bombPlanted && m_defendedBomb && !IsBombDefusing (waypoints.GetBombPosition ()) && !OutOfBombTimer ())
2015-07-17 19:23:31 +03:00
{
m_defendedBomb = false;
TaskComplete ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// half the reaction time if camping because you're more aware of enemies if camping
SetIdealReactionTimes ();
m_idealReactionTime *= 0.5f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_navTimeset = GetWorldTime ();
m_timeCamping = GetWorldTime ();
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2015-07-17 19:23:31 +03:00
GetValidWaypoint ();
if (m_nextCampDirTime < GetWorldTime ())
{
m_nextCampDirTime = GetWorldTime () + Random.Float (2.0f, 5.0f);
2015-07-17 19:23:31 +03:00
if (m_currentPath->flags & FLAG_CAMP)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
Vector dest;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// switch from 1 direction to the other
if (m_campDirection < 1)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
dest.x = m_currentPath->campStartX;
dest.y = m_currentPath->campStartY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_campDirection ^= 1;
}
else
{
dest.x = m_currentPath->campEndX;
dest.y = m_currentPath->campEndY;
m_campDirection ^= 1;
}
dest.z = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// find a visible waypoint to this direction...
// i know this is ugly hack, but i just don't want to break compatiability :)
int numFoundPoints = 0;
int foundPoints[3];
int distanceTab[3];
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
const Vector &dotA = (dest - pev->origin).Normalize2D ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
for (int i = 0; i < g_numWaypoints; i++)
{
// skip invisible waypoints or current waypoint
2015-07-24 15:10:51 +03:00
if (!waypoints.IsVisible (m_currentWaypointIndex, i) || (i == m_currentWaypointIndex))
2015-07-17 19:23:31 +03:00
continue;
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
const Vector &dotB = (waypoints.GetPath (i)->origin - pev->origin).Normalize2D ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if ((dotA | dotB) > 0.9)
{
2015-07-24 15:10:51 +03:00
int distance = static_cast <int> ((pev->origin - waypoints.GetPath (i)->origin).GetLength ());
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (numFoundPoints >= 3)
{
for (int j = 0; j < 3; j++)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (distance > distanceTab[j])
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
distanceTab[j] = distance;
foundPoints[j] = i;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
break;
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
}
else
{
foundPoints[numFoundPoints] = i;
distanceTab[numFoundPoints] = distance;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
numFoundPoints++;
2014-07-30 14:17:46 +04:00
}
}
}
2015-07-17 19:23:31 +03:00
if (--numFoundPoints >= 0)
2015-07-24 15:10:51 +03:00
m_camp = waypoints.GetPath (foundPoints[Random.Long (0, numFoundPoints)])->origin;
2014-07-30 14:17:46 +04:00
else
2015-07-24 15:10:51 +03:00
m_camp = waypoints.GetPath (GetAimingWaypoint ())->origin;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
2015-07-24 15:10:51 +03:00
m_camp = waypoints.GetPath (GetAimingWaypoint ())->origin;
2015-07-17 19:23:31 +03:00
}
// press remembered crouch button
pev->button |= m_campButtons;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// stop camping if time over or gets hurt by something else than bullets
if (GetTask ()->time < GetWorldTime () || m_lastDamageType > 0)
2015-07-17 19:23:31 +03:00
TaskComplete ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Hide (void)
{
m_aimFlags |= AIM_CAMP;
m_checkTerrain = false;
m_moveToGoal = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// half the reaction time if camping
SetIdealReactionTimes ();
m_idealReactionTime *= 0.5f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_navTimeset = GetWorldTime ();
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
GetValidWaypoint ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (HasShield () && !m_isReloading)
{
if (!IsShieldDrawn ())
pev->button |= IN_ATTACK2; // draw the shield!
else
pev->button |= IN_DUCK; // duck under if the shield is already drawn
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if we see an enemy and aren't at a good camping point leave the spot
if ((m_states & STATE_SEEING_ENEMY) || m_inBombZone)
{
if (!(m_currentPath->flags & FLAG_CAMP))
2014-07-30 14:17:46 +04:00
{
TaskComplete ();
m_campButtons = 0;
m_prevGoalIndex = -1;
2015-07-17 19:23:31 +03:00
if (!IsEntityNull (m_enemy))
CombatFight ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else if (m_lastEnemyOrigin.IsZero ()) // If we don't have an enemy we're also free to leave
2015-07-17 19:23:31 +03:00
{
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_campButtons = 0;
m_prevGoalIndex = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (GetTaskId () == TASK_HIDE)
2014-07-30 14:17:46 +04:00
TaskComplete ();
2015-07-17 19:23:31 +03:00
return;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
pev->button |= m_campButtons;
m_navTimeset = GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// stop camping if time over or gets hurt by something else than bullets
if (GetTask ()->time < GetWorldTime () || m_lastDamageType > 0)
TaskComplete ();
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_MoveToPos (void)
{
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsShieldDrawn ())
pev->button |= IN_ATTACK2;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (DoWaypointNav ()) // reached destination?
{
TaskComplete (); // we're done
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = -1;
m_position.Zero ();
2015-07-17 19:23:31 +03:00
}
else if (!GoalIsValid ()) // didn't choose goal waypoint yet?
{
DeleteSearchNodes ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
int destIndex = -1;
if (GetTask ()->data != -1 && GetTask ()->data < g_numWaypoints)
destIndex = GetTask ()->data;
else
2015-07-24 15:10:51 +03:00
destIndex = waypoints.FindNearest (m_position);
2015-07-17 19:23:31 +03:00
if (destIndex >= 0 && destIndex < g_numWaypoints)
{
m_prevGoalIndex = destIndex;
GetTask ()->data = destIndex;
FindPath (m_currentWaypointIndex, destIndex, m_pathType);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
TaskComplete ();
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_PlantBomb (void)
{
m_aimFlags |= AIM_CAMP;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_hasC4) // we're still got the C4?
{
SelectWeaponByName ("weapon_c4");
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsAlive (m_enemy) || !m_inBombZone)
TaskComplete ();
else
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_moveToGoal = false;
m_checkTerrain = false;
m_navTimeset = GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_currentPath->flags & FLAG_CROUCH)
pev->button |= (IN_ATTACK | IN_DUCK);
2014-07-30 14:17:46 +04:00
else
2015-07-17 19:23:31 +03:00
pev->button |= IN_ATTACK;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_moveSpeed = 0;
m_strafeSpeed = 0;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else // done with planting
{
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// tell teammates to move over here...
if (GetNearbyFriendsNearPosition (pev->origin, 1200) != 0)
RadioMessage (Radio_NeedBackup);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
DeleteSearchNodes ();
int index = FindDefendWaypoint (pev->origin);
2014-07-30 14:17:46 +04:00
float guardTime = mp_c4timer.GetFloat () * 0.5f + mp_c4timer.GetFloat () * 0.25f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// push camp task on to stack
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + guardTime, true);
2015-07-17 19:23:31 +03:00
// push move command
PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + guardTime, true);
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand)
2015-07-17 19:23:31 +03:00
m_campButtons |= IN_DUCK;
else
m_campButtons &= ~IN_DUCK;
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_DefuseBomb (void)
{
float fullDefuseTime = m_hasDefuser ? 7.0f : 12.0f;
2015-07-17 19:23:31 +03:00
float timeToBlowUp = GetBombTimeleft ();
float defuseRemainingTime = fullDefuseTime;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_hasProgressBar /*&& IsOnFloor ()*/)
defuseRemainingTime = fullDefuseTime - GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
bool defuseError = false;
// exception: bomb has been defused
if (waypoints.GetBombPosition ().IsZero ())
2015-07-17 19:23:31 +03:00
{
defuseError = true;
g_bombPlanted = false;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (Random.Long (0, 100) < 50 && m_numFriendsLeft != 0)
{
if (timeToBlowUp <= 3.0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (yb_communication_type.GetInt () == 2)
InstantChatterMessage (Chatter_BarelyDefused);
else if (yb_communication_type.GetInt () == 1)
2014-07-30 14:17:46 +04:00
RadioMessage (Radio_SectorClear);
}
2015-07-17 19:23:31 +03:00
else
RadioMessage (Radio_SectorClear);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
else if (defuseRemainingTime > timeToBlowUp) // exception: not time left for defusing
defuseError = true;
else if (IsValidPlayer (m_enemy))
2015-07-17 19:23:31 +03:00
{
int friends = GetNearbyFriendsNearPosition (pev->origin, 768.0f);
2014-07-30 14:17:46 +04:00
if (friends < 2 && defuseRemainingTime < timeToBlowUp)
2014-07-30 14:17:46 +04:00
{
defuseError = true;
2014-07-30 14:17:46 +04:00
if (defuseRemainingTime + 2.0f > timeToBlowUp)
defuseError = false;
if (m_numFriendsLeft > friends)
RadioMessage (Radio_NeedBackup);
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// one of exceptions is thrown. finish task.
if (defuseError)
{
m_checkTerrain = true;
m_moveToGoal = true;
2014-07-30 14:17:46 +04:00
m_destOrigin.Zero ();
m_entity.Zero ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
TaskComplete ();
return;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// to revert from pause after reload waiting && just to be sure
m_moveToGoal = false;
m_checkTerrain = true;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_moveSpeed = pev->maxspeed;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot is reloading and we close enough to start defusing
if (m_isReloading && (waypoints.GetBombPosition () - pev->origin).GetLength2D () < 80.0f)
2015-07-17 19:23:31 +03:00
{
if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((GetAmmoInClip () > 8 && m_reloadState == RELOAD_PRIMARY) || (GetAmmoInClip () > 5 && m_reloadState == RELOAD_SECONDARY)))
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
int weaponIndex = GetHighestWeapon ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// just select knife and then select weapon
SelectWeaponByName ("weapon_knife");
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (weaponIndex > 0 && weaponIndex < NUM_WEAPONS)
SelectWeaponbyNumber (weaponIndex);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_isReloading = false;
}
else // just wait here
{
m_moveToGoal = false;
m_checkTerrain = false;
2014-07-30 14:17:46 +04:00
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// head to bomb and press use button
m_aimFlags |= AIM_ENTITY;
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
m_destOrigin = waypoints.GetBombPosition ();
m_entity = waypoints.GetBombPosition ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
pev->button |= IN_USE;
// if defusing is not already started, maybe crouch before
if (!m_hasProgressBar && m_duckDefuseCheckTime < GetWorldTime ())
{
if (m_difficulty >= 2 && m_numEnemiesLeft != 0)
m_duckDefuse = true;
Vector botDuckOrigin, botStandOrigin;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (pev->button & IN_DUCK)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
botDuckOrigin = pev->origin;
botStandOrigin = pev->origin + Vector (0.0f, 0.0f, 18.0f);
2015-07-17 19:23:31 +03:00
}
else
{
botDuckOrigin = pev->origin - Vector (0.0f, 0.0f, 18.0f);
2015-07-17 19:23:31 +03:00
botStandOrigin = pev->origin;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
float duckLength = (m_entity - botDuckOrigin).GetLengthSquared ();
float standLength = (m_entity - botStandOrigin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
if (duckLength > 5625.0f || standLength > 5625.0f)
2015-07-17 19:23:31 +03:00
{
if (standLength < duckLength)
m_duckDefuse = false; // stand
2014-07-30 14:17:46 +04:00
else
2015-07-17 19:23:31 +03:00
m_duckDefuse = true; // duck
}
m_duckDefuseCheckTime = GetWorldTime () + 1.5f;
2015-07-17 19:23:31 +03:00
}
// press duck button
if (m_duckDefuse || (pev->oldbuttons & IN_DUCK))
pev->button |= IN_DUCK;
else
pev->button &= ~IN_DUCK;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// we are defusing bomb
if (m_hasProgressBar)
{
pev->button |= IN_USE;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_reloadState = RELOAD_NONE;
m_navTimeset = GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// don't move when defusing
m_moveToGoal = false;
m_checkTerrain = false;
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// notify team
if (m_numFriendsLeft != 0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
ChatterMessage (Chatter_DefusingC4);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (GetNearbyFriendsNearPosition (pev->origin, 512.0f) < 2)
RadioMessage (Radio_NeedBackup);
}
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_FollowUser (void)
{
if (IsEntityNull (m_targetEntity) || !IsAlive (m_targetEntity))
{
m_targetEntity = NULL;
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_targetEntity->v.button & IN_ATTACK)
{
MakeVectors (m_targetEntity->v.v_angle);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
TraceResult tr;
TraceLine (m_targetEntity->v.origin + m_targetEntity->v.view_ofs, g_pGlobals->v_forward * 500.0f, true, true, GetEntity (), &tr);
2015-07-17 19:23:31 +03:00
if (!IsEntityNull (tr.pHit) && IsValidPlayer (tr.pHit) && GetTeam (tr.pHit) != m_team)
2014-07-30 14:17:46 +04:00
{
m_targetEntity = NULL;
2015-07-17 19:23:31 +03:00
m_lastEnemy = tr.pHit;
m_lastEnemyOrigin = tr.pHit->v.origin;
2014-07-30 14:17:46 +04:00
TaskComplete ();
2015-07-17 19:23:31 +03:00
return;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_targetEntity->v.maxspeed != 0 && m_targetEntity->v.maxspeed < pev->maxspeed)
m_moveSpeed = m_targetEntity->v.maxspeed;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_reloadState == RELOAD_NONE && GetAmmo () != 0)
m_reloadState = RELOAD_PRIMARY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if ((m_targetEntity->v.origin - pev->origin).GetLength () > 130)
m_followWaitTime = 0.0f;
2015-07-17 19:23:31 +03:00
else
{
m_moveSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
if (m_followWaitTime == 0.0f)
2015-07-17 19:23:31 +03:00
m_followWaitTime = GetWorldTime ();
2014-07-30 14:17:46 +04:00
else
{
if (m_followWaitTime + 3.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// stop following if we have been waiting too long
m_targetEntity = NULL;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
RadioMessage (Radio_YouTakePoint);
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return;
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
}
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (yb_walking_allowed.GetBool () && m_targetEntity->v.maxspeed < m_moveSpeed && !yb_jasonmode.GetBool ())
m_moveSpeed = GetWalkSpeed ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsShieldDrawn ())
pev->button |= IN_ATTACK2;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (DoWaypointNav ()) // reached destination?
GetTask ()->data = -1;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!GoalIsValid ()) // didn't choose goal waypoint yet?
{
DeleteSearchNodes ();
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
int destIndex = waypoints.FindNearest (m_targetEntity->v.origin);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
Array <int> points;
waypoints.FindInRadius (points, 200.0f, m_targetEntity->v.origin);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
while (!points.IsEmpty ())
{
int newIndex = points.Pop ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if waypoint not yet used, assign it as dest
if (!IsPointOccupied (newIndex) && newIndex != m_currentWaypointIndex)
2015-07-17 19:23:31 +03:00
destIndex = newIndex;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
if (destIndex >= 0 && destIndex < g_numWaypoints && destIndex != m_currentWaypointIndex && m_currentWaypointIndex >= 0 && m_currentWaypointIndex < g_numWaypoints)
{
m_prevGoalIndex = destIndex;
GetTask ()->data = destIndex;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// always take the shortest path
FindShortestPath (m_currentWaypointIndex, destIndex);
}
else
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_targetEntity = NULL;
TaskComplete ();
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Throw_HE (void)
{
m_aimFlags |= AIM_GRENADE;
Vector dest = m_throw;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!(m_states & STATE_SEEING_ENEMY))
{
m_strafeSpeed = 0.0f;
m_moveSpeed = 0.0f;
m_moveToGoal = false;
}
else if (!(m_states & STATE_SUSPECT_ENEMY) && !IsEntityNull (m_enemy))
dest = m_enemy->v.origin + m_enemy->v.velocity.Get2D () * 0.5f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_isUsingGrenade = true;
m_checkTerrain = false;
2014-07-30 14:17:46 +04:00
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
if ((pev->origin - dest).GetLengthSquared () < GET_SQUARE (400.0f))
2015-07-17 19:23:31 +03:00
{
// heck, I don't wanna blow up myself
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
SelectBestWeapon ();
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return;
}
m_grenade = CheckThrow (EyePosition (), dest);
2014-07-30 14:17:46 +04:00
if (m_grenade.GetLengthSquared () < 100.0f)
2015-07-17 19:23:31 +03:00
m_grenade = CheckToss (EyePosition (), dest);
2014-07-30 14:17:46 +04:00
if (m_grenade.GetLengthSquared () <= 100.0f)
2015-07-17 19:23:31 +03:00
{
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
m_grenade = m_lookAt;
SelectBestWeapon ();
TaskComplete ();
}
else
{
edict_t *ent = NULL;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
while (!IsEntityNull (ent = FIND_ENTITY_BY_CLASSNAME (ent, "grenade")))
{
if (ent->v.owner == GetEntity () && strcmp (STRING (ent->v.model) + 9, "hegrenade.mdl") == 0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// set the correct velocity for the grenade
if (m_grenade.GetLengthSquared () > 100.0f)
2015-07-17 19:23:31 +03:00
ent->v.velocity = m_grenade;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
SelectBestWeapon ();
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
break;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsEntityNull (ent))
{
if (m_currentWeapon != WEAPON_EXPLOSIVE)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (pev->weapons & (1 << WEAPON_EXPLOSIVE))
SelectWeaponByName ("weapon_hegrenade");
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else if (!(pev->oldbuttons & IN_ATTACK))
pev->button |= IN_ATTACK;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
pev->button |= m_campButtons;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Throw_FL (void)
{
m_aimFlags |= AIM_GRENADE;
Vector dest = m_throw;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!(m_states & STATE_SEEING_ENEMY))
{
m_strafeSpeed = 0.0f;
m_moveSpeed = 0.0f;
}
else if (!(m_states & STATE_SUSPECT_ENEMY) && !IsEntityNull (m_enemy))
dest = m_enemy->v.origin + m_enemy->v.velocity.Get2D () * 0.5;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_isUsingGrenade = true;
m_checkTerrain = false;
2014-07-30 14:17:46 +04:00
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
2015-07-17 19:23:31 +03:00
m_grenade = CheckThrow (EyePosition (), dest);
2014-07-30 14:17:46 +04:00
if (m_grenade.GetLengthSquared () < 100.0f)
2015-07-17 19:23:31 +03:00
m_grenade = CheckToss (pev->origin, dest);
2014-07-30 14:17:46 +04:00
if (m_grenade.GetLengthSquared () <= 100.0f)
2015-07-17 19:23:31 +03:00
{
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
m_grenade = m_lookAt;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
SelectBestWeapon ();
TaskComplete ();
}
else
{
edict_t *ent = NULL;
while (!IsEntityNull (ent = FIND_ENTITY_BY_CLASSNAME (ent, "grenade")))
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (ent->v.owner == GetEntity () && strcmp (STRING (ent->v.model) + 9, "flashbang.mdl") == 0)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// set the correct velocity for the grenade
if (m_grenade.GetLengthSquared () > 100.0f)
2015-07-17 19:23:31 +03:00
ent->v.velocity = m_grenade;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_grenadeCheckTime = GetWorldTime () + MAX_GRENADE_TIMER;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
SelectBestWeapon ();
TaskComplete ();
break;
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
if (IsEntityNull (ent))
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (m_currentWeapon != WEAPON_FLASHBANG)
{
if (pev->weapons & (1 << WEAPON_FLASHBANG))
SelectWeaponByName ("weapon_flashbang");
}
else if (!(pev->oldbuttons & IN_ATTACK))
pev->button |= IN_ATTACK;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
}
pev->button |= m_campButtons;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_Throw_SG (void)
{
m_aimFlags |= AIM_GRENADE;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!(m_states & STATE_SEEING_ENEMY))
{
m_strafeSpeed = 0.0f;
m_moveSpeed = 0.0f;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_checkTerrain = false;
m_isUsingGrenade = true;
2014-07-30 14:17:46 +04:00
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly ();
2015-07-17 19:23:31 +03:00
Vector src = m_lastEnemyOrigin - pev->velocity;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// predict where the enemy is in 0.5 secs
if (!IsEntityNull (m_enemy))
src = src + m_enemy->v.velocity * 0.5f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_grenade = (src - EyePosition ()).Normalize ();
if (GetTask ()->time < GetWorldTime () + 0.5f)
2015-07-17 19:23:31 +03:00
{
m_aimFlags &= ~AIM_GRENADE;
m_states &= ~STATE_THROW_SG;
TaskComplete ();
return;
}
if (m_currentWeapon != WEAPON_SMOKE)
{
if (pev->weapons & (1 << WEAPON_SMOKE))
{
SelectWeaponByName ("weapon_smokegrenade");
GetTask ()->time = GetWorldTime () + MAX_GRENADE_TIMER;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
GetTask ()->time = GetWorldTime () + 0.1f;
2015-07-17 19:23:31 +03:00
}
else if (!(pev->oldbuttons & IN_ATTACK))
pev->button |= IN_ATTACK;
}
void Bot::RunTask_DoubleJump (void)
{
2015-07-24 15:10:51 +03:00
if (IsEntityNull (m_doubleJumpEntity) || !IsAlive (m_doubleJumpEntity) || (m_aimFlags & AIM_ENEMY) || (m_travelStartIndex != -1 && GetTask ()->time + (waypoints.GetTravelTime (pev->maxspeed, waypoints.GetPath (m_travelStartIndex)->origin, m_doubleJumpOrigin) + 11.0) < GetWorldTime ()))
2015-07-17 19:23:31 +03:00
{
ResetDoubleJumpState ();
return;
}
m_aimFlags |= AIM_NAVPOINT;
if (m_jumpReady)
{
m_moveToGoal = false;
m_checkTerrain = false;
m_navTimeset = GetWorldTime ();
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2015-07-17 19:23:31 +03:00
if (m_duckForJump < GetWorldTime ())
pev->button |= IN_DUCK;
2014-07-30 14:17:46 +04:00
MakeVectors (Vector::GetZero ());
2015-07-17 19:23:31 +03:00
Vector dest = EyePosition () + g_pGlobals->v_forward * 500.0f;
dest.z = 180.0f;
2015-07-17 19:23:31 +03:00
TraceResult tr;
TraceLine (EyePosition (), dest, false, true, GetEntity (), &tr);
if (tr.flFraction < 1.0f && tr.pHit == m_doubleJumpEntity)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (m_doubleJumpEntity->v.button & IN_JUMP)
2014-07-30 14:17:46 +04:00
{
m_duckForJump = GetWorldTime () + Random.Float (3.0f, 5.0f);
2015-07-17 19:23:31 +03:00
GetTask ()->time = GetWorldTime ();
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
return;
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_currentWaypointIndex == m_prevGoalIndex)
{
m_waypointOrigin = m_doubleJumpOrigin;
m_destOrigin = m_doubleJumpOrigin;
}
if (DoWaypointNav ()) // reached destination?
GetTask ()->data = -1;
if (!GoalIsValid ()) // didn't choose goal waypoint yet?
{
DeleteSearchNodes ();
2015-07-24 15:10:51 +03:00
int destIndex = waypoints.FindNearest (m_doubleJumpOrigin);
2015-07-17 19:23:31 +03:00
if (destIndex >= 0 && destIndex < g_numWaypoints)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = destIndex;
GetTask ()->data = destIndex;
m_travelStartIndex = m_currentWaypointIndex;
// Always take the shortest path
FindShortestPath (m_currentWaypointIndex, destIndex);
if (m_currentWaypointIndex == destIndex)
m_jumpReady = true;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
ResetDoubleJumpState ();
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_EscapeFromBomb (void)
{
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (!g_bombPlanted)
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (IsShieldDrawn ())
pev->button |= IN_ATTACK2;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0)
SelectWeaponByName ("weapon_knife");
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (DoWaypointNav ()) // reached destination?
{
TaskComplete (); // we're done
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// press duck button if we still have some enemies
if (GetNearbyEnemiesNearPosition (pev->origin, 2048.0f))
2015-07-17 19:23:31 +03:00
m_campButtons = IN_DUCK;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// we're reached destination point so just sit down and camp
PushTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + 10.0f, true);
2015-07-17 19:23:31 +03:00
}
else if (!GoalIsValid ()) // didn't choose goal waypoint yet?
{
DeleteSearchNodes ();
int lastSelectedGoal = -1, minPathDistance = 99999;
float safeRadius = Random.Float (1248.0f, 2048.0f);
2015-07-17 19:23:31 +03:00
for (int i = 0; i < g_numWaypoints; i++)
{
2015-07-24 15:10:51 +03:00
if ((waypoints.GetPath (i)->origin - waypoints.GetBombPosition ()).GetLength () < safeRadius || IsPointOccupied (i))
2015-07-17 19:23:31 +03:00
continue;
int pathDistance = waypoints.GetPathDistance (m_currentWaypointIndex, i);
2015-07-17 19:23:31 +03:00
if (minPathDistance > pathDistance)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
minPathDistance = pathDistance;
lastSelectedGoal = i;
2014-07-30 14:17:46 +04:00
}
}
2015-07-17 19:23:31 +03:00
if (lastSelectedGoal < 0)
2015-07-24 15:10:51 +03:00
lastSelectedGoal = waypoints.FindFarest (pev->origin, safeRadius);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_prevGoalIndex = lastSelectedGoal;
GetTask ()->data = lastSelectedGoal;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
FindShortestPath (m_currentWaypointIndex, lastSelectedGoal);
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_ShootBreakable (void)
{
m_aimFlags |= AIM_OVERRIDE;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// Breakable destroyed?
if (IsEntityNull (FindBreakable ()))
{
TaskComplete ();
return;
}
pev->button |= m_campButtons;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_checkTerrain = false;
m_moveToGoal = false;
m_navTimeset = GetWorldTime ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
Vector src = m_breakable;
m_camp = src;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// is bot facing the breakable?
if (GetShootingConeDeviation (GetEntity (), &src) >= 0.90f)
2015-07-17 19:23:31 +03:00
{
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_currentWeapon == WEAPON_KNIFE)
SelectBestWeapon ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_wantsToFire = true;
}
else
{
m_checkTerrain = true;
m_moveToGoal = true;
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask_PickupItem ()
{
if (IsEntityNull (m_pickupItem))
{
m_pickupItem = NULL;
TaskComplete ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return;
}
Vector dest = GetEntityOrigin (m_pickupItem);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_destOrigin = dest;
m_entity = dest;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// find the distance to the item
float itemDistance = (dest - pev->origin).GetLength ();
2014-07-30 14:17:46 +04:00
2015-07-17 20:44:47 +03:00
int id = 0;
2015-07-17 19:23:31 +03:00
switch (m_pickupType)
{
case PICKUP_WEAPON:
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// near to weapon?
if (itemDistance < 50.0f)
2015-07-17 19:23:31 +03:00
{
2015-07-17 20:44:47 +03:00
for (id = 0; id < 7; id++)
2014-07-30 14:17:46 +04:00
{
2015-07-17 20:44:47 +03:00
if (strcmp (g_weaponSelect[id].modelName, STRING (m_pickupItem->v.model) + 9) == 0)
2015-07-17 19:23:31 +03:00
break;
}
2014-07-30 14:17:46 +04:00
2015-07-17 20:44:47 +03:00
if (id < 7)
2015-07-17 19:23:31 +03:00
{
// secondary weapon. i.e., pistol
int weaponID = 0;
2014-07-30 14:17:46 +04:00
2015-07-17 20:44:47 +03:00
for (id = 0; id < 7; id++)
2014-07-30 14:17:46 +04:00
{
2015-07-17 20:44:47 +03:00
if (pev->weapons & (1 << g_weaponSelect[id].id))
weaponID = id;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
if (weaponID > 0)
{
SelectWeaponbyNumber (weaponID);
FakeClientCommand (GetEntity (), "drop");
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (HasShield ()) // If we have the shield...
FakeClientCommand (GetEntity (), "drop"); // discard both shield and pistol
}
EquipInBuyzone (0);
}
else
{
// primary weapon
int weaponID = GetHighestWeapon ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if ((weaponID > 6) || HasShield ())
{
SelectWeaponbyNumber (weaponID);
FakeClientCommand (GetEntity (), "drop");
}
EquipInBuyzone (0);
}
CheckSilencer (); // check the silencer
2014-07-30 14:17:46 +04:00
}
break;
2015-07-17 19:23:31 +03:00
case PICKUP_SHIELD:
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (HasShield ())
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_pickupItem = NULL;
2014-07-30 14:17:46 +04:00
break;
}
else if (itemDistance < 50.0f) // near to shield?
2015-07-17 19:23:31 +03:00
{
// get current best weapon to check if it's a primary in need to be dropped
int weaponID = GetHighestWeapon ();
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (weaponID > 6)
{
SelectWeaponbyNumber (weaponID);
FakeClientCommand (GetEntity (), "drop");
}
}
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
case PICKUP_PLANTED_C4:
m_aimFlags |= AIM_ENTITY;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_team == TEAM_CF && itemDistance < 80.0f)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
ChatterMessage (Chatter_DefusingC4);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// notify team of defusing
if (m_numFriendsLeft < 3)
RadioMessage (Radio_NeedBackup);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_moveToGoal = false;
m_checkTerrain = false;
m_moveSpeed = 0;
m_strafeSpeed = 0;
PushTask (TASK_DEFUSEBOMB, TASKPRI_DEFUSEBOMB, -1, 0.0f, false);
2014-07-30 14:17:46 +04:00
}
break;
2015-07-17 19:23:31 +03:00
case PICKUP_HOSTAGE:
m_aimFlags |= AIM_ENTITY;
if (!IsAlive (m_pickupItem))
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// don't pickup dead hostages
2014-07-30 14:17:46 +04:00
m_pickupItem = NULL;
TaskComplete ();
break;
}
if (itemDistance < 50.0f)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
float angleToEntity = InFieldOfView (dest - EyePosition ());
2014-07-30 14:17:46 +04:00
if (angleToEntity <= 10.0f) // bot faces hostage?
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
// use game dll function to make sure the hostage is correctly 'used'
MDLL_Use (m_pickupItem, GetEntity ());
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (Random.Long (0, 100) < 80)
ChatterMessage (Chatter_UseHostage);
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
for (int i = 0; i < MAX_HOSTAGES; i++)
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
if (IsEntityNull (m_hostages[i])) // store pointer to hostage so other bots don't steal from this one or bot tries to reuse it
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
m_hostages[i] = m_pickupItem;
m_pickupItem = NULL;
break;
2014-07-30 14:17:46 +04:00
}
}
}
2015-07-20 21:26:20 +03:00
IgnoreCollisionShortly (); // also don't consider being stuck
2015-07-17 19:23:31 +03:00
}
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
case PICKUP_DEFUSEKIT:
m_aimFlags |= AIM_NAVPOINT;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
if (m_hasDefuser)
{
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
}
break;
case PICKUP_BUTTON:
m_aimFlags |= AIM_ENTITY;
if (IsEntityNull (m_pickupItem) || m_buttonPushTime < GetWorldTime ()) // it's safer...
{
TaskComplete ();
m_pickupType = PICKUP_NONE;
2014-07-30 14:17:46 +04:00
break;
2015-07-17 19:23:31 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// find angles from bot origin to entity...
float angleToEntity = InFieldOfView (dest - EyePosition ());
2014-07-30 14:17:46 +04:00
if (itemDistance < 90.0f) // near to the button?
2015-07-17 19:23:31 +03:00
{
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
2015-07-17 19:23:31 +03:00
m_moveToGoal = false;
m_checkTerrain = false;
if (angleToEntity <= 10.0f) // facing it directly?
2014-07-30 14:17:46 +04:00
{
2015-07-17 19:23:31 +03:00
MDLL_Use (m_pickupItem, GetEntity ());
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
m_buttonPushTime = GetWorldTime () + 3.0f;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
TaskComplete ();
}
}
break;
}
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
void Bot::RunTask (void)
{
// this is core function that handle task execution
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
switch (GetTaskId ())
{
// normal task
default:
case TASK_NORMAL:
RunTask_Normal ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot sprays messy logos all over the place...
case TASK_SPRAY:
RunTask_Spray ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// hunt down enemy
case TASK_HUNTENEMY:
RunTask_HuntEnemy ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot seeks cover from enemy
case TASK_SEEKCOVER:
RunTask_SeekCover ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// plain attacking
case TASK_ATTACK:
RunTask_Attack ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// Bot is pausing
case TASK_PAUSE:
RunTask_Pause ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// blinded (flashbanged) behaviour
case TASK_BLINDED:
RunTask_Blinded ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// camping behaviour
case TASK_CAMP:
RunTask_Camp ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// hiding behaviour
case TASK_HIDE:
RunTask_Hide ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// moves to a position specified in position has a higher priority than task_normal
case TASK_MOVETOPOSITION:
RunTask_MoveToPos ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// planting the bomb right now
case TASK_PLANTBOMB:
RunTask_PlantBomb ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bomb defusing behaviour
case TASK_DEFUSEBOMB:
RunTask_DefuseBomb ();
break;
// follow user behaviour
case TASK_FOLLOWUSER:
RunTask_FollowUser ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// HE grenade throw behaviour
case TASK_THROWHEGRENADE:
RunTask_Throw_HE ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// flashbang throw behavior (basically the same code like for HE's)
case TASK_THROWFLASHBANG:
RunTask_Throw_FL ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// smoke grenade throw behavior
// a bit different to the others because it mostly tries to throw the sg on the ground
case TASK_THROWSMOKE:
RunTask_Throw_SG ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// bot helps human player (or other bot) to get somewhere
case TASK_DOUBLEJUMP:
RunTask_DoubleJump ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// escape from bomb behaviour
case TASK_ESCAPEFROMBOMB:
RunTask_EscapeFromBomb ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// shooting breakables in the way action
case TASK_SHOOTBREAKABLE:
RunTask_ShootBreakable ();
break;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// picking up items and stuff behaviour
case TASK_PICKUPITEM:
RunTask_PickupItem ();
2014-07-30 14:17:46 +04:00
break;
}
}
2014-08-15 21:58:43 +04:00
void Bot::CheckSpawnTimeConditions (void)
2014-07-30 14:17:46 +04:00
{
2014-08-15 21:58:43 +04:00
// this function is called instead of BotAI when buying finished, but freezetime is not yet left.
2014-07-30 14:17:46 +04:00
// switch to knife if time to do this
if (m_checkKnifeSwitch && !m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + Random.Float (4.0f, 6.5f) < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
if (Random.Long (1, 100) < 2 && yb_spraypaints.GetBool ())
PushTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, -1, GetWorldTime () + 1.0f, false);
2014-07-30 14:17:46 +04:00
if (m_difficulty >= 2 && Random.Long (0, 100) < (m_personality == PERSONALITY_RUSHER ? 99 : 50) && !m_isReloading && (g_mapType & (MAP_CS | MAP_DE | MAP_ES | MAP_AS)))
{
if (yb_jasonmode.GetBool ())
{
SelectPistol ();
FakeClientCommand (GetEntity (), "drop");
}
else
SelectWeaponByName ("weapon_knife");
}
2014-07-30 14:17:46 +04:00
m_checkKnifeSwitch = false;
if (Random.Long (0, 100) < yb_user_follow_percent.GetInt () && IsEntityNull (m_targetEntity) && !m_isLeader && !m_hasC4)
2014-07-30 14:17:46 +04:00
AttachToUser ();
}
// check if we already switched weapon mode
if (m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + Random.Float (2.0f, 3.5f) < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
if (HasShield () && IsShieldDrawn ())
pev->button |= IN_ATTACK2;
else
{
switch (m_currentWeapon)
{
case WEAPON_M4A1:
case WEAPON_USP:
CheckSilencer ();
break;
case WEAPON_FAMAS:
case WEAPON_GLOCK:
if (Random.Long (0, 100) < 50)
2014-07-30 14:17:46 +04:00
pev->button |= IN_ATTACK2;
break;
}
}
// select a leader bot for this team
2014-08-06 00:03:50 +04:00
SelectLeaderEachTeam (m_team);
2014-07-30 14:17:46 +04:00
m_checkWeaponSwitch = false;
2014-08-15 21:58:43 +04:00
}
}
2014-07-30 14:17:46 +04:00
2014-08-15 21:58:43 +04:00
void Bot::BotAI (void)
{
// this function gets called each frame and is the core of all bot ai. from here all other subroutines are called
2014-07-30 14:17:46 +04:00
2014-08-15 21:58:43 +04:00
float movedDistance; // length of different vector (distance bot moved)
TraceResult tr;
2014-07-30 14:17:46 +04:00
// increase reaction time
m_actualReactionTime += 0.3f;
2014-07-30 14:17:46 +04:00
if (m_actualReactionTime > m_idealReactionTime)
m_actualReactionTime = m_idealReactionTime;
// bot could be blinded by flashbang or smoke, recover from it
m_viewDistance += 3.0f;
2014-07-30 14:17:46 +04:00
if (m_viewDistance > m_maxViewDistance)
m_viewDistance = m_maxViewDistance;
if (m_blindTime > GetWorldTime ())
m_maxViewDistance = 4096.0f;
2014-07-30 14:17:46 +04:00
m_moveSpeed = pev->maxspeed;
if (m_prevTime <= GetWorldTime ())
{
// see how far bot has moved since the previous position...
movedDistance = (m_prevOrigin - pev->origin).GetLength ();
// save current position as previous
m_prevOrigin = pev->origin;
m_prevTime = GetWorldTime () + 0.2f;
2014-07-30 14:17:46 +04:00
}
else
movedDistance = 2.0f;
2014-07-30 14:17:46 +04:00
// if there's some radio message to respond, check it
if (m_radioOrder != 0)
CheckRadioCommands ();
// do all sensing, calculate/filter all actions here
SetConditions ();
// some stuff required by by chatter engine
2015-06-10 23:30:48 +03:00
if (yb_communication_type.GetInt () == 2)
2014-07-30 14:17:46 +04:00
{
2015-06-10 23:30:48 +03:00
if ((m_states & STATE_SEEING_ENEMY) && !IsEntityNull (m_enemy))
{
int hasFriendNearby = GetNearbyFriendsNearPosition (pev->origin, 512.0f);
if (!hasFriendNearby && Random.Long (0, 100) < 45 && (m_enemy->v.weapons & (1 << WEAPON_C4)))
2015-06-10 23:30:48 +03:00
ChatterMessage (Chatter_SpotTheBomber);
2014-07-30 14:17:46 +04:00
else if (!hasFriendNearby && Random.Long (0, 100) < 45 && m_team == TEAM_TF && IsPlayerVIP (m_enemy))
2015-06-10 23:30:48 +03:00
ChatterMessage (Chatter_VIPSpotted);
2014-07-30 14:17:46 +04:00
else if (!hasFriendNearby && Random.Long (0, 100) < 50 && GetTeam (m_enemy) != m_team && IsGroupOfEnemies (m_enemy->v.origin, 2, 384.0f))
2015-06-10 23:30:48 +03:00
ChatterMessage (Chatter_ScaredEmotion);
2014-07-30 14:17:46 +04:00
else if (!hasFriendNearby && Random.Long (0, 100) < 40 && ((m_enemy->v.weapons & (1 << WEAPON_AWP)) || (m_enemy->v.weapons & (1 << WEAPON_SCOUT)) || (m_enemy->v.weapons & (1 << WEAPON_G3SG1)) || (m_enemy->v.weapons & (1 << WEAPON_SG550))))
2015-06-10 23:30:48 +03:00
ChatterMessage (Chatter_SniperWarning);
2014-07-30 14:17:46 +04:00
// if bot is trapped under shield yell for help !
if (GetTaskId () == TASK_CAMP && HasShield () && IsShieldDrawn () && hasFriendNearby >= 2 && IsEnemyViewable (m_enemy))
InstantChatterMessage (Chatter_Pinned_Down);
}
2014-07-30 14:17:46 +04:00
2015-06-10 23:30:48 +03:00
// if bomb planted warn teammates !
if (g_canSayBombPlanted && g_bombPlanted && GetTeam (GetEntity ()) == TEAM_CF)
{
g_canSayBombPlanted = false;
ChatterMessage (Chatter_GottaFindTheBomb);
}
2014-07-30 14:17:46 +04:00
}
Vector src, destination;
m_checkTerrain = true;
m_moveToGoal = true;
m_wantsToFire = false;
AvoidGrenades (); // avoid flyings grenades
m_isUsingGrenade = false;
RunTask (); // execute current task
ChooseAimDirection (); // choose aim direction
UpdateLookAngles (); // and turn to chosen aim direction
2014-07-30 14:17:46 +04:00
// the bots wants to fire at something?
if (m_wantsToFire && !m_isUsingGrenade && m_shootTime <= GetWorldTime ())
FireWeapon (); // if bot didn't fire a bullet try again next frame
// check for reloading
if (m_reloadCheckTime <= GetWorldTime ())
CheckReload ();
// set the reaction time (surprise momentum) different each frame according to skill
2015-06-04 11:52:48 +03:00
SetIdealReactionTimes ();
2014-07-30 14:17:46 +04:00
// calculate 2 direction vectors, 1 without the up/down component
const Vector &dirOld = m_destOrigin - (pev->origin + pev->velocity * m_frameInterval);
const Vector &dirNormal = dirOld.Normalize2D ();
m_moveAngles = dirOld.ToAngles ();
m_moveAngles.ClampAngles ();
m_moveAngles.x *= -1.0f; // invert for engine
if (m_difficulty > 3 && ((m_aimFlags & AIM_ENEMY) || (m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY)) || (GetTaskId () == TASK_SEEKCOVER && (m_isReloading || m_isVIP))) && !yb_jasonmode.GetBool () && GetTaskId () != TASK_CAMP && !IsOnLadder ())
2014-07-30 14:17:46 +04:00
{
m_moveToGoal = false; // don't move to goal
m_navTimeset = GetWorldTime ();
if (IsValidPlayer (m_enemy))
CombatFight ();
}
2014-07-30 14:17:46 +04:00
// check if we need to escape from bomb
if ((g_mapType & MAP_DE) && g_bombPlanted && m_notKilled && GetTaskId () != TASK_ESCAPEFROMBOMB && GetTaskId () != TASK_CAMP && OutOfBombTimer ())
{
TaskComplete (); // complete current task
// then start escape from bomb immidiate
PushTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, -1, 0.0f, true);
2014-07-30 14:17:46 +04:00
}
// allowed to move to a destination position?
if (m_moveToGoal)
{
GetValidWaypoint ();
// Press duck button if we need to
if ((m_currentPath->flags & FLAG_CROUCH) && !(m_currentPath->flags & FLAG_CAMP))
pev->button |= IN_DUCK;
m_timeWaypointMove = GetWorldTime ();
if (IsInWater ()) // special movement for swimming here
{
// check if we need to go forward or back press the correct buttons
if (InFieldOfView (m_destOrigin - EyePosition ()) > 90.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_BACK;
else
pev->button |= IN_FORWARD;
if (m_moveAngles.x > 60.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_DUCK;
else if (m_moveAngles.x < -60.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_JUMP;
}
}
if (m_checkTerrain) // are we allowed to check blocking terrain (and react to it)?
CheckTerrain (movedDistance, dirNormal);
2014-07-30 14:17:46 +04:00
// must avoid a grenade?
if (m_needAvoidGrenade != 0)
{
// Don't duck to get away faster
pev->button &= ~IN_DUCK;
m_moveSpeed = -pev->maxspeed;
m_strafeSpeed = pev->maxspeed * m_needAvoidGrenade;
}
// time to reach waypoint
2015-06-04 11:52:48 +03:00
if (m_navTimeset + GetEstimatedReachTime () < GetWorldTime () && IsEntityNull (m_enemy))
2014-07-30 14:17:46 +04:00
{
GetValidWaypoint ();
// clear these pointers, bot mingh be stuck getting to them
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_pickupItem) && !m_hasProgressBar)
2014-07-30 14:17:46 +04:00
m_itemIgnore = m_pickupItem;
m_pickupItem = NULL;
m_breakableEntity = NULL;
m_itemCheckTime = GetWorldTime () + 5.0f;
2014-07-30 14:17:46 +04:00
m_pickupType = PICKUP_NONE;
}
2015-06-04 11:52:48 +03:00
if (m_duckTime >= GetWorldTime ())
2014-07-30 14:17:46 +04:00
pev->button |= IN_DUCK;
if (pev->button & IN_JUMP)
m_jumpTime = GetWorldTime ();
if (m_jumpTime + 0.85f > GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
if (!IsOnFloor () && !IsInWater ())
pev->button |= IN_DUCK;
}
if (!(pev->button & (IN_FORWARD | IN_BACK)))
{
if (m_moveSpeed > 0.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_FORWARD;
else if (m_moveSpeed < 0.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_BACK;
}
if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT)))
{
if (m_strafeSpeed > 0.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_MOVERIGHT;
else if (m_strafeSpeed < 0.0f)
2014-07-30 14:17:46 +04:00
pev->button |= IN_MOVELEFT;
}
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (g_hostEntity) && yb_debug.GetInt () >= 1)
2014-07-30 14:17:46 +04:00
{
bool displayDebugOverlay = false;
if (g_hostEntity->v.iuser2 == IndexOfEntity (GetEntity ()))
displayDebugOverlay = true;
if (!displayDebugOverlay && yb_debug.GetInt () >= 2)
{
Bot *nearest = NULL;
if (FindNearestPlayer (reinterpret_cast <void **> (&nearest), g_hostEntity, 128.0f, true, true, true, true) && nearest == this)
displayDebugOverlay = true;
}
2014-07-30 14:17:46 +04:00
if (displayDebugOverlay)
2014-07-30 14:17:46 +04:00
{
static float timeDebugUpdate = 0.0f;
2014-07-30 14:17:46 +04:00
static int index, goal, taskID;
if (!m_tasks.IsEmpty ())
{
if (taskID != GetTaskId () || index != m_currentWaypointIndex || goal != GetTask ()->data || timeDebugUpdate < GetWorldTime ())
{
taskID = GetTaskId ();
index = m_currentWaypointIndex;
goal = GetTask ()->data;
char taskName [80];
memset (taskName, 0, sizeof (taskName));
switch (taskID)
{
case TASK_NORMAL:
sprintf (taskName, "Normal");
break;
case TASK_PAUSE:
sprintf (taskName, "Pause");
break;
case TASK_MOVETOPOSITION:
sprintf (taskName, "MoveToPosition");
break;
case TASK_FOLLOWUSER:
sprintf (taskName, "FollowUser");
break;
case TASK_WAITFORGO:
sprintf (taskName, "WaitForGo");
break;
case TASK_PICKUPITEM:
sprintf (taskName, "PickupItem");
break;
case TASK_CAMP:
sprintf (taskName, "Camp");
break;
case TASK_PLANTBOMB:
sprintf (taskName, "PlantBomb");
break;
case TASK_DEFUSEBOMB:
sprintf (taskName, "DefuseBomb");
break;
case TASK_ATTACK:
sprintf (taskName, "AttackEnemy");
break;
case TASK_HUNTENEMY:
sprintf (taskName, "HuntEnemy");
break;
case TASK_SEEKCOVER:
sprintf (taskName, "SeekCover");
break;
case TASK_THROWHEGRENADE:
sprintf (taskName, "ThrowExpGrenade");
break;
case TASK_THROWFLASHBANG:
sprintf (taskName, "ThrowFlashGrenade");
break;
case TASK_THROWSMOKE:
sprintf (taskName, "ThrowSmokeGrenade");
break;
case TASK_DOUBLEJUMP:
sprintf (taskName, "PerformDoubleJump");
break;
case TASK_ESCAPEFROMBOMB:
sprintf (taskName, "EscapeFromBomb");
break;
case TASK_SHOOTBREAKABLE:
sprintf (taskName, "ShootBreakable");
break;
case TASK_HIDE:
sprintf (taskName, "Hide");
break;
case TASK_BLINDED:
sprintf (taskName, "Blinded");
break;
case TASK_SPRAY:
sprintf (taskName, "SprayLogo");
break;
}
char enemyName[80], weaponName[80], aimFlags[64], botType[32];
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_enemy))
strncpy (enemyName, STRING (m_enemy->v.netname), SIZEOF_CHAR (enemyName));
2015-06-04 11:52:48 +03:00
else if (!IsEntityNull (m_lastEnemy))
2014-07-30 14:17:46 +04:00
{
strcpy (enemyName, " (L)");
strncat (enemyName, STRING (m_lastEnemy->v.netname), SIZEOF_CHAR (enemyName));
2014-07-30 14:17:46 +04:00
}
else
strcpy (enemyName, " (null)");
char pickupName[80];
memset (pickupName, 0, sizeof (pickupName));
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_pickupItem))
strncpy (pickupName, STRING (m_pickupItem->v.classname), SIZEOF_CHAR (pickupName));
2014-07-30 14:17:46 +04:00
else
strcpy (pickupName, " (null)");
WeaponSelect *selectTab = &g_weaponSelect[0];
char weaponCount = 0;
while (m_currentWeapon != selectTab->id && weaponCount < NUM_WEAPONS)
{
selectTab++;
weaponCount++;
}
memset (aimFlags, 0, sizeof (aimFlags));
2014-07-30 14:17:46 +04:00
// set the aim flags
sprintf (aimFlags, "%s%s%s%s%s%s%s%s",
(m_aimFlags & AIM_NAVPOINT) ? " NavPoint" : "",
(m_aimFlags & AIM_CAMP) ? " CampPoint" : "",
(m_aimFlags & AIM_PREDICT_PATH) ? " PredictPath" : "",
(m_aimFlags & AIM_LAST_ENEMY) ? " LastEnemy" : "",
(m_aimFlags & AIM_ENTITY) ? " Entity" : "",
(m_aimFlags & AIM_ENEMY) ? " Enemy" : "",
(m_aimFlags & AIM_GRENADE) ? " Grenade" : "",
(m_aimFlags & AIM_OVERRIDE) ? " Override" : "");
2014-07-30 14:17:46 +04:00
// set the bot type
sprintf (botType, "%s%s%s", m_personality == PERSONALITY_RUSHER ? " Rusher" : "",
m_personality == PERSONALITY_CAREFUL ? " Careful" : "",
m_personality == PERSONALITY_NORMAL ? " Normal" : "");
if (weaponCount >= NUM_WEAPONS)
{
// prevent printing unknown message from known weapons
switch (m_currentWeapon)
{
case WEAPON_EXPLOSIVE:
strcpy (weaponName, "weapon_hegrenade");
2014-07-30 14:17:46 +04:00
break;
case WEAPON_FLASHBANG:
strcpy (weaponName, "weapon_flashbang");
2014-07-30 14:17:46 +04:00
break;
case WEAPON_SMOKE:
strcpy (weaponName, "weapon_smokegrenade");
2014-07-30 14:17:46 +04:00
break;
case WEAPON_C4:
strcpy (weaponName, "weapon_c4");
2014-07-30 14:17:46 +04:00
break;
default:
sprintf (weaponName, "Unknown! (%d)", m_currentWeapon);
}
}
else
strncpy (weaponName, selectTab->weaponName, SIZEOF_CHAR (weaponName));
2014-07-30 14:17:46 +04:00
char outputBuffer[512];
memset (outputBuffer, 0, sizeof (outputBuffer));
sprintf (outputBuffer, "\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s\n", STRING (pev->netname), pev->health, pev->armorvalue, taskID, taskName, GetTask ()->desire, &weaponName[7], GetAmmoInClip (), GetAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags, m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - GetWorldTime (), pev->movetype, enemyName, pickupName, botType);
MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, NULL, g_hostEntity);
WRITE_BYTE (TE_TEXTMESSAGE);
WRITE_BYTE (1);
WRITE_SHORT (FixedSigned16 (-1, 1 << 13));
WRITE_SHORT (FixedSigned16 (0, 1 << 13));
WRITE_BYTE (0);
2014-08-06 00:03:50 +04:00
WRITE_BYTE (m_team == TEAM_CF ? 0 : 255);
2014-07-30 14:17:46 +04:00
WRITE_BYTE (100);
2014-08-06 00:03:50 +04:00
WRITE_BYTE (m_team != TEAM_CF ? 0 : 255);
2014-07-30 14:17:46 +04:00
WRITE_BYTE (0);
WRITE_BYTE (255);
WRITE_BYTE (255);
WRITE_BYTE (255);
WRITE_BYTE (0);
WRITE_SHORT (FixedUnsigned16 (0, 1 << 8));
WRITE_SHORT (FixedUnsigned16 (0, 1 << 8));
WRITE_SHORT (FixedUnsigned16 (1.0, 1 << 8));
WRITE_STRING (const_cast <const char *> (&outputBuffer[0]));
MESSAGE_END ();
timeDebugUpdate = GetWorldTime () + 1.0;
}
// green = destination origin
// blue = ideal angles
// red = view angles
DrawArrow (g_hostEntity, EyePosition (), m_destOrigin, 10, 0, 0, 255, 0, 250, 5, 1);
MakeVectors (m_idealAngles);
DrawArrow (g_hostEntity, EyePosition (), EyePosition () + g_pGlobals->v_forward * 300.0f, 10, 0, 0, 0, 255, 250, 5, 1);
2014-07-30 14:17:46 +04:00
MakeVectors (pev->v_angle);
DrawArrow (g_hostEntity, EyePosition (), EyePosition () + g_pGlobals->v_forward * 300.0f, 10, 0, 255, 0, 0, 250, 5, 1);
2014-07-30 14:17:46 +04:00
// now draw line from source to destination
PathNode *node = &m_navNode[0];
while (node != NULL)
{
2015-07-24 15:10:51 +03:00
const Vector &srcPath = waypoints.GetPath (node->index)->origin;
2014-07-30 14:17:46 +04:00
node = node->next;
if (node != NULL)
{
2015-07-24 15:10:51 +03:00
const Vector &dstPath = waypoints.GetPath (node->index)->origin;
2015-06-11 14:19:52 +03:00
DrawArrow (g_hostEntity, srcPath, dstPath, 15, 0, 255, 100, 55, 200, 5, 1);
2014-07-30 14:17:46 +04:00
}
}
}
}
}
// save the previous speed (for checking if stuck)
m_prevSpeed = fabsf (m_moveSpeed);
m_lastDamageType = -1; // reset damage
pev->angles.ClampAngles ();
pev->v_angle.ClampAngles ();
}
bool Bot::HasHostage (void)
{
for (int i = 0; i < MAX_HOSTAGES; i++)
{
2015-06-04 11:52:48 +03:00
if (!IsEntityNull (m_hostages[i]))
2014-07-30 14:17:46 +04:00
{
// don't care about dead hostages
if (m_hostages[i]->v.health <= 0.0f || (pev->origin - m_hostages[i]->v.origin).GetLength () > 600.0f)
2014-07-30 14:17:46 +04:00
{
m_hostages[i] = NULL;
continue;
}
return true;
}
}
return false;
}
int Bot::GetAmmo (void)
{
2015-08-03 10:26:08 +03:00
if (g_weaponDefs[m_currentWeapon].ammo1 == -1 || g_weaponDefs[m_currentWeapon].ammo1 > 31)
2014-07-30 14:17:46 +04:00
return 0;
return m_ammo[g_weaponDefs[m_currentWeapon].ammo1];
}
void Bot::TakeDamage (edict_t *inflictor, int damage, int armor, int bits)
{
// this function gets called from the network message handler, when bot's gets hurt from any
// other player.
m_lastDamageType = bits;
2014-08-06 00:03:50 +04:00
CollectGoalExperience (damage, m_team);
2014-07-30 14:17:46 +04:00
2015-07-20 21:26:20 +03:00
if (IsValidPlayer (inflictor))
2014-07-30 14:17:46 +04:00
{
if (yb_tkpunish.GetBool () && GetTeam (inflictor) == m_team && !IsValidBot (inflictor))
2014-07-30 14:17:46 +04:00
{
// alright, die you teamkiller!!!
m_actualReactionTime = 0.0f;
2014-07-30 14:17:46 +04:00
m_seeEnemyTime = GetWorldTime();
m_enemy = inflictor;
2014-07-30 14:17:46 +04:00
m_lastEnemy = m_enemy;
m_lastEnemyOrigin = m_enemy->v.origin;
m_enemyOrigin = m_enemy->v.origin;
ChatMessage(CHAT_TEAMATTACK);
HandleChatterMessage("#Bot_TeamAttack");
ChatterMessage(Chatter_FriendlyFire);
}
else
{
// attacked by an enemy
if (pev->health > 60.0f)
2014-07-30 14:17:46 +04:00
{
m_agressionLevel += 0.1f;
2014-07-30 14:17:46 +04:00
if (m_agressionLevel > 1.0f)
m_agressionLevel += 1.0f;
2014-07-30 14:17:46 +04:00
}
else
{
m_fearLevel += 0.03f;
2014-07-30 14:17:46 +04:00
if (m_fearLevel > 1.0f)
m_fearLevel += 1.0f;
2014-07-30 14:17:46 +04:00
}
RemoveCertainTask (TASK_CAMP);
2015-06-04 11:52:48 +03:00
if (IsEntityNull (m_enemy) && m_team != GetTeam (inflictor))
2014-07-30 14:17:46 +04:00
{
m_lastEnemy = inflictor;
m_lastEnemyOrigin = inflictor->v.origin;
// FIXME - Bot doesn't necessary sees this enemy
m_seeEnemyTime = GetWorldTime ();
}
if (yb_csdm_mode.GetInt () == 0)
CollectExperienceData (inflictor, armor + damage);
2014-07-30 14:17:46 +04:00
}
}
else // hurt by unusual damage like drowning or gas
{
// leave the camping/hiding position
2015-07-24 15:10:51 +03:00
if (!waypoints.Reachable (this, waypoints.FindNearest (m_destOrigin)))
2014-07-30 14:17:46 +04:00
{
DeleteSearchNodes ();
FindWaypoint ();
}
}
}
void Bot::TakeBlinded (const Vector &fade, int alpha)
{
// this function gets called by network message handler, when screenfade message get's send
// it's used to make bot blind froumd the grenade.
if (fade.x != 255.0f || fade.y != 255.0f || fade.z != 255.0f || alpha <= 170.0f)
2014-07-30 14:17:46 +04:00
return;
m_enemy = NULL;
m_maxViewDistance = Random.Float (10.0f, 20.0f);
m_blindTime = GetWorldTime () + static_cast <float> (alpha - 200.0f) / 16.0f;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (m_difficulty <= 2)
2014-07-30 14:17:46 +04:00
{
m_blindMoveSpeed = 0.0f;
m_blindSidemoveSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
m_blindButton = IN_DUCK;
}
2015-06-04 11:52:48 +03:00
else if (m_difficulty > 2)
2014-07-30 14:17:46 +04:00
{
m_blindMoveSpeed = -pev->maxspeed;
m_blindSidemoveSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
float walkSpeed = GetWalkSpeed ();
if (Random.Long (0, 100) > 50)
2014-07-30 14:17:46 +04:00
m_blindSidemoveSpeed = walkSpeed;
else
m_blindSidemoveSpeed = -walkSpeed;
if (pev->health < 85.0f)
m_blindMoveSpeed = -walkSpeed;
2014-07-30 14:17:46 +04:00
else if (m_personality == PERSONALITY_CAREFUL)
{
m_blindMoveSpeed = 0.0f;
2014-07-30 14:17:46 +04:00
m_blindButton = IN_DUCK;
}
else
m_blindMoveSpeed = walkSpeed;
}
}
void Bot::CollectGoalExperience (int damage, int team)
{
// gets called each time a bot gets damaged by some enemy. tries to achieve a statistic about most/less dangerous
// waypoints for a destination goal used for pathfinding
2015-06-09 22:16:08 +03:00
if (g_numWaypoints < 1 || g_waypointsChanged || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0)
2014-07-30 14:17:46 +04:00
return;
// only rate goal waypoint if bot died because of the damage
// FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons
if (pev->health - damage <= 0)
{
if (team == TEAM_TF)
{
int value = (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team0Value;
value -= static_cast <int> (pev->health / 20);
if (value < -MAX_GOAL_VALUE)
value = -MAX_GOAL_VALUE;
else if (value > MAX_GOAL_VALUE)
value = MAX_GOAL_VALUE;
(g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team0Value = static_cast <signed short> (value);
}
else
{
int value = (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team1Value;
value -= static_cast <int> (pev->health / 20);
if (value < -MAX_GOAL_VALUE)
value = -MAX_GOAL_VALUE;
else if (value > MAX_GOAL_VALUE)
value = MAX_GOAL_VALUE;
(g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team1Value = static_cast <signed short> (value);
}
}
}
void Bot::CollectExperienceData (edict_t *attacker, int damage)
{
// this function gets called each time a bot gets damaged by some enemy. sotores the damage (teamspecific) done by victim.
if (!IsValidPlayer (attacker))
return;
int attackerTeam = GetTeam (attacker);
2014-08-06 00:03:50 +04:00
int victimTeam = m_team;
2014-07-30 14:17:46 +04:00
if (attackerTeam == victimTeam )
2014-07-30 14:17:46 +04:00
return;
// if these are bots also remember damage to rank destination of the bot
m_goalValue -= static_cast <float> (damage);
2015-07-24 15:10:51 +03:00
if (bots.GetBot (attacker) != NULL)
bots.GetBot (attacker)->m_goalValue += static_cast <float> (damage);
2014-07-30 14:17:46 +04:00
if (damage < 20)
return; // do not collect damage less than 20
2015-07-24 15:10:51 +03:00
int attackerIndex = waypoints.FindNearest (attacker->v.origin);
int victimIndex = waypoints.FindNearest (pev->origin);
2014-07-30 14:17:46 +04:00
if (pev->health > 20.0f)
2014-07-30 14:17:46 +04:00
{
if (victimTeam == TEAM_TF)
(g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage++;
else
(g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage++;
if ((g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage > MAX_DAMAGE_VALUE)
(g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage = MAX_DAMAGE_VALUE;
if ((g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage > MAX_DAMAGE_VALUE)
(g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage = MAX_DAMAGE_VALUE;
}
float updateDamage = IsValidBot (attacker) ? 10.0f : 7.0f;
2014-07-30 14:17:46 +04:00
// store away the damage done
if (victimTeam == TEAM_TF)
{
int value = (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team0Damage;
value += static_cast <int> (damage / updateDamage);
2014-07-30 14:17:46 +04:00
if (value > MAX_DAMAGE_VALUE)
value = MAX_DAMAGE_VALUE;
if (value > g_highestDamageT)
g_highestDamageT = value;
2014-07-30 14:17:46 +04:00
(g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team0Damage = static_cast <unsigned short> (value);
}
else
{
int value = (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team1Damage;
value += static_cast <int> (damage / updateDamage);
2014-07-30 14:17:46 +04:00
if (value > MAX_DAMAGE_VALUE)
value = MAX_DAMAGE_VALUE;
if (value > g_highestDamageCT)
g_highestDamageCT = value;
2014-07-30 14:17:46 +04:00
(g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team1Damage = static_cast <unsigned short> (value);
}
}
void Bot::HandleChatterMessage (const char *tempMessage)
{
// this function is added to prevent engine crashes with: 'Message XX started, before message XX ended', or something.
if (FStrEq (tempMessage, "#CTs_Win") && m_team == TEAM_CF)
2014-07-30 14:17:46 +04:00
{
if (g_timeRoundMid > GetWorldTime ())
ChatterMessage (Chatter_QuicklyWonTheRound);
else
ChatterMessage (Chatter_WonTheRound);
}
if (FStrEq (tempMessage, "#Terrorists_Win") && m_team == TEAM_TF)
2014-07-30 14:17:46 +04:00
{
if (g_timeRoundMid > GetWorldTime ())
ChatterMessage (Chatter_QuicklyWonTheRound);
else
ChatterMessage (Chatter_WonTheRound);
}
if (FStrEq (tempMessage, "#Bot_TeamAttack"))
ChatterMessage (Chatter_FriendlyFire);
if (FStrEq (tempMessage, "#Bot_NiceShotCommander"))
ChatterMessage (Chatter_NiceshotCommander);
if (FStrEq (tempMessage, "#Bot_NiceShotPall"))
ChatterMessage (Chatter_NiceshotPall);
}
void Bot::ChatMessage (int type, bool isTeamSay)
{
extern ConVar yb_chat;
if (g_chatFactory[type].IsEmpty () || !yb_chat.GetBool ())
return;
const char *pickedPhrase = g_chatFactory[type].GetRandomElement ().GetBuffer ();
if (IsNullString (pickedPhrase))
return;
PrepareChatMessage (const_cast <char *> (pickedPhrase));
PushMessageQueue (isTeamSay ? GSM_SAY_TEAM : GSM_SAY);
}
void Bot::DiscardWeaponForUser (edict_t *user, bool discardC4)
{
// this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop*
// command, very useful, when i'm don't have money to buy anything... )
if (IsAlive (user) && m_moneyAmount >= 2000 && HasPrimaryWeapon () && (user->v.origin - pev->origin).GetLength () <= 240.0f)
2014-07-30 14:17:46 +04:00
{
m_aimFlags |= AIM_ENTITY;
m_lookAt = user->v.origin;
if (discardC4)
{
SelectWeaponByName ("weapon_c4");
FakeClientCommand (GetEntity (), "drop");
}
else
{
SelectBestWeapon ();
FakeClientCommand (GetEntity (), "drop");
}
m_pickupItem = NULL;
m_pickupType = PICKUP_NONE;
m_itemCheckTime = GetWorldTime () + 5.0f;
2014-07-30 14:17:46 +04:00
if (m_inBuyZone)
{
m_buyingFinished = false;
m_buyState = 0;
PushMessageQueue (GSM_BUY_STUFF);
m_nextBuyTime = GetWorldTime ();
}
}
}
void Bot::ResetDoubleJumpState (void)
{
TaskComplete ();
m_doubleJumpEntity = NULL;
m_duckForJump = 0.0f;
m_doubleJumpOrigin.Zero ();
2014-07-30 14:17:46 +04:00
m_travelStartIndex = -1;
m_jumpReady = false;
}
void Bot::DebugMsg (const char *format, ...)
{
2015-06-24 17:25:39 +03:00
int level = yb_debug.GetInt ();
if (level <= 2)
2014-07-30 14:17:46 +04:00
return;
va_list ap;
char buffer[1024];
va_start (ap, format);
vsprintf (buffer, format, ap);
va_end (ap);
2015-06-24 17:25:39 +03:00
if (level == 3 && !IsEntityNull (g_hostEntity) && g_hostEntity->v.iuser2 == IndexOfEntity (GetEntity ()))
ServerPrint ("%s: %s", STRING (pev->netname), buffer);
else if (level != 3)
2015-06-20 11:45:59 +03:00
ServerPrint ("%s: %s", STRING (pev->netname), buffer);
2014-07-30 14:17:46 +04:00
2015-06-24 17:25:39 +03:00
if (level > 3)
2014-07-30 14:17:46 +04:00
AddLogEntry (false, LL_DEFAULT, "%s: %s", STRING (pev->netname), buffer);
}
2015-06-24 15:59:53 +03:00
Vector Bot::CheckToss(const Vector &start, const Vector &stop)
2014-07-30 14:17:46 +04:00
{
// this function returns the velocity at which an object should looped from start to land near end.
// returns null vector if toss is not feasible.
TraceResult tr;
float gravity = sv_gravity.GetFloat () * 0.55f;
2014-07-30 14:17:46 +04:00
Vector end = stop - pev->velocity;
end.z -= 15.0f;
2014-07-30 14:17:46 +04:00
if (fabsf (end.z - start.z) > 500.0f)
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
Vector midPoint = start + (end - start) * 0.5f;
TraceHull (midPoint, midPoint + Vector (0.0f, 0.0f, 500.0f), true, head_hull, ENT (pev), &tr);
2014-07-30 14:17:46 +04:00
if (tr.flFraction < 1.0f)
2014-07-30 14:17:46 +04:00
{
midPoint = tr.vecEndPos;
midPoint.z = tr.pHit->v.absmin.z - 1.0f;
2014-07-30 14:17:46 +04:00
}
if ((midPoint.z < start.z) || (midPoint.z < end.z))
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
float timeOne = sqrtf ((midPoint.z - start.z) / (0.5f * gravity));
float timeTwo = sqrtf ((midPoint.z - end.z) / (0.5f * gravity));
2014-07-30 14:17:46 +04:00
if (timeOne < 0.1f)
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
Vector nadeVelocity = (end - start) / (timeOne + timeTwo);
nadeVelocity.z = gravity * timeOne;
Vector apex = start + nadeVelocity * timeOne;
apex.z = midPoint.z;
TraceHull (start, apex, false, head_hull, ENT (pev), &tr);
if (tr.flFraction < 1.0f || tr.fAllSolid)
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
TraceHull (end, apex, true, head_hull, ENT (pev), &tr);
if (tr.flFraction != 1.0f)
2014-07-30 14:17:46 +04:00
{
float dot = -(tr.vecPlaneNormal | (apex - end).Normalize ());
if (dot > 0.7f || tr.flFraction < 0.8f) // 60 degrees
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
}
return nadeVelocity * 0.777f;
2014-07-30 14:17:46 +04:00
}
2015-06-24 15:59:53 +03:00
Vector Bot::CheckThrow(const Vector &start, const Vector &stop)
2014-07-30 14:17:46 +04:00
{
// this function returns the velocity vector at which an object should be thrown from start to hit end.
// returns null vector if throw is not feasible.
Vector nadeVelocity = (stop - start);
2014-07-30 14:17:46 +04:00
TraceResult tr;
float gravity = sv_gravity.GetFloat () * 0.55f;
float time = nadeVelocity.GetLength () / 195.0f;
2014-07-30 14:17:46 +04:00
if (time < 0.01f)
return Vector::GetZero ();
else if (time > 2.0f)
time = 1.2f;
2014-07-30 14:17:46 +04:00
nadeVelocity = nadeVelocity * (1.0f / time);
nadeVelocity.z += gravity * time * 0.5f;
2014-07-30 14:17:46 +04:00
Vector apex = start + (stop - start) * 0.5f;
apex.z += 0.5f * gravity * (time * 0.5f) * (time * 0.5f);
2014-07-30 14:17:46 +04:00
TraceHull (start, apex, false, head_hull, GetEntity (), &tr);
if (tr.flFraction != 1.0f)
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
TraceHull (stop, apex, true, head_hull, GetEntity (), &tr);
2014-07-30 14:17:46 +04:00
if (tr.flFraction != 1.0 || tr.fAllSolid)
2014-07-30 14:17:46 +04:00
{
float dot = -(tr.vecPlaneNormal | (apex - stop).Normalize ());
2014-07-30 14:17:46 +04:00
if (dot > 0.7f || tr.flFraction < 0.8f)
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
}
return nadeVelocity * 0.7793f;
2014-07-30 14:17:46 +04:00
}
2015-06-11 14:25:12 +03:00
Vector Bot::CheckBombAudible (void)
2014-07-30 14:17:46 +04:00
{
// this function checks if bomb is can be heard by the bot, calculations done by manual testing.
if (!g_bombPlanted || (GetTaskId () == TASK_ESCAPEFROMBOMB))
return Vector::GetZero (); // reliability check
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if (m_difficulty >= 3)
2015-07-24 15:10:51 +03:00
return waypoints.GetBombPosition();
2014-07-30 14:17:46 +04:00
2015-07-24 15:10:51 +03:00
const Vector &bombOrigin = waypoints.GetBombPosition ();
2014-07-30 14:17:46 +04:00
float timeElapsed = ((GetWorldTime () - g_timeBombPlanted) / mp_c4timer.GetFloat ()) * 100.0f;
float desiredRadius = 768.0f;
2014-07-30 14:17:46 +04:00
// start the manual calculations
if (timeElapsed > 85.0f)
desiredRadius = 4096.0f;
else if (timeElapsed > 68.0f)
desiredRadius = 2048.0f;
else if (timeElapsed > 52.0f)
desiredRadius = 1280.0f;
else if (timeElapsed > 28.0f)
desiredRadius = 1024.0f;
2014-07-30 14:17:46 +04:00
// we hear bomb if length greater than radius
if (desiredRadius < (pev->origin - bombOrigin).GetLength2D ())
return bombOrigin;
return Vector::GetZero ();
2014-07-30 14:17:46 +04:00
}
void Bot::MoveToVector (const Vector &to)
2014-07-30 14:17:46 +04:00
{
if (to.IsZero ())
2014-07-30 14:17:46 +04:00
return;
2015-07-24 15:10:51 +03:00
FindPath (m_currentWaypointIndex, waypoints.FindNearest (to), 0);
2014-07-30 14:17:46 +04:00
}
2015-06-09 23:31:46 +03:00
byte Bot::ThrottledMsec (void)
{
2015-06-09 22:58:15 +03:00
// estimate msec to use for this command based on time passed from the previous command
return static_cast <byte> ((GetWorldTime () - m_lastCommandTime) * 1000.0f);
}
2014-07-30 14:17:46 +04:00
void Bot::RunPlayerMovement (void)
{
// the purpose of this function is to compute, according to the specified computation
// method, the msec value which will be passed as an argument of pfnRunPlayerMove. This
// function is called every frame for every bot, since the RunPlayerMove is the function
// that tells the engine to put the bot character model in movement. This msec value
// tells the engine how long should the movement of the model extend inside the current
// frame. It is very important for it to be exact, else one can experience bizarre
// problems, such as bots getting stuck into each others. That's because the model's
// bounding boxes, which are the boxes the engine uses to compute and detect all the
// collisions of the model, only exist, and are only valid, while in the duration of the
// movement. That's why if you get a pfnRunPlayerMove for one boINFt that lasts a little too
2014-07-30 14:17:46 +04:00
// short in comparison with the frame's duration, the remaining time until the frame
// elapses, that bot will behave like a ghost : no movement, but bullets and players can
// pass through it. Then, when the next frame will begin, the stucking problem will arise !
2015-06-09 22:58:15 +03:00
m_frameInterval = GetWorldTime () - m_lastCommandTime;
2015-06-09 23:31:46 +03:00
byte msecVal = ThrottledMsec ();
2015-06-09 22:58:15 +03:00
m_lastCommandTime = GetWorldTime ();
(*g_engfuncs.pfnRunPlayerMove) (pev->pContainingEntity, m_moveAngles, m_moveSpeed, m_strafeSpeed, 0.0f, pev->button, pev->impulse, msecVal);
2014-07-30 14:17:46 +04:00
}
void Bot::CheckBurstMode (float distance)
{
// this function checks burst mode, and switch it depending distance to to enemy.
if (HasShield ())
return; // no checking when shiled is active
// if current weapon is glock, disable burstmode on long distances, enable it else
if (m_currentWeapon == WEAPON_GLOCK && distance < 300.0f && m_weaponBurstMode == BM_OFF)
2014-07-30 14:17:46 +04:00
pev->button |= IN_ATTACK2;
else if (m_currentWeapon == WEAPON_GLOCK && distance >= 300.0f && m_weaponBurstMode == BM_ON)
2014-07-30 14:17:46 +04:00
pev->button |= IN_ATTACK2;
// if current weapon is famas, disable burstmode on short distances, enable it else
if (m_currentWeapon == WEAPON_FAMAS && distance > 400.0f && m_weaponBurstMode == BM_OFF)
2014-07-30 14:17:46 +04:00
pev->button |= IN_ATTACK2;
else if (m_currentWeapon == WEAPON_FAMAS && distance <= 400.0f && m_weaponBurstMode == BM_ON)
2014-07-30 14:17:46 +04:00
pev->button |= IN_ATTACK2;
}
void Bot::CheckSilencer (void)
{
2015-06-04 11:52:48 +03:00
if (((m_currentWeapon == WEAPON_USP && m_difficulty < 2) || m_currentWeapon == WEAPON_M4A1) && !HasShield())
2014-07-30 14:17:46 +04:00
{
int random = (m_personality == PERSONALITY_RUSHER ? 35 : 65);
2014-07-30 14:17:46 +04:00
// aggressive bots don't like the silencer
if (Random.Long (1, 100) <= (m_currentWeapon == WEAPON_USP ? random / 3 : random))
2014-07-30 14:17:46 +04:00
{
if (pev->weaponanim > 6) // is the silencer not attached...
pev->button |= IN_ATTACK2; // attach the silencer
}
else
{
if (pev->weaponanim <= 6) // is the silencer attached...
pev->button |= IN_ATTACK2; // detach the silencer
}
}
}
float Bot::GetBombTimeleft (void)
{
if (!g_bombPlanted)
return 0.0f;
2014-07-30 14:17:46 +04:00
float timeLeft = ((g_timeBombPlanted + mp_c4timer.GetFloat ()) - GetWorldTime ());
if (timeLeft < 0.0f)
return 0.0f;
2014-07-30 14:17:46 +04:00
return timeLeft;
}
float Bot::GetEstimatedReachTime (void)
{
float estimatedTime = 2.0f; // time to reach next waypoint
2014-07-30 14:17:46 +04:00
// calculate 'real' time that we need to get from one waypoint to another
if (m_currentWaypointIndex >= 0 && m_currentWaypointIndex < g_numWaypoints && m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints)
{
2015-07-24 15:10:51 +03:00
float distance = (waypoints.GetPath (m_prevWptIndex[0])->origin - m_currentPath->origin).GetLength ();
2014-07-30 14:17:46 +04:00
// caclulate estimated time
if (pev->maxspeed <= 0.0f)
estimatedTime = 4.0f * distance / 240.0f;
2014-07-30 14:17:46 +04:00
else
estimatedTime = 4.0f * distance / pev->maxspeed;
2014-07-30 14:17:46 +04:00
2015-08-01 00:18:50 +03:00
bool longTermReachability = (m_currentPath->flags & FLAG_CROUCH) || (m_currentPath->flags & FLAG_LADDER) || (pev->button & IN_DUCK);
2014-07-30 14:17:46 +04:00
// check for special waypoints, that can slowdown our movement
2015-08-01 00:18:50 +03:00
if (longTermReachability)
estimatedTime *= 3.0f;
2014-07-30 14:17:46 +04:00
// check for too low values
2015-06-04 11:52:48 +03:00
if (estimatedTime < 1.0f)
estimatedTime = 1.0f;
2014-07-30 14:17:46 +04:00
2015-08-01 00:18:50 +03:00
const float maxReachTime = longTermReachability ? 10.0f : 5.0f;
2014-07-30 14:17:46 +04:00
// check for too high values
2015-08-01 00:18:50 +03:00
if (estimatedTime > maxReachTime)
estimatedTime = maxReachTime;
2014-07-30 14:17:46 +04:00
}
return estimatedTime;
}
bool Bot::OutOfBombTimer (void)
{
if (m_currentWaypointIndex == -1 || ((g_mapType & MAP_DE) && (m_hasProgressBar || GetTaskId () == TASK_ESCAPEFROMBOMB)))
return false; // if CT bot already start defusing, or already escaping, return false
// calculate left time
float timeLeft = GetBombTimeleft ();
// if time left greater than 13, no need to do other checks
if (timeLeft > 16.0f)
2014-07-30 14:17:46 +04:00
return false;
2015-07-24 15:10:51 +03:00
const Vector &bombOrigin = waypoints.GetBombPosition ();
2014-07-30 14:17:46 +04:00
// for terrorist, if timer is lower than eleven seconds, return true
if (static_cast <int> (timeLeft) < 16 && m_team == TEAM_TF && (bombOrigin - pev->origin).GetLength () < 1000.0f)
2014-07-30 14:17:46 +04:00
return true;
bool hasTeammatesWithDefuserKit = false;
// check if our teammates has defusal kit
for (int i = 0; i < GetMaxClients (); i++)
{
Bot *bot = NULL; // temporaly pointer to bot
// search players with defuse kit
if ((bot = bots.GetBot (i)) != NULL && GetTeam (bot->GetEntity ()) == TEAM_CF && bot->m_hasDefuser && (bombOrigin - bot->pev->origin).GetLength () < 500.0f)
2014-07-30 14:17:46 +04:00
{
hasTeammatesWithDefuserKit = true;
break;
}
}
// add reach time to left time
2015-07-24 15:10:51 +03:00
float reachTime = waypoints.GetTravelTime (pev->maxspeed, m_currentPath->origin, bombOrigin);
2014-07-30 14:17:46 +04:00
// for counter-terrorist check alos is we have time to reach position plus average defuse time
if ((timeLeft < reachTime + 6.0f && !m_hasDefuser && !hasTeammatesWithDefuserKit) || (timeLeft < reachTime + 2.0f && m_hasDefuser))
2014-07-30 14:17:46 +04:00
return true;
if (m_hasProgressBar && IsOnFloor () && ((m_hasDefuser ? 10.0f : 15.0f) > GetBombTimeleft ()))
2014-07-30 14:17:46 +04:00
return true;
return false; // return false otherwise
}
void Bot::ReactOnSound (void)
{
int hearEnemyIndex = -1;
2014-07-30 14:17:46 +04:00
Vector pasOrg = EyePosition ();
2014-07-30 14:17:46 +04:00
if (pev->flags & FL_DUCKING)
pasOrg = pasOrg + (VEC_HULL_MIN - VEC_DUCK_HULL_MIN);
2014-07-30 14:17:46 +04:00
byte *pas = ENGINE_SET_PVS (reinterpret_cast <float *> (&pasOrg));
2014-07-30 14:17:46 +04:00
float minDistance = 99999.0f;
2014-07-30 14:17:46 +04:00
// loop through all enemy clients to check for hearable stuff
for (int i = 0; i < GetMaxClients (); i++)
{
if (!(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || g_clients[i].ent == GetEntity () || g_clients[i].team == m_team || g_clients[i].timeSoundLasting < GetWorldTime ())
2014-07-30 14:17:46 +04:00
continue;
float distance = (g_clients[i].soundPosition - pev->origin).GetLength ();
if (distance > g_clients[i].hearingDistance)
2014-07-30 14:17:46 +04:00
continue;
if (!ENGINE_CHECK_VISIBILITY (g_clients[i].ent, pas))
2014-07-30 14:17:46 +04:00
continue;
if (distance < minDistance)
{
hearEnemyIndex = i;
minDistance = distance;
}
2014-07-30 14:17:46 +04:00
}
edict_t *player = NULL;
2014-07-30 14:17:46 +04:00
if (hearEnemyIndex >= 0)
{
2014-08-06 00:03:50 +04:00
if (g_clients[hearEnemyIndex].team != m_team && yb_csdm_mode.GetInt () != 2)
2014-07-30 14:17:46 +04:00
player = g_clients[hearEnemyIndex].ent;
}
// did the bot hear someone ?
if (IsValidPlayer (player))
2014-07-30 14:17:46 +04:00
{
// change to best weapon if heard something
if (m_shootTime < GetWorldTime () - 5.0f && IsOnFloor () && m_currentWeapon != WEAPON_C4 && m_currentWeapon != WEAPON_EXPLOSIVE && m_currentWeapon != WEAPON_SMOKE && m_currentWeapon != WEAPON_FLASHBANG && !yb_jasonmode.GetBool ())
2014-07-30 14:17:46 +04:00
SelectBestWeapon ();
m_heardSoundTime = GetWorldTime () + 5.0f;
2014-07-30 14:17:46 +04:00
m_states |= STATE_HEARING_ENEMY;
if ((Random.Long (0, 100) < 15) && IsEntityNull (m_enemy) && IsEntityNull (m_lastEnemy) && m_seeEnemyTime + 7.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
ChatterMessage (Chatter_HeardEnemy);
// didn't bot already have an enemy ? take this one...
if (m_lastEnemyOrigin.IsZero () || m_lastEnemy == NULL)
2014-07-30 14:17:46 +04:00
{
m_lastEnemy = player;
m_lastEnemyOrigin = player->v.origin;
}
else // bot had an enemy, check if it's the heard one
{
if (player == m_lastEnemy)
{
// bot sees enemy ? then bail out !
if (m_states & STATE_SEEING_ENEMY)
return;
m_lastEnemyOrigin = player->v.origin;
}
else
{
// if bot had an enemy but the heard one is nearer, take it instead
2015-06-04 11:52:48 +03:00
float distance = (m_lastEnemyOrigin - pev->origin).GetLengthSquared ();
2014-07-30 14:17:46 +04:00
if (distance > (player->v.origin - pev->origin).GetLengthSquared () && m_seeEnemyTime + 2.0f < GetWorldTime ())
2014-07-30 14:17:46 +04:00
{
m_lastEnemy = player;
m_lastEnemyOrigin = player->v.origin;
}
else
return;
}
}
2015-06-07 23:06:23 +03:00
extern ConVar yb_shoots_thru_walls;
// check if heard enemy can be seen
if (CheckVisibility (player, &m_lastEnemyOrigin, &m_visibility))
2015-06-07 23:06:23 +03:00
{
m_enemy = player;
m_lastEnemy = player;
m_enemyOrigin = m_lastEnemyOrigin;
m_states |= STATE_SEEING_ENEMY;
m_seeEnemyTime = GetWorldTime ();
}
else if (!m_lastEnemyOrigin.IsZero () && m_lastEnemy == player && m_seeEnemyTime + 3.0f > GetWorldTime () && yb_shoots_thru_walls.GetBool () && IsShootableThruObstacle (m_lastEnemyOrigin))
2015-06-07 23:06:23 +03:00
{
m_states |= STATE_SEEING_ENEMY;
m_seeEnemyTime = GetWorldTime ();
}
2014-07-30 14:17:46 +04:00
}
}
bool Bot::IsShootableBreakable (edict_t *ent)
{
// this function is checking that pointed by ent pointer obstacle, can be destroyed.
if (FClassnameIs (ent, "func_breakable") || (FClassnameIs (ent, "func_pushable") && (ent->v.spawnflags & SF_PUSH_BREAKABLE)))
return (ent->v.takedamage != DAMAGE_NO) && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY) && ent->v.health < 500.0f;
2014-07-30 14:17:46 +04:00
return false;
}
2015-06-04 11:52:48 +03:00
void Bot::EquipInBuyzone (int buyCount)
2014-07-30 14:17:46 +04:00
{
// this function is gets called when bot enters a buyzone, to allow bot to buy some stuff
2015-06-16 12:57:30 +03:00
bool checkBuyTime = false;
if (mp_buytime.m_eptr != NULL)
2015-07-17 20:44:47 +03:00
checkBuyTime = (g_timeRoundStart + Random.Float (10.0f, 20.0f) + mp_buytime.GetFloat () < GetWorldTime ());
2015-06-16 12:57:30 +03:00
2014-07-30 14:17:46 +04:00
// if bot is in buy zone, try to buy ammo for this weapon...
if (m_lastEquipTime + 15.0f < GetWorldTime () && m_inBuyZone && checkBuyTime && !g_bombPlanted && m_moneyAmount > g_botBuyEconomyTable[0])
2014-07-30 14:17:46 +04:00
{
m_buyingFinished = false;
2015-06-04 11:52:48 +03:00
m_buyState = buyCount;
2014-07-30 14:17:46 +04:00
// push buy message
PushMessageQueue (GSM_BUY_STUFF);
m_nextBuyTime = GetWorldTime ();
2015-06-04 11:52:48 +03:00
m_lastEquipTime = GetWorldTime ();
2014-07-30 14:17:46 +04:00
}
}
bool Bot::IsBombDefusing (const Vector &bombOrigin)
2014-07-30 14:17:46 +04:00
{
// this function finds if somebody currently defusing the bomb.
// @todo: need to check progress bar for non-bots clients.
bool defusingInProgress = false;
for (int i = 0; i < GetMaxClients (); i++)
{
2015-07-24 15:10:51 +03:00
Bot *bot = bots.GetBot (i);
2014-07-30 14:17:46 +04:00
if (bot == NULL || bot == this)
continue; // skip invalid bots
2014-08-06 00:03:50 +04:00
if (m_team != GetTeam (bot->GetEntity ()) || bot->GetTaskId () == TASK_ESCAPEFROMBOMB)
2014-07-30 14:17:46 +04:00
continue; // skip other mess
2015-06-04 11:52:48 +03:00
if ((bot->pev->origin - bombOrigin).GetLength () < 140.0f && (bot->GetTaskId () == TASK_DEFUSEBOMB || bot->m_hasProgressBar))
2014-07-30 14:17:46 +04:00
{
defusingInProgress = true;
break;
}
// take in account peoples too
2014-08-06 00:03:50 +04:00
if (defusingInProgress || !(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || g_clients[i].team != m_team || IsValidBot (g_clients[i].ent))
2014-07-30 14:17:46 +04:00
continue;
2015-06-04 11:52:48 +03:00
if ((g_clients[i].ent->v.origin - bombOrigin).GetLength () < 140.0f)
2014-07-30 14:17:46 +04:00
{
defusingInProgress = true;
break;
}
}
return defusingInProgress;
}
float Bot::GetWalkSpeed (void)
{
if ((GetTaskId () == TASK_SEEKCOVER) || (pev->flags & FL_DUCKING) || (pev->button & IN_DUCK) || (pev->oldbuttons & IN_DUCK) || (m_currentTravelFlags & PATHFLAG_JUMP) || (m_currentPath != NULL && m_currentPath->flags & FLAG_LADDER) || IsOnLadder () || IsInWater ())
return pev->maxspeed;
return static_cast <float> ((static_cast <int> (pev->maxspeed) * 0.5f) + (static_cast <int> (pev->maxspeed) / 50.0f)) - 18.0f;
}