You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
13 KiB
447 lines
13 KiB
//========= 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; |
|
}
|
|
|