You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
694 lines
17 KiB
694 lines
17 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
|
||
|
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "cs_bot.h"
|
||
|
#include "cs_nav_path.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* This method is the ONLY legal way to change a bot's current state
|
||
|
*/
|
||
|
void CCSBot::SetState( BotState *state )
|
||
|
{
|
||
|
PrintIfWatched( "%s: SetState: %s -> %s\n", GetPlayerName(), (m_state) ? m_state->GetName() : "NULL", state->GetName() );
|
||
|
|
||
|
/*
|
||
|
if ( IsDefusingBomb() )
|
||
|
{
|
||
|
const Vector *bombPos = GetGameState()->GetBombPosition();
|
||
|
if ( bombPos != NULL )
|
||
|
{
|
||
|
if ( TheCSBots()->GetBombDefuser() == this )
|
||
|
{
|
||
|
if ( TheCSBots()->IsBombPlanted() )
|
||
|
{
|
||
|
Msg( "Bot %s is switching from defusing the bomb to %s\n",
|
||
|
GetPlayerName(), state->GetName() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// if we changed state from within the special Attack state, we are no longer attacking
|
||
|
if (m_isAttacking)
|
||
|
StopAttacking();
|
||
|
|
||
|
if (m_state)
|
||
|
m_state->OnExit( this );
|
||
|
|
||
|
state->OnEnter( this );
|
||
|
|
||
|
m_state = state;
|
||
|
m_stateTimestamp = gpGlobals->curtime;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::Idle( void )
|
||
|
{
|
||
|
SetTask( SEEK_AND_DESTROY );
|
||
|
SetState( &m_idleState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::EscapeFromBomb( void )
|
||
|
{
|
||
|
SetTask( ESCAPE_FROM_BOMB );
|
||
|
SetState( &m_escapeFromBombState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::Follow( CCSPlayer *player )
|
||
|
{
|
||
|
if (player == NULL)
|
||
|
return;
|
||
|
|
||
|
// note when we began following
|
||
|
if (!m_isFollowing || m_leader != player)
|
||
|
m_followTimestamp = gpGlobals->curtime;
|
||
|
|
||
|
m_isFollowing = true;
|
||
|
m_leader = player;
|
||
|
|
||
|
SetTask( FOLLOW );
|
||
|
m_followState.SetLeader( player );
|
||
|
SetState( &m_followState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Continue following our leader after finishing what we were doing
|
||
|
*/
|
||
|
void CCSBot::ContinueFollowing( void )
|
||
|
{
|
||
|
SetTask( FOLLOW );
|
||
|
|
||
|
m_followState.SetLeader( m_leader );
|
||
|
|
||
|
SetState( &m_followState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Stop following
|
||
|
*/
|
||
|
void CCSBot::StopFollowing( void )
|
||
|
{
|
||
|
m_isFollowing = false;
|
||
|
m_leader = NULL;
|
||
|
m_allowAutoFollowTime = gpGlobals->curtime + 10.0f;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Begin process of rescuing hostages
|
||
|
*/
|
||
|
void CCSBot::RescueHostages( void )
|
||
|
{
|
||
|
SetTask( RESCUE_HOSTAGES );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Use the entity
|
||
|
*/
|
||
|
void CCSBot::UseEntity( CBaseEntity *entity )
|
||
|
{
|
||
|
m_useEntityState.SetEntity( entity );
|
||
|
SetState( &m_useEntityState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Open the door.
|
||
|
* This assumes the bot is directly in front of the door with no obstructions.
|
||
|
* NOTE: This state is special, like Attack, in that it suspends the current behavior and returns to it when done.
|
||
|
*/
|
||
|
void CCSBot::OpenDoor( CBaseEntity *door )
|
||
|
{
|
||
|
m_openDoorState.SetDoor( door );
|
||
|
m_isOpeningDoor = true;
|
||
|
m_openDoorState.OnEnter( this );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* DEPRECATED: Use TryToHide() instead.
|
||
|
* Move to a hiding place.
|
||
|
* If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
|
||
|
*/
|
||
|
void CCSBot::Hide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition )
|
||
|
{
|
||
|
DestroyPath();
|
||
|
|
||
|
CNavArea *source;
|
||
|
Vector sourcePos;
|
||
|
if (searchFromArea)
|
||
|
{
|
||
|
source = searchFromArea;
|
||
|
sourcePos = searchFromArea->GetCenter();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
source = m_lastKnownArea;
|
||
|
sourcePos = GetCentroid( this );
|
||
|
}
|
||
|
|
||
|
if (source == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "Hide from area is NULL.\n" );
|
||
|
Idle();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_hideState.SetSearchArea( source );
|
||
|
m_hideState.SetSearchRange( hideRange );
|
||
|
m_hideState.SetDuration( duration );
|
||
|
m_hideState.SetHoldPosition( holdPosition );
|
||
|
|
||
|
// search around source area for a good hiding spot
|
||
|
Vector useSpot;
|
||
|
|
||
|
const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper() );
|
||
|
if (pos == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "No available hiding spots.\n" );
|
||
|
// hide at our current position
|
||
|
useSpot = GetCentroid( this );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
useSpot = *pos;
|
||
|
}
|
||
|
|
||
|
m_hideState.SetHidingSpot( useSpot );
|
||
|
|
||
|
// build a path to our new hiding spot
|
||
|
if (ComputePath( useSpot, FASTEST_ROUTE ) == false)
|
||
|
{
|
||
|
PrintIfWatched( "Can't pathfind to hiding spot\n" );
|
||
|
Idle();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetState( &m_hideState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Move to the given hiding place
|
||
|
*/
|
||
|
void CCSBot::Hide( const Vector &hidingSpot, float duration, bool holdPosition )
|
||
|
{
|
||
|
CNavArea *hideArea = TheNavMesh->GetNearestNavArea( hidingSpot );
|
||
|
if (hideArea == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "Hiding spot off nav mesh\n" );
|
||
|
Idle();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DestroyPath();
|
||
|
|
||
|
m_hideState.SetSearchArea( hideArea );
|
||
|
m_hideState.SetSearchRange( 750.0f );
|
||
|
m_hideState.SetDuration( duration );
|
||
|
m_hideState.SetHoldPosition( holdPosition );
|
||
|
m_hideState.SetHidingSpot( hidingSpot );
|
||
|
|
||
|
// build a path to our new hiding spot
|
||
|
if (ComputePath( hidingSpot, FASTEST_ROUTE ) == false)
|
||
|
{
|
||
|
PrintIfWatched( "Can't pathfind to hiding spot\n" );
|
||
|
Idle();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetState( &m_hideState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Try to hide nearby. Return true if hiding, false if can't hide here.
|
||
|
* If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
|
||
|
*/
|
||
|
bool CCSBot::TryToHide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition, bool useNearest )
|
||
|
{
|
||
|
CNavArea *source;
|
||
|
Vector sourcePos;
|
||
|
if (searchFromArea)
|
||
|
{
|
||
|
source = searchFromArea;
|
||
|
sourcePos = searchFromArea->GetCenter();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
source = m_lastKnownArea;
|
||
|
sourcePos = GetCentroid( this );
|
||
|
}
|
||
|
|
||
|
if (source == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "Hide from area is NULL.\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_hideState.SetSearchArea( source );
|
||
|
m_hideState.SetSearchRange( hideRange );
|
||
|
m_hideState.SetDuration( duration );
|
||
|
m_hideState.SetHoldPosition( holdPosition );
|
||
|
|
||
|
// search around source area for a good hiding spot
|
||
|
const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper(), useNearest );
|
||
|
if (pos == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "No available hiding spots.\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_hideState.SetHidingSpot( *pos );
|
||
|
|
||
|
// build a path to our new hiding spot
|
||
|
if (ComputePath( *pos, FASTEST_ROUTE ) == false)
|
||
|
{
|
||
|
PrintIfWatched( "Can't pathfind to hiding spot\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SetState( &m_hideState );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Retreat to a nearby hiding spot, away from enemies
|
||
|
*/
|
||
|
bool CCSBot::TryToRetreat( float maxRange, float duration )
|
||
|
{
|
||
|
const Vector *spot = FindNearbyRetreatSpot( this, maxRange );
|
||
|
if (spot)
|
||
|
{
|
||
|
// ignore enemies for a second to give us time to hide
|
||
|
// reaching our hiding spot clears our disposition
|
||
|
IgnoreEnemies( 10.0f );
|
||
|
|
||
|
if (duration < 0.0f)
|
||
|
{
|
||
|
duration = RandomFloat( 3.0f, 15.0f );
|
||
|
}
|
||
|
|
||
|
StandUp();
|
||
|
Run();
|
||
|
Hide( *spot, duration );
|
||
|
|
||
|
PrintIfWatched( "Retreating to a safe spot!\n" );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::Hunt( void )
|
||
|
{
|
||
|
SetState( &m_huntState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Attack our the given victim
|
||
|
* NOTE: Attacking does not change our task.
|
||
|
*/
|
||
|
void CCSBot::Attack( CCSPlayer *victim )
|
||
|
{
|
||
|
if (victim == NULL)
|
||
|
return;
|
||
|
|
||
|
// zombies never attack
|
||
|
if (cv_bot_zombie.GetBool())
|
||
|
return;
|
||
|
|
||
|
// cannot attack if we are reloading
|
||
|
if (IsReloading())
|
||
|
return;
|
||
|
|
||
|
// change enemy
|
||
|
SetBotEnemy( victim );
|
||
|
|
||
|
//
|
||
|
// Do not "re-enter" the attack state if we are already attacking
|
||
|
//
|
||
|
if (IsAttacking())
|
||
|
return;
|
||
|
|
||
|
// if we're holding a grenade, throw it at the victim
|
||
|
if (IsUsingGrenade())
|
||
|
{
|
||
|
// throw towards their feet
|
||
|
ThrowGrenade( victim->GetAbsOrigin() );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// if we are currently hiding, increase our chances of crouching and holding position
|
||
|
if (IsAtHidingSpot())
|
||
|
m_attackState.SetCrouchAndHold( (RandomFloat( 0.0f, 100.0f ) < 60.0f) ? true : false );
|
||
|
else
|
||
|
m_attackState.SetCrouchAndHold( false );
|
||
|
|
||
|
//SetState( &m_attackState );
|
||
|
//PrintIfWatched( "ATTACK BEGIN (reaction time = %g (+ update time), surprise time = %g, attack delay = %g)\n",
|
||
|
// GetProfile()->GetReactionTime(), m_surpriseDelay, GetProfile()->GetAttackDelay() );
|
||
|
m_isAttacking = true;
|
||
|
m_attackState.OnEnter( this );
|
||
|
|
||
|
|
||
|
Vector victimOrigin = GetCentroid( victim );
|
||
|
|
||
|
// cheat a bit and give the bot the initial location of its victim
|
||
|
m_lastEnemyPosition = victimOrigin;
|
||
|
m_lastSawEnemyTimestamp = gpGlobals->curtime;
|
||
|
m_aimSpreadTimestamp = gpGlobals->curtime;
|
||
|
|
||
|
// compute the angle difference between where are looking, and where we need to look
|
||
|
Vector toEnemy = victimOrigin - GetCentroid( this );
|
||
|
|
||
|
QAngle idealAngle;
|
||
|
VectorAngles( toEnemy, idealAngle );
|
||
|
|
||
|
float deltaYaw = (float)fabs(m_lookYaw - idealAngle.y);
|
||
|
|
||
|
while( deltaYaw > 180.0f )
|
||
|
deltaYaw -= 360.0f;
|
||
|
|
||
|
if (deltaYaw < 0.0f)
|
||
|
deltaYaw = -deltaYaw;
|
||
|
|
||
|
// immediately aim at enemy - accuracy penalty depending on how far we must turn to aim
|
||
|
// accuracy is halved if we have to turn 180 degrees
|
||
|
float turn = deltaYaw / 180.0f;
|
||
|
float accuracy = GetProfile()->GetSkill() / (1.0f + turn);
|
||
|
|
||
|
SetAimOffset( accuracy );
|
||
|
|
||
|
// define time when aim offset will automatically be updated
|
||
|
// longer time the more we had to turn (surprise)
|
||
|
m_aimOffsetTimestamp = gpGlobals->curtime + RandomFloat( 0.25f + turn, 1.5f );
|
||
|
|
||
|
// forget any look at targets we have
|
||
|
ClearLookAt();
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Exit the Attack state
|
||
|
*/
|
||
|
void CCSBot::StopAttacking( void )
|
||
|
{
|
||
|
PrintIfWatched( "ATTACK END\n" );
|
||
|
m_attackState.OnExit( this );
|
||
|
m_isAttacking = false;
|
||
|
|
||
|
// if we are following someone, go to the Idle state after the attack to decide whether we still want to follow
|
||
|
if (IsFollowing())
|
||
|
{
|
||
|
Idle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
bool CCSBot::IsAttacking( void ) const
|
||
|
{
|
||
|
return m_isAttacking;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are escaping from the bomb
|
||
|
*/
|
||
|
bool CCSBot::IsEscapingFromBomb( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_escapeFromBombState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are defusing the bomb
|
||
|
*/
|
||
|
bool CCSBot::IsDefusingBomb( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_defuseBombState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are hiding
|
||
|
*/
|
||
|
bool CCSBot::IsHiding( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_hideState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are hiding and at our hiding spot
|
||
|
*/
|
||
|
bool CCSBot::IsAtHidingSpot( void ) const
|
||
|
{
|
||
|
if (!IsHiding())
|
||
|
return false;
|
||
|
|
||
|
return m_hideState.IsAtSpot();
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return number of seconds we have been at our current hiding spot
|
||
|
*/
|
||
|
float CCSBot::GetHidingTime( void ) const
|
||
|
{
|
||
|
if (IsHiding())
|
||
|
{
|
||
|
return m_hideState.GetHideTime();
|
||
|
}
|
||
|
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are huting
|
||
|
*/
|
||
|
bool CCSBot::IsHunting( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_huntState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are in the MoveTo state
|
||
|
*/
|
||
|
bool CCSBot::IsMovingTo( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_moveToState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Return true if we are buying
|
||
|
*/
|
||
|
bool CCSBot::IsBuying( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_buyState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
bool CCSBot::IsInvestigatingNoise( void ) const
|
||
|
{
|
||
|
if (m_state == static_cast<const BotState *>( &m_investigateNoiseState ))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Move to potentially distant position
|
||
|
*/
|
||
|
void CCSBot::MoveTo( const Vector &pos, RouteType route )
|
||
|
{
|
||
|
m_moveToState.SetGoalPosition( pos );
|
||
|
m_moveToState.SetRouteType( route );
|
||
|
SetState( &m_moveToState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::PlantBomb( void )
|
||
|
{
|
||
|
SetState( &m_plantBombState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Bomb has been dropped - go get it
|
||
|
*/
|
||
|
void CCSBot::FetchBomb( void )
|
||
|
{
|
||
|
SetState( &m_fetchBombState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::DefuseBomb( void )
|
||
|
{
|
||
|
SetState( &m_defuseBombState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Investigate recent enemy noise
|
||
|
*/
|
||
|
void CCSBot::InvestigateNoise( void )
|
||
|
{
|
||
|
SetState( &m_investigateNoiseState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
void CCSBot::Buy( void )
|
||
|
{
|
||
|
SetState( &m_buyState );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Move to a hiding spot and wait for initial encounter with enemy team.
|
||
|
* Return false if can't do this behavior (ie: no hiding spots available).
|
||
|
*/
|
||
|
bool CCSBot::MoveToInitialEncounter( void )
|
||
|
{
|
||
|
int myTeam = GetTeamNumber();
|
||
|
int enemyTeam = OtherTeam( myTeam );
|
||
|
|
||
|
// build a path to an enemy spawn point
|
||
|
CBaseEntity *enemySpawn = TheCSBots()->GetRandomSpawn( enemyTeam );
|
||
|
|
||
|
if (enemySpawn == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "MoveToInitialEncounter: No enemy spawn points?\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// build a path from us to the enemy spawn
|
||
|
CCSNavPath path;
|
||
|
PathCost cost( this, FASTEST_ROUTE );
|
||
|
path.Compute( WorldSpaceCenter(), enemySpawn->GetAbsOrigin(), cost );
|
||
|
|
||
|
if (!path.IsValid())
|
||
|
{
|
||
|
PrintIfWatched( "MoveToInitialEncounter: Pathfind failed.\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// find battlefront area where teams will first meet along this path
|
||
|
int i;
|
||
|
for( i=0; i<path.GetSegmentCount(); ++i )
|
||
|
{
|
||
|
if (path[i]->area->GetEarliestOccupyTime( myTeam ) > path[i]->area->GetEarliestOccupyTime( enemyTeam ))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == path.GetSegmentCount())
|
||
|
{
|
||
|
PrintIfWatched( "MoveToInitialEncounter: Can't find battlefront!\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// @todo Remove this evil side-effect
|
||
|
SetInitialEncounterArea( path[i]->area );
|
||
|
|
||
|
// find a hiding spot on our side of the battlefront that has LOS to it
|
||
|
const float maxRange = 1500.0f;
|
||
|
const HidingSpot *spot = FindInitialEncounterSpot( this, path[i]->area->GetCenter(), path[i]->area->GetEarliestOccupyTime( enemyTeam ), maxRange, IsSniper() );
|
||
|
|
||
|
if (spot == NULL)
|
||
|
{
|
||
|
PrintIfWatched( "MoveToInitialEncounter: Can't find a hiding spot\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
float timeToWait = path[i]->area->GetEarliestOccupyTime( enemyTeam ) - spot->GetArea()->GetEarliestOccupyTime( myTeam );
|
||
|
float minWaitTime = 4.0f * GetProfile()->GetAggression() + 3.0f;
|
||
|
if (timeToWait < minWaitTime)
|
||
|
{
|
||
|
timeToWait = minWaitTime;
|
||
|
}
|
||
|
|
||
|
Hide( spot->GetPosition(), timeToWait );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|