summaryrefslogtreecommitdiff
path: root/game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp')
-rw-r--r--game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp497
1 files changed, 497 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp b/game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp
new file mode 100644
index 0000000..a0f84c0
--- /dev/null
+++ b/game/server/tf/bot/behavior/engineer/tf_bot_engineer_building.cpp
@@ -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();
+}