|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
// Author: Michael S. Booth ([email protected]), 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; }
|