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.

392 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client's CObjectSentrygun
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_baseobject.h"
  9. #include "c_tf_player.h"
  10. #include "vgui/ILocalize.h"
  11. #include "c_obj_dispenser.h"
  12. // NVNT haptics system interface
  13. #include "c_tf_haptics.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. using namespace vgui;
  17. //-----------------------------------------------------------------------------
  18. // Purpose: RecvProxy that converts the Team's player UtlVector to entindexes
  19. //-----------------------------------------------------------------------------
  20. void RecvProxy_HealingList( const CRecvProxyData *pData, void *pStruct, void *pOut )
  21. {
  22. C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;
  23. CBaseHandle *pHandle = (CBaseHandle*)(&(pDispenser->m_hHealingTargets[pData->m_iElement]));
  24. RecvProxy_IntToEHandle( pData, pStruct, pHandle );
  25. // update the heal beams
  26. pDispenser->m_bUpdateHealingTargets = true;
  27. }
  28. void RecvProxyArrayLength_HealingArray( void *pStruct, int objectID, int currentArrayLength )
  29. {
  30. C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;
  31. if ( pDispenser->m_hHealingTargets.Size() != currentArrayLength )
  32. pDispenser->m_hHealingTargets.SetSize( currentArrayLength );
  33. // update the heal beams
  34. pDispenser->m_bUpdateHealingTargets = true;
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Dispenser object
  38. //-----------------------------------------------------------------------------
  39. IMPLEMENT_CLIENTCLASS_DT(C_ObjectDispenser, DT_ObjectDispenser, CObjectDispenser)
  40. RecvPropInt( RECVINFO( m_iState ) ),
  41. RecvPropInt( RECVINFO( m_iAmmoMetal ) ),
  42. RecvPropInt( RECVINFO( m_iMiniBombCounter ) ),
  43. RecvPropArray2(
  44. RecvProxyArrayLength_HealingArray,
  45. RecvPropInt( "healing_array_element", 0, SIZEOF_IGNORE, 0, RecvProxy_HealingList ),
  46. MAX_PLAYERS,
  47. 0,
  48. "healing_array"
  49. )
  50. END_RECV_TABLE()
  51. //-----------------------------------------------------------------------------
  52. // Purpose:
  53. //-----------------------------------------------------------------------------
  54. C_ObjectDispenser::C_ObjectDispenser()
  55. {
  56. m_bUpdateHealingTargets = false;
  57. m_bPlayingSound = false;
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose:
  61. //-----------------------------------------------------------------------------
  62. C_ObjectDispenser::~C_ObjectDispenser()
  63. {
  64. StopSound( "Building_Dispenser.Heal" );
  65. // NVNT see if local player is in the list of targets
  66. // temp. fix if dispener is destroyed will stop all healers.
  67. if(m_bPlayingSound)
  68. {
  69. if(tfHaptics.healingDispenserCount>0) {
  70. tfHaptics.healingDispenserCount --;
  71. if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
  72. tfHaptics.isBeingHealed = false;
  73. }
  74. }
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. // Input : updateType -
  79. //-----------------------------------------------------------------------------
  80. void C_ObjectDispenser::OnDataChanged( DataUpdateType_t updateType )
  81. {
  82. BaseClass::OnDataChanged( updateType );
  83. #ifdef STAGING_ONLY
  84. if ( updateType == DATA_UPDATE_CREATED )
  85. {
  86. SetNextClientThink( CLIENT_THINK_ALWAYS );
  87. }
  88. #endif // STAGING_ONLY
  89. if ( m_bUpdateHealingTargets )
  90. {
  91. UpdateEffects();
  92. m_bUpdateHealingTargets = false;
  93. }
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. //-----------------------------------------------------------------------------
  98. void C_ObjectDispenser::ClientThink()
  99. {
  100. BaseClass::ClientThink();
  101. #ifdef STAGING_ONLY
  102. C_TFPlayer *pTFOwner = GetOwner();
  103. if ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() )
  104. {
  105. UpdateEffects();
  106. }
  107. #endif // STAGING_ONLY
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose:
  111. //-----------------------------------------------------------------------------
  112. void C_ObjectDispenser::SetInvisibilityLevel( float flValue )
  113. {
  114. if ( IsEnteringOrExitingFullyInvisible( flValue ) )
  115. {
  116. UpdateEffects();
  117. }
  118. BaseClass::SetInvisibilityLevel( flValue );
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. void C_ObjectDispenser::UpdateEffects( void )
  124. {
  125. C_TFPlayer *pOwner = GetOwner();
  126. if ( GetInvisibilityLevel() == 1.f || ( pOwner && pOwner->m_Shared.IsFullyInvisible() ) )
  127. {
  128. StopEffects( true );
  129. return;
  130. }
  131. StopEffects();
  132. // Now add any new targets
  133. for ( int i = 0; i < m_hHealingTargets.Count(); i++ )
  134. {
  135. C_BaseEntity *pTarget = m_hHealingTargets[i].Get();
  136. // Loops through the healing targets, and make sure we have an effect for each of them
  137. if ( pTarget )
  138. {
  139. // don't want to show this effect for stealthed spies
  140. C_TFPlayer *pPlayer = dynamic_cast< C_TFPlayer * >( pTarget );
  141. if ( pPlayer && ( pPlayer->m_Shared.IsStealthed() || pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
  142. continue;
  143. bool bHaveEffect = false;
  144. for ( int targets = 0; targets < m_hHealingTargetEffects.Count(); targets++ )
  145. {
  146. if ( m_hHealingTargetEffects[targets].pTarget == pTarget )
  147. {
  148. bHaveEffect = true;
  149. break;
  150. }
  151. }
  152. if ( bHaveEffect )
  153. continue;
  154. // NVNT if the dispenser has started to heal the local player
  155. // notify the haptics system
  156. if(pTarget==C_BasePlayer::GetLocalPlayer())
  157. {
  158. tfHaptics.healingDispenserCount++;
  159. if(!tfHaptics.wasBeingHealedMedic) {
  160. tfHaptics.isBeingHealed = true;
  161. }
  162. }
  163. const char *pszEffectName;
  164. if ( GetTeamNumber() == TF_TEAM_RED )
  165. {
  166. pszEffectName = "dispenser_heal_red";
  167. }
  168. else
  169. {
  170. pszEffectName = "dispenser_heal_blue";
  171. }
  172. CNewParticleEffect *pEffect;
  173. // if we don't have a model, attach at the origin, otherwise use attachment 'heal_origin'
  174. if ( FBitSet( GetObjectFlags(), OF_DOESNT_HAVE_A_MODEL ) )
  175. {
  176. // offset the origin to player's chest
  177. if ( FBitSet( GetObjectFlags(), OF_PLAYER_DESTRUCTION ) )
  178. {
  179. pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 50 ) );
  180. }
  181. else
  182. {
  183. pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW );
  184. }
  185. }
  186. else
  187. {
  188. pEffect = ParticleProp()->Create( pszEffectName, PATTACH_POINT_FOLLOW, "heal_origin" );
  189. }
  190. ParticleProp()->AddControlPoint( pEffect, 1, pTarget, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector(0,0,50) );
  191. int iIndex = m_hHealingTargetEffects.AddToTail();
  192. m_hHealingTargetEffects[iIndex].pTarget = pTarget;
  193. m_hHealingTargetEffects[iIndex].pEffect = pEffect;
  194. // Start the sound over again every time we start a new beam
  195. StopSound( "Building_Dispenser.Heal" );
  196. CLocalPlayerFilter filter;
  197. EmitSound( filter, entindex(), "Building_Dispenser.Heal" );
  198. m_bPlayingSound = true;
  199. }
  200. }
  201. // Stop the sound if we're not healing anyone
  202. if ( m_bPlayingSound && m_hHealingTargets.Count() == 0 )
  203. {
  204. m_bPlayingSound = false;
  205. // stop the sound
  206. StopSound( "Building_Dispenser.Heal" );
  207. }
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose:
  211. //-----------------------------------------------------------------------------
  212. void C_ObjectDispenser::StopEffects( bool bRemoveAll /* = false */ )
  213. {
  214. // Find all the targets we've stopped healing
  215. bool bStillHealing[MAX_DISPENSER_HEALING_TARGETS] = { 0 };
  216. for ( int i = 0; i < m_hHealingTargetEffects.Count(); i++ )
  217. {
  218. bStillHealing[i] = false;
  219. // Are we still healing this target?
  220. if ( !bRemoveAll )
  221. {
  222. for ( int target = 0; target < m_hHealingTargets.Count(); target++ )
  223. {
  224. if ( m_hHealingTargets[target] && m_hHealingTargets[target] == m_hHealingTargetEffects[i].pTarget )
  225. {
  226. bStillHealing[i] = true;
  227. break;
  228. }
  229. }
  230. }
  231. }
  232. // Now remove all the dead effects
  233. for ( int i = m_hHealingTargetEffects.Count()-1; i >= 0; i-- )
  234. {
  235. if ( !bStillHealing[i] )
  236. {
  237. // NVNT if the healing target of this dispenser is the local player.
  238. // inform the haptics system interface we are no longer healing.
  239. if(m_hHealingTargetEffects[i].pTarget==C_BasePlayer::GetLocalPlayer())
  240. {
  241. if(tfHaptics.healingDispenserCount>0) {
  242. tfHaptics.healingDispenserCount --;
  243. if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
  244. tfHaptics.isBeingHealed = false;
  245. }
  246. }
  247. ParticleProp()->StopEmission( m_hHealingTargetEffects[i].pEffect );
  248. m_hHealingTargetEffects.Remove(i);
  249. }
  250. }
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose: Damage level has changed, update our effects
  254. //-----------------------------------------------------------------------------
  255. void C_ObjectDispenser::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
  256. {
  257. if ( m_hDamageEffects )
  258. {
  259. m_hDamageEffects->StopEmission( false, false );
  260. m_hDamageEffects = NULL;
  261. }
  262. const char *pszEffect = "";
  263. switch( damageLevel )
  264. {
  265. case BUILDING_DAMAGE_LEVEL_LIGHT:
  266. pszEffect = "dispenserdamage_1";
  267. break;
  268. case BUILDING_DAMAGE_LEVEL_MEDIUM:
  269. pszEffect = "dispenserdamage_2";
  270. break;
  271. case BUILDING_DAMAGE_LEVEL_HEAVY:
  272. pszEffect = "dispenserdamage_3";
  273. break;
  274. case BUILDING_DAMAGE_LEVEL_CRITICAL:
  275. pszEffect = "dispenserdamage_4";
  276. break;
  277. default:
  278. break;
  279. }
  280. if ( Q_strlen(pszEffect) > 0 )
  281. {
  282. m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN );
  283. }
  284. }
  285. //-----------------------------------------------------------------------------
  286. //
  287. //-----------------------------------------------------------------------------
  288. int C_ObjectDispenser::GetMaxMetal( void )
  289. {
  290. return DISPENSER_MAX_METAL_AMMO;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Control screen
  294. //-----------------------------------------------------------------------------
  295. DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel, "screen_obj_dispenser_blue" );
  296. DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel_Red, "screen_obj_dispenser_red" );
  297. //-----------------------------------------------------------------------------
  298. // Constructor:
  299. //-----------------------------------------------------------------------------
  300. CDispenserControlPanel::CDispenserControlPanel( vgui::Panel *parent, const char *panelName )
  301. : BaseClass( parent, "CDispenserControlPanel" )
  302. {
  303. m_pAmmoProgress = new RotatingProgressBar( this, "MeterArrow" );
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Deactivates buttons we can't afford
  307. //-----------------------------------------------------------------------------
  308. void CDispenserControlPanel::OnTickActive( C_BaseObject *pObj, C_TFPlayer *pLocalPlayer )
  309. {
  310. BaseClass::OnTickActive( pObj, pLocalPlayer );
  311. Assert( dynamic_cast< C_ObjectDispenser* >( pObj ) );
  312. m_hDispenser = static_cast< C_ObjectDispenser* >( pObj );
  313. float flProgress = m_hDispenser ? m_hDispenser->GetMetalAmmoCount() / (float)m_hDispenser->GetMaxMetal() : 0.f;
  314. m_pAmmoProgress->SetProgress( flProgress );
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose:
  318. //-----------------------------------------------------------------------------
  319. bool CDispenserControlPanel::IsVisible( void )
  320. {
  321. if ( m_hDispenser )
  322. {
  323. #ifdef STAGING_ONLY
  324. if ( m_hDispenser->IsMiniBuilding() )
  325. return false;
  326. #endif // STAGING_ONLY
  327. if ( m_hDispenser->GetInvisibilityLevel() == 1.f )
  328. return false;
  329. }
  330. return BaseClass::IsVisible();
  331. }
  332. IMPLEMENT_CLIENTCLASS_DT(C_ObjectCartDispenser, DT_ObjectCartDispenser, CObjectCartDispenser)
  333. END_RECV_TABLE()