summaryrefslogtreecommitdiff
path: root/game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp')
-rw-r--r--game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp368
1 files changed, 368 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp b/game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp
new file mode 100644
index 0000000..50ed460
--- /dev/null
+++ b/game/server/tf/bot/behavior/tf_bot_scenario_monitor.cpp
@@ -0,0 +1,368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_scenario_monitor.h
+// Behavior layer that interrupts for scenario rules (picked up flag, drop what you're doing and capture, etc)
+// Michael Booth, May 2011
+
+#include "cbase.h"
+#include "fmtstr.h"
+
+#include "tf_gamerules.h"
+#include "tf_weapon_pipebomblauncher.h"
+#include "NextBot/NavMeshEntities/func_nav_prerequisite.h"
+
+#include "bot/tf_bot.h"
+#include "bot/tf_bot_manager.h"
+#include "bot/behavior/nav_entities/tf_bot_nav_ent_destroy_entity.h"
+#include "bot/behavior/nav_entities/tf_bot_nav_ent_move_to.h"
+#include "bot/behavior/nav_entities/tf_bot_nav_ent_wait.h"
+#include "bot/behavior/tf_bot_tactical_monitor.h"
+#include "bot/behavior/tf_bot_retreat_to_cover.h"
+#include "bot/behavior/tf_bot_get_health.h"
+#include "bot/behavior/tf_bot_get_ammo.h"
+#include "bot/behavior/sniper/tf_bot_sniper_lurk.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_capture_point.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_defend_point.h"
+#include "bot/behavior/scenario/payload/tf_bot_payload_guard.h"
+#include "bot/behavior/scenario/payload/tf_bot_payload_push.h"
+#include "bot/behavior/tf_bot_use_teleporter.h"
+#include "bot/behavior/training/tf_bot_training.h"
+#include "bot/behavior/tf_bot_destroy_enemy_sentry.h"
+#include "bot/behavior/engineer/tf_bot_engineer_building.h"
+#include "bot/behavior/spy/tf_bot_spy_infiltrate.h"
+#include "bot/behavior/spy/tf_bot_spy_leave_spawn_room.h"
+#include "bot/behavior/medic/tf_bot_medic_heal.h"
+#include "bot/behavior/engineer/tf_bot_engineer_build.h"
+#include "bot/map_entities/tf_bot_hint_sentrygun.h"
+
+#ifdef TF_RAID_MODE
+#include "bot/behavior/scenario/raid/tf_bot_wander.h"
+#include "bot/behavior/scenario/raid/tf_bot_companion.h"
+#include "bot/behavior/scenario/raid/tf_bot_squad_attack.h"
+#include "bot/behavior/scenario/raid/tf_bot_guard_area.h"
+#endif // TF_RAID_MODE
+
+#include "bot/behavior/tf_bot_attack.h"
+#include "bot/behavior/tf_bot_seek_and_destroy.h"
+#include "bot/behavior/tf_bot_taunt.h"
+#include "bot/behavior/tf_bot_escort.h"
+#include "bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h"
+#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h"
+
+#include "bot/behavior/missions/tf_bot_mission_suicide_bomber.h"
+#include "bot/behavior/squad/tf_bot_escort_squad_leader.h"
+#include "bot/behavior/engineer/mvm_engineer/tf_bot_mvm_engineer_idle.h"
+#include "bot/behavior/missions/tf_bot_mission_reprogrammed.h"
+
+#include "bot/behavior/tf_bot_scenario_monitor.h"
+
+
+extern ConVar tf_bot_health_ok_ratio;
+extern ConVar tf_bot_health_critical_ratio;
+
+
+//-----------------------------------------------------------------------------------------
+// Returns the initial Action we will run concurrently as a child to us
+Action< CTFBot > *CTFBotScenarioMonitor::InitialContainedAction( CTFBot *me )
+{
+ if ( me->IsInASquad() )
+ {
+ if ( me->GetSquad()->IsLeader( me ) )
+ {
+ // I'm the leader of this Squad, so I can do what I want and the other Squaddies will support me
+ return DesiredScenarioAndClassAction( me );
+ }
+
+ // Medics are the exception - they always heal, and have special squad logic in their heal logic
+ if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
+ {
+ return new CTFBotMedicHeal;
+ }
+
+ // I'm in a Squad but not the leader, do "escort and support" Squad behavior
+ // until the Squad disbands, and then do my normal thing
+ return new CTFBotEscortSquadLeader( DesiredScenarioAndClassAction( me ) );
+ }
+
+ return DesiredScenarioAndClassAction( me );
+}
+
+
+//-----------------------------------------------------------------------------------------
+// Returns Action specific to the scenario and my class
+Action< CTFBot > *CTFBotScenarioMonitor::DesiredScenarioAndClassAction( CTFBot *me )
+{
+ switch( me->GetMission() )
+ {
+ case CTFBot::MISSION_SEEK_AND_DESTROY:
+ break;
+
+ case CTFBot::MISSION_DESTROY_SENTRIES:
+ return new CTFBotMissionSuicideBomber;
+
+ case CTFBot::MISSION_SNIPER:
+ return new CTFBotSniperLurk;
+
+#ifdef STAGING_ONLY
+ case CTFBot::MISSION_REPROGRAMMED:
+ return new CTFBotMissionReprogrammed;
+#endif
+ }
+
+#ifdef TF_RAID_MODE
+ if ( me->HasAttribute( CTFBot::IS_NPC ) )
+ {
+ // map-spawned guardians
+ return new CTFBotGuardian;
+ }
+#endif // TF_RAID_MODE
+
+#ifdef TF_RAID_MODE
+ if ( TFGameRules()->IsBossBattleMode() )
+ {
+ if ( me->GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ // bot teammates
+ return new CTFBotCompanion;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SNIPER ) )
+ {
+ return new CTFBotSniperLurk;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SPY ) )
+ {
+ return new CTFBotSpyInfiltrate;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
+ {
+ return new CTFBotMedicHeal;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ return new CTFBotEngineerBuild;
+ }
+
+ return new CTFBotEscort( TFGameRules()->GetActiveBoss() );
+ }
+ else if ( TFGameRules()->IsRaidMode() )
+ {
+ if ( me->GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ // bot teammates
+ return new CTFBotCompanion;
+ }
+
+ if ( me->IsInASquad() )
+ {
+ // squad behavior
+ return new CTFBotSquadAttack;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SCOUT ) || me->HasAttribute( CTFBot::AGGRESSIVE ) )
+ {
+ return new CTFBotWander;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SNIPER ) )
+ {
+ return new CTFBotSniperLurk;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SPY ) )
+ {
+ return new CTFBotSpyInfiltrate;
+ }
+
+ return new CTFBotGuardArea;
+ }
+#endif // TF_RAID_MODE
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ if ( me->IsPlayerClass( TF_CLASS_SPY ) )
+ {
+ return new CTFBotSpyLeaveSpawnRoom;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
+ {
+ // if I'm being healed by another medic, I should do something else other than healing
+ bool bIsBeingHealedByAMedic = false;
+ int nNumHealers = me->m_Shared.GetNumHealers();
+ for ( int i=0; i<nNumHealers; ++i )
+ {
+ CBaseEntity *pHealer = me->m_Shared.GetHealerByIndex(i);
+ if ( pHealer && pHealer->IsPlayer() )
+ {
+ bIsBeingHealedByAMedic = true;
+ break;
+ }
+ }
+
+ if ( !bIsBeingHealedByAMedic )
+ {
+ return new CTFBotMedicHeal;
+ }
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ return new CTFBotMvMEngineerIdle;
+ }
+
+ // NOTE: Snipers are intentionally left out so they go after the flag. Actual sniping behavior is done as a mission.
+
+ if ( me->HasAttribute( CTFBot::AGGRESSIVE ) )
+ {
+ // push for the point first, then attack
+ return new CTFBotPushToCapturePoint( new CTFBotFetchFlag );
+ }
+
+ // capture the flag
+ return new CTFBotFetchFlag;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_SPY ) )
+ {
+ return new CTFBotSpyInfiltrate;
+ }
+
+ if ( !TheTFBots().IsMeleeOnly() )
+ {
+ if ( me->IsPlayerClass( TF_CLASS_SNIPER ) )
+ {
+ return new CTFBotSniperLurk;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
+ {
+ return new CTFBotMedicHeal;
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ return new CTFBotEngineerBuild;
+ }
+ }
+
+ if ( me->GetFlagToFetch() )
+ {
+ // capture the flag
+ return new CTFBotFetchFlag;
+ }
+ else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT )
+ {
+ // push the cart
+ if ( me->GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ // blu is pushing
+ return new CTFBotPayloadPush;
+ }
+ else if ( me->GetTeamNumber() == TF_TEAM_RED )
+ {
+ // red is blocking
+ return new CTFBotPayloadGuard;
+ }
+ }
+ else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_CP )
+ {
+ // if we have a point we can capture - do it
+ CUtlVector< CTeamControlPoint * > captureVector;
+ TFGameRules()->CollectCapturePoints( me, &captureVector );
+
+ if ( captureVector.Count() > 0 )
+ {
+ return new CTFBotCapturePoint;
+ }
+
+ // otherwise, defend our point(s) from capture
+ CUtlVector< CTeamControlPoint * > defendVector;
+ TFGameRules()->CollectDefendPoints( me, &defendVector );
+
+ if ( defendVector.Count() > 0 )
+ {
+ return new CTFBotDefendPoint;
+ }
+
+ // likely KotH mode and/or all points are locked - assume capture
+ DevMsg( "%3.2f: %s: Gametype is CP, but I can't find a point to capture or defend!\n", gpGlobals->curtime, me->GetDebugIdentifier() );
+ return new CTFBotCapturePoint;
+ }
+ else
+ {
+ // scenario not implemented yet - just fight
+ return new CTFBotSeekAndDestroy;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotScenarioMonitor::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
+{
+ m_ignoreLostFlagTimer.Start( 20.0f );
+ m_lostFlagTimer.Invalidate();
+ return Continue();
+}
+
+
+ConVar tf_bot_fetch_lost_flag_time( "tf_bot_fetch_lost_flag_time", "10", FCVAR_CHEAT, "How long busy TFBots will ignore the dropped flag before they give up what they are doing and go after it" );
+ConVar tf_bot_flag_kill_on_touch( "tf_bot_flag_kill_on_touch", "0", FCVAR_CHEAT, "If nonzero, any bot that picks up the flag dies. For testing." );
+
+
+//-----------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotScenarioMonitor::Update( CTFBot *me, float interval )
+{
+ // CTF Scenario
+ if ( me->HasTheFlag() )
+ {
+ if ( tf_bot_flag_kill_on_touch.GetBool() )
+ {
+ me->CommitSuicide( false, true );
+ return Done( "Flag kill" );
+ }
+
+ // we just picked up the flag - drop what we're doing and take it in
+ return SuspendFor( new CTFBotDeliverFlag, "I've picked up the flag! Running it in..." );
+ }
+
+ if ( me->HasMission( CTFBot::NO_MISSION ) && m_ignoreLostFlagTimer.IsElapsed() && me->IsAllowedToPickUpFlag() )
+ {
+ CCaptureFlag *flag = me->GetFlagToFetch();
+
+ if ( flag )
+ {
+ CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() );
+ if ( carrier )
+ {
+ m_lostFlagTimer.Invalidate();
+ }
+ else
+ {
+ // flag is loose
+ if ( !m_lostFlagTimer.HasStarted() )
+ {
+ m_lostFlagTimer.Start( tf_bot_fetch_lost_flag_time.GetFloat() );
+ }
+ else if ( m_lostFlagTimer.IsElapsed() )
+ {
+ m_lostFlagTimer.Invalidate();
+
+ // if we're a Medic an actively healing someone, don't interrupt
+ if ( !me->MedicGetHealTarget() )
+ {
+ // we better go get the flag
+ return SuspendFor( new CTFBotFetchFlag( TEMPORARY_FLAG_FETCH ), "Fetching lost flag..." );
+ }
+ }
+ }
+ }
+ }
+
+ return Continue();
+}
+