Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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.
 
 
 
 
 
 

4575 lines
110 KiB

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "customentity.h"
#include "effects.h"
#include "weapons.h"
#include "decals.h"
#include "func_break.h"
#include "shake.h"
#include "player.h" //LRC - footstep stuff
#include "locus.h" //LRC - locus utilities
#include "movewith.h" //LRC - the DesiredThink system
#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired
#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them.
#define SF_FUNNEL_REPEATABLE 2 // allows a funnel to be refired
//LRC - make info_target an entity class in its own right
class CInfoTarget : public CPointEntity
{
public:
void Spawn( void );
void Precache( void );
};
LINK_ENTITY_TO_CLASS( info_target, CInfoTarget );
//LRC- force an info_target to use the sprite null.spr
#define SF_TARGET_HACK_VISIBLE 1
// Landmark class
void CInfoTarget :: Spawn( void )
{
//Precache();
pev->solid = SOLID_NOT;
if (pev->spawnflags & SF_TARGET_HACK_VISIBLE)
{
PRECACHE_MODEL("sprites/null.spr");
SET_MODEL(ENT(pev),"sprites/null.spr");
UTIL_SetSize(pev, g_vecZero, g_vecZero);
}
}
void CInfoTarget :: Precache( void )
{
if (pev->spawnflags & SF_TARGET_HACK_VISIBLE)
PRECACHE_MODEL("sprites/null.spr");
}
class CBubbling : public CBaseEntity
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void EXPORT FizzThink( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
virtual int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
static TYPEDESCRIPTION m_SaveData[];
int m_density;
int m_frequency;
int m_bubbleModel;
int m_state;
virtual STATE GetState( void ) { return m_state?STATE_ON:STATE_OFF; };
};
LINK_ENTITY_TO_CLASS( env_bubbles, CBubbling )
TYPEDESCRIPTION CBubbling::m_SaveData[] =
{
DEFINE_FIELD( CBubbling, m_density, FIELD_INTEGER ),
DEFINE_FIELD( CBubbling, m_frequency, FIELD_INTEGER ),
DEFINE_FIELD( CBubbling, m_state, FIELD_INTEGER ),
// Let spawn restore this!
//DEFINE_FIELD( CBubbling, m_bubbleModel, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CBubbling, CBaseEntity )
#define SF_BUBBLES_STARTOFF 0x0001
void CBubbling::Spawn( void )
{
Precache();
SET_MODEL( ENT( pev ), STRING( pev->model ) ); // Set size
pev->solid = SOLID_NOT; // Remove model & collisions
pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on
pev->rendermode = kRenderTransTexture;
int speed = fabs( pev->speed );
// HACKHACK!!! - Speed in rendercolor
pev->rendercolor.x = speed >> 8;
pev->rendercolor.y = speed & 255;
pev->rendercolor.z = ( pev->speed < 0 ) ? 1 : 0;
if( !( pev->spawnflags & SF_BUBBLES_STARTOFF ) )
{
SetThink( &CBubbling::FizzThink );
SetNextThink( 2.0f );
m_state = 1;
}
else
m_state = 0;
}
void CBubbling::Precache( void )
{
m_bubbleModel = PRECACHE_MODEL( "sprites/bubble.spr" ); // Precache bubble sprite
}
void CBubbling::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( ShouldToggle( useType, m_state ) )
m_state = !m_state;
if( m_state )
{
SetThink( &CBubbling::FizzThink );
SetNextThink( 0.1f );
}
else
{
SetThink( NULL );
DontThink();
}
}
void CBubbling::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "density" ) )
{
m_density = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "frequency" ) )
{
m_frequency = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "current" ) )
{
pev->speed = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
void CBubbling::FizzThink( void )
{
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, VecBModelOrigin( pev ) );
WRITE_BYTE( TE_FIZZ );
WRITE_SHORT( (short)ENTINDEX( edict() ) );
WRITE_SHORT( (short)m_bubbleModel );
WRITE_BYTE( m_density );
MESSAGE_END();
if( m_frequency > 19 ) // frequencies above 20 are treated as 20.
SetNextThink( 0.5f );
else
SetNextThink( 2.5f - ( 0.1f * m_frequency ) );
}
// --------------------------------------------------
//
// Beams
//
// --------------------------------------------------
LINK_ENTITY_TO_CLASS( beam, CBeam )
void CBeam::Spawn( void )
{
pev->solid = SOLID_NOT; // Remove model & collisions
Precache();
}
void CBeam::Precache( void )
{
if( pev->owner )
SetStartEntity( ENTINDEX( pev->owner ) );
if( pev->aiment )
SetEndEntity( ENTINDEX( pev->aiment ) );
}
void CBeam::SetStartEntity( int entityIndex )
{
pev->sequence = ( entityIndex & 0x0FFF ) | ( pev->sequence & 0xF000 );
pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex );
}
void CBeam::SetEndEntity( int entityIndex )
{
pev->skin = ( entityIndex & 0x0FFF ) | ( pev->skin & 0xF000 );
pev->aiment = g_engfuncs.pfnPEntityOfEntIndex( entityIndex );
}
// These don't take attachments into account
const Vector &CBeam::GetStartPos( void )
{
if( GetType() == BEAM_ENTS )
{
edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetStartEntity() );
return pent->v.origin;
}
return pev->origin;
}
const Vector &CBeam::GetEndPos( void )
{
int type = GetType();
if( type == BEAM_POINTS || type == BEAM_HOSE )
{
return pev->angles;
}
edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetEndEntity() );
if( pent )
return pent->v.origin;
return pev->angles;
}
CBeam *CBeam::BeamCreate( const char *pSpriteName, int width )
{
// Create a new entity with CBeam private data
CBeam *pBeam = GetClassPtr( (CBeam *)NULL );
pBeam->pev->classname = MAKE_STRING( "beam" );
pBeam->BeamInit( pSpriteName, width );
return pBeam;
}
void CBeam::BeamInit( const char *pSpriteName, int width )
{
pev->flags |= FL_CUSTOMENTITY;
SetColor( 255, 255, 255 );
SetBrightness( 255 );
SetNoise( 0 );
SetFrame( 0 );
SetScrollRate( 0 );
pev->model = MAKE_STRING( pSpriteName );
SetTexture( PRECACHE_MODEL( pSpriteName ) );
SetWidth( width );
pev->skin = 0;
pev->sequence = 0;
pev->rendermode = 0;
}
void CBeam::PointsInit( const Vector &start, const Vector &end )
{
SetType( BEAM_POINTS );
SetStartPos( start );
SetEndPos( end );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::HoseInit( const Vector &start, const Vector &direction )
{
SetType( BEAM_HOSE );
SetStartPos( start );
SetEndPos( direction );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::PointEntInit( const Vector &start, int endIndex )
{
SetType( BEAM_ENTPOINT );
SetStartPos( start );
SetEndEntity( endIndex );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::EntsInit( int startIndex, int endIndex )
{
SetType( BEAM_ENTS );
SetStartEntity( startIndex );
SetEndEntity( endIndex );
SetStartAttachment( 0 );
SetEndAttachment( 0 );
RelinkBeam();
}
void CBeam::RelinkBeam( void )
{
const Vector &startPos = GetStartPos(), &endPos = GetEndPos();
pev->mins.x = Q_min( startPos.x, endPos.x );
pev->mins.y = Q_min( startPos.y, endPos.y );
pev->mins.z = Q_min( startPos.z, endPos.z );
pev->maxs.x = Q_max( startPos.x, endPos.x );
pev->maxs.y = Q_max( startPos.y, endPos.y );
pev->maxs.z = Q_max( startPos.z, endPos.z );
pev->mins = pev->mins - pev->origin;
pev->maxs = pev->maxs - pev->origin;
UTIL_SetSize( pev, pev->mins, pev->maxs );
UTIL_SetOrigin( this, pev->origin );
}
#if 0
void CBeam::SetObjectCollisionBox( void )
{
const Vector &startPos = GetStartPos(), &endPos = GetEndPos();
pev->absmin.x = min( startPos.x, endPos.x );
pev->absmin.y = min( startPos.y, endPos.y );
pev->absmin.z = min( startPos.z, endPos.z );
pev->absmax.x = max( startPos.x, endPos.x );
pev->absmax.y = max( startPos.y, endPos.y );
pev->absmax.z = max( startPos.z, endPos.z );
}
#endif
void CBeam::TriggerTouch( CBaseEntity *pOther )
{
if( pOther->pev->flags & ( FL_CLIENT | FL_MONSTER ) )
{
if( pev->owner )
{
CBaseEntity *pOwner = CBaseEntity::Instance( pev->owner );
pOwner->Use( pOther, this, USE_TOGGLE, 0 );
}
ALERT( at_console, "Firing targets!!!\n" );
}
}
CBaseEntity *CBeam::RandomTargetname( const char *szName )
{
int total = 0;
CBaseEntity *pEntity = NULL;
CBaseEntity *pNewEntity = NULL;
while( ( pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName ) ) != NULL )
{
total++;
if( RANDOM_LONG( 0, total - 1 ) < 1 )
pEntity = pNewEntity;
}
return pEntity;
}
void CBeam::DoSparks( const Vector &start, const Vector &end )
{
if( pev->spawnflags & ( SF_BEAM_SPARKSTART | SF_BEAM_SPARKEND ) )
{
if( pev->spawnflags & SF_BEAM_SPARKSTART )
{
UTIL_Sparks( start );
}
if( pev->spawnflags & SF_BEAM_SPARKEND )
{
UTIL_Sparks( end );
}
}
}
class CLightning : public CBeam
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void Activate( void );
void EXPORT StrikeThink( void );
void EXPORT TripThink( void );
void RandomArea( void );
void RandomPoint( Vector &vecSrc );
void Zap( const Vector &vecSrc, const Vector &vecDest );
void EXPORT StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
inline BOOL ServerSide( void )
{
if( m_life == 0 && !( pev->spawnflags & SF_BEAM_RING ) )
return TRUE;
return FALSE;
}
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void BeamUpdatePoints( void ); //LRC
void BeamUpdateVars( void );
virtual STATE GetState( void ) { return m_active?STATE_OFF:STATE_ON; };
int m_active;
string_t m_iszStartEntity;
string_t m_iszEndEntity;
float m_life;
int m_boltWidth;
int m_noiseAmplitude;
int m_brightness;
int m_speed;
float m_restrike;
int m_spriteTexture;
string_t m_iszSpriteName;
int m_frameStart;
float m_radius;
};
LINK_ENTITY_TO_CLASS( env_lightning, CLightning )
LINK_ENTITY_TO_CLASS( env_beam, CLightning )
// UNDONE: Jay -- This is only a test
#if _DEBUG
class CTripBeam : public CLightning
{
void Spawn( void );
};
LINK_ENTITY_TO_CLASS( trip_beam, CTripBeam )
void CTripBeam::Spawn( void )
{
CLightning::Spawn();
SetTouch(&CTripBeam:: TriggerTouch );
pev->solid = SOLID_TRIGGER;
RelinkBeam();
}
#endif
TYPEDESCRIPTION CLightning::m_SaveData[] =
{
DEFINE_FIELD( CLightning, m_active, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_iszStartEntity, FIELD_STRING ),
DEFINE_FIELD( CLightning, m_iszEndEntity, FIELD_STRING ),
DEFINE_FIELD( CLightning, m_life, FIELD_FLOAT ),
DEFINE_FIELD( CLightning, m_boltWidth, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_noiseAmplitude, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_brightness, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_speed, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_restrike, FIELD_FLOAT ),
DEFINE_FIELD( CLightning, m_spriteTexture, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_iszSpriteName, FIELD_STRING ),
DEFINE_FIELD( CLightning, m_frameStart, FIELD_INTEGER ),
DEFINE_FIELD( CLightning, m_radius, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CLightning, CBeam )
void CLightning::Spawn( void )
{
if( FStringNull( m_iszSpriteName ) )
{
SetThink(&CLightning:: SUB_Remove );
return;
}
pev->solid = SOLID_NOT; // Remove model & collisions
Precache();
pev->dmgtime = gpGlobals->time;
//LRC- a convenience for mappers. Will this mess anything up?
if (pev->rendercolor == g_vecZero)
pev->rendercolor = Vector(255, 255, 255);
if (pev->frags == 0)
{
pev->frags = DMG_ENERGYBEAM;
}
if( ServerSide() )
{
SetThink( NULL );
if ( pev->dmg != 0 || !FStringNull(pev->target) )
{
SetThink(&CLightning:: TripThink );
SetNextThink( 0.1f );
}
if( pev->targetname )
{
if( !( pev->spawnflags & SF_BEAM_STARTON ) )
{
pev->effects = EF_NODRAW;
m_active = 0;
DontThink();
}
else
m_active = 1;
SetUse( &CLightning::ToggleUse );
}
}
else
{
m_active = 0;
if( !FStringNull( pev->targetname ) )
{
SetUse( &CLightning::StrikeUse );
}
if( FStringNull( pev->targetname ) || FBitSet( pev->spawnflags, SF_BEAM_STARTON ) )
{
SetThink( &CLightning::StrikeThink );
SetNextThink( 1.0f );
}
}
}
void CLightning::Precache( void )
{
m_spriteTexture = PRECACHE_MODEL( STRING( m_iszSpriteName ) );
CBeam::Precache();
}
void CLightning::Activate( void )
{
if( ServerSide() )
BeamUpdateVars();
CBeam::Activate();
}
void CLightning::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "LightningStart" ) )
{
m_iszStartEntity = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "LightningEnd" ) )
{
m_iszEndEntity = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "life" ) )
{
m_life = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "BoltWidth" ) )
{
m_boltWidth = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "NoiseAmplitude" ) )
{
m_noiseAmplitude = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "TextureScroll" ) )
{
m_speed = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "StrikeTime" ) )
{
m_restrike = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "texture" ) )
{
m_iszSpriteName = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "framestart" ) )
{
m_frameStart = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "Radius" ) )
{
m_radius = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "damage" ) )
{
pev->dmg = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CBeam::KeyValue( pkvd );
}
void CLightning::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( !ShouldToggle( useType, m_active ) )
return;
if( m_active )
{
m_active = 0;
SUB_UseTargets( this, USE_OFF, 0 ); //LRC
pev->effects |= EF_NODRAW;
DontThink();
}
else
{
m_active = 1;
SUB_UseTargets( this, USE_ON, 0 ); //LRC
BeamUpdatePoints();
pev->effects &= ~EF_NODRAW;
DoSparks( GetStartPos(), GetEndPos() );
if( pev->dmg > 0 )
{
SetNextThink( 0 );
pev->dmgtime = gpGlobals->time;
}
}
}
void CLightning::StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( !ShouldToggle( useType, m_active ) )
return;
if( m_active )
{
m_active = 0;
SetThink( NULL );
}
else
{
SetThink( &CLightning::StrikeThink );
SetNextThink( 0.1f );
}
if( !FBitSet( pev->spawnflags, SF_BEAM_TOGGLE ) )
SetUse( NULL );
}
int IsPointEntity( CBaseEntity *pEnt )
{
// ALERT(at_console, "IsPE: %s, %d\n", STRING(pEnt->pev->classname), pEnt->pev->modelindex);
if (pEnt->pev->modelindex && !(pEnt->pev->flags & FL_CUSTOMENTITY)) //LRC- follow (almost) any entity that has a model
return 0;
else
return 1;
return 0;
}
void CLightning::StrikeThink( void )
{
if ( m_life != 0 && m_restrike != -1) //LRC non-restriking beams! what an idea!
{
if( pev->spawnflags & SF_BEAM_RANDOM )
SetNextThink( m_life + RANDOM_FLOAT( 0.0f, m_restrike ) );
else
SetNextThink( m_life + m_restrike );
}
m_active = 1;
if( FStringNull( m_iszEndEntity ) )
{
if( FStringNull( m_iszStartEntity ) )
{
RandomArea();
}
else
{
CBaseEntity *pStart = RandomTargetname( STRING( m_iszStartEntity ) );
if( pStart != NULL )
RandomPoint( pStart->pev->origin );
else
ALERT( at_console, "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) );
}
return;
}
CBaseEntity *pStart = RandomTargetname( STRING( m_iszStartEntity ) );
CBaseEntity *pEnd = RandomTargetname( STRING( m_iszEndEntity ) );
if( pStart != NULL && pEnd != NULL )
{
if( IsPointEntity( pStart ) || IsPointEntity( pEnd ) )
{
if( pev->spawnflags & SF_BEAM_RING )
{
// don't work
//LRC- FIXME: tell the user there's a problem.
return;
}
}
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
if( IsPointEntity( pStart ) || IsPointEntity( pEnd ) )
{
if( !IsPointEntity( pEnd ) ) // One point entity must be in pEnd
{
CBaseEntity *pTemp;
pTemp = pStart;
pStart = pEnd;
pEnd = pTemp;
}
if( !IsPointEntity( pStart ) ) // One sided
{
WRITE_BYTE( TE_BEAMENTPOINT );
WRITE_SHORT( pStart->entindex() );
WRITE_COORD( pEnd->pev->origin.x );
WRITE_COORD( pEnd->pev->origin.y );
WRITE_COORD( pEnd->pev->origin.z );
}
else
{
WRITE_BYTE( TE_BEAMPOINTS );
WRITE_COORD( pStart->pev->origin.x );
WRITE_COORD( pStart->pev->origin.y );
WRITE_COORD( pStart->pev->origin.z );
WRITE_COORD( pEnd->pev->origin.x );
WRITE_COORD( pEnd->pev->origin.y );
WRITE_COORD( pEnd->pev->origin.z );
}
}
else
{
if( pev->spawnflags & SF_BEAM_RING )
WRITE_BYTE( TE_BEAMRING );
else
WRITE_BYTE( TE_BEAMENTS );
WRITE_SHORT( pStart->entindex() );
WRITE_SHORT( pEnd->entindex() );
}
WRITE_SHORT( m_spriteTexture );
WRITE_BYTE( m_frameStart ); // framestart
WRITE_BYTE( (int)pev->framerate ); // framerate
WRITE_BYTE( (int)( m_life * 10.0f ) ); // life
WRITE_BYTE( m_boltWidth ); // width
WRITE_BYTE( m_noiseAmplitude ); // noise
WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b
WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b
WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b
WRITE_BYTE( (int)pev->renderamt ); // brightness
WRITE_BYTE( m_speed ); // speed
MESSAGE_END();
DoSparks( pStart->pev->origin, pEnd->pev->origin );
if ( pev->dmg || !FStringNull(pev->target))
{
TraceResult tr;
UTIL_TraceLine( pStart->pev->origin, pEnd->pev->origin, dont_ignore_monsters, NULL, &tr );
if (pev->dmg)
BeamDamageInstant( &tr, pev->dmg );
//LRC - tripbeams
CBaseEntity* pTrip;
if (!FStringNull(pev->target) && (pTrip = GetTripEntity( &tr )) != NULL)
FireTargets(STRING(pev->target), pTrip, this, USE_TOGGLE, 0);
}
}
}
CBaseEntity* CBeam::GetTripEntity( TraceResult *ptr )
{
CBaseEntity* pTrip;
if (ptr->flFraction == 1.0f || ptr->pHit == NULL)
return NULL;
pTrip = CBaseEntity::Instance(ptr->pHit);
if (pTrip == NULL)
return NULL;
if ( FStringNull(pev->netname))
{
if (pTrip->pev->flags & (FL_CLIENT | FL_MONSTER))
return pTrip;
else
return NULL;
}
else if ( FClassnameIs(pTrip->pev, STRING(pev->netname) ))
return pTrip;
else if ( FStrEq( STRING(pTrip->pev->targetname), STRING(pev->netname) ))
return pTrip;
else
return NULL;
}
void CBeam::BeamDamage( TraceResult *ptr )
{
RelinkBeam();
if( ptr->flFraction != 1.0f && ptr->pHit != NULL )
{
CBaseEntity *pHit = CBaseEntity::Instance( ptr->pHit );
if( pHit )
{
if (pev->dmg > 0)
{
ClearMultiDamage();
pHit->TraceAttack( pev, pev->dmg * ( gpGlobals->time - pev->dmgtime ), ( ptr->vecEndPos - pev->origin ).Normalize(), ptr, DMG_ENERGYBEAM );
ApplyMultiDamage( pev, pev );
if( pev->spawnflags & SF_BEAM_DECALS )
{
if( pHit->IsBSPModel() )
UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG( 0, 4 ) );
}
}
else
{
//LRC - beams that heal people
pHit->TakeHealth( -(pev->dmg * (gpGlobals->time - pev->dmgtime)), DMG_GENERIC );
}
}
}
pev->dmgtime = gpGlobals->time;
}
//LRC - used to be DamageThink, but now it's more general.
void CLightning::TripThink( void )
{
SetNextThink( 0.1f );
TraceResult tr;
//ALERT(at_console,"TripThink\n");
if (pev->dmg != 0)
{
UTIL_TraceLine( GetStartPos(), GetEndPos(), dont_ignore_monsters, NULL, &tr );
BeamDamage( &tr );
}
//LRC - tripbeams
if (!FStringNull(pev->target))
{
// nicked from monster_tripmine:
//HACKHACK Set simple box using this really nice global!
gpGlobals->trace_flags = FTRACE_SIMPLEBOX;
UTIL_TraceLine( GetStartPos(), GetEndPos(), dont_ignore_monsters, NULL, &tr );
CBaseEntity *pTrip = GetTripEntity( &tr );
if (pTrip)
{
if (!FBitSet(pev->spawnflags, SF_BEAM_TRIPPED))
{
FireTargets(STRING(pev->target), pTrip, this, USE_TOGGLE, 0);
pev->spawnflags |= SF_BEAM_TRIPPED;
}
}
else
{
pev->spawnflags &= ~SF_BEAM_TRIPPED;
}
}
}
void CLightning::Zap( const Vector &vecSrc, const Vector &vecDest )
{
#if 1
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMPOINTS );
WRITE_COORD( vecSrc.x );
WRITE_COORD( vecSrc.y );
WRITE_COORD( vecSrc.z );
WRITE_COORD( vecDest.x );
WRITE_COORD( vecDest.y );
WRITE_COORD( vecDest.z );
WRITE_SHORT( m_spriteTexture );
WRITE_BYTE( m_frameStart ); // framestart
WRITE_BYTE( (int)pev->framerate ); // framerate
WRITE_BYTE( (int)( m_life * 10.0f ) ); // life
WRITE_BYTE( m_boltWidth ); // width
WRITE_BYTE( m_noiseAmplitude ); // noise
WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b
WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b
WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b
WRITE_BYTE( (int)pev->renderamt ); // brightness
WRITE_BYTE( m_speed ); // speed
MESSAGE_END();
#else
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_LIGHTNING );
WRITE_COORD( vecSrc.x );
WRITE_COORD( vecSrc.y );
WRITE_COORD( vecSrc.z );
WRITE_COORD( vecDest.x );
WRITE_COORD( vecDest.y );
WRITE_COORD( vecDest.z );
WRITE_BYTE( 10 );
WRITE_BYTE( 50 );
WRITE_BYTE( 40 );
WRITE_SHORT( m_spriteTexture );
MESSAGE_END();
#endif
DoSparks( vecSrc, vecDest );
}
void CLightning::RandomArea( void )
{
int iLoops;
for( iLoops = 0; iLoops < 10; iLoops++ )
{
Vector vecSrc = pev->origin;
Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ),RANDOM_FLOAT( -1.0f, 1.0f ) );
vecDir1 = vecDir1.Normalize();
TraceResult tr1;
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT( pev ), &tr1 );
if( tr1.flFraction == 1.0f )
continue;
Vector vecDir2;
do
{
vecDir2 = Vector( RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ),RANDOM_FLOAT( -1.0f, 1.0f ) );
} while( DotProduct( vecDir1, vecDir2 ) > 0.0f );
vecDir2 = vecDir2.Normalize();
TraceResult tr2;
UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, ignore_monsters, ENT( pev ), &tr2 );
if( tr2.flFraction == 1.0f )
continue;
if( ( tr1.vecEndPos - tr2.vecEndPos ).Length() < m_radius * 0.1f )
continue;
UTIL_TraceLine( tr1.vecEndPos, tr2.vecEndPos, ignore_monsters, ENT( pev ), &tr2 );
if( tr2.flFraction != 1.0f )
continue;
Zap( tr1.vecEndPos, tr2.vecEndPos );
break;
}
}
void CLightning::RandomPoint( Vector &vecSrc )
{
int iLoops;
for( iLoops = 0; iLoops < 10; iLoops++ )
{
Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ) );
vecDir1 = vecDir1.Normalize();
TraceResult tr1;
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT( pev ), &tr1 );
if( ( tr1.vecEndPos - vecSrc ).Length() < m_radius * 0.1f )
continue;
if( tr1.flFraction == 1.0f )
continue;
Zap( vecSrc, tr1.vecEndPos );
break;
}
}
// LRC: Called whenever the beam gets turned on, in case an alias changed or one of the points has moved.
void CLightning::BeamUpdatePoints( void )
{
int beamType;
int pointStart, pointEnd;
CBaseEntity *pStart = UTIL_FindEntityByTargetname ( NULL, STRING(m_iszStartEntity) );
CBaseEntity *pEnd = UTIL_FindEntityByTargetname ( NULL, STRING(m_iszEndEntity) );
if (!pStart || !pEnd) return;
pointStart = IsPointEntity( pStart );
pointEnd = IsPointEntity( pEnd );
beamType = BEAM_ENTS;
if( pointStart || pointEnd )
{
if( !pointStart ) // One point entity must be in pStart
{
CBaseEntity *pTemp;
// Swap start & end
pTemp = pStart;
pStart = pEnd;
pEnd = pTemp;
int swap = pointStart;
pointStart = pointEnd;
pointEnd = swap;
}
if( !pointEnd )
beamType = BEAM_ENTPOINT;
else
beamType = BEAM_POINTS;
}
SetType( beamType );
if( beamType == BEAM_POINTS || beamType == BEAM_ENTPOINT || beamType == BEAM_HOSE )
{
SetStartPos( pStart->pev->origin );
if( beamType == BEAM_POINTS || beamType == BEAM_HOSE )
SetEndPos( pEnd->pev->origin );
else
SetEndEntity( ENTINDEX(ENT(pEnd->pev)) );
}
else
{
SetStartEntity( ENTINDEX(ENT(pStart->pev)) );
SetEndEntity( ENTINDEX(ENT(pEnd->pev)) );
}
RelinkBeam();
}
void CLightning::BeamUpdateVars( void )
{
pev->skin = 0;
pev->sequence = 0;
pev->rendermode = 0;
pev->flags |= FL_CUSTOMENTITY;
pev->model = m_iszSpriteName;
SetTexture( m_spriteTexture );
BeamUpdatePoints(); //LRC
SetWidth( m_boltWidth );
SetNoise( m_noiseAmplitude );
SetFrame( m_frameStart );
SetScrollRate( m_speed );
if( pev->spawnflags & SF_BEAM_SHADEIN )
SetFlags( BEAM_FSHADEIN );
else if( pev->spawnflags & SF_BEAM_SHADEOUT )
SetFlags( BEAM_FSHADEOUT );
else if ( pev->spawnflags & SF_BEAM_SOLID )
SetFlags( BEAM_FSOLID );
}
LINK_ENTITY_TO_CLASS( env_laser, CLaser )
TYPEDESCRIPTION CLaser::m_SaveData[] =
{
DEFINE_FIELD( CLaser, m_pStartSprite, FIELD_CLASSPTR ),
DEFINE_FIELD( CLaser, m_pEndSprite, FIELD_CLASSPTR ),
DEFINE_FIELD( CLaser, m_iszStartSpriteName, FIELD_STRING ),
DEFINE_FIELD( CLaser, m_iszEndSpriteName, FIELD_STRING ),
DEFINE_FIELD( CLaser, m_firePosition, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CLaser, m_iProjection, FIELD_INTEGER ),
DEFINE_FIELD( CLaser, m_iStoppedBy, FIELD_INTEGER ),
DEFINE_FIELD( CLaser, m_iszStartPosition, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CLaser, CBeam )
void CLaser::Spawn( void )
{
if( FStringNull( pev->model ) )
{
SetThink(&CLaser:: SUB_Remove );
return;
}
pev->solid = SOLID_NOT; // Remove model & collisions
Precache();
SetThink( &CLaser::StrikeThink );
pev->flags |= FL_CUSTOMENTITY;
}
void CLaser::PostSpawn( void )
{
if ( m_iszStartSpriteName )
{
//LRC: allow the spritename to be the name of an env_sprite
CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(m_iszStartSpriteName));
if (pTemp == NULL)
{
m_pStartSprite = CSprite::SpriteCreate( STRING(m_iszStartSpriteName), pev->origin, TRUE );
if (m_pStartSprite)
m_pStartSprite->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx );
}
else if (!FClassnameIs(pTemp->pev, "env_sprite"))
{
ALERT(at_error, "env_laser \"%s\" found startsprite %s, but can't use: not an env_sprite\n", STRING(pev->targetname), STRING(m_iszStartSpriteName));
m_pStartSprite = NULL;
}
else
{
// use an env_sprite defined by the mapper
m_pStartSprite = (CSprite*)pTemp;
m_pStartSprite->pev->movetype = MOVETYPE_NOCLIP;
}
}
else if ( pev->spawnflags & SF_LASER_INTERPOLATE) // interpolated lasers must have sprites at the start
{
m_pStartSprite = CSprite::SpriteCreate( "sprites/null.spr", pev->origin, TRUE );
}
else
m_pStartSprite = NULL;
if ( m_iszEndSpriteName )
{
CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEndSpriteName));
if (pTemp == NULL)
{
m_pEndSprite = CSprite::SpriteCreate( STRING(m_iszEndSpriteName), pev->origin, TRUE );
if (m_pEndSprite)
m_pEndSprite->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx );
}
else if (!FClassnameIs(pTemp->pev, "env_sprite"))
{
ALERT(at_error, "env_laser \"%s\" found endsprite %s, but can't use: not an env_sprite\n", STRING(pev->targetname), STRING(m_iszEndSpriteName));
m_pEndSprite = NULL;
}
else
{
// use an env_sprite defined by the mapper
m_pEndSprite = (CSprite*)pTemp;
m_pEndSprite->pev->movetype = MOVETYPE_NOCLIP;
}
}
else if ( pev->spawnflags & SF_LASER_INTERPOLATE) // interpolated lasers must have sprites at the end
{
m_pEndSprite = CSprite::SpriteCreate( "sprites/null.spr", pev->origin, TRUE );
}
else
m_pEndSprite = NULL;
//LRC
if ( m_pStartSprite && m_pEndSprite && pev->spawnflags & SF_LASER_INTERPOLATE )
EntsInit( m_pStartSprite->entindex(), m_pEndSprite->entindex() );
else
PointsInit( pev->origin, pev->origin );
if( pev->targetname && !( pev->spawnflags & SF_BEAM_STARTON ) )
TurnOff();
else
TurnOn();
}
void CLaser::Precache( void )
{
PRECACHE_MODEL( "sprites/null.spr" );
pev->modelindex = PRECACHE_MODEL( STRING(pev->model) );
if ( m_iszStartSpriteName )
{
// UGLY HACK to check whether this is a filename: does it contain a dot?
const char *c = STRING(m_iszStartSpriteName);
while (*c)
{
if (*c == '.')
{
PRECACHE_MODEL( STRING(m_iszStartSpriteName) );
break;
}
c++; // the magic word?
}
}
if ( m_iszEndSpriteName )
{
const char *c = STRING(m_iszEndSpriteName);
while (*c)
{
if (*c == '.')
{
PRECACHE_MODEL( (char *)STRING(m_iszEndSpriteName) );
break;
}
c++;
}
}
}
void CLaser::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "LaserStart"))
{
m_iszStartPosition = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "LaserTarget"))
{
pev->message = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iTowardsMode"))
{
m_iTowardsMode = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "width" ) )
{
SetWidth( (int)atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "NoiseAmplitude" ) )
{
SetNoise( atoi( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "TextureScroll" ) )
{
SetScrollRate( atoi( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "texture" ) )
{
pev->model = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "StartSprite"))
{
m_iszStartSpriteName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "EndSprite" ) )
{
m_iszEndSpriteName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "framestart" ) )
{
pev->frame = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "damage" ) )
{
pev->dmg = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iProjection"))
{
m_iProjection = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iStoppedBy"))
{
m_iStoppedBy = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CBeam::KeyValue( pkvd );
}
void CLaser::TurnOff( void )
{
pev->effects |= EF_NODRAW;
DontThink();
if ( m_pStartSprite )
{
m_pStartSprite->TurnOff();
UTIL_SetVelocity(m_pStartSprite, g_vecZero); //LRC
}
if ( m_pEndSprite )
{
m_pEndSprite->TurnOff();
UTIL_SetVelocity(m_pEndSprite, g_vecZero); //LRC
}
}
void CLaser::TurnOn( void )
{
pev->effects &= ~EF_NODRAW;
if ( m_pStartSprite )
m_pStartSprite->TurnOn();
if ( m_pEndSprite )
m_pEndSprite->TurnOn();
pev->dmgtime = gpGlobals->time;
if ( pev->spawnflags & SF_BEAM_SHADEIN )
SetFlags( BEAM_FSHADEIN );
else if ( pev->spawnflags & SF_BEAM_SHADEOUT )
SetFlags( BEAM_FSHADEOUT );
else if ( pev->spawnflags & SF_BEAM_SOLID )
SetFlags( BEAM_FSOLID );
SetNextThink( 0 );
}
void CLaser::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int active = (GetState() == STATE_ON);
if( !ShouldToggle( useType, active ) )
return;
if( active )
{
TurnOff();
}
else
{
TurnOn();
}
}
void CLaser::FireAtPoint( Vector startpos, TraceResult &tr )
{
if (pev->spawnflags & SF_LASER_INTERPOLATE && m_pStartSprite && m_pEndSprite)
{
UTIL_SetVelocity(m_pStartSprite, (startpos - m_pStartSprite->pev->origin)*10 );
UTIL_SetVelocity(m_pEndSprite, (tr.vecEndPos - m_pEndSprite->pev->origin)*10 );
}
else
{
if (m_pStartSprite)
UTIL_AssignOrigin( m_pStartSprite, startpos );
if (m_pEndSprite)
UTIL_AssignOrigin( m_pEndSprite, tr.vecEndPos );
SetStartPos( startpos );
SetEndPos( tr.vecEndPos );
}
BeamDamage( &tr );
DoSparks( startpos, tr.vecEndPos );
}
void CLaser::StrikeThink( void )
{
Vector startpos = pev->origin;
if (m_iszStartPosition)
{
startpos = CalcLocus_Position(this, NULL, STRING(m_iszStartPosition));
}
if (m_iTowardsMode)
{
m_firePosition = startpos + CalcLocus_Velocity(this, NULL, STRING(pev->message));
}
else
{
CBaseEntity *pEnd = RandomTargetname( STRING( pev->message ) );
if( pEnd )
m_firePosition = pEnd->pev->origin;
}
TraceResult tr;
//LRC
// UTIL_TraceLine( pev->origin, m_firePosition, dont_ignore_monsters, NULL, &tr );
IGNORE_GLASS iIgnoreGlass;
if( m_iStoppedBy % 2 ) // if it's an odd number
iIgnoreGlass = ignore_glass;
else
iIgnoreGlass = dont_ignore_glass;
IGNORE_MONSTERS iIgnoreMonsters;
if( m_iStoppedBy <= 1 )
iIgnoreMonsters = dont_ignore_monsters;
else if( m_iStoppedBy <= 3 )
iIgnoreMonsters = missile;
else
iIgnoreMonsters = ignore_monsters;
if( m_iProjection )
{
Vector vecProject = startpos + 4096 * ( ( m_firePosition - startpos ).Normalize() );
UTIL_TraceLine( startpos, vecProject, iIgnoreMonsters, iIgnoreGlass, NULL, &tr );
}
else
{
UTIL_TraceLine( startpos, m_firePosition, iIgnoreMonsters, iIgnoreGlass, NULL, &tr );
}
FireAtPoint( startpos, tr );
//LRC - tripbeams
if( pev->target )
{
// nicked from monster_tripmine:
//HACKHACK Set simple box using this really nice global!
gpGlobals->trace_flags = FTRACE_SIMPLEBOX;
UTIL_TraceLine( startpos, m_firePosition, dont_ignore_monsters, NULL, &tr );
CBaseEntity *pTrip = GetTripEntity( &tr );
if( pTrip )
{
if( !FBitSet( pev->spawnflags, SF_BEAM_TRIPPED ) )
{
FireTargets( STRING( pev->target ), pTrip, this, USE_TOGGLE, 0 );
pev->spawnflags |= SF_BEAM_TRIPPED;
}
}
else
{
pev->spawnflags &= ~SF_BEAM_TRIPPED;
}
}
SetNextThink( 0.1f );
}
class CGlow : public CPointEntity
{
public:
void Spawn( void );
void Think( void );
void Animate( float frames );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
float m_lastTime;
float m_maxFrame;
};
LINK_ENTITY_TO_CLASS( env_glow, CGlow );
TYPEDESCRIPTION CGlow::m_SaveData[] =
{
DEFINE_FIELD( CGlow, m_lastTime, FIELD_TIME ),
DEFINE_FIELD( CGlow, m_maxFrame, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CGlow, CPointEntity );
void CGlow::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = 0;
pev->frame = 0;
PRECACHE_MODEL( STRING( pev->model ) );
SET_MODEL( ENT( pev ), STRING( pev->model ) );
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
if( m_maxFrame > 1.0f && pev->framerate != 0 )
SetNextThink( 0.1f );
m_lastTime = gpGlobals->time;
}
void CGlow::Think( void )
{
Animate( pev->framerate * ( gpGlobals->time - m_lastTime ) );
SetNextThink( 0.1f );
m_lastTime = gpGlobals->time;
}
void CGlow::Animate( float frames )
{
if( m_maxFrame > 0 )
pev->frame = fmod( pev->frame + frames, m_maxFrame );
}
LINK_ENTITY_TO_CLASS( env_sprite, CSprite )
TYPEDESCRIPTION CSprite::m_SaveData[] =
{
DEFINE_FIELD( CSprite, m_lastTime, FIELD_TIME ),
DEFINE_FIELD( CSprite, m_maxFrame, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CSprite, CPointEntity )
void CSprite::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = 0;
pev->frame = 0;
Precache();
SET_MODEL( ENT( pev ), STRING( pev->model ) );
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
if( pev->targetname && !( pev->spawnflags & SF_SPRITE_STARTON ) )
TurnOff();
else
TurnOn();
// Worldcraft only sets y rotation, copy to Z
if( pev->angles.y != 0 && pev->angles.z == 0 )
{
pev->angles.z = pev->angles.y;
pev->angles.y = 0;
}
}
void CSprite::Precache( void )
{
PRECACHE_MODEL( STRING( pev->model ) );
// Reset attachment after save/restore
if( pev->aiment )
SetAttachment( pev->aiment, pev->body );
else
{
// Clear attachment
pev->skin = 0;
pev->body = 0;
}
}
void CSprite::SpriteInit( const char *pSpriteName, const Vector &origin )
{
pev->model = MAKE_STRING( pSpriteName );
pev->origin = origin;
Spawn();
}
CSprite *CSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate )
{
CSprite *pSprite = GetClassPtr( (CSprite *)NULL );
pSprite->SpriteInit( pSpriteName, origin );
pSprite->pev->classname = MAKE_STRING( "env_sprite" );
pSprite->pev->solid = SOLID_NOT;
pSprite->pev->movetype = MOVETYPE_NOCLIP;
if( animate )
pSprite->TurnOn();
return pSprite;
}
void CSprite::AnimateThink( void )
{
Animate( pev->framerate * ( gpGlobals->time - m_lastTime ) );
SetNextThink( 0.1f );
m_lastTime = gpGlobals->time;
}
void CSprite::AnimateUntilDead( void )
{
if( gpGlobals->time > pev->dmgtime )
UTIL_Remove( this );
else
{
AnimateThink();
SetNextThink( 0 );
}
}
void CSprite::Expand( float scaleSpeed, float fadeSpeed )
{
pev->speed = scaleSpeed;
pev->health = fadeSpeed;
SetThink( &CSprite::ExpandThink );
SetNextThink( 0 );
m_lastTime = gpGlobals->time;
}
void CSprite::ExpandThink( void )
{
float frametime = gpGlobals->time - m_lastTime;
pev->scale += pev->speed * frametime;
pev->renderamt -= pev->health * frametime;
if( pev->renderamt <= 0 )
{
pev->renderamt = 0;
UTIL_Remove( this );
}
else
{
SetNextThink( 0.1f );
m_lastTime = gpGlobals->time;
}
}
void CSprite::Animate( float frames )
{
pev->frame += frames;
if( pev->frame > m_maxFrame )
{
if( pev->spawnflags & SF_SPRITE_ONCE )
{
TurnOff();
}
else
{
if( m_maxFrame > 0 )
pev->frame = fmod( pev->frame, m_maxFrame );
}
}
}
void CSprite::TurnOff( void )
{
pev->effects = EF_NODRAW;
DontThink();
}
void CSprite::TurnOn( void )
{
if (pev->message)
{
CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(pev->message));
if (pTemp)
SetAttachment(pTemp->edict(), pev->frags);
else
return;
}
pev->effects = 0;
if( ( pev->framerate && m_maxFrame > 1.0f ) || ( pev->spawnflags & SF_SPRITE_ONCE ) )
{
SetThink( &CSprite::AnimateThink );
SetNextThink( 0 );
m_lastTime = gpGlobals->time;
}
pev->frame = 0;
}
void CSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int on = pev->effects != EF_NODRAW;
if( ShouldToggle( useType, on ) )
{
if( on )
{
SUB_UseTargets( this, USE_OFF, 0 ); //LRC
TurnOff();
}
else
{
SUB_UseTargets( this, USE_ON, 0 ); //LRC
TurnOn();
}
}
}
//=================================================================
// env_model: like env_sprite, except you can specify a sequence.
//=================================================================
#define SF_ENVMODEL_OFF 1
#define SF_ENVMODEL_DROPTOFLOOR 2
#define SF_ENVMODEL_SOLID 4
class CEnvModel : public CBaseAnimating
{
void Spawn( void );
void Precache( void );
void EXPORT Think( void );
void KeyValue( KeyValueData *pkvd );
STATE GetState( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void SetSequence( void );
string_t m_iszSequence_On;
string_t m_iszSequence_Off;
int m_iAction_On;
int m_iAction_Off;
};
TYPEDESCRIPTION CEnvModel::m_SaveData[] =
{
DEFINE_FIELD( CEnvModel, m_iszSequence_On, FIELD_STRING ),
DEFINE_FIELD( CEnvModel, m_iszSequence_Off, FIELD_STRING ),
DEFINE_FIELD( CEnvModel, m_iAction_On, FIELD_INTEGER ),
DEFINE_FIELD( CEnvModel, m_iAction_Off, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CEnvModel, CBaseAnimating );
LINK_ENTITY_TO_CLASS( env_model, CEnvModel );
void CEnvModel::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "m_iszSequence_On"))
{
m_iszSequence_On = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszSequence_Off"))
{
m_iszSequence_Off = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iAction_On"))
{
m_iAction_On = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iAction_Off"))
{
m_iAction_Off = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
{
CBaseAnimating::KeyValue( pkvd );
}
}
void CEnvModel :: Spawn( void )
{
Precache();
SET_MODEL( ENT(pev), STRING(pev->model) );
UTIL_SetOrigin(this, pev->origin);
if (pev->spawnflags & SF_ENVMODEL_SOLID)
{
pev->solid = SOLID_SLIDEBOX;
UTIL_SetSize(pev, Vector(-10, -10, -10), Vector(10, 10, 10)); //LRCT
}
if (pev->spawnflags & SF_ENVMODEL_DROPTOFLOOR)
{
pev->origin.z += 1;
DROP_TO_FLOOR ( ENT(pev) );
}
SetBoneController( 0, 0 );
SetBoneController( 1, 0 );
SetSequence();
SetNextThink( 0.1 );
}
void CEnvModel::Precache( void )
{
PRECACHE_MODEL( (char *)STRING(pev->model) );
}
STATE CEnvModel::GetState( void )
{
if (pev->spawnflags & SF_ENVMODEL_OFF)
return STATE_OFF;
else
return STATE_ON;
}
void CEnvModel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (ShouldToggle(useType, !(pev->spawnflags & SF_ENVMODEL_OFF)))
{
if (pev->spawnflags & SF_ENVMODEL_OFF)
pev->spawnflags &= ~SF_ENVMODEL_OFF;
else
pev->spawnflags |= SF_ENVMODEL_OFF;
SetSequence();
SetNextThink( 0.1 );
}
}
void CEnvModel::Think( void )
{
int iTemp;
// ALERT(at_console, "env_model Think fr=%f\n", pev->framerate);
StudioFrameAdvance ( ); // set m_fSequenceFinished if necessary
// if (m_fSequenceLoops)
// {
// SetNextThink( 1E6 );
// return; // our work here is done.
// }
if (m_fSequenceFinished && !m_fSequenceLoops)
{
if (pev->spawnflags & SF_ENVMODEL_OFF)
iTemp = m_iAction_Off;
else
iTemp = m_iAction_On;
switch (iTemp)
{
// case 1: // loop
// pev->animtime = gpGlobals->time;
// m_fSequenceFinished = FALSE;
// m_flLastEventCheck = gpGlobals->time;
// pev->frame = 0;
// break;
case 2: // change state
if (pev->spawnflags & SF_ENVMODEL_OFF)
pev->spawnflags &= ~SF_ENVMODEL_OFF;
else
pev->spawnflags |= SF_ENVMODEL_OFF;
SetSequence();
break;
default: //remain frozen
return;
}
}
SetNextThink( 0.1 );
}
void CEnvModel :: SetSequence( void )
{
int iszSeq;
if (pev->spawnflags & SF_ENVMODEL_OFF)
iszSeq = m_iszSequence_Off;
else
iszSeq = m_iszSequence_On;
if (!iszSeq)
return;
pev->sequence = LookupSequence( STRING( iszSeq ));
if (pev->sequence == -1)
{
if (pev->targetname)
ALERT( at_error, "env_model %s: unknown sequence \"%s\"\n", STRING( pev->targetname ), STRING( iszSeq ));
else
ALERT( at_error, "env_model: unknown sequence \"%s\"\n", STRING( pev->targetname ), STRING( iszSeq ));
pev->sequence = 0;
}
pev->frame = 0;
ResetSequenceInfo( );
if (pev->spawnflags & SF_ENVMODEL_OFF)
{
if (m_iAction_Off == 1)
m_fSequenceLoops = 1;
else
m_fSequenceLoops = 0;
}
else
{
if (m_iAction_On == 1)
m_fSequenceLoops = 1;
else
m_fSequenceLoops = 0;
}
}
#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired
#define SF_GIBSHOOTER_DEBUG 4 //LRC
class CGibShooter : public CBaseDelay
{
public:
virtual void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void EXPORT ShootThink( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual CBaseEntity *CreateGib( Vector vecPos, Vector vecVel );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int m_iGibs;
int m_iGibCapacity;
int m_iGibMaterial;
int m_iGibModelIndex;
// float m_flGibVelocity;
float m_flVariance;
float m_flGibLife;
int m_iszTargetname;
int m_iszPosition;
int m_iszVelocity;
int m_iszVelFactor;
int m_iszSpawnTarget;
int m_iBloodColor;
};
TYPEDESCRIPTION CGibShooter::m_SaveData[] =
{
DEFINE_FIELD( CGibShooter, m_iGibs, FIELD_INTEGER ),
DEFINE_FIELD( CGibShooter, m_iGibCapacity, FIELD_INTEGER ),
DEFINE_FIELD( CGibShooter, m_iGibMaterial, FIELD_INTEGER ),
DEFINE_FIELD( CGibShooter, m_iGibModelIndex, FIELD_INTEGER ),
// DEFINE_FIELD( CGibShooter, m_flGibVelocity, FIELD_FLOAT ),
DEFINE_FIELD( CGibShooter, m_flVariance, FIELD_FLOAT ),
DEFINE_FIELD( CGibShooter, m_flGibLife, FIELD_FLOAT ),
DEFINE_FIELD( CGibShooter, m_iszTargetname, FIELD_STRING),
DEFINE_FIELD( CGibShooter, m_iszPosition, FIELD_STRING),
DEFINE_FIELD( CGibShooter, m_iszVelocity, FIELD_STRING),
DEFINE_FIELD( CGibShooter, m_iszVelFactor, FIELD_STRING),
DEFINE_FIELD( CGibShooter, m_iszSpawnTarget, FIELD_STRING),
DEFINE_FIELD( CGibShooter, m_iBloodColor, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CGibShooter, CBaseDelay )
LINK_ENTITY_TO_CLASS( gibshooter, CGibShooter )
void CGibShooter::Precache( void )
{
if( g_Language == LANGUAGE_GERMAN )
{
m_iGibModelIndex = PRECACHE_MODEL( "models/germanygibs.mdl" );
}
else if (m_iBloodColor == BLOOD_COLOR_YELLOW)
{
m_iGibModelIndex = PRECACHE_MODEL ("models/agibs.mdl");
}
else
{
m_iGibModelIndex = PRECACHE_MODEL( "models/hgibs.mdl" );
}
}
void CGibShooter::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "m_iGibs" ) )
{
m_iGibs = m_iGibCapacity = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "m_flVelocity" ) )
{
m_iszVelFactor = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "m_flVariance" ) )
{
m_flVariance = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "m_flGibLife" ) )
{
m_flGibLife = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszTargetName"))
{
m_iszTargetname = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszPosition"))
{
m_iszPosition = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszVelocity"))
{
m_iszVelocity = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszVelFactor"))
{
m_iszVelFactor = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszSpawnTarget"))
{
m_iszSpawnTarget = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iBloodColor"))
{
m_iBloodColor = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
{
CBaseDelay::KeyValue( pkvd );
}
}
void CGibShooter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
SetThink( &CGibShooter::ShootThink );
SetNextThink( 0 );
}
void CGibShooter::Spawn( void )
{
Precache();
pev->solid = SOLID_NOT;
pev->effects = EF_NODRAW;
// if ( m_flDelay == 0 )
// {
// m_flDelay = 0.1;
// }
if( m_flGibLife == 0 )
{
m_flGibLife = 25;
}
SetMovedir( pev );
if (pev->body == 0)
pev->body = MODEL_FRAMES( m_iGibModelIndex );
}
CBaseEntity *CGibShooter :: CreateGib ( Vector vecPos, Vector vecVel )
{
if( CVAR_GET_FLOAT( "violence_hgibs" ) == 0 )
return NULL;
CGib *pGib = GetClassPtr( (CGib *)NULL );
// if (pGib)
// ALERT(at_console, "Gib created ok\n");
pGib->pev->origin = vecPos;
pGib->pev->velocity = vecVel;
if (m_iBloodColor == BLOOD_COLOR_YELLOW)
{
pGib->Spawn( "models/agibs.mdl" );
pGib->m_bloodColor = BLOOD_COLOR_YELLOW;
}
else if (m_iBloodColor)
{
pGib->Spawn( "models/hgibs.mdl" );
pGib->m_bloodColor = m_iBloodColor;
}
else
{
pGib->Spawn( "models/hgibs.mdl" );
pGib->m_bloodColor = BLOOD_COLOR_RED;
}
if( pev->body <= 1 )
{
ALERT( at_aiconsole, "GibShooter Body is <= 1!\n" );
}
pGib->pev->body = RANDOM_LONG( 1, pev->body - 1 );// avoid throwing random amounts of the 0th gib. (skull).
float thinkTime = pGib->m_fNextThink - gpGlobals->time;
pGib->m_lifeTime = (m_flGibLife * RANDOM_FLOAT( 0.95, 1.05 )); // +/- 5%
if ( pGib->m_lifeTime < thinkTime )
{
pGib->SetNextThink( pGib->m_lifeTime );
pGib->m_lifeTime = 0;
}
pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 );
pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 );
return pGib;
}
void CGibShooter::ShootThink( void )
{
int i;
if (m_flDelay == 0) // LRC - delay is 0, fire them all at once.
{
i = m_iGibs;
}
else
{
i = 1;
SetNextThink( m_flDelay );
}
while (i > 0)
{
Vector vecShootDir;
Vector vecPos;
float flGibVelocity;
if (!FStringNull(m_iszVelFactor))
flGibVelocity = CalcLocus_Ratio(m_hActivator, STRING(m_iszVelFactor));
else
flGibVelocity = 1;
if (!FStringNull(m_iszVelocity))
{
vecShootDir = CalcLocus_Velocity(this, m_hActivator, STRING(m_iszVelocity));
flGibVelocity = flGibVelocity * vecShootDir.Length();
vecShootDir = vecShootDir.Normalize();
}
else
vecShootDir = pev->movedir;
vecShootDir = vecShootDir + gpGlobals->v_right * RANDOM_FLOAT( -1.0f, 1.0f ) * m_flVariance;;
vecShootDir = vecShootDir + gpGlobals->v_forward * RANDOM_FLOAT( -1.0f, 1.0f ) * m_flVariance;;
vecShootDir = vecShootDir + gpGlobals->v_up * RANDOM_FLOAT( -1.0f, 1.0f ) * m_flVariance;;
vecShootDir = vecShootDir.Normalize();
if (!FStringNull(m_iszPosition))
vecPos = CalcLocus_Position(this, m_hActivator, STRING(m_iszPosition));
else
vecPos = pev->origin;
CBaseEntity *pGib = CreateGib(vecPos, vecShootDir * flGibVelocity);
if( pGib )
{
pGib->pev->targetname = m_iszTargetname;
// pGib->pev->velocity = vecShootDir * flGibVelocity;
if (pev->spawnflags & SF_GIBSHOOTER_DEBUG)
ALERT(at_console, "DEBUG: %s \"%s\" creates a shot at %f %f %f; vel %f %f %f; pos \"%s\"\n", STRING(pev->classname), STRING(pev->targetname), pGib->pev->origin.x, pGib->pev->origin.y, pGib->pev->origin.z, pGib->pev->velocity.x, pGib->pev->velocity.y, pGib->pev->velocity.z, STRING(m_iszPosition));
if (m_iszSpawnTarget)
FireTargets( STRING(m_iszSpawnTarget), pGib, this, USE_TOGGLE, 0 );
}
i--;
m_iGibs--;
}
if ( m_iGibs <= 0 )
{
if( pev->spawnflags & SF_GIBSHOOTER_REPEATABLE )
{
m_iGibs = m_iGibCapacity;
SetThink( NULL );
DontThink();
}
else
{
SetThink(&CGibShooter :: SUB_Remove );
SetNextThink( 0 );
}
}
}
// Shooter particle
class CShot : public CSprite
{
public:
void Touch ( CBaseEntity *pOther );
};
void CShot :: Touch ( CBaseEntity *pOther )
{
if (pev->teleport_time > gpGlobals->time)
return;
// don't fire too often in collisions!
// teleport_time is the soonest this can be touched again.
pev->teleport_time = gpGlobals->time + 0.1f;
if (pev->netname)
FireTargets( STRING(pev->netname), this, this, USE_TOGGLE, 0 );
if (pev->message && pOther && pOther != g_pWorld)
FireTargets( STRING(pev->message), pOther, this, USE_TOGGLE, 0 );
}
class CEnvShooter : public CGibShooter
{
void Precache( void );
void KeyValue( KeyValueData *pkvd );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
void Spawn( void );
static TYPEDESCRIPTION m_SaveData[];
CBaseEntity *CreateGib( Vector vecPos, Vector vecVel );
int m_iszTouch;
int m_iszTouchOther;
int m_iPhysics;
float m_fFriction;
Vector m_vecSize;
};
TYPEDESCRIPTION CEnvShooter::m_SaveData[] =
{
DEFINE_FIELD( CEnvShooter, m_iszTouch, FIELD_STRING),
DEFINE_FIELD( CEnvShooter, m_iszTouchOther, FIELD_STRING),
DEFINE_FIELD( CEnvShooter, m_iPhysics, FIELD_INTEGER),
DEFINE_FIELD( CEnvShooter, m_fFriction, FIELD_FLOAT),
DEFINE_FIELD( CEnvShooter, m_vecSize, FIELD_VECTOR),
};
IMPLEMENT_SAVERESTORE(CEnvShooter,CGibShooter);
LINK_ENTITY_TO_CLASS( env_shooter, CEnvShooter );
void CEnvShooter::Spawn( void )
{
int iBody = pev->body;
CGibShooter::Spawn();
pev->body = iBody;
}
void CEnvShooter::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "shootmodel" ) )
{
pev->model = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if ( FStrEq( pkvd->szKeyName, "shootsounds" ) )
{
int iNoise = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
switch( iNoise )
{
case 0:
m_iGibMaterial = matGlass;
break;
case 1:
m_iGibMaterial = matWood;
break;
case 2:
m_iGibMaterial = matMetal;
break;
case 3:
m_iGibMaterial = matFlesh;
break;
case 4:
m_iGibMaterial = matRocks;
break;
default:
case -1:
m_iGibMaterial = matNone;
break;
}
}
else if (FStrEq(pkvd->szKeyName, "m_iszTouch"))
{
m_iszTouch = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszTouchOther"))
{
m_iszTouchOther = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iPhysics"))
{
m_iPhysics = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_fFriction"))
{
m_fFriction = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_vecSize"))
{
UTIL_StringToVector((float*)m_vecSize, pkvd->szValue);
m_vecSize = m_vecSize/2;
pkvd->fHandled = TRUE;
}
else
{
CGibShooter::KeyValue( pkvd );
}
}
void CEnvShooter::Precache( void )
{
if (pev->model)
m_iGibModelIndex = PRECACHE_MODEL( STRING(pev->model) );
CBreakable::MaterialSoundPrecache( (Materials)m_iGibMaterial );
}
CBaseEntity *CEnvShooter :: CreateGib ( Vector vecPos, Vector vecVel )
{
if (m_iPhysics <= 1) // normal gib or sticky gib
{
CGib *pGib = GetClassPtr( (CGib *)NULL );
pGib->pev->origin = vecPos;
pGib->pev->velocity = vecVel;
pGib->Spawn( STRING( pev->model ) );
if (m_iPhysics) // sticky gib
{
pGib->pev->movetype = MOVETYPE_TOSS;
pGib->pev->solid = SOLID_BBOX;
UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) );
pGib->SetTouch(&CGib::StickyGibTouch);
}
if ( pev->body > 0 )
pGib->pev->body = RANDOM_LONG( 0, pev->body-1 );
if (m_iBloodColor)
pGib->m_bloodColor = m_iBloodColor;
else
pGib->m_bloodColor = DONT_BLEED;
pGib->m_material = m_iGibMaterial;
pGib->pev->rendermode = pev->rendermode;
pGib->pev->renderamt = pev->renderamt;
pGib->pev->rendercolor = pev->rendercolor;
pGib->pev->renderfx = pev->renderfx;
pGib->pev->scale = pev->scale;
pGib->pev->skin = pev->skin;
float thinkTime = pGib->m_fNextThink - gpGlobals->time;
pGib->m_lifeTime = (m_flGibLife * RANDOM_FLOAT( 0.95, 1.05 )); // +/- 5%
if ( pGib->m_lifeTime < thinkTime )
{
pGib->SetNextThink( pGib->m_lifeTime );
pGib->m_lifeTime = 0;
}
pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 );
pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 );
return pGib;
}
// special shot
CShot *pShot = GetClassPtr( (CShot*)NULL );
pShot->pev->classname = MAKE_STRING("shot");
pShot->pev->solid = SOLID_SLIDEBOX;
pShot->pev->origin = vecPos;
pShot->pev->velocity = vecVel;
SET_MODEL(ENT(pShot->pev), STRING(pev->model));
UTIL_SetSize(pShot->pev, -m_vecSize, m_vecSize);
pShot->pev->renderamt = pev->renderamt;
pShot->pev->rendermode = pev->rendermode;
pShot->pev->rendercolor = pev->rendercolor;
pShot->pev->renderfx = pev->renderfx;
pShot->pev->netname = m_iszTouch;
pShot->pev->message = m_iszTouchOther;
pShot->pev->skin = pev->skin;
pShot->pev->body = pev->body;
pShot->pev->scale = pev->scale;
pShot->pev->frame = pev->frame;
pShot->pev->framerate = pev->framerate;
pShot->pev->friction = m_fFriction;
switch (m_iPhysics)
{
case 2: pShot->pev->movetype = MOVETYPE_NOCLIP; pShot->pev->solid = SOLID_NOT; break;
case 3: pShot->pev->movetype = MOVETYPE_FLYMISSILE; break;
case 4: pShot->pev->movetype = MOVETYPE_BOUNCEMISSILE; break;
case 5: pShot->pev->movetype = MOVETYPE_TOSS; break;
case 6: pShot->pev->movetype = MOVETYPE_BOUNCE; break;
}
if (pShot->pev->framerate)
{
pShot->m_maxFrame = (float) MODEL_FRAMES( pShot->pev->modelindex ) - 1;
if (pShot->m_maxFrame > 1.0f)
{
if (m_flGibLife)
{
pShot->pev->dmgtime = gpGlobals->time + m_flGibLife;
pShot->SetThink(& CSprite::AnimateUntilDead );
}
else
{
pShot->SetThink(& CSprite::AnimateThink );
}
pShot->SetNextThink( 0 );
pShot->m_lastTime = gpGlobals->time;
return pShot;
}
}
// if it's not animating
if (m_flGibLife)
{
pShot->SetThink(&CShot::SUB_Remove);
pShot->SetNextThink(m_flGibLife);
}
return pShot;
}
class CTestEffect : public CBaseDelay
{
public:
void Spawn( void );
void Precache( void );
// void KeyValue( KeyValueData *pkvd );
void EXPORT TestThink( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
int m_iLoop;
int m_iBeam;
CBeam *m_pBeam[24];
float m_flBeamTime[24];
float m_flStartTime;
};
LINK_ENTITY_TO_CLASS( test_effect, CTestEffect )
void CTestEffect::Spawn( void )
{
Precache();
}
void CTestEffect::Precache( void )
{
PRECACHE_MODEL( "sprites/lgtning.spr" );
}
void CTestEffect::TestThink( void )
{
int i;
float t = gpGlobals->time - m_flStartTime;
if( m_iBeam < 24 )
{
CBeam *pbeam = CBeam::BeamCreate( "sprites/lgtning.spr", 100 );
TraceResult tr;
Vector vecSrc = pev->origin;
Vector vecDir = Vector( RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ),RANDOM_FLOAT( -1.0f, 1.0f ) );
vecDir = vecDir.Normalize();
UTIL_TraceLine( vecSrc, vecSrc + vecDir * 128, ignore_monsters, ENT( pev ), &tr );
pbeam->PointsInit( vecSrc, tr.vecEndPos );
// pbeam->SetColor( 80, 100, 255 );
pbeam->SetColor( 255, 180, 100 );
pbeam->SetWidth( 100 );
pbeam->SetScrollRate( 12 );
m_flBeamTime[m_iBeam] = gpGlobals->time;
m_pBeam[m_iBeam] = pbeam;
m_iBeam++;
#if 0
Vector vecMid = ( vecSrc + tr.vecEndPos ) * 0.5f;
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_DLIGHT );
WRITE_COORD( vecMid.x ); // X
WRITE_COORD( vecMid.y ); // Y
WRITE_COORD( vecMid.z ); // Z
WRITE_BYTE( 20 ); // radius * 0.1
WRITE_BYTE( 255 ); // r
WRITE_BYTE( 180 ); // g
WRITE_BYTE( 100 ); // b
WRITE_BYTE( 20 ); // time * 10
WRITE_BYTE( 0 ); // decay * 0.1
MESSAGE_END( );
#endif
}
if( t < 3.0f )
{
for( i = 0; i < m_iBeam; i++ )
{
t = ( gpGlobals->time - m_flBeamTime[i] ) / ( 3.0f + m_flStartTime - m_flBeamTime[i] );
m_pBeam[i]->SetBrightness( (int)( 255.0f * t ) );
// m_pBeam[i]->SetScrollRate( 20 * t );
}
SetNextThink( 0.1f );
}
else
{
for( i = 0; i < m_iBeam; i++ )
{
UTIL_Remove( m_pBeam[i] );
}
m_flStartTime = gpGlobals->time;
m_iBeam = 0;
// pev->nextthink = gpGlobals->time;
SetThink( NULL );
}
}
void CTestEffect::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
SetThink( &CTestEffect::TestThink );
SetNextThink( 0.1f );
m_flStartTime = gpGlobals->time;
}
// Blood effects
class CBlood : public CPointEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
inline int Color( void ) { return pev->impulse; }
inline float BloodAmount( void ) { return pev->dmg; }
inline void SetColor( int color ) { pev->impulse = color; }
inline void SetBloodAmount( float amount ) { pev->dmg = amount; }
Vector Direction( CBaseEntity *pActivator ); //LRC - added pActivator, for locus system
Vector BloodPosition( CBaseEntity *pActivator );
private:
};
LINK_ENTITY_TO_CLASS( env_blood, CBlood )
#define SF_BLOOD_RANDOM 0x0001
#define SF_BLOOD_STREAM 0x0002
#define SF_BLOOD_PLAYER 0x0004
#define SF_BLOOD_DECAL 0x0008
void CBlood::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = 0;
pev->frame = 0;
SetMovedir( pev );
if (Color() == 0) SetColor( BLOOD_COLOR_RED );
}
void CBlood::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "color" ) )
{
int color = atoi( pkvd->szValue );
switch( color )
{
case 1:
SetColor( BLOOD_COLOR_YELLOW );
break;
default:
SetColor( BLOOD_COLOR_RED );
break;
}
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "amount" ) )
{
SetBloodAmount( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
Vector CBlood::Direction( CBaseEntity *pActivator )
{
if( pev->spawnflags & SF_BLOOD_RANDOM )
return UTIL_RandomBloodVector();
else if (pev->netname)
return CalcLocus_Velocity(this, pActivator, STRING(pev->netname));
else
return pev->movedir;
}
Vector CBlood::BloodPosition( CBaseEntity *pActivator )
{
if( pev->spawnflags & SF_BLOOD_PLAYER )
{
edict_t *pPlayer;
if( pActivator && pActivator->IsPlayer() )
{
pPlayer = pActivator->edict();
}
else
pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 );
if( pPlayer )
return( pPlayer->v.origin + pPlayer->v.view_ofs ) + Vector( RANDOM_FLOAT( -10, 10 ), RANDOM_FLOAT( -10, 10 ), RANDOM_FLOAT( -10, 10 ) );
// if no player found, fall through
}
else if (pev->target)
{
return CalcLocus_Position(this, pActivator, STRING(pev->target));
}
return pev->origin;
}
void CBlood::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( pev->spawnflags & SF_BLOOD_STREAM )
UTIL_BloodStream( BloodPosition(pActivator), Direction(pActivator), (Color() == BLOOD_COLOR_RED) ? 70 : Color(), BloodAmount() );
else
UTIL_BloodDrips( BloodPosition(pActivator), Direction(pActivator), Color(), BloodAmount() );
if( pev->spawnflags & SF_BLOOD_DECAL )
{
Vector forward = Direction(pActivator);
Vector start = BloodPosition( pActivator );
TraceResult tr;
UTIL_TraceLine( start, start + forward * BloodAmount() * 2, ignore_monsters, NULL, &tr );
if( tr.flFraction != 1.0f )
UTIL_BloodDecalTrace( &tr, Color() );
}
}
// Screen shake
class CShake : public CPointEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
inline float Amplitude( void ) { return pev->scale; }
inline float Frequency( void ) { return pev->dmg_save; }
inline float Duration( void ) { return pev->dmg_take; }
inline float Radius( void ) { return pev->dmg; }
inline void SetAmplitude( float amplitude ) { pev->scale = amplitude; }
inline void SetFrequency( float frequency ) { pev->dmg_save = frequency; }
inline void SetDuration( float duration ) { pev->dmg_take = duration; }
inline void SetRadius( float radius ) { pev->dmg = radius; }
STATE m_iState; //LRC
virtual STATE GetState( void ) { return m_iState; }; //LRC
void Think( void ) { m_iState = STATE_OFF; }; //LRC
private:
};
LINK_ENTITY_TO_CLASS( env_shake, CShake )
// pev->scale is amplitude
// pev->dmg_save is frequency
// pev->dmg_take is duration
// pev->dmg is radius
// radius of 0 means all players
// NOTE: UTIL_ScreenShake() will only shake players who are on the ground
#define SF_SHAKE_EVERYONE 0x0001 // Don't check radius
// UNDONE: These don't work yet
#define SF_SHAKE_DISRUPT 0x0002 // Disrupt controls
#define SF_SHAKE_INAIR 0x0004 // Shake players in air
void CShake::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = 0;
pev->frame = 0;
m_iState = STATE_OFF; //LRC
if( pev->spawnflags & SF_SHAKE_EVERYONE )
pev->dmg = 0;
}
void CShake::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "amplitude" ) )
{
SetAmplitude( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "frequency" ) )
{
SetFrequency( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "duration" ) )
{
SetDuration( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "radius" ) )
{
SetRadius( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
void CShake::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
UTIL_ScreenShake( pev->origin, Amplitude(), Frequency(), Duration(), Radius() );
m_iState = STATE_ON; //LRC
SetNextThink( Duration() ); //LRC
}
class CFade : public CPointEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
virtual STATE GetState( void ) { return m_iState; }; // LRC
void Think( void ); //LRC
inline float Duration( void ) { return pev->dmg_take; }
inline float HoldTime( void ) { return pev->dmg_save; }
inline void SetDuration( float duration ) { pev->dmg_take = duration; }
inline void SetHoldTime( float hold ) { pev->dmg_save = hold; }
STATE m_iState; // LRC. Don't saverestore this value, it's not worth it.
private:
};
LINK_ENTITY_TO_CLASS( env_fade, CFade )
// pev->dmg_take is duration
// pev->dmg_save is hold duration
#define SF_FADE_IN 0x0001 // Fade in, not out
#define SF_FADE_MODULATE 0x0002 // Modulate, don't blend
#define SF_FADE_ONLYONE 0x0004
#define SF_FADE_PERMANENT 0x0008 //LRC - hold permanently
void CFade::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = 0;
pev->frame = 0;
m_iState = STATE_OFF; //LRC
}
void CFade::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "duration" ) )
{
SetDuration( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "holdtime" ) )
{
SetHoldTime( atof( pkvd->szValue ) );
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
void CFade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int fadeFlags = 0;
m_iState = STATE_TURN_ON; //LRC
SetNextThink( Duration() ); //LRC
if( !( pev->spawnflags & SF_FADE_IN ) )
fadeFlags |= FFADE_OUT;
if( pev->spawnflags & SF_FADE_MODULATE )
fadeFlags |= FFADE_MODULATE;
if ( pev->spawnflags & SF_FADE_PERMANENT ) //LRC
fadeFlags |= FFADE_STAYOUT; //LRC
if( pev->spawnflags & SF_FADE_ONLYONE )
{
if( pActivator->IsNetClient() )
{
#ifdef XENWARRIOR
if (pActivator->IsPlayer() && ((CBasePlayer*)pActivator)->FlashlightIsOn())
{
((CBasePlayer*)pActivator)->FlashlightTurnOff();
if (pev->spawnflags & SF_FADE_PERMANENT)
g_fEnvFadeTime = gpGlobals->time + 1E6;
else
g_fEnvFadeTime = gpGlobals->time + Duration() + HoldTime();
}
#endif
UTIL_ScreenFade( pActivator, pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags );
}
}
else
{
#ifdef XENWARRIOR
CBasePlayer *pPlayer = (CBasePlayer*)UTIL_FindEntityByTargetname(NULL, "*player");
if (pPlayer)
((CBasePlayer*)pPlayer)->FlashlightTurnOff();
if (pev->spawnflags & SF_FADE_PERMANENT)
g_fEnvFadeTime = gpGlobals->time + 1E6;
else
g_fEnvFadeTime = gpGlobals->time + Duration() + HoldTime();
#endif
UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags );
}
SUB_UseTargets( this, USE_TOGGLE, 0 );
}
//LRC: a bolt-on state!
void CFade::Think( void )
{
if (m_iState == STATE_TURN_ON)
{
m_iState = STATE_ON;
if (!( pev->spawnflags & SF_FADE_PERMANENT))
SetNextThink( HoldTime() );
}
else
m_iState = STATE_OFF;
}
class CMessage : public CPointEntity
{
public:
void Spawn( void );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
private:
};
LINK_ENTITY_TO_CLASS( env_message, CMessage )
void CMessage::Spawn( void )
{
Precache();
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
switch( pev->impulse )
{
case 1:
// Medium radius
pev->speed = ATTN_STATIC;
break;
case 2:
// Large radius
pev->speed = ATTN_NORM;
break;
case 3:
//EVERYWHERE
pev->speed = ATTN_NONE;
break;
default:
case 0: // Small radius
pev->speed = ATTN_IDLE;
break;
}
pev->impulse = 0;
// No volume, use normal
if( pev->scale <= 0.0f )
pev->scale = 1.0f;
}
void CMessage::Precache( void )
{
if( pev->noise )
PRECACHE_SOUND( STRING( pev->noise ) );
}
void CMessage::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "messagesound" ) )
{
pev->noise = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq(pkvd->szKeyName, "messagevolume" ) )
{
pev->scale = atof( pkvd->szValue ) * 0.1;
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "messageattenuation" ) )
{
pev->impulse = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
void CMessage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
CBaseEntity *pPlayer = NULL;
if( pev->spawnflags & SF_MESSAGE_ALL )
UTIL_ShowMessageAll( STRING( pev->message ) );
else
{
if( pActivator && pActivator->IsPlayer() )
pPlayer = pActivator;
else
pPlayer = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) );
if( pPlayer )
UTIL_ShowMessage( STRING( pev->message ), pPlayer );
}
if( pev->noise )
EMIT_SOUND( edict(), CHAN_BODY, STRING( pev->noise ), pev->scale, pev->speed );
if( pev->spawnflags & SF_MESSAGE_ONCE )
UTIL_Remove( this );
SUB_UseTargets( this, USE_TOGGLE, 0 );
}
//=========================================================
// FunnelEffect
//=========================================================
class CEnvFunnel : public CBaseDelay
{
public:
void Spawn( void );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
int m_iSprite; // Don't save, precache
};
void CEnvFunnel::Precache( void )
{
//LRC
if (pev->netname)
m_iSprite = PRECACHE_MODEL ( STRING(pev->netname) );
else
m_iSprite = PRECACHE_MODEL( "sprites/flare6.spr" );
}
LINK_ENTITY_TO_CLASS( env_funnel, CEnvFunnel )
void CEnvFunnel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
//LRC
Vector vecPos;
if (pev->message)
vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) );
else
vecPos = pev->origin;
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_LARGEFUNNEL );
WRITE_COORD( vecPos.x );
WRITE_COORD( vecPos.y );
WRITE_COORD( vecPos.z );
WRITE_SHORT( m_iSprite );
if( pev->spawnflags & SF_FUNNEL_REVERSE )// funnel flows in reverse?
{
WRITE_SHORT( 1 );
}
else
{
WRITE_SHORT( 0 );
}
MESSAGE_END();
if (!(pev->spawnflags & SF_FUNNEL_REPEATABLE))
{
SetThink(&CEnvFunnel:: SUB_Remove );
SetNextThink( 0 );
}
}
void CEnvFunnel::Spawn( void )
{
Precache();
pev->solid = SOLID_NOT;
pev->effects = EF_NODRAW;
}
//=========================================================
// LRC - All the particle effects from Quake 1
//=========================================================
#define SF_QUAKEFX_REPEATABLE 1
class CEnvQuakeFx : public CPointEntity
{
public:
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
};
LINK_ENTITY_TO_CLASS( env_quakefx, CEnvQuakeFx );
void CEnvQuakeFx::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
Vector vecPos;
if (pev->message)
vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) );
else
vecPos = pev->origin;
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( pev->impulse );
WRITE_COORD( vecPos.x );
WRITE_COORD( vecPos.y );
WRITE_COORD( vecPos.z );
if (pev->impulse == TE_PARTICLEBURST)
{
WRITE_SHORT( pev->armortype ); // radius
WRITE_BYTE( pev->frags ); // particle colour
WRITE_BYTE( pev->health * 10 ); // duration
}
else if (pev->impulse == TE_EXPLOSION2)
{
// these fields seem to have no effect - except that it
// crashes when I send "0" for the number of colours..
WRITE_BYTE( 0 ); // colour
WRITE_BYTE( 1 ); // number of colours
}
MESSAGE_END();
if (!(pev->spawnflags & SF_QUAKEFX_REPEATABLE))
{
SetThink(&CEnvQuakeFx:: SUB_Remove );
SetNextThink( 0 );
}
}
//=========================================================
// LRC - Beam Trail effect
//=========================================================
#define SF_BEAMTRAIL_OFF 1
class CEnvBeamTrail : public CPointEntity
{
public:
void Spawn( void );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
STATE GetState( void );
void EXPORT StartTrailThink ( void );
void Affect( CBaseEntity *pTarget, USE_TYPE useType );
int m_iSprite; // Don't save, precache
};
void CEnvBeamTrail :: Precache ( void )
{
if (pev->target)
PRECACHE_MODEL("sprites/null.spr");
if (pev->netname)
m_iSprite = PRECACHE_MODEL ( STRING(pev->netname) );
}
LINK_ENTITY_TO_CLASS( env_beamtrail, CEnvBeamTrail )
STATE CEnvBeamTrail :: GetState ( void )
{
if (pev->spawnflags & SF_BEAMTRAIL_OFF)
return STATE_OFF;
else
return STATE_ON;
}
void CEnvBeamTrail :: StartTrailThink ( void )
{
pev->spawnflags |= SF_BEAMTRAIL_OFF; // fake turning off, so the Use turns it on properly
Use(this, this, USE_ON, 0);
}
void CEnvBeamTrail::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (pev->target)
{
CBaseEntity *pTarget = UTIL_FindEntityByTargetname(NULL, STRING(pev->target), pActivator );
while (pTarget)
{
Affect(pTarget, useType);
pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), pActivator );
}
}
else
{
if (!ShouldToggle( useType ))
return;
Affect(this, useType);
}
if (useType == USE_ON)
pev->spawnflags &= ~SF_BEAMTRAIL_OFF;
else if (useType == USE_OFF)
pev->spawnflags |= SF_BEAMTRAIL_OFF;
else if (useType == USE_TOGGLE)
{
if (pev->spawnflags & SF_BEAMTRAIL_OFF)
pev->spawnflags &= ~SF_BEAMTRAIL_OFF;
else
pev->spawnflags |= SF_BEAMTRAIL_OFF;
}
}
void CEnvBeamTrail::Affect( CBaseEntity *pTarget, USE_TYPE useType )
{
if (useType == USE_ON || pev->spawnflags & SF_BEAMTRAIL_OFF)
{
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMFOLLOW );
WRITE_SHORT(pTarget->entindex()); // entity
WRITE_SHORT( m_iSprite ); // model
WRITE_BYTE( pev->health*10 ); // life
WRITE_BYTE( pev->armorvalue ); // width
WRITE_BYTE( pev->rendercolor.x ); // r, g, b
WRITE_BYTE( pev->rendercolor.y ); // r, g, b
WRITE_BYTE( pev->rendercolor.z ); // r, g, b
WRITE_BYTE( pev->renderamt ); // brightness
MESSAGE_END();
}
else
{
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE(TE_KILLBEAM);
WRITE_SHORT(pTarget->entindex());
MESSAGE_END();
}
}
void CEnvBeamTrail::Spawn( void )
{
Precache();
SET_MODEL(ENT(pev), "sprites/null.spr");
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
if (!(pev->spawnflags & SF_BEAMTRAIL_OFF))
{
SetThink(&CEnvBeamTrail::StartTrailThink);
UTIL_DesiredThink( this );
}
}
//=========================================================
// LRC - custom footstep sounds
//=========================================================
#define SF_FOOTSTEPS_SET 1
#define SF_FOOTSTEPS_ONCE 2
class CEnvFootsteps : public CBaseEntity
{
public:
void Spawn( void );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
STATE GetState( void );
STATE GetState( CBaseEntity* pEnt );
void PrecacheNoise ( const char* szNoise );
};
LINK_ENTITY_TO_CLASS( env_footsteps, CEnvFootsteps );
void CEnvFootsteps::Spawn( void )
{
Precache();
}
void CEnvFootsteps :: PrecacheNoise ( const char* szNoise )
{
static char szBuf[128];
int i = 0, j = 0;
for (i = 0; szNoise[i]; i++)
{
if (szNoise[i] == '?')
{
strcpy(szBuf, szNoise);
for (j = 0; j < 4; j++)
{
szBuf[i] = j+'1';
PRECACHE_SOUND ( szBuf );
}
}
}
if (!j)
PRECACHE_SOUND ( szNoise );
}
void CEnvFootsteps :: Precache ( void )
{
if (pev->noise)
PrecacheNoise(STRING(pev->noise));
if (pev->noise1)
PrecacheNoise(STRING(pev->noise1));
if (pev->noise2)
PrecacheNoise(STRING(pev->noise2));
if (pev->noise3)
PrecacheNoise(STRING(pev->noise3));
}
STATE CEnvFootsteps::GetState()
{
if (pev->spawnflags & SF_FOOTSTEPS_SET) return STATE_OFF;
return pev->impulse ? STATE_ON : STATE_OFF;
}
STATE CEnvFootsteps::GetState(CBaseEntity* pEnt)
{
if (pev->spawnflags & SF_FOOTSTEPS_SET)
return STATE_OFF;
if (pEnt->IsPlayer())
{
// based on trigger_hurt code
int playerMask = 1 << (pEnt->entindex() - 1);
if ( pev->impulse & playerMask )
return STATE_ON;
else
return STATE_OFF;
}
else return GetState();
}
void CEnvFootsteps::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
// union floatToString ftsTemp;
//CONSIDER: add an "all players" spawnflag, like game_text?
if (pActivator && pActivator->IsPlayer())
{
int playerMask = 1 << (pActivator->entindex() - 1);
if (pev->spawnflags & SF_FOOTSTEPS_SET || ( !(pev->impulse & playerMask) && (useType == USE_ON || useType == USE_TOGGLE)))
{
pev->impulse |= playerMask;
if (pev->frags)
{
char sTemp[4];
sprintf(sTemp, "%d", (int)pev->frags);
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "stype", sTemp );
//pActivator->pev->iFootstepType = pev->frags;
}
else if (pev->noise)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "ssnd", STRING(pev->noise) );
}
if (pev->noise1)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "lsnd", STRING(pev->noise1) );
}
if (pev->noise2)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "wsnd", STRING(pev->noise2) );
}
if (pev->noise3)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "psnd", STRING(pev->noise3) );
}
// workaround for physinfo string bug: force the engine to null-terminate it
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "x", "0" );
//ALERT(at_console, "ON, InfoString = %s\n", g_engfuncs.pfnGetPhysicsInfoString(pActivator->edict()));
if (pev->spawnflags & SF_FOOTSTEPS_SET && pev->spawnflags & SF_FOOTSTEPS_ONCE)
{
UTIL_Remove(this);
}
}
else if ( (pev->impulse & playerMask) && (useType == USE_OFF || useType == USE_TOGGLE))
{
pev->impulse &= ~playerMask;
if (pev->frags)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "stype", "0" );
}
else if (pev->noise)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "ssnd", "0" );
}
if (pev->noise1)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "lsnd", "0" );
}
if (pev->noise2)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "wsnd", "0" );
}
if (pev->noise3)
{
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "psnd", "0" );
}
// workaround for physinfo string bug: force the engine to null-terminate it
g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "x", "0" );
//ALERT(at_console, "OFF, InfoString = %s\n", g_engfuncs.pfnGetPhysicsInfoString(pActivator->edict()));
if (pev->spawnflags & SF_FOOTSTEPS_ONCE)
{
UTIL_Remove(this);
}
}
else
{
//ALERT(at_console, "NO EFFECT\n");
}
}
}
//=========================================================
//LRC- the long-awaited effect. (Rain, in the desert? :)
//
//FIXME: give designers a _lot_ more control.
//=========================================================
#define MAX_RAIN_BEAMS 32
#define AXIS_X 1
#define AXIS_Y 2
#define AXIS_Z 0
#define EXTENT_OBSTRUCTED 1
#define EXTENT_ARCING 2
#define EXTENT_OBSTRUCTED_REVERSE 3
#define EXTENT_ARCING_REVERSE 4
class CEnvRain : public CBaseEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
STATE m_iState;
int m_spriteTexture;
int m_iszSpriteName; // have to saverestore this, the beams keep a link to it
int m_dripSize;
int m_minDripSpeed;
int m_maxDripSpeed;
int m_burstSize;
int m_brightness;
int m_pitch; // don't saverestore this
float m_flUpdateTime;
float m_flMaxUpdateTime;
// CBeam* m_pBeams[MAX_RAIN_BEAMS];
int m_axis;
int m_iExtent;
float m_fLifeTime;
int m_iNoise;
virtual STATE GetState( void ) { return m_iState; };
};
LINK_ENTITY_TO_CLASS( env_rain, CEnvRain );
TYPEDESCRIPTION CEnvRain::m_SaveData[] =
{
DEFINE_FIELD( CEnvRain, m_iState, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_spriteTexture, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_dripSize, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_minDripSpeed, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_maxDripSpeed, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_burstSize, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_brightness, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_flUpdateTime, FIELD_FLOAT ),
DEFINE_FIELD( CEnvRain, m_flMaxUpdateTime, FIELD_FLOAT ),
DEFINE_FIELD( CEnvRain, m_iszSpriteName, FIELD_STRING ),
DEFINE_FIELD( CEnvRain, m_axis, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_iExtent, FIELD_INTEGER ),
DEFINE_FIELD( CEnvRain, m_fLifeTime, FIELD_FLOAT ),
DEFINE_FIELD( CEnvRain, m_iNoise, FIELD_INTEGER ),
// DEFINE_FIELD( CEnvRain, m_pBeams, FIELD_CLASSPTR, MAX_RAIN_BEAMS ),
};
IMPLEMENT_SAVERESTORE( CEnvRain, CBaseEntity );
void CEnvRain::Precache( void )
{
m_spriteTexture = PRECACHE_MODEL( (char *)STRING(m_iszSpriteName) );
}
void CEnvRain::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "m_dripSize"))
{
m_dripSize = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_burstSize"))
{
m_burstSize = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_dripSpeed"))
{
int temp = atoi(pkvd->szValue);
m_maxDripSpeed = temp + (temp/4);
m_minDripSpeed = temp - (temp/4);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_brightness"))
{
m_brightness = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_flUpdateTime"))
{
m_flUpdateTime = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_flMaxUpdateTime"))
{
m_flMaxUpdateTime = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "pitch"))
{
m_pitch = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "texture"))
{
m_iszSpriteName = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_axis"))
{
m_axis = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iExtent"))
{
m_iExtent = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_fLifeTime"))
{
m_fLifeTime = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iNoise"))
{
m_iNoise = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
void CEnvRain::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (!ShouldToggle(useType)) return;
if (m_iState == STATE_ON)
{
m_iState = STATE_OFF;
DontThink();
}
else
{
m_iState = STATE_ON;
SetNextThink( 0.1 );
}
}
#define SF_RAIN_START_OFF 1
void CEnvRain::Spawn( void )
{
Precache();
SET_MODEL( ENT(pev), STRING(pev->model) ); // Set size
pev->solid = SOLID_NOT;
pev->effects = EF_NODRAW;
if (pev->rendercolor == g_vecZero)
pev->rendercolor = Vector(255,255,255);
if (m_pitch)
pev->angles.x = m_pitch;
else if (pev->angles.x == 0) // don't allow horizontal rain.
pev->angles.x = 90;
if (m_burstSize == 0) // in case the level designer forgot to set it.
m_burstSize = 2;
if (pev->spawnflags & SF_RAIN_START_OFF)
m_iState = STATE_OFF;
else
{
m_iState = STATE_ON;
SetNextThink( 0.1 );
}
}
void CEnvRain::Think( void )
{
// ALERT(at_console,"RainThink %d %d %d %s\n",m_spriteTexture,m_dripSize,m_brightness,STRING(m_iszSpriteName));
Vector vecSrc;
Vector vecDest;
UTIL_MakeVectors(pev->angles);
Vector vecOffs = gpGlobals->v_forward;
switch (m_axis)
{
case AXIS_X:
vecOffs = vecOffs * (pev->size.x / vecOffs.x);
break;
case AXIS_Y:
vecOffs = vecOffs * (pev->size.y / vecOffs.y);
break;
case AXIS_Z:
vecOffs = vecOffs * (pev->size.z / vecOffs.z);
break;
}
// ALERT(at_console,"RainThink offs.z = %f, size.z = %f\n",vecOffs.z,pev->size.z);
int repeats;
if (!m_fLifeTime && !m_flUpdateTime && !m_flMaxUpdateTime)
repeats = m_burstSize * 3;
else
repeats = m_burstSize;
int drawn = 0;
int tries = 0;
TraceResult tr;
BOOL bDraw;
while (drawn < repeats && tries < (repeats*3))
{
tries++;
if (m_axis == AXIS_X)
vecSrc.x = pev->maxs.x;
else
vecSrc.x = pev->mins.x + RANDOM_LONG(0, pev->size.x);
if (m_axis == AXIS_Y)
vecSrc.y = pev->maxs.y;
else
vecSrc.y = pev->mins.y + RANDOM_LONG(0, pev->size.y);
if (m_axis == AXIS_Z)
vecSrc.z = pev->maxs.z;
else
vecSrc.z = pev->mins.z + RANDOM_LONG(0, pev->size.z);
vecDest = vecSrc - vecOffs;
bDraw = TRUE;
switch (m_iExtent)
{
case EXTENT_OBSTRUCTED:
UTIL_TraceLine( vecSrc, vecDest, ignore_monsters, NULL, &tr);
vecDest = tr.vecEndPos;
break;
case EXTENT_OBSTRUCTED_REVERSE:
UTIL_TraceLine( vecDest, vecSrc, ignore_monsters, NULL, &tr);
vecSrc = tr.vecEndPos;
break;
case EXTENT_ARCING:
UTIL_TraceLine( vecSrc, vecDest, ignore_monsters, NULL, &tr);
if (tr.flFraction == 1.0f) bDraw = FALSE;
vecDest = tr.vecEndPos;
break;
case EXTENT_ARCING_REVERSE:
UTIL_TraceLine( vecDest, vecSrc, ignore_monsters, NULL, &tr);
if (tr.flFraction == 1.0f) bDraw = FALSE;
vecSrc = tr.vecEndPos;
break;
}
// vecDest.z = pev->mins.z;
if (!bDraw) continue;
drawn++;
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMPOINTS );
WRITE_COORD(vecDest.x);
WRITE_COORD(vecDest.y);
WRITE_COORD(vecDest.z);
WRITE_COORD(vecSrc.x);
WRITE_COORD(vecSrc.y);
WRITE_COORD(vecSrc.z);
WRITE_SHORT( m_spriteTexture );
WRITE_BYTE( (int)0 ); // framestart
WRITE_BYTE( (int)0 ); // framerate
if (m_fLifeTime) // life
WRITE_BYTE( (int)(m_fLifeTime*10) );
else if (m_flMaxUpdateTime)
WRITE_BYTE( (int)( RANDOM_FLOAT(m_flUpdateTime, m_flMaxUpdateTime)*30 ));
else
WRITE_BYTE( (int)(m_flUpdateTime * 30) ); // life
WRITE_BYTE( m_dripSize ); // width
WRITE_BYTE( m_iNoise ); // noise
WRITE_BYTE( (int)pev->rendercolor.x ); // r,
WRITE_BYTE( (int)pev->rendercolor.y ); // g,
WRITE_BYTE( (int)pev->rendercolor.z ); // b
WRITE_BYTE( m_brightness ); // brightness
WRITE_BYTE( (int)RANDOM_LONG(m_minDripSpeed,m_maxDripSpeed) ); // speed
MESSAGE_END();
}
// drawn will be false if we didn't draw anything.
if (pev->target && drawn)
FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0);
if (m_flMaxUpdateTime)
SetNextThink( RANDOM_FLOAT(m_flMaxUpdateTime, m_flUpdateTime) );
else if (m_flUpdateTime)
SetNextThink( m_flUpdateTime );
}
//==================================================================
//LRC- Xen monsters' warp-in effect, for those too lazy to build it. :)
//==================================================================
class CEnvWarpBall : public CBaseEntity
{
public:
void Precache( void );
void Spawn( void ) { Precache(); }
void Think( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
LINK_ENTITY_TO_CLASS( env_warpball, CEnvWarpBall );
void CEnvWarpBall::Precache( void )
{
PRECACHE_MODEL( "sprites/lgtning.spr" );
PRECACHE_MODEL( "sprites/Fexplo1.spr" );
PRECACHE_MODEL( "sprites/XFlare1.spr" );
PRECACHE_SOUND( "debris/beamstart2.wav" );
PRECACHE_SOUND( "debris/beamstart7.wav" );
}
void CEnvWarpBall::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int iTimes = 0;
int iDrawn = 0;
TraceResult tr;
Vector vecDest;
CBeam *pBeam;
while (iDrawn<pev->frags && iTimes<(pev->frags * 3)) // try to draw <frags> beams, but give up after 3x<frags> tries.
{
vecDest = pev->health * (Vector(RANDOM_FLOAT(-1,1), RANDOM_FLOAT(-1,1), RANDOM_FLOAT(-1,1)).Normalize());
UTIL_TraceLine( pev->origin, pev->origin + vecDest, ignore_monsters, NULL, &tr);
if (tr.flFraction != 1.0f)
{
// we hit something.
iDrawn++;
pBeam = CBeam::BeamCreate("sprites/lgtning.spr",200);
pBeam->PointsInit( pev->origin, tr.vecEndPos );
pBeam->SetColor( 197, 243, 169 );
pBeam->SetNoise( 65 );
pBeam->SetBrightness( 150 );
pBeam->SetWidth( 18 );
pBeam->SetScrollRate( 35 );
pBeam->SetThink(&CBeam:: SUB_Remove );
pBeam->SetNextThink( 1 );
}
iTimes++;
}
EMIT_SOUND( edict(), CHAN_BODY, "debris/beamstart2.wav", 1, ATTN_NORM );
CSprite *pSpr = CSprite::SpriteCreate( "sprites/Fexplo1.spr", pev->origin, TRUE );
pSpr->AnimateAndDie( 10 );
pSpr->SetTransparency(kRenderGlow, 77, 210, 130, 255, kRenderFxNoDissipation);
pSpr = CSprite::SpriteCreate( "sprites/XFlare1.spr", pev->origin, TRUE );
pSpr->AnimateAndDie( 10 );
pSpr->SetTransparency(kRenderGlow, 184, 250, 214, 255, kRenderFxNoDissipation);
SetNextThink( 0.5 );
}
void CEnvWarpBall::Think( void )
{
EMIT_SOUND( edict(), CHAN_ITEM, "debris/beamstart7.wav", 1, ATTN_NORM );
SUB_UseTargets( this, USE_TOGGLE, 0);
}
//==================================================================
//LRC- Shockwave effect, like when a Houndeye attacks.
//==================================================================
#define SF_SHOCKWAVE_CENTERED 1
#define SF_SHOCKWAVE_REPEATABLE 2
class CEnvShockwave : public CPointEntity
{
public:
void Precache( void );
void Spawn( void ) { Precache(); }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void DoEffect( Vector vecPos );
int m_iTime;
int m_iRadius;
int m_iHeight;
int m_iScrollRate;
int m_iNoise;
int m_iFrameRate;
int m_iStartFrame;
int m_iSpriteTexture;
char m_cType;
int m_iszPosition;
};
LINK_ENTITY_TO_CLASS( env_shockwave, CEnvShockwave );
TYPEDESCRIPTION CEnvShockwave::m_SaveData[] =
{
DEFINE_FIELD( CEnvShockwave, m_iHeight, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iTime, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iRadius, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iScrollRate, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iNoise, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iFrameRate, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iStartFrame, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_iSpriteTexture, FIELD_INTEGER ),
DEFINE_FIELD( CEnvShockwave, m_cType, FIELD_CHARACTER ),
DEFINE_FIELD( CEnvShockwave, m_iszPosition, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CEnvShockwave, CBaseEntity );
void CEnvShockwave::Precache( void )
{
m_iSpriteTexture = PRECACHE_MODEL( (char *)STRING(pev->netname) );
}
void CEnvShockwave::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "m_iTime"))
{
m_iTime = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iRadius"))
{
m_iRadius = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iHeight"))
{
m_iHeight = atoi(pkvd->szValue)/2; //LRC- the actual height is doubled when drawn
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iScrollRate"))
{
m_iScrollRate = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iNoise"))
{
m_iNoise = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iFrameRate"))
{
m_iFrameRate = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iStartFrame"))
{
m_iStartFrame = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_iszPosition"))
{
m_iszPosition = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "m_cType"))
{
m_cType = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
void CEnvShockwave::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
Vector vecPos;
if (m_iszPosition)
vecPos = CalcLocus_Position( this, pActivator, STRING(m_iszPosition) );
else
vecPos = pev->origin;
if (!(pev->spawnflags & SF_SHOCKWAVE_CENTERED))
vecPos.z += m_iHeight;
// blast circle
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
if (m_cType)
WRITE_BYTE( m_cType );
else
WRITE_BYTE( TE_BEAMCYLINDER );
WRITE_COORD( vecPos.x );// coord coord coord (center position)
WRITE_COORD( vecPos.y );
WRITE_COORD( vecPos.z );
WRITE_COORD( vecPos.x );// coord coord coord (axis and radius)
WRITE_COORD( vecPos.y );
WRITE_COORD( vecPos.z + m_iRadius );
WRITE_SHORT( m_iSpriteTexture ); // short (sprite index)
WRITE_BYTE( m_iStartFrame ); // byte (starting frame)
WRITE_BYTE( m_iFrameRate ); // byte (frame rate in 0.1's)
WRITE_BYTE( m_iTime ); // byte (life in 0.1's)
WRITE_BYTE( m_iHeight ); // byte (line width in 0.1's)
WRITE_BYTE( m_iNoise ); // byte (noise amplitude in 0.01's)
WRITE_BYTE( pev->rendercolor.x ); // byte,byte,byte (color)
WRITE_BYTE( pev->rendercolor.y );
WRITE_BYTE( pev->rendercolor.z );
WRITE_BYTE( pev->renderamt ); // byte (brightness)
WRITE_BYTE( m_iScrollRate ); // byte (scroll speed in 0.1's)
MESSAGE_END();
if (!(pev->spawnflags & SF_SHOCKWAVE_REPEATABLE))
{
SetThink(&CEnvShockwave:: SUB_Remove );
SetNextThink( 0 );
}
}
//==================================================================
//LRC- env_dlight; Dynamic Entity Light creator
//==================================================================
#define SF_DLIGHT_ONLYONCE 1
#define SF_DLIGHT_STARTON 2
class CEnvDLight : public CPointEntity
{
public:
void PostSpawn( void );
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
void DesiredAction( void );
virtual void MakeLight( int iTime );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
STATE GetState( void )
{
if (pev->spawnflags & SF_DLIGHT_STARTON)
return STATE_ON;
else
return STATE_OFF;
}
Vector m_vecPos;
int m_iKey;
static int ms_iNextFreeKey;
};
LINK_ENTITY_TO_CLASS( env_dlight, CEnvDLight );
TYPEDESCRIPTION CEnvDLight::m_SaveData[] =
{
DEFINE_FIELD( CEnvDLight, m_vecPos, FIELD_VECTOR ),
DEFINE_FIELD( CEnvDLight, m_iKey, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CEnvDLight, CPointEntity );
int CEnvDLight::ms_iNextFreeKey = 1;
void CEnvDLight::PostSpawn( void )
{
// each env_dlight uses its own key to reference the light on the client
m_iKey = ms_iNextFreeKey;
ms_iNextFreeKey++;
if (FStringNull(pev->targetname) || pev->spawnflags & SF_DLIGHT_STARTON)
{
UTIL_DesiredAction(this);
}
}
void CEnvDLight::DesiredAction( void )
{
pev->spawnflags &= ~SF_DLIGHT_STARTON;
Use(this, this, USE_ON, 0);
}
void CEnvDLight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (!ShouldToggle(useType))
{
return;
}
if (GetState() == STATE_ON)
{
// turn off
MakeLight(false);
pev->spawnflags &= ~SF_DLIGHT_STARTON;
DontThink();
return;
}
if (pev->message)
{
m_vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) );
}
else
{
m_vecPos = pev->origin;
}
// turn on
MakeLight(true);
pev->spawnflags |= SF_DLIGHT_STARTON;
if (pev->health)
{
SetNextThink(pev->health);
}
else
{
if (pev->spawnflags & SF_DLIGHT_ONLYONCE)
{
SetThink( &CEnvDLight::SUB_Remove );
SetNextThink( 0 );
}
}
}
extern int gmsgKeyedDLight;
void CEnvDLight::MakeLight( BOOL bActive)
{
MESSAGE_BEGIN( MSG_ALL, gmsgKeyedDLight, NULL );
WRITE_BYTE( m_iKey );
WRITE_BYTE( bActive ); // visible?
if (bActive)
{
WRITE_COORD( m_vecPos.x ); // X
WRITE_COORD( m_vecPos.y ); // Y
WRITE_COORD( m_vecPos.z ); // Z
WRITE_BYTE( pev->renderamt ); // radius * 0.1
WRITE_BYTE( pev->rendercolor.x ); // r
WRITE_BYTE( pev->rendercolor.y ); // g
WRITE_BYTE( pev->rendercolor.z ); // b
}
MESSAGE_END();
}
void CEnvDLight::Think( void )
{
// turn off the light
MakeLight( false );
pev->spawnflags &= ~SF_DLIGHT_STARTON;
if (pev->spawnflags & SF_DLIGHT_ONLYONCE)
{
SetThink( &CEnvDLight::SUB_Remove );
SetNextThink( 0 );
}
}
//==================================================================
//LRC- env_elight; Dynamic Entity Light creator
//==================================================================
class CEnvELight : public CEnvDLight
{
public:
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void MakeLight(int iTime);
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
EHANDLE m_hAttach;
};
LINK_ENTITY_TO_CLASS( env_elight, CEnvELight );
TYPEDESCRIPTION CEnvELight::m_SaveData[] =
{
DEFINE_FIELD( CEnvELight, m_hAttach, FIELD_EHANDLE ),
};
IMPLEMENT_SAVERESTORE( CEnvELight, CEnvDLight );
void CEnvELight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (pev->target)
{
m_hAttach = UTIL_FindEntityByTargetname( NULL, STRING(pev->target), pActivator);
if (m_hAttach == 0)
{
ALERT(at_console, "env_elight \"%s\" can't find target %s\n", STRING(pev->targetname), STRING(pev->target));
return; // error?
}
}
else
{
m_hAttach = this;
}
CEnvDLight::Use(pActivator, pCaller, useType, value);
}
void CEnvELight::MakeLight(int iTime)
{
if (m_hAttach == 0)
{
DontThink();
pev->takedamage = 0;
return;
}
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_ELIGHT );
WRITE_SHORT( m_hAttach->entindex( ) + 0x1000 * pev->impulse ); // entity, attachment
WRITE_COORD( m_vecPos.x ); // X
WRITE_COORD( m_vecPos.y ); // Y
WRITE_COORD( m_vecPos.z ); // Z
WRITE_COORD( pev->renderamt ); // radius * 0.1
WRITE_BYTE( pev->rendercolor.x ); // r
WRITE_BYTE( pev->rendercolor.y ); // g
WRITE_BYTE( pev->rendercolor.z ); // b
WRITE_BYTE( iTime ); // time * 10
WRITE_COORD( pev->frags ); // decay * 0.1
MESSAGE_END( );
}
//=========================================================
// LRC - Decal effect
//=========================================================
class CEnvDecal : public CPointEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
};
LINK_ENTITY_TO_CLASS( env_decal, CEnvDecal );
void CEnvDecal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int iTexture = 0;
switch(pev->impulse)
{
case 1: iTexture = DECAL_GUNSHOT1 + RANDOM_LONG(0,4); break;
case 2: iTexture = DECAL_BLOOD1 + RANDOM_LONG(0,5); break;
case 3: iTexture = DECAL_YBLOOD1 + RANDOM_LONG(0,5); break;
case 4: iTexture = DECAL_GLASSBREAK1+ RANDOM_LONG(0,2); break;
case 5: iTexture = DECAL_BIGSHOT1 + RANDOM_LONG(0,4); break;
case 6: iTexture = DECAL_SCORCH1 + RANDOM_LONG(0,1); break;
case 7: iTexture = DECAL_SPIT1 + RANDOM_LONG(0,1); break;
}
if (pev->impulse)
iTexture = gDecals[ iTexture ].index;
else
iTexture = pev->skin; // custom texture
Vector vecPos;
if (!FStringNull(pev->target))
vecPos = CalcLocus_Position( this, pActivator, STRING(pev->target) );
else
vecPos = pev->origin;
Vector vecOffs;
if (!FStringNull(pev->netname))
vecOffs = CalcLocus_Velocity( this, pActivator, STRING(pev->netname) );
else
{
UTIL_MakeVectors(pev->angles);
vecOffs = gpGlobals->v_forward;
}
if (pev->message)
vecOffs = vecOffs * CalcLocus_Ratio( pActivator, STRING(pev->message) );
else
vecOffs = vecOffs.Normalize() * 4000;
TraceResult trace;
int entityIndex;
UTIL_TraceLine( vecPos, vecPos+vecOffs, ignore_monsters, NULL, &trace );
if (trace.flFraction == 1.0f)
return; // didn't hit anything, oh well
entityIndex = (short)ENTINDEX(trace.pHit);
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY);
WRITE_BYTE( TE_BSPDECAL );
WRITE_COORD( trace.vecEndPos.x );
WRITE_COORD( trace.vecEndPos.y );
WRITE_COORD( trace.vecEndPos.z );
WRITE_SHORT( iTexture );
WRITE_SHORT( entityIndex );
if ( entityIndex )
WRITE_SHORT( (int)VARS(trace.pHit)->modelindex );
MESSAGE_END();
}
void CEnvDecal::Spawn( void )
{
if (pev->impulse == 0)
{
pev->skin = DECAL_INDEX( STRING(pev->noise) );
if ( pev->skin == 0 )
ALERT( at_console, "locus_decal \"%s\" can't find decal \"%s\"\n", STRING(pev->noise) );
}
}
//=========================================================
// Beverage Dispenser
// overloaded pev->frags, is now a flag for whether or not a can is stuck in the dispenser.
// overloaded pev->health, is now how many cans remain in the machine.
//=========================================================
class CEnvBeverage : public CBaseDelay
{
public:
void Spawn( void );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
// it's 'on' while there are cans left
virtual STATE GetState( void ) { return (pev->health > 0)?STATE_ON:STATE_OFF; };
};
void CEnvBeverage::Precache( void )
{
PRECACHE_MODEL( "models/can.mdl" );
PRECACHE_SOUND( "weapons/g_bounce3.wav" );
}
LINK_ENTITY_TO_CLASS( env_beverage, CEnvBeverage )
void CEnvBeverage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( pev->frags != 0 || pev->health <= 0 )
{
// no more cans while one is waiting in the dispenser, or if I'm out of cans.
return;
}
Vector vecPos;
if (pev->target)
vecPos = CalcLocus_Position( this, pActivator, STRING(pev->target) );
else
vecPos = pev->origin;
CBaseEntity *pCan = CBaseEntity::Create( "item_sodacan", vecPos, pev->angles, edict() );
if( pev->skin == 6 )
{
// random
pCan->pev->skin = RANDOM_LONG( 0, 5 );
}
else
{
pCan->pev->skin = pev->skin;
}
pev->frags = 1;
pev->health--;
//SetThink( &SUB_Remove );
//pev->nextthink = gpGlobals->time;
}
void CEnvBeverage::Spawn( void )
{
Precache();
pev->solid = SOLID_NOT;
pev->effects = EF_NODRAW;
pev->frags = 0;
if( pev->health == 0 )
{
pev->health = 10;
}
}
//=========================================================
// Soda can
//=========================================================
class CItemSoda : public CBaseEntity
{
public:
void Spawn( void );
void Precache( void );
void EXPORT CanThink( void );
void EXPORT CanTouch( CBaseEntity *pOther );
};
void CItemSoda::Precache( void )
{
// added for Nemo1024 --LRC
PRECACHE_MODEL( "models/can.mdl" );
PRECACHE_SOUND( "weapons/g_bounce3.wav" );
}
LINK_ENTITY_TO_CLASS( item_sodacan, CItemSoda )
void CItemSoda::Spawn( void )
{
Precache();
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_TOSS;
SET_MODEL( ENT( pev ), "models/can.mdl" );
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
SetThink( &CItemSoda::CanThink );
SetNextThink( 0.5f );
}
void CItemSoda::CanThink( void )
{
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "weapons/g_bounce3.wav", 1, ATTN_NORM );
pev->solid = SOLID_TRIGGER;
UTIL_SetSize( pev, Vector( -8, -8, 0 ), Vector( 8, 8, 8 ) );
SetThink( NULL );
SetTouch( &CItemSoda::CanTouch );
}
void CItemSoda::CanTouch( CBaseEntity *pOther )
{
if( !pOther->IsPlayer() )
{
return;
}
// spoit sound here
pOther->TakeHealth( 1, DMG_GENERIC );// a bit of health.
if( !FNullEnt( pev->owner ) )
{
// tell the machine the can was taken
pev->owner->v.frags = 0;
}
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->effects = EF_NODRAW;
SetTouch( NULL );
SetThink(&CItemSoda:: SUB_Remove );
SetNextThink( 0 );
}
//=========================================================
// LRC - env_fog, extended a bit from the DMC version
//=========================================================
#define SF_FOG_ACTIVE 1
#define SF_FOG_FADING 0x8000
class CEnvFog : public CBaseEntity
{
public:
void Spawn( void );
void Precache( void );
void EXPORT ResumeThink( void );
void EXPORT Resume2Think( void );
void EXPORT TurnOn( void );
void EXPORT TurnOff( void );
void EXPORT FadeInDone( void );
void EXPORT FadeOutDone( void );
void SendData( Vector col, int fFadeTime, int StartDist, int iEndDist);
void KeyValue( KeyValueData *pkvd );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
STATE GetState( void );
int m_iStartDist;
int m_iEndDist;
float m_iFadeIn;
float m_iFadeOut;
float m_fHoldTime;
float m_fFadeStart; // if we're fading in/out, then when did the fade start?
};
TYPEDESCRIPTION CEnvFog::m_SaveData[] =
{
DEFINE_FIELD( CEnvFog, m_iStartDist, FIELD_INTEGER ),
DEFINE_FIELD( CEnvFog, m_iEndDist, FIELD_INTEGER ),
DEFINE_FIELD( CEnvFog, m_iFadeIn, FIELD_INTEGER ),
DEFINE_FIELD( CEnvFog, m_iFadeOut, FIELD_INTEGER ),
DEFINE_FIELD( CEnvFog, m_fHoldTime, FIELD_FLOAT ),
DEFINE_FIELD( CEnvFog, m_fFadeStart, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CEnvFog, CBaseEntity );
void CEnvFog :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "startdist"))
{
m_iStartDist = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "enddist"))
{
m_iEndDist = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "fadein"))
{
m_iFadeIn = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "fadeout"))
{
m_iFadeOut = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "holdtime"))
{
m_fHoldTime = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
STATE CEnvFog::GetState( void )
{
if (pev->spawnflags & SF_FOG_ACTIVE)
{
if (pev->spawnflags & SF_FOG_FADING)
return STATE_TURN_ON;
else
return STATE_ON;
}
else
{
if (pev->spawnflags & SF_FOG_FADING)
return STATE_TURN_OFF;
else
return STATE_OFF;
}
}
void CEnvFog :: Spawn ( void )
{
pev->effects |= EF_NODRAW;
if (pev->targetname == 0)
pev->spawnflags |= SF_FOG_ACTIVE;
if (pev->spawnflags & SF_FOG_ACTIVE)
{
SetThink(&CEnvFog :: TurnOn );
UTIL_DesiredThink( this );
}
// Precache is now used only to continue after a game has loaded.
// Precache();
// things get messed up if we try to draw fog with a startdist
// or an enddist of 0, so we don't allow it.
if (m_iStartDist == 0) m_iStartDist = 1;
if (m_iEndDist == 0) m_iEndDist = 1;
}
void CEnvFog :: Precache ( void )
{
if (pev->spawnflags & SF_FOG_ACTIVE)
{
SetThink(&CEnvFog :: ResumeThink );
SetNextThink( 0.1 );
}
}
extern int gmsgSetFog;
void CEnvFog :: TurnOn ( void )
{
// ALERT(at_console, "Fog turnon %f\n", gpGlobals->time);
pev->spawnflags |= SF_FOG_ACTIVE;
if( m_iFadeIn )
{
pev->spawnflags |= SF_FOG_FADING;
SendData( pev->rendercolor, m_iFadeIn, m_iStartDist, m_iEndDist);
SetNextThink( m_iFadeIn );
SetThink(&CEnvFog :: FadeInDone );
}
else
{
pev->spawnflags &= ~SF_FOG_FADING;
SendData( pev->rendercolor, 0, m_iStartDist, m_iEndDist);
if (m_fHoldTime)
{
SetNextThink( m_fHoldTime );
SetThink(&CEnvFog :: TurnOff );
}
}
}
void CEnvFog :: TurnOff ( void )
{
// ALERT(at_console, "Fog turnoff\n");
pev->spawnflags &= ~SF_FOG_ACTIVE;
if( m_iFadeOut )
{
pev->spawnflags |= SF_FOG_FADING;
SendData( pev->rendercolor, -m_iFadeOut, m_iStartDist, m_iEndDist);
SetNextThink( m_iFadeOut );
SetThink(&CEnvFog :: FadeOutDone );
}
else
{
pev->spawnflags &= ~SF_FOG_FADING;
SendData( g_vecZero, 0, 0, 0 );
DontThink();
}
}
//yes, this intermediate think function is necessary.
// the engine seems to ignore the nextthink time when starting up.
// So this function gets called immediately after the precache finishes,
// regardless of what nextthink time is specified.
void CEnvFog :: ResumeThink ( void )
{
// ALERT(at_console, "Fog resume %f\n", gpGlobals->time);
SetThink(&CEnvFog ::FadeInDone);
SetNextThink(0.1);
}
void CEnvFog :: FadeInDone ( void )
{
pev->spawnflags &= ~SF_FOG_FADING;
SendData( pev->rendercolor, 0, m_iStartDist, m_iEndDist);
if (m_fHoldTime)
{
SetNextThink( m_fHoldTime );
SetThink(&CEnvFog :: TurnOff );
}
}
void CEnvFog :: FadeOutDone ( void )
{
pev->spawnflags &= ~SF_FOG_FADING;
SendData( g_vecZero, 0, 0, 0);
}
void CEnvFog :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
// ALERT(at_console, "Fog use %s %s\n", GetStringForUseType(useType), GetStringForState(GetState()));
if (ShouldToggle(useType))
{
if (pev->spawnflags & SF_FOG_ACTIVE)
TurnOff();
else
TurnOn();
}
}
void CEnvFog :: SendData ( Vector col, int iFadeTime, int iStartDist, int iEndDist )
{
// ALERT(at_console, "Fog send (%d %d %d), %d - %d\n", col.x, col.y, col.z, iStartDist, iEndDist);
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBasePlayer *pPlayer = (CBasePlayer*)UTIL_PlayerByIndex( i );
if ( pPlayer )
{
MESSAGE_BEGIN( MSG_ONE, gmsgSetFog, NULL, pPlayer->pev );
WRITE_BYTE ( col.x );
WRITE_BYTE ( col.y );
WRITE_BYTE ( col.z );
WRITE_SHORT ( iFadeTime );
WRITE_SHORT ( iStartDist );
WRITE_SHORT ( iEndDist );
MESSAGE_END();
// pPlayer->m_iFogStartDist = iStartDist;
// pPlayer->m_iFogEndDist = iEndDist;
// pPlayer->m_vecFogColor = col;
// pPlayer->m_bClientFogRefresh = FALSE;
}
}
}
LINK_ENTITY_TO_CLASS( env_fog, CEnvFog );
//=========================================================
// LRC - env_sky, an unreal tournament-style sky effect
//=========================================================
class CEnvSky : public CBaseEntity
{
public:
void Activate( void );
void Think( void );
};
void CEnvSky :: Activate ( void )
{
pev->effects |= EF_NODRAW;
pev->nextthink = gpGlobals->time + 1.0f;
}
extern int gmsgSetSky;
void CEnvSky :: Think ()
{
MESSAGE_BEGIN(MSG_BROADCAST, gmsgSetSky, NULL);
WRITE_BYTE(1); // mode
WRITE_COORD(pev->origin.x); // view position
WRITE_COORD(pev->origin.y);
WRITE_COORD(pev->origin.z);
MESSAGE_END();
}
LINK_ENTITY_TO_CLASS( env_sky, CEnvSky );
//=========================================================
// LRC - env_particle, uses the aurora particle system
//=========================================================
//extern int gmsgParticle = 0;
#define SF_PARTICLE_ON 1
class CParticle : public CPointEntity
{
public:
void Spawn( void );
void Activate( void );
void Precache( void );
void DesiredAction( void );
void EXPORT Think( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
};
LINK_ENTITY_TO_CLASS( env_particle, CParticle );
void CParticle::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->renderamt = 128;
pev->rendermode = kRenderTransTexture;
// 'body' determines whether the effect is active or not
pev->body = (pev->spawnflags & SF_PARTICLE_ON) != 0;
Precache();
UTIL_SetOrigin(this, pev->origin);
SET_MODEL(edict(), "sprites/null.spr");
}
void CParticle::Precache( void )
{
PRECACHE_MODEL("sprites/null.spr");
}
void CParticle::Activate( void )
{
CPointEntity::Activate();
UTIL_DesiredAction(this);
}
void CParticle::DesiredAction( void )
{
pev->nextthink = gpGlobals->time + 1;
}
void CParticle::Think( void )
{
MESSAGE_BEGIN( MSG_ALL, gmsgParticle );
WRITE_BYTE( entindex() );
WRITE_STRING( STRING(pev->message) );
MESSAGE_END();
}
void CParticle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if ( ShouldToggle( useType, pev->body ) )
{
pev->body = !pev->body;
//ALERT(at_console, "Setting body %d\n", pev->body);
}
}