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.

344 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Rising liquid that acts as a one-way portal
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "func_liquidportal.h"
  9. #include "portal_player.h"
  10. #include "isaverestore.h"
  11. #include "saverestore_utlvector.h"
  12. LINK_ENTITY_TO_CLASS( func_liquidportal, CFunc_LiquidPortal );
  13. BEGIN_DATADESC( CFunc_LiquidPortal )
  14. DEFINE_FIELD( m_hLinkedPortal, FIELD_EHANDLE ),
  15. DEFINE_FIELD( m_bFillInProgress, FIELD_BOOLEAN ),
  16. DEFINE_FIELD( m_fFillStartTime, FIELD_TIME ),
  17. DEFINE_FIELD( m_fFillEndTime, FIELD_TIME ),
  18. DEFINE_FIELD( m_matrixThisToLinked, FIELD_VMATRIX ),
  19. DEFINE_UTLVECTOR( m_hTeleportList, FIELD_EHANDLE ),
  20. DEFINE_UTLVECTOR( m_hLeftToTeleportThisFill, FIELD_EHANDLE ),
  21. DEFINE_KEYFIELD( m_strInitialLinkedPortal, FIELD_STRING, "InitialLinkedPortal" ),
  22. DEFINE_KEYFIELD( m_fFillTime, FIELD_FLOAT, "FillTime" ),
  23. // Inputs
  24. DEFINE_INPUTFUNC( FIELD_STRING, "SetLinkedLiquidPortal", InputSetLinkedLiquidPortal ),
  25. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFillTime", InputSetFillTime ),
  26. DEFINE_INPUTFUNC( FIELD_VOID, "StartFilling", InputStartFilling ),
  27. DEFINE_INPUTFUNC( FIELD_VOID, "AddActivatorToTeleportList", InputAddActivatorToTeleportList ),
  28. DEFINE_INPUTFUNC( FIELD_VOID, "RemoveActivatorFromTeleportList", InputRemoveActivatorFromTeleportList ),
  29. DEFINE_FUNCTION( CBaseEntity::Think ),
  30. END_DATADESC()
  31. IMPLEMENT_SERVERCLASS_ST( CFunc_LiquidPortal, DT_Func_LiquidPortal )
  32. SendPropEHandle( SENDINFO(m_hLinkedPortal) ),
  33. SendPropFloat( SENDINFO(m_fFillStartTime) ),
  34. SendPropFloat( SENDINFO(m_fFillEndTime) ),
  35. END_SEND_TABLE()
  36. CFunc_LiquidPortal::CFunc_LiquidPortal( void )
  37. : m_bFillInProgress( false )
  38. {
  39. m_matrixThisToLinked.Identity(); //Zero space is a bad place. No heroes to face, but we need 1's in this case.
  40. }
  41. void CFunc_LiquidPortal::Spawn( void )
  42. {
  43. BaseClass::Spawn();
  44. SetSolid( SOLID_VPHYSICS );
  45. SetSolidFlags( FSOLID_NOT_SOLID );
  46. SetMoveType( MOVETYPE_NONE );
  47. SetModel( STRING( GetModelName() ) );
  48. CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, STRING(m_strInitialLinkedPortal) );
  49. Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
  50. SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
  51. SetThink( &CFunc_LiquidPortal::Think );
  52. }
  53. void CFunc_LiquidPortal::Activate( void )
  54. {
  55. BaseClass::Activate();
  56. SetSolid( SOLID_VPHYSICS );
  57. SetSolidFlags( FSOLID_NOT_SOLID );
  58. SetMoveType( MOVETYPE_NONE );
  59. SetModel( STRING( GetModelName() ) );
  60. ComputeLinkMatrix(); //collision origin may have changed during activation
  61. SetThink( &CFunc_LiquidPortal::Think );
  62. for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
  63. {
  64. CBaseEntity *pEnt = m_hLeftToTeleportThisFill[i].Get();
  65. if( pEnt && pEnt->IsPlayer() )
  66. {
  67. ((CPortal_Player *)pEnt)->m_hSurroundingLiquidPortal = this;
  68. }
  69. }
  70. }
  71. int CFunc_LiquidPortal::Save( ISave &save )
  72. {
  73. if( !BaseClass::Save( save ) )
  74. return 0;
  75. save.StartBlock( "LiquidPortal" );
  76. short iTeleportListCount = m_hTeleportList.Count();
  77. save.WriteShort( &iTeleportListCount );
  78. if( iTeleportListCount != 0 )
  79. save.WriteEHandle( m_hTeleportList.Base(), iTeleportListCount );
  80. short iLeftToTeleportThisFillCount = m_hLeftToTeleportThisFill.Count();
  81. save.WriteShort( &iLeftToTeleportThisFillCount );
  82. if( iLeftToTeleportThisFillCount != 0 )
  83. save.WriteEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
  84. save.EndBlock();
  85. return 1;
  86. }
  87. int CFunc_LiquidPortal::Restore( IRestore &restore )
  88. {
  89. m_hTeleportList.RemoveAll();
  90. m_hLeftToTeleportThisFill.RemoveAll();
  91. if( !BaseClass::Restore( restore ) )
  92. return 0;
  93. char szBlockName[SIZE_BLOCK_NAME_BUF];
  94. restore.StartBlock( szBlockName );
  95. if( !FStrEq( szBlockName, "LiquidPortal" ) ) //loading a save without liquid portal save data
  96. return 1;
  97. short iTeleportListCount;
  98. restore.ReadShort( &iTeleportListCount );
  99. if( iTeleportListCount != 0 )
  100. {
  101. m_hTeleportList.SetCount( iTeleportListCount );
  102. restore.ReadEHandle( m_hTeleportList.Base(), iTeleportListCount );
  103. }
  104. short iLeftToTeleportThisFillCount;
  105. restore.ReadShort( &iLeftToTeleportThisFillCount );
  106. if( iLeftToTeleportThisFillCount != 0 )
  107. {
  108. m_hLeftToTeleportThisFill.SetCount( iLeftToTeleportThisFillCount );
  109. restore.ReadEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
  110. }
  111. restore.EndBlock();
  112. return 1;
  113. }
  114. void CFunc_LiquidPortal::InputSetLinkedLiquidPortal( inputdata_t &inputdata )
  115. {
  116. CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, inputdata.value.String() );
  117. Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
  118. SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
  119. }
  120. void CFunc_LiquidPortal::InputSetFillTime( inputdata_t &inputdata )
  121. {
  122. m_fFillTime = inputdata.value.Float();
  123. }
  124. void CFunc_LiquidPortal::InputStartFilling( inputdata_t &inputdata )
  125. {
  126. AssertMsg( m_fFillEndTime <= gpGlobals->curtime, "Fill already in progress." );
  127. m_fFillStartTime = gpGlobals->curtime;
  128. m_fFillEndTime = gpGlobals->curtime + m_fFillTime;
  129. m_bFillInProgress = true;
  130. //reset the teleport list for this fill
  131. m_hLeftToTeleportThisFill.RemoveAll();
  132. m_hLeftToTeleportThisFill.AddVectorToTail( m_hTeleportList );
  133. SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
  134. }
  135. void CFunc_LiquidPortal::InputAddActivatorToTeleportList( inputdata_t &inputdata )
  136. {
  137. if( inputdata.pActivator == NULL )
  138. return;
  139. for( int i = m_hTeleportList.Count(); --i >= 0; )
  140. {
  141. if( m_hTeleportList[i].Get() == inputdata.pActivator )
  142. return; //only have 1 reference of each entity
  143. }
  144. m_hTeleportList.AddToTail( inputdata.pActivator );
  145. if( m_bFillInProgress )
  146. m_hLeftToTeleportThisFill.AddToTail( inputdata.pActivator );
  147. if( inputdata.pActivator->IsPlayer() )
  148. ((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = this;
  149. }
  150. void CFunc_LiquidPortal::InputRemoveActivatorFromTeleportList( inputdata_t &inputdata )
  151. {
  152. if( inputdata.pActivator == NULL )
  153. return;
  154. for( int i = m_hTeleportList.Count(); --i >= 0; )
  155. {
  156. if( m_hTeleportList[i].Get() == inputdata.pActivator )
  157. {
  158. m_hTeleportList.FastRemove( i );
  159. if( inputdata.pActivator->IsPlayer() && (((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal.Get() == this) )
  160. ((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = NULL;
  161. if( m_bFillInProgress )
  162. {
  163. //remove from the list for this fill as well
  164. for( int j = m_hLeftToTeleportThisFill.Count(); --j >= 0; )
  165. {
  166. if( m_hLeftToTeleportThisFill[j].Get() == inputdata.pActivator )
  167. {
  168. m_hLeftToTeleportThisFill.FastRemove( j );
  169. break;
  170. }
  171. }
  172. }
  173. return;
  174. }
  175. }
  176. }
  177. void CFunc_LiquidPortal::SetLinkedLiquidPortal( CFunc_LiquidPortal *pLinked )
  178. {
  179. CFunc_LiquidPortal *pCurrentLinkedPortal = m_hLinkedPortal.Get();
  180. if( pCurrentLinkedPortal == pLinked )
  181. return;
  182. if( pCurrentLinkedPortal != NULL )
  183. {
  184. m_hLinkedPortal = NULL;
  185. pCurrentLinkedPortal->SetLinkedLiquidPortal( NULL );
  186. }
  187. m_hLinkedPortal = pLinked;
  188. if( pLinked != NULL )
  189. pLinked->SetLinkedLiquidPortal( this );
  190. ComputeLinkMatrix();
  191. }
  192. void CFunc_LiquidPortal::ComputeLinkMatrix( void )
  193. {
  194. CFunc_LiquidPortal *pLinkedPortal = m_hLinkedPortal.Get();
  195. if( pLinkedPortal )
  196. {
  197. VMatrix matLocalToWorld, matLocalToWorldInv, matRemoteToWorld;
  198. matLocalToWorld = EntityToWorldTransform();
  199. matRemoteToWorld = pLinkedPortal->EntityToWorldTransform();
  200. MatrixInverseTR( matLocalToWorld, matLocalToWorldInv );
  201. m_matrixThisToLinked = matRemoteToWorld * matLocalToWorldInv;
  202. MatrixInverseTR( m_matrixThisToLinked, pLinkedPortal->m_matrixThisToLinked );
  203. }
  204. else
  205. {
  206. m_matrixThisToLinked.Identity();
  207. }
  208. }
  209. void CFunc_LiquidPortal::TeleportImmersedEntity( CBaseEntity *pEntity )
  210. {
  211. if( pEntity == NULL )
  212. return;
  213. if( pEntity->IsPlayer() )
  214. {
  215. CPortal_Player *pEntityAsPlayer = (CPortal_Player *)pEntity;
  216. Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
  217. QAngle qNewAngles = TransformAnglesToWorldSpace( pEntityAsPlayer->EyeAngles(), m_matrixThisToLinked.As3x4() );
  218. Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
  219. pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
  220. pEntityAsPlayer->m_hSurroundingLiquidPortal = m_hLinkedPortal;
  221. }
  222. else
  223. {
  224. Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
  225. QAngle qNewAngles = TransformAnglesToWorldSpace( pEntity->GetAbsAngles(), m_matrixThisToLinked.As3x4() );
  226. Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
  227. pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
  228. }
  229. }
  230. void CFunc_LiquidPortal::Think( void )
  231. {
  232. if( m_bFillInProgress )
  233. {
  234. if( gpGlobals->curtime < m_fFillEndTime )
  235. {
  236. float fInterp = ((gpGlobals->curtime - m_fFillStartTime) / (m_fFillEndTime - m_fFillStartTime));
  237. Vector vMins, vMaxs;
  238. GetCollideable()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
  239. vMaxs.z = vMins.z + ((vMaxs.z - vMins.z) * fInterp);
  240. for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
  241. {
  242. CBaseEntity *pEntity = m_hLeftToTeleportThisFill[i].Get();
  243. if( pEntity == NULL )
  244. continue;
  245. Vector vEntMins, vEntMaxs;
  246. pEntity->GetCollideable()->WorldSpaceSurroundingBounds( &vEntMins, &vEntMaxs );
  247. if( vEntMaxs.z <= vMaxs.z )
  248. {
  249. TeleportImmersedEntity( pEntity );
  250. m_hLeftToTeleportThisFill.FastRemove( i );
  251. }
  252. }
  253. SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
  254. }
  255. else
  256. {
  257. //teleport everything that's left in the list
  258. for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
  259. {
  260. TeleportImmersedEntity( m_hLeftToTeleportThisFill[i].Get() );
  261. }
  262. m_hLeftToTeleportThisFill.RemoveAll();
  263. m_bFillInProgress = false;
  264. }
  265. }
  266. }