text: ﻿//========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $NoKeywords: $
  //=============================================================================//
  
  #include "cbase.h"
  #include "grenade_ar2.h"
  #include "hl2/weapons/weapon_ar2.h"
  #include "soundent.h"
  #include "decals.h"
  #include "shake.h"
  #include "smoke_trail.h"
  #include "ar2_explosion.h"
  #include "vstdlib/random.h"
  #include "engine/IEngineSound.h"
  #include "world.h"
  
  #ifdef PORTAL
  #include "portal_util_shared.h"
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  #define AR2_GRENADE_MAX_DANGER_RADIUS    300
  
  extern short g_sModelIndexFireball;            // (in combatweapon.cpp) holds the index for the smoke cloud
  
  // Moved to HL2_SharedGameRules because these are referenced by shared AmmoDef functions
  extern ConVar sk_plr_dmg_smg1_grenade;
  extern ConVar sk_npc_dmg_smg1_grenade;
  extern ConVar sk_max_smg1_grenade;
  
  ConVar sk_smg1_grenade_radius("sk_smg1_grenade_radius", "0");
  
  ConVar g_CV_SmokeTrail("smoke_trail", "1", 0); // temporary dust explosion switch
  
  BEGIN_DATADESC(CGrenadeAR2)
  
                      DEFINE_FIELD(m_hSmokeTrail, FIELD_EHANDLE),
                      DEFINE_FIELD(m_fSpawnTime, FIELD_TIME),
                      DEFINE_FIELD(m_fDangerRadius, FIELD_FLOAT),
  
                      // Function pointers
                      DEFINE_ENTITYFUNC(GrenadeAR2Touch),
                      DEFINE_THINKFUNC(GrenadeAR2Think),
  
  END_DATADESC()
  
  LINK_ENTITY_TO_CLASS(grenade_ar2, CGrenadeAR2);
  
  void CGrenadeAR2::Spawn(void) {
      Precache();
      SetSolid(SOLID_BBOX);
      SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE);
  
      // Hits everything but debris
      SetCollisionGroup(COLLISION_GROUP_PROJECTILE);
  
      SetModel("models/Weapons/ar2_grenade.mdl");
      UTIL_SetSize(this, Vector(-3, -3, -3), Vector(3, 3, 3));
  //	UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
  
      SetUse(&CGrenadeAR2::DetonateUse);
      SetTouch(&CGrenadeAR2::GrenadeAR2Touch);
      SetThink(&CGrenadeAR2::GrenadeAR2Think);
      SetNextThink(gpGlobals->curtime + 0.1f);
  
      if (GetOwnerEntity() && GetOwnerEntity()->IsPlayer()) {
          m_flDamage = sk_plr_dmg_smg1_grenade.GetFloat();
      } else {
          m_flDamage = sk_npc_dmg_smg1_grenade.GetFloat();
      }
  
      m_DmgRadius = sk_smg1_grenade_radius.GetFloat();
      m_takedamage = DAMAGE_YES;
      m_bIsLive = true;
      m_iHealth = 1;
  
      SetGravity(UTIL_ScaleForGravity(400));    // use a lower gravity for grenades to make them easier to see
      SetFriction(0.8);
      SetSequence(0);
  
      m_fDangerRadius = 100;
  
      m_fSpawnTime = gpGlobals->curtime;
  
      // -------------
      // Smoke trail.
      // -------------
      if (g_CV_SmokeTrail.GetInt() && !IsXbox()) {
          m_hSmokeTrail = SmokeTrail::CreateSmokeTrail();
  
          if (m_hSmokeTrail) {
              m_hSmokeTrail->m_SpawnRate = 48;
              m_hSmokeTrail->m_ParticleLifetime = 1;
              m_hSmokeTrail->m_StartColor.Init(0.1f, 0.1f, 0.1f);
              m_hSmokeTrail->m_EndColor.Init(0, 0, 0);
              m_hSmokeTrail->m_StartSize = 12;
              m_hSmokeTrail->m_EndSize = m_hSmokeTrail->m_StartSize * 4;
              m_hSmokeTrail->m_SpawnRadius = 4;
              m_hSmokeTrail->m_MinSpeed = 4;
              m_hSmokeTrail->m_MaxSpeed = 24;
              m_hSmokeTrail->m_Opacity = 0.2f;
  
              m_hSmokeTrail->SetLifetime(10.0f);
              m_hSmokeTrail->FollowEntity(this);
          }
      }
  }
  
  //-----------------------------------------------------------------------------
  // Purpose:  The grenade has a slight delay before it goes live.  That way the
  //			 person firing it can bounce it off a nearby wall.  However if it
  //			 hits another character it blows up immediately
  // Input  :
  // Output :
  //-----------------------------------------------------------------------------
  void CGrenadeAR2::GrenadeAR2Think(void) {
      SetNextThink(gpGlobals->curtime + 0.05f);
  
      if (!m_bIsLive) {
          // Go live after a short delay
          if (m_fSpawnTime + MAX_AR2_NO_COLLIDE_TIME < gpGlobals->curtime) {
              m_bIsLive = true;
          }
      }
  
      // If I just went solid and my velocity is zero, it means I'm resting on
      // the floor already when I went solid so blow up
      if (m_bIsLive) {
          if (GetAbsVelocity().Length() == 0.0 ||
              GetGroundEntity() != NULL) {
              Detonate();
          }
      }
  
      // The old way of making danger sounds would scare the crap out of EVERYONE between you and where the grenade
      // was going to hit. The radius of the danger sound now 'blossoms' over the grenade's lifetime, making it seem
      // dangerous to a larger area downrange than it does from where it was fired.
      if (m_fDangerRadius <= AR2_GRENADE_MAX_DANGER_RADIUS) {
          m_fDangerRadius += (AR2_GRENADE_MAX_DANGER_RADIUS * 0.05);
      }
  
      CSoundEnt::InsertSound(SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, m_fDangerRadius, 0.2, this,
                             SOUNDENT_CHANNEL_REPEATED_DANGER);
  }
  
  void CGrenadeAR2::Event_Killed(const CTakeDamageInfo &info) {
      Detonate();
  }
  
  void CGrenadeAR2::GrenadeAR2Touch(CBaseEntity *pOther) {
      Assert(pOther);
      if (!pOther->IsSolid())
          return;
  
      // If I'm live go ahead and blow up
      if (m_bIsLive) {
          Detonate();
      } else {
          // If I'm not live, only blow up if I'm hitting an chacter that
          // is not the owner of the weapon
          CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(pOther);
          if (pBCC && GetThrower() != pBCC) {
              m_bIsLive = true;
              Detonate();
          }
      }
  }
  
  void CGrenadeAR2::Detonate(void) {
      if (!m_bIsLive) {
          return;
      }
      m_bIsLive = false;
      m_takedamage = DAMAGE_NO;
  
      if (m_hSmokeTrail) {
          UTIL_Remove(m_hSmokeTrail);
          m_hSmokeTrail = NULL;
      }
  
      CPASFilter filter(GetAbsOrigin());
  
      te->Explosion(filter, 0.0,
                    &GetAbsOrigin(),
                    g_sModelIndexFireball,
                    2.0,
                    15,
                    TE_EXPLFLAG_NONE,
                    m_DmgRadius,
                    m_flDamage);
  
      Vector vecForward = GetAbsVelocity();
      VectorNormalize(vecForward);
      trace_t tr;
      UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + 60 * vecForward, MASK_SHOT,
                     this, COLLISION_GROUP_NONE, &tr);
  
  
      if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0)) {
          // non-world needs smaller decals
          if (tr.m_pEnt && !tr.m_pEnt->IsNPC()) {
              UTIL_DecalTrace(&tr, "SmallScorch");
          }
      } else {
          UTIL_DecalTrace(&tr, "Scorch");
      }
  
      UTIL_ScreenShake(GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START);
  
      RadiusDamage(CTakeDamageInfo(this, GetThrower(), m_flDamage, DMG_BLAST), GetAbsOrigin(), m_DmgRadius, CLASS_NONE,
                   NULL);
  
      UTIL_Remove(this);
  }
  
  void CGrenadeAR2::Precache(void) {
      PrecacheModel("models/Weapons/ar2_grenade.mdl");
  }
  
  
  CGrenadeAR2::CGrenadeAR2(void) {
      m_hSmokeTrail = NULL;
  }
  