Counter Strike : Global Offensive Source Code
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.

1281 lines
36 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A planar textured surface that breaks into increasingly smaller fragments
  4. // as it takes damage. Undamaged pieces remain attached to the world
  5. // until they are damaged. Used for window panes.
  6. //
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "ndebugoverlay.h"
  11. #include "filters.h"
  12. #include "player.h"
  13. #include "func_breakablesurf.h"
  14. #include "shattersurfacetypes.h"
  15. #include "materialsystem/imaterialsystem.h"
  16. #include "materialsystem/imaterial.h"
  17. #include "materialsystem/imaterialvar.h"
  18. #include "globals.h"
  19. #include "physics_impact_damage.h"
  20. #include "te_effect_dispatch.h"
  21. #include "GameStats.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. // Spawn flags
  25. #define SF_BREAKABLESURF_CRACK_DECALS 0x00000001
  26. #define SF_BREAKABLESURF_DAMAGE_FROM_HELD_OBJECTS 0x00000002
  27. //#############################################################################
  28. // > CWindowPane
  29. //#############################################################################
  30. #define WINDOW_PANEL_SIZE 12
  31. #define WINDOW_SMALL_SHARD_SIZE 4
  32. #define WINDOW_LARGE_SHARD_SIZE 7
  33. #define WINDOW_MAX_SUPPORT 6.75
  34. #define WINDOW_BREAK_SUPPORT 0.20
  35. #define WINDOW_PANE_BROKEN -1
  36. #define WINDOW_PANE_HEALTHY 1
  37. // Also defined in WC
  38. #define QUAD_ERR_NONE 0
  39. #define QUAD_ERR_MULT_FACES 1
  40. #define QUAD_ERR_NOT_QUAD 2
  41. //
  42. // func_breakable - bmodel that breaks into pieces after taking damage
  43. //
  44. LINK_ENTITY_TO_CLASS( window_pane, CWindowPane );
  45. BEGIN_DATADESC( CWindowPane )
  46. // Function Pointers
  47. DEFINE_FUNCTION( Die ),
  48. DEFINE_FUNCTION( PaneTouch ),
  49. END_DATADESC()
  50. //------------------------------------------------------------------------------
  51. // Purpose :
  52. // Input :
  53. // Output :
  54. //------------------------------------------------------------------------------
  55. void CWindowPane::Spawn( void )
  56. {
  57. Precache( );
  58. SetSolid( SOLID_BBOX );
  59. SetMoveType( MOVETYPE_FLYGRAVITY );
  60. m_takedamage = DAMAGE_YES;
  61. SetCollisionGroup( COLLISION_GROUP_BREAKABLE_GLASS );
  62. SetModel( "models/brokenglass_piece.mdl" );//set size and link into world.
  63. }
  64. void CWindowPane::Precache( void )
  65. {
  66. PrecacheModel( "models/brokenglass_piece.mdl" );
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Purpose:
  70. // Input : pOther -
  71. //-----------------------------------------------------------------------------
  72. void CWindowPane::PaneTouch( CBaseEntity *pOther )
  73. {
  74. if (pOther &&
  75. pOther->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS)
  76. {
  77. Die();
  78. }
  79. }
  80. //------------------------------------------------------------------------------
  81. // Purpose :
  82. // Input :
  83. // Output :
  84. //------------------------------------------------------------------------------
  85. void CWindowPane::Die( void )
  86. {
  87. Vector flForce = -1 * GetAbsVelocity();
  88. CPASFilter filter( GetAbsOrigin() );
  89. te->ShatterSurface( filter, 0.0,
  90. &GetAbsOrigin(), &GetAbsAngles(),
  91. &GetAbsVelocity(), &GetAbsOrigin(),
  92. WINDOW_PANEL_SIZE, WINDOW_PANEL_SIZE,WINDOW_SMALL_SHARD_SIZE,SHATTERSURFACE_GLASS,
  93. 255,255,255,255,255,255);
  94. UTIL_Remove(this);
  95. }
  96. ///------------------------------------------------------------------------------
  97. // Purpose :
  98. // Input :
  99. // Output :
  100. //------------------------------------------------------------------------------
  101. CWindowPane* CWindowPane::CreateWindowPane( const Vector &vecOrigin, const QAngle &vecAngles )
  102. {
  103. CWindowPane *pGlass = (CWindowPane*)CreateEntityByName( "window_pane" );
  104. if ( !pGlass )
  105. {
  106. Msg( "NULL Ent in CreateWindowPane!\n" );
  107. return NULL;
  108. }
  109. if ( pGlass->edict() )
  110. {
  111. pGlass->SetLocalOrigin( vecOrigin );
  112. pGlass->SetLocalAngles( vecAngles );
  113. pGlass->Spawn();
  114. pGlass->SetTouch(&CWindowPane::PaneTouch);
  115. pGlass->SetLocalAngularVelocity( RandomAngle(-50,50) );
  116. pGlass->m_nBody = random->RandomInt(0,2);
  117. }
  118. return pGlass;
  119. }
  120. //####################################################################################
  121. // > CBreakableSurface
  122. //####################################################################################
  123. LINK_ENTITY_TO_CLASS( func_breakable_surf, CBreakableSurface );
  124. BEGIN_DATADESC( CBreakableSurface )
  125. DEFINE_KEYFIELD( m_nSurfaceType, FIELD_INTEGER, "surfacetype"),
  126. DEFINE_KEYFIELD( m_nFragility, FIELD_INTEGER, "fragility"),
  127. DEFINE_KEYFIELD( m_vLLVertex, FIELD_VECTOR, "lowerleft" ),
  128. DEFINE_KEYFIELD( m_vULVertex, FIELD_VECTOR, "upperleft" ),
  129. DEFINE_KEYFIELD( m_vLRVertex, FIELD_VECTOR, "lowerright" ),
  130. DEFINE_KEYFIELD( m_vURVertex, FIELD_VECTOR, "upperright" ),
  131. DEFINE_KEYFIELD( m_nQuadError, FIELD_INTEGER, "error" ),
  132. DEFINE_FIELD( m_nNumWide, FIELD_INTEGER),
  133. DEFINE_FIELD( m_nNumHigh, FIELD_INTEGER),
  134. DEFINE_FIELD( m_flPanelWidth, FIELD_FLOAT),
  135. DEFINE_FIELD( m_flPanelHeight, FIELD_FLOAT),
  136. DEFINE_FIELD( m_vNormal, FIELD_VECTOR),
  137. DEFINE_FIELD( m_vCorner, FIELD_POSITION_VECTOR),
  138. DEFINE_FIELD( m_bIsBroken, FIELD_BOOLEAN),
  139. DEFINE_FIELD( m_nNumBrokenPanes, FIELD_INTEGER),
  140. // UNDONE: How to load save this? Need a way to update
  141. // the client about the state of the window upon load...
  142. // We should use client-side save/load to fix this problem.
  143. DEFINE_AUTO_ARRAY2D( m_flSupport, FIELD_FLOAT),
  144. DEFINE_ARRAY( m_RawPanelBitVec, FIELD_BOOLEAN, MAX_NUM_PANELS*MAX_NUM_PANELS ),
  145. // Function Pointers
  146. DEFINE_THINKFUNC( BreakThink ),
  147. DEFINE_ENTITYFUNC( SurfaceTouch ),
  148. DEFINE_INPUTFUNC( FIELD_VECTOR, "Shatter", InputShatter ),
  149. // DEFINE_FIELD( m_ForceUpdateClientData, CBitVec < MAX_PLAYERS > ), // No need to save/restore this, it's just a temporary flag field
  150. END_DATADESC()
  151. IMPLEMENT_SERVERCLASS_ST(CBreakableSurface, DT_BreakableSurface)
  152. SendPropInt(SENDINFO(m_nNumWide), 8, SPROP_UNSIGNED),
  153. SendPropInt(SENDINFO(m_nNumHigh), 8, SPROP_UNSIGNED),
  154. SendPropFloat(SENDINFO(m_flPanelWidth), 0, SPROP_NOSCALE),
  155. SendPropFloat(SENDINFO(m_flPanelHeight), 0, SPROP_NOSCALE),
  156. SendPropVector(SENDINFO(m_vNormal), -1, SPROP_COORD),
  157. SendPropVector(SENDINFO(m_vCorner), -1, SPROP_COORD),
  158. SendPropInt(SENDINFO(m_bIsBroken), 1, SPROP_UNSIGNED),
  159. SendPropInt(SENDINFO(m_nSurfaceType), 2, SPROP_UNSIGNED),
  160. SendPropArray3(SENDINFO_ARRAY3(m_RawPanelBitVec), SendPropInt( SENDINFO_ARRAY( m_RawPanelBitVec ), 1, SPROP_UNSIGNED ) ),
  161. END_SEND_TABLE()
  162. //-----------------------------------------------------------------------------
  163. // Purpose:
  164. //-----------------------------------------------------------------------------
  165. void CBreakableSurface::Precache(void)
  166. {
  167. UTIL_PrecacheOther( "window_pane" );
  168. // Load the edge types and styles for the specific surface type
  169. if (m_nSurfaceType == SHATTERSURFACE_TILE)
  170. {
  171. PrecacheMaterial( "models/brokentile/tilebroken_03a" );
  172. PrecacheMaterial( "models/brokentile/tilebroken_03b" );
  173. PrecacheMaterial( "models/brokentile/tilebroken_03c" );
  174. PrecacheMaterial( "models/brokentile/tilebroken_03d" );
  175. PrecacheMaterial( "models/brokentile/tilebroken_02a" );
  176. PrecacheMaterial( "models/brokentile/tilebroken_02b" );
  177. PrecacheMaterial( "models/brokentile/tilebroken_02c" );
  178. PrecacheMaterial( "models/brokentile/tilebroken_02d" );
  179. PrecacheMaterial( "models/brokentile/tilebroken_01a" );
  180. PrecacheMaterial( "models/brokentile/tilebroken_01b" );
  181. PrecacheMaterial( "models/brokentile/tilebroken_01c" );
  182. PrecacheMaterial( "models/brokentile/tilebroken_01d" );
  183. }
  184. else
  185. {
  186. PrecacheMaterial( "models/brokenglass/glassbroken_solid" );
  187. PrecacheMaterial( "models/brokenglass/glassbroken_01a" );
  188. PrecacheMaterial( "models/brokenglass/glassbroken_01b" );
  189. PrecacheMaterial( "models/brokenglass/glassbroken_01c" );
  190. PrecacheMaterial( "models/brokenglass/glassbroken_01d" );
  191. PrecacheMaterial( "models/brokenglass/glassbroken_02a" );
  192. PrecacheMaterial( "models/brokenglass/glassbroken_02b" );
  193. PrecacheMaterial( "models/brokenglass/glassbroken_02c" );
  194. PrecacheMaterial( "models/brokenglass/glassbroken_02d" );
  195. PrecacheMaterial( "models/brokenglass/glassbroken_03a" );
  196. PrecacheMaterial( "models/brokenglass/glassbroken_03b" );
  197. PrecacheMaterial( "models/brokenglass/glassbroken_03c" );
  198. PrecacheMaterial( "models/brokenglass/glassbroken_03d" );
  199. }
  200. PrecacheEffect( "GlassImpact" );
  201. PrecacheEffect( "Impact" );
  202. BaseClass::Precache();
  203. }
  204. //------------------------------------------------------------------------------
  205. // Purpose : Window has been touched. Break out pieces based on touching
  206. // entity's bounding box
  207. // Input :
  208. // Output :
  209. //------------------------------------------------------------------------------
  210. void CBreakableSurface::SurfaceTouch( CBaseEntity *pOther )
  211. {
  212. // If tile only break if object is moving fast
  213. if (m_nSurfaceType == SHATTERSURFACE_TILE)
  214. {
  215. Vector vVel;
  216. pOther->GetVelocity( &vVel, NULL );
  217. if (vVel.Length() < 500)
  218. {
  219. return;
  220. }
  221. }
  222. // Find nearest point on plane for max
  223. Vector vecAbsMins, vecAbsMaxs;
  224. pOther->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  225. Vector vToPlane = (vecAbsMaxs - m_vCorner);
  226. float vDistToPlane = DotProduct(m_vNormal,vToPlane);
  227. Vector vTouchPos = vecAbsMaxs + vDistToPlane*m_vNormal;
  228. float flMinsWidth,flMinsHeight;
  229. PanePos(vTouchPos, &flMinsWidth, &flMinsHeight);
  230. // Find nearest point on plane for mins
  231. vToPlane = (vecAbsMins - m_vCorner);
  232. vDistToPlane = DotProduct(m_vNormal,vToPlane);
  233. vTouchPos = vecAbsMins + vDistToPlane*m_vNormal;
  234. float flMaxsWidth,flMaxsHeight;
  235. PanePos(vTouchPos, &flMaxsWidth, &flMaxsHeight);
  236. int nMinWidth = Floor2Int(MAX(0, MIN(flMinsWidth,flMaxsWidth)));
  237. int nMaxWidth = Ceil2Int( fpmin(m_nNumWide, MAX(flMinsWidth,flMaxsWidth)));
  238. int nMinHeight = Floor2Int(MAX(0, MIN(flMinsHeight,flMaxsHeight)));
  239. int nMaxHeight = Ceil2Int( fpmin(m_nNumHigh, MAX(flMinsHeight,flMaxsHeight)));
  240. Vector vHitVel;
  241. pOther->GetVelocity( &vHitVel, NULL );
  242. // Move faster then penetrating object so can see shards
  243. vHitVel *= 5;
  244. // If I'm not broken yet, break me
  245. if ( !m_bIsBroken )
  246. {
  247. Die( pOther, vHitVel );
  248. }
  249. for (int height=nMinHeight;height<nMaxHeight;height++)
  250. {
  251. // Randomly break the one before so it doesn't look square
  252. if (random->RandomInt(0,1))
  253. {
  254. ShatterPane(nMinWidth-1, height,vHitVel,pOther->GetLocalOrigin());
  255. }
  256. for (int width=nMinWidth;width<nMaxWidth;width++)
  257. {
  258. ShatterPane(width, height,vHitVel,pOther->GetLocalOrigin());
  259. }
  260. // Randomly break the one after so it doesn't look square
  261. if (random->RandomInt(0,1))
  262. {
  263. ShatterPane(nMaxWidth+1, height,vHitVel,pOther->GetLocalOrigin());
  264. }
  265. }
  266. }
  267. //------------------------------------------------------------------------------
  268. // Purpose : Only take damage in trace attack
  269. // Input :
  270. // Output :
  271. //------------------------------------------------------------------------------
  272. int CBreakableSurface::OnTakeDamage( const CTakeDamageInfo &info )
  273. {
  274. if ( !m_bIsBroken && info.GetDamageType() == DMG_CRUSH )
  275. {
  276. // physics will kill me now
  277. Die( info.GetAttacker(), info.GetDamageForce() );
  278. return 0;
  279. }
  280. if ( m_nSurfaceType == SHATTERSURFACE_GLASS && info.GetDamageType() & DMG_BLAST )
  281. {
  282. Vector vecDir = info.GetInflictor()->GetAbsOrigin() - WorldSpaceCenter();
  283. VectorNormalize( vecDir );
  284. Die( info.GetAttacker(), vecDir );
  285. return 0;
  286. }
  287. // Accept slash damage, too. Manhacks and such.
  288. if ( m_nSurfaceType == SHATTERSURFACE_GLASS && (info.GetDamageType() & DMG_SLASH) )
  289. {
  290. Die( info.GetAttacker(), info.GetDamageForce() );
  291. return 0;
  292. }
  293. #ifdef INFESTED_DLL
  294. if ( m_nSurfaceType == SHATTERSURFACE_GLASS && (info.GetDamageType() & DMG_CLUB) )
  295. {
  296. Die( info.GetAttacker(), info.GetDamageForce() );
  297. return 0;
  298. }
  299. #endif
  300. return 0;
  301. }
  302. //------------------------------------------------------------------------------
  303. // Purpose: Accepts damage and breaks if health drops below zero.
  304. //------------------------------------------------------------------------------
  305. void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  306. {
  307. // [dwenger] Window break stat tracking
  308. // Make sure this pane has not already been shattered
  309. bool bWasBroken = m_bIsBroken;
  310. // Decrease health
  311. m_iHealth -= info.GetDamage();
  312. m_OnHealthChanged.Set( m_iHealth, info.GetAttacker(), this );
  313. // If I'm not broken yet, break me
  314. if (!m_bIsBroken )
  315. {
  316. Vector vSurfDir = ptr->endpos - ptr->startpos;
  317. Die( info.GetAttacker(), vSurfDir );
  318. }
  319. if (info.GetDamageType() & (DMG_BULLET | DMG_CLUB))
  320. {
  321. // Figure out which panel has taken the damage and break it
  322. float flWidth,flHeight;
  323. PanePos(ptr->endpos,&flWidth,&flHeight);
  324. int nWidth = flWidth;
  325. int nHeight = flHeight;
  326. if ( ShatterPane(nWidth, nHeight,vecDir*500,ptr->endpos) )
  327. {
  328. // [dwenger] Window break stat tracking
  329. CBasePlayer* pAttacker = ToBasePlayer(info.GetAttacker());
  330. if ( ( pAttacker ) && ( !bWasBroken ) )
  331. {
  332. gamestats->Event_WindowShattered( pAttacker );
  333. }
  334. // Do an impact hit
  335. CEffectData data;
  336. data.m_vNormal = ptr->plane.normal;
  337. data.m_vOrigin = ptr->endpos;
  338. CPASFilter filter( data.m_vOrigin );
  339. // client cannot trace against triggers
  340. filter.SetIgnorePredictionCull( true );
  341. DispatchEffect( filter, 0.0, "GlassImpact", data );
  342. }
  343. if (m_nSurfaceType == SHATTERSURFACE_GLASS)
  344. {
  345. // Break nearby panes if damages was near pane edge
  346. float flWRem = flWidth - nWidth;
  347. float flHRem = flHeight - nHeight;
  348. if (flWRem > 0.8 && nWidth != m_nNumWide-1)
  349. {
  350. ShatterPane(nWidth+1, nHeight,vecDir*500,ptr->endpos);
  351. }
  352. else if (flWRem < 0.2 && nWidth != 0)
  353. {
  354. ShatterPane(nWidth-1, nHeight,vecDir*500,ptr->endpos);
  355. }
  356. if (flHRem > 0.8 && nHeight != m_nNumHigh-1)
  357. {
  358. ShatterPane(nWidth, nHeight+1,vecDir*500,ptr->endpos);
  359. }
  360. else if (flHRem < 0.2 && nHeight != 0)
  361. {
  362. ShatterPane(nWidth, nHeight-1,vecDir*500,ptr->endpos);
  363. }
  364. // Occasionally break the pane above me
  365. if (random->RandomInt(0,1)==0)
  366. {
  367. ShatterPane(nWidth, nHeight+1,vecDir*1000,ptr->endpos);
  368. // Occasionally break the pane above that
  369. if (random->RandomInt(0,1)==0)
  370. {
  371. ShatterPane(nWidth, nHeight+2,vecDir*1000,ptr->endpos);
  372. }
  373. }
  374. }
  375. }
  376. else if (info.GetDamageType() & (DMG_SONIC | DMG_BLAST))
  377. {
  378. // ----------------------------------------
  379. // If it's tile blow out nearby tiles
  380. // ----------------------------------------
  381. if (m_nSurfaceType == SHATTERSURFACE_TILE)
  382. {
  383. // Figure out which panel has taken the damage and break it
  384. float flWidth,flHeight;
  385. if (info.GetAttacker())
  386. {
  387. PanePos(info.GetAttacker()->GetAbsOrigin(),&flWidth,&flHeight);
  388. }
  389. else
  390. {
  391. PanePos(ptr->endpos,&flWidth,&flHeight);
  392. }
  393. int nWidth = flWidth;
  394. int nHeight = flHeight;
  395. // Blow out a roughly circular patch of tile with some randomness
  396. for (int width =nWidth-4;width<nWidth+4;width++)
  397. {
  398. for (int height =nHeight-4;height<nHeight+4;height++)
  399. {
  400. if ((abs(nWidth-width)+abs(nHeight-height))<random->RandomInt(2,5))
  401. {
  402. ShatterPane(width, height,vecDir*500,ptr->endpos);
  403. }
  404. }
  405. }
  406. }
  407. // ----------------------------------------
  408. // If it's glass blow out the whole window
  409. // ----------------------------------------
  410. else
  411. {
  412. // [pfreese] Window break stat tracking
  413. CBasePlayer* pAttacker = ToBasePlayer(info.GetAttacker());
  414. if ( ( pAttacker ) && ( !bWasBroken ) )
  415. {
  416. gamestats->Event_WindowShattered( pAttacker );
  417. }
  418. float flDot = DotProduct(m_vNormal,vecDir);
  419. #ifdef CSTRIKE_DLL
  420. float damageMultiplier = info.GetDamage();
  421. #else
  422. float damageMultiplier = 1.0f;
  423. #endif
  424. Vector vBlastDir;
  425. if (flDot > 0)
  426. {
  427. vBlastDir = damageMultiplier * 3000 * m_vNormal;
  428. }
  429. else
  430. {
  431. vBlastDir = damageMultiplier * -3000 * m_vNormal;
  432. }
  433. // Has the window already been destroyed?
  434. if (m_nNumBrokenPanes >= m_nNumWide*m_nNumHigh)
  435. {
  436. return;
  437. }
  438. // ---------------------------------------------------------------
  439. // If less than 10% of my panels have been broken, blow me
  440. // up in one large glass shatter
  441. // ---------------------------------------------------------------
  442. else if ( m_nNumBrokenPanes < 0.1*(m_nNumWide*m_nNumHigh))
  443. {
  444. QAngle vAngles;
  445. VectorAngles(-1*m_vNormal,vAngles);
  446. CreateShards(m_vCorner, vAngles,vBlastDir, ptr->endpos,
  447. m_nNumWide*m_flPanelWidth, m_nNumHigh*m_flPanelHeight, WINDOW_LARGE_SHARD_SIZE);
  448. }
  449. // ---------------------------------------------------------------
  450. // Otherwise break in the longest vertical strips possible
  451. // (to cut down on the network bandwidth)
  452. // ---------------------------------------------------------------
  453. else
  454. {
  455. QAngle vAngles;
  456. VectorAngles(-1*m_vNormal,vAngles);
  457. Vector vWidthDir,vHeightDir;
  458. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  459. for (int width=0;width<m_nNumWide;width++)
  460. {
  461. int height;
  462. int nHCount = 0;
  463. for ( height=0;height<m_nNumHigh;height++)
  464. {
  465. // Keep count of how many panes
  466. if (!IsBroken(width,height))
  467. {
  468. nHCount++;
  469. }
  470. // Shatter the strip and start counting again
  471. else if (nHCount > 0)
  472. {
  473. Vector vBreakPos = m_vCorner +
  474. (width*vWidthDir*m_flPanelWidth) +
  475. ((height-nHCount)*vHeightDir*m_flPanelHeight);
  476. CreateShards(vBreakPos, vAngles,
  477. vBlastDir, ptr->endpos,
  478. m_flPanelWidth, nHCount*m_flPanelHeight,
  479. WINDOW_LARGE_SHARD_SIZE);
  480. nHCount = 0;
  481. }
  482. }
  483. if (nHCount)
  484. {
  485. Vector vBreakPos = m_vCorner +
  486. (width*vWidthDir*m_flPanelWidth) +
  487. ((height-nHCount)*vHeightDir*m_flPanelHeight);
  488. CreateShards(vBreakPos, vAngles,
  489. vBlastDir, ptr->endpos,
  490. m_flPanelWidth,nHCount*m_flPanelHeight,
  491. WINDOW_LARGE_SHARD_SIZE);
  492. }
  493. }
  494. }
  495. BreakAllPanes();
  496. }
  497. }
  498. }
  499. //------------------------------------------------------------------------------
  500. // Purpose: Break into panels
  501. // Input : pBreaker -
  502. // vDir -
  503. //-----------------------------------------------------------------------------
  504. void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir )
  505. {
  506. if ( m_bIsBroken )
  507. return;
  508. // Play a break sound
  509. PhysBreakSound( this, VPhysicsGetObject(), GetAbsOrigin() );
  510. m_bIsBroken = true;
  511. m_iHealth = 0.0f;
  512. if (pBreaker)
  513. {
  514. m_OnBreak.FireOutput( pBreaker, this );
  515. }
  516. else
  517. {
  518. m_OnBreak.FireOutput( this, this );
  519. }
  520. float flDir = -1;
  521. if ( vAttackDir.LengthSqr() > 0.001 )
  522. {
  523. float flDot = DotProduct( m_vNormal, vAttackDir );
  524. if (flDot < 0)
  525. {
  526. m_vLLVertex += m_vNormal;
  527. m_vLRVertex += m_vNormal;
  528. m_vULVertex += m_vNormal;
  529. m_vURVertex += m_vNormal;
  530. m_vNormal *= -1;
  531. flDir = 1;
  532. }
  533. }
  534. // -------------------------------------------------------
  535. // The surface has two sides, when we are killed pick
  536. // the side that the damage came from
  537. // -------------------------------------------------------
  538. Vector vWidth = m_vLLVertex - m_vLRVertex;
  539. Vector vHeight = m_vLLVertex - m_vULVertex;
  540. CrossProduct( vWidth, vHeight, m_vNormal.GetForModify() );
  541. VectorNormalize(m_vNormal.GetForModify());
  542. // ---------------------------------------------------
  543. // Make sure width and height are oriented correctly
  544. // ---------------------------------------------------
  545. QAngle vAngles;
  546. VectorAngles(-1*m_vNormal,vAngles);
  547. Vector vWidthDir,vHeightDir;
  548. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  549. float flWDist = DotProduct(vWidthDir,vWidth);
  550. if (fabs(flWDist)<0.5)
  551. {
  552. Vector vSaveHeight = vHeight;
  553. vHeight = vWidth * flDir;
  554. vWidth = vSaveHeight * flDir;
  555. }
  556. // -------------------------------------------------
  557. // Find which corner to use
  558. // -------------------------------------------------
  559. bool bLeft = (DotProduct(vWidthDir,vWidth) < 0);
  560. bool bLower = (DotProduct(vHeightDir,vHeight) < 0);
  561. if (bLeft)
  562. {
  563. m_vCorner = bLower ? m_vLLVertex : m_vULVertex;
  564. }
  565. else
  566. {
  567. m_vCorner = bLower ? m_vLRVertex : m_vURVertex;
  568. }
  569. // -------------------------------------------------
  570. // Calculate the number of panels
  571. // -------------------------------------------------
  572. float flWidth = vWidth.Length();
  573. float flHeight = vHeight.Length();
  574. m_nNumWide = flWidth / WINDOW_PANEL_SIZE;
  575. m_nNumHigh = flHeight / WINDOW_PANEL_SIZE;
  576. // If to many panels make panel size bigger
  577. if (m_nNumWide > MAX_NUM_PANELS) m_nNumWide = MAX_NUM_PANELS;
  578. if (m_nNumHigh > MAX_NUM_PANELS) m_nNumHigh = MAX_NUM_PANELS;
  579. m_flPanelWidth = flWidth / m_nNumWide;
  580. m_flPanelHeight = flHeight / m_nNumHigh;
  581. // Initialize panels
  582. for (int w=0;w<MAX_NUM_PANELS;w++)
  583. {
  584. for (int h=0;h<MAX_NUM_PANELS;h++)
  585. {
  586. SetSupport( w, h, WINDOW_PANE_HEALTHY );
  587. }
  588. }
  589. // Reset onground flags for any entity that may
  590. // have been standing on me
  591. ResetOnGroundFlags();
  592. VPhysicsDestroyObject();
  593. AddSolidFlags( FSOLID_TRIGGER );
  594. AddSolidFlags( FSOLID_NOT_SOLID );
  595. SetTouch(&CBreakableSurface::SurfaceTouch);
  596. }
  597. //------------------------------------------------------------------------------
  598. // Purpose: Set an instaneous force on the rope.
  599. // Input : Force vector.
  600. //------------------------------------------------------------------------------
  601. void CBreakableSurface::InputShatter( inputdata_t &inputdata )
  602. {
  603. Vector vecShatterInfo;
  604. inputdata.value.Vector3D(vecShatterInfo);
  605. if (!m_bIsBroken)
  606. {
  607. Die( NULL, vec3_origin );
  608. }
  609. // Figure out which panel has taken the damage and break it
  610. float flCenterX = vecShatterInfo.x * m_nNumWide;
  611. float flCenterY = vecShatterInfo.y * m_nNumHigh;
  612. // Bah: m_flPanelWidth is the width of a single panel
  613. int nMinX = (int)(flCenterX - vecShatterInfo.z / m_flPanelWidth);
  614. int nMaxX = (int)(flCenterX + vecShatterInfo.z / m_flPanelWidth) + 1;
  615. if (nMinX < 0)
  616. nMinX = 0;
  617. if (nMaxX > m_nNumWide)
  618. nMaxX = m_nNumWide;
  619. int nMinY = (int)(flCenterY - vecShatterInfo.z / m_flPanelHeight);
  620. int nMaxY = (int)(flCenterY + vecShatterInfo.z / m_flPanelHeight) + 1;
  621. if (nMinY < 0)
  622. nMinY = 0;
  623. if (nMaxY > m_nNumHigh)
  624. nMaxY = m_nNumHigh;
  625. QAngle vAngles;
  626. VectorAngles(-1*m_vNormal,vAngles);
  627. Vector vWidthDir,vHeightDir;
  628. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  629. // Blow out a roughly circular of tile with some randomness
  630. Vector2D vecActualCenter( flCenterX * m_flPanelWidth, flCenterY * m_flPanelHeight );
  631. for (int width = nMinX; width < nMaxX; width++)
  632. {
  633. for (int height = nMinY; height < nMaxY; height++)
  634. {
  635. Vector2D pt( (width + 0.5f) * m_flPanelWidth, (height + 0.5f) * m_flPanelWidth );
  636. if ( pt.DistToSqr(vecActualCenter) <= vecShatterInfo.z * vecShatterInfo.z )
  637. {
  638. Vector vBreakPos = m_vCorner +
  639. (width*vWidthDir*m_flPanelWidth) +
  640. (height*vHeightDir*m_flPanelHeight);
  641. ShatterPane( width, height, m_vNormal * 500, vBreakPos );
  642. }
  643. }
  644. }
  645. }
  646. //------------------------------------------------------------------------------
  647. // Purpose :
  648. // Input :
  649. // Output :
  650. //------------------------------------------------------------------------------
  651. void CBreakableSurface::Event_Killed( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType )
  652. {
  653. return;
  654. }
  655. //------------------------------------------------------------------------------
  656. // Purpose :
  657. // Input :
  658. // Output :
  659. //------------------------------------------------------------------------------
  660. bool CBreakableSurface::IsBroken(int nWidth, int nHeight)
  661. {
  662. if (nWidth < 0 || nWidth >= m_nNumWide) return true;
  663. if (nHeight < 0 || nHeight >= m_nNumHigh) return true;
  664. return (m_flSupport[nWidth][nHeight]==WINDOW_PANE_BROKEN);
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Purpose:
  668. // Input : w -
  669. // h -
  670. // support -
  671. //-----------------------------------------------------------------------------
  672. void CBreakableSurface::SetSupport( int w, int h, float support )
  673. {
  674. m_flSupport[ w ][ h ] = support;
  675. int offset = w + h * m_nNumWide;
  676. bool prevval = m_RawPanelBitVec.Get( offset );
  677. bool curval = prevval;
  678. if ( support < 0.0f )
  679. {
  680. curval = false;
  681. }
  682. else
  683. {
  684. curval = true;
  685. }
  686. if ( curval != prevval )
  687. {
  688. m_RawPanelBitVec.Set( offset, curval );
  689. m_RawPanelBitVec.GetForModify( offset );
  690. }
  691. }
  692. //------------------------------------------------------------------------------
  693. // Purpose :
  694. // Input :
  695. // Output :
  696. //------------------------------------------------------------------------------
  697. float CBreakableSurface::GetSupport(int nWidth, int nHeight)
  698. {
  699. return MAX(0,m_flSupport[nWidth][nHeight]);
  700. }
  701. //------------------------------------------------------------------------------
  702. // Purpose : Return the structural support for this pane. Assumes window
  703. // is upright. Still works for windows parallel to the ground
  704. // but simulation isn't quite as good
  705. // Input :
  706. // Output :
  707. //------------------------------------------------------------------------------
  708. float CBreakableSurface::RecalcSupport(int nWidth, int nHeight)
  709. {
  710. // Always has some support. Zero signifies that it has been broken
  711. float flSupport = 0.01;
  712. // ------------
  713. // Top support
  714. // ------------
  715. if (nHeight == m_nNumHigh-1)
  716. {
  717. flSupport += 1.0;
  718. }
  719. else
  720. {
  721. flSupport += GetSupport(nWidth,nHeight+1);
  722. }
  723. // ------------
  724. // Bottom Support
  725. // ------------
  726. if (nHeight == 0)
  727. {
  728. flSupport += 1.25;
  729. }
  730. else
  731. {
  732. flSupport += 1.25 * GetSupport(nWidth,nHeight-1);
  733. }
  734. // ------------
  735. // Left Support
  736. // ------------
  737. if (nWidth == 0)
  738. {
  739. flSupport += 1.0;
  740. }
  741. else
  742. {
  743. flSupport += GetSupport(nWidth-1,nHeight);
  744. }
  745. // --------------
  746. // Right Support
  747. // --------------
  748. if (nWidth == m_nNumWide-1)
  749. {
  750. flSupport += 1.0;
  751. }
  752. else
  753. {
  754. flSupport += GetSupport(nWidth+1,nHeight);
  755. }
  756. // --------------------
  757. // Bottom Left Support
  758. // --------------------
  759. if (nHeight == 0 || nWidth == 0)
  760. {
  761. flSupport += 1.0;
  762. }
  763. else
  764. {
  765. flSupport += GetSupport(nWidth-1,nHeight-1);
  766. }
  767. // ---------------------
  768. // Bottom Right Support
  769. // ---------------------
  770. if (nHeight == 0 || nWidth == m_nNumWide-1)
  771. {
  772. flSupport += 1.0;
  773. }
  774. else
  775. {
  776. flSupport += GetSupport(nWidth+1,nHeight-1);
  777. }
  778. // -----------------
  779. // Top Right Support
  780. // -----------------
  781. if (nHeight == m_nNumHigh-1 || nWidth == m_nNumWide-1)
  782. {
  783. flSupport += 0.25;
  784. }
  785. else
  786. {
  787. flSupport += 0.25 * GetSupport(nWidth+1,nHeight+1);
  788. }
  789. // -----------------
  790. // Top Left Support
  791. // -----------------
  792. if (nHeight == m_nNumHigh-1 || nWidth == 0)
  793. {
  794. flSupport += 0.25;
  795. }
  796. else
  797. {
  798. flSupport += 0.25 * GetSupport(nWidth-1,nHeight+1);
  799. }
  800. return flSupport;
  801. }
  802. //------------------------------------------------------------------------------
  803. // Purpose : Itterate through the panels and make sure none have become
  804. // unstable
  805. // Input :
  806. // Output :
  807. //------------------------------------------------------------------------------
  808. void CBreakableSurface::BreakThink(void)
  809. {
  810. // Don't calculate support if I'm tile
  811. if (m_nSurfaceType == SHATTERSURFACE_TILE)
  812. {
  813. return;
  814. }
  815. // -----------------------
  816. // Recalculate all support
  817. // -----------------------
  818. int w;
  819. float flSupport[MAX_NUM_PANELS][MAX_NUM_PANELS];
  820. for (w=0;w<m_nNumWide;w++)
  821. {
  822. for (int h=0;h<m_nNumHigh;h++)
  823. {
  824. if (!IsBroken(w,h))
  825. {
  826. flSupport[w][h] = RecalcSupport(w,h);
  827. }
  828. }
  829. }
  830. // ----------------------------------------------------
  831. // Set support and break inadequately supported panes
  832. // ----------------------------------------------------
  833. float flBreakValue = WINDOW_BREAK_SUPPORT*(m_nFragility/100.0);
  834. for (w=0;w<m_nNumWide;w++)
  835. {
  836. for (int h=0;h<m_nNumHigh;h++)
  837. {
  838. if (!IsBroken(w,h))
  839. {
  840. SetSupport( w, h, flSupport[w][h]/WINDOW_MAX_SUPPORT );
  841. if (m_flSupport[w][h] < flBreakValue)
  842. {
  843. // Occasionaly drop a pane
  844. if (random->RandomInt(0,1))
  845. {
  846. DropPane(w,h);
  847. }
  848. // Otherwise just shatter the glass
  849. else
  850. {
  851. ShatterPane(w,h,vec3_origin,vec3_origin);
  852. }
  853. SetNextThink( gpGlobals->curtime );
  854. }
  855. }
  856. }
  857. }
  858. }
  859. //------------------------------------------------------------------------------
  860. // Purpose : Given a 3D position on the window in space return the height and
  861. // width of the position from the window's corner
  862. // Input :
  863. // Output :
  864. //------------------------------------------------------------------------------
  865. void CBreakableSurface::PanePos(const Vector &vPos, float *flWidth, float *flHeight)
  866. {
  867. Vector vAttackVec = vPos - m_vCorner;
  868. QAngle vAngles;
  869. VectorAngles(-1*m_vNormal,vAngles);
  870. Vector vWidthDir,vHeightDir;
  871. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  872. float flWDist = DotProduct(vWidthDir,vAttackVec);
  873. float flHDist = DotProduct(vHeightDir,vAttackVec);
  874. // Figure out which quadrent I'm in
  875. *flWidth = flWDist/m_flPanelWidth;
  876. *flHeight = flHDist/m_flPanelHeight;
  877. }
  878. //------------------------------------------------------------------------------
  879. // Purpose :
  880. // Input :
  881. // Output :
  882. //------------------------------------------------------------------------------
  883. void CBreakableSurface::BreakAllPanes(void)
  884. {
  885. // Now tell the client all the panes have been broken
  886. for (int width=0;width<m_nNumWide;width++)
  887. {
  888. for (int height=0;height<m_nNumHigh;height++)
  889. {
  890. //SetSupport( width, height, WINDOW_PANE_BROKEN );
  891. BreakPane(width,height);
  892. }
  893. }
  894. m_nNumBrokenPanes = m_nNumWide*m_nNumHigh;
  895. }
  896. //------------------------------------------------------------------------------
  897. // Purpose : Drop a window pane entity
  898. // Input :
  899. // Output :
  900. //------------------------------------------------------------------------------
  901. void CBreakableSurface::BreakPane(int nWidth, int nHeight)
  902. {
  903. // Check parameter range
  904. if (nWidth < 0 || nWidth >= m_nNumWide) return;
  905. if (nHeight < 0 || nHeight >= m_nNumHigh) return;
  906. // Count how many panes have been broken or dropped
  907. m_nNumBrokenPanes++;
  908. SetSupport( nWidth, nHeight, WINDOW_PANE_BROKEN );
  909. SetThink(&CBreakableSurface::BreakThink);
  910. SetNextThink( gpGlobals->curtime );
  911. }
  912. //------------------------------------------------------------------------------
  913. // Purpose : Drop a window pane entity
  914. // Input :
  915. // Output :
  916. //------------------------------------------------------------------------------
  917. void CBreakableSurface::DropPane(int nWidth, int nHeight)
  918. {
  919. // Check parameter range
  920. if (nWidth < 0 || nWidth >= m_nNumWide) return;
  921. if (nHeight < 0 || nHeight >= m_nNumHigh) return;
  922. if (!IsBroken(nWidth,nHeight))
  923. {
  924. BreakPane(nWidth,nHeight);
  925. QAngle vAngles;
  926. VectorAngles(-1*m_vNormal,vAngles);
  927. Vector vWidthDir,vHeightDir;
  928. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  929. Vector vBreakPos = m_vCorner +
  930. (nWidth*vWidthDir*m_flPanelWidth) +
  931. (nHeight*vHeightDir*m_flPanelHeight);
  932. CreateShards(vBreakPos, vAngles, vec3_origin, vec3_origin,
  933. WINDOW_PANEL_SIZE, WINDOW_PANEL_SIZE,
  934. WINDOW_SMALL_SHARD_SIZE);
  935. DamageSound();
  936. CWindowPane *pPane = CWindowPane::CreateWindowPane(vBreakPos, vAngles);
  937. if (pPane)
  938. {
  939. pPane->SetLocalAngularVelocity( RandomAngle(-120,120) );
  940. }
  941. }
  942. }
  943. void CBreakableSurface::CreateShards(const Vector &vBreakPos, const QAngle &vAngles,
  944. const Vector &vForce, const Vector &vForcePos,
  945. float flWidth, float flHeight,
  946. int nShardSize)
  947. {
  948. Vector vAdjustedBreakPos = vBreakPos;
  949. Vector vAdjustedForce = vForce;
  950. int front_r,front_g,front_b;
  951. int back_r,back_g,back_b;
  952. // UNDONE: For now hardcode these colors. Later when used by more textures
  953. // we'll automate this process or expose the colors in WC
  954. if (m_nSurfaceType == SHATTERSURFACE_TILE)
  955. {
  956. // If tile shoot shards back from the shattered surface and offset slightly
  957. // from the surface.
  958. vAdjustedBreakPos -= 8*m_vNormal;
  959. vAdjustedForce = -0.75*vForce;
  960. front_r = 89;
  961. front_g = 120;
  962. front_b = 83;
  963. back_r = 99;
  964. back_g = 76;
  965. back_b = 21;
  966. }
  967. else
  968. {
  969. front_r = 255;
  970. front_g = 255;
  971. front_b = 255;
  972. back_r = 255;
  973. back_g = 255;
  974. back_b = 255;
  975. }
  976. CPASFilter filter( vAdjustedBreakPos );
  977. te->ShatterSurface(filter, 0.0,
  978. &vAdjustedBreakPos, &vAngles,
  979. &vAdjustedForce, &vForcePos,
  980. flWidth, flHeight,WINDOW_SMALL_SHARD_SIZE,m_nSurfaceType,
  981. front_r,front_g,front_b,back_r,back_g,back_b);//4);
  982. }
  983. //------------------------------------------------------------------------------
  984. // Purpose : Break a panel
  985. // Input :
  986. // Output :
  987. //------------------------------------------------------------------------------
  988. bool CBreakableSurface::ShatterPane(int nWidth, int nHeight, const Vector &vForce, const Vector &vForcePos)
  989. {
  990. // Check parameter range
  991. if (nWidth < 0 || nWidth >= m_nNumWide) return false;
  992. if (nHeight < 0 || nHeight >= m_nNumHigh) return false;
  993. if ( IsBroken(nWidth,nHeight) )
  994. return false;
  995. BreakPane(nWidth,nHeight);
  996. QAngle vAngles;
  997. VectorAngles(-1*m_vNormal,vAngles);
  998. Vector vWidthDir,vHeightDir;
  999. AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
  1000. Vector vBreakPos = m_vCorner +
  1001. (nWidth*vWidthDir*m_flPanelWidth) +
  1002. (nHeight*vHeightDir*m_flPanelHeight);
  1003. CreateShards(vBreakPos, vAngles,vForce, vForcePos, m_flPanelWidth, m_flPanelHeight, WINDOW_SMALL_SHARD_SIZE);
  1004. DamageSound();
  1005. return true;
  1006. }
  1007. //------------------------------------------------------------------------------
  1008. // Purpose :
  1009. // Input :
  1010. // Output :
  1011. //------------------------------------------------------------------------------
  1012. void CBreakableSurface::Spawn(void)
  1013. {
  1014. BaseClass::Spawn();
  1015. SetCollisionGroup( COLLISION_GROUP_BREAKABLE_GLASS );
  1016. m_bIsBroken = false;
  1017. if (m_nQuadError == QUAD_ERR_MULT_FACES)
  1018. {
  1019. Vector center = WorldSpaceCenter();
  1020. Warning( "Rejecting func_breakablesurf at (%2.2f, %2.2f, %2.2f). Has multiple faces that aren't NODRAW.\n",
  1021. center.x, center.y, center.z );
  1022. UTIL_Remove(this);
  1023. }
  1024. else if (m_nQuadError == QUAD_ERR_NOT_QUAD)
  1025. {
  1026. Vector center = WorldSpaceCenter();
  1027. Warning( "Rejecting func_breakablesurf at (%2.2f, %2.2f, %2.2f). Drawn face isn't a quad.\n",
  1028. center.x, center.y, center.z );
  1029. UTIL_Remove(this);
  1030. }
  1031. int materialCount = modelinfo->GetModelMaterialCount( const_cast<model_t*>(GetModel()) );
  1032. if( materialCount != 1 )
  1033. {
  1034. Vector center = WorldSpaceCenter();
  1035. Warning( "Encountered func_breakablesurf at (%2.2f, %2.2f, %2.2f) that has a material applied to more than one surface!\n",
  1036. center.x, center.y, center.z );
  1037. UTIL_Remove(this);
  1038. }
  1039. // Get at the first material; even if there are more than one.
  1040. IMaterial* pMaterial;
  1041. modelinfo->GetModelMaterials( const_cast<model_t*>(GetModel()), 1, &pMaterial );
  1042. // The material should point to a cracked version of itself
  1043. bool foundVar;
  1044. IMaterialVar* pCrackName = pMaterial->FindVar( "$crackmaterial", &foundVar, false );
  1045. if ( foundVar && IsPrecacheAllowed() )
  1046. {
  1047. PrecacheMaterial( pCrackName->GetStringValue() );
  1048. }
  1049. // Init the Panel bit vector to all true. ( no panes are broken )
  1050. int bitVecLength = MAX_NUM_PANELS * MAX_NUM_PANELS;
  1051. for( int i=0;i<bitVecLength;i++ )
  1052. {
  1053. m_RawPanelBitVec.Set( i, true );
  1054. }
  1055. }
  1056. void CBreakableSurface::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  1057. {
  1058. if ( !m_bIsBroken )
  1059. {
  1060. int damageType = 0;
  1061. string_t iszDamageTable = ( ( m_nSurfaceType == SHATTERSURFACE_GLASS ) ? ( "glass" ) : ( NULL_STRING ) );
  1062. bool bDamageFromHeldObjects = ( ( m_spawnflags & SF_BREAKABLESURF_DAMAGE_FROM_HELD_OBJECTS ) != 0 );
  1063. float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0, false, damageType, iszDamageTable, bDamageFromHeldObjects );
  1064. if ( damage > 10 )
  1065. {
  1066. // HACKHACK: Reset mass to get correct collision response for the object breaking this
  1067. pEvent->pObjects[index]->SetMass( 2.0f );
  1068. Vector normal, damagePos;
  1069. pEvent->pInternalData->GetSurfaceNormal( normal );
  1070. if ( index == 0 )
  1071. {
  1072. normal *= -1.0f;
  1073. }
  1074. pEvent->pInternalData->GetContactPoint( damagePos );
  1075. int otherIndex = !index;
  1076. CBaseEntity *pInflictor = pEvent->pEntities[otherIndex];
  1077. CTakeDamageInfo info( pInflictor, pInflictor, normal, damagePos, damage, damageType );
  1078. PhysCallbackDamage( this, info, *pEvent, index );
  1079. }
  1080. else if ( damage > 0 )
  1081. {
  1082. if ( m_spawnflags & SF_BREAKABLESURF_CRACK_DECALS )
  1083. {
  1084. Vector normal, damagePos;
  1085. pEvent->pInternalData->GetSurfaceNormal( normal );
  1086. if ( index == 0 )
  1087. {
  1088. normal *= -1.0f;
  1089. }
  1090. pEvent->pInternalData->GetContactPoint( damagePos );
  1091. trace_t tr;
  1092. UTIL_TraceLine ( damagePos - normal, damagePos + normal, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
  1093. // Only place decals and draw effects if we hit something valid
  1094. if ( tr.m_pEnt && tr.m_pEnt == this )
  1095. {
  1096. // Build the impact data
  1097. CEffectData data;
  1098. data.m_vOrigin = tr.endpos;
  1099. data.m_vStart = tr.startpos;
  1100. data.m_nSurfaceProp = tr.surface.surfaceProps;
  1101. data.m_nDamageType = DMG_CLUB;
  1102. data.m_nHitBox = tr.hitbox;
  1103. data.m_nEntIndex = entindex();
  1104. // Send it on its way
  1105. DispatchEffect( "Impact", data );
  1106. }
  1107. }
  1108. }
  1109. }
  1110. BaseClass::VPhysicsCollision( index, pEvent );
  1111. }