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.
240 lines
7.0 KiB
240 lines
7.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: TF version of the stickybolt code. |
|
// I broke off our own version because I didn't want to accidentally break HL2. |
|
// $Workfile: $ |
|
// $Date: $ |
|
// |
|
//----------------------------------------------------------------------------- |
|
// $Log: $ |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "c_basetempentity.h" |
|
#include "fx.h" |
|
#include "decals.h" |
|
#include "iefx.h" |
|
#include "engine/IEngineSound.h" |
|
#include "materialsystem/imaterialvar.h" |
|
#include "IEffects.h" |
|
#include "engine/IEngineTrace.h" |
|
#include "vphysics/constraints.h" |
|
#include "engine/ivmodelinfo.h" |
|
#include "tempent.h" |
|
#include "c_te_legacytempents.h" |
|
#include "engine/ivdebugoverlay.h" |
|
#include "c_te_effect_dispatch.h" |
|
#include "c_tf_player.h" |
|
#include "GameEventListener.h" |
|
#include "tf_shareddefs.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
extern IPhysicsSurfaceProps *physprops; |
|
IPhysicsObject *GetWorldPhysObject( void ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Creates a Bolt in the world and Ragdolls |
|
// For Attached Bolts on players look at hud_bowcharge "arrow_impact" which should be moved here |
|
//----------------------------------------------------------------------------- |
|
void CreateCrossbowBoltTF( const Vector &vecOrigin, const Vector &vecDirection, const int iFlags, unsigned char nColor ) |
|
{ |
|
const char* pszModelName = NULL; |
|
float flDirOffset = 5.0f; |
|
float flScale = 1.0f; |
|
float flLifeTime = 30.0f; |
|
switch ( iFlags ) |
|
{ |
|
case TF_PROJECTILE_STICKY_BALL: |
|
pszModelName = g_pszArrowModels[MODEL_SNOWBALL]; |
|
break; |
|
case TF_PROJECTILE_ARROW: |
|
pszModelName = g_pszArrowModels[MODEL_ARROW_REGULAR]; |
|
break; |
|
case TF_PROJECTILE_BUILDING_REPAIR_BOLT: |
|
pszModelName = g_pszArrowModels[MODEL_ARROW_BUILDING_REPAIR]; |
|
flDirOffset = -2.0f; |
|
break; |
|
case TF_PROJECTILE_FESTIVE_ARROW: |
|
pszModelName = g_pszArrowModels[MODEL_FESTIVE_ARROW_REGULAR]; |
|
break; |
|
case TF_PROJECTILE_HEALING_BOLT: |
|
#ifdef STAGING_ONLY |
|
case TF_PROJECTILE_MILK_BOLT: |
|
#endif |
|
pszModelName = g_pszArrowModels[MODEL_SYRINGE]; |
|
flDirOffset = 0.0f; |
|
flScale = 3.0f; |
|
break; |
|
case TF_PROJECTILE_FESTIVE_HEALING_BOLT: |
|
pszModelName = g_pszArrowModels[MODEL_FESTIVE_HEALING_BOLT]; |
|
flScale = 2.5f; |
|
break; |
|
case TF_PROJECTILE_BREAD_MONSTER: |
|
case TF_PROJECTILE_BREADMONSTER_JARATE: |
|
case TF_PROJECTILE_BREADMONSTER_MADMILK: |
|
pszModelName = g_pszArrowModels[MODEL_BREAD_MONSTER]; |
|
flLifeTime = 8.0f; |
|
flScale = 2.5f; |
|
break; |
|
case TF_PROJECTILE_GRAPPLINGHOOK: |
|
pszModelName = g_pszArrowModels[MODEL_GRAPPLINGHOOK]; |
|
flDirOffset = 0.0f; |
|
flLifeTime = 0.1f; |
|
break; |
|
#ifdef STAGING_ONLY |
|
case TF_PROJECTILE_THROWING_KNIFE: |
|
pszModelName = g_pszArrowModels[MODEL_THROWING_KNIFE]; |
|
break; |
|
case TF_PROJECTILE_SNIPERBULLET: |
|
pszModelName = g_pszArrowModels[MODEL_SYRINGE]; |
|
break; |
|
#endif // STAGING_ONLY |
|
default: |
|
// Unsupported Model |
|
Assert( 0 ); |
|
pszModelName = g_pszArrowModels[MODEL_ARROW_REGULAR]; |
|
return; |
|
} |
|
model_t *pModel = (model_t *)engine->LoadModel( pszModelName ); |
|
|
|
QAngle vAngles; |
|
VectorAngles( vecDirection, vAngles ); |
|
C_LocalTempEntity *arrow = tempents->SpawnTempModel( pModel, vecOrigin - vecDirection * flDirOffset, vAngles, Vector(0, 0, 0 ), flLifeTime, FTENT_NONE ); |
|
|
|
if ( arrow ) |
|
{ |
|
arrow->SetModelScale( flScale ); |
|
arrow->m_nSkin = nColor; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void StickRagdollNowTF( |
|
const Vector &vecOrigin, |
|
const Vector &vecDirection, |
|
const ClientEntityHandle_t &entHandle, |
|
const int boneIndexAttached, |
|
const int physicsBoneIndex, |
|
const int iShooterIndex, |
|
const int iHitGroup, |
|
const int iVictim, |
|
const int iFlags, |
|
unsigned char nColor |
|
) { |
|
Ray_t shotRay; |
|
trace_t tr; |
|
|
|
UTIL_TraceLine( vecOrigin - vecDirection * 16, vecOrigin + vecDirection * 64, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); |
|
if ( tr.surface.flags & SURF_SKY ) |
|
return; |
|
|
|
C_BaseAnimating *pModel = dynamic_cast< C_BaseAnimating * >( entHandle.Get() ); |
|
if ( pModel ) |
|
{ |
|
IPhysicsObject *pPhysicsObject = NULL; |
|
ragdoll_t *pRagdollT = NULL; |
|
if ( pModel->m_pRagdoll ) |
|
{ |
|
CRagdoll *pCRagdoll = dynamic_cast < CRagdoll * > ( pModel->m_pRagdoll ); |
|
if ( pCRagdoll ) |
|
{ |
|
pRagdollT = pCRagdoll->GetRagdoll(); |
|
if ( physicsBoneIndex < pRagdollT->listCount ) |
|
{ |
|
pPhysicsObject = pRagdollT->list[physicsBoneIndex].pObject; |
|
} |
|
} |
|
} |
|
|
|
IPhysicsObject *pReference = GetWorldPhysObject(); |
|
|
|
if ( pReference == NULL || pPhysicsObject == NULL ) |
|
return; |
|
|
|
float frand = (float) rand() / VALVE_RAND_MAX; |
|
Vector adjust = vecDirection*7 + vecDirection * frand * 7; |
|
|
|
Vector vecBonePos; |
|
QAngle boneAngles; |
|
pPhysicsObject->GetPosition( &vecBonePos, &boneAngles ); |
|
|
|
QAngle angles; |
|
pPhysicsObject->SetPosition( vecOrigin-adjust, boneAngles, true ); |
|
|
|
pPhysicsObject->EnableMotion( false ); |
|
|
|
int nNodeIndex = pRagdollT->list[physicsBoneIndex].parentIndex; |
|
|
|
// find largest mass bone |
|
float flTargetMass = 0; |
|
for ( int i = 0; i < pRagdollT->listCount; i++ ) |
|
{ |
|
flTargetMass = MAX(flTargetMass, pRagdollT->list[i].pObject->GetMass() ); |
|
} |
|
|
|
// walk the chain of bones from the pinned bone to the root and set each to the max mass |
|
// This helps transmit the impulses required to stabilize the constraint -- it keeps the body from |
|
// leaving the constraint because of some high mass bone hanging at the other end of the chain |
|
while ( nNodeIndex >= 0 ) |
|
{ |
|
if ( pRagdollT->list[nNodeIndex].pConstraint ) |
|
{ |
|
float flCurrentMass = pRagdollT->list[nNodeIndex].pObject->GetMass(); |
|
flCurrentMass = MAX(flCurrentMass, flTargetMass); |
|
pRagdollT->list[nNodeIndex].pObject->SetMass( flCurrentMass ); |
|
} |
|
nNodeIndex = pRagdollT->list[nNodeIndex].parentIndex; |
|
} |
|
} |
|
|
|
UTIL_ImpactTrace( &tr, 0 ); |
|
|
|
CreateCrossbowBoltTF( vecOrigin, vecDirection, iFlags, nColor ); |
|
|
|
//Achievement stuff. |
|
if ( iHitGroup == HITGROUP_HEAD ) |
|
{ |
|
CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
|
|
if ( pLocalPlayer && pLocalPlayer->entindex() == iShooterIndex ) |
|
{ |
|
CTFPlayer *pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictim ) ); |
|
|
|
if ( pVictim && pVictim->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ) |
|
{ |
|
IGameEvent *event = gameeventmanager->CreateEvent( "player_pinned" ); |
|
|
|
if ( event ) |
|
{ |
|
gameeventmanager->FireEventClientSide( event ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void StickyBoltCallbackTF( const CEffectData &data ) |
|
{ |
|
StickRagdollNowTF( |
|
data.m_vOrigin, |
|
data.m_vNormal, |
|
data.m_hEntity, |
|
data.m_nAttachmentIndex, |
|
data.m_nMaterial, |
|
data.m_nHitBox, |
|
data.m_nDamageType, |
|
data.m_nSurfaceProp, |
|
data.m_fFlags, |
|
data.m_nColor |
|
); |
|
} |
|
|
|
DECLARE_CLIENT_EFFECT( "TFBoltImpact", StickyBoltCallbackTF ); |