Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1039 lines
28 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The warrior bug
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "AI_Task.h"
#include "AI_Default.h"
#include "AI_Schedule.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Navigator.h"
#include "AI_Memory.h"
#include "AI_Squad.h"
#include "activitylist.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "tf_player.h"
#include "EntityList.h"
#include "ndebugoverlay.h"
#include "shake.h"
#include "monstermaker.h"
#include "decals.h"
#include "vstdlib/random.h"
#include "tf_obj.h"
#include "engine/IEngineSound.h"
#include "IEffects.h"
#include "npc_bug_warrior.h"
#include "npc_bug_hole.h"
ConVar npc_bug_warrior_health( "npc_bug_warrior_health", "150" );
ConVar npc_bug_warrior_swipe_damage( "npc_bug_warrior_swipe_damage", "20" );
BEGIN_DATADESC( CNPC_Bug_Warrior )
DEFINE_FIELD( m_flIdleDelay, FIELD_FLOAT ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_bug_warrior, CNPC_Bug_Warrior );
IMPLEMENT_CUSTOM_AI( npc_bug_warrior, CNPC_Bug_Warrior );
// Bug interactions
int g_interactionBugSquadAttacking = 0;
//==================================================
// Bug Conditions
//==================================================
enum BugConditions
{
COND_WBUG_STOP_FLEEING = LAST_SHARED_CONDITION,
COND_WBUG_RETURN_TO_BUGHOLE,
COND_WBUG_ASSIST_FELLOW_BUG,
};
//==================================================
// Bug Schedules
//==================================================
enum BugSchedules
{
SCHED_WBUG_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
SCHED_WBUG_FLEE_ENEMY,
SCHED_WBUG_CHASE_ENEMY_FAILED,
SCHED_WBUG_PATROL,
SCHED_WBUG_RETURN_TO_BUGHOLE,
SCHED_WBUG_RETURN_TO_BUGHOLE_AND_REMOVE,
SCHED_WBUG_ASSIST_FELLOW_BUG,
};
//==================================================
// Bug Tasks
//==================================================
enum BugTasks
{
TASK_WBUG_GET_PATH_TO_FLEE = LAST_SHARED_TASK,
TASK_WBUG_GET_PATH_TO_PATROL,
TASK_WBUG_GET_PATH_TO_BUGHOLE,
TASK_WBUG_HOLE_REMOVE,
TASK_WBUG_GET_PATH_TO_ASSIST,
};
//==================================================
// Bug Activities
//==================================================
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPC_Bug_Warrior::CNPC_Bug_Warrior( void )
{
m_flFieldOfView = 0.5f;
m_flIdleDelay = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Setup our schedules and tasks, etc.
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::InitCustomSchedules( void )
{
INIT_CUSTOM_AI( CNPC_Bug_Warrior );
// Register our interactions
ADD_CUSTOM_INTERACTION( g_interactionBugSquadAttacking );
// Schedules
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_CHASE_ENEMY );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_FLEE_ENEMY );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_CHASE_ENEMY_FAILED );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_PATROL );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_RETURN_TO_BUGHOLE );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_RETURN_TO_BUGHOLE_AND_REMOVE );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_ASSIST_FELLOW_BUG );
// Conditions
ADD_CUSTOM_CONDITION( CNPC_Bug_Warrior, COND_WBUG_STOP_FLEEING );
ADD_CUSTOM_CONDITION( CNPC_Bug_Warrior, COND_WBUG_RETURN_TO_BUGHOLE );
ADD_CUSTOM_CONDITION( CNPC_Bug_Warrior, COND_WBUG_ASSIST_FELLOW_BUG );
// Tasks
ADD_CUSTOM_TASK( CNPC_Bug_Warrior, TASK_WBUG_GET_PATH_TO_FLEE );
ADD_CUSTOM_TASK( CNPC_Bug_Warrior, TASK_WBUG_GET_PATH_TO_PATROL );
ADD_CUSTOM_TASK( CNPC_Bug_Warrior, TASK_WBUG_GET_PATH_TO_BUGHOLE );
ADD_CUSTOM_TASK( CNPC_Bug_Warrior, TASK_WBUG_HOLE_REMOVE );
ADD_CUSTOM_TASK( CNPC_Bug_Warrior, TASK_WBUG_GET_PATH_TO_ASSIST );
// Activities
//ADD_CUSTOM_ACTIVITY( CNPC_Bug_Warrior, ACT_BUG_WARRIOR_DISTRACT );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_CHASE_ENEMY );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_FLEE_ENEMY );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_CHASE_ENEMY_FAILED );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_PATROL );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_RETURN_TO_BUGHOLE );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_RETURN_TO_BUGHOLE_AND_REMOVE );
AI_LOAD_SCHEDULE( CNPC_Bug_Warrior, SCHED_WBUG_ASSIST_FELLOW_BUG );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::Spawn( void )
{
Precache();
SetModel( BUG_WARRIOR_MODEL );
SetHullType(HULL_WIDE_HUMAN);//HULL_WIDE_SHORT;
SetHullSizeNormal();
SetDefaultEyeOffset();
SetViewOffset( (WorldAlignMins() + WorldAlignMaxs()) * 0.5 ); // See from my center
SetDistLook( 1024.0 );
SetNavType(NAV_GROUND);
m_NPCState = NPC_STATE_NONE;
SetBloodColor( BLOOD_COLOR_YELLOW );
m_iHealth = npc_bug_warrior_health.GetFloat();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
m_iszPatrolPathName = NULL_STRING;
// Only do this if a squadname appears in the entity
if ( m_SquadName != NULL_STRING )
{
CapabilitiesAdd( bits_CAP_SQUAD );
}
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 );
NPCInit();
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::Precache( void )
{
PrecacheModel( BUG_WARRIOR_MODEL );
PrecacheScriptSound( "NPC_Bug_Warrior.AttackHit" );
PrecacheScriptSound( "NPC_Bug_Warrior.AttackSingle" );
PrecacheScriptSound( "NPC_Bug_Warrior.Idle" );
PrecacheScriptSound( "NPC_Bug_Warrior.Pain" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Bug_Warrior::SelectSchedule( void )
{
ClearCondition( COND_WBUG_STOP_FLEEING );
// Turn towards sounds
if ( HasCondition(COND_HEAR_DANGER) || HasCondition(COND_HEAR_COMBAT) )
{
CSound *pSound = GetBestSound();
if ( pSound)
{
if ( !HasCondition( COND_SEE_ENEMY ) && ( pSound->m_iType & (SOUND_PLAYER | SOUND_COMBAT) ) )
{
GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() );
}
}
}
switch ( m_NPCState )
{
case NPC_STATE_IDLE:
{
// If I have an enemy, but I don't have any nearby friends, flee
if ( HasCondition( COND_SEE_ENEMY ) && ShouldFlee() )
{
SetState( NPC_STATE_ALERT );
return SCHED_WBUG_FLEE_ENEMY;
}
// Fellow bug might be requesting assistance
if ( HasCondition( COND_WBUG_ASSIST_FELLOW_BUG ) )
return SCHED_WBUG_ASSIST_FELLOW_BUG;
// BugHole might be requesting help
if ( HasCondition( COND_WBUG_RETURN_TO_BUGHOLE ) )
return SCHED_WBUG_RETURN_TO_BUGHOLE;
// Return to my bughole
return SCHED_WBUG_RETURN_TO_BUGHOLE_AND_REMOVE;
break;
}
case NPC_STATE_ALERT:
{
// If I have an enemy, but I don't have any nearby friends, flee
if ( HasCondition( COND_SEE_ENEMY ) && ShouldFlee() )
{
SetState( NPC_STATE_ALERT );
return SCHED_WBUG_FLEE_ENEMY;
}
// Fellow bug might be requesting assistance
if ( HasCondition( COND_WBUG_ASSIST_FELLOW_BUG ) )
return SCHED_WBUG_ASSIST_FELLOW_BUG;
// BugHole might be requesting help
if ( HasCondition( COND_WBUG_RETURN_TO_BUGHOLE ) )
return SCHED_WBUG_RETURN_TO_BUGHOLE;
// If I have a patrol path, walk it
if ( m_iszPatrolPathName != NULL_STRING )
return SCHED_WBUG_PATROL;
break;
}
case NPC_STATE_COMBAT:
{
// Did I lose my enemy?
if ( HasCondition ( COND_LOST_ENEMY ) || HasCondition ( COND_ENEMY_UNREACHABLE ) )
{
SetEnemy( NULL );
m_bFightingToDeath = false;
SetState(NPC_STATE_IDLE);
return BaseClass::SelectSchedule();
}
// If I have an enemy, but I don't have any nearby friends, flee
if ( HasCondition( COND_SEE_ENEMY ) && ShouldFlee() )
return SCHED_WBUG_FLEE_ENEMY;
// If we're able to melee, do so
if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
return BaseClass::SelectSchedule();
}
break;
}
return BaseClass::SelectSchedule();
}
//-----------------------------------------------------------------------------
// Purpose: override/translate a schedule by type
// Input : Type - schedule type
// Output : int - translated type
//-----------------------------------------------------------------------------
int CNPC_Bug_Warrior::TranslateSchedule( int scheduleType )
{
if ( scheduleType == SCHED_CHASE_ENEMY )
{
// Tell my squad that I'm attacking this guy
if ( m_pSquad )
{
m_pSquad->BroadcastInteraction( g_interactionBugSquadAttacking, (void *)GetEnemy(), this );
}
return SCHED_WBUG_CHASE_ENEMY;
}
return BaseClass::TranslateSchedule(scheduleType);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::StartTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_WBUG_GET_PATH_TO_FLEE:
{
// Always tell our bughole that we're under attack
if ( m_hMyBugHole )
{
m_hMyBugHole->IncomingFleeingBug( this );
}
// We're fleeing from an enemy.
// If I have a squadmate, run to him.
CAI_BaseNPC *pSquadMate;
if ( m_pSquad && (pSquadMate = m_pSquad->NearestSquadMember(this)) != false )
{
SetTarget( pSquadMate );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
// If we have no squad, or we couldn't get a path to our squadmate, look for a bughole
if ( m_hMyBugHole )
{
SetTarget( m_hMyBugHole );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
// Give up, and fight to the death
m_bFightingToDeath = true;
TaskComplete();
}
break;
case TASK_WBUG_GET_PATH_TO_PATROL:
{
// Get a path to the next point in the patrol.
// Abort if we have no patrol path
if ( !m_iszPatrolPathName )
{
TaskFail( "Attempting to patrol without patrol path." );
return;
}
SetHintNode( CAI_HintManager::FindHint( this, HINT_BUG_PATROL_POINT, bits_HINT_NODE_RANDOM, 4096 ) );
if( !GetHintNode() )
{
TaskFail("Couldn't find patrol node");
return;
}
Vector vHintPos;
GetHintNode()->GetPosition( this, &vHintPos );
AI_NavGoal_t goal( vHintPos, ACT_RUN );
GetNavigator()->SetGoal( goal );
TaskComplete();
}
break;
case TASK_WBUG_GET_PATH_TO_BUGHOLE:
{
// Get a path back to my bughole
// If we have no squad, or we couldn't get a path to our squadmate, look for a bughole
if ( m_hMyBugHole )
{
SetTarget( m_hMyBugHole );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
TaskFail( "Couldn't get to bughole." );
}
break;
case TASK_WBUG_HOLE_REMOVE:
{
// Crawl inside the bughole and remove myself
AddEffects( EF_NODRAW );
AddSolidFlags( FSOLID_NOT_SOLID );
Event_Killed( CTakeDamageInfo( this, this, 200, DMG_CRUSH ) );
// Tell the bughole
if ( m_hMyBugHole )
{
m_hMyBugHole->BugReturned();
}
TaskComplete();
}
break;
case TASK_WBUG_GET_PATH_TO_ASSIST:
{
if ( m_hAssistTarget )
{
SetTarget( m_hAssistTarget );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
TaskFail( "Couldn't get to assist target." );
}
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::RunTask( const Task_t *pTask )
{
BaseClass::RunTask( pTask );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::FValidateHintType(CAI_Hint *pHint)
{
if ( pHint->HintType() == HINT_BUG_PATROL_POINT )
{
// Make sure the name matches the patrol path I'm on
if ( pHint->GetEntityName() == m_iszPatrolPathName )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handle specific interactions with other NPCs
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender )
{
// Check for a target found while burrowed
if ( interactionType == g_interactionBugSquadAttacking )
{
// Ignore ones sent by me
if ( sender == this )
return false;
// Interrupt me if I'm fleeing
if ( IsCurSchedule(SCHED_WBUG_FLEE_ENEMY) ||
IsCurSchedule(SCHED_WBUG_CHASE_ENEMY_FAILED) )
{
SetCondition( COND_WBUG_STOP_FLEEING );
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pVictim -
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::Event_Killed( const CTakeDamageInfo &info )
{
BaseClass::Event_Killed( info );
// Remove myself in a minute
if ( !ShouldFadeOnDeath() )
{
SetThink( SUB_Remove );
SetNextThink( gpGlobals->curtime + 20 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::MeleeAttack( float distance, float damage, QAngle& viewPunch, Vector& shove )
{
if ( GetEnemy() == NULL )
return;
// Trace directly at my target
Vector vStart = GetAbsOrigin();
vStart.z += WorldAlignSize().z * 0.5;
Vector vecForward = (GetEnemy()->EyePosition() - vStart);
VectorNormalize( vecForward );
Vector vEnd = vStart + (vecForward * distance );
// Use half the size of my target for the box
Vector vecHalfTraceBox = (GetEnemy()->WorldAlignMaxs() - GetEnemy()->WorldAlignMins()) * 0.25;
//NDebugOverlay::Box( vStart, -Vector(10,10,10), Vector(10,10,10), 0,255,0,20,1.0);
//NDebugOverlay::Box( GetEnemy()->EyePosition(), -Vector(10,10,10), Vector(10,10,10), 255,255,255,20,1.0);
CBaseEntity *pHurt = CheckTraceHullAttack( vStart, vEnd, -vecHalfTraceBox, vecHalfTraceBox, damage, DMG_SLASH );
if ( pHurt )
{
CBasePlayer *pPlayer = ToBasePlayer( pHurt );
if ( pPlayer )
{
//Kick the player angles
pPlayer->ViewPunch( viewPunch );
Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
VectorNormalize(dir);
QAngle angles;
VectorAngles( dir, angles );
Vector forward, right;
AngleVectors( angles, &forward, &right, NULL );
// Push the target back
Vector vecImpulse;
VectorMultiply( right, -shove[1], vecImpulse );
VectorMA( vecImpulse, -shove[0], forward, vecImpulse );
pHurt->ApplyAbsVelocityImpulse( vecImpulse );
}
// Play a random attack hit sound
EmitSound( "NPC_Bug_Warrior.AttackHit" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEvent -
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::HandleAnimEvent( animevent_t *pEvent )
{
switch ( pEvent->event )
{
case BUG_WARRIOR_AE_MELEE_HIT1:
MeleeAttack( BUG_WARRIOR_MELEE1_RANGE, npc_bug_warrior_swipe_damage.GetFloat(), QAngle( 20.0f, 0.0f, 0.0f ), Vector( -350.0f, 1.0f, 1.0f ) );
return;
break;
case BUG_WARRIOR_AE_MELEE_SOUND1:
{
EmitSound( "NPC_Bug_Warrior.AttackSingle" );
return;
}
break;
}
BaseClass::HandleAnimEvent( pEvent );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pInflictor -
// *pAttacker -
// flDamage -
// bitsDamageType -
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Bug_Warrior::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
Vector faceDir;
return BaseClass::OnTakeDamage_Alive( info );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::IdleSound( void )
{
EmitSound( "NPC_Bug_Warrior.Idle" );
m_flIdleDelay = gpGlobals->curtime + 4.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::PainSound( const CTakeDamageInfo &info )
{
EmitSound( "NPC_Bug_Warrior.Pain" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecTarget -
// Output : float
//-----------------------------------------------------------------------------
float CNPC_Bug_Warrior::CalcIdealYaw( const Vector &vecTarget )
{
//If we can see our enemy but not reach them, face them always
if ( ( GetEnemy() != NULL ) && ( HasCondition( COND_SEE_ENEMY ) && HasCondition( COND_ENEMY_UNREACHABLE ) ) )
{
return UTIL_VecToYaw ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
}
return BaseClass::CalcIdealYaw( vecTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : float
//-----------------------------------------------------------------------------
float CNPC_Bug_Warrior::MaxYawSpeed( void )
{
switch ( GetActivity() )
{
case ACT_IDLE:
return 15.0f;
break;
case ACT_WALK:
return 15.0f;
break;
default:
case ACT_RUN:
return 30.0f;
break;
}
return 30.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::ShouldPlayIdleSound( void )
{
//Only do idles in the right states
if ( ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT ) )
return false;
//Gagged monsters don't talk
if ( HasSpawnFlags( SF_NPC_GAG ) )
return false;
//Don't cut off another sound or play again too soon
if ( m_flIdleDelay > gpGlobals->curtime )
return false;
//Randomize it a bit
if ( random->RandomInt( 0, 20 ) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flDot -
// flDist -
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Bug_Warrior::MeleeAttack1Conditions( float flDot, float flDist )
{
if ( ( gpGlobals->curtime - m_flLastAttackTime ) < 1.0f )
return 0;
#if 0
float flPrDist, flPrDot;
Vector vecPrPos;
Vector2D vec2DPrDir;
//Get our likely position in one second
UTIL_GetPredictedPosition( GetEnemy(), 0.5f, &vecPrPos );
flPrDist = ( vecPrPos - GetAbsOrigin() ).Length();
vec2DPrDir = ( vecPrPos - GetAbsOrigin() ).AsVector2D();
Vector vecBodyDir;
BodyDirection2D( &vecBodyDir );
Vector2D vec2DBodyDir = vecBodyDir.AsVector2D();
flPrDot = DotProduct2D (vec2DPrDir, vec2DBodyDir );
if ( flPrDist > BUG_WARRIOR_MELEE1_RANGE )
return COND_TOO_FAR_TO_ATTACK;
if ( flPrDot < 0.5f )
return COND_NOT_FACING_ATTACK;
#else
if ( flDist > BUG_WARRIOR_MELEE1_RANGE )
return COND_TOO_FAR_TO_ATTACK;
if ( flDot < 0.5f )
return COND_NOT_FACING_ATTACK;
#endif
return COND_CAN_MELEE_ATTACK1;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this bug should flee
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::ShouldFlee( void )
{
// I don't flee if I'm fighting to the death
if ( m_bFightingToDeath )
return false;
// I don't flee if I'm not alone
if ( !IsAlone() )
return false;
// I don't flee if I'm within sight of a bughole
if ( m_hMyBugHole.Get() && FVisible( m_hMyBugHole ) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::IsAlone( void )
{
// If I'm not in a squad, make me just fight
if ( !m_pSquad )
return false;
// If there aren't any visible squad members, I'm alone
if ( m_pSquad->GetVisibleSquadMembers( this ) == 0 )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::SetBugHole( CMaker_BugHole *pBugHole )
{
m_hMyBugHole = pBugHole;
}
//-----------------------------------------------------------------------------
// Purpose: BugHole is calling me home to defend it
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::ReturnToBugHole( void )
{
SetCondition( COND_WBUG_RETURN_TO_BUGHOLE );
}
//-----------------------------------------------------------------------------
// Purpose: Assist a bug that's under attack and making it's way back to the bughole
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::Assist( CAI_BaseNPC *pBug )
{
// If I'm not idle, I can't assist
if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_NONE )
return;
m_hAssistTarget = pBug;
SetCondition( COND_WBUG_ASSIST_FELLOW_BUG );
SetCondition( COND_PROVOKED );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::StartPatrolling( string_t iszPatrolPathName )
{
// If I'm not idle, I can't patrol
if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_NONE )
return false;
// If I'm patrolling already, I can't patrol
if ( IsPatrolling() )
return false;
SetState( NPC_STATE_ALERT );
SetCondition( COND_PROVOKED );
// Store off my patrol name
m_iszPatrolPathName = iszPatrolPathName;
m_iPatrolPoint = 0;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this bug is currently patrolling
//-----------------------------------------------------------------------------
bool CNPC_Bug_Warrior::IsPatrolling( void )
{
return ( IsCurSchedule(SCHED_WBUG_PATROL) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Warrior::AlertSound( void )
{
if ( GetEnemy() )
{
//FIXME: We need a better solution for inner-squad alerts!!
//SOUND_DANGER is designed to frighten NPC's away. Need a different SOUND_ type.
CSoundEnt::InsertSound( SOUND_DANGER, GetEnemy()->GetAbsOrigin(), 1024, 0.5f, this );
}
}
//-----------------------------------------------------------------------------
// Purpose: Overridden for team handling
//-----------------------------------------------------------------------------
Disposition_t CNPC_Bug_Warrior::IRelationType( CBaseEntity *pTarget )
{
if ( pTarget->GetFlags() & FL_NOTARGET )
return D_NU;
if ( pTarget->Classify() == CLASS_PLAYER )
{
// Ignore stealthed enemies
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pTarget;
if ( pPlayer->IsCamouflaged() )
return D_NU;
return D_HT;
}
// Attack objects
if ( pTarget->Classify() == CLASS_MILITARY )
return D_HT;
return D_NU;
}
//------------------------------------------------------------------------------
// Purpose : Hate sentryguns more than other types of objects
//------------------------------------------------------------------------------
int CNPC_Bug_Warrior::IRelationPriority( CBaseEntity *pTarget )
{
if ( pTarget->Classify() == CLASS_MILITARY )
{
CBaseObject* pBaseObject = dynamic_cast<CBaseObject*>(pTarget);
if ( pBaseObject && pBaseObject->IsSentrygun() )
return ( BaseClass::IRelationPriority ( pTarget ) + 1 );
}
return BaseClass::IRelationPriority ( pTarget );
}
//------------------------------------------------------------------------------
//
// Schedules
//
//------------------------------------------------------------------------------
//=========================================================
// Chase Enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_CHASE_ENEMY,
" Tasks"
//" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_WBUG_CHASE_ENEMY_FAILED"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
" TASK_SET_TOLERANCE_DISTANCE 24"
" TASK_GET_PATH_TO_ENEMY 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_ENEMY_UNREACHABLE"
" COND_CAN_MELEE_ATTACK1"
" COND_CAN_MELEE_ATTACK2"
" COND_GIVE_WAY"
" COND_LOST_ENEMY"
);
//=========================================================
// Failed to chase my enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_CHASE_ENEMY_FAILED,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_WAIT_FACE_ENEMY 0.5"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
" COND_CAN_RANGE_ATTACK1"
" COND_CAN_MELEE_ATTACK1"
" COND_CAN_RANGE_ATTACK2"
" COND_CAN_MELEE_ATTACK2"
" COND_WBUG_STOP_FLEEING"
);
//=========================================================
// Flee from our enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_FLEE_ENEMY,
" Tasks"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_WBUG_CHASE_ENEMY"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_WBUG_GET_PATH_TO_FLEE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_TURN_RIGHT 180"
""
" Interrupts"
" COND_ENEMY_DEAD"
" COND_WBUG_STOP_FLEEING"
" COND_LOST_ENEMY"
);
//=========================================================
// Walk a set of patrol points
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_PATROL,
" Tasks"
//" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_WBUG_CHASE_ENEMY"
" TASK_SET_TOLERANCE_DISTANCE 256"
" TASK_WBUG_GET_PATH_TO_PATROL 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Use look around
" TASK_WAIT 2"
" TASK_WAIT_RANDOM 4"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
" COND_WBUG_RETURN_TO_BUGHOLE"
);
//=========================================================
// Return to defend a bughole
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_RETURN_TO_BUGHOLE,
" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_WBUG_GET_PATH_TO_BUGHOLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
);
//=========================================================
// Return to a bughole and remove myself
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_RETURN_TO_BUGHOLE_AND_REMOVE,
" Tasks"
" TASK_WAIT 5" // Wait for 5-10 seconds to see if anything happens
" TASK_WAIT_RANDOM 5"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_WBUG_GET_PATH_TO_BUGHOLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_WBUG_HOLE_REMOVE 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
);
//=========================================================
// Move to assist a fellow bug
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_WBUG_ASSIST_FELLOW_BUG,
" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_WBUG_GET_PATH_TO_ASSIST 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_CAN_MELEE_ATTACK1"
" COND_CAN_MELEE_ATTACK2"
" COND_GIVE_WAY"
);