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.
1155 lines
36 KiB
1155 lines
36 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "tf_weapon_jar.h" |
|
#include "decals.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
// Server specific. |
|
#else |
|
#include "soundent.h" |
|
#include "te_effect_dispatch.h" |
|
#include "tf_player.h" |
|
#include "func_break.h" |
|
#include "func_nogrenades.h" |
|
#include "Sprite.h" |
|
#include "tf_fx.h" |
|
#include "tf_team.h" |
|
#include "tf_gamestats.h" |
|
#include "tf_gamerules.h" |
|
#include "particle_parse.h" |
|
#include "bone_setup.h" |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Jar tables. |
|
// |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFJar, DT_TFWeaponJar ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFJar, DT_TFWeaponJar ) |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CTFJar ) |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_jar, CTFJar ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_jar ); |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFJarMilk, DT_TFWeaponJarMilk ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFJarMilk, DT_TFWeaponJarMilk ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_jar_milk, CTFJarMilk ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_jar_milk ); |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFCleaver, DT_TFWeaponCleaver ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFCleaver, DT_TFWeaponCleaver ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_cleaver, CTFCleaver ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_cleaver ); |
|
|
|
// Projectile tables. |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Jar, DT_TFProjectile_Jar ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_Jar, DT_TFProjectile_Jar ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_jar, CTFProjectile_Jar ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_jar ); |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_JarMilk, DT_TFProjectile_JarMilk ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_JarMilk, DT_TFProjectile_JarMilk ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_jar_milk, CTFProjectile_JarMilk ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_jar_milk ); |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Cleaver, DT_TFProjectile_Cleaver ) |
|
BEGIN_NETWORK_TABLE( CTFProjectile_Cleaver, DT_TFProjectile_Cleaver ) |
|
END_NETWORK_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_cleaver, CTFProjectile_Cleaver ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_cleaver ); |
|
|
|
#define TF_JAR_LAUNCH_SPEED 1000.f |
|
#define TF_CLEAVER_LAUNCH_SPEED 7000.f |
|
#define TF_WEAPON_PEEJAR_MODEL "models/weapons/c_models/urinejar.mdl" |
|
#define TF_WEAPON_FESTIVE_PEEJAR_MODEL "models/weapons/c_models/c_xms_urinejar.mdl" |
|
#ifdef STAGING_ONLY |
|
#define TF_WEAPON_MILKJAR_MODEL "models/workshop/weapons/c_models/c_madmilk/c_madmilk.mdl" |
|
#define TF_WEAPON_CLEAVER_MODEL "models/workshop_partner/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl" |
|
#else |
|
#define TF_WEAPON_MILKJAR_MODEL "models/weapons/c_models/c_madmilk/c_madmilk.mdl" |
|
#define TF_WEAPON_CLEAVER_MODEL "models/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl" |
|
#endif |
|
#define TF_WEAPON_PEEJAR_EXPLODE_SOUND "Jar.Explode" |
|
#define TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND "Cleaver.ImpactFlesh" |
|
#define TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND "Cleaver.ImpactWorld" |
|
|
|
#ifdef STAGING_ONLY |
|
#define TF_WEAPON_WATER_BALLOON_KILL_SOUND "Game.PenetrationKill" |
|
#define TF_WEAPON_WATER_BALLOON_HIT_SOUND "Weapon_waterbomb.hit" |
|
#define TF_WEAPON_WATER_BALLOON_SCORE_SOUND "Weapon_waterbomb.score" |
|
|
|
#define TF_BREAD_MODEL "models/props_gameplay/small_loaf.mdl" |
|
|
|
#define TF_WATERBALLOON_RADIUS 32 |
|
#define TF_WATERBALLOON_CHARGEDRADIUS 64 |
|
#define TF_WATERBALLOON_EXPLODE_SOUND "Weapon_waterbomb.explode" |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Jar functions. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFJar::CTFJar() |
|
{ |
|
} |
|
|
|
float CTFJar::GetProjectileSpeed( void ) |
|
{ |
|
return TF_JAR_LAUNCH_SPEED; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFJar::PrimaryAttack( void ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
int iJarCount = pPlayer->GetAmmoCount( m_iPrimaryAmmoType ); |
|
if ( iJarCount == 0 ) |
|
return; |
|
|
|
if ( ( pPlayer->GetWaterLevel() == WL_Eyes ) && !CanThrowUnderWater() ) |
|
return; |
|
|
|
BaseClass::PrimaryAttack(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CBaseEntity *CTFJar::FireJar( CTFPlayer *pPlayer ) |
|
{ |
|
StartEffectBarRegen(); |
|
SetContextThink( &CTFJar::TossJarThink, gpGlobals->curtime + 0.1f, "TOSS_JAR_THINK" ); |
|
|
|
return NULL; |
|
} |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Jar *CTFJar::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity, |
|
const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) |
|
{ |
|
return CTFProjectile_Jar::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo ); |
|
} |
|
#endif |
|
|
|
#ifdef GAME_DLL |
|
Vector CTFJar::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp ) |
|
{ |
|
return ( ( vecForward * GetProjectileSpeed() ) + ( vecUp * 200.0f ) + ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) + |
|
( random->RandomFloat( -10.0f, 10.0f ) * vecUp ) ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFJar::TossJarThink( void ) |
|
{ |
|
CTFPlayer *pPlayer = GetTFPlayerOwner(); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
PlayWeaponShootSound(); |
|
|
|
#ifdef GAME_DLL |
|
|
|
Vector vecForward, vecRight, vecUp; |
|
AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp ); |
|
|
|
float fRight = 8.f; |
|
if ( IsViewModelFlipped() ) |
|
{ |
|
fRight *= -1; |
|
} |
|
Vector vecSrc = pPlayer->Weapon_ShootPosition(); |
|
vecSrc += vecForward * 16.0f + vecRight * fRight + vecUp * -6.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; |
|
|
|
Vector vecVelocity = GetVelocityVector( vecForward, vecRight, vecUp ); |
|
|
|
CTFProjectile_Jar *pProjectile = CreateJarProjectile( trace.endpos, pPlayer->EyeAngles(), vecVelocity, |
|
GetAngularImpulse(), pPlayer, GetTFWpnData() ); |
|
|
|
if ( pProjectile ) |
|
{ |
|
pProjectile->SetCritical( IsCurrentAttackACrit() ); |
|
pProjectile->SetLauncher( this ); |
|
} |
|
|
|
if ( ShouldSpeakWhenFiring() ) |
|
{ |
|
pPlayer->SpeakWeaponFire( MP_CONCEPT_JARATE_LAUNCH ); |
|
} |
|
|
|
#endif |
|
} |
|
//----------------------------------------------------------------------------- |
|
void CTFJar::GetProjectileEntityName( CAttribute_String *attrProjectileEntityName ) |
|
{ |
|
static CSchemaAttributeDefHandle pAttrDef_ProjectileEntityName( "projectile entity name" ); |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pAttrDef_ProjectileEntityName && pItem ) |
|
{ |
|
//CAttribute_String attrProjectileEntityName; |
|
pItem->FindAttribute( pAttrDef_ProjectileEntityName, attrProjectileEntityName ); |
|
} |
|
} |
|
|
|
#ifdef GAME_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Jar::CTFProjectile_Jar() |
|
{ |
|
m_vCollisionVelocity = Vector( 0,0,0 ); |
|
m_iProjectileType = TF_PROJECTILE_JAR; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::Precache() |
|
{ |
|
PrecacheModel( TF_WEAPON_PEEJAR_MODEL ); |
|
PrecacheModel( TF_WEAPON_FESTIVE_PEEJAR_MODEL ); |
|
PrecacheModel( "models/weapons/c_models/c_breadmonster/c_breadmonster.mdl" ); |
|
|
|
PrecacheScriptSound( TF_WEAPON_PEEJAR_EXPLODE_SOUND ); |
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::SetCustomPipebombModel() |
|
{ |
|
// Check for Model Override |
|
int iProjectile = 0; |
|
CTFPlayer *pThrower = ToTFPlayer( GetThrower() ); |
|
if ( pThrower && pThrower->GetActiveWeapon() ) |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pThrower->GetActiveWeapon(), iProjectile, override_projectile_type ); |
|
switch ( iProjectile ) |
|
{ |
|
case TF_PROJECTILE_FESTIVE_JAR : |
|
m_iProjectileType = iProjectile; |
|
SetModel( TF_WEAPON_FESTIVE_PEEJAR_MODEL ); |
|
return; |
|
case TF_PROJECTILE_BREADMONSTER_JARATE: |
|
case TF_PROJECTILE_BREADMONSTER_MADMILK: |
|
m_iProjectileType = iProjectile; |
|
SetModel( "models/weapons/c_models/c_breadmonster/c_breadmonster.mdl" ); |
|
return; |
|
} |
|
} |
|
|
|
SetModel( TF_WEAPON_PEEJAR_MODEL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Jar* CTFProjectile_Jar::Create( const Vector &position, const QAngle &angles, |
|
const Vector &velocity, const AngularImpulse &angVelocity, |
|
CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) |
|
{ |
|
CTFProjectile_Jar *pGrenade = static_cast<CTFProjectile_Jar*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar", position, angles, pOwner ) ); |
|
if ( pGrenade ) |
|
{ |
|
// Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly. |
|
pGrenade->SetPipebombMode(); |
|
DispatchSpawn( pGrenade ); |
|
|
|
pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo ); |
|
|
|
#ifdef _X360 |
|
if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE ) |
|
{ |
|
pGrenade->SetDamage( TF_WEAPON_GRENADE_XBOX_DAMAGE ); |
|
} |
|
#endif |
|
pGrenade->m_flFullDamage = 0; |
|
|
|
pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); |
|
} |
|
|
|
return pGrenade; |
|
} |
|
|
|
extern void ExtinguishPlayer( CEconEntity *pExtinguisher, CTFPlayer *pOwner, CTFPlayer *pTarget, const char *pExtinguisherName ); |
|
|
|
void JarExplode( int iEntIndex, CTFPlayer *pAttacker, CBaseEntity *pOriginalWeapon, CBaseEntity *pWeapon, const Vector& vContactPoint, int iTeam, float flRadius, ETFCond cond, float flDuration, const char *pszImpactEffect ) |
|
{ |
|
// Splash! |
|
CPVSFilter particleFilter( vContactPoint ); |
|
TE_TFParticleEffect( particleFilter, 0.0, pszImpactEffect, vContactPoint, vec3_angle ); |
|
|
|
// Explosion effect. |
|
CBroadcastRecipientFilter soundFilter; |
|
Vector vecOrigin = vContactPoint; |
|
CBaseEntity::EmitSound( soundFilter, iEntIndex, TF_WEAPON_PEEJAR_EXPLODE_SOUND, &vecOrigin ); |
|
|
|
// Treat this trace exactly like radius damage |
|
CTraceFilterIgnorePlayers traceFilter( pAttacker, COLLISION_GROUP_PROJECTILE ); |
|
|
|
// Splash pee on everyone nearby. |
|
CBaseEntity *pListOfEntities[32]; |
|
int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, vContactPoint, flRadius, FL_CLIENT ); |
|
for ( int i = 0; i < iEntities; ++i ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( pListOfEntities[i] ); |
|
|
|
if ( !pPlayer || !pPlayer->IsAlive() ) |
|
continue; |
|
|
|
// Do a quick trace to see if there's any geometry in the way. |
|
// Pee isn't stopped by other entities. Splishy splashy. |
|
trace_t trace; |
|
UTIL_TraceLine( vContactPoint, pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace ); |
|
if ( trace.DidHitWorld() ) |
|
continue; |
|
|
|
// Drench the target. |
|
if ( pPlayer->GetTeamNumber() != iTeam ) |
|
{ |
|
if ( pPlayer->m_Shared.IsInvulnerable() ) |
|
continue; |
|
|
|
if ( pPlayer->m_Shared.InCond( TF_COND_PHASE ) || pPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) ) |
|
continue; |
|
|
|
if ( !pPlayer->CanGetWet() ) |
|
continue; |
|
|
|
pPlayer->m_Shared.AddCond( cond, flDuration, pAttacker ); |
|
pPlayer->m_Shared.SetPeeAttacker( pAttacker ); |
|
pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_JARATE_HIT ); |
|
|
|
if ( pAttacker ) |
|
{ |
|
if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.GetPercentInvisible() == 1.0f ) |
|
{ |
|
pAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_JARATE_REVEAL_SPY ); |
|
} |
|
|
|
float flStun = 1.0f; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAttacker, flStun, applies_snare_effect ); |
|
if ( flStun != 1.0f ) |
|
{ |
|
pPlayer->m_Shared.StunPlayer( flDuration, flStun, TF_STUN_MOVEMENT, pAttacker ); |
|
} |
|
|
|
// Stats tracking? |
|
if ( cond == TF_COND_URINE || cond == TF_COND_MAD_MILK ) |
|
{ |
|
if ( TFGameRules() && TFGameRules()->IsPVEModeActive() ) |
|
{ |
|
// These if statements are intentionally split to avoid falling through to the normal kKillEaterEvent_PeeVictims event if we're in |
|
// IsPVEModeActive() but not a robot, or don't have the stun. |
|
if ( pPlayer->GetTeamNumber() == TF_TEAM_PVE_INVADERS && flStun != 1.0f ) |
|
{ |
|
EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, kKillEaterEvent_RobotsSlowed ); |
|
} |
|
} |
|
else |
|
{ |
|
EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, kKillEaterEvent_PeeVictims ); |
|
} |
|
} |
|
|
|
// Tell the clients involved in the jarate |
|
CRecipientFilter involved_filter; |
|
involved_filter.AddRecipient( pPlayer ); |
|
involved_filter.AddRecipient( pAttacker ); |
|
UserMessageBegin( involved_filter, "PlayerJarated" ); |
|
WRITE_BYTE( pAttacker->entindex() ); |
|
WRITE_BYTE( pPlayer->entindex() ); |
|
MessageEnd(); |
|
|
|
const char *pszEvent = NULL; |
|
switch( cond ) |
|
{ |
|
case TF_COND_URINE: |
|
pszEvent = "jarate_attack"; |
|
break; |
|
case TF_COND_MAD_MILK: |
|
pszEvent = "milk_attack"; |
|
break; |
|
} |
|
|
|
if ( pszEvent && pszEvent[0] ) |
|
{ |
|
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"%s\" against \"%s<%i><%s><%s>\" with \"%s\" (attacker_position \"%d %d %d\") (victim_position \"%d %d %d\")\n", |
|
pAttacker->GetPlayerName(), |
|
pAttacker->GetUserID(), |
|
pAttacker->GetNetworkIDString(), |
|
pAttacker->GetTeam()->GetName(), |
|
pszEvent, |
|
pPlayer->GetPlayerName(), |
|
pPlayer->GetUserID(), |
|
pPlayer->GetNetworkIDString(), |
|
pPlayer->GetTeam()->GetName(), |
|
"tf_weapon_jar", |
|
(int)pAttacker->GetAbsOrigin().x, |
|
(int)pAttacker->GetAbsOrigin().y, |
|
(int)pAttacker->GetAbsOrigin().z, |
|
(int)pPlayer->GetAbsOrigin().x, |
|
(int)pPlayer->GetAbsOrigin().y, |
|
(int)pPlayer->GetAbsOrigin().z ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if ( pAttacker && pPlayer->m_Shared.InCond( TF_COND_BURNING ) ) |
|
{ |
|
ExtinguishPlayer( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, "tf_weapon_jar" ); |
|
|
|
// Return some percentage of the jar to the thrown weapon if extinguishing an ally |
|
auto pLauncher = dynamic_cast< CTFWeaponBase* >( pOriginalWeapon ); |
|
if ( pLauncher && pAttacker != pPlayer && pLauncher->HasEffectBarRegeneration() ) |
|
{ |
|
float fCooldown = 1.0f; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, fCooldown, extinguish_reduces_cooldown ); |
|
fCooldown = 1.0f - fCooldown; |
|
if ( fCooldown > 0 ) |
|
{ |
|
if ( pLauncher->GetEffectBarProgress() < fCooldown ) |
|
{ |
|
float fDuration = pLauncher->GetEffectBarRechargeTime(); |
|
float fIncrement = fDuration * fCooldown; |
|
pLauncher->DecrementBarRegenTime( fIncrement ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::Explode( trace_t *pTrace, int bitsDamageType ) |
|
{ |
|
SetModelName( NULL_STRING );//invisible |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
|
|
m_takedamage = DAMAGE_NO; |
|
|
|
// Pull out of the wall a bit. |
|
if ( pTrace->fraction != 1.0 ) |
|
{ |
|
SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) ); |
|
} |
|
|
|
CTFPlayer *pThrower = ToTFPlayer( GetThrower() ); |
|
JarExplode( entindex(), pThrower, GetOriginalLauncher(), GetLauncher(), GetAbsOrigin(), GetTeamNumber(), GetDamageRadius(), GetEffectCondition(), 10.f, GetImpactEffect() ); |
|
|
|
// Debug radius draw. |
|
//DrawRadius( GetDamageRadius() ); |
|
|
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::PipebombTouch( CBaseEntity *pOther ) |
|
{ |
|
if ( pOther == GetThrower() ) |
|
return; |
|
|
|
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) ) |
|
return; |
|
|
|
if ( !pOther->IsWorld() && !pOther->IsPlayer() ) |
|
return; |
|
|
|
// Don't collide with teammate if we're still in the grace period. |
|
if ( pOther->IsPlayer() && pOther->GetTeamNumber() == GetTeamNumber() && !CanCollideWithTeammates() ) |
|
{ |
|
// Exception to this rule - if we're a jar or milk, and our potential victim is on fire, then allow collision after all. |
|
// If we're a jar or milk, then still allow collision if our potential victim is on fire. |
|
if (m_iProjectileType == TF_PROJECTILE_JAR || m_iProjectileType == TF_PROJECTILE_JAR_MILK) |
|
{ |
|
auto victim = ToTFPlayer(pOther); |
|
if (!victim->m_Shared.InCond(TF_COND_BURNING)) |
|
{ |
|
return; |
|
} |
|
} |
|
else |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
// Handle hitting skybox (disappear). |
|
trace_t pTrace; |
|
Vector velDir = GetAbsVelocity(); |
|
if ( velDir.IsZero() && pOther && pOther->IsPlayer() ) |
|
{ |
|
velDir = pOther->WorldSpaceCenter() - GetAbsOrigin(); |
|
} |
|
|
|
VectorNormalize( velDir ); |
|
Vector vecSpot = GetAbsOrigin() - velDir * 32; |
|
UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace ); |
|
|
|
if ( pTrace.fraction < 1.0 && pTrace.surface.flags & SURF_SKY ) |
|
{ |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
|
|
// If we already touched a surface then we're not exploding on contact anymore. |
|
if ( m_bTouched == true ) |
|
return; |
|
|
|
OnHit( pOther ); |
|
if ( m_iProjectileType == TF_PROJECTILE_BREADMONSTER_JARATE || m_iProjectileType == TF_PROJECTILE_BREADMONSTER_MADMILK ) |
|
{ |
|
OnBreadMonsterHit( pOther, &pTrace ); |
|
} |
|
|
|
if ( ExplodesOnHit() ) |
|
{ |
|
// Save this entity as enemy, they will take 100% damage if applicable |
|
m_hEnemy = pOther; |
|
Explode( &pTrace, GetDamageType() ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::OnBreadMonsterHit( CBaseEntity *pOther, trace_t *pTrace ) |
|
{ |
|
if ( m_iProjectileType != TF_PROJECTILE_BREADMONSTER_JARATE && m_iProjectileType != TF_PROJECTILE_BREADMONSTER_MADMILK ) |
|
return; |
|
|
|
CTFPlayer *pVictim = ToTFPlayer( pOther ); |
|
if ( !pVictim || pVictim->GetTeamNumber() == GetTeamNumber() ) |
|
return; |
|
|
|
// This is a player on the other team, attach a breadmonster |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetThrower() ); |
|
|
|
// Attach Breadmonster to Victim |
|
CreateStickyAttachmentToTarget( pOwner, pVictim, pTrace ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) |
|
{ |
|
BaseClass::VPhysicsCollision( index, pEvent ); |
|
|
|
int otherIndex = !index; |
|
CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex]; |
|
|
|
if ( !pHitEntity ) |
|
return; |
|
|
|
if ( pHitEntity->IsWorld() ) |
|
{ |
|
OnHitWorld(); |
|
} |
|
|
|
// Break if we hit the world. |
|
bool bIsDynamicProp = ( NULL != dynamic_cast<CDynamicProp *>( pHitEntity ) ); |
|
if ( ExplodesOnHit() && pHitEntity && ( pHitEntity->IsWorld() || bIsDynamicProp ) ) |
|
{ |
|
// Explode immediately next frame. (Can't explode in the collision callback.) |
|
m_vCollisionVelocity = pEvent->preVelocity[index]; |
|
SetContextThink( &CTFProjectile_Jar::VPhysicsCollisionThink, gpGlobals->curtime, "JarCollisionThink" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles exploding after a vphysics collision has happened. |
|
// This prevents changing collision properties during the vphysics callback itself. |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::VPhysicsCollisionThink( void ) |
|
{ |
|
if ( !ExplodesOnHit() ) |
|
return; |
|
|
|
trace_t pTrace; |
|
Vector velDir = m_vCollisionVelocity; |
|
VectorNormalize( velDir ); |
|
Vector vecSpot = GetAbsOrigin() - velDir * 16; |
|
UTIL_TraceLine( vecSpot, vecSpot + velDir * 32, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace ); |
|
|
|
Explode( &pTrace, GetDamageType() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool CTFProjectile_Jar::PositionArrowOnBone( mstudiobbox_t *pBox, CBaseAnimating *pOtherAnim ) |
|
{ |
|
CStudioHdr *pStudioHdr = pOtherAnim->GetModelPtr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pOtherAnim->GetHitboxSet() ); |
|
if ( !set ) |
|
return false; |
|
if ( !set->numhitboxes ) // Target must have hit boxes. |
|
return false; |
|
|
|
if ( pBox->bone < 0 || pBox->bone >= pStudioHdr->numbones() ) // Bone index must be valid. |
|
return false; |
|
|
|
CBoneCache *pCache = pOtherAnim->GetBoneCache(); |
|
if ( !pCache ) |
|
return false; |
|
|
|
matrix3x4_t *bone_matrix = pCache->GetCachedBone( pBox->bone ); |
|
if ( !bone_matrix ) |
|
return false; |
|
|
|
Vector vecBoxAbsMins, vecBoxAbsMaxs; |
|
TransformAABB( *bone_matrix, pBox->bbmin, pBox->bbmax, vecBoxAbsMins, vecBoxAbsMaxs ); |
|
|
|
// Adjust the arrow so it isn't exactly in the center of the box. |
|
Vector position; |
|
Vector vecDelta = vecBoxAbsMaxs - vecBoxAbsMins; |
|
float frand = (float)rand() / VALVE_RAND_MAX; |
|
position.x = vecBoxAbsMins.x + vecDelta.x*0.6f - vecDelta.x*frand*0.2f; |
|
frand = (float)rand() / VALVE_RAND_MAX; |
|
position.y = vecBoxAbsMins.y + vecDelta.y*0.6f - vecDelta.y*frand*0.2f; |
|
frand = (float)rand() / VALVE_RAND_MAX; |
|
position.z = vecBoxAbsMins.z + vecDelta.z*0.6f - vecDelta.z*frand*0.2f; |
|
SetAbsOrigin( position ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::GetBoneAttachmentInfo( mstudiobbox_t *pBox, CBaseAnimating *pOtherAnim, Vector &bonePosition, QAngle &boneAngles, int &boneIndexAttached, int &physicsBoneIndex ) |
|
{ |
|
// Find a bone to stick to. |
|
matrix3x4_t arrowWorldSpace; |
|
MatrixCopy( EntityToWorldTransform(), arrowWorldSpace ); |
|
|
|
// Get the bone info so we can follow the bone. |
|
boneIndexAttached = pBox->bone; |
|
physicsBoneIndex = pOtherAnim->GetPhysicsBone( boneIndexAttached ); |
|
matrix3x4_t boneToWorld; |
|
pOtherAnim->GetBoneTransform( boneIndexAttached, boneToWorld ); |
|
|
|
Vector attachedBonePos; |
|
QAngle attachedBoneAngles; |
|
pOtherAnim->GetBonePosition( boneIndexAttached, attachedBonePos, attachedBoneAngles ); |
|
|
|
// Transform my current position/orientation into the hit bone's space. |
|
matrix3x4_t worldToBone, localMatrix; |
|
MatrixInvert( boneToWorld, worldToBone ); |
|
ConcatTransforms( worldToBone, arrowWorldSpace, localMatrix ); |
|
MatrixAngles( localMatrix, boneAngles, bonePosition ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Jar::CreateStickyAttachmentToTarget( CTFPlayer *pOwner, CTFPlayer *pVictim, trace_t *trace ) |
|
{ |
|
// Dont stick to the sky! |
|
if ( trace->surface.flags & SURF_SKY ) |
|
{ |
|
return; |
|
} |
|
|
|
// If I hit a player, remove the jar and replace with the face eater version |
|
CStudioHdr *pStudioHdr = NULL; |
|
mstudiohitboxset_t *set = NULL; |
|
pStudioHdr = pVictim->GetModelPtr(); |
|
if ( pStudioHdr ) |
|
{ |
|
set = pStudioHdr->pHitboxSet( pVictim->GetHitboxSet() ); |
|
} |
|
|
|
// Look for nearest hitbox |
|
mstudiobbox_t *closest_box = NULL; |
|
if ( trace->m_pEnt && trace->m_pEnt->GetTeamNumber() != GetTeamNumber() ) |
|
{ |
|
closest_box = set->pHitbox( trace->hitbox ); |
|
} |
|
|
|
if ( closest_box ) |
|
{ |
|
if ( !PositionArrowOnBone( closest_box, pVictim ) ) |
|
return; |
|
|
|
// See if we're supposed to stick in the target. |
|
Vector bonePosition = vec3_origin; |
|
QAngle boneAngles = QAngle( 0, 0, 0 ); |
|
int boneIndexAttached = -1; |
|
int physicsBoneIndex = -1; |
|
|
|
GetBoneAttachmentInfo( closest_box, pVictim, bonePosition, boneAngles, boneIndexAttached, physicsBoneIndex ); |
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "arrow_impact" ); |
|
if ( event ) |
|
{ |
|
event->SetInt( "attachedEntity", pVictim->entindex() ); |
|
event->SetInt( "shooter", pOwner->entindex() ); |
|
event->SetInt( "attachedEntity", pVictim->entindex() ); |
|
event->SetInt( "boneIndexAttached", boneIndexAttached ); |
|
event->SetFloat( "bonePositionX", bonePosition.x ); |
|
event->SetFloat( "bonePositionY", bonePosition.y ); |
|
event->SetFloat( "bonePositionZ", bonePosition.z ); |
|
event->SetFloat( "boneAnglesX", boneAngles.x ); |
|
event->SetFloat( "boneAnglesY", boneAngles.y ); |
|
event->SetFloat( "boneAnglesZ", boneAngles.z ); |
|
event->SetInt( "projectileType", GetProjectileType() ); |
|
event->SetBool( "isCrit", IsCritical() ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
} |
|
|
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CTFProjectile_Jar::GetTrailParticleName( void ) |
|
{ |
|
if ( GetTeamNumber() == TF_TEAM_BLUE ) |
|
{ |
|
return "peejar_trail_blu"; |
|
} |
|
else |
|
{ |
|
return "peejar_trail_red"; |
|
} |
|
} |
|
|
|
#endif |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Jar *CTFJarMilk::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity, |
|
const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) |
|
{ |
|
return CTFProjectile_JarMilk::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_JarMilk::Precache() |
|
{ |
|
PrecacheModel( TF_WEAPON_MILKJAR_MODEL ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
#ifdef GAME_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_JarMilk* CTFProjectile_JarMilk::Create( const Vector &position, const QAngle &angles, |
|
const Vector &velocity, const AngularImpulse &angVelocity, |
|
CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) |
|
{ |
|
CTFProjectile_JarMilk *pGrenade = static_cast<CTFProjectile_JarMilk*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar_milk", position, angles, pOwner ) ); |
|
if ( pGrenade ) |
|
{ |
|
// Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly. |
|
pGrenade->SetPipebombMode(); |
|
DispatchSpawn( pGrenade ); |
|
|
|
pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo ); |
|
|
|
#ifdef _X360 |
|
if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE ) |
|
{ |
|
pGrenade->SetDamage( TF_WEAPON_GRENADE_XBOX_DAMAGE ); |
|
} |
|
#endif |
|
pGrenade->m_flFullDamage = 0; |
|
|
|
pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); |
|
} |
|
|
|
return pGrenade; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_JarMilk::SetCustomPipebombModel() |
|
{ |
|
// Check for Model Override |
|
int iProjectile = 0; |
|
CTFPlayer *pThrower = ToTFPlayer( GetThrower() ); |
|
if ( pThrower && pThrower->GetActiveWeapon() ) |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pThrower->GetActiveWeapon(), iProjectile, override_projectile_type ); |
|
switch ( iProjectile ) |
|
{ |
|
case TF_PROJECTILE_BREADMONSTER_JARATE: |
|
case TF_PROJECTILE_BREADMONSTER_MADMILK: |
|
m_iProjectileType = iProjectile; |
|
SetModel( "models/weapons/c_models/c_breadmonster/c_breadmonster_milk.mdl" ); |
|
return; |
|
} |
|
} |
|
|
|
SetModel( TF_WEAPON_MILKJAR_MODEL ); |
|
} |
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char* CTFJarMilk::ModifyEventParticles( const char* token ) |
|
{ |
|
if ( FStrEq( token, "energydrink_splash") ) |
|
{ |
|
CEconItemView *pItem = m_AttributeManager.GetItem(); |
|
int iSystems = pItem->GetStaticData()->GetNumAttachedParticles( GetTeamNumber() ); |
|
for ( int i = 0; i < iSystems; i++ ) |
|
{ |
|
attachedparticlesystem_t *pSystem = pItem->GetStaticData()->GetAttachedParticleData( GetTeamNumber(),i ); |
|
if ( pSystem->iCustomType == 1 ) |
|
{ |
|
return pSystem->pszSystemName; |
|
} |
|
} |
|
} |
|
|
|
return BaseClass::ModifyEventParticles( token ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFJarMilk::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); |
|
if ( pOwner && pOwner->IsLocalPlayer() ) |
|
{ |
|
C_BaseEntity *pParticleEnt = pOwner->GetViewModel(0); |
|
if ( pParticleEnt ) |
|
{ |
|
pOwner->StopViewModelParticles( pParticleEnt ); |
|
} |
|
} |
|
|
|
return BaseClass::Holster( pSwitchingTo ); |
|
} |
|
|
|
#endif |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
Vector CTFCleaver::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp ) |
|
{ |
|
Vector vecVelocity; |
|
|
|
// Calculate the initial impulse on the item. |
|
vecVelocity = Vector( 0.0f, 0.0f, 0.0f ); |
|
vecVelocity += vecForward * 10; |
|
vecVelocity += vecUp * 1; |
|
VectorNormalize( vecVelocity ); |
|
vecVelocity *= 3000; |
|
|
|
return vecVelocity; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Jar *CTFCleaver::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity, |
|
const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) |
|
{ |
|
return CTFProjectile_Cleaver::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo, GetSkin() ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFCleaver::GetProjectileSpeed( void ) |
|
{ |
|
return TF_CLEAVER_LAUNCH_SPEED; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CTFCleaver::SecondaryAttack( void ) |
|
{ |
|
PrimaryAttack(); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char* CTFCleaver::ModifyEventParticles( const char* token ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFCleaver::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
return BaseClass::Holster( pSwitchingTo ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Cleaver::Precache() |
|
{ |
|
PrecacheModel( TF_WEAPON_CLEAVER_MODEL ); |
|
PrecacheScriptSound( TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND ); |
|
PrecacheScriptSound( TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Cleaver::SetCustomPipebombModel() |
|
{ |
|
SetModel( TF_WEAPON_CLEAVER_MODEL ); |
|
} |
|
|
|
CTFProjectile_Cleaver::CTFProjectile_Cleaver() |
|
{ |
|
#ifdef GAME_DLL |
|
m_bHitPlayer = false; |
|
m_bSoundPlayed = false; |
|
#endif |
|
} |
|
|
|
#ifdef GAME_DLL |
|
#define FLIGHT_TIME_TO_MAX_DMG 1.f |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Cleaver::OnHit( CBaseEntity *pOther ) |
|
{ |
|
SetModelName( NULL_STRING );//invisible |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetThrower() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
if ( !pOther || !pOther->IsPlayer() ) |
|
return; |
|
|
|
CTFPlayer *pPlayer = ToTFPlayer( pOther ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// Can't bleed an invul player. |
|
if ( pPlayer->m_Shared.IsInvulnerable() || pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) ) |
|
return; |
|
|
|
if ( pPlayer->GetTeamNumber() == pOwner->GetTeamNumber() ) |
|
return; |
|
|
|
if ( TFGameRules() && TFGameRules()->IsTruceActive() && pOwner->IsTruceValidForEnt() ) |
|
return; |
|
|
|
bool bIsCriticalHit = IsCritical(); |
|
bool bIsMiniCrit = false; |
|
float flBleedTime = 5.0f; |
|
|
|
float flLifeTime = gpGlobals->curtime - m_flCreationTime; |
|
if ( flLifeTime >= FLIGHT_TIME_TO_MAX_DMG ) |
|
{ |
|
bIsMiniCrit = true; |
|
} |
|
|
|
// just do the bleed effect directly since the bleed |
|
// attribute comes from the inflictor, which is the cleaver. |
|
pPlayer->m_Shared.MakeBleed( pOwner, (CTFCleaver *)GetLauncher(), flBleedTime ); |
|
|
|
// Give 'em a love tap. |
|
const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); |
|
trace_t *pNewTrace = const_cast<trace_t*>( pTrace ); |
|
|
|
CBaseEntity *pInflictor = GetLauncher(); |
|
CTakeDamageInfo info; |
|
info.SetAttacker( pOwner ); |
|
info.SetInflictor( pInflictor ); |
|
info.SetWeapon( pInflictor ); |
|
info.SetDamage( GetDamage() ); |
|
info.SetDamageCustom( bIsMiniCrit ? TF_DMG_CUSTOM_CLEAVER_CRIT : TF_DMG_CUSTOM_CLEAVER ); |
|
info.SetDamagePosition( GetAbsOrigin() ); |
|
int iDamageType = GetDamageType(); |
|
if ( bIsCriticalHit ) |
|
{ |
|
iDamageType |= DMG_CRITICAL; |
|
} |
|
info.SetDamageType( iDamageType ); |
|
|
|
// Hurt 'em. |
|
Vector dir; |
|
AngleVectors( GetAbsAngles(), &dir ); |
|
pPlayer->DispatchTraceAttack( info, dir, pNewTrace ); |
|
ApplyMultiDamage(); |
|
|
|
// sound effects |
|
EmitSound_t params; |
|
params.m_flSoundTime = 0; |
|
params.m_pflSoundDuration = 0; |
|
params.m_pSoundName = TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND; |
|
|
|
CPASFilter filter( GetAbsOrigin() ); |
|
filter.RemoveRecipient( pOwner ); |
|
EmitSound( filter, entindex(), params ); |
|
|
|
CSingleUserRecipientFilter attackerFilter( pOwner ); |
|
EmitSound( attackerFilter, pOwner->entindex(), params ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
SetAbsVelocity( vec3_origin ); |
|
|
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime + 2, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
|
|
m_bHitPlayer = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Cleaver::Explode( trace_t *pTrace, int bitsDamageType ) |
|
{ |
|
if ( !m_bHitPlayer ) |
|
{ |
|
if ( !m_bSoundPlayed ) |
|
{ |
|
EmitSound( TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND ); |
|
m_bSoundPlayed = true; |
|
} |
|
|
|
SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime + 2, "RemoveThink" ); |
|
SetTouch( NULL ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Cleaver::Detonate( void ) |
|
{ |
|
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() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Cleaver* CTFProjectile_Cleaver::Create( const Vector &position, const QAngle &angles, |
|
const Vector &velocity, const AngularImpulse &angVelocity, |
|
CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo, int nSkin ) |
|
{ |
|
CTFProjectile_Cleaver *pGrenade = static_cast<CTFProjectile_Cleaver*>( CBaseEntity::CreateNoSpawn( "tf_projectile_cleaver", position, angles, pOwner ) ); |
|
if ( pGrenade ) |
|
{ |
|
// Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly. |
|
pGrenade->SetPipebombMode(); |
|
DispatchSpawn( pGrenade ); |
|
|
|
pGrenade->m_nSkin = nSkin; |
|
|
|
pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo ); |
|
|
|
pGrenade->m_flFullDamage = 0; |
|
|
|
pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); |
|
} |
|
|
|
return pGrenade; |
|
} |
|
|
|
#else |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CTFProjectile_Cleaver::GetTrailParticleName( void ) |
|
{ |
|
if ( GetTeamNumber() == TF_TEAM_BLUE ) |
|
{ |
|
return "peejar_trail_blu_glow"; |
|
} |
|
else |
|
{ |
|
return "peejar_trail_red_glow"; |
|
} |
|
} |
|
|
|
#endif // GAME_DLL
|