Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

652 lines
21 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "C_Env_Meteor.h"
#include "fx_explosion.h"
#include "tempentity.h"
#include "c_tracer.h"
//=============================================================================
//
// Meteor Factory Functions
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_MeteorFactory::CreateMeteor( int nID, int iType,
const Vector &vecPosition, const Vector &vecDirection,
float flSpeed, float flStartTime, float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs )
{
C_EnvMeteor::Create( nID, iType, vecPosition, vecDirection, flSpeed, flStartTime, flDamageRadius,
vecTriggerMins, vecTriggerMaxs );
}
//=============================================================================
//
// Meteor Spawner Functions
//
void RecvProxy_MeteorTargetPositions( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CEnvMeteorSpawnerShared *pSpawner = ( CEnvMeteorSpawnerShared* )pStruct;
pSpawner->m_aTargets[pData->m_iElement].m_vecPosition.x = pData->m_Value.m_Vector[0];
pSpawner->m_aTargets[pData->m_iElement].m_vecPosition.y = pData->m_Value.m_Vector[1];
pSpawner->m_aTargets[pData->m_iElement].m_vecPosition.z = pData->m_Value.m_Vector[2];
}
void RecvProxy_MeteorTargetRadii( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CEnvMeteorSpawnerShared *pSpawner = ( CEnvMeteorSpawnerShared* )pStruct;
pSpawner->m_aTargets[pData->m_iElement].m_flRadius = pData->m_Value.m_Float;
}
void RecvProxyArrayLength_MeteorTargets( void *pStruct, int objectID, int currentArrayLength )
{
CEnvMeteorSpawnerShared *pSpawner = ( CEnvMeteorSpawnerShared* )pStruct;
if ( pSpawner->m_aTargets.Count() < currentArrayLength )
{
pSpawner->m_aTargets.SetSize( currentArrayLength );
}
}
BEGIN_RECV_TABLE_NOBASE( CEnvMeteorSpawnerShared, DT_EnvMeteorSpawnerShared )
// Setup (read from) Worldcraft.
RecvPropInt ( RECVINFO( m_iMeteorType ) ),
RecvPropInt ( RECVINFO( m_bSkybox ) ),
RecvPropFloat ( RECVINFO( m_flMinSpawnTime ) ),
RecvPropFloat ( RECVINFO( m_flMaxSpawnTime ) ),
RecvPropInt ( RECVINFO( m_nMinSpawnCount ) ),
RecvPropInt ( RECVINFO( m_nMaxSpawnCount ) ),
RecvPropFloat ( RECVINFO( m_flMinSpeed ) ),
RecvPropFloat ( RECVINFO( m_flMaxSpeed ) ),
// Setup through Init.
RecvPropFloat ( RECVINFO( m_flStartTime ) ),
RecvPropInt ( RECVINFO( m_nRandomSeed ) ),
RecvPropVector ( RECVINFO( m_vecMinBounds ) ),
RecvPropVector ( RECVINFO( m_vecMaxBounds ) ),
RecvPropVector ( RECVINFO( m_vecTriggerMins ) ),
RecvPropVector ( RECVINFO( m_vecTriggerMaxs ) ),
// Target List
RecvPropArray2( RecvProxyArrayLength_MeteorTargets,
RecvPropVector( "meteortargetposition_array_element", 0, 0, 0, RecvProxy_MeteorTargetPositions ),
16, 0, "meteortargetposition_array" ),
RecvPropArray2( RecvProxyArrayLength_MeteorTargets,
RecvPropFloat( "meteortargetradius_array_element", 0, 0, 0, RecvProxy_MeteorTargetRadii ),
16, 0, "meteortargetradius_array" )
END_RECV_TABLE()
// This table encodes the CBaseEntity data.
IMPLEMENT_CLIENTCLASS_DT( C_EnvMeteorSpawner, DT_EnvMeteorSpawner, CEnvMeteorSpawner )
RecvPropDataTable ( RECVINFO_DT( m_SpawnerShared ), 0, &REFERENCE_RECV_TABLE( DT_EnvMeteorSpawnerShared ) ),
RecvPropInt ( RECVINFO( m_fDisabled ) ),
END_RECV_TABLE()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteorSpawner::C_EnvMeteorSpawner()
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorSpawner::OnDataChanged( DataUpdateType_t updateType )
{
// Initialize the client side spawner.
m_SpawnerShared.Init( &m_Factory, m_SpawnerShared.m_nRandomSeed, m_SpawnerShared.m_flStartTime,
m_SpawnerShared.m_vecMinBounds, m_SpawnerShared.m_vecMaxBounds,
m_SpawnerShared.m_vecTriggerMins, m_SpawnerShared.m_vecTriggerMaxs );
// Set the next think to be the next spawn interval.
if ( !m_fDisabled )
{
SetNextClientThink( m_SpawnerShared.m_flNextSpawnTime );
}
}
#if 0
// Will probably be used later!!
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorSpawner::ReceiveMessage( int classID, bf_read &msg )
{
if ( classID != GetClientClass()->m_ClassID )
{
// message is for subclass
BaseClass::ReceiveMessage( classID, msg );
return;
}
m_SpawnerShared.m_flStartTime = msg.ReadLong();
m_SpawnerShared.m_flNextSpawnTime = msg.ReadLong();
SetNextClientThink( m_SpawnerShared.m_flNextSpawnTime );
}
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorSpawner::ClientThink( void )
{
SetNextClientThink( m_SpawnerShared.MeteorThink( gpGlobals->curtime ) );
}
//=============================================================================
//
// Meteor Tail Functions
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteorHead::C_EnvMeteorHead()
{
m_vecPos.Init();
m_vecPrevPos.Init();
m_flParticleScale = 1.0f;
m_pSmokeEmitter = NULL;
m_flSmokeSpawnInterval = 0.0f;
m_hSmokeMaterial = INVALID_MATERIAL_HANDLE;
m_flSmokeLifetime = 2.5f;
m_bEmitSmoke = true;
m_hFlareMaterial = INVALID_MATERIAL_HANDLE;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteorHead::~C_EnvMeteorHead()
{
Destroy();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorHead::Start( const Vector &vecOrigin, const Vector &vecDirection )
{
// Emitters.
m_pSmokeEmitter = CSimpleEmitter::Create( "MeteorTrail" );
// m_pFireEmitter = CSimpleEmitter::Create( "MeteorFire" );
if ( !m_pSmokeEmitter /*|| !m_pFireEmitter*/ )
return;
// Smoke
m_pSmokeEmitter->SetSortOrigin( vecOrigin );
m_hSmokeMaterial = m_pSmokeEmitter->GetPMaterial( "particle/SmokeStack" );
Assert( m_hSmokeMaterial != INVALID_MATERIAL_HANDLE );
// Fire
// m_pFireEmitter->SetSortOrigin( vecOrigin );
// m_hFireMaterial = m_pFireEmitter->GetPMaterial( "particle/particle_fire" );
// Assert( m_hFireMaterial != INVALID_MATERIAL_HANDLE );
// Flare
// m_hFlareMaterial = m_ParticleEffect.FindOrAddMaterial( "effects/redflare" );
VectorCopy( vecDirection, m_vecDirection );
VectorCopy( vecOrigin, m_vecPos );
m_bInitThink = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorHead::Destroy( void )
{
m_pSmokeEmitter = NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorHead::MeteorHeadThink( const Vector &vecOrigin, float flTime )
{
if ( m_bInitThink )
{
VectorCopy( vecOrigin, m_vecPrevPos );
m_bInitThink = false;
}
// Update the position of the emitters.
VectorCopy( vecOrigin, m_vecPos );
// Update Smoke
if ( m_pSmokeEmitter.IsValid() && m_bEmitSmoke )
{
m_pSmokeEmitter->SetSortOrigin( m_vecPos );
// Get distance covered
Vector vecDelta;
VectorSubtract( m_vecPos, m_vecPrevPos, vecDelta );
float flLength = vecDelta.Length();
int nParticleCount = flLength / 35.0f;
if ( nParticleCount < 1 )
{
nParticleCount = 1;
}
flLength /= nParticleCount;
Vector vecPos;
for( int iParticle = 0; iParticle < nParticleCount; ++iParticle )
{
vecPos = m_vecPrevPos + ( m_vecDirection * ( flLength * iParticle ) );
// Add some noise to the position.
Vector vecPosOffset;
vecPosOffset.Random( -m_flSmokeSpawnRadius, m_flSmokeSpawnRadius );
VectorAdd( vecPosOffset, vecPos, vecPosOffset );
SimpleParticle *pParticle = ( SimpleParticle* )m_pSmokeEmitter->AddParticle( sizeof( SimpleParticle ),
m_hSmokeMaterial,
vecPosOffset );
if ( pParticle )
{
pParticle->m_flLifetime = 0.0f;
pParticle->m_flDieTime = m_flSmokeLifetime;
// Add just a little movement.
pParticle->m_vecVelocity.Random( -5.0f, 5.0f );
pParticle->m_uchColor[0] = 255.0f;
pParticle->m_uchColor[1] = 255.0f;
pParticle->m_uchColor[2] = 255.0f;
pParticle->m_uchStartSize = 70 * m_flParticleScale;
pParticle->m_uchEndSize = 25 * m_flParticleScale;
float flAlpha = random->RandomFloat( 0.5f, 1.0f );
pParticle->m_uchStartAlpha = flAlpha * 255;
pParticle->m_uchEndAlpha = 0;
pParticle->m_flRoll = random->RandomInt( 0, 360 );
pParticle->m_flRollDelta = random->RandomFloat( -1.0f, 1.0f );
}
}
}
// Update Fire
// if ( m_pFireEmitter && m_bEmitFire )
// {
// }
// Flare
// Save off position.
VectorCopy( m_vecPos, m_vecPrevPos );
}
//=============================================================================
//
// Meteor Tail Functions
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteorTail::C_EnvMeteorTail()
{
m_TailMaterialHandle = INVALID_MATERIAL_HANDLE;
m_pParticleMgr = NULL;
m_pParticle = NULL;
m_flFadeTime = 0.5f;
m_flWidth = 3.0f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteorTail::~C_EnvMeteorTail()
{
Destroy();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorTail::Start( const Vector &vecOrigin, const Vector &vecDirection,
float flSpeed )
{
// Set the particle manager.
m_pParticleMgr = ParticleMgr();
m_pParticleMgr->AddEffect( &m_ParticleEffect, this );
m_TailMaterialHandle = m_ParticleEffect.FindOrAddMaterial( "particle/guidedplasmaprojectile" );
m_pParticle = m_ParticleEffect.AddParticle( sizeof( StandardParticle_t ), m_TailMaterialHandle );
if ( m_pParticle )
{
m_pParticle->m_Pos = vecOrigin;
}
VectorCopy( vecDirection, m_vecDirection );
m_flSpeed = flSpeed;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorTail::Destroy( void )
{
if ( m_pParticleMgr )
{
m_pParticleMgr->RemoveEffect( &m_ParticleEffect );
m_pParticleMgr = NULL;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteorTail::DrawFragment( ParticleDraw* pDraw,
const Vector &vecStart, const Vector &vecDelta,
const Vector4D &vecStartColor, const Vector4D &vecEndColor,
float flStartV, float flEndV )
{
if( !pDraw->GetMeshBuilder() )
return;
// Clip the fragment.
Vector vecVerts[4];
if ( !Tracer_ComputeVerts( vecStart, vecDelta, m_flWidth, vecVerts ) )
return;
// NOTE: Gotta get the winding right so it's not backface culled
// (we need to turn of backface culling for these bad boys)
CMeshBuilder* pMeshBuilder = pDraw->GetMeshBuilder();
pMeshBuilder->Position3f( vecVerts[0].x, vecVerts[0].y, vecVerts[0].z );
pMeshBuilder->TexCoord2f( 0, 0.0f, flStartV );
pMeshBuilder->Color4fv( vecStartColor.Base() );
pMeshBuilder->AdvanceVertex();
pMeshBuilder->Position3f( vecVerts[1].x, vecVerts[1].y, vecVerts[1].z );
pMeshBuilder->TexCoord2f( 0, 1.0f, flStartV );
pMeshBuilder->Color4fv( vecStartColor.Base() );
pMeshBuilder->AdvanceVertex();
pMeshBuilder->Position3f( vecVerts[3].x, vecVerts[3].y, vecVerts[3].z );
pMeshBuilder->TexCoord2f( 0, 1.0f, flEndV );
pMeshBuilder->Color4fv( vecEndColor.Base() );
pMeshBuilder->AdvanceVertex();
pMeshBuilder->Position3f( vecVerts[2].x, vecVerts[2].y, vecVerts[2].z );
pMeshBuilder->TexCoord2f( 0, 0.0f, flEndV );
pMeshBuilder->Color4fv( vecEndColor.Base() );
pMeshBuilder->AdvanceVertex();
}
void C_EnvMeteorTail::SimulateParticles( CParticleSimulateIterator *pIterator )
{
Particle *pParticle = (Particle*)pIterator->GetFirst();
while ( pParticle )
{
// Update the particle position.
pParticle->m_Pos = GetLocalOrigin();
pParticle = (Particle*)pIterator->GetNext();
}
}
void C_EnvMeteorTail::RenderParticles( CParticleRenderIterator *pIterator )
{
const Particle *pParticle = (const Particle *)pIterator->GetFirst();
while ( pParticle )
{
// Now draw the tail fragments...
Vector4D vecStartColor( 1.0f, 1.0f, 1.0f, 1.0f );
Vector4D vecEndColor( 1.0f, 1.0f, 1.0f, 0.0f );
Vector vecDelta, vecStartPos, vecEndPos;
// Calculate the tail.
Vector vecTailEnd;
vecTailEnd = GetLocalOrigin() + ( m_vecDirection * -m_flSpeed );
// Transform particles into camera space.
TransformParticle( m_pParticleMgr->GetModelView(), GetLocalOrigin(), vecStartPos );
TransformParticle( m_pParticleMgr->GetModelView(), vecTailEnd, vecEndPos );
float sortKey = vecStartPos.z;
// Draw the tail fragment.
VectorSubtract( vecStartPos, vecEndPos, vecDelta );
DrawFragment( pIterator->GetParticleDraw(), vecEndPos, vecDelta, vecEndColor, vecStartColor,
1.0f - vecEndColor[3], 1.0f - vecStartColor[3] );
pParticle = (const Particle *)pIterator->GetNext( sortKey );
}
}
//=============================================================================
//
// Meteor Functions
//
static g_MeteorCounter = 0;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteor::C_EnvMeteor()
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteor::~C_EnvMeteor()
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::ClientThink( void )
{
// Get the current time.
float flTime = gpGlobals->curtime;
// Update the meteor.
if ( m_Meteor.IsInSkybox( flTime ) )
{
if ( m_Meteor.m_nLocation == METEOR_LOCATION_WORLD )
{
WorldToSkyboxThink( flTime );
}
else
{
SkyboxThink( flTime );
}
}
else
{
if ( m_Meteor.m_nLocation == METEOR_LOCATION_SKYBOX )
{
SkyboxToWorldThink( flTime );
}
else
{
WorldThink( flTime );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::SkyboxThink( float flTime )
{
float flDeltaTime = flTime - m_Meteor.m_flStartTime;
if ( flDeltaTime > METEOR_MAX_LIFETIME )
{
Destroy( this );
return;
}
// Check to see if the object is passive or not - act accordingly!
if ( !m_Meteor.IsPassive( flTime ) )
{
// Update meteor position.
Vector origin;
m_Meteor.GetPositionAtTime( flTime, origin );
SetLocalOrigin( origin );
// Update the position of the tail effect.
m_TailEffect.SetLocalOrigin( GetLocalOrigin() );
m_HeadEffect.MeteorHeadThink( GetLocalOrigin(), flTime );
}
// Add the entity to the active list - update!
AddEntity();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::WorldToSkyboxThink( float flTime )
{
// Move the meteor from the world into the skybox.
m_Meteor.ConvertFromWorldToSkybox();
// Destroy the head effect. Recreate it.
m_HeadEffect.Destroy();
m_HeadEffect.Start( m_Meteor.m_vecStartPosition, m_vecTravelDir );
m_HeadEffect.SetSmokeEmission( true );
m_HeadEffect.SetParticleScale( 1.0f / 16.0f );
m_HeadEffect.m_bInitThink = true;
// Update to world model.
SetModel( "models/props/common/meteorites/meteor05.mdl" );
// Update the meteor position (move into the skybox!)
SetLocalOrigin( m_Meteor.m_vecStartPosition );
// Update (think).
SkyboxThink( flTime );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::SkyboxToWorldThink( float flTime )
{
// Move the meteor from the skybox into the world.
m_Meteor.ConvertFromSkyboxToWorld();
// Destroy the head effect. Recreate it.
m_HeadEffect.Destroy();
m_HeadEffect.Start( m_Meteor.m_vecStartPosition, m_vecTravelDir );
m_HeadEffect.SetSmokeEmission( true );
m_HeadEffect.SetParticleScale( 1.0f );
m_HeadEffect.m_bInitThink = true;
// Update to world model.
SetModel( "models/props/common/meteorites/meteor04.mdl" );
SetLocalOrigin( m_Meteor.m_vecStartPosition );
// Update (think).
WorldThink( flTime );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::WorldThink( float flTime )
{
// Update meteor position.
Vector vecEndPosition;
m_Meteor.GetPositionAtTime( flTime, vecEndPosition );
// m_Meteor must return the end position in world space for the trace to work.
Assert( GetMoveParent() == NULL );
// Msg( "Client: Time = %lf, Position: %4.2f %4.2f %4.2f\n", flTime, vecEndPosition.x, vecEndPosition.y, vecEndPosition.z );
// Check to see if we struck the world. If so, cause an explosion.
trace_t trace;
Vector vecMin, vecMax;
GetRenderBounds( vecMin, vecMax );
// NOTE: This code works only if we aren't in hierarchy!!!
Assert( !GetMoveParent() );
CTraceFilterWorldOnly traceFilter;
UTIL_TraceHull( GetAbsOrigin(), vecEndPosition, vecMin, vecMax,
MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
// Collision.
if ( ( trace.fraction < 1.0f ) && !( trace.surface.flags & SURF_SKY ) )
{
// Move up to the end.
Vector vecEnd = GetAbsOrigin() + ( ( vecEndPosition - GetAbsOrigin() ) * trace.fraction );
// Create an explosion effect!
BaseExplosionEffect().Create( vecEnd, 10, 32, TE_EXPLFLAG_NONE );
// Debugging Info!!!!
// debugoverlay->AddBoxOverlay( vecEnd, Vector( -10, -10, -10 ), Vector( 10, 10, 10 ), QAngle( 0.0f, 0.0f, 0.0f ), 255, 0, 0, 0, 100 );
Destroy( this );
return;
}
else
{
// Move to the end.
SetLocalOrigin( vecEndPosition );
}
m_TailEffect.SetLocalOrigin( GetLocalOrigin() );
m_HeadEffect.MeteorHeadThink( GetLocalOrigin(), flTime );
// Add the entity to the active list - update!
AddEntity();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_EnvMeteor *C_EnvMeteor::Create( int nID, int iMeteorType, const Vector &vecOrigin,
const Vector &vecDirection, float flSpeed, float flStartTime,
float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs )
{
C_EnvMeteor *pMeteor = new C_EnvMeteor;
if ( pMeteor )
{
pMeteor->m_Meteor.Init( nID, flStartTime, METEOR_PASSIVE_TIME, vecOrigin, vecDirection, flSpeed, flDamageRadius,
vecTriggerMins, vecTriggerMaxs );
// Initialize the meteor.
pMeteor->InitializeAsClientEntity( "models/props/common/meteorites/meteor05.mdl", RENDER_GROUP_OPAQUE_ENTITY );
// Handle forward simulation.
if ( ( pMeteor->m_Meteor.m_flStartTime + METEOR_MAX_LIFETIME ) < gpGlobals->curtime )
{
Destroy( pMeteor );
}
// Meteor Head and Tail
pMeteor->SetTravelDirection( vecDirection );
pMeteor->m_HeadEffect.SetSmokeEmission( true );
pMeteor->m_HeadEffect.Start( vecOrigin, vecDirection );
pMeteor->m_HeadEffect.SetParticleScale( 1.0f / 16.0f );
pMeteor->m_TailEffect.Start( vecOrigin, vecDirection, flSpeed );
pMeteor->SetNextClientThink( CLIENT_THINK_ALWAYS );
}
return pMeteor;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_EnvMeteor::Destroy( C_EnvMeteor *pMeteor )
{
Assert( pMeteor->GetClientHandle() != INVALID_CLIENTENTITY_HANDLE );
ClientThinkList()->AddToDeleteList( pMeteor->GetClientHandle() );
}