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>
2015-06-04 11:52:48 +03:00
ConVar yb_aim_method ( " yb_aim_method " , " 3 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_damper_coefficient_x ( " yb_aim_damper_coefficient_x " , " 0.22 " , VT_NOSERVER ) ;
ConVar yb_aim_damper_coefficient_y ( " yb_aim_damper_coefficient_y " , " 0.22 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_deviation_x ( " yb_aim_deviation_x " , " 2.0 " , VT_NOSERVER ) ;
ConVar yb_aim_deviation_y ( " yb_aim_deviation_y " , " 1.0 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_influence_x_on_y ( " yb_aim_influence_x_on_y " , " 0.26 " , VT_NOSERVER ) ;
ConVar yb_aim_influence_y_on_x ( " yb_aim_influence_y_on_x " , " 0.18 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_notarget_slowdown_ratio ( " yb_aim_notarget_slowdown_ratio " , " 0.6 " , VT_NOSERVER ) ;
ConVar yb_aim_offset_delay ( " yb_aim_offset_delay " , " 0.5 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_spring_stiffness_x ( " yb_aim_spring_stiffness_x " , " 15.0 " , VT_NOSERVER ) ;
ConVar yb_aim_spring_stiffness_y ( " yb_aim_spring_stiffness_y " , " 14.0 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
ConVar yb_aim_target_anticipation_ratio ( " yb_aim_target_anticipation_ratio " , " 5.0 " , VT_NOSERVER ) ;
2014-07-30 14:17:46 +04:00
int Bot : : FindGoal ( void )
{
// chooses a destination (goal) waypoint for a bot
2015-06-04 11:52:48 +03:00
if ( ! g_bombPlanted & & m_team = = TEAM_TF & & ( g_mapType & MAP_DE ) )
2014-07-30 14:17:46 +04:00
{
edict_t * pent = NULL ;
2015-06-04 11:52:48 +03:00
while ( ! IsEntityNull ( pent = FIND_ENTITY_BY_STRING ( pent , " classname " , " weaponbox " ) ) )
2014-07-30 14:17:46 +04:00
{
if ( strcmp ( STRING ( pent - > v . model ) , " models/w_backpack.mdl " ) = = 0 )
{
int index = g_waypoint - > FindNearest ( GetEntityOrigin ( pent ) ) ;
if ( index > = 0 & & index < g_numWaypoints )
2015-06-04 11:52:48 +03:00
return m_loosedBombWptIndex = index ;
2014-07-30 14:17:46 +04:00
break ;
}
}
}
2015-06-04 11:52:48 +03:00
int tactic ;
2014-07-30 14:17:46 +04:00
// path finding behaviour depending on map type
2015-06-09 22:16:08 +03:00
float offensive ;
float defensive ;
2014-07-30 14:17:46 +04:00
2015-06-09 22:16:08 +03:00
float goalDesire ;
float forwardDesire ;
float campDesire ;
float backoffDesire ;
float tacticChoice ;
2014-07-30 14:17:46 +04:00
Array < int > offensiveWpts ;
Array < int > defensiveWpts ;
2014-08-06 00:03:50 +04:00
switch ( m_team )
2014-07-30 14:17:46 +04:00
{
case TEAM_TF :
offensiveWpts = g_waypoint - > m_ctPoints ;
defensiveWpts = g_waypoint - > m_terrorPoints ;
break ;
case TEAM_CF :
offensiveWpts = g_waypoint - > m_terrorPoints ;
defensiveWpts = g_waypoint - > m_ctPoints ;
break ;
}
// terrorist carrying the C4?
2014-09-17 20:36:42 +04:00
if ( m_hasC4 | | m_isVIP )
2014-07-30 14:17:46 +04:00
{
tactic = 3 ;
goto TacticChoosen ;
}
2015-06-04 11:52:48 +03:00
else if ( m_team = = TEAM_CF & & HasHostage ( ) )
2014-07-30 14:17:46 +04:00
{
tactic = 2 ;
offensiveWpts = g_waypoint - > m_rescuePoints ;
goto TacticChoosen ;
}
2015-06-09 22:16:08 +03:00
offensive = m_agressionLevel * 100 ;
defensive = m_fearLevel * 100 ;
2014-07-30 14:17:46 +04:00
if ( g_mapType & ( MAP_AS | MAP_CS ) )
{
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-07-30 14:17:46 +04:00
{
defensive + = 25.0f ;
offensive - = 25.0f ;
}
2014-08-06 00:03:50 +04:00
else if ( m_team = = TEAM_CF )
2014-07-30 14:17:46 +04:00
{
2015-06-09 22:16:08 +03:00
// on hostage maps force more bots to save hostages
if ( g_mapType & MAP_CS )
{
defensive - = 25.0f - m_difficulty * 0.5f ;
offensive + = 25.0f + m_difficulty * 5.0f ;
}
else // on AS leave as is
{
defensive - = 25.0f ;
offensive + = 25.0f ;
}
2014-07-30 14:17:46 +04:00
}
}
2014-08-06 00:03:50 +04:00
else if ( ( g_mapType & MAP_DE ) & & m_team = = TEAM_CF )
2014-07-30 14:17:46 +04:00
{
if ( g_bombPlanted & & GetTaskId ( ) ! = TASK_ESCAPEFROMBOMB & & g_waypoint - > GetBombPosition ( ) ! = nullvec )
{
if ( g_bombSayString )
{
ChatMessage ( CHAT_BOMBPLANT ) ;
g_bombSayString = false ;
}
return m_chosenGoalIndex = ChooseBombWaypoint ( ) ;
}
2015-06-04 11:52:48 +03:00
defensive + = 25.0f ;
2014-07-30 14:17:46 +04:00
offensive - = 25.0f ;
}
2014-09-17 20:36:42 +04:00
else if ( ( g_mapType & MAP_DE ) & & m_team = = TEAM_TF & & g_timeRoundStart + 10.0f < GetWorldTime ( ) )
2014-07-30 14:17:46 +04:00
{
// send some terrorists to guard planter bomb
if ( g_bombPlanted & & GetTaskId ( ) ! = TASK_ESCAPEFROMBOMB & & GetBombTimeleft ( ) > = 15.0 )
return m_chosenGoalIndex = FindDefendWaypoint ( g_waypoint - > GetBombPosition ( ) ) ;
}
2015-06-09 22:16:08 +03:00
goalDesire = Random . Float ( 0.0f , 100.0f ) + offensive ;
forwardDesire = Random . Float ( 0.0f , 100.0f ) + offensive ;
campDesire = Random . Float ( 0.0f , 100.0f ) + defensive ;
backoffDesire = Random . Float ( 0.0f , 100.0f ) + defensive ;
2014-07-30 14:17:46 +04:00
2014-09-17 20:36:42 +04:00
if ( ! UsesCampGun ( ) )
campDesire = 0 ;
2014-07-30 14:17:46 +04:00
tacticChoice = backoffDesire ;
tactic = 0 ;
if ( campDesire > tacticChoice )
{
tacticChoice = campDesire ;
tactic = 1 ;
}
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
if ( forwardDesire > tacticChoice )
{
tacticChoice = forwardDesire ;
tactic = 2 ;
}
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
if ( goalDesire > tacticChoice )
tactic = 3 ;
TacticChoosen :
int goalChoices [ 4 ] = { - 1 , - 1 , - 1 , - 1 } ;
if ( tactic = = 0 & & ! defensiveWpts . IsEmpty ( ) ) // careful goal
2015-06-09 22:16:08 +03:00
FilterGoals ( defensiveWpts , goalChoices ) ;
2014-07-30 14:17:46 +04:00
else if ( tactic = = 1 & & ! g_waypoint - > m_campPoints . IsEmpty ( ) ) // camp waypoint goal
{
// pickup sniper points if possible for sniping bots
2015-06-09 22:16:08 +03:00
if ( ! g_waypoint - > m_sniperPoints . IsEmpty ( ) & & UsesSniper ( ) )
FilterGoals ( g_waypoint - > m_sniperPoints , goalChoices ) ;
2014-07-30 14:17:46 +04:00
else
2015-06-09 22:16:08 +03:00
FilterGoals ( g_waypoint - > m_campPoints , goalChoices ) ;
2014-07-30 14:17:46 +04:00
}
else if ( tactic = = 2 & & ! offensiveWpts . IsEmpty ( ) ) // offensive goal
2015-06-09 22:16:08 +03:00
FilterGoals ( offensiveWpts , goalChoices ) ;
2014-07-30 14:17:46 +04:00
else if ( tactic = = 3 & & ! g_waypoint - > m_goalPoints . IsEmpty ( ) ) // map goal waypoint
{
2015-06-06 14:19:19 +03:00
// forcee bomber to select closest goal, if round-start goal was reset by something
2015-06-04 11:52:48 +03:00
if ( m_hasC4 & & g_timeRoundStart + 20.0f < GetWorldTime ( ) )
2014-07-30 14:17:46 +04:00
{
2015-06-06 14:19:19 +03:00
float minDist = 1024.0f ;
int count = 0 ;
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
Path * path = g_waypoint - > GetPath ( i ) ;
if ( ! ( path - > flags & FLAG_GOAL ) )
continue ;
float distance = ( path - > origin - pev - > origin ) . GetLength ( ) ;
if ( distance < minDist )
{
if ( count < 4 )
{
goalChoices [ count ] = i ;
count + + ;
}
minDist = distance ;
}
}
for ( int i = 0 ; i < 4 ; i + + )
{
if ( goalChoices [ i ] = = - 1 )
{
goalChoices [ i ] = g_waypoint - > m_goalPoints . GetRandomElement ( ) ;
InternalAssert ( goalChoices [ i ] > = 0 & & goalChoices [ i ] < g_numWaypoints ) ;
}
}
2015-06-04 11:52:48 +03:00
}
else
2015-06-09 22:16:08 +03:00
FilterGoals ( g_waypoint - > m_goalPoints , goalChoices ) ;
2014-07-30 14:17:46 +04:00
}
if ( m_currentWaypointIndex = = - 1 | | m_currentWaypointIndex > = g_numWaypoints )
ChangeWptIndex ( g_waypoint - > FindNearest ( pev - > origin ) ) ;
if ( goalChoices [ 0 ] = = - 1 )
2015-06-09 15:45:34 +03:00
return m_chosenGoalIndex = Random . Long ( 0 , g_numWaypoints - 1 ) ;
2014-07-30 14:17:46 +04:00
bool isSorting = false ;
do
{
isSorting = false ;
for ( int i = 0 ; i < 3 ; i + + )
{
int testIndex = goalChoices [ i + 1 ] ;
if ( testIndex < 0 )
break ;
2015-06-09 22:16:08 +03:00
int goal1 = m_team = = TEAM_TF ? ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + goalChoices [ i ] ) - > team0Value : ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + goalChoices [ i ] ) - > team1Value ;
int goal2 = m_team = = TEAM_TF ? ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + goalChoices [ i + 1 ] ) - > team0Value : ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + goalChoices [ i + 1 ] ) - > team1Value ;
2014-07-30 14:17:46 +04:00
2015-06-09 22:16:08 +03:00
if ( goal1 < goal2 )
2014-07-30 14:17:46 +04:00
{
2015-06-09 22:16:08 +03:00
goalChoices [ i + 1 ] = goalChoices [ i ] ;
goalChoices [ i ] = testIndex ;
2014-07-30 14:17:46 +04:00
2015-06-09 22:16:08 +03:00
isSorting = true ;
2014-07-30 14:17:46 +04:00
}
}
} while ( isSorting ) ;
2015-06-09 22:16:08 +03:00
return m_chosenGoalIndex = goalChoices [ 0 ] ; // return and store goal
}
void Bot : : FilterGoals ( const Array < int > & goals , int * result )
{
// this function filters the goals, so new goal is not bot's old goal, and array of goals doesn't contains duplicate goals
int totalGoals = goals . GetElementNumber ( ) ;
int searchCount = 0 ;
for ( int index = 0 ; index < 4 ; index + + )
{
int rand = goals . GetRandomElement ( ) ;
if ( searchCount < = 8 & & ( m_prevGoalIndex = = rand | | ( ( result [ 0 ] = = rand | | result [ 1 ] = = rand | | result [ 2 ] = = rand | | result [ 3 ] = = rand ) & & totalGoals > 4 ) ) & & ! IsPointOccupied ( rand ) )
{
if ( index > 0 )
index - - ;
continue ;
}
result [ index ] = rand ;
}
2014-07-30 14:17:46 +04:00
}
bool Bot : : GoalIsValid ( void )
{
int goal = GetTask ( ) - > data ;
if ( goal = = - 1 ) // not decided about a goal
return false ;
else if ( goal = = m_currentWaypointIndex ) // no nodes needed
return true ;
else if ( m_navNode = = NULL ) // no path calculated
return false ;
// got path - check if still valid
PathNode * node = m_navNode ;
while ( node - > next ! = NULL )
node = node - > next ;
if ( node - > index = = goal )
return true ;
return false ;
}
2015-06-07 19:43:16 +03:00
void Bot : : CheckTerrain ( float movedDistance , const Vector & dir , const Vector & dirNormal )
2015-06-04 11:52:48 +03:00
{
m_isStuck = false ;
Vector src = nullvec ;
2015-06-07 19:43:16 +03:00
Vector dst = nullvec ;
Vector direction = dir ;
Vector directionNormal = dirNormal ;
2015-06-04 11:52:48 +03:00
TraceResult tr ;
2015-06-09 15:45:34 +03:00
edict_t * nearest = NULL ;
2015-06-04 11:52:48 +03:00
2015-06-11 23:22:50 +03:00
if ( g_timeRoundStart + 10.0f < GetWorldTime ( ) & & FindNearestPlayer ( reinterpret_cast < void * * > ( & nearest ) , GetEntity ( ) , pev - > maxspeed , true , false , true , true ) ) // found somebody?
2015-06-04 11:52:48 +03:00
{
MakeVectors ( m_moveAngles ) ; // use our movement angles
// try to predict where we should be next frame
Vector moved = pev - > origin + g_pGlobals - > v_forward * m_moveSpeed * m_frameInterval ;
moved = moved + g_pGlobals - > v_right * m_strafeSpeed * m_frameInterval ;
moved = moved + pev - > velocity * m_frameInterval ;
2015-06-09 15:45:34 +03:00
float nearestDistance = ( nearest - > v . origin - pev - > origin ) . GetLength2D ( ) ;
float nextFrameDistance = ( ( nearest - > v . origin + nearest - > v . velocity * m_frameInterval ) - pev - > origin ) . GetLength2D ( ) ;
2015-06-04 11:52:48 +03:00
// is player that near now or in future that we need to steer away?
2015-06-09 15:45:34 +03:00
if ( ( nearest - > v . origin - moved ) . GetLength2D ( ) < = 48.0 | | ( nearestDistance < = 56.0 & & nextFrameDistance < nearestDistance ) )
2015-06-04 11:52:48 +03:00
{
// to start strafing, we have to first figure out if the target is on the left side or right side
2015-06-09 15:45:34 +03:00
Vector dirToPoint = ( pev - > origin - nearest - > v . origin ) . SkipZ ( ) ;
2015-06-04 11:52:48 +03:00
if ( ( dirToPoint | g_pGlobals - > v_right . SkipZ ( ) ) > 0.0 )
SetStrafeSpeed ( directionNormal , pev - > maxspeed ) ;
else
SetStrafeSpeed ( directionNormal , - pev - > maxspeed ) ;
ResetCollideState ( ) ;
if ( nearestDistance < 56.0 & & ( dirToPoint | g_pGlobals - > v_forward . SkipZ ( ) ) < 0.0 )
m_moveSpeed = - pev - > maxspeed ;
}
}
// Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way
if ( ( m_moveSpeed > = 10 | | m_strafeSpeed > = 10 ) & & m_lastCollTime < GetWorldTime ( ) )
{
if ( movedDistance < 2.0 & & m_prevSpeed > = 1.0 ) // didn't we move enough previously?
{
// Then consider being stuck
m_prevTime = GetWorldTime ( ) ;
m_isStuck = true ;
if ( m_firstCollideTime = = 0.0 )
m_firstCollideTime = GetWorldTime ( ) + 0.2 ;
}
else // not stuck yet
{
// test if there's something ahead blocking the way
if ( CantMoveForward ( directionNormal , & tr ) & & ! IsOnLadder ( ) )
{
if ( m_firstCollideTime = = 0.0 )
m_firstCollideTime = GetWorldTime ( ) + 0.2 ;
else if ( m_firstCollideTime < = GetWorldTime ( ) )
m_isStuck = true ;
}
else
m_firstCollideTime = 0.0 ;
}
if ( ! m_isStuck ) // not stuck?
{
if ( m_probeTime + 0.5 < GetWorldTime ( ) )
ResetCollideState ( ) ; // reset collision memory if not being stuck for 0.5 secs
else
{
// remember to keep pressing duck if it was necessary ago
if ( m_collideMoves [ m_collStateIndex ] = = COLLISION_DUCK & & IsOnFloor ( ) | | IsInWater ( ) )
pev - > button | = IN_DUCK ;
}
2015-06-07 19:43:16 +03:00
return ;
2015-06-04 11:52:48 +03:00
}
2015-06-07 19:43:16 +03:00
// bot is stuck!
// not yet decided what to do?
if ( m_collisionState = = COLLISION_NOTDECICED )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
char bits = 0 ;
if ( IsOnLadder ( ) )
bits = PROBE_STRAFE ;
else if ( IsInWater ( ) )
bits = ( PROBE_JUMP | PROBE_STRAFE ) ;
else
2015-06-09 15:45:34 +03:00
bits = ( ( Random . Long ( 0 , 10 ) > 7 ? PROBE_JUMP : 0 ) | PROBE_STRAFE | PROBE_DUCK ) ;
2015-06-07 19:43:16 +03:00
// collision check allowed if not flying through the air
if ( IsOnFloor ( ) | | IsOnLadder ( ) | | IsInWater ( ) )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
char state [ 8 ] ;
int i = 0 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
// first 4 entries hold the possible collision states
state [ i + + ] = COLLISION_JUMP ;
state [ i + + ] = COLLISION_DUCK ;
state [ i + + ] = COLLISION_STRAFELEFT ;
state [ i + + ] = COLLISION_STRAFERIGHT ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
// now weight all possible states
if ( bits & PROBE_JUMP )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
state [ i ] = 0 ;
if ( CanJumpUp ( directionNormal ) )
state [ i ] + = 10 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( m_destOrigin . z > = pev - > origin . z + 18.0 )
state [ i ] + = 5 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( EntityIsVisible ( m_destOrigin ) )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
MakeVectors ( m_moveAngles ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
src = EyePosition ( ) ;
src = src + ( g_pGlobals - > v_right * 15 ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
TraceLine ( src , m_destOrigin , true , true , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( tr . flFraction > = 1.0 )
2015-06-04 11:52:48 +03:00
{
src = EyePosition ( ) ;
2015-06-07 19:43:16 +03:00
src = src - ( g_pGlobals - > v_right * 15 ) ;
2015-06-04 11:52:48 +03:00
TraceLine ( src , m_destOrigin , true , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction > = 1.0 )
2015-06-07 19:43:16 +03:00
state [ i ] + = 5 ;
2015-06-04 11:52:48 +03:00
}
}
2015-06-07 19:43:16 +03:00
if ( pev - > flags & FL_DUCKING )
src = pev - > origin ;
2015-06-04 11:52:48 +03:00
else
2015-06-07 19:43:16 +03:00
src = pev - > origin + Vector ( 0 , 0 , - 17 ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
dst = src + directionNormal * 30 ;
TraceLine ( src , dst , true , true , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( tr . flFraction ! = 1.0 )
state [ i ] + = 10 ;
}
else
state [ i ] = 0 ;
i + + ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( bits & PROBE_DUCK )
{
state [ i ] = 0 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( CanDuckUnder ( directionNormal ) )
state [ i ] + = 10 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( ( m_destOrigin . z + 36.0 < = pev - > origin . z ) & & EntityIsVisible ( m_destOrigin ) )
state [ i ] + = 5 ;
}
else
state [ i ] = 0 ;
i + + ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( bits & PROBE_STRAFE )
{
state [ i ] = 0 ;
state [ i + 1 ] = 0 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
// to start strafing, we have to first figure out if the target is on the left side or right side
MakeVectors ( m_moveAngles ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
Vector dirToPoint = ( pev - > origin - m_destOrigin ) . Normalize2D ( ) ;
Vector rightSide = g_pGlobals - > v_right . Normalize2D ( ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
bool dirRight = false ;
bool dirLeft = false ;
bool blockedLeft = false ;
bool blockedRight = false ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( ( dirToPoint | rightSide ) > 0 )
dirRight = true ;
else
dirLeft = true ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( m_moveSpeed > 0 )
direction = g_pGlobals - > v_forward ;
else
direction = - g_pGlobals - > v_forward ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
// now check which side is blocked
src = pev - > origin + ( g_pGlobals - > v_right * 32 ) ;
dst = src + ( direction * 32 ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
TraceHull ( src , dst , true , head_hull , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( tr . flFraction ! = 1.0 )
blockedRight = true ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
src = pev - > origin - ( g_pGlobals - > v_right * 32 ) ;
dst = src + ( direction * 32 ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
TraceHull ( src , dst , true , head_hull , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( tr . flFraction ! = 1.0 )
blockedLeft = true ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( dirLeft )
state [ i ] + = 5 ;
else
state [ i ] - = 5 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( blockedLeft )
state [ i ] - = 5 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
i + + ;
if ( dirRight )
state [ i ] + = 5 ;
2015-06-04 11:52:48 +03:00
else
2015-06-07 19:43:16 +03:00
state [ i ] - = 5 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( blockedRight )
state [ i ] - = 5 ;
}
else
{
state [ i ] = 0 ;
i + + ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
state [ i ] = 0 ;
}
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
// weighted all possible moves, now sort them to start with most probable
int temp = 0 ;
bool isSorting = false ;
do
{
isSorting = false ;
for ( i = 0 ; i < 3 ; i + + )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
if ( state [ i + 4 ] < state [ i + 5 ] )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
temp = state [ i ] ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
state [ i ] = state [ i + 1 ] ;
state [ i + 1 ] = temp ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
temp = state [ i + 4 ] ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
state [ i + 4 ] = state [ i + 5 ] ;
state [ i + 5 ] = temp ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
isSorting = true ;
2015-06-04 11:52:48 +03:00
}
2015-06-07 19:43:16 +03:00
}
} while ( isSorting ) ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
for ( i = 0 ; i < 4 ; i + + )
m_collideMoves [ i ] = state [ i ] ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
m_collideTime = GetWorldTime ( ) ;
m_probeTime = GetWorldTime ( ) + 0.5 ;
m_collisionProbeBits = bits ;
m_collisionState = COLLISION_PROBING ;
m_collStateIndex = 0 ;
2015-06-04 11:52:48 +03:00
}
2015-06-07 19:43:16 +03:00
}
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( m_collisionState = = COLLISION_PROBING )
{
if ( m_probeTime < GetWorldTime ( ) )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
m_collStateIndex + + ;
m_probeTime = GetWorldTime ( ) + 0.5 ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( m_collStateIndex > 4 )
{
m_navTimeset = GetWorldTime ( ) - 5.0 ;
ResetCollideState ( ) ;
2015-06-04 11:52:48 +03:00
}
2015-06-07 19:43:16 +03:00
}
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
if ( m_collStateIndex < = 4 )
{
switch ( m_collideMoves [ m_collStateIndex ] )
2015-06-04 11:52:48 +03:00
{
2015-06-07 19:43:16 +03:00
case COLLISION_JUMP :
if ( IsOnFloor ( ) | | IsInWater ( ) )
pev - > button | = IN_JUMP ;
break ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
case COLLISION_DUCK :
if ( IsOnFloor ( ) | | IsInWater ( ) )
pev - > button | = IN_DUCK ;
break ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
case COLLISION_STRAFELEFT :
pev - > button | = IN_MOVELEFT ;
SetStrafeSpeed ( directionNormal , - pev - > maxspeed ) ;
break ;
2015-06-04 11:52:48 +03:00
2015-06-07 19:43:16 +03:00
case COLLISION_STRAFERIGHT :
pev - > button | = IN_MOVERIGHT ;
SetStrafeSpeed ( directionNormal , pev - > maxspeed ) ;
break ;
2015-06-04 11:52:48 +03:00
}
}
}
}
}
2014-07-30 14:17:46 +04:00
bool Bot : : DoWaypointNav ( void )
{
// this function is a main path navigation
TraceResult tr , tr2 ;
// check if we need to find a waypoint...
if ( m_currentWaypointIndex = = - 1 )
{
GetValidWaypoint ( ) ;
m_waypointOrigin = m_currentPath - > origin ;
// if wayzone radios non zero vary origin a bit depending on the body angles
if ( m_currentPath - > radius > 0 )
{
2015-06-09 15:45:34 +03:00
MakeVectors ( Vector ( pev - > angles . x , AngleNormalize ( pev - > angles . y + Random . Float ( - 90 , 90 ) ) , 0.0 ) ) ;
m_waypointOrigin = m_waypointOrigin + g_pGlobals - > v_forward * Random . Float ( 0 , m_currentPath - > radius ) ;
2014-07-30 14:17:46 +04:00
}
m_navTimeset = GetWorldTime ( ) ;
}
if ( pev - > flags & FL_DUCKING )
m_destOrigin = m_waypointOrigin ;
else
m_destOrigin = m_waypointOrigin + pev - > view_ofs ;
float waypointDistance = ( pev - > origin - m_waypointOrigin ) . GetLength ( ) ;
// this waypoint has additional travel flags - care about them
if ( m_currentTravelFlags & PATHFLAG_JUMP )
{
// bot is not jumped yet?
if ( ! m_jumpFinished )
{
// if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating.
// pressing the jump button gives the illusion of the bot actual jmping.
if ( IsOnFloor ( ) | | IsOnLadder ( ) )
{
2015-06-09 22:16:08 +03:00
if ( m_desiredVelocity . x ! = 0.0f & & m_desiredVelocity . y ! = 0.0f )
2015-06-11 23:22:50 +03:00
pev - > velocity = m_desiredVelocity + m_desiredVelocity * 0.076f ;
2015-06-08 16:18:55 +03:00
2014-07-30 14:17:46 +04:00
pev - > button | = IN_JUMP ;
m_jumpFinished = true ;
m_checkTerrain = false ;
m_desiredVelocity = nullvec ;
}
}
else if ( ! yb_jasonmode . GetBool ( ) & & m_currentWeapon = = WEAPON_KNIFE & & IsOnFloor ( ) )
SelectBestWeapon ( ) ;
}
if ( m_currentPath - > flags & FLAG_LADDER )
{
if ( m_waypointOrigin . z > = ( pev - > origin . z + 16.0 ) )
m_waypointOrigin = m_currentPath - > origin + Vector ( 0 , 0 , 16 ) ;
else if ( m_waypointOrigin . z < pev - > origin . z + 16.0 & & ! IsOnLadder ( ) & & IsOnFloor ( ) & & ! ( pev - > flags & FL_DUCKING ) )
{
m_moveSpeed = waypointDistance ;
if ( m_moveSpeed < 150.0 )
m_moveSpeed = 150.0 ;
else if ( m_moveSpeed > pev - > maxspeed )
m_moveSpeed = pev - > maxspeed ;
}
}
// special lift handling (code merged from podbotmm)
if ( m_currentPath - > flags & FLAG_LIFT )
{
bool liftClosedDoorExists = false ;
// update waypoint time set
m_navTimeset = GetWorldTime ( ) ;
// trace line to door
TraceLine ( pev - > origin , m_currentPath - > origin , true , true , GetEntity ( ) , & tr2 ) ;
if ( tr2 . flFraction < 1.0 & & strcmp ( STRING ( tr2 . pHit - > v . classname ) , " func_door " ) = = 0 & & ( m_liftState = = LIFT_NO_NEARBY | | m_liftState = = LIFT_WAITING_FOR | | m_liftState = = LIFT_LOOKING_BUTTON_OUTSIDE ) & & pev - > groundentity ! = tr2 . pHit )
{
if ( m_liftState = = LIFT_NO_NEARBY )
{
m_liftState = LIFT_LOOKING_BUTTON_OUTSIDE ;
m_liftUsageTime = GetWorldTime ( ) + 7.0 ;
}
liftClosedDoorExists = true ;
}
// trace line down
TraceLine ( m_currentPath - > origin , m_currentPath - > origin + Vector ( 0 , 0 , - 50 ) , true , true , GetEntity ( ) , & tr ) ;
// if trace result shows us that it is a lift
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( tr . pHit ) & & m_navNode ! = NULL & & ( strcmp ( STRING ( tr . pHit - > v . classname ) , " func_door " ) = = 0 | | strcmp ( STRING ( tr . pHit - > v . classname ) , " func_plat " ) = = 0 | | strcmp ( STRING ( tr . pHit - > v . classname ) , " func_train " ) = = 0 ) & & ! liftClosedDoorExists )
2014-07-30 14:17:46 +04:00
{
if ( ( m_liftState = = LIFT_NO_NEARBY | | m_liftState = = LIFT_WAITING_FOR | | m_liftState = = LIFT_LOOKING_BUTTON_OUTSIDE ) & & tr . pHit - > v . velocity . z = = 0 )
{
if ( fabsf ( pev - > origin . z - tr . vecEndPos . z ) < 70.0 )
{
m_liftEntity = tr . pHit ;
m_liftState = LIFT_ENTERING_IN ;
m_liftTravelPos = m_currentPath - > origin ;
m_liftUsageTime = GetWorldTime ( ) + 5.0 ;
}
}
else if ( m_liftState = = LIFT_TRAVELING_BY )
{
m_liftState = LIFT_LEAVING ;
m_liftUsageTime = GetWorldTime ( ) + 7.0 ;
}
}
else if ( m_navNode ! = NULL ) // no lift found at waypoint
{
if ( ( m_liftState = = LIFT_NO_NEARBY | | m_liftState = = LIFT_WAITING_FOR ) & & m_navNode - > next ! = NULL )
{
if ( m_navNode - > next - > index > = 0 & & m_navNode - > next - > index < g_numWaypoints & & ( g_waypoint - > GetPath ( m_navNode - > next - > index ) - > flags & FLAG_LIFT ) )
{
TraceLine ( m_currentPath - > origin , g_waypoint - > GetPath ( m_navNode - > next - > index ) - > origin , true , true , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( tr . pHit ) & & ( strcmp ( STRING ( tr . pHit - > v . classname ) , " func_door " ) = = 0 | | strcmp ( STRING ( tr . pHit - > v . classname ) , " func_plat " ) = = 0 | | strcmp ( STRING ( tr . pHit - > v . classname ) , " func_train " ) = = 0 ) )
2014-07-30 14:17:46 +04:00
m_liftEntity = tr . pHit ;
}
m_liftState = LIFT_LOOKING_BUTTON_OUTSIDE ;
m_liftUsageTime = GetWorldTime ( ) + 15.0 ;
}
}
// bot is going to enter the lift
if ( m_liftState = = LIFT_ENTERING_IN )
{
m_destOrigin = m_liftTravelPos ;
// check if we enough to destination
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
// need to wait our following teammate ?
bool needWaitForTeammate = false ;
// if some bot is following a bot going into lift - he should take the same lift to go
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
Bot * bot = g_botManager - > GetBot ( i ) ;
if ( bot = = NULL | | bot = = this )
continue ;
2014-08-06 00:03:50 +04:00
if ( ! IsAlive ( bot - > GetEntity ( ) ) | | GetTeam ( bot - > GetEntity ( ) ) ! = m_team | | bot - > m_targetEntity ! = GetEntity ( ) | | bot - > GetTaskId ( ) ! = TASK_FOLLOWUSER )
2014-07-30 14:17:46 +04:00
continue ;
if ( bot - > pev - > groundentity = = m_liftEntity & & bot - > IsOnFloor ( ) )
break ;
bot - > m_liftEntity = m_liftEntity ;
bot - > m_liftState = LIFT_ENTERING_IN ;
bot - > m_liftTravelPos = m_liftTravelPos ;
needWaitForTeammate = true ;
}
if ( needWaitForTeammate )
{
m_liftState = LIFT_WAIT_FOR_TEAMMATES ;
m_liftUsageTime = GetWorldTime ( ) + 8.0 ;
}
else
{
m_liftState = LIFT_LOOKING_BUTTON_INSIDE ;
m_liftUsageTime = GetWorldTime ( ) + 10.0 ;
}
}
}
// bot is waiting for his teammates
if ( m_liftState = = LIFT_WAIT_FOR_TEAMMATES )
{
// need to wait our following teammate ?
bool needWaitForTeammate = false ;
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
Bot * bot = g_botManager - > GetBot ( i ) ;
if ( bot = = NULL )
continue ; // skip invalid bots
2014-08-06 00:03:50 +04:00
if ( ! IsAlive ( bot - > GetEntity ( ) ) | | GetTeam ( bot - > GetEntity ( ) ) ! = m_team | | bot - > m_targetEntity ! = GetEntity ( ) | | bot - > GetTaskId ( ) ! = TASK_FOLLOWUSER | | bot - > m_liftEntity ! = m_liftEntity )
2014-07-30 14:17:46 +04:00
continue ;
if ( bot - > pev - > groundentity = = m_liftEntity | | ! bot - > IsOnFloor ( ) )
{
needWaitForTeammate = true ;
break ;
}
}
// need to wait for teammate
if ( needWaitForTeammate )
{
m_destOrigin = m_liftTravelPos ;
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
// else we need to look for button
if ( ! needWaitForTeammate | | ( m_liftUsageTime < GetWorldTime ( ) ) )
{
m_liftState = LIFT_LOOKING_BUTTON_INSIDE ;
m_liftUsageTime = GetWorldTime ( ) + 10.0 ;
}
}
// bot is trying to find button inside a lift
if ( m_liftState = = LIFT_LOOKING_BUTTON_INSIDE )
{
edict_t * button = FindNearestButton ( STRING ( m_liftEntity - > v . targetname ) ) ;
// got a valid button entity ?
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( button ) & & pev - > groundentity = = m_liftEntity & & m_buttonPushTime + 1.0 < GetWorldTime ( ) & & m_liftEntity - > v . velocity . z = = 0.0 & & IsOnFloor ( ) )
2014-07-30 14:17:46 +04:00
{
m_pickupItem = button ;
m_pickupType = PICKUP_BUTTON ;
m_navTimeset = GetWorldTime ( ) ;
}
}
// is lift activated and bot is standing on it and lift is moving ?
if ( m_liftState = = LIFT_LOOKING_BUTTON_INSIDE | | m_liftState = = LIFT_ENTERING_IN | | m_liftState = = LIFT_WAIT_FOR_TEAMMATES | | m_liftState = = LIFT_WAITING_FOR )
{
2015-06-04 11:52:48 +03:00
if ( pev - > groundentity = = m_liftEntity & & m_liftEntity - > v . velocity . z ! = 0 & & IsOnFloor ( ) & & ( ( g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > flags & FLAG_LIFT ) | | ! IsEntityNull ( m_targetEntity ) ) )
2014-07-30 14:17:46 +04:00
{
m_liftState = LIFT_TRAVELING_BY ;
m_liftUsageTime = GetWorldTime ( ) + 14.0 ;
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
}
// bots is currently moving on lift
if ( m_liftState = = LIFT_TRAVELING_BY )
{
m_destOrigin = Vector ( m_liftTravelPos . x , m_liftTravelPos . y , pev - > origin . z ) ;
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
// need to find a button outside the lift
if ( m_liftState = = LIFT_LOOKING_BUTTON_OUTSIDE )
{
// lift is already used ?
bool liftUsed = false ;
// button has been pressed, lift should come
if ( m_buttonPushTime + 8.0 > = GetWorldTime ( ) )
{
if ( ( m_prevWptIndex [ 0 ] > = 0 ) & & ( m_prevWptIndex [ 0 ] < g_numWaypoints ) )
m_destOrigin = g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > origin ;
else
m_destOrigin = pev - > origin ;
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
else
{
edict_t * button = FindNearestButton ( STRING ( m_liftEntity - > v . targetname ) ) ;
// if we got a valid button entity
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( button ) )
2014-07-30 14:17:46 +04:00
{
// iterate though clients, and find if lift already used
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
2015-06-04 11:52:48 +03: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 ( ) | | IsEntityNull ( g_clients [ i ] . ent - > v . groundentity ) )
2014-07-30 14:17:46 +04:00
continue ;
if ( g_clients [ i ] . ent - > v . groundentity = = m_liftEntity )
{
liftUsed = true ;
break ;
}
}
// lift is currently used
if ( liftUsed )
{
if ( m_prevWptIndex [ 0 ] > = 0 & & m_prevWptIndex [ 0 ] < g_numWaypoints )
m_destOrigin = g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > origin ;
else
m_destOrigin = button - > v . origin ;
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 225 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
else
{
m_pickupItem = button ;
m_pickupType = PICKUP_BUTTON ;
m_liftState = LIFT_WAITING_FOR ;
m_navTimeset = GetWorldTime ( ) ;
m_liftUsageTime = GetWorldTime ( ) + 20.0 ;
}
}
else
{
m_liftState = LIFT_WAITING_FOR ;
m_liftUsageTime = GetWorldTime ( ) + 15.0 ;
}
}
}
// bot is waiting for lift
if ( m_liftState = = LIFT_WAITING_FOR )
{
if ( ( m_prevWptIndex [ 0 ] > = 0 ) & & ( m_prevWptIndex [ 0 ] < g_numWaypoints ) )
{
if ( ! ( g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > flags & FLAG_LIFT ) )
m_destOrigin = g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > origin ;
else if ( ( m_prevWptIndex [ 1 ] > = 0 ) & & ( m_prevWptIndex [ 0 ] < g_numWaypoints ) )
m_destOrigin = g_waypoint - > GetPath ( m_prevWptIndex [ 1 ] ) - > origin ;
}
if ( ( pev - > origin - m_destOrigin ) . GetLengthSquared ( ) < 100 )
{
m_moveSpeed = 0.0 ;
m_strafeSpeed = 0.0 ;
}
}
// if bot is waiting for lift, or going to it
if ( m_liftState = = LIFT_WAITING_FOR | | m_liftState = = LIFT_ENTERING_IN )
{
// bot fall down somewhere inside the lift's groove :)
if ( pev - > groundentity ! = m_liftEntity & & m_prevWptIndex [ 0 ] > = 0 & & m_prevWptIndex [ 0 ] < g_numWaypoints )
{
if ( ( g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > flags & FLAG_LIFT ) & & ( m_currentPath - > origin . z - pev - > origin . z ) > 50.0 & & ( g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > origin . z - pev - > origin . z ) > 50.0 )
{
m_liftState = LIFT_NO_NEARBY ;
m_liftEntity = NULL ;
m_liftUsageTime = 0.0 ;
DeleteSearchNodes ( ) ;
FindWaypoint ( ) ;
if ( m_prevWptIndex [ 2 ] > = 0 & & m_prevWptIndex [ 2 ] < g_numWaypoints )
FindShortestPath ( m_currentWaypointIndex , m_prevWptIndex [ 2 ] ) ;
return false ;
}
}
}
}
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( m_liftEntity ) & & ! ( m_currentPath - > flags & FLAG_LIFT ) )
2014-07-30 14:17:46 +04:00
{
if ( m_liftState = = LIFT_TRAVELING_BY )
{
m_liftState = LIFT_LEAVING ;
m_liftUsageTime = GetWorldTime ( ) + 10.0 ;
}
if ( m_liftState = = LIFT_LEAVING & & m_liftUsageTime < GetWorldTime ( ) & & pev - > groundentity ! = m_liftEntity )
{
m_liftState = LIFT_NO_NEARBY ;
m_liftUsageTime = 0.0 ;
m_liftEntity = NULL ;
}
}
if ( m_liftUsageTime < GetWorldTime ( ) & & m_liftUsageTime ! = 0.0 )
{
m_liftEntity = NULL ;
m_liftState = LIFT_NO_NEARBY ;
m_liftUsageTime = 0.0 ;
DeleteSearchNodes ( ) ;
if ( m_prevWptIndex [ 0 ] > = 0 & & m_prevWptIndex [ 0 ] < g_numWaypoints )
{
if ( ! ( g_waypoint - > GetPath ( m_prevWptIndex [ 0 ] ) - > flags & FLAG_LIFT ) )
ChangeWptIndex ( m_prevWptIndex [ 0 ] ) ;
else
FindWaypoint ( ) ;
}
else
FindWaypoint ( ) ;
return false ;
}
// check if we are going through a door...
TraceLine ( pev - > origin , m_waypointOrigin , true , GetEntity ( ) , & tr ) ;
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( tr . pHit ) & & IsEntityNull ( m_liftEntity ) & & strncmp ( STRING ( tr . pHit - > v . classname ) , " func_door " , 9 ) = = 0 )
2014-07-30 14:17:46 +04:00
{
// if the door is near enough...
if ( ( GetEntityOrigin ( tr . pHit ) - pev - > origin ) . GetLengthSquared ( ) < 2500 )
{
m_lastCollTime = GetWorldTime ( ) + 0.5 ; // don't consider being stuck
2015-06-09 15:45:34 +03:00
if ( Random . Long ( 1 , 100 ) < 50 )
2014-07-30 14:17:46 +04:00
MDLL_Use ( tr . pHit , GetEntity ( ) ) ; // also 'use' the door randomly
}
// make sure we are always facing the door when going through it
2015-06-07 19:43:16 +03:00
m_aimFlags & = ~ ( AIM_LAST_ENEMY | AIM_PREDICT_PATH ) ;
2014-07-30 14:17:46 +04:00
edict_t * button = FindNearestButton ( STRING ( tr . pHit - > v . targetname ) ) ;
// check if we got valid button
2015-06-04 11:52:48 +03:00
if ( ! IsEntityNull ( button ) )
2014-07-30 14:17:46 +04:00
{
m_pickupItem = button ;
m_pickupType = PICKUP_BUTTON ;
m_navTimeset = GetWorldTime ( ) ;
}
// if bot hits the door, then it opens, so wait a bit to let it open safely
if ( pev - > velocity . GetLength2D ( ) < 2 & & m_timeDoorOpen < GetWorldTime ( ) )
{
StartTask ( TASK_PAUSE , TASKPRI_PAUSE , - 1 , GetWorldTime ( ) + 1 , false ) ;
m_doorOpenAttempt + + ;
m_timeDoorOpen = GetWorldTime ( ) + 1.0 ; // retry in 1 sec until door is open
edict_t * ent = NULL ;
2015-06-20 11:45:59 +03:00
if ( m_doorOpenAttempt > 2 & & ! IsEntityNull ( ent = FIND_ENTITY_IN_SPHERE ( ent , pev - > origin , 256.0f ) ) )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
if ( IsValidPlayer ( ent ) & & IsAlive ( ent ) & & m_team ! = GetTeam ( ent ) & & GetWeaponPenetrationPower ( m_currentWeapon ) > 0 )
2014-07-30 14:17:46 +04:00
{
m_seeEnemyTime = GetWorldTime ( ) ;
m_states | = STATE_SUSPECT_ENEMY ;
m_aimFlags | = AIM_LAST_ENEMY ;
m_lastEnemy = ent ;
m_enemy = ent ;
m_lastEnemyOrigin = ent - > v . origin ;
}
2014-08-06 00:03:50 +04:00
else if ( IsValidPlayer ( ent ) & & IsAlive ( ent ) & & m_team = = GetTeam ( ent ) )
2014-07-30 14:17:46 +04:00
{
DeleteSearchNodes ( ) ;
ResetTasks ( ) ;
}
else if ( IsValidPlayer ( ent ) & & ( ! IsAlive ( ent ) | | ( ent - > v . deadflag & DEAD_DYING ) ) )
m_doorOpenAttempt = 0 ; // reset count
}
}
}
float desiredDistance = 0.0 ;
// initialize the radius for a special waypoint type, where the wpt is considered to be reached
if ( m_currentPath - > flags & FLAG_LIFT )
desiredDistance = 50 ;
else if ( ( pev - > flags & FL_DUCKING ) | | ( m_currentPath - > flags & FLAG_GOAL ) )
desiredDistance = 25 ;
else if ( m_currentPath - > flags & FLAG_LADDER )
desiredDistance = 15 ;
else if ( m_currentTravelFlags & PATHFLAG_JUMP )
desiredDistance = 0.0 ;
else
desiredDistance = m_currentPath - > radius ;
// check if waypoint has a special travelflag, so they need to be reached more precisely
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( m_currentPath - > connectionFlags [ i ] ! = 0 )
{
desiredDistance = 0 ;
break ;
}
}
// needs precise placement - check if we get past the point
2015-06-09 22:16:08 +03:00
if ( desiredDistance < 16.0 & & waypointDistance < 30 & & ( pev - > origin + ( pev - > velocity * m_frameInterval ) - m_waypointOrigin ) . GetLength ( ) > waypointDistance )
desiredDistance = waypointDistance + 1.0 ;
2014-07-30 14:17:46 +04:00
if ( waypointDistance < desiredDistance )
{
// Did we reach a destination Waypoint?
if ( GetTask ( ) - > data = = m_currentWaypointIndex )
{
// add goal values
if ( m_chosenGoalIndex ! = - 1 )
{
int waypointValue ;
int startIndex = m_chosenGoalIndex ;
int goalIndex = m_currentWaypointIndex ;
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-07-30 14:17:46 +04:00
{
waypointValue = ( g_experienceData + ( startIndex * g_numWaypoints ) + goalIndex ) - > team0Value ;
waypointValue + = static_cast < int > ( pev - > health * 0.5 ) ;
waypointValue + = static_cast < int > ( m_goalValue * 0.5 ) ;
if ( waypointValue < - MAX_GOAL_VALUE )
waypointValue = - MAX_GOAL_VALUE ;
else if ( waypointValue > MAX_GOAL_VALUE )
waypointValue = MAX_GOAL_VALUE ;
( g_experienceData + ( startIndex * g_numWaypoints ) + goalIndex ) - > team0Value = waypointValue ;
}
else
{
waypointValue = ( g_experienceData + ( startIndex * g_numWaypoints ) + goalIndex ) - > team1Value ;
waypointValue + = static_cast < int > ( pev - > health * 0.5 ) ;
waypointValue + = static_cast < int > ( m_goalValue * 0.5 ) ;
if ( waypointValue < - MAX_GOAL_VALUE )
waypointValue = - MAX_GOAL_VALUE ;
else if ( waypointValue > MAX_GOAL_VALUE )
waypointValue = MAX_GOAL_VALUE ;
( g_experienceData + ( startIndex * g_numWaypoints ) + goalIndex ) - > team1Value = waypointValue ;
}
}
return true ;
}
else if ( m_navNode = = NULL )
return false ;
2014-08-06 00:03:50 +04:00
if ( ( g_mapType & MAP_DE ) & & g_bombPlanted & & m_team = = TEAM_CF & & GetTaskId ( ) ! = TASK_ESCAPEFROMBOMB & & GetTask ( ) - > data ! = - 1 )
2014-07-30 14:17:46 +04:00
{
Vector bombOrigin = CheckBombAudible ( ) ;
// bot within 'hearable' bomb tick noises?
if ( bombOrigin ! = nullvec )
{
float distance = ( bombOrigin - g_waypoint - > GetPath ( GetTask ( ) - > data ) - > origin ) . GetLength ( ) ;
if ( distance > 512.0 )
g_waypoint - > SetGoalVisited ( GetTask ( ) - > data ) ; // doesn't hear so not a good goal
}
else
g_waypoint - > SetGoalVisited ( GetTask ( ) - > data ) ; // doesn't hear so not a good goal
}
HeadTowardWaypoint ( ) ; // do the actual movement checking
}
return false ;
}
void Bot : : FindShortestPath ( int srcIndex , int destIndex )
{
// this function finds the shortest path from source index to destination index
DeleteSearchNodes ( ) ;
m_chosenGoalIndex = srcIndex ;
m_goalValue = 0.0 ;
PathNode * node = new PathNode ;
node - > index = srcIndex ;
node - > next = NULL ;
m_navNodeStart = node ;
m_navNode = m_navNodeStart ;
while ( srcIndex ! = destIndex )
{
srcIndex = * ( g_waypoint - > m_pathMatrix + ( srcIndex * g_numWaypoints ) + destIndex ) ;
if ( srcIndex < 0 )
{
m_prevGoalIndex = - 1 ;
GetTask ( ) - > data = - 1 ;
return ;
}
node - > next = new PathNode ;
node = node - > next ;
if ( node = = NULL )
TerminateOnMalloc ( ) ;
node - > index = srcIndex ;
node - > next = NULL ;
}
}
2015-06-06 14:19:19 +03:00
// priority queue class (smallest item out first, hlsdk)
2014-07-30 14:17:46 +04:00
class PriorityQueue
{
private :
2015-06-06 14:19:19 +03:00
struct Node
2014-07-30 14:17:46 +04:00
{
2015-06-06 14:19:19 +03:00
int id ;
float pri ;
} ;
2014-07-30 14:17:46 +04:00
int m_size ;
int m_heapSize ;
2015-06-06 14:19:19 +03:00
Node * m_heap ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
public :
2014-08-05 22:26:16 +04:00
2015-06-06 14:19:19 +03:00
inline bool IsEmpty ( void )
{
return m_size = = 0 ;
}
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
inline PriorityQueue ( int initialSize )
2014-07-30 14:17:46 +04:00
{
2015-06-06 14:19:19 +03:00
m_size = 0 ;
m_heapSize = initialSize ;
m_heap = new Node [ m_heapSize ] ;
}
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
inline ~ PriorityQueue ( void )
{
delete [ ] m_heap ;
m_heap = NULL ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
// inserts a value into the priority queue
inline void Push ( int value , float pri )
{
if ( m_size > = m_heapSize )
{
m_heapSize + = 100 ;
2015-06-09 22:16:08 +03:00
m_heap = ( Node * ) realloc ( m_heap , sizeof ( Node ) * m_heapSize ) ;
2015-06-06 14:19:19 +03:00
}
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
m_heap [ m_size ] . pri = pri ;
m_heap [ m_size ] . id = value ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
int child = + + m_size - 1 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
while ( child )
{
int parent = ( child - 1 ) / 2 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
if ( m_heap [ parent ] . pri < = m_heap [ child ] . pri )
break ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
Node ref = m_heap [ child ] ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
m_heap [ child ] = m_heap [ parent ] ;
m_heap [ parent ] = ref ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
child = parent ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
}
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
// removes the smallest item from the priority queue
inline int Pop ( void )
{
int result = m_heap [ 0 ] . id ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
m_size - - ;
m_heap [ 0 ] = m_heap [ m_size ] ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
int parent = 0 ;
int child = ( 2 * parent ) + 1 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
Node ref = m_heap [ parent ] ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
while ( child < m_size )
{
int right = ( 2 * parent ) + 2 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
if ( right < m_size & & m_heap [ right ] . pri < m_heap [ child ] . pri )
child = right ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
if ( ref . pri < = m_heap [ child ] . pri )
break ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
m_heap [ parent ] = m_heap [ child ] ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
parent = child ;
child = ( 2 * parent ) + 1 ;
}
m_heap [ parent ] = ref ;
return result ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
} ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
2015-06-06 14:19:19 +03:00
float gfunctionKillsDistT ( int currentIndex , int parentIndex )
2014-07-30 14:17:46 +04:00
{
// least kills and number of nodes to goal for a team
2015-06-06 14:19:19 +03:00
if ( parentIndex = = - 1 )
return 0 ;
float cost = ( g_experienceData + ( currentIndex * g_numWaypoints ) + currentIndex ) - > team0Damage + g_highestDamageT ;
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
2014-08-05 21:04:43 +04:00
int neighbour = current - > index [ i ] ;
2014-07-30 14:17:46 +04:00
if ( neighbour ! = - 1 )
2015-06-06 14:19:19 +03:00
cost + = ( g_experienceData + ( neighbour * g_numWaypoints ) + neighbour ) - > team0Damage ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
if ( current - > flags & FLAG_CROUCH )
2015-06-06 14:19:19 +03:00
cost * = 1.5 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
return g_waypoint - > GetPathDistance ( parentIndex , currentIndex ) + cost ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
2015-06-06 14:19:19 +03:00
float gfunctionKillsDistCT ( int currentIndex , int parentIndex )
2014-07-30 14:17:46 +04:00
{
2014-08-05 21:04:43 +04:00
// least kills and number of nodes to goal for a team
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
if ( parentIndex = = - 1 )
return 0 ;
float cost = ( g_experienceData + ( currentIndex * g_numWaypoints ) + currentIndex ) - > team1Damage + g_highestDamageCT ;
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
2014-08-05 21:04:43 +04:00
int neighbour = current - > index [ i ] ;
2014-07-30 14:17:46 +04:00
if ( neighbour ! = - 1 )
2015-06-06 14:19:19 +03:00
cost + = static_cast < int > ( ( g_experienceData + ( neighbour * g_numWaypoints ) + neighbour ) - > team1Damage ) ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
if ( current - > flags & FLAG_CROUCH )
2015-06-06 14:19:19 +03:00
cost * = 1.5 ;
2014-08-05 21:04:43 +04:00
2015-06-06 14:19:19 +03:00
return g_waypoint - > GetPathDistance ( parentIndex , currentIndex ) + cost ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float gfunctionKillsDistCTWithHostage ( int currentIndex , int parentIndex )
2014-07-30 14:17:46 +04:00
{
// least kills and number of nodes to goal for a team
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
if ( current - > flags & FLAG_NOHOSTAGE )
2011-01-05 23:28:48 +04:00
return 65355 ;
2014-08-05 21:04:43 +04:00
else if ( current - > flags & ( FLAG_CROUCH | FLAG_LADDER ) )
2015-06-06 14:19:19 +03:00
return gfunctionKillsDistCT ( currentIndex , parentIndex ) * 500 ;
2014-08-05 21:04:43 +04:00
return gfunctionKillsDistCT ( currentIndex , parentIndex ) ;
}
2015-06-06 14:19:19 +03:00
float gfunctionKillsT ( int currentIndex , int parentIndex )
2014-08-05 21:04:43 +04:00
{
// least kills to goal for a team
2015-06-06 14:19:19 +03:00
float cost = ( g_experienceData + ( currentIndex * g_numWaypoints ) + currentIndex ) - > team0Damage ;
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
2014-08-05 21:04:43 +04:00
int neighbour = current - > index [ i ] ;
2014-07-30 14:17:46 +04:00
if ( neighbour ! = - 1 )
2015-06-06 14:19:19 +03:00
cost + = ( g_experienceData + ( neighbour * g_numWaypoints ) + neighbour ) - > team0Damage ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
if ( current - > flags & FLAG_CROUCH )
2015-06-06 14:19:19 +03:00
cost * = 1.5 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
return cost ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float gfunctionKillsCT ( int currentIndex , int parentIndex )
2014-07-30 14:17:46 +04:00
{
// least kills to goal for a team
2015-06-06 14:19:19 +03:00
if ( parentIndex = = - 1 )
return 0 ;
float cost = ( g_experienceData + ( currentIndex * g_numWaypoints ) + currentIndex ) - > team1Damage ;
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
2014-08-05 21:04:43 +04:00
int neighbour = current - > index [ i ] ;
2014-07-30 14:17:46 +04:00
if ( neighbour ! = - 1 )
2015-06-06 14:19:19 +03:00
cost + = ( g_experienceData + ( neighbour * g_numWaypoints ) + neighbour ) - > team1Damage ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
if ( current - > flags & FLAG_CROUCH )
2015-06-06 14:19:19 +03:00
cost * = 1.5 ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
return cost + 0.5f ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float gfunctionKillsCTWithHostage ( int currentIndex , int parentIndex )
2014-07-30 14:17:46 +04:00
{
2014-08-05 21:04:43 +04:00
// least kills to goal for a team
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
if ( parentIndex = = - 1 )
return 0 ;
2014-08-05 21:04:43 +04:00
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
if ( current - > flags & FLAG_NOHOSTAGE )
2014-08-05 22:26:16 +04:00
return 65355 ;
2014-08-05 21:04:43 +04:00
else if ( current - > flags & ( FLAG_CROUCH | FLAG_LADDER ) )
2015-06-06 14:19:19 +03:00
return gfunctionKillsDistCT ( currentIndex , parentIndex ) * 500 ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
return gfunctionKillsCT ( currentIndex , parentIndex ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float gfunctionPathDist ( int currentIndex , int parentIndex )
2014-08-05 22:26:16 +04:00
{
if ( parentIndex = = - 1 )
return 0 ;
Path * parent = g_waypoint - > GetPath ( parentIndex ) ;
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( parent - > index [ i ] = = currentIndex )
{
// we don't like ladder or crouch point
if ( current - > flags & ( FLAG_CROUCH | FLAG_LADDER ) )
return parent - > distances [ i ] * 1.5 ;
return parent - > distances [ i ] ;
}
}
return 65355 ;
}
2015-06-06 14:19:19 +03:00
float gfunctionPathDistWithHostage ( int currentIndex , int parentIndex )
2014-08-05 22:26:16 +04:00
{
Path * current = g_waypoint - > GetPath ( currentIndex ) ;
if ( current - > flags & FLAG_NOHOSTAGE )
return 65355 ;
2015-06-06 14:19:19 +03:00
2014-08-05 22:26:16 +04:00
else if ( current - > flags & ( FLAG_CROUCH | FLAG_LADDER ) )
2015-06-06 14:19:19 +03:00
return gfunctionPathDist ( currentIndex , parentIndex ) * 500 ;
2014-08-05 22:26:16 +04:00
return gfunctionPathDist ( currentIndex , parentIndex ) ;
}
2015-06-06 14:19:19 +03:00
float hfunctionSquareDist ( int index , int , int goalIndex )
2014-07-30 14:17:46 +04:00
{
2014-08-05 21:04:43 +04:00
// square distance heuristic
2011-01-05 23:28:48 +04:00
Path * start = g_waypoint - > GetPath ( index ) ;
2014-08-05 21:04:43 +04:00
Path * goal = g_waypoint - > GetPath ( goalIndex ) ;
2014-07-30 14:17:46 +04:00
2015-06-07 23:06:23 +03:00
#if 0
2011-01-05 23:28:48 +04:00
float deltaX = fabsf ( start - > origin . x - goal - > origin . x ) ;
float deltaY = fabsf ( start - > origin . y - goal - > origin . y ) ;
float deltaZ = fabsf ( start - > origin . z - goal - > origin . z ) ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
return static_cast < unsigned int > ( deltaX + deltaY + deltaZ ) ;
2015-06-07 23:06:23 +03:00
# else
float xDist = fabsf ( start - > origin . x - goal - > origin . x ) ;
float yDist = fabsf ( start - > origin . y - goal - > origin . y ) ;
float zDist = fabsf ( start - > origin . z - goal - > origin . z ) ;
if ( xDist > yDist )
return 1.4 * yDist + ( xDist - yDist ) + zDist ;
return 1.4 * xDist + ( yDist - xDist ) + zDist ;
# endif
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float hfunctionSquareDistWithHostage ( int index , int startIndex , int goalIndex )
2014-07-30 14:17:46 +04:00
{
2014-08-05 21:04:43 +04:00
// square distance heuristic with hostages
if ( g_waypoint - > GetPath ( startIndex ) - > flags & FLAG_NOHOSTAGE )
2015-06-06 14:19:19 +03:00
return 65355 ;
2014-08-05 21:04:43 +04:00
2011-01-05 23:28:48 +04:00
return hfunctionSquareDist ( index , startIndex , goalIndex ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float hfunctionNone ( int index , int startIndex , int goalIndex )
2014-07-30 14:17:46 +04:00
{
2011-01-05 23:28:48 +04:00
return hfunctionSquareDist ( index , startIndex , goalIndex ) / ( 128 * 10 ) ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
float hfunctionNumberNodes ( int index , int startIndex , int goalIndex )
2014-08-05 22:26:16 +04:00
{
2011-01-05 23:28:48 +04:00
return hfunctionSquareDist ( index , startIndex , goalIndex ) / 128 * g_highestKills ;
2014-08-05 22:26:16 +04:00
}
2014-07-30 14:17:46 +04:00
void Bot : : FindPath ( int srcIndex , int destIndex , unsigned char pathType )
{
// this function finds a path from srcIndex to destIndex
if ( srcIndex > g_numWaypoints - 1 | | srcIndex < 0 )
{
AddLogEntry ( true , LL_ERROR , " Pathfinder source path index not valid (%d) " , srcIndex ) ;
return ;
}
if ( destIndex > g_numWaypoints - 1 | | destIndex < 0 )
{
AddLogEntry ( true , LL_ERROR , " Pathfinder destination path index not valid (%d) " , destIndex ) ;
return ;
}
DeleteSearchNodes ( ) ;
m_chosenGoalIndex = srcIndex ;
m_goalValue = 0.0 ;
// A* Stuff
2015-06-06 14:19:19 +03:00
enum AStarState { OPEN , CLOSED , NEW } ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
struct AStar
2014-07-30 14:17:46 +04:00
{
2015-06-06 14:19:19 +03:00
float g ;
float f ;
2014-07-30 14:17:46 +04:00
short parentIndex ;
2015-06-06 14:19:19 +03:00
AStarState state ;
2014-07-30 14:17:46 +04:00
} astar [ MAX_WAYPOINTS ] ;
2015-06-06 14:19:19 +03:00
PriorityQueue openList ( MAX_WAYPOINTS * 0.5 ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < MAX_WAYPOINTS ; i + + )
{
astar [ i ] . g = 0 ;
astar [ i ] . f = 0 ;
astar [ i ] . parentIndex = - 1 ;
astar [ i ] . state = NEW ;
}
2015-06-06 14:19:19 +03:00
float ( * gcalc ) ( int , int ) = NULL ;
float ( * hcalc ) ( int , int , int ) = NULL ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
switch ( pathType )
2014-07-30 14:17:46 +04:00
{
2014-08-05 21:04:43 +04:00
case 0 :
if ( ( g_mapType & MAP_CS ) & & HasHostage ( ) )
{
2014-08-05 22:26:16 +04:00
gcalc = gfunctionPathDistWithHostage ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionSquareDistWithHostage ;
}
2014-07-30 14:17:46 +04:00
else
2014-08-05 21:04:43 +04:00
{
2014-08-05 22:26:16 +04:00
gcalc = gfunctionPathDist ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionSquareDist ;
}
break ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
case 1 :
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-08-05 21:04:43 +04:00
{
2014-07-30 14:17:46 +04:00
gcalc = gfunctionKillsDistT ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionSquareDist ;
}
else if ( ( g_mapType & MAP_CS ) & & HasHostage ( ) )
{
gcalc = gfunctionKillsDistCTWithHostage ;
hcalc = hfunctionSquareDistWithHostage ;
}
2014-07-30 14:17:46 +04:00
else
2014-08-05 21:04:43 +04:00
{
2014-07-30 14:17:46 +04:00
gcalc = gfunctionKillsDistCT ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionSquareDist ;
}
break ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
case 2 :
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-08-05 21:04:43 +04:00
{
2014-07-30 14:17:46 +04:00
gcalc = gfunctionKillsT ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionNone ;
}
else if ( ( g_mapType & MAP_CS ) & & HasHostage ( ) )
{
gcalc = gfunctionKillsDistCTWithHostage ;
hcalc = hfunctionNone ;
}
2014-07-30 14:17:46 +04:00
else
2014-08-05 21:04:43 +04:00
{
2014-07-30 14:17:46 +04:00
gcalc = gfunctionKillsCT ;
2014-08-05 21:04:43 +04:00
hcalc = hfunctionNone ;
}
break ;
2014-07-30 14:17:46 +04:00
}
2015-06-06 14:19:19 +03:00
2014-07-30 14:17:46 +04:00
// put start node into open list
2014-08-05 21:04:43 +04:00
astar [ srcIndex ] . g = gcalc ( srcIndex , - 1 ) ;
2011-01-05 23:28:48 +04:00
astar [ srcIndex ] . f = astar [ srcIndex ] . g + hcalc ( srcIndex , srcIndex , destIndex ) ;
2014-07-30 14:17:46 +04:00
astar [ srcIndex ] . state = OPEN ;
2015-06-06 14:19:19 +03:00
openList . Push ( srcIndex , astar [ srcIndex ] . g ) ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
while ( ! openList . IsEmpty ( ) )
2014-07-30 14:17:46 +04:00
{
// remove the first node from the open list
2015-06-06 14:19:19 +03:00
int currentIndex = openList . Pop ( ) ;
2014-07-30 14:17:46 +04:00
// is the current node the goal node?
if ( currentIndex = = destIndex )
{
// build the complete path
m_navNode = NULL ;
do
{
PathNode * path = new PathNode ;
path - > index = currentIndex ;
path - > next = m_navNode ;
m_navNode = path ;
currentIndex = astar [ currentIndex ] . parentIndex ;
} while ( currentIndex ! = - 1 ) ;
m_navNodeStart = m_navNode ;
return ;
}
if ( astar [ currentIndex ] . state ! = OPEN )
continue ;
// put current node into CLOSED list
astar [ currentIndex ] . state = CLOSED ;
// now expand the current node
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
2014-08-05 21:04:43 +04:00
int currentChild = g_waypoint - > GetPath ( currentIndex ) - > index [ i ] ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
if ( currentChild = = - 1 )
2014-07-30 14:17:46 +04:00
continue ;
// calculate the F value as F = G + H
2015-06-06 14:19:19 +03:00
float g = astar [ currentIndex ] . g + gcalc ( currentChild , currentIndex ) ;
float h = hcalc ( currentChild , srcIndex , destIndex ) ;
float f = g + h ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
if ( astar [ currentChild ] . state = = NEW | | astar [ currentChild ] . f > f )
2014-07-30 14:17:46 +04:00
{
// put the current child into open list
2014-08-05 21:04:43 +04:00
astar [ currentChild ] . parentIndex = currentIndex ;
astar [ currentChild ] . state = OPEN ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
astar [ currentChild ] . g = g ;
astar [ currentChild ] . f = f ;
2014-07-30 14:17:46 +04:00
2015-06-06 14:19:19 +03:00
openList . Push ( currentChild , g ) ;
2014-07-30 14:17:46 +04:00
}
}
}
FindShortestPath ( srcIndex , destIndex ) ; // A* found no path, try floyd pathfinder instead
}
void Bot : : DeleteSearchNodes ( void )
{
PathNode * deletingNode = NULL ;
PathNode * node = m_navNodeStart ;
while ( node ! = NULL )
{
deletingNode = node - > next ;
delete node ;
node = deletingNode ;
}
m_navNodeStart = NULL ;
m_navNode = NULL ;
m_chosenGoalIndex = - 1 ;
}
2015-06-10 23:41:55 +03:00
int Bot : : GetAimingWaypoint ( const Vector & to )
2014-07-30 14:17:46 +04:00
{
// return the most distant waypoint which is seen from the Bot to the Target and is within count
if ( m_currentWaypointIndex = = - 1 )
ChangeWptIndex ( g_waypoint - > FindNearest ( pev - > origin ) ) ;
int srcIndex = m_currentWaypointIndex ;
2015-06-10 23:41:55 +03:00
int destIndex = g_waypoint - > FindNearest ( to ) ;
2014-07-30 14:17:46 +04:00
int bestIndex = srcIndex ;
PathNode * node = new PathNode ;
node - > index = destIndex ;
node - > next = NULL ;
PathNode * startNode = node ;
while ( destIndex ! = srcIndex )
{
destIndex = * ( g_waypoint - > m_pathMatrix + ( destIndex * g_numWaypoints ) + srcIndex ) ;
if ( destIndex < 0 )
break ;
node - > next = new PathNode ;
node = node - > next ;
if ( node = = NULL )
TerminateOnMalloc ( ) ;
node - > index = destIndex ;
node - > next = NULL ;
if ( g_waypoint - > IsVisible ( m_currentWaypointIndex , destIndex ) )
{
bestIndex = destIndex ;
break ;
}
}
while ( startNode ! = NULL )
{
node = startNode - > next ;
delete startNode ;
startNode = node ;
}
return bestIndex ;
}
bool Bot : : FindWaypoint ( void )
{
// this function find a waypoint in the near of the bot if bot had lost his path of pathfinder needs
// to be restarted over again.
int waypointIndeces [ 3 ] , coveredWaypoint = - 1 , i = 0 ;
float reachDistances [ 3 ] ;
// nullify all waypoint search stuff
2015-06-04 11:52:48 +03:00
for ( i = 0 ; i < 3 ; i + + )
2014-07-30 14:17:46 +04:00
{
waypointIndeces [ i ] = - 1 ;
reachDistances [ i ] = 9999.0 ;
}
// do main search loop
2015-06-04 11:52:48 +03:00
for ( i = 0 ; i < g_numWaypoints ; i + + )
2014-07-30 14:17:46 +04:00
{
// ignore current waypoint and previous recent waypoints...
if ( i = = m_currentWaypointIndex | | i = = m_prevWptIndex [ 0 ] | | i = = m_prevWptIndex [ 1 ] | | i = = m_prevWptIndex [ 2 ] | | i = = m_prevWptIndex [ 3 ] | | i = = m_prevWptIndex [ 4 ] )
continue ;
2015-06-07 19:43:16 +03:00
if ( ( g_mapType & MAP_CS ) & & HasHostage ( ) & & ( g_waypoint - > GetPath ( i ) - > flags & FLAG_NOHOSTAGE ) )
continue ;
2014-07-30 14:17:46 +04:00
// ignore non-reacheable waypoints...
if ( ! g_waypoint - > Reachable ( this , i ) )
continue ;
// check if waypoint is already used by another bot...
2015-06-04 11:52:48 +03:00
if ( IsPointOccupied ( i ) )
2014-07-30 14:17:46 +04:00
{
coveredWaypoint = i ;
continue ;
}
// now pick 1-2 random waypoints that near the bot
float distance = ( g_waypoint - > GetPath ( i ) - > origin - pev - > origin ) . GetLengthSquared ( ) ;
// now fill the waypoint list
for ( int j = 0 ; j < 3 ; j + + )
{
if ( distance < reachDistances [ j ] )
{
waypointIndeces [ j ] = i ;
reachDistances [ j ] = distance ;
}
}
}
// now pick random one from choosen
if ( waypointIndeces [ 2 ] ! = - 1 )
2015-06-09 15:45:34 +03:00
i = Random . Long ( 0 , 2 ) ;
2014-07-30 14:17:46 +04:00
else if ( waypointIndeces [ 1 ] ! = - 1 )
2015-06-09 15:45:34 +03:00
i = Random . Long ( 0 , 1 ) ;
2014-07-30 14:17:46 +04:00
else if ( waypointIndeces [ 0 ] ! = - 1 )
2015-06-09 15:45:34 +03:00
i = Random . Long ( 0 , 0 ) ;
2014-07-30 14:17:46 +04:00
else if ( coveredWaypoint ! = - 1 )
2015-06-07 19:43:16 +03:00
{
i = 0 ;
waypointIndeces [ i ] = coveredWaypoint ;
}
2014-07-30 14:17:46 +04:00
else
{
2015-06-07 19:43:16 +03:00
i = 0 ;
2014-07-30 14:17:46 +04:00
Array < int > found ;
2015-06-07 23:06:23 +03:00
g_waypoint - > FindInRadius ( found , 256.0f , pev - > origin ) ;
2014-07-30 14:17:46 +04:00
if ( ! found . IsEmpty ( ) )
2015-06-07 23:06:23 +03:00
{
bool gotId = false ;
int random = found . GetRandomElement ( ) ; ;
while ( ! found . IsEmpty ( ) )
{
int index = found . Pop ( ) ;
if ( ! g_waypoint - > Reachable ( this , index ) )
continue ;
waypointIndeces [ i ] = index ;
gotId = true ;
break ;
}
if ( ! gotId )
waypointIndeces [ i ] = random ;
}
2014-07-30 14:17:46 +04:00
else
2015-06-09 15:45:34 +03:00
waypointIndeces [ i ] = Random . Long ( 0 , g_numWaypoints - 1 ) ;
2014-07-30 14:17:46 +04:00
}
m_collideTime = GetWorldTime ( ) ;
2015-06-07 19:43:16 +03:00
ChangeWptIndex ( waypointIndeces [ i ] ) ;
2014-07-30 14:17:46 +04:00
return true ;
}
void Bot : : GetValidWaypoint ( void )
{
// checks if the last waypoint the bot was heading for is still valid
// if bot hasn't got a waypoint we need a new one anyway or if time to get there expired get new one as well
if ( m_currentWaypointIndex = = - 1 )
{
DeleteSearchNodes ( ) ;
FindWaypoint ( ) ;
m_waypointOrigin = m_currentPath - > origin ;
// FIXME: Do some error checks if we got a waypoint
}
2015-06-04 11:52:48 +03:00
else if ( m_navTimeset + GetEstimatedReachTime ( ) < GetWorldTime ( ) & & IsEntityNull ( m_enemy ) )
2014-07-30 14:17:46 +04:00
{
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-07-30 14:17:46 +04:00
{
int value = ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + m_currentWaypointIndex ) - > team0Damage ;
value + = 100 ;
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + m_currentWaypointIndex ) - > team0Damage = static_cast < unsigned short > ( value ) ;
// affect nearby connected with victim waypoints
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( ( m_currentPath - > index [ i ] > - 1 ) & & ( m_currentPath - > index [ i ] < g_numWaypoints ) )
{
value = ( g_experienceData + ( m_currentPath - > index [ i ] * g_numWaypoints ) + m_currentPath - > index [ i ] ) - > team0Damage ;
value + = 2 ;
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
( g_experienceData + ( m_currentPath - > index [ i ] * g_numWaypoints ) + m_currentPath - > index [ i ] ) - > team0Damage = static_cast < unsigned short > ( value ) ;
}
}
}
else
{
int value = ( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + m_currentWaypointIndex ) - > team1Damage ;
value + = 100 ;
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
( g_experienceData + ( m_currentWaypointIndex * g_numWaypoints ) + m_currentWaypointIndex ) - > team1Damage = static_cast < unsigned short > ( value ) ;
// affect nearby connected with victim waypoints
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( ( m_currentPath - > index [ i ] > - 1 ) & & ( m_currentPath - > index [ i ] < g_numWaypoints ) )
{
value = ( g_experienceData + ( m_currentPath - > index [ i ] * g_numWaypoints ) + m_currentPath - > index [ i ] ) - > team1Damage ;
value + = 2 ;
if ( value > MAX_DAMAGE_VALUE )
value = MAX_DAMAGE_VALUE ;
( g_experienceData + ( m_currentPath - > index [ i ] * g_numWaypoints ) + m_currentPath - > index [ i ] ) - > team1Damage = static_cast < unsigned short > ( value ) ;
}
}
}
DeleteSearchNodes ( ) ;
2015-06-09 22:16:08 +03:00
if ( m_goalFailed > 1 )
{
int newGoal = FindGoal ( ) ;
m_prevGoalIndex = newGoal ;
m_chosenGoalIndex = newGoal ;
// remember index
GetTask ( ) - > data = newGoal ;
2015-06-11 23:22:50 +03:00
// do path finding if it's not the current waypoint
2015-06-09 22:16:08 +03:00
if ( newGoal ! = m_currentWaypointIndex )
FindPath ( m_currentWaypointIndex , newGoal , m_pathType ) ;
m_goalFailed = 0 ;
}
else
{
FindWaypoint ( ) ;
m_goalFailed + + ;
}
2014-07-30 14:17:46 +04:00
m_waypointOrigin = m_currentPath - > origin ;
}
}
void Bot : : ChangeWptIndex ( int waypointIndex )
{
2015-06-09 22:16:08 +03:00
if ( waypointIndex = = - 1 )
return ;
2014-07-30 14:17:46 +04:00
m_prevWptIndex [ 4 ] = m_prevWptIndex [ 3 ] ;
m_prevWptIndex [ 3 ] = m_prevWptIndex [ 2 ] ;
m_prevWptIndex [ 2 ] = m_prevWptIndex [ 1 ] ;
2015-06-04 11:52:48 +03:00
m_prevWptIndex [ 0 ] = m_currentWaypointIndex ;
2014-07-30 14:17:46 +04:00
m_currentWaypointIndex = waypointIndex ;
m_navTimeset = GetWorldTime ( ) ;
m_currentPath = g_waypoint - > GetPath ( m_currentWaypointIndex ) ;
2015-06-09 22:16:08 +03:00
m_waypointFlags = m_currentPath - > flags ;
2014-07-30 14:17:46 +04:00
}
int Bot : : ChooseBombWaypoint ( void )
{
// this function finds the best goal (bomb) waypoint for CTs when searching for a planted bomb.
2015-06-09 22:16:08 +03:00
Array < int > goals = g_waypoint - > m_goalPoints ;
2014-07-30 14:17:46 +04:00
if ( goals . IsEmpty ( ) )
2015-06-09 15:45:34 +03:00
return Random . Long ( 0 , g_numWaypoints - 1 ) ; // reliability check
2014-07-30 14:17:46 +04:00
Vector bombOrigin = CheckBombAudible ( ) ;
// if bomb returns no valid vector, return the current bot pos
if ( bombOrigin = = nullvec )
bombOrigin = pev - > origin ;
int goal = 0 , count = 0 ;
float lastDistance = FLT_MAX ;
// find nearest goal waypoint either to bomb (if "heard" or player)
IterateArray ( goals , i )
{
float distance = ( g_waypoint - > GetPath ( goals [ i ] ) - > origin - bombOrigin ) . GetLengthSquared ( ) ;
// check if we got more close distance
if ( distance < lastDistance )
{
goal = goals [ i ] ;
2015-06-04 11:52:48 +03:00
lastDistance = distance ;
2014-07-30 14:17:46 +04:00
}
}
while ( g_waypoint - > IsGoalVisited ( goal ) )
{
goal = goals . GetRandomElement ( ) ;
if ( count + + > = goals . GetElementNumber ( ) )
break ;
}
return goal ;
}
2015-06-10 23:41:55 +03:00
int Bot : : FindDefendWaypoint ( const Vector & origin )
2014-07-30 14:17:46 +04:00
{
// this function tries to find a good position which has a line of sight to a position,
// provides enough cover point, and is far away from the defending position
TraceResult tr ;
int waypointIndex [ MAX_PATH_INDEX ] ;
int minDistance [ MAX_PATH_INDEX ] ;
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
waypointIndex [ i ] = - 1 ;
minDistance [ i ] = 128 ;
}
int posIndex = g_waypoint - > FindNearest ( origin ) ;
int srcIndex = g_waypoint - > FindNearest ( pev - > origin ) ;
// some of points not found, return random one
if ( srcIndex = = - 1 | | posIndex = = - 1 )
2015-06-09 15:45:34 +03:00
return Random . Long ( 0 , g_numWaypoints - 1 ) ;
2014-07-30 14:17:46 +04:00
for ( int i = 0 ; i < g_numWaypoints ; i + + ) // find the best waypoint now
{
// exclude ladder & current waypoints
2015-06-04 11:52:48 +03:00
if ( ( g_waypoint - > GetPath ( i ) - > flags & FLAG_LADDER ) | | i = = srcIndex | | ! g_waypoint - > IsVisible ( i , posIndex ) | | IsPointOccupied ( i ) )
2014-07-30 14:17:46 +04:00
continue ;
// use the 'real' pathfinding distances
int distances = g_waypoint - > GetPathDistance ( srcIndex , i ) ;
// skip wayponts with distance more than 1024 units
if ( distances > 1024.0f )
continue ;
TraceLine ( g_waypoint - > GetPath ( i ) - > origin , g_waypoint - > GetPath ( posIndex ) - > origin , true , true , GetEntity ( ) , & tr ) ;
// check if line not hit anything
if ( tr . flFraction ! = 1.0 )
continue ;
for ( int j = 0 ; j < MAX_PATH_INDEX ; j + + )
{
if ( distances > minDistance [ j ] )
{
waypointIndex [ j ] = i ;
minDistance [ j ] = distances ;
break ;
}
}
}
// use statistic if we have them
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( waypointIndex [ i ] ! = - 1 )
{
Experience * exp = ( g_experienceData + ( waypointIndex [ i ] * g_numWaypoints ) + waypointIndex [ i ] ) ;
int experience = - 1 ;
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-07-30 14:17:46 +04:00
experience = exp - > team0Damage ;
else
experience = exp - > team1Damage ;
2014-08-06 00:03:50 +04:00
experience = ( experience * 100 ) / ( m_team = = TEAM_TF ? g_highestDamageT : g_highestDamageCT ) ;
2014-07-30 14:17:46 +04:00
minDistance [ i ] = ( experience * 100 ) / 8192 ;
minDistance [ i ] + = experience ;
}
}
bool isOrderChanged = false ;
// sort results waypoints for farest distance
do
{
isOrderChanged = false ;
// completely sort the data
for ( int i = 0 ; i < 3 & & waypointIndex [ i ] ! = - 1 & & waypointIndex [ i + 1 ] ! = - 1 & & minDistance [ i ] > minDistance [ i + 1 ] ; i + + )
{
int index = waypointIndex [ i ] ;
waypointIndex [ i ] = waypointIndex [ i + 1 ] ;
waypointIndex [ i + 1 ] = index ;
index = minDistance [ i ] ;
minDistance [ i ] = minDistance [ i + 1 ] ;
minDistance [ i + 1 ] = index ;
isOrderChanged = true ;
}
} while ( isOrderChanged ) ;
if ( waypointIndex [ 0 ] = = - 1 )
{
Array < int > found ;
g_waypoint - > FindInRadius ( found , 1024.0f , origin ) ;
if ( found . IsEmpty ( ) )
2015-06-09 15:45:34 +03:00
return Random . Long ( 0 , g_numWaypoints - 1 ) ; // most worst case, since there a evil error in waypoints
2014-07-30 14:17:46 +04:00
return found . GetRandomElement ( ) ;
}
int index = 0 ;
for ( ; index < MAX_PATH_INDEX ; index + + )
{
if ( waypointIndex [ index ] = = - 1 )
break ;
}
2015-06-09 15:45:34 +03:00
return waypointIndex [ Random . Long ( 0 , ( index - 1 ) / 2 ) ] ;
2014-07-30 14:17:46 +04:00
}
int Bot : : FindCoverWaypoint ( float maxDistance )
{
// this function tries to find a good cover waypoint if bot wants to hide
// do not move to a position near to the enemy
if ( maxDistance > ( m_lastEnemyOrigin - pev - > origin ) . GetLength ( ) )
maxDistance = ( m_lastEnemyOrigin - pev - > origin ) . GetLength ( ) ;
if ( maxDistance < 300.0 )
maxDistance = 300.0 ;
int srcIndex = m_currentWaypointIndex ;
int enemyIndex = g_waypoint - > FindNearest ( m_lastEnemyOrigin ) ;
Array < int > enemyIndices ;
int waypointIndex [ MAX_PATH_INDEX ] ;
int minDistance [ MAX_PATH_INDEX ] ;
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
waypointIndex [ i ] = - 1 ;
minDistance [ i ] = static_cast < int > ( maxDistance ) ;
}
if ( enemyIndex = = - 1 )
return - 1 ;
// now get enemies neigbouring points
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( g_waypoint - > GetPath ( enemyIndex ) - > index [ i ] ! = - 1 )
enemyIndices . Push ( g_waypoint - > GetPath ( enemyIndex ) - > index [ i ] ) ;
}
// ensure we're on valid point
ChangeWptIndex ( srcIndex ) ;
// find the best waypoint now
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
// exclude ladder, current waypoints and waypoints seen by the enemy
if ( ( g_waypoint - > GetPath ( i ) - > flags & FLAG_LADDER ) | | i = = srcIndex | | g_waypoint - > IsVisible ( enemyIndex , i ) )
continue ;
bool neighbourVisible = false ; // now check neighbour waypoints for visibility
IterateArray ( enemyIndices , j )
{
if ( g_waypoint - > IsVisible ( enemyIndices [ j ] , i ) )
{
neighbourVisible = true ;
break ;
}
}
// skip visible points
if ( neighbourVisible )
continue ;
// use the 'real' pathfinding distances
int distances = g_waypoint - > GetPathDistance ( srcIndex , i ) ;
int enemyDistance = g_waypoint - > GetPathDistance ( enemyIndex , i ) ;
if ( distances > = enemyDistance )
continue ;
for ( int j = 0 ; j < MAX_PATH_INDEX ; j + + )
{
if ( distances < minDistance [ j ] )
{
waypointIndex [ j ] = i ;
minDistance [ j ] = distances ;
break ;
}
}
}
// use statistic if we have them
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( waypointIndex [ i ] ! = - 1 )
{
Experience * exp = ( g_experienceData + ( waypointIndex [ i ] * g_numWaypoints ) + waypointIndex [ i ] ) ;
int experience = - 1 ;
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-07-30 14:17:46 +04:00
experience = exp - > team0Damage ;
else
experience = exp - > team1Damage ;
experience = ( experience * 100 ) / MAX_DAMAGE_VALUE ;
minDistance [ i ] = ( experience * 100 ) / 8192 ;
minDistance [ i ] + = experience ;
}
}
bool isOrderChanged ;
// sort resulting waypoints for nearest distance
do
{
isOrderChanged = false ;
for ( int i = 0 ; i < 3 & & waypointIndex [ i ] ! = - 1 & & waypointIndex [ i + 1 ] ! = - 1 & & minDistance [ i ] > minDistance [ i + 1 ] ; i + + )
{
int index = waypointIndex [ i ] ;
waypointIndex [ i ] = waypointIndex [ i + 1 ] ;
waypointIndex [ i + 1 ] = index ;
index = minDistance [ i ] ;
minDistance [ i ] = minDistance [ i + 1 ] ;
minDistance [ i + 1 ] = index ;
isOrderChanged = true ;
}
} while ( isOrderChanged ) ;
TraceResult tr ;
// take the first one which isn't spotted by the enemy
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( waypointIndex [ i ] ! = - 1 )
{
TraceLine ( m_lastEnemyOrigin + Vector ( 0 , 0 , 36 ) , g_waypoint - > GetPath ( waypointIndex [ i ] ) - > origin , true , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction < 1.0 )
return waypointIndex [ i ] ;
}
}
// if all are seen by the enemy, take the first one
if ( waypointIndex [ 0 ] ! = - 1 )
return waypointIndex [ 0 ] ;
return - 1 ; // do not use random points
}
bool Bot : : GetBestNextWaypoint ( void )
{
2015-06-11 16:53:21 +03:00
// this function does a realtime post processing of waypoints return from the
2014-07-30 14:17:46 +04:00
// pathfinder, to vary paths and find the best waypoint on our way
InternalAssert ( m_navNode ! = NULL ) ;
InternalAssert ( m_navNode - > next ! = NULL ) ;
2015-06-04 11:52:48 +03:00
if ( ! IsPointOccupied ( m_navNode - > index ) )
2014-07-30 14:17:46 +04:00
return false ;
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
int id = m_currentPath - > index [ i ] ;
if ( id ! = - 1 & & g_waypoint - > IsConnected ( id , m_navNode - > next - > index ) & & g_waypoint - > IsConnected ( m_currentWaypointIndex , id ) )
{
if ( g_waypoint - > GetPath ( id ) - > flags & FLAG_LADDER ) // don't use ladder waypoints as alternative
continue ;
2015-06-04 11:52:48 +03:00
if ( ! IsPointOccupied ( id ) )
2014-07-30 14:17:46 +04:00
{
m_navNode - > index = id ;
return true ;
}
}
}
return false ;
}
bool Bot : : HeadTowardWaypoint ( void )
{
// advances in our pathfinding list and sets the appropiate destination origins for this bot
GetValidWaypoint ( ) ; // check if old waypoints is still reliable
// no waypoints from pathfinding?
if ( m_navNode = = NULL )
return false ;
TraceResult tr ;
m_navNode = m_navNode - > next ; // advance in list
m_currentTravelFlags = 0 ; // reset travel flags (jumping etc)
// we're not at the end of the list?
if ( m_navNode ! = NULL )
{
// if in between a route, postprocess the waypoint (find better alternatives)...
if ( m_navNode ! = m_navNodeStart & & m_navNode - > next ! = NULL )
{
GetBestNextWaypoint ( ) ;
m_minSpeed = pev - > maxspeed ;
// only if we in normal task and bomb is not planted
2015-06-11 16:53:21 +03:00
if ( GetTaskId ( ) = = TASK_NORMAL & & g_timeRoundMid + 10.0f < GetWorldTime ( ) & & m_timeCamping + 30.0f < GetWorldTime ( ) & & ! g_bombPlanted & & m_personality ! = PERSONALITY_RUSHER & & ! m_hasC4 & & ! m_isVIP & & m_loosedBombWptIndex = = - 1 & & ! HasHostage ( ) )
2014-07-30 14:17:46 +04:00
{
m_campButtons = 0 ;
int waypoint = m_navNode - > next - > index ;
2011-01-05 23:28:48 +04:00
float kills = 0 ;
2014-07-30 14:17:46 +04:00
2014-08-06 00:03:50 +04:00
if ( m_team = = TEAM_TF )
2014-08-05 21:04:43 +04:00
kills = ( g_experienceData + ( waypoint * g_numWaypoints ) + waypoint ) - > team0Damage / g_highestDamageT ;
2014-07-30 14:17:46 +04:00
else
2014-08-05 21:04:43 +04:00
kills = ( g_experienceData + ( waypoint * g_numWaypoints ) + waypoint ) - > team1Damage / g_highestDamageCT ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
// if damage done higher than one
2015-06-11 16:53:21 +03:00
if ( kills > 0.15f & & g_timeRoundMid + 15.0f < GetWorldTime ( ) )
2015-06-04 11:52:48 +03:00
{
2015-06-08 22:55:57 +03:00
switch ( m_personality )
{
case PERSONALITY_NORMAL :
kills * = 0.33f ;
break ;
default :
kills * = 0.5f ;
break ;
}
2015-06-09 22:16:08 +03:00
if ( m_baseAgressionLevel < kills & & HasPrimaryWeapon ( ) )
2014-07-30 14:17:46 +04:00
{
2015-06-09 22:16:08 +03:00
StartTask ( TASK_CAMP , TASKPRI_CAMP , - 1 , GetWorldTime ( ) + Random . Float ( m_difficulty * 0.5 , m_difficulty ) * 5 , true ) ;
StartTask ( TASK_MOVETOPOSITION , TASKPRI_MOVETOPOSITION , FindDefendWaypoint ( g_waypoint - > GetPath ( waypoint ) - > origin ) , GetWorldTime ( ) + Random . Float ( 3.0f , 10.0f ) , true ) ;
2014-07-30 14:17:46 +04:00
}
}
2015-06-08 22:55:57 +03:00
else if ( g_botsCanPause & & ! IsOnLadder ( ) & & ! IsInWater ( ) & & ! m_currentTravelFlags & & IsOnFloor ( ) )
2014-07-30 14:17:46 +04:00
{
if ( static_cast < float > ( kills ) = = m_baseAgressionLevel )
m_campButtons | = IN_DUCK ;
2015-06-09 22:16:08 +03:00
else if ( Random . Long ( 1 , 100 ) > m_difficulty * 25 )
2014-07-30 14:17:46 +04:00
m_minSpeed = GetWalkSpeed ( ) ;
}
}
}
if ( m_navNode ! = NULL )
{
int destIndex = m_navNode - > index ;
// find out about connection flags
if ( m_currentWaypointIndex ! = - 1 )
{
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
if ( m_currentPath - > index [ i ] = = m_navNode - > index )
{
m_currentTravelFlags = m_currentPath - > connectionFlags [ i ] ;
m_desiredVelocity = m_currentPath - > connectionVelocity [ i ] ;
m_jumpFinished = false ;
break ;
}
}
// check if bot is going to jump
bool willJump = false ;
float jumpDistance = 0.0 ;
Vector src = nullvec ;
Vector destination = nullvec ;
// try to find out about future connection flags
if ( m_navNode - > next ! = NULL )
{
for ( int i = 0 ; i < MAX_PATH_INDEX ; i + + )
{
Path * path = g_waypoint - > GetPath ( m_navNode - > index ) ;
Path * next = g_waypoint - > GetPath ( m_navNode - > next - > index ) ;
if ( path - > index [ i ] = = m_navNode - > next - > index & & ( path - > connectionFlags [ i ] & PATHFLAG_JUMP ) )
{
src = path - > origin ;
destination = next - > origin ;
jumpDistance = ( path - > origin - next - > origin ) . GetLength ( ) ;
willJump = true ;
break ;
}
}
}
// is there a jump waypoint right ahead and do we need to draw out the light weapon ?
2015-06-04 11:52:48 +03:00
if ( willJump & & m_currentWeapon ! = WEAPON_KNIFE & & m_currentWeapon ! = WEAPON_SCOUT & & ! m_isReloading & & ! UsesPistol ( ) & & ( jumpDistance > 210 | | ( destination . z + 32.0f > src . z & & jumpDistance > 150.0f ) | | ( ( destination - src ) . GetLength2D ( ) < 60 & & jumpDistance > 60 ) ) & & IsEntityNull ( m_enemy ) )
2014-07-30 14:17:46 +04:00
SelectWeaponByName ( " weapon_knife " ) ; // draw out the knife if we needed
// bot not already on ladder but will be soon?
if ( ( g_waypoint - > GetPath ( destIndex ) - > flags & FLAG_LADDER ) & & ! IsOnLadder ( ) )
{
// get ladder waypoints used by other (first moving) bots
for ( int c = 0 ; c < GetMaxClients ( ) ; c + + )
{
Bot * otherBot = g_botManager - > GetBot ( c ) ;
// if another bot uses this ladder, wait 3 secs
if ( otherBot ! = NULL & & otherBot ! = this & & IsAlive ( otherBot - > GetEntity ( ) ) & & otherBot - > m_currentWaypointIndex = = m_navNode - > index )
{
StartTask ( TASK_PAUSE , TASKPRI_PAUSE , - 1 , GetWorldTime ( ) + 3.0 , false ) ;
return true ;
}
}
}
}
ChangeWptIndex ( destIndex ) ;
}
}
m_waypointOrigin = m_currentPath - > origin ;
// if wayzone radius non zero vary origin a bit depending on the body angles
if ( m_currentPath - > radius > 0.0f )
{
2015-06-09 15:45:34 +03:00
MakeVectors ( Vector ( pev - > angles . x , AngleNormalize ( pev - > angles . y + Random . Float ( - 90 , 90 ) ) , 0.0 ) ) ;
m_waypointOrigin = m_waypointOrigin + g_pGlobals - > v_forward * Random . Float ( 0 , m_currentPath - > radius ) ;
2014-07-30 14:17:46 +04:00
}
if ( IsOnLadder ( ) )
{
TraceLine ( Vector ( pev - > origin . x , pev - > origin . y , pev - > absmin . z ) , m_waypointOrigin , true , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction < 1.0 )
m_waypointOrigin = m_waypointOrigin + ( pev - > origin - m_waypointOrigin ) * 0.5 + Vector ( 0 , 0 , 32 ) ;
}
m_navTimeset = GetWorldTime ( ) ;
return true ;
}
bool Bot : : CantMoveForward ( const Vector & normal , TraceResult * tr )
{
// Checks if bot is blocked in his movement direction (excluding doors)
// use some TraceLines to determine if anything is blocking the current path of the bot.
Vector center = Vector ( 0 , pev - > angles . y , 0 ) ;
MakeVectors ( center ) ;
// first do a trace from the bot's eyes forward...
Vector src = EyePosition ( ) ;
Vector forward = src + normal * 24 ;
// trace from the bot's eyes straight forward...
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 )
{
if ( strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) = = 0 )
return false ;
return true ; // bot's head will hit something
}
// bot's head is clear, check at shoulder level...
// trace from the bot's shoulder left diagonal forward to the right shoulder...
src = EyePosition ( ) + Vector ( 0 , 0 , - 16 ) - g_pGlobals - > v_right * 16 ;
forward = EyePosition ( ) + Vector ( 0 , 0 , - 16 ) + g_pGlobals - > v_right * 16 + normal * 24 ;
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
// bot's head is clear, check at shoulder level...
// trace from the bot's shoulder right diagonal forward to the left shoulder...
src = EyePosition ( ) + Vector ( 0 , 0 , - 16 ) + g_pGlobals - > v_right * 16 ;
forward = EyePosition ( ) + Vector ( 0 , 0 , - 16 ) - g_pGlobals - > v_right * 16 + normal * 24 ;
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
// now check below waist
if ( pev - > flags & FL_DUCKING )
{
src = pev - > origin + Vector ( 0 , 0 , - 19 + 19 ) ;
forward = src + Vector ( 0 , 0 , 10 ) + normal * 24 ;
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
src = pev - > origin ;
forward = src + normal * 24 ;
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
}
else
{
// trace from the left waist to the right forward waist pos
src = pev - > origin + Vector ( 0 , 0 , - 17 ) - g_pGlobals - > v_right * 16 ;
forward = pev - > origin + Vector ( 0 , 0 , - 17 ) + g_pGlobals - > v_right * 16 + normal * 24 ;
// trace from the bot's waist straight forward...
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
// trace from the left waist to the right forward waist pos
src = pev - > origin + Vector ( 0 , 0 , - 17 ) + g_pGlobals - > v_right * 16 ;
forward = pev - > origin + Vector ( 0 , 0 , - 17 ) - g_pGlobals - > v_right * 16 + normal * 24 ;
TraceLine ( src , forward , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr - > pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
}
return false ; // bot can move forward, return false
}
bool Bot : : CanStrafeLeft ( TraceResult * tr )
{
// this function checks if bot can move sideways
MakeVectors ( pev - > v_angle ) ;
Vector src = pev - > origin ;
Vector left = src - g_pGlobals - > v_right * - 40 ;
// trace from the bot's waist straight left...
TraceLine ( src , left , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 )
return false ; // bot's body will hit something
src = left ;
left = left + g_pGlobals - > v_forward * 40 ;
// trace from the strafe pos straight forward...
TraceLine ( src , left , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 )
return false ; // bot's body will hit something
return true ;
}
bool Bot : : CanStrafeRight ( TraceResult * tr )
{
// this function checks if bot can move sideways
MakeVectors ( pev - > v_angle ) ;
Vector src = pev - > origin ;
Vector right = src + g_pGlobals - > v_right * 40 ;
// trace from the bot's waist straight right...
TraceLine ( src , right , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 )
return false ; // bot's body will hit something
src = right ;
right = right + g_pGlobals - > v_forward * 40 ;
// trace from the strafe pos straight forward...
TraceLine ( src , right , true , GetEntity ( ) , tr ) ;
// check if the trace hit something...
if ( tr - > flFraction < 1.0 )
return false ; // bot's body will hit something
return true ;
}
bool Bot : : CanJumpUp ( const Vector & normal )
{
// this function check if bot can jump over some obstacle
TraceResult tr ;
// can't jump if not on ground and not on ladder/swimming
if ( ! IsOnFloor ( ) & & ( IsOnLadder ( ) | | ! IsInWater ( ) ) )
return false ;
// convert current view angle to vectors for traceline math...
Vector jump = pev - > angles ;
jump . x = 0 ; // reset pitch to 0 (level horizontally)
jump . z = 0 ; // reset roll to 0 (straight up and down)
MakeVectors ( jump ) ;
// check for normal jump height first...
Vector src = pev - > origin + Vector ( 0 , 0 , - 36 + 45 ) ;
Vector dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction < 1.0 )
goto CheckDuckJump ;
else
{
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction < 1.0 )
return false ;
}
// now check same height to one side of the bot...
src = pev - > origin + g_pGlobals - > v_right * 16 + Vector ( 0 , 0 , - 36 + 45 ) ;
dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
goto CheckDuckJump ;
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now check same height on the other side of the bot...
src = pev - > origin + ( - g_pGlobals - > v_right * 16 ) + Vector ( 0 , 0 , - 36 + 45 ) ;
dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
goto CheckDuckJump ;
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
return tr . flFraction > 1.0 ;
// here we check if a duck jump would work...
CheckDuckJump :
// use center of the body first... maximum duck jump height is 62, so check one unit above that (63)
src = pev - > origin + Vector ( 0 , 0 , - 36 + 63 ) ;
dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
if ( tr . flFraction < 1.0 )
return false ;
else
{
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, check duckjump
if ( tr . flFraction < 1.0 )
return false ;
}
// now check same height to one side of the bot...
src = pev - > origin + g_pGlobals - > v_right * 16 + Vector ( 0 , 0 , - 36 + 63 ) ;
dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now check same height on the other side of the bot...
src = pev - > origin + ( - g_pGlobals - > v_right * 16 ) + Vector ( 0 , 0 , - 36 + 63 ) ;
dest = src + normal * 32 ;
// trace a line forward at maximum jump height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now trace from jump height upward to check for obstructions...
src = dest ;
dest . z = dest . z + 37 ;
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
return tr . flFraction > 1.0 ;
}
bool Bot : : CanDuckUnder ( const Vector & normal )
{
// this function check if bot can duck under obstacle
TraceResult tr ;
Vector baseHeight ;
// convert current view angle to vectors for TraceLine math...
Vector duck = pev - > angles ;
duck . x = 0 ; // reset pitch to 0 (level horizontally)
duck . z = 0 ; // reset roll to 0 (straight up and down)
MakeVectors ( duck ) ;
// use center of the body first...
if ( pev - > flags & FL_DUCKING )
baseHeight = pev - > origin + Vector ( 0 , 0 , - 17 ) ;
else
baseHeight = pev - > origin ;
Vector src = baseHeight ;
Vector dest = src + normal * 32 ;
// trace a line forward at duck height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now check same height to one side of the bot...
src = baseHeight + g_pGlobals - > v_right * 16 ;
dest = src + normal * 32 ;
// trace a line forward at duck height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
if ( tr . flFraction < 1.0 )
return false ;
// now check same height on the other side of the bot...
src = baseHeight + ( - g_pGlobals - > v_right * 16 ) ;
dest = src + normal * 32 ;
// trace a line forward at duck height...
TraceLine ( src , dest , true , GetEntity ( ) , & tr ) ;
// if trace hit something, return false
return tr . flFraction > 1.0 ;
}
bool Bot : : IsBlockedLeft ( void )
{
TraceResult tr ;
int direction = 48 ;
if ( m_moveSpeed < 0 )
direction = - 48 ;
MakeVectors ( pev - > angles ) ;
// do a trace to the left...
TraceLine ( pev - > origin , g_pGlobals - > v_forward * direction - g_pGlobals - > v_right * 48 , true , GetEntity ( ) , & tr ) ;
// check if the trace hit something...
if ( tr . flFraction < 1.0 & & strncmp ( " func_door " , STRING ( tr . pHit - > v . classname ) , 9 ) ! = 0 )
return true ; // bot's body will hit something
return false ;
}
bool Bot : : IsBlockedRight ( void )
{
TraceResult tr ;
int direction = 48 ;
if ( m_moveSpeed < 0 )
direction = - 48 ;
MakeVectors ( pev - > angles ) ;
// do a trace to the right...
TraceLine ( pev - > origin , pev - > origin + g_pGlobals - > v_forward * direction + g_pGlobals - > v_right * 48 , true , GetEntity ( ) , & tr ) ;
// check if the trace hit something...
if ( ( tr . flFraction < 1.0 ) & & ( strncmp ( " func_door " , STRING ( tr . pHit - > v . classname ) , 9 ) ! = 0 ) )
return true ; // bot's body will hit something
return false ;
}
bool Bot : : CheckWallOnLeft ( void )
{
TraceResult tr ;
MakeVectors ( pev - > angles ) ;
TraceLine ( pev - > origin , pev - > origin - g_pGlobals - > v_right * 40 , true , GetEntity ( ) , & tr ) ;
// check if the trace hit something...
if ( tr . flFraction < 1.0 )
return true ;
return false ;
}
bool Bot : : CheckWallOnRight ( void )
{
TraceResult tr ;
MakeVectors ( pev - > angles ) ;
// do a trace to the right...
TraceLine ( pev - > origin , pev - > origin + g_pGlobals - > v_right * 40 , true , GetEntity ( ) , & tr ) ;
// check if the trace hit something...
if ( tr . flFraction < 1.0 )
return true ;
return false ;
}
2015-06-10 23:41:55 +03:00
bool Bot : : IsDeadlyDrop ( const Vector & to )
2014-07-30 14:17:46 +04:00
{
// this function eturns if given location would hurt Bot with falling damage
Vector botPos = pev - > origin ;
TraceResult tr ;
2015-06-10 23:41:55 +03:00
Vector move ( ( to - botPos ) . ToYaw ( ) , 0 , 0 ) ;
2014-07-30 14:17:46 +04:00
MakeVectors ( move ) ;
2015-06-10 23:41:55 +03:00
Vector direction = ( to - botPos ) . Normalize ( ) ; // 1 unit long
2014-07-30 14:17:46 +04:00
Vector check = botPos ;
Vector down = botPos ;
down . z = down . z - 1000.0 ; // straight down 1000 units
TraceHull ( check , down , true , head_hull , GetEntity ( ) , & tr ) ;
if ( tr . flFraction > 0.036 ) // We're not on ground anymore?
tr . flFraction = 0.036 ;
float height ;
float lastHeight = tr . flFraction * 1000.0 ; // height from ground
2015-06-10 23:41:55 +03:00
float distance = ( to - check ) . GetLength ( ) ; // distance from goal
2014-07-30 14:17:46 +04:00
while ( distance > 16.0 )
{
check = check + direction * 16.0 ; // move 10 units closer to the goal...
down = check ;
down . z = down . z - 1000.0 ; // straight down 1000 units
TraceHull ( check , down , true , head_hull , GetEntity ( ) , & tr ) ;
if ( tr . fStartSolid ) // Wall blocking?
return false ;
height = tr . flFraction * 1000.0 ; // height from ground
if ( lastHeight < height - 100 ) // Drops more than 100 Units?
return true ;
lastHeight = height ;
2015-06-10 23:41:55 +03:00
distance = ( to - check ) . GetLength ( ) ; // distance from goal
2014-07-30 14:17:46 +04:00
}
return false ;
}
void Bot : : ChangePitch ( float speed )
{
// this function turns a bot towards its ideal_pitch
float idealPitch = AngleNormalize ( pev - > idealpitch ) ;
if ( yb_aim_method . GetInt ( ) = = 1 )
{
// turn to the ideal angle immediately
pev - > v_angle . x = idealPitch ;
pev - > angles . x = - idealPitch / 3 ;
return ;
}
float curent = AngleNormalize ( pev - > v_angle . x ) ;
// turn from the current v_angle pitch to the idealpitch by selecting
// the quickest way to turn to face that direction
// find the difference in the curent and ideal angle
float normalizePitch = AngleNormalize ( idealPitch - curent ) ;
if ( normalizePitch > 0 )
{
if ( normalizePitch > speed )
normalizePitch = speed ;
}
else
{
if ( normalizePitch < - speed )
normalizePitch = - speed ;
}
pev - > v_angle . x = AngleNormalize ( curent + normalizePitch ) ;
if ( pev - > v_angle . x > 89.9 )
pev - > v_angle . x = 89.9 ;
if ( pev - > v_angle . x < - 89.9 )
pev - > v_angle . x = - 89.9 ;
pev - > angles . x = - pev - > v_angle . x / 3 ;
}
void Bot : : ChangeYaw ( float speed )
{
// this function turns a bot towards its ideal_yaw
float idealPitch = AngleNormalize ( pev - > ideal_yaw ) ;
if ( yb_aim_method . GetInt ( ) = = 1 )
{
// turn to the ideal angle immediately
pev - > angles . y = ( pev - > v_angle . y = idealPitch ) ;
return ;
}
float curent = AngleNormalize ( pev - > v_angle . y ) ;
// turn from the current v_angle yaw to the ideal_yaw by selecting
// the quickest way to turn to face that direction
// find the difference in the curent and ideal angle
float normalizePitch = AngleNormalize ( idealPitch - curent ) ;
if ( normalizePitch > 0 )
{
if ( normalizePitch > speed )
normalizePitch = speed ;
}
else
{
if ( normalizePitch < - speed )
normalizePitch = - speed ;
}
pev - > v_angle . y = AngleNormalize ( curent + normalizePitch ) ;
pev - > angles . y = pev - > v_angle . y ;
}
int Bot : : GetAimingWaypoint ( void )
{
// Find a good WP to look at when camping
int count = 0 , indeces [ 3 ] ;
float distTab [ 3 ] ;
uint16 visibility [ 3 ] ;
int currentWaypoint = g_waypoint - > FindNearest ( pev - > origin ) ;
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
if ( currentWaypoint = = i | | ! g_waypoint - > IsVisible ( currentWaypoint , i ) )
continue ;
Path * path = g_waypoint - > GetPath ( i ) ;
if ( count < 3 )
{
indeces [ count ] = i ;
distTab [ count ] = ( pev - > origin - path - > origin ) . GetLengthSquared ( ) ;
visibility [ count ] = path - > vis . crouch + path - > vis . stand ;
count + + ;
}
else
{
float distance = ( pev - > origin - path - > origin ) . GetLengthSquared ( ) ;
uint16 visBits = path - > vis . crouch + path - > vis . stand ;
for ( int j = 0 ; j < 3 ; j + + )
{
if ( visBits > = visibility [ j ] & & distance > distTab [ j ] )
{
indeces [ j ] = i ;
distTab [ j ] = distance ;
visibility [ j ] = visBits ;
break ;
}
}
}
}
count - - ;
if ( count > = 0 )
2015-06-09 15:45:34 +03:00
return indeces [ Random . Long ( 0 , count ) ] ;
2014-07-30 14:17:46 +04:00
2015-06-09 15:45:34 +03:00
return Random . Long ( 0 , g_numWaypoints - 1 ) ;
2014-07-30 14:17:46 +04:00
}
void Bot : : FacePosition ( void )
{
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
// adjust all body and view angles to face an absolute vector
Vector direction = ( m_lookAt - EyePosition ( ) ) . ToAngles ( ) ;
2015-06-04 11:52:48 +03:00
direction = direction + pev - > punchangle * ( m_difficulty * 25 ) / 100.0 ;
2014-07-30 14:17:46 +04:00
direction . x * = - 1.0 ; // invert for engine
Vector deviation = ( direction - pev - > v_angle ) ;
direction . ClampAngles ( ) ;
deviation . ClampAngles ( ) ;
2015-06-04 11:52:48 +03:00
int aimMethod = yb_aim_method . GetInt ( ) ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if ( aimMethod < 1 | | aimMethod > 3 )
aimMethod = 3 ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
if ( aimMethod = = 1 )
2014-07-30 14:17:46 +04:00
pev - > v_angle = direction ;
2015-06-04 11:52:48 +03:00
else if ( aimMethod = = 2 )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
float turnSkill = static_cast < float > ( 0.05 * ( m_difficulty * 25 ) ) + 0.5 ;
2014-07-30 14:17:46 +04:00
float aimSpeed = 0.17 + turnSkill * 0.06 ;
float frameCompensation = g_pGlobals - > frametime * 1000 * 0.01 ;
if ( ( m_aimFlags & AIM_ENEMY ) & & ! ( m_aimFlags & AIM_ENTITY ) )
aimSpeed * = 1.75 ;
float momentum = ( 1.0 - aimSpeed ) * 0.5 ;
pev - > pitch_speed = ( ( pev - > pitch_speed * momentum ) + aimSpeed * deviation . x * ( 1.0 - momentum ) ) * frameCompensation ;
pev - > yaw_speed = ( ( pev - > yaw_speed * momentum ) + aimSpeed * deviation . y * ( 1.0 - momentum ) ) * frameCompensation ;
2015-06-04 11:52:48 +03:00
if ( m_difficulty < = 2 )
2014-07-30 14:17:46 +04:00
{
// influence of y movement on x axis, based on skill (less influence than x on y since it's
// easier and more natural for the bot to "move its mouse" horizontally than vertically)
pev - > pitch_speed + = pev - > yaw_speed / ( 10.0 * turnSkill ) ;
pev - > yaw_speed + = pev - > pitch_speed / ( 10.0 * turnSkill ) ;
}
pev - > v_angle . x + = pev - > pitch_speed ; // change pitch angles
pev - > v_angle . y + = pev - > yaw_speed ; // change yaw angles
}
2015-06-04 11:52:48 +03:00
else if ( aimMethod = = 3 )
2014-07-30 14:17:46 +04:00
{
Vector springStiffness ( yb_aim_spring_stiffness_x . GetFloat ( ) , yb_aim_spring_stiffness_y . GetFloat ( ) , 0 ) ;
Vector damperCoefficient ( yb_aim_damper_coefficient_x . GetFloat ( ) , yb_aim_damper_coefficient_y . GetFloat ( ) , 0 ) ;
Vector influence ( yb_aim_influence_x_on_y . GetFloat ( ) , yb_aim_influence_y_on_x . GetFloat ( ) , 0 ) ;
Vector randomization ( yb_aim_deviation_x . GetFloat ( ) , yb_aim_deviation_y . GetFloat ( ) , 0 ) ;
Vector stiffness = nullvec ;
Vector randomize = nullvec ;
m_idealAngles = direction . SkipZ ( ) ;
m_targetOriginAngularSpeed . ClampAngles ( ) ;
m_idealAngles . ClampAngles ( ) ;
2015-06-04 11:52:48 +03:00
if ( m_difficulty = = 4 )
2014-07-30 14:17:46 +04:00
{
2015-06-04 11:52:48 +03:00
influence = influence * ( ( 100 - ( m_difficulty * 25 ) ) / 100.f ) ;
randomization = randomization * ( ( 100 - ( m_difficulty * 25 ) ) / 100.f ) ;
2014-07-30 14:17:46 +04:00
}
if ( m_aimFlags & ( AIM_ENEMY | AIM_ENTITY | AIM_GRENADE | AIM_LAST_ENEMY ) | | GetTaskId ( ) = = TASK_SHOOTBREAKABLE )
{
m_playerTargetTime = GetWorldTime ( ) ;
m_randomizedIdealAngles = m_idealAngles ;
if ( IsValidPlayer ( m_enemy ) )
{
2015-06-04 11:52:48 +03:00
m_targetOriginAngularSpeed = ( ( m_enemyOrigin - pev - > origin + 1.5 * m_frameInterval * ( 1.0 * m_enemy - > v . velocity ) - 0.0 * g_pGlobals - > frametime * pev - > velocity ) . ToAngles ( ) - ( m_enemyOrigin - pev - > origin ) . ToAngles ( ) ) * 0.45 * yb_aim_target_anticipation_ratio . GetFloat ( ) * static_cast < float > ( ( m_difficulty * 25 ) / 100 ) ;
2014-07-30 14:17:46 +04:00
if ( m_angularDeviation . GetLength ( ) < 5.0 )
2015-06-04 11:52:48 +03:00
springStiffness = ( 5.0 - m_angularDeviation . GetLength ( ) ) * 0.25 * static_cast < float > ( ( m_difficulty * 25 ) / 100 ) * springStiffness + springStiffness ;
2014-07-30 14:17:46 +04:00
m_targetOriginAngularSpeed . x = - m_targetOriginAngularSpeed . x ;
if ( ( pev - > fov < 90 ) & & ( m_angularDeviation . GetLength ( ) > = 5.0 ) )
springStiffness = springStiffness * 2 ;
m_targetOriginAngularSpeed . ClampAngles ( ) ;
}
else
m_targetOriginAngularSpeed = nullvec ;
2015-06-04 11:52:48 +03:00
if ( m_difficulty > = 3 )
2014-07-30 14:17:46 +04:00
stiffness = springStiffness ;
else
2015-06-04 11:52:48 +03:00
stiffness = springStiffness * ( 0.2 + ( m_difficulty * 25 ) / 125.0 ) ;
2014-07-30 14:17:46 +04:00
}
else
{
// is it time for bot to randomize the aim direction again (more often where moving) ?
if ( m_randomizeAnglesTime < GetWorldTime ( ) & & ( ( pev - > velocity . GetLength ( ) > 1.0 & & m_angularDeviation . GetLength ( ) < 5.0 ) | | m_angularDeviation . GetLength ( ) < 1.0 ) )
{
// is the bot standing still ?
if ( pev - > velocity . GetLength ( ) < 1.0 )
randomize = randomization * 0.2 ; // randomize less
else
randomize = randomization ;
// randomize targeted location a bit (slightly towards the ground)
2015-06-09 15:45:34 +03:00
m_randomizedIdealAngles = m_idealAngles + Vector ( Random . Float ( - randomize . x * 0.5 , randomize . x * 1.5 ) , Random . Float ( - randomize . y , randomize . y ) , 0 ) ;
2014-07-30 14:17:46 +04:00
// set next time to do this
2015-06-09 15:45:34 +03:00
m_randomizeAnglesTime = GetWorldTime ( ) + Random . Float ( 0.4 , yb_aim_offset_delay . GetFloat ( ) ) ;
2014-07-30 14:17:46 +04:00
}
float stiffnessMultiplier = yb_aim_notarget_slowdown_ratio . GetFloat ( ) ;
// take in account whether the bot was targeting someone in the last N seconds
if ( GetWorldTime ( ) - ( m_playerTargetTime + yb_aim_offset_delay . GetFloat ( ) ) < yb_aim_notarget_slowdown_ratio . GetFloat ( ) * 10.0 )
{
stiffnessMultiplier = 1.0 - ( GetWorldTime ( ) - m_timeLastFired ) * 0.1 ;
// don't allow that stiffness multiplier less than zero
if ( stiffnessMultiplier < 0.0 )
stiffnessMultiplier = 0.5 ;
}
2015-06-04 11:52:48 +03:00
// also take in account the remaining deviation (slow down the aiming in the last 10°)
if ( m_difficulty < 3 & & ( m_angularDeviation . GetLength ( ) < 10.0 ) )
2014-07-30 14:17:46 +04:00
stiffnessMultiplier * = m_angularDeviation . GetLength ( ) * 0.1 ;
// slow down even more if we are not moving
2015-06-04 11:52:48 +03:00
if ( m_difficulty < 3 & & pev - > velocity . GetLength ( ) < 1.0 & & GetTaskId ( ) ! = TASK_CAMP & & GetTaskId ( ) ! = TASK_ATTACK )
2014-07-30 14:17:46 +04:00
stiffnessMultiplier * = 0.5 ;
// but don't allow getting below a certain value
if ( stiffnessMultiplier < 0.35 )
stiffnessMultiplier = 0.35 ;
stiffness = springStiffness * stiffnessMultiplier ; // increasingly slow aim
// no target means no angular speed to take in account
m_targetOriginAngularSpeed = nullvec ;
}
// compute randomized angle deviation this time
m_angularDeviation = m_randomizedIdealAngles + m_targetOriginAngularSpeed - pev - > v_angle ;
m_angularDeviation . ClampAngles ( ) ;
// spring/damper model aiming
m_aimSpeed . x = ( stiffness . x * m_angularDeviation . x ) - ( damperCoefficient . x * m_aimSpeed . x ) ;
m_aimSpeed . y = ( stiffness . y * m_angularDeviation . y ) - ( damperCoefficient . y * m_aimSpeed . y ) ;
// influence of y movement on x axis and vice versa (less influence than x on y since it's
// easier and more natural for the bot to "move its mouse" horizontally than vertically)
m_aimSpeed . x + = m_aimSpeed . y * influence . y ;
m_aimSpeed . y + = m_aimSpeed . x * influence . x ;
// move the aim cursor
pev - > v_angle = pev - > v_angle + m_frameInterval * Vector ( m_aimSpeed . x , m_aimSpeed . y , 0 ) ;
pev - > v_angle . ClampAngles ( ) ;
}
// set the body angles to point the gun correctly
pev - > angles . x = - pev - > v_angle . x * ( 1.0 / 3.0 ) ;
pev - > angles . y = pev - > v_angle . y ;
pev - > v_angle . ClampAngles ( ) ;
pev - > angles . ClampAngles ( ) ;
pev - > angles . z = pev - > v_angle . z = 0.0 ; // ignore Z component
}
2015-06-10 23:41:55 +03:00
void Bot : : SetStrafeSpeed ( const Vector & moveDir , float strafeSpeed )
2014-07-30 14:17:46 +04:00
{
MakeVectors ( pev - > angles ) ;
2015-06-10 23:41:55 +03:00
const Vector & los = ( moveDir - pev - > origin ) . Normalize2D ( ) ;
2014-07-30 14:17:46 +04:00
float dot = los | g_pGlobals - > v_forward . SkipZ ( ) ;
if ( dot > 0 & & ! CheckWallOnRight ( ) )
m_strafeSpeed = strafeSpeed ;
else if ( ! CheckWallOnLeft ( ) )
m_strafeSpeed = - strafeSpeed ;
}
int Bot : : FindPlantedBomb ( void )
{
// this function tries to find planted c4 on the defuse scenario map and returns nearest to it waypoint
2014-08-06 00:03:50 +04:00
if ( ( m_team ! = TEAM_TF ) | | ! ( g_mapType & MAP_DE ) )
2014-07-30 14:17:46 +04:00
return - 1 ; // don't search for bomb if the player is CT, or it's not defusing bomb
edict_t * bombEntity = NULL ; // temporaly pointer to bomb
// search the bomb on the map
2015-06-04 11:52:48 +03:00
while ( ! IsEntityNull ( bombEntity = FIND_ENTITY_BY_CLASSNAME ( bombEntity , " grenade " ) ) )
2014-07-30 14:17:46 +04:00
{
if ( strcmp ( STRING ( bombEntity - > v . model ) + 9 , " c4.mdl " ) = = 0 )
{
int nearestIndex = g_waypoint - > FindNearest ( GetEntityOrigin ( bombEntity ) ) ;
if ( ( nearestIndex > = 0 ) & & ( nearestIndex < g_numWaypoints ) )
return nearestIndex ;
break ;
}
}
return - 1 ;
}
2015-06-04 11:52:48 +03:00
bool Bot : : IsPointOccupied ( int index )
2014-07-30 14:17:46 +04:00
{
if ( index < 0 | | index > = g_numWaypoints )
return true ;
// first check if current waypoint of one of the bots is index waypoint
for ( int i = 0 ; i < GetMaxClients ( ) ; i + + )
{
Bot * bot = g_botManager - > GetBot ( i ) ;
if ( bot = = NULL | | bot = = this )
continue ;
// check if this waypoint is already used
2015-06-09 22:16:08 +03:00
if ( IsAlive ( bot - > GetEntity ( ) ) )
{
2015-06-14 12:55:49 +03:00
if ( ( GetShootingConeDeviation ( bot - > GetEntity ( ) , & pev - > origin ) > = 0.7f ? bot - > m_prevWptIndex [ 0 ] : m_currentWaypointIndex ) = = index | | bot - > GetTask ( ) - > data = = index )
2015-06-09 22:16:08 +03:00
return true ;
}
2014-07-30 14:17:46 +04:00
}
return false ;
}
edict_t * Bot : : FindNearestButton ( const char * targetName )
{
// this function tries to find nearest to current bot button, and returns pointer to
// it's entity, also here must be specified the target, that button must open.
if ( IsNullString ( targetName ) )
return NULL ;
float nearestDistance = FLT_MAX ;
edict_t * searchEntity = NULL , * foundEntity = NULL ;
// find the nearest button which can open our target
2015-06-14 12:55:49 +03:00
while ( ! IsEntityNull ( searchEntity = FIND_ENTITY_BY_TARGET ( searchEntity , targetName ) ) )
2014-07-30 14:17:46 +04:00
{
Vector entityOrign = GetEntityOrigin ( searchEntity ) ;
// check if this place safe
if ( ! IsDeadlyDrop ( entityOrign ) )
{
float distance = ( pev - > origin - entityOrign ) . GetLengthSquared ( ) ;
// check if we got more close button
if ( distance < = nearestDistance )
{
nearestDistance = distance ;
foundEntity = searchEntity ;
}
}
}
return foundEntity ;
}