Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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.

1783 lines
40 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"
5 years ago
#include "ropes.h"
#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;
/**
* Represents a spring that keeps samples a given distance apart.
*/
struct Spring
{
size_t p1;
size_t p2;
float restLength;
float hookConstant;
float springDampning;
};
/**
* Data for a single rope joint.
*/
struct RopeSampleData
{
Vector mPosition;
Vector mVelocity;
Vector mForce;
Vector mExternalForce;
bool mApplyExternalForce;
float mMassReciprocal;
};
/**
* 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();
const RopeSampleData* GetData() const { return &data; }
RopeSampleData* GetData() { return &data; }
CRope* GetMasterRope() { return mMasterRope; }
void SetMasterRope( CRope* pRope )
{
mMasterRope = pRope;
}
private:
RopeSampleData data;
CRope* mMasterRope;
};
class CRopeSegment : public CBaseAnimating
{
public:
void Precache();
void Spawn();
void Think();
void Touch( CBaseEntity* pOther );
static CRopeSegment* CreateSegment( CRopeSample* pSample, string_t iszModelName );
CRopeSample* GetSample() { return m_Sample; }
/**
* Applies external force to the segment.
* @param vecForce Force.
*/
void ApplyExternalForce( const Vector& vecForce );
/**
* Resets the mass to the default value.
*/
void SetMassToDefault();
/**
* Sets the default mass.
* @param flDefaultMass Mass.
*/
void SetDefaultMass( const float flDefaultMass );
/**
* Sets the mass.
* @param flMass Mass.
*/
void SetMass( const float flMass );
/**
* Sets whether the segment should cause damage on touch.
* @param bCauseDamage Whether to cause damage.
*/
void SetCauseDamageOnTouch( const bool bCauseDamage );
/**
* Sets whether the segment can be grabbed.
* @param bCanBeGrabbed Whether the segment can be grabbed.
*/
void SetCanBeGrabbed( const bool bCanBeGrabbed );
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;
};
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_HookConstant, FIELD_FLOAT ),
DEFINE_FIELD( CRope, m_SpringDampning, FIELD_FLOAT ),
DEFINE_FIELD( CRope, m_NumSamples, FIELD_INTEGER ),
DEFINE_FIELD( CRope, m_SpringCnt, 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_CurrentSys, FIELD_CLASSPTR, MAX_SAMPLES ),
DEFINE_ARRAY( CRope, m_TargetSys, 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 ),
};
int CRope::Save( CSave &save )
{
if( !CBaseDelay::Save( save ) )
return 0;
return save.WriteFields( "CBaseDelay", this, m_SaveData, ARRAYSIZE( m_SaveData ) );
}
int CRope::Restore( CRestore &restore )
{
if( !CBaseDelay::Restore( restore ) )
return 0;
int status = restore.ReadFields( "CBaseDelay", this, m_SaveData, ARRAYSIZE( m_SaveData ) );
for( size_t uiIndex = 0; uiIndex < MAX_TEMP_SAMPLES; ++uiIndex )
{
delete[] m_TempSys[ uiIndex ];
m_TempSys[ uiIndex ] = new RopeSampleData[ m_NumSamples ];
memset( m_TempSys[ uiIndex ], 0, sizeof( RopeSampleData ) * m_NumSamples );
}
m_InitialDeltaTime = 0;
m_Spring = NULL;
return status;
}
LINK_ENTITY_TO_CLASS( env_rope, CRope )
/* leak
CRope::~CRope()
{
for( size_t uiIndex = 0; uiIndex < MAX_TEMP_SAMPLES; ++uiIndex )
{
delete[] m_TempSys[ uiIndex ];
m_TempSys[ uiIndex ] = NULL;
}
}*/
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_SOUND_ARRAY( g_pszCreakSounds );
}
void CRope::Spawn()
{
pev->classname = MAKE_STRING( "env_rope" );
m_bMakeSound = true;
Precache();
mSpringsInitialized = false;
m_Gravity.x = m_Gravity.y = 0;
m_Gravity.z = -50;
mObjectAttached = false;
AddFlags( FL_ALWAYSTHINK );
m_NumSamples = m_iSegments + 1;
for( size_t uiSample = 0; uiSample < m_NumSamples; ++uiSample )
{
m_CurrentSys[ uiSample ] = CRopeSample::CreateSample();
m_CurrentSys[ uiSample ]->SetMasterRope( this );
}
memset( m_CurrentSys + m_NumSamples, 0, sizeof( CRopeSample* ) * ( MAX_SAMPLES - m_NumSamples ) );
{
CRopeSegment* pSegment = seg[ 0 ] = CRopeSegment::CreateSegment( m_CurrentSys[ 0 ], GetBodyModel() );
pSegment->SetAbsOrigin( pev->origin );
pSegment = altseg[ 0 ] = CRopeSegment::CreateSegment( m_CurrentSys[ 0 ], GetBodyModel() );
pSegment->SetAbsOrigin( pev->origin );
}
Vector origin;
Vector angles;
const Vector vecGravity = m_Gravity.Normalize();
if( m_iSegments > 2 )
{
CRopeSample** ppCurrentSys = m_CurrentSys;
for( size_t uiSeg = 1; uiSeg < m_iSegments - 1; ++uiSeg )
{
CRopeSample* pSegSample = m_CurrentSys[ uiSeg ];
seg[ uiSeg ] = CRopeSegment::CreateSegment( pSegSample, GetBodyModel() );
altseg[ uiSeg ] = CRopeSegment::CreateSegment( pSegSample, GetBodyModel() );
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_CurrentSys[ m_iSegments - 1 ];
seg[ m_iSegments - 1 ] = CRopeSegment::CreateSegment( pSegSample, GetEndingModel() );
altseg[ m_iSegments - 1 ] = CRopeSegment::CreateSegment( pSegSample, GetEndingModel() );
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 ) );
memset( m_TempSys, 0, sizeof( m_TempSys ) );
m_SpringCnt = 0;
m_InitialDeltaTime = true;
m_HookConstant = 2500;
m_SpringDampning = 0.1;
InitializeRopeSim();
SetNextThink( gpGlobals->time + 0.01 );
}
void CRope::Think()
{
if( !mSpringsInitialized )
{
InitializeSprings( m_iSegments );
}
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::Touch( CBaseEntity* pOther )
{
//Nothing.
}
void CRope::InitializeRopeSim()
{
size_t uiIndex;
for( uiIndex = 0; uiIndex < MAX_TEMP_SAMPLES; ++uiIndex )
{
delete[] m_TempSys[ uiIndex ];
m_TempSys[ uiIndex ] = NULL;
}
for( size_t uiSample = 0; uiSample < m_NumSamples; ++uiSample )
{
m_TargetSys[ uiSample ] = CRopeSample::CreateSample();
m_TargetSys[ uiSample ]->SetMasterRope( this );
}
memset( m_TargetSys + m_NumSamples, 0, sizeof( CRopeSample* ) * ( MAX_SAMPLES - m_NumSamples ) );
for( uiIndex = 0; uiIndex < MAX_TEMP_SAMPLES; ++uiIndex )
{
m_TempSys[ uiIndex ] = new RopeSampleData[ m_NumSamples ];
memset( m_TempSys[ uiIndex ], 0, sizeof( RopeSampleData ) * m_NumSamples );
}
for( size_t 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;
pSegment->SetDefaultMass( data->mMassReciprocal );
}
{
//Zero out the anchored segment's mass so it stays in place.
CRopeSample *pSample = m_CurrentSys[ 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_CurrentSys[ 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;
size_t uiNumSegs = 4;
if( m_iSegments <= 4 )
uiNumSegs = m_iSegments;
for( uiIndex = 0; uiIndex < uiNumSegs; ++uiIndex )
{
seg[ uiIndex ]->SetCanBeGrabbed( false );
altseg[ uiIndex ]->SetCanBeGrabbed( false );
}
}
void CRope::InitializeSprings( const size_t uiNumSprings )
{
m_SpringCnt = uiNumSprings;
m_Spring = new Spring[ uiNumSprings ];
if( uiNumSprings > 0 )
{
Vector vecOrigin, vecAngles;
for( size_t uiIndex = 0; uiIndex < m_SpringCnt; ++uiIndex )
{
Spring& spring = m_Spring[ uiIndex ];
spring.p1 = uiIndex;
spring.p2 = uiIndex + 1;
if( uiIndex < m_iSegments )
{
CRopeSegment* pSegment = seg[ uiIndex ];
pSegment->GetAttachment( 0, vecOrigin, vecAngles );
spring.restLength = ( pSegment->pev->origin - vecOrigin ).Length();
}
else
spring.restLength = 0;
spring.hookConstant = m_HookConstant;
spring.springDampning = m_SpringDampning;
}
}
mSpringsInitialized = true;
}
void CRope::RunSimOnSamples()
{
float flDeltaTime = 0.025;
if( m_InitialDeltaTime )
{
m_InitialDeltaTime = false;
mLastTime = gpGlobals->time;
flDeltaTime = 0;
}
size_t uiIndex = 0;
CRopeSample** ppSampleSource = m_CurrentSys;
CRopeSample** ppSampleTarget = m_TargetSys;
while( true )
{
++uiIndex;
ComputeForces( ppSampleSource );
RK4Integrate( flDeltaTime, ppSampleSource, ppSampleTarget );
mLastTime += 0.007;
if( gpGlobals->time <= mLastTime )
{
if( ( uiIndex % 2 ) != 0 )
break;
}
CRopeSample **swap = ppSampleSource;
ppSampleSource = ppSampleTarget;
ppSampleTarget = swap;
//std::swap( ppSampleSource, ppSampleTarget );
}
mLastTime = gpGlobals->time;
}
void CRope::ComputeForces( RopeSampleData* pSystem )
{
size_t uiIndex;
for( uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex )
{
ComputeSampleForce( pSystem[ uiIndex ] );
}
Spring* pSpring = m_Spring;
for( uiIndex = 0; uiIndex < m_SpringCnt; ++uiIndex, ++pSpring )
{
ComputeSpringForce( pSystem[ pSpring->p1 ], pSystem[ pSpring->p2 ], *pSpring );
}
}
void CRope::ComputeForces( CRopeSample** ppSystem )
{
size_t uiIndex;
for( uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex )
{
ComputeSampleForce( *ppSystem[ uiIndex ]->GetData() );
}
Spring* pSpring = m_Spring;
for( uiIndex = 0; uiIndex < m_SpringCnt; ++uiIndex, ++pSpring )
{
ComputeSpringForce( *ppSystem[ pSpring->p1 ]->GetData(), *ppSystem[ pSpring->p2 ]->GetData(), *pSpring );
}
}
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, const Spring& spring )
{
Vector vecDist = first.mPosition - second.mPosition;
const double flDistance = vecDist.Length();
const double flForce = ( flDistance - spring.restLength ) * spring.hookConstant;
const double flNewRelativeDist = DotProduct( first.mVelocity - second.mVelocity, vecDist ) * spring.springDampning;
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, CRopeSample** ppSampleSource, CRopeSample** ppSampleTarget )
{
const float flDeltas[ MAX_TEMP_SAMPLES - 1 ] =
{
flDeltaTime * 0.5f,
flDeltaTime * 0.5f,
flDeltaTime * 0.5f,
flDeltaTime
};
{
RopeSampleData* pTemp1 = m_TempSys[ 0 ];
RopeSampleData* pTemp2 = m_TempSys[ 1 ];
for( size_t uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 )
{
RopeSampleData *data = ppSampleSource[ uiIndex ]->GetData();
pTemp2->mForce = data->mMassReciprocal * data->mForce * flDeltas[ 0 ];
pTemp2->mVelocity = data->mVelocity * flDeltas[ 0 ];
pTemp1->mMassReciprocal = data->mMassReciprocal;
pTemp1->mVelocity = data->mVelocity + pTemp2->mForce;
pTemp1->mPosition = data->mPosition + pTemp2->mVelocity;
}
ComputeForces( m_TempSys[ 0 ] );
}
for( size_t uiStep = 2; uiStep < MAX_TEMP_SAMPLES - 1; ++uiStep )
{
RopeSampleData* pTemp1 = m_TempSys[ 0 ];
RopeSampleData* pTemp2 = m_TempSys[ uiStep ];
for( size_t uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 )
{
RopeSampleData *data = ppSampleSource[ uiIndex ]->GetData();
pTemp2->mForce = data->mMassReciprocal * pTemp1->mForce * flDeltas[ uiStep - 1 ];
pTemp2->mVelocity = pTemp1->mVelocity * flDeltas[ uiStep - 1 ];
pTemp1->mMassReciprocal = data->mMassReciprocal;
pTemp1->mVelocity = data->mVelocity + pTemp2->mForce;
pTemp1->mPosition = data->mPosition + pTemp2->mVelocity;
}
ComputeForces( m_TempSys[ 0 ] );
}
{
RopeSampleData* pTemp1 = m_TempSys[ 0 ];
RopeSampleData* pTemp2 = m_TempSys[ 4 ];
for( size_t uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2 )
{
RopeSampleData *data = ppSampleSource[ uiIndex ]->GetData();
pTemp2->mForce = data->mMassReciprocal * pTemp1->mForce * flDeltas[ 3 ];
pTemp2->mVelocity = pTemp1->mVelocity * flDeltas[ 3 ];
}
}
RopeSampleData* pTemp1 = m_TempSys[ 1 ];
RopeSampleData* pTemp2 = m_TempSys[ 2 ];
RopeSampleData* pTemp3 = m_TempSys[ 3 ];
RopeSampleData* pTemp4 = m_TempSys[ 4 ];
for( size_t uiIndex = 0; uiIndex < m_NumSamples; ++uiIndex, ++pTemp1, ++pTemp2, ++pTemp3, ++pTemp4 )
{
CRopeSample *pSource = ppSampleSource[ uiIndex ];
CRopeSample *pTarget = ppSampleTarget[ uiIndex ];
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->GetData()->mPosition = pSource->GetData()->mPosition + ( vecPosChange );//* flDeltaTime );
pTarget->GetData()->mVelocity = pSource->GetData()->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_CurrentSys[ 0 ]->GetData()->mPosition,
m_CurrentSys[ 1 ]->GetData()->mPosition,
vecAngles );
( *ppPrimarySegs )->SetAbsAngles( vecAngles );
}
TraceResult tr;
if( mObjectAttached )
{
for( size_t uiSeg = 1; uiSeg < m_iSegments; ++uiSeg )
{
CRopeSample* pSample = m_CurrentSys[ 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( size_t uiSeg = 1; uiSeg < m_iSegments; ++uiSeg )
{
UTIL_TraceLine(
ppHiddenSegs[ uiSeg ]->pev->origin,
m_CurrentSys[ uiSeg ]->GetData()->mPosition,
ignore_monsters, edict(), &tr );
if( tr.flFraction == 1.0 )
{
Vector vecOrigin = m_CurrentSys[ 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( size_t 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_CurrentSys[ 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 size_t 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( size_t 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;
}
5 years ago
if( mAttachedObjectsSegment + 1 == m_iSegments )
bOnRope = false;
else
++mAttachedObjectsSegment;
5 years ago
flNewDist -= flOffset;
flSegLength = 0;
mAttachedObjectsOffset = 0;
if( flNewDist <= 0 )
break;
}
}
}
return bOnRope;
}
Vector CRope::GetAttachedObjectsVelocity() const
{
5 years ago
if( !mObjectAttached )
return g_vecZero;
return seg[ mAttachedObjectsSegment ]->GetSample()->GetData()->mVelocity;
}
5 years ago
void CRope::ApplyForceFromPlayer( const Vector& vecForce )
{
if( !mObjectAttached )
return;
5 years ago
float flForce = 20000.0;
5 years ago
if( m_iSegments < 26 )
flForce *= ( m_iSegments / 26.0 );
5 years ago
const Vector vecScaledForce = vecForce * flForce;
5 years ago
ApplyForceToSegment( vecScaledForce, mAttachedObjectsSegment );
}
5 years ago
void CRope::ApplyForceToSegment( const Vector& vecForce, const size_t uiSegment )
{
5 years ago
if( uiSegment < m_iSegments )
{
seg[ uiSegment ]->ApplyExternalForce( vecForce );
}
else if( uiSegment == m_iSegments )
{
//Apply force to the last sample.
5 years ago
RopeSampleData *data = m_CurrentSys[ uiSegment - 1 ]->GetData();
5 years ago
data->mExternalForce = data->mExternalForce + vecForce;
5 years ago
data->mApplyExternalForce = true;
}
}
void CRope::AttachObjectToSegment( CRopeSegment* pSegment )
{
5 years ago
mObjectAttached = true;
5 years ago
detachTime = 0;
5 years ago
SetAttachedObjectsSegment( pSegment );
5 years ago
mAttachedObjectsOffset = 0;
}
5 years ago
void CRope::DetachObject()
{
5 years ago
mObjectAttached = false;
detachTime = gpGlobals->time;
}
5 years ago
bool CRope::IsAcceptingAttachment() const
{
5 years ago
if( gpGlobals->time - detachTime > 2.0 && !mObjectAttached )
{
return mDisallowPlayerAttachment != 1;
}
5 years ago
return false;
}
5 years ago
bool CRope::ShouldCreak() const
{
if( mObjectAttached && m_bMakeSound )
{
CRopeSample* pSample = seg[ mAttachedObjectsSegment ]->GetSample();
5 years ago
if( pSample->GetData()->mVelocity.Length() > 20.0 )
return RANDOM_LONG( 1, 5 ) == 1;
}
5 years ago
return false;
}
5 years ago
void CRope::Creak()
{
EMIT_SOUND( edict(), CHAN_BODY,
g_pszCreakSounds[ RANDOM_LONG( 0, ARRAYSIZE( g_pszCreakSounds ) - 1 ) ],
VOL_NORM, ATTN_NORM );
}
5 years ago
float CRope::GetSegmentLength( size_t uiSegmentIndex ) const
{
5 years ago
if( uiSegmentIndex < m_iSegments )
{
Vector vecOrigin, vecAngles;
5 years ago
CRopeSegment *pSegment = seg[ uiSegmentIndex ];
5 years ago
pSegment->GetAttachment( 0, vecOrigin, vecAngles );
5 years ago
return ( pSegment->pev->origin - vecOrigin ).Length();
}
5 years ago
return 0;
}
5 years ago
float CRope::GetRopeLength() const
{
float flLength = 0;
5 years ago
Vector vecOrigin, vecAngles;
5 years ago
for( size_t uiIndex = 0; uiIndex < m_iSegments; ++uiIndex )
{
CRopeSegment *pSegment = seg[ uiIndex ];
5 years ago
pSegment->GetAttachment( 0, vecOrigin, vecAngles );
5 years ago
flLength += ( pSegment->pev->origin - vecOrigin ).Length();
}
5 years ago
return flLength;
}
5 years ago
Vector CRope::GetRopeOrigin() const
{
5 years ago
return m_CurrentSys[ 0 ]->GetData()->mPosition;
}
5 years ago
bool CRope::IsValidSegmentIndex( const size_t uiSegment ) const
{
return uiSegment < m_iSegments;
}
5 years ago
Vector CRope::GetSegmentOrigin( const size_t uiSegment ) const
{
5 years ago
if( !IsValidSegmentIndex( uiSegment ) )
return g_vecZero;
return m_CurrentSys[ uiSegment ]->GetData()->mPosition;
}
Vector CRope::GetSegmentAttachmentPoint( const size_t 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( size_t uiIndex = 0; uiIndex < m_iSegments; ++uiIndex )
{
5 years ago
if( seg[ uiIndex ] == pSegment || altseg[ uiIndex ] == pSegment )
{
mAttachedObjectsSegment = uiIndex;
break;
}
}
}
5 years ago
Vector CRope::GetSegmentDirFromOrigin( const size_t uiSegmentIndex ) const
{
5 years ago
if( uiSegmentIndex >= m_iSegments )
return g_vecZero;
//There is one more sample than there are segments, so this is fine.
const Vector vecResult =
m_CurrentSys[ uiSegmentIndex + 1 ]->GetData()->mPosition -
m_CurrentSys[ uiSegmentIndex ]->GetData()->mPosition;
return vecResult.Normalize();
}
Vector CRope::GetAttachedObjectsPosition() const
{
if( !mObjectAttached )
return g_vecZero;
Vector vecResult;
5 years ago
if( mAttachedObjectsSegment < m_iSegments )
vecResult = m_CurrentSys[ mAttachedObjectsSegment ]->GetData()->mPosition;
5 years ago
vecResult = vecResult +
( mAttachedObjectsOffset * GetSegmentDirFromOrigin( mAttachedObjectsSegment ) );
5 years ago
return vecResult;
}
5 years ago
// Global Savedata for player
TYPEDESCRIPTION CRopeSample::m_SaveData[] =
{
5 years ago
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, mMasterRope, FIELD_CLASSPTR ),
};
IMPLEMENT_SAVERESTORE(CRopeSample, CBaseEntity)
5 years ago
LINK_ENTITY_TO_CLASS( rope_sample, CRopeSample );
5 years ago
void CRopeSample::Spawn()
{
pev->classname = MAKE_STRING( "rope_sample" );
AddEffectsFlags( EF_NODRAW );
}
5 years ago
CRopeSample* CRopeSample::CreateSample()
{
5 years ago
CRopeSample* pSample = GetClassPtr<CRopeSample>( NULL );
5 years ago
pSample->Spawn();
5 years ago
return pSample;
}
5 years ago
TYPEDESCRIPTION CRopeSegment::m_SaveData[] =
{
5 years ago
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 ),
};
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( (char*)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 );
AddFlags( FL_ALWAYSTHINK );
SetEffects( EF_NODRAW );
SetAbsOrigin( pev->origin );
UTIL_SetSize( pev, Vector( -30, -30, -30 ), Vector( 30, 30, 30 ) );
SetNextThink( gpGlobals->time + 0.5 );
}
void CRopeSegment::Think()
{
//Do nothing.
}
void CRopeSegment::Touch( CBaseEntity* pOther )
{
if( pOther->IsPlayer() )
{
5 years ago
CBasePlayer *pPlayer = static_cast<CBasePlayer*>( pOther );
5 years ago
//Electrified wires deal damage. - Solokiller
if( mCauseDamage )
{
5 years ago
pOther->TakeDamage( pev, pev, 1, DMG_SHOCK );
}
5 years ago
if( m_Sample->GetMasterRope()->IsAcceptingAttachment() && !(pPlayer->m_afPhysicsFlags & PFLAG_ONROPE) )
{
5 years ago
if( mCanBeGrabbed )
{
RopeSampleData *data = m_Sample->GetData();
pOther->SetAbsOrigin( data->mPosition );
pPlayer->SetOnRopeState( true );
pPlayer->SetRope( m_Sample->GetMasterRope() );
m_Sample->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( m_Sample->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 = m_Sample->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 );
}
}
}
}
5 years ago
CRopeSegment* CRopeSegment::CreateSegment( CRopeSample* pSample, string_t iszModelName )
{
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;
return pSegment;
}
void CRopeSegment::ApplyExternalForce( const Vector& vecForce )
{
m_Sample->GetData()->mApplyExternalForce = true;
m_Sample->GetData()->mExternalForce = m_Sample->GetData()->mExternalForce + vecForce;
}
void CRopeSegment::SetMassToDefault()
{
m_Sample->GetData()->mMassReciprocal = mDefaultMass;
}
void CRopeSegment::SetDefaultMass( const float flDefaultMass )
{
mDefaultMass = flDefaultMass;
}
void CRopeSegment::SetMass( const float flMass )
{
m_Sample->GetData()->mMassReciprocal = flMass;
}
void CRopeSegment::SetCauseDamageOnTouch( const bool bCauseDamage )
{
mCauseDamage = bCauseDamage;
}
void CRopeSegment::SetCanBeGrabbed( const bool bCanBeGrabbed )
{
5 years ago
mCanBeGrabbed = bCanBeGrabbed;
}
5 years ago
class CElectrifiedWire : public CRope
{
public:
CElectrifiedWire();
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void KeyValue( KeyValueData* pkvd );
void Precache();
void Spawn();
void Think();
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 size_t 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;
size_t m_uiNumUninsulatedSegments;
size_t m_uiUninsulatedSegments[ MAX_SEGMENTS ];
int m_iLightningSprite;
float m_flLastSparkTime;
};
CElectrifiedWire::CElectrifiedWire()
{
5 years ago
m_bIsActive = true;
m_iTipSparkFrequency = 3;
m_iBodySparkFrequency = 100;
m_iLightningFrequency = 150;
m_iXJoltForce = 0;
m_iYJoltForce = 0;
m_iZJoltForce = 0;
m_uiNumUninsulatedSegments = 0;
}
5 years ago
TYPEDESCRIPTION CElectrifiedWire::m_SaveData[] =
{
5 years ago
DEFINE_FIELD( CElectrifiedWire, m_bIsActive, FIELD_CHARACTER ),
5 years ago
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" ) )
{
5 years ago
m_iTipSparkFrequency = strtol( pkvd->szValue, NULL, 10 );
5 years ago
pkvd->fHandled = true;
}
else if( FStrEq( pkvd->szKeyName, "bodysparkfrequency" ) )
{
m_iBodySparkFrequency = strtol( pkvd->szValue, NULL, 10 );
5 years ago
pkvd->fHandled = true;
}
else if( FStrEq( pkvd->szKeyName, "lightningfrequency" ) )
{
m_iLightningFrequency = strtol( pkvd->szValue, NULL, 10 );
5 years ago
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;
}
5 years ago
else
CRope::KeyValue( pkvd );
}
void CElectrifiedWire::Precache()
{
CRope::Precache();
m_iLightningSprite = PRECACHE_MODEL( "sprites/lgtning.spr" );
}
5 years ago
void CElectrifiedWire::Spawn()
{
5 years ago
CRope::Spawn();
pev->classname = MAKE_STRING( "env_electrified_wire" );
m_uiNumUninsulatedSegments = 0;
m_bIsActive = true;
5 years ago
if( m_iBodySparkFrequency > 0 )
{
for( size_t uiIndex = 0; uiIndex < GetNumSegments(); ++uiIndex )
{
if( IsValidSegmentIndex( uiIndex ) )
{
m_uiUninsulatedSegments[ m_uiNumUninsulatedSegments++ ] = uiIndex;
}
}
}
5 years ago
if( m_uiNumUninsulatedSegments > 0 )
{
for( size_t uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex )
{
GetSegments()[ uiIndex ]->SetCauseDamageOnTouch( m_bIsActive );
GetAltSegments()[ uiIndex ]->SetCauseDamageOnTouch( m_bIsActive );
}
}
5 years ago
if( m_iTipSparkFrequency > 0 )
{
GetSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive );
GetAltSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive );
}
5 years ago
m_flLastSparkTime = gpGlobals->time;
5 years ago
SetSoundAllowed( false );
}
5 years ago
void CElectrifiedWire::Think()
{
if( gpGlobals->time - m_flLastSparkTime > 0.1 )
{
5 years ago
m_flLastSparkTime = gpGlobals->time;
5 years ago
if( m_uiNumUninsulatedSegments > 0 )
{
for( size_t uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex )
{
if( ShouldDoEffect( m_iBodySparkFrequency ) )
{
DoSpark( m_uiUninsulatedSegments[ uiIndex ], false );
}
}
}
5 years ago
if( ShouldDoEffect( m_iTipSparkFrequency ) )
{
DoSpark( GetNumSegments() - 1, true );
}
5 years ago
if( ShouldDoEffect( m_iLightningFrequency ) )
DoLightning();
}
5 years ago
CRope::Think();
}
5 years ago
void CElectrifiedWire::Use( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float flValue )
{
5 years ago
m_bIsActive = !m_bIsActive;
if( m_uiNumUninsulatedSegments > 0 )
{
5 years ago
for( size_t uiIndex = 0; uiIndex < m_uiNumUninsulatedSegments; ++uiIndex )
{
5 years ago
GetSegments()[ m_uiUninsulatedSegments[ uiIndex ] ]->SetCauseDamageOnTouch( m_bIsActive );
GetAltSegments()[ m_uiUninsulatedSegments[ uiIndex ] ]->SetCauseDamageOnTouch( m_bIsActive );
}
}
5 years ago
if( m_iTipSparkFrequency > 0 )
{
GetSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive );
GetAltSegments()[ GetNumSegments() - 1 ]->SetCauseDamageOnTouch( m_bIsActive );
}
}
5 years ago
bool CElectrifiedWire::ShouldDoEffect( const int iFrequency )
{
5 years ago
if( iFrequency <= 0 )
return false;
if( !IsActive() )
return false;
return RANDOM_LONG( 1, iFrequency ) == 1;
}
void CElectrifiedWire::DoSpark( const size_t uiSegment, const bool bExertForce )
{
const Vector vecOrigin = GetSegmentAttachmentPoint( uiSegment );
5 years ago
UTIL_Sparks( vecOrigin );
5 years ago
if( bExertForce )
{
5 years ago
const Vector vecSparkForce(
RANDOM_FLOAT( -m_iXJoltForce, m_iXJoltForce ),
RANDOM_FLOAT( -m_iYJoltForce, m_iYJoltForce ),
RANDOM_FLOAT( -m_iZJoltForce, m_iZJoltForce )
);
ApplyForceToSegment( vecSparkForce, uiSegment );
}
}
5 years ago
void CElectrifiedWire::DoLightning()
{
5 years ago
const size_t uiSegment1 = RANDOM_LONG( 0, GetNumSegments() - 1 );
size_t uiSegment2;
size_t 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();
8 years ago
}