📄 hl1_npc_turret.cpp
字号:
#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_Senses.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"
#define TURRET_SHOTS 2
#define TURRET_RANGE (100 * 12)
#define TURRET_SPREAD Vector( 0, 0, 0 )
#define TURRET_TURNRATE 30 //angles per 0.1 second
#define TURRET_MAXWAIT 15 // seconds turret will stay active w/o a target
#define TURRET_MAXSPIN 5 // seconds turret barrel will spin w/o a target
#define TURRET_MACHINE_VOLUME 0.5
typedef enum
{
// TURRET_ANIM_NONE = 0,
TURRET_ANIM_FIRE = 0,
TURRET_ANIM_SPIN,
TURRET_ANIM_DEPLOY,
TURRET_ANIM_RETIRE,
TURRET_ANIM_DIE,
} TURRET_ANIM;
#define SF_MONSTER_TURRET_AUTOACTIVATE 32
#define SF_MONSTER_TURRET_STARTINACTIVE 64
#define TURRET_GLOW_SPRITE "sprites/flare3.vmt"
#define TURRET_ORIENTATION_FLOOR 0
#define TURRET_ORIENTATION_CEILING 1
class CNPC_BaseTurret : public CAI_BaseNPC
{
DECLARE_CLASS( CNPC_BaseTurret, CAI_BaseNPC );
public:
void Spawn(void);
virtual void Precache(void);
void EXPORT TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
virtual int OnTakeDamage( const CTakeDamageInfo &info );
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
Class_T Classify( void );
// Think functions
void EXPORT ActiveThink(void);
void EXPORT SearchThink(void);
void EXPORT AutoSearchThink(void);
void EXPORT TurretDeath(void);
virtual void EXPORT SpinDownCall(void) { m_iSpin = 0; }
virtual void EXPORT SpinUpCall(void) { m_iSpin = 1; }
void EXPORT Deploy(void);
void EXPORT Retire(void);
void EXPORT Initialize(void);
virtual void Ping(void);
virtual void EyeOn(void);
virtual void EyeOff(void);
void InputActivate( inputdata_t &inputdata );
void InputDeactivate( inputdata_t &inputdata );
void Event_Killed( const CTakeDamageInfo &info );
virtual bool ShouldFadeOnDeath( void ) { return false; }
bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
// other functions
void SetTurretAnim(TURRET_ANIM anim);
int MoveTurret(void);
virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { };
float m_flMaxSpin; // Max time to spin the barrel w/o a target
int m_iSpin;
CSprite *m_pEyeGlow;
int m_eyeBrightness;
int m_iDeployHeight;
int m_iRetractHeight;
int m_iMinPitch;
int m_iBaseTurnRate; // angles per second
float m_fTurnRate; // actual turn rate
int m_iOrientation; // 0 = floor, 1 = Ceiling
int m_iOn;
int m_fBeserk; // Sometimes this bitch will just freak out
int m_iAutoStart; // true if the turret auto deploys when a target
// enters its range
Vector m_vecLastSight;
float m_flLastSight; // Last time we saw a target
float m_flMaxWait; // Max time to seach w/o a target
int m_iSearchSpeed; // Not Used!
// movement
float m_flStartYaw;
Vector m_vecCurAngles;
Vector m_vecGoalAngles;
float m_flPingTime; // Time until the next ping, used when searching
float m_flSpinUpTime; // Amount of time until the barrel should spin down when searching
float m_flDamageTime;
int m_iAmmoType;
COutputEvent m_OnActivate;
COutputEvent m_OnDeactivate;
//DEFINE_CUSTOM_AI;
DECLARE_DATADESC();
typedef CAI_BaseNPC BaseClass;
};
BEGIN_DATADESC( CNPC_BaseTurret )
//FIELDS
DEFINE_FIELD( CNPC_BaseTurret, m_flMaxSpin, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_BaseTurret, m_iSpin, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_pEyeGlow, FIELD_CLASSPTR ),
DEFINE_FIELD( CNPC_BaseTurret, m_eyeBrightness, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_iDeployHeight, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_iRetractHeight, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_iMinPitch, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_fTurnRate, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_BaseTurret, m_iOn, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_fBeserk, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_iAutoStart, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_BaseTurret, m_vecLastSight, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_BaseTurret, m_flLastSight, FIELD_TIME ),
DEFINE_FIELD( CNPC_BaseTurret, m_flStartYaw, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_BaseTurret, m_vecCurAngles, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_BaseTurret, m_vecGoalAngles, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_BaseTurret, m_flPingTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_BaseTurret, m_flSpinUpTime, FIELD_TIME ),
//KEYFIELDS
DEFINE_KEYFIELD( CNPC_BaseTurret, m_flMaxWait, FIELD_FLOAT, "maxsleep" ),
DEFINE_KEYFIELD( CNPC_BaseTurret, m_iOrientation, FIELD_INTEGER, "orientation" ),
DEFINE_KEYFIELD( CNPC_BaseTurret, m_iSearchSpeed, FIELD_INTEGER, "searchspeed" ),
DEFINE_KEYFIELD( CNPC_BaseTurret, m_iBaseTurnRate, FIELD_INTEGER, "turnrate" ),
//Use
DEFINE_USEFUNC( CNPC_BaseTurret, TurretUse ),
//Thinks
DEFINE_THINKFUNC( CNPC_BaseTurret, ActiveThink ),
DEFINE_THINKFUNC( CNPC_BaseTurret, SearchThink ),
DEFINE_THINKFUNC( CNPC_BaseTurret, AutoSearchThink ),
DEFINE_THINKFUNC( CNPC_BaseTurret, TurretDeath ),
DEFINE_THINKFUNC( CNPC_BaseTurret, SpinDownCall ),
DEFINE_THINKFUNC( CNPC_BaseTurret, SpinUpCall ),
DEFINE_THINKFUNC( CNPC_BaseTurret, Deploy ),
DEFINE_THINKFUNC( CNPC_BaseTurret, Retire ),
DEFINE_THINKFUNC( CNPC_BaseTurret, Initialize ),
//Inputs
DEFINE_INPUTFUNC( CNPC_BaseTurret, FIELD_VOID, "Activate", InputActivate ),
DEFINE_INPUTFUNC( CNPC_BaseTurret, FIELD_VOID, "Deactivate", InputDeactivate ),
//Outputs
DEFINE_OUTPUT( CNPC_BaseTurret, m_OnActivate, "OnActivate"),
DEFINE_OUTPUT( CNPC_BaseTurret, m_OnDeactivate, "OnDeactivate"),
END_DATADESC()
void CNPC_BaseTurret::Spawn()
{
Precache( );
SetNextThink( gpGlobals->curtime + 1 );
SetMoveType( MOVETYPE_FLY );
SetSequence( 0 );
m_flCycle = 0;
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
m_takedamage = DAMAGE_YES;
AddFlag( FL_AIMTARGET );
AddFlag( FL_NPC );
SetUse( &CNPC_BaseTurret::TurretUse );
if (( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
&& !( m_spawnflags & SF_MONSTER_TURRET_STARTINACTIVE ))
{
m_iAutoStart = true;
}
ResetSequenceInfo( );
SetBoneController(0, 0);
SetBoneController(1, 0);
m_flFieldOfView = VIEW_FIELD_FULL;
m_bloodColor = DONT_BLEED;
m_flDamageTime = 0;
if ( GetSpawnFlags() & SF_MONSTER_TURRET_STARTINACTIVE )
{
SetTurretAnim( TURRET_ANIM_RETIRE );
m_flCycle = 0.0f;
m_flPlaybackRate = 0.0f;
}
}
void CNPC_BaseTurret::Precache()
{
m_iAmmoType = GetAmmoDef()->Index("12mmRound");
enginesound->PrecacheSound ("turret/tu_fire1.wav");
enginesound->PrecacheSound ("turret/tu_ping.wav");
enginesound->PrecacheSound ("turret/tu_active2.wav");
enginesound->PrecacheSound ("turret/tu_die.wav");
enginesound->PrecacheSound ("turret/tu_die2.wav");
enginesound->PrecacheSound ("turret/tu_die3.wav");
enginesound->PrecacheSound ("turret/tu_deploy.wav");
enginesound->PrecacheSound ("turret/tu_spinup.wav");
enginesound->PrecacheSound ("turret/tu_spindown.wav");
enginesound->PrecacheSound ("turret/tu_search.wav");
enginesound->PrecacheSound ("turret/tu_alert.wav");
}
Class_T CNPC_BaseTurret::Classify( void )
{
if (m_iOn || m_iAutoStart)
return CLASS_MACHINE;
return CLASS_NONE;
}
//=========================================================
// TraceAttack - being attacked
//=========================================================
void CNPC_BaseTurret::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
CTakeDamageInfo ainfo = info;
if ( ptr->hitgroup == 10 )
{
// hit armor
if ( m_flDamageTime != gpGlobals->curtime || (random->RandomInt(0,10) < 1) )
{
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
m_flDamageTime = gpGlobals->curtime;
}
ainfo.SetDamage( 0.1 );// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
}
if ( m_takedamage == DAMAGE_NO )
return;
//DevMsg( 1, "traceattack: %f\n", ainfo.GetDamage() );
AddMultiDamage( info, this );
}
//=========================================================
// TakeDamage - take damage.
//=========================================================
int CNPC_BaseTurret::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
if ( m_takedamage == DAMAGE_NO )
return 0;
float flDamage = inputInfo.GetDamage();
if (!m_iOn)
flDamage /= 10.0;
m_iHealth -= flDamage;
if (m_iHealth <= 0)
{
m_iHealth = 0;
m_takedamage = DAMAGE_NO;
m_flDamageTime = gpGlobals->curtime;
// ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place???
SetUse(NULL);
SetThink(&CNPC_BaseTurret::TurretDeath);
SetNextThink( gpGlobals->curtime + 0.1 );
m_OnDeactivate.FireOutput(this, this);
return 0;
}
if (m_iHealth <= 10)
{
if (m_iOn)
{
m_fBeserk = 1;
SetThink(&CNPC_BaseTurret::SearchThink);
}
}
return 1;
}
int CNPC_BaseTurret::OnTakeDamage( const CTakeDamageInfo &info )
{
int retVal = 0;
if (!m_takedamage)
return 0;
switch( m_lifeState )
{
case LIFE_ALIVE:
retVal = OnTakeDamage_Alive( info );
if ( m_iHealth <= 0 )
{
IPhysicsObject *pPhysics = VPhysicsGetObject();
if ( pPhysics )
{
pPhysics->EnableCollisions( false );
}
Event_Killed( info );
Event_Dying();
}
return retVal;
break;
case LIFE_DYING:
return OnTakeDamage_Dying( info );
default:
case LIFE_DEAD:
return OnTakeDamage_Dead( info );
}
}
void CNPC_BaseTurret::SetTurretAnim( TURRET_ANIM anim )
{
/*
if (GetSequence() != anim)
{
switch(anim)
{
case TURRET_ANIM_FIRE:
case TURRET_ANIM_SPIN:
if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN)
{
m_flCycle = 0;
}
break;
default:
m_flCycle = 0;
break;
}
SetSequence( anim );
ResetSequenceInfo( );
switch(anim)
{
case TURRET_ANIM_RETIRE:
m_flCycle = 255;
m_flPlaybackRate = -1.0; //play the animation backwards
break;
case TURRET_ANIM_DIE:
m_flPlaybackRate = 1.0;
break;
}
//ALERT(at_console, "Turret anim #%d\n", anim);
}
*/
if (GetSequence() != anim)
{
SetSequence( anim );
ResetSequenceInfo( );
switch(anim)
{
case TURRET_ANIM_FIRE:
case TURRET_ANIM_SPIN:
if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN)
{
m_flCycle = 0;
}
break;
case TURRET_ANIM_RETIRE:
m_flCycle = 1.0;
m_flPlaybackRate = -1.0; //play the animation backwards
break;
case TURRET_ANIM_DIE:
m_flCycle = 0.0;
m_flPlaybackRate = 1.0;
break;
default:
m_flCycle = 0;
break;
}
}
}
//=========================================================
// Initialize - set up the turret, initial think
//=========================================================
void CNPC_BaseTurret::Initialize(void)
{
m_iOn = 0;
m_fBeserk = 0;
m_iSpin = 0;
SetBoneController( 0, 0 );
SetBoneController( 1, 0 );
if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE;
if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT;
QAngle angles = GetAbsAngles();
m_flStartYaw = angles.y;
if (m_iOrientation == TURRET_ORIENTATION_CEILING)
{
angles.x = 180;
angles.y += 180;
if( angles.y > 360 )
angles.y -= 360;
SetAbsAngles( angles );
// pev->idealpitch = 180; //not used?
Vector view_ofs = GetViewOffset();
view_ofs.z = -view_ofs.z;
SetViewOffset( view_ofs );
// pev->effects |= EF_INVLIGHT; //no need
}
m_vecGoalAngles.x = 0;
if (m_iAutoStart)
{
m_flLastSight = gpGlobals->curtime + m_flMaxWait;
SetThink(&CNPC_BaseTurret::AutoSearchThink);
SetNextThink( gpGlobals->curtime + 0.1 );
}
else
{
SetThink( SUB_DoNothing );
}
}
//=========================================================
// ActiveThink -
//=========================================================
void CNPC_BaseTurret::ActiveThink(void)
{
int fAttack = 0;
SetNextThink( gpGlobals->curtime + 0.1 );
StudioFrameAdvance( );
if ( (!m_iOn) || (GetEnemy() == NULL) )
{
SetEnemy( NULL );
m_flLastSight = gpGlobals->curtime + m_flMaxWait;
SetThink(&CNPC_BaseTurret::SearchThink);
return;
}
// if it's dead, look for something new
if ( !GetEnemy()->IsAlive() )
{
if (!m_flLastSight)
{
m_flLastSight = gpGlobals->curtime + 0.5; // continue-shooting timeout
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -