diff options
Diffstat (limited to 'game/server/tf/tf_ammo_pack.cpp')
| -rw-r--r-- | game/server/tf/tf_ammo_pack.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/game/server/tf/tf_ammo_pack.cpp b/game/server/tf/tf_ammo_pack.cpp new file mode 100644 index 0000000..9416ed2 --- /dev/null +++ b/game/server/tf/tf_ammo_pack.cpp @@ -0,0 +1,447 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "tf_ammo_pack.h" +#include "tf_shareddefs.h" +#include "ammodef.h" +#include "tf_gamerules.h" +#include "explode.h" +#include "tf_gamestats.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//---------------------------------------------- + +extern void SendProxy_FuncRotatingAngle( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID); + +// Network table. +IMPLEMENT_SERVERCLASS_ST( CTFAmmoPack, DT_AmmoPack ) + SendPropVector( SENDINFO( m_vecInitialVelocity ), -1, SPROP_NOSCALE ), + SendPropExclude( "DT_BaseEntity", "m_angRotation" ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ), +END_SEND_TABLE() + +BEGIN_DATADESC( CTFAmmoPack ) + DEFINE_THINKFUNC( FlyThink ), + DEFINE_ENTITYFUNC( PackTouch ), +END_DATADESC(); + +LINK_ENTITY_TO_CLASS( tf_ammo_pack, CTFAmmoPack ); + +PRECACHE_REGISTER( tf_ammo_pack ); + +#define HALLOWEEN_MODEL "models/props_halloween/pumpkin_loot.mdl" +#define CHRISTMAS_MODEL "models/items/tf_gift.mdl" + +void CTFAmmoPack::Spawn( void ) +{ + Precache(); + SetModel( STRING( GetModelName() ) ); + BaseClass::Spawn(); + + SetNextThink( gpGlobals->curtime + 0.75f ); + SetThink( &CTFAmmoPack::FlyThink ); + + SetTouch( &CTFAmmoPack::PackTouch ); + + m_flCreationTime = gpGlobals->curtime; + + // no pickup until flythink + m_bAllowOwnerPickup = false; + m_bNoPickup = false; + m_bHealthInstead = false; + m_bEmptyPack = false; + m_bObjGib = false; + m_flBonusScale = 1.f; + + // no ammo to start + memset( m_iAmmo, 0, sizeof(m_iAmmo) ); + + // Die in 30 seconds + SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + 30, "DieContext" ); + + if ( IsX360() ) + { + RemoveEffects( EF_ITEM_BLINK ); + } +} + +void CTFAmmoPack::Precache( void ) +{ + PrecacheModel( "models/items/ammopack_medium.mdl" ); + + if ( TFGameRules() ) + { + if ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) ) + { + PrecacheModel( HALLOWEEN_MODEL ); + PrecacheScriptSound( "Halloween.PumpkinDrop" ); + PrecacheScriptSound( "Halloween.PumpkinPickup" ); + } + else if ( TFGameRules()->IsHolidayActive( kHoliday_Christmas ) ) + { + PrecacheModel( CHRISTMAS_MODEL ); + PrecacheScriptSound( "Christmas.GiftDrop" ); + PrecacheScriptSound( "Christmas.GiftPickup" ); + } + } +} + +CTFAmmoPack *CTFAmmoPack::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, const char *pszModelName ) +{ + CTFAmmoPack *pAmmoPack = static_cast<CTFAmmoPack*>( CBaseAnimating::CreateNoSpawn( "tf_ammo_pack", vecOrigin, vecAngles, pOwner ) ); + if ( pAmmoPack ) + { + pAmmoPack->SetModelName( AllocPooledString( pszModelName ) ); + DispatchSpawn( pAmmoPack ); + } + + return pAmmoPack; +} + +ConVar tf_weapon_ragdoll_velocity_min( "tf_weapon_ragdoll_velocity_min", "100", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); +ConVar tf_weapon_ragdoll_velocity_max( "tf_weapon_ragdoll_velocity_max", "150", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); +ConVar tf_weapon_ragdoll_maxspeed( "tf_weapon_ragdoll_maxspeed", "300", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); + +void CTFAmmoPack::InitWeaponDrop( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon, int nSkin, bool bEmpty, bool bIsSuicide ) +{ + if ( !bEmpty ) + { + // Might be a holiday pack. + if ( !bIsSuicide && ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) || TFGameRules()->IsHolidayActive( kHoliday_TFBirthday ) ) ) + { + float frand = (float)rand() / VALVE_RAND_MAX; + if ( frand < 0.3f ) + { + MakeHolidayPack(); + } + } + else if ( !bIsSuicide && TFGameRules()->IsHolidayActive( kHoliday_Christmas ) ) + { + MakeHolidayPack(); + } + + // Fill the ammo pack with unused player ammo, if out add a minimum amount. + int iPrimary = Max( 5, pPlayer->GetAmmoCount( TF_AMMO_PRIMARY ) ); + int iSecondary = Max( 5, pPlayer->GetAmmoCount( TF_AMMO_SECONDARY ) ); + int iMetal = Clamp( pPlayer->GetAmmoCount( TF_AMMO_METAL ), 5 , 100 ); + + // Fill up the ammo pack. + GiveAmmo( iPrimary, TF_AMMO_PRIMARY ); // Gets recalculated in PackTouch + GiveAmmo( iSecondary, TF_AMMO_SECONDARY ); // Gets recalculated in PackTouch + GiveAmmo( iMetal, TF_AMMO_METAL ); + SetHealthInstead( pWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX && pPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ); + } + else + { + // This pack has nothing in it. + MakeEmptyPack(); + } + + Vector vecRight, vecUp; + AngleVectors( EyeAngles(), NULL, &vecRight, &vecUp ); + + // Calculate the initial impulse on the weapon. + Vector vecImpulse( 0.0f, 0.0f, 0.0f ); + vecImpulse += vecUp * random->RandomFloat( -0.25, 0.25 ); + vecImpulse += vecRight * random->RandomFloat( -0.25, 0.25 ); + VectorNormalize( vecImpulse ); + vecImpulse *= random->RandomFloat( tf_weapon_ragdoll_velocity_min.GetFloat(), tf_weapon_ragdoll_velocity_max.GetFloat() ); + vecImpulse += GetAbsVelocity(); + + // Cap the impulse. + float flSpeed = vecImpulse.Length(); + if ( flSpeed > tf_weapon_ragdoll_maxspeed.GetFloat() ) + { + VectorScale( vecImpulse, tf_weapon_ragdoll_maxspeed.GetFloat() / flSpeed, vecImpulse ); + } + + if ( VPhysicsGetObject() ) + { + // We can probably remove this when the mass on the weapons is correct! + VPhysicsGetObject()->SetMass( 25.0f ); + AngularImpulse angImpulse( 0, random->RandomFloat( 0, 100 ), 0 ); + VPhysicsGetObject()->SetVelocityInstantaneous( &vecImpulse, &angImpulse ); + } + + SetInitialVelocity( vecImpulse ); + + m_nSkin = nSkin; // Copy the skin from the model we're copying + + // Give the ammo pack some health, so that trains can destroy it. + SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + m_takedamage = DAMAGE_YES; + SetHealth( 900 ); + + SetBodygroup( 1, 1 ); +} + +void CTFAmmoPack::MakeHolidayPack( void ) +{ + // don't want special ammo packs during a competitive match + if ( TFGameRules()->IsMatchTypeCompetitive() ) + return; + + // Only do this on the halloween maps. + if ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) + && TFGameRules()->IsHolidayMap( kHoliday_Halloween ) + && !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) ) + { + m_PackType = AP_HALLOWEEN; + SetModelIndex( modelinfo->GetModelIndex( HALLOWEEN_MODEL ) ); + SetContextThink( &CTFAmmoPack::DropSoundThink, gpGlobals->curtime + 0.1f, "DROP_SOUND_THINK" ); + } + else if ( TFGameRules()->ShouldMakeChristmasAmmoPack() ) + { + m_PackType = AP_CHRISTMAS; + SetModelIndex( modelinfo->GetModelIndex( CHRISTMAS_MODEL ) ); + SetContextThink( &CTFAmmoPack::DropSoundThink, gpGlobals->curtime + 0.1f, "DROP_SOUND_THINK" ); + } +} + + +void CTFAmmoPack::SetBonusScale( float flBonusScale /*= 1.f*/ ) +{ + m_flBonusScale = flBonusScale; +} + + +void CTFAmmoPack::SetInitialVelocity( Vector &vecVelocity ) +{ + if ( m_PackType != AP_NORMAL ) + { + // Unusual physics for the halloween/christmas packs to make them noticable. + SetMoveType( MOVETYPE_FLYGRAVITY ); + SetAbsVelocity( vecVelocity * 2.f + Vector(0,0,200) ); + SetAbsAngles( QAngle(0,0,0) ); + UseClientSideAnimation(); + ResetSequence( LookupSequence("idle") ); + } + m_vecInitialVelocity = vecVelocity; +} + +void CTFAmmoPack::SetPickupThinkTime( float flNewThinkTime ) +{ + SetNextThink( gpGlobals->curtime + flNewThinkTime ); +} + +int CTFAmmoPack::GiveAmmo( int iCount, int iAmmoType ) +{ + if (iAmmoType == -1 || iAmmoType >= TF_AMMO_COUNT ) + { + Msg("ERROR: Attempting to give unknown ammo type (%d)\n", iAmmoType); + return 0; + } + + m_iAmmo[iAmmoType] = iCount; + + return iCount; +} + +void CTFAmmoPack::DropSoundThink( void ) +{ + if ( m_PackType == AP_HALLOWEEN ) + { + EmitSound( "Halloween.PumpkinDrop" ); + } + else if ( m_PackType == AP_CHRISTMAS ) + { + EmitSound( "Christmas.GiftDrop" ); + } +} + +void CTFAmmoPack::FlyThink( void ) +{ + m_bAllowOwnerPickup = true; + m_bNoPickup = false; +} + +void CTFAmmoPack::PackTouch( CBaseEntity *pOther ) +{ + Assert( pOther ); + + if ( pOther->IsWorld() && ( m_PackType != AP_NORMAL ) ) + { + Vector absVel = GetAbsVelocity(); + SetAbsVelocity( Vector( 0,0,absVel.z ) ); + return; + } + + if( !pOther->IsPlayer() ) + return; + + if( !pOther->IsAlive() ) + return; + + if ( m_bNoPickup ) + return; + + //Don't let the person who threw this ammo pick it up until it hits the ground. + //This way we can throw ammo to people, but not touch it as soon as we throw it ourselves + if( GetOwnerEntity() == pOther && m_bAllowOwnerPickup == false ) + return; + + CTFPlayer *pPlayer = ToTFPlayer( pOther ); + Assert( pPlayer ); + + if ( m_bEmptyPack ) + { + // Since we drop our empty packs as fakeouts, we never pick up our own empties while stealthed. + if ( GetOwnerEntity() == pOther && ( pPlayer->m_Shared.IsStealthed() || + pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) ) + return; + + // "Empty" packs can be picked up. + // Packs that can't be grabbed don't fit the expectations of the player. + GiveAmmo( 1, TF_AMMO_PRIMARY ); + UTIL_Remove( this ); + return; + } + + // The sandwich gives health instead of ammo + if ( m_bHealthInstead ) + { + // Let the sandwich fall to the ground for a bit so that people see it + if ( !m_bAllowOwnerPickup ) + return; + + // Scouts get a little more, as a reference to the scout movie + int iAmount = ( pPlayer->IsPlayerClass(TF_CLASS_SCOUT) ) ? 75 : 50; + pPlayer->TakeHealth( iAmount, DMG_GENERIC ); + IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" ); + if ( event ) + { + event->SetInt( "amount", iAmount ); + event->SetInt( "entindex", pPlayer->entindex() ); + event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX ); + gameeventmanager->FireEvent( event ); + } + + event = gameeventmanager->CreateEvent( "player_stealsandvich" ); + if ( event ) + { + if ( ToTFPlayer( GetOwnerEntity() ) ) + { + event->SetInt( "owner", ToTFPlayer( GetOwnerEntity() )->GetUserID() ); + } + event->SetInt( "target", pPlayer->GetUserID() ); + gameeventmanager->FireEvent( event ); + } + + UTIL_Remove( this ); + return; + } + + float flAmmoRatio = 0.5f; + + int iMaxPrimary = pPlayer->GetMaxAmmo(TF_AMMO_PRIMARY); + GiveAmmo( ceil( iMaxPrimary * flAmmoRatio ), TF_AMMO_PRIMARY ); + + int iMaxSecondary = pPlayer->GetMaxAmmo(TF_AMMO_SECONDARY); + GiveAmmo( ceil( iMaxSecondary * flAmmoRatio ), TF_AMMO_SECONDARY ); + + int iAmmoTaken = 0; + + for ( int i=0;i<TF_AMMO_COUNT;i++ ) + { + int iAmmoGiven = pPlayer->GiveAmmo( m_iAmmo[i], i ); + if ( iAmmoGiven > 0 && i == TF_AMMO_METAL && m_bObjGib && pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) ) + { + pPlayer->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_WASTE_METAL_GRIND, iAmmoGiven ); + } + iAmmoTaken += iAmmoGiven; + } + + // give them a chunk of cloak power + if ( pPlayer->m_Shared.AddToSpyCloakMeter( 100.0f * flAmmoRatio ) ) + { + iAmmoTaken++; + } + + if ( pPlayer->AddToSpyKnife( 100.0f * flAmmoRatio, false ) ) + { + iAmmoTaken++; + } + + // Add Charge if applicable + int iAmmoIsCharge = 0; + CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iAmmoIsCharge, ammo_gives_charge ); + if ( iAmmoIsCharge ) + { + float flCurrentCharge = pPlayer->m_Shared.GetDemomanChargeMeter(); + if ( flCurrentCharge < 100.0f ) + { + pPlayer->m_Shared.SetDemomanChargeMeter( flCurrentCharge + flAmmoRatio * 100.0f ); + iAmmoTaken++; + } + } + + if ( pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) ) + { + int iMaxGrenades1 = pPlayer->GetMaxAmmo( TF_AMMO_GRENADES1 ); + iAmmoTaken += pPlayer->GiveAmmo( ceil(iMaxGrenades1 * flAmmoRatio), TF_AMMO_GRENADES1 ); + } + + if ( m_PackType == AP_HALLOWEEN ) + { + // Send a message for the achievement tracking. + IGameEvent *event = gameeventmanager->CreateEvent( "halloween_pumpkin_grab" ); + if ( event ) + { + event->SetInt( "userid", pPlayer->GetUserID() ); + gameeventmanager->FireEvent( event ); + } + + float flBuffDuration = m_flBonusScale * 3.f; + if ( !pPlayer->m_Shared.InCond( TF_COND_CRITBOOSTED_PUMPKIN ) || (pPlayer->m_Shared.GetConditionDuration(TF_COND_CRITBOOSTED_PUMPKIN) < flBuffDuration) ) + { + pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED_PUMPKIN, flBuffDuration ); + } + pPlayer->EmitSound( "Halloween.PumpkinPickup" ); + m_PackType = AP_NORMAL; // Touch once. + iAmmoTaken++; + } + else if ( m_PackType == AP_CHRISTMAS ) + { + // Send a message for the achievement tracking. + IGameEvent *event = gameeventmanager->CreateEvent( "christmas_gift_grab" ); + if ( event ) + { + event->SetInt( "userid", pPlayer->GetUserID() ); + gameeventmanager->FireEvent( event ); + } + pPlayer->EmitSound( "Christmas.GiftPickup" ); + m_PackType = AP_NORMAL; // Touch once. + iAmmoTaken++; + } + + if ( iAmmoTaken > 0 ) + { + CTF_GameStats.Event_PlayerAmmokitPickup( pPlayer ); + + IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); + if( event ) + { + event->SetInt( "userid", pPlayer->GetUserID() ); + event->SetString( "item", "tf_ammo_pack" ); + gameeventmanager->FireEvent( event ); + } + + UTIL_Remove( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +unsigned int CTFAmmoPack::PhysicsSolidMaskForEntity( void ) const +{ + return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_DEBRIS; +} |