diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weapon_throwable.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf/tf_weapon_throwable.cpp')
| -rw-r--r-- | game/shared/tf/tf_weapon_throwable.cpp | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_throwable.cpp b/game/shared/tf/tf_weapon_throwable.cpp new file mode 100644 index 0000000..8e352ed --- /dev/null +++ b/game/shared/tf/tf_weapon_throwable.cpp @@ -0,0 +1,1234 @@ +//========= 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 |