//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: CTF CurrencyPack. // //=============================================================================// #include "cbase.h" #include "items.h" #include "tf_gamerules.h" #include "tf_shareddefs.h" #include "tf_player.h" #include "tf_team.h" #include "engine/IEngineSound.h" #include "entity_currencypack.h" #include "tf_gamestats.h" #include "tf_mann_vs_machine_stats.h" #include "world.h" #include "particle_parse.h" #include "player_vs_environment/tf_population_manager.h" #include "collisionutils.h" #include "tf_objective_resource.h" //============================================================================= // // CTF CurrencyPack defines. // #define TF_CURRENCYPACK_PICKUP_SOUND "MVM.MoneyPickup" #define TF_CURRENCYPACK_VANISH_SOUND "MVM.MoneyVanish" #define TF_CURRENCYPACK_BLINK_PERIOD 5.0f // how long pack blinks before it vanishes #define TF_CURRENCYPACK_BLINK_DURATION 0.25f // how long a blink lasts #define TF_CURRENCYPACK_GLOW_THINK_TIME 0.1f // how often should we check if cash should glow LINK_ENTITY_TO_CLASS( item_currencypack_large, CCurrencyPack ); LINK_ENTITY_TO_CLASS( item_currencypack_medium, CCurrencyPackMedium ); LINK_ENTITY_TO_CLASS( item_currencypack_small, CCurrencyPackSmall ); LINK_ENTITY_TO_CLASS( item_currencypack_custom, CCurrencyPackCustom ); IMPLEMENT_SERVERCLASS_ST( CCurrencyPack, DT_CurrencyPack ) SendPropBool( SENDINFO( m_bDistributed ) ), END_SEND_TABLE() IMPLEMENT_AUTO_LIST( ICurrencyPackAutoList ); //============================================================================= // // CTF CurrencyPack functions. // //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCurrencyPack::CCurrencyPack() { m_nAmount = 0; m_nWaveNumber = MannVsMachineStats_GetCurrentWave(); m_bTouched = false; m_bClaimed = false; m_bDistributed = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCurrencyPack::~CCurrencyPack() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCurrencyPack::UpdateOnRemove( void ) { BaseClass::UpdateOnRemove(); if ( g_pPopulationManager && !m_bTouched ) { if ( !m_bDistributed ) { g_pPopulationManager->OnCurrencyPackFade(); } DispatchParticleEffect( "mvm_cash_explosion", GetAbsOrigin(), GetAbsAngles() ); } if ( !m_bDistributed && TFObjectiveResource() ) { TFObjectiveResource()->AddMvMWorldMoney( -m_nAmount ); } } //----------------------------------------------------------------------------- // Purpose: Always transmitted to clients //----------------------------------------------------------------------------- int CCurrencyPack::UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_ALWAYS ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CCurrencyPack::ShouldTransmit( const CCheckTransmitInfo *pInfo ) { return FL_EDICT_ALWAYS; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCurrencyPack::Spawn( void ) { BaseClass::Spawn(); m_blinkCount = 0; m_blinkTimer.Invalidate(); SetContextThink( &CCurrencyPack::BlinkThink, gpGlobals->curtime + GetLifeTime() - TF_CURRENCYPACK_BLINK_PERIOD - RandomFloat( 0.0, TF_CURRENCYPACK_BLINK_DURATION ), "CurrencyPackWaitingToBlinkThink" ); // Force collision size to see if this fixes a bunch of stuck-in-geo issues goes away SetCollisionBounds( Vector( -10, -10, -10 ), Vector( 10, 10, 10 ) ); if ( m_bDistributed ) { DispatchParticleEffect( "mvm_cash_embers_red", PATTACH_ABSORIGIN_FOLLOW, this ); } else { DispatchParticleEffect( "mvm_cash_embers", PATTACH_ABSORIGIN_FOLLOW, this ); } // Store when this drops for time-based accounting - like with wave collection bonus m_nWaveNumber = MannVsMachineStats_GetCurrentWave(); // if m_nAmount != 0, we already call SetAmount m_nAmount = m_nAmount == 0 ? TFGameRules()->CalculateCurrencyAmount_ByType( GetPackSize() ) : m_nAmount; MannVsMachineStats_RoundEvent_CreditsDropped( m_nWaveNumber, m_nAmount ); if ( !m_bDistributed && TFObjectiveResource() ) { TFObjectiveResource()->AddMvMWorldMoney( m_nAmount ); } } //----------------------------------------------------------------------------- // Purpose: Blink off/on when about to expire and play expire sound //----------------------------------------------------------------------------- void CCurrencyPack::BlinkThink( void ) { // This means the pack was claimed by a player via Radius Currency Collection and // is likely flying toward them. Regardless, one second later it fires Touch(). if ( IsClaimed() ) return; ++m_blinkCount; SetRenderMode( kRenderTransAlpha ); if ( m_blinkCount & 0x1 ) { SetRenderColorA( 25 ); } else { SetRenderColorA( 255 ); } SetContextThink( &CCurrencyPack::BlinkThink, gpGlobals->curtime + TF_CURRENCYPACK_BLINK_DURATION, "CurrencyPackBlinkThink" ); } //----------------------------------------------------------------------------- // Become touchable when we are at rest //----------------------------------------------------------------------------- void CCurrencyPack::ComeToRest( void ) { BaseClass::ComeToRest(); if ( IsClaimed() || m_bDistributed ) return; // if we've come to rest in an area with no nav, just grant the money to the player if ( TheNavMesh->GetNavArea( GetAbsOrigin() ) == NULL ) { TFGameRules()->DistributeCurrencyAmount( m_nAmount ); m_bTouched = true; UTIL_Remove( this ); return; } // See if we've come to rest in a trigger_hurt for ( int i = 0; i < ITriggerHurtAutoList::AutoList().Count(); i++ ) { CTriggerHurt *pTrigger = static_cast( ITriggerHurtAutoList::AutoList()[i] ); if ( !pTrigger->m_bDisabled ) { Vector vecMins, vecMaxs; pTrigger->GetCollideable()->WorldSpaceSurroundingBounds( &vecMins, &vecMaxs ); if ( IsPointInBox( GetCollideable()->GetCollisionOrigin(), vecMins, vecMaxs ) ) { TFGameRules()->DistributeCurrencyAmount( m_nAmount ); m_bTouched = true; UTIL_Remove( this ); } } } } //----------------------------------------------------------------------------- // Purpose: Sets the value of a custom pack //----------------------------------------------------------------------------- void CCurrencyPack::SetAmount( float nAmount ) { Assert( GetPackSize() == TF_CURRENCY_PACK_CUSTOM ); // Never set an amount unless we're custom m_nAmount = nAmount; } //----------------------------------------------------------------------------- // Purpose: Distribute the money right away //----------------------------------------------------------------------------- void CCurrencyPack::DistributedBy( CBasePlayer* pMoneyMaker ) { TFGameRules()->DistributeCurrencyAmount( m_nAmount ); if ( pMoneyMaker ) { CTF_GameStats.Event_PlayerCollectedCurrency( pMoneyMaker, m_nAmount ); } m_bDistributed = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCurrencyPack::Precache( void ) { PrecacheScriptSound( TF_CURRENCYPACK_PICKUP_SOUND ); PrecacheScriptSound( TF_CURRENCYPACK_VANISH_SOUND ); PrecacheParticleSystem( "mvm_cash_embers" ); PrecacheParticleSystem( "mvm_cash_explosion" ); BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCurrencyPack::MyTouch( CBasePlayer *pPlayer ) { if ( ValidTouch( pPlayer ) && !m_bTouched ) { CTFPlayer *pTFTouchPlayer = ToTFPlayer( pPlayer ); if ( !pTFTouchPlayer ) return false; if ( pTFTouchPlayer->IsBot() ) return false; if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) { // Prevent losing team from grabbing money - screws up stats in checkpoints if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN ) { if ( TFGameRules()->GetWinningTeam() != pTFTouchPlayer->GetTeamNumber() ) return false; } // Scouts gain health when grabbing currency packs if ( pTFTouchPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SCOUT ) { const int nCurHealth = pTFTouchPlayer->GetHealth(); const int nMaxHealth = pTFTouchPlayer->GetMaxHealth(); int nHealth = nCurHealth < nMaxHealth ? 50 : 25; // If we cross the border into insanity, scale the reward const int nHealthCap = nMaxHealth * 4; if ( nCurHealth > nHealthCap ) { nHealth = RemapValClamped( nCurHealth, nHealthCap, (nHealthCap * 1.5f), 20, 5 ); } pTFTouchPlayer->TakeHealth( nHealth, DMG_IGNORE_MAXHEALTH ); } MannVsMachineStats_PlayerEvent_PickedUpCredits( pTFTouchPlayer, m_nWaveNumber, m_nAmount ); IGameEvent *event = gameeventmanager->CreateEvent( "mvm_pickup_currency" ); if ( event ) { event->SetInt( "player", pTFTouchPlayer->entindex() ); event->SetInt( "currency", m_nAmount ); gameeventmanager->FireEvent( event ); } // is the money blinking and about to burn up? if ( m_blinkCount > 0 ) { pTFTouchPlayer->AwardAchievement( ACHIEVEMENT_TF_MVM_PICKUP_MONEY_ABOUT_TO_EXPIRE ); } } CReliableBroadcastRecipientFilter filter; EmitSound( filter, entindex(), TF_CURRENCYPACK_PICKUP_SOUND ); if ( !m_bDistributed ) { TFGameRules()->DistributeCurrencyAmount( m_nAmount, pTFTouchPlayer ); CTF_GameStats.Event_PlayerCollectedCurrency( pTFTouchPlayer, m_nAmount ); } if ( ( !pTFTouchPlayer->IsPlayerClass( TF_CLASS_SPY ) ) || ( !pTFTouchPlayer->m_Shared.IsStealthed() && !pTFTouchPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) && !pTFTouchPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) ) { pTFTouchPlayer->SpeakConceptIfAllowed( MP_CONCEPT_MVM_MONEY_PICKUP ); } pTFTouchPlayer->SetLastObjectiveTime( gpGlobals->curtime ); m_bTouched = true; } return m_bTouched; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CCurrencyPackCustom::GetDefaultPowerupModel( void ) { // Custom packs should always be set to a value by hand Assert( m_nAmount > 0 ); // Try to pick a model that's appropriate to our drop amount (which is in our multiplier) if ( m_nAmount >= 25 ) return "models/items/currencypack_large.mdl"; if ( m_nAmount >= 10 ) return "models/items/currencypack_medium.mdl"; return "models/items/currencypack_small.mdl"; }