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.

1295 lines
37 KiB

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