📄 hl1_npc_agrunt.cpp
字号:
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
// events
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "AI_Default.h"
#include "AI_Task.h"
#include "AI_Schedule.h"
#include "AI_Node.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Navigator.h"
#include "AI_Route.h"
#include "AI_Squad.h"
#include "AI_SquadSlot.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "EntityList.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "te.h"
#include "hl1_ai_basenpc.h"
ConVar sk_agrunt_health( "sk_agrunt_health", "0" );
ConVar sk_agrunt_dmg_punch( "sk_agrunt_dmg_punch", "0" );
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define AGRUNT_AE_HORNET1 ( 1 )
#define AGRUNT_AE_HORNET2 ( 2 )
#define AGRUNT_AE_HORNET3 ( 3 )
#define AGRUNT_AE_HORNET4 ( 4 )
#define AGRUNT_AE_HORNET5 ( 5 )
// some events are set up in the QC file that aren't recognized by the code yet.
#define AGRUNT_AE_PUNCH ( 6 )
#define AGRUNT_AE_BITE ( 7 )
#define AGRUNT_AE_LEFT_FOOT ( 10 )
#define AGRUNT_AE_RIGHT_FOOT ( 11 )
#define AGRUNT_AE_LEFT_PUNCH ( 12 )
#define AGRUNT_AE_RIGHT_PUNCH ( 13 )
#define AGRUNT_MELEE_DIST 100
int iAgruntMuzzleFlash;
int ACT_THREAT_DISPLAY;
// -----------------------------------------------
// > Squad slots
// -----------------------------------------------
enum AGruntSquadSlot_T
{
AGRUNT_SQUAD_SLOT_HORNET1 = LAST_SHARED_SQUADSLOT,
AGRUNT_SQUAD_SLOT_HORNET2,
AGRUNT_SQUAD_SLOT_CHASE,
};
enum
{
SCHED_AGRUNT_FAIL = LAST_SHARED_SCHEDULE,
SCHED_AGRUNT_COMBAT_FAIL,
SCHED_AGRUNT_STANDOFF,
SCHED_AGRUNT_SUPPRESS_HORNET,
SCHED_AGRUNT_RANGE_ATTACK,
SCHED_AGRUNT_HIDDEN_RANGE_ATTACK,
SCHED_AGRUNT_TAKE_COVER_FROM_ENEMY,
SCHED_AGRUNT_VICTORY_DANCE,
SCHED_AGRUNT_THREAT_DISPLAY,
};
//=========================================================
// monster-specific tasks
//=========================================================
enum
{
TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_SHARED_TASK,
TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE,
};
class CNPC_AlienGrunt : public CHL1BaseNPC
{
DECLARE_CLASS( CNPC_AlienGrunt, CHL1BaseNPC );
public:
void Spawn( void );
void Precache( void );
static const char *pAttackHitSounds[];
static const char *pAttackMissSounds[];
static const char *pAttackSounds[];
static const char *pDieSounds[];
static const char *pPainSounds[];
static const char *pIdleSounds[];
static const char *pAlertSounds[];
float MaxYawSpeed( void );
Class_T Classify ( void ){ return CLASS_ALIEN_MILITARY; }
int GetSoundInterests ( void );
void HandleAnimEvent( animevent_t *pEvent );
void AlertSound( void );
void DeathSound ( void );
void PainSound ( void );
void AttackSound ( void );
bool ShouldSpeak( void );
void PrescheduleThink ( void );
bool FCanCheckAttacks ( void );
int MeleeAttack1Conditions ( float flDot, float flDist );
int RangeAttack1Conditions ( float flDot, float flDist );
void StopTalking ( void );
void StartTask( const Task_t *pTask );
int TranslateSchedule( int scheduleType ); //GetScheduleOfType
int SelectSchedule( void ); // GetSchedule
void SetObjectCollisionBox( void )
{
SetAbsMins( GetAbsOrigin() + Vector( -32, -32, 0 ) );
SetAbsMaxs( GetAbsOrigin() + Vector( 32, 32, 85 ) );
}
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
int IRelationPriority( CBaseEntity *pTarget );
/*
int IRelationship( CBaseEntity *pTarget );
*/
public:
DECLARE_DATADESC();
DEFINE_CUSTOM_AI;
bool m_fCanHornetAttack;
float m_flNextHornetAttackCheck;
float m_flNextPainTime;
// three hacky fields for speech stuff. These don't really need to be saved.
float m_flNextSpeakTime;
float m_flNextWordTime;
int m_iLastWord;
float m_flDamageTime;
};
LINK_ENTITY_TO_CLASS( monster_alien_grunt, CNPC_AlienGrunt );
BEGIN_DATADESC( CNPC_AlienGrunt )
DEFINE_FIELD( CNPC_AlienGrunt, m_fCanHornetAttack, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_AlienGrunt, m_flNextHornetAttackCheck, FIELD_TIME ),
DEFINE_FIELD( CNPC_AlienGrunt, m_flNextPainTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_AlienGrunt, m_flNextSpeakTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_AlienGrunt, m_flNextWordTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_AlienGrunt, m_iLastWord, FIELD_INTEGER ),
END_DATADESC()
const char *CNPC_AlienGrunt::pAttackHitSounds[] =
{
"zombie/claw_strike1.wav",
"zombie/claw_strike2.wav",
"zombie/claw_strike3.wav",
};
const char *CNPC_AlienGrunt::pAttackMissSounds[] =
{
"zombie/claw_miss1.wav",
"zombie/claw_miss2.wav",
};
const char *CNPC_AlienGrunt::pAttackSounds[] =
{
"agrunt/ag_attack1.wav",
"agrunt/ag_attack2.wav",
"agrunt/ag_attack3.wav",
};
const char *CNPC_AlienGrunt::pDieSounds[] =
{
"agrunt/ag_die1.wav",
"agrunt/ag_die4.wav",
"agrunt/ag_die5.wav",
};
const char *CNPC_AlienGrunt::pPainSounds[] =
{
"agrunt/ag_pain1.wav",
"agrunt/ag_pain2.wav",
"agrunt/ag_pain3.wav",
"agrunt/ag_pain4.wav",
"agrunt/ag_pain5.wav",
};
const char *CNPC_AlienGrunt::pIdleSounds[] =
{
"agrunt/ag_idle1.wav",
"agrunt/ag_idle2.wav",
"agrunt/ag_idle3.wav",
"agrunt/ag_idle4.wav",
};
const char *CNPC_AlienGrunt::pAlertSounds[] =
{
"agrunt/ag_alert1.wav",
"agrunt/ag_alert3.wav",
"agrunt/ag_alert4.wav",
"agrunt/ag_alert5.wav",
};
int CNPC_AlienGrunt::IRelationPriority( CBaseEntity *pTarget )
{
//I hate grunts more than anything.
if ( pTarget->Classify() == CLASS_HUMAN_MILITARY )
{
if ( FClassnameIs( pTarget, "monster_human_grunt" ) )
{
return ( BaseClass::IRelationPriority ( pTarget ) + 1 );
}
}
return BaseClass::IRelationPriority( pTarget );
}
void CNPC_AlienGrunt::Spawn()
{
Precache();
SetModel( "models/agrunt.mdl");
UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
m_bloodColor = BLOOD_COLOR_GREEN;
m_fEffects = 0;
m_iHealth = sk_agrunt_health.GetFloat();
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_NPCState = NPC_STATE_NONE;
CapabilitiesClear();
CapabilitiesAdd ( bits_CAP_SQUAD | bits_CAP_MOVE_GROUND );
CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK1 );
// Innate range attack for kicking
CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 );
m_HackedGunPos = Vector( 24, 64, 48 );
m_flNextSpeakTime = m_flNextWordTime = gpGlobals->curtime + 10 + random->RandomInt( 0, 10 );
SetHullType(HULL_WIDE_HUMAN);
SetHullSizeNormal();
SetRenderColor( 255, 255, 255, 255 );
NPCInit();
BaseClass::Spawn();
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_AlienGrunt::Precache()
{
int i;
engine->PrecacheModel("models/agrunt.mdl");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
enginesound->PrecacheSound((char *)pAttackHitSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
enginesound->PrecacheSound((char *)pAttackMissSounds[i]);
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
enginesound->PrecacheSound((char *)pIdleSounds[i]);
for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ )
enginesound->PrecacheSound((char *)pDieSounds[i]);
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
enginesound->PrecacheSound((char *)pPainSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
enginesound->PrecacheSound((char *)pAttackSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
enginesound->PrecacheSound((char *)pAlertSounds[i]);
enginesound->PrecacheSound( "player/pl_ladder1.wav" );
enginesound->PrecacheSound( "player/pl_ladder2.wav" );
enginesound->PrecacheSound( "player/pl_ladder3.wav" );
enginesound->PrecacheSound( "player/pl_ladder4.wav" );
enginesound->PrecacheSound( "hassault/hw_shoot1.wav" );
iAgruntMuzzleFlash = engine->PrecacheModel( "sprites/muz4.vmt" );
UTIL_PrecacheOther( "hornet" );
}
float CNPC_AlienGrunt::MaxYawSpeed( void )
{
float ys;
switch ( GetActivity() )
{
case ACT_TURN_LEFT:
case ACT_TURN_RIGHT:
ys = 110;
break;
default:
ys = 100;
}
return ys;
}
int CNPC_AlienGrunt::GetSoundInterests ( void )
{
return SOUND_WORLD |
SOUND_COMBAT |
SOUND_PLAYER |
SOUND_DANGER;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_AlienGrunt::HandleAnimEvent( animevent_t *pEvent )
{
switch( pEvent->event )
{
case AGRUNT_AE_HORNET1:
case AGRUNT_AE_HORNET2:
case AGRUNT_AE_HORNET3:
case AGRUNT_AE_HORNET4:
case AGRUNT_AE_HORNET5:
{
// m_vecEnemyLKP should be center of enemy body
Vector vecArmPos;
QAngle angArmDir;
Vector vecDirToEnemy;
QAngle angDir;
if (HasCondition( COND_SEE_ENEMY))
{
Vector vecEnemyLKP = GetEnemy()->GetAbsOrigin();
vecDirToEnemy = ( ( vecEnemyLKP ) - GetAbsOrigin() );
VectorAngles( vecDirToEnemy, angDir );
VectorNormalize( vecDirToEnemy );
}
else
{
angDir = GetAbsAngles();
angDir.x = -angDir.x;
Vector vForward;
AngleVectors( angDir, &vForward );
vecDirToEnemy = vForward;
}
m_fEffects = EF_MUZZLEFLASH;
// make angles +-180
if (angDir.x > 180)
{
angDir.x = angDir.x - 360;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -