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.
816 lines
20 KiB
816 lines
20 KiB
//========= Copyright © 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 ); |
|
} |
|
}
|
|
|