diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/bot/behavior/scenario/raid | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/bot/behavior/scenario/raid')
10 files changed, 1203 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.cpp b/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.cpp new file mode 100644 index 0000000..1d78ff3 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.cpp @@ -0,0 +1,217 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_raid_companion.cpp +// Teammate bots for Raid mode +// Michael Booth, October 2009 + +#include "cbase.h" + +#ifdef TF_RAID_MODE + +#include "team.h" +#include "bot/tf_bot.h" +#include "team_control_point_master.h" +#include "bot/behavior/medic/tf_bot_medic_heal.h" +#include "bot/behavior/scenario/raid/tf_bot_companion.h" +#include "bot/behavior/tf_bot_attack.h" +#include "bot/behavior/tf_bot_move_to_vantage_point.h" +#include "bot/behavior/engineer/tf_bot_engineer_build.h" +#include "bot/behavior/sniper/tf_bot_sniper_lurk.h" + +#include "bot/map_entities/tf_bot_generator.h" // action point + +ConVar tf_raid_companion_follow_range( "tf_raid_companion_follow_range", "150", FCVAR_CHEAT ); +ConVar tf_raid_companion_allow_bot_leader( "tf_raid_companion_allow_bot_leader", "0", FCVAR_CHEAT ); + +extern ConVar tf_bot_path_lookahead_range; + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotCompanion::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +CTFPlayer *CTFBotCompanion::GetLeader( void ) +{ + CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE ); + CTFPlayer *leader = NULL; + float leaderSpeed = FLT_MAX; + + for( int i=0; i<raidingTeam->GetNumPlayers(); ++i ) + { + CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i); + + if ( player->IsBot() && !tf_raid_companion_allow_bot_leader.GetBool() ) + continue; + +/* + if ( player->IsPlayerClass( TF_CLASS_ENGINEER ) || + player->IsPlayerClass( TF_CLASS_SNIPER ) || + player->IsPlayerClass( TF_CLASS_MEDIC ) ) + continue; +*/ + + if ( player->IsAlive() ) + { + float speed = player->GetPlayerClass()->GetMaxSpeed(); + + if ( speed < leaderSpeed ) + { + leader = player; + leaderSpeed = speed; + } + } + } + + return leader; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotCompanion::Update( CTFBot *me, float interval ) +{ + if ( me->IsPlayerClass( TF_CLASS_MEDIC ) ) + { + const CKnownEntity *patient = me->GetVisionInterface()->GetClosestKnown( me->GetTeamNumber() ); + if ( patient ) + { + return SuspendFor( new CTFBotMedicHeal ); + } + } + + CTFPlayer *leader = GetLeader(); + if ( !leader ) + return Continue(); + + CTFBotPathCost cost( me, FASTEST_ROUTE ); + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + + if ( me->IsSelf( leader ) ) + { + const float engageRange = 500.0f; + if ( threat && threat->IsVisibleRecently() && me->IsRangeLessThan( threat->GetEntity(), engageRange ) ) + { + // stop pushing ahead and kill nearby threats + return SuspendFor( new CTFBotAttack, "Attacking nearby threats" ); + } + + // head toward next capture point + CTeamControlPoint *point = me->GetMyControlPoint(); + if ( point ) + { + m_path.Update( me, point, cost ); + } + } + else + { + if ( ( !threat || threat->GetTimeSinceLastSeen() > 3.0f ) && leader->GetTimeSinceLastInjury() < 1.0f ) + { + // we don't see anything, but the leader is under attack - find a better vantage point + const float nearRange = 1000.0f; + return SuspendFor( new CTFBotMoveToVantagePoint( nearRange ), "Moving to where I can see the enemy" ); + } + + if ( leader && me->IsDistanceBetweenGreaterThan( leader, tf_raid_companion_follow_range.GetFloat() ) ) + { + m_path.Update( me, leader, cost ); + } + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotCompanion::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction ) +{ + m_path.Invalidate(); + return Continue(); +} + + + +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGuardian::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGuardian::Update( CTFBot *me, float interval ) +{ + if ( me->GetActionPoint() ) + { + const float atHomeRange = 35.0f; // 25.0f; + const Vector &home = me->GetActionPoint()->GetAbsOrigin(); + + if ( me->IsRangeGreaterThan( home, atHomeRange ) ) + { + if ( m_repathTimer.IsElapsed() && !m_path.IsValid() ) + { + m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) ); + + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_path.Compute( me, home, cost ); + } + + // move home + m_path.Update( me ); + + return Continue(); + } + } + + // at home + m_path.Invalidate(); + me->SetHomeArea( me->GetLastKnownArea() ); + + if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) ) + { + return SuspendFor( new CTFBotEngineerBuild ); + } + + if ( me->IsPlayerClass( TF_CLASS_SNIPER ) ) + { + return SuspendFor( new CTFBotSniperLurk ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGuardian::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction ) +{ + m_path.Invalidate(); + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardian::OnStuck( CTFBot *me ) +{ + m_path.Invalidate(); + return TryContinue( RESULT_IMPORTANT ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardian::OnMoveToSuccess( CTFBot *me, const Path *path ) +{ + m_path.Invalidate(); + return TryContinue( RESULT_IMPORTANT ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardian::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason ) +{ + m_path.Invalidate(); + return TryContinue( RESULT_IMPORTANT ); +} + +#endif // TF_RAID_MODE diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.h b/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.h new file mode 100644 index 0000000..e379b0b --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_companion.h @@ -0,0 +1,57 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_companion.h +// Teammate bots for Raid mode +// Michael Booth, October 2009 + +#ifndef TF_BOT_COMPANION_H +#define TF_BOT_COMPANION_H + +#ifdef TF_RAID_MODE + +#include "Path/NextBotPathFollow.h" +#include "Path/NextBotChasePath.h" + +// +// Friendly teammate bots +// +class CTFBotCompanion : public Action< CTFBot > +{ +public: + 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 "Companion"; }; + +private: + ChasePath m_path; + CTFPlayer *GetLeader( void ); +}; + + +// +// Friendly defenders of the base +// +class CTFBotGuardian : public Action< CTFBot > +{ +public: + 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 EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path ); + virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason ); + + virtual const char *GetName( void ) const { return "Guardian"; }; + +private: + PathFollower m_path; + CountdownTimer m_repathTimer; +}; + +#endif // TF_RAID_MODE + +#endif // TF_BOT_COMPANION_H diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.cpp b/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.cpp new file mode 100644 index 0000000..32759d9 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.cpp @@ -0,0 +1,261 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_guard_area.cpp +// Defend an area against intruders +// Michael Booth, October 2009 + +#include "cbase.h" + +#ifdef TF_RAID_MODE + +#include "tf_player.h" +#include "bot/tf_bot.h" +#include "team_control_point_master.h" +#include "econ_entity_creation.h" +#include "bot/behavior/tf_bot_retreat_to_cover.h" +#include "bot/behavior/sniper/tf_bot_sniper_attack.h" +#include "bot/behavior/engineer/tf_bot_engineer_build.h" +#include "bot/behavior/medic/tf_bot_medic_heal.h" +#include "bot/behavior/scenario/raid/tf_bot_wander.h" +#include "bot/behavior/scenario/raid/tf_bot_guard_area.h" +#include "bot/behavior/tf_bot_attack.h" +#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h" + +#include "nav_mesh.h" + +extern ConVar tf_bot_path_lookahead_range; +ConVar tf_bot_guard_aggro_range( "tf_bot_guard_aggro_range", "750", FCVAR_CHEAT ); +//ConVar tf_bot_guard_give_up_range( "tf_bot_guard_give_up_range", "1250", FCVAR_CHEAT ); + +ConVar tf_raid_special_vocalize_min_interval( "tf_raid_special_vocalize_min_interval", "10", FCVAR_CHEAT ); +ConVar tf_raid_special_vocalize_max_interval( "tf_raid_special_vocalize_max_interval", "15", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGuardArea::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_chasePath.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + +/* + // give this guy a hat! + randomitemcriteria_t criteria; + criteria.iItemLevel = AE_USE_SCRIPT_VALUE; + criteria.iItemQuality = AE_USE_SCRIPT_VALUE; + criteria.vecAbsOrigin = me->GetAbsOrigin(); + criteria.vecAbsAngles = vec3_angle; + + switch( me->GetPlayerClass()->GetClassIndex() ) + { + case TF_CLASS_SCOUT: criteria.pszItemName = "Scout Hat 1"; break; + case TF_CLASS_SNIPER: criteria.pszItemName = "Sniper Hat 1"; break; + case TF_CLASS_SOLDIER: criteria.pszItemName = "Soldier Pot Hat"; break; + case TF_CLASS_DEMOMAN: criteria.pszItemName = "Demo Top Hat"; break; + case TF_CLASS_MEDIC: criteria.pszItemName = "Medic Hat 1"; break; + case TF_CLASS_HEAVYWEAPONS: criteria.pszItemName = "Heavy Ushanka Hat"; break; + case TF_CLASS_PYRO: criteria.pszItemName = "Pyro Chicken Hat"; break; + case TF_CLASS_SPY: criteria.pszItemName = "Spy Derby Hat"; break; + case TF_CLASS_ENGINEER: criteria.pszItemName = "Engineer Hat 1"; break; + default: criteria.pszItemName = ""; break; + } + + CBaseEntity *hat = ItemGeneration()->GenerateRandomItem( &criteria ); + if ( hat ) + { + // Fake global id + static int s_nFakeID = 1; + static_cast< CEconEntity * >( hat )->GetAttributeContainer()->GetItem()->SetItemID( s_nFakeID++ ); + + DispatchSpawn( hat ); + static_cast< CEconEntity * >( hat )->GetAttributeContainer()->GetItem()->GenerateAttributes(); + static_cast< CEconEntity * >( hat )->GiveTo( me ); + } + else + { + Msg( "Failed to create hat\n" ); + } +*/ + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +class CFindVantagePoint : public ISearchSurroundingAreasFunctor +{ +public: + CFindVantagePoint( void ) + { + m_vantageArea = NULL; + } + + virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar ) + { + if ( travelDistanceSoFar > 2000.0f ) + return false; + + CTFNavArea *area = (CTFNavArea *)baseArea; + + CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE ); + for( int i=0; i<raidingTeam->GetNumPlayers(); ++i ) + { + CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i); + + if ( !player->IsAlive() || !player->GetLastKnownArea() ) + continue; + + CTFNavArea *playerArea = (CTFNavArea *)player->GetLastKnownArea(); + if ( playerArea->IsCompletelyVisible( area ) ) + { + // nearby area from which we can see the enemy team + m_vantageArea = area; + return false; + } + } + + return true; + } + + CTFNavArea *m_vantageArea; +}; + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGuardArea::Update( CTFBot *me, float interval ) +{ + // emit vocalizations to warn players we're in the area + if ( m_vocalizeTimer.IsElapsed() ) + { + m_vocalizeTimer.Start( RandomFloat( tf_raid_special_vocalize_min_interval.GetFloat(), tf_raid_special_vocalize_max_interval.GetFloat() ) ); + me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_JEERS ); + } + + if ( me->IsPlayerClass( TF_CLASS_MEDIC ) ) + { + return SuspendFor( new CTFBotMedicHeal ); + } + + if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) ) + { + return SuspendFor( new CTFBotEngineerBuild ); + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->IsVisibleRecently() ) + { + m_pathToVantageArea.Invalidate(); + + CTFNavArea *myArea = (CTFNavArea *)me->GetLastKnownArea(); + CTFNavArea *threatArea = (CTFNavArea *)threat->GetLastKnownArea(); + if ( myArea && threatArea ) + { + if ( threatArea->GetIncursionDistance( TF_TEAM_BLUE ) < myArea->GetIncursionDistance( TF_TEAM_BLUE ) ) + { + if ( me->IsRangeGreaterThan( threat->GetLastKnownPosition(), tf_bot_guard_aggro_range.GetFloat() ) ) + { + // threat is far off and hasn't reached us yet - hide until they are closer + return SuspendFor( new CTFBotRetreatToCover, "Hiding until threat gets closer" ); + } + } + } + + // attack! + return SuspendFor( new CTFBotAttack, "Attacking nearby threat" ); + } + else + { + // no enemy is visible + Vector moveTo = me->GetAbsOrigin(); + + // if point is being captured, move to it + CTeamControlPoint *point = me->GetMyControlPoint(); + if ( point && point->LastContestedAt() > 0.0f && ( gpGlobals->curtime - point->LastContestedAt() ) < 5.0f ) + { + // the point is, or was very recently, contested - defend it! + moveTo = point->GetAbsOrigin(); + } + else if ( me->GetHomeArea() ) + { + // no enemy is visible - return to our home position + moveTo = me->GetHomeArea()->GetCenter(); + } + + if ( !m_pathToPoint.IsValid() || m_repathTimer.IsElapsed() ) + { + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_pathToPoint.Compute( me, moveTo, cost ); + m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) ); + } + + if ( ( me->GetAbsOrigin() - moveTo ).IsLengthGreaterThan( 25.0f ) ) + { + m_pathToPoint.Update( me ); + } + + if ( me->GetHomeArea() == me->GetLastKnownArea() ) + { + // at home + if ( CTFBotPrepareStickybombTrap::IsPossible( me ) ) + { + return SuspendFor( new CTFBotPrepareStickybombTrap, "Laying sticky bombs!" ); + } + } + +/* + // no enemy is visible - move to where we can see them + if ( !m_pathToVantageArea.IsValid() ) + { + CTFNavArea *vantageArea = me->FindVantagePoint(); + if ( vantageArea ) + { + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_pathToVantageArea.Compute( me, vantageArea->GetCenter(), cost ); + } + } + + m_pathToVantageArea.Update( me ); +*/ + } + + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardArea::OnStuck( CTFBot *me ) +{ + m_chasePath.Invalidate(); + m_pathToPoint.Invalidate(); + m_pathToVantageArea.Invalidate(); + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardArea::OnMoveToSuccess( CTFBot *me, const Path *path ) +{ + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardArea::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason ) +{ + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +QueryResultType CTFBotGuardArea::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGuardArea::OnCommandApproach( CTFBot *me, const Vector &pos, float range ) +{ + return TryContinue(); +} + +#endif // TF_RAID_MODE diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.h b/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.h new file mode 100644 index 0000000..891dda7 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_guard_area.h @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_guard_area.h +// Defend an area against intruders +// Michael Booth, October 2009 + +#ifdef TF_RAID_MODE + +#ifndef TF_BOT_GUARD_AREA_H +#define TF_BOT_GUARD_AREA_H + +#include "Path/NextBotChasePath.h" + +class CTFBotGuardArea : 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 QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat? + + virtual EventDesiredResult< CTFBot > OnCommandApproach( CTFBot *me, const Vector &pos, float range ); + + virtual const char *GetName( void ) const { return "GuardArea"; }; + +private: + ChasePath m_chasePath; + PathFollower m_pathToPoint; + PathFollower m_pathToVantageArea; + CountdownTimer m_vocalizeTimer; + CountdownTimer m_repathTimer; +}; + +#endif // TF_RAID_MODE + +#endif // TF_BOT_GUARD_AREA_H diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.cpp b/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.cpp new file mode 100644 index 0000000..f80117d --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.cpp @@ -0,0 +1,164 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_mob_rush.cpp +// A member of a rushing mob of melee attackers +// Michael Booth, October 2009 + +#include "cbase.h" + +#ifdef TF_RAID_MODE + +#include "team.h" +#include "bot/tf_bot.h" +#include "bot/behavior/tf_bot_taunt.h" +#include "bot/behavior/scenario/raid/tf_bot_mob_rush.h" + + +ConVar tf_bot_taunt_range( "tf_bot_taunt_range", "100", FCVAR_CHEAT ); +ConVar tf_raid_mob_rush_vocalize_min_interval( "tf_raid_mob_rush_vocalize_min_interval", "5", FCVAR_CHEAT ); +ConVar tf_raid_mob_rush_vocalize_max_interval( "tf_raid_mob_rush_vocalize_max_interval", "8", FCVAR_CHEAT ); +ConVar tf_raid_mob_avoid_range( "tf_raid_mob_avoid_range", "100", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +CTFBotMobRush::CTFBotMobRush( CTFPlayer *victim, float reactionTime ) +{ + m_victim = victim; + + // this isn't strictly correct - we shouldn't start the timer until OnStart + m_reactionTimer.Start( reactionTime ); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotMobRush::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_vocalizeTimer.Start( RandomFloat( tf_raid_mob_rush_vocalize_min_interval.GetFloat(), tf_raid_mob_rush_vocalize_max_interval.GetFloat() ) ); + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotMobRush::Update( CTFBot *me, float interval ) +{ + // mobs use only their melee weapons + CBaseCombatWeapon *meleeWeapon = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE ); + if ( meleeWeapon ) + { + me->Weapon_Switch( meleeWeapon ); + } + + + if ( m_victim == NULL ) + { + return Done( "No victim" ); + } + + me->GetBodyInterface()->AimHeadTowards( m_victim, IBody::CRITICAL, 1.0f, NULL, "Looking at our melee target" ); + + if ( m_reactionTimer.HasStarted() ) + { + if ( m_reactionTimer.IsElapsed() ) + { + // snap out of it! + me->DoAnimationEvent( PLAYERANIMEVENT_VOICE_COMMAND_GESTURE, ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY ); + me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY ); + m_reactionTimer.Invalidate(); + } + else + { + // wait for reaction time to elapse + return Continue(); + } + } + + if ( me->IsPlayingGesture( ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY ) ) + { + // wait for "wake up" anim to finish + return Continue(); + } + + // just keep swinging + me->PressFireButton(); + + // chase them down + CTFBotPathCost cost( me, FASTEST_ROUTE ); + m_path.Update( me, m_victim, cost ); + + // avoid friends + CTeam *team = GetGlobalTeam( TF_TEAM_RED ); + for( int t=0; t<team->GetNumPlayers(); ++t ) + { + CTFPlayer *teamMember = (CTFPlayer *)team->GetPlayer(t); + + if ( !teamMember->IsAlive() ) + continue; + + Vector toBuddy = teamMember->GetAbsOrigin() - me->GetAbsOrigin(); + if ( toBuddy.IsLengthLessThan( tf_raid_mob_avoid_range.GetFloat() ) ) + { + float range = toBuddy.NormalizeInPlace(); + + me->GetLocomotionInterface()->Approach( me->GetAbsOrigin() - 100.0f * toBuddy, 1.0f - ( range / tf_raid_mob_avoid_range.GetFloat() ) ); + } + } + + + if ( !m_victim->IsAlive() && me->IsRangeLessThan( m_victim, tf_bot_taunt_range.GetFloat() ) ) + { + // we got 'em! + return ChangeTo( new CTFBotTaunt, "Taunt their corpse" ); + } + + if ( m_vocalizeTimer.IsElapsed() ) + { + m_vocalizeTimer.Start( RandomFloat( tf_raid_mob_rush_vocalize_min_interval.GetFloat(), tf_raid_mob_rush_vocalize_max_interval.GetFloat() ) ); + + if ( me->IsPlayerClass( TF_CLASS_SCOUT ) ) + me->EmitSound( "Scout.MobJabber" ); + else if ( me->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ) + me->EmitSound( "Heavy.MobJabber" ); + else + me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotMobRush::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result ) +{ + return TryToSustain( RESULT_CRITICAL, "Ignoring contact" ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotMobRush::OnInjured( CTFBot *me, const CTakeDamageInfo &info ) +{ + return TryToSustain( RESULT_CRITICAL, "Ignoring injury" ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotMobRush::OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info ) +{ + return TryToSustain( RESULT_CRITICAL, "Ignoring friend death" ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotMobRush::OnStuck( CTFBot *me ) +{ + m_path.Invalidate(); + return TryToSustain( RESULT_CRITICAL ); +} + + +//--------------------------------------------------------------------------------------------- +QueryResultType CTFBotMobRush::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} + +#endif // TF_RAID_MODE diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.h b/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.h new file mode 100644 index 0000000..0331acc --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_mob_rush.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_mob_rush.h +// A member of a rushing mob of melee attackers +// Michael Booth, October 2009 + +#ifndef TF_BOT_MOB_RUSH_H +#define TF_BOT_MOB_RUSH_H + +#ifdef TF_RAID_MODE + +#include "Path/NextBotChasePath.h" + + +//----------------------------------------------------------------------------- +class CTFBotMobRush : public Action< CTFBot > +{ +public: + CTFBotMobRush( CTFPlayer *victim, float reactionTime = 0.0f ); + + virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction ); + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + + virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL ); + virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info ); + virtual EventDesiredResult< CTFBot > OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info ); + virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me ); + + QueryResultType ShouldRetreat( const INextBot *me ) const; + + virtual const char *GetName( void ) const { return "MobRush"; }; + +private: + CHandle< CTFPlayer > m_victim; + CountdownTimer m_reactionTimer; + CountdownTimer m_tauntTimer; + CountdownTimer m_vocalizeTimer; + ChasePath m_path; +}; + +#endif // TF_RAID_MODE + +#endif // TF_BOT_MOB_RUSH_H diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.cpp b/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.cpp new file mode 100644 index 0000000..95d4362 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.cpp @@ -0,0 +1,150 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_squad_attack.cpp +// Move and attack as a small, cohesive, group +// Michael Booth, October 2009 + +#include "cbase.h" + +#ifdef TF_RAID_MODE + +#include "team.h" +#include "raid/tf_raid_logic.h" +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/raid/tf_bot_wander.h" +#include "bot/behavior/scenario/raid/tf_bot_squad_attack.h" +#include "bot/behavior/medic/tf_bot_medic_heal.h" +#include "bot/behavior/tf_bot_move_to_vantage_point.h" + + +ConVar tf_squad_radius( "tf_squad_radius", "200", FCVAR_CHEAT ); +ConVar tf_squad_debug( "tf_squad_debug", "0", FCVAR_CHEAT ); +ConVar tf_raid_squad_vocalize_min_interval( "tf_raid_squad_vocalize_min_interval", "5", FCVAR_CHEAT ); +ConVar tf_raid_squad_vocalize_max_interval( "tf_raid_squad_vocalize_max_interval", "8", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotSquadAttack::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_vocalizeTimer.Start( RandomFloat( tf_raid_squad_vocalize_min_interval.GetFloat(), tf_raid_squad_vocalize_max_interval.GetFloat() ) ); + m_victim = NULL; + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +// the leader is the slowest member of the squad +CTFBot *CTFBotSquadAttack::GetSquadLeader( CTFBot *me ) const +{ + CTFBot *leader = NULL; + float leaderSpeed = FLT_MAX; + + CTFBotSquad *squad = me->GetSquad(); + CTFBotSquad::Iterator it; + for( it = squad->GetFirstMember(); it != squad->InvalidIterator(); it = squad->GetNextMember( it ) ) + { + CTFBot *bot = it(); + + float speed = bot->GetPlayerClass()->GetMaxSpeed(); + + if ( speed < leaderSpeed ) + { + leader = bot; + leaderSpeed = speed; + } + } + + return leader; +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotSquadAttack::Update( CTFBot *me, float interval ) +{ + if ( !me->IsInASquad() ) + return Done( "Not in a squad" ); + + if ( me->IsPlayerClass( TF_CLASS_MEDIC ) ) + { + return SuspendFor( new CTFBotMedicHeal ); + } + + CTFBot *leader = GetSquadLeader( me ); + CTFBotPathCost cost( me, FASTEST_ROUTE ); + + if ( m_victim == NULL || m_victimConsiderTimer.IsElapsed() ) + { + m_victimConsiderTimer.Start( 3.0f ); + + m_victim = TFGameRules()->GetRaidLogic()->SelectRaiderToAttack(); + } + + if ( m_victim ) + { + const float engageRange = 500.0f; + if ( me->IsPlayerClass( TF_CLASS_PYRO ) || + me->IsRangeGreaterThan( m_victim->GetAbsOrigin(), engageRange ) || + !me->GetVisionInterface()->IsAbleToSee( m_victim, IVision::DISREGARD_FOV ) ) + { + if ( me->IsSelf( leader ) || me->IsRangeLessThan( leader, tf_squad_radius.GetFloat() ) ) + { + // chase down the enemy + m_chasePath.Update( me, m_victim, cost ); + } + } + + if ( !me->IsSelf( leader ) && me->IsRangeGreaterThan( leader, 1.25f * tf_squad_radius.GetFloat() ) ) + { + // too far from leader - return to him + m_chasePath.Update( me, leader, cost ); + } + + if ( tf_squad_debug.GetBool() && me->IsSelf( leader ) ) + { + NDebugOverlay::Circle( me->GetAbsOrigin(), 20.0f, 255, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + + CTFBotSquad *squad = me->GetSquad(); + CTFBotSquad::Iterator it; + for( it = squad->GetFirstMember(); it != squad->InvalidIterator(); it = squad->GetNextMember( it ) ) + { + CTFBot *bot = it(); + + if ( me->IsSelf( bot ) ) + continue; + + NDebugOverlay::Line( me->WorldSpaceCenter(), bot->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } + } + + if ( m_vocalizeTimer.IsElapsed() ) + { + m_vocalizeTimer.Start( RandomFloat( tf_raid_squad_vocalize_min_interval.GetFloat(), tf_raid_squad_vocalize_max_interval.GetFloat() ) ); + + if ( me->IsPlayerClass( TF_CLASS_SCOUT ) ) + me->EmitSound( "Scout.MobJabber" ); + else if ( me->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ) + me->EmitSound( "Heavy.MobJabber" ); + else + me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotSquadAttack::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction ) +{ + m_path.Invalidate(); + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotSquadAttack::OnStuck( CTFBot *me ) +{ + m_path.Invalidate(); + return TryContinue(); +} + +#endif // TF_RAID_MODE
\ No newline at end of file diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.h b/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.h new file mode 100644 index 0000000..a685c65 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_squad_attack.h @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_squad_attack.h +// Move and attack as a small, cohesive, group +// Michael Booth, October 2009 + +#ifndef TF_BOT_SQUAD_ATTACK_H +#define TF_BOT_SQUAD_ATTACK_H + +#ifdef TF_RAID_MODE + +#include "Path/NextBotPathFollow.h" +#include "Path/NextBotChasePath.h" + + +//----------------------------------------------------------------------------- +class CTFBotSquadAttack : public Action< CTFBot > +{ +public: + 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 ); + + QueryResultType ShouldRetreat( const INextBot *me ) const; + + virtual const char *GetName( void ) const { return "SquadPatrol"; }; + +private: + CountdownTimer m_vocalizeTimer; + PathFollower m_path; + ChasePath m_chasePath; + CHandle< CTFPlayer > m_victim; + CountdownTimer m_victimConsiderTimer; + + CTFBot *GetSquadLeader( CTFBot *me ) const; +}; + +inline QueryResultType CTFBotSquadAttack::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} + +#endif // TF_RAID_MODE + +#endif // TF_BOT_SQUAD_ATTACK_H diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.cpp b/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.cpp new file mode 100644 index 0000000..ffd08f4 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.cpp @@ -0,0 +1,175 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_wander.cpp +// Wanderering/idle enemies for Squad Co-op mode +// Michael Booth, October 2009 + +#include "cbase.h" + +#ifdef TF_RAID_MODE + +#include "team.h" +#include "raid/tf_raid_logic.h" +#include "bot/tf_bot.h" +#include "bot/behavior/scenario/raid/tf_bot_wander.h" +#include "bot/behavior/scenario/raid/tf_bot_mob_rush.h" + + +ConVar tf_raid_wanderer_aggro_range( "tf_raid_wanderer_aggro_range", "500", FCVAR_CHEAT, "If wanderers see a threat closer than this, they attack" ); +ConVar tf_raid_wanderer_notice_friend_death_range( "tf_raid_wanderer_notice_friend_death_range", "1000", FCVAR_CHEAT, "If a friend dies within this radius of a wanderer, it wakes up and attacks the attacker" ); +ConVar tf_raid_wanderer_reaction_factor( "tf_raid_wanderer_reaction_factor", "1", FCVAR_CHEAT ); +ConVar tf_raid_wanderer_vocalize_min_interval( "tf_raid_wanderer_vocalize_min_interval", "20", FCVAR_CHEAT ); +ConVar tf_raid_wanderer_vocalize_max_interval( "tf_raid_wanderer_vocalize_max_interval", "30", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +CTFBotWander::CTFBotWander( void ) +{ +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotWander::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + m_vocalizeTimer.Start( RandomFloat( tf_raid_wanderer_vocalize_min_interval.GetFloat(), tf_raid_wanderer_vocalize_max_interval.GetFloat() ) ); + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotWander::Update( CTFBot *me, float interval ) +{ + // mobs use only their melee weapons + CBaseCombatWeapon *meleeWeapon = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE ); + if ( meleeWeapon ) + { + me->Weapon_Switch( meleeWeapon ); + } + + + CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE ); + + if ( me->HasAttribute( CTFBot::AGGRESSIVE ) ) + { + // I'm a mob rusher - pick a random raider and attack them! + CTFPlayer *victim = TFGameRules()->GetRaidLogic()->SelectRaiderToAttack(); + if ( victim ) + { + return SuspendFor( new CTFBotMobRush( victim ), "Rushing a raider" ); + } + } + else if ( m_visionTimer.IsElapsed() ) + { + // I'm a wanderer - look for very nearby threats + m_visionTimer.Start( RandomFloat( 0.5f, 1.0f ) ); + + // find closest visible raider within aggro range + CTFPlayer *threat = NULL; + float closeThreatRangeSq = tf_raid_wanderer_aggro_range.GetFloat() * tf_raid_wanderer_aggro_range.GetFloat(); + + for( int i=0; i<raidingTeam->GetNumPlayers(); ++i ) + { + CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i); + + if ( !player->IsAlive() ) + continue; + + float rangeSq = me->GetRangeSquaredTo( player ); + if ( rangeSq < closeThreatRangeSq ) + { + if ( me->GetVisionInterface()->IsLineOfSightClearToEntity( player ) ) + { + threat = player; + closeThreatRangeSq = rangeSq; + } + } + } + + if ( threat ) + { + return SuspendFor( new CTFBotMobRush( threat ), "Attacking threat!" ); + } + } + + if ( m_vocalizeTimer.IsElapsed() ) + { + m_vocalizeTimer.Start( RandomFloat( tf_raid_wanderer_vocalize_min_interval.GetFloat(), tf_raid_wanderer_vocalize_max_interval.GetFloat() ) ); + + // mouth off + if ( me->IsPlayerClass( TF_CLASS_SCOUT ) ) + me->EmitSound( "Scout.WanderJabber" ); + else + me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_JEERS ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotWander::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result ) +{ + if ( other && other->IsPlayer() && me->IsEnemy( other ) ) + { + return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)other ), RESULT_IMPORTANT, "Attacking threat who touched me!" ); + } + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotWander::OnInjured( CTFBot *me, const CTakeDamageInfo &info ) +{ + if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && me->IsEnemy( info.GetAttacker() ) ) + { + return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)info.GetAttacker() ), RESULT_IMPORTANT, "Attacking threat who attacked me!" ); + } + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotWander::OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info ) +{ + if ( victim && me->IsFriend( victim ) ) + { + if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && me->IsEnemy( info.GetAttacker() ) ) + { + if ( me->IsRangeLessThan( victim, tf_raid_wanderer_notice_friend_death_range.GetFloat() ) ) + { + if ( me->GetVisionInterface()->IsAbleToSee( victim, IVision::DISREGARD_FOV ) && + me->GetVisionInterface()->IsAbleToSee( info.GetAttacker(), IVision::DISREGARD_FOV ) ) + { + float rangeToAttacker = me->GetRangeTo( info.GetAttacker() ); + float reactionTime; + + if ( rangeToAttacker < tf_raid_wanderer_aggro_range.GetFloat() ) + { + reactionTime = 0.0f; + } + else + { + reactionTime = tf_raid_wanderer_reaction_factor.GetFloat() * ( rangeToAttacker - tf_raid_wanderer_aggro_range.GetFloat() ) / tf_raid_wanderer_aggro_range.GetFloat(); + } + + return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)info.GetAttacker(), reactionTime ), RESULT_IMPORTANT, "Attacking my friend's attacker!" ); + } + } + } + } + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotWander::OnCommandAttack( CTFBot *me, CBaseEntity *victim ) +{ + return TryContinue(); +} + + +#endif // TF_RAID_MODE diff --git a/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.h b/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.h new file mode 100644 index 0000000..0364903 --- /dev/null +++ b/game/server/tf/bot/behavior/scenario/raid/tf_bot_wander.h @@ -0,0 +1,51 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_wander.h +// Wanderering/idle enemies for Squad Co-op mode +// Michael Booth, October 2009 + +#ifndef TF_BOT_WANDER_H +#define TF_BOT_WANDER_H + +#ifdef TF_RAID_MODE + +//----------------------------------------------------------------------------- +class CTFBotWander : public Action< CTFBot > +{ +public: + CTFBotWander( void ); + + virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction ); + virtual ActionResult< CTFBot > Update( CTFBot *me, float interval ); + + virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL ); + virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info ); + virtual EventDesiredResult< CTFBot > OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info ); + + virtual EventDesiredResult< CTFBot > OnCommandAttack( CTFBot *me, CBaseEntity *victim ); + + virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry? + virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat? + + virtual const char *GetName( void ) const { return "Wander"; }; + +private: + CountdownTimer m_visionTimer; + CountdownTimer m_vocalizeTimer; +}; + + +inline QueryResultType CTFBotWander::ShouldHurry( const INextBot *me ) const +{ + return ANSWER_YES; +} + + +inline QueryResultType CTFBotWander::ShouldRetreat( const INextBot *me ) const +{ + return ANSWER_NO; +} + + +#endif TF_RAID_MODE + +#endif // TF_BOT_WANDER_H |