Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_ammo_pack.h"
  8. #include "tf_shareddefs.h"
  9. #include "ammodef.h"
  10. #include "tf_gamerules.h"
  11. #include "explode.h"
  12. #include "tf_gamestats.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //----------------------------------------------
  16. extern void SendProxy_FuncRotatingAngle( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID);
  17. // Network table.
  18. IMPLEMENT_SERVERCLASS_ST( CTFAmmoPack, DT_AmmoPack )
  19. SendPropVector( SENDINFO( m_vecInitialVelocity ), -1, SPROP_NOSCALE ),
  20. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  21. SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ),
  22. SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ),
  23. SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 7, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ),
  24. END_SEND_TABLE()
  25. BEGIN_DATADESC( CTFAmmoPack )
  26. DEFINE_THINKFUNC( FlyThink ),
  27. DEFINE_ENTITYFUNC( PackTouch ),
  28. END_DATADESC();
  29. LINK_ENTITY_TO_CLASS( tf_ammo_pack, CTFAmmoPack );
  30. PRECACHE_REGISTER( tf_ammo_pack );
  31. #define HALLOWEEN_MODEL "models/props_halloween/pumpkin_loot.mdl"
  32. #define CHRISTMAS_MODEL "models/items/tf_gift.mdl"
  33. void CTFAmmoPack::Spawn( void )
  34. {
  35. Precache();
  36. SetModel( STRING( GetModelName() ) );
  37. BaseClass::Spawn();
  38. SetNextThink( gpGlobals->curtime + 0.75f );
  39. SetThink( &CTFAmmoPack::FlyThink );
  40. SetTouch( &CTFAmmoPack::PackTouch );
  41. m_flCreationTime = gpGlobals->curtime;
  42. // no pickup until flythink
  43. m_bAllowOwnerPickup = false;
  44. m_bNoPickup = false;
  45. m_bHealthInstead = false;
  46. m_bEmptyPack = false;
  47. m_bObjGib = false;
  48. m_flBonusScale = 1.f;
  49. // no ammo to start
  50. memset( m_iAmmo, 0, sizeof(m_iAmmo) );
  51. // Die in 30 seconds
  52. SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + 30, "DieContext" );
  53. if ( IsX360() )
  54. {
  55. RemoveEffects( EF_ITEM_BLINK );
  56. }
  57. }
  58. void CTFAmmoPack::Precache( void )
  59. {
  60. PrecacheModel( "models/items/ammopack_medium.mdl" );
  61. if ( TFGameRules() )
  62. {
  63. if ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
  64. {
  65. PrecacheModel( HALLOWEEN_MODEL );
  66. PrecacheScriptSound( "Halloween.PumpkinDrop" );
  67. PrecacheScriptSound( "Halloween.PumpkinPickup" );
  68. }
  69. else if ( TFGameRules()->IsHolidayActive( kHoliday_Christmas ) )
  70. {
  71. PrecacheModel( CHRISTMAS_MODEL );
  72. PrecacheScriptSound( "Christmas.GiftDrop" );
  73. PrecacheScriptSound( "Christmas.GiftPickup" );
  74. }
  75. }
  76. }
  77. CTFAmmoPack *CTFAmmoPack::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, const char *pszModelName )
  78. {
  79. CTFAmmoPack *pAmmoPack = static_cast<CTFAmmoPack*>( CBaseAnimating::CreateNoSpawn( "tf_ammo_pack", vecOrigin, vecAngles, pOwner ) );
  80. if ( pAmmoPack )
  81. {
  82. pAmmoPack->SetModelName( AllocPooledString( pszModelName ) );
  83. DispatchSpawn( pAmmoPack );
  84. }
  85. return pAmmoPack;
  86. }
  87. ConVar tf_weapon_ragdoll_velocity_min( "tf_weapon_ragdoll_velocity_min", "100", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  88. ConVar tf_weapon_ragdoll_velocity_max( "tf_weapon_ragdoll_velocity_max", "150", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  89. ConVar tf_weapon_ragdoll_maxspeed( "tf_weapon_ragdoll_maxspeed", "300", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  90. void CTFAmmoPack::InitWeaponDrop( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon, int nSkin, bool bEmpty, bool bIsSuicide )
  91. {
  92. if ( !bEmpty )
  93. {
  94. // Might be a holiday pack.
  95. if ( !bIsSuicide && ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) || TFGameRules()->IsHolidayActive( kHoliday_TFBirthday ) ) )
  96. {
  97. float frand = (float)rand() / VALVE_RAND_MAX;
  98. if ( frand < 0.3f )
  99. {
  100. MakeHolidayPack();
  101. }
  102. }
  103. else if ( !bIsSuicide && TFGameRules()->IsHolidayActive( kHoliday_Christmas ) )
  104. {
  105. MakeHolidayPack();
  106. }
  107. // Fill the ammo pack with unused player ammo, if out add a minimum amount.
  108. int iPrimary = Max( 5, pPlayer->GetAmmoCount( TF_AMMO_PRIMARY ) );
  109. int iSecondary = Max( 5, pPlayer->GetAmmoCount( TF_AMMO_SECONDARY ) );
  110. int iMetal = Clamp( pPlayer->GetAmmoCount( TF_AMMO_METAL ), 5 , 100 );
  111. // Fill up the ammo pack.
  112. GiveAmmo( iPrimary, TF_AMMO_PRIMARY ); // Gets recalculated in PackTouch
  113. GiveAmmo( iSecondary, TF_AMMO_SECONDARY ); // Gets recalculated in PackTouch
  114. GiveAmmo( iMetal, TF_AMMO_METAL );
  115. SetHealthInstead( pWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX && pPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) );
  116. }
  117. else
  118. {
  119. // This pack has nothing in it.
  120. MakeEmptyPack();
  121. }
  122. Vector vecRight, vecUp;
  123. AngleVectors( EyeAngles(), NULL, &vecRight, &vecUp );
  124. // Calculate the initial impulse on the weapon.
  125. Vector vecImpulse( 0.0f, 0.0f, 0.0f );
  126. vecImpulse += vecUp * random->RandomFloat( -0.25, 0.25 );
  127. vecImpulse += vecRight * random->RandomFloat( -0.25, 0.25 );
  128. VectorNormalize( vecImpulse );
  129. vecImpulse *= random->RandomFloat( tf_weapon_ragdoll_velocity_min.GetFloat(), tf_weapon_ragdoll_velocity_max.GetFloat() );
  130. vecImpulse += GetAbsVelocity();
  131. // Cap the impulse.
  132. float flSpeed = vecImpulse.Length();
  133. if ( flSpeed > tf_weapon_ragdoll_maxspeed.GetFloat() )
  134. {
  135. VectorScale( vecImpulse, tf_weapon_ragdoll_maxspeed.GetFloat() / flSpeed, vecImpulse );
  136. }
  137. if ( VPhysicsGetObject() )
  138. {
  139. // We can probably remove this when the mass on the weapons is correct!
  140. VPhysicsGetObject()->SetMass( 25.0f );
  141. AngularImpulse angImpulse( 0, random->RandomFloat( 0, 100 ), 0 );
  142. VPhysicsGetObject()->SetVelocityInstantaneous( &vecImpulse, &angImpulse );
  143. }
  144. SetInitialVelocity( vecImpulse );
  145. m_nSkin = nSkin; // Copy the skin from the model we're copying
  146. // Give the ammo pack some health, so that trains can destroy it.
  147. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  148. m_takedamage = DAMAGE_YES;
  149. SetHealth( 900 );
  150. SetBodygroup( 1, 1 );
  151. }
  152. void CTFAmmoPack::MakeHolidayPack( void )
  153. {
  154. // don't want special ammo packs during a competitive match
  155. if ( TFGameRules()->IsMatchTypeCompetitive() )
  156. return;
  157. // Only do this on the halloween maps.
  158. if ( TFGameRules()->IsHolidayActive( kHoliday_Halloween )
  159. && TFGameRules()->IsHolidayMap( kHoliday_Halloween )
  160. && !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  161. {
  162. m_PackType = AP_HALLOWEEN;
  163. SetModelIndex( modelinfo->GetModelIndex( HALLOWEEN_MODEL ) );
  164. SetContextThink( &CTFAmmoPack::DropSoundThink, gpGlobals->curtime + 0.1f, "DROP_SOUND_THINK" );
  165. }
  166. else if ( TFGameRules()->ShouldMakeChristmasAmmoPack() )
  167. {
  168. m_PackType = AP_CHRISTMAS;
  169. SetModelIndex( modelinfo->GetModelIndex( CHRISTMAS_MODEL ) );
  170. SetContextThink( &CTFAmmoPack::DropSoundThink, gpGlobals->curtime + 0.1f, "DROP_SOUND_THINK" );
  171. }
  172. }
  173. void CTFAmmoPack::SetBonusScale( float flBonusScale /*= 1.f*/ )
  174. {
  175. m_flBonusScale = flBonusScale;
  176. }
  177. void CTFAmmoPack::SetInitialVelocity( Vector &vecVelocity )
  178. {
  179. if ( m_PackType != AP_NORMAL )
  180. {
  181. // Unusual physics for the halloween/christmas packs to make them noticable.
  182. SetMoveType( MOVETYPE_FLYGRAVITY );
  183. SetAbsVelocity( vecVelocity * 2.f + Vector(0,0,200) );
  184. SetAbsAngles( QAngle(0,0,0) );
  185. UseClientSideAnimation();
  186. ResetSequence( LookupSequence("idle") );
  187. }
  188. m_vecInitialVelocity = vecVelocity;
  189. }
  190. void CTFAmmoPack::SetPickupThinkTime( float flNewThinkTime )
  191. {
  192. SetNextThink( gpGlobals->curtime + flNewThinkTime );
  193. }
  194. int CTFAmmoPack::GiveAmmo( int iCount, int iAmmoType )
  195. {
  196. if (iAmmoType == -1 || iAmmoType >= TF_AMMO_COUNT )
  197. {
  198. Msg("ERROR: Attempting to give unknown ammo type (%d)\n", iAmmoType);
  199. return 0;
  200. }
  201. m_iAmmo[iAmmoType] = iCount;
  202. return iCount;
  203. }
  204. void CTFAmmoPack::DropSoundThink( void )
  205. {
  206. if ( m_PackType == AP_HALLOWEEN )
  207. {
  208. EmitSound( "Halloween.PumpkinDrop" );
  209. }
  210. else if ( m_PackType == AP_CHRISTMAS )
  211. {
  212. EmitSound( "Christmas.GiftDrop" );
  213. }
  214. }
  215. void CTFAmmoPack::FlyThink( void )
  216. {
  217. m_bAllowOwnerPickup = true;
  218. m_bNoPickup = false;
  219. }
  220. void CTFAmmoPack::PackTouch( CBaseEntity *pOther )
  221. {
  222. Assert( pOther );
  223. if ( pOther->IsWorld() && ( m_PackType != AP_NORMAL ) )
  224. {
  225. Vector absVel = GetAbsVelocity();
  226. SetAbsVelocity( Vector( 0,0,absVel.z ) );
  227. return;
  228. }
  229. if( !pOther->IsPlayer() )
  230. return;
  231. if( !pOther->IsAlive() )
  232. return;
  233. if ( m_bNoPickup )
  234. return;
  235. //Don't let the person who threw this ammo pick it up until it hits the ground.
  236. //This way we can throw ammo to people, but not touch it as soon as we throw it ourselves
  237. if( GetOwnerEntity() == pOther && m_bAllowOwnerPickup == false )
  238. return;
  239. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  240. Assert( pPlayer );
  241. if ( m_bEmptyPack )
  242. {
  243. // Since we drop our empty packs as fakeouts, we never pick up our own empties while stealthed.
  244. if ( GetOwnerEntity() == pOther && ( pPlayer->m_Shared.IsStealthed() ||
  245. pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
  246. return;
  247. // "Empty" packs can be picked up.
  248. // Packs that can't be grabbed don't fit the expectations of the player.
  249. GiveAmmo( 1, TF_AMMO_PRIMARY );
  250. UTIL_Remove( this );
  251. return;
  252. }
  253. // The sandwich gives health instead of ammo
  254. if ( m_bHealthInstead )
  255. {
  256. // Let the sandwich fall to the ground for a bit so that people see it
  257. if ( !m_bAllowOwnerPickup )
  258. return;
  259. // Scouts get a little more, as a reference to the scout movie
  260. int iAmount = ( pPlayer->IsPlayerClass(TF_CLASS_SCOUT) ) ? 75 : 50;
  261. pPlayer->TakeHealth( iAmount, DMG_GENERIC );
  262. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  263. if ( event )
  264. {
  265. event->SetInt( "amount", iAmount );
  266. event->SetInt( "entindex", pPlayer->entindex() );
  267. event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX );
  268. gameeventmanager->FireEvent( event );
  269. }
  270. event = gameeventmanager->CreateEvent( "player_stealsandvich" );
  271. if ( event )
  272. {
  273. if ( ToTFPlayer( GetOwnerEntity() ) )
  274. {
  275. event->SetInt( "owner", ToTFPlayer( GetOwnerEntity() )->GetUserID() );
  276. }
  277. event->SetInt( "target", pPlayer->GetUserID() );
  278. gameeventmanager->FireEvent( event );
  279. }
  280. UTIL_Remove( this );
  281. return;
  282. }
  283. float flAmmoRatio = 0.5f;
  284. int iMaxPrimary = pPlayer->GetMaxAmmo(TF_AMMO_PRIMARY);
  285. GiveAmmo( ceil( iMaxPrimary * flAmmoRatio ), TF_AMMO_PRIMARY );
  286. int iMaxSecondary = pPlayer->GetMaxAmmo(TF_AMMO_SECONDARY);
  287. GiveAmmo( ceil( iMaxSecondary * flAmmoRatio ), TF_AMMO_SECONDARY );
  288. int iAmmoTaken = 0;
  289. for ( int i=0;i<TF_AMMO_COUNT;i++ )
  290. {
  291. int iAmmoGiven = pPlayer->GiveAmmo( m_iAmmo[i], i );
  292. if ( iAmmoGiven > 0 && i == TF_AMMO_METAL && m_bObjGib && pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
  293. {
  294. pPlayer->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_WASTE_METAL_GRIND, iAmmoGiven );
  295. }
  296. iAmmoTaken += iAmmoGiven;
  297. }
  298. // give them a chunk of cloak power
  299. if ( pPlayer->m_Shared.AddToSpyCloakMeter( 100.0f * flAmmoRatio ) )
  300. {
  301. iAmmoTaken++;
  302. }
  303. if ( pPlayer->AddToSpyKnife( 100.0f * flAmmoRatio, false ) )
  304. {
  305. iAmmoTaken++;
  306. }
  307. // Add Charge if applicable
  308. int iAmmoIsCharge = 0;
  309. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iAmmoIsCharge, ammo_gives_charge );
  310. if ( iAmmoIsCharge )
  311. {
  312. float flCurrentCharge = pPlayer->m_Shared.GetDemomanChargeMeter();
  313. if ( flCurrentCharge < 100.0f )
  314. {
  315. pPlayer->m_Shared.SetDemomanChargeMeter( flCurrentCharge + flAmmoRatio * 100.0f );
  316. iAmmoTaken++;
  317. }
  318. }
  319. if ( pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
  320. {
  321. int iMaxGrenades1 = pPlayer->GetMaxAmmo( TF_AMMO_GRENADES1 );
  322. iAmmoTaken += pPlayer->GiveAmmo( ceil(iMaxGrenades1 * flAmmoRatio), TF_AMMO_GRENADES1 );
  323. }
  324. if ( m_PackType == AP_HALLOWEEN )
  325. {
  326. // Send a message for the achievement tracking.
  327. IGameEvent *event = gameeventmanager->CreateEvent( "halloween_pumpkin_grab" );
  328. if ( event )
  329. {
  330. event->SetInt( "userid", pPlayer->GetUserID() );
  331. gameeventmanager->FireEvent( event );
  332. }
  333. float flBuffDuration = m_flBonusScale * 3.f;
  334. if ( !pPlayer->m_Shared.InCond( TF_COND_CRITBOOSTED_PUMPKIN ) || (pPlayer->m_Shared.GetConditionDuration(TF_COND_CRITBOOSTED_PUMPKIN) < flBuffDuration) )
  335. {
  336. pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED_PUMPKIN, flBuffDuration );
  337. }
  338. pPlayer->EmitSound( "Halloween.PumpkinPickup" );
  339. m_PackType = AP_NORMAL; // Touch once.
  340. iAmmoTaken++;
  341. }
  342. else if ( m_PackType == AP_CHRISTMAS )
  343. {
  344. // Send a message for the achievement tracking.
  345. IGameEvent *event = gameeventmanager->CreateEvent( "christmas_gift_grab" );
  346. if ( event )
  347. {
  348. event->SetInt( "userid", pPlayer->GetUserID() );
  349. gameeventmanager->FireEvent( event );
  350. }
  351. pPlayer->EmitSound( "Christmas.GiftPickup" );
  352. m_PackType = AP_NORMAL; // Touch once.
  353. iAmmoTaken++;
  354. }
  355. if ( iAmmoTaken > 0 )
  356. {
  357. CTF_GameStats.Event_PlayerAmmokitPickup( pPlayer );
  358. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  359. if( event )
  360. {
  361. event->SetInt( "userid", pPlayer->GetUserID() );
  362. event->SetString( "item", "tf_ammo_pack" );
  363. gameeventmanager->FireEvent( event );
  364. }
  365. UTIL_Remove( this );
  366. }
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose:
  370. //-----------------------------------------------------------------------------
  371. unsigned int CTFAmmoPack::PhysicsSolidMaskForEntity( void ) const
  372. {
  373. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_DEBRIS;
  374. }