source-engine/game/client/hl2/c_weapon_physcannon.cpp

443 lines
12 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "c_weapon__stubs.h"
#include "c_basehlcombatweapon.h"
#include "fx.h"
#include "particles_localspace.h"
#include "view.h"
#include "particles_attractor.h"
class C_WeaponPhysCannon: public C_BaseHLCombatWeapon
{
DECLARE_CLASS( C_WeaponPhysCannon, C_BaseHLCombatWeapon );
public:
C_WeaponPhysCannon( void );
DECLARE_CLIENTCLASS();
DECLARE_PREDICTABLE();
virtual void OnDataChanged( DataUpdateType_t updateType );
virtual int DrawModel( int flags );
virtual void ClientThink( void );
virtual bool ShouldUseLargeViewModelVROverride() OVERRIDE { return true; }
private:
bool SetupEmitter( void );
bool m_bIsCurrentlyUpgrading;
float m_flTimeForceView;
float m_flTimeIgnoreForceView;
bool m_bWasUpgraded;
CSmartPtr<CLocalSpaceEmitter> m_pLocalEmitter;
CSmartPtr<CSimpleEmitter> m_pEmitter;
CSmartPtr<CParticleAttractor> m_pAttractor;
};
STUB_WEAPON_CLASS_IMPLEMENT( weapon_physcannon, C_WeaponPhysCannon );
IMPLEMENT_CLIENTCLASS_DT( C_WeaponPhysCannon, DT_WeaponPhysCannon, CWeaponPhysCannon )
RecvPropBool( RECVINFO( m_bIsCurrentlyUpgrading ) ),
RecvPropFloat( RECVINFO( m_flTimeForceView) ),
END_RECV_TABLE()
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
C_WeaponPhysCannon::C_WeaponPhysCannon( void )
{
m_bWasUpgraded = false;
m_flTimeIgnoreForceView = -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_WeaponPhysCannon::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_WeaponPhysCannon::SetupEmitter( void )
{
if ( !m_pLocalEmitter.IsValid() )
{
m_pLocalEmitter = CLocalSpaceEmitter::Create( "physpowerup", GetRefEHandle(), LookupAttachment( "core" ) );
if ( m_pLocalEmitter.IsValid() == false )
return false;
}
if ( !m_pAttractor.IsValid() )
{
m_pAttractor = CParticleAttractor::Create( vec3_origin, "physpowerup_att" );
if ( m_pAttractor.IsValid() == false )
return false;
}
if ( !m_pEmitter.IsValid() )
{
m_pEmitter = CSimpleEmitter::Create( "physpowerup_glow" );
if ( m_pEmitter.IsValid() == false )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Sorts the components of a vector
//-----------------------------------------------------------------------------
static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
{
Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );
int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
if (absVec[2] > absVec[maxIdx])
{
maxIdx = 2;
}
// always choose something right-handed....
switch( maxIdx )
{
case 0:
pVecIdx[0] = 1;
pVecIdx[1] = 2;
pVecIdx[2] = 0;
break;
case 1:
pVecIdx[0] = 2;
pVecIdx[1] = 0;
pVecIdx[2] = 1;
break;
case 2:
pVecIdx[0] = 0;
pVecIdx[1] = 1;
pVecIdx[2] = 2;
break;
}
}
//-----------------------------------------------------------------------------
// Compute the bounding box's center, size, and basis
//-----------------------------------------------------------------------------
void ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld,
Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec )
{
// Compute the center of the hitbox in worldspace
Vector vecHitboxCenter;
VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter );
vecHitboxCenter *= 0.5f;
VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin );
// Get the object's basis
Vector vec[3];
MatrixGetColumn( hitboxToWorld, 0, vec[0] );
MatrixGetColumn( hitboxToWorld, 1, vec[1] );
MatrixGetColumn( hitboxToWorld, 2, vec[2] );
// vec[1] *= -1.0f;
Vector vecViewDir;
VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir );
VectorNormalize( vecViewDir );
// Project the shadow casting direction into the space of the hitbox
Vector localViewDir;
localViewDir[0] = DotProduct( vec[0], vecViewDir );
localViewDir[1] = DotProduct( vec[1], vecViewDir );
localViewDir[2] = DotProduct( vec[2], vecViewDir );
// Figure out which vector has the largest component perpendicular
// to the view direction...
// Sort by how perpendicular it is
int vecIdx[3];
SortAbsVectorComponents( localViewDir, vecIdx );
// Here's our hitbox basis vectors; namely the ones that are
// most perpendicular to the view direction
*pXVec = vec[vecIdx[0]];
*pYVec = vec[vecIdx[1]];
// Project them into a plane perpendicular to the view direction
*pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec );
*pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec );
VectorNormalize( *pXVec );
VectorNormalize( *pYVec );
// Compute the hitbox size
Vector boxSize;
VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize );
// We project the two longest sides into the vectors perpendicular
// to the projection direction, then add in the projection of the perp direction
Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) );
size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) );
// Add the third component into x and y
size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) );
size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) );
// Bloat a bit, since the shadow wants to extend outside the model a bit
size *= 2.0f;
// Clamp the minimum size
Vector2DMax( size, Vector2D(10.0f, 10.0f), size );
// Factor the size into the xvec + yvec
(*pXVec) *= size.x * 0.5f;
(*pYVec) *= size.y * 0.5f;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flags -
// Output : int
//-----------------------------------------------------------------------------
int C_WeaponPhysCannon::DrawModel( int flags )
{
// If we're not ugrading, don't do anything special
if ( m_bIsCurrentlyUpgrading == false && m_bWasUpgraded == false )
return BaseClass::DrawModel( flags );
if ( gpGlobals->frametime == 0 )
return BaseClass::DrawModel( flags );
if ( !m_bReadyToDraw )
return 0;
m_bWasUpgraded = true;
// Create the particle emitter if it's not already
if ( SetupEmitter() )
{
// Add the power-up particles
// See if we should draw
if ( m_bReadyToDraw == false )
return 0;
C_BaseAnimating *pAnimating = GetBaseAnimating();
if (!pAnimating)
return 0;
matrix3x4_t *hitboxbones[MAXSTUDIOBONES];
if ( !pAnimating->HitboxToWorldTransforms( hitboxbones ) )
return 0;
studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() );
if (!pStudioHdr)
return false;
mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() );
if ( !set )
return false;
int i;
float fadePerc = 1.0f;
if ( m_bIsCurrentlyUpgrading )
{
Vector vecSkew = vec3_origin;
// Skew the particles in front or in back of their targets
vecSkew = CurrentViewForward() * 4.0f;
float spriteScale = 1.0f;
spriteScale = clamp( spriteScale, 0.75f, 1.0f );
SimpleParticle *sParticle;
for ( i = 0; i < set->numhitboxes; ++i )
{
Vector vecAbsOrigin, xvec, yvec;
mstudiobbox_t *pBox = set->pHitbox(i);
ComputeRenderInfo( pBox, *hitboxbones[pBox->bone], &vecAbsOrigin, &xvec, &yvec );
Vector offset;
Vector xDir, yDir;
xDir = xvec;
float xScale = VectorNormalize( xDir ) * 0.75f;
yDir = yvec;
float yScale = VectorNormalize( yDir ) * 0.75f;
int numParticles = clamp( 4.0f * fadePerc, 1, 3 );
for ( int j = 0; j < numParticles; j++ )
{
offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f );
offset += vecSkew;
sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), m_pEmitter->GetPMaterial( "effects/combinemuzzle1" ), vecAbsOrigin + offset );
if ( sParticle == NULL )
return 1;
sParticle->m_vecVelocity = vec3_origin;
sParticle->m_uchStartSize = 16.0f * spriteScale;
sParticle->m_flDieTime = 0.2f;
sParticle->m_flLifetime = 0.0f;
sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
sParticle->m_flRollDelta = Helper_RandomFloat( -2.0f, 2.0f );
float alpha = 40;
sParticle->m_uchColor[0] = alpha;
sParticle->m_uchColor[1] = alpha;
sParticle->m_uchColor[2] = alpha;
sParticle->m_uchStartAlpha = alpha;
sParticle->m_uchEndAlpha = 0;
sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2;
}
}
}
}
int attachment = LookupAttachment( "core" );
Vector coreOrigin;
QAngle coreAngles;
GetAttachment( attachment, coreOrigin, coreAngles );
SimpleParticle *sParticle;
// Do the core effects
for ( int i = 0; i < 4; i++ )
{
sParticle = (SimpleParticle *) m_pLocalEmitter->AddParticle( sizeof(SimpleParticle), m_pLocalEmitter->GetPMaterial( "effects/strider_muzzle" ), vec3_origin );
if ( sParticle == NULL )
return 1;
sParticle->m_vecVelocity = vec3_origin;
sParticle->m_flDieTime = 0.1f;
sParticle->m_flLifetime = 0.0f;
sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
sParticle->m_flRollDelta = 0.0f;
float alpha = 255;
sParticle->m_uchColor[0] = alpha;
sParticle->m_uchColor[1] = alpha;
sParticle->m_uchColor[2] = alpha;
sParticle->m_uchStartAlpha = alpha;
sParticle->m_uchEndAlpha = 0;
if ( i < 2 )
{
sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
}
else
{
if ( random->RandomInt( 0, 20 ) == 0 )
{
sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
sParticle->m_uchEndSize = sParticle->m_uchStartSize * 4.0f;
sParticle->m_flDieTime = 0.25f;
}
else
{
sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
}
}
}
if ( m_bWasUpgraded && m_bIsCurrentlyUpgrading )
{
// Update our attractor point
m_pAttractor->SetAttractorOrigin( coreOrigin );
Vector offset;
for ( int i = 0; i < 4; i++ )
{
offset = coreOrigin + RandomVector( -32.0f, 32.0f );
sParticle = (SimpleParticle *) m_pAttractor->AddParticle( sizeof(SimpleParticle), m_pAttractor->GetPMaterial( "effects/strider_muzzle" ), offset );
if ( sParticle == NULL )
return 1;
sParticle->m_vecVelocity = Vector(0,0,8);
sParticle->m_flDieTime = 0.5f;
sParticle->m_flLifetime = 0.0f;
sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
sParticle->m_flRollDelta = 0.0f;
float alpha = 255;
sParticle->m_uchColor[0] = alpha;
sParticle->m_uchColor[1] = alpha;
sParticle->m_uchColor[2] = alpha;
sParticle->m_uchStartAlpha = alpha;
sParticle->m_uchEndAlpha = 0;
sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
sParticle->m_uchEndSize = 0;
}
}
return BaseClass::DrawModel( flags );
}
//---------------------------------------------------------
// On 360, raise up the player's view if the server has
// asked us to.
//---------------------------------------------------------
#define PHYSCANNON_RAISE_VIEW_GOAL 0.0f
void C_WeaponPhysCannon::ClientThink( void )
{
if( m_flTimeIgnoreForceView > gpGlobals->curtime )
return;
float flTime = (m_flTimeForceView - gpGlobals->curtime);
if( flTime < 0.0f )
return;
float flDT = 1.0f - flTime;
if( flDT > 0.0f )
{
QAngle viewangles;
engine->GetViewAngles( viewangles );
if( viewangles.x > PHYSCANNON_RAISE_VIEW_GOAL + 1.0f )
{
float flDelta = PHYSCANNON_RAISE_VIEW_GOAL - viewangles.x;
viewangles.x += (flDelta * flDT);
engine->SetViewAngles(viewangles);
}
else
{
// We've reached our goal. Ignore the forced view angles for now.
m_flTimeIgnoreForceView = m_flTimeForceView + 0.1f;
}
}
return BaseClass::ClientThink();
}