1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-05 17:13:36 +08:00
hl2sdk/game/client/swarm/c_asw_simple_alien.cpp
2010-07-22 01:46:14 -05:00

580 lines
17 KiB
C++

#include "cbase.h"
#include "c_asw_alien.h"
#include "c_asw_simple_alien.h"
#include "eventlist.h"
#include "decals.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "c_asw_generic_emitter_entity.h"
#include "c_asw_marine_resource.h"
#include "c_asw_marine.h"
#include "c_asw_game_resource.h"
#include "asw_shareddefs.h"
#include "tier0/vprof.h"
#include "datacache/imdlcache.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar asw_drone_gib_time_min;
extern ConVar asw_drone_gib_time_max;
extern ConVar asw_directional_shadows;
extern ConVar asw_alien_footstep_interval;
IMPLEMENT_CLIENTCLASS_DT(C_ASW_Simple_Alien, DT_ASW_Simple_Alien, CASW_Simple_Alien)
END_RECV_TABLE()
C_ASW_Simple_Alien::C_ASW_Simple_Alien()
: m_GlowObject( this )
{
m_bStepSideLeft = false;
m_nLastSetModel = 0;
m_vecLastRenderedPos = vec3_origin;
m_GlowObject.SetColor( Vector( 0.3f, 0.6f, 0.1f ) );
m_GlowObject.SetAlpha( 0.55f );
m_GlowObject.SetRenderFlags( false, false );
m_GlowObject.SetFullBloomRender( true );
}
void C_ASW_Simple_Alien::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
{
if (event == AE_ASW_FOOTSTEP || event == AE_MARINE_FOOTSTEP)
{
Vector vel;
EstimateAbsVelocity( vel );
surfacedata_t *pSurface = GetGroundSurface();
if (pSurface)
AlienStepSound( pSurface, GetAbsOrigin(), vel );
}
BaseClass::FireEvent(origin, angles, event, options);
}
void C_ASW_Simple_Alien::AlienStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
{
int fWalking;
float fvol;
Vector knee;
Vector feet;
float height;
float speed;
float velrun;
float velwalk;
float flduck;
int fLadder;
if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
return;
if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
return;
if ( gpGlobals->curtime - sm_flLastFootstepTime < asw_alien_footstep_interval.GetFloat() )
return;
speed = VectorLength( vecVelocity );
float groundspeed = Vector2DLength( vecVelocity.AsVector2D() );
// determine if we are on a ladder
fLadder = ( GetMoveType() == MOVETYPE_LADDER );
// UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!!
if ( ( GetFlags() & FL_DUCKING) || fLadder )
{
velwalk = 60; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow
velrun = 80;
flduck = 100;
}
else
{
velwalk = 90;
velrun = 220;
flduck = 0;
}
bool onground = true; //( GetFlags() & FL_ONGROUND );
bool movingalongground = ( groundspeed > 0.0f );
bool moving_fast_enough = ( speed >= velwalk );
// To hear step sounds you must be either on a ladder or moving along the ground AND
// You must be moving fast enough
//Msg("og=%d ma=%d mf=%d\n", onground, movingalongground, moving_fast_enough);
if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) )
return;
// MoveHelper()->PlayerSetAnimation( PLAYER_WALK );
fWalking = speed < velrun;
VectorCopy( vecOrigin, knee );
VectorCopy( vecOrigin, feet );
height = 72.0f; // bad
knee[2] = vecOrigin[2] + 0.2 * height;
// find out what we're stepping in or on...
if ( fLadder )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) );
fvol = 0.5;
}
else if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
{
static int iSkipStep = 0;
if ( iSkipStep == 0 )
{
iSkipStep++;
return;
}
if ( iSkipStep++ == 3 )
{
iSkipStep = 0;
}
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) );
fvol = 0.65;
}
else if ( enginetrace->GetPointContents( feet ) & MASK_WATER )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) );
fvol = fWalking ? 0.2 : 0.5;
}
else
{
if ( !psurface )
return;
switch ( psurface->game.material )
{
default:
case CHAR_TEX_CONCRETE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_METAL:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_DIRT:
fvol = fWalking ? 0.25 : 0.55;
break;
case CHAR_TEX_VENT:
fvol = fWalking ? 0.4 : 0.7;
break;
case CHAR_TEX_GRATE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_TILE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_SLOSH:
fvol = fWalking ? 0.2 : 0.5;
break;
}
}
// play the sound
// 65% volume if ducking
if ( GetFlags() & FL_DUCKING )
{
fvol *= 0.65;
}
PlayStepSound( feet, psurface, fvol, false );
}
surfacedata_t* C_ASW_Simple_Alien::GetGroundSurface()
{
//
// Find the name of the material that lies beneath the player.
//
Vector start, end;
VectorCopy( GetAbsOrigin(), start );
VectorCopy( start, end );
// Straight down
end.z -= 38; // was 64
// Fill in default values, just in case.
Ray_t ray;
ray.Init( start, end, GetCollideable()->OBBMins(), GetCollideable()->OBBMaxs() );
trace_t trace;
UTIL_TraceRay( ray, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NPC, &trace );
if ( trace.fraction == 1.0f )
return NULL; // no ground
return physprops->GetSurfaceData( trace.surface.surfaceProps );
}
void C_ASW_Simple_Alien::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
if ( !psurface )
return;
unsigned short stepSoundName = m_bStepSideLeft ? psurface->sounds.runStepLeft : psurface->sounds.runStepRight;
m_bStepSideLeft = !m_bStepSideLeft;
if ( !stepSoundName )
return;
//IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
const char *pSoundName = physprops->GetString( stepSoundName );
CSoundParameters params;
if ( !CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) )
return;
// do the surface dependent sound
CLocalPlayerFilter filter;
EmitSound_t ep;
ep.m_nChannel = CHAN_BODY;
ep.m_pSoundName = params.soundname;
ep.m_flVolume = fvol;
ep.m_SoundLevel = params.soundlevel;
ep.m_nFlags = 0;
ep.m_nPitch = params.pitch * 0.7f; // drone plays 'deeper' footstep sounds
ep.m_pOrigin = &vecOrigin;
EmitSound( filter, entindex(), ep );
DoAlienFootstep(vecOrigin, fvol);
}
// plays alien type specific footstep sound
void C_ASW_Simple_Alien::DoAlienFootstep(Vector &vecOrigin, float fvol)
{
CSoundParameters params;
if ( !CBaseEntity::GetParametersForSound( "ASW_Drone.FootstepSoft", params, NULL ) )
return;
CLocalPlayerFilter filter;
// do the alienfleshy foot sound
EmitSound_t ep2;
ep2.m_nChannel = CHAN_AUTO;
ep2.m_pSoundName = params.soundname;
ep2.m_flVolume = fvol * 0.2f;
ep2.m_SoundLevel = params.soundlevel;
ep2.m_nFlags = 0;
ep2.m_nPitch = params.pitch;
ep2.m_pOrigin = &vecOrigin;
EmitSound( filter, entindex(), ep2 );
sm_flLastFootstepTime = gpGlobals->curtime;
}
C_BaseAnimating * C_ASW_Simple_Alien::BecomeRagdollOnClient( void )
{
C_BaseAnimating* pEnt = BaseClass::BecomeRagdollOnClient();
C_ClientRagdoll* pRagdoll = dynamic_cast<C_ClientRagdoll*>(pEnt);
if (pRagdoll)
{
// make this ragdoll gib after a short time
// ASWTODO - get ragdolls exploding
//pRagdoll->m_fASWGibTime = gpGlobals->curtime + random->RandomFloat(asw_drone_gib_time_min.GetFloat(), asw_drone_gib_time_max.GetFloat());
// make it bleed
//C_ASW_Emitter *pEmitter = dynamic_cast<C_ASW_Emitter*>(CreateEntityByName("client_asw_emitter"));
int iNumBleeds = random->RandomInt(2,4);
for (int i=0;i<iNumBleeds;i++)
{
C_ASW_Emitter *pEmitter = new C_ASW_Emitter;
if (pEmitter)
{
if (pEmitter->InitializeAsClientEntity( NULL, false ))
{
// randomly pick a jet, a drip or a burst
float f = random->RandomFloat();
if (f < 0.33f)
Q_snprintf(pEmitter->m_szTemplateName, sizeof(pEmitter->m_szTemplateName), "dronebloodjet");
else if (f < 0.66f)
Q_snprintf(pEmitter->m_szTemplateName, sizeof(pEmitter->m_szTemplateName), "dronebloodburst");
else
Q_snprintf(pEmitter->m_szTemplateName, sizeof(pEmitter->m_szTemplateName), "droneblooddroplets");
pEmitter->m_fScale = 1.0f;
pEmitter->m_bEmit = true;
pEmitter->CreateEmitter();
pEmitter->SetAbsOrigin(pRagdoll->WorldSpaceCenter());
pEmitter->SetAbsAngles(pRagdoll->GetAbsAngles());
// randomly pick an attach point
int iAttach = random->RandomInt(0, 7);
switch (iAttach)
{
case 0: pEmitter->ClientAttach(pRagdoll, "bleed1"); break;
case 1: pEmitter->ClientAttach(pRagdoll, "bleed2"); break;
case 2: pEmitter->ClientAttach(pRagdoll, "bleed3"); break;
case 3: pEmitter->ClientAttach(pRagdoll, "bleed4"); break;
case 4: pEmitter->ClientAttach(pRagdoll, "bleed5"); break;
case 5: pEmitter->ClientAttach(pRagdoll, "bleedjaw1"); break;
case 6: pEmitter->ClientAttach(pRagdoll, "bleedleg1"); break;
case 7: pEmitter->ClientAttach(pRagdoll, "bleedleg2"); break;
}
// ASWTODO - put this in when we have ragdolls exploding
//pEmitter->SetDieTime(pRagdoll->m_fASWGibTime); // stop emitting once the ragdoll gibs
pEmitter->SetDieTime(3.0f);
}
else
{
UTIL_Remove( pEmitter );
}
}
}
}
return pRagdoll;
}
// shadow direction test
bool C_ASW_Simple_Alien::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
{
VPROF_BUDGET( "C_ASW_Simple_Alien::GetShadowCastDistance", VPROF_BUDGETGROUP_ASW_CLIENT );
if (!asw_directional_shadows.GetBool())
return false;
Vector vecCustomDir;
float fCustomContribution = 0;
if ( ASWGameResource() )
{
// go through all marines
int iMaxMarines = ASWGameResource()->GetMaxMarineResources();
for (int i=0;i<iMaxMarines;i++)
{
C_ASW_Marine_Resource *pMR = ASWGameResource()->GetMarineResource(i);
C_ASW_Marine *pMarine = pMR ? pMR->GetMarineEntity() : NULL;
if (pMarine && pMarine->m_pFlashlight) // if this is a marine with a flashlight
{
Vector diff = WorldSpaceCenter() - pMarine->EyePosition();
diff.NormalizeInPlace();
Vector vecMarineFacing(0,0,0);
AngleVectors(pMarine->GetAbsAngles(), &vecMarineFacing);
float dot = vecMarineFacing.Dot(diff);
if (dot > 0) // if the flashlight is facing us
{
vecCustomDir += dot * diff;
fCustomContribution += dot;
}
}
}
}
if (fCustomContribution > 0)
{
vecCustomDir += (*pDirection) * (1.0f - fCustomContribution);
vecCustomDir.NormalizeInPlace();
pDirection->x = vecCustomDir.x;
pDirection->y = vecCustomDir.y;
pDirection->z = vecCustomDir.z;
return true;
}
return false;
}
void C_ASW_Simple_Alien::PostDataUpdate( DataUpdateType_t updateType )
{
BaseClass::PostDataUpdate(updateType);
// If this entity was new, then latch in various values no matter what.
if ( updateType == DATA_UPDATE_CREATED )
{
// We want to think every frame.
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
// NOTE: Below is a stripped down version of PostDataUpdate with perf notes in. Do we need this anymore?
/*
MDLCACHE_CRITICAL_SECTION();
// Make sure that the correct model is referenced for this entity
if (m_nLastSetModel != m_nModelIndex)
{
m_nLastSetModel = m_nModelIndex;
SetModelByIndex( m_nModelIndex );
}
//cheap
// as c_baseanimating and c_baseentity but streamlined, since we want lots of aliens
// Cycle change? Then re-render
if ( m_flOldCycle != GetCycle() || m_nOldSequence != GetSequence() )
{
InvalidatePhysicsRecursive( ANIMATION_CHANGED );
}
// cheap - 1.59% 931.981
// with custom flcycle: cheap - 1.56% 915.995
// reset prev cycle if new sequence
if (m_nNewSequenceParity != m_nPrevNewSequenceParity)
{
// It's important not to call Reset() on a static prop, because if we call
// Reset(), then the entity will stay in the interpolated entities list
// forever, wasting CPU.
CStudioHdr *hdr = GetModelPtr();
if ( hdr && !( hdr->flags() & STUDIOHDR_FLAGS_STATIC_PROP ) )
{
// cheap without this - 1.86% 1086.119
// asw temp anim comment
m_iv_flCycle.Reset( gpGlobals->curtime );
//m_flCycle = 0; // asw added
}
}
// with custom flyccle: cheap - 1040.942 1.78%
// mostly exp - 18.09% 10657.891
// NOTE: This *has* to happen first. Otherwise, Origin + angles may be wrong
if ( m_nRenderFX == kRenderFxRagdoll && updateType == DATA_UPDATE_CREATED )
{
MoveToLastReceivedPosition( true );
}
else
{
MoveToLastReceivedPosition( false );
}
if ( m_nOldRenderMode != m_nRenderMode )
{
SetRenderMode( (RenderMode_t)m_nRenderMode, true );
}
// expensive - 19.67% 11575.138
// with custom flycycle: cheap - 1279.913 2.19%
bool animTimeChanged = ( m_flAnimTime != m_flOldAnimTime ) ? true : false;
bool originChanged = ( m_vecOldOrigin != GetLocalOrigin() ) ? true : false;
bool anglesChanged = ( m_vecOldAngRotation != GetLocalAngles() ) ? true : false;
bool simTimeChanged = ( m_flSimulationTime != m_flOldSimulationTime ) ? true : false;
// Detect simulation changes
bool simulationChanged = originChanged || anglesChanged || simTimeChanged;
// with custom flcycle: cheap - 1229.052 2.10%
//if ( !GetPredictable() && !IsClientCreated() )
{
if (animTimeChanged || simulationChanged)
{
float animchangetime = animTimeChanged ? GetLastChangeTime( LATCH_ANIMATION_VAR ) : 0;
float simchangetime = simulationChanged ? GetLastChangeTime( LATCH_SIMULATION_VAR ) : 0;
int c = m_VarMap.m_Entries.Count();
for ( int i = 0; i < c; i++ )
{
VarMapEntry_t *e = &m_VarMap.m_Entries[ i ];
IInterpolatedVar *watcher = e->watcher;
int type = watcher->GetType();
if ( type & EXCLUDE_AUTO_LATCH )
continue;
if (animTimeChanged && (type & LATCH_ANIMATION_VAR) )
{
if ( watcher->NoteChanged( gpGlobals->curtime, animchangetime ) )
e->m_bNeedsToInterpolate = true;
}
if (simulationChanged && (type & LATCH_SIMULATION_VAR) )
{
if ( watcher->NoteChanged( gpGlobals->curtime, simchangetime ) )
e->m_bNeedsToInterpolate = true;
}
}
if ( index != 0 && GetModel() && IsVisible() )
{
AddToInterpolationList();
//if ( m_InterpolationListEntry == 0xFFFF )
//m_InterpolationListEntry = g_InterpolationList.AddToTail( this );
}
}
}
//expensive - 26.21% 15504.664
// with custom flcycle: expensive - 10021.575 17.07%
// Deal with hierarchy. Have to do it here (instead of in a proxy)
// because this is the only point at which all entities are loaded
// If this condition isn't met, then a child was sent without its parent
Assert( m_hNetworkMoveParent.Get() || !m_hNetworkMoveParent.IsValid() );
HierarchySetParent(m_hNetworkMoveParent);
//MarkMessageReceived(); // asw - inline it below
m_flLastMessageTime = engine->GetLastTimeStamp();
// If this entity was new, then latch in various values no matter what.
if ( updateType == DATA_UPDATE_CREATED )
{
// Construct a random value for this instance
m_flProxyRandomValue = random->RandomFloat( 0, 1 );
m_vecLastRenderedPos = GetAbsOrigin();
ResetLatched();
}
//UpdatePartitionListEntry(); // asw - inline it
CollideType_t shouldCollide = IsRagdoll() ? ENTITY_SHOULD_RESPOND : ENTITY_SHOULD_COLLIDE;
// Choose the list based on what kind of collisions we want
int list = PARTITION_CLIENT_NON_STATIC_EDICTS;
if (shouldCollide == ENTITY_SHOULD_COLLIDE)
list |= PARTITION_CLIENT_SOLID_EDICTS;
else if (shouldCollide == ENTITY_SHOULD_RESPOND)
list |= PARTITION_CLIENT_RESPONSIVE_EDICTS;
// add the entity to the KD tree so we will collide against it
partition->RemoveAndInsert( PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_RESPONSIVE_EDICTS | PARTITION_CLIENT_NON_STATIC_EDICTS, list, CollisionProp()->GetPartitionHandle() );
// Add the entity to the nointerp list?
if ( Teleported() || IsEffectActive(EF_NOINTERP) )
AddToTeleportList();
// with custom flcycle: 9775.681 16.61%
// with custom flycycle and cleaned latch: 9803.268 16.72%
// without NoteChanged for simulation, etc: 5010.101 8.61%
// and without NoteChanged for anim 1509.852 2.59%
*/
}
void C_ASW_Simple_Alien::ClientThink()
{
BaseClass::ClientThink();
//ASWUpdateClientSideAnimation();
m_vecLastRenderedPos = WorldSpaceCenter();
C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer();
if ( pPlayer && pPlayer->IsSniperScopeActive() )
{
m_GlowObject.SetRenderFlags( true, true );
}
else
{
m_GlowObject.SetRenderFlags( false, false );
}
}
const Vector& C_ASW_Simple_Alien::GetAimTargetPos(const Vector &vecFiringSrc, bool bWeaponPrefersFlatAiming)
{
return m_vecLastRenderedPos;
}
int C_ASW_Simple_Alien::DrawModel( int flags, const RenderableInstance_t &instance )
{
m_vecLastRenderedPos = WorldSpaceCenter();
return BaseClass::DrawModel(flags, instance);
}
float C_ASW_Simple_Alien::sm_flLastFootstepTime = 0.0f;