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.

550 lines
16 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "physics_prop_statue.h"
  8. #include "baseanimating.h"
  9. #include "studio.h"
  10. #include "bone_setup.h"
  11. #include "EntityFreezing.h"
  12. //#include "particle_parse.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //-----------------------------------------------------------------------------
  16. // Networking
  17. //-----------------------------------------------------------------------------
  18. LINK_ENTITY_TO_CLASS( physics_prop_statue, CStatueProp );
  19. IMPLEMENT_SERVERCLASS_ST( CStatueProp, DT_StatueProp )
  20. SendPropEHandle( SENDINFO( m_hInitBaseAnimating ) ),
  21. SendPropBool( SENDINFO( m_bShatter ) ),
  22. SendPropInt( SENDINFO( m_nShatterFlags ), 3 ),
  23. SendPropVector( SENDINFO( m_vShatterPosition ) ),
  24. SendPropVector( SENDINFO( m_vShatterForce ) ),
  25. END_SEND_TABLE()
  26. BEGIN_DATADESC( CStatueProp )
  27. DEFINE_FIELD( m_hInitBaseAnimating, FIELD_EHANDLE ),
  28. DEFINE_FIELD( m_bShatter, FIELD_BOOLEAN ),
  29. DEFINE_FIELD( m_nShatterFlags, FIELD_INTEGER ),
  30. DEFINE_FIELD( m_vShatterPosition, FIELD_VECTOR ),
  31. DEFINE_FIELD( m_vShatterForce, FIELD_VECTOR ),
  32. DEFINE_THINKFUNC( CollisionPartnerThink ),
  33. END_DATADESC()
  34. ConVarRef *s_vcollide_wireframe = NULL;
  35. CStatueProp::CStatueProp( void )
  36. {
  37. static ConVarRef vcollide_wireframe( "vcollide_wireframe" );
  38. s_vcollide_wireframe = &vcollide_wireframe;
  39. m_pInitOBBs = NULL;
  40. }
  41. void CStatueProp::Spawn( void )
  42. {
  43. // Make it breakable
  44. SetBreakableModel( MAKE_STRING( "ConcreteChunks" ) );
  45. SetBreakableCount( 6 );
  46. SetHealth( 5 );
  47. BaseClass::Spawn();
  48. m_flFrozen = 1.0f;
  49. }
  50. void CStatueProp::Precache( void )
  51. {
  52. }
  53. bool CStatueProp::CreateVPhysics( void )
  54. {
  55. if ( m_pInitOBBs )
  56. {
  57. return CreateVPhysicsFromOBBs( m_hInitBaseAnimating );
  58. }
  59. else
  60. {
  61. if ( !CreateVPhysicsFromHitBoxes( m_hInitBaseAnimating ) )
  62. {
  63. // Init model didn't work out, so just use our own
  64. return CreateVPhysicsFromHitBoxes( this );
  65. }
  66. return true;
  67. }
  68. }
  69. void CStatueProp::VPhysicsUpdate( IPhysicsObject *pPhysics )
  70. {
  71. BaseClass::VPhysicsUpdate( pPhysics );
  72. if ( s_vcollide_wireframe->GetBool() )
  73. {
  74. const CPhysCollide *pCollide = pPhysics->GetCollide();
  75. Vector vecOrigin;
  76. QAngle angAngles;
  77. pPhysics->GetPosition( &vecOrigin, &angAngles );
  78. if ( pCollide )
  79. {
  80. Vector *outVerts;
  81. int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
  82. int triCount = vertCount / 3;
  83. int vert = 0;
  84. VMatrix tmp = SetupMatrixOrgAngles( vecOrigin, angAngles );
  85. int i;
  86. for ( i = 0; i < vertCount; i++ )
  87. {
  88. outVerts[i] = tmp.VMul4x3( outVerts[i] );
  89. }
  90. for ( i = 0; i < triCount; i++ )
  91. {
  92. NDebugOverlay::Line( outVerts[ vert + 0 ], outVerts[ vert + 1 ], 0, 255, 255, false, 0.0f );
  93. NDebugOverlay::Line( outVerts[ vert + 1 ], outVerts[ vert + 2 ], 0, 255, 255, false, 0.0f );
  94. NDebugOverlay::Line( outVerts[ vert + 2 ], outVerts[ vert + 0 ], 0, 255, 255, false, 0.0f );
  95. vert += 3;
  96. }
  97. physcollision->DestroyDebugMesh( vertCount, outVerts );
  98. }
  99. }
  100. }
  101. void CStatueProp::ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs )
  102. {
  103. CBaseAnimating *pBaseAnimating = m_hInitBaseAnimating;
  104. if ( pBaseAnimating )
  105. {
  106. pBaseAnimating->CollisionProp()->WorldSpaceSurroundingBounds( pMins, pMaxs );
  107. return;
  108. }
  109. CollisionProp()->WorldSpaceSurroundingBounds( pMins, pMaxs );
  110. }
  111. bool CStatueProp::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  112. {
  113. IPhysicsObject *pPhysObject = VPhysicsGetObject();
  114. if ( pPhysObject )
  115. {
  116. Vector vecPosition;
  117. QAngle vecAngles;
  118. pPhysObject->GetPosition( &vecPosition, &vecAngles );
  119. const CPhysCollide *pScaledCollide = pPhysObject->GetCollide();
  120. physcollision->TraceBox( ray, pScaledCollide, vecPosition, vecAngles, &tr );
  121. return tr.DidHit();
  122. }
  123. return false;
  124. }
  125. int CStatueProp::OnTakeDamage( const CTakeDamageInfo &info )
  126. {
  127. return BaseClass::OnTakeDamage( info );
  128. }
  129. void CStatueProp::Event_Killed( const CTakeDamageInfo &info )
  130. {
  131. IPhysicsObject *pPhysics = VPhysicsGetObject();
  132. if ( pPhysics && !pPhysics->IsMoveable() )
  133. {
  134. pPhysics->EnableMotion( true );
  135. VPhysicsTakeDamage( info );
  136. }
  137. m_nShatterFlags = 0; // If you have some flags to network for the shatter effect, put them here!
  138. m_vShatterPosition = info.GetDamagePosition();
  139. m_vShatterForce = info.GetDamageForce();
  140. m_bShatter = true;
  141. // Skip over breaking code!
  142. //Break( info.GetInflictor(), info );
  143. //BaseClass::Event_Killed( info );
  144. // FIXME: Short delay before we actually remove so that the client statue gets a network update before we need it
  145. // This isn't a reliable way to do this and needs to be rethought.
  146. AddSolidFlags( FSOLID_NOT_SOLID );
  147. SetNextThink( gpGlobals->curtime + 0.2f );
  148. SetThink( &CBaseEntity::SUB_Remove );
  149. }
  150. void CStatueProp::Freeze( float flFreezeAmount, CBaseEntity *pFreezer, Ray_t *pFreezeRay )
  151. {
  152. // Can't freeze a statue
  153. TakeDamage( CTakeDamageInfo( pFreezer, pFreezer, 1, DMG_GENERIC ) );
  154. }
  155. void CStatueProp::CollisionPartnerThink( void )
  156. {
  157. CBaseAnimating *pBaseAnimating = m_hInitBaseAnimating;
  158. if ( !pBaseAnimating )
  159. {
  160. // Our partner died, I have no reason to live!
  161. UTIL_Remove( this );
  162. }
  163. if ( GetHealth() <= 0 )
  164. {
  165. // Reset health here in case it was tweaked by the model parse
  166. SetHealth( 5 );
  167. m_takedamage = DAMAGE_YES;
  168. }
  169. SetNextThink( gpGlobals->curtime + 1.0f );
  170. }
  171. bool CStatueProp::CreateVPhysicsFromHitBoxes( CBaseAnimating *pInitBaseAnimating )
  172. {
  173. if ( !pInitBaseAnimating )
  174. return false;
  175. // Use the current animation sequence and cycle
  176. CopyAnimationDataFrom( pInitBaseAnimating );
  177. // Copy over any render color
  178. color24 colorRender = pInitBaseAnimating->GetRenderColor();
  179. SetRenderColor( colorRender.r, colorRender.g, colorRender.b );
  180. SetRenderAlpha( pInitBaseAnimating->GetRenderAlpha() );
  181. // Get hitbox data
  182. CStudioHdr *pStudioHdr = GetModelPtr();
  183. if ( !pStudioHdr )
  184. return false;
  185. mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
  186. if ( !set )
  187. return false;
  188. Vector position;
  189. QAngle angles;
  190. // Make enough pointers to convexes for each hitbox
  191. CPhysConvex **ppConvex = new CPhysConvex*[ set->numhitboxes ];
  192. float flTotalVolume = 0.0f;
  193. float flTotalSurfaceArea = 0.0f;
  194. for ( int i = 0; i < set->numhitboxes; i++ )
  195. {
  196. // Get the hitbox info
  197. mstudiobbox_t *pbox = set->pHitbox( i );
  198. GetBonePosition( pbox->bone, position, angles );
  199. // Accumulate volume and area
  200. Vector flDimentions = pbox->bbmax - pbox->bbmin;
  201. flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z;
  202. flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );
  203. // Get angled min and max extents
  204. Vector vecMins, vecMaxs;
  205. VectorRotate( pbox->bbmin, angles, vecMins );
  206. VectorRotate( pbox->bbmax, angles, vecMaxs );
  207. // Get the corners in world space
  208. Vector vecMinCorner = position + vecMins;
  209. Vector vecMaxCorner = position + vecMaxs;
  210. // Get the normals of the hitbox in world space
  211. Vector vecForward, vecRight, vecUp;
  212. AngleVectors( angles, &vecForward, &vecRight, &vecUp );
  213. vecRight = -vecRight;
  214. // Convert corners and normals to local space
  215. Vector vecCornerLocal[ 2 ];
  216. Vector vecNormalLocal[ 3 ];
  217. matrix3x4_t matToWorld = EntityToWorldTransform();
  218. VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] );
  219. VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] );
  220. VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] );
  221. VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] );
  222. VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );
  223. // Create 6 planes from the local oriented hit box data
  224. float pPlanes[ 4 * 6 ];
  225. for ( int iPlane = 0; iPlane < 6; ++iPlane )
  226. {
  227. int iPlaneMod2 = iPlane % 2;
  228. int iPlaneDiv2 = iPlane / 2;
  229. bool bOdd = ( iPlaneMod2 == 1 );
  230. // Plane Normal
  231. pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f );
  232. pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f );
  233. pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );
  234. // Plane D
  235. pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x +
  236. vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y +
  237. vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f );
  238. }
  239. // Create convex from the intersection of these planes
  240. ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f );
  241. }
  242. // Make a single collide out of the group of convex boxes
  243. CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, set->numhitboxes );
  244. delete[] ppConvex;
  245. // Create the physics object
  246. objectparams_t params = g_PhysDefaultObjectParams;
  247. params.pGameData = static_cast<void *>( this );
  248. int nMaterialIndex = physprops->GetSurfaceIndex( "ice" ); // use ice material
  249. IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), &params );
  250. Assert( p != NULL );
  251. // Set velocity
  252. Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity();
  253. p->SetVelocity( &vecInitialVelocity, NULL );
  254. // Compute mass
  255. float flMass;
  256. float flDensity, flThickness;
  257. physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );
  258. // Make it more hollow
  259. flThickness = MIN ( 1.0f, flThickness + 0.5f );
  260. if ( flThickness > 0.0f )
  261. {
  262. flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
  263. }
  264. else
  265. {
  266. // density is in kg/m^3, volume is in in^3
  267. flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
  268. }
  269. // Mass is somewhere between the original and if it was all ice
  270. p->SetMass( flMass );
  271. // Yes, gravity
  272. p->EnableGravity( true );
  273. // Use this as our vphysics
  274. VPhysicsSetObject( p );
  275. SetSolid( SOLID_VPHYSICS );
  276. AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  277. SetMoveType( MOVETYPE_VPHYSICS );
  278. if ( pInitBaseAnimating != this )
  279. {
  280. // Transfer children from the init base animating
  281. TransferChildren( pInitBaseAnimating, this );
  282. CBaseEntity *pChild = FirstMoveChild();
  283. while ( pChild )
  284. {
  285. CEntityFreezing *pFreezing = dynamic_cast<CEntityFreezing*>( pChild );
  286. if ( pFreezing )
  287. {
  288. pFreezing->FinishFreezing();
  289. }
  290. pChild = pChild->NextMovePeer();
  291. }
  292. }
  293. return true;
  294. }
  295. bool CStatueProp::CreateVPhysicsFromOBBs( CBaseAnimating *pInitBaseAnimating )
  296. {
  297. // Make enough pointers to convexes for each hitbox
  298. CPhysConvex **ppConvex = new CPhysConvex*[ m_pInitOBBs->Count() ];
  299. float flTotalVolume = 0.0f;
  300. float flTotalSurfaceArea = 0.0f;
  301. for ( int i = 0; i < m_pInitOBBs->Count(); i++ )
  302. {
  303. const outer_collision_obb_t *pOBB = &((*m_pInitOBBs)[ i ]);
  304. // Accumulate volume and area
  305. Vector flDimentions = pOBB->vecMaxs - pOBB->vecMins;
  306. flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z;
  307. flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );
  308. // Get angled min and max extents
  309. Vector vecMins, vecMaxs;
  310. VectorRotate( pOBB->vecMins, pOBB->angAngles, vecMins );
  311. VectorRotate( pOBB->vecMaxs, pOBB->angAngles, vecMaxs );
  312. // Get the corners in world space
  313. Vector vecMinCorner = pOBB->vecPos + vecMins;
  314. Vector vecMaxCorner = pOBB->vecPos + vecMaxs;
  315. // Get the normals of the hitbox in world space
  316. Vector vecForward, vecRight, vecUp;
  317. AngleVectors( pOBB->angAngles, &vecForward, &vecRight, &vecUp );
  318. vecRight = -vecRight;
  319. // Convert corners and normals to local space
  320. Vector vecCornerLocal[ 2 ];
  321. Vector vecNormalLocal[ 3 ];
  322. matrix3x4_t matToWorld = EntityToWorldTransform();
  323. VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] );
  324. VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] );
  325. VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] );
  326. VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] );
  327. VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );
  328. // Create 6 planes from the local oriented hit box data
  329. float pPlanes[ 4 * 6 ];
  330. for ( int iPlane = 0; iPlane < 6; ++iPlane )
  331. {
  332. int iPlaneMod2 = iPlane % 2;
  333. int iPlaneDiv2 = iPlane / 2;
  334. bool bOdd = ( iPlaneMod2 == 1 );
  335. // Plane Normal
  336. pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f );
  337. pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f );
  338. pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );
  339. // Plane D
  340. pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x +
  341. vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y +
  342. vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f );
  343. }
  344. // Create convex from the intersection of these planes
  345. ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f );
  346. }
  347. // Make a single collide out of the group of convex boxes
  348. CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, m_pInitOBBs->Count() );
  349. delete[] ppConvex;
  350. // Create the physics object
  351. objectparams_t params = g_PhysDefaultObjectParams;
  352. params.pGameData = static_cast<void *>( this );
  353. int nMaterialIndex = physprops->GetSurfaceIndex( "ice" ); // use ice material
  354. IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), &params );
  355. Assert( p != NULL );
  356. // Set velocity
  357. Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity();
  358. p->SetVelocity( &vecInitialVelocity, NULL );
  359. // Compute mass
  360. float flMass;
  361. float flDensity, flThickness;
  362. physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );
  363. // Make it more hollow
  364. flThickness = MIN ( 1.0f, flThickness + 0.5f );
  365. if ( flThickness > 0.0f )
  366. {
  367. flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
  368. }
  369. else
  370. {
  371. // density is in kg/m^3, volume is in in^3
  372. flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
  373. }
  374. // Mass is somewhere between the original and if it was all ice
  375. p->SetMass( flMass );
  376. // Yes, gravity
  377. p->EnableGravity( true );
  378. // Use this as our vphysics
  379. VPhysicsSetObject( p );
  380. SetSolid( SOLID_VPHYSICS );
  381. AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  382. SetMoveType( MOVETYPE_VPHYSICS );
  383. m_pInitOBBs = NULL;
  384. return true;
  385. }
  386. CBaseEntity *CreateServerStatue( CBaseAnimating *pAnimating, int collisionGroup )
  387. {
  388. CStatueProp *pStatue = static_cast<CStatueProp *>( CreateEntityByName( "physics_prop_statue" ) );
  389. if ( pStatue )
  390. {
  391. pStatue->m_hInitBaseAnimating = pAnimating;
  392. pStatue->SetModelName( pAnimating->GetModelName() );
  393. pStatue->SetAbsOrigin( pAnimating->GetAbsOrigin() );
  394. pStatue->SetAbsAngles( pAnimating->GetAbsAngles() );
  395. DispatchSpawn( pStatue );
  396. pStatue->Activate();
  397. }
  398. return pStatue;
  399. }
  400. CBaseEntity *CreateServerStatueFromOBBs( const CUtlVector<outer_collision_obb_t> &vecSphereOrigins, CBaseAnimating *pAnimating )
  401. {
  402. Assert( vecSphereOrigins.Count() > 0 );
  403. if ( vecSphereOrigins.Count() <= 0 )
  404. return NULL;
  405. CStatueProp *pStatue = static_cast<CStatueProp *>( CreateEntityByName( "physics_prop_statue" ) );
  406. if ( pStatue )
  407. {
  408. pStatue->m_pInitOBBs = &vecSphereOrigins;
  409. pStatue->m_hInitBaseAnimating = pAnimating;
  410. pStatue->SetModelName( pAnimating->GetModelName() );
  411. pStatue->SetAbsOrigin( pAnimating->GetAbsOrigin() );
  412. pStatue->SetAbsAngles( pAnimating->GetAbsAngles() );
  413. DispatchSpawn( pStatue );
  414. pStatue->Activate();
  415. pStatue->AddEffects( EF_NODRAW );
  416. pStatue->CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE );
  417. pStatue->AddSolidFlags( ( pAnimating->GetSolidFlags() & FSOLID_CUSTOMBOXTEST ) | ( pAnimating->GetSolidFlags() & FSOLID_CUSTOMRAYTEST ) );
  418. pAnimating->SetParent( pStatue );
  419. // You'll need to keep track of the child for collision rules
  420. pStatue->SetThink( &CStatueProp::CollisionPartnerThink );
  421. pStatue->SetNextThink( gpGlobals->curtime + 1.0f );
  422. }
  423. return pStatue;
  424. }