|
|
|
|
//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
|
|
|
//
|
|
|
|
|
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
|
|
|
|
|
//
|
|
|
|
|
// $NoKeywords: $
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#include "cbase.h"
|
|
|
|
|
#include "beam_shared.h"
|
|
|
|
|
#include "ndebugoverlay.h"
|
|
|
|
|
#include "filters.h"
|
|
|
|
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
|
|
// Keeps us from doing strcmps in the tracefilter.
|
|
|
|
|
string_t g_iszPhysicsPropClassname;
|
|
|
|
|
|
|
|
|
|
enum Touch_t
|
|
|
|
|
{
|
|
|
|
|
touch_none = 0,
|
|
|
|
|
touch_player_only,
|
|
|
|
|
touch_npc_only,
|
|
|
|
|
touch_player_or_npc,
|
|
|
|
|
touch_player_or_npc_or_physicsprop,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CEnvBeam : public CBeam
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DECLARE_CLASS( CEnvBeam, CBeam );
|
|
|
|
|
|
|
|
|
|
void Spawn( void );
|
|
|
|
|
void Precache( void );
|
|
|
|
|
void Activate( void );
|
|
|
|
|
|
|
|
|
|
void StrikeThink( void );
|
|
|
|
|
void UpdateThink( void );
|
|
|
|
|
void RandomArea( void );
|
|
|
|
|
void RandomPoint( const Vector &vecSrc );
|
|
|
|
|
void Zap( const Vector &vecSrc, const Vector &vecDest );
|
|
|
|
|
|
|
|
|
|
void Strike( void );
|
|
|
|
|
|
|
|
|
|
bool PassesTouchFilters(CBaseEntity *pOther);
|
|
|
|
|
|
|
|
|
|
void InputTurnOn( inputdata_t &inputdata );
|
|
|
|
|
void InputTurnOff( inputdata_t &inputdata );
|
|
|
|
|
void InputToggle( inputdata_t &inputdata );
|
|
|
|
|
void InputStrikeOnce( inputdata_t &inputdata );
|
|
|
|
|
|
|
|
|
|
void TurnOn( void );
|
|
|
|
|
void TurnOff( void );
|
|
|
|
|
void Toggle( void );
|
|
|
|
|
|
|
|
|
|
const char *GetDecalName( void ){ return STRING( m_iszDecal );}
|
|
|
|
|
|
|
|
|
|
inline bool ServerSide( void )
|
|
|
|
|
{
|
|
|
|
|
if ( m_life == 0 && !HasSpawnFlags(SF_BEAM_RING) )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_DATADESC();
|
|
|
|
|
|
|
|
|
|
void BeamUpdateVars( void );
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
// true if the end point vecline was specified in hammer
|
|
|
|
|
inline bool HasEndPointHandle() { return !m_vEndPointRelative.IsZero(); }
|
|
|
|
|
|
|
|
|
|
int m_active;
|
|
|
|
|
int m_spriteTexture;
|
|
|
|
|
|
|
|
|
|
string_t m_iszStartEntity;
|
|
|
|
|
string_t m_iszEndEntity;
|
|
|
|
|
float m_life;
|
|
|
|
|
float m_boltWidth;
|
|
|
|
|
float m_noiseAmplitude;
|
|
|
|
|
int m_speed;
|
|
|
|
|
float m_restrike;
|
|
|
|
|
string_t m_iszSpriteName;
|
|
|
|
|
int m_frameStart;
|
|
|
|
|
|
|
|
|
|
// endpoint may be optionally specified as a vecline instead of a target entity.
|
|
|
|
|
// note: this mechanism seems rather roundabout, because the parent CBeam has
|
|
|
|
|
// the m_vecEndPos data member; however, the CEnvBeam is programmed to overwrite it
|
|
|
|
|
// each frame with the position of a target entity, so the easiest way to
|
|
|
|
|
// implement this behavior was to put this bit of redundant data in the child
|
|
|
|
|
// class and have it get written back every frame. If this bothers you,
|
|
|
|
|
// please fix it.
|
|
|
|
|
Vector m_vEndPointWorld; // this is the point as read from the level spec; however it's not used, because
|
|
|
|
|
Vector m_vEndPointRelative; // on spawn, endpoint is transformed into local space here.
|
|
|
|
|
|
|
|
|
|
float m_radius;
|
|
|
|
|
|
|
|
|
|
Touch_t m_TouchType;
|
|
|
|
|
string_t m_iFilterName;
|
|
|
|
|
EHANDLE m_hFilter;
|
|
|
|
|
|
|
|
|
|
string_t m_iszDecal;
|
|
|
|
|
|
|
|
|
|
COutputEvent m_OnTouchedByEntity;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LINK_ENTITY_TO_CLASS( env_beam, CEnvBeam );
|
|
|
|
|
|
|
|
|
|
BEGIN_DATADESC( CEnvBeam )
|
|
|
|
|
|
|
|
|
|
DEFINE_FIELD( m_active, FIELD_INTEGER ),
|
|
|
|
|
DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ),
|
|
|
|
|
|
|
|
|
|
DEFINE_KEYFIELD( m_iszStartEntity, FIELD_STRING, "LightningStart" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_iszEndEntity, FIELD_STRING, "LightningEnd" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_vEndPointWorld, FIELD_VECTOR, "targetpoint" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_life, FIELD_FLOAT, "life" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_boltWidth, FIELD_FLOAT, "BoltWidth" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_noiseAmplitude, FIELD_FLOAT, "NoiseAmplitude" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_speed, FIELD_INTEGER, "TextureScroll" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_restrike, FIELD_FLOAT, "StrikeTime" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_iszSpriteName, FIELD_STRING, "texture" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_frameStart, FIELD_INTEGER, "framestart" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "Radius" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_TouchType, FIELD_INTEGER, "TouchType" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_iszDecal, FIELD_STRING, "decalname" ),
|
|
|
|
|
DEFINE_KEYFIELD( m_nClipStyle, FIELD_INTEGER, "ClipStyle" ),
|
|
|
|
|
|
|
|
|
|
DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
|
|
|
|
|
|
|
|
|
|
// Function Pointers
|
|
|
|
|
DEFINE_FUNCTION( StrikeThink ),
|
|
|
|
|
DEFINE_FUNCTION( UpdateThink ),
|
|
|
|
|
|
|
|
|
|
// Input functions
|
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
|
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
|
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "StrikeOnce", InputStrikeOnce ),
|
|
|
|
|
|
|
|
|
|
DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ),
|
|
|
|
|
|
|
|
|
|
END_DATADESC()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::Spawn( void )
|
|
|
|
|
{
|
|
|
|
|
if ( !m_iszSpriteName )
|
|
|
|
|
{
|
|
|
|
|
SetThink( &CEnvBeam::SUB_Remove );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseClass::Spawn();
|
|
|
|
|
|
|
|
|
|
m_noiseAmplitude = MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude);
|
|
|
|
|
|
|
|
|
|
// Check for tapering
|
|
|
|
|
if ( HasSpawnFlags( SF_BEAM_TAPEROUT ) )
|
|
|
|
|
{
|
|
|
|
|
SetWidth( m_boltWidth );
|
|
|
|
|
SetEndWidth( 0 );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetWidth( m_boltWidth );
|
|
|
|
|
SetEndWidth( GetWidth() ); // Note: EndWidth is not scaled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if a non-targetentity endpoint was specified, transform it into local relative space
|
|
|
|
|
// so it can move along with the base
|
|
|
|
|
if (!m_vEndPointWorld.IsZero())
|
|
|
|
|
{
|
|
|
|
|
WorldToEntitySpace( m_vEndPointWorld, &m_vEndPointRelative );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_vEndPointRelative.Zero();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( ServerSide() )
|
|
|
|
|
{
|
|
|
|
|
SetThink( &CEnvBeam::UpdateThink );
|
|
|
|
|
SetNextThink( gpGlobals->curtime );
|
|
|
|
|
SetFireTime( gpGlobals->curtime );
|
|
|
|
|
|
|
|
|
|
if ( GetEntityName() != NULL_STRING )
|
|
|
|
|
{
|
|
|
|
|
if ( !(m_spawnflags & SF_BEAM_STARTON) )
|
|
|
|
|
{
|
|
|
|
|
AddEffects( EF_NODRAW );
|
|
|
|
|
m_active = 0;
|
|
|
|
|
SetNextThink( TICK_NEVER_THINK );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_active = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_active = 0;
|
|
|
|
|
if ( !GetEntityName() || FBitSet(m_spawnflags, SF_BEAM_STARTON) )
|
|
|
|
|
{
|
|
|
|
|
SetThink( &CEnvBeam::StrikeThink );
|
|
|
|
|
SetNextThink( gpGlobals->curtime + 1.0f );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::Precache( void )
|
|
|
|
|
{
|
|
|
|
|
if ( !Q_stristr( STRING(m_iszSpriteName), ".vmt" ) )
|
|
|
|
|
{
|
|
|
|
|
// HACK/YWB: This was almost always the laserbeam.spr, so alloc'ing the name a second time with the proper extension isn't going to
|
|
|
|
|
// kill us on memrory.
|
|
|
|
|
//Warning( "Level Design Error: %s (%i:%s) Sprite name (%s) missing .vmt extension!\n",
|
|
|
|
|
// STRING( m_iClassname ), entindex(), GetEntityName(), STRING(m_iszSpriteName) );
|
|
|
|
|
|
|
|
|
|
char fixedname[ 512 ];
|
|
|
|
|
Q_strncpy( fixedname, STRING( m_iszSpriteName ), sizeof( fixedname ) );
|
|
|
|
|
|
|
|
|
|
Q_SetExtension( fixedname, ".vmt", sizeof( fixedname ) );
|
|
|
|
|
|
|
|
|
|
m_iszSpriteName = AllocPooledString( fixedname );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_iszPhysicsPropClassname = AllocPooledString( "prop_physics" );
|
|
|
|
|
|
|
|
|
|
m_spriteTexture = PrecacheModel( STRING(m_iszSpriteName) );
|
|
|
|
|
BaseClass::Precache();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::Activate( void )
|
|
|
|
|
{
|
|
|
|
|
// Get a handle to my filter entity if there is one
|
|
|
|
|
if (m_iFilterName != NULL_STRING)
|
|
|
|
|
{
|
|
|
|
|
m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseClass::Activate();
|
|
|
|
|
|
|
|
|
|
if ( ServerSide() )
|
|
|
|
|
BeamUpdateVars();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Input handler to turn the lightning on either continually or for
|
|
|
|
|
// interval refiring.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::InputTurnOn( inputdata_t &inputdata )
|
|
|
|
|
{
|
|
|
|
|
if ( !m_active )
|
|
|
|
|
{
|
|
|
|
|
TurnOn();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Input handler to turn the lightning off.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::InputTurnOff( inputdata_t &inputdata )
|
|
|
|
|
{
|
|
|
|
|
if ( m_active )
|
|
|
|
|
{
|
|
|
|
|
TurnOff();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Input handler to toggle the lightning on/off.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::InputToggle( inputdata_t &inputdata )
|
|
|
|
|
{
|
|
|
|
|
if ( m_active )
|
|
|
|
|
{
|
|
|
|
|
TurnOff();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TurnOn();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Input handler for making the beam strike once. This will not affect
|
|
|
|
|
// any interval refiring that might be going on. If the lifetime is set
|
|
|
|
|
// to zero (infinite) it will turn on and stay on.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::InputStrikeOnce( inputdata_t &inputdata )
|
|
|
|
|
{
|
|
|
|
|
Strike();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Turns the lightning on. If it is set for interval refiring, it will
|
|
|
|
|
// begin doing so. If it is set to be continually on, it will do so.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::TurnOn( void )
|
|
|
|
|
{
|
|
|
|
|
m_active = 1;
|
|
|
|
|
|
|
|
|
|
if ( ServerSide() )
|
|
|
|
|
{
|
|
|
|
|
RemoveEffects( EF_NODRAW );
|
|
|
|
|
DoSparks( GetAbsStartPos(), GetAbsEndPos() );
|
|
|
|
|
|
|
|
|
|
SetThink( &CEnvBeam::UpdateThink );
|
|
|
|
|
SetNextThink( gpGlobals->curtime );
|
|
|
|
|
SetFireTime( gpGlobals->curtime );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetThink( &CEnvBeam::StrikeThink );
|
|
|
|
|
SetNextThink( gpGlobals->curtime );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::TurnOff( void )
|
|
|
|
|
{
|
|
|
|
|
m_active = 0;
|
|
|
|
|
|
|
|
|
|
if ( ServerSide() )
|
|
|
|
|
{
|
|
|
|
|
AddEffects( EF_NODRAW );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetNextThink( TICK_NEVER_THINK );
|
|
|
|
|
SetThink( NULL );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Think function for striking at intervals.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::StrikeThink( void )
|
|
|
|
|
{
|
|
|
|
|
if ( m_life != 0 )
|
|
|
|
|
{
|
|
|
|
|
if ( m_spawnflags & SF_BEAM_RANDOM )
|
|
|
|
|
SetNextThink( gpGlobals->curtime + m_life + random->RandomFloat( 0, m_restrike ) );
|
|
|
|
|
else
|
|
|
|
|
SetNextThink( gpGlobals->curtime + m_life + m_restrike );
|
|
|
|
|
}
|
|
|
|
|
m_active = 1;
|
|
|
|
|
|
|
|
|
|
if (!m_iszEndEntity && !HasEndPointHandle())
|
|
|
|
|
{
|
|
|
|
|
if (!m_iszStartEntity)
|
|
|
|
|
{
|
|
|
|
|
RandomArea( );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
|
|
|
|
|
if (pStart != NULL)
|
|
|
|
|
{
|
|
|
|
|
RandomPoint( pStart->GetAbsOrigin() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Msg( "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Strike();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Strikes once for its configured lifetime.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::Strike( void )
|
|
|
|
|
{
|
|
|
|
|
CBroadcastRecipientFilter filter;
|
|
|
|
|
|
|
|
|
|
CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
|
|
|
|
|
CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) );
|
|
|
|
|
|
|
|
|
|
// if the end entity is missing, we use the Hammer-specified vector offset instead.
|
|
|
|
|
bool bEndPointFromEntity = pEnd != NULL;
|
|
|
|
|
|
|
|
|
|
if ( pStart == NULL || ( !bEndPointFromEntity && !HasEndPointHandle() ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Vector vEndPointLocation;
|
|
|
|
|
if ( bEndPointFromEntity )
|
|
|
|
|
{
|
|
|
|
|
vEndPointLocation = pEnd->GetAbsOrigin() ;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
EntityToWorldSpace( m_vEndPointRelative, &vEndPointLocation );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED );
|
|
|
|
|
|
|
|
|
|
bool pointStart = IsStaticPointEntity( pStart );
|
|
|
|
|
bool pointEnd = !bEndPointFromEntity || IsStaticPointEntity( pEnd );
|
|
|
|
|
|
|
|
|
|
if ( pointStart || pointEnd )
|
|
|
|
|
{
|
|
|
|
|
if ( m_spawnflags & SF_BEAM_RING )
|
|
|
|
|
{
|
|
|
|
|
// don't work
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
te->BeamEntPoint( filter, 0.0,
|
|
|
|
|
pointStart ? 0 : pStart->entindex(),
|
|
|
|
|
pointStart ? &pStart->GetAbsOrigin() : NULL,
|
|
|
|
|
pointEnd ? 0 : pEnd->entindex(),
|
|
|
|
|
pointEnd ? &vEndPointLocation : NULL,
|
|
|
|
|
m_spriteTexture,
|
|
|
|
|
0, // No halo
|
|
|
|
|
m_frameStart,
|
|
|
|
|
(int)m_flFrameRate,
|
|
|
|
|
m_life,
|
|
|
|
|
m_boltWidth,
|
|
|
|
|
m_boltWidth, // End width
|
|
|
|
|
0, // No fade
|
|
|
|
|
m_noiseAmplitude,
|
|
|
|
|
m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a,
|
|
|
|
|
m_speed );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ( m_spawnflags & SF_BEAM_RING)
|
|
|
|
|
{
|
|
|
|
|
te->BeamRing( filter, 0.0,
|
|
|
|
|
pStart->entindex(),
|
|
|
|
|
pEnd->entindex(),
|
|
|
|
|
m_spriteTexture,
|
|
|
|
|
0, // No halo
|
|
|
|
|
m_frameStart,
|
|
|
|
|
(int)m_flFrameRate,
|
|
|
|
|
m_life,
|
|
|
|
|
m_boltWidth,
|
|
|
|
|
0, // No spread
|
|
|
|
|
m_noiseAmplitude,
|
|
|
|
|
m_clrRender->r,
|
|
|
|
|
m_clrRender->g,
|
|
|
|
|
m_clrRender->b,
|
|
|
|
|
m_clrRender->a,
|
|
|
|
|
m_speed );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
te->BeamEnts( filter, 0.0,
|
|
|
|
|
pStart->entindex(),
|
|
|
|
|
pEnd->entindex(),
|
|
|
|
|
m_spriteTexture,
|
|
|
|
|
0, // No halo
|
|
|
|
|
m_frameStart,
|
|
|
|
|
(int)m_flFrameRate,
|
|
|
|
|
m_life,
|
|
|
|
|
m_boltWidth,
|
|
|
|
|
m_boltWidth, // End width
|
|
|
|
|
0, // No fade
|
|
|
|
|
m_noiseAmplitude,
|
|
|
|
|
m_clrRender->r,
|
|
|
|
|
m_clrRender->g,
|
|
|
|
|
m_clrRender->b,
|
|
|
|
|
m_clrRender->a,
|
|
|
|
|
m_speed );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoSparks( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin() );
|
|
|
|
|
if ( m_flDamage > 0 )
|
|
|
|
|
{
|
|
|
|
|
trace_t tr;
|
|
|
|
|
UTIL_TraceLine( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
|
|
|
|
|
BeamDamageInstant( &tr, m_flDamage );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CTraceFilterPlayersNPCs : public ITraceFilter
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
|
|
|
|
|
{
|
|
|
|
|
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
|
|
|
|
|
if ( pEntity )
|
|
|
|
|
{
|
|
|
|
|
if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
virtual TraceType_t GetTraceType() const
|
|
|
|
|
{
|
|
|
|
|
return TRACE_ENTITIES_ONLY;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CTraceFilterPlayersNPCsPhysicsProps : public ITraceFilter
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
|
|
|
|
|
{
|
|
|
|
|
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
|
|
|
|
|
if ( pEntity )
|
|
|
|
|
{
|
|
|
|
|
if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() || pEntity->m_iClassname == g_iszPhysicsPropClassname )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
virtual TraceType_t GetTraceType() const
|
|
|
|
|
{
|
|
|
|
|
return TRACE_ENTITIES_ONLY;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
bool CEnvBeam::PassesTouchFilters(CBaseEntity *pOther)
|
|
|
|
|
{
|
|
|
|
|
bool fPassedSoFar = false;
|
|
|
|
|
|
|
|
|
|
// Touched some player or NPC!
|
|
|
|
|
if( m_TouchType != touch_npc_only )
|
|
|
|
|
{
|
|
|
|
|
if( pOther->IsPlayer() )
|
|
|
|
|
{
|
|
|
|
|
fPassedSoFar = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( m_TouchType != touch_player_only )
|
|
|
|
|
{
|
|
|
|
|
if( pOther->IsNPC() )
|
|
|
|
|
{
|
|
|
|
|
fPassedSoFar = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( m_TouchType == touch_player_or_npc_or_physicsprop )
|
|
|
|
|
{
|
|
|
|
|
if( pOther->m_iClassname == g_iszPhysicsPropClassname )
|
|
|
|
|
{
|
|
|
|
|
fPassedSoFar = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( fPassedSoFar )
|
|
|
|
|
{
|
|
|
|
|
CBaseFilter* pFilter = (CBaseFilter*)(m_hFilter.Get());
|
|
|
|
|
return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::UpdateThink( void )
|
|
|
|
|
{
|
|
|
|
|
// Apply damage every 1/10th of a second.
|
|
|
|
|
if ( ( m_flDamage > 0 ) && ( gpGlobals->curtime >= m_flFireTime + 0.1 ) )
|
|
|
|
|
{
|
|
|
|
|
trace_t tr;
|
|
|
|
|
UTIL_TraceLine( GetAbsStartPos(), GetAbsEndPos(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
|
|
|
|
|
BeamDamage( &tr );
|
|
|
|
|
// BeamDamage calls RelinkBeam, so no need to call it again.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RelinkBeam();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( m_TouchType != touch_none )
|
|
|
|
|
{
|
|
|
|
|
trace_t tr;
|
|
|
|
|
Ray_t ray;
|
|
|
|
|
ray.Init( GetAbsStartPos(), GetAbsEndPos() );
|
|
|
|
|
|
|
|
|
|
if( m_TouchType == touch_player_or_npc_or_physicsprop )
|
|
|
|
|
{
|
|
|
|
|
CTraceFilterPlayersNPCsPhysicsProps traceFilter;
|
|
|
|
|
enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CTraceFilterPlayersNPCs traceFilter;
|
|
|
|
|
enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( tr.fraction != 1.0 && PassesTouchFilters( tr.m_pEnt ) )
|
|
|
|
|
{
|
|
|
|
|
m_OnTouchedByEntity.FireOutput( tr.m_pEnt, this, 0 );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetNextThink( gpGlobals->curtime );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// Input : &vecSrc -
|
|
|
|
|
// &vecDest -
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::Zap( const Vector &vecSrc, const Vector &vecDest )
|
|
|
|
|
{
|
|
|
|
|
CBroadcastRecipientFilter filter;
|
|
|
|
|
|
|
|
|
|
te->BeamPoints( filter, 0.0,
|
|
|
|
|
&vecSrc,
|
|
|
|
|
&vecDest,
|
|
|
|
|
m_spriteTexture,
|
|
|
|
|
0, // No halo
|
|
|
|
|
m_frameStart,
|
|
|
|
|
(int)m_flFrameRate,
|
|
|
|
|
m_life,
|
|
|
|
|
m_boltWidth,
|
|
|
|
|
m_boltWidth, // End width
|
|
|
|
|
0, // No fade
|
|
|
|
|
m_noiseAmplitude,
|
|
|
|
|
m_clrRender->r,
|
|
|
|
|
m_clrRender->g,
|
|
|
|
|
m_clrRender->b,
|
|
|
|
|
m_clrRender->a,
|
|
|
|
|
m_speed );
|
|
|
|
|
|
|
|
|
|
DoSparks( vecSrc, vecDest );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::RandomArea( void )
|
|
|
|
|
{
|
|
|
|
|
int iLoops = 0;
|
|
|
|
|
|
|
|
|
|
for (iLoops = 0; iLoops < 10; iLoops++)
|
|
|
|
|
{
|
|
|
|
|
Vector vecSrc = GetAbsOrigin();
|
|
|
|
|
|
|
|
|
|
Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
|
|
|
|
|
VectorNormalize( vecDir1 );
|
|
|
|
|
trace_t tr1;
|
|
|
|
|
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
|
|
|
|
|
|
|
|
|
|
if (tr1.fraction == 1.0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Vector vecDir2;
|
|
|
|
|
do {
|
|
|
|
|
vecDir2 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
|
|
|
|
|
} while (DotProduct(vecDir1, vecDir2 ) > 0);
|
|
|
|
|
VectorNormalize( vecDir2 );
|
|
|
|
|
trace_t tr2;
|
|
|
|
|
UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
|
|
|
|
|
|
|
|
|
|
if (tr2.fraction == 1.0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ((tr1.endpos - tr2.endpos).Length() < m_radius * 0.1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
UTIL_TraceLine( tr1.endpos, tr2.endpos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
|
|
|
|
|
|
|
|
|
|
if (tr2.fraction != 1.0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Zap( tr1.endpos, tr2.endpos );
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// Input : vecSrc -
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::RandomPoint( const Vector &vecSrc )
|
|
|
|
|
{
|
|
|
|
|
int iLoops = 0;
|
|
|
|
|
|
|
|
|
|
for (iLoops = 0; iLoops < 10; iLoops++)
|
|
|
|
|
{
|
|
|
|
|
Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
|
|
|
|
|
VectorNormalize( vecDir1 );
|
|
|
|
|
trace_t tr1;
|
|
|
|
|
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
|
|
|
|
|
|
|
|
|
|
if ((tr1.endpos - vecSrc).Length() < m_radius * 0.1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (tr1.fraction == 1.0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Zap( vecSrc, tr1.endpos );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEnvBeam::BeamUpdateVars( void )
|
|
|
|
|
{
|
|
|
|
|
CBaseEntity *pStart = gEntList.FindEntityByName( NULL, m_iszStartEntity );
|
|
|
|
|
CBaseEntity *pEnd = gEntList.FindEntityByName( NULL, m_iszEndEntity );
|
|
|
|
|
|
|
|
|
|
// if the end entity is missing, we use the Hammer-specified vector offset instead.
|
|
|
|
|
bool bEndPointFromEntity = pEnd != NULL;
|
|
|
|
|
|
|
|
|
|
if (( pStart == NULL ) || ( !bEndPointFromEntity && !HasEndPointHandle() ))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector vEndPointPos;
|
|
|
|
|
if ( bEndPointFromEntity )
|
|
|
|
|
{
|
|
|
|
|
vEndPointPos = pEnd->GetAbsOrigin();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
EntityToWorldSpace( m_vEndPointRelative, &vEndPointPos );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_nNumBeamEnts = 2;
|
|
|
|
|
|
|
|
|
|
m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED );
|
|
|
|
|
|
|
|
|
|
// NOTE: If the end entity is the beam itself (and the start entity
|
|
|
|
|
// isn't *also* the beam itself, we've got problems. This is a problem
|
|
|
|
|
// because SetAbsStartPos actually sets the entity's origin.
|
|
|
|
|
if ( ( pEnd == this ) && ( pStart != this ) )
|
|
|
|
|
{
|
|
|
|
|
DevMsg("env_beams cannot have the end entity be the beam itself\n"
|
|
|
|
|
"unless the start entity is also the beam itself!\n" );
|
|
|
|
|
Assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetModelName( m_iszSpriteName );
|
|
|
|
|
SetTexture( m_spriteTexture );
|
|
|
|
|
|
|
|
|
|
SetType( BEAM_ENTPOINT );
|
|
|
|
|
|
|
|
|
|
if ( IsStaticPointEntity( pStart ) )
|
|
|
|
|
{
|
|
|
|
|
SetAbsStartPos( pStart->GetAbsOrigin() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetStartEntity( pStart );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !bEndPointFromEntity || IsStaticPointEntity( pEnd ) )
|
|
|
|
|
{
|
|
|
|
|
SetAbsEndPos( vEndPointPos );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetEndEntity( pEnd );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RelinkBeam();
|
|
|
|
|
|
|
|
|
|
SetWidth( MIN(MAX_BEAM_WIDTH, m_boltWidth) );
|
|
|
|
|
SetNoise( MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude) );
|
|
|
|
|
SetFrame( m_frameStart );
|
|
|
|
|
SetScrollRate( m_speed );
|
|
|
|
|
if ( m_spawnflags & SF_BEAM_SHADEIN )
|
|
|
|
|
{
|
|
|
|
|
SetBeamFlags( FBEAM_SHADEIN );
|
|
|
|
|
}
|
|
|
|
|
else if ( m_spawnflags & SF_BEAM_SHADEOUT )
|
|
|
|
|
{
|
|
|
|
|
SetBeamFlags( FBEAM_SHADEOUT );
|
|
|
|
|
}
|
|
|
|
|
}
|