diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/rope.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/rope.cpp')
| -rw-r--r-- | mp/src/game/server/rope.cpp | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/mp/src/game/server/rope.cpp b/mp/src/game/server/rope.cpp new file mode 100644 index 00000000..47591e3c --- /dev/null +++ b/mp/src/game/server/rope.cpp @@ -0,0 +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 );
+}
+
+
+
+
|