Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

1201 lines
32 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "beam_shared.h"
#include "decals.h"
#include "model_types.h"
#include "IEffects.h"
#include "util_shared.h"
#if !defined( CLIENT_DLL )
#include "ndebugoverlay.h"
#include "sendproxy.h"
#else
#include "iviewrender_beams.h"
#include "c_pixel_visibility.h"
#include "iclientmode.h"
#include "viewrender.h"
#include "view.h"
#ifdef PORTAL
#include "c_prop_portal.h"
#endif //ifdef PORTAL
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define BEAM_DEFAULT_HALO_SCALE 10
#if !defined( CLIENT_DLL )
// Lightning target, just alias landmark
class CInfoTarget : public CPointEntity
{
public:
DECLARE_CLASS( CInfoTarget, CPointEntity );
void Spawn( void );
};
//info targets are like point entities except you can force them to spawn on the client
void CInfoTarget::Spawn( void )
{
BaseClass::Spawn();
if ( HasSpawnFlags(0x01) )
{
SetEFlags( EFL_FORCE_CHECK_TRANSMIT );
}
}
LINK_ENTITY_TO_CLASS( info_target, CInfoTarget );
#endif
//-----------------------------------------------------------------------------
// Purpose: Returns true if the given entity is a fixed target for lightning.
//-----------------------------------------------------------------------------
bool IsStaticPointEntity( CBaseEntity *pEnt )
{
if ( pEnt->GetMoveParent() )
return false;
if ( !pEnt->GetModelIndex() )
return 1;
if ( FClassnameIs( pEnt, "info_target" ) || FClassnameIs( pEnt, "info_landmark" ) ||
FClassnameIs( pEnt, "path_corner" ) )
return true;
return false;
}
#if defined( CLIENT_DLL )
extern bool ComputeBeamEntPosition( CBaseEntity *pEnt, int nAttachment, bool bInterpretAttachmentIndexAsHitboxIndex, Vector& pt );
void RecvProxy_Beam_ScrollSpeed( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_Beam *beam;
float val;
// Unpack the data.
val = pData->m_Value.m_Float;
val *= 0.1;
beam = ( C_Beam * )pStruct;
Assert( pOut == &beam->m_fSpeed );
beam->m_fSpeed = val;
}
#else
#if !defined( NO_ENTITY_PREDICTION )
static void* SendProxy_SendPredictableId( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
{
CBaseEntity *pEntity = (CBaseEntity *)pStruct;
if ( !pEntity || !pEntity->m_PredictableID->IsActive() )
return NULL;
if ( !pEntity->GetOwnerEntity() )
return NULL;
CBaseEntity *owner = pEntity->GetOwnerEntity();
if ( !owner || !owner->IsPlayer() )
return NULL;
CBasePlayer *pOwner = static_cast< CBasePlayer * >( owner );
if ( !pOwner )
return NULL;
int id_player_index = pEntity->m_PredictableID->GetPlayer();
int owner_player_index = pOwner->entindex() - 1;
// Only send to owner player
// FIXME: Is this ever not the case due to the SetOnly call?
if ( id_player_index != owner_player_index )
return NULL;
pRecipients->SetOnly( owner_player_index );
return ( void * )pVarData;
}
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendPredictableId );
#endif
#endif
LINK_ENTITY_TO_CLASS( beam, CBeam );
// This table encodes the CBeam data.
IMPLEMENT_NETWORKCLASS_ALIASED( Beam, DT_Beam )
#if !defined( NO_ENTITY_PREDICTION )
BEGIN_NETWORK_TABLE_NOBASE( CBeam, DT_BeamPredictableId )
#if !defined( CLIENT_DLL )
SendPropPredictableId( SENDINFO( m_PredictableID ) ),
SendPropInt( SENDINFO( m_bIsPlayerSimulated ), 1, SPROP_UNSIGNED ),
#else
RecvPropPredictableId( RECVINFO( m_PredictableID ) ),
RecvPropInt( RECVINFO( m_bIsPlayerSimulated ) ),
#endif
END_NETWORK_TABLE()
#endif
BEGIN_NETWORK_TABLE_NOBASE( CBeam, DT_Beam )
#if !defined( CLIENT_DLL )
SendPropInt (SENDINFO(m_nBeamType), Q_log2(NUM_BEAM_TYPES)+1, SPROP_UNSIGNED ),
SendPropInt (SENDINFO(m_nBeamFlags), NUM_BEAM_FLAGS, SPROP_UNSIGNED ),
SendPropInt (SENDINFO(m_nNumBeamEnts ), 5, SPROP_UNSIGNED ),
SendPropArray3
(
SENDINFO_ARRAY3(m_hAttachEntity),
SendPropEHandle( SENDINFO_ARRAY(m_hAttachEntity) )
),
SendPropArray3
(
SENDINFO_ARRAY3(m_nAttachIndex),
SendPropInt( SENDINFO_ARRAY(m_nAttachIndex), ATTACHMENT_INDEX_BITS, SPROP_UNSIGNED)
),
SendPropInt (SENDINFO(m_nHaloIndex), 16, SPROP_UNSIGNED ),
SendPropFloat (SENDINFO(m_fHaloScale), 0, SPROP_NOSCALE ),
SendPropFloat (SENDINFO(m_fWidth), 10, SPROP_ROUNDUP, 0.0f, MAX_BEAM_WIDTH ),
SendPropFloat (SENDINFO(m_fEndWidth), 10, SPROP_ROUNDUP, 0.0f, MAX_BEAM_WIDTH ),
SendPropFloat (SENDINFO(m_fFadeLength), 0, SPROP_NOSCALE ),
SendPropFloat (SENDINFO(m_fAmplitude), 8, SPROP_ROUNDDOWN, 0.0f, MAX_BEAM_NOISEAMPLITUDE ),
SendPropFloat (SENDINFO(m_fStartFrame), 8, SPROP_ROUNDDOWN, 0.0f, 256.0f),
SendPropFloat (SENDINFO(m_fSpeed), 8, SPROP_NOSCALE, 0.0f, MAX_BEAM_SCROLLSPEED),
SendPropInt (SENDINFO(m_nRenderFX), 8, SPROP_UNSIGNED ),
SendPropInt (SENDINFO(m_nRenderMode), 8, SPROP_UNSIGNED ),
SendPropFloat (SENDINFO(m_flFrameRate), 10, SPROP_ROUNDUP, -25.0f, 25.0f ),
SendPropFloat (SENDINFO(m_flHDRColorScale), 0, SPROP_NOSCALE, 0.0f, 100.0f ),
SendPropFloat (SENDINFO(m_flFrame), 20, SPROP_ROUNDDOWN | SPROP_CHANGES_OFTEN, 0.0f, 256.0f),
SendPropInt (SENDINFO(m_clrRender), 32, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
SendPropVector (SENDINFO(m_vecEndPos), -1, SPROP_COORD ),
#ifdef PORTAL
SendPropBool (SENDINFO(m_bDrawInMainRender) ),
SendPropBool (SENDINFO(m_bDrawInPortalRender) ),
#endif
SendPropModelIndex(SENDINFO(m_nModelIndex) ),
SendPropVector (SENDINFO(m_vecOrigin), 19, SPROP_CHANGES_OFTEN, MIN_COORD_INTEGER, MAX_COORD_INTEGER),
SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent) ),
SendPropInt (SENDINFO(m_nMinDXLevel), 8, SPROP_UNSIGNED ),
#if !defined( NO_ENTITY_PREDICTION )
SendPropDataTable( "beampredictable_id", 0, &REFERENCE_SEND_TABLE( DT_BeamPredictableId ), SendProxy_SendPredictableId ),
#endif
#else
RecvPropInt (RECVINFO(m_nBeamType)),
RecvPropInt (RECVINFO(m_nBeamFlags)),
RecvPropInt (RECVINFO(m_nNumBeamEnts)),
RecvPropArray3
(
RECVINFO_ARRAY( m_hAttachEntity ),
RecvPropEHandle (RECVINFO(m_hAttachEntity[0]))
),
RecvPropArray3
(
RECVINFO_ARRAY( m_nAttachIndex ),
RecvPropInt (RECVINFO(m_nAttachIndex[0]))
),
RecvPropInt (RECVINFO(m_nHaloIndex)),
RecvPropFloat (RECVINFO(m_fHaloScale)),
RecvPropFloat (RECVINFO(m_fWidth)),
RecvPropFloat (RECVINFO(m_fEndWidth)),
RecvPropFloat (RECVINFO(m_fFadeLength)),
RecvPropFloat (RECVINFO(m_fAmplitude)),
RecvPropFloat (RECVINFO(m_fStartFrame)),
RecvPropFloat (RECVINFO(m_fSpeed), 0, RecvProxy_Beam_ScrollSpeed ),
RecvPropFloat(RECVINFO(m_flFrameRate)),
RecvPropFloat(RECVINFO(m_flHDRColorScale)),
RecvPropInt(RECVINFO(m_clrRender)),
RecvPropInt(RECVINFO(m_nRenderFX)),
RecvPropInt(RECVINFO(m_nRenderMode)),
RecvPropFloat(RECVINFO(m_flFrame)),
RecvPropVector(RECVINFO(m_vecEndPos)),
#ifdef PORTAL
RecvPropBool(RECVINFO(m_bDrawInMainRender) ),
RecvPropBool(RECVINFO(m_bDrawInPortalRender) ),
#endif
RecvPropInt(RECVINFO(m_nModelIndex)),
RecvPropInt(RECVINFO(m_nMinDXLevel)),
RecvPropVector(RECVINFO_NAME(m_vecNetworkOrigin, m_vecOrigin)),
RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ),
#if !defined( NO_ENTITY_PREDICTION )
RecvPropDataTable( "beampredictable_id", 0, 0, &REFERENCE_RECV_TABLE( DT_BeamPredictableId ) ),
#endif
#endif
END_NETWORK_TABLE()
#if !defined( CLIENT_DLL )
BEGIN_DATADESC( CBeam )
DEFINE_FIELD( m_nHaloIndex, FIELD_MODELINDEX ),
DEFINE_FIELD( m_nBeamType, FIELD_INTEGER ),
DEFINE_FIELD( m_nBeamFlags, FIELD_INTEGER ),
DEFINE_FIELD( m_nNumBeamEnts, FIELD_INTEGER ),
DEFINE_ARRAY( m_hAttachEntity, FIELD_EHANDLE, MAX_BEAM_ENTS ),
DEFINE_ARRAY( m_nAttachIndex, FIELD_INTEGER, MAX_BEAM_ENTS ),
DEFINE_FIELD( m_nMinDXLevel, FIELD_INTEGER ),
DEFINE_FIELD( m_fWidth, FIELD_FLOAT ),
DEFINE_FIELD( m_fEndWidth, FIELD_FLOAT ),
DEFINE_FIELD( m_fFadeLength, FIELD_FLOAT ),
DEFINE_FIELD( m_fHaloScale, FIELD_FLOAT ),
DEFINE_FIELD( m_fAmplitude, FIELD_FLOAT ),
DEFINE_FIELD( m_fStartFrame, FIELD_FLOAT ),
DEFINE_FIELD( m_fSpeed, FIELD_FLOAT ),
DEFINE_FIELD( m_flFrameRate, FIELD_FLOAT ),
DEFINE_FIELD( m_flFrame, FIELD_FLOAT ),
DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ),
DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "damage" ),
DEFINE_FIELD( m_flFireTime, FIELD_TIME ),
DEFINE_FIELD( m_vecEndPos, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_hEndEntity, FIELD_EHANDLE ),
DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
#ifdef PORTAL
DEFINE_FIELD( m_bDrawInMainRender, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bDrawInPortalRender, FIELD_BOOLEAN ),
#endif
// Inputs
DEFINE_INPUTFUNC( FIELD_FLOAT, "Width", InputWidth ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Noise", InputNoise ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorRedValue", InputColorRedValue ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorGreenValue", InputColorGreenValue ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorBlueValue", InputColorBlueValue ),
DEFINE_INPUT( m_fSpeed, FIELD_FLOAT, "ScrollSpeed" ),
// don't save this
//DEFINE_FIELD( m_queryHandleHalo, FIELD_ ),
END_DATADESC()
#else
BEGIN_PREDICTION_DATA( CBeam )
DEFINE_PRED_FIELD( m_nBeamType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nNumBeamEnts, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_ARRAY( m_hAttachEntity, FIELD_EHANDLE, MAX_BEAM_ENTS, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_ARRAY( m_nAttachIndex, FIELD_INTEGER, MAX_BEAM_ENTS, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nHaloIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fHaloScale, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fWidth, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fEndWidth, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fFadeLength, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fAmplitude, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fStartFrame, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nRenderFX, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nRenderMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flFrameRate, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flFrame, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_clrRender, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nMinDXLevel, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD_TOL( m_vecEndPos, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
#ifdef PORTAL
DEFINE_PRED_FIELD( m_bDrawInMainRender, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bDrawInPortalRender, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
#endif
DEFINE_PRED_FIELD( m_nModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
DEFINE_PRED_FIELD_TOL( m_vecOrigin, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
//DEFINE_PRED_FIELD( m_pMoveParent, SendProxy_MoveParent ),
//DEFINE_PRED_FIELD( m_flHDRColorScale, SendProxy_HDRColorScale ),
END_PREDICTION_DATA()
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBeam::CBeam( void )
{
#ifdef _DEBUG
// necessary since in debug, we initialize vectors to NAN for debugging
m_vecEndPos.Init();
#endif
m_nMinDXLevel = 0;
m_flHDRColorScale = 1.0f; // default value.
#if !defined( CLIENT_DLL )
m_nDissolveType = -1;
#else
m_queryHandleHalo = 0;
#endif
#ifdef PORTAL
m_bDrawInMainRender = true;
m_bDrawInPortalRender = true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *szModelName -
//-----------------------------------------------------------------------------
void CBeam::SetModel( const char *szModelName )
{
int modelIndex = modelinfo->GetModelIndex( szModelName );
const model_t *pModel = modelinfo->GetModel( modelIndex );
if ( pModel && modelinfo->GetModelType( pModel ) != mod_sprite )
{
Msg( "Setting CBeam to non-sprite model %s\n", szModelName );
}
#if !defined( CLIENT_DLL )
UTIL_SetModel( this, szModelName );
#else
BaseClass::SetModel( szModelName );
#endif
}
void CBeam::Spawn( void )
{
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_NONE ); // Remove model & collisions
SetRenderMode( kRenderTransTexture );
// Opt out of all shadow routines
AddEffects( EF_NOSHADOW | EF_NORECEIVESHADOW );
Precache( );
}
void CBeam::Precache( void )
{
if ( GetOwnerEntity() )
{
SetStartEntity( GetOwnerEntity() );
}
if ( m_hEndEntity.Get() )
{
SetEndEntity( m_hEndEntity );
}
}
void CBeam::SetType( int type )
{
Assert( type < NUM_BEAM_TYPES );
m_nBeamType = type;
}
void CBeam::SetBeamFlags( int flags )
{
Assert( flags < (1 << NUM_BEAM_FLAGS) );
m_nBeamFlags = flags;
}
void CBeam::SetBeamFlag( int flag )
{
m_nBeamFlags |= flag;
}
int CBeam::GetType( void ) const
{
return m_nBeamType;
}
int CBeam::GetBeamFlags( void ) const
{
return m_nBeamFlags;
}
void CBeam::SetStartEntity( CBaseEntity *pEntity )
{
Assert( m_nNumBeamEnts >= 2 );
m_hAttachEntity.Set( 0, pEntity );
SetOwnerEntity( pEntity );
RelinkBeam();
pEntity->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
}
void CBeam::SetEndEntity( CBaseEntity *pEntity )
{
Assert( m_nNumBeamEnts >= 2 );
m_hAttachEntity.Set( m_nNumBeamEnts-1, pEntity );
m_hEndEntity = pEntity;
RelinkBeam();
pEntity->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
}
//-----------------------------------------------------------------------------
// This will change things so the abs position matches the requested spot
//-----------------------------------------------------------------------------
void CBeam::SetAbsStartPos( const Vector &pos )
{
if (!GetMoveParent())
{
SetStartPos( pos );
return;
}
Vector vecLocalPos;
matrix3x4_t worldToBeam;
MatrixInvert( EntityToWorldTransform(), worldToBeam );
VectorTransform( pos, worldToBeam, vecLocalPos );
SetStartPos( vecLocalPos );
}
void CBeam::SetAbsEndPos( const Vector &pos )
{
if (!GetMoveParent())
{
SetEndPos( pos );
return;
}
Vector vecLocalPos;
matrix3x4_t worldToBeam;
MatrixInvert( EntityToWorldTransform(), worldToBeam );
VectorTransform( pos, worldToBeam, vecLocalPos );
SetEndPos( vecLocalPos );
}
#if !defined( CLIENT_DLL )
// These don't take attachments into account
const Vector &CBeam::GetAbsStartPos( void ) const
{
if ( GetType() == BEAM_ENTS && GetStartEntity() )
{
edict_t *pent = engine->PEntityOfEntIndex( GetStartEntity() );
CBaseEntity *ent = CBaseEntity::Instance( pent );
if ( !ent )
{
return GetAbsOrigin();
}
return ent->GetAbsOrigin();
}
return GetAbsOrigin();
}
const Vector &CBeam::GetAbsEndPos( void ) const
{
if ( GetType() != BEAM_POINTS && GetType() != BEAM_HOSE && GetEndEntity() )
{
edict_t *pent = engine->PEntityOfEntIndex( GetEndEntity() );
CBaseEntity *ent = CBaseEntity::Instance( pent );
if ( ent )
return ent->GetAbsOrigin();
}
if (!const_cast<CBeam*>(this)->GetMoveParent())
return m_vecEndPos.Get();
// FIXME: Cache this off?
static Vector vecAbsPos;
VectorTransform( m_vecEndPos, EntityToWorldTransform(), vecAbsPos );
return vecAbsPos;
}
#else
//-----------------------------------------------------------------------------
// Unlike the server, these take attachments into account
//-----------------------------------------------------------------------------
const Vector &C_Beam::GetAbsStartPos( void ) const
{
static Vector vecStartAbsPosition;
if ( GetType() != BEAM_POINTS && GetType() != BEAM_HOSE )
{
if (ComputeBeamEntPosition( m_hAttachEntity[0], m_nAttachIndex[0], false, vecStartAbsPosition ))
return vecStartAbsPosition;
}
return GetAbsOrigin();
}
const Vector &C_Beam::GetAbsEndPos( void ) const
{
static Vector vecEndAbsPosition;
if ( GetType() != BEAM_POINTS && GetType() != BEAM_HOSE )
{
if (ComputeBeamEntPosition( m_hAttachEntity[m_nNumBeamEnts-1], m_nAttachIndex[m_nNumBeamEnts-1], false, vecEndAbsPosition ))
return vecEndAbsPosition;
}
if (!const_cast<C_Beam*>(this)->GetMoveParent())
return m_vecEndPos.Get();
// FIXME: Cache this off?
VectorTransform( m_vecEndPos, EntityToWorldTransform(), vecEndAbsPosition );
return vecEndAbsPosition;
}
#endif
CBeam *CBeam::BeamCreate( const char *pSpriteName, float width )
{
// Create a new entity with CBeam private data
CBeam *pBeam = CREATE_ENTITY( CBeam, "beam" );
pBeam->BeamInit( pSpriteName, width );
return pBeam;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSpriteName -
// &origin -
// animate -
// Output : CSprite
//-----------------------------------------------------------------------------
CBeam *CBeam::BeamCreatePredictable( const char *module, int line, bool persist, const char *pSpriteName, float width, CBasePlayer *pOwner )
{
#if !defined( NO_ENTITY_PREDICTION )
CBeam *pBeam = ( CBeam * )CBaseEntity::CreatePredictedEntityByName( "beam", module, line, persist );
if ( pBeam )
{
pBeam->BeamInit( pSpriteName, width );
pBeam->SetOwnerEntity( pOwner );
pBeam->SetPlayerSimulated( pOwner );
}
return pBeam;
#else
return NULL;
#endif
}
void CBeam::BeamInit( const char *pSpriteName, float width )
{
SetColor( 255, 255, 255 );
SetBrightness( 255 );
SetNoise( 0 );
SetFrame( 0 );
SetScrollRate( 0 );
SetModelName( MAKE_STRING( pSpriteName ) );
SetRenderMode( kRenderTransTexture );
SetTexture( PrecacheModel( pSpriteName ) );
SetWidth( width );
SetEndWidth( width );
SetFadeLength( 0 ); // No fade
for (int i=0;i<MAX_BEAM_ENTS;i++)
{
m_hAttachEntity.Set( i, NULL );
m_nAttachIndex.Set( i, 0 );
}
m_nHaloIndex = 0;
m_fHaloScale = BEAM_DEFAULT_HALO_SCALE;
m_nBeamType = 0;
m_nBeamFlags = 0;
}
void CBeam::PointsInit( const Vector &start, const Vector &end )
{
SetType( BEAM_POINTS );
m_nNumBeamEnts = 2;
SetStartPos( start );
SetEndPos( end );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::HoseInit( const Vector &start, const Vector &direction )
{
SetType( BEAM_HOSE );
m_nNumBeamEnts = 2;
SetStartPos( start );
SetEndPos( direction );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::PointEntInit( const Vector &start, CBaseEntity *pEndEntity )
{
SetType( BEAM_ENTPOINT );
m_nNumBeamEnts = 2;
SetStartPos( start );
SetEndEntity( pEndEntity );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::EntsInit( CBaseEntity *pStartEntity, CBaseEntity *pEndEntity )
{
SetType( BEAM_ENTS );
m_nNumBeamEnts = 2;
SetStartEntity( pStartEntity );
SetEndEntity( pEndEntity );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::LaserInit( CBaseEntity *pStartEntity, CBaseEntity *pEndEntity )
{
SetType( BEAM_LASER );
m_nNumBeamEnts = 2;
SetStartEntity( pStartEntity );
SetEndEntity( pEndEntity );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::SplineInit( int nNumEnts, CBaseEntity** pEntList, int *attachment )
{
if (nNumEnts < 2)
{
Msg("ERROR: Min of 2 ents required for spline beam.\n");
}
else if (nNumEnts > MAX_BEAM_ENTS)
{
Msg("ERROR: Max of %i ents allowed for spline beam.\n",MAX_BEAM_ENTS);
}
SetType( BEAM_SPLINE );
for (int i=0;i<nNumEnts;i++)
{
m_hAttachEntity.Set( i, pEntList[i] );
m_nAttachIndex.Set( i, attachment[i] );
}
m_nNumBeamEnts = nNumEnts;
RelinkBeam();
}
void CBeam::RelinkBeam( void )
{
// FIXME: Why doesn't this just define the absbox too?
// It seems that we don't need to recompute the absbox
// in CBaseEntity::SetObjectCollisionBox, in fact the absbox
// computed there seems way too big
Vector startPos = GetAbsStartPos(), endPos = GetAbsEndPos();
Vector vecAbsExtra1, vecAbsExtra2;
bool bUseExtraPoints = false;
#ifdef PORTAL
CBaseEntity *pStartEntity = GetStartEntityPtr();
CTraceFilterSkipClassname traceFilter( pStartEntity, "prop_energy_ball", COLLISION_GROUP_NONE );
ITraceFilter *pEntityBeamTraceFilter = NULL;
if ( pStartEntity )
pEntityBeamTraceFilter = pStartEntity->GetBeamTraceFilter();
CTraceFilterChain traceFilterChain( &traceFilter, pEntityBeamTraceFilter );
bUseExtraPoints = UTIL_Portal_Trace_Beam( this, startPos, endPos, vecAbsExtra1, vecAbsExtra2, &traceFilterChain );
#endif
// UNDONE: Should we do this to make the boxes smaller?
//SetAbsOrigin( startPos );
Vector vecBeamMin, vecBeamMax;
VectorMin( startPos, endPos, vecBeamMin );
VectorMax( startPos, endPos, vecBeamMax );
if ( bUseExtraPoints )
{
VectorMin( vecBeamMin, vecAbsExtra1, vecBeamMin );
VectorMin( vecBeamMin, vecAbsExtra2, vecBeamMin );
VectorMax( vecBeamMax, vecAbsExtra1, vecBeamMax );
VectorMax( vecBeamMax, vecAbsExtra2, vecBeamMax );
}
SetCollisionBounds( vecBeamMin - GetAbsOrigin(), vecBeamMax - GetAbsOrigin() );
}
CBaseEntity *CBeam::RandomTargetname( const char *szName )
{
#if !defined( CLIENT_DLL )
int total = 0;
CBaseEntity *pEntity = NULL;
CBaseEntity *pNewEntity = NULL;
while ((pNewEntity = gEntList.FindEntityByName( pNewEntity, szName )) != NULL)
{
total++;
if (random->RandomInt(0,total-1) < 1)
pEntity = pNewEntity;
}
return pEntity;
#else
return NULL;
#endif
}
void CBeam::DoSparks( const Vector &start, const Vector &end )
{
#if !defined( CLIENT_DLL )
if ( HasSpawnFlags(SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) )
{
if ( HasSpawnFlags( SF_BEAM_SPARKSTART ) )
{
g_pEffects->Sparks( start );
}
if ( HasSpawnFlags( SF_BEAM_SPARKEND ) )
{
g_pEffects->Sparks( end );
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Damages anything in the beam.
// Input : ptr -
//-----------------------------------------------------------------------------
void CBeam::BeamDamage( trace_t *ptr )
{
RelinkBeam();
#if !defined( CLIENT_DLL )
if ( ptr->fraction != 1.0 && ptr->m_pEnt != NULL )
{
CBaseEntity *pHit = ptr->m_pEnt;
if ( pHit )
{
ClearMultiDamage();
Vector dir = ptr->endpos - GetAbsOrigin();
VectorNormalize( dir );
int nDamageType = DMG_ENERGYBEAM;
#ifndef HL1_DLL
if (m_nDissolveType == 0)
{
nDamageType = DMG_DISSOLVE;
}
else if ( m_nDissolveType > 0 )
{
nDamageType = DMG_DISSOLVE | DMG_SHOCK;
}
#endif
CTakeDamageInfo info( this, this, m_flDamage * (gpGlobals->curtime - m_flFireTime), nDamageType );
CalculateMeleeDamageForce( &info, dir, ptr->endpos );
pHit->DispatchTraceAttack( info, dir, ptr );
ApplyMultiDamage();
if ( HasSpawnFlags( SF_BEAM_DECALS ) )
{
if ( pHit->IsBSPModel() )
{
UTIL_DecalTrace( ptr, GetDecalName() );
}
}
}
}
#endif
m_flFireTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBeam::TurnOn( void )
{
AddEffects( EF_NODRAW );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBeam::TurnOff( void )
{
RemoveEffects( EF_NODRAW );
}
#if !defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Purpose: Input handler for the beam width. Sets the end width based on the
// beam width.
// Input : Beam width in tenths of world units.
//-----------------------------------------------------------------------------
void CBeam::InputWidth( inputdata_t &inputdata )
{
SetWidth( inputdata.value.Float() );
SetEndWidth( inputdata.value.Float() );
}
void CBeam::InputColorRedValue( inputdata_t &inputdata )
{
int nNewColor = clamp( FastFloatToSmallInt(inputdata.value.Float()), 0, 255 );
SetColor( nNewColor, m_clrRender->g, m_clrRender->b );
}
void CBeam::InputColorGreenValue( inputdata_t &inputdata )
{
int nNewColor =clamp( FastFloatToSmallInt(inputdata.value.Float()), 0, 255 );
SetColor( m_clrRender->r, nNewColor, m_clrRender->b );
}
void CBeam::InputColorBlueValue( inputdata_t &inputdata )
{
int nNewColor = clamp( FastFloatToSmallInt(inputdata.value.Float()), 0, 255 );
SetColor( m_clrRender->r, m_clrRender->g, nNewColor );
}
void CBeam::InputNoise( inputdata_t &inputdata )
{
SetNoise( inputdata.value.Float() );
}
int CBeam::UpdateTransmitState( void )
{
// we must call ShouldTransmit() if we have a move parent
if ( GetMoveParent() )
return SetTransmitState( FL_EDICT_FULLCHECK );
return BaseClass::UpdateTransmitState( );
}
void CBeam::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
{
// Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
return;
BaseClass::SetTransmit( pInfo, bAlways );
// Force our attached entities to go too...
for ( int i=0; i < MAX_BEAM_ENTS; ++i )
{
if ( m_hAttachEntity[i].Get() )
{
m_hAttachEntity[i]->SetTransmit( pInfo, bAlways );
}
}
}
int CBeam::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
if ( IsEffectActive( EF_NODRAW ) )
return FL_EDICT_DONTSEND;
// Transmit us with the same rules as our move parent
if ( GetMoveParent() )
{
return GetMoveParent()->ShouldTransmit( pInfo );
}
return BaseClass::ShouldTransmit( pInfo );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Draw any debug text overlays.
// Output : Returns the new text offset from the top.
//-----------------------------------------------------------------------------
int CBeam::DrawDebugTextOverlays(void)
{
#if !defined( CLIENT_DLL )
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
// Print state
char tempstr[512];
Q_snprintf(tempstr, sizeof(tempstr), "start: (%.2f,%.2f,%.2f)", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
EntityText(text_offset,tempstr,0);
text_offset++;
Q_snprintf(tempstr, sizeof(tempstr), "end : (%.2f,%.2f,%.2f)", m_vecEndPos.GetX(), m_vecEndPos.GetY(), m_vecEndPos.GetZ());
EntityText(text_offset,tempstr,0);
text_offset++;
}
return text_offset;
#else
return 0;
#endif
}
#if defined( CLIENT_DLL )
// Purpose:
// Input : isbeingremoved -
// *predicted -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBeam::OnPredictedEntityRemove( bool isbeingremoved, C_BaseEntity *predicted )
{
BaseClass::OnPredictedEntityRemove( isbeingremoved, predicted );
CBeam *beam = dynamic_cast< CBeam * >( predicted );
if ( !beam )
{
// Hrm, we didn't link up to correct type!!!
Assert( 0 );
// Delete right away since it's fucked up
return true;
}
if ( beam->IsEFlagSet( EFL_KILLME ) )
{
// Don't delete right away
AddEFlags( EFL_KILLME );
return false;
}
// Go ahead and delete if it's not short-lived
return true;
}
extern bool g_bRenderingScreenshot;
extern ConVar r_drawviewmodel;
int CBeam::DrawModel( int flags )
{
if ( !m_bReadyToDraw )
return 0;
if ( IsMarkedForDeletion() )
return 0;
if ( CurrentViewID() == VIEW_SHADOW_DEPTH_TEXTURE )
return 0;
#ifdef PORTAL
if ( ( !g_pPortalRender->IsRenderingPortal() && !m_bDrawInMainRender ) ||
( g_pPortalRender->IsRenderingPortal() && !m_bDrawInPortalRender ) )
{
return 0;
}
#endif //#ifdef PORTAL
// Tracker 16432: If rendering a savegame screenshot don't draw beams
// who have viewmodels as their attached entity
if ( g_bRenderingScreenshot || !r_drawviewmodel.GetBool() )
{
// If the beam is attached
for (int i=0;i<MAX_BEAM_ENTS;i++)
{
C_BaseViewModel *vm = dynamic_cast<C_BaseViewModel *>(m_hAttachEntity[i].Get());
if ( vm )
{
return 0;
}
}
}
beams->DrawBeam( this );
return 0;
}
void CBeam::OnDataChanged( DataUpdateType_t updateType )
{
MarkMessageReceived();
// Make sure that the correct model is referenced for this entity
SetModelPointer( modelinfo->GetModel( GetModelIndex() ) );
// Convert weapon world models to viewmodels if they're weapons being carried by the local player
for (int i=0;i<MAX_BEAM_ENTS;i++)
{
C_BaseEntity *pEnt = m_hAttachEntity[i].Get();
if ( pEnt )
{
C_BaseCombatWeapon *pWpn = dynamic_cast<C_BaseCombatWeapon *>(pEnt);
if ( pWpn && pWpn->ShouldDrawUsingViewModel() )
{
C_BasePlayer *player = ToBasePlayer( pWpn->GetOwner() );
// Use GetRenderedWeaponModel() instead?
C_BaseViewModel *pViewModel = player ? player->GetViewModel( 0 ) : NULL;
if ( pViewModel )
{
// Get the viewmodel and use it instead
m_hAttachEntity.Set( i, pViewModel );
}
}
}
}
// Compute the bounds here...
Vector mins, maxs;
ComputeBounds( mins, maxs );
SetCollisionBounds( mins, maxs );
}
bool CBeam::IsTransparent( void )
{
return true;
}
bool CBeam::ShouldDraw()
{
if ( m_nMinDXLevel != 0 )
{
if ( m_nMinDXLevel > g_pMaterialSystemHardwareConfig->GetDXSupportLevel() )
return false;
}
return BaseClass::ShouldDraw();
}
//-----------------------------------------------------------------------------
// Purpose: Adds to beam entity list
//-----------------------------------------------------------------------------
void CBeam::AddEntity( void )
{
// If set to invisible, skip. Do this before resetting the entity pointer so it has
// valid data to decide whether it's visible.
if ( !ShouldDraw() )
{
return;
}
//FIXME: If we're hooked up to an attachment point, then recompute our bounds every frame
if ( m_hAttachEntity[0].Get() || m_hAttachEntity[1].Get() )
{
// Compute the bounds here...
Vector mins, maxs;
ComputeBounds( mins, maxs );
SetCollisionBounds( mins, maxs );
}
MoveToLastReceivedPosition();
}
//-----------------------------------------------------------------------------
// Computes the bounding box of a beam local to the origin of the beam
//-----------------------------------------------------------------------------
void CBeam::ComputeBounds( Vector& mins, Vector& maxs )
{
Vector vecAbsStart = GetAbsStartPos();
Vector vecAbsEnd = GetAbsEndPos();
// May need extra points for creating the min/max bounds
bool bUseExtraPoints = false;
Vector vecAbsExtra1, vecAbsExtra2;
#ifdef PORTAL
CBaseEntity *pStartEntity = GetStartEntityPtr();
CTraceFilterSkipClassname traceFilter( pStartEntity, "prop_energy_ball", COLLISION_GROUP_NONE );
ITraceFilter *pEntityBeamTraceFilter = NULL;
if ( pStartEntity )
pEntityBeamTraceFilter = pStartEntity->GetBeamTraceFilter();
CTraceFilterChain traceFilterChain( &traceFilter, pEntityBeamTraceFilter );
bUseExtraPoints = UTIL_Portal_Trace_Beam( this, vecAbsStart, vecAbsEnd, vecAbsExtra1, vecAbsExtra2, &traceFilterChain );
#endif
switch( GetType() )
{
case BEAM_LASER:
case BEAM_ENTS:
case BEAM_SPLINE:
case BEAM_ENTPOINT:
{
// Compute the bounds here...
Vector attachmentPoint( 0, 0, 0 );
mins.Init( 99999, 99999, 99999 );
maxs.Init( -99999, -99999, -99999 );
for (int i = 0; i < m_nNumBeamEnts; ++i )
{
C_BaseEntity *pTestEnt = m_hAttachEntity[i].Get();
if ( pTestEnt )
{
if ( pTestEnt == this )
{
mins = maxs = GetAbsOrigin();
}
else
{
// We do this so we don't have to calculate attachments (and do expensive bone-setup calculations) on our attachments.
Vector attMins, attMaxs;
m_hAttachEntity[i]->GetRenderBoundsWorldspace( attMins, attMaxs );
mins = mins.Min( attMins );
mins = mins.Min( attMaxs );
maxs = maxs.Max( attMins );
maxs = maxs.Max( attMaxs );
}
//ASSERT_COORD( mins );
//ASSERT_COORD( maxs );
}
else
{
if (i == 0)
{
VectorCopy( vecAbsStart, attachmentPoint );
}
else if (i == 1)
{
VectorCopy( vecAbsEnd, attachmentPoint );
}
else
{
Assert(0);
}
mins = mins.Min( attachmentPoint );
maxs = maxs.Max( attachmentPoint );
}
}
}
break;
case BEAM_POINTS:
default:
{
for (int i = 0; i < 3; ++i)
{
if (vecAbsStart[i] < vecAbsEnd[i])
{
mins[i] = vecAbsStart[i];
maxs[i] = vecAbsEnd[i];
}
else
{
mins[i] = vecAbsEnd[i];
maxs[i] = vecAbsStart[i];
}
}
}
break;
}
if ( bUseExtraPoints )
{
mins = mins.Min( vecAbsExtra1 );
mins = mins.Min( vecAbsExtra2 );
maxs = maxs.Max( vecAbsExtra1 );
maxs = maxs.Max( vecAbsExtra2 );
}
// bloat the bounding box by the width of the beam
float rad = 0.5f * MAX( m_fWidth.Get(), m_fEndWidth.Get() );
Vector vecRad( rad, rad, rad );
mins -= vecRad;
maxs += vecRad;
// Make sure the bounds are measured in *relative coords*
Vector vecAbsOrigin = GetAbsOrigin();
mins -= vecAbsOrigin;
maxs -= vecAbsOrigin;
}
#endif