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.

541 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "func_respawnroom.h"
  8. #include "func_no_build.h"
  9. #include "tf_team.h"
  10. #include "ndebugoverlay.h"
  11. #include "tf_gamerules.h"
  12. #include "entity_tfstart.h"
  13. #include "modelentities.h"
  14. #include "tf_obj_sentrygun.h"
  15. #include "entity_rune.h"
  16. #include "tf_item.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Visualizes a respawn room to the enemy team
  21. //-----------------------------------------------------------------------------
  22. DECLARE_AUTO_LIST( IFuncRespawnRoomVisualizerAutoList );
  23. class CFuncRespawnRoomVisualizer : public CFuncBrush, public IFuncRespawnRoomVisualizerAutoList
  24. {
  25. DECLARE_CLASS( CFuncRespawnRoomVisualizer, CFuncBrush );
  26. public:
  27. DECLARE_DATADESC();
  28. DECLARE_SERVERCLASS();
  29. CFuncRespawnRoomVisualizer();
  30. virtual void Spawn( void );
  31. void InputRoundActivate( inputdata_t &inputdata );
  32. int DrawDebugTextOverlays( void );
  33. CFuncRespawnRoom *GetRespawnRoom( void ) { return m_hRespawnRoom; }
  34. virtual int UpdateTransmitState( void );
  35. virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
  36. virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const;
  37. void InputSetSolid( inputdata_t &inputdata );
  38. void SetActive( bool bActive );
  39. protected:
  40. string_t m_iszRespawnRoomName;
  41. CHandle<CFuncRespawnRoom> m_hRespawnRoom;
  42. bool m_bSolid;
  43. };
  44. IMPLEMENT_AUTO_LIST( IFuncRespawnRoomVisualizerAutoList );
  45. IMPLEMENT_AUTO_LIST( IFuncRespawnRoomAutoList );
  46. LINK_ENTITY_TO_CLASS( func_respawnroom, CFuncRespawnRoom);
  47. BEGIN_DATADESC( CFuncRespawnRoom )
  48. DEFINE_FUNCTION( CFuncRespawnRoomShim::Touch ),
  49. // inputs
  50. DEFINE_INPUTFUNC( FIELD_VOID, "SetActive", InputSetActive ),
  51. DEFINE_INPUTFUNC( FIELD_VOID, "SetInactive", InputSetInactive ),
  52. DEFINE_INPUTFUNC( FIELD_VOID, "ToggleActive", InputToggleActive ),
  53. DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
  54. END_DATADESC()
  55. IMPLEMENT_SERVERCLASS_ST( CFuncRespawnRoom, DT_FuncRespawnRoom )
  56. END_SEND_TABLE()
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. CFuncRespawnRoom::CFuncRespawnRoom()
  61. {
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Initializes the resource zone
  65. //-----------------------------------------------------------------------------
  66. void CFuncRespawnRoom::Spawn( void )
  67. {
  68. AddSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS);
  69. BaseClass::Spawn();
  70. InitTrigger();
  71. SetCollisionGroup( TFCOLLISION_GROUP_RESPAWNROOMS );
  72. m_bActive = true;
  73. SetTouch( &CFuncRespawnRoom::RespawnRoomTouch );
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Purpose:
  77. //-----------------------------------------------------------------------------
  78. void CFuncRespawnRoom::Activate( void )
  79. {
  80. BaseClass::Activate();
  81. m_iOriginalTeam = GetTeamNumber();
  82. SetActive( true );
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose:
  86. // Input : pOther - The thing that touched us.
  87. //-----------------------------------------------------------------------------
  88. void CFuncRespawnRoom::RespawnRoomTouch(CBaseEntity *pOther)
  89. {
  90. if ( TFGameRules()->IsMannVsMachineMode() )
  91. {
  92. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  93. {
  94. return;
  95. }
  96. }
  97. if ( PassesTriggerFilters( pOther ) )
  98. {
  99. if ( pOther->IsPlayer() && InSameTeam( pOther ) )
  100. {
  101. // Players carrying the flag drop it if they try to run into a respawn room
  102. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  103. if ( pPlayer->HasTheFlag() )
  104. {
  105. pPlayer->DropFlag();
  106. }
  107. else if ( TFGameRules() && TFGameRules()->GetGameType() == TF_GAMETYPE_PD && pPlayer->HasItem() && ( pPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG ) )
  108. {
  109. pPlayer->GetItem()->Drop( pPlayer, true, true, true );
  110. }
  111. if ( pPlayer->m_Shared.IsCarryingObject() && TFGameRules()->IsMannVsMachineMode() )
  112. {
  113. CObjectSentrygun *pSentry = dynamic_cast< CObjectSentrygun* >( pPlayer->m_Shared.GetCarriedObject() );
  114. if ( pSentry )
  115. {
  116. pSentry->UpdatePlacement();
  117. pSentry->DetonateObject();
  118. }
  119. }
  120. // Drop your powerup rune when entering a respawn room.
  121. // False parameter ensures rune isn't unintentionally 'thrown' into the respawn room
  122. pPlayer->DropRune( false );
  123. }
  124. }
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose:
  128. //-----------------------------------------------------------------------------
  129. void CFuncRespawnRoom::StartTouch(CBaseEntity *pOther)
  130. {
  131. CTFPlayer *pTFPlayer = ToTFPlayer( pOther );
  132. if ( pTFPlayer )
  133. {
  134. pTFPlayer->m_Shared.IncrementRespawnTouchCount();
  135. }
  136. BaseClass::StartTouch( pOther );
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose:
  140. //-----------------------------------------------------------------------------
  141. void CFuncRespawnRoom::EndTouch(CBaseEntity *pOther)
  142. {
  143. CTFPlayer *pTFPlayer = ToTFPlayer( pOther );
  144. if ( pTFPlayer )
  145. {
  146. pTFPlayer->m_Shared.DecrementRespawnTouchCount();
  147. }
  148. BaseClass::EndTouch( pOther );
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose:
  152. //-----------------------------------------------------------------------------
  153. void CFuncRespawnRoom::InputSetActive( inputdata_t &inputdata )
  154. {
  155. SetActive( true );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. void CFuncRespawnRoom::InputSetInactive( inputdata_t &inputdata )
  161. {
  162. SetActive( false );
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. //-----------------------------------------------------------------------------
  167. void CFuncRespawnRoom::InputToggleActive( inputdata_t &inputdata )
  168. {
  169. if ( m_bActive )
  170. {
  171. SetActive( false );
  172. }
  173. else
  174. {
  175. SetActive( true );
  176. }
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose:
  180. //-----------------------------------------------------------------------------
  181. void CFuncRespawnRoom::InputRoundActivate( inputdata_t &input )
  182. {
  183. if ( m_iOriginalTeam == TEAM_UNASSIGNED )
  184. {
  185. ChangeTeam( TEAM_UNASSIGNED );
  186. // If we don't have a team, find a respawn point inside us that we can derive a team from.
  187. for ( int i=0; i<ITFTeamSpawnAutoList::AutoList().Count(); ++i )
  188. {
  189. CTFTeamSpawn *pTFSpawn = static_cast< CTFTeamSpawn* >( ITFTeamSpawnAutoList::AutoList()[i] );
  190. if ( PointIsWithin( pTFSpawn->GetAbsOrigin() ) )
  191. {
  192. if ( !pTFSpawn->IsDisabled() && pTFSpawn->GetTeamNumber() > LAST_SHARED_TEAM )
  193. {
  194. ChangeTeam( pTFSpawn->GetTeamNumber() );
  195. break;
  196. }
  197. }
  198. }
  199. if ( GetTeamNumber() == TEAM_UNASSIGNED )
  200. {
  201. DevMsg( "Unassigned %s(%s) failed to find an info_player_teamspawn within it to use.\n", GetClassname(), GetDebugName() );
  202. }
  203. }
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose:
  207. //-----------------------------------------------------------------------------
  208. void CFuncRespawnRoom::ChangeTeam( int iTeamNum )
  209. {
  210. BaseClass::ChangeTeam( iTeamNum );
  211. for ( int i = m_hVisualizers.Count()-1; i >= 0; i-- )
  212. {
  213. if ( m_hVisualizers[i] )
  214. {
  215. Assert( m_hVisualizers[i]->GetRespawnRoom() == this );
  216. m_hVisualizers[i]->ChangeTeam( iTeamNum );
  217. }
  218. else
  219. {
  220. m_hVisualizers.Remove(i);
  221. }
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. //-----------------------------------------------------------------------------
  227. void CFuncRespawnRoom::SetActive( bool bActive )
  228. {
  229. m_bActive = bActive;
  230. if ( m_bActive )
  231. {
  232. Enable();
  233. }
  234. else
  235. {
  236. Disable();
  237. }
  238. for ( int i = m_hVisualizers.Count()-1; i >= 0; i-- )
  239. {
  240. if ( m_hVisualizers[i] )
  241. {
  242. Assert( m_hVisualizers[i]->GetRespawnRoom() == this );
  243. m_hVisualizers[i]->SetActive( m_bActive );
  244. }
  245. else
  246. {
  247. m_hVisualizers.Remove(i);
  248. }
  249. }
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose:
  253. //-----------------------------------------------------------------------------
  254. bool CFuncRespawnRoom::GetActive() const
  255. {
  256. return m_bActive;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose:
  260. //-----------------------------------------------------------------------------
  261. void CFuncRespawnRoom::AddVisualizer( CFuncRespawnRoomVisualizer *pViz )
  262. {
  263. if ( m_hVisualizers.Find(pViz) == m_hVisualizers.InvalidIndex() )
  264. {
  265. m_hVisualizers.AddToTail( pViz );
  266. }
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Is a given point contained within a respawn room?
  270. //-----------------------------------------------------------------------------
  271. bool PointInRespawnRoom( const CBaseEntity *pTarget, const Vector &vecOrigin, bool bTouching_SameTeamOnly /*= false*/ )
  272. {
  273. // Find out whether we're in a respawn room or not
  274. for ( int i=0; i<IFuncRespawnRoomAutoList::AutoList().Count(); ++i )
  275. {
  276. CFuncRespawnRoom *pRespawnRoom = static_cast< CFuncRespawnRoom* >( IFuncRespawnRoomAutoList::AutoList()[i] );
  277. // Are we within this respawn room?
  278. if ( pRespawnRoom->GetActive() )
  279. {
  280. if ( pRespawnRoom->PointIsWithin( vecOrigin ) )
  281. {
  282. if ( !pTarget || pRespawnRoom->GetTeamNumber() == TEAM_UNASSIGNED || pRespawnRoom->InSameTeam( pTarget ) )
  283. return true;
  284. }
  285. else
  286. {
  287. if ( pTarget && pRespawnRoom->IsTouching( pTarget ) )
  288. {
  289. if ( !bTouching_SameTeamOnly || ( pRespawnRoom->GetTeamNumber() == TEAM_UNASSIGNED || pRespawnRoom->InSameTeam( pTarget ) ) )
  290. return true;
  291. }
  292. }
  293. }
  294. }
  295. return false;
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose:
  299. //-----------------------------------------------------------------------------
  300. bool PointsCrossRespawnRoomVisualizer( const Vector& vecStart, const Vector &vecEnd, int nTeamToIgnore )
  301. {
  302. // Setup the ray.
  303. Ray_t ray;
  304. ray.Init( vecStart, vecEnd );
  305. for ( int i=0; i<IFuncRespawnRoomVisualizerAutoList::AutoList().Count(); ++i )
  306. {
  307. CFuncRespawnRoomVisualizer *pEntity = static_cast< CFuncRespawnRoomVisualizer* >( IFuncRespawnRoomVisualizerAutoList::AutoList()[i] );
  308. if( pEntity->GetTeamNumber() == nTeamToIgnore && nTeamToIgnore != TEAM_UNASSIGNED )
  309. continue;
  310. trace_t trace;
  311. enginetrace->ClipRayToEntity( ray, MASK_ALL, pEntity, &trace );
  312. if ( trace.fraction < 1.0f )
  313. {
  314. return true;
  315. }
  316. }
  317. return false;
  318. }
  319. //===========================================================================================================
  320. LINK_ENTITY_TO_CLASS( func_respawnroomvisualizer, CFuncRespawnRoomVisualizer);
  321. BEGIN_DATADESC( CFuncRespawnRoomVisualizer )
  322. DEFINE_KEYFIELD( m_iszRespawnRoomName, FIELD_STRING, "respawnroomname" ),
  323. DEFINE_KEYFIELD( m_bSolid, FIELD_BOOLEAN, "solid_to_enemies" ),
  324. // inputs
  325. DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
  326. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetSolid", InputSetSolid ),
  327. END_DATADESC()
  328. IMPLEMENT_SERVERCLASS_ST( CFuncRespawnRoomVisualizer, DT_FuncRespawnRoomVisualizer )
  329. END_SEND_TABLE()
  330. CFuncRespawnRoomVisualizer::CFuncRespawnRoomVisualizer()
  331. {
  332. m_bSolid = true;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. //-----------------------------------------------------------------------------
  337. void CFuncRespawnRoomVisualizer::Spawn( void )
  338. {
  339. BaseClass::Spawn();
  340. SetActive( true );
  341. SetCollisionGroup( TFCOLLISION_GROUP_RESPAWNROOMS );
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Purpose:
  345. //-----------------------------------------------------------------------------
  346. void CFuncRespawnRoomVisualizer::InputRoundActivate( inputdata_t &inputdata )
  347. {
  348. if ( m_iszRespawnRoomName != NULL_STRING )
  349. {
  350. m_hRespawnRoom = dynamic_cast<CFuncRespawnRoom*>(gEntList.FindEntityByName( NULL, m_iszRespawnRoomName ));
  351. if ( m_hRespawnRoom )
  352. {
  353. m_hRespawnRoom->AddVisualizer( this );
  354. ChangeTeam( m_hRespawnRoom->GetTeamNumber() );
  355. }
  356. else
  357. {
  358. Warning("%s(%s) was unable to find func_respawnroomvisualizer named '%s'\n", GetClassname(), GetDebugName(), STRING(m_iszRespawnRoomName) );
  359. }
  360. }
  361. SetActive( m_bSolid );
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Draw any debug text overlays
  365. // Input :
  366. // Output : Current text offset from the top
  367. //-----------------------------------------------------------------------------
  368. int CFuncRespawnRoomVisualizer::DrawDebugTextOverlays( void )
  369. {
  370. int text_offset = BaseClass::DrawDebugTextOverlays();
  371. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  372. {
  373. char tempstr[512];
  374. Q_snprintf(tempstr,sizeof(tempstr),"TeamNumber: %d", GetTeamNumber() );
  375. EntityText(text_offset,tempstr,0);
  376. text_offset++;
  377. color32 teamcolor = g_aTeamColors[ GetTeamNumber() ];
  378. teamcolor.a = 0;
  379. if ( m_hRespawnRoom )
  380. {
  381. NDebugOverlay::Line( GetAbsOrigin(), m_hRespawnRoom->WorldSpaceCenter(), teamcolor.r, teamcolor.g, teamcolor.b, false, 0.1 );
  382. }
  383. }
  384. return text_offset;
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose:
  388. //-----------------------------------------------------------------------------
  389. void CFuncRespawnRoomVisualizer::InputSetSolid( inputdata_t &inputdata )
  390. {
  391. m_bSolid = inputdata.value.Bool();
  392. SetActive( m_bSolid );
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose:
  396. //-----------------------------------------------------------------------------
  397. int CFuncRespawnRoomVisualizer::UpdateTransmitState()
  398. {
  399. //return SetTransmitState( FL_EDICT_FULLCHECK );
  400. return SetTransmitState( FL_EDICT_ALWAYS );
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Purpose: Only transmit this entity to clients that aren't in our team
  404. //-----------------------------------------------------------------------------
  405. int CFuncRespawnRoomVisualizer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  406. {
  407. if ( !m_hRespawnRoom || m_hRespawnRoom->GetActive() )
  408. {
  409. // Respawn rooms are open in win state
  410. if ( TFGameRules()->State_Get() != GR_STATE_TEAM_WIN && GetTeamNumber() != TEAM_UNASSIGNED )
  411. {
  412. // Only transmit to enemy players
  413. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  414. if ( pRecipientEntity->GetTeamNumber() > LAST_SHARED_TEAM && !InSameTeam(pRecipientEntity) )
  415. return FL_EDICT_ALWAYS;
  416. }
  417. }
  418. return FL_EDICT_DONTSEND;
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. //-----------------------------------------------------------------------------
  423. bool CFuncRespawnRoomVisualizer::ShouldCollide( int collisionGroup, int contentsMask ) const
  424. {
  425. // Respawn rooms are open in win state
  426. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
  427. return false;
  428. if ( GetTeamNumber() == TEAM_UNASSIGNED )
  429. return false;
  430. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
  431. {
  432. switch( GetTeamNumber() )
  433. {
  434. case TF_TEAM_BLUE:
  435. if ( !(contentsMask & CONTENTS_BLUETEAM) )
  436. return false;
  437. break;
  438. case TF_TEAM_RED:
  439. if ( !(contentsMask & CONTENTS_REDTEAM) )
  440. return false;
  441. break;
  442. }
  443. return true;
  444. }
  445. return false;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Purpose:
  449. //-----------------------------------------------------------------------------
  450. void CFuncRespawnRoomVisualizer::SetActive( bool bActive )
  451. {
  452. if ( bActive )
  453. {
  454. // We're a trigger, but we want to be solid. Our ShouldCollide() will make
  455. // us non-solid to members of the team that spawns here.
  456. RemoveSolidFlags( FSOLID_TRIGGER );
  457. RemoveSolidFlags( FSOLID_NOT_SOLID );
  458. }
  459. else
  460. {
  461. AddSolidFlags( FSOLID_NOT_SOLID );
  462. AddSolidFlags( FSOLID_TRIGGER );
  463. }
  464. }