mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-11 03:09:36 +00:00
1
This commit is contained in:
+123
@@ -0,0 +1,123 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_build_sentry.h"
|
||||
#include "bot/map_entities/tf_bot_hint_sentrygun.h"
|
||||
#include "bot/map_entities/tf_bot_hint_teleporter_exit.h"
|
||||
#include "string_t.h"
|
||||
#include "tf_fx.h"
|
||||
|
||||
extern ConVar tf_bot_engineer_mvm_building_health_multiplier;
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMvMEngineerBuildSentryGun::CTFBotMvMEngineerBuildSentryGun( CTFBotHintSentrygun* pSentryHint )
|
||||
{
|
||||
m_sentryBuildHint = pSentryHint;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerBuildSentryGun::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
me->StartBuildingObjectOfType( OBJ_SENTRYGUN );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerBuildSentryGun::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( m_sentryBuildHint == NULL )
|
||||
return Done( "No hint entity" );
|
||||
|
||||
float rangeToBuildSpot = me->GetRangeTo( m_sentryBuildHint->GetAbsOrigin() );
|
||||
|
||||
if ( rangeToBuildSpot < 200.0f )
|
||||
{
|
||||
// crouch as we get close so we don't overshoot
|
||||
me->PressCrouchButton();
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( m_sentryBuildHint->GetAbsOrigin(), IBody::MANDATORY, 0.1f, NULL, "Placing sentry" );
|
||||
}
|
||||
|
||||
// various interruptions could mean we're away from our build location - move to it
|
||||
if ( rangeToBuildSpot > 25.0f )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, SAFEST_ROUTE );
|
||||
m_path.Compute( me, m_sentryBuildHint->GetAbsOrigin(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( !m_path.IsValid() )
|
||||
{
|
||||
return Done( "Path failed" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( !m_delayBuildTime.HasStarted() )
|
||||
{
|
||||
m_delayBuildTime.Start( 0.1f );
|
||||
TFGameRules()->PushAllPlayersAway( m_sentryBuildHint->GetAbsOrigin(), 400, 500, TF_TEAM_RED );
|
||||
}
|
||||
else if ( m_delayBuildTime.HasStarted() && m_delayBuildTime.IsElapsed() )
|
||||
{
|
||||
// destroy previous object
|
||||
me->DetonateObjectOfType( OBJ_SENTRYGUN, MODE_SENTRYGUN_NORMAL, true );
|
||||
|
||||
// directly create a sentry gun at the precise position and orientation desired
|
||||
m_sentry = (CObjectSentrygun *)CreateEntityByName( "obj_sentrygun" );
|
||||
if ( m_sentry )
|
||||
{
|
||||
m_sentry->SetName( m_sentryBuildHint->GetEntityName() );
|
||||
|
||||
m_sentryBuildHint->IncrementUseCount();
|
||||
m_sentry->m_nDefaultUpgradeLevel = 2;
|
||||
|
||||
m_sentry->SetAbsOrigin( m_sentryBuildHint->GetAbsOrigin() );
|
||||
m_sentry->SetAbsAngles( QAngle( 0, m_sentryBuildHint->GetAbsAngles().y, 0 ) );
|
||||
m_sentry->Spawn();
|
||||
|
||||
m_sentry->StartPlacement( me );
|
||||
m_sentry->StartBuilding( me );
|
||||
|
||||
// the sentry owns this hint now
|
||||
m_sentryBuildHint->SetOwnerEntity( m_sentry );
|
||||
|
||||
m_sentry = NULL;
|
||||
}
|
||||
|
||||
return Done( "Built a sentry" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotMvMEngineerBuildSentryGun::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
if ( m_sentry.Get() )
|
||||
{
|
||||
m_sentry->DropCarriedObject( me );
|
||||
UTIL_Remove( m_sentry );
|
||||
m_sentry = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#ifndef TF_BOT_MVM_ENGINEER_BUILD_SENTRYGUN_H
|
||||
#define TF_BOT_MVM_ENGINEER_BUILD_SENTRYGUN_H
|
||||
|
||||
class CTFBotHintSentrygun;
|
||||
|
||||
class CTFBotMvMEngineerBuildSentryGun : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMvMEngineerBuildSentryGun( CTFBotHintSentrygun* pSentryHint );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual const char *GetName( void ) const { return "MvMEngineerBuildSentryGun"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFBotHintSentrygun > m_sentryBuildHint;
|
||||
CHandle< CObjectSentrygun > m_sentry;
|
||||
|
||||
CountdownTimer m_delayBuildTime;
|
||||
CountdownTimer m_repathTimer;
|
||||
PathFollower m_path;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_MVM_ENGINEER_BUILD_SENTRYGUN_H
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#include "cbase.h"
|
||||
#include "string_t.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_build_teleporter.h"
|
||||
#include "bot/map_entities/tf_bot_hint_teleporter_exit.h"
|
||||
|
||||
ConVar tf_bot_engineer_mvm_building_health_multiplier( "tf_bot_engineer_building_health_multiplier", "2", FCVAR_CHEAT );
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMvMEngineerBuildTeleportExit::CTFBotMvMEngineerBuildTeleportExit( CTFBotHintTeleporterExit *hint )
|
||||
{
|
||||
m_teleporterBuildHint = hint;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerBuildTeleportExit::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerBuildTeleportExit::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( m_teleporterBuildHint == NULL )
|
||||
return Done( "No hint entity" );
|
||||
|
||||
// various interruptions could mean we're away from our build location - move to it
|
||||
if ( me->IsRangeGreaterThan( m_teleporterBuildHint->GetAbsOrigin(), 25.0f ) )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, m_teleporterBuildHint->GetAbsOrigin(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( !m_path.IsValid() )
|
||||
{
|
||||
return Done( "Path failed" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( !m_delayBuildTime.HasStarted() )
|
||||
{
|
||||
m_delayBuildTime.Start( 0.1f );
|
||||
TFGameRules()->PushAllPlayersAway( m_teleporterBuildHint->GetAbsOrigin(), 400, 500, TF_TEAM_RED );
|
||||
}
|
||||
else if ( m_delayBuildTime.IsElapsed() )
|
||||
{
|
||||
// destroy previous object
|
||||
me->DetonateObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT, true );
|
||||
|
||||
// directly create at the precise position and orientation desired
|
||||
CObjectTeleporter* myTeleporter = (CObjectTeleporter *)CreateEntityByName( "obj_teleporter" );
|
||||
if ( myTeleporter )
|
||||
{
|
||||
myTeleporter->SetAbsOrigin( m_teleporterBuildHint->GetAbsOrigin() );
|
||||
myTeleporter->SetAbsAngles( QAngle( 0, m_teleporterBuildHint->GetAbsAngles().y, 0 ) );
|
||||
myTeleporter->SetObjectMode( MODE_TELEPORTER_EXIT );
|
||||
myTeleporter->Spawn();
|
||||
|
||||
myTeleporter->SetTeleportWhere( me->GetTeleportWhere() );
|
||||
|
||||
if ( me->ShouldQuickBuild() )
|
||||
{
|
||||
myTeleporter->ForceQuickBuild();
|
||||
}
|
||||
|
||||
myTeleporter->StartPlacement( me );
|
||||
myTeleporter->StartBuilding( me );
|
||||
|
||||
int iHealth = myTeleporter->GetMaxHealthForCurrentLevel() * tf_bot_engineer_mvm_building_health_multiplier.GetFloat();
|
||||
myTeleporter->SetMaxHealth( iHealth );
|
||||
myTeleporter->SetHealth( iHealth );
|
||||
|
||||
m_teleporterBuildHint->SetOwnerEntity( myTeleporter );
|
||||
|
||||
me->EmitSound( "Engineer.MVM_AutoBuildingTeleporter02" );
|
||||
|
||||
return Done( "Teleport exit built" );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#ifndef TF_BOT_MVM_ENGINEER_BUILD_TELEPORTER_H
|
||||
#define TF_BOT_MVM_ENGINEER_BUILD_TELEPORTER_H
|
||||
|
||||
class CTFBotHintTeleporterExit;
|
||||
|
||||
class CTFBotMvMEngineerBuildTeleportExit : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMvMEngineerBuildTeleportExit( CTFBotHintTeleporterExit *hint );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "MvMEngineerBuildTeleportExit"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFBotHintTeleporterExit > m_teleporterBuildHint;
|
||||
|
||||
CountdownTimer m_delayBuildTime;
|
||||
CountdownTimer m_repathTimer;
|
||||
PathFollower m_path;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_MVM_ENGINEER_BUILD_TELEPORTER_H
|
||||
@@ -0,0 +1,662 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh/tf_nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_teleporter.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/map_entities/tf_bot_hint_engineer_nest.h"
|
||||
#include "bot/map_entities/tf_bot_hint_sentrygun.h"
|
||||
#include "bot/map_entities/tf_bot_hint_teleporter_exit.h"
|
||||
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_idle.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_build_sentry.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_build_teleporter.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_teleport_spawn.h"
|
||||
#include "bot/behavior/tf_bot_retreat_to_cover.h"
|
||||
|
||||
|
||||
ConVar tf_bot_engineer_mvm_sentry_hint_bomb_forward_range( "tf_bot_engineer_mvm_sentry_hint_bomb_forward_range", "0", FCVAR_CHEAT );
|
||||
ConVar tf_bot_engineer_mvm_sentry_hint_bomb_backward_range( "tf_bot_engineer_mvm_sentry_hint_bomb_backward_range", "3000", FCVAR_CHEAT );
|
||||
ConVar tf_bot_engineer_mvm_hint_min_distance_from_bomb( "tf_bot_engineer_mvm_hint_min_distance_from_bomb", "1300", FCVAR_CHEAT );
|
||||
|
||||
struct BombInfo_t
|
||||
{
|
||||
Vector m_vPosition;
|
||||
float m_flMinBattleFront;
|
||||
float m_flMaxBattleFront;
|
||||
};
|
||||
|
||||
|
||||
bool GetBombInfo( BombInfo_t* pBombInfo = NULL )
|
||||
{
|
||||
// find the incursion distance of the current "front" (the location of the bomb)
|
||||
|
||||
// first find farthest bomb delivery distance of invading team since maps
|
||||
// have different spawn room sizes and geometries
|
||||
float battlefront = 0.0f;
|
||||
|
||||
for( int n=0; n<TheNavAreas.Count(); ++n )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)TheNavAreas[n];
|
||||
|
||||
if ( area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE | TF_NAV_SPAWN_ROOM_RED ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float areaDistanceToTarget = area->GetTravelDistanceToBombTarget();
|
||||
if ( areaDistanceToTarget > battlefront && areaDistanceToTarget > 0.0f )
|
||||
{
|
||||
battlefront = areaDistanceToTarget;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// find the travel distance from the bomb to the delivery target and use it as the front
|
||||
CCaptureFlag *flag = NULL;
|
||||
Vector vBombSpot(0, 0, 0);
|
||||
for ( int i=0; i<ICaptureFlagAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CCaptureFlag *pTempFlag = static_cast< CCaptureFlag* >( ICaptureFlagAutoList::AutoList()[i] );
|
||||
Vector vTempBombSpot;
|
||||
CTFPlayer *carrier = ToTFPlayer( pTempFlag->GetOwnerEntity() );
|
||||
if ( carrier )
|
||||
{
|
||||
vTempBombSpot = carrier->GetAbsOrigin();
|
||||
}
|
||||
else
|
||||
{
|
||||
vTempBombSpot = pTempFlag->WorldSpaceCenter();
|
||||
}
|
||||
|
||||
CTFNavArea *flagArea = (CTFNavArea *)TheNavMesh->GetNearestNavArea( vTempBombSpot, false, 1000.0f );
|
||||
if ( flagArea )
|
||||
{
|
||||
float flagDistanceToTarget = flagArea->GetTravelDistanceToBombTarget();
|
||||
|
||||
if ( flagDistanceToTarget < battlefront && flagDistanceToTarget >= 0.0f )
|
||||
{
|
||||
battlefront = flagDistanceToTarget;
|
||||
flag = pTempFlag;
|
||||
vBombSpot = vTempBombSpot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float flMaxBattlefront = battlefront + tf_bot_engineer_mvm_sentry_hint_bomb_backward_range.GetFloat();
|
||||
float flMinBattlefront = battlefront - tf_bot_engineer_mvm_sentry_hint_bomb_forward_range.GetFloat();
|
||||
|
||||
if ( pBombInfo )
|
||||
{
|
||||
pBombInfo->m_vPosition = vBombSpot;
|
||||
pBombInfo->m_flMinBattleFront = flMinBattlefront;
|
||||
pBombInfo->m_flMaxBattleFront = flMaxBattlefront;
|
||||
}
|
||||
|
||||
return flag ? true : false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerIdle::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
me->StopLookingAroundForEnemies();
|
||||
|
||||
m_sentryHint = NULL;
|
||||
m_teleporterHint = NULL;
|
||||
m_nestHint = NULL;
|
||||
m_nTeleportedCount = 0;
|
||||
m_bTeleportedToHint = false;
|
||||
m_bTriedToDetonateStaleNest = false;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
void CTFBotMvMEngineerIdle::TakeOverStaleNest( CBaseTFBotHintEntity* pHint, CTFBot *me )
|
||||
{
|
||||
if ( pHint != NULL && pHint->OwnerObjectHasNoOwner() )
|
||||
{
|
||||
CBaseObject* pObj = static_cast< CBaseObject* >( pHint->GetOwnerEntity() );
|
||||
pObj->SetOwnerEntity( me );
|
||||
pObj->SetBuilder( me );
|
||||
me->AddObject( pObj );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CTFBotMvMEngineerIdle::ShouldAdvanceNestSpot( CTFBot *me )
|
||||
{
|
||||
if ( !m_nestHint )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !m_reevaluateNestTimer.HasStarted() )
|
||||
{
|
||||
m_reevaluateNestTimer.Start( 5.f );
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( int i=0; i<me->GetObjectCount(); ++i )
|
||||
{
|
||||
CBaseObject *pObj = me->GetObject( i );
|
||||
if ( pObj && pObj->GetHealth() < pObj->GetMaxHealth() )
|
||||
{
|
||||
// if the nest is under attack, don't advance the nest
|
||||
m_reevaluateNestTimer.Start( 5.f );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_reevaluateNestTimer.IsElapsed() )
|
||||
{
|
||||
m_reevaluateNestTimer.Invalidate();
|
||||
}
|
||||
|
||||
BombInfo_t bombInfo;
|
||||
if ( GetBombInfo( &bombInfo ) )
|
||||
{
|
||||
if ( m_nestHint )
|
||||
{
|
||||
CTFNavArea *hintArea = (CTFNavArea *)TheNavMesh->GetNearestNavArea( m_nestHint->GetAbsOrigin(), false, 1000.0f );
|
||||
if ( hintArea )
|
||||
{
|
||||
float hintDistanceToTarget = hintArea->GetTravelDistanceToBombTarget();
|
||||
|
||||
bool bShouldAdvance = ( hintDistanceToTarget > bombInfo.m_flMaxBattleFront );
|
||||
|
||||
return bShouldAdvance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CTFBotMvMEngineerIdle::TryToDetonateStaleNest()
|
||||
{
|
||||
if ( m_bTriedToDetonateStaleNest )
|
||||
return;
|
||||
|
||||
// wait until the engy finish building his nest
|
||||
if ( ( m_sentryHint && !m_sentryHint->OwnerObjectFinishBuilding() ) ||
|
||||
( m_teleporterHint && !m_teleporterHint->OwnerObjectFinishBuilding() ) )
|
||||
return;
|
||||
|
||||
// collect all existing and active teleporter hints
|
||||
CUtlVector< CTFBotHintEngineerNest* > activeEngineerNest;
|
||||
for ( int i=0; i<ITFBotHintEntityAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CBaseTFBotHintEntity *pHint = static_cast<CBaseTFBotHintEntity*>( ITFBotHintEntityAutoList::AutoList()[i] );
|
||||
if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_ENGINEER_NEST ) && pHint->IsEnabled() && pHint->GetOwnerEntity() == NULL )
|
||||
{
|
||||
activeEngineerNest.AddToTail( static_cast< CTFBotHintEngineerNest* >( pHint ) );
|
||||
}
|
||||
}
|
||||
|
||||
// try to detonate stale nest that's out of range, when engineer finished building his nest
|
||||
for ( int i=0; i<activeEngineerNest.Count(); ++i )
|
||||
{
|
||||
CTFBotHintEngineerNest *pNest = activeEngineerNest[i];
|
||||
if ( pNest->IsStaleNest() )
|
||||
{
|
||||
pNest->DetonateStaleNest();
|
||||
}
|
||||
}
|
||||
|
||||
m_bTriedToDetonateStaleNest = true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerIdle::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !me->IsAlive() )
|
||||
{
|
||||
// don't do anything when I'm dead
|
||||
return Done();
|
||||
}
|
||||
|
||||
// Always equip my wrench
|
||||
CBaseCombatWeapon *wrench = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( wrench )
|
||||
{
|
||||
me->Weapon_Switch( wrench );
|
||||
}
|
||||
|
||||
if ( m_nestHint == NULL || ShouldAdvanceNestSpot( me ) )
|
||||
{
|
||||
if ( m_findHintTimer.HasStarted() && !m_findHintTimer.IsElapsed() )
|
||||
{
|
||||
// too soon
|
||||
return Continue();
|
||||
}
|
||||
|
||||
m_findHintTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
// figure out where to teleport into the map
|
||||
bool bShouldTeleportToHint = me->HasAttribute( CTFBot::TELEPORT_TO_HINT );
|
||||
bool bShouldCheckForBlockingObject = !m_bTeleportedToHint && bShouldTeleportToHint;
|
||||
CHandle< CTFBotHintEngineerNest > newNest = NULL;
|
||||
if ( !CTFBotMvMEngineerHintFinder::FindHint( bShouldCheckForBlockingObject, !bShouldTeleportToHint, &newNest ) )
|
||||
{
|
||||
// try again next time
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// unown the old nest
|
||||
if ( m_nestHint )
|
||||
{
|
||||
m_nestHint->SetOwnerEntity( NULL );
|
||||
}
|
||||
|
||||
m_nestHint = newNest;
|
||||
m_nestHint->SetOwnerEntity( me );
|
||||
m_sentryHint = m_nestHint->GetSentryHint();
|
||||
TakeOverStaleNest( m_sentryHint, me );
|
||||
|
||||
if ( me->GetTeleportWhere().Count() > 0 )
|
||||
{
|
||||
m_teleporterHint = m_nestHint->GetTeleporterHint();
|
||||
TakeOverStaleNest( m_teleporterHint, me );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_bTeleportedToHint && me->HasAttribute( CTFBot::TELEPORT_TO_HINT ) )
|
||||
{
|
||||
m_nTeleportedCount++;
|
||||
bool bFirstTeleportSpawn = m_nTeleportedCount == 1;
|
||||
m_bTeleportedToHint = true;
|
||||
return SuspendFor( new CTFBotMvMEngineerTeleportSpawn( m_nestHint, bFirstTeleportSpawn ), "In spawn area - teleport to the teleporter hint" );
|
||||
}
|
||||
|
||||
const float rebuildInterval = 3.0f;
|
||||
CObjectSentrygun *mySentry = NULL;
|
||||
if ( m_sentryHint )
|
||||
{
|
||||
if ( m_sentryHint->GetOwnerEntity() && m_sentryHint->GetOwnerEntity()->IsBaseObject() )
|
||||
{
|
||||
mySentry = assert_cast< CObjectSentrygun* >( m_sentryHint->GetOwnerEntity() );
|
||||
}
|
||||
|
||||
if ( mySentry )
|
||||
{
|
||||
// force an interval between sentry being destroyed and me trying to rebuild it
|
||||
m_sentryRebuildTimer.Start( rebuildInterval );
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if there's a stale object on the hint
|
||||
if ( m_sentryHint->GetOwnerEntity() && m_sentryHint->GetOwnerEntity()->IsBaseObject() )
|
||||
{
|
||||
mySentry = assert_cast< CObjectSentrygun* >( m_sentryHint->GetOwnerEntity() );
|
||||
me->AddObject( mySentry );
|
||||
mySentry->SetOwnerEntity( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_sentryRebuildTimer.IsElapsed() )
|
||||
{
|
||||
return SuspendFor( new CTFBotMvMEngineerBuildSentryGun( m_sentryHint ), "No sentry - building a new one" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// run away!
|
||||
return SuspendFor( new CTFBotRetreatToCover( 1.0f ), "Lost my sentry - retreat!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( mySentry && mySentry->GetHealth() < mySentry->GetMaxHealth() && !mySentry->IsBuilding() )
|
||||
{
|
||||
// track when sentry was last hurt
|
||||
m_sentryInjuredTimer.Start( 3.0f );
|
||||
}
|
||||
|
||||
|
||||
CObjectTeleporter *myTeleporter = NULL;
|
||||
if ( m_teleporterHint && m_sentryInjuredTimer.IsElapsed() )
|
||||
{
|
||||
if ( m_teleporterHint->GetOwnerEntity() && m_teleporterHint->GetOwnerEntity()->IsBaseObject() )
|
||||
{
|
||||
// force an interval between teleporter being destroyed and me trying to rebuild it
|
||||
myTeleporter = assert_cast< CObjectTeleporter* >( m_teleporterHint->GetOwnerEntity() );
|
||||
m_teleporterRebuildTimer.Start( rebuildInterval );
|
||||
}
|
||||
else if ( m_teleporterRebuildTimer.IsElapsed() )
|
||||
{
|
||||
return SuspendFor( new CTFBotMvMEngineerBuildTeleportExit( m_teleporterHint ), "Sentry is safe - building a teleport exit" );
|
||||
}
|
||||
}
|
||||
|
||||
// fix teleporter if sentry is not hurt
|
||||
if ( myTeleporter && m_sentryInjuredTimer.IsElapsed() && myTeleporter->GetHealth() < myTeleporter->GetMaxHealth() && !myTeleporter->IsBuilding() )
|
||||
{
|
||||
float rangeToTeleporter = me->GetDistanceBetween( myTeleporter );
|
||||
|
||||
const float nearTeleporterRange = 75.0f;
|
||||
|
||||
if ( rangeToTeleporter < 1.2f * nearTeleporterRange )
|
||||
{
|
||||
// crouch as I get close
|
||||
me->PressCrouchButton();
|
||||
}
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
Vector toTeleporter = myTeleporter->GetAbsOrigin() - me->GetAbsOrigin();
|
||||
Vector hittingTeleporterSpot = myTeleporter->GetAbsOrigin() - 50.0f * toTeleporter.Normalized();
|
||||
|
||||
CTFBotPathCost cost( me, SAFEST_ROUTE );
|
||||
m_path.Compute( me, hittingTeleporterSpot, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( rangeToTeleporter < nearTeleporterRange )
|
||||
{
|
||||
// we are in position - hit sentry with wrench
|
||||
me->GetBodyInterface()->AimHeadTowards( myTeleporter->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Work on my Teleporter" );
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
else if ( mySentry )
|
||||
{
|
||||
float rangeToSentry = me->GetDistanceBetween( mySentry );
|
||||
|
||||
const float nearSentryRange = 75.0f;
|
||||
|
||||
if ( rangeToSentry < 1.2f * nearSentryRange )
|
||||
{
|
||||
// crouch as I get close
|
||||
me->PressCrouchButton();
|
||||
}
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
Vector mySentryForward;
|
||||
AngleVectors( mySentry->GetTurretAngles(), &mySentryForward );
|
||||
|
||||
Vector behindSentrySpot = mySentry->GetAbsOrigin() - 50.0f * mySentryForward;
|
||||
|
||||
CTFBotPathCost cost( me, SAFEST_ROUTE );
|
||||
m_path.Compute( me, behindSentrySpot, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( rangeToSentry < nearSentryRange )
|
||||
{
|
||||
// we are in position - hit sentry with wrench
|
||||
me->GetBodyInterface()->AimHeadTowards( mySentry->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Work on my Sentry" );
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
|
||||
TryToDetonateStaleNest();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotMvMEngineerIdle::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotMvMEngineerIdle::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotMvMEngineerIdle::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
CTFBotHintEngineerNest* SelectOutOfRangeNest( const CUtlVector< CTFBotHintEngineerNest* >& nestVector )
|
||||
{
|
||||
if ( nestVector.Count() )
|
||||
{
|
||||
for ( int i=0; i<nestVector.Count(); ++i )
|
||||
{
|
||||
if ( nestVector[i]->IsStaleNest() )
|
||||
{
|
||||
return nestVector[i];
|
||||
}
|
||||
}
|
||||
|
||||
int which = RandomInt( 0, nestVector.Count() - 1 );
|
||||
return nestVector[which];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotMvMEngineerHintFinder::FindHint( bool bShouldCheckForBlockingObjects, bool bAllowOutOfRangeNest, CHandle< CTFBotHintEngineerNest >* pFoundNest /*= NULL*/ )
|
||||
{
|
||||
// collect all existing and active teleporter hints
|
||||
CUtlVector< CTFBotHintEngineerNest* > activeEngineerNest;
|
||||
for ( int i=0; i<ITFBotHintEntityAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CBaseTFBotHintEntity *pHint = static_cast<CBaseTFBotHintEntity*>( ITFBotHintEntityAutoList::AutoList()[i] );
|
||||
if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_ENGINEER_NEST ) && pHint->IsEnabled() && pHint->GetOwnerEntity() == NULL )
|
||||
{
|
||||
activeEngineerNest.AddToTail( static_cast< CTFBotHintEngineerNest* >( pHint ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( activeEngineerNest.Count() == 0 )
|
||||
{
|
||||
if ( pFoundNest )
|
||||
{
|
||||
*pFoundNest = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BombInfo_t bombInfo;
|
||||
GetBombInfo( &bombInfo );
|
||||
|
||||
CUtlVector< CTFBotHintEngineerNest* > forwardOutOfRangeHintVector;
|
||||
CUtlVector< CTFBotHintEngineerNest* > backwardOutOfRangeHintVector;
|
||||
|
||||
CUtlVector< CTFBotHintEngineerNest* > freeAtFrontHintVector;
|
||||
CUtlVector< CTFBotHintEngineerNest* > staleAtFrontHintVector;
|
||||
for( int i=0; i<activeEngineerNest.Count(); ++i )
|
||||
{
|
||||
CTFBotHintEngineerNest* pCurrentNest = activeEngineerNest[i];
|
||||
const Vector& vNestPosition = pCurrentNest->GetAbsOrigin();
|
||||
CTFNavArea *hintArea = (CTFNavArea *)TheNavMesh->GetNearestNavArea( vNestPosition, false, 1000.0f );
|
||||
if ( !hintArea )
|
||||
{
|
||||
Warning( "Sentry hint has NULL nav area!\n" );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
float hintDistanceToTarget = hintArea->GetTravelDistanceToBombTarget();
|
||||
if ( hintDistanceToTarget > bombInfo.m_flMinBattleFront && hintDistanceToTarget < bombInfo.m_flMaxBattleFront )
|
||||
{
|
||||
if ( bShouldCheckForBlockingObjects )
|
||||
{
|
||||
// check for blocking players and objects
|
||||
CBaseEntity *pList[256];
|
||||
int count = UTIL_EntitiesInBox( pList, ARRAYSIZE( pList ), vNestPosition + VEC_HULL_MIN, vNestPosition + VEC_HULL_MAX, FL_CLIENT|FL_OBJECT );
|
||||
if ( count > 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// this hint is in range of the front
|
||||
if ( pCurrentNest->IsStaleNest() )
|
||||
{
|
||||
// some dead engineer was here and left his object(s) behind. I should take over
|
||||
staleAtFrontHintVector.AddToTail( pCurrentNest );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( VectorLength( bombInfo.m_vPosition - vNestPosition ) < tf_bot_engineer_mvm_hint_min_distance_from_bomb.GetFloat() )
|
||||
{
|
||||
// the hint is too close to the bomb, don't go there
|
||||
continue;
|
||||
}
|
||||
// this hint is also unowned
|
||||
freeAtFrontHintVector.AddToTail( pCurrentNest );
|
||||
}
|
||||
}
|
||||
else if ( hintDistanceToTarget > bombInfo.m_flMaxBattleFront )
|
||||
{
|
||||
forwardOutOfRangeHintVector.AddToTail( pCurrentNest );
|
||||
}
|
||||
else
|
||||
{
|
||||
backwardOutOfRangeHintVector.AddToTail( pCurrentNest );
|
||||
}
|
||||
}
|
||||
|
||||
CTFBotHintEngineerNest *hint = NULL;
|
||||
if ( freeAtFrontHintVector.Count() == 0 && staleAtFrontHintVector.Count() == 0 )
|
||||
{
|
||||
if ( bAllowOutOfRangeNest )
|
||||
{
|
||||
// try to advance forward before falling backward
|
||||
hint = SelectOutOfRangeNest( forwardOutOfRangeHintVector );
|
||||
if ( !hint )
|
||||
{
|
||||
hint = SelectOutOfRangeNest( backwardOutOfRangeHintVector );
|
||||
}
|
||||
}
|
||||
|
||||
// no hints are in range, or they are all in use
|
||||
if ( pFoundNest )
|
||||
{
|
||||
*pFoundNest = hint;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// try to pick stale nest in range first
|
||||
if ( staleAtFrontHintVector.Count() )
|
||||
{
|
||||
int whichHint = RandomInt( 0, staleAtFrontHintVector.Count()-1 );
|
||||
hint = staleAtFrontHintVector[ whichHint ];
|
||||
}
|
||||
// if I didn't find any stale nest, try to find a free one
|
||||
else if ( freeAtFrontHintVector.Count() )
|
||||
{
|
||||
int whichHint = RandomInt( 0, freeAtFrontHintVector.Count()-1 );
|
||||
hint = freeAtFrontHintVector[ whichHint ];
|
||||
}
|
||||
|
||||
if ( pFoundNest )
|
||||
{
|
||||
*pFoundNest = hint;
|
||||
}
|
||||
}
|
||||
|
||||
return hint != NULL;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
CON_COMMAND_F( tf_bot_mvm_show_engineer_hint_region, "Show the nav areas MvM engineer bots will consider when selecting sentry and teleporter hints", FCVAR_CHEAT )
|
||||
{
|
||||
if ( !UTIL_IsCommandIssuedByServerAdmin() )
|
||||
return;
|
||||
|
||||
CBasePlayer *pPlayer = UTIL_GetCommandClient();
|
||||
|
||||
trace_t result;
|
||||
Vector forward;
|
||||
pPlayer->EyeVectors( &forward );
|
||||
|
||||
UTIL_TraceLine( pPlayer->EyePosition(),
|
||||
pPlayer->EyePosition() + forward * 10000.0f, MASK_SOLID,
|
||||
pPlayer, COLLISION_GROUP_NONE, &result );
|
||||
|
||||
float flDrawTime = 5.0f;
|
||||
|
||||
if ( result.DidHit() )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)TheTFNavMesh()->GetNearestNavArea( result.endpos );
|
||||
|
||||
if ( area )
|
||||
{
|
||||
float battlefront = area->GetTravelDistanceToBombTarget();
|
||||
|
||||
float maxBattlefront = battlefront + tf_bot_engineer_mvm_sentry_hint_bomb_backward_range.GetFloat();
|
||||
float minBattlefront = battlefront - tf_bot_engineer_mvm_sentry_hint_bomb_forward_range.GetFloat();
|
||||
|
||||
CUtlVector< CTFNavArea * > battlefrontAreaVector;
|
||||
TheTFNavMesh()->CollectAreaWithinBombTravelRange( &battlefrontAreaVector, minBattlefront, maxBattlefront );
|
||||
|
||||
CUtlVector< CTFNavArea * > hintAreaVector;
|
||||
for ( int i=0; i<ITFBotHintEntityAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CBaseTFBotHintEntity *pHint = static_cast< CBaseTFBotHintEntity* >( ITFBotHintEntityAutoList::AutoList()[i] );
|
||||
hintAreaVector.AddToTail( (CTFNavArea*)TheNavMesh->GetNearestNavArea( pHint ) );
|
||||
}
|
||||
|
||||
for( int i=0; i<battlefrontAreaVector.Count(); ++i )
|
||||
{
|
||||
CTFNavArea *fillArea = battlefrontAreaVector[i];
|
||||
|
||||
if ( fillArea->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE || TF_NAV_SPAWN_ROOM_RED ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fillArea->DrawFilled( 255, 100, 0, 0, flDrawTime );
|
||||
|
||||
for ( int j=0; j<hintAreaVector.Count(); ++j )
|
||||
{
|
||||
if ( fillArea == hintAreaVector[j] )
|
||||
{
|
||||
CBaseTFBotHintEntity *pHint = static_cast< CBaseTFBotHintEntity* >( ITFBotHintEntityAutoList::AutoList()[j] );
|
||||
Color color;
|
||||
if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_SENTRYGUN ) )
|
||||
{
|
||||
color = Color( 0, 255, 0 );
|
||||
}
|
||||
else if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_TELEPORTER_EXIT ) )
|
||||
{
|
||||
color = Color( 0, 0, 255 );
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bTooCloseToBomb = VectorLength( result.endpos - pHint->GetAbsOrigin() ) < tf_bot_engineer_mvm_hint_min_distance_from_bomb.GetFloat();
|
||||
color = bTooCloseToBomb ? Color( 255, 0, 0 ) : Color( 255, 255, 0 );
|
||||
}
|
||||
NDebugOverlay::Sphere( pHint->GetAbsOrigin(), 50, color.r(), color.g(), color.b(), true, flDrawTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDebugOverlay::Sphere( result.endpos, tf_bot_engineer_mvm_hint_min_distance_from_bomb.GetFloat(), 255, 255, 0, false, flDrawTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// Michael Booth, September 2012
|
||||
|
||||
#ifndef TF_BOT_MVM_ENGINEER_IDLE_H
|
||||
#define TF_BOT_MVM_ENGINEER_IDLE_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CBaseTFBotHintEntity;
|
||||
class CTFBotHintSentrygun;
|
||||
class CTFBotHintTeleporterExit;
|
||||
class CTFBotHintEngineerNest;
|
||||
|
||||
class CTFBotMvMEngineerIdle : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const;
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
|
||||
virtual const char *GetName( void ) const { return "MvMEngineerIdle"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
CountdownTimer m_sentryInjuredTimer;
|
||||
CountdownTimer m_sentryRebuildTimer;
|
||||
CountdownTimer m_teleporterRebuildTimer;
|
||||
CountdownTimer m_findHintTimer;
|
||||
CountdownTimer m_reevaluateNestTimer;
|
||||
|
||||
int m_nTeleportedCount;
|
||||
bool m_bTeleportedToHint;
|
||||
CHandle< CTFBotHintTeleporterExit > m_teleporterHint;
|
||||
CHandle< CTFBotHintSentrygun > m_sentryHint;
|
||||
CHandle< CTFBotHintEngineerNest > m_nestHint;
|
||||
|
||||
void TakeOverStaleNest( CBaseTFBotHintEntity* pHint, CTFBot *me );
|
||||
bool ShouldAdvanceNestSpot( CTFBot *me );
|
||||
|
||||
void TryToDetonateStaleNest();
|
||||
bool m_bTriedToDetonateStaleNest;
|
||||
};
|
||||
|
||||
class CTFBotMvMEngineerHintFinder
|
||||
{
|
||||
public:
|
||||
static bool FindHint( bool bShouldCheckForBlockingObjects, bool bAllowOutOfRangeNest, CHandle< CTFBotHintEngineerNest >* pFoundNest = NULL );
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_MVM_ENGINEER_IDLE_H
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_teleport_spawn.h"
|
||||
#include "bot/map_entities/tf_bot_hint_entity.h"
|
||||
#include "string_t.h"
|
||||
#include "tf_fx.h"
|
||||
#include "player_vs_environment/tf_population_manager.h"
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMvMEngineerTeleportSpawn::CTFBotMvMEngineerTeleportSpawn( CBaseTFBotHintEntity* pHint, bool bFirstTeleportSpawn )
|
||||
{
|
||||
m_hintEntity = pHint;
|
||||
m_bFirstTeleportSpawn = bFirstTeleportSpawn;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerTeleportSpawn::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
if ( !me->HasAttribute( CTFBot::TELEPORT_TO_HINT ) )
|
||||
{
|
||||
return Done( "Cannot teleport to hint with out Attributes TeleportToHint" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMvMEngineerTeleportSpawn::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !m_teleportDelay.HasStarted() )
|
||||
{
|
||||
m_teleportDelay.Start( 0.1f );
|
||||
if ( m_hintEntity )
|
||||
TFGameRules()->PushAllPlayersAway( m_hintEntity->GetAbsOrigin(), 400, 500, TF_TEAM_RED );
|
||||
}
|
||||
else if ( m_teleportDelay.IsElapsed() )
|
||||
{
|
||||
if ( !m_hintEntity )
|
||||
return Done( "Cannot teleport to hint as m_hintEntity is NULL" );
|
||||
|
||||
// teleport the engineer to the sentry spawn point
|
||||
QAngle angles = m_hintEntity->GetAbsAngles();
|
||||
Vector origin = m_hintEntity->GetAbsOrigin();
|
||||
origin.z += 10.f; // move up off the around a little bit to prevent the engineer from getting stuck in the ground
|
||||
|
||||
me->Teleport( &origin, &angles, NULL );
|
||||
|
||||
CPVSFilter filter( origin );
|
||||
TE_TFParticleEffect( filter, 0.0, "teleported_blue", origin, vec3_angle );
|
||||
TE_TFParticleEffect( filter, 0.0, "player_sparkles_blue", origin, vec3_angle );
|
||||
|
||||
if ( m_bFirstTeleportSpawn )
|
||||
{
|
||||
// notify players that engineer's teleported into the map
|
||||
TE_TFParticleEffect( filter, 0.0, "teleported_mvm_bot", origin, vec3_angle );
|
||||
me->EmitSound( "Engineer.MVM_BattleCry07" );
|
||||
m_hintEntity->EmitSound( "MVM.Robot_Engineer_Spawn" );
|
||||
|
||||
if ( g_pPopulationManager )
|
||||
{
|
||||
CWave *pWave = g_pPopulationManager->GetCurrentWave();
|
||||
if ( pWave )
|
||||
{
|
||||
if ( pWave->NumEngineersTeleportSpawned() == 0 )
|
||||
{
|
||||
TFGameRules()->BroadcastSound( 255, "Announcer.MVM_First_Engineer_Teleport_Spawned" );
|
||||
}
|
||||
else
|
||||
{
|
||||
TFGameRules()->BroadcastSound( 255, "Announcer.MVM_Another_Engineer_Teleport_Spawned" );
|
||||
}
|
||||
|
||||
pWave->IncrementEngineerTeleportSpawned();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Done( "Teleported" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_BOT_MVM_ENGINEER_TELEPORT_SPAWN_H
|
||||
#define TF_BOT_MVM_ENGINEER_TELEPORT_SPAWN_H
|
||||
|
||||
class CBaseTFBotHintEntity;
|
||||
|
||||
class CTFBotMvMEngineerTeleportSpawn : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMvMEngineerTeleportSpawn( CBaseTFBotHintEntity* pHint, bool bFirstTeleportSpawn );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "MvMEngineerTeleportSpawn"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_teleportDelay;
|
||||
CHandle< CBaseTFBotHintEntity > m_hintEntity;
|
||||
bool m_bFirstTeleportSpawn;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_MVM_ENGINEER_TELEPORT_SPAWN_H
|
||||
@@ -0,0 +1,118 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build.cpp
|
||||
// Engineer building his buildings
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_teleport_entrance.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
|
||||
|
||||
#include "raid/tf_raid_logic.h"
|
||||
|
||||
// this was useful when engineers build at their normal (slow) rate to make sure initial sentries get built in time
|
||||
ConVar tf_raid_engineer_infinte_metal( "tf_raid_engineer_infinte_metal", "1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
Action< CTFBot > *CTFBotEngineerBuild::InitialContainedAction( CTFBot *me )
|
||||
{
|
||||
if ( TFGameRules()->IsPVEModeActive() )
|
||||
{
|
||||
return new CTFBotEngineerMoveToBuild;
|
||||
}
|
||||
|
||||
return new CTFBotEngineerBuildTeleportEntrance;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuild::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuild::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( TFGameRules()->IsPVEModeActive() && tf_raid_engineer_infinte_metal.GetBool() )
|
||||
{
|
||||
// infinite ammo
|
||||
me->GiveAmmo( 1000, TF_AMMO_METAL, true );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuild::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerBuild::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Hack to disable ammo/health gathering elsewhere
|
||||
QueryResultType CTFBotEngineerBuild::ShouldHurry( const INextBot *meBot ) const
|
||||
{
|
||||
CTFBot *me = (CTFBot *)meBot->GetEntity();
|
||||
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
CObjectDispenser *myDispenser = (CObjectDispenser *)me->GetObjectOfType( OBJ_DISPENSER );
|
||||
|
||||
if ( mySentry && myDispenser && !mySentry->IsBuilding() && !myDispenser->IsBuilding() && me->GetActiveTFWeapon() && me->GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_WRENCH )
|
||||
{
|
||||
if ( me->IsAmmoLow() && myDispenser->GetAvailableMetal() <= 0 )
|
||||
{
|
||||
// we're totally out of metal - collect some nearby
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
// by being in a "hurry" we wont collect health and ammo
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotEngineerBuild::ShouldAttack( const INextBot *meBot, const CKnownEntity *them ) const
|
||||
{
|
||||
CTFBot *me = (CTFBot *)meBot->GetEntity();
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
|
||||
CTFPlayer *themPlayer = ToTFPlayer( them->GetEntity() );
|
||||
|
||||
if ( themPlayer && themPlayer->IsPlayerClass( TF_CLASS_SPY ) )
|
||||
{
|
||||
// Engineers hate Spies
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
if ( mySentry && me->IsRangeLessThan( mySentry, 100.0f ) )
|
||||
{
|
||||
// focus on keeping our sentry alive
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build.h
|
||||
// Engineer building his buildings
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILD_H
|
||||
#define TF_BOT_ENGINEER_BUILD_H
|
||||
|
||||
class CTFBotHintTeleporterExit;
|
||||
|
||||
|
||||
class CTFBotEngineerBuild : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual Action< CTFBot > *InitialContainedAction( CTFBot *me );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuild"; };
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILD_H
|
||||
@@ -0,0 +1,212 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_dispenser.cpp
|
||||
// Engineer building his Dispenser near his Sentry
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_dispenser.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildDispenser::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_placementTriesLeft = 3;
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class PressFireButtonIfValidBuildPositionReply : public INextBotReply
|
||||
{
|
||||
public:
|
||||
PressFireButtonIfValidBuildPositionReply( void )
|
||||
{
|
||||
m_builder = NULL;
|
||||
}
|
||||
|
||||
void SetBuilder( CTFWeaponBuilder *builder )
|
||||
{
|
||||
m_builder = builder;
|
||||
}
|
||||
|
||||
// invoked when process completed successfully
|
||||
virtual void OnSuccess( INextBot *bot )
|
||||
{
|
||||
if ( m_builder != NULL && m_builder->IsValidPlacement() )
|
||||
{
|
||||
INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
|
||||
if ( playerInput )
|
||||
{
|
||||
playerInput->PressFireButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CTFWeaponBuilder *m_builder;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildDispenser::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
return Done( "Ouch! I'm under attack" );
|
||||
}
|
||||
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
if ( !mySentry )
|
||||
{
|
||||
return Done( "No Sentry" );
|
||||
}
|
||||
|
||||
if ( mySentry->GetTimeSinceLastInjury() < 1.0f || mySentry->GetHealth() < mySentry->GetMaxHealth() )
|
||||
{
|
||||
return Done( "Need to repair my Sentry" );
|
||||
}
|
||||
|
||||
CObjectDispenser *myDispenser = (CObjectDispenser *)me->GetObjectOfType( OBJ_DISPENSER );
|
||||
if ( myDispenser )
|
||||
{
|
||||
return Done( "Dispenser built" );
|
||||
}
|
||||
|
||||
if ( m_placementTriesLeft <= 0 )
|
||||
{
|
||||
return Done( "Can't find a place to build a Dispenser" );
|
||||
}
|
||||
|
||||
if ( me->CanBuild( OBJ_DISPENSER ) == CB_NEED_RESOURCES )
|
||||
{
|
||||
if ( m_getAmmoTimer.IsElapsed() && CTFBotGetAmmo::IsPossible( me ) )
|
||||
{
|
||||
// need more metal - get some
|
||||
m_getAmmoTimer.Start( 1.0f );
|
||||
return SuspendFor( new CTFBotGetAmmo, "Need more metal to build" );
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
// work on my sentry while I wait for ammo to show up
|
||||
me->GetBodyInterface()->AimHeadTowards( mySentry->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Work on sentry while I wait for ammo to show up" );
|
||||
me->PressFireButton();
|
||||
return Continue();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// if my sentry is under attack, forgo building a dispenser - focus on keeping the sentry alive
|
||||
if ( mySentry->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
CBaseCombatWeapon *wrench = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( wrench )
|
||||
{
|
||||
me->Weapon_Switch( wrench );
|
||||
}
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( mySentry->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Focusing on keeping my besieged sentry alive" );
|
||||
me->PressFireButton();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// move behind the Sentry (our chosen build location)
|
||||
Vector buildSpot = mySentry->GetAbsOrigin() - 75.0f * mySentry->BodyDirection2D();
|
||||
|
||||
// the ground might be steeply sloped (ie: stairs), so find the actual ground
|
||||
buildSpot.z += HumanHeight;
|
||||
TheNavMesh->GetSimpleGroundHeight( buildSpot, &buildSpot.z );
|
||||
|
||||
if ( me->IsDistanceBetweenLessThan( buildSpot, 100.0f ) )
|
||||
{
|
||||
// crouch as we get close so we slow down and hit our mark
|
||||
me->PressCrouchButton();
|
||||
}
|
||||
|
||||
// if too far away from our build location, move closer
|
||||
if ( me->IsDistanceBetweenGreaterThan( buildSpot, 25.0f ) )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, buildSpot, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// we're at our build spot behind our sentry now - build a Dispenser
|
||||
CTFWeaponBuilder *builder = dynamic_cast< CTFWeaponBuilder * >( me->GetActiveTFWeapon() );
|
||||
if ( !builder || builder->GetType() != OBJ_DISPENSER || builder->m_hObjectBeingBuilt == NULL )
|
||||
{
|
||||
// at home position, build the object
|
||||
me->StartBuildingObjectOfType( OBJ_DISPENSER );
|
||||
}
|
||||
else if ( m_searchTimer.IsElapsed() )
|
||||
{
|
||||
// rotate to find valid spot
|
||||
Vector toSentry = mySentry->GetAbsOrigin() - me->GetAbsOrigin();
|
||||
toSentry.NormalizeInPlace();
|
||||
|
||||
Vector forward = -toSentry;
|
||||
|
||||
float angle = RandomFloat( -M_PI/2.0f, M_PI/2.0f );
|
||||
float s, c;
|
||||
FastSinCos( angle, &s, &c );
|
||||
|
||||
forward.x = toSentry.x * c - toSentry.y * s;
|
||||
forward.y = toSentry.x * s + toSentry.y * c;
|
||||
forward.z = 0.0f;
|
||||
|
||||
static PressFireButtonIfValidBuildPositionReply buildReply;
|
||||
|
||||
buildReply.SetBuilder( builder );
|
||||
me->GetBodyInterface()->AimHeadTowards( me->EyePosition() - 100.0f * forward, IBody::CRITICAL, 1.0f, &buildReply, "Trying to place my dispenser" );
|
||||
|
||||
m_searchTimer.Start( 1.0f );
|
||||
|
||||
--m_placementTriesLeft;
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotEngineerBuildDispenser::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
me->GetBodyInterface()->ClearPendingAimReply();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildDispenser::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
m_repathTimer.Invalidate();
|
||||
me->GetBodyInterface()->ClearPendingAimReply();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_dispenser.h
|
||||
// Engineer building his Dispenser near his Sentry
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILD_DISPENSER_H
|
||||
#define TF_BOT_ENGINEER_BUILD_DISPENSER_H
|
||||
|
||||
|
||||
class CTFBotEngineerBuildDispenser : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuildDispenser"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_searchTimer;
|
||||
CountdownTimer m_getAmmoTimer;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
int m_placementTriesLeft;
|
||||
PathFollower m_path;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILD_DISPENSER_H
|
||||
@@ -0,0 +1,194 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_sentrygun.cpp
|
||||
// Engineer building his Sentry gun
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_sentrygun.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
#include "bot/map_entities/tf_bot_hint_sentrygun.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuildSentryGun::CTFBotEngineerBuildSentryGun( void )
|
||||
{
|
||||
m_sentryBuildHint = NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuildSentryGun::CTFBotEngineerBuildSentryGun( CTFBotHintSentrygun *sentryBuildHint )
|
||||
{
|
||||
m_sentryBuildHint = sentryBuildHint;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildSentryGun::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_sentryTriesLeft = 5;
|
||||
m_giveUpTimer.Invalidate();
|
||||
|
||||
m_searchTimer.Invalidate();
|
||||
m_wanderWay = 1;
|
||||
m_needToAimSentry = true;
|
||||
|
||||
m_sentryBuildLocation = ( m_sentryBuildHint == NULL ) ? me->GetAbsOrigin() : m_sentryBuildHint->GetAbsOrigin();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildSentryGun::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
return Done( "Ouch! I'm under attack" );
|
||||
}
|
||||
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
if ( mySentry )
|
||||
{
|
||||
return Done( "Sentry built" );
|
||||
}
|
||||
|
||||
// collect metal as we move to our build location
|
||||
if ( me->CanBuild( OBJ_SENTRYGUN ) == CB_NEED_RESOURCES )
|
||||
{
|
||||
if ( m_getAmmoTimer.IsElapsed() && CTFBotGetAmmo::IsPossible( me ) )
|
||||
{
|
||||
// need more metal - get some
|
||||
m_getAmmoTimer.Start( 1.0f );
|
||||
return SuspendFor( new CTFBotGetAmmo, "Need more metal to build my Sentry" );
|
||||
}
|
||||
}
|
||||
|
||||
// various interruptions could mean we're away from our build location - move to it
|
||||
if ( me->IsRangeGreaterThan( m_sentryBuildLocation, 25.0f ) )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, m_sentryBuildLocation, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( !m_path.IsValid() )
|
||||
{
|
||||
return Done( "Path failed" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// we are at our build location
|
||||
if ( m_sentryTriesLeft <= 0 )
|
||||
{
|
||||
// couldn't build here
|
||||
return Done( "Couldn't find a place to build" );
|
||||
}
|
||||
|
||||
// attempt to build a Sentry here
|
||||
if ( m_sentryBuildHint != NULL )
|
||||
{
|
||||
// directly create a sentry gun at the precise position and orientation desired
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)CreateEntityByName( "obj_sentrygun" );
|
||||
if ( mySentry )
|
||||
{
|
||||
m_sentryBuildHint->IncrementUseCount();
|
||||
|
||||
mySentry->SetAbsOrigin( m_sentryBuildHint->GetAbsOrigin() );
|
||||
mySentry->SetAbsAngles( QAngle( 0, m_sentryBuildHint->GetAbsAngles().y, 0 ) );
|
||||
mySentry->Spawn();
|
||||
|
||||
mySentry->StartPlacement( me );
|
||||
mySentry->StartBuilding( me );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no precise build location - go through the normal build process
|
||||
|
||||
CTFWeaponBuilder *builder = dynamic_cast< CTFWeaponBuilder * >( me->GetActiveTFWeapon() );
|
||||
if ( !builder || builder->GetType() != OBJ_SENTRYGUN || builder->m_hObjectBeingBuilt == NULL )
|
||||
{
|
||||
// at home position, build a sentry (switch to the sentry builder "gun")
|
||||
me->StartBuildingObjectOfType( OBJ_SENTRYGUN );
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// orient sentry towards where enemies enter this region
|
||||
if ( m_needToAimSentry )
|
||||
{
|
||||
CTFNavArea *myArea = (CTFNavArea *)me->GetLastKnownArea();
|
||||
if ( myArea )
|
||||
{
|
||||
CUtlVector< CTFNavArea * > invasionVector;
|
||||
myArea->GetEnemyInvasionAreaVector( me->GetTeamNumber() );
|
||||
|
||||
if ( invasionVector.Count() > 0 )
|
||||
{
|
||||
// orient sentry towards where enemies enter this region
|
||||
int which = RandomInt( 0, invasionVector.Count()-1 );
|
||||
me->GetBodyInterface()->AimHeadTowards( invasionVector[ which ]->GetCenter(), IBody::CRITICAL, 1.0f, NULL, "Sentry build orientation" );
|
||||
m_needToAimSentry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->GetBodyInterface()->IsHeadSteady() )
|
||||
{
|
||||
if ( builder->IsValidPlacement() )
|
||||
{
|
||||
// build the sentry
|
||||
me->PressFireButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
// move around a bit to find valid spot
|
||||
if ( m_searchTimer.IsElapsed() )
|
||||
{
|
||||
m_wanderWay = RandomInt( 0, 3 );
|
||||
m_needToAimSentry = true;
|
||||
m_searchTimer.Start( RandomFloat( 0.1f, 0.25f ) );
|
||||
--m_sentryTriesLeft;
|
||||
}
|
||||
|
||||
switch( m_wanderWay )
|
||||
{
|
||||
case 0: me->PressForwardButton(); break;
|
||||
case 1: me->PressBackwardButton(); break;
|
||||
case 2: me->PressRightButton(); break;
|
||||
case 3: me->PressLeftButton(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildSentryGun::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_sentrygun.h
|
||||
// Engineer building his Sentry gun
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILD_SENTRYGUN_H
|
||||
#define TF_BOT_ENGINEER_BUILD_SENTRYGUN_H
|
||||
|
||||
class CTFBotHintSentrygun;
|
||||
|
||||
|
||||
class CTFBotEngineerBuildSentryGun : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotEngineerBuildSentryGun( void );
|
||||
CTFBotEngineerBuildSentryGun( CTFBotHintSentrygun *sentryBuildHint );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuildSentryGun"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_searchTimer;
|
||||
CountdownTimer m_giveUpTimer;
|
||||
CountdownTimer m_getAmmoTimer;
|
||||
CountdownTimer m_repathTimer;
|
||||
CountdownTimer m_buildTeleporterExitTimer;
|
||||
|
||||
int m_sentryTriesLeft;
|
||||
PathFollower m_path;
|
||||
|
||||
CTFBotHintSentrygun *m_sentryBuildHint;
|
||||
Vector m_sentryBuildLocation;
|
||||
|
||||
int m_wanderWay;
|
||||
bool m_needToAimSentry;
|
||||
Vector m_sentryBuildAimTarget;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILD_SENTRYGUN_H
|
||||
@@ -0,0 +1,112 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_teleport_entrance.cpp
|
||||
// Engineer building a teleport entrance right outside of the spawn room
|
||||
// Michael Booth, May 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_teleport_entrance.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_max_teleport_entrance_travel( "tf_bot_max_teleport_entrance_travel", "1500", FCVAR_CHEAT, "Don't plant teleport entrances farther than this travel distance from our spawn room" );
|
||||
ConVar tf_bot_teleport_build_surface_normal_limit( "tf_bot_teleport_build_surface_normal_limit", "0.99", FCVAR_CHEAT, "If the ground normal Z component is less that this value, Engineer bots won't place their entrance teleporter" );
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildTeleportEntrance::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildTeleportEntrance::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( !point )
|
||||
{
|
||||
// wait until a control point becomes available
|
||||
return Continue();
|
||||
}
|
||||
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
|
||||
if ( !myArea )
|
||||
{
|
||||
return Done( "No nav mesh!" );
|
||||
}
|
||||
|
||||
if ( myArea->GetIncursionDistance( me->GetTeamNumber() ) > tf_bot_max_teleport_entrance_travel.GetFloat() )
|
||||
{
|
||||
return ChangeTo( new CTFBotEngineerMoveToBuild, "Too far from our spawn room to build teleporter entrance" );
|
||||
}
|
||||
|
||||
// make sure we go back to our resupply cabinet after planting the teleporter entrance before we move on
|
||||
if ( !me->IsAmmoFull() && CTFBotGetAmmo::IsPossible( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotGetAmmo, "Refilling ammo" );
|
||||
}
|
||||
|
||||
CBaseObject *myTeleportEntrance = me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_ENTRANCE );
|
||||
if ( myTeleportEntrance )
|
||||
{
|
||||
// successfully built
|
||||
return ChangeTo( new CTFBotEngineerMoveToBuild, "Teleport entrance built" );
|
||||
}
|
||||
|
||||
// head towards the control point and build as soon as we can
|
||||
if ( !m_path.IsValid() )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, point->GetAbsOrigin(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
// build
|
||||
CTFWeaponBase *myGun = me->GetActiveTFWeapon();
|
||||
if ( myGun )
|
||||
{
|
||||
CTFWeaponBuilder *builder = dynamic_cast< CTFWeaponBuilder * >( myGun );
|
||||
if ( builder )
|
||||
{
|
||||
// don't build on slopes - causes player blockages
|
||||
Vector forward;
|
||||
me->EyeVectors( &forward );
|
||||
|
||||
const float placementRange = 30.0f;
|
||||
forward *= placementRange;
|
||||
|
||||
trace_t result;
|
||||
UTIL_TraceLine( me->WorldSpaceCenter() + Vector( forward.x, forward.y, 0.0f ), me->WorldSpaceCenter() + Vector( forward.x, forward.y, -200.0f ), MASK_PLAYERSOLID, me, COLLISION_GROUP_NONE, &result );
|
||||
|
||||
if ( builder->IsValidPlacement() && result.DidHit() && result.plane.normal.z > tf_bot_teleport_build_surface_normal_limit.GetFloat() )
|
||||
{
|
||||
// place it down
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// switch to teleporter builder
|
||||
me->StartBuildingObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_ENTRANCE );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerBuildTeleportEntrance::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_teleport_entrance.h
|
||||
// Engineer building a teleport entrance right outside of the spawn room
|
||||
// Michael Booth, May 2009
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILD_TELEPORT_ENTRANCE_H
|
||||
#define TF_BOT_ENGINEER_BUILD_TELEPORT_ENTRANCE_H
|
||||
|
||||
class CTFBotEngineerBuildTeleportEntrance : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuildTeleportEntrance"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILD_TELEPORT_ENTRANCE_H
|
||||
@@ -0,0 +1,190 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_teleport_exit.cpp
|
||||
// Engineer building a teleport exit
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_teleport_exit.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuildTeleportExit::CTFBotEngineerBuildTeleportExit( void )
|
||||
{
|
||||
m_hasPreciseBuildLocation = false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuildTeleportExit::CTFBotEngineerBuildTeleportExit( const Vector &buildLocation, float buildAngle )
|
||||
{
|
||||
m_hasPreciseBuildLocation = true;
|
||||
m_buildLocation = buildLocation;
|
||||
m_buildAngle = buildAngle;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildTeleportExit::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
if ( !m_hasPreciseBuildLocation )
|
||||
{
|
||||
// if no specific build location given, just build right where we are
|
||||
m_buildLocation = me->GetAbsOrigin();
|
||||
}
|
||||
|
||||
m_giveUpTimer.Start( 3.1f );
|
||||
m_path.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildTeleportExit::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
return Done( "Ouch! I'm under attack" );
|
||||
}
|
||||
|
||||
CBaseObject *myTeleportEntrance = me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT );
|
||||
if ( myTeleportEntrance )
|
||||
{
|
||||
// successfully built
|
||||
return Done( "Teleport exit built" );
|
||||
}
|
||||
|
||||
|
||||
// collect metal as we move to our build location
|
||||
if ( me->CanBuild( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT ) == CB_NEED_RESOURCES )
|
||||
{
|
||||
if ( m_getAmmoTimer.IsElapsed() && CTFBotGetAmmo::IsPossible( me ) )
|
||||
{
|
||||
// need more metal - get some
|
||||
m_getAmmoTimer.Start( 1.0f );
|
||||
return SuspendFor( new CTFBotGetAmmo, "Need more metal to build my Teleporter Exit" );
|
||||
}
|
||||
}
|
||||
|
||||
// move near our build position
|
||||
const float buildRange = 50.0f;
|
||||
if ( me->IsRangeGreaterThan( m_buildLocation, buildRange ) )
|
||||
{
|
||||
// move into position
|
||||
if ( !m_path.IsValid() || m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, m_buildLocation, cost );
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
// don't give up until we've reached our build location
|
||||
m_giveUpTimer.Reset();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// in position to build
|
||||
if ( m_giveUpTimer.IsElapsed() )
|
||||
{
|
||||
return Done( "Taking too long - giving up" );
|
||||
}
|
||||
|
||||
if ( m_hasPreciseBuildLocation )
|
||||
{
|
||||
me->GetBodyInterface()->AimHeadTowards( m_buildLocation, IBody::CRITICAL, 1.0f, NULL, "Looking toward my precise build location" );
|
||||
|
||||
// directly create a teleporter exit at the precise position and orientation desired
|
||||
CObjectTeleporter *myTeleporterExit = (CObjectTeleporter *)CreateEntityByName( "obj_teleporter" );
|
||||
if ( myTeleporterExit )
|
||||
{
|
||||
myTeleporterExit->SetObjectMode( MODE_TELEPORTER_EXIT );
|
||||
myTeleporterExit->SetAbsOrigin( m_buildLocation );
|
||||
myTeleporterExit->SetAbsAngles( QAngle( 0, m_buildAngle, 0 ) );
|
||||
myTeleporterExit->Spawn();
|
||||
|
||||
myTeleporterExit->StartPlacement( me );
|
||||
myTeleporterExit->StartBuilding( me );
|
||||
myTeleporterExit->SetBuilder( me );
|
||||
|
||||
// teleporter exits are solid blockers - put engineer on top of exit or he'll be stuck
|
||||
Vector myNewOrigin = me->GetAbsOrigin();
|
||||
myNewOrigin.z += me->GetLocomotionInterface()->GetStepHeight();
|
||||
|
||||
me->SetAbsOrigin( myNewOrigin );
|
||||
|
||||
return Done( "Teleport exit built at precise location" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
// build exit roughly at this spot
|
||||
CTFWeaponBase *myGun = me->GetActiveTFWeapon();
|
||||
if ( myGun )
|
||||
{
|
||||
CTFWeaponBuilder *builder = dynamic_cast< CTFWeaponBuilder * >( myGun );
|
||||
if ( builder )
|
||||
{
|
||||
if ( builder->IsValidPlacement() )
|
||||
{
|
||||
// place it down
|
||||
me->PressFireButton();
|
||||
}
|
||||
else if ( m_searchTimer.IsElapsed() )
|
||||
{
|
||||
// rotate to find valid spot
|
||||
Vector forward;
|
||||
float angle = RandomFloat( -M_PI, M_PI );
|
||||
FastSinCos( angle, &forward.y, &forward.x );
|
||||
|
||||
forward.z = 0.0f;
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( me->EyePosition() - 100.0f * forward, IBody::CRITICAL, 1.0f, NULL, "Trying to place my teleport exit" );
|
||||
|
||||
m_searchTimer.Start( 1.0f );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// switch to teleporter builder
|
||||
me->StartBuildingObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuildTeleportExit::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_giveUpTimer.Reset();
|
||||
m_path.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerBuildTeleportExit::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_build_teleport_exit.h
|
||||
// Engineer building a teleport exit
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILD_TELEPORT_EXIT_H
|
||||
#define TF_BOT_ENGINEER_BUILD_TELEPORT_EXIT_H
|
||||
|
||||
class CTFBotEngineerBuildTeleportExit : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotEngineerBuildTeleportExit( void );
|
||||
CTFBotEngineerBuildTeleportExit( const Vector &buildLocation, float buildAngle );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuildTeleportExit"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
|
||||
bool m_hasPreciseBuildLocation;
|
||||
Vector m_buildLocation;
|
||||
float m_buildAngle;
|
||||
|
||||
CountdownTimer m_giveUpTimer;
|
||||
CountdownTimer m_repathTimer;
|
||||
CountdownTimer m_getAmmoTimer;
|
||||
CountdownTimer m_searchTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILD_TELEPORT_EXIT_H
|
||||
@@ -0,0 +1,497 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_building.cpp
|
||||
// At building location, constructing buildings
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "team_train_watcher.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_building.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_teleport_exit.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_sentrygun.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_dispenser.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
#include "bot/map_entities/tf_bot_hint_teleporter_exit.h"
|
||||
#include "bot/map_entities/tf_bot_hint_sentrygun.h"
|
||||
#include "NextBotUtil.h"
|
||||
|
||||
|
||||
ConVar tf_bot_engineer_retaliate_range( "tf_bot_engineer_retaliate_range", "750", FCVAR_CHEAT, "If attacker who destroyed sentry is closer than this, attack. Otherwise, retreat" );
|
||||
ConVar tf_bot_engineer_exit_near_sentry_range( "tf_bot_engineer_exit_near_sentry_range", "2500", FCVAR_CHEAT, "Maximum travel distance between a bot's Sentry gun and its Teleporter Exit" );
|
||||
ConVar tf_bot_engineer_max_sentry_travel_distance_to_point( "tf_bot_engineer_max_sentry_travel_distance_to_point", "2500", FCVAR_CHEAT, "Maximum travel distance between a bot's Sentry gun and the currently contested point" );
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
const int MaxPlacementAttempts = 5;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuilding::CTFBotEngineerBuilding( void )
|
||||
{
|
||||
m_sentryBuildHint = NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEngineerBuilding::CTFBotEngineerBuilding( CTFBotHintSentrygun *sentryBuildHint )
|
||||
{
|
||||
m_sentryBuildHint = sentryBuildHint;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuilding::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_sentryTriesLeft = MaxPlacementAttempts;
|
||||
|
||||
m_territoryRangeTimer.Invalidate();
|
||||
|
||||
m_hasBuiltSentry = false;
|
||||
m_isSentryOutOfPosition = false;
|
||||
m_nearbyMetalStatus = NEARBY_METAL_UNKNOWN;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Everything is built, upgrade/maintain it
|
||||
// TODO: Upgrade/maintain nearby friendly buildings, too.
|
||||
void CTFBotEngineerBuilding::UpgradeAndMaintainBuildings( CTFBot *me )
|
||||
{
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
CObjectDispenser *myDispenser = (CObjectDispenser *)me->GetObjectOfType( OBJ_DISPENSER );
|
||||
|
||||
if ( !mySentry )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CBaseCombatWeapon *wrench = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( wrench )
|
||||
{
|
||||
me->Weapon_Switch( wrench );
|
||||
}
|
||||
|
||||
const float tooFarRange = 75.0f;
|
||||
|
||||
if ( !myDispenser )
|
||||
{
|
||||
// just work on our sentry
|
||||
float rangeToSentry = me->GetDistanceBetween( mySentry );
|
||||
|
||||
if ( rangeToSentry < 1.2f * tooFarRange )
|
||||
{
|
||||
// crouch both for cover behind our buildings, but also to slow us down so we hit our move goal more accurately
|
||||
me->PressCrouchButton();
|
||||
}
|
||||
|
||||
if ( rangeToSentry > tooFarRange )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, mySentry->GetAbsOrigin(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are in position - work on our buildings
|
||||
me->StopLookingAroundForEnemies();
|
||||
me->GetBodyInterface()->AimHeadTowards( mySentry->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Work on my Sentry" );
|
||||
me->PressFireButton();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// sit near both buildings
|
||||
Vector betweenMyBuildings = ( mySentry->GetAbsOrigin() + myDispenser->GetAbsOrigin() ) / 2.0f;
|
||||
|
||||
// try to equalize distance between both
|
||||
float rangeToSentry = me->GetDistanceBetween( mySentry );
|
||||
float rangeToDispenser = me->GetDistanceBetween( myDispenser );
|
||||
|
||||
const float equalTolerance = 25.0f;
|
||||
|
||||
if ( rangeToSentry < 1.2f * tooFarRange && rangeToDispenser < 1.2f * tooFarRange )
|
||||
{
|
||||
// crouch both for cover behind our buildings, but also to slow us down so we hit our move goal more accurately
|
||||
me->PressCrouchButton();
|
||||
}
|
||||
|
||||
if ( fabs( rangeToDispenser - rangeToSentry ) > equalTolerance || rangeToSentry > tooFarRange || rangeToDispenser > tooFarRange )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, betweenMyBuildings, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
|
||||
if ( rangeToSentry < tooFarRange || rangeToDispenser < tooFarRange )
|
||||
{
|
||||
// we are (nearly) in position - work on our buildings
|
||||
m_searchTimer.Invalidate();
|
||||
|
||||
CBaseObject *workTarget = mySentry;
|
||||
|
||||
if ( mySentry->HasSapper() || mySentry->IsPlasmaDisabled() )
|
||||
workTarget = mySentry;
|
||||
else if ( myDispenser->HasSapper() || myDispenser->IsPlasmaDisabled() )
|
||||
workTarget = myDispenser;
|
||||
else if ( mySentry->GetTimeSinceLastInjury() < 1.0f || mySentry->GetHealth() < mySentry->GetMaxHealth() )
|
||||
workTarget = mySentry;
|
||||
else if ( mySentry->IsBuilding() )
|
||||
workTarget = mySentry;
|
||||
else if ( myDispenser->IsBuilding() )
|
||||
workTarget = myDispenser;
|
||||
else if ( mySentry->GetUpgradeLevel() < 3 )
|
||||
workTarget = mySentry;
|
||||
else if ( myDispenser->GetHealth() < myDispenser->GetMaxHealth() )
|
||||
workTarget = myDispenser;
|
||||
else if ( myDispenser->GetUpgradeLevel() < mySentry->GetUpgradeLevel() )
|
||||
workTarget = myDispenser;
|
||||
|
||||
me->StopLookingAroundForEnemies();
|
||||
me->GetBodyInterface()->AimHeadTowards( workTarget->WorldSpaceCenter(), IBody::CRITICAL, 1.0f, NULL, "Work on my buildings" );
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotEngineerBuilding::IsMetalSourceNearby( CTFBot *me ) const
|
||||
{
|
||||
CUtlVector< CNavArea * > nearbyVector;
|
||||
CollectSurroundingAreas( &nearbyVector, me->GetLastKnownArea(), 2000.0f, me->GetLocomotionInterface()->GetStepHeight(), me->GetLocomotionInterface()->GetStepHeight() );
|
||||
|
||||
for( int i=0; i<nearbyVector.Count(); ++i )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)nearbyVector[i];
|
||||
if ( area->HasAttributeTF( TF_NAV_HAS_AMMO ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// this assumes all spawn rooms have resupply cabinets
|
||||
if ( me->GetTeamNumber() == TF_TEAM_RED && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_RED ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( me->GetTeamNumber() == TF_TEAM_BLUE && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotEngineerBuilding::CheckIfSentryIsOutOfPosition( CTFBot *me ) const
|
||||
{
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
|
||||
if ( !mySentry )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// payload
|
||||
if ( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT )
|
||||
{
|
||||
CTeamTrainWatcher *trainWatcher;
|
||||
|
||||
if ( me->GetTeamNumber() == TF_TEAM_BLUE )
|
||||
{
|
||||
trainWatcher = TFGameRules()->GetPayloadToPush( me->GetTeamNumber() );
|
||||
}
|
||||
else
|
||||
{
|
||||
trainWatcher = TFGameRules()->GetPayloadToBlock( me->GetTeamNumber() );
|
||||
}
|
||||
|
||||
if ( trainWatcher )
|
||||
{
|
||||
float sentryDistanceAlongPath;
|
||||
trainWatcher->ProjectPointOntoPath( mySentry->GetAbsOrigin(), NULL, &sentryDistanceAlongPath );
|
||||
|
||||
const float behindTrainTolerance = SENTRY_MAX_RANGE;
|
||||
return ( trainWatcher->GetTrainDistanceAlongTrack() > sentryDistanceAlongPath + behindTrainTolerance );
|
||||
}
|
||||
}
|
||||
|
||||
// control points
|
||||
mySentry->UpdateLastKnownArea();
|
||||
CNavArea *sentryArea = mySentry->GetLastKnownArea();
|
||||
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( point )
|
||||
{
|
||||
CTFNavArea *pointArea = TheTFNavMesh()->GetControlPointCenterArea( point->GetPointIndex() );
|
||||
|
||||
if ( sentryArea && pointArea )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
if ( NavAreaTravelDistance( sentryArea, pointArea, cost, tf_bot_engineer_max_sentry_travel_distance_to_point.GetFloat() ) < 0 &&
|
||||
NavAreaTravelDistance( pointArea, sentryArea, cost, tf_bot_engineer_max_sentry_travel_distance_to_point.GetFloat() ) < 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuilding::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CObjectSentrygun *mySentry = (CObjectSentrygun *)me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
CObjectDispenser *myDispenser = (CObjectDispenser *)me->GetObjectOfType( OBJ_DISPENSER );
|
||||
CObjectTeleporter *myTeleportEntrance = (CObjectTeleporter *)me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_ENTRANCE );
|
||||
CObjectTeleporter *myTeleportExit = (CObjectTeleporter *)me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT );
|
||||
|
||||
bool isUnderAttack = ( me->GetTimeSinceLastInjury() < 1.0f );
|
||||
isUnderAttack |= ( mySentry && ( mySentry->HasSapper() || mySentry->IsPlasmaDisabled() ) );
|
||||
isUnderAttack |= ( myDispenser && ( myDispenser->HasSapper() || myDispenser->IsPlasmaDisabled() ) );
|
||||
|
||||
me->StartLookingAroundForEnemies();
|
||||
|
||||
// try to build a Sentry
|
||||
if ( !mySentry )
|
||||
{
|
||||
m_nearbyMetalStatus = NEARBY_METAL_UNKNOWN;
|
||||
|
||||
// react to nearby threats if our sentry is down
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
if ( !m_hasBuiltSentry && m_sentryTriesLeft > 0 )
|
||||
{
|
||||
--m_sentryTriesLeft;
|
||||
|
||||
if ( m_sentryBuildHint )
|
||||
{
|
||||
return SuspendFor( new CTFBotEngineerBuildSentryGun( m_sentryBuildHint ), "Building a Sentry at a hint location" );
|
||||
}
|
||||
|
||||
return SuspendFor( new CTFBotEngineerBuildSentryGun, "Building a Sentry" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't build a Sentry here - pick a new place
|
||||
return ChangeTo( new CTFBotEngineerMoveToBuild, "Couldn't find a place to build" );
|
||||
}
|
||||
}
|
||||
|
||||
// I have a Sentry
|
||||
m_hasBuiltSentry = true;
|
||||
|
||||
if ( m_sentryBuildHint != NULL && !m_sentryBuildHint->IsEnabled() )
|
||||
{
|
||||
// our hint has been disabled and no longer has influence on our behavior
|
||||
m_sentryBuildHint = NULL;
|
||||
}
|
||||
|
||||
// periodically check that our Sentry is still near the contested point
|
||||
if ( m_sentryBuildHint == NULL || !m_sentryBuildHint->IsSticky() )
|
||||
{
|
||||
if ( !m_isSentryOutOfPosition && m_territoryRangeTimer.IsElapsed() )
|
||||
{
|
||||
m_territoryRangeTimer.Start( RandomFloat( 3.0f, 5.0f ) );
|
||||
|
||||
m_isSentryOutOfPosition = CheckIfSentryIsOutOfPosition( me );
|
||||
}
|
||||
|
||||
m_isSentryOutOfPosition = false;
|
||||
|
||||
if ( m_isSentryOutOfPosition )
|
||||
{
|
||||
// the point has moved, only keep sentry as long as it keeps attacking
|
||||
if ( mySentry->GetTimeSinceLastFired() > 10.0f )
|
||||
{
|
||||
mySentry->DetonateObject();
|
||||
|
||||
// if we built here because of a hint, disable that hint so we don't use it and rebuild here again
|
||||
if ( m_sentryBuildHint != NULL )
|
||||
{
|
||||
inputdata_t dummy;
|
||||
m_sentryBuildHint->InputDisable( dummy );
|
||||
|
||||
m_sentryBuildHint = NULL;
|
||||
}
|
||||
|
||||
if ( myDispenser )
|
||||
{
|
||||
myDispenser->DetonateObject();
|
||||
}
|
||||
|
||||
if ( myTeleportExit )
|
||||
{
|
||||
myTeleportExit->DetonateObject();
|
||||
}
|
||||
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_MOVEUP );
|
||||
|
||||
return ChangeTo( new CTFBotEngineerMoveToBuild, "Need to move my gear closer to the point!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if my dispenser is too far away from my sentry, destroy and rebuild it next update
|
||||
// @TODO: Flag hint-built entities for a larger range
|
||||
const float maxSeparation = 500.0f;
|
||||
if ( myDispenser )
|
||||
{
|
||||
if ( ( mySentry->GetAbsOrigin() - myDispenser->GetAbsOrigin() ).IsLengthGreaterThan( maxSeparation ) )
|
||||
{
|
||||
myDispenser->DestroyObject();
|
||||
myDispenser = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// build up the sentry all the way if there is a metal source nearby
|
||||
if ( mySentry->GetUpgradeLevel() < 3 )
|
||||
{
|
||||
if ( m_nearbyMetalStatus == NEARBY_METAL_UNKNOWN )
|
||||
{
|
||||
m_nearbyMetalStatus = IsMetalSourceNearby( me ) ? NEARBY_METAL_EXISTS : NEARBY_METAL_NONE;
|
||||
}
|
||||
|
||||
if ( m_nearbyMetalStatus == NEARBY_METAL_EXISTS )
|
||||
{
|
||||
UpgradeAndMaintainBuildings( me );
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ( myTeleportExit )
|
||||
{
|
||||
// if my teleporter exit is too far away from my sentry, destroy and rebuild it next update
|
||||
if ( ( mySentry->GetAbsOrigin() - myTeleportExit->GetAbsOrigin() ).IsLengthGreaterThan( maxSeparation ) )
|
||||
{
|
||||
myTeleportExit->DestroyObject();
|
||||
myTeleportExit = NULL;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// try to build a Dispenser (build after tele exit in training)
|
||||
if ( !TFGameRules()->IsInTraining() || myTeleportExit )
|
||||
{
|
||||
const float dispenserRebuildInterval = 10.0f;
|
||||
if ( myDispenser )
|
||||
{
|
||||
// don't rebuild immediately after building is destroyed
|
||||
m_dispenserRetryTimer.Start( dispenserRebuildInterval );
|
||||
}
|
||||
else if ( m_dispenserRetryTimer.IsElapsed() && !isUnderAttack )
|
||||
{
|
||||
m_dispenserRetryTimer.Start( dispenserRebuildInterval );
|
||||
|
||||
return SuspendFor( new CTFBotEngineerBuildDispenser, "Building a Dispenser" );
|
||||
}
|
||||
}
|
||||
|
||||
// try to build a Teleporter Exit
|
||||
const float exitRebuildInterval = TFGameRules()->IsInTraining() ? 5.0f : 30.0f;
|
||||
if ( myTeleportExit )
|
||||
{
|
||||
// don't rebuild immediately after building is destroyed
|
||||
m_teleportExitRetryTimer.Start( exitRebuildInterval );
|
||||
}
|
||||
else if ( m_teleportExitRetryTimer.IsElapsed() && myTeleportEntrance && !isUnderAttack )
|
||||
{
|
||||
m_teleportExitRetryTimer.Start( exitRebuildInterval );
|
||||
|
||||
// we need to build a teleporter exit yet
|
||||
if ( m_sentryBuildHint != NULL )
|
||||
{
|
||||
// if there are teleporter exit hints, find the closest one to our sentry and use it
|
||||
CUtlVector< CBaseEntity * > hintVector;
|
||||
CTFBotHintTeleporterExit *hint = NULL;
|
||||
while( ( hint = (CTFBotHintTeleporterExit *)gEntList.FindEntityByClassname( hint, "bot_hint_teleporter_exit" ) ) != NULL )
|
||||
{
|
||||
if ( hint->IsEnabled() && hint->InSameTeam( me ) )
|
||||
{
|
||||
hintVector.AddToTail( hint );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hintVector.Count() > 0 )
|
||||
{
|
||||
mySentry->UpdateLastKnownArea();
|
||||
CBaseEntity *closeHint = SelectClosestEntityByTravelDistance( me, hintVector, mySentry->GetLastKnownArea(), tf_bot_engineer_exit_near_sentry_range.GetFloat() );
|
||||
|
||||
if ( closeHint )
|
||||
{
|
||||
return SuspendFor( new CTFBotEngineerBuildTeleportExit( closeHint->GetAbsOrigin(), closeHint->GetAbsAngles().y ), "Building teleporter exit at nearby hint" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( me->IsRangeLessThan( mySentry, 300.0f ) )
|
||||
{
|
||||
// drop a teleporter exit near our sentry
|
||||
return SuspendFor( new CTFBotEngineerBuildTeleportExit(), "Building teleporter exit" );
|
||||
}
|
||||
}
|
||||
|
||||
// everything is built - maintain them
|
||||
UpgradeAndMaintainBuildings( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotEngineerBuilding::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
me->StartLookingAroundForEnemies();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerBuilding::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerBuilding::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerBuilding::OnTerritoryCaptured( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_building.h
|
||||
// At building location, constructing buildings
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_BUILDING_H
|
||||
#define TF_BOT_ENGINEER_BUILDING_H
|
||||
|
||||
class CTFBotHintSentrygun;
|
||||
|
||||
|
||||
class CTFBotEngineerBuilding : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotEngineerBuilding( void );
|
||||
CTFBotEngineerBuilding( CTFBotHintSentrygun *sentryBuildHint );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerBuilding"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_searchTimer;
|
||||
CountdownTimer m_getAmmoTimer;
|
||||
CountdownTimer m_repathTimer;
|
||||
CountdownTimer m_buildTeleporterExitTimer;
|
||||
|
||||
int m_sentryTriesLeft;
|
||||
|
||||
CountdownTimer m_dispenserRetryTimer;
|
||||
CountdownTimer m_teleportExitRetryTimer;
|
||||
|
||||
PathFollower m_path;
|
||||
|
||||
CHandle< CTFBotHintSentrygun > m_sentryBuildHint;
|
||||
|
||||
bool m_hasBuiltSentry;
|
||||
|
||||
enum NearbyMetalType
|
||||
{
|
||||
NEARBY_METAL_UNKNOWN,
|
||||
NEARBY_METAL_NONE,
|
||||
NEARBY_METAL_EXISTS
|
||||
};
|
||||
|
||||
NearbyMetalType m_nearbyMetalStatus;
|
||||
|
||||
CountdownTimer m_territoryRangeTimer;
|
||||
bool m_isSentryOutOfPosition;
|
||||
bool CheckIfSentryIsOutOfPosition( CTFBot *me ) const;
|
||||
|
||||
void UpgradeAndMaintainBuildings( CTFBot *me );
|
||||
bool IsMetalSourceNearby( CTFBot *me ) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ENGINEER_BUILDING_H
|
||||
@@ -0,0 +1,504 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_move_to_build.cpp
|
||||
// Engineer moving into position to build
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh/tf_nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_weapon_builder.h"
|
||||
#include "team_train_watcher.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_move_to_build.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_building.h"
|
||||
#include "bot/map_entities/tf_bot_hint_sentrygun.h"
|
||||
#include "bot/behavior/tf_bot_get_ammo.h"
|
||||
#include "bot/behavior/tf_bot_retreat_to_cover.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build_teleport_exit.h"
|
||||
#include "trigger_area_capture.h"
|
||||
|
||||
#include "raid/tf_raid_logic.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_debug_sentry_placement( "tf_bot_debug_sentry_placement", "0", FCVAR_CHEAT );
|
||||
ConVar tf_bot_max_teleport_exit_travel_to_point( "tf_bot_max_teleport_exit_travel_to_point", "2500", FCVAR_CHEAT, "In an offensive engineer bot's tele exit is farther from the point than this, destroy it" );
|
||||
ConVar tf_bot_min_teleport_travel( "tf_bot_min_teleport_travel", "3000", FCVAR_CHEAT, "Minimum travel distance between teleporter entrance and exit before engineer bot will build one" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
static Vector s_pointCentroid;
|
||||
|
||||
int CompareRangeToPoint( CTFNavArea * const *area1, CTFNavArea * const *area2 )
|
||||
{
|
||||
float d1 = ( (*area1)->GetCenter() - s_pointCentroid ).LengthSqr();
|
||||
float d2 = ( (*area2)->GetCenter() - s_pointCentroid ).LengthSqr();
|
||||
|
||||
// reversed so farthest is sorted first in the vector
|
||||
if ( d1 < d2 )
|
||||
return 1;
|
||||
|
||||
if ( d1 > d2 )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotEngineerMoveToBuild::CollectBuildAreas( CTFBot *me )
|
||||
{
|
||||
// if we have a predesignated build area, we're done
|
||||
if ( me->GetHomeArea() )
|
||||
return;
|
||||
|
||||
m_sentryAreaVector.RemoveAll();
|
||||
|
||||
CUtlVector< CTFNavArea * > pointAreaVector;
|
||||
Vector pointCentroid = vec3_origin;
|
||||
float pointEnemyIncursion = 0.0f;
|
||||
int i;
|
||||
|
||||
int myTeam = me->GetTeamNumber();
|
||||
int enemyTeam = ( myTeam == TF_TEAM_BLUE ) ? TF_TEAM_RED : TF_TEAM_BLUE;
|
||||
|
||||
CCaptureZone *zone = me->GetFlagCaptureZone();
|
||||
if ( zone )
|
||||
{
|
||||
// NOTE: Not strictly the right thing - should defend location of our team's flag
|
||||
CTFNavArea *zoneArea = (CTFNavArea *)TheTFNavMesh()->GetNearestNavArea( zone->WorldSpaceCenter(), false, 500.0f, true );
|
||||
if ( zoneArea )
|
||||
{
|
||||
pointAreaVector.AddToTail( zoneArea );
|
||||
pointCentroid += zoneArea->GetCenter();
|
||||
pointEnemyIncursion += zoneArea->GetIncursionDistance( enemyTeam );
|
||||
}
|
||||
}
|
||||
else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT )
|
||||
{
|
||||
CTeamTrainWatcher *trainWatcher;
|
||||
|
||||
if ( myTeam == TF_TEAM_BLUE )
|
||||
{
|
||||
trainWatcher = TFGameRules()->GetPayloadToPush( me->GetTeamNumber() );
|
||||
}
|
||||
else
|
||||
{
|
||||
trainWatcher = TFGameRules()->GetPayloadToBlock( me->GetTeamNumber() );
|
||||
}
|
||||
|
||||
if ( trainWatcher )
|
||||
{
|
||||
Vector checkpointPos = trainWatcher->GetNextCheckpointPosition();
|
||||
|
||||
CTFNavArea *checkpointArea = (CTFNavArea *)TheTFNavMesh()->GetNearestNavArea( checkpointPos, false, 500.0f, true );
|
||||
if ( checkpointArea )
|
||||
{
|
||||
pointAreaVector.AddToTail( checkpointArea );
|
||||
pointCentroid += checkpointArea->GetCenter();
|
||||
pointEnemyIncursion += checkpointArea->GetIncursionDistance( enemyTeam );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// collect all areas overlapping the point
|
||||
CTeamControlPoint *ctrlPoint = me->GetMyControlPoint();
|
||||
if ( !ctrlPoint )
|
||||
return;
|
||||
|
||||
const CUtlVector< CTFNavArea * > *ctrlPointAreaVector = TheTFNavMesh()->GetControlPointAreas( ctrlPoint->GetPointIndex() );
|
||||
|
||||
if ( ctrlPointAreaVector )
|
||||
{
|
||||
for( i=0; i<ctrlPointAreaVector->Count(); ++i )
|
||||
{
|
||||
CTFNavArea *area = ctrlPointAreaVector->Element(i);
|
||||
|
||||
pointAreaVector.AddToTail( area );
|
||||
pointCentroid += area->GetCenter();
|
||||
pointEnemyIncursion += area->GetIncursionDistance( enemyTeam );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( pointAreaVector.Count() == 0 )
|
||||
return;
|
||||
|
||||
pointCentroid /= pointAreaVector.Count();
|
||||
pointEnemyIncursion /= pointAreaVector.Count();
|
||||
|
||||
|
||||
// collect all areas that can see the point
|
||||
CUtlVector< CTFNavArea * > exposedAreaVector;
|
||||
for( i=0; i<pointAreaVector.Count(); ++i )
|
||||
{
|
||||
CTFAreaCollector collect;
|
||||
pointAreaVector[i]->ForAllPotentiallyVisibleAreas( collect );
|
||||
|
||||
for( int j=0; j<collect.m_vector.Count(); ++j )
|
||||
{
|
||||
CTFNavArea *visibleArea = collect.m_vector[j];
|
||||
|
||||
|
||||
if ( visibleArea->GetIncursionDistance( myTeam ) < 0 || visibleArea->GetIncursionDistance( enemyTeam ) < 0 )
|
||||
continue;
|
||||
|
||||
if ( TFGameRules()->IsInKothMode() )
|
||||
{
|
||||
// ignore areas the enemy can reach first
|
||||
if ( visibleArea->GetIncursionDistance( myTeam ) >= visibleArea->GetIncursionDistance( enemyTeam ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
// incursion flow is badly behaved at cap #1, stage #2 in dustbowl
|
||||
// else
|
||||
// {
|
||||
// if ( pointEnemyIncursion > visibleArea->GetIncursionDistance( enemyTeam ) )
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if ( TFGameRules()->GetGameType() == TF_GAMETYPE_CP )
|
||||
{
|
||||
// don't build directly on the point
|
||||
if ( visibleArea->HasAttributeTF( TF_NAV_CONTROL_POINT ) )
|
||||
continue;
|
||||
|
||||
// ignore areas below the point
|
||||
const float tooFarBelow = 150.0f;
|
||||
if ( visibleArea->GetCenter().z < pointCentroid.z - tooFarBelow )
|
||||
continue;
|
||||
|
||||
// ignore areas too far from the point for the sentry gun to reach
|
||||
const float tolerance = 1.1f;
|
||||
if ( ( visibleArea->GetCenter() - pointCentroid ).IsLengthGreaterThan( SENTRY_MAX_RANGE * tolerance ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore areas that don't have clear line of FIRE (not sight)
|
||||
const float sentryEyeHeight = 60.0f;
|
||||
const float pointFlagHeight = 70.0f; // 100.0f;
|
||||
if ( !me->IsLineOfFireClear( visibleArea->GetCenter() + Vector( 0, 0, sentryEyeHeight ), pointCentroid + Vector( 0, 0, pointFlagHeight ) ) )
|
||||
continue;
|
||||
|
||||
if ( !exposedAreaVector.HasElement( visibleArea ) )
|
||||
exposedAreaVector.AddToTail( visibleArea );
|
||||
}
|
||||
}
|
||||
|
||||
// keep the farthest away areas
|
||||
const float keepRatio = 1.0f; // 0.5f;
|
||||
s_pointCentroid = pointCentroid;
|
||||
exposedAreaVector.Sort( CompareRangeToPoint );
|
||||
|
||||
for( i=0; i<exposedAreaVector.Count() * keepRatio; ++i )
|
||||
{
|
||||
CTFNavArea *usableArea = exposedAreaVector[i];
|
||||
|
||||
m_sentryAreaVector.AddToTail( usableArea );
|
||||
}
|
||||
|
||||
// calculate total surface area
|
||||
m_totalSurfaceArea = 0.0f;
|
||||
FOR_EACH_VEC( m_sentryAreaVector, it )
|
||||
{
|
||||
CTFNavArea *area = m_sentryAreaVector[ it ];
|
||||
|
||||
m_totalSurfaceArea += area->GetSizeX() * area->GetSizeY();
|
||||
|
||||
if ( tf_bot_debug_sentry_placement.GetBool() )
|
||||
{
|
||||
TheNavMesh->AddToSelectedSet( area );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Doesn't recompute the potential areas, just reselected from the list
|
||||
*/
|
||||
void CTFBotEngineerMoveToBuild::SelectBuildLocation( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
|
||||
m_sentryBuildHint = NULL;
|
||||
m_sentryBuildLocation = vec3_origin;
|
||||
|
||||
|
||||
// if we have a build spot, use it
|
||||
if ( me->GetHomeArea() )
|
||||
{
|
||||
m_sentryBuildLocation = me->GetHomeArea()->GetCenter();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have a set of specific build locations, pick one of them
|
||||
CUtlVector< CTFBotHintSentrygun * > sentryHintVector;
|
||||
|
||||
CTFBotHintSentrygun *sentryHint;
|
||||
for( sentryHint = static_cast< CTFBotHintSentrygun * >( gEntList.FindEntityByClassname( NULL, "bot_hint_sentrygun" ) );
|
||||
sentryHint;
|
||||
sentryHint = static_cast< CTFBotHintSentrygun * >( gEntList.FindEntityByClassname( sentryHint, "bot_hint_sentrygun" ) ) )
|
||||
{
|
||||
// clear the previous owner if it is us
|
||||
if ( sentryHint->GetPlayerOwner() == me )
|
||||
{
|
||||
sentryHint->SetPlayerOwner( NULL );
|
||||
}
|
||||
if ( sentryHint->IsAvailableForSelection( me ) )
|
||||
{
|
||||
sentryHintVector.AddToTail( sentryHint );
|
||||
}
|
||||
}
|
||||
|
||||
if ( sentryHintVector.Count() > 0 )
|
||||
{
|
||||
int which = RandomInt( 0, sentryHintVector.Count()-1 );
|
||||
|
||||
m_sentryBuildHint = sentryHintVector[ which ];
|
||||
m_sentryBuildHint->SetPlayerOwner( me );
|
||||
m_sentryBuildLocation = m_sentryBuildHint->GetAbsOrigin();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// collect nav area candidates
|
||||
CollectBuildAreas( me );
|
||||
|
||||
// choose based on surface area to avoid biasing finely subdivided areas of the mesh
|
||||
float which = RandomFloat( 0.0f, m_totalSurfaceArea - 1.0f );
|
||||
float soFar = 0.0f;
|
||||
FOR_EACH_VEC( m_sentryAreaVector, sit )
|
||||
{
|
||||
CTFNavArea *area = m_sentryAreaVector[ sit ];
|
||||
|
||||
soFar += area->GetSizeX() * area->GetSizeY();
|
||||
|
||||
if ( which < soFar )
|
||||
{
|
||||
m_sentryBuildLocation = area->GetRandomPoint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !HushAsserts() )
|
||||
{
|
||||
Assert( !"Failed to find a build location" );
|
||||
}
|
||||
m_sentryBuildLocation = me->GetAbsOrigin();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerMoveToBuild::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
if ( TFGameRules()->IsRaidMode() )
|
||||
{
|
||||
if ( me->GetHomeArea() && TFGameRules()->GetRaidLogic() )
|
||||
{
|
||||
// try to pick a new area
|
||||
CTFNavArea *sentryArea = TFGameRules()->GetRaidLogic()->SelectRaidSentryArea();
|
||||
if ( sentryArea )
|
||||
{
|
||||
me->SetHomeArea( sentryArea );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
SelectBuildLocation( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEngineerMoveToBuild::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->WasPointJustLost() )
|
||||
{
|
||||
if ( m_fallBackTimer.HasStarted() )
|
||||
{
|
||||
if ( m_fallBackTimer.IsElapsed() )
|
||||
{
|
||||
SelectBuildLocation( me );
|
||||
m_fallBackTimer.Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait a moment while we decide where to build near fallback point
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBaseObject *mySentry = me->GetObjectOfType( OBJ_SENTRYGUN );
|
||||
if ( mySentry )
|
||||
{
|
||||
// we already have a sentry from a previous life - continue what we were doing
|
||||
|
||||
// if we used a sentry hint last time, reuse it
|
||||
CTFBotHintSentrygun *sentryHint;
|
||||
for( sentryHint = static_cast< CTFBotHintSentrygun * >( gEntList.FindEntityByClassname( NULL, "bot_hint_sentrygun" ) );
|
||||
sentryHint;
|
||||
sentryHint = static_cast< CTFBotHintSentrygun * >( gEntList.FindEntityByClassname( sentryHint, "bot_hint_sentrygun" ) ) )
|
||||
{
|
||||
if ( sentryHint->GetPlayerOwner() == me )
|
||||
{
|
||||
return ChangeTo( new CTFBotEngineerBuilding( sentryHint ), "Going back to my existing sentry nest and reusing a sentry hint" );
|
||||
}
|
||||
}
|
||||
|
||||
return ChangeTo( new CTFBotEngineerBuilding, "Going back to my existing sentry nest" );
|
||||
}
|
||||
|
||||
// offensive engineers need to place a forward teleporter
|
||||
if ( TFGameRules()->GetGameType() == TF_GAMETYPE_CP && !TFGameRules()->IsInKothMode() && me->GetTeamNumber() == TF_TEAM_BLUE )
|
||||
{
|
||||
CObjectTeleporter *myTeleportExit = (CObjectTeleporter *)me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT );
|
||||
int myTeam = me->GetTeamNumber();
|
||||
|
||||
if ( myTeleportExit )
|
||||
{
|
||||
// if exit is too far from the point, destroy it and try again
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( point )
|
||||
{
|
||||
CTFNavArea *pointArea = TheTFNavMesh()->GetControlPointCenterArea( point->GetPointIndex() );
|
||||
|
||||
myTeleportExit->UpdateLastKnownArea();
|
||||
CTFNavArea *exitArea = (CTFNavArea *)myTeleportExit->GetLastKnownArea();
|
||||
|
||||
if ( pointArea && exitArea )
|
||||
{
|
||||
float travelToPoint = fabs( exitArea->GetIncursionDistance( myTeam ) - pointArea->GetIncursionDistance( myTeam ) );
|
||||
|
||||
if ( travelToPoint > tf_bot_max_teleport_exit_travel_to_point.GetFloat() )
|
||||
{
|
||||
// too far, destroy it
|
||||
myTeleportExit->DestroyObject();
|
||||
myTeleportExit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CObjectTeleporter *myTeleportEntrance = (CObjectTeleporter *)me->GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_ENTRANCE );
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
|
||||
bool shouldBuildExit = true;
|
||||
|
||||
// if we have a teleporter entrance, don't place the exit too close to it
|
||||
if ( myTeleportEntrance && myArea )
|
||||
{
|
||||
myTeleportEntrance->UpdateLastKnownArea();
|
||||
CTFNavArea *enterArea = (CTFNavArea *)myTeleportEntrance->GetLastKnownArea();
|
||||
|
||||
if ( enterArea )
|
||||
{
|
||||
float travelBetween = fabs( enterArea->GetIncursionDistance( myTeam ) - myArea->GetIncursionDistance( myTeam ) );
|
||||
|
||||
if ( travelBetween < tf_bot_min_teleport_travel.GetFloat() )
|
||||
{
|
||||
shouldBuildExit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( shouldBuildExit )
|
||||
{
|
||||
// no exit yet - need to place one
|
||||
// when we see the enemy, retreat to cover and build the exit there
|
||||
if ( me->GetVisionInterface()->GetPrimaryKnownThreat( true ) )
|
||||
{
|
||||
if ( !me->m_Shared.InCond( TF_COND_INVULNERABLE ) && ShouldRetreat( me ) != ANSWER_NO )
|
||||
{
|
||||
Action< CTFBot > *nextActionWhenInCover = new CTFBotEngineerBuildTeleportExit;
|
||||
return SuspendFor( new CTFBotRetreatToCover( nextActionWhenInCover ), "Retreating to a safe place to build my teleporter exit" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move to build position
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, SAFEST_ROUTE );
|
||||
m_path.Compute( me, m_sentryBuildLocation, cost );
|
||||
}
|
||||
|
||||
Vector forward;
|
||||
me->EyeVectors( &forward );
|
||||
forward.z = 0.0f;
|
||||
forward.NormalizeInPlace();
|
||||
|
||||
Vector myBlueprintPosition = me->GetAbsOrigin() + 50.0f * forward;
|
||||
|
||||
const float closeToHome = 25.0f;
|
||||
Vector toBuild = m_sentryBuildLocation - myBlueprintPosition;
|
||||
Vector toMe = m_sentryBuildLocation - me->GetAbsOrigin();
|
||||
|
||||
if ( me->GetLocomotionInterface()->IsOnGround() )
|
||||
{
|
||||
// we need to wait until we're on the ground since the Build action assumes our position OnStart is where we are going to build
|
||||
if ( toMe.AsVector2D().IsLengthLessThan( closeToHome ) || toBuild.AsVector2D().IsLengthLessThan( closeToHome ) )
|
||||
{
|
||||
if ( m_sentryBuildHint != NULL )
|
||||
{
|
||||
return ChangeTo( new CTFBotEngineerBuilding( m_sentryBuildHint ), "Reached my precise build location" );
|
||||
}
|
||||
|
||||
return ChangeTo( new CTFBotEngineerBuilding, "Reached my build location" );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerMoveToBuild::OnStuck( CTFBot *me )
|
||||
{
|
||||
// SelectBuildLocation( me );
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerMoveToBuild::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerMoveToBuild::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
SelectBuildLocation( me );
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotEngineerMoveToBuild::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
// we have to wait a moment until contested point changes to select a new build spot
|
||||
m_fallBackTimer.Start( 0.2f );
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_engineer_move_to_build.h
|
||||
// Engineer moving into position to build
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_BOT_ENGINEER_MOVE_TO_BUILD_H
|
||||
#define TF_BOT_ENGINEER_MOVE_TO_BUILD_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotHintSentrygun;
|
||||
|
||||
|
||||
class CTFBotEngineerMoveToBuild : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EngineerMoveToBuild"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFBotHintSentrygun > m_sentryBuildHint;
|
||||
Vector m_sentryBuildLocation;
|
||||
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
CUtlVector< CTFNavArea * > m_sentryAreaVector;
|
||||
float m_totalSurfaceArea;
|
||||
void CollectBuildAreas( CTFBot *me );
|
||||
|
||||
void SelectBuildLocation( CTFBot *me );
|
||||
CountdownTimer m_fallBackTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_ENGINEER_MOVE_TO_BUILD_H
|
||||
Reference in New Issue
Block a user