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.

429 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. //=============================================================================
  5. #include "cbase.h"
  6. #include "tf_weapon_lunchbox.h"
  7. #include "tf_fx_shared.h"
  8. // Client specific.
  9. #ifdef CLIENT_DLL
  10. #include "c_tf_player.h"
  11. // Server specific.
  12. #else
  13. #include "tf_player.h"
  14. #include "entity_healthkit.h"
  15. #include "econ_item_view.h"
  16. #include "econ_item_system.h"
  17. #include "tf_gamestats.h"
  18. #endif
  19. //=============================================================================
  20. //
  21. // Weapon Lunchbox tables.
  22. //
  23. IMPLEMENT_NETWORKCLASS_ALIASED( TFLunchBox, DT_WeaponLunchBox )
  24. BEGIN_NETWORK_TABLE( CTFLunchBox, DT_WeaponLunchBox )
  25. END_NETWORK_TABLE()
  26. BEGIN_PREDICTION_DATA( CTFLunchBox )
  27. END_PREDICTION_DATA()
  28. LINK_ENTITY_TO_CLASS( tf_weapon_lunchbox, CTFLunchBox );
  29. PRECACHE_WEAPON_REGISTER( tf_weapon_lunchbox );
  30. //=============================================================================
  31. IMPLEMENT_NETWORKCLASS_ALIASED( TFLunchBox_Drink, DT_TFLunchBox_Drink )
  32. BEGIN_NETWORK_TABLE( CTFLunchBox_Drink, DT_TFLunchBox_Drink )
  33. END_NETWORK_TABLE()
  34. BEGIN_PREDICTION_DATA( CTFLunchBox_Drink )
  35. END_PREDICTION_DATA()
  36. LINK_ENTITY_TO_CLASS( tf_weapon_lunchbox_drink, CTFLunchBox_Drink );
  37. PRECACHE_WEAPON_REGISTER( tf_weapon_lunchbox_drink );
  38. // Server specific.
  39. #ifndef CLIENT_DLL
  40. BEGIN_DATADESC( CTFLunchBox )
  41. END_DATADESC()
  42. #endif
  43. #define LUNCHBOX_DROP_MODEL "models/items/plate.mdl"
  44. #define LUNCHBOX_STEAK_DROP_MODEL "models/items/plate_steak.mdl"
  45. #define LUNCHBOX_ROBOT_DROP_MODEL "models/items/plate_robo_sandwich.mdl"
  46. #define LUNCHBOX_FESTIVE_DROP_MODEL "models/items/plate_sandwich_xmas.mdl"
  47. #define LUNCHBOX_CHOCOLATE_BAR "models/props_halloween/halloween_medkit_small.mdl"
  48. #define LUNCHBOX_DROPPED_MINS Vector( -17, -17, -10 )
  49. #define LUNCHBOX_DROPPED_MAXS Vector( 17, 17, 10 )
  50. static const char *s_pszLunchboxMaxHealThink = "LunchboxMaxHealThink";
  51. //=============================================================================
  52. //
  53. // Weapon Lunchbox functions.
  54. //
  55. //-----------------------------------------------------------------------------
  56. // Purpose:
  57. //-----------------------------------------------------------------------------
  58. CTFLunchBox::CTFLunchBox()
  59. {
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose:
  63. //-----------------------------------------------------------------------------
  64. void CTFLunchBox::UpdateOnRemove( void )
  65. {
  66. #ifndef CLIENT_DLL
  67. // If we're removed, we remove any dropped powerups. This prevents an exploit
  68. // where they switch classes away & back to get another lunchbox to drop with.
  69. if ( m_hThrownPowerup )
  70. {
  71. UTIL_Remove( m_hThrownPowerup );
  72. }
  73. #endif
  74. BaseClass::UpdateOnRemove();
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. //-----------------------------------------------------------------------------
  79. void CTFLunchBox::Precache( void )
  80. {
  81. if ( DropAllowed() )
  82. {
  83. PrecacheModel( "models/items/medkit_medium.mdl" );
  84. PrecacheModel( "models/items/medkit_medium_bday.mdl" );
  85. PrecacheModel( LUNCHBOX_DROP_MODEL );
  86. PrecacheModel( LUNCHBOX_STEAK_DROP_MODEL );
  87. PrecacheModel( LUNCHBOX_ROBOT_DROP_MODEL );
  88. PrecacheModel( LUNCHBOX_FESTIVE_DROP_MODEL );
  89. PrecacheModel( LUNCHBOX_CHOCOLATE_BAR );
  90. }
  91. BaseClass::Precache();
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. void CTFLunchBox::WeaponReset( void )
  97. {
  98. BaseClass::WeaponReset();
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. //-----------------------------------------------------------------------------
  103. bool CTFLunchBox::UsesPrimaryAmmo( void )
  104. {
  105. return CBaseCombatWeapon::UsesPrimaryAmmo();
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //-----------------------------------------------------------------------------
  110. bool CTFLunchBox::DropAllowed( void )
  111. {
  112. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  113. if ( pOwner )
  114. {
  115. if ( pOwner->m_Shared.InCond( TF_COND_TAUNTING ) )
  116. return false;
  117. }
  118. return true;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. void CTFLunchBox::PrimaryAttack( void )
  124. {
  125. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  126. if ( !pOwner )
  127. return;
  128. if ( !HasAmmo() )
  129. return;
  130. #if GAME_DLL
  131. pOwner->Taunt();
  132. m_flNextPrimaryAttack = pOwner->GetTauntRemoveTime() + 0.1f;
  133. #else
  134. m_flNextPrimaryAttack = gpGlobals->curtime + 2.0f; // this will be corrected by the game server
  135. #endif
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Purpose:
  139. //-----------------------------------------------------------------------------
  140. void CTFLunchBox::SecondaryAttack( void )
  141. {
  142. if ( !DropAllowed() )
  143. return;
  144. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  145. if ( !pPlayer )
  146. return;
  147. if ( !HasAmmo() )
  148. return;
  149. #ifndef CLIENT_DLL
  150. if ( m_hThrownPowerup )
  151. {
  152. UTIL_Remove( m_hThrownPowerup );
  153. }
  154. // Throw out the medikit
  155. Vector vecSrc = pPlayer->EyePosition() + Vector(0,0,-8);
  156. QAngle angForward = pPlayer->EyeAngles() + QAngle(-10,0,0);
  157. int nLunchBoxType = GetLunchboxType();
  158. const char *pszHealthKit;
  159. switch ( nLunchBoxType )
  160. {
  161. case LUNCHBOX_ADDS_MAXHEALTH:
  162. pszHealthKit = "item_healthkit_small";
  163. break;
  164. case LUNCHBOX_ADDS_AMMO:
  165. pszHealthKit = "item_healthammokit";
  166. break;
  167. default:
  168. pszHealthKit = "item_healthkit_medium";
  169. }
  170. CHealthKit *pMedKit = assert_cast<CHealthKit*>( CBaseEntity::Create( pszHealthKit, vecSrc, angForward, pPlayer ) );
  171. if ( pMedKit )
  172. {
  173. Vector vecForward, vecRight, vecUp;
  174. AngleVectors( angForward, &vecForward, &vecRight, &vecUp );
  175. Vector vecVelocity = vecForward * 500.0;
  176. if ( nLunchBoxType == LUNCHBOX_ADDS_MINICRITS )
  177. {
  178. pMedKit->SetModel( LUNCHBOX_STEAK_DROP_MODEL );
  179. }
  180. else if ( nLunchBoxType == LUNCHBOX_STANDARD_ROBO )
  181. {
  182. pMedKit->SetModel( LUNCHBOX_ROBOT_DROP_MODEL );
  183. pMedKit->m_nSkin = ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
  184. }
  185. else if ( nLunchBoxType == LUNCHBOX_STANDARD_FESTIVE )
  186. {
  187. pMedKit->SetModel( LUNCHBOX_FESTIVE_DROP_MODEL );
  188. pMedKit->m_nSkin = ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
  189. }
  190. else if ( nLunchBoxType == LUNCHBOX_ADDS_MAXHEALTH )
  191. {
  192. pMedKit->SetModel( LUNCHBOX_CHOCOLATE_BAR );
  193. }
  194. else
  195. {
  196. pMedKit->SetModel( LUNCHBOX_DROP_MODEL );
  197. }
  198. // clear out the overrides so the thrown sandvich/steak look correct in either vision mode
  199. pMedKit->ClearModelIndexOverrides();
  200. pMedKit->SetAbsAngles( vec3_angle );
  201. pMedKit->SetSize( LUNCHBOX_DROPPED_MINS, LUNCHBOX_DROPPED_MAXS );
  202. // the thrower has to wait 0.3 to pickup the powerup (so he can throw it while running forward)
  203. pMedKit->DropSingleInstance( vecVelocity, pPlayer, 0.3 );
  204. }
  205. m_hThrownPowerup = pMedKit;
  206. #endif
  207. pPlayer->RemoveAmmo( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot, m_iPrimaryAmmoType );
  208. g_pGameRules->SwitchToNextBestWeapon( pPlayer, this );
  209. StartEffectBarRegen();
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose:
  213. //-----------------------------------------------------------------------------
  214. void CTFLunchBox::DrainAmmo( bool bForceCooldown )
  215. {
  216. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  217. if ( !pOwner )
  218. return;
  219. #ifdef GAME_DLL
  220. int iLunchboxType = GetLunchboxType();
  221. // If we're damaged while eating/taunting, bForceCooldown will be true
  222. if ( pOwner->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  223. {
  224. if ( pOwner->GetHealth() < pOwner->GetMaxHealth() || GetLunchboxType() == LUNCHBOX_ADDS_MINICRITS || iLunchboxType == LUNCHBOX_ADDS_MAXHEALTH || bForceCooldown )
  225. {
  226. StartEffectBarRegen();
  227. }
  228. else // Full health regular sandwhich, I can eat forever
  229. {
  230. return;
  231. }
  232. }
  233. else if ( pOwner->IsPlayerClass( TF_CLASS_SCOUT ) )
  234. {
  235. StartEffectBarRegen();
  236. }
  237. // Strange Tracking. Only go through if we have ammo at this point.
  238. if ( !pOwner->IsBot() && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  239. {
  240. EconEntity_OnOwnerKillEaterEventNoPartner( dynamic_cast<CEconEntity *>( this ), pOwner, kKillEaterEvent_FoodEaten );
  241. }
  242. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  243. #else
  244. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  245. StartEffectBarRegen();
  246. #endif
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. //-----------------------------------------------------------------------------
  251. void CTFLunchBox::Detach( void )
  252. {
  253. #ifdef GAME_DLL
  254. // Terrible - but for now, we're the only place that adds this (custom) attribute
  255. if ( GetLunchboxType() == LUNCHBOX_ADDS_MAXHEALTH )
  256. {
  257. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  258. if ( pOwner )
  259. {
  260. // Prevents use-then-switch-class exploit (heavy->scout)
  261. // Not a big deal in pubs, but it can mess with competitive
  262. pOwner->RemoveCustomAttribute( "hidden maxhealth non buffed" );
  263. }
  264. }
  265. #endif
  266. BaseClass::Detach();
  267. }
  268. #ifdef GAME_DLL
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. void CTFLunchBox::ApplyBiteEffects( CTFPlayer *pPlayer )
  273. {
  274. int nLunchBoxType = GetLunchboxType();
  275. if ( nLunchBoxType == LUNCHBOX_ADDS_MAXHEALTH )
  276. {
  277. // add 50 health to player for 30 seconds
  278. pPlayer->AddCustomAttribute( "hidden maxhealth non buffed", 50.f, 30.f );
  279. }
  280. else if ( nLunchBoxType == LUNCHBOX_ADDS_MINICRITS )
  281. {
  282. static const float s_fSteakSandwichDuration = 16.0f;
  283. // Steak sandvich.
  284. pPlayer->m_Shared.AddCond( TF_COND_ENERGY_BUFF, s_fSteakSandwichDuration );
  285. pPlayer->m_Shared.AddCond( TF_COND_CANNOT_SWITCH_FROM_MELEE, s_fSteakSandwichDuration );
  286. pPlayer->m_Shared.SetBiteEffectWasApplied();
  287. return;
  288. }
  289. // Then heal the player
  290. int iHeal = ( nLunchBoxType == LUNCHBOX_ADDS_MAXHEALTH ) ? 25 : 75;
  291. int iHealType = DMG_GENERIC;
  292. if ( nLunchBoxType == LUNCHBOX_ADDS_MAXHEALTH && pPlayer->GetHealth() < 400 )
  293. {
  294. iHealType = DMG_IGNORE_MAXHEALTH;
  295. iHeal = Min( 25, 400 - pPlayer->GetHealth() );
  296. }
  297. float flHealScale = 1.0f;
  298. CALL_ATTRIB_HOOK_FLOAT( flHealScale, lunchbox_healing_scale );
  299. iHeal = iHeal * flHealScale;
  300. int iHealed = pPlayer->TakeHealth( iHeal, iHealType );
  301. if ( iHealed > 0 )
  302. {
  303. CTF_GameStats.Event_PlayerHealedOther( pPlayer, iHealed );
  304. }
  305. // Restore ammo if applicable
  306. if ( nLunchBoxType == LUNCHBOX_ADDS_AMMO )
  307. {
  308. int maxPrimary = pPlayer->GetMaxAmmo( TF_AMMO_PRIMARY );
  309. pPlayer->GiveAmmo( maxPrimary * 0.25, TF_AMMO_PRIMARY, true );
  310. }
  311. }
  312. #endif // GAME_DLL
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Energy Drink
  315. //-----------------------------------------------------------------------------
  316. CTFLunchBox_Drink::CTFLunchBox_Drink()
  317. {
  318. }
  319. #ifdef CLIENT_DLL
  320. //-----------------------------------------------------------------------------
  321. // Purpose:
  322. //-----------------------------------------------------------------------------
  323. bool CTFLunchBox_Drink::Holster( CBaseCombatWeapon *pSwitchingTo )
  324. {
  325. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  326. if ( pOwner && pOwner->IsLocalPlayer() )
  327. {
  328. C_BaseEntity *pParticleEnt = pOwner->GetViewModel(0);
  329. if ( pParticleEnt )
  330. {
  331. pOwner->StopViewModelParticles( pParticleEnt );
  332. }
  333. }
  334. return BaseClass::Holster( pSwitchingTo );
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose:
  338. //-----------------------------------------------------------------------------
  339. const char* CTFLunchBox_Drink::ModifyEventParticles( const char* token )
  340. {
  341. if ( GetLunchboxType() == LUNCHBOX_ADDS_MINICRITS )
  342. {
  343. if ( FStrEq( token, "energydrink_splash") )
  344. {
  345. CEconItemView *pItem = m_AttributeManager.GetItem();
  346. int iSystems = pItem->GetStaticData()->GetNumAttachedParticles( GetTeamNumber() );
  347. for ( int i = 0; i < iSystems; i++ )
  348. {
  349. attachedparticlesystem_t *pSystem = pItem->GetStaticData()->GetAttachedParticleData( GetTeamNumber(),i );
  350. if ( pSystem->iCustomType == 1 )
  351. {
  352. return pSystem->pszSystemName;
  353. }
  354. }
  355. }
  356. }
  357. return BaseClass::ModifyEventParticles( token );
  358. }
  359. #endif // CLIENT_DLL