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.

468 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Entities for use in the Robot Destruction TF2 game mode.
  4. //
  5. //=========================================================================//
  6. #include "cbase.h"
  7. #include "tf_logic_player_destruction.h"
  8. #ifdef GAME_DLL
  9. #include "tf_player.h"
  10. #include "entity_capture_flag.h"
  11. #include "tf_obj_dispenser.h"
  12. #include "tf_gamerules.h"
  13. #else
  14. #include "c_tf_player.h"
  15. #endif // GAME_DLL
  16. BEGIN_DATADESC( CPlayerDestructionDispenser )
  17. END_DATADESC()
  18. IMPLEMENT_NETWORKCLASS_ALIASED( PlayerDestructionDispenser, DT_PlayerDestructionDispenser )
  19. LINK_ENTITY_TO_CLASS( pd_dispenser, CPlayerDestructionDispenser );
  20. BEGIN_NETWORK_TABLE( CPlayerDestructionDispenser, DT_PlayerDestructionDispenser )
  21. END_NETWORK_TABLE()
  22. #ifdef GAME_DLL
  23. BEGIN_DATADESC( CTFPlayerDestructionLogic )
  24. DEFINE_KEYFIELD( m_iszPropModelName, FIELD_STRING, "prop_model_name" ),
  25. DEFINE_KEYFIELD( m_iszPropDropSound, FIELD_STRING, "prop_drop_sound" ),
  26. DEFINE_KEYFIELD( m_iszPropPickupSound, FIELD_STRING, "prop_pickup_sound" ),
  27. DEFINE_KEYFIELD( m_nMinPoints, FIELD_INTEGER, "min_points" ),
  28. DEFINE_KEYFIELD( m_nPointsPerPlayer, FIELD_INTEGER, "points_per_player" ),
  29. DEFINE_KEYFIELD( m_nFlagResetDelay, FIELD_INTEGER, "flag_reset_delay" ),
  30. DEFINE_KEYFIELD( m_nHealDistance, FIELD_INTEGER, "heal_distance" ),
  31. DEFINE_INPUTFUNC( FIELD_VOID, "ScoreRedPoints", InputScoreRedPoints ),
  32. DEFINE_INPUTFUNC( FIELD_VOID, "ScoreBluePoints", InputScoreBluePoints ),
  33. DEFINE_INPUTFUNC( FIELD_VOID, "EnableMaxScoreUpdating", InputEnableMaxScoreUpdating ),
  34. DEFINE_INPUTFUNC( FIELD_VOID, "DisableMaxScoreUpdating", InputDisableMaxScoreUpdating ),
  35. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCountdownTimer", InputSetCountdownTimer ),
  36. DEFINE_INPUTFUNC( FIELD_STRING, "SetCountdownImage", InputSetCountdownImage ),
  37. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFlagResetDelay", InputSetFlagResetDelay ),
  38. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPointsOnPlayerDeath", InputSetPointsOnPlayerDeath ),
  39. DEFINE_OUTPUT( m_OnRedScoreChanged, "OnRedScoreChanged" ),
  40. DEFINE_OUTPUT( m_OnBlueScoreChanged, "OnBlueScoreChanged" ),
  41. DEFINE_OUTPUT( m_OnCountdownTimerExpired, "OnCountdownTimerExpired" ),
  42. END_DATADESC()
  43. #endif
  44. LINK_ENTITY_TO_CLASS( tf_logic_player_destruction, CTFPlayerDestructionLogic );
  45. IMPLEMENT_NETWORKCLASS_ALIASED( TFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
  46. BEGIN_NETWORK_TABLE( CTFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
  47. #ifdef CLIENT_DLL
  48. RecvPropEHandle( RECVINFO( m_hRedTeamLeader ) ),
  49. RecvPropEHandle( RECVINFO( m_hBlueTeamLeader ) ),
  50. RecvPropString( RECVINFO( m_iszCountdownImage ) ),
  51. RecvPropBool( RECVINFO( m_bUsingCountdownImage ) ),
  52. #else
  53. SendPropEHandle( SENDINFO( m_hRedTeamLeader ) ),
  54. SendPropEHandle( SENDINFO( m_hBlueTeamLeader ) ),
  55. SendPropStringT( SENDINFO( m_iszCountdownImage ) ),
  56. SendPropBool( SENDINFO( m_bUsingCountdownImage ) ),
  57. #endif
  58. END_NETWORK_TABLE()
  59. //-----------------------------------------------------------------------------
  60. // Purpose:
  61. //-----------------------------------------------------------------------------
  62. CTFPlayerDestructionLogic::CTFPlayerDestructionLogic()
  63. {
  64. #ifdef GAME_DLL
  65. m_iszPropModelName = MAKE_STRING( "models/flag/flag.mdl" );
  66. ListenForGameEvent( "player_disconnect" );
  67. m_bMaxScoreUpdatingAllowed = false;
  68. m_nFlagResetDelay = 60;
  69. m_nHealDistance = 450;
  70. m_nPointsOnPlayerDeath = 1;
  71. #endif // GAME_DLL
  72. m_hRedTeamLeader = NULL;
  73. m_hBlueTeamLeader = NULL;
  74. m_bUsingCountdownImage = false;
  75. #ifdef CLIENT_DLL
  76. m_iszCountdownImage[0] = '\0';
  77. #else
  78. m_iszCountdownImage.Set( NULL_STRING );
  79. #endif
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose:
  83. //-----------------------------------------------------------------------------
  84. CTFPlayerDestructionLogic* CTFPlayerDestructionLogic::GetPlayerDestructionLogic()
  85. {
  86. return assert_cast< CTFPlayerDestructionLogic* >( CTFRobotDestructionLogic::GetRobotDestructionLogic() );
  87. }
  88. #ifdef GAME_DLL
  89. //-----------------------------------------------------------------------------
  90. // Purpose:
  91. //-----------------------------------------------------------------------------
  92. void CTFPlayerDestructionLogic::Precache()
  93. {
  94. BaseClass::Precache();
  95. PrecacheModel( GetPropModelName() );
  96. PrecacheScriptSound( STRING( m_iszPropDropSound ) );
  97. PrecacheScriptSound( STRING( m_iszPropPickupSound ) );
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. const char *CTFPlayerDestructionLogic::GetPropModelName() const
  103. {
  104. return STRING( m_iszPropModelName );
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. //-----------------------------------------------------------------------------
  109. void CTFPlayerDestructionLogic::CalcTeamLeader( int iTeam )
  110. {
  111. // team leader's changed team, recalculate team leader for that team
  112. if ( m_hRedTeamLeader.Get() && m_hRedTeamLeader.Get()->GetTeamNumber() != TF_TEAM_RED )
  113. {
  114. m_hRedTeamLeader = NULL;
  115. CalcTeamLeader( TF_TEAM_RED );
  116. }
  117. if ( m_hBlueTeamLeader.Get() && m_hBlueTeamLeader.Get()->GetTeamNumber() != TF_TEAM_BLUE )
  118. {
  119. m_hBlueTeamLeader = NULL;
  120. CalcTeamLeader( TF_TEAM_BLUE );
  121. }
  122. CUtlVector< CTFPlayer * > playerVector;
  123. CollectPlayers( &playerVector, iTeam, COLLECT_ONLY_LIVING_PLAYERS );
  124. CTFPlayer *pTeamLeader = iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
  125. int iCurrentLeadingPoint = 0;
  126. if ( pTeamLeader && pTeamLeader->HasItem() )
  127. {
  128. CCaptureFlag *pFlag = dynamic_cast<CCaptureFlag*>( pTeamLeader->GetItem() );
  129. if ( pFlag )
  130. {
  131. iCurrentLeadingPoint = pFlag->GetPointValue();
  132. }
  133. }
  134. else
  135. {
  136. // reset team leader
  137. pTeamLeader = NULL;
  138. if ( iTeam == TF_TEAM_RED )
  139. {
  140. m_hRedTeamLeader = NULL;
  141. UTIL_Remove( m_hRedDispenser );
  142. m_hRedDispenser = NULL;
  143. }
  144. else
  145. {
  146. m_hBlueTeamLeader = NULL;
  147. UTIL_Remove( m_hBlueDispenser );
  148. m_hBlueDispenser = NULL;
  149. }
  150. }
  151. // find new team leader
  152. CTFPlayer *pNewTeamLeader = NULL;
  153. FOR_EACH_VEC( playerVector, i )
  154. {
  155. CTFPlayer *pPlayer = playerVector[i];
  156. if ( pPlayer == pTeamLeader )
  157. continue;
  158. // community request from Watergate author to never have a SPY be the team leader
  159. if ( pPlayer->HasItem() && !pPlayer->IsPlayerClass( TF_CLASS_SPY ) )
  160. {
  161. CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag* >( pPlayer->GetItem() );
  162. if ( pFlag && pFlag->GetPointValue() > iCurrentLeadingPoint )
  163. {
  164. iCurrentLeadingPoint = pFlag->GetPointValue();
  165. pNewTeamLeader = pPlayer;
  166. }
  167. }
  168. }
  169. // set new leader
  170. if ( pNewTeamLeader )
  171. {
  172. CObjectDispenser *pDispenser = NULL;
  173. if ( iTeam == TF_TEAM_RED )
  174. {
  175. m_hRedTeamLeader = pNewTeamLeader;
  176. if ( !m_hRedDispenser )
  177. {
  178. m_hRedDispenser = CreateDispenser( iTeam );
  179. }
  180. pDispenser = m_hRedDispenser;
  181. }
  182. else
  183. {
  184. m_hBlueTeamLeader = pNewTeamLeader;
  185. if ( !m_hBlueDispenser )
  186. {
  187. m_hBlueDispenser = CreateDispenser( iTeam );
  188. }
  189. pDispenser = m_hBlueDispenser;
  190. }
  191. if ( pDispenser )
  192. {
  193. pDispenser->SetOwnerEntity( pNewTeamLeader );
  194. pDispenser->FollowEntity( pNewTeamLeader );
  195. pDispenser->SetBuilder( pNewTeamLeader );
  196. }
  197. }
  198. }
  199. void CTFPlayerDestructionLogic::FireGameEvent( IGameEvent *pEvent )
  200. {
  201. const char* pszName = pEvent->GetName();
  202. if ( FStrEq( pszName, "player_spawn" ) || FStrEq( pszName, "player_disconnect" ) )
  203. {
  204. EvaluatePlayerCount();
  205. return;
  206. }
  207. else if( FStrEq( pszName, "teamplay_pre_round_time_left" ) )
  208. {
  209. // Eat this event so the RD logic doesn't talk
  210. return;
  211. }
  212. BaseClass::FireGameEvent( pEvent );
  213. }
  214. void CTFPlayerDestructionLogic::OnRedScoreChanged()
  215. {
  216. m_OnRedScoreChanged.Set( (float)m_nRedScore / m_nMaxPoints, this, this );
  217. }
  218. void CTFPlayerDestructionLogic::OnBlueScoreChanged()
  219. {
  220. m_OnBlueScoreChanged.Set( (float)m_nBlueScore / m_nMaxPoints, this, this );
  221. }
  222. void CTFPlayerDestructionLogic::EvaluatePlayerCount()
  223. {
  224. // Bail if we're not allowed
  225. if ( !m_bMaxScoreUpdatingAllowed )
  226. return;
  227. CUtlVector< CTFPlayer* > vecAllPlayers;
  228. CollectPlayers( &vecAllPlayers );
  229. m_nMaxPoints = Max( m_nMinPoints, m_nPointsPerPlayer * vecAllPlayers.Count() );
  230. }
  231. void CTFPlayerDestructionLogic::InputScoreRedPoints( inputdata_t& inputdata )
  232. {
  233. ScorePoints( TF_TEAM_RED, 1, SCORE_CORES_COLLECTED, NULL );
  234. }
  235. void CTFPlayerDestructionLogic::InputScoreBluePoints( inputdata_t& inputdata )
  236. {
  237. ScorePoints( TF_TEAM_BLUE, 1, SCORE_CORES_COLLECTED, NULL );
  238. }
  239. void CTFPlayerDestructionLogic::InputEnableMaxScoreUpdating( inputdata_t& inputdata )
  240. {
  241. m_bMaxScoreUpdatingAllowed = true;
  242. EvaluatePlayerCount();
  243. }
  244. void CTFPlayerDestructionLogic::InputDisableMaxScoreUpdating( inputdata_t& inputdata )
  245. {
  246. EvaluatePlayerCount();
  247. m_bMaxScoreUpdatingAllowed = false;
  248. }
  249. void CTFPlayerDestructionLogic::InputSetCountdownTimer( inputdata_t& inputdata )
  250. {
  251. int nTime = inputdata.value.Int();
  252. if ( nTime > 0 )
  253. {
  254. SetCountdownEndTime( gpGlobals->curtime + nTime );
  255. SetThink( &CTFPlayerDestructionLogic::CountdownThink );
  256. SetNextThink( gpGlobals->curtime + 0.05f );
  257. }
  258. else
  259. {
  260. SetCountdownEndTime( -1.f );
  261. SetThink( NULL );
  262. }
  263. }
  264. void CTFPlayerDestructionLogic::CountdownThink( void )
  265. {
  266. if ( m_flCountdownEndTime > -1.f )
  267. {
  268. // if we're done, just reset the end time
  269. if ( m_flCountdownEndTime < gpGlobals->curtime )
  270. {
  271. m_OnCountdownTimerExpired.FireOutput( this, this );
  272. m_flCountdownEndTime = -1.f;
  273. SetThink( NULL );
  274. return;
  275. }
  276. }
  277. SetNextThink( gpGlobals->curtime + 0.05f );
  278. }
  279. void CTFPlayerDestructionLogic::InputSetCountdownImage( inputdata_t& inputdata )
  280. {
  281. m_bUsingCountdownImage = true;
  282. m_iszCountdownImage = inputdata.value.StringID();
  283. }
  284. void CTFPlayerDestructionLogic::InputSetFlagResetDelay( inputdata_t& inputdata )
  285. {
  286. int nDelay = inputdata.value.Int();
  287. if ( nDelay < 0 )
  288. {
  289. nDelay = 0;
  290. }
  291. m_nFlagResetDelay = nDelay;
  292. }
  293. void CTFPlayerDestructionLogic::InputSetPointsOnPlayerDeath( inputdata_t& inputdata )
  294. {
  295. int nPointsOnPlayerDeath = inputdata.value.Int();
  296. if ( nPointsOnPlayerDeath < 0 )
  297. {
  298. nPointsOnPlayerDeath = 0;
  299. }
  300. m_nPointsOnPlayerDeath = nPointsOnPlayerDeath;
  301. }
  302. CObjectDispenser *CTFPlayerDestructionLogic::CreateDispenser( int iTeam )
  303. {
  304. CPlayerDestructionDispenser *pDispenser = static_cast< CPlayerDestructionDispenser* >( CBaseEntity::CreateNoSpawn( "pd_dispenser", vec3_origin, vec3_angle, NULL ) );
  305. pDispenser->ChangeTeam( iTeam );
  306. pDispenser->SetObjectFlags( pDispenser->GetObjectFlags() | OF_DOESNT_HAVE_A_MODEL | OF_PLAYER_DESTRUCTION );
  307. pDispenser->m_iUpgradeLevel = 1;
  308. DispatchSpawn( pDispenser );
  309. pDispenser->FinishedBuilding();
  310. pDispenser->AddEffects( EF_NODRAW );
  311. pDispenser->DisableAmmoPickupSound();
  312. pDispenser->DisableGenerateMetalSound();
  313. pDispenser->m_takedamage = DAMAGE_NO;
  314. CBaseEntity *pTouchTrigger = pDispenser->GetTouchTrigger();
  315. if ( pTouchTrigger )
  316. {
  317. pTouchTrigger->FollowEntity( pDispenser );
  318. }
  319. return pDispenser;
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose:
  323. //-----------------------------------------------------------------------------
  324. void CTFPlayerDestructionLogic::PlayPropDropSound( CTFPlayer *pPlayer )
  325. {
  326. PlaySound( STRING( m_iszPropDropSound ), pPlayer );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. //-----------------------------------------------------------------------------
  331. void CTFPlayerDestructionLogic::PlayPropPickupSound( CTFPlayer *pPlayer )
  332. {
  333. PlaySound( STRING( m_iszPropPickupSound ), pPlayer );
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose:
  337. //-----------------------------------------------------------------------------
  338. void CTFPlayerDestructionLogic::PlaySound( const char *pszSound, CTFPlayer *pPlayer )
  339. {
  340. EmitSound_t params;
  341. params.m_pSoundName = pszSound;
  342. params.m_flSoundTime = 0;
  343. params.m_pflSoundDuration = 0;
  344. params.m_SoundLevel = SNDLVL_70dB;
  345. CPASFilter filter( pPlayer->GetAbsOrigin() );
  346. pPlayer->EmitSound( filter, pPlayer->entindex(), params );
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose:
  350. //-----------------------------------------------------------------------------
  351. void CPlayerDestructionDispenser::Spawn( void )
  352. {
  353. // This cast is for the benefit of GCC
  354. m_fObjectFlags |= (int)OF_DOESNT_HAVE_A_MODEL;
  355. m_takedamage = DAMAGE_NO;
  356. m_iUpgradeLevel = 1;
  357. TFGameRules()->OnDispenserBuilt( this );
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose: Finished building
  361. //-----------------------------------------------------------------------------
  362. void CPlayerDestructionDispenser::OnGoActive( void )
  363. {
  364. BaseClass::OnGoActive();
  365. if ( m_hTouchTrigger )
  366. {
  367. m_hTouchTrigger->SetParent( GetParent() );
  368. }
  369. SetModel( "" );
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Spawn the vgui control screens on the object
  373. //-----------------------------------------------------------------------------
  374. void CPlayerDestructionDispenser::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  375. {
  376. // no panels
  377. return;
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose:
  381. //-----------------------------------------------------------------------------
  382. void CTFPlayerDestructionLogic::TeamWin( int nTeam )
  383. {
  384. if ( TFGameRules() )
  385. {
  386. TFGameRules()->SetWinningTeam( nTeam, WINREASON_PD_POINTS );
  387. }
  388. }
  389. #endif // GAME_DLL
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. //-----------------------------------------------------------------------------
  393. CTFPlayer *CTFPlayerDestructionLogic::GetTeamLeader( int iTeam ) const
  394. {
  395. return iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
  396. }