Files
HL2Overcharged/game/server/physics_prop_ragdoll.cpp
2025-05-21 21:20:08 +03:00

2934 lines
97 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "baseanimating.h"
#include "studio.h"
#include "physics.h"
#include "physics_saverestore.h"
#include "ai_basenpc.h"
#include "datacache/imdlcache.h"
#include "bone_setup.h"
#include "physics_prop_ragdoll.h"
#include "KeyValues.h"
#include "props.h"
#include "RagdollBoogie.h"
#include "AI_Criteria.h"
#include "ragdoll_shared.h"
#include "hierarchy.h"
#include "te_effect_dispatch.h"
#include "decals.h"
#include "basecombatcharacter.h"
#include "weapon_physcannon.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
const char *GetMassEquivalent(float flMass);
#define RAGDOLL_VISUALIZE 0
//-----------------------------------------------------------------------------
// ThinkContext
//-----------------------------------------------------------------------------
const char *s_pFadeOutContext = "RagdollFadeOutContext";
const char *s_pDebrisContext = "DebrisContext";
const float ATTACHED_DAMPING_SCALE = 50.0f;
//-----------------------------------------------------------------------------
// Spawnflags
//-----------------------------------------------------------------------------
#define SF_RAGDOLLPROP_DEBRIS 0x0004
#define SF_RAGDOLLPROP_USE_LRU_RETIREMENT 0x1000
#define SF_RAGDOLLPROP_ALLOW_DISSOLVE 0x2000 // Allow this prop to be dissolved
#define SF_RAGDOLLPROP_MOTIONDISABLED 0x4000
#define SF_RAGDOLLPROP_ALLOW_STRETCH 0x8000
#define SF_RAGDOLLPROP_STARTASLEEP 0x10000
//-----------------------------------------------------------------------------
// Networking
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( physics_prop_ragdoll, CRagdollProp );
LINK_ENTITY_TO_CLASS( prop_ragdoll, CRagdollProp );
EXTERN_SEND_TABLE(DT_Ragdoll)
IMPLEMENT_SERVERCLASS_ST(CRagdollProp, DT_Ragdoll)
SendPropArray (SendPropQAngles(SENDINFO_ARRAY(m_ragAngles), 13, 0 ), m_ragAngles),
SendPropArray (SendPropVector(SENDINFO_ARRAY(m_ragPos), -1, SPROP_COORD ), m_ragPos),
SendPropEHandle(SENDINFO( m_hUnragdoll ) ),
SendPropFloat(SENDINFO(m_flBlendWeight), 8, SPROP_ROUNDDOWN, 0.0f, 1.0f ),
SendPropInt(SENDINFO(m_nOverlaySequence), 11),
END_SEND_TABLE()
#define DEFINE_RAGDOLL_ELEMENT( i ) \
DEFINE_FIELD( m_ragdoll.list[i].originParentSpace, FIELD_VECTOR ), \
DEFINE_PHYSPTR( m_ragdoll.list[i].pObject ), \
DEFINE_PHYSPTR( m_ragdoll.list[i].pConstraint ), \
DEFINE_FIELD( m_ragdoll.list[i].parentIndex, FIELD_INTEGER )
BEGIN_DATADESC(CRagdollProp)
// m_ragdoll (custom handling)
DEFINE_AUTO_ARRAY ( m_ragdoll.boneIndex, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY ( m_ragPos, FIELD_POSITION_VECTOR ),
DEFINE_AUTO_ARRAY ( m_ragAngles, FIELD_VECTOR ),
DEFINE_KEYFIELD(m_anglesOverrideString, FIELD_STRING, "angleOverride" ),
DEFINE_FIELD( m_lastUpdateTickCount, FIELD_INTEGER ),
DEFINE_FIELD( m_allAsleep, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hDamageEntity, FIELD_EHANDLE ),
DEFINE_FIELD( m_hKiller, FIELD_EHANDLE ),
DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartRagdollBoogie", InputStartRadgollBoogie ),
DEFINE_INPUTFUNC( FIELD_VOID, "EnableMotion", InputEnableMotion ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "FadeAndRemove", InputFadeAndRemove ),
DEFINE_FIELD( m_hUnragdoll, FIELD_EHANDLE ),
DEFINE_FIELD( m_bFirstCollisionAfterLaunch, FIELD_BOOLEAN ),
DEFINE_FIELD(m_flNextDropConstrainted, FIELD_FLOAT),
DEFINE_FIELD(pDroppedWeapon, FIELD_EHANDLE),
DEFINE_FIELD(m_pConstraint, FIELD_CLASSPTR),
DEFINE_FIELD(constrainted, FIELD_BOOLEAN),
DEFINE_FIELD( m_flBlendWeight, FIELD_FLOAT ),
DEFINE_FIELD( m_nOverlaySequence, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_ragdollMins, FIELD_VECTOR ),
DEFINE_AUTO_ARRAY( m_ragdollMaxs, FIELD_VECTOR ),
// Physics Influence
DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
DEFINE_FIELD( m_flFadeOutStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flFadeTime, FIELD_FLOAT),
DEFINE_FIELD( m_strSourceClassName, FIELD_STRING ),
DEFINE_FIELD( m_bHasBeenPhysgunned, FIELD_BOOLEAN ),
// think functions
DEFINE_THINKFUNC( SetDebrisThink ),
DEFINE_THINKFUNC( ClearFlagsThink ),
DEFINE_THINKFUNC( FadeOutThink ),
DEFINE_FIELD( m_ragdoll.listCount, FIELD_INTEGER ),
DEFINE_FIELD( m_ragdoll.allowStretch, FIELD_BOOLEAN ),
DEFINE_PHYSPTR( m_ragdoll.pGroup ),
DEFINE_FIELD( m_flDefaultFadeScale, FIELD_FLOAT ),
//DEFINE_RAGDOLL_ELEMENT( 0 ),
DEFINE_RAGDOLL_ELEMENT( 1 ),
DEFINE_RAGDOLL_ELEMENT( 2 ),
DEFINE_RAGDOLL_ELEMENT( 3 ),
DEFINE_RAGDOLL_ELEMENT( 4 ),
DEFINE_RAGDOLL_ELEMENT( 5 ),
DEFINE_RAGDOLL_ELEMENT( 6 ),
DEFINE_RAGDOLL_ELEMENT( 7 ),
DEFINE_RAGDOLL_ELEMENT( 8 ),
DEFINE_RAGDOLL_ELEMENT( 9 ),
DEFINE_RAGDOLL_ELEMENT( 10 ),
DEFINE_RAGDOLL_ELEMENT( 11 ),
DEFINE_RAGDOLL_ELEMENT( 12 ),
DEFINE_RAGDOLL_ELEMENT( 13 ),
DEFINE_RAGDOLL_ELEMENT( 14 ),
DEFINE_RAGDOLL_ELEMENT( 15 ),
DEFINE_RAGDOLL_ELEMENT( 16 ),
DEFINE_RAGDOLL_ELEMENT( 17 ),
DEFINE_RAGDOLL_ELEMENT( 18 ),
DEFINE_RAGDOLL_ELEMENT( 19 ),
DEFINE_RAGDOLL_ELEMENT( 20 ),
DEFINE_RAGDOLL_ELEMENT( 21 ),
DEFINE_RAGDOLL_ELEMENT( 22 ),
DEFINE_RAGDOLL_ELEMENT( 23 ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Disable auto fading under dx7 or when level fades are specified
//-----------------------------------------------------------------------------
void CRagdollProp::DisableAutoFade()
{
m_flFadeScale = 0;
m_flDefaultFadeScale = 0;
}
void CRagdollProp::CreateBloodStream(trace_t *ptr)
{
/*const surfacedata_t *ptexdata = physprops->GetSurfaceData(ptr->surface.surfaceProps);
if ((char)ptexdata->game.material == CHAR_TEX_ALIENFLESH)
{
BloodColor = 1;
}
if ((char)ptexdata->game.material == CHAR_TEX_BLOODYFLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
else
BloodColor = 0;
}
if ((char)ptexdata->game.material == CHAR_TEX_FLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
else
BloodColor = 0;
}
if ((char)ptexdata->game.material == CHAR_TEX_ANTLION)
{
BloodColor = 2;
}
if (((char)ptexdata->game.material == CHAR_TEX_CONCRETE) || ((char)ptexdata->game.material == CHAR_TEX_DIRT)
|| ((char)ptexdata->game.material == CHAR_TEX_EGGSHELL) || ((char)ptexdata->game.material == CHAR_TEX_GRATE)
|| ((char)ptexdata->game.material == CHAR_TEX_CLIP) || ((char)ptexdata->game.material == CHAR_TEX_PLASTIC)
|| ((char)ptexdata->game.material == CHAR_TEX_METAL) || ((char)ptexdata->game.material == CHAR_TEX_SAND)
|| ((char)ptexdata->game.material == CHAR_TEX_FOLIAGE) || ((char)ptexdata->game.material == CHAR_TEX_COMPUTER)
|| ((char)ptexdata->game.material == CHAR_TEX_SLOSH) || ((char)ptexdata->game.material == CHAR_TEX_TILE)
|| ((char)ptexdata->game.material == CHAR_TEX_VENT) || ((char)ptexdata->game.material == CHAR_TEX_WOOD)
|| ((char)ptexdata->game.material == CHAR_TEX_GLASS) || ((char)ptexdata->game.material == CHAR_TEX_WARPSHIELD))
{
BloodColor = -1;
}*/
if (pBlood == NULL)
{
QAngle orig;
//pBlood = (CBloodEmitter*)CreateEntityByName("blood_emitter");
VectorAngles(ptr->startpos - ptr->endpos, orig);
pBlood = static_cast<CBloodEmitter*>(CBaseEntity::Create("blood_emitter", ptr->endpos, GetAbsAngles(), NULL));
pBlood->normal = ptr->plane.normal;
pBlood->spawn = ptr->endpos;
pBlood->direction = -direction * 7.0f;
pBlood->timeThink = 0.05f;//Approach(0.5f, 0.05f, 10);
pBlood->composeColor = 0;// BloodColor;
pBlood->Spawn();
/*CBaseEntity *pBlood = NULL;
pBlood = CBaseEntity::Create("item_empty_mag", ptr->endpos, GetAbsAngles(), this);
pBlood->PrecacheModel("models/props_combine/w_bunker_gun01.mdl");
pBlood->SetModel("models/props_combine/w_bunker_gun01.mdl");
pBlood->SetSolid(SOLID_VPHYSICS);
pBlood->SetCollisionGroup(COLLISION_GROUP_DEBRIS);
pBlood->SetMoveType(MOVETYPE_VPHYSICS);
pBlood->Spawn();*/
IPhysicsObject *pMyPhysics = VPhysicsGetObject();
IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
/*int count = */VPhysicsGetObjectList(pList, ARRAYSIZE(pList));
pMyPhysics = pList[ptr->hitbox];
//IPhysicsObject *pPhysics = pBlood->VPhysicsGetObject();
if (pBlood->thisPhysics)
{
//IPhysicsConstraint *m_pConstraint;
constraint_fixedparams_t constraintParams;
constraintParams.Defaults();
constraintParams.InitWithCurrentObjectState(pBlood->thisPhysics, pMyPhysics);
constraintParams.constraint.Defaults();
constraintParams.constraint.forceLimit = ImpulseScale(pBlood->thisPhysics->GetMass(), 200);
constraintParams.constraint.torqueLimit = ImpulseScale(pBlood->thisPhysics->GetMass(), 800);
m_pConstraint = physenv->CreateFixedConstraint(pBlood->thisPhysics, pMyPhysics, NULL, constraintParams);
m_pConstraint->SetGameData((void *)this);
}
//pBlood->SetLocalOrigin(originPos);
//VectorAngles(-direction, orig);
//pBlood->SetLocalAngles(orig);
//pBlood->SetLocalAngles(GetLocalAngles());
//pBlood->SetAbsAngles(GetAbsAngles());
//pBlood->SetParent(this);
//pBlood->SetParent(this);
//pBlood->SetOwnerEntity(this);
}
//pBlood = NULL;
//UTIL_Remove(pBlood);
/*pBlood = (CBaseAnimating*)CreateEntityByName("prop_dynamic");//OverCharged Blood
if (pBlood)
{
QAngle orig;
VectorAngles(-direction, orig);
PrecacheModel("models/alyx_emptool_prop.mdl");
pBlood->SetModel("models/alyx_emptool_prop.mdl");
pBlood->SetName(AllocPooledString("Alyx_Emptool"));
//int iAttachment = LookupAttachment("Emp_Holster");
pBlood->SetSolid(SOLID_NONE);
pBlood->SetAbsOrigin(originPos);
pBlood->SetAbsAngles(orig);
pBlood->AddSpawnFlags(SF_DYNAMICPROP_NO_VPHYSICS);
pBlood->AddEffects(EF_PARENT_ANIMATES);
pBlood->SetParent(this);
pBlood->SetOwnerEntity(this);
pBlood->Spawn();
}*/
}
void CRagdollProp::Spawn( void )
{
// Starts out as the default fade scale value
m_flNextDropConstrainted = 0.f;
m_flDefaultFadeScale = m_flFadeScale;
constrainted = false;
// NOTE: If this fires, then the assert or the datadesc is wrong! (see DEFINE_RAGDOLL_ELEMENT above)
Assert( RAGDOLL_MAX_ELEMENTS == 24 );
Precache();
SetModel( STRING( GetModelName() ) );
CStudioHdr *pStudioHdr = GetModelPtr( );
if ( pStudioHdr->flags() & STUDIOHDR_FLAGS_NO_FORCED_FADE )
{
DisableAutoFade();
}
else
{
m_flFadeScale = m_flDefaultFadeScale;
}
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
BaseClass::SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // FIXME: shouldn't this be a subset of the bones
// this is useless info after the initial conditions are set
SetAbsAngles( vec3_angle );
int collisionGroup = (m_spawnflags & SF_RAGDOLLPROP_DEBRIS) ? COLLISION_GROUP_DEBRIS : COLLISION_GROUP_NONE;
bool bWake = (m_spawnflags & SF_RAGDOLLPROP_STARTASLEEP) ? false : true;
InitRagdoll( vec3_origin, 0, vec3_origin, pBoneToWorld, pBoneToWorld, 0, collisionGroup, true, bWake );
m_lastUpdateTickCount = 0;
m_flBlendWeight = 0.0f;
m_nOverlaySequence = -1;
health = 100;
// Unless specified, do not allow this to be dissolved
if ( HasSpawnFlags( SF_RAGDOLLPROP_ALLOW_DISSOLVE ) == false )
{
AddEFlags( EFL_NO_DISSOLVE );
}
if ( HasSpawnFlags(SF_RAGDOLLPROP_MOTIONDISABLED) )
{
DisableMotion();
}
if( m_bStartDisabled )
{
AddEffects( EF_NODRAW );
}
}
void CRagdollProp::SetSourceClassName( const char *pClassname )
{
m_strSourceClassName = MAKE_STRING( pClassname );
}
void CRagdollProp::OnSave( IEntitySaveUtils *pUtils )
{
if ( !m_ragdoll.listCount )
return;
// Don't save ragdoll element 0, base class saves the pointer in
// m_pPhysicsObject
Assert( m_ragdoll.list[0].parentIndex == -1 );
Assert( m_ragdoll.list[0].pConstraint == NULL );
Assert( m_ragdoll.list[0].originParentSpace == vec3_origin );
Assert( m_ragdoll.list[0].pObject != NULL );
VPhysicsSetObject( NULL ); // squelch a warning message
VPhysicsSetObject( m_ragdoll.list[0].pObject ); // make sure object zero is saved by CBaseEntity
BaseClass::OnSave( pUtils );
}
void CRagdollProp::OnRestore()
{
// rebuild element 0 since it isn't saved
// NOTE: This breaks the rules - the pointer needs to get fixed in Restore()
m_ragdoll.list[0].pObject = VPhysicsGetObject();
m_ragdoll.list[0].parentIndex = -1;
m_ragdoll.list[0].originParentSpace.Init();
BaseClass::OnRestore();
if ( !m_ragdoll.listCount )
return;
// JAY: Reset collision relationships
RagdollSetupCollisions( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
VPhysicsUpdate( VPhysicsGetObject() );
}
void CRagdollProp::CalcRagdollSize( void )
{
CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
CollisionProp()->RemoveSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
}
void CRagdollProp::UpdateOnRemove( void )
{
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
if ( m_ragdoll.list[i].pObject )
{
g_pPhysSaveRestoreManager->ForgetModel( m_ragdoll.list[i].pObject );
}
}
//NDebugOverlay::Box(m_ragdoll.list[1].originParentSpace, Vector(-2, -2, -2), Vector(2, 2, 2), 255, 255, 0, 20, .1);
// Set to null so that the destructor's call to DestroyObject won't destroy
// m_pObjects[ 0 ] twice since that's the physics object for the prop
VPhysicsSetObject( NULL );
RagdollDestroy( m_ragdoll );
// Chain to base after doing our own cleanup to mimic
// destructor unwind order
BaseClass::UpdateOnRemove();
}
CRagdollProp::CRagdollProp( void )
{
m_strSourceClassName = NULL_STRING;
m_anglesOverrideString = NULL_STRING;
m_ragdoll.listCount = 0;
Assert( (1<<RAGDOLL_INDEX_BITS) >=RAGDOLL_MAX_ELEMENTS );
m_allAsleep = false;
m_flFadeScale = 1;
m_flDefaultFadeScale = 1;
health = 100;
BloodColor = -1;
}
CRagdollProp::~CRagdollProp( void )
{
}
void CRagdollProp::Precache( void )
{
PrecacheModel( STRING( GetModelName() ) );
BaseClass::Precache();
}
int CRagdollProp::ObjectCaps()
{
return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::InitRagdollAnimation()
{
m_flAnimTime = gpGlobals->curtime;
m_flPlaybackRate = 0.0;
SetCycle( 0 );
// put into ACT_DIERAGDOLL if it exists, otherwise use sequence 0
int nSequence = SelectWeightedSequence( ACT_DIERAGDOLL );
if ( nSequence < 0 )
{
ResetSequence( 0 );
}
else
{
ResetSequence( nSequence );
}
}
//-----------------------------------------------------------------------------
// Response system stuff
//-----------------------------------------------------------------------------
IResponseSystem *CRagdollProp::GetResponseSystem()
{
extern IResponseSystem *g_pResponseSystem;
// Just use the general NPC response system; we often come from NPCs after all
return g_pResponseSystem;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::ModifyOrAppendCriteria( AI_CriteriaSet& set )
{
BaseClass::ModifyOrAppendCriteria( set );
if ( m_strSourceClassName != NULL_STRING )
{
set.RemoveCriteria( "classname" );
set.AppendCriteria( "classname", STRING(m_strSourceClassName) );
set.AppendCriteria( "ragdoll", "1" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
{
m_hPhysicsAttacker = pPhysGunUser;
m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
// Clear out the classname if we've been physgunned before
// so that the screams, etc. don't happen. Simulate that the first
// major punt or throw has been enough to kill him.
if ( m_bHasBeenPhysgunned )
{
m_strSourceClassName = NULL_STRING;
}
m_bHasBeenPhysgunned = true;
if (/*HasPhysgunInteraction("onpickup", "boogie") && */PlayerHasMegaPhysCannon())
{
if ( reason == PUNTED_BY_CANNON )
{
CRagdollBoogie::Create( this, random->RandomInt(50, 150), gpGlobals->curtime, random->RandomFloat(0.3f, 1.8f), SF_RAGDOLL_BOOGIE_ELECTRICAL );
}
else
{
CRagdollBoogie::Create(this, random->RandomInt(50, 150), gpGlobals->curtime, random->RandomFloat(0.3f, 1.2f), 0.0f);
}
}
if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
{
s_RagdollLRU.MoveToTopOfLRU( this );
}
if ( !HasSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON ) )
return;
ragdoll_t *pRagdollPhys = GetRagdoll( );
for ( int j = 0; j < pRagdollPhys->listCount; ++j )
{
pRagdollPhys->list[j].pObject->Wake();
pRagdollPhys->list[j].pObject->EnableMotion( true );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
{
CDefaultPlayerPickupVPhysics::OnPhysGunDrop( pPhysGunUser, Reason );
m_hPhysicsAttacker = pPhysGunUser;
m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
if (/*HasPhysgunInteraction("onpickup", "boogie") && */PlayerHasMegaPhysCannon())
{
CRagdollBoogie::Create(this, random->RandomInt(50, 150), gpGlobals->curtime, random->RandomFloat(0.3f, 1.8f), SF_RAGDOLL_BOOGIE_ELECTRICAL);
}
if ( HasSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ) )
{
s_RagdollLRU.MoveToTopOfLRU( this );
}
// Make sure it's interactive debris for at most 5 seconds
if ( GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
{
SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
}
if ( Reason != LAUNCHED_BY_CANNON )
return;
if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
{
Vector vecAverageCenter( 0, 0, 0 );
// Get the average position, apply forces to produce a spin
int j;
ragdoll_t *pRagdollPhys = GetRagdoll( );
for ( j = 0; j < pRagdollPhys->listCount; ++j )
{
Vector vecCenter;
pRagdollPhys->list[j].pObject->GetPosition( &vecCenter, NULL );
vecAverageCenter += vecCenter;
}
vecAverageCenter /= pRagdollPhys->listCount;
Vector vecZAxis( 0, 0, 1 );
for ( j = 0; j < pRagdollPhys->listCount; ++j )
{
Vector vecDelta;
pRagdollPhys->list[j].pObject->GetPosition( &vecDelta, NULL );
vecDelta -= vecAverageCenter;
Vector vecDir;
CrossProduct( vecZAxis, vecDelta, vecDir );
vecDir *= 100;
pRagdollPhys->list[j].pObject->AddVelocity( &vecDir, NULL );
}
}
PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
m_bFirstCollisionAfterLaunch = true;
}
//-----------------------------------------------------------------------------
// Physics attacker
//-----------------------------------------------------------------------------
CBasePlayer *CRagdollProp::HasPhysicsAttacker( float dt )
{
if (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime)
{
return m_hPhysicsAttacker;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
BaseClass::VPhysicsCollision( index, pEvent );
CBaseEntity *pHitEntity = pEvent->pEntities[!index];
if ( pHitEntity == this )
return;
// Don't take physics damage from whoever's holding him with the physcannon.
if ( VPhysicsGetObject() && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
{
if ( pHitEntity && (pHitEntity == HasPhysicsAttacker( FLT_MAX )) )
return;
}
// Don't bother taking damage from the physics attacker
if ( pHitEntity && HasPhysicsAttacker( 0.5f ) == pHitEntity )
return;
if( m_bFirstCollisionAfterLaunch )
{
HandleFirstCollisionInteractions( index, pEvent );
}
if ( m_takedamage != DAMAGE_NO )
{
int damageType = 0;
float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0f, true, damageType );
if ( damage > 0 )
{
// Take extra damage after we're punted by the physcannon
if ( m_bFirstCollisionAfterLaunch )
{
damage *= 10;
}
CBaseEntity *pHitEntity = pEvent->pEntities[!index];
if ( !pHitEntity )
{
// hit world
pHitEntity = GetContainingEntity( INDEXENT(0) );
}
Vector damagePos;
pEvent->pInternalData->GetContactPoint( damagePos );
Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
if ( damageForce == vec3_origin )
{
// This can happen if this entity is motion disabled, and can't move.
// Use the velocity of the entity that hit us instead.
damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
}
// FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
}
}
if (cvar->FindVar("oc_ragdoll_show_blood")->GetInt() == 1 && BloodColor >= 0)
{
trace_t tr;
Vector damagePos;
pEvent->pInternalData->GetContactPoint(damagePos);
pEvent->postVelocity->DistTo(tr.endpos);
pEvent->pInternalData->GetSurfaceNormal(tr.endpos);
IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
int count = VPhysicsGetObjectList(pList, ARRAYSIZE(pList));
for (int i = 0; i < count; i++)
{
if (Q_strstr(STRING(GetModelName()), "models/props_lab/"))
{
BloodColor = -1;
continue;
}
int material = pList[i]->GetMaterialIndex();
const surfacedata_t *pSurfaceData = physprops->GetSurfaceData(material);
// Is flesh ?, don't allow pickup
if (pSurfaceData->game.material == CHAR_TEX_ALIENFLESH)
{
BloodColor = 1;
}
if (pSurfaceData->game.material == CHAR_TEX_BLOODYFLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
/*else
BloodColor = 0;*/
}
if (pSurfaceData->game.material == CHAR_TEX_FLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
/*else
BloodColor = 0;*/
}
if (pSurfaceData->game.material == CHAR_TEX_ANTLION)
{
BloodColor = 2;
}
if ((pSurfaceData->game.material == CHAR_TEX_CONCRETE) || (pSurfaceData->game.material == CHAR_TEX_DIRT)
|| (pSurfaceData->game.material == CHAR_TEX_EGGSHELL) || (pSurfaceData->game.material == CHAR_TEX_GRATE)
|| (pSurfaceData->game.material == CHAR_TEX_CLIP) || (pSurfaceData->game.material == CHAR_TEX_PLASTIC)
|| (pSurfaceData->game.material == CHAR_TEX_METAL) || (pSurfaceData->game.material == CHAR_TEX_SAND)
|| (pSurfaceData->game.material == CHAR_TEX_FOLIAGE) || (pSurfaceData->game.material == CHAR_TEX_COMPUTER)
|| (pSurfaceData->game.material == CHAR_TEX_SLOSH) || (pSurfaceData->game.material == CHAR_TEX_TILE)
|| (pSurfaceData->game.material == CHAR_TEX_VENT) || (pSurfaceData->game.material == CHAR_TEX_WOOD)
|| (pSurfaceData->game.material == CHAR_TEX_GLASS) || (pSurfaceData->game.material == CHAR_TEX_WARPSHIELD))
{
BloodColor = -1;
}
}
if (cvar->FindVar("oc_blood_displays_on_all_normals")->GetInt() == 0)
{
UTIL_TraceLine(damagePos, tr.endpos + damagePos, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &tr);
}
else if (cvar->FindVar("oc_blood_displays_on_all_normals")->GetInt() == 1)
{
UTIL_TraceLine(damagePos, tr.endpos + damagePos, MASK_ALL, this, COLLISION_GROUP_INTERACTIVE, &tr);//COLLISION_GROUP_INTERACTIVE
}
int RandBlood = RandomInt(1, 10);
if (RandBlood == 5)
{
SpawnBlood(damagePos, tr.endpos, BloodColor, 100);
UTIL_BloodDecalTrace(&tr, BloodColor);
}
}
if ( m_bFirstCollisionAfterLaunch )
{
// Setup the think function to remove the flags
SetThink( &CRagdollProp::ClearFlagsThink );
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CRagdollProp::HasPhysgunInteraction( const char *pszKeyName, const char *pszValue )
{
KeyValues *modelKeyValues = new KeyValues("");
if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
{
KeyValues *pkvPropData = modelKeyValues->FindKey("physgun_interactions");
if ( pkvPropData )
{
char const *pszBase = pkvPropData->GetString( pszKeyName );
if ( pszBase && pszBase[0] && !stricmp( pszBase, pszValue ) )
{
modelKeyValues->deleteThis();
return true;
}
}
}
modelKeyValues->deleteThis();
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CRagdollProp::HandleFirstCollisionInteractions( int index, gamevcollisionevent_t *pEvent )
{
IPhysicsObject *pObj = VPhysicsGetObject();
if ( !pObj)
return;
if( HasPhysgunInteraction( "onfirstimpact", "break" ) )
{
// Looks like it's best to break by having the object damage itself.
CTakeDamageInfo info;
info.SetDamage( m_iHealth );
info.SetAttacker( this );
info.SetInflictor( this );
info.SetDamageType( DMG_GENERIC );
Vector vecPosition;
Vector vecVelocity;
VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
VPhysicsGetObject()->GetPosition( &vecPosition, NULL );
info.SetDamageForce( vecVelocity );
info.SetDamagePosition( vecPosition );
TakeDamage( info );
return;
}
if( HasPhysgunInteraction( "onfirstimpact", "paintsplat" ) )
{
IPhysicsObject *pObj = VPhysicsGetObject();
Vector vecPos;
pObj->GetPosition( &vecPos, NULL );
trace_t tr;
UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
switch( random->RandomInt( 1, 3 ) )
{
case 1:
UTIL_DecalTrace( &tr, "PaintSplatBlue" );
break;
case 2:
UTIL_DecalTrace( &tr, "PaintSplatGreen" );
break;
case 3:
UTIL_DecalTrace( &tr, "PaintSplatPink" );
break;
}
}
bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" );
if( bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) )
{
IPhysicsObject *pObj = VPhysicsGetObject();
Vector vecPos;
pObj->GetPosition( &vecPos, NULL );
trace_t tr;
UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
/*UTIL_BloodDecalTrace( &tr, bAlienBloodSplat ? BLOOD_COLOR_GREEN : BLOOD_COLOR_RED );
QAngle vecAngles;
VectorAngles(-tr.plane.normal, vecAngles);
DispatchParticleEffect("blood_impact_red_01", tr.plane.normal, vecAngles);
debugoverlay->AddLineOverlay(vecPos, vecPos + pEvent->preVelocity[0] * 1.5, 0, 255, 0, false, 0);*/
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollProp::ClearFlagsThink( void )
{
PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
m_bFirstCollisionAfterLaunch = false;
SetThink( NULL );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
AngularImpulse CRagdollProp::PhysGunLaunchAngularImpulse()
{
if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) )
{
// Don't add in random angular impulse if this object is supposed to spin in a specific way.
AngularImpulse ang( 0, 0, 0 );
return ang;
}
return CDefaultPlayerPickupVPhysics::PhysGunLaunchAngularImpulse();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : activity -
//-----------------------------------------------------------------------------
void CRagdollProp::SetOverlaySequence( Activity activity )
{
int seq = SelectWeightedSequence( activity );
if ( seq < 0 )
{
m_nOverlaySequence = -1;
}
else
{
m_nOverlaySequence = seq;
}
}
void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll )
{
/*if (m_ragdoll.listCount == NULL || physenv == NULL)
{
Error("Current model %s does't have ragdoll definition \n", this->GetModelName());
return;
}*/
SetCollisionGroup( collisionGroup );
// Make sure it's interactive debris for at most 5 seconds
if ( collisionGroup == COLLISION_GROUP_INTERACTIVE_DEBRIS )
{
SetContextThink( &CRagdollProp::SetDebrisThink, gpGlobals->curtime + 5, s_pDebrisContext );
}
SetMoveType( MOVETYPE_VPHYSICS );
SetSolid( SOLID_VPHYSICS );
AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
m_takedamage = DAMAGE_EVENTS_ONLY;
ragdollparams_t params;
params.pGameData = static_cast<void *>( static_cast<CBaseEntity *>(this) );
params.modelIndex = GetModelIndex();
params.pCollide = modelinfo->GetVCollide( params.modelIndex );
params.pStudioHdr = GetModelPtr();
params.forceVector = forceVector;
params.forceBoneIndex = forceBone;
params.forcePosition = forcePos;
params.pCurrentBones = pBoneToWorld;
params.jointFrictionScale = 1.0;
params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH);
params.fixedConstraints = false;
RagdollCreate( m_ragdoll, params, physenv );
RagdollApplyAnimationAsVelocity( m_ragdoll, pPrevBones, pBoneToWorld, dt );
if ( m_anglesOverrideString != NULL_STRING && Q_strlen(m_anglesOverrideString.ToCStr()) > 0 )
{
char szToken[2048];
const char *pStr = nexttoken(szToken, STRING(m_anglesOverrideString), ',');
// anglesOverride is index,angles,index,angles (e.g. "1, 22.5 123.0 0.0, 2, 0 0 0, 3, 0 0 180.0")
while ( szToken[0] != 0 )
{
int objectIndex = atoi(szToken);
// sanity check to make sure this token is an integer
Assert( atof(szToken) == ((float)objectIndex) );
pStr = nexttoken(szToken, pStr, ',');
Assert( szToken[0] );
if ( objectIndex >= m_ragdoll.listCount )
{
Warning("Bad ragdoll pose in entity %s, model (%s) at %s, model changed?\n", GetDebugName(), GetModelName().ToCStr(), VecToString(GetAbsOrigin()) );
}
else if ( szToken[0] != 0 )
{
QAngle angles;
Assert( objectIndex >= 0 && objectIndex < RAGDOLL_MAX_ELEMENTS );
UTIL_StringToVector( angles.Base(), szToken );
int boneIndex = m_ragdoll.boneIndex[objectIndex];
AngleMatrix( angles, pBoneToWorld[boneIndex] );
const ragdollelement_t &element = m_ragdoll.list[objectIndex];
Vector out;
if ( element.parentIndex >= 0 )
{
int parentBoneIndex = m_ragdoll.boneIndex[element.parentIndex];
VectorTransform( element.originParentSpace, pBoneToWorld[parentBoneIndex], out );
}
else
{
out = GetAbsOrigin();
}
MatrixSetColumn( out, 3, pBoneToWorld[boneIndex] );
element.pObject->SetPositionMatrix( pBoneToWorld[boneIndex], true );
}
pStr = nexttoken(szToken, pStr, ',');
}
}
if ( activateRagdoll )
{
MEM_ALLOC_CREDIT();
RagdollActivate( m_ragdoll, params.pCollide, GetModelIndex(), bWakeRagdoll );
}
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
g_pPhysSaveRestoreManager->AssociateModel( m_ragdoll.list[i].pObject, GetModelIndex() );
physcollision->CollideGetAABB( &m_ragdollMins[i], &m_ragdollMaxs[i], m_ragdoll.list[i].pObject->GetCollide(), vec3_origin, vec3_angle );
}
VPhysicsSetObject( m_ragdoll.list[0].pObject );
CalcRagdollSize();
/*Vector bonePos;
QAngle boneAng;
GetBonePosition(forceBone, bonePos, boneAng);
trace_t ptr;
UTIL_TraceLine(bonePos, bonePos, MASK_SHOT, this, COLLISION_GROUP_NONE, &ptr);
//if (cvar->FindVar("oc_ragdoll_enable_bloodstream")->GetInt())//OverCharged Blood
{
DevMsg("forceVector forcePos\n");
Vector originPos0 = forcePos;
Vector direction0 = -forceVector;
//trace_t *pNewTrace = const_cast<trace_t*>(&ptr);
WorldToEntitySpace(originPos0, &originPos);
WorldToEntitySpace(direction0, &direction);
CreateBloodStream(&ptr);
}*/
}
/*static ConVar oc_ragdoll_adjust_weapon_offsets("oc_ragdoll_adjust_weapon_offsets", "0");
static ConVar oc_ragdoll_adjust_weapon_position_x("oc_ragdoll_adjust_weapon_position_x", "0");
static ConVar oc_ragdoll_adjust_weapon_position_y("oc_ragdoll_adjust_weapon_position_y", "0");
static ConVar oc_ragdoll_adjust_weapon_position_z("oc_ragdoll_adjust_weapon_position_z", "0");
static ConVar oc_ragdoll_adjust_weapon_angle_x("oc_ragdoll_adjust_weapon_angle_x", "180");
static ConVar oc_ragdoll_adjust_weapon_angle_y("oc_ragdoll_adjust_weapon_angle_y", "0");//90
static ConVar oc_ragdoll_adjust_weapon_angle_z("oc_ragdoll_adjust_weapon_angle_z", "0");//-90*/
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
#include <map>
// Comparator function to sort pairs
// according to second value
bool cmp(std::pair<float, int>& a,
std::pair<float, int>& b)
{
return a.second < b.second;
}
void sort(std::map<float, int>& M)
{
// Declare vector of pairs
std::vector<std::pair<float, int> > A;
// Copy key-value pair from Map
// to vector of pairs
for (auto& it : M) {
A.push_back(it);
}
// Sort using comparator function
sort(A.begin(), A.end(), cmp);
}
#include "basebludgeonweapon.h"
void CRagdollProp::ConstrainWeapon(CBaseCombatWeapon *pWeapon, const char* pAttachmentName, const char* pWeaponName)
{
/*CBaseCombatWeapon *pAnim = dynamic_cast<CBaseCombatWeapon*>(pWeapon);
if (!pAnim)
return;*/
if (!Q_strlen(pWeaponName) && !Q_strstr(pWeaponName, "weapon_"))
return;
if (!cvar->FindVar("oc_ragdoll_serverside")->GetInt() || !pWeapon)
return;
if (pWeapon->thisType == TYPE_NONE ||
pWeapon->thisType == TYPE_GRENADE ||
pWeapon->thisType == TYPE_LAST)
return;
bool allowFire = true;
CBaseHLBludgeonWeapon *pMelee = dynamic_cast<CBaseHLBludgeonWeapon*>(pWeapon);
if (pWeapon->thisType == TYPE_MELEE ||
pWeapon->thisType == TYPE_BEAM ||
pWeapon->thisType == TYPE_RPG ||
pMelee)
//FClassnameIs(pWeapon, "weapon_stunstick"))
//FStrEq(pWeapon->GetClassname(), "weapon_stunstick"))
allowFire = false;
CBaseAnimating *pAnimating = GetBaseAnimating();
if (pAnimating)
{
pDroppedWeapon = dynamic_cast<CBaseCombatWeapon*>(pWeapon);
int m_nBodyBone = pAnimating->LookupBone("ValveBiped.bip01_r_hand");// ("ValveBiped.bip01_r_forearm");
int m_nBodyBoneCombines = GetAttachmentBone(pAnimating->LookupAttachment(pAttachmentName));
switch (m_nBodyBone)
{
case 31:
m_nBodyBone = GetPhysicsBone(m_nBodyBoneCombines) + 1;
break;
default:
m_nBodyBone = GetPhysicsBone(m_nBodyBone);//5
break;
}
IPhysicsObject *pMyPhysics = VPhysicsGetObject();
IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
/*int count = */VPhysicsGetObjectList(pList, ARRAYSIZE(pList));
pMyPhysics = pList[m_nBodyBone];
if (pMyPhysics)
{
constrainted = true;
if (cvar->FindVar("oc_ragdoll_death_weapon_holding_time")->GetFloat() == -1)
constrainted = false;
int maxRandomRange = cvar->FindVar("oc_ragdoll_death_weapon_holding_rand_range")->GetInt();
if (cvar->FindVar("oc_ragdoll_death_weapon_holding_rand_range")->GetInt() > 0)
constrainted = random->RandomInt(0, maxRandomRange) > 0;
else
constrainted = true;
if (!constrainted)
return;
if (pDroppedWeapon->thisType == TYPE_SHOTGUN ||
pDroppedWeapon->thisType == TYPE_ANNABELLE ||
pDroppedWeapon->thisType == TYPE_PISTOL)
{
pDroppedWeapon->m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f;
}
if (allowFire)
pWeapon->EnableHoldingFire(true);
IPhysicsObject *pPhysics = pWeapon->VPhysicsGetObject();
if (pPhysics)
{
//IPhysicsConstraint *m_pConstraint;
constraint_fixedparams_t constraintParams;
constraintParams.Defaults();
constraintParams.InitWithCurrentObjectState(pPhysics, pMyPhysics);
constraintParams.constraint.Defaults();
constraintParams.constraint.forceLimit = ImpulseScale(pPhysics->GetMass(), 200);
constraintParams.constraint.torqueLimit = ImpulseScale(pPhysics->GetMass(), 800);
m_pConstraint = physenv->CreateFixedConstraint(pPhysics, pMyPhysics, NULL, constraintParams);
m_pConstraint->SetGameData((void *)this);
}
float holdTime = cvar->FindVar("oc_ragdoll_death_weapon_holding_time")->GetFloat() ?
random->RandomFloat(cvar->FindVar("oc_ragdoll_death_weapon_holding_time")->GetFloat()*0.8f, cvar->FindVar("oc_ragdoll_death_weapon_holding_time")->GetFloat()*1.2f) :
7.f;
m_flNextDropConstrainted = gpGlobals->curtime + holdTime;
}
}
}
void CRagdollProp::ConstraintedWeaponThink()
{
if (pDroppedWeapon == NULL || pDroppedWeapon == nullptr)// || pDroppedWeapon->IsMarkedForDeletion())
{
if (m_pConstraint)
{
physenv->DestroyConstraint(m_pConstraint);
m_pConstraint = NULL;
}
return;
}
if (pDroppedWeapon->IsMarkedForDeletion())
{
pDroppedWeapon->EnableHoldingFire(false);
if (m_pConstraint)
{
physenv->DestroyConstraint(m_pConstraint);
m_pConstraint = NULL;
}
return;
}
if (pDroppedWeapon && !pDroppedWeapon->IsMarkedForDeletion() && pDroppedWeapon->EnabledHoldingFire())
{
if (pDroppedWeapon->thisType == TYPE_SHOTGUN ||
pDroppedWeapon->thisType == TYPE_ANNABELLE ||
pDroppedWeapon->thisType == TYPE_PISTOL)
{
if (pDroppedWeapon->m_flNextPrimaryAttack < gpGlobals->curtime)
{
pDroppedWeapon->PrimaryAttackHolding(pDroppedWeapon);
pDroppedWeapon->EnableHoldingFire(false);
}
}
else if (pDroppedWeapon->thisType == TYPE_SMG)
{
if (pDroppedWeapon->m_flNextPrimaryAttack < gpGlobals->curtime)
pDroppedWeapon->PrimaryAttackHolding(pDroppedWeapon);
}
}
if (m_flNextDropConstrainted < gpGlobals->curtime)
{
//if (!pDroppedWeapon->IsMarkedForDeletion())
pDroppedWeapon->EnableHoldingFire(false);
if (m_pConstraint)
{
physenv->DestroyConstraint(m_pConstraint);
m_pConstraint = NULL;
}
}
}
void CRagdollProp::SetDebrisThink()
{
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
RecheckCollisionFilter();
}
void CRagdollProp::SetDamageEntity( CBaseEntity *pEntity )
{
// Damage passing
m_hDamageEntity = pEntity;
// Set our takedamage to match it
if ( pEntity )
{
m_takedamage = pEntity->m_takedamage;
}
else
{
m_takedamage = DAMAGE_EVENTS_ONLY;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CRagdollProp::OnTakeDamage( const CTakeDamageInfo &info )
{
// If we have a damage entity, we want to pass damage to it. Add the
// Never Ragdoll flag, on the assumption that if the entity dies, we'll
// actually be taking the role of its ragdoll.
if ( m_hDamageEntity.Get() )
{
CTakeDamageInfo subInfo = info;
subInfo.AddDamageType( DMG_REMOVENORAGDOLL );
return m_hDamageEntity->OnTakeDamage( subInfo );
}
return BaseClass::OnTakeDamage( info );
}
//-----------------------------------------------------------------------------
// Purpose: Force all the ragdoll's bone's physics objects to recheck their collision filters
//-----------------------------------------------------------------------------
void CRagdollProp::RecheckCollisionFilter( void )
{
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
m_ragdoll.list[i].pObject->RecheckCollisionFilter();
}
}
void CRagdollProp::DispatchBloodOverlay(const CTakeDamageInfo &info, int BloodColor)
{
int i = random->RandomInt(0, 5);
// Handle the viewmodel blood splatter overlay effect here:
if (cvar->FindVar("oc_player_bloodoverlay")->GetInt() && i <= 3 && (info.GetDamageType() & (DMG_BULLET | DMG_AIRBOAT | DMG_SLASH | DMG_DISSOLVE | DMG_GAUSS | DMG_BLAST | DMG_CLUB | DMG_BUCKSHOT)))
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (pPlayer && (BloodColor != BLOOD_COLOR_MECH))
{
trace_t tr;
Vector PlrPos = pPlayer->GetAbsOrigin();
//PlrPos.z += 120;
//AI_TraceLine(PlrPos, info.GetDamagePosition(), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr);
DevMsg("DistTo: %.2f \n", PlrPos.DistTo(info.GetDamagePosition()));
//if (VectorLength(tr.startpos + tr.endpos) < 100.0f)// if (pPlayer->GetAbsOrigin().DistTo(info.GetDamagePosition()) < 50.0f)
/*const surfacedata_t *ptexdata = physprops->GetSurfaceData(tr.surface.surfaceProps);
bool isFlesh = ((char)ptexdata->game.material == CHAR_TEX_ALIENFLESH) ||
((char)ptexdata->game.material == CHAR_TEX_BLOODYFLESH) ||
((char)ptexdata->game.material == CHAR_TEX_FLESH) ||
((char)ptexdata->game.material == CHAR_TEX_ANTLION);
if (PlrPos.DistTo(tr.endpos) <= 50 && isFlesh)*/
if (PlrPos.DistTo(info.GetDamagePosition()) <= 55)
{
int yellowBloodFrames = cvar->FindVar("oc_player_bloodoverlay_yellow_count")->GetInt();
int redBloodFrames = cvar->FindVar("oc_player_bloodoverlay_red_count")->GetInt();
int redBloodVar = random->RandomInt(yellowBloodFrames, yellowBloodFrames + redBloodFrames - 1);
int yellowBloodVar = random->RandomInt(0, yellowBloodFrames - 1);
switch (BloodColor)
{
case BLOOD_COLOR_RED:
{
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(redBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(redBloodVar);
}
break;
case BLOOD_COLOR_GREEN:
{
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;
case BLOOD_COLOR_ZOMBIE:
{
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;
case BLOOD_COLOR_YELLOW:
{
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;
case BLOOD_COLOR_ANTLION_WORKER:
{ pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;
case BLOOD_COLOR_ANTLION:
{
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;
/*case BLOOD_COLOR_MECH:
{
int blueBloodFrames = cvar->FindVar("oc_player_bloodoverlay_blue_count")->GetInt();
int acidBloodFrames = cvar->FindVar("oc_player_bloodoverlay_green_count")->GetInt();
int blueBloodVar = random->RandomInt(yellowBloodFrames + redBloodFrames + acidBloodFrames, yellowBloodFrames + redBloodFrames + acidBloodFrames + blueBloodFrames - 1);
pPlayer->SetupBloodOverlay(true);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(true);
pPlayer->SetupBloodOverlayDelay(cvar->FindVar("oc_player_bloodoverlay_lifetime")->GetFloat());
pPlayer->SetupBloodOverlayFrame(yellowBloodVar);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlayFrame(yellowBloodVar);
}
break;*/
default:
{
pPlayer->SetupBloodOverlay(false);
if (pPlayer->GetActiveWeapon())
pPlayer->SetupWeaponBloodOverlay(false);
}
break;
}
}
}
}
}
void CRagdollProp::TraceAttack(const CTakeDamageInfo &info, const Vector &dir, trace_t *ptr, CDmgAccumulator *pAccumulator)
{
/*if (cvar->FindVar("oc_ragdoll_enable_bloodstream")->GetInt())//OverCharged Blood
{
//Vector originPos0 = info.GetDamagePosition();
//Vector direction0 = -ptr->plane.normal;
originPos = info.GetDamagePosition();
direction = -info.GetDamageForce();//-ptr->plane.normal;
//direction = this->EyePosition();
hitbox = ptr->hitbox;
//this->GetBonePosition()
//WorldToEntitySpace(originPos0, &originPos);
//WorldToEntitySpace(direction0, &direction);
CreateBloodStream(ptr);
}*/
if ( ptr->physicsbone >= 0 && ptr->physicsbone < m_ragdoll.listCount )
{
VPhysicsSwapObject( m_ragdoll.list[ptr->physicsbone].pObject );
}
if (FStrEq(STRING(GetModelName()), "models/props_lab/"))
{
return;
}
if ((info.GetDamageType() & DMG_DISSOLVE_EGON))
{
CRagdollBoogie::Create(this, 70, gpGlobals->curtime, 1.5, SF_RAGDOLL_BOOGIE_ELECTRICAL);
}
if ((info.GetDamageType() & DMG_PHYSGUN))
{
CRagdollBoogie::Create(this, 100, gpGlobals->curtime, 2.0, SF_RAGDOLL_BOOGIE_ELECTRICAL);
}
if ((info.GetDamageType() & DMG_GAUSS))
{
CRagdollBoogie::Create(this, 50, gpGlobals->curtime, 1.0, SF_RAGDOLL_BOOGIE_ELECTRICAL);
}
if ((info.GetDamageType() & DMG_DISSOLVE ))// && (info.GetDamage() > 6.f))
{
health -= info.GetDamage();
//DevMsg("health: %.i \n", health);
if (health <= 50)
{
int nDissolveType = ENTITY_DISSOLVE_NORMAL;
if (GetDamageType() & DMG_SHOCK)
{
nDissolveType = ENTITY_DISSOLVE_ELECTRICAL;
Dissolve(NULL, gpGlobals->curtime, false, nDissolveType);
}
else
{
nDissolveType = ENTITY_DISSOLVE_NORMAL;
Dissolve(NULL, gpGlobals->curtime, false, nDissolveType);
}
}
}
if (cvar->FindVar("oc_ragdoll_show_blood")->GetInt() == 1 && BloodColor >= 0)
{
// make blood decal on the wall!
trace_t Bloodtr;
Vector vecTraceDir;
float flNoise;
int cCount;
int i;
if (info.GetDamage() < 10)
{
flNoise = 0.1;
cCount = 1;
}
else if (info.GetDamage() < 25)
{
flNoise = 0.2;
cCount = 2;
}
else
{
flNoise = 0.3;
cCount = 4;
}
//int Color = BloodColor();
float flTraceDist = (GetDamageType() & DMG_AIRBOAT) ? 384 : 172;
const surfacedata_t *ptexdata = physprops->GetSurfaceData(ptr->surface.surfaceProps);//ptr.surface.surfaceProps);
if ((char)ptexdata->game.material == CHAR_TEX_ALIENFLESH)
{
BloodColor = 1;
}
if ((char)ptexdata->game.material == CHAR_TEX_BLOODYFLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
/*else
BloodColor = 0;*/
}
if ((char)ptexdata->game.material == CHAR_TEX_FLESH)
{
if (FStrEq(STRING(GetModelName()), "models/Zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/Zombie/zombie_soldier_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/Headcrabblack.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_legs.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Classic_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Poison.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Fast_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_torso.mdl") ||
FStrEq(STRING(GetModelName()), "models/zombie/Zombie_soldier_legs.mdl"))
{
BloodColor = 1;
}
/*else
BloodColor = 0;*/
}
if ((char)ptexdata->game.material == CHAR_TEX_ANTLION)
{
BloodColor = 2;
}
if (((char)ptexdata->game.material == CHAR_TEX_CONCRETE) || ((char)ptexdata->game.material == CHAR_TEX_DIRT)
|| ((char)ptexdata->game.material == CHAR_TEX_EGGSHELL) || ((char)ptexdata->game.material == CHAR_TEX_GRATE)
|| ((char)ptexdata->game.material == CHAR_TEX_CLIP) || ((char)ptexdata->game.material == CHAR_TEX_PLASTIC)
|| ((char)ptexdata->game.material == CHAR_TEX_METAL) || ((char)ptexdata->game.material == CHAR_TEX_SAND)
|| ((char)ptexdata->game.material == CHAR_TEX_FOLIAGE) || ((char)ptexdata->game.material == CHAR_TEX_COMPUTER)
|| ((char)ptexdata->game.material == CHAR_TEX_SLOSH) || ((char)ptexdata->game.material == CHAR_TEX_TILE)
|| ((char)ptexdata->game.material == CHAR_TEX_VENT) || ((char)ptexdata->game.material == CHAR_TEX_WOOD)
|| ((char)ptexdata->game.material == CHAR_TEX_GLASS) || ((char)ptexdata->game.material == CHAR_TEX_WARPSHIELD))
{
BloodColor = -1;
}
if ((info.GetDamageType() & DMG_DISSOLVE_EGON) || (info.GetDamageType() & DMG_DISSOLVE) || (info.GetDamageType() & DMG_BURN))
{
}
else
{
if (BloodColor >= 0 && cvar->FindVar("oc_ragdoll_enable_blooddrips")->GetInt())
{
CEffectData data;
data.m_vOrigin = ptr->endpos;
data.m_vNormal = ptr->plane.normal;
QAngle ang;
VectorAngles(dir, ang);
data.m_vAngles = ang;
data.m_nAttachmentIndex = BloodColor;
DispatchEffect("BloodDrips", data);
}
SpawnBlood(ptr->endpos, dir, BloodColor, 100);
DispatchBloodOverlay(info, BloodColor);
for (i = 0; i < cCount; i++)
{
vecTraceDir = dir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
vecTraceDir.x += random->RandomFloat(-flNoise, flNoise);
vecTraceDir.y += random->RandomFloat(-flNoise, flNoise);
vecTraceDir.z += random->RandomFloat(-flNoise, flNoise);
if (cvar->FindVar("oc_blood_displays_on_all_normals")->GetInt() == 0)
{
// Don't bleed on grates.
AI_TraceLine(ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
//UTIL_TraceLine(ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
if (Bloodtr.fraction != 1.0)
{
UTIL_BloodDecalTrace(&Bloodtr, BloodColor);
}
}
else if (cvar->FindVar("oc_blood_displays_on_all_normals")->GetInt() == 1)
{
AI_TraceLine(ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_ALL, this, COLLISION_GROUP_INTERACTIVE, &Bloodtr);
//UTIL_TraceLine(ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
if (Bloodtr.fraction != 1.0)
{
UTIL_BloodDecalTrace(&Bloodtr, BloodColor);
}
}
}
}
}
BaseClass::TraceAttack(info, dir, ptr, pAccumulator);
}
void CRagdollProp::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
{
// no ragdoll, fall through to base class
if ( !m_ragdoll.listCount )
{
BaseClass::SetupBones( pBoneToWorld, boneMask );
return;
}
// Not really ideal, but it'll work for now
UpdateModelScale();
MDLCACHE_CRITICAL_SECTION();
CStudioHdr *pStudioHdr = GetModelPtr( );
bool sim[MAXSTUDIOBONES];
memset( sim, 0, pStudioHdr->numbones() );
int i;
CBoneAccessor boneaccessor( pBoneToWorld );
for ( i = 0; i < m_ragdoll.listCount; i++ )
{
// during restore this may be NULL
if ( !m_ragdoll.list[i].pObject )
continue;
if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
{
sim[m_ragdoll.boneIndex[i]] = true;
}
}
mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
for ( i = 0; i < pStudioHdr->numbones(); i++ )
{
if ( sim[i] )
continue;
if ( !(pStudioHdr->boneFlags(i) & boneMask) )
continue;
matrix3x4_t matBoneLocal;
AngleMatrix( pbones[i].rot, pbones[i].pos, matBoneLocal );
ConcatTransforms( pBoneToWorld[pbones[i].parent], matBoneLocal, pBoneToWorld[i]);
}
}
bool CRagdollProp::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
{
#if 0
// PERFORMANCE: Use hitboxes for rays instead of vcollides if this is a performance problem
if ( ray.m_IsRay )
{
return BaseClass::TestCollision( ray, mask, trace );
}
#endif
CStudioHdr *pStudioHdr = GetModelPtr( );
if (!pStudioHdr)
return false;
// Just iterate all of the elements and trace the box against each one.
// NOTE: This is pretty expensive for small/dense characters
trace_t tr;
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
Vector position;
QAngle angles;
if( m_ragdoll.list[i].pObject )
{
m_ragdoll.list[i].pObject->GetPosition( &position, &angles );
physcollision->TraceBox( ray, m_ragdoll.list[i].pObject->GetCollide(), position, angles, &tr );
if ( tr.fraction < trace.fraction )
{
tr.physicsbone = i;
tr.surface.surfaceProps = m_ragdoll.list[i].pObject->GetMaterialIndex();
trace = tr;
}
}
else
{
DevWarning("Bogus object in Ragdoll Prop's ragdoll list!\n");
}
}
if ( trace.fraction >= 1 )
{
return false;
}
return true;
}
void CRagdollProp::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
{
// newAngles is a relative transform for the entity
// But a ragdoll entity has identity orientation by design
// so we compute a relative transform here based on the previous transform
matrix3x4_t startMatrixInv;
MatrixInvert( EntityToWorldTransform(), startMatrixInv );
matrix3x4_t endMatrix;
MatrixCopy( EntityToWorldTransform(), endMatrix );
if ( newAngles )
{
AngleMatrix( *newAngles, endMatrix );
}
if ( newPosition )
{
PositionMatrix( *newPosition, endMatrix );
}
// now endMatrix is the refernce matrix for the entity at the target position
matrix3x4_t xform;
ConcatTransforms( endMatrix, startMatrixInv, xform );
// now xform is the relative transform the entity must undergo
// we need to call the base class and it will teleport our vphysics object,
// so set object 0 up and compute the origin/angles for its new position (base implementation has side effects)
VPhysicsSwapObject( m_ragdoll.list[0].pObject );
matrix3x4_t obj0source, obj0Target;
m_ragdoll.list[0].pObject->GetPositionMatrix( &obj0source );
ConcatTransforms( xform, obj0source, obj0Target );
Vector obj0Pos;
QAngle obj0Angles;
MatrixAngles( obj0Target, obj0Angles, obj0Pos );
BaseClass::Teleport( &obj0Pos, &obj0Angles, newVelocity );
for ( int i = 1; i < m_ragdoll.listCount; i++ )
{
matrix3x4_t matrix, newMatrix;
m_ragdoll.list[i].pObject->GetPositionMatrix( &matrix );
ConcatTransforms( xform, matrix, newMatrix );
m_ragdoll.list[i].pObject->SetPositionMatrix( newMatrix, true );
UpdateNetworkDataFromVPhysics( m_ragdoll.list[i].pObject, i );
}
// fixup/relink object 0
UpdateNetworkDataFromVPhysics( m_ragdoll.list[0].pObject, 0 );
}
void CRagdollProp::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
if ( m_lastUpdateTickCount == (unsigned int)gpGlobals->tickcount )
return;
m_lastUpdateTickCount = gpGlobals->tickcount;
//NetworkStateChanged();
if (constrainted)
{
ConstraintedWeaponThink();
}
matrix3x4_t boneToWorld[MAXSTUDIOBONES];
QAngle angles;
Vector surroundingMins, surroundingMaxs;
int i;
for ( i = 0; i < m_ragdoll.listCount; i++ )
{
CBoneAccessor boneaccessor( boneToWorld );
if ( RagdollGetBoneMatrix( m_ragdoll, boneaccessor, i ) )
{
Vector vNewPos;
MatrixAngles( boneToWorld[m_ragdoll.boneIndex[i]], angles, vNewPos );
m_ragPos.Set( i, vNewPos );
m_ragAngles.Set( i, angles );
}
else
{
m_ragPos.GetForModify(i).Init();
m_ragAngles.GetForModify(i).Init();
}
}
// BUGBUG: Use the ragdollmins/maxs to do this instead of the collides
m_allAsleep = RagdollIsAsleep( m_ragdoll );
// Don't scream after you've come to rest
if ( m_allAsleep )
{
m_strSourceClassName = NULL_STRING;
}
else
{
if ( m_ragdoll.pGroup->IsInErrorState() )
{
RagdollSolveSeparation( m_ragdoll, this );
}
}
// Interactive debris converts back to debris when it comes to rest
if ( m_allAsleep && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
{
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
RecheckCollisionFilter();
SetContextThink( NULL, gpGlobals->curtime, s_pDebrisContext );
}
Vector vecFullMins, vecFullMaxs;
vecFullMins = m_ragPos[0];
vecFullMaxs = m_ragPos[0];
for ( i = 0; i < m_ragdoll.listCount; i++ )
{
Vector mins, maxs;
matrix3x4_t update;
if ( !m_ragdoll.list[i].pObject )
{
m_ragdollMins[i].Init();
m_ragdollMaxs[i].Init();
continue;
}
m_ragdoll.list[i].pObject->GetPositionMatrix( &update );
TransformAABB( update, m_ragdollMins[i], m_ragdollMaxs[i], mins, maxs );
for ( int j = 0; j < 3; j++ )
{
if ( mins[j] < vecFullMins[j] )
{
vecFullMins[j] = mins[j];
}
if ( maxs[j] > vecFullMaxs[j] )
{
vecFullMaxs[j] = maxs[j];
}
}
}
SetAbsOrigin( m_ragPos[0] );
SetAbsAngles( vec3_angle );
const Vector &vecOrigin = CollisionProp()->GetCollisionOrigin();
CollisionProp()->AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
CollisionProp()->SetSurroundingBoundsType( USE_COLLISION_BOUNDS_NEVER_VPHYSICS );
SetCollisionBounds( vecFullMins - vecOrigin, vecFullMaxs - vecOrigin );
CollisionProp()->MarkSurroundingBoundsDirty();
PhysicsTouchTriggers();
}
int CRagdollProp::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
{
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
if ( i < listMax )
{
pList[i] = m_ragdoll.list[i].pObject;
}
}
return m_ragdoll.listCount;
}
void CRagdollProp::UpdateNetworkDataFromVPhysics( IPhysicsObject *pPhysics, int index )
{
Assert(index < m_ragdoll.listCount);
QAngle angles;
Vector vPos;
m_ragdoll.list[index].pObject->GetPosition( &vPos, &angles );
m_ragPos.Set( index, vPos );
m_ragAngles.Set( index, angles );
// move/relink if root moved
if ( index == 0 )
{
SetAbsOrigin( m_ragPos[0] );
PhysicsTouchTriggers();
}
}
//-----------------------------------------------------------------------------
// Fade out due to the LRU telling it do
//-----------------------------------------------------------------------------
#define FADE_OUT_LENGTH 0.5f
void CRagdollProp::FadeOut( float flDelay, float fadeTime )
{
if ( IsFading() )
return;
m_flFadeTime = ( fadeTime == -1 ) ? FADE_OUT_LENGTH : fadeTime;
m_flFadeOutStartTime = gpGlobals->curtime + flDelay;
m_flFadeScale = 0;
SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + flDelay + 0.01f, s_pFadeOutContext );
}
bool CRagdollProp::IsFading()
{
return ( GetNextThink( s_pFadeOutContext ) >= gpGlobals->curtime );
}
void CRagdollProp::FadeOutThink(void)
{
float dt = gpGlobals->curtime - m_flFadeOutStartTime;
if ( dt < 0 )
{
SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + 0.1, s_pFadeOutContext );
}
else if ( dt < m_flFadeTime )
{
float alpha = 1.0f - dt / m_flFadeTime;
int nFade = (int)(alpha * 255.0f);
m_nRenderMode = kRenderTransTexture;
SetRenderColorA( nFade );
NetworkStateChanged();
SetContextThink( &CRagdollProp::FadeOutThink, gpGlobals->curtime + TICK_INTERVAL, s_pFadeOutContext );
}
else
{
// Necessary to cause it to do the appropriate death cleanup
// Yeah, the player may have nothing to do with it, but
// passing NULL to TakeDamage causes bad things to happen
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC );
TakeDamage( info );
UTIL_Remove( this );
}
}
//-----------------------------------------------------------------------------
// Purpose: Draw any debug text overlays
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
int CRagdollProp::DrawDebugTextOverlays(void)
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
if (m_ragdoll.listCount)
{
float mass = 0;
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
if ( m_ragdoll.list[i].pObject != NULL )
{
mass += m_ragdoll.list[i].pObject->GetMass();
}
}
char tempstr[512];
Q_snprintf(tempstr, sizeof(tempstr),"Mass: %.2f kg / %.2f lb (%s)", mass, kg2lbs(mass), GetMassEquivalent(mass) );
EntityText( text_offset, tempstr, 0);
text_offset++;
}
}
return text_offset;
}
void CRagdollProp::DrawDebugGeometryOverlays()
{
if (m_debugOverlays & OVERLAY_BBOX_BIT)
{
DrawServerHitboxes();
}
if (m_debugOverlays & OVERLAY_PIVOT_BIT)
{
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
if ( m_ragdoll.list[i].pObject )
{
float mass = m_ragdoll.list[i].pObject->GetMass();
Vector pos;
m_ragdoll.list[i].pObject->GetPosition( &pos, NULL );
CFmtStr str("mass %.1f", mass );
NDebugOverlay::EntityTextAtPosition( pos, 0, str.Access(), 0, 0, 255, 0, 255 );
}
}
}
BaseClass::DrawDebugGeometryOverlays();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CRagdollProp::SetUnragdoll( CBaseAnimating *pOther )
{
m_hUnragdoll = pOther;
}
//===============================================================================================================
// RagdollPropAttached
//===============================================================================================================
class CRagdollPropAttached : public CRagdollProp
{
DECLARE_CLASS( CRagdollPropAttached, CRagdollProp );
public:
CRagdollPropAttached()
{
m_bShouldDetach = false;
}
~CRagdollPropAttached()
{
physenv->DestroyConstraint( m_pAttachConstraint );
m_pAttachConstraint = NULL;
}
void InitRagdollAttached( IPhysicsObject *pAttached, const Vector &forceVector, int forceBone, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, CBaseAnimating *pFollow, int boneIndexRoot, const Vector &boneLocalOrigin, int parentBoneAttach, const Vector &worldAttachOrigin );
void DetachOnNextUpdate();
void VPhysicsUpdate( IPhysicsObject *pPhysics );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
private:
void Detach();
CNetworkVar( int, m_boneIndexAttached );
CNetworkVar( int, m_ragdollAttachedObjectIndex );
CNetworkVector( m_attachmentPointBoneSpace );
CNetworkVector( m_attachmentPointRagdollSpace );
bool m_bShouldDetach;
IPhysicsConstraint *m_pAttachConstraint;
};
LINK_ENTITY_TO_CLASS( prop_ragdoll_attached, CRagdollPropAttached );
EXTERN_SEND_TABLE(DT_Ragdoll_Attached)
IMPLEMENT_SERVERCLASS_ST(CRagdollPropAttached, DT_Ragdoll_Attached)
SendPropInt( SENDINFO( m_boneIndexAttached ), MAXSTUDIOBONEBITS, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_ragdollAttachedObjectIndex ), RAGDOLL_INDEX_BITS, SPROP_UNSIGNED ),
SendPropVector(SENDINFO(m_attachmentPointBoneSpace), -1, SPROP_COORD ),
SendPropVector(SENDINFO(m_attachmentPointRagdollSpace), -1, SPROP_COORD ),
END_SEND_TABLE()
BEGIN_DATADESC(CRagdollPropAttached)
DEFINE_FIELD( m_boneIndexAttached, FIELD_INTEGER ),
DEFINE_FIELD( m_ragdollAttachedObjectIndex, FIELD_INTEGER ),
DEFINE_FIELD( m_attachmentPointBoneSpace, FIELD_VECTOR ),
DEFINE_FIELD( m_attachmentPointRagdollSpace, FIELD_VECTOR ),
DEFINE_FIELD( m_bShouldDetach, FIELD_BOOLEAN ),
DEFINE_PHYSPTR( m_pAttachConstraint ),
END_DATADESC()
static void SyncAnimatingWithPhysics( CBaseAnimating *pAnimating )
{
IPhysicsObject *pPhysics = pAnimating->VPhysicsGetObject();
if ( pPhysics )
{
Vector pos;
pPhysics->GetShadowPosition( &pos, NULL );
pAnimating->SetAbsOrigin( pos );
}
}
CBaseAnimating *CreateServerRagdollSubmodel(CBaseAnimating *pOwner, const char *pModelName, const Vector &position, const QAngle &angles, int collisionGroup, int bloodColor)
{
CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", position, angles, pOwner );
pRagdoll->SetModelName( AllocPooledString( pModelName ) );
pRagdoll->SetModel( STRING(pRagdoll->GetModelName()) );
pRagdoll->BloodColor = bloodColor;
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
pRagdoll->ResetSequence( 0 );
if (pOwner->m_bIsBodygrouped)
{
pRagdoll->SetBodygroup(pOwner->m_iBodyGroup, pOwner->m_iBodyGroupValue);
}
// let bone merging do the work of copying everything over for us
pRagdoll->SetParent( pOwner );
pRagdoll->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
// HACKHACK: don't want this parent anymore
pRagdoll->SetParent( NULL );
memcpy( pBoneToWorldNext, pBoneToWorld, sizeof(pBoneToWorld) );
pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, 0.1, collisionGroup, true );
return pRagdoll;
}
CBaseEntity *CreateServerRagdoll(CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, bool bUseLRURetirement, int bloodColor)
{
if ( info.GetDamageType() & (DMG_VEHICLE|DMG_CRUSH) )
{
// if the entity was killed by physics or a vehicle, move to the vphysics shadow position before creating the ragdoll.
SyncAnimatingWithPhysics( pAnimating );
}
CTakeDamageInfo newinfo = info;
if (info.GetDamageType() & DMG_DISSOLVE)
{
newinfo.SetDamageForce(info.GetDamageForce() * cvar->FindVar("oc_ragdoll_serverside_dissolve_impulse")->GetFloat());
}
CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
pRagdoll->CopyAnimationDataFrom( pAnimating );
pRagdoll->SetOwnerEntity( pAnimating );
pRagdoll->BloodColor = bloodColor;
pRagdoll->InitRagdollAnimation();
if (pAnimating->m_bIsBodygrouped)
{
pRagdoll->SetBodygroup(pAnimating->m_iBodyGroup, pAnimating->m_iBodyGroupValue);
}
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
float dt = 0.1f;
// Copy over dissolve state...
if ( pAnimating->IsEFlagSet( EFL_NO_DISSOLVE ) )
{
pRagdoll->AddEFlags( EFL_NO_DISSOLVE );
}
// NOTE: This currently is only necessary to prevent manhacks from
// colliding with server ragdolls they kill
pRagdoll->SetKiller(newinfo.GetInflictor());
pRagdoll->SetSourceClassName( pAnimating->GetClassname() );
/*trace_t Bloodtr;
AI_TraceLine(info.GetDamagePosition(), info.GetDamagePosition(), MASK_ALL, pRagdoll, COLLISION_GROUP_INTERACTIVE, &Bloodtr);
//UTIL_TraceLine(ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
if (Bloodtr.fraction != 1.0)
{
UTIL_BloodDecalTrace(&Bloodtr, 1);
}
CEffectData data;
data.m_vOrigin = Bloodtr.endpos;
data.m_vStart = Bloodtr.startpos;
data.m_nSurfaceProp = Bloodtr.surface.surfaceProps;
data.m_nHitBox = Bloodtr.hitbox;
// Send it on its way
//if (!pCustomImpactName)
{
DispatchEffect("Impact", data);
}
//else
{
DispatchEffect("bloodspray", data);
}*/
/*trace_t tr;
UTIL_TraceLine(info.GetDamagePosition(), info.GetDamagePosition() + info.GetDamagePosition(), MASK_ALL, pRagdoll, COLLISION_GROUP_NONE, &tr);//COLLISION_GROUP_INTERACTIVE
UTIL_DecalTrace(&tr, "Blood");
UTIL_BloodImpact(tr.startpos, tr.endpos, 1, 1);
UTIL_BloodSpray(tr.startpos, tr.endpos, 1, 1, FX_BLOODSPRAY_ALL);
pRagdoll->TraceAttack(info, info.GetDamagePosition(), &tr, NULL);
CBroadcastRecipientFilter filter;
te->Decal(filter, 10.0, &tr.endpos, &tr.startpos, ENTINDEX(tr.m_pEnt), tr.hitbox, pRagdoll->entindex());*/
// NPC_STATE_DEAD npc's will have their COND_IN_PVS cleared, so this needs to force SetupBones to happen
unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags();
pAnimating->SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
// UNDONE: Extract velocity from bones via animation (like we do on the client)
// UNDONE: For now, just move each bone by the total entity velocity if set.
// Get Bones positions before
// Store current cycle
float fSequenceDuration = pAnimating->SequenceDuration( pAnimating->GetSequence() );
float fSequenceTime = pAnimating->GetCycle() * fSequenceDuration;
if( fSequenceTime <= dt && fSequenceTime > 0.0f )
{
// Avoid having negative cycle
dt = fSequenceTime;
}
float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f);
float fCurCycle = pAnimating->GetCycle();
// Get current bones positions
pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
// Get previous bones positions
pAnimating->SetCycle( fPreviousCycle );
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
// Restore current cycle
pAnimating->SetCycle( fCurCycle );
// Reset previous bone flags
pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
pAnimating->SetBoneCacheFlags( fPrevFlags );
Vector vel = pAnimating->GetAbsVelocity();
if( ( vel.Length() == 0 ) && ( dt > 0 ) )
{
// Compute animation velocity
CStudioHdr *pstudiohdr = pAnimating->GetModelPtr();
if ( pstudiohdr )
{
Vector deltaPos;
QAngle deltaAngles;
if (Studio_SeqMovement( pstudiohdr,
pAnimating->GetSequence(),
fPreviousCycle,
pAnimating->GetCycle(),
pAnimating->GetPoseParameterArray(),
deltaPos,
deltaAngles ))
{
VectorRotate( deltaPos, pAnimating->EntityToWorldTransform(), vel );
vel /= dt;
}
}
}
if ( vel.LengthSqr() > 0 )
{
int numbones = pAnimating->GetModelPtr()->numbones();
vel *= dt;
for ( int i = 0; i < numbones; i++ )
{
Vector pos;
MatrixGetColumn( pBoneToWorld[i], 3, pos );
pos -= vel;
MatrixSetColumn( pos, 3, pBoneToWorld[i] );
}
}
#if RAGDOLL_VISUALIZE
pAnimating->DrawRawSkeleton( pBoneToWorld, BONE_USED_BY_ANYTHING, true, 20, false );
pAnimating->DrawRawSkeleton( pBoneToWorldNext, BONE_USED_BY_ANYTHING, true, 20, true );
#endif
// Is this a vehicle / NPC collision?
if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() )
{
// init the ragdoll with no forces
pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
// apply vehicle forces
// Get a list of bones with hitboxes below the plane of impact
//int boxList[128];
Vector normal(0,0,-1);
//int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) );
// distribute force over mass of entire character
float massScale = Studio_GetMass(pAnimating->GetModelPtr());
massScale = clamp( massScale, 1.f, 1.e4f );
massScale = 1.f / massScale;
// distribute the force
// BUGBUG: This will hit the same bone twice if it has two hitboxes!!!!
//ragdoll_t *pRagInfo = pRagdoll->GetRagdoll();
//if (pRagInfo != NULL)
{
//for (int i = 0; i < count; i++)
{
//int physBone = pAnimating->GetPhysicsBone(pAnimating->GetHitboxBone(boxList[i]));
//IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject;
//pPhysics->ApplyForceCenter(info.GetDamageForce() * pPhysics->GetMass() * massScale);
IPhysicsObject *pPhysics = pRagdoll->VPhysicsGetObject();
pRagdoll->ApplyAbsVelocityImpulse(newinfo.GetDamageForce() * pPhysics->GetMass() * massScale);
}
}
}
else
{
pRagdoll->InitRagdoll(newinfo.GetDamageForce(), forceBone, newinfo.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true);
}
// Are we dissolving?
if ( pAnimating->IsDissolving() )
{
IPhysicsObject *pPhysics = pRagdoll->VPhysicsGetObject();
if (pPhysics)
{
pPhysics->SetMass(1750.0f);
pPhysics->EnableGravity(false);
pPhysics->EnableDrag(false);
}
pRagdoll->SetGravity(-1000);
pRagdoll->TransferDissolveFrom( pAnimating );
}
else if ( bUseLRURetirement )
{
pRagdoll->AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT );
s_RagdollLRU.MoveToTopOfLRU( pRagdoll );
}
// Tracker 22598: If we don't set the OBB mins/maxs to something valid here, then the client will have a zero sized hull
// for the ragdoll for one frame until Vphysics updates the real obb bounds after the first simulation frame. Having
// a zero sized hull makes the ragdoll think it should be faded/alpha'd to zero for a frame, so you get a blink where
// the ragdoll doesn't draw initially.
Vector mins, maxs;
mins = pAnimating->CollisionProp()->OBBMins();
maxs = pAnimating->CollisionProp()->OBBMaxs();
pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
return pRagdoll;
}
CBaseEntity *CreateServerRagdoll(CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, CBaseCombatWeapon *pWeapon, const char* pAttachmentName, const char* pWeaponName, bool constraintWeapon, bool bUseLRURetirement, int bloodColor)
{
if (info.GetDamageType() & (DMG_VEHICLE | DMG_CRUSH))
{
// if the entity was killed by physics or a vehicle, move to the vphysics shadow position before creating the ragdoll.
SyncAnimatingWithPhysics(pAnimating);
}
CTakeDamageInfo newinfo = info;
if (info.GetDamageType() & DMG_DISSOLVE)
{
newinfo.SetDamageForce(info.GetDamageForce() * cvar->FindVar("oc_ragdoll_serverside_dissolve_impulse")->GetFloat());
}
CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn("prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL);
pRagdoll->CopyAnimationDataFrom(pAnimating);
pRagdoll->SetOwnerEntity(pAnimating);
pRagdoll->BloodColor = bloodColor;
pRagdoll->InitRagdollAnimation();
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
/*IPhysicsObject *pPhysics = pRagdoll->VPhysicsGetObject();
float massScale = Studio_GetMass(pAnimating->GetModelPtr());
massScale = clamp(massScale, 1.f, 1.e4f);
massScale = 1.f / massScale;
Vector origin = pAnimating->GetAbsOrigin();
origin.z += 500000;
pRagdoll->ApplyAbsVelocityImpulse(origin * pPhysics->GetMass() * massScale);*/
//pPhysics->ApplyForceCenter(10000 * pPhysics->GetMass() * massScale);
if (pAnimating->m_bIsBodygrouped)
{
pRagdoll->SetBodygroup(pAnimating->m_iBodyGroup, pAnimating->m_iBodyGroupValue);
}
float dt = 0.1f;
// Copy over dissolve state...
if (pAnimating->IsEFlagSet(EFL_NO_DISSOLVE))
{
pRagdoll->AddEFlags(EFL_NO_DISSOLVE);
}
pRagdoll->SetKiller(newinfo.GetInflictor());
pRagdoll->SetSourceClassName(pAnimating->GetClassname());
unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags();
pAnimating->SetBoneCacheFlags(BCF_NO_ANIMATION_SKIP);
float fSequenceDuration = pAnimating->SequenceDuration(pAnimating->GetSequence());
float fSequenceTime = pAnimating->GetCycle() * fSequenceDuration;
if (fSequenceTime <= dt && fSequenceTime > 0.0f)
{
// Avoid having negative cycle
dt = fSequenceTime;
}
float fPreviousCycle = clamp(pAnimating->GetCycle() - (dt * (1 / fSequenceDuration)), 0.f, 1.f);
float fCurCycle = pAnimating->GetCycle();
// Get current bones positions
pAnimating->SetupBones(pBoneToWorldNext, BONE_USED_BY_ANYTHING);
// Get previous bones positions
pAnimating->SetCycle(fPreviousCycle);
pAnimating->SetupBones(pBoneToWorld, BONE_USED_BY_ANYTHING);
// Restore current cycle
pAnimating->SetCycle(fCurCycle);
// Reset previous bone flags
pAnimating->ClearBoneCacheFlags(BCF_NO_ANIMATION_SKIP);
pAnimating->SetBoneCacheFlags(fPrevFlags);
Vector vel = pAnimating->GetAbsVelocity();
if ((vel.Length() == 0) && (dt > 0))
{
// Compute animation velocity
CStudioHdr *pstudiohdr = pAnimating->GetModelPtr();
if (pstudiohdr)
{
Vector deltaPos;
QAngle deltaAngles;
if (Studio_SeqMovement(pstudiohdr,
pAnimating->GetSequence(),
fPreviousCycle,
pAnimating->GetCycle(),
pAnimating->GetPoseParameterArray(),
deltaPos,
deltaAngles))
{
VectorRotate(deltaPos, pAnimating->EntityToWorldTransform(), vel);
vel /= dt;
}
}
}
if (vel.LengthSqr() > 0)
{
int numbones = pAnimating->GetModelPtr()->numbones();
vel *= dt;
for (int i = 0; i < numbones; i++)
{
Vector pos;
MatrixGetColumn(pBoneToWorld[i], 3, pos);
pos -= vel;
MatrixSetColumn(pos, 3, pBoneToWorld[i]);
}
}
#if RAGDOLL_VISUALIZE
pAnimating->DrawRawSkeleton(pBoneToWorld, BONE_USED_BY_ANYTHING, true, 20, false);
pAnimating->DrawRawSkeleton(pBoneToWorldNext, BONE_USED_BY_ANYTHING, true, 20, true);
#endif
// Is this a vehicle / NPC collision?
if ((info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer())
{
// init the ragdoll with no forces
pRagdoll->InitRagdoll(vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true);
Vector normal(0, 0, -1);
float massScale = Studio_GetMass(pAnimating->GetModelPtr());
massScale = clamp(massScale, 1.f, 1.e4f);
massScale = 1.f / massScale;
IPhysicsObject *pPhysics = pRagdoll->VPhysicsGetObject();
pRagdoll->ApplyAbsVelocityImpulse(newinfo.GetDamageForce() * pPhysics->GetMass() * massScale);
}
else
{
pRagdoll->InitRagdoll(newinfo.GetDamageForce(), forceBone, newinfo.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true);
}
// Are we dissolving?
if (pAnimating->IsDissolving())
{
IPhysicsObject *pPhysics = pRagdoll->VPhysicsGetObject();
if (pPhysics)
{
pPhysics->SetMass(1750.0f);
pPhysics->EnableGravity(false);
pPhysics->EnableDrag(false);
}
pRagdoll->SetGravity(-1000);
pRagdoll->TransferDissolveFrom(pAnimating);
}
else if (bUseLRURetirement)
{
pRagdoll->AddSpawnFlags(SF_RAGDOLLPROP_USE_LRU_RETIREMENT);
s_RagdollLRU.MoveToTopOfLRU(pRagdoll);
}
Vector mins, maxs;
mins = pAnimating->CollisionProp()->OBBMins();
maxs = pAnimating->CollisionProp()->OBBMaxs();
pRagdoll->CollisionProp()->SetCollisionBounds(mins, maxs);
if (pRagdoll && constraintWeapon)//cvar->FindVar("oc_ragdoll_death_weapon_holding")->GetInt())
pRagdoll->ConstrainWeapon(pWeapon, pAttachmentName, pWeaponName);
return pRagdoll;
}
void CRagdollPropAttached::DetachOnNextUpdate()
{
m_bShouldDetach = true;
}
void CRagdollPropAttached::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
if ( m_bShouldDetach )
{
Detach();
m_bShouldDetach = false;
}
BaseClass::VPhysicsUpdate( pPhysics );
}
void CRagdollPropAttached::Detach()
{
SetParent(NULL);
SetOwnerEntity( NULL );
SetAbsAngles( vec3_angle );
SetMoveType( MOVETYPE_VPHYSICS );
RemoveSolidFlags( FSOLID_NOT_SOLID );
physenv->DestroyConstraint( m_pAttachConstraint );
m_pAttachConstraint = NULL;
const float dampingScale = 1.0f / ATTACHED_DAMPING_SCALE;
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
float damping, rotdamping;
m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
damping *= dampingScale;
rotdamping *= dampingScale;
m_ragdoll.list[i].pObject->SetDamping( &damping, &damping );
}
// Go non-solid
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
RecheckCollisionFilter();
}
void CRagdollPropAttached::InitRagdollAttached(
IPhysicsObject *pAttached,
const Vector &forceVector,
int forceBone,
matrix3x4_t *pPrevBones,
matrix3x4_t *pBoneToWorld,
float dt,
int collisionGroup,
CBaseAnimating *pFollow,
int boneIndexRoot,
const Vector &boneLocalOrigin,
int parentBoneAttach,
const Vector &worldAttachOrigin )
{
int ragdollAttachedIndex = 0;
if ( parentBoneAttach > 0 )
{
CStudioHdr *pStudioHdr = GetModelPtr();
mstudiobone_t *pBone = pStudioHdr->pBone( parentBoneAttach );
ragdollAttachedIndex = pBone->physicsbone;
}
InitRagdoll( forceVector, forceBone, vec3_origin, pPrevBones, pBoneToWorld, dt, collisionGroup, false );
IPhysicsObject *pRefObject = m_ragdoll.list[ragdollAttachedIndex].pObject;
Vector attachmentPointRagdollSpace;
pRefObject->WorldToLocal( &attachmentPointRagdollSpace, worldAttachOrigin );
constraint_ragdollparams_t constraint;
constraint.Defaults();
matrix3x4_t tmp, worldToAttached, worldToReference, constraintToWorld;
Vector offsetWS;
pAttached->LocalToWorld( &offsetWS, boneLocalOrigin );
QAngle followAng = QAngle(0, pFollow->GetAbsAngles().y, 0 );
AngleMatrix( followAng, offsetWS, constraintToWorld );
constraint.axes[0].SetAxisFriction( -2, 2, 20 );
constraint.axes[1].SetAxisFriction( 0, 0, 0 );
constraint.axes[2].SetAxisFriction( -15, 15, 20 );
// Exaggerate the bone's ability to pull the mass of the ragdoll around
constraint.constraint.bodyMassScale[1] = 50.0f;
pAttached->GetPositionMatrix( &tmp );
MatrixInvert( tmp, worldToAttached );
pRefObject->GetPositionMatrix( &tmp );
MatrixInvert( tmp, worldToReference );
ConcatTransforms( worldToReference, constraintToWorld, constraint.constraintToReference );
ConcatTransforms( worldToAttached, constraintToWorld, constraint.constraintToAttached );
// for now, just slam this to be the passed in value
MatrixSetColumn( attachmentPointRagdollSpace, 3, constraint.constraintToReference );
PhysDisableEntityCollisions( pAttached, m_ragdoll.list[0].pObject );
m_pAttachConstraint = physenv->CreateRagdollConstraint( pRefObject, pAttached, m_ragdoll.pGroup, constraint );
SetParent( pFollow );
SetOwnerEntity( pFollow );
RagdollActivate( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() );
// add a bunch of dampening to the ragdoll
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
float damping, rotdamping;
m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping );
damping *= ATTACHED_DAMPING_SCALE;
rotdamping *= ATTACHED_DAMPING_SCALE;
m_ragdoll.list[i].pObject->SetDamping( &damping, &rotdamping );
}
m_boneIndexAttached = boneIndexRoot;
m_ragdollAttachedObjectIndex = ragdollAttachedIndex;
m_attachmentPointBoneSpace = boneLocalOrigin;
Vector vTemp;
MatrixGetColumn( constraint.constraintToReference, 3, vTemp );
m_attachmentPointRagdollSpace = vTemp;
}
CRagdollProp *CreateServerRagdollAttached( CBaseAnimating *pAnimating, const Vector &vecForce, int forceBone, int collisionGroup, IPhysicsObject *pAttached, CBaseAnimating *pParentEntity, int boneAttach, const Vector &originAttached, int parentBoneAttach, const Vector &boneOrigin )
{
// Return immediately if the model doesn't have a vcollide
if ( modelinfo->GetVCollide( pAnimating->GetModelIndex() ) == NULL )
return NULL;
CRagdollPropAttached *pRagdoll = (CRagdollPropAttached *)CBaseEntity::CreateNoSpawn( "prop_ragdoll_attached", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
pRagdoll->CopyAnimationDataFrom( pAnimating );
if (pAnimating->m_bIsBodygrouped)
{
pRagdoll->SetBodygroup(pAnimating->m_iBodyGroup, pAnimating->m_iBodyGroupValue);
}
pRagdoll->InitRagdollAnimation();
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
pRagdoll->InitRagdollAttached( pAttached, vecForce, forceBone, pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, pParentEntity, boneAttach, boneOrigin, parentBoneAttach, originAttached );
return pRagdoll;
}
void DetachAttachedRagdoll( CBaseEntity *pRagdollIn )
{
CRagdollPropAttached *pRagdoll = dynamic_cast<CRagdollPropAttached *>(pRagdollIn);
if ( pRagdoll )
{
pRagdoll->DetachOnNextUpdate();
}
}
void DetachAttachedRagdollsForEntity( CBaseEntity *pRagdollParent )
{
CUtlVector<CBaseEntity *> list;
GetAllChildren( pRagdollParent, list );
for ( int i = list.Count()-1; i >= 0; --i )
{
DetachAttachedRagdoll( list[i] );
}
}
bool Ragdoll_IsPropRagdoll( CBaseEntity *pEntity )
{
if ( dynamic_cast<CRagdollProp *>(pEntity) != NULL )
return true;
return false;
}
ragdoll_t *Ragdoll_GetRagdoll( CBaseEntity *pEntity )
{
CRagdollProp *pProp = dynamic_cast<CRagdollProp *>(pEntity);
if ( pProp )
return pProp->GetRagdoll();
return NULL;
}
void CRagdollProp::GetAngleOverrideFromCurrentState( char *pOut, int size )
{
pOut[0] = 0;
for ( int i = 0; i < m_ragdoll.listCount; i++ )
{
if ( i != 0 )
{
Q_strncat( pOut, ",", size, COPY_ALL_CHARACTERS );
}
CFmtStr str("%d,%.2f %.2f %.2f", i, m_ragAngles[i].x, m_ragAngles[i].y, m_ragAngles[i].z );
Q_strncat( pOut, str, size, COPY_ALL_CHARACTERS );
}
}
void CRagdollProp::DisableMotion( void )
{
for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
{
IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
if ( pPhysicsObject != NULL )
{
pPhysicsObject->EnableMotion( false );
}
}
}
void CRagdollProp::InputStartRadgollBoogie( inputdata_t &inputdata )
{
float duration = inputdata.value.Float();
if( duration <= 0.0f )
{
duration = 5.0f;
}
CRagdollBoogie::Create( this, 100, gpGlobals->curtime, duration, 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Enable physics motion and collision response (on by default)
//-----------------------------------------------------------------------------
void CRagdollProp::InputEnableMotion( inputdata_t &inputdata )
{
for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll )
{
IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject;
if ( pPhysicsObject != NULL )
{
pPhysicsObject->EnableMotion( true );
pPhysicsObject->Wake();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Disable any physics motion or collision response
//-----------------------------------------------------------------------------
void CRagdollProp::InputDisableMotion( inputdata_t &inputdata )
{
DisableMotion();
}
void CRagdollProp::InputTurnOn( inputdata_t &inputdata )
{
RemoveEffects( EF_NODRAW );
}
void CRagdollProp::InputTurnOff( inputdata_t &inputdata )
{
AddEffects( EF_NODRAW );
}
void CRagdollProp::InputFadeAndRemove( inputdata_t &inputdata )
{
float flFadeDuration = inputdata.value.Float();
if( flFadeDuration == 0.0f )
flFadeDuration = 1.0f;
FadeOut( 0.0f, flFadeDuration );
}
void Ragdoll_GetAngleOverrideString( char *pOut, int size, CBaseEntity *pEntity )
{
CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp *>(pEntity);
if ( pRagdoll )
{
pRagdoll->GetAngleOverrideFromCurrentState( pOut, size );
}
}