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.
767 lines
20 KiB
767 lines
20 KiB
![]()
4 years ago
|
//========= 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 );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|