2015-06-04 11:52:48 +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>
ConVar yb_shoots_thru_walls ( " yb_shoots_thru_walls " , " 1 " ) ;
ConVar yb_ignore_enemies ( " yb_ignore_enemies " , " 0 " ) ;
2015-06-04 11:52:48 +03:00
ConVar yb_csdm_mode ( " yb_csdm_mode " , " 0 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
ConVar mp_friendlyfire ( " mp_friendlyfire " , NULL , VT_NOREGISTER ) ;
int Bot : : GetNearbyFriendsNearPosition ( Vector origin , int radius )
{
2014-08-06 00:03:50 +04:00
int count = 0 ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . team ! = m_team | | g_clients [ i ] . ent = = GetEntity ( ) )
2014-07-30 14:17:46 +04:00
continue ;
if ( ( g_clients [ i ] . origin - origin ) . GetLengthSquared ( ) < static_cast < float > ( radius * radius ) )
count + + ;
}
return count ;
}
int Bot : : GetNearbyEnemiesNearPosition ( Vector origin , int radius )
{
2014-08-06 00:03:50 +04:00
int count = 0 ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . team = = m_team )
2014-07-30 14:17:46 +04:00
continue ;
if ( ( g_clients [ i ] . origin - origin ) . GetLengthSquared ( ) < static_cast < float > ( radius * radius ) )
count + + ;
}
return count ;
}
bool Bot : : LookupEnemy ( void )
{
// this function tries to find the best suitable enemy for the bot
m_visibility = 0 ;
// do not search for enemies while we're blinded, or shooting disabled by user
if ( m_blindTime > GetWorldTime ( ) | | yb_ignore_enemies . GetBool ( ) )
return false ;
// do not check for new enemy too fast
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( m_enemy ) & & m_enemyUpdateTime + 2.5f > GetWorldTime ( ) & & ! ( m_states & STATE_SUSPECT_ENEMY ) )
2014-07-30 14:17:46 +04:00
{
m_aimFlags | = AIM_ENEMY ;
2015-06-04 11:52:48 +03:00
m_states | = STATE_SUSPECT_ENEMY ;
2014-07-30 14:17:46 +04:00
return true ;
}
edict_t * player , * newEnemy = NULL ;
float nearestDistance = m_viewDistance ;
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
// setup potentially visible set for this bot
Vector potentialVisibility = EyePosition ( ) ;
if ( pev - > flags & FL_DUCKING )
potentialVisibility = potentialVisibility + ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ) ;
byte * pvs = ENGINE_SET_PVS ( reinterpret_cast < float * > ( & potentialVisibility ) ) ;
// clear suspected flag
if ( m_seeEnemyTime + 4.0 < GetWorldTime ( ) )
m_states & = ~ STATE_SUSPECT_ENEMY ;
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( m_enemy ) )
2014-07-30 14:17:46 +04:00
{
player = m_enemy ;
// is player is alive
if ( IsAlive ( player ) & & IsEnemyViewable ( player ) )
newEnemy = player ;
}
// the old enemy is no longer visible or
2015-06-04 11:52:48 +03:00
if ( IsEntityNull ( newEnemy ) )
2014-07-30 14:17:46 +04:00
{
m_enemyUpdateTime = GetWorldTime ( ) + 0.25 ;
// search the world for players...
2015-06-04 11:52:48 +03:00
for ( int i = 0 ; i < GetMaxClients ( ) ; 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 ) | | ( g_clients [ i ] . ent = = GetEntity ( ) ) )
2014-07-30 14:17:46 +04:00
continue ;
player = g_clients [ i ] . ent ;
// let the engine check if this player is potentially visible
if ( ! ENGINE_CHECK_VISIBILITY ( player , pvs ) )
continue ;
// skip glowed players, in free for all mode, we can't hit them
if ( player - > v . renderfx = = kRenderFxGlowShell & & yb_csdm_mode . GetInt ( ) > = 1 )
continue ;
// do some blind by smoke grenade
2015-06-04 11:52:48 +03:00
if ( m_blindRecognizeTime < GetWorldTime ( ) & & IsBehindSmokeClouds ( player ) )
2014-08-15 21:58:43 +04:00
{
2015-06-04 11:52:48 +03:00
m_blindRecognizeTime = GetWorldTime ( ) + g_randGen . Float ( 1.0 , 2.0 ) ;
2014-07-30 14:17:46 +04:00
2014-08-15 21:58:43 +04:00
if ( g_randGen . Long ( 0 , 100 ) < 50 )
ChatterMessage ( Chatter_BehindSmoke ) ;
}
2014-07-30 14:17:46 +04:00
if ( player - > v . button & ( IN_ATTACK | IN_ATTACK2 ) )
m_blindRecognizeTime = GetWorldTime ( ) - 0.1 ;
// see if bot can see the player...
if ( m_blindRecognizeTime < GetWorldTime ( ) & & IsEnemyViewable ( player ) )
{
float distance = ( player - > v . origin - pev - > origin ) . GetLength ( ) ;
if ( distance < nearestDistance )
{
if ( IsEnemyProtectedByShield ( player ) )
continue ;
nearestDistance = distance ;
newEnemy = player ;
// aim VIP first on AS maps...
if ( ( g_mapType & MAP_AS ) & & * ( INFOKEY_VALUE ( GET_INFOKEYBUFFER ( player ) , " model " ) ) = = ' v ' )
break ;
}
}
}
}
if ( IsValidPlayer ( newEnemy ) )
{
g_botsCanPause = true ;
m_aimFlags | = AIM_ENEMY ;
if ( newEnemy = = m_enemy )
{
// if enemy is still visible and in field of view, keep it keep track of when we last saw an enemy
m_seeEnemyTime = GetWorldTime ( ) ;
// zero out reaction time
m_actualReactionTime = 0.0 ;
m_lastEnemy = newEnemy ;
m_lastEnemyOrigin = newEnemy - > v . origin ;
return true ;
}
else
{
2015-06-04 11:52:48 +03:00
if ( m_seeEnemyTime + 3.0 < GetWorldTime ( ) & & ( m_hasC4 | | HasHostage ( ) | | ! IsEntityNull ( m_targetEntity ) ) )
2014-07-30 14:17:46 +04:00
RadioMessage ( Radio_EnemySpotted ) ;
m_targetEntity = NULL ; // stop following when we see an enemy...
2015-06-04 11:52:48 +03:00
if ( g_randGen . Long ( 0 , 100 ) < m_difficulty * 25 )
m_enemySurpriseTime = GetWorldTime ( ) + m_actualReactionTime / 3 ;
2014-07-30 14:17:46 +04:00
else
m_enemySurpriseTime = GetWorldTime ( ) + m_actualReactionTime ;
// zero out reaction time
m_actualReactionTime = 0.0 ;
m_enemy = newEnemy ;
m_lastEnemy = newEnemy ;
m_lastEnemyOrigin = newEnemy - > v . origin ;
m_enemyReachableTimer = 0.0 ;
// keep track of when we last saw an enemy
m_seeEnemyTime = GetWorldTime ( ) ;
// now alarm all teammates who see this bot & don't have an actual enemy of the bots enemy should simulate human players seeing a teammate firing
for ( int j = 0 ; j < GetMaxClients ( ) ; j + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ j ] . flags & CF_USED ) | | ! ( g_clients [ j ] . flags & CF_ALIVE ) | | g_clients [ j ] . team ! = m_team | | g_clients [ j ] . ent = = GetEntity ( ) )
2014-07-30 14:17:46 +04:00
continue ;
Bot * friendBot = g_botManager - > GetBot ( g_clients [ j ] . ent ) ;
if ( friendBot ! = NULL )
{
2015-06-04 11:52:48 +03:00
if ( friendBot - > m_seeEnemyTime + 2.0 < GetWorldTime ( ) | | IsEntityNull ( friendBot - > m_lastEnemy ) )
2014-07-30 14:17:46 +04:00
{
if ( IsVisible ( pev - > origin , ENT ( friendBot - > pev ) ) )
{
friendBot - > m_lastEnemy = newEnemy ;
friendBot - > m_lastEnemyOrigin = m_lastEnemyOrigin ;
friendBot - > m_seeEnemyTime = GetWorldTime ( ) ;
}
}
}
}
return true ;
}
}
2015-06-04 11:52:48 +03:00
else if ( ! IsEntityNull ( m_enemy ) )
2014-07-30 14:17:46 +04:00
{
newEnemy = m_enemy ;
m_lastEnemy = newEnemy ;
if ( ! IsAlive ( newEnemy ) )
{
m_enemy = NULL ;
// shoot at dying players if no new enemy to give some more human-like illusion
if ( m_seeEnemyTime + 0.1 > GetWorldTime ( ) )
{
if ( ! UsesSniper ( ) )
{
m_shootAtDeadTime = GetWorldTime ( ) + 0.2 ;
m_actualReactionTime = 0.0 ;
m_states | = STATE_SUSPECT_ENEMY ;
return true ;
}
return false ;
}
else if ( m_shootAtDeadTime > GetWorldTime ( ) )
{
m_actualReactionTime = 0.0 ;
m_states | = STATE_SUSPECT_ENEMY ;
return true ;
}
return false ;
}
// if no enemy visible check if last one shoot able through wall
2015-06-04 11:52:48 +03:00
if ( yb_shoots_thru_walls . GetBool ( ) & & m_difficulty > = 2 & & IsShootableThruObstacle ( newEnemy - > v . origin ) )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
m_seeEnemyTime = GetWorldTime ( ) - 0.35f ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
m_states | = STATE_SUSPECT_ENEMY ;
m_aimFlags | = AIM_LAST_ENEMY ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
m_enemy = newEnemy ;
m_lastEnemy = newEnemy ;
m_lastEnemyOrigin = newEnemy - > v . origin ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
return true ;
2014-07-30 14:17:46 +04:00
}
}
// check if bots should reload...
2015-06-04 11:52:48 +03:00
if ( ( m_aimFlags < = AIM_PREDICT_ENEMY & & m_seeEnemyTime + 3.0 < GetWorldTime ( ) & & ! ( m_states & ( STATE_SEEING_ENEMY | STATE_HEARING_ENEMY ) ) & & IsEntityNull ( m_lastEnemy ) & & IsEntityNull ( m_enemy ) & & GetTaskId ( ) ! = TASK_SHOOTBREAKABLE & & GetTaskId ( ) ! = TASK_PLANTBOMB & & GetTaskId ( ) ! = TASK_DEFUSEBOMB ) | | g_roundEnded )
2014-07-30 14:17:46 +04:00
{
if ( ! m_reloadState )
m_reloadState = RELOAD_PRIMARY ;
}
// is the bot using a sniper rifle or a zoomable rifle?
if ( ( UsesSniper ( ) | | UsesZoomableRifle ( ) ) & & m_zoomCheckTime + 1.0 < GetWorldTime ( ) )
{
if ( pev - > fov < 90 ) // let the bot zoom out
pev - > button | = IN_ATTACK2 ;
else
m_zoomCheckTime = 0.0 ;
}
return false ;
}
Vector Bot : : GetAimPosition ( void )
{
// the purpose of this function, is to make bot aiming not so ideal. it's mutate m_enemyOrigin enemy vector
// returned from visibility check function.
float distance = ( m_enemy - > v . origin - pev - > origin ) . GetLength ( ) ;
// get enemy position initially
Vector targetOrigin = m_enemy - > v . origin ;
2015-06-04 11:52:48 +03:00
Vector randomize = nullvec ;
const Vector & adjust = Vector ( g_randGen . Float ( m_enemy - > v . mins . x * 0.5f , m_enemy - > v . maxs . x * 0.5f ) , g_randGen . Float ( m_enemy - > v . mins . y * 0.5f , m_enemy - > v . maxs . y * 0.5f ) , g_randGen . Float ( m_enemy - > v . mins . z * 0.5f , m_enemy - > v . maxs . z * 0.5f ) ) ;
2014-07-30 14:17:46 +04:00
// do not aim at head, at long distance (only if not using sniper weapon)
2015-06-04 11:52:48 +03:00
if ( ( m_visibility & VISIBLE_BODY ) & & ! UsesSniper ( ) & & ! UsesPistol ( ) & & ( distance > ( m_difficulty = = 4 ? 2400.0 : 1200.0 ) ) )
2014-07-30 14:17:46 +04:00
m_visibility & = ~ VISIBLE_HEAD ;
// if we only suspect an enemy behind a wall take the worst skill
if ( ( m_states & STATE_SUSPECT_ENEMY ) & & ! ( m_states & STATE_SEEING_ENEMY ) )
2015-06-04 11:52:48 +03:00
targetOrigin = targetOrigin + adjust ;
2014-07-30 14:17:46 +04:00
else
{
// now take in account different parts of enemy body
if ( m_visibility & ( VISIBLE_HEAD | VISIBLE_BODY ) ) // visible head & body
{
2015-06-04 11:52:48 +03:00
int headshotFreq [ 5 ] = { 20 , 40 , 60 , 90 , 100 } ;
2014-07-30 14:17:46 +04:00
// now check is our skill match to aim at head, else aim at enemy body
2015-06-04 11:52:48 +03:00
if ( ( g_randGen . Long ( 1 , 100 ) < headshotFreq [ m_difficulty ] ) | | UsesPistol ( ) )
2014-07-30 14:17:46 +04:00
targetOrigin = targetOrigin + m_enemy - > v . view_ofs + Vector ( 0.0f , 0.0f , GetZOffset ( distance ) ) ;
else
2015-06-04 11:52:48 +03:00
targetOrigin = targetOrigin + Vector ( 0.0f , 0.0f , GetZOffset ( distance ) ) ;
2014-07-30 14:17:46 +04:00
}
else if ( m_visibility & VISIBLE_BODY ) // visible only body
2015-06-04 11:52:48 +03:00
targetOrigin = targetOrigin + Vector ( 0.0f , 0.0f , GetZOffset ( distance ) ) ;
2014-07-30 14:17:46 +04:00
else if ( m_visibility & VISIBLE_OTHER ) // random part of body is visible
targetOrigin = m_enemyOrigin ;
2015-06-04 11:52:48 +03:00
else if ( m_visibility & VISIBLE_HEAD ) // visible only head
targetOrigin = targetOrigin + m_enemy - > v . view_ofs + Vector ( 0.0f , 0.0f , GetZOffset ( distance ) ) ;
2014-07-30 14:17:46 +04:00
else // something goes wrong, use last enemy origin
2015-06-04 11:52:48 +03:00
{
2014-07-30 14:17:46 +04:00
targetOrigin = m_lastEnemyOrigin ;
2015-06-04 11:52:48 +03:00
if ( m_difficulty < 3 )
randomize = adjust ;
}
2014-07-30 14:17:46 +04:00
m_lastEnemyOrigin = targetOrigin ;
}
2015-06-04 11:52:48 +03:00
const Vector & velocity = UsesSniper ( ) ? nullvec : ( 1.0f * m_frameInterval * m_enemy - > v . velocity - 1.0 * m_frameInterval * pev - > velocity ) . SkipZ ( ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if ( m_difficulty < 3 & & randomize ! = nullvec )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
float divOffs = ( m_enemyOrigin - pev - > origin ) . GetLength ( ) ;
2014-07-30 14:17:46 +04:00
if ( pev - > fov < 40 )
2015-06-04 11:52:48 +03:00
divOffs = divOffs / 2000 ;
2014-07-30 14:17:46 +04:00
else if ( pev - > fov < 90 )
2015-06-04 11:52:48 +03:00
divOffs = divOffs / 1000 ;
2014-07-30 14:17:46 +04:00
else
2015-06-04 11:52:48 +03:00
divOffs = divOffs / 500 ;
2014-07-30 14:17:46 +04:00
// randomize the target position
2015-06-04 11:52:48 +03:00
m_enemyOrigin = divOffs * randomize + velocity ;
2014-07-30 14:17:46 +04:00
}
else
m_enemyOrigin = targetOrigin ;
return m_enemyOrigin ;
}
float Bot : : GetZOffset ( float distance )
{
// got it from pbmm
bool sniper = UsesSniper ( ) ;
bool pistol = UsesPistol ( ) ;
bool rifle = UsesRifle ( ) ;
bool zoomableRifle = UsesZoomableRifle ( ) ;
bool submachine = UsesSubmachineGun ( ) ;
bool shotgun = ( m_currentWeapon = = WEAPON_XM1014 | | m_currentWeapon = = WEAPON_M3 ) ;
bool m249 = m_currentWeapon = = WEAPON_M249 ;
const float BurstDistance = 300.0f ;
const float DoubleBurstDistance = BurstDistance * 2 ;
2015-06-04 11:52:48 +03:00
float result = 3.5f ;
2014-07-30 14:17:46 +04:00
if ( distance < 2800.0f & & distance > DoubleBurstDistance )
{
if ( sniper ) result = 3.5f ;
else if ( zoomableRifle ) result = 4.5f ;
else if ( pistol ) result = 6.5f ;
else if ( submachine ) result = 5.5f ;
else if ( rifle ) result = 5.5f ;
else if ( m249 ) result = 2.5f ;
else if ( shotgun ) result = 10.5f ;
}
else if ( distance > BurstDistance & & distance < = DoubleBurstDistance )
{
if ( sniper ) result = 3.5f ;
else if ( zoomableRifle ) result = 3.5f ;
else if ( pistol ) result = 6.5f ;
else if ( submachine ) result = 3.5f ;
2015-06-04 11:52:48 +03:00
else if ( rifle ) result = 1.6f ;
2014-07-30 14:17:46 +04:00
else if ( m249 ) result = - 1.0f ;
else if ( shotgun ) result = 10.0f ;
}
else if ( distance < BurstDistance )
{
if ( sniper ) result = 4.5f ;
else if ( zoomableRifle ) result = - 5.0f ;
else if ( pistol ) result = 4.5f ;
else if ( submachine ) result = - 4.5f ;
else if ( rifle ) result = - 4.5f ;
else if ( m249 ) result = - 6.0f ;
else if ( shotgun ) result = - 5.0f ;
}
return result ;
}
bool Bot : : IsFriendInLineOfFire ( float distance )
{
// bot can't hurt teammates, if friendly fire is not enabled...
if ( ! mp_friendlyfire . GetBool ( ) | | yb_csdm_mode . GetInt ( ) > 0 )
return false ;
MakeVectors ( pev - > v_angle ) ;
TraceResult tr ;
TraceLine ( EyePosition ( ) , EyePosition ( ) + pev - > v_angle . Normalize ( ) * distance , false , false , GetEntity ( ) , & tr ) ;
// check if we hit something
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( tr . pHit ) )
2014-07-30 14:17:46 +04:00
{
2014-09-17 20:36:42 +04:00
int playerIndex = IndexOfEntity ( tr . pHit ) - 1 ;
2014-07-30 14:17:46 +04:00
// check valid range
2014-08-06 00:03:50 +04:00
if ( playerIndex > = 0 & & playerIndex < GetMaxClients ( ) & & g_clients [ playerIndex ] . team = = m_team & & ( g_clients [ playerIndex ] . flags & CF_ALIVE ) )
2014-07-30 14:17:46 +04:00
return true ;
}
// search the world for players
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . team ! = m_team | | g_clients [ i ] . ent = = GetEntity ( ) )
2014-07-30 14:17:46 +04:00
continue ;
edict_t * ent = g_clients [ i ] . ent ;
float friendDistance = ( ent - > v . origin - pev - > origin ) . GetLength ( ) ;
float squareDistance = sqrtf ( 1089.0 + ( friendDistance * friendDistance ) ) ;
if ( GetShootingConeDeviation ( GetEntity ( ) , & ent - > v . origin ) > ( friendDistance * friendDistance ) / ( squareDistance * squareDistance ) & & friendDistance < = distance )
return true ;
}
return false ;
}
bool Bot : : IsShootableThruObstacle ( Vector dest )
{
2015-06-04 11:52:48 +03:00
// this function returns true if enemy can be shoot through some obstacle, false otherwise.
// credits goes to Immortal_BLG
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
return false ;
2015-06-04 11:52:48 +03:00
int penetratePower = GetWeaponPenetrationPower ( m_currentWeapon ) ;
if ( penetratePower = = 0 )
return false ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
// set conditions....
Vector source = EyePosition ( ) ;
const Vector & direction = ( dest - source ) . Normalize ( ) * 8.0f ; // 8 units long
2014-07-30 14:17:46 +04:00
TraceResult tr ;
2015-06-04 11:52:48 +03:00
do
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
// trace from the bot's eyes to destination...
TraceLine ( source , dest , true , GetEntity ( ) , & tr ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if ( tr . fStartSolid )
{
if ( tr . fAllSolid )
return false ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
// move 8 units closer to the destination....
source + = direction ;
}
else
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
// check if line hit anything
if ( tr . flFraction = = 1.0f )
return true ;
- - penetratePower ;
// move 8 units closer to the destination....
source = tr . vecEndPos + direction ;
2014-07-30 14:17:46 +04:00
}
2015-06-04 11:52:48 +03:00
} while ( penetratePower > 0 ) ;
2014-07-30 14:17:46 +04:00
return false ;
}
bool Bot : : DoFirePause ( float distance , FireDelay * fireDelay )
{
// returns true if bot needs to pause between firing to compensate for punchangle & weapon spread
if ( UsesSniper ( ) )
{
m_shootTime = GetWorldTime ( ) ;
return false ;
}
if ( m_firePause > GetWorldTime ( ) )
return true ;
2015-06-04 11:52:48 +03:00
float offset = 0.0f ;
const float BurstDistance = 300.0f ;
if ( distance < BurstDistance ) // KWo - 09.04.2010
return false ;
else if ( distance < 2 * BurstDistance )
offset = 10.0 ;
else
offset = 5.0 ;
float angle = sqrtf ( ( fabsf ( pev - > punchangle . y ) * Math : : MATH_PI / 180.0 ) * ( fabsf ( pev - > punchangle . y ) * Math : : MATH_PI / 180.0 ) + ( fabsf ( pev - > punchangle . x ) * Math : : MATH_PI / 180.0 ) * ( fabsf ( pev - > punchangle . x ) * Math : : MATH_PI / 180.0 ) ) ;
2014-07-30 14:17:46 +04:00
// check if we need to compensate recoil
2015-06-04 11:52:48 +03:00
if ( tanf ( angle ) * distance > offset + 30.0f + ( ( 100 - ( m_difficulty * 25 ) ) / 100.f ) )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
if ( m_firePause < GetWorldTime ( ) - 0.4f )
m_firePause = GetWorldTime ( ) + g_randGen . Float ( 0.4f , 0.4f + 0.3f * ( ( 100 - ( m_difficulty * 25 ) ) / 100.f ) ) ;
2014-07-30 14:17:46 +04:00
return true ;
}
2015-06-04 11:52:48 +03:00
if ( m_difficulty < 3 & & fireDelay - > maxFireBullets + g_randGen . Long ( 0 , 1 ) < = m_burstShotsFired )
2014-07-30 14:17:46 +04:00
{
float delayTime = 0.1 * distance / fireDelay - > minBurstPauseFactor ;
2015-06-04 11:52:48 +03:00
if ( delayTime > ( 125.0 / ( m_difficulty * 25 + 1 ) ) )
delayTime = 125.0 / ( m_difficulty * 25 + 1 ) ;
2014-07-30 14:17:46 +04:00
m_firePause = GetWorldTime ( ) + delayTime ;
m_burstShotsFired = 0 ;
return true ;
}
return false ;
}
void Bot : : FireWeapon ( void )
{
// this function will return true if weapon was fired, false otherwise
float distance = ( m_lookAt - EyePosition ( ) ) . GetLength ( ) ; // how far away is the enemy?
// if using grenade stop this
if ( m_isUsingGrenade )
{
m_shootTime = GetWorldTime ( ) + 0.1 ;
return ;
}
// or if friend in line of fire, stop this too but do not update shoot time
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( m_enemy ) & & IsFriendInLineOfFire ( distance ) )
2014-07-30 14:17:46 +04:00
return ;
FireDelay * delay = & g_fireDelay [ 0 ] ;
WeaponSelect * selectTab = & g_weaponSelect [ 0 ] ;
edict_t * enemy = m_enemy ;
int selectId = WEAPON_KNIFE , selectIndex = 0 , chosenWeaponIndex = 0 ;
int weapons = pev - > weapons ;
// if jason mode use knife only
if ( yb_jasonmode . GetBool ( ) )
goto WeaponSelectEnd ;
2015-06-04 11:52:48 +03:00
// use knife if near and good difficulty (l33t dude!)
if ( m_difficulty > = 3 & & pev - > health > 80 & & ! IsEntityNull ( enemy ) & & pev - > health > = enemy - > v . health & & distance < 100.0f & & ! IsGroupOfEnemies ( pev - > origin ) )
2014-07-30 14:17:46 +04:00
goto WeaponSelectEnd ;
// loop through all the weapons until terminator is found...
while ( selectTab [ selectIndex ] . id )
{
// is the bot carrying this weapon?
if ( weapons & ( 1 < < selectTab [ selectIndex ] . id ) )
{
// is enough ammo available to fire AND check is better to use pistol in our current situation...
if ( m_ammoInClip [ selectTab [ selectIndex ] . id ] > 0 & & ! IsWeaponBadInDistance ( selectIndex , distance ) )
chosenWeaponIndex = selectIndex ;
}
selectIndex + + ;
}
selectId = selectTab [ chosenWeaponIndex ] . id ;
// if no available weapon...
if ( chosenWeaponIndex = = 0 )
{
selectIndex = 0 ;
// loop through all the weapons until terminator is found...
while ( selectTab [ selectIndex ] . id )
{
int id = selectTab [ selectIndex ] . id ;
// is the bot carrying this weapon?
if ( weapons & ( 1 < < id ) )
{
if ( g_weaponDefs [ id ] . ammo1 ! = - 1 & & m_ammo [ g_weaponDefs [ id ] . ammo1 ] > = selectTab [ selectIndex ] . minPrimaryAmmo )
{
// available ammo found, reload weapon
if ( m_reloadState = = RELOAD_NONE | | m_reloadCheckTime > GetWorldTime ( ) )
{
m_isReloading = true ;
m_reloadState = RELOAD_PRIMARY ;
m_reloadCheckTime = GetWorldTime ( ) ;
RadioMessage ( Radio_NeedBackup ) ;
}
return ;
}
}
selectIndex + + ;
}
selectId = WEAPON_KNIFE ; // no available ammo, use knife!
}
// ignore enemies protected by shields
if ( IsEnemyProtectedByShield ( m_enemy ) & & ! ( m_currentWeapon = = WEAPON_KNIFE ) & & IsEnemyViewable ( m_enemy ) )
{
if ( ! g_bombPlanted & & g_randGen . Float ( 0 , 100 ) < 50 )
2015-06-04 11:52:48 +03:00
StartTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , GetWorldTime ( ) + g_randGen . Float ( 5 , 10 ) , true ) ;
2014-07-30 14:17:46 +04:00
}
// check if bot has shield
if ( HasShield ( ) & & m_shieldCheckTime < GetWorldTime ( ) & & GetTaskId ( ) ! = TASK_CAMP & & IsEnemyViewable ( m_enemy ) )
{
if ( IsGroupOfEnemies ( pev - > origin , 3 , 750 ) & & ! IsShieldDrawn ( ) & & ! g_bombPlanted )
StartTask ( TASK_SEEKCOVER , TASKPRI_SEEKCOVER , - 1 , GetWorldTime ( ) + g_randGen . Float ( 10 , 20 ) , true ) ;
if ( distance > = 750 | | ( ( m_enemy - > v . button & IN_ATTACK ) & & ! IsShieldDrawn ( ) ) )
{
2015-06-04 11:52:48 +03:00
pev - > button | = IN_ATTACK2 ; // draw the shield
2014-07-30 14:17:46 +04:00
pev - > button & = ~ IN_DUCK ;
if ( IsGroupOfEnemies ( pev - > origin , 3 , 550 ) | | ( GetNearbyEnemiesNearPosition ( pev - > origin , 550 ) > = 3 & & IsShieldDrawn ( ) ) )
{
ChooseAimDirection ( ) ;
FacePosition ( ) ;
}
else if ( ! g_bombPlanted )
StartTask ( TASK_CAMP , TASKPRI_PAUSE , - 1 , GetWorldTime ( ) + g_randGen . Float ( 10 , 20 ) , true ) ;
if ( IsEnemyProtectedByShield ( m_lastEnemy ) & & ! ( m_currentWeapon = = WEAPON_KNIFE ) & & IsEnemyViewable ( m_lastEnemy ) )
{
pev - > button & = ~ IN_ATTACK ;
StartTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , GetWorldTime ( ) + g_randGen . Float ( 10 , 20 ) , true ) ;
}
}
2015-06-04 11:52:48 +03:00
else if ( IsShieldDrawn ( ) | | ( ! IsEntityNull ( m_enemy ) & & ( m_enemy - > v . button & IN_RELOAD ) | | ! IsEnemyViewable ( m_enemy ) ) )
2014-07-30 14:17:46 +04:00
{
pev - > button | = ( IN_ATTACK2 | IN_DUCK ) ; // draw out the shield
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
if ( ! g_bombPlanted )
StartTask ( TASK_SEEKCOVER , TASKPRI_SEEKCOVER , - 1 , GetWorldTime ( ) + g_randGen . Float ( 10 , 25 ) , true ) ;
}
m_shieldCheckTime = GetWorldTime ( ) + 0.5 ;
}
WeaponSelectEnd :
// we want to fire weapon, don't reload now
if ( ! m_isReloading )
{
m_reloadState = RELOAD_NONE ;
m_reloadCheckTime = GetWorldTime ( ) + 3.0 ;
}
// select this weapon if it isn't already selected
if ( m_currentWeapon ! = selectId )
{
SelectWeaponByName ( g_weaponDefs [ selectId ] . className ) ;
// reset burst fire variables
m_firePause = 0.0 ;
m_timeLastFired = 0.0 ;
m_burstShotsFired = 0 ;
return ;
}
if ( delay [ chosenWeaponIndex ] . weaponIndex ! = selectId )
return ;
if ( selectTab [ chosenWeaponIndex ] . id ! = selectId )
{
chosenWeaponIndex = 0 ;
// loop through all the weapons until terminator is found...
while ( selectTab [ chosenWeaponIndex ] . id )
{
if ( selectTab [ chosenWeaponIndex ] . id = = selectId )
break ;
chosenWeaponIndex + + ;
}
}
// if we're have a glock or famas vary burst fire mode
CheckBurstMode ( distance ) ;
if ( HasShield ( ) & & m_shieldCheckTime < GetWorldTime ( ) & & GetTaskId ( ) ! = TASK_CAMP ) // better shield gun usage
{
if ( ( distance > = 750 ) & & ! IsShieldDrawn ( ) )
pev - > button | = IN_ATTACK2 ; // draw the shield
2015-06-04 11:52:48 +03:00
else if ( IsShieldDrawn ( ) | | ( ! IsEntityNull ( m_enemy ) & & ( m_enemy - > v . button & IN_RELOAD ) | | ! IsEnemyViewable ( m_enemy ) ) )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_ATTACK2 ; // draw out the shield
m_shieldCheckTime = GetWorldTime ( ) + 1.0 ;
}
if ( UsesSniper ( ) & & m_zoomCheckTime < GetWorldTime ( ) ) // is the bot holding a sniper rifle?
{
if ( distance > 1500 & & pev - > fov > = 40 ) // should the bot switch to the long-range zoom?
pev - > button | = IN_ATTACK2 ;
else if ( distance > 150 & & pev - > fov > = 90 ) // else should the bot switch to the close-range zoom ?
pev - > button | = IN_ATTACK2 ;
else if ( distance < = 150 & & pev - > fov < 90 ) // else should the bot restore the normal view ?
pev - > button | = IN_ATTACK2 ;
m_zoomCheckTime = GetWorldTime ( ) ;
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( m_enemy ) & & ( pev - > velocity . x ! = 0 | | pev - > velocity . y ! = 0 | | pev - > velocity . z ! = 0 ) & & ( pev - > basevelocity . x ! = 0 | | pev - > basevelocity . y ! = 0 | | pev - > basevelocity . z ! = 0 ) )
2014-07-30 14:17:46 +04:00
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
m_navTimeset = GetWorldTime ( ) ;
}
}
2015-06-04 11:52:48 +03:00
else if ( UsesZoomableRifle ( ) & & m_zoomCheckTime < GetWorldTime ( ) & & m_difficulty < 3 ) // else is the bot holding a zoomable rifle?
2014-07-30 14:17:46 +04:00
{
if ( distance > 800 & & pev - > fov > = 90 ) // should the bot switch to zoomed mode?
pev - > button | = IN_ATTACK2 ;
else if ( distance < = 800 & & pev - > fov < 90 ) // else should the bot restore the normal view?
pev - > button | = IN_ATTACK2 ;
m_zoomCheckTime = GetWorldTime ( ) ;
}
if ( HasPrimaryWeapon ( ) & & GetAmmoInClip ( ) < = 0 )
{
if ( GetAmmo ( ) < = 0 & & ! ( m_states & = ~ ( STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG ) ) )
SelectPistol ( ) ;
pev - > button | = IN_RELOAD ;
pev - > button & = ~ IN_ATTACK ;
return ;
}
const float baseDelay = delay [ chosenWeaponIndex ] . primaryBaseDelay ;
2015-06-04 11:52:48 +03:00
const float minDelay = delay [ chosenWeaponIndex ] . primaryMinDelay [ abs ( m_difficulty - 4 ) ] ;
const float maxDelay = delay [ chosenWeaponIndex ] . primaryMaxDelay [ abs ( m_difficulty - 4 ) ] ;
2014-07-30 14:17:46 +04:00
// need to care for burst fire?
if ( distance < 256.0 | | m_blindTime > GetWorldTime ( ) )
{
if ( selectId = = WEAPON_KNIFE )
{
2015-06-04 11:52:48 +03:00
if ( distance < 102.0f )
2014-07-30 14:17:46 +04:00
{
if ( g_randGen . Long ( 1 , 100 ) < 30 )
pev - > button | = IN_ATTACK ; // use primary attack
else
pev - > button | = IN_ATTACK2 ; // use secondary attack
}
}
else
{
LookupEnemy ( ) ;
if ( selectTab [ chosenWeaponIndex ] . primaryFireHold & & m_ammo [ g_weaponDefs [ selectTab [ selectIndex ] . id ] . ammo1 ] > = selectTab [ selectIndex ] . minPrimaryAmmo ) // if automatic weapon, just press attack
{
if ( ! IsEnemyProtectedByShield ( m_lastEnemy ) )
{
pev - > button & = ~ IN_ATTACK ;
LookupEnemy ( ) ;
}
pev - > button | = IN_ATTACK ;
}
else // if not, toggle buttons
{
if ( ( pev - > oldbuttons & IN_ATTACK ) = = 0 )
{
if ( ! IsEnemyProtectedByShield ( m_lastEnemy ) )
{
pev - > button & = ~ IN_ATTACK ;
LookupEnemy ( ) ;
}
pev - > button | = IN_ATTACK ;
}
}
}
if ( pev - > button & IN_ATTACK )
m_shootTime = GetWorldTime ( ) ;
}
else
{
if ( DoFirePause ( distance , & delay [ chosenWeaponIndex ] ) )
return ;
// don't attack with knife over long distance
if ( selectId = = WEAPON_KNIFE )
return ;
if ( selectTab [ chosenWeaponIndex ] . primaryFireHold )
{
m_shootTime = GetWorldTime ( ) ;
m_zoomCheckTime = GetWorldTime ( ) ;
if ( ! IsEnemyProtectedByShield ( m_lastEnemy ) )
{
pev - > button & = ~ IN_ATTACK ;
LookupEnemy ( ) ;
}
pev - > button | = IN_ATTACK ; // use primary attack
}
else
{
if ( ! IsEnemyProtectedByShield ( m_lastEnemy ) )
{
pev - > button & = ~ IN_ATTACK ;
2015-06-04 11:52:48 +03:00
LookupEnemy ( ) ;
2014-07-30 14:17:46 +04:00
}
pev - > button | = IN_ATTACK ;
m_shootTime = GetWorldTime ( ) + baseDelay + g_randGen . Float ( minDelay , maxDelay ) ;
m_zoomCheckTime = GetWorldTime ( ) ;
}
}
}
bool Bot : : IsWeaponBadInDistance ( int weaponIndex , float distance )
{
// this function checks, is it better to use pistol instead of current primary weapon
// to attack our enemy, since current weapon is not very good in this situation.
int weaponID = g_weaponSelect [ weaponIndex ] . id ;
2015-06-04 11:52:48 +03:00
if ( weaponID = = WEAPON_KNIFE )
return false ;
2014-07-30 14:17:46 +04:00
// check is ammo available for secondary weapon
if ( m_ammoInClip [ g_weaponSelect [ GetBestSecondaryWeaponCarried ( ) ] . id ] > = 1 )
return false ;
// better use pistol in short range distances, when using sniper weapons
if ( ( weaponID = = WEAPON_SCOUT | | weaponID = = WEAPON_AWP | | weaponID = = WEAPON_G3SG1 | | weaponID = = WEAPON_SG550 ) & & distance < 300.0 )
return true ;
// shotguns is too inaccurate at long distances, so weapon is bad
if ( ( weaponID = = WEAPON_M3 | | weaponID = = WEAPON_XM1014 ) & & distance > 750.0 )
return true ;
return false ;
}
void Bot : : FocusEnemy ( void )
{
// aim for the head and/or body
Vector enemyOrigin = GetAimPosition ( ) ;
m_lookAt = enemyOrigin ;
if ( m_enemySurpriseTime > GetWorldTime ( ) )
return ;
enemyOrigin = ( enemyOrigin - EyePosition ( ) ) . SkipZ ( ) ;
float distance = enemyOrigin . GetLength ( ) ; // how far away is the enemy scum?
2014-09-09 18:29:42 +04:00
if ( distance < 128.0f )
2014-07-30 14:17:46 +04:00
{
if ( m_currentWeapon = = WEAPON_KNIFE )
{
2014-09-09 18:29:42 +04:00
if ( distance < = 80.0f )
2014-07-30 14:17:46 +04:00
m_wantsToFire = true ;
}
else
2014-09-09 18:29:42 +04:00
m_wantsToFire = GetShootingConeDeviation ( GetEntity ( ) , & m_enemyOrigin ) > 0.8f ;
2014-07-30 14:17:46 +04:00
}
else
{
if ( m_currentWeapon = = WEAPON_KNIFE )
m_wantsToFire = true ;
else
{
float dot = GetShootingConeDeviation ( GetEntity ( ) , & m_enemyOrigin ) ;
if ( dot < 0.90 )
m_wantsToFire = false ;
else
{
float enemyDot = GetShootingConeDeviation ( m_enemy , & pev - > origin ) ;
// enemy faces bot?
if ( enemyDot > = 0.90 )
m_wantsToFire = true ;
else
{
if ( dot > 0.99 )
m_wantsToFire = true ;
else
m_wantsToFire = false ;
}
}
}
}
}
void Bot : : CombatFight ( void )
{
// no enemy? no need to do strafing
2015-06-04 11:52:48 +03:00
if ( IsEntityNull ( m_enemy ) )
2014-07-30 14:17:46 +04:00
return ;
Vector enemyOrigin = m_lookAt ;
if ( m_currentWeapon = = WEAPON_KNIFE )
m_destOrigin = m_enemy - > v . origin ;
enemyOrigin = ( enemyOrigin - EyePosition ( ) ) . SkipZ ( ) ; // ignore z component (up & down)
float distance = enemyOrigin . GetLength ( ) ; // how far away is the enemy scum?
2015-06-04 11:52:48 +03:00
if ( m_timeWaypointMove + m_frameInterval + 0.5f < GetWorldTime ( ) )
2014-07-30 14:17:46 +04:00
{
if ( m_currentWeapon = = WEAPON_KNIFE )
return ;
int approach ;
if ( ( m_states & STATE_SUSPECT_ENEMY ) & & ! ( m_states & STATE_SEEING_ENEMY ) ) // if suspecting enemy stand still
approach = 49 ;
else if ( m_isReloading | | m_isVIP ) // if reloading or vip back off
approach = 29 ;
else if ( m_currentWeapon = = WEAPON_KNIFE ) // knife?
approach = 100 ;
else
{
approach = static_cast < int > ( pev - > health * m_agressionLevel ) ;
if ( UsesSniper ( ) & & ( approach > 49 ) )
approach = 49 ;
}
if ( UsesPistol ( ) & & ! ( m_enemy - > v . weapons & ( 1 < < WEAPON_ELITE ) | | m_enemy - > v . weapons & ( 1 < < WEAPON_FIVESEVEN ) | | m_enemy - > v . weapons & ( 1 < < WEAPON_GLOCK ) | | m_enemy - > v . weapons & ( 1 < < WEAPON_USP ) | | m_enemy - > v . weapons & ( 1 < < WEAPON_DEAGLE ) | | m_enemy - > v . weapons & ( 1 < < WEAPON_SG550 ) ) & & ! g_bombPlanted )
{
m_fearLevel + = 0.5 ;
CheckGrenades ( ) ;
2015-06-04 11:52:48 +03:00
CheckThrow ( EyePosition ( ) , m_throw ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if ( ( m_states & STATE_SEEING_ENEMY ) & & ! m_hasC4 )
StartTask ( TASK_SEEKCOVER , TASKPRI_SEEKCOVER , - 1 , g_randGen . Long ( 10 , 20 ) , true ) ;
2014-07-30 14:17:46 +04:00
}
// If using sniper do not jump around !
if ( UsesSniper ( ) & & m_states & STATE_SEEING_ENEMY | | IsEnemyViewable ( m_enemy ) & & ! m_isStuck )
pev - > button & = ~ IN_JUMP ;
// only take cover when bomb is not planted and enemy can see the bot or the bot is VIP
2015-06-04 11:52:48 +03:00
if ( approach < 30 & & ! g_bombPlanted & & ( IsInViewCone ( m_enemy - > v . origin ) | | m_isVIP ) )
2014-07-30 14:17:46 +04:00
{
m_moveSpeed = - pev - > maxspeed ;
TaskItem * task = GetTask ( ) ;
task - > id = TASK_SEEKCOVER ;
task - > resume = true ;
task - > desire = TASKPRI_ATTACK + 1.0 ;
}
else if ( approach < 50 )
m_moveSpeed = 0.0 ;
else
m_moveSpeed = pev - > maxspeed ;
if ( distance < 96 & & m_currentWeapon ! = WEAPON_KNIFE )
m_moveSpeed = - pev - > maxspeed ;
if ( UsesSniper ( ) )
{
m_fightStyle = 1 ;
m_lastFightStyleCheck = GetWorldTime ( ) ;
}
else if ( UsesRifle ( ) | | UsesSubmachineGun ( ) )
{
if ( m_lastFightStyleCheck + 3.0 < GetWorldTime ( ) )
{
int rand = g_randGen . Long ( 1 , 100 ) ;
if ( distance < 450 )
m_fightStyle = 0 ;
else if ( distance < 1024 )
{
if ( rand < ( UsesSubmachineGun ( ) ? 50 : 30 ) )
m_fightStyle = 0 ;
else
m_fightStyle = 1 ;
}
else
{
if ( rand < ( UsesSubmachineGun ( ) ? 80 : 93 ) )
m_fightStyle = 1 ;
else
m_fightStyle = 0 ;
}
m_lastFightStyleCheck = GetWorldTime ( ) ;
}
}
else
{
if ( m_lastFightStyleCheck + 3.0 < GetWorldTime ( ) )
{
2015-06-04 11:52:48 +03:00
if ( g_randGen . Long ( 0 , 100 ) < 50 )
2014-07-30 14:17:46 +04:00
m_fightStyle = 1 ;
else
m_fightStyle = 0 ;
m_lastFightStyleCheck = GetWorldTime ( ) ;
}
}
2015-06-04 11:52:48 +03:00
if ( ( m_difficulty > = 1 & & m_fightStyle = = 0 ) | | ( ( pev - > button & IN_RELOAD ) | | m_isReloading ) | | ( UsesPistol ( ) & & distance < 500.0 ) )
2014-07-30 14:17:46 +04:00
{
if ( m_strafeSetTime < GetWorldTime ( ) )
{
// to start strafing, we have to first figure out if the target is on the left side or right side
MakeVectors ( m_enemy - > v . v_angle ) ;
Vector dirToPoint = ( pev - > origin - m_enemy - > v . origin ) . Normalize2D ( ) ;
Vector rightSide = g_pGlobals - > v_right . Normalize2D ( ) ;
if ( ( dirToPoint | rightSide ) < 0 )
m_combatStrafeDir = 1 ;
else
m_combatStrafeDir = 0 ;
if ( g_randGen . Long ( 1 , 100 ) < 30 )
m_combatStrafeDir ^ = 1 ;
2015-06-04 11:52:48 +03:00
m_strafeSetTime = GetWorldTime ( ) + g_randGen . Float ( 0.5 , 3.0 ) ;
2014-07-30 14:17:46 +04:00
}
if ( m_combatStrafeDir = = 0 )
{
if ( ! CheckWallOnLeft ( ) )
2015-06-04 11:52:48 +03:00
m_strafeSpeed = - pev - > maxspeed ;
2014-07-30 14:17:46 +04:00
else
{
m_combatStrafeDir ^ = 1 ;
2015-06-04 11:52:48 +03:00
m_strafeSetTime = GetWorldTime ( ) + 1.0 ;
2014-07-30 14:17:46 +04:00
}
}
else
{
if ( ! CheckWallOnRight ( ) )
2015-06-04 11:52:48 +03:00
m_strafeSpeed = - pev - > maxspeed ;
2014-07-30 14:17:46 +04:00
else
{
m_combatStrafeDir ^ = 1 ;
m_strafeSetTime = GetWorldTime ( ) + 1.0 ;
}
}
2015-06-04 11:52:48 +03:00
if ( m_difficulty > = 3 & & ( m_jumpTime + 5.0 < GetWorldTime ( ) & & IsOnFloor ( ) & & g_randGen . Long ( 0 , 1000 ) < ( m_isReloading ? 8 : 2 ) & & pev - > velocity . GetLength2D ( ) > 150.0 ) )
2014-07-30 14:17:46 +04:00
pev - > button | = IN_JUMP ;
2015-06-04 11:52:48 +03:00
if ( m_moveSpeed > 0.0 & & distance > 150.0 )
2014-07-30 14:17:46 +04:00
m_moveSpeed = 0.0 ;
}
else if ( m_fightStyle = = 1 )
{
bool shouldDuck = true ; // should duck
// check the enemy height
float enemyHalfHeight = ( ( m_enemy - > v . flags & FL_DUCKING ) = = FL_DUCKING ? 36.0 : 72.0 ) / 2 ;
// check center/feet
if ( ! IsVisible ( m_enemy - > v . origin , GetEntity ( ) ) & & ! IsVisible ( m_enemy - > v . origin + Vector ( 0 , 0 , - enemyHalfHeight ) , GetEntity ( ) ) )
shouldDuck = false ;
int nearestToEnemyPoint = g_waypoint - > FindNearest ( m_enemy - > v . origin ) ;
if ( shouldDuck & & GetTaskId ( ) ! = TASK_SEEKCOVER & & GetTaskId ( ) ! = TASK_HUNTENEMY & & ( m_visibility & VISIBLE_BODY ) & & ! ( m_visibility & VISIBLE_OTHER ) & & g_waypoint - > IsDuckVisible ( m_currentWaypointIndex , nearestToEnemyPoint ) )
2015-06-06 14:19:19 +03:00
m_duckTime = GetWorldTime ( ) + 0.5f ;
2014-07-30 14:17:46 +04:00
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
m_navTimeset = GetWorldTime ( ) ;
}
}
if ( m_duckTime > GetWorldTime ( ) )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
2015-06-04 11:52:48 +03:00
if ( m_moveSpeed > 0.0f )
2014-07-30 14:17:46 +04:00
m_moveSpeed = GetWalkSpeed ( ) ;
if ( m_isReloading )
{
m_moveSpeed = - pev - > maxspeed ;
2015-06-04 11:52:48 +03:00
m_duckTime = GetWorldTime ( ) - 1.0f ;
2014-07-30 14:17:46 +04:00
}
2015-06-04 11:52:48 +03:00
if ( ! IsInWater ( ) & & ! IsOnLadder ( ) & & ( m_moveSpeed > 0.0f | | m_strafeSpeed > = 0.0f ) )
2014-07-30 14:17:46 +04:00
{
MakeVectors ( pev - > v_angle ) ;
if ( IsDeadlyDrop ( pev - > origin + ( g_pGlobals - > v_forward * m_moveSpeed * 0.2 ) + ( g_pGlobals - > v_right * m_strafeSpeed * 0.2 ) + ( pev - > velocity * m_frameInterval ) ) )
{
m_strafeSpeed = - m_strafeSpeed ;
m_moveSpeed = - m_moveSpeed ;
pev - > button & = ~ IN_JUMP ;
}
}
}
bool Bot : : HasPrimaryWeapon ( void )
{
// this function returns returns true, if bot has a primary weapon
return ( pev - > weapons & WEAPON_PRIMARY ) ! = 0 ;
}
bool Bot : : HasSecondaryWeapon ( void )
{
// this function returns returns true, if bot has a secondary weapon
return ( pev - > weapons & WEAPON_SECONDARY ) ! = 0 ;
}
bool Bot : : HasShield ( void )
{
// this function returns true, if bot has a tactical shield
return strncmp ( STRING ( pev - > viewmodel ) , " models/shield/v_shield_ " , 23 ) = = 0 ;
}
bool Bot : : IsShieldDrawn ( void )
{
// this function returns true, is the tactical shield is drawn
if ( ! HasShield ( ) )
return false ;
return pev - > weaponanim = = 6 | | pev - > weaponanim = = 7 ;
}
bool Bot : : IsEnemyProtectedByShield ( edict_t * enemy )
{
// this function returns true, if enemy protected by the shield
2015-06-04 11:52:48 +03:00
if ( IsEntityNull ( enemy ) | | ( HasShield ( ) & & IsShieldDrawn ( ) ) )
2014-07-30 14:17:46 +04:00
return false ;
// check if enemy has shield and this shield is drawn
if ( strncmp ( STRING ( enemy - > v . viewmodel ) , " models/shield/v_shield_ " , 23 ) = = 0 & & ( enemy - > v . weaponanim = = 6 | | enemy - > v . weaponanim = = 7 ) )
{
if ( : : IsInViewCone ( pev - > origin , enemy ) )
return true ;
}
return false ;
}
bool Bot : : UsesSniper ( void )
{
// this function returns true, if returns if bot is using a sniper rifle
return m_currentWeapon = = WEAPON_AWP | | m_currentWeapon = = WEAPON_G3SG1 | | m_currentWeapon = = WEAPON_SCOUT | | m_currentWeapon = = WEAPON_SG550 ;
}
bool Bot : : UsesRifle ( void )
{
WeaponSelect * selectTab = & g_weaponSelect [ 0 ] ;
int count = 0 ;
while ( selectTab - > id )
{
if ( m_currentWeapon = = selectTab - > id )
break ;
selectTab + + ;
count + + ;
}
if ( selectTab - > id & & count > 13 )
return true ;
return false ;
}
bool Bot : : UsesPistol ( void )
{
WeaponSelect * selectTab = & g_weaponSelect [ 0 ] ;
int count = 0 ;
// loop through all the weapons until terminator is found
while ( selectTab - > id )
{
if ( m_currentWeapon = = selectTab - > id )
break ;
selectTab + + ;
count + + ;
}
if ( selectTab - > id & & count < 7 )
return true ;
return false ;
}
2014-09-17 20:36:42 +04:00
bool Bot : : UsesCampGun ( void )
{
return UsesSubmachineGun ( ) | | UsesRifle ( ) | | UsesSniper ( ) ;
}
2014-07-30 14:17:46 +04:00
bool Bot : : UsesSubmachineGun ( void )
{
return m_currentWeapon = = WEAPON_MP5 | | m_currentWeapon = = WEAPON_TMP | | m_currentWeapon = = WEAPON_P90 | | m_currentWeapon = = WEAPON_MAC10 | | m_currentWeapon = = WEAPON_UMP45 ;
}
bool Bot : : UsesZoomableRifle ( void )
{
return m_currentWeapon = = WEAPON_AUG | | m_currentWeapon = = WEAPON_SG552 ;
}
bool Bot : : UsesBadPrimary ( void )
{
return m_currentWeapon = = WEAPON_XM1014 | | m_currentWeapon = = WEAPON_M3 | | m_currentWeapon = = WEAPON_UMP45 | | m_currentWeapon = = WEAPON_MAC10 | | m_currentWeapon = = WEAPON_TMP | | m_currentWeapon = = WEAPON_P90 ;
}
int Bot : : CheckGrenades ( void )
{
if ( pev - > weapons & ( 1 < < WEAPON_EXPLOSIVE ) )
return WEAPON_EXPLOSIVE ;
else if ( pev - > weapons & ( 1 < < WEAPON_FLASHBANG ) )
return WEAPON_FLASHBANG ;
else if ( pev - > weapons & ( 1 < < WEAPON_SMOKE ) )
return WEAPON_SMOKE ;
return - 1 ;
}
void Bot : : SelectBestWeapon ( void )
{
// this function chooses best weapon, from weapons that bot currently own, and change
// current weapon to best one.
if ( yb_jasonmode . GetBool ( ) )
{
// if knife mode activated, force bot to use knife
SelectWeaponByName ( " weapon_knife " ) ;
return ;
}
if ( m_isReloading )
return ;
WeaponSelect * selectTab = & g_weaponSelect [ 0 ] ;
int selectIndex = 0 ;
int chosenWeaponIndex = 0 ;
// loop through all the weapons until terminator is found...
while ( selectTab [ selectIndex ] . id )
{
// is the bot NOT carrying this weapon?
if ( ! ( pev - > weapons & ( 1 < < selectTab [ selectIndex ] . id ) ) )
{
selectIndex + + ; // skip to next weapon
continue ;
}
int id = selectTab [ selectIndex ] . id ;
bool ammoLeft = false ;
// is the bot already holding this weapon and there is still ammo in clip?
if ( selectTab [ selectIndex ] . id = = m_currentWeapon & & ( GetAmmoInClip ( ) < 0 | | GetAmmoInClip ( ) > = selectTab [ selectIndex ] . minPrimaryAmmo ) )
ammoLeft = true ;
// is no ammo required for this weapon OR enough ammo available to fire
if ( g_weaponDefs [ id ] . ammo1 < 0 | | m_ammo [ g_weaponDefs [ id ] . ammo1 ] > = selectTab [ selectIndex ] . minPrimaryAmmo )
ammoLeft = true ;
if ( ammoLeft )
chosenWeaponIndex = selectIndex ;
selectIndex + + ;
}
chosenWeaponIndex % = NUM_WEAPONS + 1 ;
selectIndex = chosenWeaponIndex ;
int id = selectTab [ selectIndex ] . id ;
// select this weapon if it isn't already selected
if ( m_currentWeapon ! = id )
SelectWeaponByName ( selectTab [ selectIndex ] . weaponName ) ;
m_isReloading = false ;
m_reloadState = RELOAD_NONE ;
}
void Bot : : SelectPistol ( void )
{
int oldWeapons = pev - > weapons ;
pev - > weapons & = ~ WEAPON_PRIMARY ;
SelectBestWeapon ( ) ;
pev - > weapons = oldWeapons ;
}
int Bot : : GetHighestWeapon ( void )
{
WeaponSelect * selectTab = & g_weaponSelect [ 0 ] ;
int weapons = pev - > weapons ;
int num = 0 ;
int i = 0 ;
// loop through all the weapons until terminator is found...
while ( selectTab - > id )
{
// is the bot carrying this weapon?
if ( weapons & ( 1 < < selectTab - > id ) )
num = i ;
i + + ;
selectTab + + ;
}
return num ;
}
void Bot : : SelectWeaponByName ( const char * name )
{
FakeClientCommand ( GetEntity ( ) , name ) ;
}
void Bot : : SelectWeaponbyNumber ( int num )
{
FakeClientCommand ( GetEntity ( ) , g_weaponSelect [ num ] . weaponName ) ;
}
void Bot : : AttachToUser ( void )
{
// this function forces bot to join to user
Array < edict_t * > foundUsers ;
// search friends near us
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . team ! = m_team | | g_clients [ i ] . ent = = GetEntity ( ) )
2014-07-30 14:17:46 +04:00
continue ;
if ( EntityIsVisible ( g_clients [ i ] . origin ) & & ! IsValidBot ( g_clients [ i ] . ent ) )
foundUsers . Push ( g_clients [ i ] . ent ) ;
}
if ( foundUsers . IsEmpty ( ) )
return ;
m_targetEntity = foundUsers . GetRandomElement ( ) ;
ChatterMessage ( Chatter_LeadOnSir ) ;
StartTask ( TASK_FOLLOWUSER , TASKPRI_FOLLOWUSER , - 1 , 0.0 , true ) ;
}
void Bot : : CommandTeam ( void )
{
// prevent spamming
if ( m_timeTeamOrder > GetWorldTime ( ) + 2 | | yb_csdm_mode . GetInt ( ) = = 2 | | yb_communication_type . GetInt ( ) = = 0 )
return ;
bool memberNear = false ;
bool memberExists = false ;
// search teammates seen by this bot
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2014-08-06 00:03:50 +04:00
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . team ! = m_team | | g_clients [ i ] . ent = = GetEntity ( ) )
2014-07-30 14:17:46 +04:00
continue ;
memberExists = true ;
if ( EntityIsVisible ( g_clients [ i ] . origin ) )
{
memberNear = true ;
break ;
}
}
if ( memberNear ) // has teammates ?
{
if ( m_personality = = PERSONALITY_RUSHER & & ! ( yb_communication_type . GetInt ( ) = = 2 ) )
RadioMessage ( Radio_StormTheFront ) ;
else if ( ! m_personality = = PERSONALITY_RUSHER & & ! ( yb_communication_type . GetInt ( ) = = 2 ) )
RadioMessage ( Radio_Fallback ) ;
}
else if ( memberExists & & yb_communication_type . GetInt ( ) = = 1 )
RadioMessage ( Radio_TakingFire ) ;
else if ( memberExists & & yb_communication_type . GetInt ( ) = = 2 )
ChatterMessage ( Chatter_ScaredEmotion ) ;
m_timeTeamOrder = GetWorldTime ( ) + g_randGen . Float ( 5.0 , 30.0 ) ;
}
bool Bot : : IsGroupOfEnemies ( Vector location , int numEnemies , int radius )
{
int numPlayers = 0 ;
// search the world for enemy players...
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
if ( ! ( g_clients [ i ] . flags & CF_USED ) | | ! ( g_clients [ i ] . flags & CF_ALIVE ) | | g_clients [ i ] . ent = = GetEntity ( ) )
continue ;
if ( ( g_clients [ i ] . ent - > v . origin - location ) . GetLength ( ) < radius )
{
// don't target our teammates...
2014-08-06 00:03:50 +04:00
if ( g_clients [ i ] . team = = m_team )
2014-07-30 14:17:46 +04:00
return false ;
if ( numPlayers + + > numEnemies )
return true ;
}
}
return false ;
}
void Bot : : CheckReload ( void )
{
// check the reload state
if ( GetTaskId ( ) = = TASK_PLANTBOMB | | GetTaskId ( ) = = TASK_DEFUSEBOMB | | GetTaskId ( ) = = TASK_PICKUPITEM | | GetTaskId ( ) = = TASK_THROWFLASHBANG | | GetTaskId ( ) = = TASK_THROWSMOKE | | m_isUsingGrenade )
{
m_reloadState = RELOAD_NONE ;
return ;
}
m_isReloading = false ; // update reloading status
m_reloadCheckTime = GetWorldTime ( ) + 3.0 ;
if ( m_reloadState ! = RELOAD_NONE )
{
int weaponIndex = 0 , maxClip = 0 ;
int weapons = pev - > weapons ;
if ( m_reloadState = = RELOAD_PRIMARY )
weapons & = WEAPON_PRIMARY ;
else if ( m_reloadState = = RELOAD_SECONDARY )
weapons & = WEAPON_SECONDARY ;
if ( weapons = = 0 )
{
m_reloadState + + ;
if ( m_reloadState > RELOAD_SECONDARY )
m_reloadState = RELOAD_NONE ;
return ;
}
for ( int i = 1 ; i < MAX_WEAPONS ; i + + )
{
if ( weapons & ( 1 < < i ) )
{
weaponIndex = i ;
break ;
}
}
InternalAssert ( weaponIndex ) ;
switch ( weaponIndex )
{
case WEAPON_M249 :
maxClip = 100 ;
break ;
case WEAPON_P90 :
maxClip = 50 ;
break ;
case WEAPON_GALIL :
maxClip = 35 ;
break ;
case WEAPON_ELITE :
case WEAPON_MP5 :
case WEAPON_TMP :
case WEAPON_MAC10 :
case WEAPON_M4A1 :
case WEAPON_AK47 :
case WEAPON_SG552 :
case WEAPON_AUG :
case WEAPON_SG550 :
maxClip = 30 ;
break ;
case WEAPON_UMP45 :
case WEAPON_FAMAS :
maxClip = 25 ;
break ;
case WEAPON_GLOCK :
case WEAPON_FIVESEVEN :
case WEAPON_G3SG1 :
maxClip = 20 ;
break ;
case WEAPON_P228 :
maxClip = 13 ;
break ;
case WEAPON_USP :
maxClip = 12 ;
break ;
case WEAPON_AWP :
case WEAPON_SCOUT :
maxClip = 10 ;
break ;
case WEAPON_M3 :
maxClip = 8 ;
break ;
case WEAPON_DEAGLE :
case WEAPON_XM1014 :
maxClip = 7 ;
break ;
}
if ( m_ammoInClip [ weaponIndex ] < ( maxClip * 0.8 ) & & m_ammo [ g_weaponDefs [ weaponIndex ] . ammo1 ] > 0 )
{
if ( m_currentWeapon ! = weaponIndex )
SelectWeaponByName ( g_weaponDefs [ weaponIndex ] . className ) ;
pev - > button & = ~ IN_ATTACK ;
if ( ( pev - > oldbuttons & IN_RELOAD ) = = RELOAD_NONE )
pev - > button | = IN_RELOAD ; // press reload button
m_isReloading = true ;
}
else
{
// if we have enemy don't reload next weapon
if ( ( m_states & ( STATE_SEEING_ENEMY | STATE_HEARING_ENEMY ) ) | | m_seeEnemyTime + 5.0 > GetWorldTime ( ) )
{
m_reloadState = RELOAD_NONE ;
return ;
}
m_reloadState + + ;
if ( m_reloadState > RELOAD_SECONDARY )
m_reloadState = RELOAD_NONE ;
return ;
}
}
}