summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_hornet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/hl1/hl1_npc_hornet.cpp')
-rw-r--r--game/server/hl1/hl1_npc_hornet.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_hornet.cpp b/game/server/hl1/hl1_npc_hornet.cpp
new file mode 100644
index 0000000..d2b09ee
--- /dev/null
+++ b/game/server/hl1/hl1_npc_hornet.cpp
@@ -0,0 +1,400 @@
+//========= 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 "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 "te.h"
+#include "hl1_npc_hornet.h"
+
+int iHornetTrail;
+int iHornetPuff;
+
+LINK_ENTITY_TO_CLASS( hornet, CNPC_Hornet );
+
+extern ConVar sk_npc_dmg_hornet;
+extern ConVar sk_plr_dmg_hornet;
+
+BEGIN_DATADESC( CNPC_Hornet )
+ DEFINE_FIELD( m_flStopAttack, FIELD_TIME ),
+ DEFINE_FIELD( m_iHornetType, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flFlySpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flDamage, FIELD_INTEGER ),
+ DEFINE_FIELD( m_vecEnemyLKP, FIELD_POSITION_VECTOR ),
+
+
+ DEFINE_ENTITYFUNC( DieTouch ),
+ DEFINE_THINKFUNC( StartDart ),
+ DEFINE_THINKFUNC( StartTrack ),
+ DEFINE_ENTITYFUNC( DartTouch ),
+ DEFINE_ENTITYFUNC( TrackTouch ),
+ DEFINE_THINKFUNC( TrackTarget ),
+END_DATADESC()
+
+//=========================================================
+//=========================================================
+void CNPC_Hornet::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLY );
+ SetSolid( SOLID_BBOX );
+ m_takedamage = DAMAGE_YES;
+ AddFlag( FL_NPC );
+ m_iHealth = 1;// weak!
+ m_bloodColor = DONT_BLEED;
+
+ if ( g_pGameRules->IsMultiplayer() )
+ {
+ // hornets don't live as long in multiplayer
+ m_flStopAttack = gpGlobals->curtime + 3.5;
+ }
+ else
+ {
+ m_flStopAttack = gpGlobals->curtime + 5.0;
+ }
+
+ m_flFieldOfView = 0.9; // +- 25 degrees
+
+ if ( random->RandomInt ( 1, 5 ) <= 2 )
+ {
+ m_iHornetType = HORNET_TYPE_RED;
+ m_flFlySpeed = HORNET_RED_SPEED;
+ }
+ else
+ {
+ m_iHornetType = HORNET_TYPE_ORANGE;
+ m_flFlySpeed = HORNET_ORANGE_SPEED;
+ }
+
+ SetModel( "models/hornet.mdl" );
+ UTIL_SetSize( this, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
+
+ SetTouch( &CNPC_Hornet::DieTouch );
+ SetThink( &CNPC_Hornet::StartTrack );
+
+ if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
+ {
+ m_flDamage = sk_plr_dmg_hornet.GetFloat();
+ }
+ else
+ {
+ // no real owner, or owner isn't a client.
+ m_flDamage = sk_npc_dmg_hornet.GetFloat();
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ ResetSequenceInfo();
+
+ m_vecEnemyLKP = vec3_origin;
+}
+
+
+void CNPC_Hornet::Precache()
+{
+ PrecacheModel("models/hornet.mdl");
+
+ iHornetPuff = PrecacheModel( "sprites/muz1.vmt" );
+ iHornetTrail = PrecacheModel("sprites/laserbeam.vmt");
+
+ PrecacheScriptSound( "Hornet.Die" );
+ PrecacheScriptSound( "Hornet.Buzz" );
+}
+
+//=========================================================
+// hornets will never get mad at each other, no matter who the owner is.
+//=========================================================
+Disposition_t CNPC_Hornet::IRelationType( CBaseEntity *pTarget )
+{
+ if ( pTarget->GetModelIndex() == GetModelIndex() )
+ {
+ return D_NU;
+ }
+
+ return BaseClass::IRelationType( pTarget );
+}
+
+//=========================================================
+// ID's Hornet as their owner
+//=========================================================
+Class_T CNPC_Hornet::Classify ( void )
+{
+ if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
+ {
+ return CLASS_PLAYER_BIOWEAPON;
+ }
+
+ return CLASS_ALIEN_BIOWEAPON;
+}
+
+//=========================================================
+// StartDart - starts a hornet out just flying straight.
+//=========================================================
+void CNPC_Hornet::StartDart ( void )
+{
+ IgniteTrail();
+
+ SetTouch( &CNPC_Hornet::DartTouch );
+
+ SetThink( &CBaseEntity::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 4 );
+}
+
+
+void CNPC_Hornet::DieTouch ( CBaseEntity *pOther )
+{
+ if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ {
+ return;
+ }
+
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Hornet.Die" );
+
+ CTakeDamageInfo info( this, GetOwnerEntity(), m_flDamage, DMG_BULLET );
+ CalculateBulletDamageForce( &info, GetAmmoDef()->Index("Hornet"), GetAbsVelocity(), GetAbsOrigin() );
+ pOther->TakeDamage( info );
+
+ m_takedamage = DAMAGE_NO;
+
+ AddEffects( EF_NODRAW );
+
+ AddSolidFlags( FSOLID_NOT_SOLID );// intangible
+
+ UTIL_Remove( this );
+ SetTouch( NULL );
+}
+
+
+//=========================================================
+// StartTrack - starts a hornet out tracking its target
+//=========================================================
+void CNPC_Hornet:: StartTrack ( void )
+{
+ IgniteTrail();
+
+ SetTouch( &CNPC_Hornet::TrackTouch );
+ SetThink( &CNPC_Hornet::TrackTarget );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+void TE_BeamFollow( IRecipientFilter& filter, float delay,
+ int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth,
+ float fadeLength,float r, float g, float b, float a );
+
+void CNPC_Hornet::IgniteTrail( void )
+{
+ Vector vColor;
+
+ if ( m_iHornetType == HORNET_TYPE_RED )
+ vColor = Vector ( 179, 39, 14 );
+ else
+ vColor = Vector ( 255, 128, 0 );
+
+ CBroadcastRecipientFilter filter;
+ TE_BeamFollow( filter, 0.0,
+ entindex(),
+ iHornetTrail,
+ 0,
+ 1,
+ 2,
+ 0.5,
+ 0.5,
+ vColor.x,
+ vColor.y,
+ vColor.z,
+ 128 );
+}
+
+
+unsigned int CNPC_Hornet::PhysicsSolidMaskForEntity( void ) const
+{
+ unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
+
+ iMask &= ~CONTENTS_MONSTERCLIP;
+
+ return iMask;
+}
+
+//=========================================================
+// Tracking Hornet hit something
+//=========================================================
+void CNPC_Hornet::TrackTouch ( CBaseEntity *pOther )
+{
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ {
+ return;
+ }
+
+ if ( pOther == GetOwnerEntity() || pOther->GetModelIndex() == GetModelIndex() )
+ {// bumped into the guy that shot it.
+ //SetSolid( SOLID_NOT );
+ return;
+ }
+
+ int nRelationship = IRelationType( pOther );
+ if ( (nRelationship == D_FR || nRelationship == D_NU || nRelationship == D_LI) )
+ {
+ // hit something we don't want to hurt, so turn around.
+ Vector vecVel = GetAbsVelocity();
+
+ VectorNormalize( vecVel );
+
+ vecVel.x *= -1;
+ vecVel.y *= -1;
+
+ SetAbsOrigin( GetAbsOrigin() + vecVel * 4 ); // bounce the hornet off a bit.
+ SetAbsVelocity( vecVel * m_flFlySpeed );
+
+ return;
+ }
+
+ DieTouch( pOther );
+}
+
+void CNPC_Hornet::DartTouch( CBaseEntity *pOther )
+{
+ DieTouch( pOther );
+}
+
+//=========================================================
+// Hornet is flying, gently tracking target
+//=========================================================
+void CNPC_Hornet::TrackTarget ( void )
+{
+ Vector vecFlightDir;
+ Vector vecDirToEnemy;
+ float flDelta;
+
+ StudioFrameAdvance( );
+
+ if (gpGlobals->curtime > m_flStopAttack)
+ {
+ SetTouch( NULL );
+ SetThink( &CBaseEntity::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ return;
+ }
+
+ // UNDONE: The player pointer should come back after returning from another level
+ if ( GetEnemy() == NULL )
+ {// enemy is dead.
+ GetSenses()->Look( 1024 );
+ SetEnemy( BestEnemy() );
+ }
+
+ if ( GetEnemy() != NULL && FVisible( GetEnemy() ))
+ {
+ m_vecEnemyLKP = GetEnemy()->BodyTarget( GetAbsOrigin() );
+ }
+ else
+ {
+ m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1;
+ }
+
+ vecDirToEnemy = m_vecEnemyLKP - GetAbsOrigin();
+ VectorNormalize( vecDirToEnemy );
+
+ if ( GetAbsVelocity().Length() < 0.1 )
+ vecFlightDir = vecDirToEnemy;
+ else
+ {
+ vecFlightDir = GetAbsVelocity();
+ VectorNormalize( vecFlightDir );
+ }
+
+ SetAbsVelocity( vecFlightDir + vecDirToEnemy );
+
+ // measure how far the turn is, the wider the turn, the slow we'll go this time.
+ flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
+
+ if ( flDelta < 0.5 )
+ {// hafta turn wide again. play sound
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Hornet.Buzz" );
+ }
+
+ if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
+ {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
+ flDelta = 0.25;
+ }
+
+ Vector vecVel = vecFlightDir + vecDirToEnemy;
+ VectorNormalize( vecVel );
+
+ if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_NPC) )
+ {
+ // random pattern only applies to hornets fired by monsters, not players.
+
+ vecVel.x += random->RandomFloat ( -0.10, 0.10 );// scramble the flight dir a bit.
+ vecVel.y += random->RandomFloat ( -0.10, 0.10 );
+ vecVel.z += random->RandomFloat ( -0.10, 0.10 );
+ }
+
+ switch ( m_iHornetType )
+ {
+ case HORNET_TYPE_RED:
+ SetAbsVelocity( vecVel * ( m_flFlySpeed * flDelta ) );// scale the dir by the ( speed * width of turn )
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
+ break;
+ default:
+ Assert( false ); //fall through if release
+ case HORNET_TYPE_ORANGE:
+ SetAbsVelocity( vecVel * m_flFlySpeed );// do not have to slow down to turn.
+ SetNextThink( gpGlobals->curtime + 0.1f );// fixed think time
+ break;
+ }
+
+ QAngle angNewAngles;
+ VectorAngles( GetAbsVelocity(), angNewAngles );
+ SetAbsAngles( angNewAngles );
+
+ SetSolid( SOLID_BBOX );
+
+ // if hornet is close to the enemy, jet in a straight line for a half second.
+ // (only in the single player game)
+ if ( GetEnemy() != NULL && !g_pGameRules->IsMultiplayer() )
+ {
+ if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 )
+ {
+ CPVSFilter filter( GetAbsOrigin() );
+ te->Sprite( filter, 0.0,
+ &GetAbsOrigin(), // pos
+ iHornetPuff, // model
+ 0.2, //size
+ 128 // brightness
+ );
+
+ CPASAttenuationFilter filter2( this );
+ EmitSound( filter2, entindex(), "Hornet.Buzz" );
+ SetAbsVelocity( GetAbsVelocity() * 2 );
+ SetNextThink( gpGlobals->curtime + 1.0f );
+ // don't attack again
+ m_flStopAttack = gpGlobals->curtime;
+ }
+ }
+}