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.

465 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client's CObjectTeleporter
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_baseobject.h"
  9. #include "c_tf_player.h"
  10. #include "vgui/ILocalize.h"
  11. #include "c_obj_teleporter.h"
  12. #include "soundenvelope.h"
  13. #include "vgui/ILocalize.h"
  14. #include "tf_gamerules.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. using namespace vgui;
  18. #define TELEPORTER_MINS Vector( -24, -24, 0)
  19. #define TELEPORTER_MAXS Vector( 24, 24, 12)
  20. //-----------------------------------------------------------------------------
  21. // Purpose: Teleporter object
  22. //-----------------------------------------------------------------------------
  23. IMPLEMENT_CLIENTCLASS_DT(C_ObjectTeleporter, DT_ObjectTeleporter, CObjectTeleporter)
  24. RecvPropInt( RECVINFO(m_iState) ),
  25. RecvPropTime( RECVINFO(m_flRechargeTime) ),
  26. RecvPropTime( RECVINFO(m_flCurrentRechargeDuration) ),
  27. RecvPropInt( RECVINFO(m_iTimesUsed) ),
  28. RecvPropFloat( RECVINFO(m_flYawToExit) ),
  29. RecvPropBool( RECVINFO(m_bMatchBuilding) ),
  30. END_RECV_TABLE()
  31. //-----------------------------------------------------------------------------
  32. // Purpose:
  33. //-----------------------------------------------------------------------------
  34. C_ObjectTeleporter::C_ObjectTeleporter()
  35. {
  36. m_hChargedEffect = NULL;
  37. m_hDirectionEffect = NULL;
  38. m_hChargedLeftArmEffect = NULL;
  39. m_hChargedRightArmEffect = NULL;
  40. m_iDirectionArrowPoseParam = 0;
  41. m_pSpinSound = NULL;
  42. m_bMatchBuilding = false;
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose:
  46. //-----------------------------------------------------------------------------
  47. void C_ObjectTeleporter::UpdateOnRemove( void )
  48. {
  49. StopActiveEffects();
  50. StopChargedEffects();
  51. if ( m_pSpinSound )
  52. {
  53. CSoundEnvelopeController::GetController().SoundDestroy( m_pSpinSound );
  54. }
  55. BaseClass::UpdateOnRemove();
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. void C_ObjectTeleporter::OnPreDataChanged( DataUpdateType_t updateType )
  61. {
  62. BaseClass::OnPreDataChanged( updateType );
  63. m_iOldState = m_iState;
  64. m_bOldMatchBuilding = m_bMatchBuilding;
  65. }
  66. void C_ObjectTeleporter::StartBuildingEffects()
  67. {
  68. StopBuildingEffects();
  69. char szEffect[128];
  70. // arm glow effects
  71. Q_snprintf( szEffect, sizeof(szEffect), "teleporter_arms_circle_%s_blink", ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" );
  72. Assert( m_hBuildingLeftArmEffect.m_pObject == NULL );
  73. m_hBuildingLeftArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 1 );
  74. Assert( m_hBuildingRightArmEffect.m_pObject == NULL );
  75. m_hBuildingRightArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 3 );
  76. }
  77. void C_ObjectTeleporter::StartChargedEffects()
  78. {
  79. StopChargedEffects();
  80. char szEffect[128];
  81. Q_snprintf( szEffect, sizeof(szEffect), "teleporter_%s_charged_level%d",
  82. ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue", GetUpgradeLevel() );
  83. Assert( m_hChargedEffect.m_pObject == NULL );
  84. m_hChargedEffect = ParticleProp()->Create( szEffect, PATTACH_ABSORIGIN );
  85. }
  86. void C_ObjectTeleporter::StartActiveEffects()
  87. {
  88. StopActiveEffects();
  89. char szEffect[128];
  90. Q_snprintf( szEffect, sizeof(szEffect), "teleporter_%s_%s_level%d",
  91. ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue",
  92. GetObjectMode() == MODE_TELEPORTER_ENTRANCE ? "entrance" : "exit",
  93. GetUpgradeLevel() );
  94. Assert( m_hDirectionEffect.m_pObject == NULL );
  95. m_hDirectionEffect = ParticleProp()->Create( szEffect, PATTACH_ABSORIGIN );
  96. // arm glow effects
  97. Q_snprintf( szEffect, sizeof(szEffect), "teleporter_arms_circle_%s",
  98. ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" );
  99. Assert( m_hChargedLeftArmEffect.m_pObject == NULL );
  100. m_hChargedLeftArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 1 );
  101. Assert( m_hChargedRightArmEffect.m_pObject == NULL );
  102. m_hChargedRightArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 3 );
  103. // always reinitializes sound since this only gets called when the sound needs to start or change
  104. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  105. if ( m_pSpinSound )
  106. {
  107. controller.SoundDestroy( m_pSpinSound );
  108. m_pSpinSound = NULL;
  109. }
  110. char szSound[128];
  111. Q_snprintf( szSound, sizeof(szSound), "Building_Teleporter.SpinLevel%d", GetUpgradeLevel());
  112. CLocalPlayerFilter filter;
  113. m_pSpinSound = controller.SoundCreate( filter, entindex(), szSound );
  114. controller.Play( m_pSpinSound, 1.0, 100 );
  115. }
  116. void C_ObjectTeleporter::StopBuildingEffects()
  117. {
  118. if ( m_hBuildingLeftArmEffect )
  119. {
  120. ParticleProp()->StopEmission( m_hBuildingLeftArmEffect );
  121. m_hBuildingLeftArmEffect = NULL;
  122. }
  123. if ( m_hBuildingRightArmEffect )
  124. {
  125. ParticleProp()->StopEmission( m_hBuildingRightArmEffect );
  126. m_hBuildingRightArmEffect = NULL;
  127. }
  128. }
  129. void C_ObjectTeleporter::StopChargedEffects()
  130. {
  131. if ( m_hChargedEffect )
  132. {
  133. ParticleProp()->StopEmission( m_hChargedEffect );
  134. m_hChargedEffect = NULL;
  135. }
  136. }
  137. void C_ObjectTeleporter::StopActiveEffects()
  138. {
  139. if ( m_hDirectionEffect )
  140. {
  141. ParticleProp()->StopEmission( m_hDirectionEffect );
  142. m_hDirectionEffect = NULL;
  143. }
  144. if ( m_hChargedLeftArmEffect )
  145. {
  146. ParticleProp()->StopEmission( m_hChargedLeftArmEffect );
  147. m_hChargedLeftArmEffect = NULL;
  148. }
  149. if ( m_hChargedRightArmEffect )
  150. {
  151. ParticleProp()->StopEmission( m_hChargedRightArmEffect );
  152. m_hChargedRightArmEffect = NULL;
  153. }
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void C_ObjectTeleporter::SetInvisibilityLevel( float flValue )
  159. {
  160. if ( IsEnteringOrExitingFullyInvisible( flValue ) )
  161. {
  162. UpdateTeleporterEffects();
  163. }
  164. BaseClass::SetInvisibilityLevel( flValue );
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. void C_ObjectTeleporter::UpdateTeleporterEffects( void )
  170. {
  171. #ifdef STAGING_ONLY
  172. C_TFPlayer *pTFOwner = GetOwner();
  173. if ( ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() ) || GetInvisibilityLevel() == 1.f )
  174. {
  175. StopActiveEffects();
  176. StopBuildingEffects();
  177. StopChargedEffects();
  178. return;
  179. }
  180. #endif // STAGING_ONLY
  181. if ( m_bMatchBuilding )
  182. {
  183. StartBuildingEffects();
  184. }
  185. else
  186. {
  187. StopBuildingEffects();
  188. }
  189. // In MVM, teleporter from invaders act as spawn point. Always play active effect
  190. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  191. {
  192. if ( m_iState != TELEPORTER_STATE_BUILDING && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  193. {
  194. StartChargedEffects();
  195. StartActiveEffects();
  196. return;
  197. }
  198. }
  199. if ( m_iState == TELEPORTER_STATE_READY )
  200. {
  201. StartChargedEffects();
  202. }
  203. else
  204. {
  205. StopChargedEffects();
  206. }
  207. if ( m_iState > TELEPORTER_STATE_IDLE && m_iOldState <= TELEPORTER_STATE_IDLE )
  208. {
  209. StartActiveEffects();
  210. }
  211. else if ( ( m_iState <= TELEPORTER_STATE_IDLE || m_iState == TELEPORTER_STATE_UPGRADING ) && m_iOldState > TELEPORTER_STATE_IDLE )
  212. {
  213. StopActiveEffects();
  214. }
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose:
  218. //-----------------------------------------------------------------------------
  219. void C_ObjectTeleporter::OnDataChanged( DataUpdateType_t updateType )
  220. {
  221. BaseClass::OnDataChanged( updateType );
  222. if ( m_bOldMatchBuilding != m_bMatchBuilding )
  223. {
  224. m_bOldMatchBuilding = m_bMatchBuilding;
  225. UpdateTeleporterEffects();
  226. }
  227. if ( m_iOldState != m_iState )
  228. {
  229. UpdateTeleporterEffects();
  230. m_iOldState = m_iState;
  231. }
  232. // update the pitch based on our playback rate
  233. if ( m_pSpinSound )
  234. {
  235. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  236. controller.SoundChangePitch( m_pSpinSound, GetPlaybackRate() * 100.0f, 0.1 );
  237. }
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose:
  241. //-----------------------------------------------------------------------------
  242. float C_ObjectTeleporter::GetChargeTime( void )
  243. {
  244. float flTime = m_flRechargeTime - gpGlobals->curtime;
  245. if ( flTime < 0 )
  246. return 0;
  247. return flTime;
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. //-----------------------------------------------------------------------------
  252. int C_ObjectTeleporter::GetTimesUsed( void )
  253. {
  254. return m_iTimesUsed;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. CStudioHdr *C_ObjectTeleporter::OnNewModel( void )
  260. {
  261. CStudioHdr *hdr = BaseClass::OnNewModel();
  262. m_iDirectionArrowPoseParam = LookupPoseParameter( "direction" );
  263. SetNextClientThink( CLIENT_THINK_ALWAYS );
  264. return hdr;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Update the direction arrow
  268. //-----------------------------------------------------------------------------
  269. void C_ObjectTeleporter::ClientThink( void )
  270. {
  271. if ( m_iState >= TELEPORTER_STATE_READY )
  272. {
  273. SetPoseParameter( m_iDirectionArrowPoseParam, m_flYawToExit);
  274. }
  275. #ifdef STAGING_ONLY
  276. C_TFPlayer *pTFOwner = GetOwner();
  277. if ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() )
  278. {
  279. UpdateTeleporterEffects();
  280. }
  281. #endif // STAGING_ONLY
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose:
  285. //-----------------------------------------------------------------------------
  286. void C_ObjectTeleporter::GetTargetIDDataString( OUT_Z_BYTECAP(iMaxLenInBytes) wchar_t *sDataString, int iMaxLenInBytes )
  287. {
  288. Assert( iMaxLenInBytes >= sizeof(sDataString[0]) );
  289. wchar_t wzBaseString[MAX_ID_STRING];
  290. BaseClass::GetTargetIDDataString( wzBaseString, sizeof( wzBaseString ) );
  291. sDataString[0] = '\0';
  292. if ( m_iState == TELEPORTER_STATE_RECHARGING && gpGlobals->curtime < m_flRechargeTime )
  293. {
  294. float flPercent = clamp( ( m_flRechargeTime - gpGlobals->curtime ) / m_flCurrentRechargeDuration, 0.0f, 1.0f );
  295. wchar_t wszRecharging[ 32 ];
  296. _snwprintf( wszRecharging, ARRAYSIZE(wszRecharging) - 1, L"%.0f", 100 - (flPercent * 100) );
  297. wszRecharging[ ARRAYSIZE(wszRecharging)-1 ] = '\0';
  298. const char *printFormatString = "#TF_playerid_object_recharging";
  299. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find(printFormatString),
  300. 1,
  301. wszRecharging );
  302. }
  303. else if ( m_iState == TELEPORTER_STATE_IDLE )
  304. {
  305. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_teleporter_nomatch" ), 0 );
  306. }
  307. // Concatenate the base level string
  308. V_wcsncat( sDataString, L" ", iMaxLenInBytes / sizeof( wchar_t ) );
  309. V_wcsncat( sDataString, wzBaseString, iMaxLenInBytes / sizeof( wchar_t ) );
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose: Damage level has changed, update our effects
  313. //-----------------------------------------------------------------------------
  314. void C_ObjectTeleporter::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
  315. {
  316. if ( m_hDamageEffects )
  317. {
  318. m_hDamageEffects->StopEmission( false, false );
  319. m_hDamageEffects = NULL;
  320. }
  321. const char *pszEffect = "";
  322. switch( damageLevel )
  323. {
  324. case BUILDING_DAMAGE_LEVEL_LIGHT:
  325. pszEffect = "tpdamage_1";
  326. break;
  327. case BUILDING_DAMAGE_LEVEL_MEDIUM:
  328. pszEffect = "tpdamage_2";
  329. break;
  330. case BUILDING_DAMAGE_LEVEL_HEAVY:
  331. pszEffect = "tpdamage_3";
  332. break;
  333. case BUILDING_DAMAGE_LEVEL_CRITICAL:
  334. pszEffect = "tpdamage_4";
  335. break;
  336. default:
  337. break;
  338. }
  339. if ( Q_strlen(pszEffect) > 0 )
  340. {
  341. m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN );
  342. }
  343. }
  344. //-----------------------------------------------------------------------------
  345. //
  346. //-----------------------------------------------------------------------------
  347. bool C_ObjectTeleporter::IsPlacementPosValid( void )
  348. {
  349. bool bResult = BaseClass::IsPlacementPosValid();
  350. if ( !bResult )
  351. {
  352. return false;
  353. }
  354. // m_vecBuildOrigin is the proposed build origin
  355. // start above the teleporter position
  356. Vector vecTestPos = m_vecBuildOrigin;
  357. vecTestPos.z += TELEPORTER_MAXS.z;
  358. // make sure we can fit a player on top in this pos
  359. trace_t tr;
  360. UTIL_TraceHull( vecTestPos, vecTestPos, VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID | CONTENTS_PLAYERCLIP, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
  361. return ( tr.fraction >= 1.0 );
  362. }
  363. //-----------------------------------------------------------------------------
  364. //
  365. //-----------------------------------------------------------------------------
  366. void C_ObjectTeleporter::UpgradeLevelChanged( void )
  367. {
  368. StopActiveEffects();
  369. StopChargedEffects();
  370. if ( m_iState >= TELEPORTER_STATE_READY && m_iState != TELEPORTER_STATE_UPGRADING )
  371. {
  372. StartActiveEffects();
  373. if ( m_iState != TELEPORTER_STATE_RECHARGING )
  374. {
  375. StartChargedEffects();
  376. }
  377. }
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose:
  381. //-----------------------------------------------------------------------------
  382. void C_ObjectTeleporter::OnGoInactive( void )
  383. {
  384. StopActiveEffects();
  385. StopBuildingEffects();
  386. StopChargedEffects();
  387. BaseClass::OnGoInactive();
  388. }