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.
1745 lines
42 KiB
1745 lines
42 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $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_memory.h" |
|
#include "ai_route.h" |
|
#include "ai_motor.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 "hl1_ai_basenpc.h" |
|
#include "ai_senses.h" |
|
#include "Sprite.h" |
|
#include "beam_shared.h" |
|
#include "logicrelay.h" |
|
#include "ai_navigator.h" |
|
|
|
|
|
#define N_SCALE 15 |
|
#define N_SPHERES 20 |
|
|
|
ConVar sk_nihilanth_health( "sk_nihilanth_health", "800" ); |
|
ConVar sk_nihilanth_zap( "sk_nihilanth_zap", "30" ); |
|
|
|
class CNPC_Nihilanth : public CHL1BaseNPC |
|
{ |
|
DECLARE_CLASS( CNPC_Nihilanth, CHL1BaseNPC ); |
|
public: |
|
void Spawn( void ); |
|
void Precache( void ); |
|
|
|
Class_T Classify( void ) { return CLASS_ALIEN_MILITARY; }; |
|
|
|
/* void Killed( entvars_t *pevAttacker, int iGib ); |
|
void GibMonster( void ); |
|
|
|
void TargetSphere( USE_TYPE useType, float value ); |
|
CBaseEntity *RandomTargetname( const char *szName ); |
|
void MakeFriend( Vector vecPos ); |
|
|
|
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); |
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); |
|
*/ |
|
|
|
int OnTakeDamage_Alive( const CTakeDamageInfo &info ); |
|
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); |
|
bool ShouldGib( const CTakeDamageInfo &info ) { return false; } |
|
|
|
void PainSound( const CTakeDamageInfo &info ); |
|
void DeathSound( const CTakeDamageInfo &info ); |
|
|
|
void StartupThink( void ); |
|
void NullThink( void ); |
|
|
|
void HuntThink( void ); |
|
void DyingThink( void ); |
|
|
|
void Flight( void ); |
|
void NextActivity( void ); |
|
void FloatSequence( void ); |
|
void HandleAnimEvent( animevent_t *pEvent ); |
|
bool EmitSphere( void ); |
|
|
|
void ShootBalls( void ); |
|
bool AbsorbSphere( void ); |
|
|
|
void MakeFriend( Vector vecStart ); |
|
|
|
void InputTurnBabyOn( inputdata_t &inputdata ); |
|
void InputTurnBabyOff( inputdata_t &inputdata ); |
|
|
|
float m_flForce; |
|
|
|
float m_flNextPainSound; |
|
|
|
Vector m_velocity; |
|
Vector m_avelocity; |
|
|
|
Vector m_vecTarget; |
|
Vector m_posTarget; |
|
|
|
Vector m_vecDesired; |
|
Vector m_posDesired; |
|
|
|
float m_flMinZ; |
|
float m_flMaxZ; |
|
|
|
Vector m_vecGoal; |
|
|
|
float m_flLastSeen; |
|
float m_flPrevSeen; |
|
|
|
int m_irritation; |
|
|
|
int m_iLevel; |
|
int m_iTeleport; |
|
|
|
EHANDLE m_hRecharger; |
|
|
|
EHANDLE m_hSphere[N_SPHERES]; |
|
int m_iActiveSpheres; |
|
|
|
float m_flAdj; |
|
|
|
CSprite *m_pBall; |
|
|
|
char m_szRechargerTarget[64]; |
|
char m_szDrawUse[64]; |
|
char m_szTeleportUse[64]; |
|
char m_szTeleportTouch[64]; |
|
char m_szDeadUse[64]; |
|
char m_szDeadTouch[64]; |
|
|
|
float m_flShootEnd; |
|
float m_flShootTime; |
|
|
|
EHANDLE m_hFriend[3]; |
|
|
|
bool m_bDead; |
|
|
|
DECLARE_DATADESC(); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( monster_nihilanth, CNPC_Nihilanth ); |
|
|
|
BEGIN_DATADESC( CNPC_Nihilanth ) |
|
DEFINE_FIELD( m_flForce, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flNextPainSound, FIELD_TIME ), |
|
DEFINE_FIELD( m_velocity, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_avelocity, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_posTarget, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( m_vecDesired, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_posDesired, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( m_flMinZ, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMaxZ, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_flLastSeen, FIELD_TIME ), |
|
DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ), |
|
DEFINE_FIELD( m_irritation, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iLevel, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iTeleport, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_hRecharger, FIELD_EHANDLE ), |
|
DEFINE_ARRAY( m_hSphere, FIELD_EHANDLE, N_SPHERES ), |
|
DEFINE_FIELD( m_iActiveSpheres, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_flAdj, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_pBall, FIELD_CLASSPTR ), |
|
DEFINE_ARRAY( m_szRechargerTarget, FIELD_CHARACTER, 64 ), |
|
DEFINE_ARRAY( m_szDrawUse, FIELD_CHARACTER, 64 ), |
|
DEFINE_ARRAY( m_szTeleportUse, FIELD_CHARACTER, 64 ), |
|
DEFINE_ARRAY( m_szTeleportTouch, FIELD_CHARACTER, 64 ), |
|
DEFINE_ARRAY( m_szDeadUse, FIELD_CHARACTER, 64 ), |
|
DEFINE_ARRAY( m_szDeadTouch, FIELD_CHARACTER, 64 ), |
|
DEFINE_FIELD( m_flShootEnd, FIELD_TIME ), |
|
DEFINE_FIELD( m_flShootTime, FIELD_TIME ), |
|
DEFINE_ARRAY( m_hFriend, FIELD_EHANDLE, 3 ), |
|
DEFINE_FIELD( m_bDead, FIELD_BOOLEAN ), |
|
DEFINE_THINKFUNC( NullThink ), |
|
DEFINE_THINKFUNC( StartupThink ), |
|
DEFINE_THINKFUNC( HuntThink ), |
|
DEFINE_THINKFUNC( DyingThink ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "TurnBabyOn", InputTurnBabyOn ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "TurnBabyOff", InputTurnBabyOff ), |
|
|
|
END_DATADESC() |
|
|
|
class CNihilanthHVR : public CAI_BaseNPC |
|
{ |
|
DECLARE_CLASS( CNihilanthHVR, CAI_BaseNPC ); |
|
public: |
|
void Spawn( void ); |
|
void Precache( void ); |
|
|
|
void CircleInit( CBaseEntity *pTarget ); |
|
void AbsorbInit( void ); |
|
void GreenBallInit( void ); |
|
|
|
|
|
void RemoveTouch( CBaseEntity *pOther ); |
|
|
|
/*void Zap( void ); |
|
void Teleport( void );*/ |
|
|
|
void HoverThink( void ); |
|
bool CircleTarget( Vector vecTarget ); |
|
void BounceTouch( CBaseEntity *pOther ); |
|
|
|
void ZapThink( void ); |
|
void ZapInit( CBaseEntity *pEnemy ); |
|
void ZapTouch( CBaseEntity *pOther ); |
|
|
|
void TeleportThink( void ); |
|
void TeleportTouch( CBaseEntity *pOther ); |
|
|
|
void MovetoTarget( Vector vecTarget ); |
|
|
|
void DissipateThink( void ); |
|
|
|
CSprite *SpriteInit( const char *pSpriteName, CNihilanthHVR *pOwner ); |
|
|
|
void TeleportInit( CNPC_Nihilanth *pOwner, CBaseEntity *pEnemy, CBaseEntity *pTarget, CBaseEntity *pTouch ); |
|
|
|
float m_flIdealVel; |
|
Vector m_vecIdeal; |
|
CNPC_Nihilanth *m_pNihilanth; |
|
EHANDLE m_hTouch; |
|
|
|
|
|
float m_flBallScale; |
|
|
|
void SetSprite( CBaseEntity *pSprite ) |
|
{ |
|
m_hSprite = pSprite; |
|
} |
|
|
|
CBaseEntity *GetSprite( void ) |
|
{ |
|
return m_hSprite.Get(); |
|
} |
|
|
|
void SetBeam( CBaseEntity *pBeam ) |
|
{ |
|
m_hBeam = pBeam; |
|
} |
|
|
|
CBaseEntity *GetBeam( void ) |
|
{ |
|
return m_hBeam.Get(); |
|
} |
|
|
|
private: |
|
|
|
EHANDLE m_hSprite; |
|
EHANDLE m_hBeam; |
|
|
|
|
|
DECLARE_DATADESC(); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( nihilanth_energy_ball, CNihilanthHVR ); |
|
|
|
|
|
BEGIN_DATADESC( CNihilanthHVR ) |
|
DEFINE_FIELD( m_flIdealVel, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flBallScale, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_vecIdeal, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_pNihilanth, FIELD_CLASSPTR ), |
|
DEFINE_FIELD( m_hTouch, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ), |
|
|
|
DEFINE_THINKFUNC( HoverThink ), |
|
DEFINE_ENTITYFUNC( BounceTouch ), |
|
DEFINE_THINKFUNC( ZapThink ), |
|
DEFINE_ENTITYFUNC( ZapTouch ), |
|
DEFINE_THINKFUNC( DissipateThink ), |
|
DEFINE_THINKFUNC( TeleportThink ), |
|
DEFINE_ENTITYFUNC( TeleportTouch ), |
|
DEFINE_ENTITYFUNC( RemoveTouch ), |
|
END_DATADESC() |
|
|
|
|
|
//========================================================= |
|
// Nihilanth, final Boss monster |
|
//========================================================= |
|
|
|
void CNPC_Nihilanth::Spawn( void ) |
|
{ |
|
Precache( ); |
|
// motor |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
SetModel( "models/nihilanth.mdl" ); |
|
//UTIL_SetSize( this, Vector( -300, -300, 0), Vector(300, 300, 512)); |
|
//UTIL_SetSize(this, Vector( -32, -32, 0), Vector(32, 32, 64 )); |
|
|
|
UTIL_SetSize(this, Vector( -16 * N_SCALE, -16 * N_SCALE, -48 * N_SCALE ), Vector( 16 * N_SCALE, 16 * N_SCALE, 28 * N_SCALE ) ); |
|
|
|
Vector vecSurroundingMins( -16 * N_SCALE, -16 * N_SCALE, -48 * N_SCALE ); |
|
Vector vecSurroundingMaxs( 16 * N_SCALE, 16 * N_SCALE, 28 * N_SCALE ); |
|
CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs ); |
|
|
|
UTIL_SetOrigin( this, GetAbsOrigin() - Vector( 0, 0, 64 ) ); |
|
|
|
AddFlag( FL_NPC ); |
|
m_takedamage = DAMAGE_AIM; |
|
m_iHealth = sk_nihilanth_health.GetFloat(); |
|
SetViewOffset ( Vector( 0, 0, 300 ) ); |
|
|
|
m_flFieldOfView = -1; // 360 degrees |
|
|
|
SetSequence( 0 ); |
|
ResetSequenceInfo( ); |
|
|
|
InitBoneControllers(); |
|
|
|
SetThink( &CNPC_Nihilanth::StartupThink ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
m_vecDesired = Vector( 1, 0, 0 ); |
|
m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, 512 ); |
|
|
|
m_iLevel = 1; |
|
m_iTeleport = 1; |
|
|
|
if (m_szRechargerTarget[0] == '\0') Q_strncpy( m_szRechargerTarget, "n_recharger", sizeof( m_szRechargerTarget ) ); |
|
if (m_szDrawUse[0] == '\0') Q_strncpy( m_szDrawUse, "n_draw", sizeof( m_szDrawUse ) ); |
|
if (m_szTeleportUse[0] == '\0') Q_strncpy( m_szTeleportUse, "n_leaving", sizeof( m_szTeleportUse ) ); |
|
if (m_szTeleportTouch[0] == '\0') Q_strncpy( m_szTeleportTouch, "n_teleport", sizeof( m_szTeleportTouch ) ); |
|
if (m_szDeadUse[0] == '\0') Q_strncpy( m_szDeadUse, "n_dead", sizeof( m_szDeadUse ) ); |
|
if (m_szDeadTouch[0] == '\0') Q_strncpy( m_szDeadTouch, "n_ending", sizeof( m_szDeadTouch ) ); |
|
|
|
SetBloodColor( BLOOD_COLOR_YELLOW ); |
|
} |
|
|
|
|
|
void CNPC_Nihilanth::Precache( void ) |
|
{ |
|
PrecacheModel("models/nihilanth.mdl"); |
|
PrecacheModel("sprites/lgtning.vmt"); |
|
UTIL_PrecacheOther( "nihilanth_energy_ball" ); |
|
UTIL_PrecacheOther( "monster_alien_controller" ); |
|
UTIL_PrecacheOther( "monster_alien_slave" ); |
|
|
|
PrecacheScriptSound( "Nihilanth.PainLaugh" ); |
|
PrecacheScriptSound( "Nihilanth.Pain" ); |
|
PrecacheScriptSound( "Nihilanth.Die" ); |
|
PrecacheScriptSound( "Nihilanth.FriendBeam" ); |
|
PrecacheScriptSound( "Nihilanth.Attack" ); |
|
PrecacheScriptSound( "Nihilanth.BallAttack" ); |
|
PrecacheScriptSound( "Nihilanth.Recharge" ); |
|
|
|
} |
|
|
|
void CNPC_Nihilanth::PainSound( const CTakeDamageInfo &info ) |
|
{ |
|
if (m_flNextPainSound > gpGlobals->curtime) |
|
return; |
|
|
|
m_flNextPainSound = gpGlobals->curtime + random->RandomFloat( 2, 5 ); |
|
|
|
if ( m_iHealth > sk_nihilanth_health.GetFloat() / 2 ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.PainLaugh" ); |
|
} |
|
else if (m_irritation >= 2) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.Pain" ); |
|
} |
|
} |
|
|
|
void CNPC_Nihilanth::DeathSound( const CTakeDamageInfo &info ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.Die" ); |
|
} |
|
|
|
int CNPC_Nihilanth::OnTakeDamage_Alive( const CTakeDamageInfo &info ) |
|
{ |
|
if ( info.GetInflictor() == this ) |
|
return 0; |
|
|
|
if ( m_bDead ) |
|
return 0; |
|
|
|
if ( info.GetDamage() >= m_iHealth ) |
|
{ |
|
m_iHealth = 1; |
|
if ( m_irritation != 3 ) |
|
return 0; |
|
} |
|
|
|
PainSound( info ); |
|
|
|
m_iHealth -= info.GetDamage(); |
|
|
|
if( m_iHealth < 0 ) |
|
{ |
|
m_iHealth = 1; |
|
m_bDead = true; |
|
m_takedamage = DAMAGE_NO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void CNPC_Nihilanth::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
if (m_irritation == 3) |
|
m_irritation = 2; |
|
|
|
if (m_irritation == 2 && ptr->hitgroup == 0 && info.GetDamage() > 2) |
|
m_irritation = 3; |
|
|
|
if (m_irritation != 3) |
|
{ |
|
Vector vecBlood = (ptr->endpos - GetAbsOrigin() ); |
|
|
|
VectorNormalize( vecBlood ); |
|
|
|
UTIL_BloodStream( ptr->endpos, vecBlood, BloodColor(), info.GetDamage() + (100 - 100 * (m_iHealth / sk_nihilanth_health.GetFloat() ))); |
|
} |
|
|
|
// SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage * 5.0);// a little surface blood. |
|
AddMultiDamage( info, this ); |
|
} |
|
|
|
bool CNPC_Nihilanth::EmitSphere( void ) |
|
{ |
|
m_iActiveSpheres = 0; |
|
int empty = 0; |
|
|
|
for (int i = 0; i < N_SPHERES; i++) |
|
{ |
|
if (m_hSphere[i] != NULL) |
|
{ |
|
m_iActiveSpheres++; |
|
} |
|
else |
|
{ |
|
empty = i; |
|
} |
|
} |
|
|
|
if (m_iActiveSpheres >= N_SPHERES) |
|
return false; |
|
|
|
Vector vecSrc = m_hRecharger->GetAbsOrigin(); |
|
CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( GetAbsAngles() ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc ); |
|
pEntity->CircleInit( this ); |
|
m_hSphere[empty] = pEntity; |
|
|
|
return true; |
|
} |
|
|
|
void CNPC_Nihilanth::NullThink( void ) |
|
{ |
|
StudioFrameAdvance( ); |
|
SetNextThink( gpGlobals->curtime + 0.5 ); |
|
} |
|
|
|
void CNPC_Nihilanth::StartupThink( void ) |
|
{ |
|
m_irritation = 0; |
|
m_flAdj = 512; |
|
|
|
CBaseEntity *pEntity; |
|
|
|
pEntity = gEntList.FindEntityByName( NULL, "n_min" ); |
|
if (pEntity) |
|
m_flMinZ = pEntity->GetAbsOrigin().z; |
|
else |
|
m_flMinZ = -4096; |
|
|
|
pEntity = gEntList.FindEntityByName( NULL, "n_max" ); |
|
if (pEntity) |
|
m_flMaxZ = pEntity->GetAbsOrigin().z; |
|
else |
|
m_flMaxZ = 4096; |
|
|
|
m_hRecharger = this; |
|
|
|
//TODO |
|
for (int i = 0; i < N_SPHERES; i++) |
|
{ |
|
EmitSphere(); |
|
} |
|
|
|
m_hRecharger = NULL; |
|
|
|
SetUse( NULL ); |
|
SetThink( &CNPC_Nihilanth::HuntThink); |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
} |
|
|
|
void CNPC_Nihilanth::InputTurnBabyOn( inputdata_t &inputdata ) |
|
{ |
|
if ( m_irritation == 0 ) |
|
{ |
|
m_irritation = 1; |
|
} |
|
} |
|
|
|
void CNPC_Nihilanth::InputTurnBabyOff( inputdata_t &inputdata ) |
|
{ |
|
CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, m_szDeadTouch ); |
|
|
|
if ( pTouch && GetEnemy() != NULL ) |
|
pTouch->Touch( GetEnemy() ); |
|
} |
|
|
|
bool CNPC_Nihilanth::AbsorbSphere( void ) |
|
{ |
|
for (int i = 0; i < N_SPHERES; i++) |
|
{ |
|
if (m_hSphere[i] != NULL) |
|
{ |
|
CNihilanthHVR *pSphere = (CNihilanthHVR *)m_hSphere[i].Get(); |
|
pSphere->AbsorbInit(); |
|
m_hSphere[i] = NULL; |
|
m_iActiveSpheres--; |
|
return TRUE; |
|
} |
|
} |
|
return FALSE; |
|
} |
|
|
|
void CNPC_Nihilanth::HuntThink( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
DispatchAnimEvents( this ); |
|
StudioFrameAdvance( ); |
|
|
|
ShootBalls(); |
|
|
|
// if dead, force cancelation of current animation |
|
if ( m_bDead ) |
|
{ |
|
SetThink( &CNPC_Nihilanth::DyingThink ); |
|
SetCycle( 1.0f ); |
|
|
|
StudioFrameAdvance(); |
|
return; |
|
} |
|
|
|
// ALERT( at_console, "health %.0f\n", pev->health ); |
|
|
|
// if damaged, try to abosorb some spheres |
|
if ( m_iHealth < sk_nihilanth_health.GetFloat() && AbsorbSphere() ) |
|
{ |
|
m_iHealth += sk_nihilanth_health.GetFloat() / N_SPHERES; |
|
} |
|
|
|
// get new sequence |
|
if ( IsSequenceFinished() ) |
|
{ |
|
SetCycle( 0 ); |
|
|
|
NextActivity( ); |
|
ResetSequenceInfo( ); |
|
m_flPlaybackRate = 2.0 - 1.0 * ( m_iHealth / sk_nihilanth_health.GetFloat() ); |
|
} |
|
|
|
// look for current enemy |
|
if ( GetEnemy() != NULL && m_hRecharger == NULL) |
|
{ |
|
if (FVisible( GetEnemy() )) |
|
{ |
|
if (m_flLastSeen < gpGlobals->curtime - 5) |
|
m_flPrevSeen = gpGlobals->curtime; |
|
|
|
m_flLastSeen = gpGlobals->curtime; |
|
m_posTarget = GetEnemy()->GetAbsOrigin(); |
|
m_vecTarget = m_posTarget - GetAbsOrigin(); |
|
|
|
VectorNormalize( m_vecTarget ); |
|
|
|
m_vecDesired = m_vecTarget; |
|
m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, m_posTarget.z + m_flAdj ); |
|
} |
|
else |
|
{ |
|
m_flAdj = MIN( m_flAdj + 10, 1000 ); |
|
} |
|
} |
|
|
|
// don't go too high |
|
if (m_posDesired.z > m_flMaxZ) |
|
m_posDesired.z = m_flMaxZ; |
|
|
|
// don't go too low |
|
if (m_posDesired.z < m_flMinZ) |
|
m_posDesired.z = m_flMinZ; |
|
|
|
Flight( ); |
|
} |
|
|
|
void CNPC_Nihilanth::Flight( void ) |
|
{ |
|
Vector vForward, vRight, vUp; |
|
|
|
QAngle vAngle = QAngle( GetAbsAngles().x + m_avelocity.x, GetAbsAngles().y + m_avelocity.y, GetAbsAngles().z + m_avelocity.z ); |
|
|
|
AngleVectors( vAngle, &vForward, &vRight, &vUp ); |
|
float flSide = DotProduct( m_vecDesired, vRight ); |
|
|
|
if (flSide < 0) |
|
{ |
|
if (m_avelocity.y < 180) |
|
{ |
|
m_avelocity.y += 6; // 9 * (3.0/2.0); |
|
} |
|
} |
|
else |
|
{ |
|
if (m_avelocity.y > -180) |
|
{ |
|
m_avelocity.y -= 6; // 9 * (3.0/2.0); |
|
} |
|
} |
|
m_avelocity.y *= 0.98; |
|
|
|
// estimate where I'll be in two seconds |
|
Vector vecEst = GetAbsOrigin() + m_velocity * 2.0 + vUp * m_flForce * 20; |
|
|
|
// add immediate force |
|
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); |
|
|
|
m_velocity.x += vUp.x * m_flForce; |
|
m_velocity.y += vUp.y * m_flForce; |
|
m_velocity.z += vUp.z * m_flForce; |
|
|
|
|
|
float flSpeed = m_velocity.Length(); |
|
float flDir = DotProduct( Vector( vForward.x, vForward.y, 0 ), Vector( m_velocity.x, m_velocity.y, 0 ) ); |
|
if (flDir < 0) |
|
flSpeed = -flSpeed; |
|
|
|
// sideways drag |
|
m_velocity.x = m_velocity.x * (1.0 - fabs( vRight.x ) * 0.05); |
|
m_velocity.y = m_velocity.y * (1.0 - fabs( vRight.y ) * 0.05); |
|
m_velocity.z = m_velocity.z * (1.0 - fabs( vRight.z ) * 0.05); |
|
|
|
// general drag |
|
m_velocity = m_velocity * 0.995; |
|
|
|
// apply power to stay correct height |
|
if (m_flForce < 100 && vecEst.z < m_posDesired.z) |
|
{ |
|
m_flForce += 10; |
|
} |
|
else if (m_flForce > -100 && vecEst.z > m_posDesired.z) |
|
{ |
|
if (vecEst.z > m_posDesired.z) |
|
m_flForce -= 10; |
|
} |
|
|
|
SetAbsVelocity( m_velocity ); |
|
|
|
vAngle = QAngle( GetAbsAngles().x + m_avelocity.x * 0.1, GetAbsAngles().y + m_avelocity.y * 0.1, GetAbsAngles().z + m_avelocity.z * 0.1 ); |
|
|
|
SetAbsAngles( vAngle ); |
|
|
|
// ALERT( at_console, "%5.0f %5.0f : %4.0f : %3.0f : %2.0f\n", m_posDesired.z, pev->origin.z, m_velocity.z, m_avelocity.y, m_flForce ); |
|
} |
|
|
|
void CNPC_Nihilanth::NextActivity( ) |
|
{ |
|
Vector vForward, vRight, vUp; |
|
|
|
SetIdealActivity( ACT_DO_NOT_DISTURB ); |
|
|
|
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); |
|
|
|
if (m_irritation >= 2) |
|
{ |
|
if (m_pBall == NULL) |
|
{ |
|
m_pBall = CSprite::SpriteCreate( "sprites/tele1.vmt", GetAbsOrigin(), true ); |
|
if (m_pBall) |
|
{ |
|
m_pBall->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); |
|
m_pBall->SetAttachment( this, 1 ); |
|
m_pBall->SetScale( 4.0 ); |
|
m_pBall->m_flSpriteFramerate = 10.0f; |
|
m_pBall->TurnOn( ); |
|
} |
|
} |
|
|
|
if (m_pBall) |
|
{ |
|
CBroadcastRecipientFilter filterlight; |
|
Vector vOrigin; |
|
QAngle vAngle; |
|
|
|
GetAttachment( 2, vOrigin, vAngle ); |
|
|
|
te->DynamicLight( filterlight, 0.0, &vOrigin, 255, 192, 64, 0, 256, 20, 0 ); |
|
} |
|
} |
|
|
|
|
|
if (( m_iHealth < sk_nihilanth_health.GetFloat() / 2 || m_iActiveSpheres < N_SPHERES / 2) && m_hRecharger == NULL && m_iLevel <= 9) |
|
{ |
|
char szName[64]; |
|
|
|
CBaseEntity *pEnt = NULL; |
|
CBaseEntity *pRecharger = NULL; |
|
float flDist = 8192; |
|
|
|
Q_snprintf(szName, sizeof( szName ), "%s%d", m_szRechargerTarget, m_iLevel ); |
|
|
|
while ((pEnt = gEntList.FindEntityByName( pEnt, szName )) != NULL ) |
|
{ |
|
float flLocal = (pEnt->GetAbsOrigin() - GetAbsOrigin() ).Length(); |
|
|
|
if ( flLocal < flDist ) |
|
{ |
|
flDist = flLocal; |
|
pRecharger = pEnt; |
|
} |
|
} |
|
|
|
if (pRecharger) |
|
{ |
|
m_hRecharger = pRecharger; |
|
m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, pRecharger->GetAbsOrigin().z ); |
|
m_vecDesired = pRecharger->GetAbsOrigin() - m_posDesired; |
|
|
|
VectorNormalize( m_vecDesired ); |
|
|
|
m_vecDesired.z = 0; |
|
|
|
VectorNormalize( m_vecDesired ); |
|
} |
|
else |
|
{ |
|
m_hRecharger = NULL; |
|
Msg( "nihilanth can't find %s\n", szName ); |
|
|
|
m_iLevel++; |
|
|
|
if ( m_iLevel > 9 ) |
|
m_irritation = 2; |
|
} |
|
} |
|
|
|
float flDist = ( m_posDesired - GetAbsOrigin() ).Length(); |
|
float flDot = DotProduct( m_vecDesired, vForward ); |
|
|
|
if (m_hRecharger != NULL) |
|
{ |
|
// at we at power up yet? |
|
if (flDist < 128.0) |
|
{ |
|
int iseq = LookupSequence( "recharge" ); |
|
|
|
if (iseq != GetSequence()) |
|
{ |
|
char szText[64]; |
|
|
|
Q_snprintf( szText, sizeof( szText ), "%s%d", m_szDrawUse, m_iLevel ); |
|
FireTargets( szText, this, this, USE_ON, 1.0 ); |
|
|
|
Msg( "fireing %s\n", szText ); |
|
} |
|
SetSequence ( LookupSequence( "recharge" ) ); |
|
} |
|
else |
|
{ |
|
FloatSequence( ); |
|
} |
|
return; |
|
} |
|
|
|
if (GetEnemy() != NULL && !GetEnemy()->IsAlive()) |
|
{ |
|
SetEnemy( NULL ); |
|
} |
|
|
|
if (m_flLastSeen + 15 < gpGlobals->curtime) |
|
{ |
|
SetEnemy( NULL ); |
|
} |
|
|
|
if ( GetEnemy() == NULL) |
|
{ |
|
GetSenses()->Look( 4096 ); |
|
SetEnemy( BestEnemy() ); |
|
} |
|
|
|
if ( GetEnemy() != NULL && m_irritation != 0) |
|
{ |
|
if (m_flLastSeen + 5 > gpGlobals->curtime && flDist < 256 && flDot > 0) |
|
{ |
|
if (m_irritation >= 2 && m_iHealth < sk_nihilanth_health.GetFloat() / 2.0) |
|
{ |
|
SetSequence( LookupSequence( "attack1_open" ) ); |
|
} |
|
else |
|
{ |
|
if ( random->RandomInt(0, 1 ) == 0) |
|
{ |
|
SetSequence( LookupSequence( "attack1" ) ); // zap |
|
} |
|
else |
|
{ |
|
char szText[64]; |
|
|
|
Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportTouch, m_iTeleport ); |
|
CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, szText ); |
|
|
|
Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportUse, m_iTeleport ); |
|
CBaseEntity *pTrigger = gEntList.FindEntityByName( NULL, szText ); |
|
|
|
if (pTrigger != NULL || pTouch != NULL) |
|
{ |
|
SetSequence( LookupSequence( "attack2" ) ); // teleport |
|
} |
|
else |
|
{ |
|
m_iTeleport++; |
|
SetSequence( LookupSequence( "attack1" ) ); // zap |
|
} |
|
} |
|
} |
|
return; |
|
} |
|
} |
|
|
|
FloatSequence( ); |
|
} |
|
|
|
void CNPC_Nihilanth::MakeFriend( Vector vecStart ) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < 3; i++) |
|
{ |
|
if (m_hFriend[i] != NULL && !m_hFriend[i]->IsAlive()) |
|
{ |
|
if ( m_nRenderMode == kRenderNormal) // don't do it if they are already fading |
|
m_hFriend[i]->MyNPCPointer()->CorpseFade(); |
|
|
|
m_hFriend[i] = NULL; |
|
} |
|
|
|
if (m_hFriend[i] == NULL) |
|
{ |
|
if ( random->RandomInt( 0, 1 ) == 0) |
|
{ |
|
int iNode = GetNavigator()->GetNetwork()->NearestNodeToPoint( vecStart ); |
|
CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNode ); |
|
|
|
if ( pNode && pNode->GetType() == NODE_AIR ) |
|
{ |
|
trace_t tr; |
|
Vector vNodeOrigin = pNode->GetOrigin(); |
|
|
|
UTIL_TraceHull( vNodeOrigin + Vector( 0, 0, 32 ), vNodeOrigin + Vector( 0, 0, 32 ), Vector(-40,-40, 0), Vector(40, 40, 100), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if ( tr.startsolid == 0 ) |
|
m_hFriend[i] = Create("monster_alien_controller", vNodeOrigin, GetAbsAngles() ); |
|
} |
|
} |
|
else |
|
{ |
|
int iNode = GetNavigator()->GetNetwork()->NearestNodeToPoint( vecStart ); |
|
CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNode ); |
|
|
|
if ( pNode && ( pNode->GetType() == NODE_GROUND || pNode->GetType() == NODE_WATER ) ) |
|
{ |
|
trace_t tr; |
|
Vector vNodeOrigin = pNode->GetOrigin(); |
|
|
|
UTIL_TraceHull( vNodeOrigin + Vector( 0, 0, 36 ), vNodeOrigin + Vector( 0, 0, 36 ), Vector( -15, -15, 0), Vector( 20, 15, 72 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if (tr.startsolid == 0) |
|
m_hFriend[i] = Create("monster_alien_slave", vNodeOrigin, GetAbsAngles() ); |
|
} |
|
} |
|
if (m_hFriend[i] != NULL) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, m_hFriend[i]->entindex(), "Nihilanth.FriendBeam" ); |
|
} |
|
|
|
return; |
|
} |
|
} |
|
} |
|
|
|
void CNPC_Nihilanth::ShootBalls( void ) |
|
{ |
|
if (m_flShootEnd > gpGlobals->curtime) |
|
{ |
|
Vector vecHand; |
|
QAngle vecAngle; |
|
|
|
while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->curtime) |
|
{ |
|
if ( GetEnemy() != NULL) |
|
{ |
|
Vector vecSrc, vecDir; |
|
CNihilanthHVR *pEntity = NULL; |
|
|
|
GetAttachment( 3, vecHand, vecAngle ); |
|
vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime); |
|
vecDir = m_posTarget - GetAbsOrigin(); |
|
VectorNormalize( vecDir ); |
|
vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime); |
|
|
|
pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( vecAngle ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->SetAbsVelocity( vecDir * 200.0 ); |
|
pEntity->ZapInit( GetEnemy() ); |
|
|
|
GetAttachment( 4, vecHand, vecAngle ); |
|
vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime); |
|
vecDir = m_posTarget - GetAbsOrigin(); |
|
VectorNormalize( vecDir ); |
|
|
|
vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime); |
|
|
|
pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( vecAngle ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->SetAbsVelocity( vecDir * 200.0 ); |
|
pEntity->ZapInit( GetEnemy() ); |
|
|
|
} |
|
|
|
m_flShootTime += 0.2; |
|
} |
|
} |
|
} |
|
|
|
void CNPC_Nihilanth::FloatSequence( void ) |
|
{ |
|
if (m_irritation >= 2) |
|
{ |
|
SetSequence( LookupSequence( "float_open" ) ); |
|
} |
|
else if (m_avelocity.y > 30) |
|
{ |
|
SetSequence( LookupSequence( "walk_r" ) ); |
|
} |
|
else if (m_avelocity.y < -30) |
|
{ |
|
SetSequence( LookupSequence( "walk_l" ) ); |
|
} |
|
else if (m_velocity.z > 30) |
|
{ |
|
SetSequence( LookupSequence( "walk_u" ) ); |
|
} |
|
else if (m_velocity.z < -30) |
|
{ |
|
SetSequence( LookupSequence( "walk_d" ) ); |
|
} |
|
else |
|
{ |
|
SetSequence( LookupSequence( "float" ) ); |
|
} |
|
} |
|
|
|
void CNPC_Nihilanth::DyingThink( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
DispatchAnimEvents( this ); |
|
StudioFrameAdvance( ); |
|
|
|
if ( m_lifeState == LIFE_ALIVE ) |
|
{ |
|
CTakeDamageInfo info; |
|
DeathSound( info ); |
|
m_lifeState = LIFE_DYING; |
|
|
|
m_posDesired.z = m_flMaxZ; |
|
} |
|
|
|
if ( GetAbsOrigin().z < m_flMaxZ && m_lifeState == LIFE_DEAD ) |
|
{ |
|
SetAbsOrigin( Vector( GetAbsOrigin().x, GetAbsOrigin().y, m_flMaxZ ) ); |
|
SetAbsVelocity( Vector( 0, 0, 0 ) ); |
|
} |
|
|
|
if ( m_lifeState == LIFE_DYING ) |
|
{ |
|
Flight( ); |
|
|
|
if (fabs( GetAbsOrigin().z - m_flMaxZ ) < 16) |
|
{ |
|
CBaseEntity *pTrigger = NULL; |
|
|
|
SetAbsVelocity( Vector( 0, 0, 0 ) ); |
|
SetGravity( 0 ); |
|
|
|
while( ( pTrigger = gEntList.FindEntityByName( pTrigger, m_szDeadUse ) ) != NULL ) |
|
{ |
|
CLogicRelay *pRelay = (CLogicRelay*)pTrigger; |
|
pRelay->m_OnTrigger.FireOutput( this, this ); |
|
} |
|
|
|
m_lifeState = LIFE_DEAD; |
|
} |
|
} |
|
|
|
if ( IsSequenceFinished() ) |
|
{ |
|
QAngle qAngularVel = GetLocalAngularVelocity(); |
|
|
|
qAngularVel.y += random->RandomFloat( -100, 100 ); |
|
|
|
if ( qAngularVel.y < -100) |
|
qAngularVel.y = -100; |
|
if ( qAngularVel.y > 100) |
|
qAngularVel.y = 100; |
|
|
|
SetLocalAngularVelocity( qAngularVel ); |
|
SetSequence( LookupSequence( "die1" ) ); |
|
} |
|
|
|
if ( m_pBall ) |
|
{ |
|
if (m_pBall->GetBrightness() > 0) |
|
{ |
|
m_pBall->SetBrightness( MAX( 0, m_pBall->GetBrightness() - 7 ), 0 ); |
|
} |
|
else |
|
{ |
|
UTIL_Remove( m_pBall ); |
|
m_pBall = NULL; |
|
} |
|
} |
|
|
|
Vector vecDir, vecSrc; |
|
QAngle vecAngles; |
|
Vector vForward, vRight, vUp; |
|
|
|
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); |
|
|
|
int iAttachment = random->RandomInt( 1, 4 ); |
|
|
|
do { |
|
vecDir = Vector( random->RandomFloat( -1, 1 ), random->RandomFloat( -1, 1 ), random->RandomFloat( -1, 1 ) ); |
|
} while (DotProduct( vecDir, vecDir) > 1.0); |
|
|
|
switch( random->RandomInt( 1, 4 )) |
|
{ |
|
case 1: // head |
|
vecDir.z = fabs( vecDir.z ) * 0.5; |
|
vecDir = vecDir + 2 * vUp; |
|
break; |
|
case 2: // eyes |
|
if (DotProduct( vecDir, vForward ) < 0) |
|
vecDir = vecDir * -1; |
|
|
|
vecDir = vecDir + 2 * vForward; |
|
break; |
|
case 3: // left hand |
|
if (DotProduct( vecDir, vRight ) > 0) |
|
vecDir = vecDir * -1; |
|
vecDir = vecDir - 2 * vRight; |
|
break; |
|
case 4: // right hand |
|
if (DotProduct( vecDir, vRight ) < 0) |
|
vecDir = vecDir * -1; |
|
vecDir = vecDir + 2 * vRight; |
|
break; |
|
} |
|
|
|
GetAttachment( iAttachment, vecSrc, vecAngles ); |
|
|
|
trace_t tr; |
|
|
|
UTIL_TraceLine( vecSrc, vecSrc + vecDir * 4096, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 16 ); |
|
|
|
if ( pBeam == NULL ) |
|
return; |
|
|
|
pBeam->PointEntInit( tr.endpos, this ); |
|
pBeam->SetEndAttachment( iAttachment ); |
|
pBeam->SetColor( 64, 128, 255 ); |
|
pBeam->SetFadeLength( 50 ); |
|
pBeam->SetBrightness( 255 ); |
|
pBeam->SetNoise( 12 ); |
|
pBeam->SetScrollRate( 1.0 ); |
|
pBeam->LiveForTime( 0.5 ); |
|
|
|
GetAttachment( 2, vecSrc, vecAngles ); |
|
CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( GetAbsAngles() ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->SetAbsVelocity( Vector ( random->RandomFloat( -0.7, 0.7 ), random->RandomFloat( -0.7, 0.7 ), 1.0 ) * 600.0 ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->GreenBallInit(); |
|
|
|
return; |
|
} |
|
|
|
|
|
void CNPC_Nihilanth::HandleAnimEvent( animevent_t *pEvent ) |
|
{ |
|
switch( pEvent->event ) |
|
{ |
|
case 1: // shoot |
|
break; |
|
case 2: // zen |
|
if ( GetEnemy() != NULL) |
|
{ |
|
Vector vOrigin; |
|
QAngle vAngle; |
|
CPASAttenuationFilter filter( this ); |
|
|
|
if ( random->RandomInt(0,4) == 0) |
|
EmitSound( filter, entindex(), "Nihilanth.Attack" ); |
|
|
|
EmitSound( filter, entindex(), "Nihilanth.BallAttack" ); |
|
|
|
GetAttachment( 2, vOrigin, vAngle); |
|
|
|
CBroadcastRecipientFilter filterlight; |
|
|
|
te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 ); |
|
|
|
GetAttachment( 3, vOrigin, vAngle); |
|
te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 ); |
|
|
|
m_flShootTime = gpGlobals->curtime; |
|
m_flShootEnd = gpGlobals->curtime + 1.0; |
|
} |
|
break; |
|
case 3: // prayer |
|
if (GetEnemy() != NULL) |
|
{ |
|
char szText[32]; |
|
|
|
Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportTouch, m_iTeleport ); |
|
CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, szText ); |
|
|
|
Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportUse, m_iTeleport ); |
|
CBaseEntity *pTrigger = gEntList.FindEntityByName( NULL, szText ); |
|
|
|
if (pTrigger != NULL || pTouch != NULL) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.Attack" ); |
|
|
|
Vector vecSrc; |
|
QAngle vecAngles; |
|
|
|
GetAttachment( 2, vecSrc, vecAngles ); |
|
|
|
CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( vecAngles ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->TeleportInit( this, GetEnemy(), pTrigger, pTouch ); |
|
|
|
pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc ); |
|
pEntity->SetAbsVelocity( Vector( GetAbsVelocity().x, GetAbsVelocity().y, GetAbsVelocity().z * 0.2 ) ); |
|
|
|
} |
|
else |
|
{ |
|
Vector vOrigin; |
|
QAngle vAngle; |
|
|
|
m_iTeleport++; // unexpected failure |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.BallAttack" ); |
|
|
|
Msg( "nihilanth can't target %s\n", szText ); |
|
|
|
GetAttachment( 2, vOrigin, vAngle); |
|
|
|
CBroadcastRecipientFilter filterlight; |
|
|
|
te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 ); |
|
|
|
GetAttachment( 3, vOrigin, vAngle); |
|
te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 ); |
|
|
|
m_flShootTime = gpGlobals->curtime; |
|
m_flShootEnd = gpGlobals->curtime + 1.0; |
|
} |
|
} |
|
break; |
|
case 4: // get a sphere |
|
{ |
|
if (m_hRecharger != NULL) |
|
{ |
|
if (!EmitSphere( )) |
|
{ |
|
m_hRecharger = NULL; |
|
} |
|
} |
|
} |
|
break; |
|
case 5: // start up sphere machine |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Nihilanth.Recharge" ); |
|
} |
|
break; |
|
case 6: |
|
if ( GetEnemy() != NULL) |
|
{ |
|
Vector vecSrc; |
|
QAngle vecAngles; |
|
GetAttachment( 3, vecSrc, vecAngles ); |
|
|
|
CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" ); |
|
|
|
pEntity->SetAbsOrigin( vecSrc ); |
|
pEntity->SetAbsAngles( vecAngles ); |
|
pEntity->SetOwnerEntity( this ); |
|
pEntity->Spawn(); |
|
|
|
pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc ); |
|
pEntity->ZapInit( GetEnemy() ); |
|
} |
|
break; |
|
case 7: |
|
/* |
|
Vector vecSrc, vecAngles; |
|
GetAttachment( 0, vecSrc, vecAngles ); |
|
CNihilanthHVR *pEntity = (CNihilanthHVR *)Create( "nihilanth_energy_ball", vecSrc, pev->angles, edict() ); |
|
pEntity->pev->velocity = Vector ( RANDOM_FLOAT( -0.7, 0.7 ), RANDOM_FLOAT( -0.7, 0.7 ), 1.0 ) * 600.0; |
|
pEntity->GreenBallInit( ); |
|
*/ |
|
break; |
|
} |
|
} |
|
|
|
|
|
//========================================================= |
|
// Controller bouncy ball attack |
|
//========================================================= |
|
|
|
|
|
|
|
void CNihilanthHVR::Spawn( void ) |
|
{ |
|
Precache( ); |
|
} |
|
|
|
|
|
void CNihilanthHVR::Precache( void ) |
|
{ |
|
PrecacheModel("sprites/flare6.vmt"); |
|
PrecacheModel("sprites/nhth1.vmt"); |
|
PrecacheModel("sprites/exit1.vmt"); |
|
PrecacheModel("sprites/tele1.vmt"); |
|
PrecacheModel("sprites/animglow01.vmt"); |
|
PrecacheModel("sprites/xspark4.vmt"); |
|
PrecacheModel("sprites/muzzleflash3.vmt"); |
|
|
|
PrecacheModel("sprites/laserbeam.vmt"); |
|
|
|
PrecacheScriptSound( "NihilanthHVR.Zap" ); |
|
PrecacheScriptSound( "NihilanthHVR.TeleAttack" ); |
|
} |
|
|
|
void CNihilanthHVR::CircleInit( CBaseEntity *pTarget ) |
|
{ |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetSolid( SOLID_NONE ); |
|
|
|
UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0)); |
|
UTIL_SetOrigin( this, GetAbsOrigin() ); |
|
|
|
SetThink( &CNihilanthHVR::HoverThink ); |
|
SetTouch( &CNihilanthHVR::BounceTouch ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
CSprite *pSprite = SpriteInit( "sprites/muzzleflash3.vmt", this ); |
|
|
|
if ( pSprite ) |
|
{ |
|
m_flBallScale = 2.0f; |
|
pSprite->SetScale( 2.0 ); |
|
pSprite->SetTransparency( kRenderTransAdd, 255, 224, 192, 255, kRenderFxNone ); |
|
} |
|
|
|
SetTarget( pTarget ); |
|
} |
|
|
|
CSprite *CNihilanthHVR::SpriteInit( const char *pSpriteName, CNihilanthHVR *pOwner ) |
|
{ |
|
pOwner->SetSprite( CSprite::SpriteCreate( pSpriteName, pOwner->GetAbsOrigin(), true ) ); |
|
|
|
CSprite *pSprite = (CSprite*)pOwner->GetSprite(); |
|
|
|
if ( pSprite ) |
|
{ |
|
pSprite->SetAttachment( pOwner, 0 ); |
|
pSprite->SetOwnerEntity( pOwner ); |
|
pSprite->AnimateForTime( 5, 9999 ); |
|
} |
|
|
|
return pSprite; |
|
} |
|
|
|
void CNihilanthHVR::HoverThink( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
if ( GetTarget() != NULL ) |
|
{ |
|
CircleTarget( GetTarget()->GetAbsOrigin() + Vector( 0, 0, 16 * N_SCALE ) ); |
|
} |
|
else |
|
{ |
|
UTIL_Remove( GetSprite() ); |
|
UTIL_Remove( this ); |
|
} |
|
} |
|
|
|
void CNihilanthHVR::BounceTouch( CBaseEntity *pOther ) |
|
{ |
|
Vector vecDir = m_vecIdeal; |
|
|
|
VectorNormalize( vecDir ); |
|
|
|
const trace_t &tr = GetTouchTrace(); |
|
|
|
float n = -DotProduct(tr.plane.normal, vecDir); |
|
|
|
vecDir = 2.0 * tr.plane.normal * n + vecDir; |
|
|
|
m_vecIdeal = vecDir * m_vecIdeal.Length(); |
|
} |
|
|
|
bool CNihilanthHVR::CircleTarget( Vector vecTarget ) |
|
{ |
|
bool fClose = false; |
|
|
|
vecTarget = vecTarget + Vector( 0, 0, 64 ); |
|
|
|
Vector vecDest = vecTarget; |
|
Vector vecEst = GetAbsOrigin() + GetAbsVelocity() * 0.5; |
|
Vector vecSrc = GetAbsOrigin(); |
|
vecDest.z = 0; |
|
vecEst.z = 0; |
|
vecSrc.z = 0; |
|
float d1 = (vecDest - vecSrc).Length() - 24 * N_SCALE; |
|
float d2 = (vecDest - vecEst).Length() - 24 * N_SCALE; |
|
|
|
if ( m_vecIdeal == vec3_origin ) |
|
{ |
|
m_vecIdeal = GetAbsVelocity(); |
|
} |
|
|
|
if (d1 < 0 && d2 <= d1) |
|
{ |
|
// ALERT( at_console, "too close\n"); |
|
Vector vTemp = (vecDest - vecSrc); |
|
|
|
VectorNormalize( vTemp ); |
|
|
|
m_vecIdeal = m_vecIdeal - vTemp * 50; |
|
} |
|
|
|
else if (d1 > 0 && d2 >= d1) |
|
{ |
|
Vector vTemp = (vecDest - vecSrc); |
|
|
|
VectorNormalize( vTemp ); |
|
|
|
m_vecIdeal = m_vecIdeal + vTemp * 50; |
|
} |
|
|
|
SetLocalAngularVelocity( QAngle( GetLocalAngularVelocity().x, GetLocalAngularVelocity().y, d1 * 20 ) ); |
|
|
|
if (d1 < 32) |
|
{ |
|
fClose = true; |
|
} |
|
|
|
m_vecIdeal = m_vecIdeal + Vector( random->RandomFloat( -2, 2 ), random->RandomFloat( -2, 2 ), random->RandomFloat( -2, 2 )); |
|
|
|
float flIdealZ = m_vecIdeal.z; |
|
|
|
m_vecIdeal = Vector( m_vecIdeal.x, m_vecIdeal.y, 0 ); |
|
|
|
VectorNormalize( m_vecIdeal ); |
|
|
|
m_vecIdeal = (m_vecIdeal * 200) + Vector( 0, 0, flIdealZ ); |
|
|
|
// move up/down |
|
d1 = vecTarget.z - GetAbsOrigin().z; |
|
if (d1 > 0 && m_vecIdeal.z < 200) |
|
m_vecIdeal.z += 200; |
|
else if (d1 < 0 && m_vecIdeal.z > -200) |
|
m_vecIdeal.z -= 200; |
|
|
|
SetAbsVelocity( m_vecIdeal ); |
|
|
|
// ALERT( at_console, "%.0f %.0f %.0f\n", m_vecIdeal.x, m_vecIdeal.y, m_vecIdeal.z ); |
|
return fClose; |
|
} |
|
|
|
|
|
void CNihilanthHVR::ZapInit( CBaseEntity *pEnemy ) |
|
{ |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
CSprite *pSprite = SpriteInit( "sprites/nhth1.vmt", this ); |
|
|
|
if ( pSprite ) |
|
{ |
|
m_flBallScale = 2.0f; |
|
pSprite->SetScale( 2.0 ); |
|
pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); |
|
} |
|
|
|
|
|
Vector vVelocity = pEnemy->GetAbsOrigin() - GetAbsOrigin(); |
|
VectorNormalize( vVelocity ); |
|
SetAbsVelocity ( vVelocity * 300 ); |
|
|
|
SetEnemy( pEnemy ); |
|
SetThink( &CNihilanthHVR::ZapThink ); |
|
SetTouch( &CNihilanthHVR::ZapTouch ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "NihilanthHVR.Zap" ); |
|
} |
|
|
|
void CNihilanthHVR::ZapThink( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.05 ); |
|
|
|
// check world boundaries |
|
if ( GetEnemy() == NULL || GetAbsOrigin().x < -4096 || GetAbsOrigin().x > 4096 || GetAbsOrigin().y < -4096 || GetAbsOrigin().y > 4096 || GetAbsOrigin().z < -4096 || GetAbsOrigin().z > 4096) |
|
{ |
|
SetTouch( NULL ); |
|
UTIL_Remove( GetSprite() ); |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
|
|
if ( GetAbsVelocity().Length() < 2000) |
|
{ |
|
SetAbsVelocity( GetAbsVelocity() * 1.2 ); |
|
} |
|
|
|
if (( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin()).Length() < 256) |
|
{ |
|
trace_t tr; |
|
|
|
UTIL_TraceLine( GetAbsOrigin(), GetEnemy()->WorldSpaceCenter(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
CBaseEntity *pEntity = tr.m_pEnt; |
|
|
|
if (pEntity != NULL && pEntity->m_takedamage ) |
|
{ |
|
ClearMultiDamage( ); |
|
CTakeDamageInfo info( this, this, sk_nihilanth_zap.GetFloat(), DMG_SHOCK ); |
|
CalculateMeleeDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos ); |
|
pEntity->DispatchTraceAttack( info, GetAbsVelocity(), &tr ); |
|
ApplyMultiDamage(); |
|
} |
|
|
|
CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 2.0f ); |
|
|
|
if ( pBeam == NULL ) |
|
return; |
|
|
|
pBeam->PointEntInit( tr.endpos, this ); |
|
pBeam->SetColor( 64, 196, 255 ); |
|
pBeam->SetBrightness( 255 ); |
|
pBeam->SetNoise( 7.2 ); |
|
pBeam->SetScrollRate( 10 ); |
|
pBeam->LiveForTime( 0.1 ); |
|
|
|
UTIL_EmitAmbientSound( GetSoundSourceIndex(), tr.endpos, "Controller.ElectroSound", 0.5, SNDLVL_NORM, 0, random->RandomInt( 140, 160 ) ); |
|
|
|
SetTouch( NULL ); |
|
GetSprite()->SetThink( &CBaseEntity::SUB_Remove ); |
|
SetThink( &CBaseEntity::SUB_Remove ); |
|
SetNextThink( gpGlobals->curtime + 0.2 ); |
|
GetSprite()->SetNextThink( gpGlobals->curtime + 0.2 ); |
|
return; |
|
} |
|
|
|
CBroadcastRecipientFilter filterlight; |
|
te->DynamicLight( filterlight, 0.0, &GetAbsOrigin(), 128, 128, 255, 0, 128, 10, 128 ); |
|
} |
|
|
|
|
|
void CNihilanthHVR::ZapTouch( CBaseEntity *pOther ) |
|
{ |
|
UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin(), "Controller.ElectroSound", 1.0, SNDLVL_NORM, 0, random->RandomInt( 90, 95 ) ); |
|
|
|
RadiusDamage( CTakeDamageInfo( this, this, 50, DMG_SHOCK ), GetAbsOrigin(), 125, CLASS_NONE, NULL ); |
|
SetAbsVelocity( GetAbsVelocity() * 0 ); |
|
|
|
SetTouch( NULL ); |
|
UTIL_Remove( GetSprite() ); |
|
UTIL_Remove( this ); |
|
SetNextThink( gpGlobals->curtime + 0.2 ); |
|
} |
|
|
|
void CNihilanthHVR::AbsorbInit( void ) |
|
{ |
|
CBroadcastRecipientFilter filter; |
|
|
|
SetThink( &CNihilanthHVR::DissipateThink ); |
|
SetRenderColorA( 255 ); |
|
|
|
SetBeam( CBeam::BeamCreate( "sprites/laserbeam.vmt", 8.0f ) ); |
|
|
|
CBeam *pBeam = (CBeam*)GetBeam(); |
|
|
|
if ( pBeam == NULL ) |
|
return; |
|
|
|
pBeam->EntsInit( this, GetTarget() ); |
|
pBeam->SetEndAttachment( 1 ); |
|
pBeam->SetColor( 255, 128, 64 ); |
|
pBeam->SetBrightness( 255 ); |
|
pBeam->SetNoise( 18 ); |
|
} |
|
|
|
void CNihilanthHVR::DissipateThink( void ) |
|
{ |
|
CSprite *pSprite = (CSprite*)GetSprite(); |
|
|
|
SetNextThink ( gpGlobals->curtime + 0.1 ); |
|
|
|
if ( m_flBallScale > 5.0) |
|
{ |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
UTIL_Remove( GetBeam() ); |
|
} |
|
|
|
pSprite->SetBrightness( pSprite->GetBrightness() - 7, 0 ); |
|
|
|
m_flBallScale += 0.1; |
|
pSprite->SetScale( m_flBallScale ); |
|
|
|
if ( GetTarget() != NULL) |
|
{ |
|
CircleTarget( GetTarget()->GetAbsOrigin() + Vector( 0, 0, 4096 ) ); |
|
} |
|
else |
|
{ |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
UTIL_Remove( GetBeam() ); |
|
} |
|
|
|
/* MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); |
|
WRITE_BYTE( TE_ELIGHT ); |
|
WRITE_SHORT( entindex( ) ); // entity, attachment |
|
WRITE_COORD( pev->origin.x ); // origin |
|
WRITE_COORD( pev->origin.y ); |
|
WRITE_COORD( pev->origin.z ); |
|
WRITE_COORD( pev->renderamt ); // radius |
|
WRITE_BYTE( 255 ); // R |
|
WRITE_BYTE( 192 ); // G |
|
WRITE_BYTE( 64 ); // B |
|
WRITE_BYTE( 2 ); // life * 10 |
|
WRITE_COORD( 0 ); // decay |
|
MESSAGE_END();*/ |
|
} |
|
|
|
void CNihilanthHVR::TeleportInit( CNPC_Nihilanth *pOwner, CBaseEntity *pEnemy, CBaseEntity *pTarget, CBaseEntity *pTouch ) |
|
{ |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
SetModel( "" ); |
|
|
|
CSprite *pSprite = SpriteInit( "sprites/exit1.vmt", this ); |
|
|
|
if ( pSprite ) |
|
{ |
|
m_flBallScale = 2.0f; |
|
pSprite->SetScale( 2.0 ); |
|
pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); |
|
} |
|
|
|
|
|
m_pNihilanth = pOwner; |
|
SetEnemy( pEnemy ); |
|
SetTarget( pTarget ); |
|
m_hTouch = pTouch; |
|
|
|
SetThink( &CNihilanthHVR::TeleportThink ); |
|
SetTouch( &CNihilanthHVR::TeleportTouch ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "NihilanthHVR.TeleAttack" ); |
|
} |
|
|
|
void CNihilanthHVR::MovetoTarget( Vector vecTarget ) |
|
{ |
|
if ( m_vecIdeal == vec3_origin ) |
|
{ |
|
m_vecIdeal = GetAbsVelocity(); |
|
} |
|
|
|
// accelerate |
|
float flSpeed = m_vecIdeal.Length(); |
|
|
|
if (flSpeed > 300) |
|
{ |
|
VectorNormalize( m_vecIdeal ); |
|
m_vecIdeal= m_vecIdeal * 300; |
|
} |
|
|
|
Vector vTemp = vecTarget - GetAbsOrigin(); |
|
VectorNormalize( vTemp ); |
|
|
|
m_vecIdeal = m_vecIdeal + vTemp * 300; |
|
SetAbsVelocity( m_vecIdeal ); |
|
} |
|
|
|
void CNihilanthHVR::TeleportThink( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
// check world boundaries |
|
if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() || GetAbsOrigin().x < -4096 || GetAbsOrigin().x > 4096 || GetAbsOrigin().y < -4096 || GetAbsOrigin().y > 4096 || GetAbsOrigin().z < -4096 || GetAbsOrigin().z > 4096) |
|
{ |
|
StopSound( entindex(), "NihilanthHVR.TeleAttack" ); |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
return; |
|
} |
|
|
|
if (( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin() ).Length() < 128) |
|
{ |
|
StopSound( entindex(), "NihilanthHVR.TeleAttack" ); |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
|
|
if ( GetTarget() != NULL) |
|
{ |
|
CLogicRelay *pRelay = (CLogicRelay*)GetTarget(); |
|
pRelay->m_OnTrigger.FireOutput( this, this ); |
|
} |
|
|
|
if ( m_hTouch != NULL && GetEnemy() != NULL ) |
|
m_hTouch->Touch( GetEnemy() ); |
|
} |
|
else |
|
{ |
|
MovetoTarget( GetEnemy()->WorldSpaceCenter( ) ); |
|
} |
|
|
|
CBroadcastRecipientFilter filterlight; |
|
te->DynamicLight( filterlight, 0.0, &GetAbsOrigin(), 0, 255, 0, 0, 256, 1.0, 256 ); |
|
} |
|
|
|
void CNihilanthHVR::TeleportTouch( CBaseEntity *pOther ) |
|
{ |
|
CBaseEntity *pEnemy = GetEnemy(); |
|
|
|
if (pOther == pEnemy) |
|
{ |
|
if (GetTarget() != NULL) |
|
{ |
|
if ( GetTarget() != NULL) |
|
{ |
|
CLogicRelay *pRelay = (CLogicRelay*)GetTarget(); |
|
pRelay->m_OnTrigger.FireOutput( this, this ); |
|
} |
|
} |
|
|
|
if (m_hTouch != NULL && pEnemy != NULL ) |
|
m_hTouch->Touch( pEnemy ); |
|
} |
|
else |
|
{ |
|
m_pNihilanth->MakeFriend( GetAbsOrigin() ); |
|
} |
|
|
|
SetTouch( NULL ); |
|
StopSound( entindex(), "NihilanthHVR.TeleAttack" ); |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
} |
|
|
|
void CNihilanthHVR::GreenBallInit( ) |
|
{ |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
SetModel( "" ); |
|
|
|
CSprite *pSprite = SpriteInit( "sprites/exit1.spr", this ); |
|
|
|
if ( pSprite ) |
|
{ |
|
pSprite->SetScale( 1.0 ); |
|
pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); |
|
} |
|
|
|
SetTouch( &CNihilanthHVR::RemoveTouch ); |
|
} |
|
|
|
void CNihilanthHVR::RemoveTouch( CBaseEntity *pOther ) |
|
{ |
|
StopSound( entindex(), "NihilanthHVR.TeleAttack" ); |
|
UTIL_Remove( this ); |
|
UTIL_Remove( GetSprite() ); |
|
}
|
|
|