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.
1297 lines
37 KiB
1297 lines
37 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: A planar textured surface that breaks into increasingly smaller fragments |
|
// as it takes damage. Undamaged pieces remain attached to the world |
|
// until they are damaged. Used for window panes. |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "ndebugoverlay.h" |
|
#include "filters.h" |
|
#include "player.h" |
|
#include "func_breakablesurf.h" |
|
#include "shattersurfacetypes.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "materialsystem/imaterialvar.h" |
|
#include "globals.h" |
|
#include "physics_impact_damage.h" |
|
#include "te_effect_dispatch.h" |
|
|
|
//============================================================================= |
|
// HPE_BEGIN |
|
// [dwenger] Necessary for stats tracking |
|
//============================================================================= |
|
#include "gamestats.h" |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// Spawn flags |
|
#define SF_BREAKABLESURF_CRACK_DECALS 0x00000001 |
|
#define SF_BREAKABLESURF_DAMAGE_FROM_HELD_OBJECTS 0x00000002 |
|
|
|
//############################################################################# |
|
// > CWindowPane |
|
//############################################################################# |
|
#define WINDOW_PANEL_SIZE 12 |
|
#define WINDOW_SMALL_SHARD_SIZE 4 |
|
#define WINDOW_LARGE_SHARD_SIZE 7 |
|
#define WINDOW_MAX_SUPPORT 6.75 |
|
#define WINDOW_BREAK_SUPPORT 0.20 |
|
|
|
#define WINDOW_PANE_BROKEN -1 |
|
#define WINDOW_PANE_HEALTHY 1 |
|
|
|
// Also defined in WC |
|
#define QUAD_ERR_NONE 0 |
|
#define QUAD_ERR_MULT_FACES 1 |
|
#define QUAD_ERR_NOT_QUAD 2 |
|
|
|
// |
|
// func_breakable - bmodel that breaks into pieces after taking damage |
|
// |
|
LINK_ENTITY_TO_CLASS( window_pane, CWindowPane ); |
|
BEGIN_DATADESC( CWindowPane ) |
|
|
|
// Function Pointers |
|
DEFINE_FUNCTION( Die ), |
|
DEFINE_FUNCTION( PaneTouch ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CWindowPane::Spawn( void ) |
|
{ |
|
Precache( ); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
SetMoveType( MOVETYPE_FLYGRAVITY ); |
|
m_takedamage = DAMAGE_YES; |
|
|
|
SetCollisionGroup( COLLISION_GROUP_BREAKABLE_GLASS ); |
|
|
|
SetModel( "models/brokenglass_piece.mdl" );//set size and link into world. |
|
} |
|
|
|
void CWindowPane::Precache( void ) |
|
{ |
|
PrecacheModel( "models/brokenglass_piece.mdl" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pOther - |
|
//----------------------------------------------------------------------------- |
|
void CWindowPane::PaneTouch( CBaseEntity *pOther ) |
|
{ |
|
if (pOther && |
|
pOther->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS) |
|
{ |
|
Die(); |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CWindowPane::Die( void ) |
|
{ |
|
Vector flForce = -1 * GetAbsVelocity(); |
|
|
|
CPASFilter filter( GetAbsOrigin() ); |
|
te->ShatterSurface( filter, 0.0, |
|
&GetAbsOrigin(), &GetAbsAngles(), |
|
&GetAbsVelocity(), &GetAbsOrigin(), |
|
WINDOW_PANEL_SIZE, WINDOW_PANEL_SIZE,WINDOW_SMALL_SHARD_SIZE,SHATTERSURFACE_GLASS, |
|
255,255,255,255,255,255); |
|
|
|
UTIL_Remove(this); |
|
} |
|
|
|
///------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
CWindowPane* CWindowPane::CreateWindowPane( const Vector &vecOrigin, const QAngle &vecAngles ) |
|
{ |
|
CWindowPane *pGlass = (CWindowPane*)CreateEntityByName( "window_pane" ); |
|
if ( !pGlass ) |
|
{ |
|
Msg( "NULL Ent in CreateWindowPane!\n" ); |
|
return NULL; |
|
} |
|
|
|
if ( pGlass->edict() ) |
|
{ |
|
pGlass->SetLocalOrigin( vecOrigin ); |
|
pGlass->SetLocalAngles( vecAngles ); |
|
pGlass->Spawn(); |
|
pGlass->SetTouch(&CWindowPane::PaneTouch); |
|
pGlass->SetLocalAngularVelocity( RandomAngle(-50,50) ); |
|
pGlass->m_nBody = random->RandomInt(0,2); |
|
} |
|
return pGlass; |
|
} |
|
|
|
|
|
//#################################################################################### |
|
// > CBreakableSurface |
|
//#################################################################################### |
|
LINK_ENTITY_TO_CLASS( func_breakable_surf, CBreakableSurface ); |
|
|
|
BEGIN_DATADESC( CBreakableSurface ) |
|
|
|
DEFINE_KEYFIELD( m_nSurfaceType, FIELD_INTEGER, "surfacetype"), |
|
DEFINE_KEYFIELD( m_nFragility, FIELD_INTEGER, "fragility"), |
|
DEFINE_KEYFIELD( m_vLLVertex, FIELD_VECTOR, "lowerleft" ), |
|
DEFINE_KEYFIELD( m_vULVertex, FIELD_VECTOR, "upperleft" ), |
|
DEFINE_KEYFIELD( m_vLRVertex, FIELD_VECTOR, "lowerright" ), |
|
DEFINE_KEYFIELD( m_vURVertex, FIELD_VECTOR, "upperright" ), |
|
DEFINE_KEYFIELD( m_nQuadError, FIELD_INTEGER, "error" ), |
|
|
|
DEFINE_FIELD( m_nNumWide, FIELD_INTEGER), |
|
DEFINE_FIELD( m_nNumHigh, FIELD_INTEGER), |
|
DEFINE_FIELD( m_flPanelWidth, FIELD_FLOAT), |
|
DEFINE_FIELD( m_flPanelHeight, FIELD_FLOAT), |
|
DEFINE_FIELD( m_vNormal, FIELD_VECTOR), |
|
DEFINE_FIELD( m_vCorner, FIELD_POSITION_VECTOR), |
|
DEFINE_FIELD( m_bIsBroken, FIELD_BOOLEAN), |
|
DEFINE_FIELD( m_nNumBrokenPanes, FIELD_INTEGER), |
|
|
|
// UNDONE: How to load save this? Need a way to update |
|
// the client about the state of the window upon load... |
|
// We should use client-side save/load to fix this problem. |
|
DEFINE_AUTO_ARRAY2D( m_flSupport, FIELD_FLOAT), |
|
DEFINE_ARRAY( m_RawPanelBitVec, FIELD_BOOLEAN, MAX_NUM_PANELS*MAX_NUM_PANELS ), |
|
|
|
// Function Pointers |
|
DEFINE_THINKFUNC( BreakThink ), |
|
DEFINE_ENTITYFUNC( SurfaceTouch ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VECTOR, "Shatter", InputShatter ), |
|
|
|
// DEFINE_FIELD( m_ForceUpdateClientData, CBitVec < MAX_PLAYERS > ), // No need to save/restore this, it's just a temporary flag field |
|
END_DATADESC() |
|
|
|
|
|
IMPLEMENT_SERVERCLASS_ST(CBreakableSurface, DT_BreakableSurface) |
|
SendPropInt(SENDINFO(m_nNumWide), 8, SPROP_UNSIGNED), |
|
SendPropInt(SENDINFO(m_nNumHigh), 8, SPROP_UNSIGNED), |
|
SendPropFloat(SENDINFO(m_flPanelWidth), 0, SPROP_NOSCALE), |
|
SendPropFloat(SENDINFO(m_flPanelHeight), 0, SPROP_NOSCALE), |
|
SendPropVector(SENDINFO(m_vNormal), -1, SPROP_COORD), |
|
SendPropVector(SENDINFO(m_vCorner), -1, SPROP_COORD), |
|
SendPropInt(SENDINFO(m_bIsBroken), 1, SPROP_UNSIGNED), |
|
SendPropInt(SENDINFO(m_nSurfaceType), 2, SPROP_UNSIGNED), |
|
SendPropArray3(SENDINFO_ARRAY3(m_RawPanelBitVec), SendPropInt( SENDINFO_ARRAY( m_RawPanelBitVec ), 1, SPROP_UNSIGNED ) ), |
|
END_SEND_TABLE() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBreakableSurface::Precache(void) |
|
{ |
|
UTIL_PrecacheOther( "window_pane" ); |
|
|
|
// Load the edge types and styles for the specific surface type |
|
if (m_nSurfaceType == SHATTERSURFACE_TILE) |
|
{ |
|
PrecacheMaterial( "models/brokentile/tilebroken_03a" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_03b" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_03c" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_03d" ); |
|
|
|
PrecacheMaterial( "models/brokentile/tilebroken_02a" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_02b" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_02c" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_02d" ); |
|
|
|
PrecacheMaterial( "models/brokentile/tilebroken_01a" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_01b" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_01c" ); |
|
PrecacheMaterial( "models/brokentile/tilebroken_01d" ); |
|
} |
|
else |
|
{ |
|
PrecacheMaterial( "models/brokenglass/glassbroken_solid" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_01a" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_01b" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_01c" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_01d" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_02a" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_02b" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_02c" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_02d" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_03a" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_03b" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_03c" ); |
|
PrecacheMaterial( "models/brokenglass/glassbroken_03d" ); |
|
} |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Window has been touched. Break out pieces based on touching |
|
// entity's bounding box |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::SurfaceTouch( CBaseEntity *pOther ) |
|
{ |
|
// If tile only break if object is moving fast |
|
if (m_nSurfaceType == SHATTERSURFACE_TILE) |
|
{ |
|
Vector vVel; |
|
pOther->GetVelocity( &vVel, NULL ); |
|
if (vVel.Length() < 500) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
// Find nearest point on plane for max |
|
Vector vecAbsMins, vecAbsMaxs; |
|
pOther->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); |
|
Vector vToPlane = (vecAbsMaxs - m_vCorner); |
|
float vDistToPlane = DotProduct(m_vNormal,vToPlane); |
|
Vector vTouchPos = vecAbsMaxs + vDistToPlane*m_vNormal; |
|
|
|
float flMinsWidth,flMinsHeight; |
|
PanePos(vTouchPos, &flMinsWidth, &flMinsHeight); |
|
|
|
// Find nearest point on plane for mins |
|
vToPlane = (vecAbsMins - m_vCorner); |
|
vDistToPlane = DotProduct(m_vNormal,vToPlane); |
|
vTouchPos = vecAbsMins + vDistToPlane*m_vNormal; |
|
|
|
float flMaxsWidth,flMaxsHeight; |
|
PanePos(vTouchPos, &flMaxsWidth, &flMaxsHeight); |
|
|
|
int nMinWidth = Floor2Int(MAX(0, MIN(flMinsWidth,flMaxsWidth))); |
|
int nMaxWidth = Ceil2Int(MIN(m_nNumWide,MAX(flMinsWidth,flMaxsWidth))); |
|
|
|
int nMinHeight = Floor2Int(MAX(0, MIN(flMinsHeight,flMaxsHeight))); |
|
int nMaxHeight = Ceil2Int(MIN(m_nNumHigh,MAX(flMinsHeight,flMaxsHeight))); |
|
|
|
Vector vHitVel; |
|
pOther->GetVelocity( &vHitVel, NULL ); |
|
|
|
// Move faster then penetrating object so can see shards |
|
vHitVel *= 5; |
|
|
|
// If I'm not broken yet, break me |
|
if ( !m_bIsBroken ) |
|
{ |
|
Die( pOther, vHitVel ); |
|
} |
|
|
|
for (int height=nMinHeight;height<nMaxHeight;height++) |
|
{ |
|
// Randomly break the one before so it doesn't look square |
|
if (random->RandomInt(0,1)) |
|
{ |
|
ShatterPane(nMinWidth-1, height,vHitVel,pOther->GetLocalOrigin()); |
|
} |
|
for (int width=nMinWidth;width<nMaxWidth;width++) |
|
{ |
|
ShatterPane(width, height,vHitVel,pOther->GetLocalOrigin()); |
|
} |
|
// Randomly break the one after so it doesn't look square |
|
if (random->RandomInt(0,1)) |
|
{ |
|
ShatterPane(nMaxWidth+1, height,vHitVel,pOther->GetLocalOrigin()); |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Only take damage in trace attack |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
int CBreakableSurface::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
if ( !m_bIsBroken && info.GetDamageType() == DMG_CRUSH ) |
|
{ |
|
// physics will kill me now |
|
Die( info.GetAttacker(), info.GetDamageForce() ); |
|
return 0; |
|
} |
|
|
|
if ( m_nSurfaceType == SHATTERSURFACE_GLASS && info.GetDamageType() & DMG_BLAST ) |
|
{ |
|
Vector vecDir = info.GetInflictor()->GetAbsOrigin() - WorldSpaceCenter(); |
|
VectorNormalize( vecDir ); |
|
Die( info.GetAttacker(), vecDir ); |
|
return 0; |
|
} |
|
|
|
// Accept slash damage, too. Manhacks and such. |
|
if ( m_nSurfaceType == SHATTERSURFACE_GLASS && (info.GetDamageType() & DMG_SLASH) ) |
|
{ |
|
Die( info.GetAttacker(), info.GetDamageForce() ); |
|
return 0; |
|
} |
|
|
|
|
|
return 0; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Accepts damage and breaks if health drops below zero. |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Window break stat tracking |
|
//============================================================================= |
|
|
|
// Make sure this pane has not already been shattered |
|
bool bWasBroken = m_bIsBroken; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
// Decrease health |
|
m_iHealth -= info.GetDamage(); |
|
m_OnHealthChanged.Set( m_iHealth, info.GetAttacker(), this ); |
|
|
|
// If I'm not broken yet, break me |
|
if (!m_bIsBroken ) |
|
{ |
|
Vector vSurfDir = ptr->endpos - ptr->startpos; |
|
Die( info.GetAttacker(), vSurfDir ); |
|
} |
|
|
|
if (info.GetDamageType() & (DMG_BULLET | DMG_CLUB)) |
|
{ |
|
// Figure out which panel has taken the damage and break it |
|
float flWidth,flHeight; |
|
PanePos(ptr->endpos,&flWidth,&flHeight); |
|
int nWidth = flWidth; |
|
int nHeight = flHeight; |
|
|
|
if ( ShatterPane(nWidth, nHeight,vecDir*500,ptr->endpos) ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Window break stat tracking |
|
//============================================================================= |
|
|
|
CBasePlayer* pAttacker = ToBasePlayer(info.GetAttacker()); |
|
if ( ( pAttacker ) && ( !bWasBroken ) ) |
|
{ |
|
gamestats->Event_WindowShattered( pAttacker ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
// Do an impact hit |
|
CEffectData data; |
|
|
|
data.m_vNormal = ptr->plane.normal; |
|
data.m_vOrigin = ptr->endpos; |
|
|
|
CPASFilter filter( data.m_vOrigin ); |
|
|
|
// client cannot trace against triggers |
|
filter.SetIgnorePredictionCull( true ); |
|
|
|
te->DispatchEffect( filter, 0.0, data.m_vOrigin, "GlassImpact", data ); |
|
} |
|
|
|
if (m_nSurfaceType == SHATTERSURFACE_GLASS) |
|
{ |
|
// Break nearby panes if damages was near pane edge |
|
float flWRem = flWidth - nWidth; |
|
float flHRem = flHeight - nHeight; |
|
|
|
if (flWRem > 0.8 && nWidth != m_nNumWide-1) |
|
{ |
|
ShatterPane(nWidth+1, nHeight,vecDir*500,ptr->endpos); |
|
} |
|
else if (flWRem < 0.2 && nWidth != 0) |
|
{ |
|
ShatterPane(nWidth-1, nHeight,vecDir*500,ptr->endpos); |
|
} |
|
if (flHRem > 0.8 && nHeight != m_nNumHigh-1) |
|
{ |
|
ShatterPane(nWidth, nHeight+1,vecDir*500,ptr->endpos); |
|
} |
|
else if (flHRem < 0.2 && nHeight != 0) |
|
{ |
|
ShatterPane(nWidth, nHeight-1,vecDir*500,ptr->endpos); |
|
} |
|
|
|
// Occasionally break the pane above me |
|
if (random->RandomInt(0,1)==0) |
|
{ |
|
ShatterPane(nWidth, nHeight+1,vecDir*1000,ptr->endpos); |
|
// Occasionally break the pane above that |
|
if (random->RandomInt(0,1)==0) |
|
{ |
|
ShatterPane(nWidth, nHeight+2,vecDir*1000,ptr->endpos); |
|
} |
|
} |
|
} |
|
} |
|
else if (info.GetDamageType() & (DMG_SONIC | DMG_BLAST)) |
|
{ |
|
// ---------------------------------------- |
|
// If it's tile blow out nearby tiles |
|
// ---------------------------------------- |
|
if (m_nSurfaceType == SHATTERSURFACE_TILE) |
|
{ |
|
// Figure out which panel has taken the damage and break it |
|
float flWidth,flHeight; |
|
if (info.GetAttacker()) |
|
{ |
|
PanePos(info.GetAttacker()->GetAbsOrigin(),&flWidth,&flHeight); |
|
} |
|
else |
|
{ |
|
PanePos(ptr->endpos,&flWidth,&flHeight); |
|
} |
|
int nWidth = flWidth; |
|
int nHeight = flHeight; |
|
|
|
// Blow out a roughly circular patch of tile with some randomness |
|
for (int width =nWidth-4;width<nWidth+4;width++) |
|
{ |
|
for (int height =nHeight-4;height<nHeight+4;height++) |
|
{ |
|
if ((abs(nWidth-width)+abs(nHeight-height))<random->RandomInt(2,5)) |
|
{ |
|
ShatterPane(width, height,vecDir*500,ptr->endpos); |
|
} |
|
} |
|
} |
|
} |
|
// ---------------------------------------- |
|
// If it's glass blow out the whole window |
|
// ---------------------------------------- |
|
else |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [pfreese] Window break stat tracking |
|
//============================================================================= |
|
|
|
CBasePlayer* pAttacker = ToBasePlayer(info.GetAttacker()); |
|
if ( ( pAttacker ) && ( !bWasBroken ) ) |
|
{ |
|
gamestats->Event_WindowShattered( pAttacker ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
float flDot = DotProduct(m_vNormal,vecDir); |
|
|
|
#ifdef CSTRIKE_DLL |
|
float damageMultiplier = info.GetDamage(); |
|
#else |
|
float damageMultiplier = 1.0f; |
|
#endif |
|
|
|
Vector vBlastDir; |
|
if (flDot > 0) |
|
{ |
|
vBlastDir = damageMultiplier * 3000 * m_vNormal; |
|
} |
|
else |
|
{ |
|
vBlastDir = damageMultiplier * -3000 * m_vNormal; |
|
} |
|
|
|
// Has the window already been destroyed? |
|
if (m_nNumBrokenPanes >= m_nNumWide*m_nNumHigh) |
|
{ |
|
return; |
|
} |
|
// --------------------------------------------------------------- |
|
// If less than 10% of my panels have been broken, blow me |
|
// up in one large glass shatter |
|
// --------------------------------------------------------------- |
|
else if ( m_nNumBrokenPanes < 0.1*(m_nNumWide*m_nNumHigh)) |
|
{ |
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
|
|
CreateShards(m_vCorner, vAngles,vBlastDir, ptr->endpos, |
|
m_nNumWide*m_flPanelWidth, m_nNumHigh*m_flPanelHeight, WINDOW_LARGE_SHARD_SIZE); |
|
} |
|
// --------------------------------------------------------------- |
|
// Otherwise break in the longest vertical strips possible |
|
// (to cut down on the network bandwidth) |
|
// --------------------------------------------------------------- |
|
else |
|
{ |
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
|
|
for (int width=0;width<m_nNumWide;width++) |
|
{ |
|
int height; |
|
int nHCount = 0; |
|
for ( height=0;height<m_nNumHigh;height++) |
|
{ |
|
// Keep count of how many panes |
|
if (!IsBroken(width,height)) |
|
{ |
|
nHCount++; |
|
} |
|
// Shatter the strip and start counting again |
|
else if (nHCount > 0) |
|
{ |
|
Vector vBreakPos = m_vCorner + |
|
(width*vWidthDir*m_flPanelWidth) + |
|
((height-nHCount)*vHeightDir*m_flPanelHeight); |
|
|
|
CreateShards(vBreakPos, vAngles, |
|
vBlastDir, ptr->endpos, |
|
m_flPanelWidth, nHCount*m_flPanelHeight, |
|
WINDOW_LARGE_SHARD_SIZE); |
|
|
|
nHCount = 0; |
|
} |
|
} |
|
if (nHCount) |
|
{ |
|
Vector vBreakPos = m_vCorner + |
|
(width*vWidthDir*m_flPanelWidth) + |
|
((height-nHCount)*vHeightDir*m_flPanelHeight); |
|
|
|
CreateShards(vBreakPos, vAngles, |
|
vBlastDir, ptr->endpos, |
|
m_flPanelWidth,nHCount*m_flPanelHeight, |
|
WINDOW_LARGE_SHARD_SIZE); |
|
} |
|
} |
|
} |
|
|
|
BreakAllPanes(); |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Break into panels |
|
// Input : pBreaker - |
|
// vDir - |
|
//----------------------------------------------------------------------------- |
|
void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir ) |
|
{ |
|
if ( m_bIsBroken ) |
|
return; |
|
|
|
// Play a break sound |
|
PhysBreakSound( this, VPhysicsGetObject(), GetAbsOrigin() ); |
|
|
|
m_bIsBroken = true; |
|
m_iHealth = 0.0f; |
|
|
|
if (pBreaker) |
|
{ |
|
m_OnBreak.FireOutput( pBreaker, this ); |
|
} |
|
else |
|
{ |
|
m_OnBreak.FireOutput( this, this ); |
|
} |
|
|
|
float flDir = -1; |
|
|
|
if ( vAttackDir.LengthSqr() > 0.001 ) |
|
{ |
|
float flDot = DotProduct( m_vNormal, vAttackDir ); |
|
if (flDot < 0) |
|
{ |
|
m_vLLVertex += m_vNormal; |
|
m_vLRVertex += m_vNormal; |
|
m_vULVertex += m_vNormal; |
|
m_vURVertex += m_vNormal; |
|
m_vNormal *= -1; |
|
flDir = 1; |
|
} |
|
} |
|
|
|
// ------------------------------------------------------- |
|
// The surface has two sides, when we are killed pick |
|
// the side that the damage came from |
|
// ------------------------------------------------------- |
|
Vector vWidth = m_vLLVertex - m_vLRVertex; |
|
Vector vHeight = m_vLLVertex - m_vULVertex; |
|
CrossProduct( vWidth, vHeight, m_vNormal.GetForModify() ); |
|
VectorNormalize(m_vNormal.GetForModify()); |
|
|
|
// --------------------------------------------------- |
|
// Make sure width and height are oriented correctly |
|
// --------------------------------------------------- |
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
|
|
float flWDist = DotProduct(vWidthDir,vWidth); |
|
if (fabs(flWDist)<0.5) |
|
{ |
|
Vector vSaveHeight = vHeight; |
|
vHeight = vWidth * flDir; |
|
vWidth = vSaveHeight * flDir; |
|
} |
|
|
|
// ------------------------------------------------- |
|
// Find which corner to use |
|
// ------------------------------------------------- |
|
bool bLeft = (DotProduct(vWidthDir,vWidth) < 0); |
|
bool bLower = (DotProduct(vHeightDir,vHeight) < 0); |
|
if (bLeft) |
|
{ |
|
m_vCorner = bLower ? m_vLLVertex : m_vULVertex; |
|
} |
|
else |
|
{ |
|
m_vCorner = bLower ? m_vLRVertex : m_vURVertex; |
|
} |
|
|
|
// ------------------------------------------------- |
|
// Calculate the number of panels |
|
// ------------------------------------------------- |
|
float flWidth = vWidth.Length(); |
|
float flHeight = vHeight.Length(); |
|
m_nNumWide = flWidth / WINDOW_PANEL_SIZE; |
|
m_nNumHigh = flHeight / WINDOW_PANEL_SIZE; |
|
|
|
// If to many panels make panel size bigger |
|
if (m_nNumWide > MAX_NUM_PANELS) m_nNumWide = MAX_NUM_PANELS; |
|
if (m_nNumHigh > MAX_NUM_PANELS) m_nNumHigh = MAX_NUM_PANELS; |
|
|
|
m_flPanelWidth = flWidth / m_nNumWide; |
|
m_flPanelHeight = flHeight / m_nNumHigh; |
|
|
|
// Initialize panels |
|
for (int w=0;w<MAX_NUM_PANELS;w++) |
|
{ |
|
for (int h=0;h<MAX_NUM_PANELS;h++) |
|
{ |
|
SetSupport( w, h, WINDOW_PANE_HEALTHY ); |
|
} |
|
} |
|
|
|
// Reset onground flags for any entity that may |
|
// have been standing on me |
|
ResetOnGroundFlags(); |
|
|
|
VPhysicsDestroyObject(); |
|
AddSolidFlags( FSOLID_TRIGGER ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
SetTouch(&CBreakableSurface::SurfaceTouch); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Set an instaneous force on the rope. |
|
// Input : Force vector. |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::InputShatter( inputdata_t &inputdata ) |
|
{ |
|
Vector vecShatterInfo; |
|
inputdata.value.Vector3D(vecShatterInfo); |
|
|
|
if (!m_bIsBroken) |
|
{ |
|
Die( NULL, vec3_origin ); |
|
} |
|
|
|
// Figure out which panel has taken the damage and break it |
|
float flCenterX = vecShatterInfo.x * m_nNumWide; |
|
float flCenterY = vecShatterInfo.y * m_nNumHigh; |
|
|
|
// Bah: m_flPanelWidth is the width of a single panel |
|
int nMinX = (int)(flCenterX - vecShatterInfo.z / m_flPanelWidth); |
|
int nMaxX = (int)(flCenterX + vecShatterInfo.z / m_flPanelWidth) + 1; |
|
if (nMinX < 0) |
|
nMinX = 0; |
|
if (nMaxX > m_nNumWide) |
|
nMaxX = m_nNumWide; |
|
|
|
int nMinY = (int)(flCenterY - vecShatterInfo.z / m_flPanelHeight); |
|
int nMaxY = (int)(flCenterY + vecShatterInfo.z / m_flPanelHeight) + 1; |
|
|
|
if (nMinY < 0) |
|
nMinY = 0; |
|
if (nMaxY > m_nNumHigh) |
|
nMaxY = m_nNumHigh; |
|
|
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
|
|
// Blow out a roughly circular of tile with some randomness |
|
Vector2D vecActualCenter( flCenterX * m_flPanelWidth, flCenterY * m_flPanelHeight ); |
|
for (int width = nMinX; width < nMaxX; width++) |
|
{ |
|
for (int height = nMinY; height < nMaxY; height++) |
|
{ |
|
Vector2D pt( (width + 0.5f) * m_flPanelWidth, (height + 0.5f) * m_flPanelWidth ); |
|
if ( pt.DistToSqr(vecActualCenter) <= vecShatterInfo.z * vecShatterInfo.z ) |
|
{ |
|
Vector vBreakPos = m_vCorner + |
|
(width*vWidthDir*m_flPanelWidth) + |
|
(height*vHeightDir*m_flPanelHeight); |
|
|
|
ShatterPane( width, height, m_vNormal * 500, vBreakPos ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::Event_Killed( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType ) |
|
{ |
|
return; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
bool CBreakableSurface::IsBroken(int nWidth, int nHeight) |
|
{ |
|
if (nWidth < 0 || nWidth >= m_nNumWide) return true; |
|
if (nHeight < 0 || nHeight >= m_nNumHigh) return true; |
|
|
|
return (m_flSupport[nWidth][nHeight]==WINDOW_PANE_BROKEN); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : w - |
|
// h - |
|
// support - |
|
//----------------------------------------------------------------------------- |
|
void CBreakableSurface::SetSupport( int w, int h, float support ) |
|
{ |
|
m_flSupport[ w ][ h ] = support; |
|
|
|
int offset = w + h * m_nNumWide; |
|
|
|
bool prevval = m_RawPanelBitVec.Get( offset ); |
|
bool curval = prevval; |
|
|
|
if ( support < 0.0f ) |
|
{ |
|
curval = false; |
|
} |
|
else |
|
{ |
|
curval = true; |
|
} |
|
if ( curval != prevval ) |
|
{ |
|
m_RawPanelBitVec.Set( offset, curval ); |
|
m_RawPanelBitVec.GetForModify( offset ); |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
float CBreakableSurface::GetSupport(int nWidth, int nHeight) |
|
{ |
|
return MAX(0,m_flSupport[nWidth][nHeight]); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Return the structural support for this pane. Assumes window |
|
// is upright. Still works for windows parallel to the ground |
|
// but simulation isn't quite as good |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
float CBreakableSurface::RecalcSupport(int nWidth, int nHeight) |
|
{ |
|
// Always has some support. Zero signifies that it has been broken |
|
float flSupport = 0.01; |
|
|
|
// ------------ |
|
// Top support |
|
// ------------ |
|
if (nHeight == m_nNumHigh-1) |
|
{ |
|
flSupport += 1.0; |
|
} |
|
else |
|
{ |
|
flSupport += GetSupport(nWidth,nHeight+1); |
|
} |
|
|
|
// ------------ |
|
// Bottom Support |
|
// ------------ |
|
if (nHeight == 0) |
|
{ |
|
flSupport += 1.25; |
|
} |
|
else |
|
{ |
|
flSupport += 1.25 * GetSupport(nWidth,nHeight-1); |
|
} |
|
|
|
// ------------ |
|
// Left Support |
|
// ------------ |
|
if (nWidth == 0) |
|
{ |
|
flSupport += 1.0; |
|
} |
|
else |
|
{ |
|
flSupport += GetSupport(nWidth-1,nHeight); |
|
} |
|
|
|
// -------------- |
|
// Right Support |
|
// -------------- |
|
if (nWidth == m_nNumWide-1) |
|
{ |
|
flSupport += 1.0; |
|
} |
|
else |
|
{ |
|
flSupport += GetSupport(nWidth+1,nHeight); |
|
} |
|
|
|
// -------------------- |
|
// Bottom Left Support |
|
// -------------------- |
|
if (nHeight == 0 || nWidth == 0) |
|
{ |
|
flSupport += 1.0; |
|
} |
|
else |
|
{ |
|
flSupport += GetSupport(nWidth-1,nHeight-1); |
|
} |
|
|
|
// --------------------- |
|
// Bottom Right Support |
|
// --------------------- |
|
if (nHeight == 0 || nWidth == m_nNumWide-1) |
|
{ |
|
flSupport += 1.0; |
|
} |
|
else |
|
{ |
|
flSupport += GetSupport(nWidth+1,nHeight-1); |
|
} |
|
|
|
// ----------------- |
|
// Top Right Support |
|
// ----------------- |
|
if (nHeight == m_nNumHigh-1 || nWidth == m_nNumWide-1) |
|
{ |
|
flSupport += 0.25; |
|
} |
|
else |
|
{ |
|
flSupport += 0.25 * GetSupport(nWidth+1,nHeight+1); |
|
} |
|
|
|
// ----------------- |
|
// Top Left Support |
|
// ----------------- |
|
if (nHeight == m_nNumHigh-1 || nWidth == 0) |
|
{ |
|
flSupport += 0.25; |
|
} |
|
else |
|
{ |
|
flSupport += 0.25 * GetSupport(nWidth-1,nHeight+1); |
|
} |
|
|
|
return flSupport; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Itterate through the panels and make sure none have become |
|
// unstable |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::BreakThink(void) |
|
{ |
|
// Don't calculate support if I'm tile |
|
if (m_nSurfaceType == SHATTERSURFACE_TILE) |
|
{ |
|
return; |
|
} |
|
|
|
// ----------------------- |
|
// Recalculate all support |
|
// ----------------------- |
|
int w; |
|
float flSupport[MAX_NUM_PANELS][MAX_NUM_PANELS]; |
|
for (w=0;w<m_nNumWide;w++) |
|
{ |
|
for (int h=0;h<m_nNumHigh;h++) |
|
{ |
|
if (!IsBroken(w,h)) |
|
{ |
|
flSupport[w][h] = RecalcSupport(w,h); |
|
} |
|
} |
|
} |
|
|
|
// ---------------------------------------------------- |
|
// Set support and break inadequately supported panes |
|
// ---------------------------------------------------- |
|
float flBreakValue = WINDOW_BREAK_SUPPORT*(m_nFragility/100.0); |
|
for (w=0;w<m_nNumWide;w++) |
|
{ |
|
for (int h=0;h<m_nNumHigh;h++) |
|
{ |
|
if (!IsBroken(w,h)) |
|
{ |
|
SetSupport( w, h, flSupport[w][h]/WINDOW_MAX_SUPPORT ); |
|
if (m_flSupport[w][h] < flBreakValue) |
|
{ |
|
// Occasionaly drop a pane |
|
if (random->RandomInt(0,1)) |
|
{ |
|
DropPane(w,h); |
|
} |
|
// Otherwise just shatter the glass |
|
else |
|
{ |
|
ShatterPane(w,h,vec3_origin,vec3_origin); |
|
} |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Given a 3D position on the window in space return the height and |
|
// width of the position from the window's corner |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::PanePos(const Vector &vPos, float *flWidth, float *flHeight) |
|
{ |
|
Vector vAttackVec = vPos - m_vCorner; |
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
float flWDist = DotProduct(vWidthDir,vAttackVec); |
|
float flHDist = DotProduct(vHeightDir,vAttackVec); |
|
|
|
// Figure out which quadrent I'm in |
|
*flWidth = flWDist/m_flPanelWidth; |
|
*flHeight = flHDist/m_flPanelHeight; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::BreakAllPanes(void) |
|
{ |
|
// Now tell the client all the panes have been broken |
|
for (int width=0;width<m_nNumWide;width++) |
|
{ |
|
for (int height=0;height<m_nNumHigh;height++) |
|
{ |
|
//SetSupport( width, height, WINDOW_PANE_BROKEN ); |
|
|
|
BreakPane(width,height); |
|
} |
|
} |
|
|
|
m_nNumBrokenPanes = m_nNumWide*m_nNumHigh; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Drop a window pane entity |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::BreakPane(int nWidth, int nHeight) |
|
{ |
|
// Check parameter range |
|
if (nWidth < 0 || nWidth >= m_nNumWide) return; |
|
if (nHeight < 0 || nHeight >= m_nNumHigh) return; |
|
|
|
// Count how many panes have been broken or dropped |
|
m_nNumBrokenPanes++; |
|
SetSupport( nWidth, nHeight, WINDOW_PANE_BROKEN ); |
|
|
|
SetThink(&CBreakableSurface::BreakThink); |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Drop a window pane entity |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::DropPane(int nWidth, int nHeight) |
|
{ |
|
// Check parameter range |
|
if (nWidth < 0 || nWidth >= m_nNumWide) return; |
|
if (nHeight < 0 || nHeight >= m_nNumHigh) return; |
|
|
|
if (!IsBroken(nWidth,nHeight)) |
|
{ |
|
BreakPane(nWidth,nHeight); |
|
|
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
|
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
Vector vBreakPos = m_vCorner + |
|
(nWidth*vWidthDir*m_flPanelWidth) + |
|
(nHeight*vHeightDir*m_flPanelHeight); |
|
|
|
CreateShards(vBreakPos, vAngles, vec3_origin, vec3_origin, |
|
WINDOW_PANEL_SIZE, WINDOW_PANEL_SIZE, |
|
WINDOW_SMALL_SHARD_SIZE); |
|
|
|
DamageSound(); |
|
|
|
CWindowPane *pPane = CWindowPane::CreateWindowPane(vBreakPos, vAngles); |
|
if (pPane) |
|
{ |
|
pPane->SetLocalAngularVelocity( RandomAngle(-120,120) ); |
|
} |
|
} |
|
} |
|
|
|
void CBreakableSurface::CreateShards(const Vector &vBreakPos, const QAngle &vAngles, |
|
const Vector &vForce, const Vector &vForcePos, |
|
float flWidth, float flHeight, |
|
int nShardSize) |
|
{ |
|
Vector vAdjustedBreakPos = vBreakPos; |
|
Vector vAdjustedForce = vForce; |
|
int front_r,front_g,front_b; |
|
int back_r,back_g,back_b; |
|
|
|
|
|
// UNDONE: For now hardcode these colors. Later when used by more textures |
|
// we'll automate this process or expose the colors in WC |
|
if (m_nSurfaceType == SHATTERSURFACE_TILE) |
|
{ |
|
// If tile shoot shards back from the shattered surface and offset slightly |
|
// from the surface. |
|
vAdjustedBreakPos -= 8*m_vNormal; |
|
vAdjustedForce = -0.75*vForce; |
|
front_r = 89; |
|
front_g = 120; |
|
front_b = 83; |
|
back_r = 99; |
|
back_g = 76; |
|
back_b = 21; |
|
} |
|
else |
|
{ |
|
front_r = 255; |
|
front_g = 255; |
|
front_b = 255; |
|
back_r = 255; |
|
back_g = 255; |
|
back_b = 255; |
|
} |
|
|
|
CPASFilter filter( vAdjustedBreakPos ); |
|
te->ShatterSurface(filter, 0.0, |
|
&vAdjustedBreakPos, &vAngles, |
|
&vAdjustedForce, &vForcePos, |
|
flWidth, flHeight,WINDOW_SMALL_SHARD_SIZE,m_nSurfaceType, |
|
front_r,front_g,front_b,back_r,back_g,back_b);//4); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Break a panel |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
bool CBreakableSurface::ShatterPane(int nWidth, int nHeight, const Vector &vForce, const Vector &vForcePos) |
|
{ |
|
// Check parameter range |
|
if (nWidth < 0 || nWidth >= m_nNumWide) return false; |
|
if (nHeight < 0 || nHeight >= m_nNumHigh) return false; |
|
|
|
if ( IsBroken(nWidth,nHeight) ) |
|
return false; |
|
|
|
BreakPane(nWidth,nHeight); |
|
|
|
QAngle vAngles; |
|
VectorAngles(-1*m_vNormal,vAngles); |
|
Vector vWidthDir,vHeightDir; |
|
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir); |
|
Vector vBreakPos = m_vCorner + |
|
(nWidth*vWidthDir*m_flPanelWidth) + |
|
(nHeight*vHeightDir*m_flPanelHeight); |
|
|
|
CreateShards(vBreakPos, vAngles,vForce, vForcePos, m_flPanelWidth, m_flPanelHeight, WINDOW_SMALL_SHARD_SIZE); |
|
|
|
DamageSound(); |
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CBreakableSurface::Spawn(void) |
|
{ |
|
BaseClass::Spawn(); |
|
SetCollisionGroup( COLLISION_GROUP_BREAKABLE_GLASS ); |
|
m_bIsBroken = false; |
|
|
|
if (m_nQuadError == QUAD_ERR_MULT_FACES) |
|
{ |
|
Warning("Rejecting func_breakablesurf. Has multiple faces that aren't NODRAW.\n"); |
|
UTIL_Remove(this); |
|
} |
|
else if (m_nQuadError == QUAD_ERR_NOT_QUAD) |
|
{ |
|
Warning("Rejecting func_breakablesurf. Drawn face isn't a quad.\n"); |
|
UTIL_Remove(this); |
|
} |
|
|
|
int materialCount = modelinfo->GetModelMaterialCount( const_cast<model_t*>(GetModel()) ); |
|
if( materialCount != 1 ) |
|
{ |
|
Warning( "Encountered func_breakablesurf that has a material applied to more than one surface!\n" ); |
|
UTIL_Remove(this); |
|
} |
|
|
|
// Get at the first material; even if there are more than one. |
|
IMaterial* pMaterial; |
|
modelinfo->GetModelMaterials( const_cast<model_t*>(GetModel()), 1, &pMaterial ); |
|
|
|
// The material should point to a cracked version of itself |
|
bool foundVar; |
|
IMaterialVar* pCrackName = pMaterial->FindVar( "$crackmaterial", &foundVar, false ); |
|
if (foundVar) |
|
{ |
|
PrecacheMaterial( pCrackName->GetStringValue() ); |
|
} |
|
|
|
// Init the Panel bit vector to all true. ( no panes are broken ) |
|
int bitVecLength = MAX_NUM_PANELS * MAX_NUM_PANELS; |
|
|
|
for( int i=0;i<bitVecLength;i++ ) |
|
{ |
|
m_RawPanelBitVec.Set( i, true ); |
|
} |
|
} |
|
|
|
void CBreakableSurface::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) |
|
{ |
|
if ( !m_bIsBroken ) |
|
{ |
|
int damageType = 0; |
|
string_t iszDamageTable = ( ( m_nSurfaceType == SHATTERSURFACE_GLASS ) ? ( "glass" ) : ( NULL_STRING ) ); |
|
bool bDamageFromHeldObjects = ( ( m_spawnflags & SF_BREAKABLESURF_DAMAGE_FROM_HELD_OBJECTS ) != 0 ); |
|
float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0, false, damageType, iszDamageTable, bDamageFromHeldObjects ); |
|
|
|
if ( damage > 10 ) |
|
{ |
|
// HACKHACK: Reset mass to get correct collision response for the object breaking this |
|
pEvent->pObjects[index]->SetMass( 2.0f ); |
|
|
|
Vector normal, damagePos; |
|
pEvent->pInternalData->GetSurfaceNormal( normal ); |
|
if ( index == 0 ) |
|
{ |
|
normal *= -1.0f; |
|
} |
|
pEvent->pInternalData->GetContactPoint( damagePos ); |
|
int otherIndex = !index; |
|
CBaseEntity *pInflictor = pEvent->pEntities[otherIndex]; |
|
CTakeDamageInfo info( pInflictor, pInflictor, normal, damagePos, damage, damageType ); |
|
PhysCallbackDamage( this, info, *pEvent, index ); |
|
} |
|
else if ( damage > 0 ) |
|
{ |
|
if ( m_spawnflags & SF_BREAKABLESURF_CRACK_DECALS ) |
|
{ |
|
|
|
Vector normal, damagePos; |
|
pEvent->pInternalData->GetSurfaceNormal( normal ); |
|
if ( index == 0 ) |
|
{ |
|
normal *= -1.0f; |
|
} |
|
pEvent->pInternalData->GetContactPoint( damagePos ); |
|
|
|
trace_t tr; |
|
UTIL_TraceLine ( damagePos - normal, damagePos + normal, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); |
|
|
|
// Only place decals and draw effects if we hit something valid |
|
if ( tr.m_pEnt && tr.m_pEnt == this ) |
|
{ |
|
// Build the impact data |
|
CEffectData data; |
|
data.m_vOrigin = tr.endpos; |
|
data.m_vStart = tr.startpos; |
|
data.m_nSurfaceProp = tr.surface.surfaceProps; |
|
data.m_nDamageType = DMG_CLUB; |
|
data.m_nHitBox = tr.hitbox; |
|
data.m_nEntIndex = entindex(); |
|
|
|
// Send it on its way |
|
DispatchEffect( "Impact", data ); |
|
} |
|
} |
|
} |
|
} |
|
BaseClass::VPhysicsCollision( index, pEvent ); |
|
} |
|
|
|
|