mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-02 17:48:11 +03:00
696 lines
22 KiB
C++
696 lines
22 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Gravity well device
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "grenade_hopwire.h"
|
|
#include "rope.h"
|
|
#include "rope_shared.h"
|
|
#include "beam_shared.h"
|
|
#include "particle_parse.h"
|
|
#include "physics.h"
|
|
#include "physics_saverestore.h"
|
|
#include "explode.h"
|
|
#include "physics_prop_ragdoll.h"
|
|
#include "movevars_shared.h"
|
|
#include "IEffects.h"
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
ConVar oc_weapon_hopwire_vortex_spheresize("oc_weapon_hopwire_vortex_spheresize", "8", FCVAR_ARCHIVE);
|
|
ConVar oc_weapon_hopwire_vortex_stepsize("oc_weapon_hopwire_vortex_stepsize", "0.1", FCVAR_ARCHIVE);
|
|
ConVar oc_weapon_hopwire_vortex_lifetime("oc_weapon_hopwire_vortex_lifetime", "3.0", FCVAR_ARCHIVE);
|
|
ConVar oc_weapon_hopwire_vortex("oc_weapon_hopwire_vortex", "1");
|
|
ConVar oc_weapon_hopwire_trap("oc_weapon_hopwire_trap", "1");
|
|
ConVar oc_weapon_hopwire_strider_kill_dist_h("oc_weapon_hopwire_strider_kill_dist_h", "300");
|
|
ConVar oc_weapon_hopwire_strider_kill_dist_v("oc_weapon_hopwire_strider_kill_dist_v", "256");
|
|
ConVar oc_weapon_hopwire_strider_hits("oc_weapon_hopwire_strider_hits", "1");
|
|
ConVar oc_weapon_hopwire_hopheight("oc_weapon_hopwire_hopheight", "400", FCVAR_ARCHIVE);
|
|
ConVar oc_weapon_hopwire_model("oc_weapon_hopwire_model", "models/weapons/w_grenade.mdl", FCVAR_REPLICATED | FCVAR_ARCHIVE, "Set the model of hopwire weapon.");
|
|
|
|
ConVar g_debug_hopwire( "g_debug_hopwire", "0" );
|
|
|
|
#define DENSE_BALL_MODEL "models/props_junk/metal_paintcan001b.mdl"
|
|
|
|
#define MAX_HOP_HEIGHT (oc_weapon_hopwire_hopheight.GetFloat()) // Maximum amount the grenade will "hop" upwards when detonated
|
|
|
|
//extern ConVar oc_weapon_cguard_sphere_model;
|
|
|
|
#define VORTEX_MODEL "models/ballsphere.mdl"//oc_weapon_cguard_sphere_model.GetString()
|
|
|
|
class CGravityVortexController : public CBaseAnimating//CBaseEntity
|
|
{
|
|
DECLARE_CLASS(CGravityVortexController, CBaseAnimating);
|
|
DECLARE_DATADESC();
|
|
|
|
public:
|
|
void Precache(void);
|
|
void Spawn(void);
|
|
CGravityVortexController( void ) : m_flEndTime( 0.0f ), m_flRadius( 256 ), m_flStrength( 256 ), m_flMass( 0.0f ) {}
|
|
float GetConsumedMass( void ) const;
|
|
|
|
static CGravityVortexController *Create( const Vector &origin, float radius, float strength, float duration );
|
|
|
|
private:
|
|
|
|
void ConsumeEntity( CBaseEntity *pEnt );
|
|
void PullPlayersInRange( void );
|
|
bool KillNPCInRange( CBaseEntity *pVictim, IPhysicsObject **pPhysObj );
|
|
void CreateDenseBall( void );
|
|
void PullThink( void );
|
|
void StartPull( const Vector &origin, float radius, float strength, float duration );
|
|
|
|
float m_flMass; // Mass consumed by the vortex
|
|
float m_flEndTime; // Time when the vortex will stop functioning
|
|
float m_flRadius; // Area of effect for the vortex
|
|
float m_flStrength; // Pulling strength of the vortex
|
|
};
|
|
|
|
void CGravityVortexController::Precache(void)
|
|
{
|
|
PrecacheModel(VORTEX_MODEL);
|
|
|
|
PrecacheParticleSystem("hopwire_vortex");
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
void CGravityVortexController::Spawn(void)
|
|
{
|
|
Precache();
|
|
|
|
SetModel(VORTEX_MODEL);
|
|
|
|
//BaseClass::Spawn();
|
|
|
|
DispatchParticleEffect("hopwire_vortex", PATTACH_ABSORIGIN_FOLLOW, this);
|
|
|
|
float size = oc_weapon_hopwire_vortex_spheresize.GetFloat();
|
|
|
|
size = Clamp(size, 0.1f, 50.f);
|
|
|
|
SetModelScale(size);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the amount of mass consumed by the vortex
|
|
//-----------------------------------------------------------------------------
|
|
float CGravityVortexController::GetConsumedMass( void ) const
|
|
{
|
|
return m_flMass;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds the entity's mass to the aggregate mass consumed
|
|
//-----------------------------------------------------------------------------
|
|
void CGravityVortexController::ConsumeEntity( CBaseEntity *pEnt )
|
|
{
|
|
// Get our base physics object
|
|
IPhysicsObject *pPhysObject = pEnt->VPhysicsGetObject();
|
|
if ( pPhysObject == NULL )
|
|
return;
|
|
|
|
// Ragdolls need to report the sum of all their parts
|
|
CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnt );
|
|
if ( pRagdoll != NULL )
|
|
{
|
|
// Find the aggregate mass of the whole ragdoll
|
|
ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
|
|
for ( int j = 0; j < pRagdollPhys->listCount; ++j )
|
|
{
|
|
m_flMass += pRagdollPhys->list[j].pObject->GetMass();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise we just take the normal mass
|
|
m_flMass += pPhysObject->GetMass();
|
|
}
|
|
|
|
// Destroy the entity
|
|
UTIL_Remove( pEnt );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Causes players within the radius to be sucked in
|
|
//-----------------------------------------------------------------------------
|
|
void CGravityVortexController::PullPlayersInRange( void )
|
|
{
|
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
|
|
|
Vector vecForce = GetAbsOrigin() - pPlayer->GetAbsOrigin();//->WorldSpaceCenter();
|
|
|
|
float dist = VectorNormalize( vecForce );
|
|
|
|
// FIXME: Need a more deterministic method here
|
|
if ( dist < 128.0f )
|
|
{
|
|
// Kill the player (with falling death sound and effects)
|
|
CTakeDamageInfo deathInfo( this, this, GetAbsOrigin(), GetAbsOrigin(), 200, DMG_FALL );
|
|
deathInfo.SetDamageForce(vecForce);
|
|
pPlayer->TakeDamage( deathInfo );
|
|
|
|
if ( pPlayer->IsAlive() == false )
|
|
{
|
|
color32 black = { 0, 0, 0, 255 };
|
|
UTIL_ScreenFade( pPlayer, black, 0.1f, 0.0f, (FFADE_OUT|FFADE_STAYOUT) );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Must be within the radius
|
|
if ( dist > m_flRadius )
|
|
return;
|
|
|
|
float mass = pPlayer->VPhysicsGetObject()->GetMass();
|
|
float playerForce = m_flStrength * 0.05f;
|
|
|
|
// Find the pull force
|
|
// NOTE: We might want to make this non-linear to give more of a "grace distance"
|
|
vecForce *= ( 1.0f - ( dist / m_flRadius ) ) * playerForce * mass;
|
|
vecForce[2] *= 0.025f;
|
|
|
|
//pPlayer->SetBaseVelocity( vecForce );
|
|
pPlayer->VelocityPunch(vecForce);
|
|
//pPlayer->AddFlag( FL_BASEVELOCITY );
|
|
|
|
// Make sure the player moves
|
|
if ( vecForce.z > 0 && ( pPlayer->GetFlags() & FL_ONGROUND) )
|
|
{
|
|
pPlayer->SetGroundEntity( NULL );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Attempts to kill an NPC if it's within range and other criteria
|
|
// Input : *pVictim - NPC to assess
|
|
// **pPhysObj - pointer to the ragdoll created if the NPC is killed
|
|
// Output : bool - whether or not the NPC was killed and the returned pointer is valid
|
|
//-----------------------------------------------------------------------------
|
|
bool CGravityVortexController::KillNPCInRange( CBaseEntity *pVictim, IPhysicsObject **pPhysObj )
|
|
{
|
|
CBaseCombatCharacter *pBCC = pVictim->MyCombatCharacterPointer();
|
|
|
|
// See if we can ragdoll
|
|
if ( pBCC != NULL && pBCC->CanBecomeRagdoll() )
|
|
{
|
|
if (!(cvar->FindVar("oc_ragdoll_serverside")->GetInt()))
|
|
{
|
|
if (FClassnameIs(pBCC, "npc_strider"))
|
|
return false;
|
|
// also if player
|
|
if (FClassnameIs(pBCC, "player"))
|
|
return false;
|
|
|
|
CTakeDamageInfo info(this, this, 1.0f, DMG_GENERIC);
|
|
CBaseEntity *pRagdoll = CreateServerRagdoll(pBCC, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true, pBCC->BloodColor());
|
|
pRagdoll->SetCollisionBounds(pVictim->CollisionProp()->OBBMins(), pVictim->CollisionProp()->OBBMaxs());
|
|
|
|
CTakeDamageInfo ragdollInfo(this, this, 100000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL);
|
|
|
|
pVictim->TakeDamage(ragdollInfo);
|
|
|
|
// Return the pointer to the ragdoll
|
|
*pPhysObj = pRagdoll->VPhysicsGetObject();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Necessary to cause it to do the appropriate death cleanup
|
|
CTakeDamageInfo ragdollInfo(this, this, 50.0, DMG_GENERIC);// , DMG_GENERIC | DMG_REMOVENORAGDOLL);//10000.0
|
|
|
|
if (FClassnameIs(pBCC, "player"))
|
|
ragdollInfo.SetDamageForce(Vector(0, 0, 0));
|
|
else
|
|
ragdollInfo.SetDamageForce(pVictim->GetAbsOrigin() + GetAbsOrigin());
|
|
//ragdollInfo.ScaleDamageForce(-10.f);
|
|
//pVictim->Event_Killed(ragdollInfo);
|
|
pVictim->TakeDamage(ragdollInfo);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!(cvar->FindVar("oc_ragdoll_serverside")->GetInt()))
|
|
*pPhysObj = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Creates a dense ball with a mass equal to the aggregate mass consumed by the vortex
|
|
//-----------------------------------------------------------------------------
|
|
void CGravityVortexController::CreateDenseBall( void )
|
|
{
|
|
CBaseEntity *pBall = CreateEntityByName( "prop_physics" );
|
|
|
|
pBall->SetModel( DENSE_BALL_MODEL );
|
|
pBall->SetAbsOrigin( GetAbsOrigin() );
|
|
pBall->Spawn();
|
|
|
|
IPhysicsObject *pObj = pBall->VPhysicsGetObject();
|
|
if ( pObj != NULL )
|
|
{
|
|
pObj->SetMass( GetConsumedMass() );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Pulls physical objects towards the vortex center, killing them if they come too near
|
|
//-----------------------------------------------------------------------------
|
|
void CGravityVortexController::PullThink( void )
|
|
{
|
|
float size = GetModelScale() - (oc_weapon_hopwire_vortex_stepsize.GetFloat() * gpGlobals->frametime);
|
|
|
|
size = Clamp(size, 0.1f, 50.f);
|
|
|
|
//DevMsg("size: %.2f \n",size);
|
|
|
|
SetModelScale(size, 0.0f);
|
|
|
|
// Pull any players close enough to us
|
|
PullPlayersInRange();
|
|
|
|
Vector mins, maxs;
|
|
mins = GetAbsOrigin() - Vector( m_flRadius, m_flRadius, m_flRadius );
|
|
maxs = GetAbsOrigin() + Vector( m_flRadius, m_flRadius, m_flRadius );
|
|
|
|
// Draw debug information
|
|
if ( g_debug_hopwire.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( GetAbsOrigin(), mins - GetAbsOrigin(), maxs - GetAbsOrigin(), 0, 255, 0, 16, 4.0f );
|
|
}
|
|
|
|
CBaseEntity *pEnts[128];
|
|
int numEnts = UTIL_EntitiesInBox( pEnts, 128, mins, maxs, 0 );
|
|
|
|
for ( int i = 0; i < numEnts; i++ )
|
|
{
|
|
IPhysicsObject *pPhysObject = NULL;
|
|
|
|
if (!(cvar->FindVar("oc_ragdoll_serverside")->GetInt()))
|
|
{
|
|
if (KillNPCInRange(pEnts[i], &pPhysObject) == false)
|
|
{
|
|
// If we didn't have a valid victim, see if we can just get the vphysics object
|
|
pPhysObject = pEnts[i]->VPhysicsGetObject();
|
|
if (pPhysObject == NULL)
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pEnts[i] && (pEnts[i]->IsNPC() || pEnts[i]->IsPlayer()) && pEnts[i]->IsAlive())
|
|
{
|
|
KillNPCInRange(pEnts[i], &pPhysObject);
|
|
}
|
|
else
|
|
{
|
|
if (pEnts[i])
|
|
pPhysObject = pEnts[i]->VPhysicsGetObject();
|
|
}
|
|
}
|
|
|
|
if (pPhysObject == NULL)
|
|
continue;
|
|
|
|
|
|
float mass;
|
|
|
|
CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnts[i] );
|
|
if ( pRagdoll != NULL )
|
|
{
|
|
ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
|
|
mass = 0.0f;
|
|
|
|
// Find the aggregate mass of the whole ragdoll
|
|
for ( int j = 0; j < pRagdollPhys->listCount; ++j )
|
|
{
|
|
mass += pRagdollPhys->list[j].pObject->GetMass();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mass = pPhysObject->GetMass();
|
|
}
|
|
|
|
Vector vecForce = GetAbsOrigin() - pEnts[i]->WorldSpaceCenter();
|
|
Vector vecForce2D = vecForce;
|
|
vecForce2D[2] = 0.0f;
|
|
float dist2D = VectorNormalize( vecForce2D );
|
|
float dist = VectorNormalize( vecForce );
|
|
|
|
// FIXME: Need a more deterministic method here
|
|
if ( dist < 48.0f )
|
|
{
|
|
ConsumeEntity( pEnts[i] );
|
|
continue;
|
|
}
|
|
|
|
// Must be within the radius
|
|
if ( dist > m_flRadius )
|
|
continue;
|
|
|
|
// Find the pull force
|
|
vecForce *= ( 1.0f - ( dist2D / m_flRadius ) ) * m_flStrength * mass;
|
|
|
|
if ( pEnts[i]->VPhysicsGetObject() )
|
|
{
|
|
// Pull the object in
|
|
pEnts[i]->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, GetAbsOrigin(), m_flStrength, DMG_BLAST ) );
|
|
}
|
|
}
|
|
|
|
// Keep going if need-be
|
|
if ( m_flEndTime > gpGlobals->curtime )
|
|
{
|
|
SetThink( &CGravityVortexController::PullThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
else
|
|
{
|
|
StopSound("Weapon_HopWire.Vortex");
|
|
|
|
StopParticleEffects(this);
|
|
|
|
UTIL_Remove(this);
|
|
|
|
//CreateDenseBall();
|
|
}
|
|
|
|
//SetModelScale(1.f);// *Clamp(m_flEndTime - gpGlobals->curtime, 0.f, 1.f));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Starts the vortex working
|
|
//-----------------------------------------------------------------------------
|
|
void CGravityVortexController::StartPull( const Vector &origin, float radius, float strength, float duration )
|
|
{
|
|
SetAbsOrigin( origin );
|
|
m_flEndTime = gpGlobals->curtime + duration;
|
|
m_flRadius = radius;
|
|
m_flStrength= strength;
|
|
|
|
//PrecacheModel(VORTEX_MODEL);
|
|
SetModel(VORTEX_MODEL);
|
|
|
|
SetModelScale(oc_weapon_hopwire_vortex_spheresize.GetFloat(), 0.0f);
|
|
|
|
Vector newOrigin = GetBaseAnimating()->GetLocalOrigin();
|
|
newOrigin.z -= 30.f;
|
|
GetBaseAnimating()->SetLocalOrigin(newOrigin);
|
|
|
|
PrecacheScriptSound("Weapon_HopWire.Vortex");
|
|
EmitSound("Weapon_HopWire.Vortex");
|
|
|
|
SetThink( &CGravityVortexController::PullThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Creation utility
|
|
//-----------------------------------------------------------------------------
|
|
CGravityVortexController *CGravityVortexController::Create( const Vector &origin, float radius, float strength, float duration )
|
|
{
|
|
// Create an instance of the vortex
|
|
CGravityVortexController *pVortex = (CGravityVortexController *) CreateEntityByName( "vortex_controller" );
|
|
if ( pVortex == NULL )
|
|
return NULL;
|
|
|
|
pVortex->Spawn();
|
|
|
|
// Start the vortex working
|
|
pVortex->StartPull( origin, radius, strength, duration );
|
|
|
|
return pVortex;
|
|
}
|
|
|
|
BEGIN_DATADESC( CGravityVortexController )
|
|
DEFINE_FIELD( m_flMass, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_flEndTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_flStrength, FIELD_FLOAT ),
|
|
|
|
DEFINE_THINKFUNC( PullThink ),
|
|
END_DATADESC()
|
|
|
|
LINK_ENTITY_TO_CLASS( vortex_controller, CGravityVortexController );
|
|
|
|
#define HOPWIRE_MODEL "models/Weapons/w_hopwire.mdl"
|
|
//#define GRENADE_MODEL_CLOSED aa_wpn_hopwire_model.GetString() //"models/weapons/w_hopwire.mdl"
|
|
//#define GRENADE_MODEL_OPEN aa_wpn_hopwire_model.GetString() //"models/weapons/w_hopwire.mdl"
|
|
|
|
BEGIN_DATADESC( CGrenadeHopwire )
|
|
DEFINE_FIELD( m_hVortexController, FIELD_EHANDLE ),
|
|
|
|
DEFINE_THINKFUNC( EndThink ),
|
|
DEFINE_THINKFUNC( CombatThink ),
|
|
END_DATADESC()
|
|
|
|
LINK_ENTITY_TO_CLASS( npc_grenade_hopwire, CGrenadeHopwire );
|
|
|
|
IMPLEMENT_SERVERCLASS_ST( CGrenadeHopwire, DT_GrenadeHopwire )
|
|
END_SEND_TABLE()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::Spawn( void )
|
|
{
|
|
Precache();
|
|
|
|
//SetModel( GRENADE_MODEL_CLOSED );
|
|
SetModel(HOPWIRE_MODEL);
|
|
|
|
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
|
|
|
CreateVPhysics();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CGrenadeHopwire::CreateVPhysics()
|
|
{
|
|
// Create the object in the physics system
|
|
VPhysicsInitNormal( SOLID_BBOX, 0, false );
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::Precache( void )
|
|
{
|
|
// FIXME: Replace
|
|
PrecacheSound("NPC_Strider.Shoot"); // Light Kill : Why removed ? o_0
|
|
PrecacheSound("d3_citadel.portal_open_rift"); // Light Kill : New sound
|
|
//PrecacheSound("d3_citadel.weapon_zapper_beam_loop2");
|
|
|
|
PrecacheModel(VORTEX_MODEL);
|
|
|
|
PrecacheModel(HOPWIRE_MODEL);
|
|
|
|
PrecacheModel( DENSE_BALL_MODEL );
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : timer -
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::SetTimer( float timer )
|
|
{
|
|
SetThink( &CBaseGrenade::PreDetonate );
|
|
SetNextThink( gpGlobals->curtime + timer );
|
|
}
|
|
|
|
#define MAX_STRIDER_KILL_DISTANCE_HORZ (oc_weapon_hopwire_strider_kill_dist_h.GetFloat()) // Distance a Strider will be killed if within
|
|
#define MAX_STRIDER_KILL_DISTANCE_VERT (oc_weapon_hopwire_strider_kill_dist_v.GetFloat()) // Distance a Strider will be killed if within
|
|
|
|
#define MAX_STRIDER_STUN_DISTANCE_HORZ (MAX_STRIDER_KILL_DISTANCE_HORZ*2) // Distance a Strider will be stunned if within
|
|
#define MAX_STRIDER_STUN_DISTANCE_VERT (MAX_STRIDER_KILL_DISTANCE_VERT*2) // Distance a Strider will be stunned if within
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::KillStriders( void )
|
|
{
|
|
CBaseEntity *pEnts[128];
|
|
Vector mins, maxs;
|
|
|
|
ClearBounds( mins, maxs );
|
|
AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs );
|
|
AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs );
|
|
AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs );
|
|
AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs );
|
|
|
|
// FIXME: It's probably much faster to simply iterate over the striders in the map, rather than any entity in the radius - jdw
|
|
|
|
// Find any striders in range of us
|
|
int numTargets = UTIL_EntitiesInBox( pEnts, ARRAYSIZE( pEnts ), GetAbsOrigin()+mins, GetAbsOrigin()+maxs, FL_NPC );
|
|
float targetDistHorz, targetDistVert;
|
|
|
|
for ( int i = 0; i < numTargets; i++ )
|
|
{
|
|
// Only affect striders
|
|
if ( FClassnameIs( pEnts[i], "npc_strider" ) == false )
|
|
continue;
|
|
|
|
// We categorize our spatial relation to the strider in horizontal and vertical terms, so that we can specify both parameters separately
|
|
targetDistHorz = UTIL_DistApprox2D( pEnts[i]->GetAbsOrigin(), GetAbsOrigin() );
|
|
targetDistVert = fabs( pEnts[i]->GetAbsOrigin()[2] - GetAbsOrigin()[2] );
|
|
|
|
if ( targetDistHorz < MAX_STRIDER_KILL_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_KILL_DISTANCE_VERT )
|
|
{
|
|
// Kill the strider
|
|
float fracDamage = (pEnts[i]->GetMaxHealth() / oc_weapon_hopwire_strider_hits.GetFloat()) + 1.0f;
|
|
CTakeDamageInfo killInfo( this, this, fracDamage, DMG_GENERIC );
|
|
Vector killDir = pEnts[i]->GetAbsOrigin() - GetAbsOrigin();
|
|
VectorNormalize( killDir );
|
|
|
|
killInfo.SetDamageForce( killDir * -1000.0f );
|
|
killInfo.SetDamagePosition( GetAbsOrigin() );
|
|
|
|
pEnts[i]->TakeDamage( killInfo );
|
|
}
|
|
else if ( targetDistHorz < MAX_STRIDER_STUN_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_STUN_DISTANCE_VERT )
|
|
{
|
|
// Stun the strider
|
|
CTakeDamageInfo killInfo( this, this, 200.0f, DMG_GENERIC );
|
|
pEnts[i]->TakeDamage( killInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::EndThink( void )
|
|
{
|
|
if (oc_weapon_hopwire_vortex.GetBool())
|
|
{
|
|
EntityMessageBegin( this, true );
|
|
WRITE_BYTE( 1 );
|
|
MessageEnd();
|
|
}
|
|
|
|
SetThink( &CBaseEntity::SUB_Remove );
|
|
SetNextThink( gpGlobals->curtime + 1.0f );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::CombatThink( void )
|
|
{
|
|
// Stop the grenade from moving
|
|
AddEFlags( EF_NODRAW );
|
|
AddFlag( FSOLID_NOT_SOLID );
|
|
VPhysicsDestroyObject();
|
|
SetAbsVelocity( vec3_origin );
|
|
SetMoveType( MOVETYPE_NONE );
|
|
|
|
// Do special behaviors if there are any striders in the area
|
|
KillStriders();
|
|
|
|
// FIXME: Replace
|
|
EmitSound("NPC_Strider.Shoot");
|
|
EmitSound("d3_citadel.portal_open_rift");
|
|
//EmitSound("d3_citadel.weapon_zapper_beam_loop2");
|
|
|
|
// Quick screen flash
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetThrower() );
|
|
color32 white = { 255,255,255,255 };
|
|
UTIL_ScreenFade( pPlayer, white, 0.2f, 0.0f, FFADE_IN );
|
|
|
|
Vector Up = GetAbsOrigin();
|
|
Up.z += 10.f;
|
|
|
|
g_pEffects->Dust(GetAbsOrigin(), Up, 12, 50);
|
|
UTIL_Smoke(GetAbsOrigin(), random->RandomInt(10, 15), 10);
|
|
|
|
// Create the vortex controller to pull entities towards us
|
|
if (oc_weapon_hopwire_vortex.GetBool())
|
|
{
|
|
m_hVortexController = CGravityVortexController::Create(GetAbsOrigin(), 512, 150, oc_weapon_hopwire_vortex_lifetime.GetFloat());
|
|
|
|
// Start our client-side effect
|
|
EntityMessageBegin( this, true );
|
|
WRITE_BYTE( 0 );
|
|
MessageEnd();
|
|
|
|
// Begin to stop in two seconds
|
|
SetThink( &CGrenadeHopwire::EndThink );
|
|
SetNextThink( gpGlobals->curtime + 2.0f );
|
|
}
|
|
else
|
|
{
|
|
// Remove us immediately
|
|
SetThink( &CBaseEntity::SUB_Remove );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
|
|
{
|
|
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
|
|
|
if ( pPhysicsObject != NULL )
|
|
{
|
|
pPhysicsObject->AddVelocity( &velocity, &angVelocity );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Hop off the ground to start deployment
|
|
//-----------------------------------------------------------------------------
|
|
void CGrenadeHopwire::Detonate( void )
|
|
{
|
|
AngularImpulse hopAngle = RandomAngularImpulse( -300, 300 );
|
|
|
|
//Find out how tall the ceiling is and always try to hop halfway
|
|
//trace_t tr;
|
|
//UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MAX_HOP_HEIGHT*2 ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
|
|
|
// Jump half the height to the found ceiling
|
|
float hopHeight = MAX_HOP_HEIGHT;//min( MAX_HOP_HEIGHT, (MAX_HOP_HEIGHT*tr.fraction) );
|
|
|
|
//Add upwards velocity for the "hop"
|
|
Vector hopVel( 0.0f, 0.0f, hopHeight );
|
|
SetVelocity( hopVel, hopAngle );
|
|
|
|
// Get the time until the apex of the hop
|
|
float apexTime = sqrt( hopHeight / sv_gravity.GetFloat() );
|
|
|
|
// Explode at the apex
|
|
SetThink( &CGrenadeHopwire::CombatThink );
|
|
SetNextThink( gpGlobals->curtime + apexTime);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CBaseGrenade *HopWire_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer )
|
|
{
|
|
CGrenadeHopwire *pGrenade = (CGrenadeHopwire *) CBaseEntity::Create( "npc_grenade_hopwire", position, angles, pOwner );
|
|
|
|
// Only set ourselves to detonate on a timer if we're not a trap hopwire
|
|
if ( oc_weapon_hopwire_trap.GetBool() == false )
|
|
{
|
|
pGrenade->SetTimer( timer );
|
|
}
|
|
|
|
pGrenade->SetVelocity( velocity, angVelocity );
|
|
pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) );
|
|
|
|
return pGrenade;
|
|
}
|