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.

397 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Condition Objects
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_condition.h"
  8. #ifdef GAME_DLL
  9. #include "tf_player.h"
  10. #else
  11. #include "c_tf_player.h"
  12. #include "achievementmgr.h"
  13. #include "baseachievement.h"
  14. #include "achievements_tf.h"
  15. #endif
  16. #ifdef CLIENT_DLL
  17. BEGIN_PREDICTION_DATA_NO_BASE( CTFConditionList )
  18. DEFINE_PRED_FIELD( _condition_bits, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  19. END_PREDICTION_DATA()
  20. BEGIN_RECV_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive )
  21. RecvPropInt( RECVINFO( _condition_bits ) ),
  22. END_RECV_TABLE()
  23. #else
  24. BEGIN_SEND_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive )
  25. SendPropInt( SENDINFO( _condition_bits ), MIN( TF_COND_LAST, 32 ), SPROP_UNSIGNED ),
  26. END_SEND_TABLE()
  27. #endif
  28. //-----------------------------------------------------------------------------
  29. // Ctor
  30. //-----------------------------------------------------------------------------
  31. CTFConditionList::CTFConditionList()
  32. {
  33. _condition_bits = _old_condition_bits = 0;
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Condition factory.
  37. //-----------------------------------------------------------------------------
  38. bool CTFConditionList::Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
  39. {
  40. #ifdef GAME_DLL
  41. return _Add( type, duration, outer, provider );
  42. #else
  43. return type == TF_COND_CRITBOOSTED;
  44. #endif
  45. }
  46. bool CTFConditionList::_Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
  47. {
  48. // If we already have a condition of this type, ask it to handle the addition of another.
  49. for ( int i = 0; i < _conditions.Count(); ++i )
  50. {
  51. if ( _conditions[i]->GetType() == type )
  52. {
  53. _conditions[i]->Add( duration );
  54. _conditions[i]->SetProvider( provider );
  55. return true;
  56. }
  57. }
  58. // Add a new condition.
  59. CTFCondition* newCond = NULL;
  60. switch ( type )
  61. {
  62. // TODO: Register new conditions anonymously instead of switching.
  63. case TF_COND_CRITBOOSTED:
  64. newCond = new CTFCondition_CritBoost( type, duration, outer, provider );
  65. break;
  66. }
  67. if ( newCond )
  68. {
  69. _condition_bits |= (1<<type);
  70. _old_condition_bits |= (1<<type);
  71. _conditions.AddToTail( newCond );
  72. newCond->OnAdded();
  73. newCond->SetProvider( provider );
  74. return true;
  75. }
  76. return false;
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Remove a condition from the player.
  80. //-----------------------------------------------------------------------------
  81. bool CTFConditionList::Remove( ETFCond type, bool ignore_duration )
  82. {
  83. #ifdef GAME_DLL
  84. bool bConditionListHandledRemoval = _Remove( type, ignore_duration );
  85. // The condition list only handles one type of condition. Written slightly weird
  86. // to avoid unused variable warnings with asserts disabled.
  87. if ( bConditionListHandledRemoval )
  88. {
  89. Assert( type == TF_COND_CRITBOOSTED );
  90. }
  91. #endif
  92. return type == TF_COND_CRITBOOSTED;
  93. }
  94. bool CTFConditionList::_Remove( ETFCond type, bool ignore_duration )
  95. {
  96. for ( int i=_conditions.Count()-1; i>=0; --i )
  97. {
  98. CTFCondition* cond = _conditions[i];
  99. if ( !cond || cond->GetType() != type )
  100. continue;
  101. if ( cond->UsesMinDuration() && !ignore_duration && cond->GetMinDuration() > 0 )
  102. {
  103. cond->SetMaxDuration( cond->GetMinDuration() );
  104. continue; // Can't remove conditions that haven't expired.
  105. }
  106. _conditions.Remove( i );
  107. _condition_bits &= ~(1<<type);
  108. _old_condition_bits &= ~(1<<type);
  109. cond->OnRemoved();
  110. delete cond;
  111. return true;
  112. }
  113. return false;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Clear all conditions from the player.
  117. //-----------------------------------------------------------------------------
  118. void CTFConditionList::RemoveAll()
  119. {
  120. _condition_bits = 0;
  121. _old_condition_bits = 0;
  122. for ( int i=0; i<_conditions.Count(); ++i )
  123. {
  124. _conditions[i]->OnRemoved();
  125. }
  126. _conditions.PurgeAndDeleteElements();
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Checks if we have at least one of a given condition applied.
  130. //-----------------------------------------------------------------------------
  131. bool CTFConditionList::InCond( ETFCond type ) const
  132. {
  133. return ( ( _condition_bits & (1<<type) ) != 0 );
  134. }
  135. //-----------------------------------------------------------------------------
  136. //
  137. //-----------------------------------------------------------------------------
  138. CBaseEntity *CTFConditionList::GetProvider( ETFCond type ) const
  139. {
  140. CBaseEntity *pProvider = NULL;
  141. for ( int i = 0; i < _conditions.Count(); ++i )
  142. {
  143. if ( _conditions[i]->GetType() == type )
  144. {
  145. pProvider = _conditions[i]->GetProvider();
  146. break;
  147. }
  148. }
  149. return pProvider;
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Client/Server periodic condition think.
  153. //-----------------------------------------------------------------------------
  154. void CTFConditionList::Think()
  155. {
  156. for ( int i=0; i<_conditions.Count(); ++i )
  157. {
  158. _conditions[i]->OnThink();
  159. }
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Server only per-frame think.
  163. //-----------------------------------------------------------------------------
  164. void CTFConditionList::ServerThink()
  165. {
  166. #ifdef GAME_DLL
  167. for ( int i=0; i<_conditions.Count(); ++i )
  168. {
  169. CTFCondition* cond = _conditions[i];
  170. if ( cond->GetMaxDuration() > PERMANENT_CONDITION ||
  171. cond->GetMinDuration() > PERMANENT_CONDITION )
  172. {
  173. // Reduce the duration over time.
  174. float reduction = gpGlobals->frametime;
  175. // Healable conditions expire faster when we have healers.
  176. int numHealers = cond->GetOuter()->m_Shared.GetNumHealers();
  177. if ( cond->IsHealable() && numHealers > 0 )
  178. {
  179. reduction += numHealers * reduction * 4;
  180. }
  181. // Decrement min duration.
  182. if ( cond->GetMinDuration() > PERMANENT_CONDITION )
  183. {
  184. cond->SetMinDuration( MAX( cond->GetMinDuration() - reduction, 0 ) );
  185. }
  186. // Decrement max duration.
  187. if ( cond->GetMaxDuration() > PERMANENT_CONDITION )
  188. {
  189. cond->SetMaxDuration( MAX( cond->GetMaxDuration() - reduction, 0 ) );
  190. if ( cond->GetMaxDuration() < cond->GetMinDuration() )
  191. {
  192. cond->SetMaxDuration( cond->GetMinDuration() );
  193. }
  194. }
  195. if ( cond->GetMaxDuration() == 0 )
  196. {
  197. Remove( cond->GetType() );
  198. continue;
  199. }
  200. }
  201. _conditions[i]->OnServerThink();
  202. }
  203. #endif
  204. }
  205. #ifdef CLIENT_DLL
  206. //-----------------------------------------------------------------------------
  207. //
  208. //-----------------------------------------------------------------------------
  209. void CTFConditionList::OnPreDataChanged( void )
  210. {
  211. // _old_condition_bits = _condition_bits;
  212. }
  213. //-----------------------------------------------------------------------------
  214. //
  215. //-----------------------------------------------------------------------------
  216. void CTFConditionList::OnDataChanged( CTFPlayer* outer )
  217. {
  218. // Is there a way to improve this by hooking directly into network state changed?
  219. if ( _old_condition_bits != _condition_bits )
  220. {
  221. UpdateClientConditions( outer );
  222. _old_condition_bits = _condition_bits;
  223. }
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Creates or destroys conditions to make sure our state matches the server.
  227. //-----------------------------------------------------------------------------
  228. void CTFConditionList::UpdateClientConditions( CTFPlayer* outer )
  229. {
  230. int nCondChanged = _condition_bits ^ _old_condition_bits;
  231. int nCondAdded = nCondChanged & _condition_bits;
  232. int nCondRemoved = nCondChanged & _old_condition_bits;
  233. int i;
  234. for ( i=0;i<TF_COND_LAST;i++ )
  235. {
  236. if ( nCondAdded & (1<<i) )
  237. {
  238. _Add( (ETFCond)i, PERMANENT_CONDITION, outer );
  239. }
  240. else if ( nCondRemoved & (1<<i) )
  241. {
  242. _Remove( (ETFCond)i );
  243. }
  244. }
  245. }
  246. #endif
  247. //-----------------------------------------------------------------------------
  248. // Ctor
  249. //-----------------------------------------------------------------------------
  250. CTFCondition::CTFCondition( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
  251. : _type( type ),
  252. _min_duration( 0 ),
  253. _max_duration( duration ),
  254. _outer( outer ),
  255. _provider( provider )
  256. {
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Dtor
  260. //-----------------------------------------------------------------------------
  261. CTFCondition::~CTFCondition()
  262. {
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Called if we try to add another condition of a type we already have.
  266. //-----------------------------------------------------------------------------
  267. void CTFCondition::Add( float duration )
  268. {
  269. if ( duration != PERMANENT_CONDITION )
  270. {
  271. // If our new duration is not permanent, is shorter than
  272. // our current duration, and is longer than our min duration
  273. // make it our new min duration.
  274. if ( GetMaxDuration() == PERMANENT_CONDITION ||
  275. duration < GetMaxDuration() )
  276. {
  277. if ( duration > GetMinDuration() )
  278. SetMinDuration( duration );
  279. return;
  280. }
  281. }
  282. else if ( GetMaxDuration() != PERMANENT_CONDITION )
  283. {
  284. // If our current duration is not permanent and we are adding a
  285. // permanent duration, make our old finite duration the new min duration.
  286. // This ensures we last at least that long.
  287. SetMinDuration( GetMaxDuration() );
  288. }
  289. SetMaxDuration( duration );
  290. }
  291. //=============================================================================
  292. // Crit Boost
  293. //=============================================================================
  294. CTFCondition_CritBoost::CTFCondition_CritBoost( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
  295. : CTFCondition( type, duration, outer, provider )
  296. {
  297. Assert( type == TF_COND_CRITBOOSTED );
  298. }
  299. void CTFCondition_CritBoost::OnAdded()
  300. {
  301. #ifdef CLIENT_DLL
  302. GetOuter()->m_Shared.UpdateCritBoostEffect();
  303. if ( GetOuter()->IsLocalPlayer() && GetOuter()->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  304. {
  305. g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_RECEIVE_UBER_GRIND );
  306. }
  307. #endif
  308. }
  309. void CTFCondition_CritBoost::OnRemoved()
  310. {
  311. #ifdef CLIENT_DLL
  312. GetOuter()->m_Shared.UpdateCritBoostEffect();
  313. #endif
  314. }
  315. void CTFCondition_CritBoost::OnThink()
  316. {
  317. #ifdef CLIENT_DLL
  318. if ( GetOuter()->m_pCritBoostEffect )
  319. {
  320. CBaseEntity *pWeapon = NULL;
  321. // Use GetRenderedWeaponModel() instead?
  322. if ( GetOuter()->IsLocalPlayer() )
  323. {
  324. pWeapon = GetOuter()->GetViewModel(0);
  325. }
  326. else
  327. {
  328. pWeapon = GetOuter()->GetActiveWeapon();
  329. }
  330. // Transfer the crit boosted effect if we've switched weapons
  331. if ( GetOuter()->m_pCritBoostEffect->GetOwner() != pWeapon )
  332. {
  333. GetOuter()->m_Shared.UpdateCritBoostEffect();
  334. }
  335. }
  336. #endif
  337. }
  338. void CTFCondition_CritBoost::OnServerThink()
  339. {
  340. }