You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1683 lines
38 KiB
1683 lines
38 KiB
/*** |
|
* |
|
* Copyright (c) 2001, Valve LLC. All rights reserved. |
|
* |
|
* This product contains software technology licensed from Id |
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. |
|
* All Rights Reserved. |
|
* |
|
* Use, distribution, and modification of this source code and/or resulting |
|
* object code is restricted to non-commercial enhancements to products from |
|
* Valve LLC. All other use, distribution, or modification is prohibited |
|
* without written permission from Valve LLC. |
|
* |
|
****/ |
|
|
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "squadmonster.h" |
|
#include "player.h" |
|
#include "weapons.h" |
|
#include "decals.h" |
|
#include "gamerules.h" |
|
#include "effects.h" |
|
#include "saverestore.h" |
|
|
|
#include "ropes.h" |
|
|
|
#define HOOK_CONSTANT 2500.0f |
|
#define SPRING_DAMPING 0.1f |
|
#define ROPE_IGNORE_SAMPLES 4 // integrator may be hanging if less than |
|
|
|
//#define SetAbsOrigin(x) pev->origin = x; |
|
#define SetAbsAngles(x) pev->angles = x; |
|
#define SetAbsVelociy(x) pev->velocity = x; |
|
#define SetNextThink(x) pev->nextthink = x; |
|
#define SetEffects(x) pev->effects = x; |
|
#define SetSolidType(x) pev->solid = x; |
|
#define AddFlags(x) pev->flags |= x; |
|
#define SetMoveType(x) pev->movetype = x; |
|
#define AddEffectsFlags(x) pev->effects |= x; |
|
|
|
/** |
|
* Data for a single rope joint. |
|
*/ |
|
struct RopeSampleData |
|
{ |
|
Vector mPosition; |
|
Vector mVelocity; |
|
Vector mForce; |
|
Vector mExternalForce; |
|
|
|
bool mApplyExternalForce; |
|
float mMassReciprocal; |
|
float restLength; |
|
}; |
|
|
|
#define MAX_LIST_SEGMENTS 5 |
|
RopeSampleData g_pTempList[MAX_LIST_SEGMENTS][MAX_SEGMENTS]; |
|
|
|
/** |
|
* Represents a single joint in a rope. There are numSegments + 1 samples in a rope. |
|
*/ |
|
class CRopeSample : public CBaseEntity |
|
{ |
|
public: |
|
|
|
void Spawn(); |
|
virtual int Save( CSave &save ); |
|
virtual int Restore( CRestore &restore ); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
|
|
static CRopeSample* CreateSample(); |
|
|
|
RopeSampleData* GetData() { |
|
if (swapped) |
|
return &data2; |
|
else |
|
return &data; |
|
} |
|
RopeSampleData* GetData2() { |
|
if (swapped) |
|
return &data; |
|
else |
|
return &data2; |
|
} |
|
|
|
void Swap() |
|
{ |
|
swapped = !swapped; |
|
} |
|
|
|
private: |
|
RopeSampleData data; |
|
RopeSampleData data2; |
|
BOOL swapped; |
|
}; |
|
|
|
|
|
class CRopeSegment : public CBaseAnimating |
|
{ |
|
public: |
|
|
|
void Precache(); |
|
|
|
void Spawn(); |
|
|
|
void Touch( CBaseEntity* pOther ); |
|
|
|
void SetAbsOrigin(const Vector& pos) |
|
{ |
|
pev->origin = pos; |
|
} |
|
|
|
static CRopeSegment* CreateSegment(CRopeSample* pSample, string_t iszModelName , CRope *rope); |
|
|
|
CRopeSample* GetSample() { return m_Sample; } |
|
|
|
void ApplyExternalForce( const Vector& vecForce ); |
|
|
|
void SetCauseDamageOnTouch( const bool bCauseDamage ); |
|
void SetCanBeGrabbed( const bool bCanBeGrabbed ); |
|
CRope* GetMasterRope() { return mMasterRope; } |
|
void SetMasterRope( CRope* pRope ) |
|
{ |
|
mMasterRope = pRope; |
|
} |
|
|
|
virtual int Save( CSave &save ); |
|
virtual int Restore( CRestore &restore ); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
|
|
private: |
|
CRopeSample* m_Sample; |
|
string_t mModelName; |
|
float mDefaultMass; |
|
bool mCauseDamage; |
|
bool mCanBeGrabbed; |
|
CRope* mMasterRope; |
|
}; |
|
|
|
static const char* const g_pszCreakSounds[] = |
|
{ |
|
"items/rope1.wav", |
|
"items/rope2.wav", |
|
"items/rope3.wav" |
|
}; |
|
|
|
TYPEDESCRIPTION CRope::m_SaveData[] = |
|
{ DEFINE_FIELD( CRope, m_iSegments, FIELD_INTEGER ), |
|
DEFINE_FIELD( CRope, m_bToggle, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRope, m_InitialDeltaTime, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRope, mLastTime, FIELD_TIME ), |
|
DEFINE_FIELD( CRope, m_LastEndPos, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( CRope, m_Gravity, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRope, m_NumSamples, FIELD_INTEGER ), |
|
DEFINE_FIELD( CRope, mObjectAttached, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRope, mAttachedObjectsSegment, FIELD_INTEGER ), |
|
DEFINE_FIELD( CRope, detachTime, FIELD_TIME ), |
|
DEFINE_ARRAY( CRope, seg, FIELD_CLASSPTR, MAX_SEGMENTS ), |
|
DEFINE_ARRAY( CRope, altseg, FIELD_CLASSPTR, MAX_SEGMENTS ), |
|
DEFINE_ARRAY( CRope, m_Samples, FIELD_CLASSPTR, MAX_SAMPLES ), |
|
DEFINE_FIELD( CRope, mDisallowPlayerAttachment, FIELD_INTEGER ), |
|
DEFINE_FIELD( CRope, mBodyModel, FIELD_STRING ), |
|
DEFINE_FIELD( CRope, mEndingModel, FIELD_STRING ), |
|
DEFINE_FIELD( CRope, mAttachedObjectsOffset, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRope, m_bMakeSound, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRope, m_activated, FIELD_CHARACTER ), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE( CRope, CBaseDelay ) |
|
|
|
LINK_ENTITY_TO_CLASS( env_rope, CRope ) |
|
|
|
void CRope::KeyValue( KeyValueData* pkvd ) |
|
{ |
|
if( FStrEq( pkvd->szKeyName, "segments" ) ) |
|
{ |
|
pkvd->fHandled = true; |
|
|
|
m_iSegments = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
if( m_iSegments >= MAX_SEGMENTS ) |
|
m_iSegments = MAX_SEGMENTS - 1; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "bodymodel" ) ) |
|
{ |
|
pkvd->fHandled = true; |
|
|
|
mBodyModel = ALLOC_STRING( pkvd->szValue ); |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "endingmodel" ) ) |
|
{ |
|
pkvd->fHandled = true; |
|
|
|
mEndingModel = ALLOC_STRING( pkvd->szValue ); |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "disable" ) ) |
|
{ |
|
pkvd->fHandled = true; |
|
|
|
mDisallowPlayerAttachment = strtol( pkvd->szValue, NULL, 10 ); |
|
} |
|
else |
|
CBaseDelay::KeyValue( pkvd ); |
|
} |
|
|
|
CRope::CRope() |
|
{ |
|
mBodyModel = MAKE_STRING( "models/rope16.mdl" ); |
|
mEndingModel = MAKE_STRING( "models/rope16.mdl" ); |
|
|
|
} |
|
|
|
void CRope::Precache() |
|
{ |
|
CBaseDelay::Precache(); |
|
|
|
UTIL_PrecacheOther( "rope_segment" ); |
|
UTIL_PrecacheOther( "rope_sample" ); |
|
|
|
PRECACHE_MODEL(STRING(GetBodyModel())); |
|
PRECACHE_MODEL(STRING(GetEndingModel())); |
|
PRECACHE_SOUND_ARRAY( g_pszCreakSounds ); |
|
} |
|
|
|
void CRope::Spawn() |
|
{ |
|
pev->classname = MAKE_STRING( "env_rope" ); |
|
|
|
m_bMakeSound = true; |
|
|
|
Precache(); |
|
|
|
m_Gravity.x = m_Gravity.y = 0; |
|
m_Gravity.z = -50; |
|
|
|
mObjectAttached = false; |
|
|
|
|
|
m_NumSamples = m_iSegments + 1; |
|
|
|
m_activated = false; |
|
} |
|
|
|
void CRope::Activate() |
|
{ |
|
if (!m_activated) |
|
{ |
|
InitRope(); |
|
m_activated = true; |
|
} |
|
} |
|
|
|
void CRope::InitRope() |
|
{ |
|
AddFlags( FL_ALWAYSTHINK ); |
|
|
|
for( int uiSample = 0; uiSample < m_NumSamples; ++uiSample ) |
|
{ |
|
m_Samples[ uiSample ] = CRopeSample::CreateSample(); |
|
UTIL_SetOrigin(m_Samples[ uiSample ]->pev, pev->origin); |
|
} |
|
|
|
memset( m_Samples + m_NumSamples, 0, sizeof( CRopeSample* ) * ( MAX_SAMPLES - m_NumSamples ) ); |
|
|
|
{ |
|
CRopeSegment* pSegment = seg[ 0 ] = CRopeSegment::CreateSegment( m_Samples[ 0 ], GetBodyModel(), this ); |
|
|
|
pSegment->SetAbsOrigin( pev->origin ); |
|
|
|
pSegment = altseg[ 0 ] = CRopeSegment::CreateSegment( m_Samples[ 0 ], GetBodyModel(), this ); |
|
|
|
pSegment->SetAbsOrigin( pev->origin ); |
|
} |
|
|
|
Vector origin; |
|
Vector angles; |
|
|
|
const Vector vecGravity = m_Gravity.Normalize(); |
|
|
|
if( m_iSegments > 2 ) |
|
{ |
|
CRopeSample** ppCurrentSys = m_Samples; |
|
|
|
for( int uiSeg = 1; uiSeg < m_iSegments - 1; ++uiSeg ) |
|
{ |
|
CRopeSample* pSegSample = m_Samples[ uiSeg ]; |
|
seg[ uiSeg ] = CRopeSegment::CreateSegment( pSegSample, GetBodyModel(), this ); |
|
|
|
altseg[ uiSeg ] = CRopeSegment::CreateSegment( pSegSample, GetBodyModel(), this ); |
|
|
|
CRopeSegment* pCurrent = seg[ uiSeg - 1 ]; |
|
|
|
pCurrent->GetAttachment( 0, origin, angles ); |
|
|
|
Vector vecPos = origin - pCurrent->pev->origin; |
|
|
|
const float flLength = vecPos.Length(); |
|
|
|
origin = flLength * vecGravity + pCurrent->pev->origin; |
|
|
|
seg[ uiSeg ]->SetAbsOrigin( origin ); |
|
altseg[ uiSeg ]->SetAbsOrigin( origin ); |
|
} |
|
} |
|
|
|
CRopeSample* pSegSample = m_Samples[ m_iSegments - 1 ]; |
|
seg[ m_iSegments - 1 ] = CRopeSegment::CreateSegment( pSegSample, GetEndingModel(), this ); |
|
|
|
altseg[ m_iSegments - 1 ] = CRopeSegment::CreateSegment( pSegSample, GetEndingModel(), this ); |
|
|
|
CRopeSegment* pCurrent = seg[ m_iSegments - 2 ]; |
|
|
|
pCurrent->GetAttachment( 0, origin, angles ); |
|
|
|
Vector vecPos = origin - pCurrent->pev->origin; |
|
|
|
const float flLength = vecPos.Length(); |
|
|
|
origin = flLength * vecGravity + pCurrent->pev->origin; |
|
|
|
seg[ m_iSegments - 1 ]->SetAbsOrigin( origin ); |
|
altseg[ m_iSegments - 1 ]->SetAbsOrigin( origin ); |
|
|
|
memset( seg + m_iSegments, 0, sizeof( CRopeSegment* ) * ( MAX_SEGMENTS - m_iSegments ) ); |
|
memset( altseg + m_iSegments, 0, sizeof( CRopeSegment* ) * ( MAX_SEGMENTS - m_iSegments ) ); |
|
|
|
m_InitialDeltaTime = true; |
|
|
|
InitializeRopeSim(); |
|
|
|
SetThink(&CRope::RopeThink); |
|
SetNextThink( gpGlobals->time + 0.01 ); |
|
} |
|
|
|
void CRope::RopeThink() |
|
{ |
|
m_bToggle = !m_bToggle; |
|
|
|
RunSimOnSamples(); |
|
|
|
CRopeSegment** ppPrimarySegs; |
|
CRopeSegment** ppHiddenSegs; |
|
|
|
if( m_bToggle ) |
|
{ |
|
ppPrimarySegs = altseg; |
|
ppHiddenSegs = seg; |
|
} |
|
else |
|
{ |
|
ppPrimarySegs = seg; |
|
ppHiddenSegs = altseg; |
|
} |
|
|
|
SetRopeSegments( m_iSegments, ppPrimarySegs, ppHiddenSegs ); |
|
|
|
if( ShouldCreak() ) |
|
{ |
|
Creak(); |
|
} |
|
|
|
SetNextThink( gpGlobals->time + 0.001 ); |
|
} |
|
|
|
void CRope::InitializeRopeSim() |
|
{ |
|
int uiIndex; |
|
|
|
for( int uiSeg = 0; uiSeg < m_iSegments; ++uiSeg ) |
|
{ |
|
CRopeSegment* pSegment = seg[ uiSeg ]; |
|
CRopeSample* pSample = pSegment->GetSample(); |
|
|
|
RopeSampleData *data = pSample->GetData(); |
|
|
|
data->mPosition = pSegment->pev->origin; |
|
|
|
data->mVelocity = g_vecZero; |
|
data->mForce = g_vecZero; |
|
data->mMassReciprocal = 1; |
|
data->mApplyExternalForce = false; |
|
data->mExternalForce = g_vecZero; |
|
|
|
Vector vecOrigin, vecAngles; |
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
data->restLength = ( pSegment->pev->origin - vecOrigin ).Length(); |
|
} |
|
|
|
{ |
|
//Zero out the anchored segment's mass so it stays in place. |
|
CRopeSample *pSample = m_Samples[ 0 ]; |
|
|
|
pSample->GetData()->mMassReciprocal = 0; |
|
} |
|
|
|
CRopeSegment* pSegment = seg[ m_iSegments - 1 ]; |
|
|
|
Vector vecOrigin, vecAngles; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
Vector vecDistance = vecOrigin - pSegment->pev->origin; |
|
|
|
const float flLength = vecDistance.Length(); |
|
|
|
const Vector vecGravity = m_Gravity.Normalize(); |
|
|
|
vecOrigin = vecGravity * flLength + pSegment->pev->origin; |
|
|
|
CRopeSample* pSample = m_Samples[ m_NumSamples - 1 ]; |
|
|
|
RopeSampleData *data = pSample->GetData(); |
|
|
|
data->mPosition = vecOrigin; |
|
|
|
m_LastEndPos = vecOrigin; |
|
|
|
data->mVelocity = g_vecZero; |
|
|
|
data->mForce = g_vecZero; |
|
|
|
data->mMassReciprocal = 0.2; |
|
|
|
data->mApplyExternalForce = false; |
|
|
|
int uiNumSegs = ROPE_IGNORE_SAMPLES; |
|
|
|
if( m_iSegments <= ROPE_IGNORE_SAMPLES ) |
|
uiNumSegs = m_iSegments; |
|
|
|
for( uiIndex = 0; uiIndex < uiNumSegs; ++uiIndex ) |
|
{ |
|
seg[ uiIndex ]->SetCanBeGrabbed( false ); |
|
altseg[ uiIndex ]->SetCanBeGrabbed( false ); |
|
} |
|
} |
|
|
|
void CRope::RunSimOnSamples() |
|
{ |
|
float flDeltaTime = 0.025; |
|
|
|
if( m_InitialDeltaTime ) |
|
{ |
|
m_InitialDeltaTime = false; |
|
mLastTime = gpGlobals->time; |
|
flDeltaTime = 0; |
|
} |
|
|
|
int uiIndex = 0; |
|
|
|
while( true ) |
|
{ |
|
++uiIndex; |
|
|
|
ComputeForces( m_Samples ); |
|
RK4Integrate( flDeltaTime ); |
|
|
|
mLastTime += 0.007; |
|
|
|
if( gpGlobals->time <= mLastTime ) |
|
{ |
|
if( ( uiIndex % 2 ) != 0 ) |
|
break; |
|
} |
|
|
|
for (int i=0; i<m_NumSamples; ++i) |
|
{ |
|
m_Samples[i]->Swap(); |
|
} |
|
|
|
//CRopeSample **swap = ppSampleSource; |
|
//ppSampleSource = ppSampleTarget; |
|
//ppSampleTarget = swap; |
|
|
|
//std::swap( ppSampleSource, ppSampleTarget ); |
|
|
|
} |
|
|
|
mLastTime = gpGlobals->time; |
|
} |
|
|
|
void CRope::ComputeForces( RopeSampleData* pSystem ) |
|
{ |
|
int uiIndex; |
|
for( uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex ) |
|
{ |
|
ComputeSampleForce( pSystem[ uiIndex ] ); |
|
} |
|
|
|
for( uiIndex = 0; uiIndex < m_iSegments; ++uiIndex ) |
|
{ |
|
ComputeSpringForce( pSystem[ uiIndex ], pSystem[ uiIndex+1 ] ); |
|
} |
|
} |
|
|
|
void CRope::ComputeForces( CRopeSample** ppSystem ) |
|
{ |
|
int uiIndex; |
|
for( uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex ) |
|
{ |
|
ComputeSampleForce( *ppSystem[ uiIndex ]->GetData() ); |
|
} |
|
|
|
for( uiIndex = 0; uiIndex < m_iSegments; ++uiIndex ) |
|
{ |
|
ComputeSpringForce( *ppSystem[ uiIndex ]->GetData(), *ppSystem[ uiIndex+1 ]->GetData() ); |
|
} |
|
} |
|
|
|
void CRope::ComputeSampleForce( RopeSampleData& data ) |
|
{ |
|
data.mForce = g_vecZero; |
|
|
|
if( data.mMassReciprocal != 0.0 ) |
|
{ |
|
data.mForce = data.mForce + ( m_Gravity / data.mMassReciprocal ); |
|
} |
|
|
|
if( data.mApplyExternalForce ) |
|
{ |
|
data.mForce = data.mForce + data.mExternalForce; |
|
|
|
data.mExternalForce = g_vecZero; |
|
data.mApplyExternalForce = false; |
|
} |
|
|
|
if( DotProduct( m_Gravity, data.mVelocity ) >= 0 ) |
|
{ |
|
data.mForce = data.mForce + data.mVelocity * -0.04; |
|
} |
|
else |
|
{ |
|
data.mForce = data.mForce - data.mVelocity; |
|
} |
|
} |
|
|
|
void CRope::ComputeSpringForce( RopeSampleData& first, RopeSampleData& second ) |
|
{ |
|
Vector vecDist = first.mPosition - second.mPosition; |
|
|
|
const double flDistance = vecDist.Length(); |
|
|
|
const double flForce = ( flDistance - first.restLength ) * HOOK_CONSTANT; |
|
|
|
const double flNewRelativeDist = DotProduct( first.mVelocity - second.mVelocity, vecDist ) * SPRING_DAMPING; |
|
|
|
vecDist = vecDist.Normalize(); |
|
|
|
const double flSpringFactor = -( flNewRelativeDist / flDistance + flForce ); |
|
|
|
const Vector vecForce = flSpringFactor * vecDist; |
|
|
|
first.mForce = first.mForce + vecForce; |
|
|
|
second.mForce = second.mForce - vecForce; |
|
} |
|
|
|
void CRope::RK4Integrate( const float flDeltaTime ) |
|
{ |
|
const float flDeltas[ MAX_LIST_SEGMENTS - 1 ] = |
|
{ |
|
flDeltaTime * 0.5f, |
|
flDeltaTime * 0.5f, |
|
flDeltaTime * 0.5f, |
|
flDeltaTime |
|
}; |
|
|
|
{ |
|
RopeSampleData* pTemp1 = g_pTempList[ 0 ]; |
|
RopeSampleData* pTemp2 = g_pTempList[ 1 ]; |
|
|
|
for( int uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 ) |
|
{ |
|
RopeSampleData *data = m_Samples[ uiIndex ]->GetData(); |
|
|
|
pTemp2->mForce = data->mMassReciprocal * data->mForce * flDeltas[ 0 ]; |
|
pTemp2->mVelocity = data->mVelocity * flDeltas[ 0 ]; |
|
pTemp2->restLength = data->restLength; |
|
|
|
pTemp1->mMassReciprocal = data->mMassReciprocal; |
|
pTemp1->mVelocity = data->mVelocity + pTemp2->mForce; |
|
pTemp1->mPosition = data->mPosition + pTemp2->mVelocity; |
|
pTemp1->restLength = data->restLength; |
|
} |
|
|
|
ComputeForces( g_pTempList[ 0 ] ); |
|
} |
|
|
|
for( int uiStep = 2; uiStep < MAX_LIST_SEGMENTS - 1; ++uiStep ) |
|
{ |
|
RopeSampleData* pTemp1 = g_pTempList[ 0 ]; |
|
RopeSampleData* pTemp2 = g_pTempList[ uiStep ]; |
|
|
|
for( int uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 ) |
|
{ |
|
RopeSampleData *data = m_Samples[ uiIndex ]->GetData(); |
|
|
|
pTemp2->mForce = data->mMassReciprocal * pTemp1->mForce * flDeltas[ uiStep - 1 ]; |
|
pTemp2->mVelocity = pTemp1->mVelocity * flDeltas[ uiStep - 1 ]; |
|
pTemp2->restLength = data->restLength; |
|
|
|
pTemp1->mMassReciprocal = data->mMassReciprocal; |
|
pTemp1->mVelocity = data->mVelocity + pTemp2->mForce; |
|
pTemp1->mPosition = data->mPosition + pTemp2->mVelocity; |
|
pTemp1->restLength = data->restLength; |
|
} |
|
|
|
ComputeForces( g_pTempList[ 0 ] ); |
|
} |
|
|
|
{ |
|
RopeSampleData* pTemp1 = g_pTempList[ 0 ]; |
|
RopeSampleData* pTemp2 = g_pTempList[ 4 ]; |
|
|
|
for( int uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 ) |
|
{ |
|
RopeSampleData *data = m_Samples[ uiIndex ]->GetData(); |
|
|
|
pTemp2->mForce = data->mMassReciprocal * pTemp1->mForce * flDeltas[ 3 ]; |
|
|
|
pTemp2->mVelocity = pTemp1->mVelocity * flDeltas[ 3 ]; |
|
} |
|
} |
|
|
|
RopeSampleData* pTemp1 = g_pTempList[ 1 ]; |
|
RopeSampleData* pTemp2 = g_pTempList[ 2 ]; |
|
RopeSampleData* pTemp3 = g_pTempList[ 3 ]; |
|
RopeSampleData* pTemp4 = g_pTempList[ 4 ]; |
|
|
|
for( int uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2, ++pTemp3, ++pTemp4 ) |
|
{ |
|
RopeSampleData *pSource = m_Samples[ uiIndex ]->GetData(); |
|
RopeSampleData *pTarget = m_Samples[ uiIndex ]->GetData2(); |
|
|
|
const Vector vecPosChange = 1.0f / 6.0f * ( pTemp1->mVelocity + ( pTemp2->mVelocity + pTemp3->mVelocity ) * 2 + pTemp4->mVelocity ); |
|
|
|
const Vector vecVelChange = 1.0f / 6.0f * ( pTemp1->mForce + ( pTemp2->mForce + pTemp3->mForce ) * 2 + pTemp4->mForce ); |
|
|
|
pTarget->mPosition = pSource->mPosition + ( vecPosChange );//* flDeltaTime ); |
|
|
|
pTarget->mVelocity = pSource->mVelocity + ( vecVelChange );//* flDeltaTime ); |
|
} |
|
} |
|
|
|
//TODO move to common header - Solokiller |
|
static const Vector DOWN( 0, 0, -1 ); |
|
|
|
static const Vector RIGHT( 0, 1, 0 ); |
|
|
|
void GetAlignmentAngles( const Vector& vecTop, const Vector& vecBottom, Vector& vecOut ) |
|
{ |
|
Vector vecDist = vecBottom - vecTop; |
|
|
|
Vector vecResult = vecDist.Normalize(); |
|
|
|
const float flRoll = acos( DotProduct( vecResult, RIGHT ) ) * ( 180.0 / M_PI ); |
|
|
|
vecOut.z = -flRoll; |
|
|
|
vecDist.y = 0; |
|
|
|
vecResult = vecDist.Normalize(); |
|
|
|
const float flPitch = acos( DotProduct( vecResult, DOWN ) ) * ( 180.0 / M_PI ); |
|
|
|
vecOut.x = ( vecResult.x >= 0.0 ) ? flPitch : -flPitch; |
|
vecOut.y = 0; |
|
} |
|
|
|
void TruncateEpsilon( Vector& vec ) |
|
{ |
|
Vector vec1 = vec * 10.0; |
|
vec1.x += 0.5; |
|
vec = vec1 / 10; |
|
} |
|
|
|
void CRope::TraceModels( CRopeSegment** ppPrimarySegs, CRopeSegment** ppHiddenSegs ) |
|
{ |
|
if( m_iSegments > 1 ) |
|
{ |
|
Vector vecAngles; |
|
|
|
GetAlignmentAngles( |
|
m_Samples[ 0 ]->GetData()->mPosition, |
|
m_Samples[ 1 ]->GetData()->mPosition, |
|
vecAngles ); |
|
|
|
( *ppPrimarySegs )->SetAbsAngles( vecAngles ); |
|
} |
|
|
|
TraceResult tr; |
|
|
|
if( mObjectAttached ) |
|
{ |
|
for( int uiSeg = 1; uiSeg < m_iSegments; ++uiSeg ) |
|
{ |
|
CRopeSample* pSample = m_Samples[ uiSeg ]; |
|
|
|
Vector vecDist = pSample->GetData()->mPosition - ppHiddenSegs[ uiSeg ]->pev->origin; |
|
|
|
vecDist = vecDist.Normalize(); |
|
|
|
const float flTraceDist = ( uiSeg - mAttachedObjectsSegment + 2 ) < 5 ? 50 : 10; |
|
|
|
const Vector vecTraceDist = vecDist * flTraceDist; |
|
|
|
const Vector vecEnd = pSample->GetData()->mPosition + vecTraceDist; |
|
|
|
UTIL_TraceLine( ppHiddenSegs[ uiSeg ]->pev->origin, vecEnd, ignore_monsters, edict(), &tr ); |
|
|
|
if( tr.flFraction == 1.0 && tr.fAllSolid ) |
|
{ |
|
break; |
|
} |
|
|
|
if( tr.flFraction != 1.0 || tr.fStartSolid || !tr.fInOpen ) |
|
{ |
|
Vector vecOrigin = tr.vecEndPos - vecTraceDist; |
|
|
|
TruncateEpsilon( vecOrigin ); |
|
|
|
ppPrimarySegs[ uiSeg ]->SetAbsOrigin( vecOrigin ); |
|
|
|
Vector vecNormal = tr.vecPlaneNormal.Normalize() * 20000.0; |
|
|
|
RopeSampleData *data = ppPrimarySegs[ uiSeg ]->GetSample()->GetData(); |
|
|
|
data->mApplyExternalForce = true; |
|
|
|
data->mExternalForce = vecNormal; |
|
|
|
data->mVelocity = g_vecZero; |
|
} |
|
else |
|
{ |
|
Vector vecOrigin = pSample->GetData()->mPosition; |
|
|
|
TruncateEpsilon( vecOrigin ); |
|
|
|
ppPrimarySegs[ uiSeg ]->SetAbsOrigin( vecOrigin ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
for( int uiSeg = 1; uiSeg < m_iSegments; ++uiSeg ) |
|
{ |
|
UTIL_TraceLine( |
|
ppHiddenSegs[ uiSeg ]->pev->origin, |
|
m_Samples[ uiSeg ]->GetData()->mPosition, |
|
ignore_monsters, edict(), &tr ); |
|
|
|
if( tr.flFraction == 1.0 ) |
|
{ |
|
Vector vecOrigin = m_Samples[ uiSeg ]->GetData()->mPosition; |
|
|
|
TruncateEpsilon( vecOrigin ); |
|
|
|
ppPrimarySegs[ uiSeg ]->SetAbsOrigin( vecOrigin ); |
|
} |
|
else |
|
{ |
|
CBaseEntity* pEnt = (CBaseEntity*)GET_PRIVATE( tr.pHit ); |
|
const Vector vecNormal = tr.vecPlaneNormal.Normalize(); |
|
|
|
Vector vecOrigin = tr.vecEndPos + vecNormal * 10.0; |
|
|
|
TruncateEpsilon( vecOrigin ); |
|
|
|
ppPrimarySegs[ uiSeg ]->SetAbsOrigin( vecOrigin ); |
|
|
|
ppPrimarySegs[ uiSeg ]->GetSample()->GetData()->mApplyExternalForce = true; |
|
|
|
ppPrimarySegs[ uiSeg ]->GetSample()->GetData()->mExternalForce = vecNormal * 40000.0; |
|
} |
|
} |
|
} |
|
|
|
Vector vecAngles; |
|
|
|
for( int uiSeg = 1; uiSeg < m_iSegments; ++uiSeg ) |
|
{ |
|
CRopeSegment *pSegment = ppPrimarySegs[ uiSeg - 1 ]; |
|
CRopeSegment *pSegment2 = ppPrimarySegs[ uiSeg ]; |
|
|
|
GetAlignmentAngles( pSegment->pev->origin, pSegment2->pev->origin, vecAngles ); |
|
|
|
pSegment->SetAbsAngles( vecAngles ); |
|
} |
|
|
|
if( m_iSegments > 1 ) |
|
{ |
|
CRopeSample *pSample = m_Samples[ m_NumSamples - 1 ]; |
|
|
|
UTIL_TraceLine( m_LastEndPos, pSample->GetData()->mPosition, ignore_monsters, edict(), &tr ); |
|
|
|
if( tr.flFraction == 1.0 ) |
|
{ |
|
m_LastEndPos = pSample->GetData()->mPosition; |
|
} |
|
else |
|
{ |
|
m_LastEndPos = tr.vecEndPos; |
|
|
|
pSample->GetData()->mApplyExternalForce = true; |
|
|
|
pSample->GetData()->mExternalForce = tr.vecPlaneNormal.Normalize() * 40000.0; |
|
} |
|
|
|
CRopeSegment *pSegment = ppPrimarySegs[ m_NumSamples - 2 ]; |
|
|
|
Vector vecAngles; |
|
|
|
GetAlignmentAngles( pSegment->pev->origin, m_LastEndPos, vecAngles ); |
|
|
|
pSegment->SetAbsAngles( vecAngles ); |
|
} |
|
} |
|
|
|
void CRope::SetRopeSegments( const int uiNumSegments, |
|
CRopeSegment** ppPrimarySegs, CRopeSegment** ppHiddenSegs ) |
|
{ |
|
if( uiNumSegments > 0 ) |
|
{ |
|
TraceModels( ppPrimarySegs, ppHiddenSegs ); |
|
|
|
ppPrimarySegs[ 0 ]->SetSolidType( SOLID_TRIGGER ); |
|
ppPrimarySegs[ 0 ]->SetEffects( 0 ); |
|
|
|
ppHiddenSegs[ 0 ]->SetSolidType( SOLID_NOT ); |
|
ppHiddenSegs[ 0 ]->SetEffects( EF_NODRAW ); |
|
|
|
for( int uiIndex = 1; uiIndex < uiNumSegments; ++uiIndex ) |
|
{ |
|
CRopeSegment* pPrim = ppPrimarySegs[ uiIndex ]; |
|
CRopeSegment* pHidden = ppHiddenSegs[ uiIndex ]; |
|
|
|
pPrim->SetSolidType( SOLID_TRIGGER ); |
|
pPrim->SetEffects( 0 ); |
|
|
|
pHidden->SetSolidType( SOLID_NOT ); |
|
pHidden->SetEffects( EF_NODRAW ); |
|
|
|
Vector vecOrigin = pPrim->pev->origin; |
|
|
|
//vecOrigin.x += 10.0; |
|
//vecOrigin.y += 10.0; |
|
|
|
pHidden->SetAbsOrigin( vecOrigin ); |
|
} |
|
} |
|
} |
|
|
|
bool CRope::MoveUp( const float flDeltaTime ) |
|
{ |
|
if( mAttachedObjectsSegment > 4 ) |
|
{ |
|
float flDistance = flDeltaTime * 128.0; |
|
|
|
Vector vecOrigin, vecAngles; |
|
|
|
while( true ) |
|
{ |
|
float flOldDist = flDistance; |
|
|
|
flDistance = 0; |
|
|
|
if( flOldDist <= 0 ) |
|
break; |
|
|
|
if( mAttachedObjectsSegment <= 3 ) |
|
break; |
|
|
|
if( flOldDist > mAttachedObjectsOffset ) |
|
{ |
|
flDistance = flOldDist - mAttachedObjectsOffset; |
|
|
|
--mAttachedObjectsSegment; |
|
|
|
float flNewOffset = 0; |
|
|
|
if( mAttachedObjectsSegment < m_iSegments ) |
|
{ |
|
CRopeSegment *pSegment = seg[ mAttachedObjectsSegment ]; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
flNewOffset = ( pSegment->pev->origin - vecOrigin ).Length(); |
|
} |
|
|
|
mAttachedObjectsOffset = flNewOffset; |
|
} |
|
else |
|
{ |
|
mAttachedObjectsOffset -= flOldDist; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CRope::MoveDown( const float flDeltaTime ) |
|
{ |
|
if( !mObjectAttached ) |
|
return false; |
|
|
|
float flDistance = flDeltaTime * 128.0; |
|
|
|
Vector vecOrigin, vecAngles; |
|
|
|
CRopeSegment* pSegment; |
|
|
|
bool bOnRope = true; |
|
|
|
bool bDoIteration = true; |
|
|
|
while( bDoIteration ) |
|
{ |
|
bDoIteration = false; |
|
|
|
if( flDistance > 0.0 ) |
|
{ |
|
float flNewDist = flDistance; |
|
float flSegLength = 0.0; |
|
|
|
while( bOnRope ) |
|
{ |
|
if( mAttachedObjectsSegment < m_iSegments ) |
|
{ |
|
pSegment = seg[ mAttachedObjectsSegment ]; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
flSegLength = ( pSegment->pev->origin - vecOrigin ).Length(); |
|
} |
|
|
|
const float flOffset = flSegLength - mAttachedObjectsOffset; |
|
|
|
if( flNewDist <= flOffset ) |
|
{ |
|
mAttachedObjectsOffset += flNewDist; |
|
flDistance = 0; |
|
bDoIteration = true; |
|
break; |
|
} |
|
|
|
if( mAttachedObjectsSegment + 1 == m_iSegments ) |
|
bOnRope = false; |
|
else |
|
++mAttachedObjectsSegment; |
|
|
|
flNewDist -= flOffset; |
|
flSegLength = 0; |
|
|
|
mAttachedObjectsOffset = 0; |
|
|
|
if( flNewDist <= 0 ) |
|
break; |
|
} |
|
} |
|
} |
|
|
|
return bOnRope; |
|
} |
|
|
|
Vector CRope::GetAttachedObjectsVelocity() const |
|
{ |
|
if( !mObjectAttached ) |
|
return g_vecZero; |
|
|
|
return seg[ mAttachedObjectsSegment ]->GetSample()->GetData()->mVelocity; |
|
} |
|
|
|
void CRope::ApplyForceFromPlayer( const Vector& vecForce ) |
|
{ |
|
if( !mObjectAttached ) |
|
return; |
|
|
|
float flForce = 20000.0; |
|
|
|
if( m_iSegments < 26 ) |
|
flForce *= ( m_iSegments / 26.0 ); |
|
|
|
const Vector vecScaledForce = vecForce * flForce; |
|
|
|
ApplyForceToSegment( vecScaledForce, mAttachedObjectsSegment ); |
|
} |
|
|
|
void CRope::ApplyForceToSegment( const Vector& vecForce, const int uiSegment ) |
|
{ |
|
if( uiSegment < m_iSegments ) |
|
{ |
|
seg[ uiSegment ]->ApplyExternalForce( vecForce ); |
|
} |
|
else if( uiSegment == m_iSegments ) |
|
{ |
|
//Apply force to the last sample. |
|
|
|
RopeSampleData *data = m_Samples[ uiSegment - 1 ]->GetData(); |
|
|
|
data->mExternalForce = data->mExternalForce + vecForce; |
|
|
|
data->mApplyExternalForce = true; |
|
} |
|
} |
|
|
|
void CRope::AttachObjectToSegment( CRopeSegment* pSegment ) |
|
{ |
|
mObjectAttached = true; |
|
|
|
detachTime = 0; |
|
|
|
SetAttachedObjectsSegment( pSegment ); |
|
|
|
mAttachedObjectsOffset = 0; |
|
} |
|
|
|
void CRope::DetachObject() |
|
{ |
|
mObjectAttached = false; |
|
detachTime = gpGlobals->time; |
|
} |
|
|
|
bool CRope::IsAcceptingAttachment() const |
|
{ |
|
if( gpGlobals->time - detachTime > 2.0 && !mObjectAttached ) |
|
{ |
|
return mDisallowPlayerAttachment != 1; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CRope::ShouldCreak() const |
|
{ |
|
if( mObjectAttached && m_bMakeSound ) |
|
{ |
|
CRopeSample* pSample = seg[ mAttachedObjectsSegment ]->GetSample(); |
|
|
|
if( pSample->GetData()->mVelocity.Length() > 20.0 ) |
|
return RANDOM_LONG( 1, 5 ) == 1; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void CRope::Creak() |
|
{ |
|
EMIT_SOUND( edict(), CHAN_BODY, |
|
g_pszCreakSounds[ RANDOM_LONG( 0, ARRAYSIZE( g_pszCreakSounds ) - 1 ) ], |
|
VOL_NORM, ATTN_NORM ); |
|
} |
|
|
|
float CRope::GetSegmentLength( int uiSegmentIndex ) const |
|
{ |
|
if( uiSegmentIndex < m_iSegments ) |
|
{ |
|
Vector vecOrigin, vecAngles; |
|
|
|
CRopeSegment *pSegment = seg[ uiSegmentIndex ]; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
return ( pSegment->pev->origin - vecOrigin ).Length(); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
float CRope::GetRopeLength() const |
|
{ |
|
float flLength = 0; |
|
|
|
Vector vecOrigin, vecAngles; |
|
|
|
for( int uiIndex = 0; uiIndex < m_iSegments; ++uiIndex ) |
|
{ |
|
CRopeSegment *pSegment = seg[ uiIndex ]; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
flLength += ( pSegment->pev->origin - vecOrigin ).Length(); |
|
} |
|
|
|
return flLength; |
|
} |
|
|
|
Vector CRope::GetRopeOrigin() const |
|
{ |
|
return m_Samples[ 0 ]->GetData()->mPosition; |
|
} |
|
|
|
bool CRope::IsValidSegmentIndex( const int uiSegment ) const |
|
{ |
|
return uiSegment < m_iSegments; |
|
} |
|
|
|
Vector CRope::GetSegmentOrigin( const int uiSegment ) const |
|
{ |
|
if( !IsValidSegmentIndex( uiSegment ) ) |
|
return g_vecZero; |
|
|
|
return m_Samples[ uiSegment ]->GetData()->mPosition; |
|
} |
|
|
|
Vector CRope::GetSegmentAttachmentPoint( const int uiSegment ) const |
|
{ |
|
if( !IsValidSegmentIndex( uiSegment ) ) |
|
return g_vecZero; |
|
|
|
Vector vecOrigin, vecAngles; |
|
|
|
CRopeSegment *pSegment = m_bToggle ? altseg[ uiSegment ] : seg[ uiSegment ]; |
|
|
|
pSegment->GetAttachment( 0, vecOrigin, vecAngles ); |
|
|
|
return vecOrigin; |
|
} |
|
|
|
void CRope::SetAttachedObjectsSegment( CRopeSegment* pSegment ) |
|
{ |
|
for( int uiIndex = 0; uiIndex < m_iSegments; ++uiIndex ) |
|
{ |
|
if( seg[ uiIndex ] == pSegment || altseg[ uiIndex ] == pSegment ) |
|
{ |
|
mAttachedObjectsSegment = uiIndex; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
Vector CRope::GetSegmentDirFromOrigin( const int uiSegmentIndex ) const |
|
{ |
|
if( uiSegmentIndex >= m_iSegments ) |
|
return g_vecZero; |
|
|
|
//There is one more sample than there are segments, so this is fine. |
|
const Vector vecResult = |
|
m_Samples[ uiSegmentIndex + 1 ]->GetData()->mPosition - |
|
m_Samples[ uiSegmentIndex ]->GetData()->mPosition; |
|
|
|
return vecResult.Normalize(); |
|
} |
|
|
|
Vector CRope::GetAttachedObjectsPosition() const |
|
{ |
|
if( !mObjectAttached ) |
|
return g_vecZero; |
|
|
|
Vector vecResult; |
|
|
|
if( mAttachedObjectsSegment < m_iSegments ) |
|
vecResult = m_Samples[ mAttachedObjectsSegment ]->GetData()->mPosition; |
|
|
|
vecResult = vecResult + |
|
( mAttachedObjectsOffset * GetSegmentDirFromOrigin( mAttachedObjectsSegment ) ); |
|
|
|
return vecResult; |
|
} |
|
|
|
|
|
|
|
// Global Savedata for player |
|
TYPEDESCRIPTION CRopeSample::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CRopeSample, data.mPosition, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data.mVelocity, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data.mForce, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data.mExternalForce, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data.mApplyExternalForce, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRopeSample, data.mMassReciprocal, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRopeSample, data.restLength, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRopeSample, data2.mPosition, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data2.mVelocity, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data2.mForce, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data2.mExternalForce, FIELD_VECTOR ), |
|
DEFINE_FIELD( CRopeSample, data2.mApplyExternalForce, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRopeSample, data2.mMassReciprocal, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRopeSample, data2.restLength, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRopeSample, swapped, FIELD_BOOLEAN ), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE(CRopeSample, CBaseEntity) |
|
|
|
LINK_ENTITY_TO_CLASS( rope_sample, CRopeSample ); |
|
|
|
void CRopeSample::Spawn() |
|
{ |
|
pev->classname = MAKE_STRING( "rope_sample" ); |
|
|
|
AddEffectsFlags( EF_NODRAW ); |
|
} |
|
|
|
CRopeSample* CRopeSample::CreateSample() |
|
{ |
|
CRopeSample* pSample = GetClassPtr<CRopeSample>( NULL ); |
|
|
|
pSample->Spawn(); |
|
|
|
return pSample; |
|
} |
|
|
|
|
|
TYPEDESCRIPTION CRopeSegment::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CRopeSegment, m_Sample, FIELD_CLASSPTR ), |
|
DEFINE_FIELD( CRopeSegment, mModelName, FIELD_STRING ), |
|
DEFINE_FIELD( CRopeSegment, mDefaultMass, FIELD_FLOAT ), |
|
DEFINE_FIELD( CRopeSegment, mCauseDamage, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRopeSegment, mCanBeGrabbed, FIELD_CHARACTER ), |
|
DEFINE_FIELD( CRopeSegment, mMasterRope, FIELD_CLASSPTR ), |
|
}; |
|
IMPLEMENT_SAVERESTORE( CRopeSegment, CBaseAnimating ) |
|
|
|
LINK_ENTITY_TO_CLASS( rope_segment, CRopeSegment ); |
|
|
|
void CRopeSegment::Precache() |
|
{ |
|
CBaseAnimating::Precache(); |
|
if( !mModelName ) |
|
mModelName = MAKE_STRING( "models/rope16.mdl" ); |
|
|
|
PRECACHE_MODEL( STRING( mModelName ) ); |
|
PRECACHE_SOUND( "items/grab_rope.wav" ); |
|
} |
|
|
|
void CRopeSegment::Spawn() |
|
{ |
|
pev->classname = MAKE_STRING( "rope_segment" ); |
|
|
|
Precache(); |
|
|
|
SET_MODEL( edict(), STRING( mModelName ) ); |
|
|
|
SetMoveType( MOVETYPE_NOCLIP ); |
|
SetSolidType( SOLID_TRIGGER ); |
|
SetEffects( EF_NODRAW ); |
|
SetAbsOrigin( pev->origin ); |
|
|
|
UTIL_SetSize( pev, Vector( -30, -30, -30 ), Vector( 30, 30, 30 ) ); |
|
|
|
SetNextThink( gpGlobals->time + 0.5 ); |
|
} |
|
|
|
void CRopeSegment::Touch( CBaseEntity* pOther ) |
|
{ |
|
if( pOther->IsPlayer() ) |
|
{ |
|
CBasePlayer *pPlayer = static_cast<CBasePlayer*>( pOther ); |
|
|
|
//Electrified wires deal damage. - Solokiller |
|
if( mCauseDamage ) |
|
{ |
|
pOther->TakeDamage( pev, pev, 1, DMG_SHOCK ); |
|
} |
|
|
|
if (pPlayer->m_afPhysicsFlags & PFLAG_ONBARNACLE) |
|
return; |
|
|
|
if( GetMasterRope()->IsAcceptingAttachment() && !(pPlayer->m_afPhysicsFlags & PFLAG_ONROPE) ) |
|
{ |
|
if( mCanBeGrabbed ) |
|
{ |
|
RopeSampleData *data = m_Sample->GetData(); |
|
|
|
pOther->pev->origin = data->mPosition; |
|
|
|
pPlayer->SetOnRopeState( true ); |
|
pPlayer->SetRope( GetMasterRope() ); |
|
GetMasterRope()->AttachObjectToSegment( this ); |
|
|
|
const Vector& vecVelocity = pOther->pev->velocity; |
|
|
|
if( vecVelocity.Length() > 0.5 ) |
|
{ |
|
//Apply some external force to move the rope. - Solokiller |
|
data->mApplyExternalForce = true; |
|
|
|
data->mExternalForce = data->mExternalForce + vecVelocity * 750; |
|
} |
|
|
|
if( GetMasterRope()->IsSoundAllowed() ) |
|
{ |
|
EMIT_SOUND( edict(), CHAN_BODY, "items/grab_rope.wav", 1.0, ATTN_NORM ); |
|
} |
|
} |
|
else |
|
{ |
|
//This segment cannot be grabbed, so grab the highest one if possible. - Solokiller |
|
CRope *pRope = GetMasterRope(); |
|
|
|
CRopeSegment* pSegment; |
|
|
|
if( pRope->GetNumSegments() <= 4 ) |
|
{ |
|
//Fewer than 5 segments exist, so allow grabbing the last one. - Solokiller |
|
pSegment = pRope->GetSegments()[ pRope->GetNumSegments() - 1 ]; |
|
pSegment->SetCanBeGrabbed( true ); |
|
} |
|
else |
|
{ |
|
pSegment = pRope->GetSegments()[ 4 ]; |
|
} |
|
|
|
pSegment->Touch( pOther ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
CRopeSegment* CRopeSegment::CreateSegment( CRopeSample* pSample, string_t iszModelName, CRope* rope ) |
|
{ |
|
CRopeSegment* pSegment = GetClassPtr<CRopeSegment>( NULL ); |
|
|
|
pSegment->mModelName = iszModelName; |
|
|
|
pSegment->Spawn(); |
|
|
|
pSegment->m_Sample = pSample; |
|
|
|
pSegment->mCauseDamage = false; |
|
pSegment->mCanBeGrabbed = true; |
|
pSegment->mDefaultMass = pSample->GetData()->mMassReciprocal; |
|
pSegment->SetMasterRope(rope); |
|
|
|
return pSegment; |
|
} |
|
|
|
void CRopeSegment::ApplyExternalForce( const Vector& vecForce ) |
|
{ |
|
m_Sample->GetData()->mApplyExternalForce = true; |
|
|
|
m_Sample->GetData()->mExternalForce = m_Sample->GetData()->mExternalForce + vecForce; |
|
} |
|
|
|
void CRopeSegment::SetCauseDamageOnTouch( const bool bCauseDamage ) |
|
{ |
|
mCauseDamage = bCauseDamage; |
|
} |
|
|
|
void CRopeSegment::SetCanBeGrabbed( const bool bCanBeGrabbed ) |
|
{ |
|
mCanBeGrabbed = bCanBeGrabbed; |
|
} |
|
|
|
|
|
class CElectrifiedWire : public CRope |
|
{ |
|
public: |
|
CElectrifiedWire(); |
|
void InitElectrifiedRope(); |
|
virtual int Save( CSave &save ); |
|
virtual int Restore( CRestore &restore ); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
void KeyValue( KeyValueData* pkvd ); |
|
|
|
void Precache(); |
|
|
|
void Spawn(); |
|
void Activate(); |
|
|
|
void EXPORT ElectrifiedRopeThink(); |
|
|
|
void Use( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float flValue ); |
|
|
|
/** |
|
* @return Whether the wire is active. |
|
*/ |
|
bool IsActive() const { return m_bIsActive; } |
|
|
|
/** |
|
* @param iFrequency Frequency. |
|
* @return Whether the spark effect should be performed. |
|
*/ |
|
bool ShouldDoEffect( const int iFrequency ); |
|
|
|
/** |
|
* Do spark effects. |
|
*/ |
|
void DoSpark( const int uiSegment, const bool bExertForce ); |
|
|
|
/** |
|
* Do lightning effects. |
|
*/ |
|
void DoLightning(); |
|
|
|
private: |
|
bool m_bIsActive; |
|
|
|
int m_iTipSparkFrequency; |
|
int m_iBodySparkFrequency; |
|
int m_iLightningFrequency; |
|
|
|
int m_iXJoltForce; |
|
int m_iYJoltForce; |
|
int m_iZJoltForce; |
|
|
|
int m_uiNumUninsulatedSegments; |
|
int m_uiUninsulatedSegments[ MAX_SEGMENTS ]; |
|
|
|
int m_iLightningSprite; |
|
|
|
float m_flLastSparkTime; |
|
}; |
|
|
|
CElectrifiedWire::CElectrifiedWire() |
|
{ |
|
m_bIsActive = true; |
|
m_iTipSparkFrequency = 3; |
|
m_iBodySparkFrequency = 100; |
|
m_iLightningFrequency = 150; |
|
m_iXJoltForce = 0; |
|
m_iYJoltForce = 0; |
|
m_iZJoltForce = 0; |
|
m_uiNumUninsulatedSegments = 0; |
|
} |
|
|
|
|
|
TYPEDESCRIPTION CElectrifiedWire::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CElectrifiedWire, m_bIsActive, FIELD_CHARACTER ), |
|
|
|
DEFINE_FIELD( CElectrifiedWire, m_iTipSparkFrequency, FIELD_INTEGER ), |
|
DEFINE_FIELD( CElectrifiedWire, m_iBodySparkFrequency, FIELD_INTEGER ), |
|
DEFINE_FIELD( CElectrifiedWire, m_iLightningFrequency, FIELD_INTEGER ), |
|
|
|
DEFINE_FIELD( CElectrifiedWire, m_iXJoltForce, FIELD_INTEGER ), |
|
DEFINE_FIELD( CElectrifiedWire, m_iYJoltForce, FIELD_INTEGER ), |
|
DEFINE_FIELD( CElectrifiedWire, m_iZJoltForce, FIELD_INTEGER ), |
|
|
|
DEFINE_FIELD( CElectrifiedWire, m_uiNumUninsulatedSegments, FIELD_INTEGER ), |
|
DEFINE_ARRAY( CElectrifiedWire, m_uiUninsulatedSegments, FIELD_INTEGER, MAX_SEGMENTS ), |
|
|
|
//DEFINE_FIELD( m_iLightningSprite, FIELD_INTEGER ), //Not restored, reset in Precache. - Solokiller |
|
|
|
DEFINE_FIELD( CElectrifiedWire, m_flLastSparkTime, FIELD_TIME ), |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( env_electrified_wire, CElectrifiedWire ); |
|
IMPLEMENT_SAVERESTORE( CElectrifiedWire, CRope ); |
|
|
|
|
|
void CElectrifiedWire::KeyValue( KeyValueData* pkvd ) |
|
{ |
|
if( FStrEq( pkvd->szKeyName, "sparkfrequency" ) ) |
|
{ |
|
m_iTipSparkFrequency = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "bodysparkfrequency" ) ) |
|
{ |
|
m_iBodySparkFrequency = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "lightningfrequency" ) ) |
|
{ |
|
m_iLightningFrequency = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "xforce" ) ) |
|
{ |
|
m_iXJoltForce = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "yforce" ) ) |
|
{ |
|
m_iYJoltForce = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else if( FStrEq( pkvd->szKeyName, "zforce" ) ) |
|
{ |
|
m_iZJoltForce = strtol( pkvd->szValue, NULL, 10 ); |
|
|
|
pkvd->fHandled = true; |
|
} |
|
else |
|
CRope::KeyValue( pkvd ); |
|
} |
|
|
|
void CElectrifiedWire::Precache() |
|
{ |
|
CRope::Precache(); |
|
|
|
m_iLightningSprite = PRECACHE_MODEL( "sprites/lgtning.spr" ); |
|
} |
|
|
|
void CElectrifiedWire::Spawn() |
|
{ |
|
CRope::Spawn(); |
|
pev->classname = MAKE_STRING( "env_electrified_wire" ); |
|
} |
|
|
|
void CElectrifiedWire::Activate() |
|
{ |
|
if (!m_activated) |
|
{ |
|
InitElectrifiedRope(); |
|
m_activated = true; |
|
} |
|
} |
|
|
|
void CElectrifiedWire::InitElectrifiedRope() |
|
{ |
|
InitRope(); |
|
|
|
m_uiNumUninsulatedSegments = 0; |
|
m_bIsActive = true; |
|
|
|
if( m_iBodySparkFrequency > 0 ) |
|
{ |
|
for( int uiIndex = 0; uiIndex < GetNumSegments(); ++uiIndex ) |
|
{ |
|
if( IsValidSegmentIndex( uiIndex ) ) |
|
{ |
|
m_uiUninsulatedSegments[ m_uiNumUninsulatedSegments++ ] = uiIndex; |
|
} |
|
} |
|
} |
|
|
|
if( m_uiNumUninsulatedSegments > 0 ) |
|
{ |
|
for( int uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex ) |
|
{ |
|
GetSegments()[ uiIndex ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
GetAltSegments()[ uiIndex ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
} |
|
} |
|
|
|
if( m_iTipSparkFrequency > 0 ) |
|
{ |
|
GetSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
GetAltSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
} |
|
|
|
m_flLastSparkTime = gpGlobals->time; |
|
|
|
SetSoundAllowed( false ); |
|
|
|
pev->nextthink = gpGlobals->time + 0.01; |
|
SetThink(&CElectrifiedWire::ElectrifiedRopeThink); |
|
} |
|
|
|
void CElectrifiedWire::ElectrifiedRopeThink() |
|
{ |
|
if( gpGlobals->time - m_flLastSparkTime > 0.1 ) |
|
{ |
|
m_flLastSparkTime = gpGlobals->time; |
|
|
|
if( m_uiNumUninsulatedSegments > 0 ) |
|
{ |
|
for( int uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex ) |
|
{ |
|
if( ShouldDoEffect( m_iBodySparkFrequency ) ) |
|
{ |
|
DoSpark( m_uiUninsulatedSegments[ uiIndex ], false ); |
|
} |
|
} |
|
} |
|
|
|
if( ShouldDoEffect( m_iTipSparkFrequency ) ) |
|
{ |
|
DoSpark( GetNumSegments() - 1, true ); |
|
} |
|
|
|
if( ShouldDoEffect( m_iLightningFrequency ) ) |
|
DoLightning(); |
|
} |
|
|
|
CRope::RopeThink(); |
|
} |
|
|
|
void CElectrifiedWire::Use( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float flValue ) |
|
{ |
|
m_bIsActive = !m_bIsActive; |
|
|
|
if( m_uiNumUninsulatedSegments > 0 ) |
|
{ |
|
for( int uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex ) |
|
{ |
|
GetSegments()[ m_uiUninsulatedSegments[ uiIndex ] ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
GetAltSegments()[ m_uiUninsulatedSegments[ uiIndex ] ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
} |
|
} |
|
|
|
if( m_iTipSparkFrequency > 0 ) |
|
{ |
|
GetSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
GetAltSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive ); |
|
} |
|
} |
|
|
|
bool CElectrifiedWire::ShouldDoEffect( const int iFrequency ) |
|
{ |
|
if( iFrequency <= 0 ) |
|
return false; |
|
|
|
if( !IsActive() ) |
|
return false; |
|
|
|
return RANDOM_LONG( 1, iFrequency ) == 1; |
|
} |
|
|
|
void CElectrifiedWire::DoSpark( const int uiSegment, const bool bExertForce ) |
|
{ |
|
const Vector vecOrigin = GetSegmentAttachmentPoint( uiSegment ); |
|
|
|
UTIL_Sparks( vecOrigin ); |
|
|
|
if( bExertForce ) |
|
{ |
|
const Vector vecSparkForce( |
|
RANDOM_FLOAT( -m_iXJoltForce, m_iXJoltForce ), |
|
RANDOM_FLOAT( -m_iYJoltForce, m_iYJoltForce ), |
|
RANDOM_FLOAT( -m_iZJoltForce, m_iZJoltForce ) |
|
); |
|
|
|
ApplyForceToSegment( vecSparkForce, uiSegment ); |
|
} |
|
} |
|
|
|
void CElectrifiedWire::DoLightning() |
|
{ |
|
const int uiSegment1 = RANDOM_LONG( 0, GetNumSegments() - 1 ); |
|
|
|
int uiSegment2; |
|
|
|
int uiIndex; |
|
|
|
//Try to get a random segment. |
|
for( uiIndex = 0; uiIndex < 10; ++uiIndex ) |
|
{ |
|
uiSegment2 = RANDOM_LONG( 0, GetNumSegments() - 1 ); |
|
|
|
if( uiSegment2 != uiSegment1 ) |
|
break; |
|
} |
|
|
|
if( uiIndex >= 10 ) |
|
return; |
|
|
|
CRopeSegment* pSegment1; |
|
CRopeSegment* pSegment2; |
|
|
|
if( GetToggleValue() ) |
|
{ |
|
pSegment1 = GetAltSegments()[ uiSegment1 ]; |
|
pSegment2 = GetAltSegments()[ uiSegment2 ]; |
|
} |
|
else |
|
{ |
|
pSegment1 = GetSegments()[ uiSegment1 ]; |
|
pSegment2 = GetSegments()[ uiSegment2 ]; |
|
} |
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); |
|
WRITE_BYTE( TE_BEAMENTS ); |
|
WRITE_SHORT( pSegment1->entindex() ); |
|
WRITE_SHORT( pSegment2->entindex() ); |
|
WRITE_SHORT( m_iLightningSprite ); |
|
WRITE_BYTE( 0 ); |
|
WRITE_BYTE( 0 ); |
|
WRITE_BYTE( 1 ); |
|
WRITE_BYTE( 10 ); |
|
WRITE_BYTE( 80 ); |
|
WRITE_BYTE( 255 ); |
|
WRITE_BYTE( 255 ); |
|
WRITE_BYTE( 255 ); |
|
WRITE_BYTE( 255 ); |
|
WRITE_BYTE( 255 ); |
|
MESSAGE_END(); |
|
}
|
|
|