460 lines
13 KiB
C++
460 lines
13 KiB
C++
#include "cbase.h"
|
|
#include "asw_missile_round_shared.h"
|
|
#include "asw_gamerules.h"
|
|
#include "asw_melee_system.h"
|
|
#include "asw_trace_filter_shot.h"
|
|
#ifdef CLIENT_DLL
|
|
#include "particles_simple.h"
|
|
#else
|
|
#include "world.h"
|
|
#include "asw_util_shared.h"
|
|
#include "iasw_spawnable_npc.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_marine_resource.h"
|
|
#include "particle_parse.h"
|
|
#include "Sprite.h"
|
|
#include "SpriteTrail.h"
|
|
#include "te_effect_dispatch.h"
|
|
#include "utlvector.h"
|
|
#include "asw_game_resource.h"
|
|
#include "asw_player.h"
|
|
#include "inetchannelinfo.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Missile_Round, DT_ASW_Missile_Round );
|
|
|
|
BEGIN_NETWORK_TABLE( CASW_Missile_Round, DT_ASW_Missile_Round )
|
|
#ifdef GAME_DLL
|
|
SendPropInt( SENDINFO( m_nParticleTrail ) ),
|
|
SendPropBool( SENDINFO( m_bDetonated ) ),
|
|
SendPropVector( SENDINFO( m_vEndPosition ), 0, SPROP_NOSCALE ),
|
|
#else
|
|
RecvPropInt( RECVINFO( m_nParticleTrail ) ),
|
|
RecvPropBool( RECVINFO( m_bDetonated ) ),
|
|
RecvPropVector( RECVINFO( m_vEndPosition ) ),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
#ifdef GAME_DLL
|
|
|
|
LINK_ENTITY_TO_CLASS( asw_missile_round, CASW_Missile_Round );
|
|
PRECACHE_REGISTER( asw_missile_round );
|
|
|
|
BEGIN_DATADESC( CASW_Missile_Round )
|
|
END_DATADESC()
|
|
|
|
CUtlVector<CASW_Missile_Round*> g_vecMissileRounds;
|
|
extern ConVar sv_maxunlag;
|
|
ConVar sv_unlag_alien_projectiles( "sv_unlag_alien_projectiles", "1", FCVAR_NONE, "If enabled, server will rewind time based on a player's ping when doing ranger projectile collision vs marines." );
|
|
|
|
CASW_Missile_Round::CASW_Missile_Round()
|
|
{
|
|
m_nParticleTrail = -1;
|
|
m_vEndPosition.Init();
|
|
m_bDetonated = false;
|
|
m_bMarineFriendly = false;
|
|
|
|
g_vecMissileRounds.AddToTail( this );
|
|
}
|
|
|
|
CASW_Missile_Round::~CASW_Missile_Round()
|
|
{
|
|
g_vecMissileRounds.FindAndRemove( this );
|
|
}
|
|
|
|
void CASW_Missile_Round::Setup( const CASW_AlienShot &shot, const Vector &position, const QAngle &angles, const Vector &velocity, CBaseEntity *pOwner )
|
|
{
|
|
m_ShotDef = shot;
|
|
SetAbsAngles( angles );
|
|
UTIL_SetOrigin( this, position );
|
|
m_hOwner = pOwner;
|
|
SetOwnerEntity( pOwner );
|
|
|
|
SetAbsVelocity( velocity );
|
|
QAngle vecAngVelocity( random->RandomFloat( -200, 200 ), random->RandomFloat( -200, 200 ), random->RandomFloat( -200, 200 ) );
|
|
SetLocalAngularVelocity( vecAngVelocity );
|
|
|
|
m_LagCompensation.Init(this);
|
|
}
|
|
|
|
void CASW_Missile_Round::Spawn( void )
|
|
{
|
|
Precache();
|
|
SetModel( m_ShotDef.m_strModel );
|
|
|
|
//SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM );
|
|
SetMoveType( MOVETYPE_CUSTOM );
|
|
|
|
m_takedamage = m_ShotDef.m_bShootable ? DAMAGE_YES : DAMAGE_NO;
|
|
m_iHealth = 1;
|
|
|
|
SetSize( -Vector(m_ShotDef.m_flSize,m_ShotDef.m_flSize,m_ShotDef.m_flSize), Vector(m_ShotDef.m_flSize,m_ShotDef.m_flSize,m_ShotDef.m_flSize) );
|
|
SetSolid( SOLID_NONE );
|
|
SetGravity( m_ShotDef.m_flGravity );
|
|
SetCollisionGroup( ASW_COLLISION_GROUP_ALIEN_MISSILE );
|
|
|
|
SetTouch( &CASW_Missile_Round::Touch );
|
|
|
|
ResetSequence( LookupSequence( "MortarBugProjectile_Closed" ) );
|
|
|
|
EmitSound( m_ShotDef.m_strSound_spawn );
|
|
|
|
SetThink( &CBaseEntity::SUB_Remove );
|
|
SetNextThink( gpGlobals->curtime + m_ShotDef.m_flFuse );
|
|
|
|
m_vecOldPosition = GetAbsOrigin();
|
|
}
|
|
|
|
void CASW_Missile_Round::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
|
|
{
|
|
if ( m_bDetonated )
|
|
{
|
|
*pNewPosition = GetAbsOrigin();
|
|
*pNewVelocity = GetAbsVelocity();
|
|
return;
|
|
}
|
|
Vector vNorm = GetAbsVelocity();
|
|
VectorNormalize( vNorm );
|
|
QAngle angles;
|
|
VectorAngles( vNorm, angles );
|
|
SetAbsAngles( angles );
|
|
|
|
Vector vNewPosition = GetAbsOrigin() + GetAbsVelocity() * gpGlobals->frametime;
|
|
|
|
trace_t tr;
|
|
CASWTraceFilterShot filter( this, NULL, GetCollisionGroup() );
|
|
|
|
if ( m_bMarineFriendly )
|
|
{
|
|
filter.SetSkipMarines( true );
|
|
filter.SetSkipAliens( false );
|
|
}
|
|
else
|
|
{
|
|
filter.SetSkipAliens( true );
|
|
filter.SetSkipMarines( false );
|
|
filter.SetSkipRollingMarines( true );
|
|
}
|
|
UTIL_TraceHull( GetAbsOrigin(), vNewPosition, WorldAlignMins(), WorldAlignMaxs(), MASK_SHOT, &filter, &tr );
|
|
vNewPosition = tr.endpos;
|
|
|
|
if ( tr.DidHit() && tr.m_pEnt )
|
|
{
|
|
if ( tr.m_pEnt->Classify() == CLASS_ASW_MARINE )
|
|
{
|
|
/*
|
|
float flDesiredMoveDist = (GetAbsVelocity() * gpGlobals->frametime ).Length();
|
|
float flMoveDist = ( tr.endpos - GetAbsOrigin() ).Length();
|
|
if ( flDesiredMoveDist)
|
|
float flMoveAmount =
|
|
filter.SetSkipMarinesReflectingProjectiles( true );
|
|
*/
|
|
CASW_Marine *pMarine = assert_cast<CASW_Marine*>( tr.m_pEnt );
|
|
if ( pMarine->IsReflectingProjectiles() )
|
|
{
|
|
// reflect
|
|
m_bMarineFriendly = true;
|
|
SetCollisionGroup( ASW_COLLISION_GROUP_PLAYER_MISSILE );
|
|
SetOwnerEntity( pMarine );
|
|
SetAbsVelocity( -GetAbsVelocity() );
|
|
|
|
*pNewPosition = GetAbsOrigin();
|
|
*pNewVelocity = GetAbsVelocity();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetAbsOrigin( vNewPosition );
|
|
|
|
DoLagCompensatedMarineCollision();
|
|
|
|
UpdatePhysicsShadowToCurrentPosition(gpGlobals->frametime);
|
|
PhysicsTouchTriggers();
|
|
|
|
*pNewPosition = GetAbsOrigin();
|
|
*pNewVelocity = GetAbsVelocity();
|
|
|
|
if ( tr.m_pEnt )
|
|
{
|
|
MissileHit( tr.m_pEnt, tr );
|
|
}
|
|
}
|
|
|
|
void CASW_Missile_Round::Precache( )
|
|
{
|
|
BaseClass::Precache();
|
|
|
|
if ( !m_ShotDef.m_strModel.IsEmpty() )
|
|
PrecacheModel( m_ShotDef.m_strModel );
|
|
|
|
if ( !m_ShotDef.m_strSound_spawn.IsEmpty() )
|
|
PrecacheScriptSound( m_ShotDef.m_strSound_spawn );
|
|
|
|
if ( !m_ShotDef.m_strSound_hitNPC.IsEmpty() )
|
|
PrecacheScriptSound( m_ShotDef.m_strSound_hitNPC );
|
|
|
|
if ( !m_ShotDef.m_strSound_hitWorld.IsEmpty() )
|
|
PrecacheScriptSound( m_ShotDef.m_strSound_hitWorld );
|
|
|
|
if ( !m_ShotDef.m_strParticles_trail.IsEmpty() )
|
|
m_nParticleTrail = PrecacheParticleSystem( m_ShotDef.m_strParticles_trail );
|
|
|
|
if ( !m_ShotDef.m_strParticles_hit.IsEmpty() )
|
|
PrecacheParticleSystem( m_ShotDef.m_strParticles_hit );
|
|
}
|
|
|
|
void CASW_Missile_Round::DoLagCompensatedMarineCollision()
|
|
{
|
|
if ( !ASWGameResource() )
|
|
return;
|
|
|
|
if ( gpGlobals->maxClients > 1 )
|
|
{
|
|
m_LagCompensation.StorePositionHistory();
|
|
}
|
|
|
|
Vector vecMovement = GetAbsOrigin() - m_vecOldPosition;
|
|
|
|
int nMaxResources = ASWGameResource()->GetMaxMarineResources();
|
|
for ( int i = 0; i < nMaxResources; i++ )
|
|
{
|
|
CASW_Marine_Resource *pMR = ASWGameResource()->GetMarineResource( i );
|
|
if ( !pMR )
|
|
continue;
|
|
|
|
CASW_Marine *pMarine = pMR->GetMarineEntity();
|
|
if ( !pMarine )
|
|
continue;
|
|
|
|
if ( pMarine->GetCurrentMeleeAttack() && pMarine->GetCurrentMeleeAttack()->m_nAttackID == CASW_Melee_System::s_nRollAttackID )
|
|
continue;
|
|
|
|
Vector vecPos = GetAbsOrigin();
|
|
|
|
if ( pMarine->IsInhabited() && sv_unlag_alien_projectiles.GetBool() )
|
|
{
|
|
CASW_Player *pPlayer = pMarine->GetCommander();
|
|
if ( !pPlayer )
|
|
continue;
|
|
|
|
float correct = 0.0f;
|
|
|
|
INetChannelInfo *nci = engine->GetPlayerNetInfo( pPlayer->entindex() );
|
|
if ( nci )
|
|
{
|
|
correct+= nci->GetLatency( FLOW_OUTGOING ) * 0.5f; // add network latency
|
|
}
|
|
|
|
int lerpTicks = TIME_TO_TICKS( pPlayer->m_fLerpTime ); // calc number of view interpolation ticks - 1
|
|
correct += TICKS_TO_TIME( lerpTicks ); // add view interpolation latency see C_BaseEntity::GetInterpolationAmount()
|
|
correct = clamp( correct, 0.0f, sv_maxunlag.GetFloat() ); // check bounds [0,sv_maxunlag]
|
|
|
|
// correct tick send by player
|
|
int targettick = gpGlobals->tickcount - TIME_TO_TICKS( correct );
|
|
|
|
// check the player has enough lag to warrant doing the work of lag compensation
|
|
const float fLaggedTime = TICKS_TO_TIME( targettick );
|
|
if ( gpGlobals->curtime - fLaggedTime < ASW_MIN_LAG_TIME )
|
|
continue;
|
|
|
|
vecPos = m_LagCompensation.GetLaggedPosition( fLaggedTime );
|
|
|
|
//NDebugOverlay::Box( vecPos, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), 0, 0, 255, 0, 0.1f );
|
|
}
|
|
|
|
trace_t tr;
|
|
CTraceFilterOnlyHitThis traceFilter( pMarine );
|
|
AI_TraceLine( vecPos - vecMovement, vecPos, MASK_SHOT, &traceFilter, &tr );
|
|
|
|
if ( tr.DidHitNonWorldEntity() )
|
|
{
|
|
MissileHit( tr.m_pEnt, tr );
|
|
}
|
|
|
|
if ( m_bDetonated )
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_vecOldPosition = GetAbsOrigin();
|
|
}
|
|
}
|
|
|
|
void CASW_Missile_Round::Touch( CBaseEntity *pOther )
|
|
{
|
|
if ( m_bDetonated )
|
|
return;
|
|
|
|
if ( !pOther || pOther == GetOwnerEntity() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// make sure we don't die on things we shouldn't
|
|
if ( !ASWGameRules() || !ASWGameRules()->ShouldCollide( GetCollisionGroup(), pOther->GetCollisionGroup() ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
trace_t tr;
|
|
tr = BaseClass::GetTouchTrace();
|
|
MissileHit( pOther, tr );
|
|
}
|
|
|
|
void CASW_Missile_Round::MissileHit( CBaseEntity *pEnt, trace_t &tr )
|
|
{
|
|
// don't collide with marines doing a roll
|
|
if ( pEnt->Classify() == CLASS_ASW_MARINE )
|
|
{
|
|
CASW_Marine *pMarine = static_cast<CASW_Marine*>( pEnt );
|
|
if ( pMarine->GetCurrentMeleeAttack() && pMarine->GetCurrentMeleeAttack()->m_nAttackID == CASW_Melee_System::s_nRollAttackID )
|
|
{
|
|
return;
|
|
}
|
|
if ( pMarine->IsReflectingProjectiles() )
|
|
{
|
|
// TODO: ignore this marine (don't collide with him again)
|
|
// TODO: change direction
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( pEnt->m_takedamage != DAMAGE_NO )
|
|
{
|
|
trace_t tr2;
|
|
Vector vecNormalizedVel = GetAbsVelocity();
|
|
|
|
ClearMultiDamage();
|
|
VectorNormalize( vecNormalizedVel );
|
|
|
|
CTakeDamageInfo dmgInfo( this, m_hOwner, m_ShotDef.m_flDamage_direct, DMG_GENERIC | DMG_NEVERGIB );
|
|
CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
|
|
dmgInfo.SetDamagePosition( tr.endpos );
|
|
trace_t *pTrace = &tr;
|
|
pEnt->DispatchTraceAttack( dmgInfo, vecNormalizedVel, pTrace );
|
|
|
|
ApplyMultiDamage();
|
|
|
|
// keep going through the glass.
|
|
if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS )
|
|
return;
|
|
|
|
Vector vForward;
|
|
|
|
AngleVectors( GetAbsAngles(), &vForward );
|
|
VectorNormalize ( vForward );
|
|
|
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_OPAQUE, pEnt, COLLISION_GROUP_NONE, &tr2 );
|
|
}
|
|
else
|
|
{
|
|
if ( m_ShotDef.m_flBounce != 0.0f )
|
|
{
|
|
Vector vecVel = GetAbsVelocity();
|
|
float flDot = vecVel.Dot( tr.plane.normal );
|
|
vecVel -= ( m_ShotDef.m_flBounce + 1.0f ) * flDot * tr.plane.normal;
|
|
SetAbsVelocity( vecVel );
|
|
EmitSound( m_ShotDef.m_strSound_hitWorld );
|
|
return;
|
|
}
|
|
|
|
// Put a mark unless we've hit the sky
|
|
if ( ( tr.surface.flags & SURF_SKY ) == false )
|
|
{
|
|
UTIL_ImpactTrace( &tr, DMG_GENERIC );
|
|
}
|
|
}
|
|
|
|
DispatchParticleEffect( m_ShotDef.m_strParticles_hit, GetAbsOrigin(), QAngle( 0, 0, 0 ) );
|
|
|
|
// play body "thwack" sound
|
|
CASW_Marine *pMarine = CASW_Marine::AsMarine( pEnt );
|
|
if ( pMarine )
|
|
EmitSound( m_ShotDef.m_strSound_hitNPC );
|
|
else
|
|
EmitSound( m_ShotDef.m_strSound_hitWorld );
|
|
|
|
m_bDetonated = true;
|
|
m_vEndPosition = GetAbsOrigin();
|
|
|
|
SetTouch( NULL );
|
|
SetAbsVelocity( Vector( 0, 0, 0 ) );
|
|
SetSolid( SOLID_NONE );
|
|
SetThink( &CBaseEntity::SUB_Remove );
|
|
SetNextThink( gpGlobals->curtime + 1.0f );
|
|
}
|
|
|
|
CASW_Missile_Round* CASW_Missile_Round::Missile_Round_Create( const CASW_AlienShot &shot, const Vector &position, const QAngle &angles, const Vector &velocity, CBaseEntity *pOwner )
|
|
{
|
|
CASW_Missile_Round *pMissile = ( CASW_Missile_Round * )CreateEntityByName( "asw_missile_round" );
|
|
pMissile->Setup( shot, position, angles, velocity, pOwner );
|
|
pMissile->Spawn();
|
|
return pMissile;
|
|
}
|
|
|
|
#endif // GAME_DLL
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
CASW_Missile_Round::CASW_Missile_Round()
|
|
{
|
|
m_bDetonated = false;
|
|
m_vEndPosition.Init();
|
|
m_pTrail = NULL;
|
|
}
|
|
|
|
void CASW_Missile_Round::ClientThink()
|
|
{
|
|
// wait until our origin is the same as the server and then hide our model and particle trail
|
|
Vector origin = GetAbsOrigin();
|
|
if ( ( m_vEndPosition - origin ).LengthSqr() < 1.0f )
|
|
{
|
|
SetModelIndex( 0 );
|
|
if ( m_pTrail )
|
|
{
|
|
ParticleProp()->StopEmission( m_pTrail );
|
|
m_pTrail = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CASW_Missile_Round::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 )
|
|
{
|
|
if ( m_nParticleTrail >= 0 )
|
|
{
|
|
m_pTrail = ParticleProp()->CreatePrecached( m_nParticleTrail, PATTACH_ABSORIGIN_FOLLOW );
|
|
if (m_pTrail )
|
|
{
|
|
ParticleProp()->AddControlPoint( m_pTrail, 1, this, PATTACH_ABSORIGIN_FOLLOW );
|
|
m_pTrail->SetControlPoint( 0, GetAbsOrigin() );
|
|
m_pTrail->SetControlPoint( 1, GetAbsOrigin() );
|
|
m_pTrail->SetControlPointEntity( 0, this );
|
|
m_pTrail->SetControlPointEntity( 1, this );
|
|
}
|
|
}
|
|
}
|
|
else if ( updateType == DATA_UPDATE_DATATABLE_CHANGED )
|
|
{
|
|
if ( m_bDetonated )
|
|
{
|
|
SetNextClientThink(CLIENT_THINK_ALWAYS);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // CLIENT_DLL
|