diff options
Diffstat (limited to 'game/server/tf/bot/behavior/scenario/capture_the_flag')
8 files changed, 981 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.cpp b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.cpp new file mode 100644 index 0000000..95f1480 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.cpp @@ -0,0 +1,126 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_attack_flag_defenders.cpp +// Attack enemies that are preventing the flag from reaching its destination +// Michael Booth, May 2011 + +#include "cbase.h" + +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h" + +ConVar tf_bot_flag_escort_range( "tf_bot_flag_escort_range", "500", FCVAR_CHEAT ); + +extern ConVar tf_bot_flag_escort_max_count; + +extern int GetBotEscortCount( int team ); + + +//--------------------------------------------------------------------------------------------- +CTFBotAttackFlagDefenders::CTFBotAttackFlagDefenders( float minDuration ) +{ + if ( minDuration > 0.0f ) + { + m_minDurationTimer.Start( minDuration ); + } + else + { + m_minDurationTimer.Invalidate(); + } +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotAttackFlagDefenders::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + + m_chasePlayer = NULL; + return CTFBotAttack::OnStart( me, priorAction ); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotAttackFlagDefenders::Update( CTFBot *me, float interval ) +{ + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->IsVisibleRecently() ) + { + // prepare to fight + me->EquipBestWeaponForThreat( threat ); + } + + if ( m_watchFlagTimer.IsElapsed() && m_minDurationTimer.IsElapsed() ) + { + m_watchFlagTimer.Start( RandomFloat( 1.0f, 3.0f ) ); + + CCaptureFlag *flag = me->GetFlagToFetch(); + + if ( !flag ) + { + return Done( "No flag" ); + } + + // can't reach flag if it is at home + if ( !TFGameRules()->IsMannVsMachineMode() || !flag->IsHome() ) + { + CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() ); + if ( !carrier ) + { + return Done( "Flag was dropped" ); + } + + if ( me->IsSelf( carrier ) ) + { + return Done( "I picked up the flag!" ); + } + + // escort the flag carrier, unless the carrier is in a squad + CTFBot *botCarrier = ToTFBot( carrier ); + if ( !botCarrier || !botCarrier->IsInASquad() ) + { + if ( me->IsRangeLessThan( carrier, tf_bot_flag_escort_range.GetFloat() ) ) + { + if ( GetBotEscortCount( me->GetTeamNumber() ) < tf_bot_flag_escort_max_count.GetInt() ) + { + return ChangeTo( new CTFBotEscortFlagCarrier, "Near flag carrier - escorting" ); + } + } + } + } + } + + ActionResult< CTFBot > result = CTFBotAttack::Update( me, interval ); + + if ( result.IsDone() ) + { + // nothing to attack, move towards a random player + + if ( m_chasePlayer == NULL || !m_chasePlayer->IsAlive() ) + { + m_chasePlayer = me->SelectRandomReachableEnemy(); + } + + if ( m_chasePlayer == NULL ) + { + // everyone is dead or hiding in the spawn room - go escort the flag + return ChangeTo( new CTFBotEscortFlagCarrier, "No reachable victim - escorting flag" ); + } + + // cheat and "see" our victim so we know where to go + me->GetVisionInterface()->AddKnownEntity( m_chasePlayer ); + + if ( m_repathTimer.IsElapsed() ) + { + m_repathTimer.Start( RandomFloat( 1.0f, 3.0f ) ); + + CTFBotPathCost cost( me, DEFAULT_ROUTE ); + float maxPathLength = TFGameRules()->IsMannVsMachineMode() ? TFBOT_MVM_MAX_PATH_LENGTH : 0.0f; + m_path.Compute( me, m_chasePlayer, cost, maxPathLength ); + } + + m_path.Update( me ); + } + + return Continue(); +} diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h new file mode 100644 index 0000000..662ef8a --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_attack_flag_defenders.h +// Attack enemies that are preventing the flag from reaching its destination +// Michael Booth, May 2011 + +#ifndef TF_BOT_ATTACK_FLAG_DEFENDERS_H +#define TF_BOT_ATTACK_FLAG_DEFENDERS_H + +#include "Path/NextBotPathFollow.h" +#include "bot/behavior/tf_bot_attack.h" + + +//----------------------------------------------------------------------------- +class CTFBotAttackFlagDefenders : public CTFBotAttack +{ +public: + CTFBotAttackFlagDefenders( float minDuration = -1.0f ); + virtual ~CTFBotAttackFlagDefenders() { } + + virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction ); + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + + virtual const char *GetName( void ) const { return "AttackFlagDefenders"; } + +private: + CountdownTimer m_minDurationTimer; + CountdownTimer m_watchFlagTimer; + CHandle< CTFPlayer > m_chasePlayer; + PathFollower m_path; + CountdownTimer m_repathTimer; +}; + + +#endif // TF_BOT_ATTACK_FLAG_DEFENDERS_H diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.cpp b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.cpp new file mode 100644 index 0000000..873c36e --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.cpp @@ -0,0 +1,422 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_deliver_flag.cpp +// Take the flag we are holding to its destination +// Michael Booth, May 2011 + +#include "cbase.h" + +#include "tf_player_shared.h" + +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h" +#include "bot/behavior/tf_bot_taunt.h" +#include "bot/behavior/tf_bot_mvm_deploy_bomb.h" + +#include "tf_objective_resource.h" +#include "player_vs_environment/tf_population_manager.h" +#include "econ_item_system.h" +#include "tf_gamestats.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 "particle_parse.h" + +ConVar tf_mvm_bot_allow_flag_carrier_to_fight( "tf_mvm_bot_allow_flag_carrier_to_fight", "1", FCVAR_CHEAT ); + +ConVar tf_mvm_bot_flag_carrier_interval_to_1st_upgrade( "tf_mvm_bot_flag_carrier_interval_to_1st_upgrade", "5", FCVAR_CHEAT ); +ConVar tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade( "tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade", "15", FCVAR_CHEAT ); +ConVar tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade( "tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade", "15", FCVAR_CHEAT ); + +ConVar tf_mvm_bot_flag_carrier_health_regen( "tf_mvm_bot_flag_carrier_health_regen", "45.0f", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotDeliverFlag::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_flTotalTravelDistance = -1.0f; + + m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + + if ( tf_mvm_bot_allow_flag_carrier_to_fight.GetBool() == false ) + { + me->SetAttribute( CTFBot::SUPPRESS_FIRE ); + } + + // mini-bosses don't upgrade - they are already tough + if ( me->IsMiniBoss() ) + { + m_upgradeLevel = DONT_UPGRADE; + if ( TFObjectiveResource() ) + { + // Set threat level to max + TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 4 ); + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 ); + } + } + else + { + m_upgradeLevel = 0; + m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_1st_upgrade.GetFloat() ); + if ( TFObjectiveResource() ) + { + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() ); + } + + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +// In Mann Vs Machine, the flag carrier gets stronger the longer he carries the flag +bool CTFBotDeliverFlag::UpgradeOverTime( CTFBot *me ) +{ + if ( TFGameRules()->IsMannVsMachineMode() && m_upgradeLevel != DONT_UPGRADE ) + { + CTFNavArea *myArea = me->GetLastKnownArea(); + int spawnRoomFlag = me->GetTeamNumber() == TF_TEAM_RED ? TF_NAV_SPAWN_ROOM_RED : TF_NAV_SPAWN_ROOM_BLUE; + + if ( myArea && myArea->HasAttributeTF( spawnRoomFlag ) ) + { + // don't start counting down until we leave the spawn + m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_1st_upgrade.GetFloat() ); + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() ); + } + + // do defensive buff effect ourselves (since we're not a soldier) + if ( m_upgradeLevel > 0 && m_buffPulseTimer.IsElapsed() ) + { + m_buffPulseTimer.Start( 1.0f ); + + CUtlVector< CTFPlayer * > playerVector; + CollectPlayers( &playerVector, me->GetTeamNumber(), COLLECT_ONLY_LIVING_PLAYERS ); + + const float buffRadius = 450.0f; + + for( int i=0; i<playerVector.Count(); ++i ) + { + if ( me->IsRangeLessThan( playerVector[i], buffRadius ) ) + { + playerVector[i]->m_Shared.AddCond( TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK, 1.2f ); + } + } + } + + // the flag carrier gets stronger the longer he holds the flag + if ( m_upgradeTimer.IsElapsed() ) + { + const int maxLevel = 3; + + if ( m_upgradeLevel < maxLevel ) + { + ++m_upgradeLevel; + + TFGameRules()->BroadcastSound( 255, "MVM.Warning" ); + + switch( m_upgradeLevel ) + { + //--------------------------------------- + case 1: + m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade.GetFloat() ); + + // permanent buff banner effect (handled above) + + // update the objective resource so clients have the information + if ( TFObjectiveResource() ) + { + TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 1 ); + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() ); + TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE1, TF_TEAM_PVE_DEFENDERS ); + DispatchParticleEffect( "mvm_levelup1", PATTACH_POINT_FOLLOW, me, "head" ); + } + return true; + + //--------------------------------------- + case 2: + { + static CSchemaAttributeDefHandle pAttrDef_HealthRegen( "health regen" ); + + m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade.GetFloat() ); + + if ( !pAttrDef_HealthRegen ) + { + Warning( "TFBotSpawner: Invalid attribute 'health regen'\n" ); + } + else + { + CAttributeList *pAttrList = me->GetAttributeList(); + if ( pAttrList ) + { + pAttrList->SetRuntimeAttributeValue( pAttrDef_HealthRegen, tf_mvm_bot_flag_carrier_health_regen.GetFloat() ); + } + } + + // update the objective resource so clients have the information + if ( TFObjectiveResource() ) + { + TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 2 ); + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() ); + TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE2, TF_TEAM_PVE_DEFENDERS ); + DispatchParticleEffect( "mvm_levelup2", PATTACH_POINT_FOLLOW, me, "head" ); + } + return true; + } + + //--------------------------------------- + case 3: + // add critz + me->m_Shared.AddCond( TF_COND_CRITBOOSTED ); + + // update the objective resource so clients have the information + if ( TFObjectiveResource() ) + { + TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 3 ); + TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 ); + TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 ); + TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE3, TF_TEAM_PVE_DEFENDERS ); + DispatchParticleEffect( "mvm_levelup3", PATTACH_POINT_FOLLOW, me, "head" ); + } + return true; + } + } + } + } + + return false; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotDeliverFlag::Update( CTFBot *me, float interval ) +{ + CCaptureFlag *flag = me->GetFlagToFetch(); + + if ( !flag ) + { + return Done( "No flag" ); + } + + CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() ); + if ( !carrier || !me->IsSelf( carrier ) ) + { + return Done( "I'm no longer carrying the flag" ); + } + + if ( TFGameRules()->IsMannVsMachineMode() ) + { + // let the bomb carrier use it's buff banners/etc + Action< CTFBot > *result = me->OpportunisticallyUseWeaponAbilities(); + if ( result ) + { + return SuspendFor( result, "Opportunistically using buff item" ); + } + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->IsVisibleRecently() ) + { + // prepare to fight + me->EquipBestWeaponForThreat( threat ); + } + + // deliver the flag + if ( m_repathTimer.IsElapsed() ) + { + CCaptureZone *zone = me->GetFlagCaptureZone(); + + if ( !zone ) + { + return Done( "No flag capture zone exists!" ); + } + + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_path.Compute( me, zone->WorldSpaceCenter(), cost ); + + float flOldTravelDistance = m_flTotalTravelDistance; + + m_flTotalTravelDistance = NavAreaTravelDistance( me->GetLastKnownArea(), TheNavMesh->GetNavArea( zone->WorldSpaceCenter() ), cost ); + + if ( flOldTravelDistance != -1.0f && m_flTotalTravelDistance - flOldTravelDistance > 2000.0f ) + { + TFGameRules()->BroadcastSound( 255, "Announcer.MVM_Bomb_Reset" ); + + // Look for players that helped with the reset and send an event + CUtlVector<CTFPlayer *> playerVector; + CollectPlayers( &playerVector, TF_TEAM_PVE_DEFENDERS ); + FOR_EACH_VEC( playerVector, i ) + { + CTFPlayer *pPlayer = playerVector[i]; + if ( !pPlayer ) + continue; + + if ( me->m_AchievementData.IsPusherInHistory( pPlayer, 3.f ) ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "mvm_bomb_reset_by_player" ); + if ( event ) + { + event->SetInt( "player", pPlayer->entindex() ); + gameeventmanager->FireEvent( event ); + } + + CTF_GameStats.Event_PlayerAwardBonusPoints( pPlayer, me, 100 ); + } + } + } + + m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); + } + + m_path.Update( me ); + + if ( UpgradeOverTime( me ) ) + { + return SuspendFor( new CTFBotTaunt, "Taunting for our new upgrade" ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +void CTFBotDeliverFlag::OnEnd( CTFBot *me, Action< CTFBot > *nextAction ) +{ + me->ClearAttribute( CTFBot::SUPPRESS_FIRE ); + + if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) + { + me->m_Shared.ResetRageBuffs(); + } +} + + +//--------------------------------------------------------------------------------------------- +QueryResultType CTFBotDeliverFlag::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const +{ + if ( tf_mvm_bot_allow_flag_carrier_to_fight.GetBool() ) + { + return ANSWER_UNDEFINED; + } + + return ANSWER_NO; +} + + +//--------------------------------------------------------------------------------------------- +// are we in a hurry? +QueryResultType CTFBotDeliverFlag::ShouldHurry( const INextBot *me ) const +{ + return ANSWER_YES; +} + + +//--------------------------------------------------------------------------------------------- +// is it time to retreat? +QueryResultType CTFBotDeliverFlag::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotDeliverFlag::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result ) +{ + if ( TFGameRules()->IsMannVsMachineMode() && other && FClassnameIs( other, "func_capturezone" ) ) + { + return TrySuspendFor( new CTFBotMvMDeployBomb, RESULT_CRITICAL, "Delivering the bomb!" ); + } + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +CTFBotPushToCapturePoint::CTFBotPushToCapturePoint( Action< CTFBot > *nextAction ) +{ + m_nextAction = nextAction; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotPushToCapturePoint::Update( CTFBot *me, float interval ) +{ + // flag collection and delivery is handled by our parent behavior, ScenarioMonitor + + CCaptureZone *zone = me->GetFlagCaptureZone(); + + if ( !zone ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "No flag capture zone exists!" ); + } + + return Done( "No flag capture zone exists!" ); + } + + Vector toZone = zone->WorldSpaceCenter() - me->GetAbsOrigin(); + if ( toZone.AsVector2D().IsLengthLessThan( 50.0f ) ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "At destination" ); + } + + return Done( "At destination" ); + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->IsVisibleRecently() ) + { + // prepare to fight + me->EquipBestWeaponForThreat( threat ); + } + + if ( m_repathTimer.IsElapsed() ) + { + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_path.Compute( me, zone->WorldSpaceCenter(), cost ); + + m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); + } + + m_path.Update( me ); + + return Continue(); +} + +//----------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotPushToCapturePoint::OnNavAreaChanged( CTFBot *me, CNavArea *newArea, CNavArea *oldArea ) +{ + // does the area we are entering have a prerequisite? + if ( newArea && newArea->HasPrerequisite( me ) ) + { + const CUtlVector< CHandle< CFuncNavPrerequisite > > &prereqVector = newArea->GetPrerequisiteVector(); + + for( int i=0; i<prereqVector.Count(); ++i ) + { + const CFuncNavPrerequisite *prereq = prereqVector[i]; + if ( prereq && prereq->IsEnabled() && const_cast< CFuncNavPrerequisite * >( prereq )->PassesTriggerFilters( me ) ) + { + // this prerequisite applies to me + if ( prereq->IsTask( CFuncNavPrerequisite::TASK_WAIT ) ) + { + return TrySuspendFor( new CTFBotNavEntWait( prereq ), RESULT_IMPORTANT, "Prerequisite commands me to wait" ); + } + else if ( prereq->IsTask( CFuncNavPrerequisite::TASK_MOVE_TO_ENTITY ) ) + { + return TrySuspendFor( new CTFBotNavEntMoveTo( prereq ), RESULT_IMPORTANT, "Prerequisite commands me to move to an entity" ); + } + } + } + } + + return TryContinue(); +} diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h new file mode 100644 index 0000000..1e8dfdf --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h @@ -0,0 +1,63 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_deliver_flag.h +// Take the flag we are holding to its destination +// Michael Booth, May 2011 + +#ifndef TF_BOT_DELIVER_FLAG_H +#define TF_BOT_DELIVER_FLAG_H + +#include "Path/NextBotPathFollow.h" + + +//----------------------------------------------------------------------------- +class CTFBotDeliverFlag : 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 QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; + virtual QueryResultType ShouldHurry( const INextBot *me ) const; + virtual QueryResultType ShouldRetreat( const INextBot *me ) const; + + virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL ); + + virtual const char *GetName( void ) const { return "DeliverFlag"; }; + +private: + PathFollower m_path; + CountdownTimer m_repathTimer; + float m_flTotalTravelDistance; + + bool UpgradeOverTime( CTFBot *me ); + CountdownTimer m_upgradeTimer; + +#define DONT_UPGRADE -1 + int m_upgradeLevel; + + CountdownTimer m_buffPulseTimer; +}; + + +//----------------------------------------------------------------------------- +class CTFBotPushToCapturePoint : public Action< CTFBot > +{ +public: + CTFBotPushToCapturePoint( Action< CTFBot > *nextAction = NULL ); + virtual ~CTFBotPushToCapturePoint() { } + + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + virtual EventDesiredResult< CTFBot > OnNavAreaChanged( CTFBot *me, CNavArea *newArea, CNavArea *oldArea ); + + virtual const char *GetName( void ) const { return "PushToCapturePoint"; }; + +private: + PathFollower m_path; + CountdownTimer m_repathTimer; + + Action< CTFBot > *m_nextAction; +}; + + +#endif // TF_BOT_DELIVER_FLAG_H diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.cpp b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.cpp new file mode 100644 index 0000000..ca48e19 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.cpp @@ -0,0 +1,142 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_escort_flag_carrier.cpp +// Escort the flag carrier to their destination +// Michael Booth, May 2011 + +#include "cbase.h" + +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h" + +extern ConVar tf_bot_flag_escort_range; + +ConVar tf_bot_flag_escort_give_up_range( "tf_bot_flag_escort_give_up_range", "1000", FCVAR_CHEAT ); +ConVar tf_bot_flag_escort_max_count( "tf_bot_flag_escort_max_count", "4", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +// +// Count the number of TFBots currently engaged in the "EscortFlagCarrier" behavior +// +int GetBotEscortCount( int team ) +{ + int count = 0; + + CUtlVector< CTFPlayer * > livePlayerVector; + CollectPlayers( &livePlayerVector, team, COLLECT_ONLY_LIVING_PLAYERS ); + + int i; + for( i=0; i<livePlayerVector.Count(); ++i ) + { + CTFBot *bot = dynamic_cast< CTFBot * >( livePlayerVector[i] ); + if ( bot ) + { + Behavior< CTFBot > *behavior = (Behavior< CTFBot > *)bot->GetIntentionInterface()->FirstContainedResponder(); + if ( behavior ) + { + Action< CTFBot > *action = (Action< CTFBot > *)behavior->FirstContainedResponder(); + + while( action && action->GetActiveChildAction() ) + { + action = action->GetActiveChildAction(); + } + + if ( action && action->IsNamed( "EscortFlagCarrier" ) ) + { + ++count; + } + } + } + } + + return count; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotEscortFlagCarrier::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotEscortFlagCarrier::Update( CTFBot *me, float interval ) +{ + CCaptureFlag *flag = me->GetFlagToFetch(); + + if ( !flag ) + { + return Done( "No flag" ); + } + + CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() ); + if ( !carrier ) + { + return Done( "Flag was dropped" ); + } + else if ( me->IsSelf( carrier ) ) + { + return Done( "I picked up the flag!" ); + } + + // stay near the carrier + if ( me->IsRangeGreaterThan( carrier, tf_bot_flag_escort_give_up_range.GetFloat() ) ) + { + if ( me->SelectRandomReachableEnemy() ) + { + // too far away - give up + return ChangeTo( new CTFBotAttackFlagDefenders, "Too far from flag carrier - attack defenders!" ); + } + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->IsVisibleRecently() ) + { + // prepare to fight + me->EquipBestWeaponForThreat( threat ); + } + + CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon(); + if ( myWeapon && myWeapon->IsMeleeWeapon() ) + { + if ( me->IsRangeLessThan( carrier, tf_bot_flag_escort_range.GetFloat() ) && me->IsLineOfSightClear( carrier ) ) + { + ActionResult< CTFBot > result = m_meleeAttackAction.Update( me, interval ); + + if ( result.IsContinue() ) + { + // we have a melee target, and we're still reasonably close to the flag carrier + return Continue(); + } + } + } + + if ( me->IsRangeGreaterThan( carrier, 0.5f * tf_bot_flag_escort_range.GetFloat() ) ) + { + // move near carrier + if ( m_repathTimer.IsElapsed() ) + { + if ( GetBotEscortCount( me->GetTeamNumber() ) > tf_bot_flag_escort_max_count.GetInt() ) + { + if ( me->SelectRandomReachableEnemy() ) + { + return Done( "Too many flag escorts - giving up" ); + } + } + + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_path.Compute( me, carrier, cost ); + + m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); + } + + m_path.Update( me ); + } + + return Continue(); +} diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h new file mode 100644 index 0000000..f8e5f9d --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_escort_flag_carrier.h +// Escort the flag carrier to their destination +// Michael Booth, May 2011 + +#ifndef TF_BOT_ESCORT_FLAG_CARRIER_H +#define TF_BOT_ESCORT_FLAG_CARRIER_H + + +#include "Path/NextBotPathFollow.h" +#include "bot/behavior/tf_bot_melee_attack.h" + + +//----------------------------------------------------------------------------- +class CTFBotEscortFlagCarrier : public Action< CTFBot > +{ +public: + virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction ); + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + + virtual const char *GetName( void ) const { return "EscortFlagCarrier"; }; + +private: + PathFollower m_path; + CountdownTimer m_repathTimer; + + CTFBotMeleeAttack m_meleeAttackAction; +}; + + +#endif // TF_BOT_ESCORT_FLAG_CARRIER_H diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.cpp b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.cpp new file mode 100644 index 0000000..84e02ce --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.cpp @@ -0,0 +1,128 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_fetch_flag.cpp +// Go get the flag! +// Michael Booth, May 2011 + +#include "cbase.h" + +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h" +#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h" + + +//--------------------------------------------------------------------------------------------- +CTFBotFetchFlag::CTFBotFetchFlag( bool isTemporary ) +{ + m_isTemporary = isTemporary; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotFetchFlag::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotFetchFlag::Update( CTFBot *me, float interval ) +{ + CCaptureFlag *flag = me->GetFlagToFetch(); + + if ( !flag ) + { + if ( TFGameRules()->IsMannVsMachineMode() ) + { + return SuspendFor( new CTFBotAttackFlagDefenders, "Flag flag exists - Attacking the enemy flag defenders" ); + } + + return Done( "No flag" ); + } + + // uncloak so we can attack + if ( me->m_Shared.IsStealthed() ) + { + me->PressAltFireButton(); + } + + if ( TFGameRules()->IsMannVsMachineMode() && flag->IsHome() ) + { + if ( gpGlobals->curtime - me->GetSpawnTime() < 1.0f && me->GetTeamNumber() != TEAM_SPECTATOR ) + { + // we just spawned - give us the flag + flag->PickUp( me, true ); + } + else + { + if ( m_isTemporary ) + { + return Done( "Flag unreachable" ); + } + + // flag is at home and we're out in the world - can't reach it + return SuspendFor( new CTFBotAttackFlagDefenders, "Flag unreachable at home - Attacking the enemy flag defenders" ); + } + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat ) + { + me->EquipBestWeaponForThreat( threat ); + } + + CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() ); + if ( carrier ) + { + if ( m_isTemporary ) + { + return Done( "Someone else picked up the flag" ); + } + + // NOTE: if I've picked up the flag, the ScenarioMonitor will handle it + return SuspendFor( new CTFBotAttackFlagDefenders, "Someone has the flag - attacking the enemy defenders" ); + } + + // go pick up the flag + if ( m_repathTimer.IsElapsed() ) + { + CTFBotPathCost cost( me, DEFAULT_ROUTE ); + float maxPathLength = TFGameRules()->IsMannVsMachineMode() ? TFBOT_MVM_MAX_PATH_LENGTH : 0.0f; + if ( m_path.Compute( me, flag->WorldSpaceCenter(), cost, maxPathLength ) == false ) + { + if ( flag->IsDropped() ) + { + // flag is unreachable - attack for awhile and hope someone else can dislodge it + return SuspendFor( new CTFBotAttackFlagDefenders( RandomFloat( 5.0f, 10.0f ) ), "Flag unreachable - Attacking" ); + + // just give it to me + // flag->PickUp( me, true ); + } + } + + m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); + } + + m_path.Update( me ); + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +// are we in a hurry? +QueryResultType CTFBotFetchFlag::ShouldHurry( const INextBot *me ) const +{ + return ANSWER_YES; +} + + +//--------------------------------------------------------------------------------------------- +// is it time to retreat? +QueryResultType CTFBotFetchFlag::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} diff --git a/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h new file mode 100644 index 0000000..5ca8818 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_fetch_flag.h +// Go get the flag! +// Michael Booth, May 2011 + +#ifndef TF_BOT_FETCH_FLAG_H +#define TF_BOT_FETCH_FLAG_H + +#include "Path/NextBotPathFollow.h" + + +//----------------------------------------------------------------------------- +class CTFBotFetchFlag : public Action< CTFBot > +{ +public: + #define TEMPORARY_FLAG_FETCH true + CTFBotFetchFlag( bool isTemporary = false ); + virtual ~CTFBotFetchFlag() { } + + virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction ); + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + + virtual QueryResultType ShouldHurry( const INextBot *me ) const; + virtual QueryResultType ShouldRetreat( const INextBot *me ) const; + + virtual const char *GetName( void ) const { return "FetchFlag"; }; + +private: + bool m_isTemporary; + PathFollower m_path; + CountdownTimer m_repathTimer; +}; + + +#endif // TF_BOT_FETCH_FLAG_H |