summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_projectile_energy_ring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_projectile_energy_ring.cpp')
-rw-r--r--game/shared/tf/tf_projectile_energy_ring.cpp509
1 files changed, 509 insertions, 0 deletions
diff --git a/game/shared/tf/tf_projectile_energy_ring.cpp b/game/shared/tf/tf_projectile_energy_ring.cpp
new file mode 100644
index 0000000..7b9154e
--- /dev/null
+++ b/game/shared/tf/tf_projectile_energy_ring.cpp
@@ -0,0 +1,509 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// TF Energy Ring
+//
+//=============================================================================
+#include "cbase.h"
+#include "tf_projectile_energy_ring.h"
+#include "tf_weapon_raygun.h"
+
+#ifdef CLIENT_DLL
+#include "c_basetempentity.h"
+#include "c_te_legacytempents.h"
+#include "c_te_effect_dispatch.h"
+#include "input.h"
+#include "c_tf_player.h"
+#include "cliententitylist.h"
+#endif
+
+#ifdef GAME_DLL
+#include "tf_player.h"
+#include "tf_player_shared.h"
+#include "particle_parse.h"
+#include "tf_pumpkin_bomb.h"
+#include "halloween/merasmus/merasmus_trick_or_treat_prop.h"
+#include "tf_robot_destruction_robot.h"
+#endif
+
+#define ENERGY_RING_DISPATCH_EFFECT "ClientProjectile_EnergyRing"
+#define ENERGY_RING_DISPATCH_EFFECT_POMSON "ClientProjectile_EnergyRingPomson"
+
+const char* g_pszEnergyRingModel ( "models/weapons/w_models/w_drg_ball.mdl" );
+
+const char* g_pszPomsonImpactFleshSound ( "Weapon_Pomson.ProjectileImpactWorld" );
+const char* g_pszPomsonImpactWorldSound ( "Weapon_Pomson.ProjectileImpactFlesh" );
+const char* g_pszPomsonTrailParticle ( "drg_pomson_projectile" );
+const char* g_pszPomsonTrailParticleCrit ( "drg_pomson_projectile_crit" );
+
+const char* g_pszBisonImpactFleshSound ( "Weapon_Bison.ProjectileImpactWorld" );
+const char* g_pszBisonImpactWorldSound ( "Weapon_Bison.ProjectileImpactFlesh" );
+const char* g_pszBisonTrailParticle ( "drg_bison_projectile" );
+const char* g_pszBisonTrailParticleCrit ( "drg_bison_projectile_crit" );
+
+const char* g_pszEnergyProjectileImpactParticle ( "drg_pomson_impact" );
+//=============================================================================
+//
+// TF Energy Ring Projectile functions
+//
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_EnergyRing, DT_TFProjectile_EnergyRing )
+
+BEGIN_NETWORK_TABLE( CTFProjectile_EnergyRing, DT_TFProjectile_EnergyRing )
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+LINK_ENTITY_TO_CLASS( tf_projectile_energy_ring, CTFProjectile_EnergyRing );
+PRECACHE_WEAPON_REGISTER( tf_projectile_energy_ring );
+
+short g_sModelIndexRing;
+void PrecacheRing(void *pUser)
+{
+ g_sModelIndexRing = modelinfo->GetModelIndex( g_pszEnergyRingModel );
+}
+PRECACHE_REGISTER_FN(PrecacheRing);
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFProjectile_EnergyRing::CTFProjectile_EnergyRing()
+{
+ m_vecPrevPos = vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFProjectile_EnergyRing::GetProjectileModelName( void )
+{
+ return g_pszEnergyRingModel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFProjectile_EnergyRing::GetGravity( void )
+{
+ return 0.f;
+}
+
+float CTFProjectile_EnergyRing::GetInitialVelocity( void )
+{
+ return ShouldPenetrate() ? 840.f : 1200.f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFProjectile_EnergyRing *CTFProjectile_EnergyRing::Create( CTFWeaponBaseGun *pLauncher, const Vector &vecOrigin, const QAngle& vecAngles, float fSpeed, float fGravity,
+ CBaseEntity *pOwner, CBaseEntity *pScorer, Vector vColor1, Vector vColor2, bool bCritical )
+{
+ CTFProjectile_EnergyRing *pRing = NULL;
+
+#ifdef GAME_DLL
+ Vector vecForward, vecRight, vecUp;
+ AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
+
+ pRing = static_cast<CTFProjectile_EnergyRing*>( CBaseEntity::Create( "tf_projectile_energy_ring", vecOrigin, vecAngles, pOwner ) );
+ if ( !pRing )
+ return NULL;
+
+ // Initialize the owner.
+ pRing->SetOwnerEntity( pOwner );
+ pRing->SetLauncher( pLauncher );
+
+ pRing->SetScorer( pScorer );
+
+ // Spawn.
+ pRing->Spawn();
+
+ Vector vecVelocity = vecForward * pRing->GetInitialVelocity();
+ pRing->SetAbsVelocity( vecVelocity );
+
+ // Setup the initial angles.
+ QAngle angles;
+ VectorAngles( vecVelocity, angles );
+ pRing->SetAbsAngles( angles );
+
+ // Set team.
+ pRing->ChangeTeam( pOwner->GetTeamNumber() );
+
+ if ( pScorer )
+ {
+ pRing->SetTruceValidForEnt( pScorer->IsTruceValidForEnt() );
+ }
+#endif
+
+#ifdef CLIENT_DLL
+ // This is silly code to support demos when the client created its own effects
+ // for the Pomson and Righteous Bison
+ CTFRaygun* pRaygun = assert_cast< CTFRaygun* >( pLauncher );
+
+ if ( pRaygun && !pRaygun->UseNewProjectileCode() )
+ {
+ if ( pRaygun->GetWeaponID() == TF_WEAPON_DRG_POMSON )
+ {
+ pRing = static_cast<CTFProjectile_EnergyRing*>( CTFBaseProjectile::Create( "tf_projectile_energy_ring", vecOrigin, vecAngles, pOwner,
+ 1200.f, g_sModelIndexRing,
+ ENERGY_RING_DISPATCH_EFFECT_POMSON, pScorer, bCritical, vColor1, vColor2 ) );
+ }
+ else
+ {
+ pRing = static_cast<CTFProjectile_EnergyRing*>( CTFBaseProjectile::Create( "tf_projectile_energy_ring", vecOrigin, vecAngles, pOwner,
+ 1200.f, g_sModelIndexRing,
+ ENERGY_RING_DISPATCH_EFFECT, pScorer, bCritical, vColor1, vColor2 ) );
+ }
+
+ if ( pRing )
+ {
+ pRing->SetRenderMode( kRenderNone );
+ pRing->SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
+ pRing->SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS );
+ }
+ }
+#endif
+
+ return pRing;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFProjectile_EnergyRing::Spawn()
+{
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM );
+ SetRenderMode( kRenderNone );
+ SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
+ SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFProjectile_EnergyRing::Precache()
+{
+ PrecacheParticleSystem( g_pszEnergyProjectileImpactParticle );
+
+ PrecacheParticleSystem( g_pszBisonTrailParticle );
+ PrecacheParticleSystem( g_pszBisonTrailParticleCrit );
+ PrecacheScriptSound( g_pszBisonImpactWorldSound );
+ PrecacheScriptSound( g_pszBisonImpactFleshSound );
+
+ PrecacheParticleSystem( g_pszPomsonTrailParticle );
+ PrecacheParticleSystem( g_pszPomsonTrailParticleCrit );
+ PrecacheScriptSound( g_pszPomsonImpactWorldSound );
+ PrecacheScriptSound( g_pszPomsonImpactFleshSound );
+
+ BaseClass::Precache();
+}
+
+#ifdef GAME_DLL
+
+struct collidelist_t
+{
+ const CPhysCollide *pCollide;
+ Vector origin;
+ QAngle angles;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFProjectile_EnergyRing::ProjectileTouch( CBaseEntity *pOther )
+{
+ // Verify a correct "other."
+ Assert( pOther );
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsSolidFlagSet( FSOLID_NOT_SOLID )
+ || pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS )
+ return;
+
+ CBaseEntity* pOwner = GetOwnerEntity();
+ // Don't shoot ourselves
+ if ( pOwner == pOther )
+ return;
+
+ // Handle hitting skybox (disappear).
+ const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
+ if( pTrace->surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // pass through ladders
+ if( pTrace->surface.flags & CONTENTS_LADDER )
+ return;
+
+ // Used when checking against things like FUNC_BRUSHES
+ if ( !pOther->IsWorld() && pOther->GetSolid() == SOLID_VPHYSICS )
+ {
+ CPhysCollide *pTriggerCollide = modelinfo->GetVCollide( GetModelIndex() )->solids[0];
+ Assert( pTriggerCollide );
+
+ CUtlVector<collidelist_t> collideList;
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int physicsCount = pOther->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ vcollide_t *pVCollide = modelinfo->GetVCollide( pOther->GetModelIndex() );
+
+ if ( physicsCount )
+ {
+ for ( int i = 0; i < physicsCount; i++ )
+ {
+ const CPhysCollide *pCollide = pList[i]->GetCollide();
+ if ( pCollide )
+ {
+ collidelist_t element;
+ element.pCollide = pCollide;
+ pList[i]->GetPosition( &element.origin, &element.angles );
+ collideList.AddToTail( element );
+ }
+ }
+ }
+ else if ( pVCollide && pVCollide->solidCount )
+ {
+ collidelist_t element;
+ element.pCollide = pVCollide->solids[0];
+ element.origin = pOther->GetAbsOrigin();
+ element.angles = pOther->GetAbsAngles();
+ collideList.AddToTail( element );
+ }
+ else
+ {
+ return;
+ }
+
+ for ( int i = collideList.Count()-1; i >= 0; --i )
+ {
+ const collidelist_t &element = collideList[i];
+ trace_t tr;
+ physcollision->TraceCollide( pTrace->startpos, element.origin, element.pCollide, element.angles, pTriggerCollide, GetAbsOrigin(), GetAbsAngles(), &tr );
+ if ( !tr.DidHit() )
+ return;
+ }
+ }
+
+ // The stuff we collide with
+ bool bCombatEntity = pOther->IsPlayer() ||
+ pOther->IsBaseObject() ||
+ pOther->IsCombatCharacter() ||
+ pOther->IsCombatItem();
+
+ if ( !bCombatEntity )
+ {
+ // Couple more things that we collide with
+ // HACK: these are the same checks we do in CTFProjectile_Arrow::ArrowTouch()...need to figure out a better way to do this when we have time
+ CTFPumpkinBomb *pPumpkinBomb = dynamic_cast<CTFPumpkinBomb*>( pOther );
+ CTFMerasmusTrickOrTreatProp *pMerasmusProp = dynamic_cast<CTFMerasmusTrickOrTreatProp*>( pOther );
+ CTFRobotDestruction_Robot *pRobot = dynamic_cast<CTFRobotDestruction_Robot*>( pOther );
+ if ( pPumpkinBomb || pMerasmusProp || pRobot )
+ {
+ bCombatEntity = true;
+ }
+ }
+
+ if ( bCombatEntity && ( pOther != pOwner ) )
+ {
+ // Bison projectiles shouldn't collide with friendly things
+ if ( pOther->GetTeamNumber() == GetTeamNumber() && ShouldPenetrate() )
+ {
+ return;
+ }
+
+ FOR_EACH_VEC( m_vecHitEnemies, i )
+ {
+ // Check if we've already damaged this entity. If so, don't do it again
+ if ( m_vecHitEnemies[i] == pOther )
+ return;
+ }
+
+ const int nMaxPenetrates = 5;
+ const int nDamage = GetDamage() * pow( 0.75f, m_vecHitEnemies.Count() );
+
+ CTakeDamageInfo info( this, pOwner, GetLauncher(), nDamage, GetDamageType(), TF_DMG_CUSTOM_PLASMA );
+ info.SetReportedPosition( pOwner->GetAbsOrigin() );
+ info.SetDamagePosition( pTrace->endpos );
+
+ if ( info.GetDamageType() & DMG_CRITICAL )
+ {
+ info.SetCritType( CTakeDamageInfo::CRIT_FULL );
+ }
+
+ trace_t traceAttack;
+ UTIL_TraceLine( WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_SOLID|CONTENTS_HITBOX, this, COLLISION_GROUP_NONE, &traceAttack );
+
+ pOther->DispatchTraceAttack( info, GetAbsVelocity(), &traceAttack );
+
+ ApplyMultiDamage();
+
+ m_vecHitEnemies.AddToTail( pOther );
+
+ // Get a position on whatever we hit
+ Vector vecDelta = pOther->GetAbsOrigin() - GetAbsOrigin();
+ Vector vecNormalVel = GetAbsVelocity().Normalized();
+ Vector vecNewPos = ( DotProduct( vecDelta, vecNormalVel ) * vecNormalVel ) + GetAbsOrigin();
+
+ PlayImpactEffects( vecNewPos, pOther->IsPlayer() );
+
+ int iPenetrate = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( GetOwnerEntity(), iPenetrate, energy_weapon_penetration );
+ if ( iPenetrate && m_vecHitEnemies.Count() < nMaxPenetrates )
+ {
+ return;
+ }
+
+
+ UTIL_Remove( this );
+ return;
+ }
+
+ if ( pOther->IsWorld() )
+ {
+ SetAbsVelocity( vec3_origin );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ }
+
+ PlayImpactEffects( pTrace->endpos, false );
+
+ // Remove by default. Fixes this entity living forever on things like doors.
+ UTIL_Remove( this );
+}
+
+void CTFProjectile_EnergyRing::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
+{
+ PlayImpactEffects( trace.endpos, false );
+
+ // Remove by default. Fixes this entity living forever on things like doors.
+ UTIL_Remove( this );
+}
+
+void CTFProjectile_EnergyRing::PlayImpactEffects( const Vector& vecPos, bool bHitFlesh )
+{
+ CTFWeaponBaseGun* pTFGun = dynamic_cast< CTFWeaponBaseGun* >( GetLauncher() );
+ if ( pTFGun )
+ {
+ DispatchParticleEffect( g_pszEnergyProjectileImpactParticle, vecPos, GetAbsAngles(), pTFGun->GetParticleColor( 1 ), pTFGun->GetParticleColor( 2 ), true, NULL, 0 );
+ const char* pszSoundString = NULL;
+ if ( ShouldPenetrate() )
+ {
+ pszSoundString = bHitFlesh ? g_pszBisonImpactFleshSound : g_pszBisonImpactWorldSound;
+ }
+ else
+ {
+ pszSoundString = bHitFlesh ? g_pszPomsonImpactFleshSound : g_pszPomsonImpactWorldSound;
+ }
+ EmitSound( pszSoundString );
+ }
+}
+
+#else
+
+void CTFProjectile_EnergyRing::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ CNewParticleEffect* pEffect = ParticleProp()->Create( GetTrailParticleName(), PATTACH_ABSORIGIN_FOLLOW );
+ CTFWeaponBaseGun* pTFGun = dynamic_cast< CTFWeaponBaseGun* >( GetLauncher() );
+ if ( pEffect && pTFGun )
+ {
+ pEffect->SetControlPoint( CUSTOM_COLOR_CP1, pTFGun->GetParticleColor( 0 ) );
+ pEffect->SetControlPoint( CUSTOM_COLOR_CP2, pTFGun->GetParticleColor( 1 ) );
+ }
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFProjectile_EnergyRing::GetDamage()
+{
+ return ShouldPenetrate() ? 45.f : 60.f;
+}
+
+bool CTFProjectile_EnergyRing::ShouldPenetrate() const
+{
+ int iPenetrate = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( GetOwnerEntity(), iPenetrate, energy_weapon_penetration );
+
+ return iPenetrate != 0;
+}
+
+const char* CTFProjectile_EnergyRing::GetTrailParticleName() const
+{
+ if ( ShouldPenetrate() ) // Righteous Bison
+ {
+ return IsCritical() ? g_pszBisonTrailParticleCrit : g_pszBisonTrailParticle;
+ }
+ else // Pomson
+ {
+ return IsCritical() ? g_pszPomsonTrailParticleCrit : g_pszPomsonTrailParticle;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// The following is legacy code to support old demos
+//-----------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+void CreateClientSideEnergyRing( const char* pszStandardParticle, const char* pszCritParticle, const CEffectData &data, int nFlags )
+{
+ C_BaseEntity *entity = ClientEntityList().GetBaseEntityFromHandle( data.m_hEntity );
+ if ( !entity )
+ {
+ return;
+ }
+
+ C_TFPlayer *pPlayer = dynamic_cast< C_TFPlayer * >( entity->GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ C_LocalTempEntity *pRing = ClientsideProjectileCallback( data, 0.f );
+ if ( pRing )
+ {
+ bool bCritical = ( ( data.m_nDamageType & DMG_CRITICAL ) != 0 );
+ CNewParticleEffect* pEffect = pRing->AddParticleEffect( bCritical ? pszCritParticle : pszStandardParticle );
+ if ( pEffect )
+ {
+ pEffect->SetControlPoint( CUSTOM_COLOR_CP1, data.m_CustomColors.m_vecColor1 );
+ pEffect->SetControlPoint( CUSTOM_COLOR_CP2, data.m_CustomColors.m_vecColor2 );
+ }
+
+ pRing->AddEffects( EF_NOSHADOW );
+ pRing->flags = nFlags;
+ pRing->SetRenderMode( kRenderNone );
+ pRing->SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
+ pRing->SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Bison effect callback
+//-----------------------------------------------------------------------------
+void ClientsideProjectileRingCallback( const CEffectData &data )
+{
+ CreateClientSideEnergyRing( g_pszBisonTrailParticle, g_pszBisonTrailParticleCrit, data, FTENT_COLLIDEKILL | FTENT_COLLIDEPROPS | FTENT_ATTACHTOTARGET | FTENT_ALIGNTOMOTION | FTENT_CLIENTSIDEPARTICLES );
+}
+
+DECLARE_CLIENT_EFFECT( ENERGY_RING_DISPATCH_EFFECT, ClientsideProjectileRingCallback );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Pomson effect callback
+//-----------------------------------------------------------------------------
+void ClientsideProjectileRingPomsonCallback( const CEffectData &data )
+{
+ CreateClientSideEnergyRing( g_pszPomsonTrailParticle, g_pszPomsonTrailParticleCrit, data, FTENT_COLLIDEALL | FTENT_USEFASTCOLLISIONS | FTENT_ATTACHTOTARGET | FTENT_ALIGNTOMOTION | FTENT_CLIENTSIDEPARTICLES );
+}
+
+DECLARE_CLIENT_EFFECT( ENERGY_RING_DISPATCH_EFFECT_POMSON, ClientsideProjectileRingPomsonCallback );
+
+#endif \ No newline at end of file