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.

1101 lines
33 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "team_control_point.h"
  8. #include "player.h"
  9. #include "teamplay_gamerules.h"
  10. #include "teamplayroundbased_gamerules.h"
  11. #include "team.h"
  12. #include "team_control_point_master.h"
  13. #include "mp_shareddefs.h"
  14. #include "engine/IEngineSound.h"
  15. #include "soundenvelope.h"
  16. #ifdef TF_DLL
  17. #include "tf_shareddefs.h"
  18. #include "tf_gamerules.h"
  19. #endif
  20. #define CONTROL_POINT_UNLOCK_THINK "UnlockThink"
  21. BEGIN_DATADESC(CTeamControlPoint)
  22. DEFINE_KEYFIELD( m_iszPrintName, FIELD_STRING, "point_printname" ),
  23. DEFINE_KEYFIELD( m_iCPGroup, FIELD_INTEGER, "point_group" ),
  24. DEFINE_KEYFIELD( m_iDefaultOwner, FIELD_INTEGER, "point_default_owner" ),
  25. DEFINE_KEYFIELD( m_iPointIndex, FIELD_INTEGER, "point_index" ),
  26. DEFINE_KEYFIELD( m_iWarnOnCap, FIELD_INTEGER, "point_warn_on_cap" ),
  27. DEFINE_KEYFIELD( m_iszWarnSound, FIELD_STRING, "point_warn_sound" ),
  28. DEFINE_KEYFIELD( m_iszCaptureStartSound, FIELD_STRING, "point_capture_start_sound" ),
  29. DEFINE_KEYFIELD( m_iszCaptureEndSound, FIELD_STRING, "point_capture_end_sound" ),
  30. DEFINE_KEYFIELD( m_iszCaptureInProgress, FIELD_STRING, "point_capture_progress_sound" ),
  31. DEFINE_KEYFIELD( m_iszCaptureInterrupted, FIELD_STRING, "point_capture_interrupted_sound" ),
  32. DEFINE_KEYFIELD( m_bRandomOwnerOnRestart, FIELD_BOOLEAN, "random_owner_on_restart" ),
  33. DEFINE_KEYFIELD( m_bLocked, FIELD_BOOLEAN, "point_start_locked" ),
  34. DEFINE_FUNCTION( UnlockThink ),
  35. // DEFINE_FIELD( m_iTeam, FIELD_INTEGER ),
  36. // DEFINE_FIELD( m_iIndex, FIELD_INTEGER ),
  37. // DEFINE_FIELD( m_TeamData, CUtlVector < perteamdata_t > ),
  38. // DEFINE_FIELD( m_bPointVisible, FIELD_INTEGER ),
  39. // DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  40. // DEFINE_FIELD( m_iszName, FIELD_STRING ),
  41. // DEFINE_FIELD( m_bStartDisabled, FIELD_BOOLEAN ),
  42. // DEFINE_FIELD( m_flLastContestedAt, FIELD_FLOAT ),
  43. // DEFINE_FIELD( m_pCaptureInProgressSound, CSoundPatch ),
  44. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetOwner", InputSetOwner ),
  45. DEFINE_INPUTFUNC( FIELD_VOID, "ShowModel", InputShowModel ),
  46. DEFINE_INPUTFUNC( FIELD_VOID, "HideModel", InputHideModel ),
  47. DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
  48. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetLocked", InputSetLocked ),
  49. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetUnlockTime", InputSetUnlockTime ),
  50. DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ), // these are fired whenever the point changes modes
  51. DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
  52. DEFINE_OUTPUT( m_OnCapReset, "OnCapReset" ),
  53. DEFINE_OUTPUT( m_OnOwnerChangedToTeam1, "OnOwnerChangedToTeam1" ), // these are fired when a team does the work to change the owner
  54. DEFINE_OUTPUT( m_OnOwnerChangedToTeam2, "OnOwnerChangedToTeam2" ),
  55. DEFINE_OUTPUT( m_OnRoundStartOwnedByTeam1, "OnRoundStartOwnedByTeam1" ), // these are fired when a round is starting
  56. DEFINE_OUTPUT( m_OnRoundStartOwnedByTeam2, "OnRoundStartOwnedByTeam2" ),
  57. DEFINE_OUTPUT( m_OnUnlocked, "OnUnlocked" ),
  58. DEFINE_THINKFUNC( AnimThink ),
  59. END_DATADESC();
  60. LINK_ENTITY_TO_CLASS( team_control_point, CTeamControlPoint );
  61. //-----------------------------------------------------------------------------
  62. // Purpose:
  63. //-----------------------------------------------------------------------------
  64. CTeamControlPoint::CTeamControlPoint()
  65. {
  66. m_TeamData.SetSize( GetNumberOfTeams() );
  67. m_pCaptureInProgressSound = NULL;
  68. m_bLocked = false;
  69. m_flUnlockTime = -1;
  70. m_bBotsIgnore = false;
  71. #ifdef TF_DLL
  72. UseClientSideAnimation();
  73. #endif
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Purpose:
  77. //-----------------------------------------------------------------------------
  78. void CTeamControlPoint::Spawn( void )
  79. {
  80. // Validate our default team
  81. if ( m_iDefaultOwner < 0 || m_iDefaultOwner >= GetNumberOfTeams() )
  82. {
  83. Warning( "team_control_point '%s' has bad point_default_owner.\n", GetDebugName() );
  84. m_iDefaultOwner = TEAM_UNASSIGNED;
  85. }
  86. #ifdef TF_DLL
  87. if ( m_iszCaptureStartSound == NULL_STRING )
  88. {
  89. m_iszCaptureStartSound = AllocPooledString( "Hologram.Start" );
  90. }
  91. if ( m_iszCaptureEndSound == NULL_STRING )
  92. {
  93. m_iszCaptureEndSound = AllocPooledString( "Hologram.Stop" );
  94. }
  95. if ( m_iszCaptureInProgress == NULL_STRING )
  96. {
  97. m_iszCaptureInProgress = AllocPooledString( "Hologram.Move" );
  98. }
  99. if ( m_iszCaptureInterrupted == NULL_STRING )
  100. {
  101. m_iszCaptureInterrupted = AllocPooledString( "Hologram.Interrupted" );
  102. }
  103. #endif
  104. Precache();
  105. InternalSetOwner( m_iDefaultOwner, false ); //init the owner of this point
  106. TeamplayRoundBasedRules()->RecalculateControlPointState();
  107. SetActive( !m_bStartDisabled );
  108. BaseClass::Spawn();
  109. SetPlaybackRate( 1.0 );
  110. SetThink( &CTeamControlPoint::AnimThink );
  111. SetNextThink( gpGlobals->curtime + 0.1f );
  112. if ( FBitSet( m_spawnflags, SF_CAP_POINT_HIDE_MODEL ) )
  113. {
  114. AddEffects( EF_NODRAW );
  115. }
  116. if ( FBitSet( m_spawnflags, SF_CAP_POINT_HIDE_SHADOW ) )
  117. {
  118. AddEffects( EF_NOSHADOW );
  119. }
  120. m_bBotsIgnore = FBitSet( m_spawnflags, SF_CAP_POINT_BOTS_IGNORE ) > 0;
  121. m_flLastContestedAt = -1;
  122. m_pCaptureInProgressSound = NULL;
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose:
  126. //-----------------------------------------------------------------------------
  127. bool CTeamControlPoint::KeyValue( const char *szKeyName, const char *szValue )
  128. {
  129. if ( !Q_strncmp( szKeyName, "team_capsound_", 14 ) )
  130. {
  131. int iTeam = atoi(szKeyName+14);
  132. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  133. m_TeamData[iTeam].iszCapSound = AllocPooledString(szValue);
  134. }
  135. else if ( !Q_strncmp( szKeyName, "team_model_", 11 ) )
  136. {
  137. int iTeam = atoi(szKeyName+11);
  138. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  139. m_TeamData[iTeam].iszModel = AllocPooledString(szValue);
  140. }
  141. else if ( !Q_strncmp( szKeyName, "team_timedpoints_", 17 ) )
  142. {
  143. int iTeam = atoi(szKeyName+17);
  144. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  145. m_TeamData[iTeam].iTimedPoints = atoi(szValue);
  146. }
  147. else if ( !Q_strncmp( szKeyName, "team_bodygroup_", 15 ) )
  148. {
  149. int iTeam = atoi(szKeyName+15);
  150. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  151. m_TeamData[iTeam].iModelBodygroup = atoi(szValue);
  152. }
  153. else if ( !Q_strncmp( szKeyName, "team_icon_", 10 ) )
  154. {
  155. int iTeam = atoi(szKeyName+10);
  156. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  157. m_TeamData[iTeam].iszIcon = AllocPooledString(szValue);
  158. }
  159. else if ( !Q_strncmp( szKeyName, "team_overlay_", 13 ) )
  160. {
  161. int iTeam = atoi(szKeyName+13);
  162. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  163. m_TeamData[iTeam].iszOverlay = AllocPooledString(szValue);
  164. }
  165. else if ( !Q_strncmp( szKeyName, "team_previouspoint_", 19 ) )
  166. {
  167. int iTeam;
  168. int iPoint = 0;
  169. sscanf( szKeyName+19, "%d_%d", &iTeam, &iPoint );
  170. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  171. Assert( iPoint >= 0 && iPoint < MAX_PREVIOUS_POINTS );
  172. m_TeamData[iTeam].iszPreviousPoint[iPoint] = AllocPooledString(szValue);
  173. }
  174. else
  175. {
  176. return BaseClass::KeyValue( szKeyName, szValue );
  177. }
  178. return true;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. void CTeamControlPoint::Precache( void )
  184. {
  185. for ( int i = 0; i < m_TeamData.Count(); i++ )
  186. {
  187. // Skip over spectator
  188. if ( i == TEAM_SPECTATOR )
  189. continue;
  190. if ( m_TeamData[i].iszCapSound != NULL_STRING )
  191. {
  192. PrecacheScriptSound( STRING(m_TeamData[i].iszCapSound) );
  193. }
  194. if ( m_TeamData[i].iszModel != NULL_STRING )
  195. {
  196. PrecacheModel( STRING(m_TeamData[i].iszModel) );
  197. }
  198. if ( m_TeamData[i].iszIcon != NULL_STRING )
  199. {
  200. PrecacheMaterial( STRING( m_TeamData[i].iszIcon ) );
  201. m_TeamData[i].iIcon = GetMaterialIndex( STRING( m_TeamData[i].iszIcon ) );
  202. Assert( m_TeamData[i].iIcon != 0 );
  203. }
  204. if ( !m_TeamData[i].iIcon )
  205. {
  206. Warning( "Invalid hud icon material for team %d in control point '%s' ( point index %d )\n", i, GetDebugName(), GetPointIndex() );
  207. }
  208. if ( m_TeamData[i].iszOverlay != NULL_STRING )
  209. {
  210. PrecacheMaterial( STRING( m_TeamData[i].iszOverlay ) );
  211. m_TeamData[i].iOverlay = GetMaterialIndex( STRING( m_TeamData[i].iszOverlay ) );
  212. Assert( m_TeamData[i].iOverlay != 0 );
  213. if ( !m_TeamData[i].iOverlay )
  214. {
  215. Warning( "Invalid hud overlay material for team %d in control point '%s' ( point index %d )\n", i, GetDebugName(), GetPointIndex() );
  216. }
  217. }
  218. }
  219. PrecacheScriptSound( STRING( m_iszCaptureStartSound ) );
  220. PrecacheScriptSound( STRING( m_iszCaptureEndSound ) );
  221. PrecacheScriptSound( STRING( m_iszCaptureInProgress ) );
  222. PrecacheScriptSound( STRING( m_iszCaptureInterrupted ) );
  223. if ( m_iszWarnSound != NULL_STRING )
  224. {
  225. PrecacheScriptSound( STRING( m_iszWarnSound ) );
  226. }
  227. #ifdef TF_DLL
  228. PrecacheScriptSound( "Announcer.ControlPointContested" );
  229. PrecacheScriptSound( "Announcer.ControlPointContested_Neutral" );
  230. #endif
  231. }
  232. //------------------------------------------------------------------------------
  233. // Purpose:
  234. //------------------------------------------------------------------------------
  235. void CTeamControlPoint::AnimThink( void )
  236. {
  237. StudioFrameAdvance();
  238. DispatchAnimEvents(this);
  239. SetNextThink( gpGlobals->curtime + 0.1f );
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose: Used by ControlMaster to this point to its default owner
  243. //-----------------------------------------------------------------------------
  244. void CTeamControlPoint::InputReset( inputdata_t &input )
  245. {
  246. m_flLastContestedAt = -1;
  247. InternalSetOwner( m_iDefaultOwner, false );
  248. ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
  249. TeamplayRoundBasedRules()->RecalculateControlPointState();
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose:
  253. //-----------------------------------------------------------------------------
  254. void CTeamControlPoint::HandleScoring( int iTeam )
  255. {
  256. if ( TeamplayRoundBasedRules() && !TeamplayRoundBasedRules()->ShouldScorePerRound() )
  257. {
  258. GetGlobalTeam( iTeam )->AddScore( 1 );
  259. TeamplayRoundBasedRules()->HandleTeamScoreModify( iTeam, 1 );
  260. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  261. if ( pMaster && !pMaster->WouldNewCPOwnerWinGame( this, iTeam ) )
  262. {
  263. #ifdef TF_DLL
  264. if ( TeamplayRoundBasedRules()->GetGameType() == TF_GAMETYPE_ESCORT )
  265. {
  266. CBroadcastRecipientFilter filter;
  267. EmitSound( filter, entindex(), "Hud.EndRoundScored" );
  268. }
  269. else
  270. #endif
  271. {
  272. CTeamRecipientFilter filter( iTeam );
  273. EmitSound( filter, entindex(), "Hud.EndRoundScored" );
  274. }
  275. }
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose: Used by Area caps to set the owner
  280. //-----------------------------------------------------------------------------
  281. void CTeamControlPoint::InputSetOwner( inputdata_t &input )
  282. {
  283. int iCapTeam = input.value.Int();
  284. Assert( iCapTeam >= 0 && iCapTeam < GetNumberOfTeams() );
  285. Assert( input.pCaller );
  286. if ( !input.pCaller )
  287. return;
  288. if ( GetOwner() == iCapTeam )
  289. return;
  290. if ( TeamplayGameRules()->PointsMayBeCaptured() )
  291. {
  292. // must be done before setting the owner
  293. HandleScoring( iCapTeam );
  294. if ( input.pCaller->IsPlayer() )
  295. {
  296. int iCappingPlayer = input.pCaller->entindex();
  297. InternalSetOwner( iCapTeam, true, 1, &iCappingPlayer );
  298. }
  299. else
  300. {
  301. InternalSetOwner( iCapTeam, false );
  302. }
  303. ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
  304. TeamplayRoundBasedRules()->RecalculateControlPointState();
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. void CTeamControlPoint::InputShowModel( inputdata_t &input )
  311. {
  312. RemoveEffects( EF_NODRAW );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. void CTeamControlPoint::InputHideModel( inputdata_t &input )
  318. {
  319. AddEffects( EF_NODRAW );
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose:
  323. //-----------------------------------------------------------------------------
  324. int CTeamControlPoint::GetCurrentHudIconIndex( void )
  325. {
  326. return m_TeamData[GetOwner()].iIcon;
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. //-----------------------------------------------------------------------------
  331. int CTeamControlPoint::GetHudIconIndexForTeam( int iGameTeam )
  332. {
  333. return m_TeamData[iGameTeam].iIcon;
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose:
  337. //-----------------------------------------------------------------------------
  338. int CTeamControlPoint::GetHudOverlayIndexForTeam( int iGameTeam )
  339. {
  340. return m_TeamData[iGameTeam].iOverlay;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. //-----------------------------------------------------------------------------
  345. int CTeamControlPoint::GetPreviousPointForTeam( int iGameTeam, int iPrevPoint )
  346. {
  347. Assert( iPrevPoint >= 0 && iPrevPoint < MAX_PREVIOUS_POINTS );
  348. int iRetVal = -1;
  349. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, STRING(m_TeamData[iGameTeam].iszPreviousPoint[iPrevPoint]) );
  350. if ( pEntity )
  351. {
  352. CTeamControlPoint *pPoint = dynamic_cast<CTeamControlPoint*>( pEntity );
  353. if ( pPoint )
  354. {
  355. iRetVal = pPoint->GetPointIndex();
  356. }
  357. }
  358. return iRetVal;
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose:
  362. //-----------------------------------------------------------------------------
  363. void CTeamControlPoint::ForceOwner( int iTeam )
  364. {
  365. InternalSetOwner( iTeam, false, 0, 0 );
  366. ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
  367. TeamplayRoundBasedRules()->RecalculateControlPointState();
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose:
  371. //-----------------------------------------------------------------------------
  372. void CTeamControlPoint::SetOwner( int iCapTeam, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
  373. {
  374. if ( TeamplayGameRules()->PointsMayBeCaptured() )
  375. {
  376. // must be done before setting the owner
  377. HandleScoring( iCapTeam );
  378. InternalSetOwner( iCapTeam, bMakeSound, iNumCappers, pCappingPlayers );
  379. ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
  380. TeamplayRoundBasedRules()->RecalculateControlPointState();
  381. }
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void CTeamControlPoint::CaptureStart( int iCapTeam, int iNumCappingPlayers, int *pCappingPlayers )
  387. {
  388. int iNumCappers = iNumCappingPlayers;
  389. float flLastOwnershipChangeTime = -1.f;
  390. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() );
  391. while( pEnt )
  392. {
  393. CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt );
  394. if ( pMaster && pMaster->IsActive() )
  395. {
  396. flLastOwnershipChangeTime = pMaster->GetLastOwnershipChangeTime();
  397. }
  398. pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() );
  399. }
  400. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_startcapture" );
  401. if ( event )
  402. {
  403. event->SetInt( "cp", m_iPointIndex );
  404. event->SetString( "cpname", STRING( m_iszPrintName ) );
  405. event->SetInt( "team", m_iTeam );
  406. event->SetInt( "capteam", iCapTeam );
  407. event->SetFloat( "captime", gpGlobals->curtime - flLastOwnershipChangeTime );
  408. // safety check
  409. if ( iNumCappers > 8 )
  410. {
  411. iNumCappers = 8;
  412. }
  413. char cappers[9]; // pCappingPlayers should be max length 8
  414. int i;
  415. for( i = 0 ; i < iNumCappers ; i++ )
  416. {
  417. cappers[i] = (char)pCappingPlayers[i];
  418. }
  419. cappers[i] = '\0';
  420. // pCappingPlayers is a null terminated list of player indices
  421. event->SetString( "cappers", cappers );
  422. event->SetInt( "priority", 7 );
  423. gameeventmanager->FireEvent( event );
  424. }
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose:
  428. //-----------------------------------------------------------------------------
  429. void CTeamControlPoint::CaptureEnd( void )
  430. {
  431. StopLoopingSounds();
  432. if ( !FBitSet( m_spawnflags, SF_CAP_POINT_NO_CAP_SOUNDS ) )
  433. {
  434. EmitSound( STRING( m_iszCaptureEndSound ) );
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose:
  439. //-----------------------------------------------------------------------------
  440. void CTeamControlPoint::CaptureInterrupted( bool bBlocked )
  441. {
  442. StopLoopingSounds();
  443. if ( FBitSet( m_spawnflags, SF_CAP_POINT_NO_CAP_SOUNDS ) )
  444. {
  445. return;
  446. }
  447. const char *pSoundName = NULL;
  448. if ( bBlocked == true )
  449. {
  450. pSoundName = STRING( m_iszCaptureInterrupted );
  451. }
  452. else
  453. {
  454. pSoundName = STRING( m_iszCaptureInProgress );
  455. EmitSound( STRING( m_iszCaptureStartSound ) );
  456. }
  457. if ( m_pCaptureInProgressSound == NULL && pSoundName != NULL )
  458. {
  459. CPASFilter filter( GetAbsOrigin() );
  460. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  461. m_pCaptureInProgressSound = controller.SoundCreate( filter, entindex(), pSoundName );
  462. controller.Play( m_pCaptureInProgressSound, 1.0, 100 );
  463. }
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. //-----------------------------------------------------------------------------
  468. void CTeamControlPoint::StopLoopingSounds( void )
  469. {
  470. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  471. if ( m_pCaptureInProgressSound )
  472. {
  473. controller.SoundDestroy( m_pCaptureInProgressSound );
  474. m_pCaptureInProgressSound = NULL;
  475. }
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Sets the new owner of the point, plays the appropriate sound and shows the right model
  479. //-----------------------------------------------------------------------------
  480. void CTeamControlPoint::InternalSetOwner( int iCapTeam, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
  481. {
  482. Assert( iCapTeam >= 0 && iCapTeam < GetNumberOfTeams() );
  483. int iOldTeam = m_iTeam;
  484. m_iTeam = iCapTeam;
  485. ChangeTeam( iCapTeam );
  486. if ( bMakeSound )
  487. {
  488. CBroadcastRecipientFilter filter;
  489. EmitSound( filter, entindex(), STRING( m_TeamData[m_iTeam].iszCapSound ) );
  490. }
  491. // Update visuals
  492. SetModel( STRING(m_TeamData[m_iTeam].iszModel) );
  493. SetBodygroup( 0, m_iTeam );
  494. m_nSkin = ( m_iTeam == TEAM_UNASSIGNED ) ? 2 : (m_iTeam - 2);
  495. ResetSequence( LookupSequence("idle") );
  496. // We add 1 to the index because we consider the default "no points capped" as 0.
  497. TeamplayGameRules()->SetLastCapPointChanged( m_iPointIndex+1 );
  498. // Determine the pose parameters for each team
  499. for ( int i = 0; i < m_TeamData.Count(); i++ )
  500. {
  501. // Skip spectator
  502. if ( i == TEAM_SPECTATOR )
  503. continue;
  504. if ( GetModelPtr() && GetModelPtr()->SequencesAvailable() )
  505. {
  506. m_TeamData[i].iTeamPoseParam = LookupPoseParameter( UTIL_VarArgs( "cappoint_%d_percentage", i ) );
  507. }
  508. else
  509. {
  510. m_TeamData[i].iTeamPoseParam = -1;
  511. }
  512. }
  513. UpdateCapPercentage();
  514. if ( m_iTeam == TEAM_UNASSIGNED )
  515. {
  516. m_OnCapReset.FireOutput( this, this );
  517. }
  518. else
  519. {
  520. // Remap team to get first game team = 1
  521. switch ( m_iTeam - FIRST_GAME_TEAM+1 )
  522. {
  523. case 1:
  524. m_OnCapTeam1.FireOutput( this, this );
  525. break;
  526. case 2:
  527. m_OnCapTeam2.FireOutput( this, this );
  528. break;
  529. default:
  530. Assert(0);
  531. break;
  532. }
  533. }
  534. // If we're playing a sound, this is a true cap by players.
  535. if ( bMakeSound )
  536. {
  537. if ( iOldTeam > LAST_SHARED_TEAM && iOldTeam != m_iTeam )
  538. {
  539. // Make the members of our old team say something
  540. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  541. {
  542. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
  543. if ( !pPlayer )
  544. continue;
  545. if ( pPlayer->GetTeamNumber() == iOldTeam )
  546. {
  547. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_LOST_CONTROL_POINT );
  548. }
  549. }
  550. }
  551. for( int i = 0; i < iNumCappers; i++ )
  552. {
  553. int playerIndex = pCappingPlayers[i];
  554. Assert( playerIndex > 0 && playerIndex <= gpGlobals->maxClients );
  555. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( playerIndex ) );
  556. PlayerCapped( pPlayer );
  557. #ifdef TF_DLL
  558. if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_EOTL ) )
  559. {
  560. TFGameRules()->DropBonusDuck( pPlayer->GetAbsOrigin(), ToTFPlayer( pPlayer ), NULL, NULL, false, true );
  561. }
  562. #endif
  563. }
  564. // Remap team to get first game team = 1
  565. switch ( m_iTeam - FIRST_GAME_TEAM+1 )
  566. {
  567. case 1:
  568. m_OnOwnerChangedToTeam1.FireOutput( this, this );
  569. break;
  570. case 2:
  571. m_OnOwnerChangedToTeam2.FireOutput( this, this );
  572. break;
  573. }
  574. if ( m_iTeam != TEAM_UNASSIGNED && iNumCappers )
  575. {
  576. SendCapString( m_iTeam, iNumCappers, pCappingPlayers );
  577. }
  578. #ifdef TF_DLL
  579. if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
  580. {
  581. TFGameRules()->DropHalloweenSoulPackToTeam( 5, GetAbsOrigin(), m_iTeam, TEAM_SPECTATOR );
  582. }
  583. #endif
  584. }
  585. // Have control point master check the win conditions now!
  586. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() );
  587. while( pEnt )
  588. {
  589. CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt );
  590. if ( pMaster->IsActive() )
  591. {
  592. pMaster->CheckWinConditions();
  593. pMaster->SetLastOwnershipChangeTime( gpGlobals->curtime );
  594. }
  595. pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() );
  596. }
  597. }
  598. //-----------------------------------------------------------------------------
  599. // Purpose:
  600. //-----------------------------------------------------------------------------
  601. void CTeamControlPoint::SendCapString( int iCapTeam, int iNumCappingPlayers, int *pCappingPlayers )
  602. {
  603. if ( strlen( STRING(m_iszPrintName) ) <= 0 )
  604. return;
  605. int iNumCappers = iNumCappingPlayers;
  606. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_captured" );
  607. if ( event )
  608. {
  609. event->SetInt( "cp", m_iPointIndex );
  610. event->SetString( "cpname", STRING( m_iszPrintName ) );
  611. event->SetInt( "team", iCapTeam );
  612. // safety check
  613. if ( iNumCappers > 8 )
  614. {
  615. iNumCappers = 8;
  616. }
  617. char cappers[9]; // pCappingPlayers should be max length 8
  618. int i;
  619. for( i = 0 ; i < iNumCappers ; i++ )
  620. {
  621. cappers[i] = (char)pCappingPlayers[i];
  622. }
  623. cappers[i] = '\0';
  624. // pCappingPlayers is a null terminated list of player indices
  625. event->SetString( "cappers", cappers );
  626. event->SetInt( "priority", 9 );
  627. gameeventmanager->FireEvent( event );
  628. }
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Purpose:
  632. //-----------------------------------------------------------------------------
  633. void CTeamControlPoint::CaptureBlocked( CBaseMultiplayerPlayer *pPlayer, CBaseMultiplayerPlayer *pVictim )
  634. {
  635. if( strlen( STRING(m_iszPrintName) ) <= 0 )
  636. return;
  637. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_capture_blocked" );
  638. if ( event )
  639. {
  640. event->SetInt( "cp", m_iPointIndex );
  641. event->SetString( "cpname", STRING(m_iszPrintName) );
  642. event->SetInt( "blocker", pPlayer->entindex() );
  643. event->SetInt( "priority", 9 );
  644. if ( pVictim )
  645. {
  646. event->SetInt( "victim", pVictim->entindex() );
  647. }
  648. gameeventmanager->FireEvent( event );
  649. }
  650. PlayerBlocked( pPlayer );
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Purpose:
  654. //-----------------------------------------------------------------------------
  655. int CTeamControlPoint::GetOwner( void ) const
  656. {
  657. return m_iTeam;
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. //-----------------------------------------------------------------------------
  662. int CTeamControlPoint::GetDefaultOwner( void ) const
  663. {
  664. return m_iDefaultOwner;
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Purpose:
  668. //-----------------------------------------------------------------------------
  669. int CTeamControlPoint::GetCPGroup( void )
  670. {
  671. return m_iCPGroup;
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose: Returns the time-based point value of this control point
  675. //-----------------------------------------------------------------------------
  676. int CTeamControlPoint::PointValue( void )
  677. {
  678. if ( GetOwner() != m_iDefaultOwner )
  679. return m_TeamData[ GetOwner() ].iTimedPoints;
  680. return 0;
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose:
  684. //-----------------------------------------------------------------------------
  685. void CTeamControlPoint::SetActive( bool active )
  686. {
  687. m_bActive = active;
  688. if( active )
  689. {
  690. RemoveEffects( EF_NODRAW );
  691. }
  692. else
  693. {
  694. AddEffects( EF_NODRAW );
  695. }
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Purpose:
  699. //-----------------------------------------------------------------------------
  700. void CTeamControlPoint::SetCappersRequiredForTeam( int iGameTeam, int iCappers )
  701. {
  702. m_TeamData[iGameTeam].iPlayersRequired = iCappers;
  703. }
  704. //-----------------------------------------------------------------------------
  705. // Purpose: Return true if this point has ever been contested, false if the enemy has never contested this point yet
  706. //-----------------------------------------------------------------------------
  707. bool CTeamControlPoint::HasBeenContested( void ) const
  708. {
  709. return m_flLastContestedAt > 0.0f;
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose:
  713. //-----------------------------------------------------------------------------
  714. float CTeamControlPoint::LastContestedAt( void )
  715. {
  716. return m_flLastContestedAt;
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Purpose:
  720. //-----------------------------------------------------------------------------
  721. void CTeamControlPoint::SetLastContestedAt( float flTime )
  722. {
  723. m_flLastContestedAt = flTime;
  724. }
  725. //-----------------------------------------------------------------------------
  726. // Purpose:
  727. //-----------------------------------------------------------------------------
  728. void CTeamControlPoint::UpdateCapPercentage( void )
  729. {
  730. for ( int i = LAST_SHARED_TEAM+1; i < m_TeamData.Count(); i++ )
  731. {
  732. // Skip spectator
  733. if ( i == TEAM_SPECTATOR )
  734. continue;
  735. float flPerc = GetTeamCapPercentage(i);
  736. if ( m_TeamData[i].iTeamPoseParam != -1 )
  737. {
  738. SetPoseParameter( m_TeamData[i].iTeamPoseParam, flPerc );
  739. }
  740. }
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose:
  744. //-----------------------------------------------------------------------------
  745. float CTeamControlPoint::GetTeamCapPercentage( int iTeam )
  746. {
  747. int iCappingTeam = ObjectiveResource()->GetCappingTeam( GetPointIndex() );
  748. if ( iCappingTeam == TEAM_UNASSIGNED )
  749. {
  750. // No-one's capping this point.
  751. if ( iTeam == m_iTeam )
  752. return 1.0;
  753. return 0.0;
  754. }
  755. float flCapPerc = ObjectiveResource()->GetCPCapPercentage( GetPointIndex() );
  756. if ( iTeam == iCappingTeam )
  757. return (1.0 - flCapPerc);
  758. if ( iTeam == m_iTeam )
  759. return flCapPerc;
  760. return 0.0;
  761. }
  762. //-----------------------------------------------------------------------------
  763. // Purpose:
  764. //-----------------------------------------------------------------------------
  765. int CTeamControlPoint::DrawDebugTextOverlays( void )
  766. {
  767. int text_offset = BaseClass::DrawDebugTextOverlays();
  768. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  769. {
  770. char tempstr[1024];
  771. Q_snprintf(tempstr, sizeof(tempstr), "INDEX: (%d)", GetPointIndex() );
  772. EntityText(text_offset,tempstr,0);
  773. text_offset++;
  774. Q_snprintf( tempstr, sizeof(tempstr), "Red Previous Points: ");
  775. for ( int i = 0; i < MAX_PREVIOUS_POINTS; i++ )
  776. {
  777. if ( m_TeamData[2].iszPreviousPoint[i] != NULL_STRING )
  778. {
  779. Q_strncat( tempstr, STRING(m_TeamData[2].iszPreviousPoint[i]), 1024, COPY_ALL_CHARACTERS );
  780. Q_strncat( tempstr, ", ", 1024, COPY_ALL_CHARACTERS );
  781. }
  782. }
  783. EntityText(text_offset,tempstr,0);
  784. text_offset++;
  785. Q_snprintf( tempstr, sizeof(tempstr), "Blue Previous Points: " );
  786. for ( int i = 0; i < MAX_PREVIOUS_POINTS; i++ )
  787. {
  788. if ( m_TeamData[3].iszPreviousPoint[i] != NULL_STRING )
  789. {
  790. Q_strncat( tempstr, STRING(m_TeamData[3].iszPreviousPoint[i]), 1024, COPY_ALL_CHARACTERS );
  791. Q_strncat( tempstr, ", ", 1024, COPY_ALL_CHARACTERS );
  792. }
  793. }
  794. EntityText(text_offset,tempstr,0);
  795. text_offset++;
  796. for ( int i = 0; i < MAX_CONTROL_POINT_TEAMS; i++ )
  797. {
  798. if ( ObjectiveResource()->GetBaseControlPointForTeam(i) == GetPointIndex() )
  799. {
  800. Q_snprintf(tempstr, sizeof(tempstr), "Base Control Point for Team %d", i );
  801. EntityText(text_offset,tempstr,0);
  802. text_offset++;
  803. }
  804. }
  805. }
  806. return text_offset;
  807. }
  808. //-----------------------------------------------------------------------------
  809. // Purpose: The specified player took part in capping this point.
  810. //-----------------------------------------------------------------------------
  811. void CTeamControlPoint::PlayerCapped( CBaseMultiplayerPlayer *pPlayer )
  812. {
  813. if ( pPlayer )
  814. {
  815. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_CAPTURED_POINT );
  816. }
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose: The specified player blocked the enemy team from capping this point.
  820. //-----------------------------------------------------------------------------
  821. void CTeamControlPoint::PlayerBlocked( CBaseMultiplayerPlayer *pPlayer )
  822. {
  823. if ( pPlayer )
  824. {
  825. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_CAPTURE_BLOCKED );
  826. }
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Purpose:
  830. //-----------------------------------------------------------------------------
  831. void CTeamControlPoint::InputRoundActivate( inputdata_t &inputdata )
  832. {
  833. switch ( m_iTeam - FIRST_GAME_TEAM+1 )
  834. {
  835. case 1:
  836. m_OnRoundStartOwnedByTeam1.FireOutput( this, this );
  837. break;
  838. case 2:
  839. m_OnRoundStartOwnedByTeam2.FireOutput( this, this );
  840. break;
  841. }
  842. InternalSetLocked( m_bLocked );
  843. }
  844. //-----------------------------------------------------------------------------
  845. // Purpose:
  846. //-----------------------------------------------------------------------------
  847. void CTeamControlPoint::InputSetLocked( inputdata_t &inputdata )
  848. {
  849. // never lock/unlock the point if we're in waiting for players
  850. if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
  851. return;
  852. bool bLocked = inputdata.value.Int() > 0;
  853. InternalSetLocked( bLocked );
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Purpose:
  857. //-----------------------------------------------------------------------------
  858. void CTeamControlPoint::InternalSetLocked( bool bLocked )
  859. {
  860. if ( !bLocked && m_bLocked )
  861. {
  862. // unlocked this point
  863. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_unlocked" );
  864. if ( event )
  865. {
  866. event->SetInt( "cp", m_iPointIndex );
  867. event->SetString( "cpname", STRING( m_iszPrintName ) );
  868. event->SetInt( "team", m_iTeam );
  869. gameeventmanager->FireEvent( event );
  870. }
  871. }
  872. else if ( bLocked && !m_bLocked )
  873. {
  874. // locked this point
  875. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_locked" );
  876. if ( event )
  877. {
  878. event->SetInt( "cp", m_iPointIndex );
  879. event->SetString( "cpname", STRING( m_iszPrintName ) );
  880. event->SetInt( "team", m_iTeam );
  881. gameeventmanager->FireEvent( event );
  882. }
  883. }
  884. m_bLocked = bLocked;
  885. if ( ObjectiveResource() && GetPointIndex() < ObjectiveResource()->GetNumControlPoints() )
  886. {
  887. ObjectiveResource()->SetCPLocked( GetPointIndex(), m_bLocked );
  888. ObjectiveResource()->SetCPUnlockTime( GetPointIndex(), 0.0f );
  889. }
  890. if ( !m_bLocked )
  891. {
  892. m_flUnlockTime = -1;
  893. m_OnUnlocked.FireOutput( this, this );
  894. SetContextThink( NULL, 0, CONTROL_POINT_UNLOCK_THINK );
  895. }
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Purpose:
  899. //-----------------------------------------------------------------------------
  900. void CTeamControlPoint::InputSetUnlockTime( inputdata_t &inputdata )
  901. {
  902. // never lock/unlock the point if we're in waiting for players
  903. if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
  904. return;
  905. int nTime = inputdata.value.Int();
  906. if ( nTime <= 0 )
  907. {
  908. InternalSetLocked( false );
  909. return;
  910. }
  911. m_flUnlockTime = gpGlobals->curtime + nTime;
  912. if ( ObjectiveResource() )
  913. {
  914. ObjectiveResource()->SetCPUnlockTime( GetPointIndex(), m_flUnlockTime );
  915. }
  916. SetContextThink( &CTeamControlPoint::UnlockThink, gpGlobals->curtime + 0.1, CONTROL_POINT_UNLOCK_THINK );
  917. }
  918. //-----------------------------------------------------------------------------
  919. // Purpose:
  920. //-----------------------------------------------------------------------------
  921. void CTeamControlPoint::UnlockThink( void )
  922. {
  923. if ( m_flUnlockTime > 0 &&
  924. m_flUnlockTime < gpGlobals->curtime &&
  925. ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->State_Get() == GR_STATE_RND_RUNNING ) )
  926. {
  927. InternalSetLocked( false );
  928. return;
  929. }
  930. SetContextThink( &CTeamControlPoint::UnlockThink, gpGlobals->curtime + 0.1, CONTROL_POINT_UNLOCK_THINK );
  931. }