diff options
Diffstat (limited to 'game/shared/tf/tf_weapon_mechanical_arm.cpp')
| -rw-r--r-- | game/shared/tf/tf_weapon_mechanical_arm.cpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_mechanical_arm.cpp b/game/shared/tf/tf_weapon_mechanical_arm.cpp new file mode 100644 index 0000000..95a1a48 --- /dev/null +++ b/game/shared/tf/tf_weapon_mechanical_arm.cpp @@ -0,0 +1,537 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "tf_weapon_mechanical_arm.h" +#include "in_buttons.h" + +#if !defined( CLIENT_DLL ) +#include "tf_player.h" +#include "tf_gamestats.h" +#include "ilagcompensationmanager.h" +#include "particle_parse.h" +#include "tf_fx.h" +#include "tf_weapon_grenade_pipebomb.h" +#include "tf_team.h" +#include "tf_passtime_logic.h" +#else +#include "c_tf_player.h" +#endif + + +//============================================================================= +// +// tables. +// + +IMPLEMENT_NETWORKCLASS_ALIASED( TFMechanicalArm, DT_TFMechanicalArm ) + +BEGIN_NETWORK_TABLE( CTFMechanicalArm, DT_TFMechanicalArm ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CTFMechanicalArm ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( tf_weapon_mechanical_arm, CTFMechanicalArm ); +PRECACHE_WEAPON_REGISTER( tf_weapon_mechanical_arm ); + +#define AMMO_BASE_PROJECTILE_SHOCK 10 +#define AMMO_PER_PROJECTILE_SHOCK 5 + +//============================================================================= +// +// CTFMechanicalArm +// + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFMechanicalArm::CTFMechanicalArm() +{ +#ifdef CLIENT_DLL + m_pParticleBeamEffect = NULL; + m_pParticleBeamSpark = NULL; + m_pEffectOwner = NULL; +#endif // CLIENT_DLL +} + +CTFMechanicalArm::~CTFMechanicalArm() +{ +#ifdef CLIENT_DLL + if ( m_pEffectOwner ) + { + if ( m_pParticleBeamEffect ) + { + m_pEffectOwner->ParticleProp()->StopEmissionAndDestroyImmediately( m_pParticleBeamEffect ); + m_pParticleBeamEffect = NULL; + } + + if ( m_pParticleBeamSpark ) + { + m_pEffectOwner->ParticleProp()->StopEmissionAndDestroyImmediately( m_pParticleBeamSpark ); + m_pParticleBeamSpark = NULL; + } + + m_pEffectOwner = NULL; + } +#endif // CLIENT_DLL +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMechanicalArm::Precache() +{ + BaseClass::Precache(); + + PrecacheParticleSystem( "dxhr_arm_muzzleflash" ); + PrecacheParticleSystem( "dxhr_arm_muzzleflash2" ); + PrecacheParticleSystem( "dxhr_arm_impact" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFMechanicalArm::ShockAttack( void ) +{ + CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); + if ( !pOwner ) + return false; + + if ( pOwner->GetWaterLevel() == WL_Eyes ) + return false; + + // Enough ammo to shock at least one target? + if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) < ( AMMO_BASE_PROJECTILE_SHOCK + AMMO_PER_PROJECTILE_SHOCK ) ) + return false; + +#ifdef GAME_DLL + if ( pOwner->m_Shared.IsStealthed() ) + { + pOwner->RemoveInvisibility(); + } + + lagcompensation->StartLagCompensation( pOwner, pOwner->GetCurrentCommand() ); + + Vector vecEye = pOwner->EyePosition(); + Vector vecForward, vecRight, vecUp; + AngleVectors( pOwner->EyeAngles(), &vecForward, &vecRight, &vecUp ); + Vector vecSize = Vector( 128, 128, 64 ); + float flMaxElement = 0.0f; + for ( int i = 0; i < 3; ++i ) + { + flMaxElement = MAX( flMaxElement, vecSize[i] ); + } + Vector vecCenter = vecEye + vecForward * flMaxElement; + + CUtlVector< CBaseEntity* > vecShockTargets; + + // Get a list of entities in the box defined by vecSize at VecCenter. + // We will then try to deflect everything in the box. + const int maxCollectedEntities = 64; + CBaseEntity *pObjects[ maxCollectedEntities ]; + int count = UTIL_EntitiesInBox( pObjects, maxCollectedEntities, vecCenter - vecSize, vecCenter + vecSize, FL_GRENADE | FL_CLIENT | FL_FAKECLIENT ); + for ( int i = 0; i < count; i++ ) + { + if ( IsValidVictim( pOwner, pObjects[i] ) ) + { + vecShockTargets.AddToTail( pObjects[i] ); + } + } + + // Remove the base cost for attempting to fire, regardless of what we hit + pOwner->RemoveAmmo( AMMO_BASE_PROJECTILE_SHOCK, m_iPrimaryAmmoType ); + + // We attacked by didn't hit anything + if ( vecShockTargets.Count() == 0 ) + { + lagcompensation->FinishLagCompensation( pOwner ); + return true; + } + + // Horribly hacked muzzle position + Vector vForward, vRight, vUp; + AngleVectors( pOwner->EyeAngles(), &vForward, &vRight, &vUp ); + + Vector vStart = pOwner->EyePosition() + + (vForward * 40.0f) + + (vRight * 15.0f) + + (vUp * -10.0f); + + FOR_EACH_VEC( vecShockTargets, i ) + { + if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) < AMMO_PER_PROJECTILE_SHOCK ) + break; + + CBaseEntity* pTarget = vecShockTargets[i]; + Vector vecTarget = pTarget->WorldSpaceCenter(); + + ShockVictim( pOwner, pTarget ); + pOwner->RemoveAmmo( AMMO_PER_PROJECTILE_SHOCK, m_iPrimaryAmmoType ); + + // Play an effect where the target is + CPVSFilter filter( vecTarget ); + const char *shootsound = GetShootSound( SPECIAL3 ); + if ( shootsound && *shootsound ) + { + EmitSound( shootsound ); + } + + // play the electrical effect from the gun to the pipes + te_tf_particle_effects_control_point_t controlPoint = { PATTACH_WORLDORIGIN, vecTarget }; + TE_TFParticleEffectComplex( filter, 0.0f, "dxhr_arm_muzzleflash", vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, pTarget, PATTACH_CUSTOMORIGIN ); + } + + lagcompensation->FinishLagCompensation( pOwner ); +#endif + + return true; +} + +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +bool CTFMechanicalArm::IsValidVictim( CTFPlayer *pOwner, CBaseEntity *pTarget ) +{ + if ( pTarget == NULL ) + return false; + + if ( pTarget == pOwner ) + return false; + + if ( pTarget->IsPlayer() && pTarget->GetTeamNumber() == TEAM_SPECTATOR ) + return false; + + if ( pTarget->GetTeamNumber() == pOwner->GetTeamNumber() ) + return false; + + if ( pTarget->IsPlayer() && !pTarget->IsAlive() ) + return false; + + if ( !pTarget->IsDeflectable() && !FClassnameIs( pTarget, "prop_physics" ) ) + return false; + + if ( pOwner->FVisible( pTarget, MASK_SOLID_BRUSHONLY ) == false ) + return false; + + if ( g_pPasstimeLogic && ( g_pPasstimeLogic->GetBall() == pTarget ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +void CTFMechanicalArm::ShockVictim( CTFPlayer *pOwner, CBaseEntity *pTarget ) +{ + // Projectile + if ( !pTarget->IsPlayer() ) + { + pTarget->SetThink( &BaseClass::SUB_Remove ); + pTarget->SetNextThink( gpGlobals->curtime ); + pTarget->SetTouch( NULL ); + pTarget->AddEffects( EF_NODRAW ); + pTarget->RemoveFlag( FL_GRENADE ); + } + + // deal damage + CTakeDamageInfo info; + info.SetDamageType( DMG_SHOCK ); + info.SetAttacker( pOwner ); + info.SetInflictor( this ); + info.SetWeapon( this ); + info.SetDamage( 20 ); + info.SetDamagePosition( pTarget->WorldSpaceCenter() ); + pTarget->TakeDamage( info ); + + // Achievement + CTFGrenadePipebombProjectile *pPipebomb = dynamic_cast<CTFGrenadePipebombProjectile*>( pTarget ); + if ( pPipebomb && pPipebomb->HasStickyEffects() ) + { + // If we are near a building, award achievement progress. + CTFTeam *pTeam = pOwner->GetTFTeam(); + if ( pTeam ) + { + for ( int j = 0; j < pTeam->GetNumObjects(); j++ ) + { + CBaseObject *pTemp = pTeam->GetObject( j ); + if ( pTemp && ( pTemp->ObjectType() != OBJ_ATTACHMENT_SAPPER ) ) + { + if ( ( pTemp->GetAbsOrigin().DistTo( pPipebomb->GetAbsOrigin() ) < 100 ) && + ( pTemp->FVisible( pPipebomb, MASK_SOLID_BRUSHONLY ) ) ) + { + pOwner->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_DESTROY_STICKIES, 1 ); + break; // Only one award per sticky. + } + } + } + } + } +} +#endif + +//----------------------------------------------------------------------------- +void CTFMechanicalArm::SecondaryAttack( void ) +{ + CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); + if ( !pOwner ) + return; + + // Are we capable of firing again? + if ( m_flNextSecondaryAttack > gpGlobals->curtime ) + return; + + if ( m_iPrimaryAmmoType == TF_AMMO_METAL ) + { + if ( ( GetAmmoPerShot() > GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) ) || + ( pOwner->GetWaterLevel() == WL_Eyes ) ) + { + WeaponSound( EMPTY ); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.67f; + return; + } + } + + if ( !CanAttack() ) + return; + + if ( ShockAttack() ) + { + WeaponSound( SPECIAL3 ); + } + else + { + WeaponSound( EMPTY ); + } + +#ifdef CLIENT_DLL + // Play an effect on the client so they have some kind of visual feedback that something happened + int iParticleAttachment = LookupAttachment( "muzzle" ); + CNewParticleEffect* pEffect = ParticleProp()->Create( "dxhr_sniper_fizzle", PATTACH_POINT_FOLLOW, iParticleAttachment ); + ParticleProp()->AddControlPoint( pEffect, 1, this, PATTACH_POINT_FOLLOW, "muzzle" ); +#endif + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + pOwner->SetAnimation( PLAYER_ATTACK1 ); + + m_flNextPrimaryAttack = gpGlobals->curtime + 0.67f; + m_flNextSecondaryAttack = gpGlobals->curtime + 0.67f; +} + +#ifdef CLIENT_DLL +void CTFMechanicalArm::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + UpdateParticleBeam(); +} + +//----------------------------------------------------------------------------- +void CTFMechanicalArm::StopParticleBeam( void ) +{ + if ( !m_pEffectOwner ) + return; + + // Different owners, kill the old effect + if ( m_pParticleBeamEffect ) + { + m_pEffectOwner->ParticleProp()->StopEmissionAndDestroyImmediately( m_pParticleBeamEffect ); + m_pParticleBeamEffect = NULL; + } + + if ( m_pParticleBeamSpark ) + { + m_pEffectOwner->ParticleProp()->StopEmissionAndDestroyImmediately( m_pParticleBeamSpark ); + m_pParticleBeamSpark = NULL; + } + + m_pEffectOwner = NULL; +} + +//----------------------------------------------------------------------------- +void CTFMechanicalArm::UpdateParticleBeam() +{ + // Update Particle + // If we are attacking, update the particle (make it render) + CTFPlayer *pFiringPlayer = ToTFPlayer( GetOwnerEntity() ); + if ( !pFiringPlayer ) + { + StopParticleBeam(); + return; + } + + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + C_BaseEntity *pEffectOwner = this; + if ( pLocalPlayer == pFiringPlayer ) + { + pEffectOwner = pLocalPlayer->GetRenderedWeaponModel(); + if ( !pEffectOwner ) + { + StopParticleBeam(); + return; + } + } + + if ( m_pEffectOwner && m_pEffectOwner != pEffectOwner ) + { + StopParticleBeam(); + return; + } + + if ( m_flNextSecondaryAttack > gpGlobals->curtime ) + { + StopParticleBeam(); + return; + } + + m_pEffectOwner = pEffectOwner; + + // Constantly perform the shock attack and update control points if attack is down and we've already fired + if ( pFiringPlayer + && pFiringPlayer->m_nButtons & IN_ATTACK + && pFiringPlayer->GetActiveWeapon() == this + && pFiringPlayer->GetWaterLevel() != WL_Eyes + && pFiringPlayer->m_flNextAttack < gpGlobals->curtime ) + { + trace_t tr; + Vector vecAiming; + pFiringPlayer->EyeVectors( &vecAiming ); + Vector vecEnd = pFiringPlayer->EyePosition() + vecAiming * 256.0f; + UTIL_TraceLine( pFiringPlayer->EyePosition(), vecEnd, ( MASK_SHOT & ~CONTENTS_HITBOX ), pFiringPlayer, DMG_GENERIC, &tr ); + + // Line laser + if ( !m_pParticleBeamEffect ) + { + const char *pszEffectName = "dxhr_arm_muzzleflash2"; + m_pParticleBeamEffect = pEffectOwner->ParticleProp()->Create( pszEffectName, PATTACH_POINT_FOLLOW, "muzzle" ); + } + if ( m_pParticleBeamEffect ) + { + Vector vEndPos = tr.endpos - pFiringPlayer->GetAbsOrigin(); + pEffectOwner->ParticleProp()->AddControlPoint( m_pParticleBeamEffect, 1, pFiringPlayer, PATTACH_ABSORIGIN_FOLLOW, NULL, vEndPos ); + } + + // Spark + if ( !m_pParticleBeamSpark && tr.m_pEnt && tr.m_pEnt->IsPlayer( ) ) + { + m_pParticleBeamSpark = pEffectOwner->ParticleProp( )->Create( "dxhr_arm_impact", PATTACH_ABSORIGIN_FOLLOW ); + } + else if ( m_pParticleBeamSpark && (!tr.m_pEnt || !tr.m_pEnt->IsPlayer() ) ) + { + m_pEffectOwner->ParticleProp()->StopEmissionAndDestroyImmediately( m_pParticleBeamSpark ); + m_pParticleBeamSpark = NULL; + } + + if ( m_pParticleBeamSpark ) + { + Vector vEndPos = tr.endpos - pFiringPlayer->GetAbsOrigin(); + pEffectOwner->ParticleProp( )->AddControlPoint( m_pParticleBeamSpark, 1, pFiringPlayer, PATTACH_ABSORIGIN_FOLLOW, NULL, vEndPos ); + } + } + else if ( m_pEffectOwner ) + { + StopParticleBeam( ); + } +} +#endif // CLIENT_DLL + +//----------------------------------------------------------------------------- +void CTFMechanicalArm::PrimaryAttack() +{ + CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); + if ( !pOwner ) + return; + + float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay ); + + if ( m_iPrimaryAmmoType == TF_AMMO_METAL ) + { + if ( ( GetAmmoPerShot() > GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) ) || + ( pOwner->GetWaterLevel( ) == WL_Eyes ) ) + { + WeaponSound( EMPTY ); + m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; + return; + } + } + + // Are we capable of firing again? + if ( m_flNextPrimaryAttack > gpGlobals->curtime ) + return; + + // Get the player owning the weapon. + CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); + if ( !pPlayer ) + return; + + if ( !CanAttack() ) + return; + +#ifdef GAME_DLL + CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, false ); + + int iAmmoPerShot = 0; + CALL_ATTRIB_HOOK_INT( iAmmoPerShot, mod_ammo_per_shot ); + pOwner->RemoveAmmo( iAmmoPerShot, m_iPrimaryAmmoType ); + + //int nAmmoToTake = bShocked ? 0 : GetAmmoPerShot(); + //pOwner->RemoveAmmo( nAmmoToTake, m_iPrimaryAmmoType ); + + FireProjectile( pPlayer ); +#endif + +#ifdef CLIENT_DLL + // Play an effect on the client so they have some kind of visual feedback that something happened + int iParticleAttachment = LookupAttachment( "muzzle" ); + CNewParticleEffect* pEffect = ParticleProp()->Create( "dxhr_sniper_fizzle", PATTACH_POINT_FOLLOW, iParticleAttachment ); + ParticleProp()->AddControlPoint( pEffect, 1, this, PATTACH_POINT_FOLLOW, "muzzle" ); +#endif + + WeaponSound( SINGLE ); + + // Set the weapon mode. + m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + // Set next attack times. + m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; + + SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); +} + +//----------------------------------------------------------------------------- +int CTFMechanicalArm::GetAmmoPerShot( void ) +{ + // Used by normal fire code, we only decrement ammo on ticks which uses an Attr + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFMechanicalArm::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState ) +{ + if ( !pOwner ) + return false; + + iState = pOwner->GetActiveWeapon() == this; + + bool res = BaseClass::UpdateBodygroups( pOwner, iState ); + + CTFPlayer *pTFOwner = ToTFPlayer( pOwner ); + if ( pTFOwner ) + { + CBaseViewModel *pVM = pTFOwner->GetViewModel(); + if ( pVM ) + { + pVM->SetBodygroup( 1, iState ); + } + } + + return res; +}
\ No newline at end of file |