summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_snark.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_snark.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_snark.cpp')
-rw-r--r--game/server/hl1/hl1_npc_snark.cpp527
1 files changed, 527 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_snark.cpp b/game/server/hl1/hl1_npc_snark.cpp
new file mode 100644
index 0000000..cb33749
--- /dev/null
+++ b/game/server/hl1/hl1_npc_snark.cpp
@@ -0,0 +1,527 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Projectile shot from the MP5
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include "cbase.h"
+#include "soundent.h"
+#include "engine/IEngineSound.h"
+#include "ai_senses.h"
+#include "hl1_npc_snark.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+
+ConVar sk_snark_health ( "sk_snark_health", "0" );
+ConVar sk_snark_dmg_bite ( "sk_snark_dmg_bite", "0" );
+ConVar sk_snark_dmg_pop ( "sk_snark_dmg_pop", "0" );
+
+
+LINK_ENTITY_TO_CLASS( monster_snark, CSnark);
+
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CSnark )
+ DEFINE_FIELD( m_flDie, FIELD_TIME ),
+ DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flNextHunt, FIELD_TIME ),
+ DEFINE_FIELD( m_flNextHit, FIELD_TIME ),
+ DEFINE_FIELD( m_posPrev, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iMyClass, FIELD_INTEGER ),
+
+ DEFINE_ENTITYFUNC( SuperBounceTouch ),
+ DEFINE_THINKFUNC( HuntThink ),
+END_DATADESC()
+
+
+#define SQUEEK_DETONATE_DELAY 15.0
+#define SNARK_EXPLOSION_VOLUME 512
+
+
+enum w_squeak_e {
+ WSQUEAK_IDLE1 = 0,
+ WSQUEAK_FIDGET,
+ WSQUEAK_JUMP,
+ WSQUEAK_RUN,
+};
+
+float CSnark::m_flNextBounceSoundTime = 0;
+
+void CSnark::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( "models/w_squeak2.mdl" );
+
+ PrecacheScriptSound( "Snark.Die" );
+ PrecacheScriptSound( "Snark.Gibbed" );
+ PrecacheScriptSound( "Snark.Squeak" );
+ PrecacheScriptSound( "Snark.Deploy" );
+ PrecacheScriptSound( "Snark.Bounce" );
+
+}
+
+
+void CSnark::Spawn( void )
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ SetFriction(1.0);
+
+ SetModel( "models/w_squeak2.mdl" );
+ UTIL_SetSize( this, Vector( -4, -4, 0 ), Vector( 4, 4, 8 ) );
+
+ SetBloodColor( BLOOD_COLOR_YELLOW );
+
+ SetTouch( &CSnark::SuperBounceTouch );
+ SetThink( &CSnark::HuntThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ m_flNextHit = gpGlobals->curtime;
+ m_flNextHunt = gpGlobals->curtime + 1E6;
+ m_flNextBounceSoundTime = gpGlobals->curtime;
+
+ AddFlag( FL_AIMTARGET | FL_NPC );
+ m_takedamage = DAMAGE_YES;
+
+ m_iHealth = sk_snark_health.GetFloat();
+ m_iMaxHealth = m_iHealth;
+
+ SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for snarks
+ SetFriction( 0.5 );
+
+ SetDamage( sk_snark_dmg_pop.GetFloat() );
+
+ m_flDie = gpGlobals->curtime + SQUEEK_DETONATE_DELAY;
+
+ m_flFieldOfView = 0; // 180 degrees
+
+ if ( GetOwnerEntity() )
+ m_hOwner = GetOwnerEntity();
+
+ m_flNextBounceSoundTime = gpGlobals->curtime;// reset each time a snark is spawned.
+
+ SetSequence( WSQUEAK_RUN );
+ ResetSequenceInfo( );
+
+ m_iMyClass = CLASS_NONE;
+
+ m_posPrev = Vector( 0, 0, 0 );
+}
+
+
+Class_T CSnark::Classify( void )
+{
+ if ( m_iMyClass != CLASS_NONE )
+ return m_iMyClass; // protect against recursion
+
+ if ( GetEnemy() != NULL )
+ {
+ m_iMyClass = CLASS_INSECT; // no one cares about it
+ switch( GetEnemy()->Classify( ) )
+ {
+ case CLASS_PLAYER:
+ case CLASS_HUMAN_PASSIVE:
+ case CLASS_HUMAN_MILITARY:
+ m_iMyClass = CLASS_NONE;
+ return CLASS_ALIEN_MILITARY; // barney's get mad, grunts get mad at it
+ }
+ m_iMyClass = CLASS_NONE;
+ }
+
+ return CLASS_ALIEN_BIOWEAPON;
+}
+
+
+void CSnark::Event_Killed( const CTakeDamageInfo &inputInfo )
+{
+// pev->model = iStringNull;// make invisible
+ SetThink( &CSnark::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ SetTouch( NULL );
+
+ // since squeak grenades never leave a body behind, clear out their takedamage now.
+ // Squeaks do a bit of radius damage when they pop, and that radius damage will
+ // continue to call this function unless we acknowledge the Squeak's death now. (sjb)
+ m_takedamage = DAMAGE_NO;
+
+ // play squeek blast
+ CPASAttenuationFilter filter( this, 0.5 );
+ EmitSound( filter, entindex(), "Snark.Die" );
+
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SNARK_EXPLOSION_VOLUME, 3.0 );
+
+ UTIL_BloodDrips( WorldSpaceCenter(), Vector( 0, 0, 0 ), BLOOD_COLOR_YELLOW, 80 );
+
+ if ( m_hOwner != NULL )
+ {
+ RadiusDamage( CTakeDamageInfo( this, m_hOwner, GetDamage(), DMG_BLAST ), GetAbsOrigin(), GetDamage() * 2.5, CLASS_NONE, NULL );
+ }
+ else
+ {
+ RadiusDamage( CTakeDamageInfo( this, this, GetDamage(), DMG_BLAST ), GetAbsOrigin(), GetDamage() * 2.5, CLASS_NONE, NULL );
+ }
+
+ // reset owner so death message happens
+ if ( m_hOwner != NULL )
+ SetOwnerEntity( m_hOwner );
+
+ CTakeDamageInfo info = inputInfo;
+ int iGibDamage = g_pGameRules->Damage_GetShouldGibCorpse();
+ info.SetDamageType( iGibDamage );
+
+ BaseClass::Event_Killed( info );
+}
+
+
+bool CSnark::Event_Gibbed( const CTakeDamageInfo &info )
+{
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Snark.Gibbed" );
+
+ return BaseClass::Event_Gibbed( info );
+}
+
+
+void CSnark::HuntThink( void )
+{
+ if (!IsInWorld())
+ {
+ SetTouch( NULL );
+ UTIL_Remove( this );
+ return;
+ }
+
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ //FIXME: There's a problem in this movetype that causes it to set a ground entity but never recheck to clear it
+ // For now, we stomp it clear and force it to revalidate -- jdw
+
+ SetGroundEntity( NULL );
+ PhysicsStepRecheckGround();
+
+ // explode when ready
+ if ( gpGlobals->curtime >= m_flDie )
+ {
+ g_vecAttackDir = GetAbsVelocity();
+ VectorNormalize( g_vecAttackDir );
+ m_iHealth = -1;
+ CTakeDamageInfo info( this, this, 1, DMG_GENERIC );
+ Event_Killed( info );
+ return;
+ }
+
+ // float
+ if ( GetWaterLevel() != 0)
+ {
+ if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
+ {
+ SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM );
+ }
+
+ Vector vecVel = GetAbsVelocity();
+ vecVel *= 0.9;
+ vecVel.z += 8.0;
+ SetAbsVelocity( vecVel );
+ }
+ else if ( GetMoveType() == MOVETYPE_FLY )
+ {
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ }
+
+ // return if not time to hunt
+ if ( m_flNextHunt > gpGlobals->curtime )
+ return;
+
+ m_flNextHunt = gpGlobals->curtime + 2.0;
+
+ Vector vecFlat = GetAbsVelocity();
+ vecFlat.z = 0;
+ VectorNormalize( vecFlat );
+
+ if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() )
+ {
+ // find target, bounce a bit towards it.
+ GetSenses()->Look( 1024 );
+ SetEnemy( BestEnemy() );
+ }
+
+ // squeek if it's about time blow up
+ if ( (m_flDie - gpGlobals->curtime <= 0.5) && (m_flDie - gpGlobals->curtime >= 0.3) )
+ {
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Snark.Squeak" );
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 );
+ }
+
+ // higher pitch as squeeker gets closer to detonation time
+ float flpitch = 155.0 - 60.0 * ( (m_flDie - gpGlobals->curtime) / SQUEEK_DETONATE_DELAY );
+ if ( flpitch < 80 )
+ flpitch = 80;
+
+ if ( GetEnemy() != NULL )
+ {
+ if ( FVisible( GetEnemy() ) )
+ {
+ m_vecTarget = GetEnemy()->EyePosition() - GetAbsOrigin();
+ VectorNormalize( m_vecTarget );
+ }
+
+ float flVel = GetAbsVelocity().Length();
+ float flAdj = 50.0 / ( flVel + 10.0 );
+
+ if ( flAdj > 1.2 )
+ flAdj = 1.2;
+
+ // ALERT( at_console, "think : enemy\n");
+
+ // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z );
+
+ SetAbsVelocity( GetAbsVelocity() * flAdj + (m_vecTarget * 300) );
+ }
+
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
+ }
+ else
+ {
+ QAngle angVel = GetLocalAngularVelocity();
+ if ( angVel == QAngle( 0, 0, 0 ) )
+ {
+ angVel.x = random->RandomFloat( -100, 100 );
+ angVel.z = random->RandomFloat( -100, 100 );
+ SetLocalAngularVelocity( angVel );
+ }
+ }
+
+ if ( ( GetAbsOrigin() - m_posPrev ).Length() < 1.0 )
+ {
+ Vector vecVel = GetAbsVelocity();
+ vecVel.x = random->RandomFloat( -100, 100 );
+ vecVel.y = random->RandomFloat( -100, 100 );
+ SetAbsVelocity( vecVel );
+ }
+
+ m_posPrev = GetAbsOrigin();
+
+ QAngle angles;
+ VectorAngles( GetAbsVelocity(), angles );
+ angles.z = 0;
+ angles.x = 0;
+ SetAbsAngles( angles );
+}
+
+unsigned int CSnark::PhysicsSolidMaskForEntity( void ) const
+{
+ unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
+
+ iMask &= ~CONTENTS_MONSTERCLIP;
+
+ return iMask;
+}
+
+
+// Custom collision that provides a good bounce when we hit walls
+// and also gives gravity velocity so the snarks fall off of edges.
+void CSnark::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
+{
+ // Get the impact surface's friction.
+ float flSurfaceFriction;
+ physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, &flSurfaceFriction, NULL );
+
+ Vector vecAbsVelocity = GetAbsVelocity();
+
+ // If we hit a wall
+ if ( trace.plane.normal.z <= 0.7 ) // Floor
+ {
+ Vector vecDir = vecAbsVelocity;
+
+ float speed = vecDir.Length();
+
+ VectorNormalize( vecDir );
+
+ float hitDot = DotProduct( trace.plane.normal, -vecDir );
+
+ Vector vReflection = 2.0f * trace.plane.normal * hitDot + vecDir;
+
+ SetAbsVelocity( vReflection * speed * 0.6f );
+
+ return;
+ }
+
+ // Stop if on ground.
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ if ( vecVelocity.z < ( 800 * gpGlobals->frametime ) )
+ {
+ vecAbsVelocity.z = 0.0f;
+
+ // Recompute speedsqr based on the new absvel
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+ }
+ SetAbsVelocity( vecAbsVelocity );
+
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ vecAbsVelocity += GetBaseVelocity();
+ vecAbsVelocity *= ( 1.0f - trace.fraction ) * gpGlobals->frametime * flSurfaceFriction;
+ PhysicsPushEntity( vecAbsVelocity, &trace );
+ }
+}
+
+void CSnark::SuperBounceTouch( CBaseEntity *pOther )
+{
+ float flpitch;
+ trace_t tr;
+ tr = CBaseEntity::GetTouchTrace( );
+
+ // don't hit the guy that launched this grenade
+ if ( GetOwnerEntity() && ( pOther == GetOwnerEntity() ) )
+ return;
+
+ // at least until we've bounced once
+ SetOwnerEntity( NULL );
+
+ QAngle angles = GetAbsAngles();
+ angles.x = 0;
+ angles.z = 0;
+ SetAbsAngles( angles );
+
+ // avoid bouncing too much
+ if ( m_flNextHit > gpGlobals->curtime)
+ return;
+
+ // higher pitch as squeeker gets closer to detonation time
+ flpitch = 155.0 - 60.0 * ( ( m_flDie - gpGlobals->curtime ) / SQUEEK_DETONATE_DELAY );
+
+ if ( pOther->m_takedamage && m_flNextAttack < gpGlobals->curtime )
+ {
+ // attack!
+
+ // make sure it's me who has touched them
+ if ( tr.m_pEnt == pOther )
+ {
+ // and it's not another squeakgrenade
+ if ( tr.m_pEnt->GetModelIndex() != GetModelIndex() )
+ {
+ // ALERT( at_console, "hit enemy\n");
+ ClearMultiDamage();
+
+ Vector vecForward;
+ AngleVectors( GetAbsAngles(), &vecForward );
+
+ if ( m_hOwner != NULL )
+ {
+ CTakeDamageInfo info( this, m_hOwner, sk_snark_dmg_bite.GetFloat(), DMG_SLASH );
+ CalculateMeleeDamageForce( &info, vecForward, tr.endpos );
+ pOther->DispatchTraceAttack( info, vecForward, &tr );
+ }
+ else
+ {
+ CTakeDamageInfo info( this, this, sk_snark_dmg_bite.GetFloat(), DMG_SLASH );
+ CalculateMeleeDamageForce( &info, vecForward, tr.endpos );
+ pOther->DispatchTraceAttack( info, vecForward, &tr );
+ }
+
+ ApplyMultiDamage();
+
+ SetDamage( GetDamage() + sk_snark_dmg_pop.GetFloat() ); // add more explosion damage
+ // m_flDie += 2.0; // add more life
+
+ // make bite sound
+ CPASAttenuationFilter filter( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "Snark.Deploy", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = (int)flpitch;
+
+ EmitSound( filter, entindex(), ep );
+ }
+ m_flNextAttack = gpGlobals->curtime + 0.5;
+ }
+ }
+ else
+ {
+ // ALERT( at_console, "been hit\n");
+ }
+ }
+
+ m_flNextHit = gpGlobals->curtime + 0.1;
+ m_flNextHunt = gpGlobals->curtime;
+
+ if ( g_pGameRules->IsMultiplayer() )
+ {
+ // in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows.
+ if ( gpGlobals->curtime < m_flNextBounceSoundTime )
+ {
+ // too soon!
+ return;
+ }
+ }
+
+ if ( !( GetFlags() & FL_ONGROUND ) )
+ {
+ // play bounce sound
+ CPASAttenuationFilter filter2( this );
+
+ CSoundParameters params;
+ if ( GetParametersForSound( "Snark.Bounce", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = (int)flpitch;
+
+ EmitSound( filter2, entindex(), ep );
+ }
+
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 );
+ }
+ else
+ {
+ // skittering sound
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 100, 0.1 );
+ }
+
+ m_flNextBounceSoundTime = gpGlobals->curtime + 0.5;// half second.
+}
+
+
+bool CSnark::IsValidEnemy( CBaseEntity *pEnemy )
+{
+ return CHL1BaseNPC::IsValidEnemy( pEnemy );
+}
+