📄 hl1_npc_controller.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_Route.h"
#include "AI_Navigator.h"
//#include "hl1_npc_controller.h"
#include "ai_basenpc_flyer.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 "Sprite.h"
#include "AI_MoveProbe.h"
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define CONTROLLER_AE_HEAD_OPEN 1
#define CONTROLLER_AE_BALL_SHOOT 2
#define CONTROLLER_AE_SMALL_SHOOT 3
#define CONTROLLER_AE_POWERUP_FULL 4
#define CONTROLLER_AE_POWERUP_HALF 5
#define CONTROLLER_FLINCH_DELAY 2 // at most one flinch every n secs
#define DIST_TO_CHECK 200
ConVar sk_controller_health ( "sk_controller_health", "60" );
ConVar sk_controller_dmgzap ( "sk_controller_dmgzap", "15" );
ConVar sk_controller_speedball ( "sk_controller_speedball", "650" );
ConVar sk_controller_dmgball ( "sk_controller_dmgball", "3" );
class CSprite;
class CNPC_Controller;
enum
{
TASK_CONTROLLER_CHASE_ENEMY = LAST_SHARED_TASK,
TASK_CONTROLLER_STRAFE,
TASK_CONTROLLER_TAKECOVER,
TASK_CONTROLLER_FAIL,
};
enum
{
SCHED_CONTROLLER_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
SCHED_CONTROLLER_STRAFE,
SCHED_CONTROLLER_TAKECOVER,
SCHED_CONTROLLER_FAIL,
};
class CControllerNavigator : public CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator>
{
typedef CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator> BaseClass;
public:
CControllerNavigator( CNPC_Controller *pOuter )
: BaseClass( pOuter )
{
}
bool ActivityIsLocomotive( Activity activity ) { return true; }
};
class CNPC_Controller : public CAI_BaseFlyingBot
{
public:
DECLARE_CLASS( CNPC_Controller, CAI_BaseFlyingBot );
DEFINE_CUSTOM_AI;
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
float MaxYawSpeed( void ) { return 120.0f; }
Class_T Classify ( void ) { return CLASS_ALIEN_MILITARY; }
void HandleAnimEvent( animevent_t *pEvent );
void RunAI( void );
int RangeAttack1Conditions ( float flDot, float flDist ); // balls
int RangeAttack2Conditions ( float flDot, float flDist ); // head
int MeleeAttack1Conditions ( float flDot, float flDist ) { return COND_NONE; }
int MeleeAttack2Conditions ( float flDot, float flDist ) { return COND_NONE; }
int TranslateSchedule( int scheduleType );
void StartTask ( const Task_t *pTask );
void RunTask ( const Task_t *pTask );
void Stop( void );
bool OverridePathMove( float flInterval );
bool OverrideMove( float flInterval );
void MoveToTarget( float flInterval, const Vector &vecMoveTarget );
void SetActivity ( Activity NewActivity );
bool ShouldAdvanceRoute( float flWaypointDist );
int LookupFloat( );
friend class CControllerNavigator;
CAI_Navigator *CreateNavigator()
{
return new CControllerNavigator( this );
}
bool ShouldGib( const CTakeDamageInfo &info );
bool HasAlienGibs( void ) { return true; }
bool HasHumanGibs( void ) { return false; }
float m_flNextFlinch;
float m_flShootTime;
float m_flShootEnd;
void PainSound( void );
void AlertSound( void );
void IdleSound( void );
void AttackSound( void );
void DeathSound( void );
static const char *pAttackSounds[];
static const char *pIdleSounds[];
static const char *pAlertSounds[];
static const char *pPainSounds[];
static const char *pDeathSounds[];
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
void Event_Killed( const CTakeDamageInfo &info );
CSprite *m_pBall[2]; // hand balls
int m_iBall[2]; // how bright it should be
float m_iBallTime[2]; // when it should be that color
int m_iBallCurrent[2]; // current brightness
Vector m_vecEstVelocity;
Vector m_velocity;
bool m_fInCombat;
void SetSequence( int nSequence );
int IRelationPriority( CBaseEntity *pTarget );
};
class CNPC_ControllerHeadBall : public CAI_BaseNPC
{
public:
DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void EXPORT HuntThink( void );
void EXPORT KillThink( void );
void EXPORT BounceTouch( CBaseEntity *pOther );
void MovetoTarget( Vector vecTarget );
int m_iTrail;
int m_flNextAttack;
float m_flSpawnTime;
Vector m_vecIdeal;
EHANDLE m_hOwner;
CSprite *m_pSprite;
};
class CNPC_ControllerZapBall : public CAI_BaseNPC
{
public:
DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void EXPORT AnimateThink( void );
void EXPORT ExplodeTouch( CBaseEntity *pOther );
void Kill( void );
EHANDLE m_hOwner;
float m_flSpawnTime;
CSprite *m_pSprite;
};
LINK_ENTITY_TO_CLASS( monster_alien_controller, CNPC_Controller );
BEGIN_DATADESC( CNPC_Controller )
DEFINE_ARRAY( CNPC_Controller, m_pBall, FIELD_CLASSPTR, 2 ),
DEFINE_ARRAY( CNPC_Controller, m_iBall, FIELD_INTEGER, 2 ),
DEFINE_ARRAY( CNPC_Controller, m_iBallTime, FIELD_TIME, 2 ),
DEFINE_ARRAY( CNPC_Controller, m_iBallCurrent, FIELD_INTEGER, 2 ),
DEFINE_FIELD( CNPC_Controller, m_vecEstVelocity, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_Controller, m_velocity, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_Controller, m_fInCombat, FIELD_BOOLEAN ),
END_DATADESC()
void CNPC_Controller::Spawn()
{
Precache( );
SetModel( "models/controller.mdl" );
UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
SetGravity(0.001);
m_bloodColor = BLOOD_COLOR_GREEN;
m_iHealth = sk_controller_health.GetFloat();
m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_NPCState = NPC_STATE_NONE;
SetRenderColor( 255, 255, 255, 255 );
CapabilitiesClear();
AddFlag( FL_FLY );
SetNavType( NAV_FLY );
CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_MOVE_SHOOT);
NPCInit();
SetDefaultEyeOffset();
}
const char *CNPC_Controller::pAttackSounds[] =
{
"controller/con_attack1.wav",
"controller/con_attack2.wav",
"controller/con_attack3.wav",
};
const char *CNPC_Controller::pIdleSounds[] =
{
"controller/con_idle1.wav",
"controller/con_idle2.wav",
"controller/con_idle3.wav",
"controller/con_idle4.wav",
"controller/con_idle5.wav",
};
const char *CNPC_Controller::pAlertSounds[] =
{
"controller/con_alert1.wav",
"controller/con_alert2.wav",
"controller/con_alert3.wav",
};
const char *CNPC_Controller::pPainSounds[] =
{
"controller/con_pain1.wav",
"controller/con_pain2.wav",
"controller/con_pain3.wav",
};
const char *CNPC_Controller::pDeathSounds[] =
{
"controller/con_die1.wav",
"controller/con_die2.wav",
};
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Controller::Precache()
{
engine->PrecacheModel("models/controller.mdl");
PRECACHE_SOUND_ARRAY( pAttackSounds );
PRECACHE_SOUND_ARRAY( pIdleSounds );
PRECACHE_SOUND_ARRAY( pAlertSounds );
PRECACHE_SOUND_ARRAY( pPainSounds );
PRECACHE_SOUND_ARRAY( pDeathSounds );
engine->PrecacheModel( "sprites/xspark4.vmt");
UTIL_PrecacheOther( "controller_energy_ball" );
UTIL_PrecacheOther( "controller_head_ball" );
}
//=========================================================
// TakeDamage -
//=========================================================
int CNPC_Controller::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
PainSound();
return BaseClass::OnTakeDamage_Alive( info );
}
bool CNPC_Controller::ShouldGib( const CTakeDamageInfo &info )
{
if ( info.GetDamageType() & DMG_NEVERGIB )
return false;
if ( ( info.GetDamageType() & DMG_GIB_CORPSE && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
return true;
return false;
}
int CNPC_Controller::IRelationPriority( CBaseEntity *pTarget )
{
if ( pTarget->Classify() == CLASS_PLAYER )
{
return BaseClass::IRelationPriority ( pTarget ) + 1;
}
return BaseClass::IRelationPriority( pTarget );
}
void CNPC_Controller::Event_Killed( const CTakeDamageInfo &info )
{
if( ShouldGib(info) )
{
//remove the balls
if (m_pBall[0])
{
UTIL_Remove( m_pBall[0] );
m_pBall[0] = NULL;
}
if (m_pBall[1])
{
UTIL_Remove( m_pBall[1] );
m_pBall[1] = NULL;
}
}
else
{
// fade balls
if (m_pBall[0])
{
m_pBall[0]->FadeAndDie( 2 );
m_pBall[0] = NULL;
}
if (m_pBall[1])
{
m_pBall[1]->FadeAndDie( 2 );
m_pBall[1] = NULL;
}
}
BaseClass::Event_Killed( info );
}
void CNPC_Controller::PainSound( void )
{
if (random->RandomInt(0,5) < 2)
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, RANDOM_SOUND_ARRAY( pPainSounds ), 1, ATTN_NORM );
}
}
void CNPC_Controller::AlertSound( void )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, RANDOM_SOUND_ARRAY( pAlertSounds ), 1, ATTN_NORM );
}
void CNPC_Controller::IdleSound( void )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, RANDOM_SOUND_ARRAY( pIdleSounds ), 1, ATTN_NORM );
}
void CNPC_Controller::AttackSound( void )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, RANDOM_SOUND_ARRAY( pAttackSounds ), 1, ATTN_NORM );
}
void CNPC_Controller::DeathSound( void )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, RANDOM_SOUND_ARRAY( pDeathSounds ), 1, ATTN_NORM );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Controller::HandleAnimEvent( animevent_t *pEvent )
{
switch( pEvent->event )
{
case CONTROLLER_AE_HEAD_OPEN:
{
Vector vecStart;
QAngle angleGun;
GetAttachment( 0, vecStart, angleGun );
// BUGBUG - attach to attachment point!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -