mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 04:31:05 +00:00
1
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user