You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1234 lines
38 KiB
1234 lines
38 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "tf_weapon_throwable.h" |
|
#include "tf_gamerules.h" |
|
#include "in_buttons.h" |
|
#include "basetypes.h" |
|
#include "tf_weaponbase_gun.h" |
|
#include "effect_dispatch_data.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
// Server specific. |
|
#else |
|
#include "tf_player.h" |
|
#include "tf_fx.h" |
|
#include "te_effect_dispatch.h" |
|
#include "bone_setup.h" |
|
#include "tf_target_dummy.h" |
|
#endif |
|
|
|
|
|
// Base |
|
// Launcher |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFThrowable, DT_TFWeaponThrowable ) |
|
BEGIN_NETWORK_TABLE( CTFThrowable, DT_TFWeaponThrowable ) |
|
#ifdef CLIENT_DLL |
|
RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ), |
|
#else |
|
SendPropFloat( SENDINFO( m_flChargeBeginTime ) ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFThrowable ) |
|
END_PREDICTION_DATA() |
|
|
|
//LINK_ENTITY_TO_CLASS( tf_weapon_throwable, CTFThrowable ); |
|
//PRECACHE_WEAPON_REGISTER( tf_weapon_throwable ); |
|
|
|
#ifdef STAGING_ONLY |
|
CREATE_SIMPLE_WEAPON_TABLE( TFThrowablePrimary, tf_weapon_throwable_primary ) |
|
CREATE_SIMPLE_WEAPON_TABLE( TFThrowableSecondary, tf_weapon_throwable_secondary ) |
|
CREATE_SIMPLE_WEAPON_TABLE( TFThrowableMelee, tf_weapon_throwable_melee ) |
|
CREATE_SIMPLE_WEAPON_TABLE( TFThrowableUtility, tf_weapon_throwable_utility ) |
|
#endif |
|
|
|
// Projectile |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Throwable, DT_TFProjectile_Throwable ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_Throwable, DT_TFProjectile_Throwable ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_throwable, CTFProjectile_Throwable ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_throwable ); |
|
|
|
// Projectile Repel |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableRepel, DT_TFProjectile_ThrowableRepel ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableRepel, DT_TFProjectile_ThrowableRepel ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_throwable_repel, CTFProjectile_ThrowableRepel ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_repel ); |
|
|
|
// Projectile Brick |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableBrick, DT_TFProjectile_ThrowableBrick ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableBrick, DT_TFProjectile_ThrowableBrick ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_throwable_brick, CTFProjectile_ThrowableBrick ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_brick ); |
|
|
|
// Projectile Bread Monster |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableBreadMonster, DT_TFProjectile_ThrowableBreadMonster ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableBreadMonster, DT_TFProjectile_ThrowableBreadMonster ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_throwable_breadmonster, CTFProjectile_ThrowableBreadMonster ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_breadmonster ); |
|
|
|
#ifdef STAGING_ONLY |
|
// Projectile Target Dummy |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableTargetDummy, DT_TFProjectile_ThrowableTargetDummy ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ConcGrenade, DT_TFProjectile_ThrowableTargetDummy ) |
|
END_NETWORK_TABLE() |
|
LINK_ENTITY_TO_CLASS( tf_projectile_target_dummy, CTFProjectile_ThrowableTargetDummy ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_target_dummy ); |
|
|
|
// Projectile Concussion Grenade |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ConcGrenade, DT_TFProjectile_ConcGrenade ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ConcGrenade, DT_TFProjectile_ConcGrenade ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_grenade_concussion, CTFProjectile_ConcGrenade ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_concussion ); |
|
|
|
// Projectile Teleport Grenade |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_TeleportGrenade, DT_TFProjectile_TeleportGrenade ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_TeleportGrenade, DT_TFProjectile_TeleportGrenade ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_grenade_teleport, CTFProjectile_TeleportGrenade ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_teleport ); |
|
|
|
// Projectile Chain Grenade |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_GravityGrenade, DT_TFProjectile_GravityGrenade ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_GravityGrenade, DT_TFProjectile_GravityGrenade ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_grenade_gravity, CTFProjectile_GravityGrenade ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_gravity ); |
|
|
|
// Projectile Chain Grenade |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowingKnife, DT_TFProjectile_ThrowingKnife ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_ThrowingKnife, DT_TFProjectile_ThrowingKnife ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_throwing_knife, CTFProjectile_ThrowingKnife ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_throwing_knife ); |
|
|
|
// Projectile Smoke Grenade |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SmokeGrenade, DT_TFProjectile_SmokeGrenade ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_SmokeGrenade, DT_TFProjectile_SmokeGrenade ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_grenade_smoke, CTFProjectile_SmokeGrenade ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_smoke ); |
|
#endif // STAGING_ONLY |
|
|
|
#define TF_GRENADE_TIMER "Weapon_Grenade.Timer" |
|
#define TF_GRENADE_CHARGE "Weapon_LooseCannon.Charge" |
|
|
|
//**************************************************************************** |
|
// Throwable Weapon |
|
//**************************************************************************** |
|
CTFThrowable::CTFThrowable( void ) |
|
{ |
|
m_flChargeBeginTime = -1.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFThrowable::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
|
|
PrecacheModel( g_pszArrowModels[MODEL_BREAD_MONSTER] ); |
|
PrecacheModel( g_pszArrowModels[MODEL_THROWING_KNIFE] ); |
|
#ifdef STAGING_ONLY |
|
PrecacheScriptSound( "Weapon_Grenade_Concussion.Explode" ); |
|
PrecacheScriptSound( "Weapon_Grenade_Teleport.Explode" ); |
|
PrecacheScriptSound( TF_GRENADE_TIMER ); |
|
#endif // STAGING_ONLY |
|
PrecacheScriptSound( TF_GRENADE_CHARGE ); |
|
|
|
PrecacheScriptSound( "Weapon_bm_throwable.throw" ); |
|
PrecacheScriptSound( "Weapon_bm_throwable.smash" ); |
|
|
|
PrecacheParticleSystem( "grenade_smoke_cycle" ); |
|
PrecacheParticleSystem( "blood_bread_biting" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
float CTFThrowable::InternalGetEffectBarRechargeTime( void ) |
|
{ |
|
float flRechargeTime = 0; |
|
CALL_ATTRIB_HOOK_FLOAT( flRechargeTime, throwable_recharge_time ); |
|
if ( flRechargeTime ) |
|
return flRechargeTime; |
|
return 10.0f; // default |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
float CTFThrowable::GetDetonationTime() |
|
{ |
|
float flDetonationTime = 0; |
|
CALL_ATTRIB_HOOK_FLOAT( flDetonationTime, throwable_detonation_time ); |
|
if ( flDetonationTime ) |
|
return flDetonationTime; |
|
return 5.0f; // default |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFThrowable::PrimaryAttack( void ) |
|
{ |
|
if ( !CanCharge() ) |
|
{ |
|
// Fire |
|
BaseClass::PrimaryAttack(); |
|
return; |
|
} |
|
|
|
if ( m_flChargeBeginTime > 0 ) |
|
return; |
|
|
|
// Do all the Checks and start a charged (primed) attack |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// Check for ammunition. |
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) < 1 ) |
|
return; |
|
|
|
// Are we capable of firing again? |
|
if ( m_flNextPrimaryAttack > gpGlobals->curtime ) |
|
return; |
|
|
|
if ( pPlayer->GetWaterLevel() == WL_Eyes ) |
|
return; |
|
|
|
if ( !CanAttack() ) |
|
return; |
|
|
|
if ( m_flChargeBeginTime <= 0 ) |
|
{ |
|
// Set the weapon mode. |
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; |
|
SendWeaponAnim( ACT_VM_PULLBACK ); // TODO : Anim! |
|
#ifdef GAME_DLL |
|
// save that we had the attack button down |
|
m_flChargeBeginTime = gpGlobals->curtime; |
|
#endif // GAME_LL |
|
|
|
#ifdef CLIENT_DLL |
|
if ( pPlayer == C_BasePlayer::GetLocalPlayer() ) |
|
{ |
|
int iCanBeCharged = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable ); |
|
if ( iCanBeCharged ) |
|
{ |
|
EmitSound( TF_GRENADE_CHARGE ); |
|
} |
|
else |
|
{ |
|
EmitSound( TF_GRENADE_TIMER ); |
|
} |
|
} |
|
#endif // CLIENT_DLL |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFThrowable::ItemPostFrame( void ) |
|
{ |
|
// Get the player owning the weapon. |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
if ( m_flChargeBeginTime > 0.f && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
{ |
|
bool bFiredWeapon = false; |
|
// If we're not holding down the attack button, launch our grenade |
|
if ( !( pPlayer->m_nButtons & IN_ATTACK ) ) |
|
{ |
|
FireProjectile( pPlayer ); |
|
bFiredWeapon = true; |
|
} |
|
// Misfire |
|
else if ( m_flChargeBeginTime + GetDetonationTime() < gpGlobals->curtime ) |
|
{ |
|
CTFProjectile_Throwable * pThrowable = dynamic_cast<CTFProjectile_Throwable*>( FireProjectile( pPlayer ) ); |
|
if ( pThrowable ) |
|
{ |
|
#ifdef GAME_DLL |
|
pThrowable->Misfire(); |
|
#endif // GAME_DLL |
|
} |
|
|
|
bFiredWeapon = true; |
|
} |
|
|
|
if ( bFiredWeapon ) |
|
{ |
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK ); |
|
pPlayer->SetAnimation( PLAYER_ATTACK1 ); |
|
#ifdef GAME_DLL |
|
m_flChargeBeginTime = -1.0f; // reset |
|
#endif // GAME_DLL |
|
// Set next attack times. |
|
float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; |
|
m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; |
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); |
|
#ifdef CLIENT_DLL |
|
int iCanBeCharged = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable ); |
|
if ( iCanBeCharged ) |
|
{ |
|
StopSound( TF_GRENADE_CHARGE ); |
|
} |
|
#endif // CLIENT_DLL |
|
} |
|
} |
|
BaseClass::ItemPostFrame(); |
|
} |
|
|
|
// ITFChargeUpWeapon |
|
//----------------------------------------------------------------------------- |
|
// Primable is for timed explosions |
|
// Charagable is for things like distance or power increases |
|
// Can't really have both but can have neither |
|
bool CTFThrowable::CanCharge() |
|
{ |
|
int iCanBePrimed = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable ); |
|
|
|
int iCanBeCharged = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable ); |
|
|
|
return iCanBeCharged || iCanBePrimed ; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
float CTFThrowable::GetChargeBeginTime( void ) |
|
{ |
|
float flDetonateTimeLength = GetDetonationTime(); |
|
// float flModDetonateTimeLength = 0; |
|
|
|
int iCanBePrimed = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable ); |
|
|
|
// Use reverse logic for primable grenades (Counts down to boom) |
|
// Full charge since we haven't fired |
|
if ( iCanBePrimed ) |
|
{ |
|
if ( m_flChargeBeginTime < 0 ) |
|
{ |
|
return gpGlobals->curtime - flDetonateTimeLength; |
|
} |
|
return gpGlobals->curtime - Clamp( m_flChargeBeginTime + flDetonateTimeLength - gpGlobals->curtime, 0.f, flDetonateTimeLength ); |
|
} |
|
|
|
return m_flChargeBeginTime; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
float CTFThrowable::GetChargeMaxTime( void ) |
|
{ |
|
return GetDetonationTime(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
CBaseEntity *CTFThrowable::FireJar( CTFPlayer *pPlayer ) |
|
{ |
|
#ifdef GAME_DLL |
|
return FireProjectileInternal(); |
|
#endif |
|
return NULL; |
|
} |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
void CTFThrowable::TossJarThink( void ) |
|
{ |
|
FireProjectileInternal(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Throwable *CTFThrowable::FireProjectileInternal( void ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return NULL; |
|
|
|
CAttribute_String attrProjectileEntityName; |
|
GetProjectileEntityName( &attrProjectileEntityName ); |
|
if ( !attrProjectileEntityName.has_value() ) |
|
return NULL; |
|
|
|
Vector vecForward, vecRight, vecUp; |
|
AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp ); |
|
|
|
float fRight = 8.f; |
|
if ( IsViewModelFlipped() ) |
|
{ |
|
fRight *= -1; |
|
} |
|
Vector vecSrc = pPlayer->Weapon_ShootPosition(); |
|
|
|
// Make spell toss position at the hand |
|
vecSrc = vecSrc + ( vecUp * -9.0f ) + ( vecRight * 7.0f ) + ( vecForward * 3.0f ); |
|
|
|
trace_t trace; |
|
Vector vecEye = pPlayer->EyePosition(); |
|
CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE ); |
|
UTIL_TraceHull( vecEye, vecSrc, -Vector( 8, 8, 8 ), Vector( 8, 8, 8 ), MASK_SOLID_BRUSHONLY, &traceFilter, &trace ); |
|
|
|
// If we started in solid, don't let them fire at all |
|
if ( trace.startsolid ) |
|
return NULL; |
|
|
|
CalcIsAttackCritical(); |
|
|
|
// Create the Grenade and Intialize it appropriately |
|
CTFProjectile_Throwable *pGrenade = static_cast<CTFProjectile_Throwable*>( CBaseEntity::CreateNoSpawn( attrProjectileEntityName.value().c_str(), trace.endpos, pPlayer->EyeAngles(), pPlayer ) ); |
|
if ( pGrenade ) |
|
{ |
|
// Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly. |
|
pGrenade->SetPipebombMode(); |
|
pGrenade->SetLauncher( this ); |
|
pGrenade->SetCritical( IsCurrentAttackACrit() ); |
|
|
|
DispatchSpawn( pGrenade ); |
|
|
|
// Calculate a charge percentage |
|
// For now Charge just effects exit velocity |
|
int iCanBeCharged = 0; |
|
float flChargePercent = 0; |
|
float flDetonateTime = GetDetonationTime(); |
|
CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable ); |
|
if ( iCanBeCharged ) |
|
{ |
|
flChargePercent = RemapVal( gpGlobals->curtime, m_flChargeBeginTime, m_flChargeBeginTime + flDetonateTime, 0.0f, 1.0f ); |
|
} |
|
|
|
Vector vecVelocity = pGrenade->GetVelocityVector( vecForward, vecRight, vecUp, flChargePercent ); |
|
AngularImpulse angVelocity = pGrenade->GetAngularImpulse(); |
|
|
|
pGrenade->InitGrenade( vecVelocity, angVelocity, pPlayer, GetTFWpnData() ); |
|
pGrenade->InitThrowable( flChargePercent ); |
|
pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); |
|
|
|
if ( flDetonateTime > 0 ) |
|
{ |
|
// Check if this has been primed |
|
int iCanBePrimed = 0; |
|
CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable ); |
|
if ( m_flChargeBeginTime > 0 && iCanBePrimed > 0 ) |
|
{ |
|
flDetonateTime = ( m_flChargeBeginTime + flDetonateTime - gpGlobals->curtime ); |
|
} |
|
pGrenade->SetDetonateTimerLength( flDetonateTime ); |
|
} |
|
pGrenade->m_flFullDamage = 0; |
|
|
|
if ( pGrenade->GetThrowSoundEffect() ) |
|
{ |
|
pGrenade->EmitSound( pGrenade->GetThrowSoundEffect() ); |
|
} |
|
} |
|
|
|
StartEffectBarRegen(); |
|
|
|
return pGrenade; |
|
} |
|
#endif // GAME_DLL |
|
|
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
// Throwable Projectile |
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
#ifdef GAME_DLL |
|
CTFProjectile_Throwable::CTFProjectile_Throwable( void ) |
|
{ |
|
m_flChargePercent = 0; |
|
m_bHit = false; |
|
} |
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
// Get Initial Velocity |
|
Vector CTFProjectile_Throwable::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, float flCharge ) |
|
{ |
|
// Scale the projectile speed up to a maximum of 3000? |
|
float flSpeed = RemapVal( flCharge, 0, 1.0f, GetProjectileSpeed(), GetProjectileMaxSpeed() ); |
|
|
|
return ( ( flSpeed * vecForward ) + |
|
( ( random->RandomFloat( -10.0f, 10.0f ) + 200.0f ) * vecUp ) + |
|
( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) ); |
|
} |
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
void CTFProjectile_Throwable::OnHit( CBaseEntity *pOther ) |
|
{ |
|
if ( m_bHit ) |
|
return; |
|
|
|
if ( ExplodesOnHit() ) |
|
{ |
|
Explode(); |
|
} |
|
|
|
m_bHit = true; |
|
} |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Throwable::Explode() |
|
{ |
|
trace_t tr; |
|
Vector vecSpot;// trace starts here! |
|
SetThink( NULL ); |
|
vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 ); |
|
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr); |
|
Explode( &tr, GetDamageType() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Throwable::Explode( trace_t *pTrace, int bitsDamageType ) |
|
{ |
|
if ( GetThrower() ) |
|
{ |
|
InitialExplodeEffects( NULL, pTrace ); |
|
|
|
// Particle |
|
const char* pszExplodeEffect = GetExplodeEffectParticle(); |
|
if ( pszExplodeEffect && pszExplodeEffect[0] != '\0' ) |
|
{ |
|
CPVSFilter filter( GetAbsOrigin() ); |
|
TE_TFParticleEffect( filter, 0.0, pszExplodeEffect, GetAbsOrigin(), vec3_angle ); |
|
} |
|
|
|
// Sounds |
|
const char* pszSoundEffect = GetExplodeEffectSound(); |
|
if ( pszSoundEffect && pszSoundEffect[0] != '\0' ) |
|
{ |
|
EmitSound( pszSoundEffect ); |
|
} |
|
} |
|
|
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// THROWABLE REPEL |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowableRepel::OnHit( CBaseEntity *pOther ) |
|
{ |
|
if ( m_bHit ) |
|
return; |
|
|
|
CTFPlayer *pPlayer = dynamic_cast< CTFPlayer*>( pOther ); |
|
|
|
if ( pPlayer && !pPlayer->InSameTeam( GetThrower() ) ) |
|
{ |
|
CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() ); |
|
trace_t trace; |
|
UTIL_TraceLine( GetAbsOrigin(), pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace ); |
|
|
|
// Apply AirBlast Force |
|
Vector vecToTarget; |
|
vecToTarget = pPlayer->GetAbsOrigin() - this->GetAbsOrigin(); |
|
vecToTarget.z = 0; |
|
VectorNormalize( vecToTarget ); |
|
|
|
// Quick Fix Uber is immune |
|
if ( pPlayer->m_Shared.InCond( TF_COND_MEGAHEAL )) |
|
return; |
|
|
|
float flForce = 300.0f * m_flChargePercent + 350.0f; |
|
pPlayer->ApplyAirBlastImpulse( vecToTarget * flForce + Vector( 0, 0, flForce ) ); |
|
pPlayer->ApplyPunchImpulseX( RandomInt( -50, -30 ) ); |
|
|
|
// Apply Damage to Victim |
|
CTakeDamageInfo info; |
|
info.SetAttacker( GetThrower() ); |
|
info.SetInflictor( this ); |
|
info.SetWeapon( GetLauncher() ); |
|
info.SetDamage( GetDamage() ); |
|
info.SetDamageCustom( GetCustomDamageType() ); |
|
info.SetDamagePosition( this->GetAbsOrigin() ); |
|
info.SetDamageType( DMG_CLUB | DMG_PREVENT_PHYSICS_FORCE ); |
|
|
|
//Vector dir; |
|
//AngleVectors( GetAbsAngles(), &dir ); |
|
|
|
pPlayer->DispatchTraceAttack( info, vecToTarget, &trace ); |
|
ApplyMultiDamage(); |
|
} |
|
|
|
BaseClass::OnHit( pOther ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
// THROWABLE BRICK |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowableBrick::OnHit( CBaseEntity *pOther ) |
|
{ |
|
if ( m_bHit ) |
|
return; |
|
|
|
CTFPlayer *pPlayer = dynamic_cast< CTFPlayer*>( pOther ); |
|
|
|
if ( pPlayer && !pPlayer->InSameTeam( GetThrower() ) ) |
|
{ |
|
CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() ); |
|
trace_t trace; |
|
UTIL_TraceLine( GetAbsOrigin(), pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace ); |
|
|
|
Vector vecToTarget; |
|
vecToTarget = pPlayer->WorldSpaceCenter() - this->WorldSpaceCenter(); |
|
VectorNormalize( vecToTarget ); |
|
|
|
// Apply Damage to Victim |
|
CTakeDamageInfo info; |
|
info.SetAttacker( GetThrower() ); |
|
info.SetInflictor( this ); |
|
info.SetWeapon( GetLauncher() ); |
|
info.SetDamage( GetDamage() ); |
|
info.SetDamageCustom( GetCustomDamageType() ); |
|
info.SetDamagePosition( GetAbsOrigin() ); |
|
info.SetDamageType( DMG_CLUB ); |
|
|
|
pPlayer->DispatchTraceAttack( info, vecToTarget, &trace ); |
|
pPlayer->ApplyPunchImpulseX( RandomInt( 15, 20 ) ); |
|
ApplyMultiDamage(); |
|
} |
|
|
|
BaseClass::OnHit( pOther ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
// THROWABLE BREADMONSTER |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowableBreadMonster::OnHit( CBaseEntity *pOther ) |
|
{ |
|
if ( m_bHit ) |
|
return; |
|
|
|
CTFPlayer *pVictim = dynamic_cast< CTFPlayer*>( pOther ); |
|
CTFPlayer *pOwner = dynamic_cast< CTFPlayer*>( GetThrower() ); |
|
|
|
if ( pVictim && pOwner && !pVictim->InSameTeam( pOwner ) ) |
|
{ |
|
m_bHit = true; |
|
CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() ); |
|
trace_t trace; |
|
Vector vEndPos = pVictim->WorldSpaceCenter(); |
|
vEndPos.z = WorldSpaceCenter().z + 1.0f; |
|
UTIL_TraceLine( WorldSpaceCenter(), vEndPos, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &tracefilter, &trace ); |
|
|
|
Vector vecToTarget; |
|
vecToTarget = pVictim->WorldSpaceCenter() - this->WorldSpaceCenter(); |
|
VectorNormalize( vecToTarget ); |
|
|
|
// Apply Damage to Victim |
|
CTakeDamageInfo info; |
|
info.SetAttacker( GetThrower() ); |
|
info.SetInflictor( this ); |
|
info.SetWeapon( GetLauncher() ); |
|
info.SetDamage( GetDamage() ); |
|
info.SetDamageCustom( GetCustomDamageType() ); |
|
info.SetDamagePosition( GetAbsOrigin() ); |
|
|
|
int iDamageType = DMG_CLUB; |
|
if ( IsCritical() ) |
|
{ |
|
iDamageType |= DMG_CRITICAL; |
|
} |
|
info.SetDamageType( iDamageType ); |
|
|
|
pVictim->DispatchTraceAttack( info, vecToTarget, &trace ); |
|
pVictim->ApplyPunchImpulseX( RandomInt( 15, 20 ) ); |
|
pVictim->m_Shared.MakeBleed( pOwner, dynamic_cast< CTFWeaponBase * >( GetLauncher() ), 5.0f, 1.0f ); |
|
ApplyMultiDamage(); |
|
|
|
// Bread Particle |
|
CPVSFilter filter( vEndPos ); |
|
TE_TFParticleEffect( filter, 0.0, "blood_bread_biting", vEndPos, vec3_angle ); |
|
|
|
// Attach Breadmonster to Victim |
|
CreateStickyAttachmentToTarget( pOwner, pVictim, &trace ); |
|
|
|
BaseClass::Explode(); |
|
return; |
|
} |
|
else // its a dud |
|
{ |
|
BaseClass::Explode(); |
|
return; |
|
} |
|
BaseClass::OnHit( pOther ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowableBreadMonster::Detonate() |
|
{ |
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowableBreadMonster::Explode( trace_t *pTrace, int bitsDamageType ) |
|
{ |
|
if ( !m_bHit ) |
|
{ |
|
// TODO, Spawn Debris / Flopping BreadInstead |
|
trace_t tr; |
|
Vector velDir = m_vCollisionVelocity; |
|
VectorNormalize( velDir ); |
|
Vector vecSpot = GetAbsOrigin() - velDir * 32; |
|
UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &tr ); |
|
if ( tr.fraction < 1.0 && tr.surface.flags & SURF_SKY ) |
|
{ |
|
// We hit the skybox, go away soon. |
|
return; |
|
} |
|
|
|
// Create a breadmonster in the world |
|
CEffectData data; |
|
data.m_vOrigin = tr.endpos; |
|
data.m_vNormal = velDir; |
|
data.m_nEntIndex = 0; |
|
data.m_nAttachmentIndex = 0; |
|
data.m_nMaterial = 0; |
|
data.m_fFlags = TF_PROJECTILE_BREAD_MONSTER; |
|
data.m_nColor = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0; |
|
|
|
DispatchEffect( "TFBoltImpact", data ); |
|
} |
|
|
|
BaseClass::Explode( pTrace, bitsDamageType ); |
|
} |
|
|
|
#endif // GAME_DLL |
|
// |
|
//#ifdef CLIENT_DLL |
|
// |
|
//static CUtlMap< const char*, CUtlString > s_TeamParticleMap; |
|
//static bool s_TeamParticleMapInited = false; |
|
// |
|
////----------------------------------------------------------------------------- |
|
//const char *CTFProjectile_Throwable::GetTrailParticleName( void ) |
|
//{ |
|
// // Check for Particles |
|
// int iDynamicParticleEffect = 0; |
|
// CALL_ATTRIB_HOOK_INT_ON_OTHER( GetLauncher(), iDynamicParticleEffect, set_attached_particle ); |
|
// if ( iDynamicParticleEffect > 0 ) |
|
// { |
|
// // Init Map Once |
|
// if ( !s_TeamParticleMapInited ) |
|
// { |
|
// SetDefLessFunc( s_TeamParticleMap ); |
|
// s_TeamParticleMapInited = true; |
|
// } |
|
// |
|
// attachedparticlesystem_t *pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iDynamicParticleEffect ); |
|
// if ( pParticleSystem ) |
|
// { |
|
// // TF Team Color Particles |
|
// const char * pName = pParticleSystem->pszSystemName; |
|
// if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pName, "_teamcolor_red" )) |
|
// { |
|
// int index = s_TeamParticleMap.Find( pName ); |
|
// if ( !s_TeamParticleMap.IsValidIndex( index ) ) |
|
// { |
|
// char pBlue[256]; |
|
// V_StrSubst( pName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 ); |
|
// CUtlString pBlueString( pBlue ); |
|
// index = s_TeamParticleMap.Insert( pName, pBlueString ); |
|
// } |
|
// return s_TeamParticleMap[index].String(); |
|
// } |
|
// else if ( GetTeamNumber() == TF_TEAM_RED && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_blue" )) |
|
// { |
|
// // Guard against accidentally giving out the blue team color (support tool) |
|
// int index = s_TeamParticleMap.Find( pName ); |
|
// if ( !s_TeamParticleMap.IsValidIndex( index ) ) |
|
// { |
|
// char pRed[256]; |
|
// V_StrSubst( pName, "_teamcolor_blue", "_teamcolor_red", pRed, 256 ); |
|
// CUtlString pRedString( pRed ); |
|
// index = s_TeamParticleMap.Insert( pName, pRedString ); |
|
// } |
|
// return s_TeamParticleMap[index].String(); |
|
// } |
|
// |
|
// return pName; |
|
// } |
|
// } |
|
// |
|
// if ( GetTeamNumber() == TF_TEAM_BLUE ) |
|
// { |
|
// return "trail_basic_blue"; |
|
// } |
|
// else |
|
// { |
|
// return "trail_basic_red"; |
|
// } |
|
//} |
|
// |
|
//#endif // CLIENT_DLL |
|
|
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
#ifdef STAGING_ONLY |
|
#ifdef GAME_DLL |
|
|
|
|
|
void CTFProjectile_ThrowableTargetDummy::Explode() |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetThrower() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
CTFTargetDummy::Create( GetAbsOrigin(), GetAbsAngles(), pPlayer ); |
|
BaseClass::Explode(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ConcGrenade::Detonate( ) |
|
{ |
|
Explode(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ConcGrenade::Misfire( ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetThrower() ); |
|
if ( pPlayer ) |
|
{ |
|
SetAbsOrigin( pPlayer->GetAbsOrigin() ); |
|
} |
|
|
|
Explode(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ConcGrenade::Explode( ) |
|
{ |
|
// Apply pushback |
|
const float flMaxForce = 900.f; |
|
const float flMaxSelfForce = 800.f; |
|
const int nMaxEnts = MAX_PLAYERS; |
|
CBaseEntity *pObjects[ nMaxEnts ]; |
|
int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, GetAbsOrigin(), GetDamageRadius(), FL_CLIENT ); |
|
CTFPlayer *pThrower = ToTFPlayer( GetThrower() ); |
|
|
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
if ( !pObjects[i] ) |
|
continue; |
|
|
|
if ( !pObjects[i]->IsAlive() ) |
|
continue; |
|
|
|
// Only affect the thrower from same team |
|
if ( InSameTeam( pObjects[i] ) && pObjects[i] != pThrower ) |
|
continue; |
|
|
|
if ( !FVisible( pObjects[i], MASK_OPAQUE ) ) |
|
continue; |
|
|
|
if ( !pObjects[i]->IsPlayer() ) |
|
continue; |
|
|
|
CTFPlayer *pTFPlayer = static_cast< CTFPlayer* >( pObjects[i] ); |
|
if ( !pTFPlayer ) |
|
continue; |
|
|
|
// Conc does more force the further away you are from the blast radius |
|
Vector vecPushDir = pTFPlayer->GetAbsOrigin() - GetAbsOrigin(); |
|
float flForce = RemapVal( vecPushDir.Length(), 0, GetDamageRadius(), 0, flMaxForce ); |
|
|
|
if ( flForce < 250.0f && pObjects[i] == pThrower ) // Hold case |
|
{ |
|
AngularImpulse ang; |
|
pTFPlayer->GetVelocity( &vecPushDir, &ang ); |
|
flForce = flMaxSelfForce; |
|
} |
|
VectorNormalize( vecPushDir ); |
|
vecPushDir.z *= 1.5f; |
|
pTFPlayer->ApplyAirBlastImpulse( vecPushDir * flForce ); |
|
pTFPlayer->ApplyPunchImpulseX( RandomInt( -50, -30 ) ); |
|
|
|
// if ( pObjects[i] == pThrower ) |
|
// { |
|
// // Apply 'Bonk' lines to make target more visible for 2 seconds |
|
// pThrower->m_Shared.AddCond( TF_COND_SELF_CONC, 2 ); |
|
// } |
|
} |
|
|
|
BaseClass::Explode(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_TeleportGrenade::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
SetContextThink( &CTFProjectile_TeleportGrenade::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_TeleportGrenade::RecordPosThink( void ) |
|
{ |
|
m_vecTrailingPos.AddToTail( GetAbsOrigin() ); |
|
|
|
// Only retain 5 positions |
|
if ( m_vecTrailingPos.Count() > 5 ) |
|
{ |
|
m_vecTrailingPos.Remove( 0 ); |
|
} |
|
|
|
SetContextThink( &CTFProjectile_TeleportGrenade::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_TeleportGrenade::Explode( trace_t *pTrace, int bitsDamageType ) |
|
{ |
|
CTFPlayer *pThrower = ToTFPlayer( GetThrower() ); |
|
if ( !pThrower || !pThrower->IsAlive() ) |
|
return; |
|
|
|
trace_t traceHull; |
|
CTraceFilterIgnoreTeammates traceFilter( this, COLLISION_GROUP_PLAYER_MOVEMENT, GetTeamNumber() ); |
|
unsigned int nMask = pThrower->GetTeamNumber() == TF_TEAM_RED ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM; |
|
nMask |= MASK_PLAYERSOLID; |
|
|
|
// Try a few spots |
|
FOR_EACH_VEC_BACK( m_vecTrailingPos, i ) |
|
{ |
|
// Try positions starting with the current, and moving back in time a bit |
|
Vector vecStart = m_vecTrailingPos[i]; |
|
UTIL_TraceHull( vecStart, vecStart, VEC_HULL_MIN, VEC_HULL_MAX, nMask, &traceFilter, &traceHull ); |
|
|
|
if ( !traceHull.DidHit() ) |
|
{ |
|
// Place a teleport effect where they came from |
|
const Vector& vecOrigin = pThrower->GetAbsOrigin(); |
|
CPVSFilter pvsFilter( vecOrigin ); |
|
TE_TFParticleEffect( pvsFilter, 0.f, GetExplodeEffectParticle(), vecOrigin, vec3_angle ); |
|
|
|
// Move 'em! |
|
pThrower->Teleport( &vecStart, &pThrower->GetAbsAngles(), NULL ); |
|
|
|
// Do a zoom effect |
|
pThrower->SetFOV( pThrower, 0.f, 0.3f, 120.f ); |
|
|
|
// Screen flash |
|
color32 fadeColor = { 255, 255, 255, 100 }; |
|
UTIL_ScreenFade( pThrower, fadeColor, 0.25f, 0.4f, FFADE_IN ); |
|
|
|
if ( TFGameRules() ) |
|
{ |
|
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_TELEPORT, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED ); |
|
} |
|
} |
|
} |
|
|
|
BaseClass::Explode( pTrace, bitsDamageType ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_GravityGrenade::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
m_flStartTime = -1.f; |
|
m_flNextPulseEffectTime = -1.f; |
|
m_bHitWorld = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_GravityGrenade::TrapThink( void ) |
|
{ |
|
if ( gpGlobals->curtime > m_flStartTime + 5.f ) |
|
{ |
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
return; |
|
} |
|
|
|
PulseTrap(); |
|
|
|
SetContextThink( &CTFProjectile_GravityGrenade::TrapThink, gpGlobals->curtime + 0.15f, "TrapThink" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_GravityGrenade::OnHitWorld( void ) |
|
{ |
|
if ( !m_bHitWorld ) |
|
{ |
|
SetDetonateTimerLength( FLT_MAX ); |
|
|
|
m_bHitWorld = true; |
|
m_flStartTime = gpGlobals->curtime; |
|
|
|
AddSolidFlags( FSOLID_TRIGGER ); |
|
SetTouch( NULL ); |
|
|
|
SetContextThink( &CTFProjectile_GravityGrenade::TrapThink, gpGlobals->curtime, "TrapThink" ); |
|
} |
|
|
|
BaseClass::OnHitWorld(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_GravityGrenade::PulseTrap( void ) |
|
{ |
|
const int nMaxEnts = 32; |
|
|
|
Vector vecPos = GetAbsOrigin(); |
|
CBaseEntity *pObjects[ nMaxEnts ]; |
|
int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecPos, GetDamageRadius(), FL_CLIENT ); |
|
|
|
// Iterate through sphere's contents |
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
CBaseCombatCharacter *pEntity = pObjects[i]->MyCombatCharacterPointer(); |
|
if ( !pEntity ) |
|
continue; |
|
|
|
if ( InSameTeam( pEntity ) ) |
|
continue; |
|
|
|
if ( !FVisible( pEntity, MASK_OPAQUE ) ) |
|
continue; |
|
|
|
// Draw player toward us |
|
Vector vecSourcePos = pEntity->GetAbsOrigin(); |
|
Vector vecTargetPos = GetAbsOrigin(); |
|
Vector vecVelocity = ( vecTargetPos - vecSourcePos ) * 2.f; |
|
vecVelocity.z += 50.f; |
|
|
|
if ( pEntity->GetFlags() & FL_ONGROUND ) |
|
{ |
|
vecVelocity.z += 150.f; |
|
pEntity->SetGroundEntity( NULL ); |
|
pEntity->SetGroundChangeTime( gpGlobals->curtime + 0.5f ); |
|
} |
|
|
|
pEntity->Teleport( NULL, NULL, &vecVelocity ); |
|
} |
|
|
|
// NDebugOverlay::Sphere( vecPos, GetDamageRadius(), 0, 255, 0, false, 0.35f ); |
|
|
|
PulseEffects(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_GravityGrenade::PulseEffects( void ) |
|
{ |
|
if ( gpGlobals->curtime < m_flNextPulseEffectTime ) |
|
return; |
|
|
|
Vector vecOrigin = GetAbsOrigin(); |
|
CPVSFilter filter( vecOrigin ); |
|
TE_TFParticleEffect( filter, 0.f, "Explosion_ShockWave_01", vecOrigin, vec3_angle ); |
|
EmitSound( filter, entindex(), "Weapon_Upgrade.ExplosiveHeadshot" ); |
|
|
|
m_flNextPulseEffectTime = gpGlobals->curtime + 1.f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// THROWABLE KNIFE |
|
//----------------------------------------------------------------------------- |
|
Vector CTFProjectile_ThrowingKnife::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, float flCharge ) |
|
{ |
|
// Scale the projectile speed up to a maximum of 3000? |
|
float flSpeed = RemapVal( flCharge, 0, 1.0f, GetProjectileSpeed(), GetProjectileMaxSpeed() ); |
|
|
|
return ( ( flSpeed * vecForward ) + |
|
( flSpeed * 0.1 * vecUp ) ); |
|
} |
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowingKnife::OnHit( CBaseEntity *pOther ) |
|
{ |
|
if ( m_bHit ) |
|
return; |
|
|
|
CTFPlayer *pVictim = dynamic_cast< CTFPlayer*>( pOther ); |
|
CTFPlayer *pOwner = dynamic_cast< CTFPlayer*>( GetThrower() ); |
|
|
|
if ( pVictim && pOwner && !pVictim->InSameTeam( pOwner ) ) |
|
{ |
|
CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() ); |
|
trace_t trace; |
|
UTIL_TraceLine( GetAbsOrigin(), pVictim->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace ); |
|
|
|
Vector entForward; |
|
AngleVectors( pVictim->EyeAngles(), &entForward ); |
|
Vector toEnt = pVictim->GetAbsOrigin() - this->GetAbsOrigin(); |
|
toEnt.z = 0; |
|
entForward.z = 0; |
|
toEnt.NormalizeInPlace(); |
|
entForward.NormalizeInPlace(); |
|
|
|
// Is from behind? |
|
bool bIsFromBehind = DotProduct( toEnt, entForward ) > 0.7071f; |
|
|
|
// Apply Damage to Victim |
|
CTakeDamageInfo info; |
|
info.SetAttacker( GetThrower() ); |
|
info.SetInflictor( this ); |
|
info.SetWeapon( GetLauncher() ); |
|
info.SetDamageCustom( GetCustomDamageType() ); |
|
info.SetDamagePosition( GetAbsOrigin() ); |
|
|
|
int iDamageType = DMG_CLUB; |
|
if ( bIsFromBehind ) |
|
{ |
|
iDamageType |= DMG_CRITICAL; |
|
} |
|
info.SetDamageType( iDamageType ); |
|
info.SetDamage( bIsFromBehind ? GetBackHitDamage() : GetDamage() ); |
|
|
|
pVictim->DispatchTraceAttack( info, toEnt, &trace ); |
|
ApplyMultiDamage(); |
|
|
|
CreateStickyAttachmentToTarget( pOwner, pVictim, &trace ); |
|
|
|
Explode(); |
|
return; |
|
} |
|
else // its a dud, mark as hit and let it roll around |
|
{ |
|
m_bHit = true; |
|
} |
|
BaseClass::OnHit( pOther ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_ThrowingKnife::Detonate() |
|
{ |
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_SmokeGrenade::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
m_flStartTime = -1.f; |
|
m_bHitWorld = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_SmokeGrenade::OnHitWorld( void ) |
|
{ |
|
if ( !m_bHitWorld ) |
|
{ |
|
SetDetonateTimerLength( FLT_MAX ); |
|
// VPhysicsGetObject()->EnableMotion( false ); |
|
|
|
m_bHitWorld = true; |
|
m_flStartTime = gpGlobals->curtime; |
|
|
|
const char *pszSoundEffect = GetExplodeEffectSound(); |
|
if ( pszSoundEffect && pszSoundEffect[0] != '\0' ) |
|
{ |
|
EmitSound( pszSoundEffect ); |
|
} |
|
|
|
AddSolidFlags( FSOLID_TRIGGER ); |
|
SetTouch( NULL ); |
|
|
|
SetContextThink( &CTFProjectile_SmokeGrenade::SmokeThink, gpGlobals->curtime, "SmokeThink" ); |
|
} |
|
|
|
BaseClass::OnHitWorld(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_SmokeGrenade::SmokeThink( void ) |
|
{ |
|
if ( gpGlobals->curtime > m_flStartTime + 6.f ) |
|
{ |
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
return; |
|
} |
|
|
|
CPVSFilter filter( GetAbsOrigin() ); |
|
TE_TFParticleEffect( filter, 0.f, "grenade_smoke_cycle", GetAbsOrigin(), vec3_angle ); |
|
|
|
const int nMaxEnts = 32; |
|
|
|
Vector vecPos = GetAbsOrigin(); |
|
CBaseEntity *pObjects[ nMaxEnts ]; |
|
int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecPos, GetDamageRadius(), FL_CLIENT ); |
|
|
|
// Iterate through sphere's contents |
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
CBaseCombatCharacter *pEntity = pObjects[i]->MyCombatCharacterPointer(); |
|
if ( !pEntity ) |
|
continue; |
|
|
|
if ( !InSameTeam( pEntity ) ) |
|
continue; |
|
|
|
if ( !FVisible( pEntity, MASK_OPAQUE ) ) |
|
continue; |
|
|
|
if ( !pEntity->IsPlayer() ) |
|
continue; |
|
|
|
CTFPlayer *pTFPlayer = ToTFPlayer( pEntity ); |
|
if ( !pTFPlayer ) |
|
continue; |
|
|
|
pTFPlayer->m_Shared.AddCond( TF_COND_OBSCURED_SMOKE, 0.5f ); |
|
} |
|
|
|
// NDebugOverlay::Sphere( vecPos, GetDamageRadius(), 0, 255, 0, false, 0.35f ); |
|
|
|
SetContextThink( &CTFProjectile_SmokeGrenade::SmokeThink, gpGlobals->curtime + 0.3f, "SmokeThink" ); |
|
} |
|
#endif // GAME_DLL |
|
#endif // STAGING_ONLY
|
|
|