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
![]()
4 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"
|
||
|
);
|