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
1039 lines
28 KiB
//========= 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" |
|
); |