aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/rope.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/rope.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/rope.cpp')
-rw-r--r--mp/src/game/server/rope.cpp1532
1 files changed, 766 insertions, 766 deletions
diff --git a/mp/src/game/server/rope.cpp b/mp/src/game/server/rope.cpp
index 47591e3c..7ca53298 100644
--- a/mp/src/game/server/rope.cpp
+++ b/mp/src/game/server/rope.cpp
@@ -1,766 +1,766 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "rope.h"
-#include "entitylist.h"
-#include "rope_shared.h"
-#include "sendproxy.h"
-#include "rope_helpers.h"
-#include "te_effect_dispatch.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//--------------------------------------------
-// Rope Spawn Flags
-//--------------------------------------------
-#define SF_ROPE_RESIZE 1 // Automatically resize the rope
-
-// -------------------------------------------------------------------------------- //
-// Fun With Tables.
-// -------------------------------------------------------------------------------- //
-
-LINK_ENTITY_TO_CLASS( move_rope, CRopeKeyframe );
-LINK_ENTITY_TO_CLASS( keyframe_rope, CRopeKeyframe );
-
-IMPLEMENT_SERVERCLASS_ST_NOBASE( CRopeKeyframe, DT_RopeKeyframe )
- SendPropEHandle(SENDINFO(m_hStartPoint)),
- SendPropEHandle(SENDINFO(m_hEndPoint)),
- SendPropInt( SENDINFO(m_iStartAttachment), 5, 0 ),
- SendPropInt( SENDINFO(m_iEndAttachment), 5, 0 ),
-
- SendPropInt( SENDINFO(m_Slack), 12 ),
- SendPropInt( SENDINFO(m_RopeLength), 15 ),
- SendPropInt( SENDINFO(m_fLockedPoints), 4, SPROP_UNSIGNED ),
- SendPropInt( SENDINFO(m_RopeFlags), ROPE_NUMFLAGS, SPROP_UNSIGNED ),
- SendPropInt( SENDINFO(m_nSegments), 4, SPROP_UNSIGNED ),
- SendPropBool( SENDINFO(m_bConstrainBetweenEndpoints) ),
- SendPropInt( SENDINFO(m_iRopeMaterialModelIndex), 16, SPROP_UNSIGNED ),
- SendPropInt( SENDINFO(m_Subdiv), 4, SPROP_UNSIGNED ),
-
- SendPropFloat( SENDINFO(m_TextureScale), 10, 0, 0.1f, 10.0f ),
- SendPropFloat( SENDINFO(m_Width), 0, SPROP_NOSCALE ),
- SendPropFloat( SENDINFO(m_flScrollSpeed), 0, SPROP_NOSCALE ),
-
- SendPropVector(SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
- SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent) ),
-
- SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED),
-END_SEND_TABLE()
-
-
-BEGIN_DATADESC( CRopeKeyframe )
-
- DEFINE_FIELD( m_RopeFlags, FIELD_INTEGER ),
-
- DEFINE_KEYFIELD( m_iNextLinkName, FIELD_STRING, "NextKey" ),
- DEFINE_KEYFIELD( m_Slack, FIELD_INTEGER, "Slack" ),
- DEFINE_KEYFIELD( m_Width, FIELD_FLOAT, "Width" ),
- DEFINE_KEYFIELD( m_TextureScale, FIELD_FLOAT, "TextureScale" ),
- DEFINE_FIELD( m_nSegments, FIELD_INTEGER ),
- DEFINE_FIELD( m_bConstrainBetweenEndpoints, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_strRopeMaterialModel, FIELD_STRING ),
- DEFINE_FIELD( m_iRopeMaterialModelIndex, FIELD_MODELINDEX ),
- DEFINE_KEYFIELD( m_Subdiv, FIELD_INTEGER, "Subdiv" ),
- DEFINE_FIELD( m_RopeLength, FIELD_INTEGER ),
- DEFINE_FIELD( m_fLockedPoints, FIELD_INTEGER ),
- DEFINE_FIELD( m_bCreatedFromMapFile, FIELD_BOOLEAN ),
- DEFINE_KEYFIELD( m_flScrollSpeed, FIELD_FLOAT, "ScrollSpeed" ),
-
- DEFINE_FIELD( m_bStartPointValid, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bEndPointValid, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_hStartPoint, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hEndPoint, FIELD_EHANDLE ),
- DEFINE_FIELD( m_iStartAttachment, FIELD_SHORT ),
- DEFINE_FIELD( m_iEndAttachment, FIELD_SHORT ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScrollSpeed", InputSetScrollSpeed ),
- DEFINE_INPUTFUNC( FIELD_VECTOR, "SetForce", InputSetForce ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ),
-
-END_DATADESC()
-
-
-
-// -------------------------------------------------------------------------------- //
-// CRopeKeyframe implementation.
-// -------------------------------------------------------------------------------- //
-
-CRopeKeyframe::CRopeKeyframe()
-{
- AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
-
- m_takedamage = DAMAGE_YES;
-
- m_iStartAttachment = m_iEndAttachment = 0;
-
- m_Slack = 0;
- m_Width = 2;
- m_TextureScale = 4; // 4:1
- m_nSegments = 5;
- m_RopeLength = 20;
- m_fLockedPoints = (int) (ROPE_LOCK_START_POINT | ROPE_LOCK_END_POINT); // by default, both points are locked
- m_flScrollSpeed = 0;
- m_RopeFlags = ROPE_SIMULATE | ROPE_INITIAL_HANG;
- m_iRopeMaterialModelIndex = -1;
- m_Subdiv = 2;
-
- m_bCreatedFromMapFile = true;
-}
-
-
-CRopeKeyframe::~CRopeKeyframe()
-{
- // Release transmit state ownership.
- SetStartPoint( NULL, 0 );
- SetEndPoint( NULL, 0 );
- SetParent( NULL, 0 );
-}
-
-
-void CRopeKeyframe::SetAttachmentPoint( CBaseHandle &hOutEnt, short &iOutAttachment, CBaseEntity *pEnt, int iAttachment )
-{
- // Unforce our previously attached entity from transmitting.
- CBaseEntity *pCurEnt = gEntList.GetBaseEntity( hOutEnt );
- if ( pCurEnt && pCurEnt->edict() )
- {
- pCurEnt->DecrementTransmitStateOwnedCounter();
- pCurEnt->DispatchUpdateTransmitState();
- }
-
- hOutEnt = pEnt;
- iOutAttachment = iAttachment;
-
- // Force this entity to transmit.
- if ( pEnt )
- {
- pEnt->SetTransmitState( FL_EDICT_ALWAYS );
- pEnt->IncrementTransmitStateOwnedCounter();
- }
-
- EndpointsChanged();
-}
-
-
-void CRopeKeyframe::SetStartPoint( CBaseEntity *pStartPoint, int attachment )
-{
- SetAttachmentPoint( m_hStartPoint.GetForModify(), m_iStartAttachment.GetForModify(), pStartPoint, attachment );
-}
-
-void CRopeKeyframe::SetEndPoint( CBaseEntity *pEndPoint, int attachment )
-{
- SetAttachmentPoint( m_hEndPoint.GetForModify(), m_iEndAttachment.GetForModify(), pEndPoint, attachment );
-}
-
-void CRopeKeyframe::SetParent( CBaseEntity *pNewParent, int iAttachment )
-{
- CBaseEntity *pCurParent = GetMoveParent();
- if ( pCurParent )
- {
- pCurParent->DecrementTransmitStateOwnedCounter();
- pCurParent->DispatchUpdateTransmitState();
- }
-
- // Make sure our move parent always transmits or we get asserts on the client.
- if ( pNewParent )
- {
- pNewParent->IncrementTransmitStateOwnedCounter();
- pNewParent->SetTransmitState( FL_EDICT_ALWAYS );
- }
-
- BaseClass::SetParent( pNewParent, iAttachment );
-}
-
-void CRopeKeyframe::EnablePlayerWeaponAttach( bool bAttach )
-{
- int newFlags = m_RopeFlags;
- if ( bAttach )
- newFlags |= ROPE_PLAYER_WPN_ATTACH;
- else
- newFlags &= ~ROPE_PLAYER_WPN_ATTACH;
-
- if ( newFlags != m_RopeFlags )
- {
- m_RopeFlags = newFlags;
- }
-}
-
-
-CRopeKeyframe* CRopeKeyframe::Create(
- CBaseEntity *pStartEnt,
- CBaseEntity *pEndEnt,
- int iStartAttachment,
- int iEndAttachment,
- int ropeWidth,
- const char *pMaterialName,
- int numSegments
- )
-{
- CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
- if( !pRet )
- return NULL;
-
- pRet->SetStartPoint( pStartEnt, iStartAttachment );
- pRet->SetEndPoint( pEndEnt, iEndAttachment );
- pRet->m_bCreatedFromMapFile = false;
- pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
-
- pRet->Init();
-
- pRet->SetMaterial( pMaterialName );
- pRet->m_Width = ropeWidth;
- pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
-
- return pRet;
-}
-
-
-CRopeKeyframe* CRopeKeyframe::CreateWithSecondPointDetached(
- CBaseEntity *pStartEnt,
- int iStartAttachment,
- int ropeLength,
- int ropeWidth,
- const char *pMaterialName,
- int numSegments,
- bool bInitialHang
- )
-{
- CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
- if( !pRet )
- return NULL;
-
- pRet->SetStartPoint( pStartEnt, iStartAttachment );
- pRet->SetEndPoint( NULL, 0 );
- pRet->m_bCreatedFromMapFile = false;
- pRet->m_fLockedPoints.Set( ROPE_LOCK_START_POINT ); // Only attach the first point.
-
- if( !bInitialHang )
- {
- pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
- }
-
- pRet->Init();
-
- pRet->SetMaterial( pMaterialName );
- pRet->m_RopeLength = ropeLength;
- pRet->m_Width = ropeWidth;
- pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
-
- return pRet;
-}
-
-void CRopeKeyframe::ActivateStartDirectionConstraints( bool bEnable )
-{
- if (bEnable)
- {
- m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_START_DIRECTION );
- }
- else
- {
- m_fLockedPoints &= ~((int)ROPE_LOCK_START_DIRECTION);
- }
-}
-
-
-void CRopeKeyframe::ActivateEndDirectionConstraints( bool bEnable )
-{
- if (bEnable)
- {
- m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_END_DIRECTION );
- }
- else
- {
- m_fLockedPoints &= ~((int)ROPE_LOCK_END_DIRECTION);
- }
-}
-
-
-void CRopeKeyframe::ShakeRopes( const Vector &vCenter, float flRadius, float flMagnitude )
-{
- CEffectData shakeData;
- shakeData.m_vOrigin = vCenter;
- shakeData.m_flRadius = flRadius;
- shakeData.m_flMagnitude = flMagnitude;
- DispatchEffect( "ShakeRopes", shakeData );
-}
-
-
-bool CRopeKeyframe::SetupHangDistance( float flHangDist )
-{
- CBaseEntity *pEnt1 = m_hStartPoint.Get();
- CBaseEntity *pEnt2 = m_hEndPoint.Get();
- if ( !pEnt1 || !pEnt2 )
- return false;
-
- // Calculate starting conditions so we can force it to hang down N inches.
- Vector v1 = pEnt1->GetAbsOrigin();
- if ( pEnt1->GetBaseAnimating() )
- pEnt1->GetBaseAnimating()->GetAttachment( m_iStartAttachment, v1 );
-
- Vector v2 = pEnt2->GetAbsOrigin();
- if ( pEnt2->GetBaseAnimating() )
- pEnt2->GetBaseAnimating()->GetAttachment( m_iEndAttachment, v2 );
-
- float flSlack, flLen;
- CalcRopeStartingConditions( v1, v2, ROPE_MAX_SEGMENTS, flHangDist, &flLen, &flSlack );
-
- m_RopeLength = (int)flLen;
- m_Slack = (int)flSlack;
- return true;
-}
-
-
-void CRopeKeyframe::Init()
-{
- SetLocalAngles( vec3_angle );
- RecalculateLength();
-
- m_nSegments = clamp( (int) m_nSegments, 2, ROPE_MAX_SEGMENTS );
-
- UpdateBBox( true );
-
- m_bStartPointValid = (m_hStartPoint.Get() != NULL);
- m_bEndPointValid = (m_hEndPoint.Get() != NULL);
-}
-
-
-void CRopeKeyframe::Activate()
-{
- BaseClass::Activate();
-
- if( !m_bCreatedFromMapFile )
- return;
-
- // Legacy support..
- if ( m_iRopeMaterialModelIndex == -1 )
- m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
-
- // Find the next entity in our chain.
- CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, m_iNextLinkName );
- if( pEnt && pEnt->edict() )
- {
- SetEndPoint( pEnt );
-
- if( m_spawnflags & SF_ROPE_RESIZE )
- m_RopeFlags |= ROPE_RESIZE;
- }
- else
- {
- // If we're from the map file, and we don't have a target ent, and
- // "Start Dangling" wasn't set, then this rope keyframe doesn't have
- // any rope coming out of it.
- if ( m_fLockedPoints & (int)ROPE_LOCK_END_POINT )
- {
- m_RopeFlags &= ~ROPE_SIMULATE;
- }
- }
-
- // By default, our start point is our own entity.
- SetStartPoint( this );
-
- // If we don't do this here, then when we save/load, we won't "own" the transmit
- // state of our parent, so the client might get our entity without our parent entity.
- SetParent( GetParent(), GetParentAttachment() );
-
- EndpointsChanged();
-
- Init();
-}
-
-void CRopeKeyframe::EndpointsChanged()
-{
- CBaseEntity *pStartEnt = m_hStartPoint.Get();
- if ( pStartEnt )
- {
- if ( (pStartEnt != this) || GetMoveParent() )
- {
- WatchPositionChanges( this, pStartEnt );
- }
- }
- CBaseEntity *pEndEnt = m_hEndPoint.Get();
- if ( pEndEnt )
- {
- if ( (pEndEnt != this) || GetMoveParent() )
- {
- WatchPositionChanges( this, pEndEnt );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Calculate the length of the rope
-//-----------------------------------------------------------------------------
-void CRopeKeyframe::RecalculateLength( void )
-{
- // Get my entities
- if( m_hEndPoint.Get() )
- {
- CBaseEntity *pStartEnt = m_hStartPoint.Get();
- CBaseEntity *pEndEnt = m_hEndPoint.Get();
-
- // Set the length
- m_RopeLength = (int)( pStartEnt->GetAbsOrigin() - pEndEnt->GetAbsOrigin() ).Length();
- }
- else
- {
- m_RopeLength = 0;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: This should remove the rope next time it reaches a resting state.
-// Right now only the client knows when it reaches a resting state, so
-// for now it just removes itself after a short time.
-//-----------------------------------------------------------------------------
-void CRopeKeyframe::DieAtNextRest( void )
-{
- SetThink( &CBaseEntity::SUB_Remove );
- SetNextThink( gpGlobals->curtime + 1.0f );
-}
-
-
-void CRopeKeyframe::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
-{
- if ( !pInfo->m_pTransmitEdict->Get( entindex() ) )
- {
- BaseClass::SetTransmit( pInfo, bAlways );
-
- // Make sure our target ents are sent too.
- CBaseEntity *pEnt = m_hStartPoint;
- if ( pEnt )
- pEnt->SetTransmit( pInfo, bAlways );
-
- pEnt = m_hEndPoint;
- if ( pEnt )
- pEnt->SetTransmit( pInfo, bAlways );
- }
-}
-
-
-bool CRopeKeyframe::GetEndPointPos2( CBaseEntity *pAttached, int iAttachment, Vector &vPos )
-{
- if( !pAttached )
- return false;
-
- if ( iAttachment > 0 )
- {
- CBaseAnimating *pAnim = pAttached->GetBaseAnimating();
- if ( pAnim )
- {
- if( !pAnim->GetAttachment( iAttachment, vPos ) )
- return false;
- }
- else
- {
- return false;
- }
- }
- else
- {
- vPos = pAttached->GetAbsOrigin();
- }
-
- return true;
-}
-
-
-bool CRopeKeyframe::GetEndPointPos( int iPt, Vector &v )
-{
- if ( iPt == 0 )
- return GetEndPointPos2( m_hStartPoint, m_iStartAttachment, v );
- else
- return GetEndPointPos2( m_hEndPoint, m_iEndAttachment, v );
-}
-
-
-void CRopeKeyframe::UpdateBBox( bool bForceRelink )
-{
- Vector v1, v2;
- Vector vMin, vMax;
- if ( GetEndPointPos( 0, v1 ) )
- {
- if ( GetEndPointPos( 1, v2 ) )
- {
- VectorMin( v1, v2, vMin );
- VectorMax( v1, v2, vMax );
-
- // Set our bounds to enclose both endpoints and relink.
- vMin -= GetAbsOrigin();
- vMax -= GetAbsOrigin();
- }
- else
- {
- vMin = vMax = v1 - GetAbsOrigin();
- }
- }
- else
- {
- vMin = vMax = Vector( 0, 0, 0 );
- }
-
- if ( WorldAlignMins() != vMin || WorldAlignMaxs() != vMax )
- {
- UTIL_SetSize( this, vMin, vMax );
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Propagate force to each link in the rope. Check for loops
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CRopeKeyframe::PropagateForce(CBaseEntity *pActivator, CBaseEntity *pCaller, CBaseEntity *pFirstLink, float x, float y, float z)
-{
- EntityMessageBegin( this, true );
- WRITE_FLOAT( x );
- WRITE_FLOAT( y );
- WRITE_FLOAT( z );
- MessageEnd();
-
- // UNDONE: Doesn't deal with intermediate loops
- // Propagate to next segment
- CRopeKeyframe *pNextLink = dynamic_cast<CRopeKeyframe*>((CBaseEntity *)m_hEndPoint);
- if (pNextLink && pNextLink != pFirstLink)
- {
- pNextLink->PropagateForce(pActivator, pCaller, pFirstLink, x, y, z);
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose: Set an instaneous force on the rope.
-// Input : Force vector.
-//------------------------------------------------------------------------------
-void CRopeKeyframe::InputSetForce( inputdata_t &inputdata )
-{
- Vector vecForce;
- inputdata.value.Vector3D(vecForce);
- PropagateForce( inputdata.pActivator, inputdata.pCaller, this, vecForce.x, vecForce.y, vecForce.z );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Breaks the rope if able
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CRopeKeyframe::InputBreak( inputdata_t &inputdata )
-{
- //Route through the damage code
- Break();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Breaks the rope
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CRopeKeyframe::Break( void )
-{
- DetachPoint( 0 );
-
- // Find whoever references us and detach us from them.
- // UNDONE: PERFORMANCE: This is very slow!!!
- CRopeKeyframe *pTest = NULL;
- pTest = gEntList.NextEntByClass( pTest );
- while ( pTest )
- {
- if( stricmp( STRING(pTest->m_iNextLinkName), STRING(GetEntityName()) ) == 0 )
- {
- pTest->DetachPoint( 1 );
- }
-
- pTest = gEntList.NextEntByClass( pTest );
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CRopeKeyframe::NotifyPositionChanged( CBaseEntity *pEntity )
-{
- // Update our bbox?
- UpdateBBox( false );
-
- CBaseEntity *ents[2] = { m_hStartPoint.Get(), m_hEndPoint.Get() };
- if ( (m_RopeFlags & ROPE_RESIZE) && ents[0] && ents[0]->edict() && ents[1] && ents[1]->edict() )
- {
- int len = (int)( ents[0]->GetAbsOrigin() - ents[1]->GetAbsOrigin() ).Length() + m_Slack;
- if ( len != m_RopeLength )
- {
- m_RopeLength = len;
- }
- }
-
- // Figure out if our attachment points have gone away and make sure to update the client if they have.
- bool *pValid[2] = { &m_bStartPointValid, &m_bEndPointValid };
- for ( int i=0; i < 2; i++ )
- {
- bool bCurrentlyValid = ( ents[i] != NULL );
- if ( *pValid[i] != bCurrentlyValid )
- {
- *pValid[i] = bCurrentlyValid;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Take damage will break the rope
-//-----------------------------------------------------------------------------
-int CRopeKeyframe::OnTakeDamage( const CTakeDamageInfo &info )
-{
- // Only allow this if it's been marked
- if( !(m_RopeFlags & ROPE_BREAKABLE) )
- return false;
-
- Break();
- return 0;
-}
-
-
-void CRopeKeyframe::Precache()
-{
- m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
- BaseClass::Precache();
-}
-
-
-void CRopeKeyframe::DetachPoint( int iPoint )
-{
- Assert( iPoint == 0 || iPoint == 1 );
-
- m_fLockedPoints &= ~(1 << iPoint);
-}
-
-
-void CRopeKeyframe::EnableCollision()
-{
- if( !( m_RopeFlags & ROPE_COLLIDE ) )
- {
- m_RopeFlags |= ROPE_COLLIDE;
- }
-}
-
-void CRopeKeyframe::EnableWind( bool bEnable )
-{
- int flag = 0;
- if ( !bEnable )
- flag |= ROPE_NO_WIND;
-
- if ( (m_RopeFlags & ROPE_NO_WIND) != flag )
- {
- m_RopeFlags |= flag;
- }
-}
-
-
-bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue )
-{
- if( stricmp( szKeyName, "Breakable" ) == 0 )
- {
- if( atoi( szValue ) == 1 )
- m_RopeFlags |= ROPE_BREAKABLE;
- }
- else if( stricmp( szKeyName, "Collide" ) == 0 )
- {
- if( atoi( szValue ) == 1 )
- m_RopeFlags |= ROPE_COLLIDE;
- }
- else if( stricmp( szKeyName, "Barbed" ) == 0 )
- {
- if( atoi( szValue ) == 1 )
- m_RopeFlags |= ROPE_BARBED;
- }
- else if( stricmp( szKeyName, "Dangling" ) == 0 )
- {
- if( atoi( szValue ) == 1 )
- m_fLockedPoints &= ~ROPE_LOCK_END_POINT; // detach our dest point
-
- return true;
- }
- else if( stricmp( szKeyName, "Type" ) == 0 )
- {
- int iType = atoi( szValue );
- if( iType == 0 )
- m_nSegments = ROPE_MAX_SEGMENTS;
- else if( iType == 1 )
- m_nSegments = ROPE_TYPE1_NUMSEGMENTS;
- else
- m_nSegments = ROPE_TYPE2_NUMSEGMENTS;
- }
- else if ( stricmp( szKeyName, "RopeShader" ) == 0 )
- {
- // Legacy support for the RopeShader parameter.
- int iShader = atoi( szValue );
- if ( iShader == 0 )
- {
- m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
- }
- else if ( iShader == 1 )
- {
- m_iRopeMaterialModelIndex = PrecacheModel( "cable/rope.vmt" );
- }
- else
- {
- m_iRopeMaterialModelIndex = PrecacheModel( "cable/chain.vmt" );
- }
- }
- else if ( stricmp( szKeyName, "RopeMaterial" ) == 0 )
- {
- // Make sure we have a vmt extension.
- if ( Q_stristr( szValue, ".vmt" ) )
- {
- SetMaterial( szValue );
- }
- else
- {
- char str[512];
- Q_snprintf( str, sizeof( str ), "%s.vmt", szValue );
- SetMaterial( str );
- }
- }
- else if ( stricmp( szKeyName, "NoWind" ) == 0 )
- {
- if ( atoi( szValue ) == 1 )
- {
- m_RopeFlags |= ROPE_NO_WIND;
- }
- }
-
- return BaseClass::KeyValue( szKeyName, szValue );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler that sets the scroll speed.
-//-----------------------------------------------------------------------------
-void CRopeKeyframe::InputSetScrollSpeed( inputdata_t &inputdata )
-{
- m_flScrollSpeed = inputdata.value.Float();
-}
-
-
-void CRopeKeyframe::SetMaterial( const char *pName )
-{
- m_strRopeMaterialModel = AllocPooledString( pName );
- m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
-}
-
-int CRopeKeyframe::UpdateTransmitState()
-{
- // Certain entities like sprites and ropes are strewn throughout the level and they rarely change.
- // For these entities, it's more efficient to transmit them once and then always leave them on
- // the client. Otherwise, the server will have to send big bursts of data with the entity states
- // as they come in and out of the PVS.
- return SetTransmitState( FL_EDICT_ALWAYS );
-}
-
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "rope.h"
+#include "entitylist.h"
+#include "rope_shared.h"
+#include "sendproxy.h"
+#include "rope_helpers.h"
+#include "te_effect_dispatch.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------------------
+// Rope Spawn Flags
+//--------------------------------------------
+#define SF_ROPE_RESIZE 1 // Automatically resize the rope
+
+// -------------------------------------------------------------------------------- //
+// Fun With Tables.
+// -------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( move_rope, CRopeKeyframe );
+LINK_ENTITY_TO_CLASS( keyframe_rope, CRopeKeyframe );
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CRopeKeyframe, DT_RopeKeyframe )
+ SendPropEHandle(SENDINFO(m_hStartPoint)),
+ SendPropEHandle(SENDINFO(m_hEndPoint)),
+ SendPropInt( SENDINFO(m_iStartAttachment), 5, 0 ),
+ SendPropInt( SENDINFO(m_iEndAttachment), 5, 0 ),
+
+ SendPropInt( SENDINFO(m_Slack), 12 ),
+ SendPropInt( SENDINFO(m_RopeLength), 15 ),
+ SendPropInt( SENDINFO(m_fLockedPoints), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_RopeFlags), ROPE_NUMFLAGS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_nSegments), 4, SPROP_UNSIGNED ),
+ SendPropBool( SENDINFO(m_bConstrainBetweenEndpoints) ),
+ SendPropInt( SENDINFO(m_iRopeMaterialModelIndex), 16, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_Subdiv), 4, SPROP_UNSIGNED ),
+
+ SendPropFloat( SENDINFO(m_TextureScale), 10, 0, 0.1f, 10.0f ),
+ SendPropFloat( SENDINFO(m_Width), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flScrollSpeed), 0, SPROP_NOSCALE ),
+
+ SendPropVector(SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
+ SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent) ),
+
+ SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED),
+END_SEND_TABLE()
+
+
+BEGIN_DATADESC( CRopeKeyframe )
+
+ DEFINE_FIELD( m_RopeFlags, FIELD_INTEGER ),
+
+ DEFINE_KEYFIELD( m_iNextLinkName, FIELD_STRING, "NextKey" ),
+ DEFINE_KEYFIELD( m_Slack, FIELD_INTEGER, "Slack" ),
+ DEFINE_KEYFIELD( m_Width, FIELD_FLOAT, "Width" ),
+ DEFINE_KEYFIELD( m_TextureScale, FIELD_FLOAT, "TextureScale" ),
+ DEFINE_FIELD( m_nSegments, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bConstrainBetweenEndpoints, FIELD_BOOLEAN ),
+
+ DEFINE_FIELD( m_strRopeMaterialModel, FIELD_STRING ),
+ DEFINE_FIELD( m_iRopeMaterialModelIndex, FIELD_MODELINDEX ),
+ DEFINE_KEYFIELD( m_Subdiv, FIELD_INTEGER, "Subdiv" ),
+ DEFINE_FIELD( m_RopeLength, FIELD_INTEGER ),
+ DEFINE_FIELD( m_fLockedPoints, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bCreatedFromMapFile, FIELD_BOOLEAN ),
+ DEFINE_KEYFIELD( m_flScrollSpeed, FIELD_FLOAT, "ScrollSpeed" ),
+
+ DEFINE_FIELD( m_bStartPointValid, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bEndPointValid, FIELD_BOOLEAN ),
+
+ DEFINE_FIELD( m_hStartPoint, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hEndPoint, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iStartAttachment, FIELD_SHORT ),
+ DEFINE_FIELD( m_iEndAttachment, FIELD_SHORT ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScrollSpeed", InputSetScrollSpeed ),
+ DEFINE_INPUTFUNC( FIELD_VECTOR, "SetForce", InputSetForce ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ),
+
+END_DATADESC()
+
+
+
+// -------------------------------------------------------------------------------- //
+// CRopeKeyframe implementation.
+// -------------------------------------------------------------------------------- //
+
+CRopeKeyframe::CRopeKeyframe()
+{
+ AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
+
+ m_takedamage = DAMAGE_YES;
+
+ m_iStartAttachment = m_iEndAttachment = 0;
+
+ m_Slack = 0;
+ m_Width = 2;
+ m_TextureScale = 4; // 4:1
+ m_nSegments = 5;
+ m_RopeLength = 20;
+ m_fLockedPoints = (int) (ROPE_LOCK_START_POINT | ROPE_LOCK_END_POINT); // by default, both points are locked
+ m_flScrollSpeed = 0;
+ m_RopeFlags = ROPE_SIMULATE | ROPE_INITIAL_HANG;
+ m_iRopeMaterialModelIndex = -1;
+ m_Subdiv = 2;
+
+ m_bCreatedFromMapFile = true;
+}
+
+
+CRopeKeyframe::~CRopeKeyframe()
+{
+ // Release transmit state ownership.
+ SetStartPoint( NULL, 0 );
+ SetEndPoint( NULL, 0 );
+ SetParent( NULL, 0 );
+}
+
+
+void CRopeKeyframe::SetAttachmentPoint( CBaseHandle &hOutEnt, short &iOutAttachment, CBaseEntity *pEnt, int iAttachment )
+{
+ // Unforce our previously attached entity from transmitting.
+ CBaseEntity *pCurEnt = gEntList.GetBaseEntity( hOutEnt );
+ if ( pCurEnt && pCurEnt->edict() )
+ {
+ pCurEnt->DecrementTransmitStateOwnedCounter();
+ pCurEnt->DispatchUpdateTransmitState();
+ }
+
+ hOutEnt = pEnt;
+ iOutAttachment = iAttachment;
+
+ // Force this entity to transmit.
+ if ( pEnt )
+ {
+ pEnt->SetTransmitState( FL_EDICT_ALWAYS );
+ pEnt->IncrementTransmitStateOwnedCounter();
+ }
+
+ EndpointsChanged();
+}
+
+
+void CRopeKeyframe::SetStartPoint( CBaseEntity *pStartPoint, int attachment )
+{
+ SetAttachmentPoint( m_hStartPoint.GetForModify(), m_iStartAttachment.GetForModify(), pStartPoint, attachment );
+}
+
+void CRopeKeyframe::SetEndPoint( CBaseEntity *pEndPoint, int attachment )
+{
+ SetAttachmentPoint( m_hEndPoint.GetForModify(), m_iEndAttachment.GetForModify(), pEndPoint, attachment );
+}
+
+void CRopeKeyframe::SetParent( CBaseEntity *pNewParent, int iAttachment )
+{
+ CBaseEntity *pCurParent = GetMoveParent();
+ if ( pCurParent )
+ {
+ pCurParent->DecrementTransmitStateOwnedCounter();
+ pCurParent->DispatchUpdateTransmitState();
+ }
+
+ // Make sure our move parent always transmits or we get asserts on the client.
+ if ( pNewParent )
+ {
+ pNewParent->IncrementTransmitStateOwnedCounter();
+ pNewParent->SetTransmitState( FL_EDICT_ALWAYS );
+ }
+
+ BaseClass::SetParent( pNewParent, iAttachment );
+}
+
+void CRopeKeyframe::EnablePlayerWeaponAttach( bool bAttach )
+{
+ int newFlags = m_RopeFlags;
+ if ( bAttach )
+ newFlags |= ROPE_PLAYER_WPN_ATTACH;
+ else
+ newFlags &= ~ROPE_PLAYER_WPN_ATTACH;
+
+ if ( newFlags != m_RopeFlags )
+ {
+ m_RopeFlags = newFlags;
+ }
+}
+
+
+CRopeKeyframe* CRopeKeyframe::Create(
+ CBaseEntity *pStartEnt,
+ CBaseEntity *pEndEnt,
+ int iStartAttachment,
+ int iEndAttachment,
+ int ropeWidth,
+ const char *pMaterialName,
+ int numSegments
+ )
+{
+ CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
+ if( !pRet )
+ return NULL;
+
+ pRet->SetStartPoint( pStartEnt, iStartAttachment );
+ pRet->SetEndPoint( pEndEnt, iEndAttachment );
+ pRet->m_bCreatedFromMapFile = false;
+ pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
+
+ pRet->Init();
+
+ pRet->SetMaterial( pMaterialName );
+ pRet->m_Width = ropeWidth;
+ pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
+
+ return pRet;
+}
+
+
+CRopeKeyframe* CRopeKeyframe::CreateWithSecondPointDetached(
+ CBaseEntity *pStartEnt,
+ int iStartAttachment,
+ int ropeLength,
+ int ropeWidth,
+ const char *pMaterialName,
+ int numSegments,
+ bool bInitialHang
+ )
+{
+ CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
+ if( !pRet )
+ return NULL;
+
+ pRet->SetStartPoint( pStartEnt, iStartAttachment );
+ pRet->SetEndPoint( NULL, 0 );
+ pRet->m_bCreatedFromMapFile = false;
+ pRet->m_fLockedPoints.Set( ROPE_LOCK_START_POINT ); // Only attach the first point.
+
+ if( !bInitialHang )
+ {
+ pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
+ }
+
+ pRet->Init();
+
+ pRet->SetMaterial( pMaterialName );
+ pRet->m_RopeLength = ropeLength;
+ pRet->m_Width = ropeWidth;
+ pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
+
+ return pRet;
+}
+
+void CRopeKeyframe::ActivateStartDirectionConstraints( bool bEnable )
+{
+ if (bEnable)
+ {
+ m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_START_DIRECTION );
+ }
+ else
+ {
+ m_fLockedPoints &= ~((int)ROPE_LOCK_START_DIRECTION);
+ }
+}
+
+
+void CRopeKeyframe::ActivateEndDirectionConstraints( bool bEnable )
+{
+ if (bEnable)
+ {
+ m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_END_DIRECTION );
+ }
+ else
+ {
+ m_fLockedPoints &= ~((int)ROPE_LOCK_END_DIRECTION);
+ }
+}
+
+
+void CRopeKeyframe::ShakeRopes( const Vector &vCenter, float flRadius, float flMagnitude )
+{
+ CEffectData shakeData;
+ shakeData.m_vOrigin = vCenter;
+ shakeData.m_flRadius = flRadius;
+ shakeData.m_flMagnitude = flMagnitude;
+ DispatchEffect( "ShakeRopes", shakeData );
+}
+
+
+bool CRopeKeyframe::SetupHangDistance( float flHangDist )
+{
+ CBaseEntity *pEnt1 = m_hStartPoint.Get();
+ CBaseEntity *pEnt2 = m_hEndPoint.Get();
+ if ( !pEnt1 || !pEnt2 )
+ return false;
+
+ // Calculate starting conditions so we can force it to hang down N inches.
+ Vector v1 = pEnt1->GetAbsOrigin();
+ if ( pEnt1->GetBaseAnimating() )
+ pEnt1->GetBaseAnimating()->GetAttachment( m_iStartAttachment, v1 );
+
+ Vector v2 = pEnt2->GetAbsOrigin();
+ if ( pEnt2->GetBaseAnimating() )
+ pEnt2->GetBaseAnimating()->GetAttachment( m_iEndAttachment, v2 );
+
+ float flSlack, flLen;
+ CalcRopeStartingConditions( v1, v2, ROPE_MAX_SEGMENTS, flHangDist, &flLen, &flSlack );
+
+ m_RopeLength = (int)flLen;
+ m_Slack = (int)flSlack;
+ return true;
+}
+
+
+void CRopeKeyframe::Init()
+{
+ SetLocalAngles( vec3_angle );
+ RecalculateLength();
+
+ m_nSegments = clamp( (int) m_nSegments, 2, ROPE_MAX_SEGMENTS );
+
+ UpdateBBox( true );
+
+ m_bStartPointValid = (m_hStartPoint.Get() != NULL);
+ m_bEndPointValid = (m_hEndPoint.Get() != NULL);
+}
+
+
+void CRopeKeyframe::Activate()
+{
+ BaseClass::Activate();
+
+ if( !m_bCreatedFromMapFile )
+ return;
+
+ // Legacy support..
+ if ( m_iRopeMaterialModelIndex == -1 )
+ m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
+
+ // Find the next entity in our chain.
+ CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, m_iNextLinkName );
+ if( pEnt && pEnt->edict() )
+ {
+ SetEndPoint( pEnt );
+
+ if( m_spawnflags & SF_ROPE_RESIZE )
+ m_RopeFlags |= ROPE_RESIZE;
+ }
+ else
+ {
+ // If we're from the map file, and we don't have a target ent, and
+ // "Start Dangling" wasn't set, then this rope keyframe doesn't have
+ // any rope coming out of it.
+ if ( m_fLockedPoints & (int)ROPE_LOCK_END_POINT )
+ {
+ m_RopeFlags &= ~ROPE_SIMULATE;
+ }
+ }
+
+ // By default, our start point is our own entity.
+ SetStartPoint( this );
+
+ // If we don't do this here, then when we save/load, we won't "own" the transmit
+ // state of our parent, so the client might get our entity without our parent entity.
+ SetParent( GetParent(), GetParentAttachment() );
+
+ EndpointsChanged();
+
+ Init();
+}
+
+void CRopeKeyframe::EndpointsChanged()
+{
+ CBaseEntity *pStartEnt = m_hStartPoint.Get();
+ if ( pStartEnt )
+ {
+ if ( (pStartEnt != this) || GetMoveParent() )
+ {
+ WatchPositionChanges( this, pStartEnt );
+ }
+ }
+ CBaseEntity *pEndEnt = m_hEndPoint.Get();
+ if ( pEndEnt )
+ {
+ if ( (pEndEnt != this) || GetMoveParent() )
+ {
+ WatchPositionChanges( this, pEndEnt );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the length of the rope
+//-----------------------------------------------------------------------------
+void CRopeKeyframe::RecalculateLength( void )
+{
+ // Get my entities
+ if( m_hEndPoint.Get() )
+ {
+ CBaseEntity *pStartEnt = m_hStartPoint.Get();
+ CBaseEntity *pEndEnt = m_hEndPoint.Get();
+
+ // Set the length
+ m_RopeLength = (int)( pStartEnt->GetAbsOrigin() - pEndEnt->GetAbsOrigin() ).Length();
+ }
+ else
+ {
+ m_RopeLength = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This should remove the rope next time it reaches a resting state.
+// Right now only the client knows when it reaches a resting state, so
+// for now it just removes itself after a short time.
+//-----------------------------------------------------------------------------
+void CRopeKeyframe::DieAtNextRest( void )
+{
+ SetThink( &CBaseEntity::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 1.0f );
+}
+
+
+void CRopeKeyframe::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+{
+ if ( !pInfo->m_pTransmitEdict->Get( entindex() ) )
+ {
+ BaseClass::SetTransmit( pInfo, bAlways );
+
+ // Make sure our target ents are sent too.
+ CBaseEntity *pEnt = m_hStartPoint;
+ if ( pEnt )
+ pEnt->SetTransmit( pInfo, bAlways );
+
+ pEnt = m_hEndPoint;
+ if ( pEnt )
+ pEnt->SetTransmit( pInfo, bAlways );
+ }
+}
+
+
+bool CRopeKeyframe::GetEndPointPos2( CBaseEntity *pAttached, int iAttachment, Vector &vPos )
+{
+ if( !pAttached )
+ return false;
+
+ if ( iAttachment > 0 )
+ {
+ CBaseAnimating *pAnim = pAttached->GetBaseAnimating();
+ if ( pAnim )
+ {
+ if( !pAnim->GetAttachment( iAttachment, vPos ) )
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ vPos = pAttached->GetAbsOrigin();
+ }
+
+ return true;
+}
+
+
+bool CRopeKeyframe::GetEndPointPos( int iPt, Vector &v )
+{
+ if ( iPt == 0 )
+ return GetEndPointPos2( m_hStartPoint, m_iStartAttachment, v );
+ else
+ return GetEndPointPos2( m_hEndPoint, m_iEndAttachment, v );
+}
+
+
+void CRopeKeyframe::UpdateBBox( bool bForceRelink )
+{
+ Vector v1, v2;
+ Vector vMin, vMax;
+ if ( GetEndPointPos( 0, v1 ) )
+ {
+ if ( GetEndPointPos( 1, v2 ) )
+ {
+ VectorMin( v1, v2, vMin );
+ VectorMax( v1, v2, vMax );
+
+ // Set our bounds to enclose both endpoints and relink.
+ vMin -= GetAbsOrigin();
+ vMax -= GetAbsOrigin();
+ }
+ else
+ {
+ vMin = vMax = v1 - GetAbsOrigin();
+ }
+ }
+ else
+ {
+ vMin = vMax = Vector( 0, 0, 0 );
+ }
+
+ if ( WorldAlignMins() != vMin || WorldAlignMaxs() != vMax )
+ {
+ UTIL_SetSize( this, vMin, vMax );
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Propagate force to each link in the rope. Check for loops
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CRopeKeyframe::PropagateForce(CBaseEntity *pActivator, CBaseEntity *pCaller, CBaseEntity *pFirstLink, float x, float y, float z)
+{
+ EntityMessageBegin( this, true );
+ WRITE_FLOAT( x );
+ WRITE_FLOAT( y );
+ WRITE_FLOAT( z );
+ MessageEnd();
+
+ // UNDONE: Doesn't deal with intermediate loops
+ // Propagate to next segment
+ CRopeKeyframe *pNextLink = dynamic_cast<CRopeKeyframe*>((CBaseEntity *)m_hEndPoint);
+ if (pNextLink && pNextLink != pFirstLink)
+ {
+ pNextLink->PropagateForce(pActivator, pCaller, pFirstLink, x, y, z);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose: Set an instaneous force on the rope.
+// Input : Force vector.
+//------------------------------------------------------------------------------
+void CRopeKeyframe::InputSetForce( inputdata_t &inputdata )
+{
+ Vector vecForce;
+ inputdata.value.Vector3D(vecForce);
+ PropagateForce( inputdata.pActivator, inputdata.pCaller, this, vecForce.x, vecForce.y, vecForce.z );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Breaks the rope if able
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CRopeKeyframe::InputBreak( inputdata_t &inputdata )
+{
+ //Route through the damage code
+ Break();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Breaks the rope
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CRopeKeyframe::Break( void )
+{
+ DetachPoint( 0 );
+
+ // Find whoever references us and detach us from them.
+ // UNDONE: PERFORMANCE: This is very slow!!!
+ CRopeKeyframe *pTest = NULL;
+ pTest = gEntList.NextEntByClass( pTest );
+ while ( pTest )
+ {
+ if( stricmp( STRING(pTest->m_iNextLinkName), STRING(GetEntityName()) ) == 0 )
+ {
+ pTest->DetachPoint( 1 );
+ }
+
+ pTest = gEntList.NextEntByClass( pTest );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CRopeKeyframe::NotifyPositionChanged( CBaseEntity *pEntity )
+{
+ // Update our bbox?
+ UpdateBBox( false );
+
+ CBaseEntity *ents[2] = { m_hStartPoint.Get(), m_hEndPoint.Get() };
+ if ( (m_RopeFlags & ROPE_RESIZE) && ents[0] && ents[0]->edict() && ents[1] && ents[1]->edict() )
+ {
+ int len = (int)( ents[0]->GetAbsOrigin() - ents[1]->GetAbsOrigin() ).Length() + m_Slack;
+ if ( len != m_RopeLength )
+ {
+ m_RopeLength = len;
+ }
+ }
+
+ // Figure out if our attachment points have gone away and make sure to update the client if they have.
+ bool *pValid[2] = { &m_bStartPointValid, &m_bEndPointValid };
+ for ( int i=0; i < 2; i++ )
+ {
+ bool bCurrentlyValid = ( ents[i] != NULL );
+ if ( *pValid[i] != bCurrentlyValid )
+ {
+ *pValid[i] = bCurrentlyValid;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Take damage will break the rope
+//-----------------------------------------------------------------------------
+int CRopeKeyframe::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ // Only allow this if it's been marked
+ if( !(m_RopeFlags & ROPE_BREAKABLE) )
+ return false;
+
+ Break();
+ return 0;
+}
+
+
+void CRopeKeyframe::Precache()
+{
+ m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
+ BaseClass::Precache();
+}
+
+
+void CRopeKeyframe::DetachPoint( int iPoint )
+{
+ Assert( iPoint == 0 || iPoint == 1 );
+
+ m_fLockedPoints &= ~(1 << iPoint);
+}
+
+
+void CRopeKeyframe::EnableCollision()
+{
+ if( !( m_RopeFlags & ROPE_COLLIDE ) )
+ {
+ m_RopeFlags |= ROPE_COLLIDE;
+ }
+}
+
+void CRopeKeyframe::EnableWind( bool bEnable )
+{
+ int flag = 0;
+ if ( !bEnable )
+ flag |= ROPE_NO_WIND;
+
+ if ( (m_RopeFlags & ROPE_NO_WIND) != flag )
+ {
+ m_RopeFlags |= flag;
+ }
+}
+
+
+bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if( stricmp( szKeyName, "Breakable" ) == 0 )
+ {
+ if( atoi( szValue ) == 1 )
+ m_RopeFlags |= ROPE_BREAKABLE;
+ }
+ else if( stricmp( szKeyName, "Collide" ) == 0 )
+ {
+ if( atoi( szValue ) == 1 )
+ m_RopeFlags |= ROPE_COLLIDE;
+ }
+ else if( stricmp( szKeyName, "Barbed" ) == 0 )
+ {
+ if( atoi( szValue ) == 1 )
+ m_RopeFlags |= ROPE_BARBED;
+ }
+ else if( stricmp( szKeyName, "Dangling" ) == 0 )
+ {
+ if( atoi( szValue ) == 1 )
+ m_fLockedPoints &= ~ROPE_LOCK_END_POINT; // detach our dest point
+
+ return true;
+ }
+ else if( stricmp( szKeyName, "Type" ) == 0 )
+ {
+ int iType = atoi( szValue );
+ if( iType == 0 )
+ m_nSegments = ROPE_MAX_SEGMENTS;
+ else if( iType == 1 )
+ m_nSegments = ROPE_TYPE1_NUMSEGMENTS;
+ else
+ m_nSegments = ROPE_TYPE2_NUMSEGMENTS;
+ }
+ else if ( stricmp( szKeyName, "RopeShader" ) == 0 )
+ {
+ // Legacy support for the RopeShader parameter.
+ int iShader = atoi( szValue );
+ if ( iShader == 0 )
+ {
+ m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
+ }
+ else if ( iShader == 1 )
+ {
+ m_iRopeMaterialModelIndex = PrecacheModel( "cable/rope.vmt" );
+ }
+ else
+ {
+ m_iRopeMaterialModelIndex = PrecacheModel( "cable/chain.vmt" );
+ }
+ }
+ else if ( stricmp( szKeyName, "RopeMaterial" ) == 0 )
+ {
+ // Make sure we have a vmt extension.
+ if ( Q_stristr( szValue, ".vmt" ) )
+ {
+ SetMaterial( szValue );
+ }
+ else
+ {
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%s.vmt", szValue );
+ SetMaterial( str );
+ }
+ }
+ else if ( stricmp( szKeyName, "NoWind" ) == 0 )
+ {
+ if ( atoi( szValue ) == 1 )
+ {
+ m_RopeFlags |= ROPE_NO_WIND;
+ }
+ }
+
+ return BaseClass::KeyValue( szKeyName, szValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler that sets the scroll speed.
+//-----------------------------------------------------------------------------
+void CRopeKeyframe::InputSetScrollSpeed( inputdata_t &inputdata )
+{
+ m_flScrollSpeed = inputdata.value.Float();
+}
+
+
+void CRopeKeyframe::SetMaterial( const char *pName )
+{
+ m_strRopeMaterialModel = AllocPooledString( pName );
+ m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
+}
+
+int CRopeKeyframe::UpdateTransmitState()
+{
+ // Certain entities like sprites and ropes are strewn throughout the level and they rarely change.
+ // For these entities, it's more efficient to transmit them once and then always leave them on
+ // the client. Otherwise, the server will have to send big bursts of data with the entity states
+ // as they come in and out of the PVS.
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+
+
+