Team Fortress 2 Source Code as on 22/4/2020
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.

766 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "rope.h"
  9. #include "entitylist.h"
  10. #include "rope_shared.h"
  11. #include "sendproxy.h"
  12. #include "rope_helpers.h"
  13. #include "te_effect_dispatch.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. //--------------------------------------------
  17. // Rope Spawn Flags
  18. //--------------------------------------------
  19. #define SF_ROPE_RESIZE 1 // Automatically resize the rope
  20. // -------------------------------------------------------------------------------- //
  21. // Fun With Tables.
  22. // -------------------------------------------------------------------------------- //
  23. LINK_ENTITY_TO_CLASS( move_rope, CRopeKeyframe );
  24. LINK_ENTITY_TO_CLASS( keyframe_rope, CRopeKeyframe );
  25. IMPLEMENT_SERVERCLASS_ST_NOBASE( CRopeKeyframe, DT_RopeKeyframe )
  26. SendPropEHandle(SENDINFO(m_hStartPoint)),
  27. SendPropEHandle(SENDINFO(m_hEndPoint)),
  28. SendPropInt( SENDINFO(m_iStartAttachment), 5, 0 ),
  29. SendPropInt( SENDINFO(m_iEndAttachment), 5, 0 ),
  30. SendPropInt( SENDINFO(m_Slack), 12 ),
  31. SendPropInt( SENDINFO(m_RopeLength), 15 ),
  32. SendPropInt( SENDINFO(m_fLockedPoints), 4, SPROP_UNSIGNED ),
  33. SendPropInt( SENDINFO(m_RopeFlags), ROPE_NUMFLAGS, SPROP_UNSIGNED ),
  34. SendPropInt( SENDINFO(m_nSegments), 4, SPROP_UNSIGNED ),
  35. SendPropBool( SENDINFO(m_bConstrainBetweenEndpoints) ),
  36. SendPropInt( SENDINFO(m_iRopeMaterialModelIndex), 16, SPROP_UNSIGNED ),
  37. SendPropInt( SENDINFO(m_Subdiv), 4, SPROP_UNSIGNED ),
  38. SendPropFloat( SENDINFO(m_TextureScale), 10, 0, 0.1f, 10.0f ),
  39. SendPropFloat( SENDINFO(m_Width), 0, SPROP_NOSCALE ),
  40. SendPropFloat( SENDINFO(m_flScrollSpeed), 0, SPROP_NOSCALE ),
  41. SendPropVector(SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
  42. SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent) ),
  43. SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED),
  44. END_SEND_TABLE()
  45. BEGIN_DATADESC( CRopeKeyframe )
  46. DEFINE_FIELD( m_RopeFlags, FIELD_INTEGER ),
  47. DEFINE_KEYFIELD( m_iNextLinkName, FIELD_STRING, "NextKey" ),
  48. DEFINE_KEYFIELD( m_Slack, FIELD_INTEGER, "Slack" ),
  49. DEFINE_KEYFIELD( m_Width, FIELD_FLOAT, "Width" ),
  50. DEFINE_KEYFIELD( m_TextureScale, FIELD_FLOAT, "TextureScale" ),
  51. DEFINE_FIELD( m_nSegments, FIELD_INTEGER ),
  52. DEFINE_FIELD( m_bConstrainBetweenEndpoints, FIELD_BOOLEAN ),
  53. DEFINE_FIELD( m_strRopeMaterialModel, FIELD_STRING ),
  54. DEFINE_FIELD( m_iRopeMaterialModelIndex, FIELD_MODELINDEX ),
  55. DEFINE_KEYFIELD( m_Subdiv, FIELD_INTEGER, "Subdiv" ),
  56. DEFINE_FIELD( m_RopeLength, FIELD_INTEGER ),
  57. DEFINE_FIELD( m_fLockedPoints, FIELD_INTEGER ),
  58. DEFINE_FIELD( m_bCreatedFromMapFile, FIELD_BOOLEAN ),
  59. DEFINE_KEYFIELD( m_flScrollSpeed, FIELD_FLOAT, "ScrollSpeed" ),
  60. DEFINE_FIELD( m_bStartPointValid, FIELD_BOOLEAN ),
  61. DEFINE_FIELD( m_bEndPointValid, FIELD_BOOLEAN ),
  62. DEFINE_FIELD( m_hStartPoint, FIELD_EHANDLE ),
  63. DEFINE_FIELD( m_hEndPoint, FIELD_EHANDLE ),
  64. DEFINE_FIELD( m_iStartAttachment, FIELD_SHORT ),
  65. DEFINE_FIELD( m_iEndAttachment, FIELD_SHORT ),
  66. // Inputs
  67. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScrollSpeed", InputSetScrollSpeed ),
  68. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetForce", InputSetForce ),
  69. DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ),
  70. END_DATADESC()
  71. // -------------------------------------------------------------------------------- //
  72. // CRopeKeyframe implementation.
  73. // -------------------------------------------------------------------------------- //
  74. CRopeKeyframe::CRopeKeyframe()
  75. {
  76. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  77. m_takedamage = DAMAGE_YES;
  78. m_iStartAttachment = m_iEndAttachment = 0;
  79. m_Slack = 0;
  80. m_Width = 2;
  81. m_TextureScale = 4; // 4:1
  82. m_nSegments = 5;
  83. m_RopeLength = 20;
  84. m_fLockedPoints = (int) (ROPE_LOCK_START_POINT | ROPE_LOCK_END_POINT); // by default, both points are locked
  85. m_flScrollSpeed = 0;
  86. m_RopeFlags = ROPE_SIMULATE | ROPE_INITIAL_HANG;
  87. m_iRopeMaterialModelIndex = -1;
  88. m_Subdiv = 2;
  89. m_bCreatedFromMapFile = true;
  90. }
  91. CRopeKeyframe::~CRopeKeyframe()
  92. {
  93. // Release transmit state ownership.
  94. SetStartPoint( NULL, 0 );
  95. SetEndPoint( NULL, 0 );
  96. SetParent( NULL, 0 );
  97. }
  98. void CRopeKeyframe::SetAttachmentPoint( CBaseHandle &hOutEnt, short &iOutAttachment, CBaseEntity *pEnt, int iAttachment )
  99. {
  100. // Unforce our previously attached entity from transmitting.
  101. CBaseEntity *pCurEnt = gEntList.GetBaseEntity( hOutEnt );
  102. if ( pCurEnt && pCurEnt->edict() )
  103. {
  104. pCurEnt->DecrementTransmitStateOwnedCounter();
  105. pCurEnt->DispatchUpdateTransmitState();
  106. }
  107. hOutEnt = pEnt;
  108. iOutAttachment = iAttachment;
  109. // Force this entity to transmit.
  110. if ( pEnt )
  111. {
  112. pEnt->SetTransmitState( FL_EDICT_ALWAYS );
  113. pEnt->IncrementTransmitStateOwnedCounter();
  114. }
  115. EndpointsChanged();
  116. }
  117. void CRopeKeyframe::SetStartPoint( CBaseEntity *pStartPoint, int attachment )
  118. {
  119. SetAttachmentPoint( m_hStartPoint.GetForModify(), m_iStartAttachment.GetForModify(), pStartPoint, attachment );
  120. }
  121. void CRopeKeyframe::SetEndPoint( CBaseEntity *pEndPoint, int attachment )
  122. {
  123. SetAttachmentPoint( m_hEndPoint.GetForModify(), m_iEndAttachment.GetForModify(), pEndPoint, attachment );
  124. }
  125. void CRopeKeyframe::SetParent( CBaseEntity *pNewParent, int iAttachment )
  126. {
  127. CBaseEntity *pCurParent = GetMoveParent();
  128. if ( pCurParent )
  129. {
  130. pCurParent->DecrementTransmitStateOwnedCounter();
  131. pCurParent->DispatchUpdateTransmitState();
  132. }
  133. // Make sure our move parent always transmits or we get asserts on the client.
  134. if ( pNewParent )
  135. {
  136. pNewParent->IncrementTransmitStateOwnedCounter();
  137. pNewParent->SetTransmitState( FL_EDICT_ALWAYS );
  138. }
  139. BaseClass::SetParent( pNewParent, iAttachment );
  140. }
  141. void CRopeKeyframe::EnablePlayerWeaponAttach( bool bAttach )
  142. {
  143. int newFlags = m_RopeFlags;
  144. if ( bAttach )
  145. newFlags |= ROPE_PLAYER_WPN_ATTACH;
  146. else
  147. newFlags &= ~ROPE_PLAYER_WPN_ATTACH;
  148. if ( newFlags != m_RopeFlags )
  149. {
  150. m_RopeFlags = newFlags;
  151. }
  152. }
  153. CRopeKeyframe* CRopeKeyframe::Create(
  154. CBaseEntity *pStartEnt,
  155. CBaseEntity *pEndEnt,
  156. int iStartAttachment,
  157. int iEndAttachment,
  158. int ropeWidth,
  159. const char *pMaterialName,
  160. int numSegments
  161. )
  162. {
  163. CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
  164. if( !pRet )
  165. return NULL;
  166. pRet->SetStartPoint( pStartEnt, iStartAttachment );
  167. pRet->SetEndPoint( pEndEnt, iEndAttachment );
  168. pRet->m_bCreatedFromMapFile = false;
  169. pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
  170. pRet->Init();
  171. pRet->SetMaterial( pMaterialName );
  172. pRet->m_Width = ropeWidth;
  173. pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
  174. return pRet;
  175. }
  176. CRopeKeyframe* CRopeKeyframe::CreateWithSecondPointDetached(
  177. CBaseEntity *pStartEnt,
  178. int iStartAttachment,
  179. int ropeLength,
  180. int ropeWidth,
  181. const char *pMaterialName,
  182. int numSegments,
  183. bool bInitialHang
  184. )
  185. {
  186. CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" );
  187. if( !pRet )
  188. return NULL;
  189. pRet->SetStartPoint( pStartEnt, iStartAttachment );
  190. pRet->SetEndPoint( NULL, 0 );
  191. pRet->m_bCreatedFromMapFile = false;
  192. pRet->m_fLockedPoints.Set( ROPE_LOCK_START_POINT ); // Only attach the first point.
  193. if( !bInitialHang )
  194. {
  195. pRet->m_RopeFlags &= ~ROPE_INITIAL_HANG;
  196. }
  197. pRet->Init();
  198. pRet->SetMaterial( pMaterialName );
  199. pRet->m_RopeLength = ropeLength;
  200. pRet->m_Width = ropeWidth;
  201. pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
  202. return pRet;
  203. }
  204. void CRopeKeyframe::ActivateStartDirectionConstraints( bool bEnable )
  205. {
  206. if (bEnable)
  207. {
  208. m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_START_DIRECTION );
  209. }
  210. else
  211. {
  212. m_fLockedPoints &= ~((int)ROPE_LOCK_START_DIRECTION);
  213. }
  214. }
  215. void CRopeKeyframe::ActivateEndDirectionConstraints( bool bEnable )
  216. {
  217. if (bEnable)
  218. {
  219. m_fLockedPoints.Set( m_fLockedPoints | ROPE_LOCK_END_DIRECTION );
  220. }
  221. else
  222. {
  223. m_fLockedPoints &= ~((int)ROPE_LOCK_END_DIRECTION);
  224. }
  225. }
  226. void CRopeKeyframe::ShakeRopes( const Vector &vCenter, float flRadius, float flMagnitude )
  227. {
  228. CEffectData shakeData;
  229. shakeData.m_vOrigin = vCenter;
  230. shakeData.m_flRadius = flRadius;
  231. shakeData.m_flMagnitude = flMagnitude;
  232. DispatchEffect( "ShakeRopes", shakeData );
  233. }
  234. bool CRopeKeyframe::SetupHangDistance( float flHangDist )
  235. {
  236. CBaseEntity *pEnt1 = m_hStartPoint.Get();
  237. CBaseEntity *pEnt2 = m_hEndPoint.Get();
  238. if ( !pEnt1 || !pEnt2 )
  239. return false;
  240. // Calculate starting conditions so we can force it to hang down N inches.
  241. Vector v1 = pEnt1->GetAbsOrigin();
  242. if ( pEnt1->GetBaseAnimating() )
  243. pEnt1->GetBaseAnimating()->GetAttachment( m_iStartAttachment, v1 );
  244. Vector v2 = pEnt2->GetAbsOrigin();
  245. if ( pEnt2->GetBaseAnimating() )
  246. pEnt2->GetBaseAnimating()->GetAttachment( m_iEndAttachment, v2 );
  247. float flSlack, flLen;
  248. CalcRopeStartingConditions( v1, v2, ROPE_MAX_SEGMENTS, flHangDist, &flLen, &flSlack );
  249. m_RopeLength = (int)flLen;
  250. m_Slack = (int)flSlack;
  251. return true;
  252. }
  253. void CRopeKeyframe::Init()
  254. {
  255. SetLocalAngles( vec3_angle );
  256. RecalculateLength();
  257. m_nSegments = clamp( (int) m_nSegments, 2, ROPE_MAX_SEGMENTS );
  258. UpdateBBox( true );
  259. m_bStartPointValid = (m_hStartPoint.Get() != NULL);
  260. m_bEndPointValid = (m_hEndPoint.Get() != NULL);
  261. }
  262. void CRopeKeyframe::Activate()
  263. {
  264. BaseClass::Activate();
  265. if( !m_bCreatedFromMapFile )
  266. return;
  267. // Legacy support..
  268. if ( m_iRopeMaterialModelIndex == -1 )
  269. m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
  270. // Find the next entity in our chain.
  271. CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, m_iNextLinkName );
  272. if( pEnt && pEnt->edict() )
  273. {
  274. SetEndPoint( pEnt );
  275. if( m_spawnflags & SF_ROPE_RESIZE )
  276. m_RopeFlags |= ROPE_RESIZE;
  277. }
  278. else
  279. {
  280. // If we're from the map file, and we don't have a target ent, and
  281. // "Start Dangling" wasn't set, then this rope keyframe doesn't have
  282. // any rope coming out of it.
  283. if ( m_fLockedPoints & (int)ROPE_LOCK_END_POINT )
  284. {
  285. m_RopeFlags &= ~ROPE_SIMULATE;
  286. }
  287. }
  288. // By default, our start point is our own entity.
  289. SetStartPoint( this );
  290. // If we don't do this here, then when we save/load, we won't "own" the transmit
  291. // state of our parent, so the client might get our entity without our parent entity.
  292. SetParent( GetParent(), GetParentAttachment() );
  293. EndpointsChanged();
  294. Init();
  295. }
  296. void CRopeKeyframe::EndpointsChanged()
  297. {
  298. CBaseEntity *pStartEnt = m_hStartPoint.Get();
  299. if ( pStartEnt )
  300. {
  301. if ( (pStartEnt != this) || GetMoveParent() )
  302. {
  303. WatchPositionChanges( this, pStartEnt );
  304. }
  305. }
  306. CBaseEntity *pEndEnt = m_hEndPoint.Get();
  307. if ( pEndEnt )
  308. {
  309. if ( (pEndEnt != this) || GetMoveParent() )
  310. {
  311. WatchPositionChanges( this, pEndEnt );
  312. }
  313. }
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Calculate the length of the rope
  317. //-----------------------------------------------------------------------------
  318. void CRopeKeyframe::RecalculateLength( void )
  319. {
  320. // Get my entities
  321. if( m_hEndPoint.Get() )
  322. {
  323. CBaseEntity *pStartEnt = m_hStartPoint.Get();
  324. CBaseEntity *pEndEnt = m_hEndPoint.Get();
  325. // Set the length
  326. m_RopeLength = (int)( pStartEnt->GetAbsOrigin() - pEndEnt->GetAbsOrigin() ).Length();
  327. }
  328. else
  329. {
  330. m_RopeLength = 0;
  331. }
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose: This should remove the rope next time it reaches a resting state.
  335. // Right now only the client knows when it reaches a resting state, so
  336. // for now it just removes itself after a short time.
  337. //-----------------------------------------------------------------------------
  338. void CRopeKeyframe::DieAtNextRest( void )
  339. {
  340. SetThink( &CBaseEntity::SUB_Remove );
  341. SetNextThink( gpGlobals->curtime + 1.0f );
  342. }
  343. void CRopeKeyframe::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  344. {
  345. if ( !pInfo->m_pTransmitEdict->Get( entindex() ) )
  346. {
  347. BaseClass::SetTransmit( pInfo, bAlways );
  348. // Make sure our target ents are sent too.
  349. CBaseEntity *pEnt = m_hStartPoint;
  350. if ( pEnt )
  351. pEnt->SetTransmit( pInfo, bAlways );
  352. pEnt = m_hEndPoint;
  353. if ( pEnt )
  354. pEnt->SetTransmit( pInfo, bAlways );
  355. }
  356. }
  357. bool CRopeKeyframe::GetEndPointPos2( CBaseEntity *pAttached, int iAttachment, Vector &vPos )
  358. {
  359. if( !pAttached )
  360. return false;
  361. if ( iAttachment > 0 )
  362. {
  363. CBaseAnimating *pAnim = pAttached->GetBaseAnimating();
  364. if ( pAnim )
  365. {
  366. if( !pAnim->GetAttachment( iAttachment, vPos ) )
  367. return false;
  368. }
  369. else
  370. {
  371. return false;
  372. }
  373. }
  374. else
  375. {
  376. vPos = pAttached->GetAbsOrigin();
  377. }
  378. return true;
  379. }
  380. bool CRopeKeyframe::GetEndPointPos( int iPt, Vector &v )
  381. {
  382. if ( iPt == 0 )
  383. return GetEndPointPos2( m_hStartPoint, m_iStartAttachment, v );
  384. else
  385. return GetEndPointPos2( m_hEndPoint, m_iEndAttachment, v );
  386. }
  387. void CRopeKeyframe::UpdateBBox( bool bForceRelink )
  388. {
  389. Vector v1, v2;
  390. Vector vMin, vMax;
  391. if ( GetEndPointPos( 0, v1 ) )
  392. {
  393. if ( GetEndPointPos( 1, v2 ) )
  394. {
  395. VectorMin( v1, v2, vMin );
  396. VectorMax( v1, v2, vMax );
  397. // Set our bounds to enclose both endpoints and relink.
  398. vMin -= GetAbsOrigin();
  399. vMax -= GetAbsOrigin();
  400. }
  401. else
  402. {
  403. vMin = vMax = v1 - GetAbsOrigin();
  404. }
  405. }
  406. else
  407. {
  408. vMin = vMax = Vector( 0, 0, 0 );
  409. }
  410. if ( WorldAlignMins() != vMin || WorldAlignMaxs() != vMax )
  411. {
  412. UTIL_SetSize( this, vMin, vMax );
  413. }
  414. }
  415. //------------------------------------------------------------------------------
  416. // Purpose : Propagate force to each link in the rope. Check for loops
  417. // Input :
  418. // Output :
  419. //------------------------------------------------------------------------------
  420. void CRopeKeyframe::PropagateForce(CBaseEntity *pActivator, CBaseEntity *pCaller, CBaseEntity *pFirstLink, float x, float y, float z)
  421. {
  422. EntityMessageBegin( this, true );
  423. WRITE_FLOAT( x );
  424. WRITE_FLOAT( y );
  425. WRITE_FLOAT( z );
  426. MessageEnd();
  427. // UNDONE: Doesn't deal with intermediate loops
  428. // Propagate to next segment
  429. CRopeKeyframe *pNextLink = dynamic_cast<CRopeKeyframe*>((CBaseEntity *)m_hEndPoint);
  430. if (pNextLink && pNextLink != pFirstLink)
  431. {
  432. pNextLink->PropagateForce(pActivator, pCaller, pFirstLink, x, y, z);
  433. }
  434. }
  435. //------------------------------------------------------------------------------
  436. // Purpose: Set an instaneous force on the rope.
  437. // Input : Force vector.
  438. //------------------------------------------------------------------------------
  439. void CRopeKeyframe::InputSetForce( inputdata_t &inputdata )
  440. {
  441. Vector vecForce;
  442. inputdata.value.Vector3D(vecForce);
  443. PropagateForce( inputdata.pActivator, inputdata.pCaller, this, vecForce.x, vecForce.y, vecForce.z );
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Breaks the rope if able
  447. // Input : &inputdata -
  448. //-----------------------------------------------------------------------------
  449. void CRopeKeyframe::InputBreak( inputdata_t &inputdata )
  450. {
  451. //Route through the damage code
  452. Break();
  453. }
  454. //-----------------------------------------------------------------------------
  455. // Purpose: Breaks the rope
  456. // Output : Returns true on success, false on failure.
  457. //-----------------------------------------------------------------------------
  458. bool CRopeKeyframe::Break( void )
  459. {
  460. DetachPoint( 0 );
  461. // Find whoever references us and detach us from them.
  462. // UNDONE: PERFORMANCE: This is very slow!!!
  463. CRopeKeyframe *pTest = NULL;
  464. pTest = gEntList.NextEntByClass( pTest );
  465. while ( pTest )
  466. {
  467. if( stricmp( STRING(pTest->m_iNextLinkName), STRING(GetEntityName()) ) == 0 )
  468. {
  469. pTest->DetachPoint( 1 );
  470. }
  471. pTest = gEntList.NextEntByClass( pTest );
  472. }
  473. return true;
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Purpose:
  477. //-----------------------------------------------------------------------------
  478. void CRopeKeyframe::NotifyPositionChanged( CBaseEntity *pEntity )
  479. {
  480. // Update our bbox?
  481. UpdateBBox( false );
  482. CBaseEntity *ents[2] = { m_hStartPoint.Get(), m_hEndPoint.Get() };
  483. if ( (m_RopeFlags & ROPE_RESIZE) && ents[0] && ents[0]->edict() && ents[1] && ents[1]->edict() )
  484. {
  485. int len = (int)( ents[0]->GetAbsOrigin() - ents[1]->GetAbsOrigin() ).Length() + m_Slack;
  486. if ( len != m_RopeLength )
  487. {
  488. m_RopeLength = len;
  489. }
  490. }
  491. // Figure out if our attachment points have gone away and make sure to update the client if they have.
  492. bool *pValid[2] = { &m_bStartPointValid, &m_bEndPointValid };
  493. for ( int i=0; i < 2; i++ )
  494. {
  495. bool bCurrentlyValid = ( ents[i] != NULL );
  496. if ( *pValid[i] != bCurrentlyValid )
  497. {
  498. *pValid[i] = bCurrentlyValid;
  499. }
  500. }
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Take damage will break the rope
  504. //-----------------------------------------------------------------------------
  505. int CRopeKeyframe::OnTakeDamage( const CTakeDamageInfo &info )
  506. {
  507. // Only allow this if it's been marked
  508. if( !(m_RopeFlags & ROPE_BREAKABLE) )
  509. return false;
  510. Break();
  511. return 0;
  512. }
  513. void CRopeKeyframe::Precache()
  514. {
  515. m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
  516. BaseClass::Precache();
  517. }
  518. void CRopeKeyframe::DetachPoint( int iPoint )
  519. {
  520. Assert( iPoint == 0 || iPoint == 1 );
  521. m_fLockedPoints &= ~(1 << iPoint);
  522. }
  523. void CRopeKeyframe::EnableCollision()
  524. {
  525. if( !( m_RopeFlags & ROPE_COLLIDE ) )
  526. {
  527. m_RopeFlags |= ROPE_COLLIDE;
  528. }
  529. }
  530. void CRopeKeyframe::EnableWind( bool bEnable )
  531. {
  532. int flag = 0;
  533. if ( !bEnable )
  534. flag |= ROPE_NO_WIND;
  535. if ( (m_RopeFlags & ROPE_NO_WIND) != flag )
  536. {
  537. m_RopeFlags |= flag;
  538. }
  539. }
  540. bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue )
  541. {
  542. if( stricmp( szKeyName, "Breakable" ) == 0 )
  543. {
  544. if( atoi( szValue ) == 1 )
  545. m_RopeFlags |= ROPE_BREAKABLE;
  546. }
  547. else if( stricmp( szKeyName, "Collide" ) == 0 )
  548. {
  549. if( atoi( szValue ) == 1 )
  550. m_RopeFlags |= ROPE_COLLIDE;
  551. }
  552. else if( stricmp( szKeyName, "Barbed" ) == 0 )
  553. {
  554. if( atoi( szValue ) == 1 )
  555. m_RopeFlags |= ROPE_BARBED;
  556. }
  557. else if( stricmp( szKeyName, "Dangling" ) == 0 )
  558. {
  559. if( atoi( szValue ) == 1 )
  560. m_fLockedPoints &= ~ROPE_LOCK_END_POINT; // detach our dest point
  561. return true;
  562. }
  563. else if( stricmp( szKeyName, "Type" ) == 0 )
  564. {
  565. int iType = atoi( szValue );
  566. if( iType == 0 )
  567. m_nSegments = ROPE_MAX_SEGMENTS;
  568. else if( iType == 1 )
  569. m_nSegments = ROPE_TYPE1_NUMSEGMENTS;
  570. else
  571. m_nSegments = ROPE_TYPE2_NUMSEGMENTS;
  572. }
  573. else if ( stricmp( szKeyName, "RopeShader" ) == 0 )
  574. {
  575. // Legacy support for the RopeShader parameter.
  576. int iShader = atoi( szValue );
  577. if ( iShader == 0 )
  578. {
  579. m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" );
  580. }
  581. else if ( iShader == 1 )
  582. {
  583. m_iRopeMaterialModelIndex = PrecacheModel( "cable/rope.vmt" );
  584. }
  585. else
  586. {
  587. m_iRopeMaterialModelIndex = PrecacheModel( "cable/chain.vmt" );
  588. }
  589. }
  590. else if ( stricmp( szKeyName, "RopeMaterial" ) == 0 )
  591. {
  592. // Make sure we have a vmt extension.
  593. if ( Q_stristr( szValue, ".vmt" ) )
  594. {
  595. SetMaterial( szValue );
  596. }
  597. else
  598. {
  599. char str[512];
  600. Q_snprintf( str, sizeof( str ), "%s.vmt", szValue );
  601. SetMaterial( str );
  602. }
  603. }
  604. else if ( stricmp( szKeyName, "NoWind" ) == 0 )
  605. {
  606. if ( atoi( szValue ) == 1 )
  607. {
  608. m_RopeFlags |= ROPE_NO_WIND;
  609. }
  610. }
  611. return BaseClass::KeyValue( szKeyName, szValue );
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: Input handler that sets the scroll speed.
  615. //-----------------------------------------------------------------------------
  616. void CRopeKeyframe::InputSetScrollSpeed( inputdata_t &inputdata )
  617. {
  618. m_flScrollSpeed = inputdata.value.Float();
  619. }
  620. void CRopeKeyframe::SetMaterial( const char *pName )
  621. {
  622. m_strRopeMaterialModel = AllocPooledString( pName );
  623. m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) );
  624. }
  625. int CRopeKeyframe::UpdateTransmitState()
  626. {
  627. // Certain entities like sprites and ropes are strewn throughout the level and they rarely change.
  628. // For these entities, it's more efficient to transmit them once and then always leave them on
  629. // the client. Otherwise, the server will have to send big bursts of data with the entity states
  630. // as they come in and out of the PVS.
  631. return SetTransmitState( FL_EDICT_ALWAYS );
  632. }