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.

590 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "dod_area_capture.h"
  8. #include "dod_player.h"
  9. #include "dod_gamerules.h"
  10. #include "dod_control_point.h"
  11. #include "dod_team.h"
  12. #include "dod_objective_resource.h"
  13. //-------------------------------------------------------------------
  14. // CAreaCapture
  15. // An area entity that players must remain in in order to active another entity
  16. // Triggers are fired on start of capture, on end of capture and on broken capture
  17. // Can either be capped by both teams at once, or just by one
  18. // Time to capture and number of people required to capture are both passed by the mapper
  19. BEGIN_DATADESC(CAreaCapture)
  20. // Touch functions
  21. DEFINE_FUNCTION( AreaTouch ),
  22. // Think functions
  23. DEFINE_THINKFUNC( Think ),
  24. // Keyfields
  25. DEFINE_KEYFIELD( m_iszCapPointName, FIELD_STRING, "area_cap_point" ),
  26. DEFINE_KEYFIELD( m_nAlliesNumCap, FIELD_INTEGER, "area_allies_numcap" ),
  27. DEFINE_KEYFIELD( m_nAxisNumCap, FIELD_INTEGER, "area_axis_numcap" ),
  28. DEFINE_KEYFIELD( m_flCapTime, FIELD_FLOAT, "area_time_to_cap" ),
  29. DEFINE_KEYFIELD( m_bAlliesCanCap, FIELD_BOOLEAN, "area_allies_cancap" ),
  30. DEFINE_KEYFIELD( m_bAxisCanCap, FIELD_BOOLEAN, "area_axis_cancap" ),
  31. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  32. // Inputs
  33. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  34. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  35. DEFINE_INPUTFUNC( FIELD_VOID, "RoundInit", InputRoundInit ),
  36. // Outputs
  37. DEFINE_OUTPUT( m_AlliesStartOutput, "OnAlliesStartCap" ),
  38. DEFINE_OUTPUT( m_AlliesBreakOutput, "OnAlliesBreakCap" ),
  39. DEFINE_OUTPUT( m_AlliesCapOutput, "OnAlliesEndCap" ),
  40. DEFINE_OUTPUT( m_AxisStartOutput, "OnAxisStartCap" ),
  41. DEFINE_OUTPUT( m_AxisBreakOutput, "OnAxisBreakCap" ),
  42. DEFINE_OUTPUT( m_AxisCapOutput, "OnAxisEndCap" ),
  43. DEFINE_OUTPUT( m_StartOutput, "OnStartCap" ),
  44. DEFINE_OUTPUT( m_BreakOutput, "OnBreakCap" ),
  45. DEFINE_OUTPUT( m_CapOutput, "OnEndCap" ),
  46. // DEFINE_OUTPUT( m_OnStartCap, "OnStartCap" );
  47. // DEFINE_OUTPUT( m_OnBreakCap,
  48. // DEFINE_OUTPUT( m_OnEnterNoObj, "OnEnterNoObj" );
  49. END_DATADESC();
  50. LINK_ENTITY_TO_CLASS( dod_capture_area, CAreaCapture );
  51. void CAreaCapture::Spawn( void )
  52. {
  53. BaseClass::Spawn();
  54. InitTrigger();
  55. Precache();
  56. m_iAreaIndex = -1;
  57. SetTouch ( &CAreaCapture::AreaTouch );
  58. m_bCapturing = false;
  59. m_nCapturingTeam = TEAM_UNASSIGNED;
  60. m_nOwningTeam = TEAM_UNASSIGNED;
  61. m_fTimeRemaining = 0.0f;
  62. SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
  63. if( m_nAlliesNumCap < 1 )
  64. m_nAlliesNumCap = 1;
  65. if( m_nAxisNumCap < 1 )
  66. m_nAxisNumCap = 1;
  67. m_bDisabled = false;
  68. m_iCapAttemptNumber = 0;
  69. }
  70. void CAreaCapture::Precache( void )
  71. {
  72. }
  73. bool CAreaCapture::KeyValue( const char *szKeyName, const char *szValue )
  74. {
  75. return BaseClass::KeyValue( szKeyName, szValue );
  76. }
  77. //sends to all players at the start of the round
  78. //needed?
  79. void CAreaCapture::area_SetIndex( int index )
  80. {
  81. m_iAreaIndex = index;
  82. }
  83. bool CAreaCapture::IsActive( void )
  84. {
  85. return !m_bDisabled;
  86. }
  87. void CAreaCapture::AreaTouch( CBaseEntity *pOther )
  88. {
  89. //if they are touching, set their SIGNAL flag on, and their m_iCapAreaNum to ours
  90. //then in think do all the scoring
  91. if( !IsActive() )
  92. return;
  93. //Don't cap areas unless the round is running
  94. if( DODGameRules()->State_Get() != STATE_RND_RUNNING || DODGameRules()->IsInWarmup() )
  95. return;
  96. Assert( m_iAreaIndex != -1 );
  97. if( m_pPoint )
  98. {
  99. m_nOwningTeam = m_pPoint->GetOwner();
  100. }
  101. //dont touch for non-alive or non-players
  102. if( !pOther->IsPlayer() )
  103. return;
  104. if( !pOther->IsAlive() )
  105. return;
  106. CDODPlayer *pPlayer = ToDODPlayer(pOther);
  107. ASSERT( pPlayer );
  108. if ( pPlayer->GetTeamNumber() != m_nOwningTeam )
  109. {
  110. bool bAbleToCap = ( pPlayer->GetTeamNumber() == TEAM_ALLIES && m_bAlliesCanCap ) ||
  111. ( pPlayer->GetTeamNumber() == TEAM_AXIS && m_bAxisCanCap );
  112. if ( bAbleToCap )
  113. pPlayer->HintMessage( HINT_IN_AREA_CAP );
  114. }
  115. pPlayer->m_signals.Signal( SIGNAL_CAPTUREAREA );
  116. //add them to this area
  117. pPlayer->SetCapAreaIndex( m_iAreaIndex );
  118. if ( m_pPoint )
  119. {
  120. pPlayer->SetCPIndex( m_pPoint->GetPointIndex() );
  121. }
  122. }
  123. /* three ways to be capturing a cap area
  124. * 1) have the required number of people in the area
  125. * 2) have less than the required number on your team, new required num is everyone
  126. * 3) have less than the required number alive, new required is numAlive, but time is lengthened
  127. */
  128. ConVar dod_simulatemultiplecappers( "dod_simulatemultiplecappers", "1", FCVAR_CHEAT );
  129. void CAreaCapture::Think( void )
  130. {
  131. SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
  132. if( DODGameRules()->State_Get() != STATE_RND_RUNNING )
  133. {
  134. // If we were being capped, cancel it
  135. if( m_nNumAllies > 0 || m_nNumAxis > 0 )
  136. {
  137. m_nNumAllies = 0;
  138. m_nNumAxis = 0;
  139. SendNumPlayers();
  140. if( m_pPoint )
  141. {
  142. g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), TEAM_UNASSIGNED );
  143. }
  144. }
  145. return;
  146. }
  147. // go through our list of players
  148. int iNumAllies = 0;
  149. int iNumAxis = 0;
  150. CDODPlayer *pFirstAlliedTouching = NULL;
  151. CDODPlayer *pFirstAxisTouching = NULL;
  152. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  153. {
  154. CBaseEntity *ent = UTIL_PlayerByIndex( i );
  155. if ( ent )
  156. {
  157. CDODPlayer *pPlayer = ToDODPlayer(ent);
  158. //First check if the player is in fact in this area
  159. if ( ( pPlayer->m_signals.GetState() & SIGNAL_CAPTUREAREA ) &&
  160. pPlayer->GetCapAreaIndex() == m_iAreaIndex &&
  161. pPlayer->IsAlive() ) // alive check is kinda unnecessary, but there is some
  162. // case where non-present people are messing up this count
  163. {
  164. if ( pPlayer->GetTeamNumber() == TEAM_ALLIES )
  165. {
  166. if ( iNumAllies == 0 )
  167. pFirstAlliedTouching = pPlayer;
  168. iNumAllies++;
  169. }
  170. else if ( pPlayer->GetTeamNumber() == TEAM_AXIS )
  171. {
  172. if ( iNumAxis == 0 )
  173. pFirstAxisTouching = pPlayer;
  174. iNumAxis++;
  175. }
  176. }
  177. }
  178. }
  179. iNumAllies *= dod_simulatemultiplecappers.GetInt();
  180. iNumAxis *= dod_simulatemultiplecappers.GetInt();
  181. if( iNumAllies != m_nNumAllies || iNumAxis != m_nNumAxis )
  182. {
  183. m_nNumAllies = iNumAllies;
  184. m_nNumAxis = iNumAxis;
  185. SendNumPlayers();
  186. }
  187. // when a player blocks, tell them the cap index and attempt number
  188. // only give successive blocks to them if the attempt number is different
  189. if( m_bCapturing )
  190. {
  191. //its a regular cap
  192. //Subtract some time from the cap
  193. m_fTimeRemaining -= AREA_THINK_TIME;
  194. //if both teams are in the area
  195. if( iNumAllies > 0 && iNumAxis > 0 )
  196. {
  197. // See if anyone gets credit for the block
  198. float flPercentToGo = m_fTimeRemaining / m_flCapTime;
  199. if ( flPercentToGo <= 0.5 && m_pPoint )
  200. {
  201. // find the first player that is not on the capturing team
  202. // they have just broken a cap and should be rewarded
  203. // tell the player the capture attempt number, for checking later
  204. CDODPlayer *pBlockingPlayer = ( m_nCapturingTeam == TEAM_ALLIES ) ? pFirstAxisTouching : pFirstAlliedTouching;
  205. if ( pBlockingPlayer )
  206. {
  207. if ( pBlockingPlayer->GetCapAreaIndex() == m_iAreaIndex &&
  208. pBlockingPlayer->GetLastBlockCapAttempt() == m_iCapAttemptNumber )
  209. {
  210. // this is a repeat block on the same cap, ignore it
  211. NULL;
  212. }
  213. else
  214. {
  215. m_pPoint->CaptureBlocked( pBlockingPlayer );
  216. pBlockingPlayer->StoreCaptureBlock( m_iAreaIndex, m_iCapAttemptNumber );
  217. }
  218. }
  219. }
  220. BreakCapture( false );
  221. return;
  222. }
  223. //if no-one is in the area
  224. if( iNumAllies == 0 && iNumAxis == 0 )
  225. {
  226. BreakCapture( true );
  227. return;
  228. }
  229. if( m_nCapturingTeam == TEAM_ALLIES )
  230. {
  231. if( iNumAllies < m_nAlliesNumCap )
  232. {
  233. BreakCapture( true );
  234. }
  235. }
  236. else if( m_nCapturingTeam == TEAM_AXIS )
  237. {
  238. if( iNumAxis < m_nAxisNumCap )
  239. {
  240. BreakCapture( true );
  241. }
  242. }
  243. //if the cap is done
  244. if( m_fTimeRemaining <= 0 )
  245. {
  246. EndCapture( m_nCapturingTeam );
  247. return; //we're done
  248. }
  249. }
  250. else //not capturing yet
  251. {
  252. bool bStarted = false;
  253. if( iNumAllies > 0 && iNumAxis <= 0 && m_bAlliesCanCap && m_nOwningTeam != TEAM_ALLIES )
  254. {
  255. if( iNumAllies >= m_nAlliesNumCap )
  256. {
  257. m_iCappingRequired = m_nAlliesNumCap;
  258. m_iCappingPlayers = iNumAllies;
  259. StartCapture( TEAM_ALLIES, CAPTURE_NORMAL );
  260. bStarted = true;
  261. }
  262. }
  263. else if( iNumAxis > 0 && iNumAllies <= 0 && m_bAxisCanCap && m_nOwningTeam != TEAM_AXIS )
  264. {
  265. if( iNumAxis >= m_nAxisNumCap )
  266. {
  267. m_iCappingRequired = m_nAxisNumCap;
  268. m_iCappingPlayers = iNumAxis;
  269. StartCapture( TEAM_AXIS, CAPTURE_NORMAL );
  270. bStarted = true;
  271. }
  272. }
  273. }
  274. }
  275. void CAreaCapture::SetOwner( int team )
  276. {
  277. //break any current capturing
  278. BreakCapture( false );
  279. //set the owner to the passed value
  280. m_nOwningTeam = team;
  281. g_pObjectiveResource->SetOwningTeam( m_pPoint->GetPointIndex(), m_nOwningTeam );
  282. }
  283. void CAreaCapture::SendNumPlayers( CBasePlayer *pPlayer )
  284. {
  285. if( !m_pPoint )
  286. return;
  287. int index = m_pPoint->GetPointIndex();
  288. g_pObjectiveResource->SetNumPlayers( index, TEAM_ALLIES, m_nNumAllies );
  289. g_pObjectiveResource->SetNumPlayers( index, TEAM_AXIS, m_nNumAxis );
  290. }
  291. void CAreaCapture::StartCapture( int team, int capmode )
  292. {
  293. int iNumCappers = 0;
  294. //trigger start
  295. if( team == TEAM_ALLIES )
  296. {
  297. m_AlliesStartOutput.FireOutput(this,this);
  298. iNumCappers = m_nAlliesNumCap;
  299. }
  300. else if( team == TEAM_AXIS )
  301. {
  302. m_AxisStartOutput.FireOutput(this,this);
  303. iNumCappers = m_nAxisNumCap;
  304. }
  305. m_StartOutput.FireOutput(this,this);
  306. m_nCapturingTeam = team;
  307. m_fTimeRemaining = m_flCapTime;
  308. m_bCapturing = true;
  309. m_iCapMode = capmode;
  310. if( m_pPoint )
  311. {
  312. //send a message that we're starting to cap this area
  313. g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), m_nCapturingTeam );
  314. }
  315. }
  316. #define MAX_AREA_CAPPERS 9
  317. void CAreaCapture::EndCapture( int team )
  318. {
  319. m_iCapAttemptNumber++;
  320. //do the triggering
  321. if( team == TEAM_ALLIES )
  322. {
  323. m_AlliesCapOutput.FireOutput(this,this);
  324. }
  325. else if( team == TEAM_AXIS )
  326. {
  327. m_AxisCapOutput.FireOutput(this,this);
  328. }
  329. m_CapOutput.FireOutput(this,this);
  330. m_iCappingRequired = 0;
  331. m_iCappingPlayers = 0;
  332. int numcappers = 0;
  333. int cappingplayers[MAX_AREA_CAPPERS];
  334. CDODPlayer *pCappingPlayer = NULL;
  335. CDODTeam *pTeam = GetGlobalDODTeam(team);
  336. if ( pTeam )
  337. {
  338. int iCount = pTeam->GetNumPlayers();
  339. for ( int i=0;i<iCount;i++ )
  340. {
  341. CDODPlayer *pPlayer = ToDODPlayer( pTeam->GetPlayer(i) );
  342. if ( pPlayer )
  343. {
  344. if( ( pPlayer->m_signals.GetState() & SIGNAL_CAPTUREAREA ) &&
  345. pPlayer->GetCapAreaIndex() == m_iAreaIndex &&
  346. pPlayer->IsAlive() )
  347. {
  348. if( pCappingPlayer == NULL )
  349. pCappingPlayer = pPlayer;
  350. if ( numcappers < MAX_AREA_CAPPERS-1 )
  351. {
  352. cappingplayers[numcappers] = pPlayer->entindex();
  353. numcappers++;
  354. }
  355. }
  356. }
  357. }
  358. }
  359. if ( numcappers < MAX_AREA_CAPPERS )
  360. {
  361. cappingplayers[numcappers] = 0; //null terminate :)
  362. }
  363. m_nOwningTeam = team;
  364. m_bCapturing = false;
  365. m_fTimeRemaining = 0.0f;
  366. //there may have been more than one capper, but only report this one.
  367. //he hasnt gotten points yet, and his name will go in the cap string if its needed
  368. //first capper gets name sent and points given by flag.
  369. //other cappers get points manually above, no name in message
  370. //send the player in the cap string
  371. if( m_pPoint )
  372. {
  373. DODGameRules()->CapEvent( CAP_EVENT_FLAG, m_nOwningTeam );
  374. g_pObjectiveResource->SetOwningTeam( m_pPoint->GetPointIndex(), m_nOwningTeam );
  375. m_pPoint->SetOwner( m_nOwningTeam, true, numcappers, cappingplayers );
  376. }
  377. }
  378. void CAreaCapture::BreakCapture( bool bNotEnoughPlayers )
  379. {
  380. if( m_bCapturing )
  381. {
  382. m_iCappingRequired = 0;
  383. m_iCappingPlayers = 0;
  384. if( m_nCapturingTeam == TEAM_ALLIES )
  385. m_AlliesBreakOutput.FireOutput(this,this);
  386. else if( m_nCapturingTeam == TEAM_AXIS )
  387. m_AxisBreakOutput.FireOutput(this,this);
  388. m_BreakOutput.FireOutput(this,this);
  389. m_bCapturing = false;
  390. if( m_pPoint )
  391. {
  392. g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), TEAM_UNASSIGNED );
  393. }
  394. if ( bNotEnoughPlayers )
  395. {
  396. m_iCapAttemptNumber++;
  397. }
  398. }
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Purpose:
  402. //-----------------------------------------------------------------------------
  403. void CAreaCapture::InputDisable( inputdata_t &inputdata )
  404. {
  405. m_bDisabled = true;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose:
  409. //-----------------------------------------------------------------------------
  410. void CAreaCapture::InputEnable( inputdata_t &inputdata )
  411. {
  412. m_bDisabled = false;
  413. }
  414. void CAreaCapture::InputRoundInit( inputdata_t &inputdata )
  415. {
  416. // find the flag we're linked to
  417. if( !m_pPoint )
  418. {
  419. m_pPoint = dynamic_cast<CControlPoint*>( gEntList.FindEntityByName(NULL, STRING(m_iszCapPointName) ) );
  420. if ( m_pPoint )
  421. {
  422. m_pPoint->SetNumCappersRequired( m_nAlliesNumCap, m_nAxisNumCap );
  423. g_pObjectiveResource->SetCPRequiredCappers( m_pPoint->GetPointIndex(), m_nAlliesNumCap, m_nAxisNumCap );
  424. g_pObjectiveResource->SetCPCapTime( m_pPoint->GetPointIndex(), m_flCapTime, m_flCapTime );
  425. }
  426. }
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose: Check if this player's death causes a block
  430. // return FALSE if the player is not in this area
  431. // return TRUE otherwise ( eg player is in area, but his death does not cause break )
  432. //-----------------------------------------------------------------------------
  433. bool CAreaCapture::CheckIfDeathCausesBlock( CDODPlayer *pVictim, CDODPlayer *pKiller )
  434. {
  435. // This shouldn't happen
  436. if ( !pVictim || !pKiller )
  437. {
  438. Assert( !"Why are null players getting here?" );
  439. return false;
  440. }
  441. // make sure this player is in this area
  442. if ( pVictim->GetCapAreaIndex() != m_iAreaIndex )
  443. return false;
  444. // Teamkills shouldn't give a block reward
  445. if ( pVictim->GetTeamNumber() == pKiller->GetTeamNumber() )
  446. return true;
  447. // return if the area is not being capped
  448. if ( !m_bCapturing )
  449. return true;
  450. int iTeam = pVictim->GetTeamNumber();
  451. // return if this player's team is not capping the area
  452. if ( iTeam != m_nCapturingTeam )
  453. return true;
  454. bool bBlocked = false;
  455. if ( m_nCapturingTeam == TEAM_ALLIES )
  456. {
  457. if ( m_nNumAllies-1 < m_nAlliesNumCap )
  458. bBlocked = true;
  459. }
  460. else if ( m_nCapturingTeam == TEAM_AXIS )
  461. {
  462. if ( m_nNumAxis-1 < m_nAxisNumCap )
  463. bBlocked = true;
  464. }
  465. // break early incase we kill multiple people in the same frame
  466. if ( bBlocked )
  467. {
  468. m_pPoint->CaptureBlocked( pKiller );
  469. BreakCapture( false );
  470. }
  471. return true;
  472. }