/*** * * 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 "game.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 #include #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 = pev->speed > 0 ? pev->speed : -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.0 ); 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.1 ); } 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.5 ); else SetNextThink( 2.5 - (0.1 * 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)<<12); pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); } void CBeam::SetEndEntity( int entityIndex ) { pev->skin = (entityIndex & 0x0FFF) | ((pev->skin&0xF000)<<12); 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( (char *)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 = min( startPos.x, endPos.x ); pev->mins.y = min( startPos.y, endPos.y ); pev->mins.z = min( startPos.z, endPos.z ); pev->maxs.x = max( startPos.x, endPos.x ); pev->maxs.y = max( startPos.y, endPos.y ); pev->maxs.z = 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_debug, "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; int m_iszStartEntity; int m_iszEndEntity; float m_life; int m_boltWidth; int m_noiseAmplitude; int m_brightness; int m_speed; float m_restrike; int m_spriteTexture; int 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.1 ); } 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.0 ); } } } void CLightning::Precache( void ) { m_spriteTexture = PRECACHE_MODEL( (char *)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.1 ); } 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, 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_debug, "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.0) ); // 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( 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.0 || 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.0 && 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, pev->frags ); ApplyMultiDamage( pev, pev ); if ( pev->spawnflags & SF_BEAM_DECALS ) { if ( pHit->IsBSPModel() ) UTIL_TraceCustomDecal( ptr, "shot" ); } } 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.1 ); 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)) { //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.0) ); // 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( 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 = 0; for (iLoops = 0; iLoops < 10; iLoops++) { Vector vecSrc = pev->origin; Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); vecDir1 = vecDir1.Normalize(); TraceResult tr1; UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT(pev), &tr1 ); if (tr1.flFraction == 1.0) continue; Vector vecDir2; do { vecDir2 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); } while (DotProduct(vecDir1, vecDir2 ) > 0); vecDir2 = vecDir2.Normalize(); TraceResult tr2; UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, ignore_monsters, ENT(pev), &tr2 ); if (tr2.flFraction == 1.0) continue; if ((tr1.vecEndPos - tr2.vecEndPos).Length() < m_radius * 0.1) continue; UTIL_TraceLine( tr1.vecEndPos, tr2.vecEndPos, ignore_monsters, ENT(pev), &tr2 ); if (tr2.flFraction != 1.0) continue; Zap( tr1.vecEndPos, tr2.vecEndPos ); break; } } void CLightning::RandomPoint( Vector &vecSrc ) { int iLoops = 0; for (iLoops = 0; iLoops < 10; iLoops++) { Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); 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.1) continue; if (tr1.flFraction == 1.0) 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( (char *)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( (char *)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( 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) { //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.1 ); } 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->frame = 0; PRECACHE_MODEL( (char *)STRING(pev->model) ); SET_MODEL( ENT(pev), STRING(pev->model) ); m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; if ( m_maxFrame > 1.0 && pev->framerate != 0 ) SetNextThink( 0.1 ); m_lastTime = gpGlobals->time; } void CGlow::Think( void ) { Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); SetNextThink( 0.1 ); 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->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( (char *)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.1 ); 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.1 ); 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 &= ~EF_NODRAW; if ( (pev->framerate && m_maxFrame > 1.0) || (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 #define SF_ENVMODEL_NEWLIGHTING 8 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 0 // g-cont. just for testing gloss-effect pev->movetype = MOVETYPE_NOCLIP; pev->avelocity = Vector( 5, 5, 5 ); #endif 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) ); } if( !m_pMoveWith && FBitSet( pev->spawnflags, SF_ENVMODEL_NEWLIGHTING )) { // tell the client about static entity SetBits( pev->iuser1, CF_STATIC_ENTITY ); } 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; } } // =================== ENV_STATIC ============================================== #define SF_STATIC_SOLID BIT( 0 ) #define SF_STATIC_DROPTOFLOOR BIT( 1 ) #define SF_STATIC_NOSHADOW BIT( 2 ) // hlrad affected class CEnvStatic : public CBaseEntity { public: void Spawn( void ); void AutoSetSize( void ); virtual int ObjectCaps( void ) { return 0; } void SetObjectCollisionBox( void ); void KeyValue( KeyValueData *pkvd ); int vertex_light_cache; int surface_light_cache; }; LINK_ENTITY_TO_CLASS( env_static, CEnvStatic ); void CEnvStatic :: KeyValue( KeyValueData *pkvd ) { if( FStrEq(pkvd->szKeyName, "xform")) { UTIL_StringToVector( (float*)pev->startpos, pkvd->szValue ); pkvd->fHandled = TRUE; } else if( FStrEq(pkvd->szKeyName, "vlight_cache")) { vertex_light_cache = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } else if( FStrEq(pkvd->szKeyName, "flight_cache")) { surface_light_cache = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } else CBaseEntity::KeyValue( pkvd ); } void CEnvStatic :: Spawn( void ) { if( pev->startpos == g_vecZero ) pev->startpos = Vector( pev->scale, pev->scale, pev->scale ); if( surface_light_cache ) { SetBits( pev->iuser1, CF_STATIC_LIGHTMAPPED ); pev->colormap = surface_light_cache; } else { pev->colormap = vertex_light_cache; } // check xform values if( pev->startpos.x < 0.01f ) pev->startpos.x = 1.0f; if( pev->startpos.y < 0.01f ) pev->startpos.y = 1.0f; if( pev->startpos.z < 0.01f ) pev->startpos.z = 1.0f; if( pev->startpos.x > 16.0f ) pev->startpos.x = 16.0f; if( pev->startpos.y > 16.0f ) pev->startpos.y = 16.0f; if( pev->startpos.z > 16.0f ) pev->startpos.z = 16.0f; PRECACHE_MODEL( (char *)STRING(pev->model) ); SET_MODEL( ENT(pev), STRING(pev->model) ); // tell the client about static entity SetBits( pev->iuser1, CF_STATIC_ENTITY ); if( FBitSet( pev->spawnflags, SF_STATIC_SOLID )) { if( g_fPhysicInitialized ) pev->solid = SOLID_CUSTOM; pev->movetype = MOVETYPE_NONE; AutoSetSize(); } if( FBitSet( pev->spawnflags, SF_STATIC_DROPTOFLOOR )) { TraceResult trace; Vector vecStart = pev->origin; Vector vecEnd = pev->origin - Vector( 0, 0, 256 ); TRACE_LINE( vecStart, vecEnd, TRUE, edict(), &trace ); if( !trace.fAllSolid && trace.flFraction < 1.0f ) UTIL_SetOrigin( this, trace.vecEndPos ); } else { UTIL_SetOrigin( this, pev->origin ); } if( g_fXashEngine ) { if( FBitSet( pev->spawnflags, SF_STATIC_SOLID )) { if( !g_precache_meshes || g_precache_meshes->value ) { if( !UTIL_GetCollisionMesh( pev->modelindex )) { // for some reasons we can't build collision mesh // for this model, just make it non-solid pev->solid = SOLID_NOT; MAKE_STATIC( edict() ); } } } else { // remove from server MAKE_STATIC( edict() ); } } } void CEnvStatic :: SetObjectCollisionBox( void ) { Vector angles = pev->angles; angles.x = -angles.x; // Stupid Quake bug workaround // expand for rotation TransformAABB( matrix4x4( pev->origin, angles ), pev->mins, pev->maxs, pev->absmin, pev->absmax ); pev->absmin.x -= 1; pev->absmin.y -= 1; pev->absmin.z -= 1; pev->absmax.x += 1; pev->absmax.y += 1; pev->absmax.z += 1; } // automatically set collision box void CEnvStatic :: AutoSetSize( void ) { studiohdr_t *pstudiohdr; pstudiohdr = (studiohdr_t *)GET_MODEL_PTR( edict() ); if( pstudiohdr == NULL ) { UTIL_SetSize( pev, Vector( -10, -10, -10 ), Vector( 10, 10, 10 )); ALERT( at_error, "env_static: unable to fetch model pointer!\n" ); return; } mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); UTIL_SetSize( pev, pseqdesc[pev->sequence].bbmin * pev->startpos, pseqdesc[pev->sequence].bbmax * pev->startpos ); } #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, 1) * m_flVariance;; vecShootDir = vecShootDir + gpGlobals->v_forward * RANDOM_FLOAT( -1, 1) * m_flVariance;; vecShootDir = vecShootDir + gpGlobals->v_up * RANDOM_FLOAT( -1, 1) * 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_debug, "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.1; 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( (char *)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.0) { 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.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); 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.5; 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.0) { for (i = 0; i < m_iBeam; i++) { t = (gpGlobals->time - m_flBeamTime[i]) / ( 3 + m_flStartTime - m_flBeamTime[i]); m_pBeam[i]->SetBrightness( 255 * t ); // m_pBeam[i]->SetScrollRate( 20 * t ); } SetNextThink( 0.1 ); } 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.1 ); 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->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.0 ) 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->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->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() ) { UTIL_ScreenFade( pActivator, pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags ); } } else { 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 ) pev->scale = 1.0; } void CMessage::Precache( void ) { if ( pev->noise ) PRECACHE_SOUND( (char *)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 ( (char*)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 ( (char*)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- 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 #define EXTENT_ARCING_THROUGH 5 //AJH 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.0) bDraw = FALSE; vecDest = tr.vecEndPos; break; case EXTENT_ARCING_THROUGH: //AJH - Arcs full length of brush only when blocked UTIL_TraceLine( vecDest, vecSrc, dont_ignore_monsters, NULL, &tr); if (tr.flFraction == 1.0) bDraw = FALSE; break; case EXTENT_ARCING_REVERSE: UTIL_TraceLine( vecDest, vecSrc, ignore_monsters, NULL, &tr); if (tr.flFraction == 1.0) 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 (iDrawnfrags && iTimes<(pev->frags * 3)) // try to draw beams, but give up after 3x 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.0) { // 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( 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( 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 == NULL) { 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 == NULL) { 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( ); } //========================================================= // 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.5 ); } 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.0; } 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 Precache( void ); void SendInitMessage( CBasePlayer *player ); // buz 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::SendInitMessage( CBasePlayer *player ) { MESSAGE_BEGIN( MSG_ONE, gmsgParticle, NULL, player->pev ); WRITE_ENTITY( entindex() ); WRITE_STRING( STRING(pev->message) ); WRITE_BYTE( 0 ); // attachment MESSAGE_END(); } void CParticle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if ( ShouldToggle( useType, pev->body ) ) { pev->body = !pev->body; } } // ======= rain ========= void CRainSettings :: Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; pev->effects |= EF_NODRAW; } void CRainSettings::KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "m_flDistance")) { Rain_Distance = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_iMode")) { Rain_Mode = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else { CBaseEntity::KeyValue( pkvd ); } } LINK_ENTITY_TO_CLASS( rain_settings, CRainSettings ); TYPEDESCRIPTION CRainSettings::m_SaveData[] = { DEFINE_FIELD( CRainSettings, Rain_Distance, FIELD_FLOAT ), DEFINE_FIELD( CRainSettings, Rain_Mode, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CRainSettings, CBaseEntity ); void CRainModify::Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; pev->effects |= EF_NODRAW; if (FStringNull(pev->targetname)) pev->spawnflags |= 1; } void CRainModify::KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "m_iDripsPerSecond")) { Rain_Drips = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_flWindX")) { Rain_windX = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_flWindY")) { Rain_windY = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_flRandX")) { Rain_randX = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_flRandY")) { Rain_randY = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "m_flTime")) { fadeTime = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else { CBaseEntity::KeyValue( pkvd ); } } void CRainModify::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if (pev->spawnflags & 1) return; // constant if (gpGlobals->deathmatch) { ALERT(at_console, "Rain error: only static rain in multiplayer\n"); return; // not in multiplayer } CBasePlayer *pPlayer; pPlayer = (CBasePlayer *)CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(1)); if (fadeTime) { // write to 'ideal' settings pPlayer->Rain_ideal_dripsPerSecond = Rain_Drips; pPlayer->Rain_ideal_randX = Rain_randX; pPlayer->Rain_ideal_randY = Rain_randY; pPlayer->Rain_ideal_windX = Rain_windX; pPlayer->Rain_ideal_windY = Rain_windY; pPlayer->Rain_endFade = gpGlobals->time + fadeTime; pPlayer->Rain_nextFadeUpdate = gpGlobals->time + 1; } else { pPlayer->Rain_dripsPerSecond = Rain_Drips; pPlayer->Rain_randX = Rain_randX; pPlayer->Rain_randY = Rain_randY; pPlayer->Rain_windX = Rain_windX; pPlayer->Rain_windY = Rain_windY; pPlayer->Rain_needsUpdate = 1; } } LINK_ENTITY_TO_CLASS( rain_modify, CRainModify ); TYPEDESCRIPTION CRainModify::m_SaveData[] = { DEFINE_FIELD( CRainModify, fadeTime, FIELD_FLOAT ), DEFINE_FIELD( CRainModify, Rain_Drips, FIELD_INTEGER ), DEFINE_FIELD( CRainModify, Rain_randX, FIELD_FLOAT ), DEFINE_FIELD( CRainModify, Rain_randY, FIELD_FLOAT ), DEFINE_FIELD( CRainModify, Rain_windX, FIELD_FLOAT ), DEFINE_FIELD( CRainModify, Rain_windY, FIELD_FLOAT ), }; IMPLEMENT_SAVERESTORE( CRainModify, CBaseEntity ); // ================================= // buz: static decals // // netname - decal group name // skin - direction specified // ================================= class CStaticDecal : public CPointEntity { public: void KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "texture")) { pev->netname = ALLOC_STRING(pkvd->szValue); pkvd->fHandled = TRUE; } else CBaseEntity::KeyValue( pkvd ); } int PasteDecal( int dir ) { Vector vecdir = g_vecZero; vecdir[dir % 3] = (dir & 1) ? 32.0f : -32.0f; TraceResult tr; UTIL_TraceLine ( pev->origin, pev->origin + vecdir, ignore_monsters, edict(), &tr ); return UTIL_TraceCustomDecal( &tr, STRING( pev->netname ), pev->angles.y, TRUE ); } void Spawn( void ) { if( pev->skin <= 0 || pev->skin > 6 ) { // try all directions for( int i = 0; i < 6; i++ ) if( PasteDecal( i )) break; if( i == 6 ) ALERT( at_warning, "failed to place decal %s\n", STRING( pev->netname )); } else { // try specified direction PasteDecal( pev->skin - 1 ); } // NOTE: don't need to keep this entity // with new custom decal save\restore system UTIL_Remove( this ); } }; LINK_ENTITY_TO_CLASS( env_static_decal, CStaticDecal ); LINK_ENTITY_TO_CLASS( infodecal, CStaticDecal ); // now an alias // ================================= // g-cont: puddles // // just a prefab // ================================= class CPuddleDecal : public CPointEntity { public: void Spawn( void ) { TraceResult tr; UTIL_TraceLine ( pev->origin, pev->origin + Vector( 0.0f, 0.0f, -64.0f ), ignore_monsters, edict(), &tr ); UTIL_TraceCustomDecal( &tr, "puddle", pev->angles.y, TRUE ); // NOTE: don't need to keep this entity // with new custom decal save\restore system UTIL_Remove( this ); } }; LINK_ENTITY_TO_CLASS( env_puddle, CPuddleDecal ); // ================================= // buz: 3d sky info messages // // envpos_sky: sets view origin in 3d sky // origin - origin // skin - ambientlight // body - shadelight // flags - use amblight hack | use shadelight hack // // envpos_world: sets view origin in world (when sky movement requed) // origin - origin // health - speed // // ================================= extern int gmsgSkymarker_Sky; extern int gmsgSkymarker_World; class CEnvPos_Sky : public CPointEntity { public: void Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; pev->effects |= EF_NODRAW; } void SendInitMessage( CBasePlayer *player ) { MESSAGE_BEGIN( MSG_ONE, gmsgSkymarker_Sky, NULL, player->pev ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); MESSAGE_END(); } }; class CEnvPos_World : public CPointEntity { public: void Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; pev->effects |= EF_NODRAW; } void SendInitMessage( CBasePlayer *player ) { MESSAGE_BEGIN( MSG_ONE, gmsgSkymarker_World, NULL, player->pev ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); WRITE_COORD( pev->health ); MESSAGE_END(); } }; LINK_ENTITY_TO_CLASS( envpos_sky, CEnvPos_Sky ); LINK_ENTITY_TO_CLASS( envpos_world, CEnvPos_World ); // buz: trigger_followme // formally, it does not any special, just fires target entity with chosen use type, // and player sent as caller class CTriggerFollow : public CPointEntity { public: void Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; pev->effects |= EF_NODRAW; } void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBaseEntity *pEnt = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(1)); if (pEnt && pEnt->IsPlayer()) { USE_TYPE send = USE_TOGGLE; if (pev->impulse == 0) send = USE_ON; else if (pev->impulse == 1) send = USE_OFF; FireTargets(STRING(pev->target), this, pEnt, send, 0); } else ALERT(at_console, "ERROR: cant get player!\n"); } }; LINK_ENTITY_TO_CLASS( trigger_followme, CTriggerFollow ); // buz: dynamic light entity #define SF_DYNLIGHT_STARTOFF 1 #define SF_DYNLIGHT_NOSHADOW 2 #define SF_DYNLIGHT_NOBUMP 4 #define SF_DYNLIGHT_FLARE 8 class CDynamicLight : public CPointEntity { public: void Spawn() { pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; Precache(); if( pev->sequence ) pev->renderfx = 72; // dynamic light with avi-texture else pev->renderfx = 71; // dynamic light SET_MODEL(ENT(pev),"sprites/null.spr"); // should be visible to send to client pev->renderamt = pev->renderamt / 8; // spotlight if( pev->scale ) { // create second PVS point pev->enemy = Create( "info_target", pev->origin, g_vecZero, edict() )->edict(); SET_MODEL( pev->enemy, "sprites/null.spr" ); // allow to collect visibility info UTIL_SetSize ( VARS( pev->enemy ), Vector ( -8, -8, -8 ), Vector ( 8, 8, 8 ) ); // to prevent disapperaing from PVS (renderamt is premultiplied by 0.125) UTIL_SetSize( pev, Vector( -pev->renderamt, -pev->renderamt, -pev->renderamt ), Vector( pev->renderamt, pev->renderamt, pev->renderamt )); } else { // to prevent disapperaing from PVS (renderamt is premultiplied by 0.125) UTIL_SetSize( pev, Vector( -pev->renderamt * 4, -pev->renderamt * 4, -pev->renderamt * 4 ), Vector( pev->renderamt * 4, pev->renderamt * 4, pev->renderamt * 4 )); } if( pev->spawnflags & SF_DYNLIGHT_NOSHADOW ) pev->effects |= EF_NOSHADOW; if( pev->spawnflags & SF_DYNLIGHT_NOBUMP ) pev->effects |= EF_NOBUMP; if( pev->spawnflags & SF_DYNLIGHT_FLARE ) pev->effects |= EF_LENSFLARE; if( pev->spawnflags & SF_DYNLIGHT_STARTOFF ) { pev->effects |= EF_NODRAW; } else if( pev->sequence ) { SetThink( CineThink ); SetNextThink( 0.1f ); } else if( pev->scale ) { SetThink( PVSThink ); SetNextThink( 0.1f ); } } void Precache() { PRECACHE_MODEL("sprites/null.spr"); if( !FStringNull( pev->message )) { const char *ext = UTIL_FileExtension( STRING( pev->message )); if( !Q_stricmp( ext, "avi" )) { // 0 if movie not found pev->sequence = UTIL_PrecacheMovie( pev->message ); } } } STATE GetState( void ) { if (pev->effects & EF_NODRAW) return STATE_OFF; else return STATE_ON; } void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if( useType == USE_ON ) pev->effects &= ~EF_NODRAW; else if( useType == USE_OFF ) pev->effects |= EF_NODRAW; else if( useType == USE_TOGGLE ) { if( pev->effects & EF_NODRAW ) pev->effects &= ~EF_NODRAW; else pev->effects |= EF_NODRAW; } if( pev->effects & EF_NODRAW ) { DontThink(); } else if( pev->sequence ) { SetThink( CineThink ); SetNextThink( 0.1f ); } else if( pev->scale ) { SetThink( PVSThink ); SetNextThink( 0.1f ); } } void UpdatePVSPoint( void ) { TraceResult tr; Vector forward; UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); Vector vecSrc = pev->origin + forward * 8.0f; Vector vecEnd = vecSrc + forward * (pev->renderamt * 8.0f); UTIL_TraceLine( vecSrc, vecEnd, ignore_monsters, edict(), &tr ); // this is our second PVS point CBaseEntity *pVisHelper = CBaseEntity::Instance( pev->enemy ); if( pVisHelper ) UTIL_SetOrigin( pVisHelper, tr.vecEndPos + tr.vecPlaneNormal * 8.0f ); } void EXPORT CineThink( void ) { UpdatePVSPoint(); // update as 30 frames per second pev->fuser2 += CIN_FRAMETIME; SetNextThink( CIN_FRAMETIME ); } void EXPORT PVSThink( void ) { UpdatePVSPoint(); // static light should be updated in case // moving platform under them SetNextThink( m_pMoveWith ? 0.01f : 0.1f ); } }; LINK_ENTITY_TO_CLASS( env_dynlight, CDynamicLight ); // =================== FUNC_SCREENMOVIE ============================================== #define SF_SCREENMOVIE_START_ON BIT( 0 ) #define SF_SCREENMOVIE_PASSABLE BIT( 1 ) #define SF_SCREENMOVIE_LOOPED BIT( 2 ) #define SF_SCREENMOVIE_MONOCRHOME BIT( 3 ) // black & white #define SF_SCREENMOVIE_SOUND BIT( 4 ) // allow sound class CFuncScreenMovie : public CBaseDelay { public: void Spawn( void ); void Precache( void ); void KeyValue( KeyValueData *pkvd ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } STATE GetState( void ) { return (pev->body) ? STATE_ON : STATE_OFF; } void EXPORT CineThink( void ); }; LINK_ENTITY_TO_CLASS( func_screenmovie, CFuncScreenMovie ); void CFuncScreenMovie::KeyValue( KeyValueData *pkvd ) { if( FStrEq( pkvd->szKeyName, "movie" )) { pev->message = ALLOC_STRING( pkvd->szValue ); pkvd->fHandled = TRUE; } else CBaseDelay::KeyValue( pkvd ); } void CFuncScreenMovie :: Precache( void ) { // store movie name as event index pev->sequence = UTIL_PrecacheMovie( pev->message, FBitSet( pev->spawnflags, SF_SCREENMOVIE_SOUND )); } void CFuncScreenMovie :: Spawn( void ) { Precache(); pev->movetype = MOVETYPE_PUSH; if( FBitSet( pev->spawnflags, SF_SCREENMOVIE_PASSABLE )) pev->solid = SOLID_NOT; else pev->solid = SOLID_BSP; SET_MODEL( ENT(pev), STRING(pev->model) ); pev->effects |= EF_SCREENMOVIE; if( FBitSet( pev->spawnflags, SF_SCREENMOVIE_LOOPED )) pev->iuser1 |= CF_LOOPED_MOVIE; if( FBitSet( pev->spawnflags, SF_SCREENMOVIE_MONOCRHOME )) pev->iuser1 |= CF_MONOCHROME; if( FBitSet( pev->spawnflags, SF_SCREENMOVIE_SOUND )) pev->iuser1 |= CF_MOVIE_SOUND; // enable monitor if( FBitSet( pev->spawnflags, SF_SCREENMOVIE_START_ON )) { SetThink( SUB_CallUseToggle ); SetNextThink( 0.1 ); } } void CFuncScreenMovie :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if( !pev->sequence ) { ALERT( at_error, "func_screenmovie with name %s can't loaded video %s\n", STRING( pev->targetname ), STRING( pev->message )); return; } SetThink( CineThink ); if( ShouldToggle( useType )) { pev->body = !pev->body; if( pev->body ) SetNextThink( CIN_FRAMETIME ); else DontThink(); } } void CFuncScreenMovie :: CineThink( void ) { // update as 30 frames per second pev->fuser2 += CIN_FRAMETIME; SetNextThink( CIN_FRAMETIME ); } class CBlurEffect : public CBaseDelay { public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void ApplyBlur( CBasePlayer *pPlayer ); void KeyValue( KeyValueData *pkvd ); void Think( void ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } CBasePlayer *m_pPlayer; }; TYPEDESCRIPTION CBlurEffect :: m_SaveData[] = { DEFINE_FIELD( CBlurEffect, m_pPlayer, FIELD_CLASSPTR ), }; IMPLEMENT_SAVERESTORE( CBlurEffect, CBaseDelay ); LINK_ENTITY_TO_CLASS( env_blur, CBlurEffect ); void CBlurEffect :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "fadetime")) { pev->frags = atof(pkvd->szValue); pev->frags = bound( 0.0f, pev->frags, 1000.0f ); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "magnitude")) { pev->armorvalue = atof(pkvd->szValue); pev->armorvalue = bound( 0.0f, pev->armorvalue, 1.0f ); pkvd->fHandled = TRUE; } else CBaseDelay::KeyValue( pkvd ); } void CBlurEffect :: ApplyBlur( CBasePlayer *pPlayer ) { if( !pPlayer ) return; // not spawned? m_pPlayer = pPlayer; if( pev->frags ) { pev->health = m_pPlayer->m_flBlurAmount; // start pos pev->max_health = pev->armorvalue - pPlayer->m_flBlurAmount; pev->dmgtime = gpGlobals->time; SetNextThink( 0.01 ); } else { m_pPlayer->m_flBlurAmount = pev->armorvalue; pev->max_health = pev->armorvalue; pev->health = pev->dmgtime = 0.0f; } } void CBlurEffect :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if( pev->dmgtime ) return; // fading out if( !pActivator || !pActivator->IsPlayer( )) ApplyBlur( (CBasePlayer *)UTIL_PlayerByIndex( 1 )); else ApplyBlur( (CBasePlayer *)pActivator ); } void CBlurEffect :: Think( void ) { float elapsed = gpGlobals->time - pev->dmgtime; float f = elapsed / pev->frags; f = bound( 0.0f, f, 1.0f ); m_pPlayer->m_flBlurAmount = pev->health + pev->max_health * f; if( f == 1.0f ) // hit 100% fade { pev->dmgtime = 0.0f; return; } SetNextThink( 0.01 ); } //======================= // Gradient sprite class //======================= class CGradientControl : public CBaseEntity { public: void Spawn( void ); void Think( void ); int m_iGradAmt; int m_iGlowAmt; }; void CGradientControl::Spawn( void ) { m_iGradAmt = 0; m_iGlowAmt = 0; SetNextThink(0.01); } void CGradientControl::Think( void ) { CBaseEntity *pPlayer, *pGlow, *pGradient; pPlayer = UTIL_FindEntityByClassname( NULL, "player" ); pGlow = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); pGradient = UTIL_FindEntityByTargetname( NULL, STRING( pev->message ) ); if (pPlayer && pGlow && pGradient) { if ( !m_iGradAmt) m_iGradAmt = pGradient->pev->renderamt; if ( !m_iGlowAmt) m_iGlowAmt = pGlow->pev->renderamt; TraceResult tr; UTIL_TraceLine( pev->origin, pPlayer->pev->origin, ignore_monsters, ENT(pev), &tr ); Vector vecDir = pev->origin - pPlayer->pev->origin; float flDist = vecDir.Length(); float flDist2 = flDist; flDist-=60; if ( flDist < 0 ) flDist = 0; if ( flDist <= pev->health ) { pGradient->pev->renderamt = fabs( flDist / pev->health * m_iGradAmt ); pGlow->pev->renderamt = fabs( pev->health / flDist2 * m_iGlowAmt ); } else { pGradient->pev->renderamt = m_iGradAmt; pGlow->pev->renderamt = m_iGlowAmt; } } SetNextThink(0.01); } LINK_ENTITY_TO_CLASS( env_gradient, CGradientControl );