//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Implements the Sticky Bolt code. This constraints ragdolls to the world // after being hit by a crossbow bolt. If something here is acting funny // let me know - Adrian. // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "c_basetempentity.h" #include "fx.h" #include "decals.h" #include "iefx.h" #include "engine/IEngineSound.h" #include "materialsystem/imaterialvar.h" #include "IEffects.h" #include "engine/IEngineTrace.h" #include "vphysics/constraints.h" #include "engine/ivmodelinfo.h" #include "tempent.h" #include "c_te_legacytempents.h" #include "engine/ivdebugoverlay.h" #include "c_te_effect_dispatch.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern IPhysicsSurfaceProps *physprops; IPhysicsObject *GetWorldPhysObject( void ); extern ITempEnts* tempents; class CRagdollBoltEnumerator : public IPartitionEnumerator { public: //Forced constructor CRagdollBoltEnumerator( Ray_t& shot, Vector vOrigin ) { m_rayShot = shot; m_vWorld = vOrigin; } //Actual work code IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) { C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() ); if ( pEnt == NULL ) return ITERATION_CONTINUE; C_BaseAnimating *pModel = static_cast< C_BaseAnimating * >( pEnt ); if ( pModel == NULL ) return ITERATION_CONTINUE; trace_t tr; enginetrace->ClipRayToEntity( m_rayShot, MASK_SHOT, pModel, &tr ); IPhysicsObject *pPhysicsObject = NULL; //Find the real object we hit. if( tr.physicsbone >= 0 ) { if ( pModel->m_pRagdoll ) { CRagdoll *pCRagdoll = dynamic_cast < CRagdoll * > ( pModel->m_pRagdoll ); if ( pCRagdoll ) { ragdoll_t *pRagdollT = pCRagdoll->GetRagdoll(); if ( tr.physicsbone < pRagdollT->listCount ) { pPhysicsObject = pRagdollT->list[tr.physicsbone].pObject; } } } } if ( pPhysicsObject == NULL ) return ITERATION_CONTINUE; if ( tr.fraction < 1.0 ) { IPhysicsObject *pReference = GetWorldPhysObject(); if ( pReference == NULL || pPhysicsObject == NULL ) return ITERATION_CONTINUE; float flMass = pPhysicsObject->GetMass(); pPhysicsObject->SetMass( flMass * 2 ); constraint_ballsocketparams_t ballsocket; ballsocket.Defaults(); pReference->WorldToLocal( &ballsocket.constraintPosition[0], m_vWorld ); pPhysicsObject->WorldToLocal( &ballsocket.constraintPosition[1], tr.endpos ); physenv->CreateBallsocketConstraint( pReference, pPhysicsObject, NULL, ballsocket ); //Play a sound CPASAttenuationFilter filter( pEnt ); EmitSound_t ep; ep.m_nChannel = CHAN_VOICE; ep.m_pSoundName = "Weapon_Crossbow.BoltSkewer"; ep.m_flVolume = 1.0f; ep.m_SoundLevel = SNDLVL_NORM; ep.m_pOrigin = &pEnt->GetAbsOrigin(); C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); return ITERATION_STOP; } return ITERATION_CONTINUE; } private: Ray_t m_rayShot; Vector m_vWorld; }; void CreateCrossbowBolt( const Vector &vecOrigin, const Vector &vecDirection ) { model_t *pModel = (model_t *)engine->LoadModel( "models/crossbow_bolt.mdl" ); QAngle vAngles; VectorAngles( vecDirection, vAngles ); if ( gpGlobals->maxClients > 1 ) { tempents->SpawnTempModel( pModel, vecOrigin - vecDirection * 8, vAngles, Vector(0, 0, 0 ), 30.0f, FTENT_NONE ); } else { tempents->SpawnTempModel( pModel, vecOrigin - vecDirection * 8, vAngles, Vector(0, 0, 0 ), 1, FTENT_NEVERDIE ); } } void StickRagdollNow( const Vector &vecOrigin, const Vector &vecDirection ) { Ray_t shotRay; trace_t tr; UTIL_TraceLine( vecOrigin, vecOrigin + vecDirection * 16, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); if ( tr.surface.flags & SURF_SKY ) return; Vector vecEnd = vecOrigin - vecDirection * 128; shotRay.Init( vecOrigin, vecEnd ); CRagdollBoltEnumerator ragdollEnum( shotRay, vecOrigin ); ::partition->EnumerateElementsAlongRay( PARTITION_CLIENT_RESPONSIVE_EDICTS, shotRay, false, &ragdollEnum ); CreateCrossbowBolt( vecOrigin, vecDirection ); } //----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void StickyBoltCallback( const CEffectData &data ) { StickRagdollNow( data.m_vOrigin, data.m_vNormal ); } DECLARE_CLIENT_EFFECT( "BoltImpact", StickyBoltCallback );