2016-03-05 21:04:46 +03:00
//
2014-09-09 18:29:42 +04:00
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
2014-07-30 14:17:46 +04:00
//
2014-09-09 18:29:42 +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>
2016-01-04 15:28:38 +03:00
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 " ) ;
2016-01-03 22:03:02 +03:00
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 " ) ;
2016-01-04 15:28:38 +03:00
ConVar yb_chatter_path ( " yb_chatter_path " , " sound/radio/bot " ) ;
2015-06-15 23:51:13 +03:00
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 ( ) ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
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 )
{
2016-03-12 14:35:44 +03:00
if ( m_notKilled = = otherBot - > m_notKilled )
2014-07-30 14:17:46 +04:00
{
otherBot - > m_sayTextBuffer . entityIndex = entityIndex ;
strcpy ( otherBot - > m_sayTextBuffer . sayText , m_tempStrings ) ;
}
2016-03-01 13:37:10 +03:00
otherBot - > m_sayTextBuffer . timeNextChat = engine . Time ( ) + otherBot - > m_sayTextBuffer . chatDelay ;
2014-07-30 14:17:46 +04:00
}
}
}
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..
2016-03-01 13:37:10 +03:00
engine . TestLine ( EyePosition ( ) , destination , TRACE_IGNORE_MONSTERS , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
// check if line of sight to object is not blocked (i.e. visible)
2016-01-04 18:26:06 +03:00
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 ;
2015-08-15 18:09:15 +03:00
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...
2016-03-01 13:37:10 +03:00
engine . TestLine ( fromBody ? pev - > origin - Vector ( 0.0f , 0.0f , 1.0f ) : EyePosition ( ) , dest , TRACE_IGNORE_EVERYTHING , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
// check if line of sight to object is not blocked (i.e. visible)
2016-01-04 18:26:06 +03:00
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...
2016-03-01 13:37:10 +03:00
if ( m_lastEnemy = = NULL | | yb_ignore_enemies . GetBool ( ) | | yb_jasonmode . GetBool ( ) | | m_grenadeCheckTime > engine . Time ( ) | | 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
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + 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 )
{
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + 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 ) ) )
2015-07-12 17:18:20 +03:00
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
2015-07-12 17:18:20 +03:00
if ( GetNearbyFriendsNearPosition ( m_lastEnemy - > v . origin , 256.0f ) > 0 )
2015-06-11 16:53:21 +03:00
allowThrowing = false ;
2016-03-01 13:37:10 +03:00
if ( allowThrowing & & m_seeEnemyTime + 2.0 < engine . Time ( ) )
2015-06-11 16:53:21 +03:00
{
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03:00
if ( searchRadius < 128.0f )
searchRadius = 128.0f ;
2015-06-11 16:53:21 +03:00
2015-08-02 20:31:14 +03:00
Array < int > predictedPoints ;
2015-06-11 16:53:21 +03:00
// search waypoints
2015-08-15 18:09:15 +03:00
waypoints . FindInRadius ( predictedPoints , searchRadius , enemyPredict , 5 ) ;
2015-06-11 16:53:21 +03:00
2015-08-02 20:31:14 +03:00
FOR_EACH_AE ( predictedPoints , it )
2015-06-11 16:53:21 +03:00
{
allowThrowing = true ;
// check the throwing
2015-08-02 20:31:14 +03:00
m_throw = waypoints . GetPath ( predictedPoints [ it ] ) - > origin ;
2015-06-11 16:53:21 +03:00
Vector src = CheckThrow ( EyePosition ( ) , m_throw ) ;
2015-08-15 18:09:15 +03:00
if ( src . GetLengthSquared ( ) < 100.0f )
2015-06-11 16:53:21 +03:00
src = CheckToss ( EyePosition ( ) , m_throw ) ;
2015-08-15 18:09:15 +03:00
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?
2016-03-01 13:37:10 +03:00
if ( ( m_states & STATE_SEEING_ENEMY ) & & GetShootingConeDeviation ( m_enemy , & pev - > origin ) > = 0.70f & & m_seeEnemyTime + 2.0f < engine . Time ( ) )
2015-06-11 16:53:21 +03:00
m_states & = ~ STATE_THROW_SG ;
else
m_states | = STATE_THROW_SG ;
}
}
}
2015-08-15 18:09:15 +03:00
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 ;
2015-08-15 18:09:15 +03:00
waypoints . FindInRadius ( inRadius , 256.0f , m_lastEnemy - > v . origin + ( m_lastEnemy - > v . velocity * 0.5f ) . Get2D ( ) ) ;
2015-06-11 16:53:21 +03:00
2015-06-28 19:43:31 +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
2015-07-12 17:18:20 +03:00
int friendCount = GetNearbyFriendsNearPosition ( path - > origin , 256.0f ) ;
2016-03-09 23:32:45 +03:00
if ( friendCount > 0 & & m_difficulty < 4 )
2015-06-11 16:53:21 +03:00
continue ;
m_throw = path - > origin ;
Vector src = CheckThrow ( EyePosition ( ) , m_throw ) ;
2015-08-15 18:09:15 +03:00
if ( src . GetLengthSquared ( ) < 100.0f )
2015-06-11 16:53:21 +03:00
src = CheckToss ( EyePosition ( ) , m_throw ) ;
2015-08-15 18:09:15 +03:00
if ( src . IsZero ( ) )
2015-06-11 16:53:21 +03:00
continue ;
allowThrowing = true ;
break ;
}
// start concussion grenade throwing?
2016-03-01 13:37:10 +03:00
if ( allowThrowing & & m_seeEnemyTime + 2.0f < engine . Time ( ) )
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 )
2016-03-01 13:37:10 +03:00
PushTask ( TASK_THROWHEGRENADE , TASKPRI_THROWGRENADE , - 1 , engine . Time ( ) + 3.0f , false ) ;
2015-06-11 16:53:21 +03:00
else if ( m_states & STATE_THROW_FB )
2016-03-01 13:37:10 +03:00
PushTask ( TASK_THROWFLASHBANG , TASKPRI_THROWGRENADE , - 1 , engine . Time ( ) + 3.0f , false ) ;
2015-06-11 16:53:21 +03:00
else if ( m_states & STATE_THROW_SG )
2016-03-01 13:37:10 +03:00
PushTask ( TASK_THROWSMOKE , TASKPRI_THROWGRENADE , - 1 , engine . Time ( ) + 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 ) )
2016-02-06 23:37:58 +03:00
{
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + MAX_GRENADE_TIMER ;
m_maxThrowTimer = engine . Time ( ) + MAX_GRENADE_TIMER * 2.0f ;
2016-02-06 23:37:58 +03:00
}
2015-06-11 16:53:21 +03:00
}
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
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( 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 ;
}
2016-03-12 19:56:09 +03:00
Array < edict_t * > activeGrenades = bots . GetActiveGrenades ( ) ;
2014-07-30 14:17:46 +04:00
// find all grenades on the map
2015-06-28 19:43:31 +03:00
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
2015-06-11 23:22:50 +03:00
if ( ent - > v . effects & EF_NODRAW )
continue ;
2014-07-30 14:17:46 +04:00
// check if visible to the bot
2015-08-15 18:09:15 +03:00
if ( ! EntityIsVisible ( ent - > v . origin ) & & InFieldOfView ( ent - > v . origin - EyePosition ( ) ) > pev - > fov * 0.5f )
2014-07-30 14:17:46 +04:00
continue ;
2016-03-01 13:37:10 +03:00
if ( m_turnAwayFromFlashbang < engine . Time ( ) & & 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
{
2016-03-01 13:37:10 +03:00
pev - > v_angle . y = AngleNormalize ( ( engine . GetAbsOrigin ( ent ) - EyePosition ( ) ) . ToAngles ( ) . y + 180.0f ) ;
2015-07-11 19:54:46 +03:00
2014-07-30 14:17:46 +04:00
m_canChooseAimDirection = false ;
2016-03-01 13:37:10 +03:00
m_turnAwayFromFlashbang = engine . Time ( ) + 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
{
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_avoidGrenade ) )
2014-07-30 14:17:46 +04:00
return ;
2016-03-05 23:08:07 +03:00
if ( engine . 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 ( ) ;
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03: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 ( ) ;
2016-03-12 19:56:09 +03:00
Array < edict_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
2015-06-28 19:43:31 +03:00
FOR_EACH_AE ( activeGrenades , it )
2014-07-30 14:17:46 +04:00
{
2015-06-11 23:22:50 +03: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
2015-06-11 23:22:50 +03:00
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
2015-08-15 18:09:15 +03:00
if ( ! EntityIsVisible ( ent - > v . origin ) & & InFieldOfView ( ent - > v . origin - EyePosition ( ) ) > pev - > fov / 3.0f )
2014-07-30 14:17:46 +04:00
continue ;
2016-03-01 13:37:10 +03:00
const Vector & entityOrigin = engine . GetAbsOrigin ( grenade ) ;
2015-06-11 14:19:52 +03:00
const Vector & betweenNade = ( entityOrigin - pev - > origin ) . Normalize ( ) ;
2015-06-28 19:43:31 +03:00
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 ;
}
2015-06-09 15:45:34 +03:00
void Bot : : VerifyBreakable ( edict_t * touch )
{
2015-07-17 19:23:31 +03:00
if ( ! IsShootableBreakable ( touch ) )
2015-06-09 15:45:34 +03:00
return ;
m_breakableEntity = FindBreakable ( ) ;
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_breakableEntity ) )
2015-06-09 15:45:34 +03:00
return ;
m_campButtons = pev - > button & IN_DUCK ;
2015-08-15 18:09:15 +03:00
PushTask ( TASK_SHOOTBREAKABLE , TASKPRI_SHOOTBREAKABLE , - 1 , 0.0f , false ) ;
2015-06-09 15:45:34 +03:00
}
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 ;
2016-03-12 14:35:44 +03:00
engine . TestLine ( pev - > origin , pev - > origin + ( m_destOrigin - pev - > origin ) . Normalize ( ) * 72.0f , TRACE_IGNORE_NONE , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03: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 ) )
{
2016-03-12 14:35:44 +03:00
m_breakableOrigin = engine . GetAbsOrigin ( ent ) ;
2014-07-30 14:17:46 +04:00
return ent ;
}
}
2016-03-12 14:35:44 +03:00
engine . TestLine ( EyePosition ( ) , EyePosition ( ) + ( m_destOrigin - EyePosition ( ) ) . Normalize ( ) * 72.0f , TRACE_IGNORE_NONE , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction ! = 1.0f )
2014-07-30 14:17:46 +04:00
{
edict_t * ent = tr . pHit ;
if ( IsShootableBreakable ( ent ) )
{
2016-03-12 14:35:44 +03:00
m_breakableOrigin = engine . GetAbsOrigin ( ent ) ;
2014-07-30 14:17:46 +04:00
return ent ;
}
}
m_breakableEntity = NULL ;
2016-03-12 14:35:44 +03:00
m_breakableOrigin . 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 ;
}
2015-06-09 15:45:34 +03:00
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...
2015-06-14 12:55:49 +03:00
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 ;
2015-08-15 18:09:15 +03:00
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
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_pickupItem ) )
2014-07-30 14:17:46 +04:00
{
bool itemExists = false ;
pickupItem = m_pickupItem ;
2016-03-05 23:08:07 +03:00
while ( ! engine . IsNullEntity ( 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 )
{
2016-03-01 13:37:10 +03:00
if ( ItemIsVisible ( engine . GetAbsOrigin ( ent ) , const_cast < char * > ( STRING ( ent - > v . classname ) ) ) )
2014-07-30 14:17:46 +04:00
itemExists = true ;
2015-06-11 14:19:52 +03:00
2014-07-30 14:17:46 +04:00
break ;
}
}
2016-01-07 18:49:55 +03:00
2014-07-30 14:17:46 +04:00
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 ;
2015-08-15 18:09:15 +03:00
Vector pickupOrigin ;
Vector entityOrigin ;
2014-07-30 14:17:46 +04:00
m_pickupItem = NULL ;
m_pickupType = PICKUP_NONE ;
2016-03-05 23:08:07 +03:00
while ( ! engine . IsNullEntity ( ent = FIND_ENTITY_IN_SPHERE ( ent , pev - > origin , searchRadius ) ) )
2014-07-30 14:17:46 +04:00
{
2015-06-28 19:43:31 +03: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
2016-03-01 13:37:10 +03:00
entityOrigin = engine . GetAbsOrigin ( ent ) ;
2014-07-30 14:17:46 +04:00
// 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 ;
}
2016-01-30 13:15:50 +03:00
else if ( strncmp ( " item_thighpack " , STRING ( ent - > v . classname ) , 14 ) = = 0 & & m_team = = CT & & ! 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 ;
2015-08-15 18:09:15 +03:00
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 ;
2015-08-15 18:09:15 +03:00
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 ;
}
2016-01-30 13:15:50 +03:00
else if ( m_team = = TERRORIST ) // 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 ;
2016-03-01 13:37:10 +03:00
if ( ! m_defendHostage & & m_difficulty > = 3 & & Random . Long ( 0 , 100 ) < 30 & & m_timeCamping + 15.0f < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
int index = FindDefendWaypoint ( entityOrigin ) ;
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + Random . Float ( 30.0f , 60.0f ) , true ) ; // push camp task on to stack
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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 ( ) ;
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
if ( timeMidBlowup > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
RemoveCertainTask ( TASK_MOVETOPOSITION ) ; // remove any move tasks
2015-07-12 17:18:20 +03:00
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 ;
2015-06-09 15:45:34 +03:00
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
}
}
}
2016-01-30 13:15:50 +03:00
else if ( m_team = = CT )
2014-07-30 14:17:46 +04:00
{
if ( pickupType = = PICKUP_HOSTAGE )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) | | ent - > v . health < = 0 )
2014-07-30 14:17:46 +04:00
allowPickup = false ; // never pickup dead hostage
2016-03-01 13:37:10 +03:00
else for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-03-05 23:08:07 +03:00
if ( ( bot = bots . GetBot ( i ) ) ! = NULL & & bot - > m_notKilled )
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 ( ) )
{
2016-01-07 18:49:55 +03:00
if ( IsValidPlayer ( m_enemy ) )
2014-07-30 14:17:46 +04:00
{
allowPickup = false ;
return ;
}
2015-06-09 15:45:34 +03:00
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
2015-07-12 17:18:20 +03:00
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 ;
2015-06-09 15:45:34 +03:00
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 ;
2015-06-09 15:45:34 +03:00
if ( ! m_defendedBomb & & m_difficulty > = 2 & & Random . Long ( 0 , 100 ) < 80 )
2014-07-30 14:17:46 +04:00
{
int index = FindDefendWaypoint ( entityOrigin ) ;
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + Random . Float ( 30.0f , 70.0f ) , true ) ; // push camp task on to stack
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( pickupItem ) )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-03-05 23:08:07 +03:00
if ( ( bot = bots . GetBot ( i ) ) ! = NULL & & bot - > m_notKilled & & 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
2016-03-01 13:37:10 +03:00
engine . TestLine ( src , * dest , TRACE_IGNORE_MONSTERS , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
// check if the trace hit something...
2016-01-04 18:26:06 +03:00
if ( tr . flFraction < 1.0f )
2014-07-30 14:17:46 +04:00
{
float length = ( tr . vecEndPos - src ) . GetLengthSquared ( ) ;
2015-08-15 18:09:15 +03:00
if ( length > 10000.0f )
2014-07-30 14:17:46 +04:00
return ;
2015-07-12 17:18:20 +03:00
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 ;
2015-07-12 17:18:20 +03:00
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 ;
2016-01-04 18:26:06 +03:00
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.
2016-02-11 21:50:05 +03:00
if ( ( g_gameFlags & GAME_LEGACY ) | | yb_communication_type . GetInt ( ) ! = 2 )
2014-07-30 14:17:46 +04:00
return ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2015-06-24 15:38:48 +03:00
{
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 ;
2016-03-12 14:35:44 +03:00
MESSAGE_BEGIN ( MSG_ONE , engine . FindMessageId ( 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.
2016-02-11 21:50:05 +03:00
if ( yb_communication_type . GetInt ( ) ! = 2 | | g_chatterFactory [ type ] . IsEmpty ( ) | | ( g_gameFlags & GAME_LEGACY ) | | ! g_sendAudioFinished )
2014-07-30 14:17:46 +04:00
return ;
2014-08-06 00:03:50 +04:00
if ( m_notKilled )
2014-07-30 14:17:46 +04:00
SwitchChatterIcon ( true ) ;
// delay only reportteam
2016-03-09 22:34:24 +03:00
if ( type = = Radio_ReportTeam )
2014-07-30 14:17:46 +04:00
{
2016-03-05 21:04:46 +03:00
if ( m_timeRepotingInDelay < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
return ;
2016-03-05 21:04:46 +03:00
m_timeRepotingInDelay = engine . Time ( ) + Random . Float ( 30.0f , 60.0f ) ;
2014-07-30 14:17:46 +04:00
}
String defaultSound = g_chatterFactory [ type ] . GetRandomElement ( ) . name ;
String painSound = g_chatterFactory [ Chatter_DiePain ] . GetRandomElement ( ) . name ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-03-05 23:08:07 +03:00
edict_t * ent = engine . EntityOfIndex ( i ) ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
if ( ! IsValidPlayer ( ent ) | | IsValidBot ( ent ) | | engine . GetTeam ( ent ) ! = m_team )
2014-07-30 14:17:46 +04:00
continue ;
g_sendAudioFinished = false ;
2016-03-12 14:35:44 +03:00
MESSAGE_BEGIN ( MSG_ONE , engine . FindMessageId ( 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
2015-07-12 17:18:20 +03:00
if ( yb_communication_type . GetInt ( ) = = 0 | | m_numFriendsLeft = = 0 )
2014-07-30 14:17:46 +04:00
return ;
2016-02-11 21:50:05 +03:00
if ( g_chatterFactory [ message ] . IsEmpty ( ) | | ( g_gameFlags & GAME_LEGACY ) | | yb_communication_type . GetInt ( ) ! = 2 )
2014-07-30 14:17:46 +04:00
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)
2016-02-11 21:50:05 +03:00
if ( ( g_gameFlags & GAME_LEGACY ) | | yb_communication_type . GetInt ( ) ! = 2 | | g_chatterFactory [ message ] . IsEmpty ( ) | | m_numFriendsLeft = = 0 )
2014-07-30 14:17:46 +04:00
return ;
bool shouldExecute = false ;
2016-03-01 13:37:10 +03:00
if ( m_chatterTimes [ message ] < engine . Time ( ) | | m_chatterTimes [ message ] = = 99999.0f )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
if ( m_chatterTimes [ message ] ! = 99999.0f )
2016-03-01 13:37:10 +03:00
m_chatterTimes [ message ] = engine . Time ( ) + 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
2016-03-12 14:35:44 +03:00
int state = GetMessageQueue ( ) ;
2014-07-30 14:17:46 +04:00
// nothing to do?
2016-03-12 14:35:44 +03:00
if ( state = = GSM_IDLE | | ( state = = GSM_RADIO & & yb_csdm_mode . GetInt ( ) = = 2 ) )
2014-07-30 14:17:46 +04:00
return ;
2016-03-12 14:35:44 +03:00
switch ( state )
2014-07-30 14:17:46 +04:00
{
case GSM_BUY_STUFF : // general buy message
// buy weapon
2016-03-01 13:37:10 +03:00
if ( m_nextBuyTime > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
// 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 ;
2016-03-01 13:37:10 +03:00
m_nextBuyTime = engine . Time ( ) + 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 ( ) )
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_FINISHED ;
2014-07-30 14:17:46 +04:00
// if fun-mode no need to buy
if ( yb_jasonmode . GetBool ( ) )
{
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_FINISHED ;
2014-07-30 14:17:46 +04:00
SelectWeaponByName ( " weapon_knife " ) ;
}
// prevent vip from buying
2015-06-28 19:43:31 +03:00
if ( IsPlayerVIP ( GetEntity ( ) ) )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
m_isVIP = true ;
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_FINISHED ;
2016-02-29 23:50:16 +03:00
m_pathType = SEARCH_PATH_FASTEST ;
2014-07-30 14:17:46 +04:00
}
// prevent terrorists from buying on es maps
2016-01-30 13:15:50 +03:00
if ( ( g_mapType & MAP_ES ) & & m_team = = TERRORIST )
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 ) )
{
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_FINISHED ;
2014-07-30 14:17:46 +04:00
if ( g_mapType & MAP_KA )
yb_jasonmode . SetInt ( 1 ) ;
}
2016-02-29 22:49:19 +03:00
if ( m_buyState > BUYSTATE_FINISHED - 1 )
2014-07-30 14:17:46 +04:00
{
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
2016-03-01 13:37:10 +03:00
if ( g_lastRadioTime [ m_team ] + 1.0f < engine . Time ( ) )
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 )
{
2016-03-01 13:37:10 +03:00
if ( m_radioSelect = = g_lastRadio [ m_team ] & & g_lastRadioTime [ m_team ] + 1.5f > engine . Time ( ) )
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
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; 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 )
{
2016-03-05 23:08:07 +03:00
if ( pev ! = bot - > pev & & bot - > m_team = = 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 :
2015-06-09 15:45:34 +03:00
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 )
{
2016-01-30 13:15:50 +03:00
if ( ( g_mapType & MAP_DE ) & & m_team = = TERRORIST & & 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 ) ;
2015-06-09 15:45:34 +03:00
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 ) ;
}
2015-06-09 15:45:34 +03:00
else if ( Random . Long ( 0 , 100 ) < 40 )
2014-07-30 14:17:46 +04:00
InstantChatterMessage ( Chatter_ReportingIn ) ;
2015-06-07 19:43:16 +03:00
2014-07-30 14:17:46 +04:00
break ;
case TASK_MOVETOPOSITION :
InstantChatterMessage ( Chatter_GoingToCamp ) ;
break ;
case TASK_CAMP :
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 0 , 100 ) < 40 )
2014-07-30 14:17:46 +04:00
{
2016-01-30 13:15:50 +03:00
if ( g_bombPlanted & & m_team = = TERRORIST )
2014-07-30 14:17:46 +04:00
InstantChatterMessage ( Chatter_GuardDroppedC4 ) ;
2016-01-30 13:15:50 +03:00
else if ( m_inVIPZone & & m_team = = TERRORIST )
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 ;
}
}
2016-02-11 21:50:05 +03:00
if ( ( m_radioSelect ! = Radio_ReportingIn & & g_radioInsteadVoice ) | | yb_communication_type . GetInt ( ) ! = 2 | | g_chatterFactory [ m_radioSelect ] . IsEmpty ( ) | | ( g_gameFlags & GAME_LEGACY ) )
2014-07-30 14:17:46 +04:00
{
if ( m_radioSelect < Radio_GoGoGo )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " radio1 " ) ;
2014-07-30 14:17:46 +04:00
else if ( m_radioSelect < Radio_Affirmative )
{
m_radioSelect - = Radio_GoGoGo - 1 ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " radio2 " ) ;
2014-07-30 14:17:46 +04:00
}
else
{
m_radioSelect - = Radio_Affirmative - 1 ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " radio3 " ) ;
2014-07-30 14:17:46 +04:00
}
// select correct menu item for this radio message
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , m_radioSelect ) ;
2014-07-30 14:17:46 +04:00
}
else if ( m_radioSelect ! = - 1 & & m_radioSelect ! = Radio_ReportingIn )
InstantChatterMessage ( m_radioSelect ) ;
g_radioInsteadVoice = false ; // reset radio to voice
2016-03-01 13:37:10 +03:00
g_lastRadioTime [ m_team ] = engine . Time ( ) ; // 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 ( ' ; ' ) ;
2015-06-28 19:43:31 +03:00
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
2015-06-28 19:43:31 +03:00
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 ;
}
2016-03-01 00:22:42 +03:00
int Bot : : PickBestFromRandom ( int * random , int count , int moneySave )
2015-06-30 00:49:03 +03:00
{
// this function taken from gina bot, it's picks best available weapon from random choice
2016-03-01 00:22:42 +03:00
float buyFactor = ( m_moneyAmount - static_cast < float > ( moneySave ) ) / ( 16000.0f - static_cast < float > ( moneySave ) ) * 3.0f ;
2015-06-30 00:49:03 +03:00
if ( buyFactor < 1.0f )
buyFactor = 1.0f ;
2015-08-15 18:09:15 +03:00
return random [ static_cast < int > ( static_cast < float > ( count - 1 ) * log10f ( Random . Float ( 1 , powf ( 10.0f , buyFactor ) ) ) / buyFactor + 0.5f ) ] ;
2015-06-30 00:49:03 +03:00
}
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 ;
2016-03-01 13:37:10 +03:00
m_nextBuyTime = engine . Time ( ) + 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 ;
2015-06-30 00:49:03 +03:00
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
2016-02-06 23:37:58 +03:00
// do this, because xash engine is not capable to run all the features goldsrc, but we have cs 1.6 on it, so buy table must be the same
2016-02-11 21:50:05 +03:00
bool isOldGame = ( g_gameFlags & GAME_LEGACY ) & & ! ( g_gameFlags & GAME_XASH ) ;
2016-02-06 23:37:58 +03:00
2014-07-30 14:17:46 +04:00
switch ( m_buyState )
{
2016-02-29 22:49:19 +03:00
case BUYSTATE_PRIMARY_WEAPON : // 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
{
2016-03-01 00:22:42 +03:00
int moneySave = 0 ;
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...
2016-02-06 23:37:58 +03:00
if ( isOldGame & & selectedWeapon - > buySelect = = - 1 )
2014-07-30 14:17:46 +04:00
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 ;
2016-02-29 22:49:19 +03:00
int * limit = g_botBuyEconomyTable ;
int prostock = 0 ;
2015-06-04 11:52:48 +03:00
2016-02-29 22:49:19 +03:00
// filter out weapons with bot economics
2014-07-30 14:17:46 +04:00
switch ( m_personality )
{
case PERSONALITY_RUSHER :
2016-02-29 22:49:19 +03:00
prostock = limit [ ECO_PROSTOCK_RUSHER ] ;
2014-07-30 14:17:46 +04:00
break ;
case PERSONALITY_CAREFUL :
2016-02-29 22:49:19 +03:00
prostock = limit [ ECO_PROSTOCK_CAREFUL ] ;
2014-07-30 14:17:46 +04:00
break ;
case PERSONALITY_NORMAL :
2016-02-29 22:49:19 +03:00
prostock = limit [ ECO_PROSTOCK_NORMAL ] ;
2014-07-30 14:17:46 +04:00
break ;
}
2016-01-30 13:15:50 +03:00
if ( m_team = = CT )
2014-07-30 14:17:46 +04:00
{
switch ( selectedWeapon - > id )
{
case WEAPON_TMP :
case WEAPON_UMP45 :
case WEAPON_P90 :
case WEAPON_MP5 :
2016-02-29 22:49:19 +03:00
if ( m_moneyAmount > limit [ ECO_SMG_GT_CT ] + prostock )
2014-07-30 14:17:46 +04:00
ignoreWeapon = true ;
break ;
}
2016-02-29 22:49:19 +03:00
if ( selectedWeapon - > id = = WEAPON_SHIELD & & m_moneyAmount > limit [ ECO_SHIELDGUN_GT ] )
2014-07-30 14:17:46 +04:00
ignoreWeapon = true ;
}
2016-01-30 13:15:50 +03:00
else if ( m_team = = TERRORIST )
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 :
2016-02-29 22:49:19 +03:00
if ( m_moneyAmount > limit [ ECO_SMG_GT_TE ] + prostock )
2014-07-30 14:17:46 +04:00
ignoreWeapon = true ;
break ;
}
}
switch ( selectedWeapon - > id )
{
case WEAPON_XM1014 :
case WEAPON_M3 :
2016-03-01 00:22:42 +03:00
if ( m_moneyAmount < limit [ ECO_SHOTGUN_LT ] )
2014-07-30 14:17:46 +04:00
ignoreWeapon = true ;
2016-03-01 00:22:42 +03:00
if ( m_moneyAmount > = limit [ ECO_SHOTGUN_GT ] )
ignoreWeapon = false ;
2014-07-30 14:17:46 +04:00
break ;
}
switch ( selectedWeapon - > id )
{
case WEAPON_SG550 :
case WEAPON_G3SG1 :
2015-07-19 13:39:00 +03:00
case WEAPON_AWP :
2014-07-30 14:17:46 +04:00
case WEAPON_M249 :
2016-03-01 00:22:42 +03:00
if ( m_moneyAmount < limit [ ECO_HEAVY_LT ] )
2014-07-30 14:17:46 +04:00
ignoreWeapon = true ;
2016-03-01 00:22:42 +03:00
if ( m_moneyAmount > = limit [ ECO_HEAVY_GT ] )
ignoreWeapon = false ;
2014-07-30 14:17:46 +04:00
break ;
}
if ( ignoreWeapon & & g_weaponSelect [ 25 ] . teamStandard = = 1 & & yb_economics_rounds . GetBool ( ) )
continue ;
2016-02-29 22:49:19 +03:00
// save money for grenade for example?
2016-03-01 00:22:42 +03:00
moneySave = Random . Long ( 300 , 600 ) ;
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 )
2016-03-01 00:22:42 +03:00
chosenWeapon = PickBestFromRandom ( choices , foundWeapons , moneySave ) ;
2014-07-30 14:17:46 +04:00
else
chosenWeapon = choices [ foundWeapons - 1 ] ;
selectedWeapon = & g_weaponSelect [ chosenWeapon ] ;
}
else
selectedWeapon = NULL ;
if ( selectedWeapon ! = NULL )
{
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buy;menuselect %d " , selectedWeapon - > buyGroup ) ;
2014-07-30 14:17:46 +04:00
2016-02-06 23:37:58 +03:00
if ( isOldGame )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > buySelect ) ;
2014-07-30 14:17:46 +04:00
else // SteamCS buy menu is different from the old one
{
2016-01-30 13:15:50 +03:00
if ( m_team = = TERRORIST )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > newBuySelectT ) ;
2014-07-30 14:17:46 +04:00
else
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > newBuySelectCT ) ;
2014-07-30 14:17:46 +04:00
}
}
}
else if ( HasPrimaryWeapon ( ) & & ! HasShield ( ) )
{
m_reloadState = RELOAD_PRIMARY ;
break ;
}
else if ( ( HasSecondaryWeapon ( ) & & ! HasShield ( ) ) | | HasShield ( ) )
{
m_reloadState = RELOAD_SECONDARY ;
break ;
}
2016-02-29 22:49:19 +03:00
case BUYSTATE_ARMOR_VESTHELM : // if armor is damaged and bot has some money, buy some armor
2015-06-09 15:45:34 +03:00
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 ) )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip;menuselect 2 " ) ;
2014-07-30 14:17:46 +04:00
else if ( ! IsRestricted ( WEAPON_ARMOR ) )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip;menuselect 1 " ) ;
2014-07-30 14:17:46 +04:00
}
break ;
2016-02-29 22:49:19 +03:00
case BUYSTATE_SECONDARY_WEAPON : // if bot has still some money, buy a better secondary weapon
2015-06-09 15:45:34 +03:00
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 ;
2016-02-06 23:37:58 +03:00
if ( isOldGame & & selectedWeapon - > buySelect = = - 1 )
2014-07-30 14:17:46 +04:00
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 ;
2015-06-09 15:45:34 +03:00
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 )
2016-03-01 00:22:42 +03:00
chosenWeapon = PickBestFromRandom ( choices , foundWeapons , Random . Long ( 100 , 200 ) ) ;
2014-07-30 14:17:46 +04:00
else
chosenWeapon = choices [ foundWeapons - 1 ] ;
selectedWeapon = & g_weaponSelect [ chosenWeapon ] ;
}
else
selectedWeapon = NULL ;
if ( selectedWeapon ! = NULL )
{
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buy;menuselect %d " , selectedWeapon - > buyGroup ) ;
2014-07-30 14:17:46 +04:00
2016-02-06 23:37:58 +03:00
if ( isOldGame )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > buySelect ) ;
2015-06-30 00:49:03 +03:00
2014-07-30 14:17:46 +04:00
else // steam cs buy menu is different from old one
{
2016-03-05 23:08:07 +03:00
if ( engine . GetTeam ( GetEntity ( ) ) = = TERRORIST )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > newBuySelectT ) ;
2014-07-30 14:17:46 +04:00
else
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , selectedWeapon - > newBuySelectCT ) ;
2014-07-30 14:17:46 +04:00
}
}
}
break ;
2016-02-29 22:49:19 +03:00
case BUYSTATE_GRENADES : // if bot has still some money, choose if bot should buy a grenade or not
2015-06-09 15:45:34 +03:00
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
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip " ) ;
engine . IssueBotCommand ( GetEntity ( ) , " menuselect 4 " ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-09 15:45:34 +03:00
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'
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip " ) ;
engine . IssueBotCommand ( GetEntity ( ) , " menuselect 3 " ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-09 15:45:34 +03:00
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
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip " ) ;
engine . IssueBotCommand ( GetEntity ( ) , " menuselect 5 " ) ;
2014-07-30 14:17:46 +04:00
}
break ;
2016-02-29 22:49:19 +03:00
case BUYSTATE_DEFUSER : // if bot is CT and we're on a bomb map, randomly buy the defuse kit
2016-01-30 13:15:50 +03:00
if ( ( g_mapType & MAP_DE ) & & m_team = = CT & & Random . Long ( 1 , 100 ) < 80 & & m_moneyAmount > 200 & & ! IsRestricted ( WEAPON_DEFUSER ) )
2014-07-30 14:17:46 +04:00
{
2016-02-06 23:37:58 +03:00
if ( isOldGame )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyequip;menuselect 6 " ) ;
2014-07-30 14:17:46 +04:00
else
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " defuser " ) ; // use alias in SteamCS
2014-07-30 14:17:46 +04:00
}
break ;
2016-02-29 22:49:19 +03:00
case BUYSTATE_AMMO : // buy enough primary & secondary ammo (do not check for money here)
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < = 5 ; i + + )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buyammo%d " , Random . Long ( 1 , 2 ) ) ; // simulate human
2014-07-30 14:17:46 +04:00
// buy enough secondary ammo
if ( HasPrimaryWeapon ( ) )
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buy;menuselect 7 " ) ;
2014-07-30 14:17:46 +04:00
// buy enough primary ammo
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " buy;menuselect 6 " ) ;
2014-07-30 14:17:46 +04:00
// try to reload secondary weapon
if ( m_reloadState ! = RELOAD_PRIMARY )
m_reloadState = RELOAD_SECONDARY ;
break ;
}
m_buyState + + ;
PushMessageQueue ( GSM_BUY_STUFF ) ;
}
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
2016-03-01 13:37:10 +03:00
if ( m_nextEmotionUpdate > engine . Time ( ) )
2015-07-17 19:23:31 +03:00
return ;
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
2016-03-01 13:37:10 +03:00
m_nextEmotionUpdate = engine . Time ( ) + 1.0f ;
2015-07-17 19:23:31 +03:00
}
2016-01-14 23:32:38 +03:00
void Bot : : SetConditionsOverride ( void )
{
if ( m_currentWeapon ! = WEAPON_KNIFE & & 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 ( ) )
{
m_moveToGoal = false ; // don't move to goal
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2016-01-14 23:32:38 +03:00
if ( IsValidPlayer ( m_enemy ) )
CombatFight ( ) ;
}
// 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 ) ;
}
// special handling, if we have a knife in our hands
if ( m_currentWeapon = = WEAPON_KNIFE & & IsValidPlayer ( m_enemy ) & & ( GetTaskId ( ) ! = TASK_MOVETOPOSITION | | GetTask ( ) - > desire ! = TASKPRI_HIDE ) )
{
2016-02-11 21:50:05 +03:00
float length = ( pev - > origin - m_enemy - > v . origin ) . GetLength2D ( ) ;
// do waypoint movement if enemy is not reacheable with a knife
if ( length > 100.0f & & ( m_states & STATE_SEEING_ENEMY ) )
2016-01-14 23:32:38 +03:00
{
int nearestToEnemyPoint = waypoints . FindNearest ( m_enemy - > v . origin ) ;
if ( nearestToEnemyPoint ! = - 1 & & nearestToEnemyPoint ! = m_currentWaypointIndex & & fabsf ( waypoints . GetPath ( nearestToEnemyPoint ) - > origin . z - m_enemy - > v . origin . z ) < 16.0f )
{
2016-03-01 13:37:10 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_HIDE , nearestToEnemyPoint , engine . Time ( ) + Random . Float ( 5.0f , 10.0f ) , true ) ;
2016-02-11 21:50:05 +03:00
m_isEnemyReachable = false ;
2016-01-14 23:32:38 +03:00
m_enemy = NULL ;
2016-02-11 21:50:05 +03:00
2016-03-01 13:37:10 +03:00
m_enemyIgnoreTimer = engine . Time ( ) + ( ( length / pev - > maxspeed ) * 0.5f ) ;
2016-01-14 23:32:38 +03:00
}
}
}
}
2015-07-17 19:23:31 +03:00
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?
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_lastVictim ) )
2014-07-30 14:17:46 +04:00
{
2016-03-05 23:08:07 +03:00
if ( engine . 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
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 10 )
2014-07-30 14:17:46 +04:00
ChatMessage ( CHAT_KILLING ) ;
2015-06-09 15:45:34 +03:00
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
{
2015-07-25 16:51:48 +03:00
switch ( GetNearbyEnemiesNearPosition ( pev - > origin , 99999.0f ) )
2014-07-30 14:17:46 +04:00
{
case 0 :
2015-06-09 15:45:34 +03:00
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
2016-01-30 13:15:50 +03:00
if ( m_team = = CT & & 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
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_lastEnemy ) )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
if ( ! IsAlive ( m_lastEnemy ) & & m_shootAtDeadTime < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
m_lastEnemyOrigin . Zero ( ) ;
2014-07-30 14:17:46 +04:00
m_lastEnemy = NULL ;
}
}
else
{
2015-08-15 18:09:15 +03:00
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)
2016-03-01 13:37:10 +03:00
if ( ! yb_ignore_enemies . GetBool ( ) & & m_soundUpdateTime < engine . Time ( ) & & m_blindTime < engine . Time ( ) & & m_seeEnemyTime + 1.0f < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
ReactOnSound ( ) ;
2016-03-01 13:37:10 +03:00
m_soundUpdateTime = engine . Time ( ) + 0.25f ;
2014-07-30 14:17:46 +04:00
}
2016-03-01 13:37:10 +03:00
else if ( m_heardSoundTime < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
m_states & = ~ STATE_HEARING_ENEMY ;
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_enemy ) & & ! engine . IsNullEntity ( 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
2016-03-05 23:08:07 +03:00
if ( m_itemCheckTime < engine . Time ( ) | | ! engine . IsNullEntity ( m_pickupItem ) )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
m_itemCheckTime = engine . Time ( ) + 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 ;
2015-08-15 18:09:15 +03:00
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 )
2015-08-15 18:09:15 +03:00
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 ( ) )
{
2015-08-15 18:09:15 +03:00
tempFear = tempFear * 1.5f ;
tempAgression = tempAgression * 0.5f ;
2014-07-30 14:17:46 +04:00
}
// bot found some item to use?
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_pickupItem ) & & GetTaskId ( ) ! = TASK_ESCAPEFROMBOMB )
2014-07-30 14:17:46 +04:00
{
m_states | = STATE_PICKUP_ITEM ;
if ( m_pickupType = = PICKUP_BUTTON )
2015-08-15 18:09:15 +03:00
g_taskFilters [ TASK_PICKUPITEM ] . desire = 50.0f ; // always pickup button
2014-07-30 14:17:46 +04:00
else
{
2016-03-01 13:37:10 +03:00
float distance = ( 500.0f - ( engine . GetAbsOrigin ( m_pickupItem ) - pev - > origin ) . GetLength ( ) ) * 0.2f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03: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 ;
2015-08-15 18:09:15 +03:00
g_taskFilters [ TASK_PICKUPITEM ] . desire = 0.0f ;
2014-07-30 14:17:46 +04:00
}
// calculate desire to attack
2015-06-07 19:43:16 +03:00
if ( ( m_states & STATE_SEEING_ENEMY ) & & ReactOnEnemy ( ) )
2014-07-30 14:17:46 +04:00
g_taskFilters [ TASK_ATTACK ] . desire = TASKPRI_ATTACK ;
else
2015-08-15 18:09:15 +03:00
g_taskFilters [ TASK_ATTACK ] . desire = 0.0f ;
2014-07-30 14:17:46 +04:00
// calculate desires to seek cover or hunt
2016-02-11 21:50:05 +03:00
if ( IsValidPlayer ( m_lastEnemy ) & & ! m_lastEnemyOrigin . IsZero ( ) & & ! m_hasC4 )
2014-07-30 14:17:46 +04:00
{
float distance = ( m_lastEnemyOrigin - pev - > origin ) . GetLength ( ) ;
// retreat level depends on bot health
2015-08-15 18:09:15 +03:00
float retreatLevel = ( 100.0f - ( pev - > health > 100.0f ? 100.0f : pev - > health ) ) * tempFear ;
2016-03-01 13:37:10 +03:00
float timeSeen = m_seeEnemyTime - engine . Time ( ) ;
float timeHeard = m_heardSoundTime - engine . Time ( ) ;
2015-08-15 18:09:15 +03:00
float ratio = 0.0f ;
2014-07-30 14:17:46 +04:00
if ( timeSeen > timeHeard )
{
2015-08-15 18:09:15 +03:00
timeSeen + = 10.0f ;
ratio = timeSeen * 0.1f ;
2014-07-30 14:17:46 +04:00
}
else
{
2015-08-15 18:09:15 +03:00
timeHeard + = 10.0f ;
ratio = timeHeard * 0.1f ;
2014-07-30 14:17:46 +04:00
}
2016-01-14 23:32:38 +03:00
if ( g_bombPlanted | | m_isStuck | | m_currentWeapon = = WEAPON_KNIFE )
2015-08-15 18:09:15 +03:00
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 )
2015-08-15 18:09:15 +03:00
ratio * = 3.0f ; // triple the seek cover desire if bot is VIP or reloading
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03: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
2016-03-05 23:08:07 +03:00
if ( GetTaskId ( ) ! = TASK_ESCAPEFROMBOMB & & engine . IsNullEntity ( m_enemy ) & & g_timeRoundMid < engine . Time ( ) & & ! m_isUsingGrenade & & m_currentWaypointIndex ! = waypoints . FindNearest ( m_lastEnemyOrigin ) & & m_personality ! = PERSONALITY_CAREFUL )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
float desireLevel = 4096.0f - ( ( 1.0f - tempAgression ) * distance ) ;
2015-06-24 15:38:48 +03:00
2015-08-15 18:09:15 +03:00
desireLevel = ( 100.0f * desireLevel ) / 4096.0 ;
2014-07-30 14:17:46 +04:00
desireLevel - = retreatLevel ;
2015-08-15 18:09:15 +03:00
if ( desireLevel > 89.0f )
desireLevel = 89.0f ;
2014-07-30 14:17:46 +04:00
g_taskFilters [ TASK_HUNTENEMY ] . desire = desireLevel ;
}
else
2015-08-15 18:09:15 +03:00
g_taskFilters [ TASK_HUNTENEMY ] . desire = 0.0f ;
2014-07-30 14:17:46 +04:00
}
else
{
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
if ( m_blindTime > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
g_taskFilters [ TASK_BLINDED ] . desire = TASKPRI_BLINDED ;
else
2015-08-15 18:09:15 +03:00
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.
2015-08-15 18:09:15 +03:00
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)
2015-08-15 18:09:15 +03:00
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 ) ;
2015-08-15 18:09:15 +03:00
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 ( ) ) ;
2015-07-12 17:18:20 +03:00
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 ( ) ;
}
2015-07-12 17:18:20 +03:00
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
2016-01-27 21:40:47 +03:00
int taskId = GetTaskId ( ) ;
2014-07-30 14:17:46 +04:00
// leader bot?
2016-01-27 21:40:47 +03:00
if ( m_isLeader & & taskId = = TASK_SEEKCOVER )
2014-07-30 14:17:46 +04:00
CommandTeam ( ) ; // reorganize team if fleeing
2016-01-27 21:40:47 +03:00
if ( taskId = = TASK_CAMP )
2014-07-30 14:17:46 +04:00
SelectBestWeapon ( ) ;
// this is best place to handle some voice commands report team some info
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 0 , 100 ) < 95 )
2014-07-30 14:17:46 +04:00
{
2016-01-27 21:40:47 +03:00
if ( taskId = = TASK_BLINDED )
2014-07-30 14:17:46 +04:00
InstantChatterMessage ( Chatter_GotBlinded ) ;
2016-01-27 21:40:47 +03:00
else if ( taskId = = TASK_PLANTBOMB )
2014-07-30 14:17:46 +04:00
InstantChatterMessage ( Chatter_PlantingC4 ) ;
}
2016-01-27 21:40:47 +03:00
if ( Random . Long ( 0 , 100 ) < 80 & & taskId = = 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 ;
2016-01-30 13:15:50 +03:00
if ( Random . Long ( 0 , 100 ) < 80 & & GetTaskId ( ) = = TASK_CAMP & & m_team = = TERRORIST & & 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 ;
2015-08-15 18:09:15 +03:00
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 ;
}
2015-06-28 19:43:31 +03:00
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 )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_enemy ) | | GetTaskId ( ) = = TASK_SEEKCOVER )
2014-07-30 14:17:46 +04:00
return false ;
// 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
2016-01-14 23:32:38 +03:00
if ( ( m_enemy - > v . origin - pev - > origin ) . GetLength ( ) < 256.0f | | IsInViewCone ( m_enemy - > v . origin ) )
2014-07-30 14:17:46 +04:00
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 ;
2016-03-01 13:37:10 +03:00
if ( m_enemyReachableTimer < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
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 ( ) ;
2016-01-04 18:26:06 +03:00
float pathDist = static_cast < float > ( waypoints . GetPathDistance ( i , enemyIndex ) ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( pathDist - lineDist > 112.0f )
2014-07-30 14:17:46 +04:00
m_isEnemyReachable = false ;
else
m_isEnemyReachable = true ;
2016-03-01 13:37:10 +03:00
m_enemyReachableTimer = engine . Time ( ) + 1.0f ;
2014-07-30 14:17:46 +04:00
}
if ( m_isEnemyReachable )
{
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ; // override existing movement by attack movement
2014-07-30 14:17:46 +04:00
return true ;
}
return false ;
}
bool Bot : : LastEnemyShootable ( void )
{
2015-06-04 11:52:48 +03:00
// don't allow shooting through walls
2016-03-05 23:08:07 +03:00
if ( ! ( m_aimFlags & AIM_LAST_ENEMY ) | | m_lastEnemyOrigin . IsZero ( ) | | engine . IsNullEntity ( m_lastEnemy ) )
2014-07-30 14:17:46 +04:00
return false ;
2015-08-15 18:09:15 +03:00
return GetShootingConeDeviation ( GetEntity ( ) , & m_lastEnemyOrigin ) > = 0.90f & & IsShootableThruObstacle ( m_lastEnemyOrigin ) ;
2014-07-30 14:17:46 +04:00
}
void Bot : : CheckRadioCommands ( void )
{
2016-03-08 17:22:44 +03:00
// this function handling radio and reacting to it
2014-07-30 14:17:46 +04:00
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 ) )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_targetEntity ) & & engine . IsNullEntity ( 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
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; 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 )
{
2016-03-05 23:08:07 +03:00
if ( bot - > m_notKilled )
2014-07-30 14:17:46 +04:00
{
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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
PushTask ( TASK_FOLLOWUSER , TASKPRI_FOLLOWUSER , - 1 , 0.0f , true ) ;
2014-07-30 14:17:46 +04:00
}
else if ( numFollowers > allowedFollowers )
{
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; ( i < engine . MaxClients ( ) & & 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 )
{
2016-03-05 23:08:07 +03:00
if ( bot - > m_notKilled )
2014-07-30 14:17:46 +04:00
{
if ( bot - > m_targetEntity = = m_radioEntity )
{
bot - > m_targetEntity = NULL ;
numFollowers - - ;
}
}
}
}
}
2016-02-01 00:30:28 +03:00
else if ( m_radioOrder ! = Chatter_GoingToPlantBomb & & Random . Long ( 0 , 100 ) < 50 )
2014-07-30 14:17:46 +04:00
RadioMessage ( Radio_Negative ) ;
}
2016-02-01 00:30:28 +03:00
else if ( m_radioOrder ! = Chatter_GoingToPlantBomb & & Random . Long ( 0 , 100 ) < 50 )
2014-07-30 14:17:46 +04:00
RadioMessage ( Radio_Negative ) ;
}
break ;
case Radio_HoldPosition :
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_targetEntity ) )
2014-07-30 14:17:46 +04:00
{
if ( m_targetEntity = = m_radioEntity )
{
m_targetEntity = NULL ;
RadioMessage ( Radio_Affirmative ) ;
m_campButtons = 0 ;
2016-03-01 13:37:10 +03:00
PushTask ( TASK_PAUSE , TASKPRI_PAUSE , - 1 , engine . Time ( ) + 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 :
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_targetEntity ) )
2014-07-30 14:17:46 +04:00
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_enemy ) & & m_seeEnemyTime + 4.0f < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2015-06-11 23:22:50 +03:00
// decrease fear levels to lower probability of bot seeking cover again
2015-08-15 18:09:15 +03:00
m_fearLevel - = 0.2f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel < 0.0f )
m_fearLevel = 0.0f ;
2014-07-30 14:17:46 +04:00
2015-06-09 15:45:34 +03: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 :
2016-03-05 23:08:07 +03:00
if ( ( ( engine . IsNullEntity ( m_enemy ) & & EntityIsVisible ( m_radioEntity - > v . origin ) ) | | distance < 2048.0f | | ! m_moveToC4 ) & & Random . Long ( 0 , 100 ) > 50 & & m_seeEnemyTime + 4.0f < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
m_fearLevel - = 0.1f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel < 0.0f )
m_fearLevel = 0.0f ;
2014-07-30 14:17:46 +04:00
2015-06-09 15:45:34 +03: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
}
2016-02-01 00:30:28 +03:00
else if ( Random . Long ( 0 , 100 ) < 60 & & m_radioOrder = = Radio_NeedBackup )
2014-07-30 14:17:46 +04:00
RadioMessage ( Radio_Negative ) ;
break ;
case Radio_GoGoGo :
if ( m_radioEntity = = m_targetEntity )
{
2015-06-09 15:45:34 +03:00
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 ;
2015-08-15 18:09:15 +03:00
m_fearLevel - = 0.2f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel < 0.0f )
m_fearLevel = 0.0f ;
2014-07-30 14:17:46 +04:00
}
2016-03-05 23:08:07 +03:00
else if ( ( engine . IsNullEntity ( m_enemy ) & & EntityIsVisible ( m_radioEntity - > v . origin ) ) | | distance < 2048.0f )
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 )
{
2015-08-15 18:09:15 +03:00
m_fearLevel - = 0.2f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03: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
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_targetEntity = NULL ;
MakeVectors ( m_radioEntity - > v . v_angle ) ;
2015-08-15 18:09:15 +03:00
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 ( ) ;
2015-08-15 18:09:15 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , - 1 , 0.0f , true ) ;
2014-07-30 14:17:46 +04:00
}
}
2016-03-05 23:08:07 +03:00
else if ( ! engine . IsNullEntity ( 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 :
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_enemy ) & & distance < 2048.0f & & g_bombPlanted & & m_team = = TERRORIST )
2014-07-30 14:17:46 +04:00
{
RadioMessage ( Radio_Affirmative ) ;
if ( GetTaskId ( ) = = TASK_CAMP )
RemoveCertainTask ( TASK_CAMP ) ;
m_targetEntity = NULL ;
2015-08-15 18:09:15 +03:00
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
2016-02-01 00:30:28 +03:00
if ( m_team = = CT & & 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 :
2016-03-05 23:08:07 +03:00
if ( ( ( engine . IsNullEntity ( 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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_targetEntity = NULL ;
MakeVectors ( m_radioEntity - > v . v_angle ) ;
2015-08-15 18:09:15 +03:00
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 ( ) ;
2015-08-15 18:09:15 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , - 1 , 0.0f , true ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
m_fearLevel - = 0.3f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel < 0.0f )
m_fearLevel = 0.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
m_agressionLevel + = 0.3f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_agressionLevel > 1.0f )
m_agressionLevel = 1.0f ;
2014-07-30 14:17:46 +04:00
}
break ;
case Radio_Fallback :
2016-03-05 23:08:07 +03:00
if ( ( engine . IsNullEntity ( m_enemy ) & & EntityIsVisible ( m_radioEntity - > v . origin ) ) | | distance < 1024.0f )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
m_fearLevel + = 0.5f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel > 1.0f )
m_fearLevel = 1.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
m_agressionLevel - = 0.5f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_agressionLevel < 0.0f )
m_agressionLevel = 0.0f ;
2014-07-30 14:17:46 +04:00
if ( GetTaskId ( ) = = TASK_CAMP )
2015-08-15 18:09:15 +03:00
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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_targetEntity = NULL ;
2016-03-01 13:37:10 +03:00
m_seeEnemyTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
// if bot has no enemy
2015-08-15 18:09:15 +03:00
if ( m_lastEnemyOrigin . IsZero ( ) )
2014-07-30 14:17:46 +04:00
{
2015-07-12 17:18:20 +03:00
float nearestDistance = 99999.0f ;
2014-07-30 14:17:46 +04:00
// take nearest enemy to ordering player
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
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 ;
2016-01-14 23:32:38 +03:00
2014-07-30 14:17:46 +04:00
m_lastEnemy = enemy ;
m_lastEnemyOrigin = enemy - > v . origin ;
}
}
}
DeleteSearchNodes ( ) ;
}
}
break ;
case Radio_ReportTeam :
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 0 , 100 ) < 30 )
2015-08-15 18:09:15 +03:00
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
2016-03-09 22:34:24 +03:00
if ( ! g_bombPlanted )
break ;
// check if it's a ct command
if ( engine . GetTeam ( m_radioEntity ) = = CT & & m_team = = CT & & IsValidBot ( m_radioEntity ) & & g_timeNextBombUpdate < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2016-03-09 22:34:24 +03:00
float minDistance = 99999.0f ;
2014-07-30 14:17:46 +04:00
int bombPoint = - 1 ;
2016-03-09 22:34:24 +03:00
// find nearest bomb waypoint to player
FOR_EACH_AE ( waypoints . m_goalPoints , i )
2014-07-30 14:17:46 +04:00
{
2016-03-09 22:34:24 +03:00
distance = ( waypoints . GetPath ( waypoints . m_goalPoints [ i ] ) - > origin - m_radioEntity - > v . origin ) . GetLengthSquared ( ) ;
2016-03-05 21:04:46 +03:00
2016-03-09 22:34:24 +03:00
if ( distance < minDistance )
2014-07-30 14:17:46 +04:00
{
2016-03-09 22:34:24 +03:00
minDistance = distance ;
bombPoint = waypoints . m_goalPoints [ i ] ;
2016-03-05 21:04:46 +03:00
}
2016-03-09 22:34:24 +03:00
}
2014-07-30 14:17:46 +04:00
2016-03-09 22:34:24 +03:00
// mark this waypoint as restricted point
if ( bombPoint ! = - 1 & & ! waypoints . IsGoalVisited ( bombPoint ) )
{
// does this bot want to defuse?
if ( GetTaskId ( ) = = TASK_NORMAL )
2016-03-05 21:04:46 +03:00
{
2016-03-09 22:34:24 +03:00
// is he approaching this goal?
if ( GetTask ( ) - > data = = bombPoint )
2014-07-30 14:17:46 +04:00
{
2016-03-09 22:34:24 +03:00
GetTask ( ) - > data = - 1 ;
RadioMessage ( Radio_Affirmative ) ;
2015-06-28 19:43:31 +03:00
2014-07-30 14:17:46 +04:00
}
}
2016-03-09 22:34:24 +03:00
waypoints . SetGoalVisited ( bombPoint ) ;
2014-07-30 14:17:46 +04:00
}
2016-03-09 22:34:24 +03:00
g_timeNextBombUpdate = engine . Time ( ) + 0.5f ;
2014-07-30 14:17:46 +04:00
}
break ;
case Radio_GetInPosition :
2016-03-05 23:08:07 +03:00
if ( ( engine . IsNullEntity ( 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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) + 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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_targetEntity = NULL ;
2016-03-01 13:37:10 +03:00
m_seeEnemyTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
// if bot has no enemy
2015-08-15 18:09:15 +03:00
if ( m_lastEnemyOrigin . IsZero ( ) )
2014-07-30 14:17:46 +04:00
{
2015-07-12 17:18:20 +03: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
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
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
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + Random . Float ( 30.0f , 60.0f ) , true ) ;
2014-07-30 14:17:46 +04:00
// push move command
2016-03-01 13:37:10 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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
2016-03-01 13:37:10 +03:00
if ( taskID = = TASK_MOVETOPOSITION | | m_headedTime + 15.0f < engine . Time ( ) | | ! IsAlive ( m_radioEntity ) | | m_hasC4 )
2015-06-04 11:52:48 +03:00
return ;
2015-06-09 15:45:34 +03:00
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 )
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) ;
2015-06-04 11:52:48 +03:00
2016-03-01 13:37:10 +03:00
m_headedTime = engine . Time ( ) ;
2015-06-04 11:52:48 +03:00
m_position = m_radioEntity - > v . origin ;
DeleteSearchNodes ( ) ;
2015-08-15 18:09:15 +03:00
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 )
{
2016-01-30 13:15:50 +03:00
if ( m_isVIP & & ! g_leaderChoosen [ CT ] )
2014-07-30 14:17:46 +04:00
{
// vip bot is the leader
m_isLeader = true ;
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 50 )
2014-07-30 14:17:46 +04:00
{
RadioMessage ( Radio_FollowMe ) ;
m_campButtons = 0 ;
}
2016-01-30 13:15:50 +03:00
g_leaderChoosen [ CT ] = true ;
2014-07-30 14:17:46 +04:00
}
2016-03-05 23:08:07 +03:00
else if ( team = = TERRORIST & & ! g_leaderChoosen [ TERRORIST ] )
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
Bot * botLeader = bots . GetHighestFragsBot ( team ) ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
if ( botLeader ! = NULL & & botLeader - > m_notKilled )
2014-07-30 14:17:46 +04:00
{
botLeader - > m_isLeader = true ;
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 45 )
2014-07-30 14:17:46 +04:00
botLeader - > RadioMessage ( Radio_FollowMe ) ;
}
2016-01-30 13:15:50 +03:00
g_leaderChoosen [ TERRORIST ] = true ;
2014-07-30 14:17:46 +04:00
}
}
else if ( g_mapType & MAP_DE )
{
2016-01-30 13:15:50 +03:00
if ( team = = TERRORIST & & ! g_leaderChoosen [ TERRORIST ] )
2014-07-30 14:17:46 +04:00
{
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
2015-06-09 15:45:34 +03:00
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 ;
}
2016-01-30 13:15:50 +03:00
g_leaderChoosen [ TERRORIST ] = true ;
2014-07-30 14:17:46 +04:00
}
}
2016-01-30 13:15:50 +03:00
else if ( ! g_leaderChoosen [ CT ] )
2014-07-30 14:17:46 +04:00
{
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 ;
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 30 )
2014-07-30 14:17:46 +04:00
botLeader - > RadioMessage ( Radio_FollowMe ) ;
}
2016-01-30 13:15:50 +03:00
g_leaderChoosen [ CT ] = true ;
2014-07-30 14:17:46 +04:00
}
}
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 ;
2015-06-09 15:45:34 +03:00
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 ;
2016-01-30 13:15:50 +03:00
if ( Random . Long ( 1 , 100 ) < ( team = = TERRORIST ? 30 : 40 ) )
2014-07-30 14:17:46 +04:00
botLeader - > RadioMessage ( Radio_FollowMe ) ;
}
}
}
void Bot : : ChooseAimDirection ( void )
{
2015-06-11 23:22:50 +03:00
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 ) )
{
2015-06-07 19:43:16 +03:00
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 )
2015-08-15 18:09:15 +03:00
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?
2016-03-01 13:37:10 +03:00
if ( m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
// feel free to fire if shootable
if ( ! UsesSniper ( ) & & LastEnemyShootable ( ) )
m_wantsToFire = true ;
}
}
2015-06-07 19:43:16 +03:00
else if ( flags & AIM_PREDICT_PATH )
2014-07-30 14:17:46 +04:00
{
2015-07-19 13:39:00 +03:00
bool changePredictedEnemy = true ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
if ( m_trackingEdict = = m_lastEnemy )
{
2016-03-01 13:37:10 +03:00
if ( m_timeNextTracking < engine . Time ( ) )
2016-03-12 19:56:09 +03:00
changePredictedEnemy = false ;
2014-07-30 14:17:46 +04:00
}
2015-07-19 13:39:00 +03: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 ;
2015-07-19 13:39:00 +03:00
m_camp = m_lookAt ;
2014-07-30 14:17:46 +04:00
2016-03-12 19:56:09 +03:00
m_timeNextTracking = engine . Time ( ) + 1.25f ;
2015-07-19 13:39:00 +03:00
m_trackingEdict = m_lastEnemy ;
2014-07-30 14:17:46 +04:00
}
2015-07-19 13:39:00 +03:00
else
m_lookAt = m_camp ;
2014-07-30 14:17:46 +04:00
}
else if ( flags & AIM_CAMP )
m_lookAt = m_camp ;
2015-06-07 19:43:16 +03:00
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 ;
2016-01-30 13:15:50 +03:00
if ( m_team = = TERRORIST )
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
}
}
}
2015-06-28 19:43:31 +03:00
2015-08-15 18:09:15 +03:00
if ( m_lookAt . IsZero ( ) )
2014-07-30 14:17:46 +04:00
m_lookAt = m_destOrigin ;
}
2015-07-26 21:47:29 +03:00
void Bot : : Think ( void )
2015-07-19 13:39:00 +03:00
{
2016-03-01 13:37:10 +03:00
if ( m_thinkFps < = engine . Time ( ) )
2015-07-19 13:39:00 +03:00
{
2015-07-22 23:04:43 +03:00
// execute delayed think
2016-03-12 14:35:44 +03:00
ThinkFrame ( ) ;
2015-07-19 13:39:00 +03:00
// skip some frames
2016-03-01 13:37:10 +03:00
m_thinkFps = engine . Time ( ) + m_thinkInterval ;
2015-07-19 13:39:00 +03:00
}
else
2015-07-26 13:42:51 +03:00
UpdateLookAngles ( ) ;
2015-07-19 13:39:00 +03:00
}
2016-03-12 14:35:44 +03:00
void Bot : : ThinkFrame ( 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 =)
2015-08-15 18:09:15 +03:00
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 ( ) ) ;
2016-03-05 23:08:07 +03:00
m_team = engine . GetTeam ( GetEntity ( ) ) ;
2014-07-30 14:17:46 +04:00
2016-01-30 13:15:50 +03:00
if ( m_team = = TERRORIST & & ( 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
2015-07-26 21:47:29 +03:00
if ( m_voteKickIndex ! = m_lastVoteKick & & yb_tkpunish . GetBool ( ) ) // we got a teamkiller? vote him away...
2014-07-30 14:17:46 +04:00
{
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " vote %d " , m_voteKickIndex ) ;
2014-07-30 14:17:46 +04:00
m_lastVoteKick = m_voteKickIndex ;
// if bot tk punishment is enabled slay the tk
2016-03-05 23:08:07 +03:00
if ( yb_tkpunish . GetInt ( ) ! = 2 | | IsValidBot ( engine . EntityOfIndex ( m_voteKickIndex ) ) )
2014-07-30 14:17:46 +04:00
return ;
2016-03-05 23:08:07 +03:00
edict_t * killer = engine . EntityOfIndex ( m_lastVoteKick ) ;
2015-06-20 12:56:22 +03:00
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?
{
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " votemap %d " , m_voteMap ) ;
2014-07-30 14:17:46 +04:00
m_voteMap = 0 ;
}
extern ConVar yb_chat ;
2016-03-01 13:37:10 +03:00
if ( yb_chat . GetBool ( ) & & ! RepliesToPlayer ( ) & & m_lastChatTime + 10.0 < engine . Time ( ) & & g_lastChatTime + 5.0f < engine . Time ( ) ) // bot chatting turned on?
2014-07-30 14:17:46 +04:00
{
// say a text every now and then
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 1500 ) < 2 )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
m_lastChatTime = engine . Time ( ) ;
g_lastChatTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
char * pickedPhrase = const_cast < char * > ( g_chatFactory [ CHAT_DEAD ] . GetRandomElement ( ) . GetBuffer ( ) ) ;
bool sayBufferExists = false ;
// search for last messages, sayed
2015-06-28 19:43:31 +03:00
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
2015-06-09 15:45:34 +03:00
if ( m_sayTextBuffer . lastUsedSentences . GetElementNumber ( ) > Random . Long ( 4 , 6 ) )
2014-07-30 14:17:46 +04:00
m_sayTextBuffer . lastUsedSentences . RemoveAll ( ) ;
}
}
}
2015-08-15 18:09:15 +03:00
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 ;
2015-12-26 17:19:20 +03:00
# ifdef XASH_CSDM
if ( m_notKilled )
botMovement = true ;
# endif
2014-07-30 14:17:46 +04:00
CheckMessageQueue ( ) ; // check for pending messages
2015-07-19 13:39:00 +03:00
// remove voice icon
2016-03-12 19:56:09 +03:00
if ( g_lastRadioTime [ g_clients [ engine . IndexOfEntity ( GetEntity ( ) ) - 1 ] . team2 ] + Random . Float ( 0.8f , 2.1f ) < engine . Time ( ) )
2015-07-19 13:39:00 +03:00
SwitchChatterIcon ( false ) ; // hide icon
2015-07-26 21:47:29 +03:00
if ( botMovement )
2014-07-30 14:17:46 +04:00
BotAI ( ) ; // execute main code
2015-07-19 13:39:00 +03:00
RunPlayerMovement ( ) ; // run the player movement
2014-07-30 14:17:46 +04:00
}
2015-07-12 17:18:20 +03:00
void Bot : : PeriodicThink ( void )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
if ( m_timePeriodicUpdate > engine . Time ( ) )
2015-07-12 17:18:20 +03:00
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
2015-07-12 17:18:20 +03:00
m_numFriendsLeft = GetNearbyFriendsNearPosition ( pev - > origin , 99999.0f ) ;
m_numEnemiesLeft = GetNearbyEnemiesNearPosition ( pev - > origin , 99999.0f ) ;
2016-01-30 13:15:50 +03:00
if ( g_bombPlanted & & m_team = = CT & & ( 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-12 17:18:20 +03:00
2015-07-17 19:23:31 +03:00
CheckSpawnTimeConditions ( ) ;
2015-07-19 13:39:00 +03:00
// clear enemy far away
2016-03-05 23:08:07 +03:00
if ( ! m_lastEnemyOrigin . IsZero ( ) & & ! engine . IsNullEntity ( m_lastEnemy ) & & ( pev - > origin - m_lastEnemyOrigin ) . GetLength ( ) > = 1600.0f )
2015-07-19 13:39:00 +03:00
{
2015-07-19 13:45:45 +03:00
m_lastEnemy = NULL ;
2015-08-15 18:09:15 +03:00
m_lastEnemyOrigin . Zero ( ) ;
2015-07-19 13:39:00 +03:00
}
2016-03-01 13:37:10 +03:00
m_timePeriodicUpdate = engine . Time ( ) + 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)
2016-03-05 23:08:07 +03:00
if ( m_currentWeapon = = WEAPON_KNIFE & & ( engine . IsNullEntity ( m_lastEnemy ) | | ! IsAlive ( m_lastEnemy ) ) & & engine . IsNullEntity ( m_enemy ) & & m_knifeAttackTime < engine . Time ( ) & & ! 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
2016-03-01 13:37:10 +03:00
m_knifeAttackTime = engine . Time ( ) + Random . Float ( 2.5f , 6.0f ) ;
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_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
2016-01-30 13:15:50 +03:00
if ( g_bombPlanted & & m_team = = CT & & 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
2015-08-15 18:09:15 +03: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
2016-03-05 23:08:07 +03:00
if ( m_timeLogoSpray < engine . Time ( ) & & yb_spraypaints . GetBool ( ) & & Random . Long ( 1 , 100 ) < 60 & & m_moveSpeed > GetWalkSpeed ( ) & & engine . IsNullEntity ( m_pickupItem ) )
2016-01-14 23:32:38 +03:00
{
2016-01-30 13:15:50 +03:00
if ( ! ( ( g_mapType & MAP_DE ) & & g_bombPlanted & & m_team = = CT ) )
2016-03-01 13:37:10 +03:00
PushTask ( TASK_SPRAY , TASKPRI_SPRAYLOGO , - 1 , engine . Time ( ) + 1.0f , false ) ;
2016-01-14 23:32:38 +03:00
}
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// reached waypoint is a camp waypoint
2016-01-03 22:03:02 +03:00
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
2016-03-01 13:37:10 +03:00
if ( HasPrimaryWeapon ( ) & & m_timeCamping + 10.0f < engine . Time ( ) & & ! 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
2016-01-30 13:15:50 +03:00
if ( m_team = = TERRORIST )
2015-07-17 19:23:31 +03:00
{
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
2016-03-09 22:34:24 +03:00
if ( campingAllowed & & ( IsPlayerVIP ( GetEntity ( ) ) | | ( ( g_mapType & MAP_DE ) & & m_team = = TERRORIST & & ! g_bombPlanted & & m_hasC4 ) ) )
2015-07-17 19:23:31 +03:00
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
2016-01-30 13:15:50 +03:00
if ( campingAllowed & & IsPointOccupied ( m_currentWaypointIndex ) )
2015-07-17 19:23:31 +03:00
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
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + 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
2016-01-30 13:15:50 +03:00
if ( Random . Long ( 0 , 100 ) < 60 )
2015-07-17 19:23:31 +03:00
RadioMessage ( Radio_InPosition ) ;
m_moveToGoal = false ;
m_checkTerrain = false ;
2016-01-14 23:32:38 +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
}
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?
2016-01-30 13:15:50 +03:00
if ( m_team = = CT & & HasHostage ( ) )
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
}
2016-01-30 13:15:50 +03:00
else if ( m_team = = TERRORIST & & Random . Long ( 0 , 100 ) < 80 )
2015-07-17 19:23:31 +03:00
{
int index = FindDefendWaypoint ( m_currentPath - > origin ) ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + Random . Float ( 60.0f , 120.0f ) , true ) ; // push camp task on to stack
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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
}
2016-01-30 13:15:50 +03:00
else if ( ( g_mapType & MAP_DE ) & & ( ( m_currentPath - > flags & FLAG_GOAL ) | | m_inBombZone ) )
2015-07-17 19:23:31 +03:00
{
// is it a terrorist carrying the bomb?
if ( m_hasC4 )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03: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
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + Random . Float ( 4.0f , 8.0f ) , true ) ;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
2015-08-15 18:09:15 +03:00
PushTask ( TASK_PLANTBOMB , TASKPRI_PLANTBOMB , - 1 , 0.0f , false ) ;
2015-07-17 19:23:31 +03:00
}
2016-01-30 13:15:50 +03:00
else if ( m_team = = CT )
2015-07-17 19:23:31 +03:00
{
2016-02-11 21:50:05 +03:00
if ( ! g_bombPlanted & & GetNearbyFriendsNearPosition ( pev - > origin , 210.0f ) < 4 )
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
2016-02-11 21:50:05 +03:00
float campTime = Random . Float ( 25.0f , 40.f ) ;
// rusher bots don't like to camp too much
if ( m_personality = = PERSONALITY_RUSHER )
campTime * = 0.5f ;
2016-03-05 21:04:46 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + campTime , true ) ; // push camp task on to stack
2016-03-01 13:37:10 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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 ( ) )
{
2016-01-14 23:32:38 +03:00
m_moveSpeed = 0.0f ;
2015-07-17 19:23:31 +03:00
DeleteSearchNodes ( ) ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// did we already decide about a goal before?
2016-01-30 13:15:50 +03:00
int destIndex = GetTask ( ) - > data ! = - 1 ? GetTask ( ) - > data : 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 )
2016-02-29 23:50:16 +03:00
FindPath ( m_currentWaypointIndex , destIndex , ( ( g_bombPlanted & & m_team = = CT ) | | yb_debug_goal . GetInt ( ) ! = - 1 ) ? SEARCH_PATH_FASTEST : m_pathType ) ;
2015-07-17 19:23:31 +03:00
}
else
{
if ( ! ( pev - > flags & FL_DUCKING ) & & m_minSpeed ! = pev - > maxspeed )
m_moveSpeed = m_minSpeed ;
}
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
if ( ( yb_walking_allowed . GetBool ( ) & & mp_footsteps . GetBool ( ) ) & & m_difficulty > = 2 & & ! ( m_aimFlags & AIM_ENEMY ) & & ( m_heardSoundTime + 8.0f > = engine . Time ( ) | | ( 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
2016-03-01 13:37:10 +03:00
if ( m_seeEnemyTime + Random . Float ( 45.0f , 80.0f ) < engine . Time ( ) & & Random . Long ( 0 , 100 ) < 30 & & g_timeRoundStart + 20.0f < engine . Time ( ) & & m_askCheckTime < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
{
2016-03-01 13:37:10 +03:00
m_askCheckTime = engine . Time ( ) + Random . Float ( 45.0f , 80.0f ) ;
2015-07-17 19:23:31 +03:00
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?
2016-03-01 13:37:10 +03:00
if ( m_timeLogoSpray < engine . Time ( ) & & GetTask ( ) - > time > engine . Time ( ) )
2015-07-17 19:23:31 +03:00
{
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 ;
2016-03-01 13:37:10 +03:00
engine . TestLine ( EyePosition ( ) , sprayOrigin , TRACE_IGNORE_MONSTERS , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// no wall in front?
2016-01-04 18:26:06 +03:00
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
2016-03-01 13:37:10 +03:00
if ( GetTask ( ) - > time - 0.5f < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
{
// emit spraycan sound
2015-08-15 18:09:15 +03:00
EMIT_SOUND_DYN2 ( GetEntity ( ) , CHAN_VOICE , " player/sprayer.wav " , 1.0 , ATTN_NORM , 0 , 100.0f ) ;
2016-03-01 13:37:10 +03:00
engine . TestLine ( EyePosition ( ) , EyePosition ( ) + g_pGlobals - > v_forward * 128.0f , TRACE_IGNORE_MONSTERS , 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 ) ;
2016-03-01 13:37:10 +03:00
m_timeLogoSpray = engine . Time ( ) + Random . Float ( 60.0f , 90.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
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2015-07-17 19:23:31 +03:00
m_moveSpeed = 0.0f ;
m_strafeSpeed = 0.0f ;
2015-07-01 00:47:39 +03:00
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...
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_enemy ) | | engine . IsNullEntity ( m_lastEnemy ) )
2015-07-17 19:23:31 +03:00
{
// 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 ;
2015-08-15 18:09:15 +03:00
m_lastEnemyOrigin . Zero ( ) ;
2015-07-17 19:23:31 +03:00
}
2016-03-05 23:08:07 +03:00
else if ( engine . GetTeam ( m_lastEnemy ) = = m_team )
2015-07-17 19:23:31 +03:00
{
// 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 ;
2015-08-15 18:09:15 +03:00
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
{
2016-03-01 13:37:10 +03:00
if ( m_currentPath - > radius < 32 & & ! IsOnLadder ( ) & & ! IsInWater ( ) & & m_seeEnemyTime + 4.0f > engine . Time ( ) & & m_difficulty < 2 )
2015-07-17 19:23:31 +03:00
pev - > button | = IN_DUCK ;
2014-07-30 14:17:46 +04:00
}
2015-08-15 18:09:15 +03: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
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_lastEnemy ) | | ! IsAlive ( m_lastEnemy ) )
2015-07-17 19:23:31 +03:00
{
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 ;
2016-02-29 23:50:16 +03:00
m_pathType = SEARCH_PATH_FASTEST ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// start hide task
2016-03-01 13:37:10 +03:00
PushTask ( TASK_HIDE , TASKPRI_HIDE , - 1 , engine . Time ( ) + 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
}
2015-08-15 18:09:15 +03: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 )
2016-02-29 23:50:16 +03:00
FindPath ( m_currentWaypointIndex , destIndex , SEARCH_PATH_FASTEST ) ;
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_Attack ( void )
{
m_moveToGoal = false ;
m_checkTerrain = false ;
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_enemy ) )
2015-07-17 19:23:31 +03:00
{
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 ( ) ;
2016-01-14 23:32:38 +03:00
if ( m_currentWeapon = = WEAPON_KNIFE & & ! m_lastEnemyOrigin . IsZero ( ) )
m_destOrigin = m_lastEnemyOrigin ;
2015-07-17 19:23:31 +03:00
}
else
{
TaskComplete ( ) ;
m_destOrigin = m_lastEnemyOrigin ;
}
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
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_Pause ( void )
{
m_moveToGoal = false ;
m_checkTerrain = false ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2015-08-15 18:09:15 +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
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?
2015-08-15 18:09:15 +03:00
if ( m_viewDistance < 500.0f & & m_difficulty > = 2 )
2015-07-17 19:23:31 +03:00
{
// go mad!
2015-08-15 18:09:15 +03:00
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 ) ;
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
if ( GetTask ( ) - > time < engine . Time ( ) | | 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_Blinded ( void )
{
m_moveToGoal = false ;
m_checkTerrain = false ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// if bot remembers last enemy position
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
if ( m_blindTime < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
TaskComplete ( ) ;
}
2015-06-04 11:52:48 +03:00
2015-07-17 19:23:31 +03:00
void Bot : : RunTask_Camp ( void )
{
2016-01-03 22:03:02 +03:00
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
2016-01-30 13:15:50 +03:00
if ( m_team = = CT & & 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
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
m_timeCamping = engine . Time ( ) ;
2015-07-17 19:23:31 +03:00
2015-08-15 18:09:15 +03:00
m_moveSpeed = 0.0f ;
m_strafeSpeed = 0.0f ;
2015-07-17 19:23:31 +03:00
GetValidWaypoint ( ) ;
2016-03-01 13:37:10 +03:00
if ( m_nextCampDirTime < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
{
2016-03-01 13:37:10 +03:00
m_nextCampDirTime = engine . Time ( ) + 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
2016-03-01 13:37:10 +03:00
if ( GetTask ( ) - > time < engine . Time ( ) | | 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
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2015-08-15 18:09:15 +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
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 ;
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_enemy ) )
2015-07-17 19:23:31 +03:00
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
}
2015-08-15 18:09:15 +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 ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
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
2016-03-01 13:37:10 +03:00
if ( GetTask ( ) - > time < engine . Time ( ) | | 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_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 ;
2015-08-15 18:09:15 +03:00
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 ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
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
2016-01-14 23:32:38 +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
}
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...
2016-02-29 23:50:16 +03:00
if ( GetNearbyFriendsNearPosition ( pev - > origin , 1200.0f ) ! = 0 )
2015-07-17 19:23:31 +03:00
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
2016-01-04 18:26:06 +03: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
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + guardTime , true ) ;
2015-07-17 19:23:31 +03:00
// push move command
2016-03-01 13:37:10 +03:00
PushTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , index , engine . Time ( ) + 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 )
{
2015-12-16 17:20:03 +03:00
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 ()*/ )
2016-03-01 13:37:10 +03:00
defuseRemainingTime = fullDefuseTime - engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
bool defuseError = false ;
// exception: bomb has been defused
2015-08-15 18:09:15 +03:00
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 ;
2015-12-16 17:20:03 +03:00
else if ( IsValidPlayer ( m_enemy ) )
2015-07-17 19:23:31 +03:00
{
2015-12-16 17:20:03 +03:00
int friends = GetNearbyFriendsNearPosition ( pev - > origin , 768.0f ) ;
2014-07-30 14:17:46 +04:00
2015-12-16 17:20:03 +03:00
if ( friends < 2 & & defuseRemainingTime < timeToBlowUp )
2014-07-30 14:17:46 +04:00
{
2015-12-16 17:20:03 +03:00
defuseError = true ;
2014-07-30 14:17:46 +04:00
2015-12-16 17:20:03 +03: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
2015-08-15 18:09:15 +03: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 ;
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03:00
if ( m_isReloading & & ( waypoints . GetBombPosition ( ) - pev - > origin ) . GetLength2D ( ) < 80.0f )
2015-07-17 19:23:31 +03:00
{
2015-08-15 18:09:15 +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
2015-08-15 18:09:15 +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
}
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
2016-03-01 13:37:10 +03:00
if ( ! m_hasProgressBar & & m_duckDefuseCheckTime < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
{
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 ;
2015-08-15 18:09:15 +03:00
botStandOrigin = pev - > origin + Vector ( 0.0f , 0.0f , 18.0f ) ;
2015-07-17 19:23:31 +03:00
}
else
{
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03: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
}
2016-03-01 13:37:10 +03:00
m_duckDefuseCheckTime = engine . Time ( ) + 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 ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
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 ;
2015-08-15 18:09:15 +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
// 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 )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_targetEntity ) | | ! IsAlive ( m_targetEntity ) )
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
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 ;
2016-03-01 13:37:10 +03:00
engine . TestLine ( m_targetEntity - > v . origin + m_targetEntity - > v . view_ofs , g_pGlobals - > v_forward * 500.0f , TRACE_IGNORE_EVERYTHING , GetEntity ( ) , & tr ) ;
2015-07-17 19:23:31 +03:00
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( tr . pHit ) & & IsValidPlayer ( tr . pHit ) & & engine . 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 )
2015-08-15 18:09:15 +03:00
m_followWaitTime = 0.0f ;
2015-07-17 19:23:31 +03:00
else
{
2015-08-15 18:09:15 +03:00
m_moveSpeed = 0.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_followWaitTime = = 0.0f )
2016-03-01 13:37:10 +03:00
m_followWaitTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
else
{
2016-03-01 13:37:10 +03:00
if ( m_followWaitTime + 3.0f < engine . Time ( ) )
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 ;
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03:00
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 ;
}
2016-03-05 23:08:07 +03:00
else if ( ! ( m_states & STATE_SUSPECT_ENEMY ) & & ! engine . IsNullEntity ( m_enemy ) )
2015-08-15 18:09:15 +03:00
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 ( ) ;
2016-03-01 13:37:10 +03:00
if ( m_maxThrowTimer < engine . Time ( ) | | ( pev - > origin - dest ) . GetLengthSquared ( ) < GET_SQUARE ( 400.0f ) )
2015-07-17 19:23:31 +03:00
{
// heck, I don't wanna blow up myself
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + 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
2015-08-15 18:09:15 +03: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
2015-08-15 18:09:15 +03:00
if ( m_grenade . GetLengthSquared ( ) < = 100.0f )
2015-07-17 19:23:31 +03:00
{
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + MAX_GRENADE_TIMER ;
2015-07-17 19:23:31 +03:00
m_grenade = m_lookAt ;
SelectBestWeapon ( ) ;
TaskComplete ( ) ;
}
else
{
edict_t * ent = NULL ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
while ( ! engine . IsNullEntity ( ent = FIND_ENTITY_BY_CLASSNAME ( ent , " grenade " ) ) )
2015-07-17 19:23:31 +03:00
{
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
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + 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
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) )
2015-07-17 19:23:31 +03:00
{
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 ;
}
2016-03-05 23:08:07 +03:00
else if ( ! ( m_states & STATE_SUSPECT_ENEMY ) & & ! engine . IsNullEntity ( m_enemy ) )
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03: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
2016-03-01 13:37:10 +03:00
if ( m_maxThrowTimer < engine . Time ( ) | | m_grenade . GetLengthSquared ( ) < = 100.0f )
2015-07-17 19:23:31 +03:00
{
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + MAX_GRENADE_TIMER ;
2015-07-17 19:23:31 +03:00
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 ;
2016-03-05 23:08:07 +03:00
while ( ! engine . IsNullEntity ( 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
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
m_grenadeCheckTime = engine . Time ( ) + 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
}
}
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( 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
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_enemy ) )
2015-08-15 18:09:15 +03:00
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 ( ) ;
2016-03-01 13:37:10 +03:00
if ( m_maxThrowTimer < engine . Time ( ) | | GetTask ( ) - > time < engine . Time ( ) + 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 " ) ;
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) + MAX_GRENADE_TIMER ;
2014-07-30 14:17:46 +04:00
}
2015-07-17 19:23:31 +03:00
else
2016-03-01 13:37:10 +03:00
GetTask ( ) - > time = engine . Time ( ) + 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 )
{
2016-03-05 21:04:46 +03:00
if ( ! 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 ) < engine . Time ( ) ) )
2015-07-17 19:23:31 +03:00
{
ResetDoubleJumpState ( ) ;
return ;
}
m_aimFlags | = AIM_NAVPOINT ;
if ( m_jumpReady )
{
m_moveToGoal = false ;
m_checkTerrain = false ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2015-08-15 18:09:15 +03:00
m_moveSpeed = 0.0f ;
m_strafeSpeed = 0.0f ;
2015-07-17 19:23:31 +03:00
2016-03-01 13:37:10 +03:00
if ( m_duckForJump < engine . Time ( ) )
2015-07-17 19:23:31 +03:00
pev - > button | = IN_DUCK ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
MakeVectors ( Vector : : GetZero ( ) ) ;
2015-07-17 19:23:31 +03:00
2015-08-15 18:09:15 +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 ;
2016-03-01 13:37:10 +03:00
engine . TestLine ( EyePosition ( ) , dest , TRACE_IGNORE_GLASS , GetEntity ( ) , & tr ) ;
2015-07-17 19:23:31 +03:00
2016-01-04 18:26:06 +03:00
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
{
2016-03-01 13:37:10 +03:00
m_duckForJump = engine . Time ( ) + Random . Float ( 3.0f , 5.0f ) ;
GetTask ( ) - > time = engine . Time ( ) ;
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
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
PushTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , engine . Time ( ) + 10.0f , true ) ;
2015-07-17 19:23:31 +03:00
}
else if ( ! GoalIsValid ( ) ) // didn't choose goal waypoint yet?
{
DeleteSearchNodes ( ) ;
2016-01-04 18:26:06 +03:00
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 ;
2016-01-04 18:26:06 +03:00
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?
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( FindBreakable ( ) ) )
2015-07-17 19:23:31 +03:00
{
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 ;
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-12 14:35:44 +03:00
Vector src = m_breakableOrigin ;
2015-07-17 19:23:31 +03:00
m_camp = src ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
// is bot facing the breakable?
2015-08-15 18:09:15 +03:00
if ( GetShootingConeDeviation ( GetEntity ( ) , & src ) > = 0.90f )
2015-07-17 19:23:31 +03:00
{
2015-08-15 18:09:15 +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 ( )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_pickupItem ) )
2015-07-17 19:23:31 +03:00
{
m_pickupItem = NULL ;
TaskComplete ( ) ;
2014-07-30 14:17:46 +04:00
2015-07-17 19:23:31 +03:00
return ;
}
2016-03-01 13:37:10 +03:00
Vector dest = engine . GetAbsOrigin ( 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 19:23:31 +03:00
switch ( m_pickupType )
{
2016-01-27 21:40:47 +03:00
case PICKUP_DROPPED_C4 :
case PICKUP_NONE :
break ;
2015-07-17 19:23:31 +03:00
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?
2015-08-15 18:09:15 +03:00
if ( itemDistance < 50.0f )
2015-07-17 19:23:31 +03:00
{
2016-03-05 21:04:46 +03:00
int id = 0 ;
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
2016-03-09 19:17:56 +03:00
int wid = 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 ) )
2016-03-09 19:17:56 +03:00
wid = id ;
2014-07-30 14:17:46 +04:00
}
2016-03-09 19:17:56 +03:00
if ( wid > 0 )
2015-07-17 19:23:31 +03:00
{
2016-03-09 19:17:56 +03:00
SelectWeaponbyNumber ( wid ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( 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...
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ; // discard both shield and pistol
2015-07-17 19:23:31 +03:00
}
2016-02-29 22:49:19 +03:00
EquipInBuyzone ( BUYSTATE_PRIMARY_WEAPON ) ;
2015-07-17 19:23:31 +03:00
}
else
{
// primary weapon
2016-03-09 19:17:56 +03:00
int wid = GetHighestWeapon ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
if ( wid > 6 | | HasShield ( ) )
2015-07-17 19:23:31 +03:00
{
2016-03-09 19:17:56 +03:00
SelectWeaponbyNumber ( wid ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ;
2015-07-17 19:23:31 +03:00
}
2016-02-29 22:49:19 +03:00
EquipInBuyzone ( BUYSTATE_PRIMARY_WEAPON ) ;
2015-07-17 19:23:31 +03:00
}
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 ;
}
2015-08-15 18:09:15 +03:00
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
2016-03-09 19:17:56 +03:00
int wid = GetHighestWeapon ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
if ( wid > 6 )
2015-07-17 19:23:31 +03:00
{
2016-03-09 19:17:56 +03:00
SelectWeaponbyNumber ( wid ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ;
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_PLANTED_C4 :
m_aimFlags | = AIM_ENTITY ;
2014-07-30 14:17:46 +04:00
2016-01-30 13:15:50 +03:00
if ( m_team = = CT & & 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 ;
2016-01-14 23:32:38 +03:00
m_moveSpeed = 0.0f ;
m_strafeSpeed = 0.0f ;
2015-07-17 19:23:31 +03:00
2015-08-15 18:09:15 +03:00
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 ;
}
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03: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
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( 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 ;
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_pickupItem ) | | m_buttonPushTime < engine . Time ( ) ) // it's safer...
2015-07-17 19:23:31 +03:00
{
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
2015-08-15 18:09:15 +03:00
if ( itemDistance < 90.0f ) // near to the button?
2015-07-17 19:23:31 +03:00
{
2015-08-15 18:09:15 +03:00
m_moveSpeed = 0.0f ;
m_strafeSpeed = 0.0f ;
2015-07-17 19:23:31 +03:00
m_moveToGoal = false ;
m_checkTerrain = false ;
2015-08-15 18:09:15 +03:00
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 ;
2016-03-01 13:37:10 +03:00
m_buttonPushTime = engine . Time ( ) + 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
2016-03-01 13:37:10 +03:00
if ( m_checkKnifeSwitch & & ! m_checkWeaponSwitch & & m_buyingFinished & & m_spawnTime + Random . Float ( 4.0f , 6.5f ) < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 2 & & yb_spraypaints . GetBool ( ) )
2016-03-01 13:37:10 +03:00
PushTask ( TASK_SPRAY , TASKPRI_SPRAYLOGO , - 1 , engine . Time ( ) + 1.0f , false ) ;
2014-07-30 14:17:46 +04:00
2015-06-09 15:45:34 +03: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 ) ) )
2015-06-14 12:55:49 +03:00
{
if ( yb_jasonmode . GetBool ( ) )
{
SelectPistol ( ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ;
2015-06-14 12:55:49 +03:00
}
else
SelectWeaponByName ( " weapon_knife " ) ;
}
2014-07-30 14:17:46 +04:00
m_checkKnifeSwitch = false ;
2016-03-05 23:08:07 +03:00
if ( Random . Long ( 0 , 100 ) < yb_user_follow_percent . GetInt ( ) & & engine . IsNullEntity ( m_targetEntity ) & & ! m_isLeader & & ! m_hasC4 )
2014-07-30 14:17:46 +04:00
AttachToUser ( ) ;
}
// check if we already switched weapon mode
2016-03-01 13:37:10 +03:00
if ( m_checkWeaponSwitch & & m_buyingFinished & & m_spawnTime + Random . Float ( 2.0f , 3.5f ) < engine . Time ( ) )
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 :
2015-06-09 15:45:34 +03:00
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
2016-03-05 21:04:46 +03:00
float movedDistance = 2.0f ; // length of different vector (distance bot moved)
2014-07-30 14:17:46 +04:00
// increase reaction time
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03:00
m_viewDistance + = 3.0f ;
2014-07-30 14:17:46 +04:00
if ( m_viewDistance > m_maxViewDistance )
m_viewDistance = m_maxViewDistance ;
2016-03-01 13:37:10 +03:00
if ( m_blindTime > engine . Time ( ) )
2015-08-15 18:09:15 +03:00
m_maxViewDistance = 4096.0f ;
2014-07-30 14:17:46 +04:00
m_moveSpeed = pev - > maxspeed ;
2016-03-01 13:37:10 +03:00
if ( m_prevTime < = engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
// 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 ;
2016-03-01 13:37:10 +03:00
m_prevTime = engine . Time ( ) + 0.2f ;
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
{
2016-03-05 23:08:07 +03:00
if ( ( m_states & STATE_SEEING_ENEMY ) & & ! engine . IsNullEntity ( m_enemy ) )
2015-06-10 23:30:48 +03:00
{
2015-07-12 17:18:20 +03:00
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
2016-01-30 13:15:50 +03:00
else if ( ! hasFriendNearby & & Random . Long ( 0 , 100 ) < 45 & & m_team = = TERRORIST & & IsPlayerVIP ( m_enemy ) )
2015-06-10 23:30:48 +03:00
ChatterMessage ( Chatter_VIPSpotted ) ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
else if ( ! hasFriendNearby & & Random . Long ( 0 , 100 ) < 50 & & engine . 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
2015-07-12 17:18:20 +03: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
2015-07-12 17:18:20 +03: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 !
2016-03-05 23:08:07 +03:00
if ( g_canSayBombPlanted & & g_bombPlanted & & engine . GetTeam ( GetEntity ( ) ) = = CT )
2015-06-10 23:30:48 +03:00
{
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
2015-07-19 13:39:00 +03:00
UpdateLookAngles ( ) ; // and turn to chosen aim direction
2014-07-30 14:17:46 +04:00
// the bots wants to fire at something?
2016-03-01 13:37:10 +03:00
if ( m_wantsToFire & & ! m_isUsingGrenade & & m_shootTime < = engine . Time ( ) )
2014-07-30 14:17:46 +04:00
FireWeapon ( ) ; // if bot didn't fire a bullet try again next frame
// check for reloading
2016-03-01 13:37:10 +03:00
if ( m_reloadCheckTime < = engine . Time ( ) )
2014-07-30 14:17:46 +04:00
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
2015-06-07 19:43:16 +03:00
// calculate 2 direction vectors, 1 without the up/down component
2015-07-01 00:47:39 +03:00
const Vector & dirOld = m_destOrigin - ( pev - > origin + pev - > velocity * m_frameInterval ) ;
const Vector & dirNormal = dirOld . Normalize2D ( ) ;
2015-06-07 19:43:16 +03:00
2015-07-01 00:47:39 +03:00
m_moveAngles = dirOld . ToAngles ( ) ;
2015-06-07 19:43:16 +03:00
m_moveAngles . ClampAngles ( ) ;
2015-08-15 18:09:15 +03:00
m_moveAngles . x * = - 1.0f ; // invert for engine
2015-06-07 19:43:16 +03:00
2016-01-14 23:32:38 +03:00
SetConditionsOverride ( ) ;
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 ;
2016-03-01 13:37:10 +03:00
m_timeWaypointMove = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
if ( IsInWater ( ) ) // special movement for swimming here
{
// check if we need to go forward or back press the correct buttons
2015-08-15 18:09:15 +03:00
if ( InFieldOfView ( m_destOrigin - EyePosition ( ) ) > 90.0f )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_BACK ;
else
pev - > button | = IN_FORWARD ;
2015-08-15 18:09:15 +03:00
if ( m_moveAngles . x > 60.0f )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_DUCK ;
2015-08-15 18:09:15 +03:00
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)?
2015-07-01 00:47:39 +03:00
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
2016-03-05 23:08:07 +03:00
if ( m_navTimeset + GetEstimatedReachTime ( ) < engine . Time ( ) & & engine . IsNullEntity ( m_enemy ) )
2014-07-30 14:17:46 +04:00
{
GetValidWaypoint ( ) ;
// clear these pointers, bot mingh be stuck getting to them
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_pickupItem ) & & ! m_hasProgressBar )
2014-07-30 14:17:46 +04:00
m_itemIgnore = m_pickupItem ;
m_pickupItem = NULL ;
m_breakableEntity = NULL ;
2016-03-01 13:37:10 +03:00
m_itemCheckTime = engine . Time ( ) + 5.0f ;
2014-07-30 14:17:46 +04:00
m_pickupType = PICKUP_NONE ;
}
2016-03-01 13:37:10 +03:00
if ( m_duckTime > = engine . Time ( ) )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_DUCK ;
if ( pev - > button & IN_JUMP )
2016-03-01 13:37:10 +03:00
m_jumpTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
if ( m_jumpTime + 0.85f > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
if ( ! IsOnFloor ( ) & & ! IsInWater ( ) )
pev - > button | = IN_DUCK ;
}
if ( ! ( pev - > button & ( IN_FORWARD | IN_BACK ) ) )
{
2015-08-15 18:09:15 +03:00
if ( m_moveSpeed > 0.0f )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_FORWARD ;
2015-08-15 18:09:15 +03:00
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 ) ) )
{
2015-08-15 18:09:15 +03:00
if ( m_strafeSpeed > 0.0f )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_MOVERIGHT ;
2015-08-15 18:09:15 +03:00
else if ( m_strafeSpeed < 0.0f )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_MOVELEFT ;
}
2016-01-14 23:32:38 +03:00
// display some debugging thingy to host entity
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( g_hostEntity ) & & yb_debug . GetInt ( ) > = 1 )
2016-01-14 23:32:38 +03:00
DisplayDebugOverlay ( ) ;
// save the previous speed (for checking if stuck)
m_prevSpeed = fabsf ( m_moveSpeed ) ;
m_lastDamageType = - 1 ; // reset damage
}
void Bot : : DisplayDebugOverlay ( void )
{
bool displayDebugOverlay = false ;
2016-03-05 23:08:07 +03:00
if ( g_hostEntity - > v . iuser2 = = engine . IndexOfEntity ( GetEntity ( ) ) )
2016-01-14 23:32:38 +03:00
displayDebugOverlay = true ;
if ( ! displayDebugOverlay & & yb_debug . GetInt ( ) > = 2 )
2014-07-30 14:17:46 +04:00
{
2016-01-14 23:32:38 +03:00
Bot * nearest = NULL ;
2015-12-26 01:31:46 +03:00
2016-01-14 23:32:38 +03:00
if ( FindNearestPlayer ( reinterpret_cast < void * * > ( & nearest ) , g_hostEntity , 128.0f , true , true , true , true ) & & nearest = = this )
2015-12-26 01:31:46 +03:00
displayDebugOverlay = true ;
2016-01-14 23:32:38 +03:00
}
2015-12-26 01:31:46 +03:00
2016-01-14 23:32:38 +03:00
if ( displayDebugOverlay )
{
static float timeDebugUpdate = 0.0f ;
static int index , goal , taskID ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
if ( ! m_tasks . IsEmpty ( ) )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
if ( taskID ! = GetTaskId ( ) | | index ! = m_currentWaypointIndex | | goal ! = GetTask ( ) - > data | | timeDebugUpdate < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2016-01-14 23:32:38 +03:00
taskID = GetTaskId ( ) ;
index = m_currentWaypointIndex ;
goal = GetTask ( ) - > data ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
char taskName [ 80 ] ;
memset ( taskName , 0 , sizeof ( taskName ) ) ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
switch ( taskID )
{
case TASK_NORMAL :
sprintf ( taskName , " Normal " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_PAUSE :
sprintf ( taskName , " Pause " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_MOVETOPOSITION :
sprintf ( taskName , " MoveToPosition " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_FOLLOWUSER :
sprintf ( taskName , " FollowUser " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_WAITFORGO :
sprintf ( taskName , " WaitForGo " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_PICKUPITEM :
sprintf ( taskName , " PickupItem " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_CAMP :
sprintf ( taskName , " Camp " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_PLANTBOMB :
sprintf ( taskName , " PlantBomb " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_DEFUSEBOMB :
sprintf ( taskName , " DefuseBomb " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_ATTACK :
sprintf ( taskName , " AttackEnemy " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_HUNTENEMY :
sprintf ( taskName , " HuntEnemy " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_SEEKCOVER :
sprintf ( taskName , " SeekCover " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_THROWHEGRENADE :
sprintf ( taskName , " ThrowExpGrenade " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_THROWFLASHBANG :
sprintf ( taskName , " ThrowFlashGrenade " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_THROWSMOKE :
sprintf ( taskName , " ThrowSmokeGrenade " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_DOUBLEJUMP :
sprintf ( taskName , " PerformDoubleJump " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_ESCAPEFROMBOMB :
sprintf ( taskName , " EscapeFromBomb " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_SHOOTBREAKABLE :
sprintf ( taskName , " ShootBreakable " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_HIDE :
sprintf ( taskName , " Hide " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_BLINDED :
sprintf ( taskName , " Blinded " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case TASK_SPRAY :
sprintf ( taskName , " SprayLogo " ) ;
break ;
}
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
char enemyName [ 80 ] , weaponName [ 80 ] , aimFlags [ 64 ] , botType [ 32 ] ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_enemy ) )
2016-01-14 23:32:38 +03:00
strncpy ( enemyName , STRING ( m_enemy - > v . netname ) , SIZEOF_CHAR ( enemyName ) ) ;
2016-03-05 23:08:07 +03:00
else if ( ! engine . IsNullEntity ( m_lastEnemy ) )
2016-01-14 23:32:38 +03:00
{
strcpy ( enemyName , " (L) " ) ;
strncat ( enemyName , STRING ( m_lastEnemy - > v . netname ) , SIZEOF_CHAR ( enemyName ) ) ;
}
else
strcpy ( enemyName , " (null) " ) ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
char pickupName [ 80 ] ;
memset ( pickupName , 0 , sizeof ( pickupName ) ) ;
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_pickupItem ) )
2016-01-14 23:32:38 +03:00
strncpy ( pickupName , STRING ( m_pickupItem - > v . classname ) , SIZEOF_CHAR ( pickupName ) ) ;
else
strcpy ( pickupName , " (null) " ) ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
WeaponSelect * tab = & g_weaponSelect [ 0 ] ;
2016-01-14 23:32:38 +03:00
char weaponCount = 0 ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
while ( m_currentWeapon ! = tab - > id & & weaponCount < NUM_WEAPONS )
2016-01-14 23:32:38 +03:00
{
2016-03-09 19:17:56 +03:00
tab + + ;
2016-01-14 23:32:38 +03:00
weaponCount + + ;
}
memset ( aimFlags , 0 , sizeof ( aimFlags ) ) ;
// 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 " : " " ) ;
// 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 )
2014-07-30 14:17:46 +04:00
{
2016-01-14 23:32:38 +03:00
case WEAPON_EXPLOSIVE :
strcpy ( weaponName , " weapon_hegrenade " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case WEAPON_FLASHBANG :
strcpy ( weaponName , " weapon_flashbang " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case WEAPON_SMOKE :
strcpy ( weaponName , " weapon_smokegrenade " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
case WEAPON_C4 :
strcpy ( weaponName , " weapon_c4 " ) ;
break ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
default :
sprintf ( weaponName , " Unknown! (%d) " , m_currentWeapon ) ;
2014-07-30 14:17:46 +04:00
}
}
2016-01-14 23:32:38 +03:00
else
2016-03-09 19:17:56 +03:00
strncpy ( weaponName , tab - > weaponName , SIZEOF_CHAR ( weaponName ) ) ;
2016-01-14 23:32:38 +03:00
char outputBuffer [ 512 ] ;
memset ( outputBuffer , 0 , sizeof ( outputBuffer ) ) ;
2016-03-01 13:37:10 +03:00
sprintf ( outputBuffer , " \n \n \n \n %s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f \n Item: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s \n SP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d \n Enemy=%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 - engine . Time ( ) , pev - > movetype , enemyName , pickupName , botType ) ;
2016-01-14 23:32:38 +03:00
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 ) ;
2016-01-30 13:15:50 +03:00
WRITE_BYTE ( m_team = = CT ? 0 : 255 ) ;
2016-01-14 23:32:38 +03:00
WRITE_BYTE ( 100 ) ;
2016-01-30 13:15:50 +03:00
WRITE_BYTE ( m_team ! = CT ? 0 : 255 ) ;
2016-01-14 23:32:38 +03: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 ( ) ;
2016-03-01 13:37:10 +03:00
timeDebugUpdate = engine . Time ( ) + 1.0 ;
2016-01-14 23:32:38 +03:00
}
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
// green = destination origin
// blue = ideal angles
// red = view angles
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
engine . DrawLine ( g_hostEntity , EyePosition ( ) , m_destOrigin , 10 , 0 , 0 , 255 , 0 , 250 , 5 , 1 , DRAW_ARROW ) ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
MakeVectors ( m_idealAngles ) ;
2016-03-01 13:37:10 +03:00
engine . DrawLine ( g_hostEntity , EyePosition ( ) - Vector ( 0.0f , 0.0f , 16.0f ) , EyePosition ( ) + g_pGlobals - > v_forward * 300.0f , 10 , 0 , 0 , 0 , 255 , 250 , 5 , 1 , DRAW_ARROW ) ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
MakeVectors ( pev - > v_angle ) ;
2016-03-01 13:37:10 +03:00
engine . DrawLine ( g_hostEntity , EyePosition ( ) - Vector ( 0.0f , 0.0f , 32.0f ) , EyePosition ( ) + g_pGlobals - > v_forward * 300.0f , 10 , 0 , 255 , 0 , 0 , 250 , 5 , 1 , DRAW_ARROW ) ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
// now draw line from source to destination
PathNode * node = & m_navNode [ 0 ] ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
while ( node ! = NULL )
{
const Vector & srcPath = waypoints . GetPath ( node - > index ) - > origin ;
node = node - > next ;
2014-07-30 14:17:46 +04:00
2016-01-14 23:32:38 +03:00
if ( node ! = NULL )
{
const Vector & dstPath = waypoints . GetPath ( node - > index ) - > origin ;
2016-03-01 13:37:10 +03:00
engine . DrawLine ( g_hostEntity , srcPath , dstPath , 15 , 0 , 255 , 100 , 55 , 200 , 5 , 1 , DRAW_ARROW ) ;
2014-07-30 14:17:46 +04:00
}
}
}
}
}
bool Bot : : HasHostage ( void )
{
for ( int i = 0 ; i < MAX_HOSTAGES ; i + + )
{
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_hostages [ i ] ) )
2014-07-30 14:17:46 +04:00
{
// don't care about dead hostages
2015-08-15 18:09:15 +03:00
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
{
2016-03-05 23:08:07 +03:00
if ( yb_tkpunish . GetBool ( ) & & engine . GetTeam ( inflictor ) = = m_team & & ! IsValidBot ( inflictor ) )
2014-07-30 14:17:46 +04:00
{
// alright, die you teamkiller!!!
2015-06-28 19:43:31 +03:00
m_actualReactionTime = 0.0f ;
2016-03-01 13:37:10 +03:00
m_seeEnemyTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_enemy = inflictor ;
2014-08-05 21:04:43 +04:00
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
2015-08-15 18:09:15 +03:00
if ( pev - > health > 60.0f )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
m_agressionLevel + = 0.1f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_agressionLevel > 1.0f )
m_agressionLevel + = 1.0f ;
2014-07-30 14:17:46 +04:00
}
else
{
2015-08-15 18:09:15 +03:00
m_fearLevel + = 0.03f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( m_fearLevel > 1.0f )
m_fearLevel + = 1.0f ;
2014-07-30 14:17:46 +04:00
}
RemoveCertainTask ( TASK_CAMP ) ;
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_enemy ) & & m_team ! = engine . 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
2016-03-01 13:37:10 +03:00
m_seeEnemyTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
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 ( ) ;
}
}
}
2016-03-12 14:35:44 +03:00
void Bot : : TakeBlinded ( int r , int g , int b , int alpha )
2014-07-30 14:17:46 +04:00
{
// this function gets called by network message handler, when screenfade message get's send
2016-03-09 19:17:56 +03:00
// it's used to make bot blind from the grenade.
2014-07-30 14:17:46 +04:00
2016-03-12 14:35:44 +03:00
if ( r ! = 255 | | g ! = 255 | | b ! = 255 | | alpha < = 170 )
2014-07-30 14:17:46 +04:00
return ;
m_enemy = NULL ;
2015-08-15 18:09:15 +03:00
m_maxViewDistance = Random . Float ( 10.0f , 20.0f ) ;
2016-03-12 14:35:44 +03:00
m_blindTime = engine . Time ( ) + static_cast < float > ( alpha - 200 ) / 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
{
2015-08-15 18:09:15 +03:00
m_blindMoveSpeed = 0.0f ;
m_blindSidemoveSpeed = 0.0f ;
2014-07-30 14:17:46 +04:00
m_blindButton = IN_DUCK ;
2016-03-09 19:17:56 +03:00
return ;
2014-07-30 14:17:46 +04:00
}
2016-03-09 19:17:56 +03:00
m_blindMoveSpeed = - pev - > maxspeed ;
m_blindSidemoveSpeed = 0.0f ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
float walkSpeed = GetWalkSpeed ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-09 19:17:56 +03:00
if ( Random . Long ( 0 , 100 ) > 50 )
m_blindSidemoveSpeed = walkSpeed ;
else
m_blindSidemoveSpeed = - walkSpeed ;
if ( pev - > health < 85.0f )
m_blindMoveSpeed = - walkSpeed ;
else if ( m_personality = = PERSONALITY_CAREFUL )
{
m_blindMoveSpeed = 0.0f ;
m_blindButton = IN_DUCK ;
2014-07-30 14:17:46 +04:00
}
2016-03-09 19:17:56 +03:00
else
m_blindMoveSpeed = walkSpeed ;
2014-07-30 14:17:46 +04:00
}
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 )
{
2016-01-30 13:15:50 +03:00
if ( team = = TERRORIST )
2014-07-30 14:17:46 +04:00
{
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 ;
2016-03-05 23:08:07 +03:00
int attackerTeam = engine . GetTeam ( attacker ) ;
2014-08-06 00:03:50 +04:00
int victimTeam = m_team ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +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
2015-08-15 18:09:15 +03:00
if ( pev - > health > 20.0f )
2014-07-30 14:17:46 +04:00
{
2016-01-30 13:15:50 +03:00
if ( victimTeam = = TERRORIST )
2014-07-30 14:17:46 +04:00
( 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 ;
}
2015-08-15 18:09:15 +03:00
float updateDamage = IsValidBot ( attacker ) ? 10.0f : 7.0f ;
2014-07-30 14:17:46 +04:00
// store away the damage done
2016-01-30 13:15:50 +03:00
if ( victimTeam = = TERRORIST )
2014-07-30 14:17:46 +04:00
{
int value = ( g_experienceData + ( victimIndex * g_numWaypoints ) + attackerIndex ) - > team0Damage ;
2015-08-03 12:36:57 +03:00
value + = static_cast < int > ( damage / updateDamage ) ;
2014-07-30 14:17:46 +04:00
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
2014-08-05 21:04:43 +04:00
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 ;
2015-08-03 12:36:57 +03:00
value + = static_cast < int > ( damage / updateDamage ) ;
2014-07-30 14:17:46 +04:00
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
2014-08-05 21:04:43 +04:00
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.
2016-01-30 13:15:50 +03:00
if ( FStrEq ( tempMessage , " #CTs_Win " ) & & m_team = = CT )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
if ( g_timeRoundMid > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
ChatterMessage ( Chatter_QuicklyWonTheRound ) ;
else
ChatterMessage ( Chatter_WonTheRound ) ;
}
2016-01-30 13:15:50 +03:00
if ( FStrEq ( tempMessage , " #Terrorists_Win " ) & & m_team = = TERRORIST )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
if ( g_timeRoundMid > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
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... )
2015-08-15 18:09:15 +03:00
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 " ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ;
2014-07-30 14:17:46 +04:00
}
else
{
SelectBestWeapon ( ) ;
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " drop " ) ;
2014-07-30 14:17:46 +04:00
}
m_pickupItem = NULL ;
m_pickupType = PICKUP_NONE ;
2016-03-01 13:37:10 +03:00
m_itemCheckTime = engine . Time ( ) + 5.0f ;
2014-07-30 14:17:46 +04:00
if ( m_inBuyZone )
{
m_buyingFinished = false ;
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_PRIMARY_WEAPON ;
2014-07-30 14:17:46 +04:00
PushMessageQueue ( GSM_BUY_STUFF ) ;
2016-03-01 13:37:10 +03:00
m_nextBuyTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
}
}
}
void Bot : : ResetDoubleJumpState ( void )
{
TaskComplete ( ) ;
m_doubleJumpEntity = NULL ;
2015-08-15 18:09:15 +03:00
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 ;
2016-03-12 14:35:44 +03:00
char buffer [ MAX_PRINT_BUFFER ] ;
2014-07-30 14:17:46 +04:00
va_start ( ap , format ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( buffer , SIZEOF_CHAR ( buffer ) , format , ap ) ;
2014-07-30 14:17:46 +04:00
va_end ( ap ) ;
2016-03-12 14:35:44 +03:00
char printBuf [ MAX_PRINT_BUFFER ] ;
2016-01-30 13:15:50 +03:00
sprintf ( printBuf , " %s: %s " , STRING ( pev - > netname ) , buffer ) ;
bool playMessage = false ;
2016-03-05 23:08:07 +03:00
if ( level = = 3 & & ! engine . IsNullEntity ( g_hostEntity ) & & g_hostEntity - > v . iuser2 = = engine . IndexOfEntity ( GetEntity ( ) ) )
2016-01-30 13:15:50 +03:00
playMessage = true ;
2015-06-24 17:25:39 +03:00
else if ( level ! = 3 )
2016-01-30 13:15:50 +03:00
playMessage = true ;
if ( playMessage & & level > 3 )
AddLogEntry ( false , LL_DEFAULT , printBuf ) ;
2014-07-30 14:17:46 +04:00
2016-01-30 13:15:50 +03:00
if ( playMessage )
{
2016-03-01 13:37:10 +03:00
engine . Printf ( printBuf ) ;
2016-01-30 13:15:50 +03:00
SayText ( printBuf ) ;
}
2014-07-30 14:17:46 +04:00
}
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 ;
2015-08-15 18:09:15 +03:00
float gravity = sv_gravity . GetFloat ( ) * 0.55f ;
2014-07-30 14:17:46 +04:00
2015-06-24 15:38:48 +03:00
Vector end = stop - pev - > velocity ;
2015-08-15 18:09:15 +03:00
end . z - = 15.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( fabsf ( end . z - start . z ) > 500.0f )
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
Vector midPoint = start + ( end - start ) * 0.5f ;
2016-03-12 14:35:44 +03:00
engine . TestHull ( midPoint , midPoint + Vector ( 0.0f , 0.0f , 500.0f ) , TRACE_IGNORE_MONSTERS , head_hull , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction < 1.0f )
2014-07-30 14:17:46 +04:00
{
midPoint = tr . vecEndPos ;
2015-08-15 18:09:15 +03:00
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 ) )
2015-08-15 18:09:15 +03:00
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03: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
2015-08-15 18:09:15 +03: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 ;
2016-03-12 14:35:44 +03:00
engine . TestHull ( start , apex , TRACE_IGNORE_NONE , head_hull , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction < 1.0f | | tr . fAllSolid )
2015-08-15 18:09:15 +03:00
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-12 14:35:44 +03:00
engine . TestHull ( end , apex , TRACE_IGNORE_MONSTERS , head_hull , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction ! = 1.0f )
2014-07-30 14:17:46 +04:00
{
float dot = - ( tr . vecPlaneNormal | ( apex - end ) . Normalize ( ) ) ;
2015-08-15 18:09:15 +03:00
if ( dot > 0.7f | | tr . flFraction < 0.8f ) // 60 degrees
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
}
2015-08-15 18:09:15 +03: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.
2015-06-24 15:38:48 +03:00
Vector nadeVelocity = ( stop - start ) ;
2014-07-30 14:17:46 +04:00
TraceResult tr ;
2015-08-15 18:09:15 +03:00
float gravity = sv_gravity . GetFloat ( ) * 0.55f ;
float time = nadeVelocity . GetLength ( ) / 195.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( time < 0.01f )
return Vector : : GetZero ( ) ;
else if ( time > 2.0f )
time = 1.2f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
nadeVelocity = nadeVelocity * ( 1.0f / time ) ;
nadeVelocity . z + = gravity * time * 0.5f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03: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
2016-03-01 13:37:10 +03:00
engine . TestHull ( start , apex , TRACE_IGNORE_NONE , head_hull , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction ! = 1.0f )
2015-08-15 18:09:15 +03:00
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
engine . TestHull ( stop , apex , TRACE_IGNORE_MONSTERS , head_hull , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction ! = 1.0 | | tr . fAllSolid )
2014-07-30 14:17:46 +04:00
{
2015-06-24 15:38:48 +03:00
float dot = - ( tr . vecPlaneNormal | ( apex - stop ) . Normalize ( ) ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( dot > 0.7f | | tr . flFraction < 0.8f )
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
}
2015-08-15 18:09:15 +03: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.
2016-01-07 18:49:55 +03:00
if ( ! g_bombPlanted | | GetTaskId ( ) = = TASK_ESCAPEFROMBOMB )
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
float timeElapsed = ( ( engine . Time ( ) - g_timeBombPlanted ) / mp_c4timer . GetFloat ( ) ) * 100.0f ;
2015-08-15 18:09:15 +03:00
float desiredRadius = 768.0f ;
2014-07-30 14:17:46 +04:00
// start the manual calculations
2015-08-15 18:09:15 +03:00
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 ;
2015-08-15 18:09:15 +03:00
return Vector : : GetZero ( ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-10 23:41:55 +03:00
void Bot : : MoveToVector ( const Vector & to )
2014-07-30 14:17:46 +04:00
{
2015-08-15 18:09:15 +03:00
if ( to . IsZero ( ) )
2014-07-30 14:17:46 +04:00
return ;
2016-02-29 23:50:16 +03:00
FindPath ( m_currentWaypointIndex , waypoints . FindNearest ( to ) , SEARCH_PATH_FASTEST ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-09 23:31:46 +03:00
byte Bot : : ThrottledMsec ( void )
2014-07-30 22:59:34 +04:00
{
2015-06-09 22:58:15 +03:00
// estimate msec to use for this command based on time passed from the previous command
2015-08-02 20:31:14 +03:00
2016-03-01 13:37:10 +03:00
return static_cast < byte > ( ( engine . Time ( ) - m_lastCommandTime ) * 1000.0f ) ;
2014-07-30 22:59:34 +04:00
}
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
2015-06-07 19:43:16 +03:00
// 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 !
2016-03-01 13:37:10 +03:00
m_frameInterval = engine . Time ( ) - m_lastCommandTime ;
2015-06-09 22:58:15 +03:00
2015-06-09 23:31:46 +03:00
byte msecVal = ThrottledMsec ( ) ;
2016-03-01 13:37:10 +03:00
m_lastCommandTime = engine . Time ( ) ;
2015-06-09 22:58:15 +03:00
2015-07-19 13:39:00 +03:00
( * 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
2015-08-15 18:09:15 +03:00
if ( m_currentWeapon = = WEAPON_GLOCK & & distance < 300.0f & & m_weaponBurstMode = = BM_OFF )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_ATTACK2 ;
2015-08-15 18:09:15 +03:00
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
2015-08-15 18:09:15 +03:00
if ( m_currentWeapon = = WEAPON_FAMAS & & distance > 400.0f & & m_weaponBurstMode = = BM_OFF )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_ATTACK2 ;
2015-08-15 18:09:15 +03:00
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
{
2015-07-19 13:39:00 +03:00
int random = ( m_personality = = PERSONALITY_RUSHER ? 35 : 65 ) ;
2014-07-30 14:17:46 +04:00
// aggressive bots don't like the silencer
2015-07-19 13:39:00 +03:00
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 )
2015-08-15 18:09:15 +03:00
return 0.0f ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
float timeLeft = ( ( g_timeBombPlanted + mp_c4timer . GetFloat ( ) ) - engine . Time ( ) ) ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
if ( timeLeft < 0.0f )
return 0.0f ;
2014-07-30 14:17:46 +04:00
return timeLeft ;
}
float Bot : : GetEstimatedReachTime ( void )
{
2015-06-07 19:43:16 +03:00
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
2015-07-23 00:01:09 +03:00
if ( pev - > maxspeed < = 0.0f )
estimatedTime = 4.0f * distance / 240.0f ;
2014-07-30 14:17:46 +04:00
else
2015-07-23 00:01:09 +03:00
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 )
2015-07-23 00:01:09 +03:00
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
2016-02-06 23:37:58 +03:00
if ( timeLeft > 13.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
2016-02-06 23:37:58 +03:00
// for terrorist, if timer is lower than 13 seconds, return true
if ( static_cast < int > ( timeLeft ) < 13 & & m_team = = TERRORIST & & ( 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
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
Bot * bot = NULL ; // temporaly pointer to bot
// search players with defuse kit
2016-03-05 23:08:07 +03:00
if ( ( bot = bots . GetBot ( i ) ) ! = NULL & & bot - > m_team = = CT & & 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
2015-08-15 18:09:15 +03:00
if ( ( timeLeft < reachTime + 6.0f & & ! m_hasDefuser & & ! hasTeammatesWithDefuserKit ) | | ( timeLeft < reachTime + 2.0f & & m_hasDefuser ) )
2014-07-30 14:17:46 +04:00
return true ;
2015-08-15 18:09:15 +03:00
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 )
{
2015-07-19 13:39:00 +03:00
int hearEnemyIndex = - 1 ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
Vector pasOrg = EyePosition ( ) ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
if ( pev - > flags & FL_DUCKING )
pasOrg = pasOrg + ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ) ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
byte * pas = ENGINE_SET_PVS ( reinterpret_cast < float * > ( & pasOrg ) ) ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
float minDistance = 99999.0f ;
2014-07-30 14:17:46 +04:00
// loop through all enemy clients to check for hearable stuff
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-03-01 13:37:10 +03:00
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 < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
continue ;
float distance = ( g_clients [ i ] . soundPosition - pev - > origin ) . GetLength ( ) ;
2015-07-19 13:39:00 +03:00
if ( distance > g_clients [ i ] . hearingDistance )
2014-07-30 14:17:46 +04:00
continue ;
2015-07-19 13:39:00 +03:00
if ( ! ENGINE_CHECK_VISIBILITY ( g_clients [ i ] . ent , pas ) )
2014-07-30 14:17:46 +04:00
continue ;
2015-07-19 13:39:00 +03:00
if ( distance < minDistance )
{
hearEnemyIndex = i ;
minDistance = distance ;
}
2014-07-30 14:17:46 +04:00
}
2015-07-19 13:39:00 +03:00
edict_t * player = NULL ;
2014-07-30 14:17:46 +04:00
2016-01-30 13:15:50 +03:00
if ( hearEnemyIndex > = 0 & & g_clients [ hearEnemyIndex ] . team ! = m_team & & yb_csdm_mode . GetInt ( ) ! = 2 )
player = g_clients [ hearEnemyIndex ] . ent ;
2014-07-30 14:17:46 +04:00
// did the bot hear someone ?
2015-06-28 19:43:31 +03:00
if ( IsValidPlayer ( player ) )
2014-07-30 14:17:46 +04:00
{
// change to best weapon if heard something
2016-03-01 13:37:10 +03:00
if ( m_shootTime < engine . Time ( ) - 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 ( ) ;
2016-03-01 13:37:10 +03:00
m_heardSoundTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
m_states | = STATE_HEARING_ENEMY ;
2016-03-05 23:08:07 +03:00
if ( ( Random . Long ( 0 , 100 ) < 15 ) & & engine . IsNullEntity ( m_enemy ) & & engine . IsNullEntity ( m_lastEnemy ) & & m_seeEnemyTime + 7.0f < engine . Time ( ) )
2014-07-30 14:17:46 +04:00
ChatterMessage ( Chatter_HeardEnemy ) ;
// didn't bot already have an enemy ? take this one...
2015-08-15 18:09:15 +03:00
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
2016-03-01 13:37:10 +03:00
if ( distance > ( player - > v . origin - pev - > origin ) . GetLengthSquared ( ) & & m_seeEnemyTime + 2.0f < engine . Time ( ) )
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
2015-06-11 23:22:50 +03:00
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 ;
2016-03-01 13:37:10 +03:00
m_seeEnemyTime = engine . Time ( ) ;
2015-06-07 23:06:23 +03:00
}
2016-03-09 22:34:24 +03:00
else // check if heard enemy can be shoot through some obstacle
{
if ( m_difficulty > 2 & & m_lastEnemy = = player & & m_seeEnemyTime + 3.0 > engine . Time ( ) & & yb_shoots_thru_walls . GetBool ( ) & & IsShootableThruObstacle ( player - > v . origin + player - > v . view_ofs ) )
{
m_enemy = player ;
m_lastEnemy = player ;
m_enemyOrigin = player - > v . origin ;
m_lastEnemyOrigin = player - > v . origin ;
m_states | = ( STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY ) ;
m_seeEnemyTime = engine . Time ( ) ;
}
}
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 ) ) )
2015-08-15 18:09:15 +03:00
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 ;
}
2016-02-29 22:49:19 +03:00
void Bot : : EquipInBuyzone ( int buyState )
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 )
2016-03-01 13:37:10 +03:00
checkBuyTime = ( g_timeRoundStart + Random . Float ( 10.0f , 20.0f ) + mp_buytime . GetFloat ( ) < engine . Time ( ) ) ;
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...
2016-03-01 13:37:10 +03:00
if ( m_seeEnemyTime + 5.0f < engine . Time ( ) & & m_lastEquipTime + 15.0f < engine . Time ( ) & & m_inBuyZone & & checkBuyTime & & ! g_bombPlanted & & m_moneyAmount > g_botBuyEconomyTable [ 0 ] )
2014-07-30 14:17:46 +04:00
{
m_buyingFinished = false ;
2016-02-29 22:49:19 +03:00
m_buyState = buyState ;
2014-07-30 14:17:46 +04:00
// push buy message
PushMessageQueue ( GSM_BUY_STUFF ) ;
2016-03-01 13:37:10 +03:00
m_nextBuyTime = engine . Time ( ) ;
m_lastEquipTime = engine . Time ( ) ;
2014-07-30 14:17:46 +04:00
}
}
2015-06-10 23:41:55 +03:00
bool Bot : : IsBombDefusing ( const Vector & bombOrigin )
2014-07-30 14:17:46 +04:00
{
// this function finds if somebody currently defusing the bomb.
2016-03-12 19:56:09 +03:00
if ( ! g_bombPlanted )
return false ;
2014-07-30 14:17:46 +04:00
bool defusingInProgress = false ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; 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 | | bot = = this )
continue ; // skip invalid bots
2016-03-05 23:08:07 +03:00
if ( m_team ! = bot - > m_team | | 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 ;
}
2016-03-01 22:52:17 +03:00
Client * client = & g_clients [ i ] ;
2014-07-30 14:17:46 +04:00
// take in account peoples too
2016-03-01 22:52:17 +03:00
if ( defusingInProgress | | ! ( client - > flags & CF_USED ) | | ! ( client - > flags & CF_ALIVE ) | | client - > team ! = m_team | | IsValidBot ( g_clients [ i ] . ent ) )
2014-07-30 14:17:46 +04:00
continue ;
2016-03-01 22:52:17 +03:00
if ( ( client - > ent - > v . origin - bombOrigin ) . GetLength ( ) < 140.0f & & ( ( client - > ent - > v . button | client - > ent - > v . oldbuttons ) & IN_USE ) )
2014-07-30 14:17:46 +04:00
{
defusingInProgress = true ;
break ;
}
}
return defusingInProgress ;
}
2015-07-12 12:58:20 +03:00
float Bot : : GetWalkSpeed ( void )
{
2015-07-12 17:18:20 +03:00
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 ( ) )
2015-07-12 12:58:20 +03:00
return pev - > maxspeed ;
return static_cast < float > ( ( static_cast < int > ( pev - > maxspeed ) * 0.5f ) + ( static_cast < int > ( pev - > maxspeed ) / 50.0f ) ) - 18.0f ;
}