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.

1233 lines
34 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "team_train_watcher.h"
  8. #include "trigger_area_capture.h"
  9. #include "player.h"
  10. #include "teamplay_gamerules.h"
  11. #include "team.h"
  12. #include "team_objectiveresource.h"
  13. #include "team_control_point_master.h"
  14. #include "teamplayroundbased_gamerules.h"
  15. extern ConVar mp_capstyle;
  16. extern ConVar mp_blockstyle;
  17. extern ConVar mp_capdeteriorate_time;
  18. IMPLEMENT_AUTO_LIST( ITriggerAreaCaptureAutoList );
  19. BEGIN_DATADESC(CTriggerAreaCapture)
  20. // Touch functions
  21. DEFINE_FUNCTION( CTriggerAreaCaptureShim::Touch ),
  22. // Think functions
  23. DEFINE_THINKFUNC( CaptureThink ),
  24. // Keyfields
  25. DEFINE_KEYFIELD( m_iszCapPointName, FIELD_STRING, "area_cap_point" ),
  26. DEFINE_KEYFIELD( m_flCapTime, FIELD_FLOAT, "area_time_to_cap" ),
  27. // DEFINE_FIELD( m_iCapMode, FIELD_INTEGER ),
  28. // DEFINE_FIELD( m_bCapturing, FIELD_BOOLEAN ),
  29. // DEFINE_FIELD( m_nCapturingTeam, FIELD_INTEGER ),
  30. // DEFINE_FIELD( m_nOwningTeam, FIELD_INTEGER ),
  31. // DEFINE_FIELD( m_nTeamInZone, FIELD_INTEGER ),
  32. // DEFINE_FIELD( m_fTimeRemaining, FIELD_FLOAT ),
  33. // DEFINE_FIELD( m_flLastReductionTime, FIELD_FLOAT ),
  34. // DEFINE_FIELD( m_bBlocked, FIELD_BOOLEAN ),
  35. // DEFINE_FIELD( m_TeamData, CUtlVector < perteamdata_t > ),
  36. // DEFINE_FIELD( m_Blockers, CUtlVector < blockers_t > ),
  37. // DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  38. // DEFINE_FIELD( m_hPoint, CHandle < CTeamControlPoint > ),
  39. // DEFINE_FIELD( m_bRequiresObject, FIELD_BOOLEAN ),
  40. // DEFINE_FIELD( m_iCapAttemptNumber, FIELD_INTEGER ),
  41. // Inputs
  42. DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),
  43. DEFINE_INPUTFUNC( FIELD_STRING, "SetTeamCanCap", InputSetTeamCanCap ),
  44. DEFINE_INPUTFUNC( FIELD_STRING, "SetControlPoint", InputSetControlPoint ),
  45. DEFINE_INPUTFUNC( FIELD_VOID, "CaptureCurrentCP", InputCaptureCurrentCP ),
  46. // Outputs
  47. DEFINE_OUTPUT( m_OnStartTeam1, "OnStartTeam1" ),
  48. DEFINE_OUTPUT( m_OnStartTeam2, "OnStartTeam2" ),
  49. DEFINE_OUTPUT( m_OnBreakTeam1, "OnBreakTeam1" ),
  50. DEFINE_OUTPUT( m_OnBreakTeam2, "OnBreakTeam2" ),
  51. DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ),
  52. DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
  53. DEFINE_OUTPUT( m_StartOutput, "OnStartCap" ),
  54. DEFINE_OUTPUT( m_BreakOutput, "OnBreakCap" ),
  55. DEFINE_OUTPUT( m_CapOutput, "OnEndCap" ),
  56. DEFINE_OUTPUT( m_OnNumCappersChanged, "OnNumCappersChanged" ),
  57. DEFINE_OUTPUT( m_OnNumCappersChanged2, "OnNumCappersChanged2" ),
  58. END_DATADESC();
  59. LINK_ENTITY_TO_CLASS( trigger_capture_area, CTriggerAreaCapture );
  60. //-----------------------------------------------------------------------------
  61. // Purpose:
  62. //-----------------------------------------------------------------------------
  63. CTriggerAreaCapture::CTriggerAreaCapture()
  64. {
  65. m_TeamData.SetSize( GetNumberOfTeams() );
  66. m_bStartTouch = false;
  67. m_hTrainWatcher = NULL;
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose:
  71. //-----------------------------------------------------------------------------
  72. void CTriggerAreaCapture::Spawn( void )
  73. {
  74. BaseClass::Spawn();
  75. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
  76. InitTrigger();
  77. Precache();
  78. SetTouch ( &CTriggerAreaCaptureShim::Touch );
  79. SetThink( &CTriggerAreaCapture::CaptureThink );
  80. SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
  81. for ( int i = 0; i < m_TeamData.Count(); i++ )
  82. {
  83. if ( m_TeamData[i].iNumRequiredToCap < 1 )
  84. {
  85. m_TeamData[i].iNumRequiredToCap = 1;
  86. }
  87. if ( m_TeamData[i].iNumRequiredToStartCap < 1 )
  88. {
  89. m_TeamData[i].iNumRequiredToStartCap = 1;
  90. }
  91. }
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. bool CTriggerAreaCapture::KeyValue( const char *szKeyName, const char *szValue )
  97. {
  98. if ( !Q_strncmp( szKeyName, "team_numcap_", 12 ) )
  99. {
  100. int iTeam = atoi(szKeyName+12);
  101. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  102. m_TeamData[iTeam].iNumRequiredToCap = atoi(szValue);
  103. }
  104. else if ( !Q_strncmp( szKeyName, "team_cancap_", 12 ) )
  105. {
  106. int iTeam = atoi(szKeyName+12);
  107. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  108. m_TeamData[iTeam].bCanCap = (atoi(szValue) != 0);
  109. }
  110. else if ( !Q_strncmp( szKeyName, "team_spawn_", 11 ) )
  111. {
  112. int iTeam = atoi(szKeyName+11);
  113. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  114. m_TeamData[iTeam].iSpawnAdjust = atoi(szValue);
  115. }
  116. else if ( !Q_strncmp( szKeyName, "team_startcap_", 14 ) )
  117. {
  118. int iTeam = atoi(szKeyName+14);
  119. Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
  120. m_TeamData[iTeam].iNumRequiredToStartCap = atoi(szValue);
  121. }
  122. else
  123. {
  124. return BaseClass::KeyValue( szKeyName, szValue );
  125. }
  126. return true;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CTriggerAreaCapture::Precache( void )
  132. {
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose:
  136. //-----------------------------------------------------------------------------
  137. bool CTriggerAreaCapture::IsActive( void )
  138. {
  139. return !m_bDisabled;
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose:
  143. //-----------------------------------------------------------------------------
  144. void CTriggerAreaCapture::StartTouch(CBaseEntity *pOther)
  145. {
  146. BaseClass::StartTouch( pOther );
  147. if ( PassesTriggerFilters(pOther) && m_hPoint )
  148. {
  149. m_nOwningTeam = m_hPoint->GetOwner();
  150. IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_starttouch" );
  151. if ( event )
  152. {
  153. event->SetInt( "player", pOther->entindex() );
  154. event->SetInt( "area", m_hPoint->GetPointIndex() );
  155. gameeventmanager->FireEvent( event );
  156. }
  157. // Call capture think immediately to make it update our area's player counts.
  158. // If we don't do this, the player can receive the above event telling him he's
  159. // in a zone, but the objective resource still thinks he's not.
  160. m_bStartTouch = true;
  161. CaptureThink();
  162. m_bStartTouch = false;
  163. if ( m_bCapturing )
  164. {
  165. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  166. if ( pMaster )
  167. {
  168. float flRate = pMaster->GetPartialCapturePointRate();
  169. if ( flRate > 0.0f )
  170. {
  171. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
  172. if ( pPlayer && pPlayer->GetTeamNumber() == m_nCapturingTeam )
  173. {
  174. pPlayer->StartScoringEscortPoints( flRate );
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CTriggerAreaCapture::EndTouch(CBaseEntity *pOther)
  185. {
  186. if ( IsTouching( pOther ) && m_hPoint )
  187. {
  188. IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_endtouch" );
  189. if ( event )
  190. {
  191. event->SetInt( "player", pOther->entindex() );
  192. event->SetInt( "area", m_hPoint->GetPointIndex() );
  193. gameeventmanager->FireEvent( event );
  194. }
  195. // incase we leave but the area keeps capturing
  196. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
  197. if ( pPlayer )
  198. {
  199. pPlayer->StopScoringEscortPoints();
  200. }
  201. }
  202. BaseClass::EndTouch( pOther );
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. bool CTriggerAreaCapture::CaptureModeScalesWithPlayers() const
  208. {
  209. return mp_capstyle.GetBool();
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose:
  213. //-----------------------------------------------------------------------------
  214. void CTriggerAreaCapture::AreaTouch( CBaseEntity *pOther )
  215. {
  216. if ( !IsActive() )
  217. return;
  218. if ( !PassesTriggerFilters(pOther) )
  219. return;
  220. // Don't cap areas unless the round is running
  221. if ( !TeamplayGameRules()->PointsMayBeCaptured() )
  222. return;
  223. // dont touch for non-alive or non-players
  224. if( !pOther->IsPlayer() || !pOther->IsAlive() )
  225. return;
  226. // make sure this point is in the round being played (if we're playing one)
  227. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  228. if ( pMaster && m_hPoint )
  229. {
  230. if ( !pMaster->IsInRound( m_hPoint ) )
  231. {
  232. return;
  233. }
  234. }
  235. if ( m_hPoint )
  236. {
  237. m_nOwningTeam = m_hPoint->GetOwner();
  238. }
  239. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
  240. Assert( pPlayer );
  241. if ( pPlayer->GetTeamNumber() != m_nOwningTeam )
  242. {
  243. if ( m_TeamData[ pPlayer->GetTeamNumber() ].bCanCap )
  244. {
  245. DisplayCapHintTo( pPlayer );
  246. }
  247. }
  248. }
  249. ConVar mp_simulatemultiplecappers( "mp_simulatemultiplecappers", "1", FCVAR_CHEAT );
  250. #define MAX_CAPTURE_TEAMS 8
  251. //-----------------------------------------------------------------------------
  252. // Purpose:
  253. //-----------------------------------------------------------------------------
  254. void CTriggerAreaCapture::CaptureThink( void )
  255. {
  256. SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
  257. // make sure this point is in the round being played (if we're playing one)
  258. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  259. if ( pMaster && m_hPoint )
  260. {
  261. if ( !pMaster->IsInRound( m_hPoint ) )
  262. {
  263. return;
  264. }
  265. }
  266. if ( !TeamplayGameRules()->PointsMayBeCaptured() )
  267. {
  268. // Points aren't allowed to be captured. If we were
  269. // being captured, we need to clean up and reset.
  270. if ( m_bCapturing )
  271. {
  272. BreakCapture( false );
  273. UpdateNumPlayers();
  274. }
  275. return;
  276. }
  277. // go through our list of players
  278. Assert( GetNumberOfTeams() <= MAX_CAPTURE_TEAMS );
  279. int iNumPlayers[MAX_CAPTURE_TEAMS];
  280. int iNumBlockablePlayers[MAX_CAPTURE_TEAMS]; // Players in the zone who can't cap, but can block / pause caps
  281. CBaseMultiplayerPlayer *pFirstPlayerTouching[MAX_CAPTURE_TEAMS];
  282. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  283. {
  284. iNumPlayers[i] = 0;
  285. iNumBlockablePlayers[i] = 0;
  286. pFirstPlayerTouching[i] = NULL;
  287. }
  288. if ( m_hPoint )
  289. {
  290. // Loop through the entities we're touching, and find players
  291. for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
  292. {
  293. CBaseEntity *ent = m_hTouchingEntities[i];
  294. if ( ent && ent->IsPlayer() )
  295. {
  296. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(ent);
  297. if ( pPlayer->IsAlive() )
  298. {
  299. int iTeam = pPlayer->GetTeamNumber();
  300. // If a team's not allowed to cap a point, don't count players in it at all
  301. if ( !TeamplayGameRules()->TeamMayCapturePoint( iTeam, m_hPoint->GetPointIndex() ) )
  302. continue;
  303. if ( !TeamplayGameRules()->PlayerMayCapturePoint( pPlayer, m_hPoint->GetPointIndex() ) )
  304. {
  305. if ( TeamplayGameRules()->PlayerMayBlockPoint( pPlayer, m_hPoint->GetPointIndex() ) )
  306. {
  307. if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
  308. {
  309. pFirstPlayerTouching[iTeam] = pPlayer;
  310. }
  311. iNumBlockablePlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
  312. pPlayer->SetLastObjectiveTime( gpGlobals->curtime );
  313. }
  314. continue;
  315. }
  316. if ( iTeam >= FIRST_GAME_TEAM )
  317. {
  318. if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
  319. {
  320. pFirstPlayerTouching[iTeam] = pPlayer;
  321. }
  322. iNumPlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
  323. pPlayer->SetLastObjectiveTime( gpGlobals->curtime );
  324. }
  325. }
  326. }
  327. }
  328. }
  329. int iTeamsInZone = 0;
  330. bool bUpdatePlayers = false;
  331. m_nTeamInZone = TEAM_UNASSIGNED;
  332. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  333. {
  334. iNumPlayers[i] *= mp_simulatemultiplecappers.GetInt();
  335. if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
  336. {
  337. m_TeamData[i].iNumTouching = iNumPlayers[i];
  338. bUpdatePlayers = true;
  339. }
  340. m_TeamData[i].iBlockedTouching = m_TeamData[i].iNumTouching;
  341. if ( m_TeamData[i].iNumTouching )
  342. {
  343. iTeamsInZone++;
  344. m_nTeamInZone = i;
  345. }
  346. }
  347. if ( iTeamsInZone > 1 )
  348. {
  349. m_nTeamInZone = TEAM_UNASSIGNED;
  350. }
  351. else
  352. {
  353. // If we've got non-cappable, yet blockable players here for the team that's defending, they
  354. // need to block the cap. This catches cases like the TF invulnerability, which needs to block
  355. // caps, but isn't allowed to contribute to a cap.
  356. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  357. {
  358. if ( !iNumBlockablePlayers[i] || m_nTeamInZone == i )
  359. continue;
  360. iTeamsInZone++;
  361. }
  362. }
  363. UpdateTeamInZone();
  364. bool bBlocked = false;
  365. // If the cap is being blocked, reset the number of players so the client
  366. // knows to stop the capture as well.
  367. if ( mp_blockstyle.GetInt() == 1 )
  368. {
  369. if ( m_bCapturing && iTeamsInZone > 1 )
  370. {
  371. bBlocked = true;
  372. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  373. {
  374. iNumPlayers[i] = 0;
  375. if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
  376. {
  377. m_TeamData[i].iNumTouching = iNumPlayers[i];
  378. bUpdatePlayers = true;
  379. }
  380. }
  381. }
  382. }
  383. if ( bUpdatePlayers )
  384. {
  385. UpdateNumPlayers( bBlocked );
  386. }
  387. // When a player blocks, tell them the cap index and attempt number
  388. // only give successive blocks to them if the attempt number is different
  389. if ( m_bCapturing )
  390. {
  391. if ( m_hPoint )
  392. {
  393. m_hPoint->SetLastContestedAt( gpGlobals->curtime );
  394. }
  395. // Calculate the amount of modification to the cap time
  396. float flTimeDelta = gpGlobals->curtime - m_flLastReductionTime;
  397. float flReduction = flTimeDelta;
  398. if ( CaptureModeScalesWithPlayers() )
  399. {
  400. // Diminishing returns for successive players.
  401. for ( int i = 1; i < m_TeamData[m_nTeamInZone].iNumTouching; i++ )
  402. {
  403. flReduction += (flTimeDelta / (float)(i+1));
  404. }
  405. }
  406. m_flLastReductionTime = gpGlobals->curtime;
  407. //if more than one team is in the zone
  408. if( iTeamsInZone > 1 )
  409. {
  410. if ( !m_bBlocked )
  411. {
  412. m_bBlocked = true;
  413. UpdateBlocked();
  414. }
  415. // See if anyone gets credit for the block
  416. float flPercentToGo = m_fTimeRemaining / m_flCapTime;
  417. if ( CaptureModeScalesWithPlayers() )
  418. {
  419. flPercentToGo = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
  420. }
  421. if ( ( flPercentToGo <= 0.5 || TeamplayGameRules()->PointsMayAlwaysBeBlocked() ) && m_hPoint )
  422. {
  423. // find the first player that is not on the capturing team
  424. // they have just broken a cap and should be rewarded
  425. // tell the player the capture attempt number, for checking later
  426. CBaseMultiplayerPlayer *pBlockingPlayer = NULL;
  427. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  428. {
  429. if ( m_nCapturingTeam == i )
  430. continue;
  431. if ( pFirstPlayerTouching[i] )
  432. {
  433. pBlockingPlayer = pFirstPlayerTouching[i];
  434. break;
  435. }
  436. }
  437. Assert( pBlockingPlayer );
  438. if ( pBlockingPlayer )
  439. {
  440. bool bRepeatBlocker = false;
  441. for ( int i = m_Blockers.Count()-1; i >= 0; i-- )
  442. {
  443. if ( m_Blockers[i].hPlayer != pBlockingPlayer )
  444. continue;
  445. // If this guy's was a blocker, but not valid now, remove him from the list
  446. if ( m_Blockers[i].iCapAttemptNumber != m_iCapAttemptNumber || !IsTouching(m_Blockers[i].hPlayer) ||
  447. ( TeamplayGameRules()->PointsMayAlwaysBeBlocked() && m_Blockers[i].flNextBlockTime < gpGlobals->curtime && m_bStartTouch ) )
  448. {
  449. m_Blockers.Remove(i);
  450. continue;
  451. }
  452. bRepeatBlocker = true;
  453. break;
  454. }
  455. if ( !bRepeatBlocker )
  456. {
  457. m_hPoint->CaptureBlocked( pBlockingPlayer, NULL );
  458. // Add this guy to our blocker list
  459. int iNew = m_Blockers.AddToTail();
  460. m_Blockers[iNew].hPlayer = pBlockingPlayer;
  461. m_Blockers[iNew].iCapAttemptNumber = m_iCapAttemptNumber;
  462. m_Blockers[iNew].flNextBlockTime = gpGlobals->curtime + 10.0f;
  463. }
  464. }
  465. }
  466. if ( mp_blockstyle.GetInt() == 0 )
  467. {
  468. BreakCapture( false );
  469. }
  470. return;
  471. }
  472. if ( m_bBlocked )
  473. {
  474. m_bBlocked = false;
  475. UpdateBlocked();
  476. }
  477. float flTotalTimeToCap = m_flCapTime;
  478. if ( CaptureModeScalesWithPlayers() )
  479. {
  480. flTotalTimeToCap = ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
  481. }
  482. // Now remove the reduction amount after we've determined there's only 1 team in the area
  483. if ( m_nCapturingTeam == m_nTeamInZone )
  484. {
  485. SetCapTimeRemaining( m_fTimeRemaining - flReduction );
  486. }
  487. else if ( m_nOwningTeam == TEAM_UNASSIGNED && m_nTeamInZone != TEAM_UNASSIGNED )
  488. {
  489. SetCapTimeRemaining( m_fTimeRemaining + flReduction );
  490. }
  491. else
  492. {
  493. // Caps deteriorate over time
  494. if ( TeamplayRoundBasedRules() && m_hPoint && TeamplayRoundBasedRules()->TeamMayCapturePoint(m_nCapturingTeam,m_hPoint->GetPointIndex()) )
  495. {
  496. float flDecreaseScale = CaptureModeScalesWithPlayers() ? mp_capdeteriorate_time.GetFloat() : flTotalTimeToCap;
  497. float flDecrease = (flTotalTimeToCap / flDecreaseScale) * flTimeDelta;
  498. if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->InOvertime() )
  499. {
  500. flDecrease *= 6;
  501. }
  502. SetCapTimeRemaining( m_fTimeRemaining + flDecrease );
  503. }
  504. else
  505. {
  506. SetCapTimeRemaining( flTotalTimeToCap );
  507. }
  508. }
  509. /*
  510. //if no-one is in the area
  511. if( iTeamsInZone == 0 )
  512. {
  513. BreakCapture( true );
  514. return;
  515. }
  516. //if they've lost the number of players needed to cap
  517. int iTeamMembersHere = m_TeamData[m_nCapturingTeam].iNumTouching + iNumBlockablePlayers[m_nCapturingTeam];
  518. if ( (iTeamMembersHere == 0 ) || (mp_capstyle.GetInt() == 0 && iTeamMembersHere < m_TeamData[m_nCapturingTeam].iNumRequiredToCap) )
  519. {
  520. BreakCapture( true );
  521. return;
  522. }
  523. */
  524. // if the cap is done
  525. if ( m_fTimeRemaining <= 0 )
  526. {
  527. EndCapture( m_nCapturingTeam );
  528. return; //we're done
  529. }
  530. else
  531. {
  532. // We may get several simultaneous CaptureThink calls from StartTouch if there are several players on the trigger
  533. // when it is enabled (like in Raid mode). We haven't started reducing m_fTimeRemaining yet but the second call to CaptureThink
  534. // from StartTouch has m_bCapturing set to true and we hit this condition and call BreakCapture right away.
  535. // We put this check here to prevent calling BreakCapture from the StartTouch call to CaptureThink. If the capture should
  536. // really be broken it will happen the next time the trigger thinks on its own.
  537. if ( !m_bStartTouch )
  538. {
  539. if ( m_fTimeRemaining >= flTotalTimeToCap )
  540. {
  541. BreakCapture( false );
  542. return;
  543. }
  544. }
  545. }
  546. }
  547. else
  548. {
  549. // If there are any teams in the zone that aren't the owner, try to start capping
  550. if ( iTeamsInZone > 0 )
  551. {
  552. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  553. {
  554. if ( !m_TeamData[i].bCanCap || m_nOwningTeam == i )
  555. continue;
  556. if ( m_TeamData[i].iNumTouching == 0 )
  557. continue;
  558. if ( m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToStartCap )
  559. continue;
  560. if ( !CaptureModeScalesWithPlayers() && m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToCap )
  561. continue;
  562. StartCapture( i, CAPTURE_NORMAL );
  563. break;
  564. }
  565. }
  566. }
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose:
  570. //-----------------------------------------------------------------------------
  571. void CTriggerAreaCapture::SetCapTimeRemaining( float flTime )
  572. {
  573. m_fTimeRemaining = flTime;
  574. float flCapPercentage = 0;
  575. if ( m_nCapturingTeam )
  576. {
  577. flCapPercentage = m_fTimeRemaining / m_flCapTime;
  578. if ( CaptureModeScalesWithPlayers() )
  579. {
  580. flCapPercentage = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
  581. }
  582. }
  583. ObjectiveResource()->SetCPCapPercentage( m_hPoint->GetPointIndex(), flCapPercentage );
  584. if ( m_hPoint )
  585. {
  586. m_hPoint->UpdateCapPercentage();
  587. }
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose:
  591. //-----------------------------------------------------------------------------
  592. void CTriggerAreaCapture::SetOwner( int team )
  593. {
  594. //break any current capturing
  595. BreakCapture( false );
  596. HandleRespawnTimeAdjustments( m_nOwningTeam, team );
  597. //set the owner to the passed value
  598. m_nOwningTeam = team;
  599. UpdateOwningTeam();
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose:
  603. //-----------------------------------------------------------------------------
  604. void CTriggerAreaCapture::ForceOwner( int team )
  605. {
  606. SetOwner( team );
  607. if ( m_hPoint )
  608. {
  609. m_hPoint->ForceOwner( team );
  610. }
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose:
  614. //-----------------------------------------------------------------------------
  615. void CTriggerAreaCapture::HandleRespawnTimeAdjustments( int oldTeam, int newTeam )
  616. {
  617. if ( oldTeam > LAST_SHARED_TEAM )
  618. {
  619. // reverse the adjust made when the old team captured this point (if we made one)
  620. if ( m_TeamData[oldTeam].iSpawnAdjust != 0 )
  621. {
  622. TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( oldTeam, -m_TeamData[oldTeam].iSpawnAdjust );
  623. }
  624. }
  625. if ( newTeam > LAST_SHARED_TEAM )
  626. {
  627. if ( m_TeamData[newTeam].iSpawnAdjust != 0 )
  628. {
  629. TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( newTeam, m_TeamData[newTeam].iSpawnAdjust );
  630. }
  631. }
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. void CTriggerAreaCapture::StartCapture( int team, int capmode )
  637. {
  638. // Remap team to get first game team = 1
  639. switch ( team - FIRST_GAME_TEAM+1 )
  640. {
  641. case 1:
  642. m_OnStartTeam1.FireOutput( this, this );
  643. break;
  644. case 2:
  645. m_OnStartTeam2.FireOutput( this, this );
  646. break;
  647. default:
  648. Assert(0);
  649. break;
  650. }
  651. m_StartOutput.FireOutput(this,this);
  652. m_nCapturingTeam = team;
  653. OnStartCapture( m_nCapturingTeam );
  654. UpdateNumPlayers();
  655. if ( CaptureModeScalesWithPlayers() )
  656. {
  657. SetCapTimeRemaining( ((m_flCapTime * 2) * m_TeamData[team].iNumRequiredToCap) );
  658. }
  659. else
  660. {
  661. SetCapTimeRemaining( m_flCapTime );
  662. }
  663. m_bCapturing = true;
  664. m_bBlocked = false;
  665. m_iCapMode = capmode;
  666. m_flLastReductionTime = gpGlobals->curtime;
  667. UpdateCappingTeam( m_nCapturingTeam );
  668. UpdateBlocked();
  669. if( m_hPoint )
  670. {
  671. int numcappers = 0;
  672. int cappingplayers[MAX_AREA_CAPPERS];
  673. GetNumCappingPlayers( m_nCapturingTeam, numcappers, cappingplayers );
  674. m_hPoint->CaptureStart( m_nCapturingTeam, numcappers, cappingplayers );
  675. }
  676. // tell all touching players to start racking up capture points
  677. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  678. if ( pMaster )
  679. {
  680. float flRate = pMaster->GetPartialCapturePointRate();
  681. if ( flRate > 0.0f )
  682. {
  683. // for each player touch
  684. CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
  685. if ( pTeam )
  686. {
  687. for ( int i=0;i<pTeam->GetNumPlayers();i++ )
  688. {
  689. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
  690. if ( pPlayer && IsTouching( pPlayer ) )
  691. {
  692. pPlayer->StartScoringEscortPoints( flRate );
  693. }
  694. }
  695. }
  696. }
  697. }
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Purpose:
  701. //-----------------------------------------------------------------------------
  702. void CTriggerAreaCapture::GetNumCappingPlayers( int team, int &numcappers, int *cappingplayers )
  703. {
  704. numcappers = 0;
  705. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  706. {
  707. CBaseEntity *ent = UTIL_PlayerByIndex( i );
  708. if ( ent )
  709. {
  710. CBaseMultiplayerPlayer *player = ToBaseMultiplayerPlayer(ent);
  711. if ( IsTouching( player ) && ( player->GetTeamNumber() == team ) ) // need to make sure disguised spies aren't included in the list of capping players
  712. {
  713. if ( numcappers < MAX_AREA_CAPPERS-1 )
  714. {
  715. cappingplayers[numcappers] = i;
  716. numcappers++;
  717. }
  718. }
  719. }
  720. }
  721. if ( numcappers < MAX_AREA_CAPPERS )
  722. {
  723. cappingplayers[numcappers] = 0; //null terminate :)
  724. }
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose:
  728. //-----------------------------------------------------------------------------
  729. void CTriggerAreaCapture::EndCapture( int team )
  730. {
  731. IncrementCapAttemptNumber();
  732. // Remap team to get first game team = 1
  733. switch ( team - FIRST_GAME_TEAM+1 )
  734. {
  735. case 1:
  736. m_OnCapTeam1.FireOutput( this, this );
  737. break;
  738. case 2:
  739. m_OnCapTeam2.FireOutput( this, this );
  740. break;
  741. default:
  742. Assert(0);
  743. break;
  744. }
  745. m_CapOutput.FireOutput(this,this);
  746. int numcappers = 0;
  747. int cappingplayers[MAX_AREA_CAPPERS];
  748. GetNumCappingPlayers( team, numcappers, cappingplayers );
  749. // Handle this before we assign the new team as the owner of this area
  750. HandleRespawnTimeAdjustments( m_nOwningTeam, team );
  751. m_nOwningTeam = team;
  752. m_bCapturing = false;
  753. m_nCapturingTeam = TEAM_UNASSIGNED;
  754. SetCapTimeRemaining( 0 );
  755. // play any special cap sounds. need to do this before we update the owner of the point.
  756. if ( TeamplayRoundBasedRules() )
  757. {
  758. TeamplayRoundBasedRules()->PlaySpecialCapSounds( m_nOwningTeam, m_hPoint.Get() );
  759. }
  760. //there may have been more than one capper, but only report this one.
  761. //he hasn't gotten points yet, and his name will go in the cap string if its needed
  762. //first capper gets name sent and points given by flag.
  763. //other cappers get points manually above, no name in message
  764. //send the player in the cap string
  765. if( m_hPoint )
  766. {
  767. OnEndCapture( m_nOwningTeam );
  768. UpdateOwningTeam();
  769. m_hPoint->SetOwner( m_nOwningTeam, true, numcappers, cappingplayers );
  770. m_hPoint->CaptureEnd();
  771. }
  772. SetNumCappers( 0 );
  773. // tell all touching players to stop racking up capture points
  774. CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
  775. if ( pTeam )
  776. {
  777. for ( int i=0;i<pTeam->GetNumPlayers();i++ )
  778. {
  779. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
  780. if ( pPlayer && IsTouching( pPlayer ) )
  781. {
  782. pPlayer->StopScoringEscortPoints();
  783. }
  784. }
  785. }
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Purpose:
  789. //-----------------------------------------------------------------------------
  790. void CTriggerAreaCapture::BreakCapture( bool bNotEnoughPlayers )
  791. {
  792. if( m_bCapturing )
  793. {
  794. // Remap team to get first game team = 1
  795. switch ( m_nCapturingTeam - FIRST_GAME_TEAM+1 )
  796. {
  797. case 1:
  798. m_OnBreakTeam1.FireOutput( this, this );
  799. break;
  800. case 2:
  801. m_OnBreakTeam2.FireOutput( this, this );
  802. break;
  803. default:
  804. Assert(0);
  805. break;
  806. }
  807. m_BreakOutput.FireOutput(this,this);
  808. m_bCapturing = false;
  809. m_nCapturingTeam = TEAM_UNASSIGNED;
  810. UpdateCappingTeam( TEAM_UNASSIGNED );
  811. if ( bNotEnoughPlayers )
  812. {
  813. IncrementCapAttemptNumber();
  814. }
  815. SetCapTimeRemaining( 0 );
  816. if( m_hPoint )
  817. {
  818. m_hPoint->CaptureEnd();
  819. // The point reverted to it's previous owner.
  820. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_capture_broken" );
  821. if ( event )
  822. {
  823. event->SetInt( "cp", m_hPoint->GetPointIndex() );
  824. event->SetString( "cpname", m_hPoint->GetName() );
  825. event->SetFloat( "time_remaining", m_fTimeRemaining );
  826. gameeventmanager->FireEvent( event );
  827. }
  828. }
  829. SetNumCappers( 0 );
  830. // tell all touching players to stop racking up capture points
  831. CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
  832. if ( pTeam )
  833. {
  834. for ( int i=0;i<pTeam->GetNumPlayers();i++ )
  835. {
  836. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
  837. if ( pPlayer && IsTouching( pPlayer ) )
  838. {
  839. pPlayer->StopScoringEscortPoints();
  840. }
  841. }
  842. }
  843. }
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose:
  847. //-----------------------------------------------------------------------------
  848. void CTriggerAreaCapture::IncrementCapAttemptNumber( void )
  849. {
  850. m_iCapAttemptNumber++;
  851. m_Blockers.Purge();
  852. }
  853. //-----------------------------------------------------------------------------
  854. // Purpose:
  855. //-----------------------------------------------------------------------------
  856. void CTriggerAreaCapture::InputRoundSpawn( inputdata_t &inputdata )
  857. {
  858. // find the flag we're linked to
  859. if( !m_hPoint )
  860. {
  861. m_hPoint = dynamic_cast<CTeamControlPoint*>( gEntList.FindEntityByName(NULL, STRING(m_iszCapPointName) ) );
  862. if ( m_hPoint )
  863. {
  864. m_nOwningTeam = m_hPoint->GetOwner();
  865. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  866. {
  867. m_hPoint->SetCappersRequiredForTeam( i, m_TeamData[i].iNumRequiredToCap );
  868. ObjectiveResource()->SetCPRequiredCappers( m_hPoint->GetPointIndex(), i, m_TeamData[i].iNumRequiredToCap );
  869. ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), i, m_TeamData[i].bCanCap );
  870. if ( CaptureModeScalesWithPlayers() )
  871. {
  872. ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, (m_flCapTime * 2) * m_TeamData[i].iNumRequiredToCap );
  873. }
  874. else
  875. {
  876. ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, m_flCapTime );
  877. }
  878. ObjectiveResource()->SetCPCapTimeScalesWithPlayers( m_hPoint->GetPointIndex(), CaptureModeScalesWithPlayers() );
  879. }
  880. }
  881. }
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose:
  885. //-----------------------------------------------------------------------------
  886. void CTriggerAreaCapture::InputSetTeamCanCap( inputdata_t &inputdata )
  887. {
  888. // Get the interaction name & target
  889. char parseString[255];
  890. Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
  891. char *pszParam = strtok(parseString," ");
  892. if ( pszParam && pszParam[0] )
  893. {
  894. int iTeam = atoi( pszParam );
  895. pszParam = strtok(NULL," ");
  896. if ( pszParam && pszParam[0] )
  897. {
  898. bool bCanCap = (atoi(pszParam) != 0);
  899. if ( iTeam >= 0 && iTeam < GetNumberOfTeams() )
  900. {
  901. m_TeamData[iTeam].bCanCap = bCanCap;
  902. if ( m_hPoint )
  903. {
  904. ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), iTeam, m_TeamData[iTeam].bCanCap );
  905. }
  906. return;
  907. }
  908. }
  909. }
  910. Warning("%s(%s) received SetTeamCanCap input with invalid format. Format should be: <team number> <can cap (0/1)>.\n", GetClassname(), GetDebugName() );
  911. }
  912. void CTriggerAreaCapture::InputCaptureCurrentCP( inputdata_t &inputdata )
  913. {
  914. if ( m_bCapturing )
  915. {
  916. EndCapture( m_nCapturingTeam );
  917. }
  918. }
  919. void CTriggerAreaCapture::InputSetControlPoint( inputdata_t &inputdata )
  920. {
  921. BreakCapture( false ); // clear the capping for the previous point, forces us to recalc on the new one
  922. char parseString[255];
  923. Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
  924. m_iszCapPointName = MAKE_STRING( parseString );
  925. m_hPoint = NULL; // force a reset of this
  926. InputRoundSpawn( inputdata );
  927. // force everyone touching to re-touch so the hud gets set up properly
  928. for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
  929. {
  930. CBaseEntity *ent = m_hTouchingEntities[i];
  931. if ( ent && ent->IsPlayer() )
  932. {
  933. EndTouch( ent );
  934. StartTouch( ent );
  935. }
  936. }
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose: Check if this player's death causes a block
  940. // return FALSE if the player is not in this area
  941. // return TRUE otherwise ( eg player is in area, but his death does not cause break )
  942. //-----------------------------------------------------------------------------
  943. bool CTriggerAreaCapture::CheckIfDeathCausesBlock( CBaseMultiplayerPlayer *pVictim, CBaseMultiplayerPlayer *pKiller )
  944. {
  945. if ( !pVictim || !pKiller )
  946. return false;
  947. // make sure this player is in this area
  948. if ( !IsTouching( pVictim ) )
  949. return false;
  950. // Teamkills shouldn't give a block reward
  951. if ( pVictim->GetTeamNumber() == pKiller->GetTeamNumber() )
  952. return true;
  953. // return if the area is not being capped
  954. if ( !m_bCapturing )
  955. return true;
  956. int iTeam = pVictim->GetTeamNumber();
  957. // return if this player's team is not capping the area
  958. if ( iTeam != m_nCapturingTeam )
  959. return true;
  960. // break early incase we kill multiple people in the same frame
  961. bool bBreakCap = false;
  962. if ( CaptureModeScalesWithPlayers() )
  963. {
  964. bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 ) <= 0;
  965. }
  966. else
  967. {
  968. bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 < m_TeamData[m_nCapturingTeam].iNumRequiredToCap );
  969. }
  970. if ( bBreakCap )
  971. {
  972. m_hPoint->CaptureBlocked( pKiller, pVictim );
  973. //BreakCapture( true );
  974. }
  975. return true;
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose:
  979. //-----------------------------------------------------------------------------
  980. void CTriggerAreaCapture::UpdateNumPlayers( bool bBlocked /*= false */ )
  981. {
  982. if( !m_hPoint )
  983. return;
  984. int index = m_hPoint->GetPointIndex();
  985. for ( int i = 0; i < m_TeamData.Count(); i++ )
  986. {
  987. if ( i >= FIRST_GAME_TEAM && i == m_nCapturingTeam )
  988. {
  989. SetNumCappers( m_TeamData[i].iNumTouching, bBlocked );
  990. }
  991. ObjectiveResource()->SetNumPlayers( index, i, m_TeamData[i].iNumTouching );
  992. }
  993. }
  994. //-----------------------------------------------------------------------------
  995. // Purpose:
  996. //-----------------------------------------------------------------------------
  997. void CTriggerAreaCapture::UpdateOwningTeam( void )
  998. {
  999. if ( m_hPoint )
  1000. {
  1001. ObjectiveResource()->SetOwningTeam( m_hPoint->GetPointIndex(), m_nOwningTeam );
  1002. }
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose:
  1006. //-----------------------------------------------------------------------------
  1007. void CTriggerAreaCapture::UpdateCappingTeam( int iTeam )
  1008. {
  1009. if ( m_hPoint )
  1010. {
  1011. ObjectiveResource()->SetCappingTeam( m_hPoint->GetPointIndex(), iTeam );
  1012. }
  1013. }
  1014. //-----------------------------------------------------------------------------
  1015. // Purpose:
  1016. //-----------------------------------------------------------------------------
  1017. void CTriggerAreaCapture::UpdateTeamInZone( void )
  1018. {
  1019. if ( m_hPoint )
  1020. {
  1021. ObjectiveResource()->SetTeamInZone( m_hPoint->GetPointIndex(), m_nTeamInZone );
  1022. }
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. // Purpose:
  1026. //-----------------------------------------------------------------------------
  1027. void CTriggerAreaCapture::UpdateBlocked( void )
  1028. {
  1029. if ( m_hPoint )
  1030. {
  1031. ObjectiveResource()->SetCapBlocked( m_hPoint->GetPointIndex(), m_bBlocked );
  1032. m_hPoint->CaptureInterrupted( m_bBlocked );
  1033. }
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Purpose:
  1037. //-----------------------------------------------------------------------------
  1038. void CTriggerAreaCapture::SetNumCappers( int nNumCappers, bool bBlocked /* = false */ )
  1039. {
  1040. m_OnNumCappersChanged.Set( nNumCappers, this, this );
  1041. // m_OnNumCappersChanged2 sets -1 for a blocked cart (for movement decisions on hills)
  1042. if ( bBlocked )
  1043. {
  1044. nNumCappers = -1;
  1045. }
  1046. m_OnNumCappersChanged2.Set( nNumCappers, this, this );
  1047. if ( m_hTrainWatcher.Get() )
  1048. {
  1049. m_hTrainWatcher->SetNumTrainCappers( nNumCappers, this );
  1050. }
  1051. }