|
|
//========= 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
|