diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_snark.cpp | |
| download | archived-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.cpp | 527 |
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 ); +} + |