//========= Copyright Valve Corporation, All rights reserved. ============// // // TF Rocket Launcher // //============================================================================= #include "cbase.h" #include "tf_weapon_particle_cannon.h" #include "tf_fx_shared.h" #include "in_buttons.h" // Client specific. #ifdef CLIENT_DLL #include "c_tf_player.h" #include #include #include "soundenvelope.h" #include "particle_property.h" #include "c_tf_gamestats.h" // Server specific. #else #include "tf_gamestats.h" #include "tf_player.h" #include "tf_projectile_energy_ball.h" #endif //============================================================================= // // Particle cannon tables. // IMPLEMENT_NETWORKCLASS_ALIASED( TFParticleCannon, DT_ParticleCannon ) BEGIN_NETWORK_TABLE( CTFParticleCannon, DT_ParticleCannon ) #ifdef CLIENT_DLL RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ), RecvPropInt( RECVINFO( m_iChargeEffect ) ) #else SendPropFloat( SENDINFO( m_flChargeBeginTime ) ), SendPropInt( SENDINFO( m_iChargeEffect ) ) #endif END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CTFParticleCannon ) END_PREDICTION_DATA() LINK_ENTITY_TO_CLASS( tf_weapon_particle_cannon, CTFParticleCannon ); PRECACHE_WEAPON_REGISTER( tf_weapon_particle_cannon ); // Server specific. #ifndef CLIENT_DLL BEGIN_DATADESC( CTFParticleCannon ) END_DATADESC() #endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFParticleCannon::CTFParticleCannon() : CTFRocketLauncher() { #ifdef CLIENT_DLL m_bEffectsThinking = false; m_iChargeEffectBase = 0; #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFParticleCannon::GetProjectileSpeed( void ) { return 1100.f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFParticleCannon::GetProjectileGravity( void ) { return 0.f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFParticleCannon::IsViewModelFlipped( void ) { return !BaseClass::IsViewModelFlipped(); // Invert because arrows are backwards by default. } //----------------------------------------------------------------------------- // Purpose: Reset the charge when we holster //----------------------------------------------------------------------------- bool CTFParticleCannon::Holster( CBaseCombatWeapon *pSwitchingTo ) { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_AIMING ) ) return false; m_flChargeBeginTime = 0; #ifdef CLIENT_DLL ParticleProp()->Init( this ); ParticleProp()->StopParticlesNamed( "drg_cowmangler_idle", true ); m_bEffectsThinking = false; #endif StopSound( "Weapon_CowMangler.Charging" ); return BaseClass::Holster( pSwitchingTo ); } //----------------------------------------------------------------------------- // Purpose: Reset the charge when we deploy //----------------------------------------------------------------------------- bool CTFParticleCannon::Deploy( void ) { m_flChargeBeginTime = 0; #ifdef CLIENT_DLL m_bEffectsThinking = true; SetContextThink( &CTFParticleCannon::ClientEffectsThink, gpGlobals->curtime + rand() % 5, "PC_EFFECTS_THINK" ); #endif return BaseClass::Deploy(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::WeaponReset( void ) { BaseClass::WeaponReset(); m_flChargeBeginTime = 0.0f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::ItemPostFrame( void ) { BaseClass::ItemPostFrame(); if ( m_flChargeBeginTime > 0 ) { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; // If we're not holding down the attack button, launch our grenade float flTotalChargeTime = gpGlobals->curtime - m_flChargeBeginTime; if ( flTotalChargeTime >= GetChargeForceReleaseTime() ) { FireChargedShot(); } } #ifdef CLIENT_DLL if ( !m_bEffectsThinking ) { m_bEffectsThinking = true; SetContextThink( &CTFParticleCannon::ClientEffectsThink, gpGlobals->curtime + rand() % 5, "PC_EFFECTS_THINK" ); } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::PrimaryAttack( void ) { if ( m_flChargeBeginTime > 0 ) return; if ( !Energy_HasEnergy() ) return; m_bChargedShot = false; BaseClass::PrimaryAttack(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::SecondaryAttack( void ) { // Check for ammunition. if ( !Energy_FullyCharged() ) { Reload(); return; } // Are we capable of firing again? if ( m_flNextPrimaryAttack > gpGlobals->curtime ) return; if ( m_flChargeBeginTime > 0 ) return; if ( !CanAttack() ) { m_flChargeBeginTime = 0; return; } m_bChargedShot = true; // Set the weapon mode. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; // save that we had the attack button down m_flChargeBeginTime = gpGlobals->curtime; CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( pPlayer ) { SendWeaponAnim( ACT_PRIMARY_VM_PRIMARYATTACK_3 ); pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY_SUPER ); } WeaponSound( SPECIAL1 ); pPlayer->m_Shared.AddCond( TF_COND_AIMING ); pPlayer->TeamFortress_SetSpeed(); m_iChargeEffect++; } #ifdef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::CreateChargeEffect() { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( pPlayer ) { DispatchParticleEffect( "drg_cowmangler_muzzleflash_chargeup", PATTACH_POINT_FOLLOW, GetAppropriateWorldOrViewModel(), "muzzle", GetParticleColor( 1 ), GetParticleColor( 2 ) ); } } #endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::FireChargedShot() { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; if ( !pPlayer->IsAlive() ) return; pPlayer->m_Shared.RemoveCond( TF_COND_AIMING ); pPlayer->TeamFortress_SetSpeed(); #ifndef CLIENT_DLL CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, false ); #else C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, false ); #endif // SendWeaponAnim( ACT_VM_PRIMARYATTACK ); // pPlayer->SetAnimation( PLAYER_ATTACK1 ); CBaseEntity* pProj = FireProjectile( pPlayer ); ModifyProjectile( pProj ); float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay ); m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); m_iReloadMode.Set( TF_RELOAD_START ); m_flChargeBeginTime = 0.0f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::ModifyProjectile( CBaseEntity* pProj ) { #ifdef GAME_DLL CTFProjectile_EnergyBall* pEnergyBall = dynamic_cast( pProj ); if ( pEnergyBall == NULL ) return; pEnergyBall->SetChargedShot( m_bChargedShot ); pEnergyBall->SetColor( 1, GetParticleColor( 1 ) ); pEnergyBall->SetColor( 2, GetParticleColor( 2 ) ); #endif if ( m_bChargedShot ) { Energy_DrainEnergy( Energy_GetMaxEnergy() ); } else { Energy_DrainEnergy(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFParticleCannon::GetProgress( void ) { return Energy_GetEnergy() / Energy_GetMaxEnergy(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CTFParticleCannon::GetMuzzleFlashParticleEffect( void ) { if ( m_bChargedShot ) { return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_muzzleflash_charged" : "drg_cow_muzzleflash_charged_blue"; } else { return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_muzzleflash_normal" : "drg_cow_muzzleflash_normal_blue"; } } #ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::Precache() { BaseClass::Precache(); PrecacheParticleSystem( "drg_cow_explosioncore_charged" ); PrecacheParticleSystem( "drg_cow_explosioncore_charged_blue" ); PrecacheParticleSystem( "drg_cow_explosioncore_normal" ); PrecacheParticleSystem( "drg_cow_explosioncore_normal_blue" ); PrecacheParticleSystem( "drg_cow_muzzleflash_charged" ); PrecacheParticleSystem( "drg_cow_muzzleflash_charged_blue" ); PrecacheParticleSystem( "drg_cow_muzzleflash_normal" ); PrecacheParticleSystem( "drg_cow_muzzleflash_normal_blue" ); PrecacheParticleSystem( "drg_cow_idle" ); PrecacheScriptSound( "Weapon_CowMangler.ReloadFinal" ); } #endif #ifdef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if ( IsCarrierAlive() && ( WeaponState() == WEAPON_IS_ACTIVE ) ) { if ( m_iChargeEffect != m_iChargeEffectBase ) { CreateChargeEffect(); m_iChargeEffectBase = m_iChargeEffect; } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::ClientEffectsThink( void ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( !pPlayer ) return; if ( !pPlayer->IsLocalPlayer() ) return; if ( !pPlayer->GetViewModel() ) return; if ( !m_bEffectsThinking ) return; SetContextThink( &CTFParticleCannon::ClientEffectsThink, gpGlobals->curtime + 2 + rand() % 5, "PC_EFFECTS_THINK" ); if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) return; const char* mounts[4] = { "crit_frontspark1", "crit_frontspark2", "crit_frontspark3", "crit_frontspark4" }; int iPoint = rand() % 4; ParticleProp()->Init( this ); const char *pszIdleParticle = ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_idle" : "drg_cow_idle_blue"; CNewParticleEffect* pEffect = ParticleProp()->Create( pszIdleParticle, PATTACH_POINT_FOLLOW, mounts[iPoint] ); if ( pEffect ) { pEffect->SetControlPoint( CUSTOM_COLOR_CP1, GetParticleColor( 1 ) ); pEffect->SetControlPoint( CUSTOM_COLOR_CP2, GetParticleColor( 2 ) ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::DispatchMuzzleFlash( const char* effectName, C_BaseEntity* pAttachEnt ) { DispatchParticleEffect( effectName, PATTACH_POINT_FOLLOW, pAttachEnt, "muzzle", GetParticleColor( 1 ), GetParticleColor( 2 ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::CreateMuzzleFlashEffects( C_BaseEntity *pAttachEnt, int nIndex ) { // Don't call direct parent. We don't want back blast effects. CTFWeaponBaseGun::CreateMuzzleFlashEffects( pAttachEnt, nIndex ); } #endif //----------------------------------------------------------------------------- // Purpose: Utility function for default colors. //----------------------------------------------------------------------------- Vector GetParticleColorForTeam( int iTeam, int iColor ) { if ( iColor == 1 ) { if ( iTeam == TF_TEAM_RED ) return TF_PARTICLE_WEAPON_RED_1; else return TF_PARTICLE_WEAPON_BLUE_1; } else { if ( iTeam == TF_TEAM_RED ) return TF_PARTICLE_WEAPON_RED_2; else return TF_PARTICLE_WEAPON_BLUE_2; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFParticleCannon::PlayWeaponShootSound( void ) { if ( m_bChargedShot ) { // WeaponSound( BURST ); } else { WeaponSound( SINGLE ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- char const *CTFParticleCannon::GetShootSound( int iIndex ) const { if ( iIndex == RELOAD ) { bool bLastReload = (Energy_GetEnergy()+Energy_GetRechargeCost()) == Energy_GetMaxEnergy(); if ( bLastReload ) { return "Weapon_CowMangler.ReloadFinal"; } } return BaseClass::GetShootSound(iIndex); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFParticleCannon::OwnerCanTaunt( void ) { if ( m_flChargeBeginTime > 0 ) { return false; } else { return true; } }