//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: env_flare from hl2 (original) // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "player.h" #include "gamerules.h" #include "basehlcombatweapon.h" #include "decals.h" #include "in_buttons.h" #include "soundenvelope.h" #include "IEffects.h" #include "engine/IEngineSound.h" #include "overcharged/env_flare.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define FLARE_LAUNCH_SPEED 1500 LINK_ENTITY_TO_CLASS(env_flare, CFlare); BEGIN_DATADESC(CFlare) DEFINE_FIELD(m_pOwner, FIELD_CLASSPTR), DEFINE_FIELD(m_nBounces, FIELD_INTEGER), DEFINE_FIELD(m_flTimeBurnOut, FIELD_TIME), DEFINE_KEYFIELD(m_flScale, FIELD_FLOAT, "scale"), DEFINE_KEYFIELD(m_flDuration, FIELD_FLOAT, "duration"), DEFINE_FIELD(m_flNextDamage, FIELD_TIME), DEFINE_SOUNDPATCH(m_pBurnSound), DEFINE_FIELD(m_bFading, FIELD_BOOLEAN), DEFINE_FIELD(m_bLight, FIELD_BOOLEAN), DEFINE_FIELD(m_bSmoke, FIELD_BOOLEAN), DEFINE_FIELD(m_bPropFlare, FIELD_BOOLEAN), DEFINE_FIELD(m_bInActiveList, FIELD_BOOLEAN), DEFINE_FIELD(m_pNextFlare, FIELD_CLASSPTR), //Input functions DEFINE_INPUTFUNC(FIELD_FLOAT, "Start", InputStart), DEFINE_INPUTFUNC(FIELD_FLOAT, "Die", InputDie), DEFINE_INPUTFUNC(FIELD_FLOAT, "Launch", InputLaunch), // Function Pointers DEFINE_FUNCTION(FlareTouch), DEFINE_FUNCTION(FlareBurnTouch), DEFINE_FUNCTION(FlareThink), END_DATADESC() //Data-tables IMPLEMENT_SERVERCLASS_ST(CFlare, DT_Flare) SendPropFloat(SENDINFO(m_flTimeBurnOut), 0, SPROP_NOSCALE), SendPropFloat(SENDINFO(m_flScale), 0, SPROP_NOSCALE), SendPropInt(SENDINFO(m_bLight), 1, SPROP_UNSIGNED), SendPropInt(SENDINFO(m_bSmoke), 1, SPROP_UNSIGNED), SendPropInt(SENDINFO(m_bPropFlare), 1, SPROP_UNSIGNED), END_SEND_TABLE() CFlare *CFlare::activeFlares = NULL; CFlare *CFlare::GetActiveFlares(void) { return CFlare::activeFlares; } Class_T CFlare::Classify(void) { return CLASS_FLARE; } CBaseEntity *CreateFlare(Vector vOrigin, QAngle Angles, CBaseEntity *pOwner, float flDuration) { CFlare *pFlare = CFlare::Create(vOrigin, Angles, pOwner, flDuration); if (pFlare) { pFlare->m_bPropFlare = true; } return pFlare; } void KillFlare(CBaseEntity *pOwnerEntity, CBaseEntity *pEntity, float flKillTime) { CFlare *pFlare = dynamic_cast< CFlare *>(pEntity); if (pFlare) { float flDieTime = (pFlare->m_flTimeBurnOut - gpGlobals->curtime) - flKillTime; if (flDieTime > 1.0f) { pFlare->Die(flDieTime); pOwnerEntity->SetNextThink(gpGlobals->curtime + flDieTime + 3.0f); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CFlare::CFlare(void) { m_flScale = 4.0f; m_nBounces = 0; m_bFading = false; m_bLight = true; m_bSmoke = true; m_flNextDamage = gpGlobals->curtime; m_lifeState = LIFE_ALIVE; m_iHealth = 100; m_bPropFlare = false; m_bInActiveList = false; m_pNextFlare = NULL; } CFlare::~CFlare() { CSoundEnvelopeController::GetController().SoundDestroy(m_pBurnSound); m_pBurnSound = NULL; RemoveFromActiveFlares(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Precache(void) { PrecacheModel("models/weapons/flare.mdl"); PrecacheScriptSound("Weapon_FlareGun.Burn"); // FIXME: needed to precache the fire model. Shouldn't have to do this. UTIL_PrecacheOther("_firesmoke"); } //----------------------------------------------------------------------------- // Purpose: // Input : &restore - // Output : int //----------------------------------------------------------------------------- int CFlare::Restore(IRestore &restore) { int result = BaseClass::Restore(restore); if (m_spawnflags & SF_FLARE_NO_DLIGHT) { m_bLight = false; } if (m_spawnflags & SF_FLARE_NO_SMOKE) { m_bSmoke = false; } return result; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Spawn(void) { Precache(); SetModel("models/weapons/flare.mdl"); UTIL_SetSize(this, Vector(-2, -2, -2), Vector(2, 2, 2)); SetSolid(SOLID_BBOX); AddSolidFlags(FSOLID_NOT_SOLID); SetMoveType(MOVETYPE_NONE); SetFriction(0.6f); SetGravity(UTIL_ScaleForGravity(400)); m_flTimeBurnOut = gpGlobals->curtime + 30; AddEffects(EF_NOSHADOW | EF_NORECEIVESHADOW); if (m_spawnflags & SF_FLARE_NO_DLIGHT) { m_bLight = false; } if (m_spawnflags & SF_FLARE_NO_SMOKE) { m_bSmoke = false; } if (m_spawnflags & SF_FLARE_INFINITE) { m_flTimeBurnOut = -1.0f; } if (m_spawnflags & SF_FLARE_START_OFF) { AddEffects(EF_NODRAW); } AddFlag(FL_OBJECT); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Activate(void) { BaseClass::Activate(); // Start the burning sound if we're already on if ((m_spawnflags & SF_FLARE_START_OFF) == false) { StartBurnSound(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::StartBurnSound(void) { if (m_pBurnSound == NULL) { CPASAttenuationFilter filter(this); m_pBurnSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), CHAN_WEAPON, "Weapon_FlareGun.Burn", 3.0f); } } //----------------------------------------------------------------------------- // Purpose: // Input : vecOrigin - // vecAngles - // *pOwner - // Output : CFlare //----------------------------------------------------------------------------- CFlare *CFlare::Create(Vector vecOrigin, QAngle vecAngles, CBaseEntity *pOwner, float lifetime) { CFlare *pFlare = (CFlare *)CreateEntityByName("env_flare"); if (pFlare == NULL) return NULL; UTIL_SetOrigin(pFlare, vecOrigin); pFlare->SetLocalAngles(vecAngles); pFlare->Spawn(); pFlare->SetTouch(&CFlare::FlareTouch); pFlare->SetThink(&CFlare::FlareThink); //Start up the flare pFlare->Start(lifetime); //Don't start sparking immediately pFlare->SetNextThink(gpGlobals->curtime + 0.5f); //Burn out time pFlare->m_flTimeBurnOut = gpGlobals->curtime + lifetime; pFlare->RemoveSolidFlags(FSOLID_NOT_SOLID); pFlare->AddSolidFlags(FSOLID_NOT_STANDABLE); pFlare->SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE); pFlare->SetOwnerEntity(pOwner); pFlare->m_pOwner = pOwner; return pFlare; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- unsigned int CFlare::PhysicsSolidMaskForEntity(void) const { return MASK_NPCSOLID; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::FlareThink(void) { float deltaTime = (m_flTimeBurnOut - gpGlobals->curtime); if (!m_bInActiveList && ((deltaTime > FLARE_BLIND_TIME) || (m_flTimeBurnOut == -1.0f))) { AddToActiveFlares(); } if (m_flTimeBurnOut != -1.0f) { //Fading away if ((deltaTime <= FLARE_DECAY_TIME) && (m_bFading == false)) { m_bFading = true; CSoundEnvelopeController::GetController().SoundChangePitch(m_pBurnSound, 60, deltaTime); CSoundEnvelopeController::GetController().SoundFadeOut(m_pBurnSound, deltaTime); } // if flare is no longer bright, remove it from active flare list if (m_bInActiveList && (deltaTime <= FLARE_BLIND_TIME)) { RemoveFromActiveFlares(); } //Burned out if (m_flTimeBurnOut < gpGlobals->curtime) { UTIL_Remove(this); return; } } //Act differently underwater if (GetWaterLevel() > 1) { UTIL_Bubbles(GetAbsOrigin() + Vector(-2, -2, -2), GetAbsOrigin() + Vector(2, 2, 2), 1); m_bSmoke = false; } else { //Shoot sparks if (random->RandomInt(0, 8) == 1) { g_pEffects->Sparks(GetAbsOrigin()); } } //Next update SetNextThink(gpGlobals->curtime + 0.1f); } //----------------------------------------------------------------------------- // Purpose: // Input : *pOther - //----------------------------------------------------------------------------- void CFlare::FlareBurnTouch(CBaseEntity *pOther) { if (pOther && pOther->m_takedamage && (m_flNextDamage < gpGlobals->curtime) && (!FClassnameIs(pOther, "prop_vehicle_jeep") && !FClassnameIs(pOther, "prop_vehicle_apc") && !FClassnameIs(pOther, "prop_vehicle_airboat"))) { pOther->TakeDamage(CTakeDamageInfo(this, m_pOwner, 1, (DMG_BULLET | DMG_BURN))); m_flNextDamage = gpGlobals->curtime + 1.0f; } } //----------------------------------------------------------------------------- // Purpose: // Input : *pOther - //----------------------------------------------------------------------------- void CFlare::FlareTouch(CBaseEntity *pOther) { Assert(pOther); if (!pOther->IsSolid() || FClassnameIs(pOther, "prop_vehicle_jeep") || FClassnameIs(pOther, "prop_vehicle_apc") || FClassnameIs(pOther, "prop_vehicle_airboat")) return; //if (!pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS)) //return; //if (pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS)) // BriJee TEST //return; /*if (pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER)) //test { // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. if ((pOther->m_takedamage == DAMAGE_NO) || (pOther->m_takedamage == DAMAGE_EVENTS_ONLY)) return; }*/ if ((m_nBounces < 10) && (GetWaterLevel() < 1)) { // Throw some real chunks here g_pEffects->Sparks(GetAbsOrigin()); } //If the flare hit a person or NPC, do damage here. if (pOther && pOther->m_takedamage) { //The Flare is the iRifle round right now. No damage, just ignite. (sjb) /*//Damage is a function of how fast the flare is flying. int iDamage = GetAbsVelocity().Length() / 50.0f; //if ( iDamage < 12 ) //5 //{ //Clamp minimum damage iDamage = 12; //5 //} //Use m_pOwner, not GetOwnerEntity() pOther->TakeDamage( CTakeDamageInfo( this, m_pOwner, iDamage, (DMG_BULLET|DMG_BURN) ) ); m_flNextDamage = gpGlobals->curtime + 1.0f;*/ CBaseAnimating *pAnim; pAnim = dynamic_cast(pOther); if (pAnim) { pAnim->Ignite(30.0f); } Vector vecNewVelocity = GetAbsVelocity(); vecNewVelocity *= 0.1f; SetAbsVelocity(vecNewVelocity); SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE); SetGravity(1.0f); Die(0.5); return; } else { // hit the world, check the material type here, see if the flare should stick. trace_t tr; tr = CBaseEntity::GetTouchTrace(); //Only do this on the first bounce if (m_nBounces == 0) { const surfacedata_t *pdata = physprops->GetSurfaceData(tr.surface.surfaceProps); if (pdata != NULL) { //Only embed into concrete and wood (jdw: too obscure for players?) //if ( ( pdata->gameMaterial == 'C' ) || ( pdata->gameMaterial == 'W' ) ) { Vector impactDir = (tr.endpos - tr.startpos); VectorNormalize(impactDir); float surfDot = tr.plane.normal.Dot(impactDir); //Do not stick to ceilings or on shallow impacts if ((tr.plane.normal.z > -0.5f) && (surfDot < -0.9f)) { RemoveSolidFlags(FSOLID_NOT_SOLID); AddSolidFlags(FSOLID_TRIGGER); UTIL_SetOrigin(this, tr.endpos + (tr.plane.normal * 2.0f)); SetAbsVelocity(vec3_origin); SetMoveType(MOVETYPE_NONE); SetTouch(&CFlare::FlareBurnTouch); int index = decalsystem->GetDecalIndexForName("SmallScorch"); if (index >= 0) { CBroadcastRecipientFilter filter; te->Decal(filter, 0.0, &tr.endpos, &tr.startpos, ENTINDEX(tr.m_pEnt), tr.hitbox, index); } CPASAttenuationFilter filter2(this, "Flare.Touch"); EmitSound(filter2, entindex(), "Flare.Touch"); return; } } } } //Scorch decal if (GetAbsVelocity().LengthSqr() > (250 * 250)) { int index = decalsystem->GetDecalIndexForName("FadingScorch"); if (index >= 0) { CBroadcastRecipientFilter filter; te->Decal(filter, 0.0, &tr.endpos, &tr.startpos, ENTINDEX(tr.m_pEnt), tr.hitbox, index); } } // Change our flight characteristics SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE); SetGravity(UTIL_ScaleForGravity(640)); m_nBounces++; //After the first bounce, smacking into whoever fired the flare is fair game //SetOwnerEntity(this); // Slow down Vector vecNewVelocity = GetAbsVelocity(); vecNewVelocity.x *= 0.8f; vecNewVelocity.y *= 0.8f; SetAbsVelocity(vecNewVelocity); //Stopped? if (GetAbsVelocity().Length() < 64.0f) { SetAbsVelocity(vec3_origin); SetMoveType(MOVETYPE_NONE); RemoveSolidFlags(FSOLID_NOT_SOLID); AddSolidFlags(FSOLID_TRIGGER); SetTouch(&CFlare::FlareBurnTouch); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Start(float lifeTime) { StartBurnSound(); if (m_pBurnSound != NULL) { CSoundEnvelopeController::GetController().Play(m_pBurnSound, 0.0f, 60); CSoundEnvelopeController::GetController().SoundChangeVolume(m_pBurnSound, 0.8f, 2.0f); CSoundEnvelopeController::GetController().SoundChangePitch(m_pBurnSound, 100, 2.0f); } if (lifeTime > 0) { m_flTimeBurnOut = gpGlobals->curtime + lifeTime; } else { m_flTimeBurnOut = -1.0f; } RemoveEffects(EF_NODRAW); SetThink(&CFlare::FlareThink); SetNextThink(gpGlobals->curtime + 0.1f); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Die(float fadeTime) { m_flTimeBurnOut = gpGlobals->curtime + fadeTime; SetThink(&CFlare::FlareThink); SetNextThink(gpGlobals->curtime + 0.1f); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CFlare::Launch(const Vector &direction, float speed) { // Make sure we're visible if (m_spawnflags & SF_FLARE_INFINITE) { Start(-1); } else { Start(8.0f); } SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE); // Punch our velocity towards our facing SetAbsVelocity(direction * speed); SetGravity(1.0f); } //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CFlare::InputStart(inputdata_t &inputdata) { Start(inputdata.value.Float()); } //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CFlare::InputDie(inputdata_t &inputdata) { Die(inputdata.value.Float()); } //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CFlare::InputLaunch(inputdata_t &inputdata) { Vector direction; AngleVectors(GetAbsAngles(), &direction); float speed = inputdata.value.Float(); if (speed == 0) { speed = FLARE_LAUNCH_SPEED; } Launch(direction, speed); } //----------------------------------------------------------------------------- // Purpose: Removes flare from active flare list //----------------------------------------------------------------------------- void CFlare::RemoveFromActiveFlares(void) { CFlare *pFlare; CFlare *pPrevFlare; if (!m_bInActiveList) return; pPrevFlare = NULL; for (pFlare = CFlare::activeFlares; pFlare != NULL; pFlare = pFlare->m_pNextFlare) { if (pFlare == this) { if (pPrevFlare) { pPrevFlare->m_pNextFlare = m_pNextFlare; } else { activeFlares = m_pNextFlare; } break; } pPrevFlare = pFlare; } m_pNextFlare = NULL; m_bInActiveList = false; } //----------------------------------------------------------------------------- // Purpose: Adds flare to active flare list //----------------------------------------------------------------------------- void CFlare::AddToActiveFlares(void) { if (!m_bInActiveList) { m_pNextFlare = CFlare::activeFlares; CFlare::activeFlares = this; m_bInActiveList = true; } }