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.

388 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Dissolve entity to be attached to target entity. Serves two purposes:
  4. //
  5. // 1) An entity that can be placed by a level designer and triggered
  6. // to ignite a target entity.
  7. //
  8. // 2) An entity that can be created at runtime to ignite a target entity.
  9. //
  10. //=============================================================================//
  11. #include "cbase.h"
  12. #include "EntityDissolve.h"
  13. #include "baseanimating.h"
  14. #include "physics_prop_ragdoll.h"
  15. #include "ai_basenpc.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. static const char *s_pElectroThinkContext = "ElectroThinkContext";
  19. //-----------------------------------------------------------------------------
  20. // Lifetime
  21. //-----------------------------------------------------------------------------
  22. #define DISSOLVE_FADE_IN_START_TIME 0.0f
  23. #define DISSOLVE_FADE_IN_END_TIME 1.0f
  24. #define DISSOLVE_FADE_OUT_MODEL_START_TIME 1.9f
  25. #define DISSOLVE_FADE_OUT_MODEL_END_TIME 2.0f
  26. #define DISSOLVE_FADE_OUT_START_TIME 2.0f
  27. #define DISSOLVE_FADE_OUT_END_TIME 2.0f
  28. //-----------------------------------------------------------------------------
  29. // Model
  30. //-----------------------------------------------------------------------------
  31. #define DISSOLVE_SPRITE_NAME "sprites/blueglow1.vmt"
  32. //-----------------------------------------------------------------------------
  33. // Save/load
  34. //-----------------------------------------------------------------------------
  35. BEGIN_DATADESC( CEntityDissolve )
  36. DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
  37. DEFINE_FIELD( m_flFadeInStart, FIELD_FLOAT ),
  38. DEFINE_FIELD( m_flFadeInLength, FIELD_FLOAT ),
  39. DEFINE_FIELD( m_flFadeOutModelStart, FIELD_FLOAT ),
  40. DEFINE_FIELD( m_flFadeOutModelLength, FIELD_FLOAT ),
  41. DEFINE_FIELD( m_flFadeOutStart, FIELD_FLOAT ),
  42. DEFINE_FIELD( m_flFadeOutLength, FIELD_FLOAT ),
  43. DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
  44. DEFINE_FIELD( m_vDissolverOrigin, FIELD_VECTOR ),
  45. DEFINE_KEYFIELD( m_nMagnitude, FIELD_INTEGER, "magnitude" ),
  46. DEFINE_FUNCTION( DissolveThink ),
  47. DEFINE_FUNCTION( ElectrocuteThink ),
  48. DEFINE_INPUTFUNC( FIELD_STRING, "Dissolve", InputDissolve ),
  49. END_DATADESC()
  50. //-----------------------------------------------------------------------------
  51. // Networking
  52. //-----------------------------------------------------------------------------
  53. IMPLEMENT_SERVERCLASS_ST( CEntityDissolve, DT_EntityDissolve )
  54. SendPropTime( SENDINFO( m_flStartTime ) ),
  55. SendPropFloat( SENDINFO( m_flFadeInStart ), 0, SPROP_NOSCALE ),
  56. SendPropFloat( SENDINFO( m_flFadeInLength ), 0, SPROP_NOSCALE ),
  57. SendPropFloat( SENDINFO( m_flFadeOutModelStart ), 0, SPROP_NOSCALE ),
  58. SendPropFloat( SENDINFO( m_flFadeOutModelLength ), 0, SPROP_NOSCALE ),
  59. SendPropFloat( SENDINFO( m_flFadeOutStart ), 0, SPROP_NOSCALE ),
  60. SendPropFloat( SENDINFO( m_flFadeOutLength ), 0, SPROP_NOSCALE ),
  61. SendPropInt( SENDINFO( m_nDissolveType ), ENTITY_DISSOLVE_BITS, SPROP_UNSIGNED ),
  62. SendPropVector (SENDINFO(m_vDissolverOrigin), 0, SPROP_NOSCALE ),
  63. SendPropInt( SENDINFO( m_nMagnitude ), 8, SPROP_UNSIGNED ),
  64. END_SEND_TABLE()
  65. LINK_ENTITY_TO_CLASS( env_entity_dissolver, CEntityDissolve );
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. CEntityDissolve::CEntityDissolve( void )
  70. {
  71. m_flStartTime = 0.0f;
  72. m_nMagnitude = 250;
  73. }
  74. CEntityDissolve::~CEntityDissolve( void )
  75. {
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Precache
  79. //-----------------------------------------------------------------------------
  80. void CEntityDissolve::Precache()
  81. {
  82. if ( NULL_STRING == GetModelName() )
  83. {
  84. PrecacheModel( DISSOLVE_SPRITE_NAME );
  85. }
  86. else
  87. {
  88. PrecacheModel( STRING( GetModelName() ) );
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Spawn
  93. //-----------------------------------------------------------------------------
  94. void CEntityDissolve::Spawn()
  95. {
  96. BaseClass::Spawn();
  97. Precache();
  98. UTIL_SetModel( this, STRING( GetModelName() ) );
  99. if ( (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
  100. {
  101. if ( dynamic_cast< CRagdollProp* >( GetMoveParent() ) )
  102. {
  103. SetContextThink( &CEntityDissolve::ElectrocuteThink, gpGlobals->curtime + 0.01f, s_pElectroThinkContext );
  104. }
  105. }
  106. // Setup our times
  107. m_flFadeInStart = DISSOLVE_FADE_IN_START_TIME;
  108. m_flFadeInLength = DISSOLVE_FADE_IN_END_TIME - DISSOLVE_FADE_IN_START_TIME;
  109. m_flFadeOutModelStart = DISSOLVE_FADE_OUT_MODEL_START_TIME;
  110. m_flFadeOutModelLength = DISSOLVE_FADE_OUT_MODEL_END_TIME - DISSOLVE_FADE_OUT_MODEL_START_TIME;
  111. m_flFadeOutStart = DISSOLVE_FADE_OUT_START_TIME;
  112. m_flFadeOutLength = DISSOLVE_FADE_OUT_END_TIME - DISSOLVE_FADE_OUT_START_TIME;
  113. if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
  114. {
  115. m_flFadeInStart = 0.0f;
  116. m_flFadeOutStart = CORE_DISSOLVE_FADE_START;
  117. m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START;
  118. m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH;
  119. m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH;
  120. }
  121. m_nRenderMode = kRenderTransColor;
  122. SetRenderColor( 255, 255, 255, 255 );
  123. m_nRenderFX = kRenderFxNone;
  124. SetThink( &CEntityDissolve::DissolveThink );
  125. if ( gpGlobals->curtime > m_flStartTime )
  126. {
  127. // Necessary for server-side ragdolls
  128. DissolveThink();
  129. }
  130. else
  131. {
  132. SetNextThink( gpGlobals->curtime + 0.01f );
  133. }
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. // Input : inputdata -
  138. //-----------------------------------------------------------------------------
  139. void CEntityDissolve::InputDissolve( inputdata_t &inputdata )
  140. {
  141. string_t strTarget = inputdata.value.StringID();
  142. if (strTarget == NULL_STRING)
  143. {
  144. strTarget = m_target;
  145. }
  146. CBaseEntity *pTarget = NULL;
  147. while ((pTarget = gEntList.FindEntityGeneric(pTarget, STRING(strTarget), this, inputdata.pActivator)) != NULL)
  148. {
  149. CBaseAnimating *pBaseAnim = pTarget->GetBaseAnimating();
  150. if (pBaseAnim)
  151. {
  152. pBaseAnim->Dissolve( NULL, gpGlobals->curtime, false, m_nDissolveType, GetAbsOrigin(), m_nMagnitude );
  153. }
  154. }
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Creates a flame and attaches it to a target entity.
  158. // Input : pTarget -
  159. //-----------------------------------------------------------------------------
  160. CEntityDissolve *CEntityDissolve::Create( CBaseEntity *pTarget, const char *pMaterialName,
  161. float flStartTime, int nDissolveType, bool *pRagdollCreated )
  162. {
  163. if ( pRagdollCreated )
  164. {
  165. *pRagdollCreated = false;
  166. }
  167. if ( !pMaterialName )
  168. {
  169. pMaterialName = DISSOLVE_SPRITE_NAME;
  170. }
  171. if ( pTarget->IsPlayer() )
  172. {
  173. // Simply immediately kill the player.
  174. CBasePlayer *pPlayer = assert_cast< CBasePlayer* >( pTarget );
  175. pPlayer->SetArmorValue( 0 );
  176. CTakeDamageInfo info( pPlayer, pPlayer, pPlayer->GetHealth(), DMG_GENERIC | DMG_REMOVENORAGDOLL | DMG_PREVENT_PHYSICS_FORCE );
  177. pPlayer->TakeDamage( info );
  178. return NULL;
  179. }
  180. CEntityDissolve *pDissolve = (CEntityDissolve *) CreateEntityByName( "env_entity_dissolver" );
  181. if ( pDissolve == NULL )
  182. return NULL;
  183. pDissolve->m_nDissolveType = nDissolveType;
  184. if ( (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
  185. {
  186. if ( pTarget->IsNPC() && pTarget->MyNPCPointer()->CanBecomeRagdoll() )
  187. {
  188. CTakeDamageInfo info;
  189. CBaseEntity *pRagdoll = CreateServerRagdoll( pTarget->MyNPCPointer(), 0, info, COLLISION_GROUP_DEBRIS, true );
  190. pRagdoll->SetCollisionBounds( pTarget->CollisionProp()->OBBMins(), pTarget->CollisionProp()->OBBMaxs() );
  191. // Necessary to cause it to do the appropriate death cleanup
  192. if ( pTarget->m_lifeState == LIFE_ALIVE )
  193. {
  194. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  195. CTakeDamageInfo ragdollInfo( pPlayer, pPlayer, 10000.0, DMG_SHOCK | DMG_REMOVENORAGDOLL | DMG_PREVENT_PHYSICS_FORCE );
  196. pTarget->TakeDamage( ragdollInfo );
  197. }
  198. if ( pRagdollCreated )
  199. {
  200. *pRagdollCreated = true;
  201. }
  202. UTIL_Remove( pTarget );
  203. pTarget = pRagdoll;
  204. }
  205. }
  206. pDissolve->SetModelName( AllocPooledString(pMaterialName) );
  207. pDissolve->AttachToEntity( pTarget );
  208. pDissolve->SetStartTime( flStartTime );
  209. pDissolve->Spawn();
  210. // Send to the client even though we don't have a model
  211. pDissolve->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  212. // Play any appropriate noises when we start to dissolve
  213. if ( (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
  214. {
  215. pTarget->DispatchResponse( "TLK_ELECTROCUTESCREAM" );
  216. }
  217. else
  218. {
  219. pTarget->DispatchResponse( "TLK_DISSOLVESCREAM" );
  220. }
  221. return pDissolve;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // What type of dissolve?
  225. //-----------------------------------------------------------------------------
  226. CEntityDissolve *CEntityDissolve::Create( CBaseEntity *pTarget, CBaseEntity *pSource )
  227. {
  228. // Look for other boogies on the ragdoll + kill them
  229. for ( CBaseEntity *pChild = pSource->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
  230. {
  231. CEntityDissolve *pDissolve = dynamic_cast<CEntityDissolve*>(pChild);
  232. if ( !pDissolve )
  233. continue;
  234. return Create( pTarget, STRING( pDissolve->GetModelName() ), pDissolve->m_flStartTime, pDissolve->m_nDissolveType );
  235. }
  236. return NULL;
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose: Attaches the flame to an entity and moves with it
  240. // Input : pTarget - target entity to attach to
  241. //-----------------------------------------------------------------------------
  242. void CEntityDissolve::AttachToEntity( CBaseEntity *pTarget )
  243. {
  244. // So our dissolver follows the entity around on the server.
  245. SetParent( pTarget );
  246. SetLocalOrigin( vec3_origin );
  247. SetLocalAngles( vec3_angle );
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. // Input : lifetime -
  252. //-----------------------------------------------------------------------------
  253. void CEntityDissolve::SetStartTime( float flStartTime )
  254. {
  255. m_flStartTime = flStartTime;
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose: Burn targets around us
  259. //-----------------------------------------------------------------------------
  260. void CEntityDissolve::DissolveThink( void )
  261. {
  262. CBaseAnimating *pTarget = ( GetMoveParent() ) ? GetMoveParent()->GetBaseAnimating() : NULL;
  263. if ( GetModelName() == NULL_STRING && pTarget == NULL )
  264. return;
  265. if ( pTarget == NULL )
  266. {
  267. UTIL_Remove( this );
  268. return;
  269. }
  270. // Turn them into debris
  271. pTarget->SetCollisionGroup( COLLISION_GROUP_DISSOLVING );
  272. if ( pTarget && pTarget->GetFlags() & FL_TRANSRAGDOLL )
  273. {
  274. SetRenderColorA( 0 );
  275. }
  276. float dt = gpGlobals->curtime - m_flStartTime;
  277. if ( dt < m_flFadeInStart )
  278. {
  279. SetNextThink( m_flStartTime + m_flFadeInStart );
  280. return;
  281. }
  282. // If we're done fading, then kill our target entity and us
  283. if ( dt >= m_flFadeOutStart + m_flFadeOutLength )
  284. {
  285. // Necessary to cause it to do the appropriate death cleanup
  286. // Yeah, the player may have nothing to do with it, but
  287. // passing NULL to TakeDamage causes bad things to happen
  288. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  289. int iNoPhysicsDamage = g_pGameRules->Damage_GetNoPhysicsForce();
  290. CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL | iNoPhysicsDamage );
  291. pTarget->TakeDamage( info );
  292. if ( pTarget != pPlayer )
  293. {
  294. UTIL_Remove( pTarget );
  295. }
  296. UTIL_Remove( this );
  297. return;
  298. }
  299. SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Purpose: Burn targets around us
  303. //-----------------------------------------------------------------------------
  304. void CEntityDissolve::ElectrocuteThink( void )
  305. {
  306. CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( GetMoveParent() );
  307. if ( !pRagdoll )
  308. return;
  309. ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( );
  310. for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  311. {
  312. Vector vecForce;
  313. vecForce = RandomVector( -2400.0f, 2400.0f );
  314. pRagdollPhys->list[j].pObject->ApplyForceCenter( vecForce );
  315. }
  316. SetContextThink( &CEntityDissolve::ElectrocuteThink, gpGlobals->curtime + random->RandomFloat( 0.1, 0.2f ),
  317. s_pElectroThinkContext );
  318. }