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.

1248 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Clients CBaseObject
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_baseobject.h"
  9. #include "c_tf_player.h"
  10. #include "hud.h"
  11. #include "c_tf_team.h"
  12. #include "engine/IEngineSound.h"
  13. #include "particles_simple.h"
  14. #include "functionproxy.h"
  15. #include "IEffects.h"
  16. #include "model_types.h"
  17. #include "particlemgr.h"
  18. #include "particle_collision.h"
  19. #include "c_tf_weapon_builder.h"
  20. #include "ivrenderview.h"
  21. #include "ObjectControlPanel.h"
  22. #include "engine/ivmodelinfo.h"
  23. #include "c_te_effect_dispatch.h"
  24. #include "toolframework_client.h"
  25. #include "tf_hud_building_status.h"
  26. #include "cl_animevent.h"
  27. #include "eventlist.h"
  28. #include "c_obj_sapper.h"
  29. #include "tf_gamerules.h"
  30. #include "tf_hud_spectator_extras.h"
  31. #include "tf_proxyentity.h"
  32. // NVNT for building forces
  33. #include "haptics/haptic_utils.h"
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include "tier0/memdbgon.h"
  36. // forward declarations
  37. void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
  38. #define MAX_VISIBLE_BUILDPOINT_DISTANCE (400 * 400)
  39. // Remove aliasing of name due to shared code
  40. #undef CBaseObject
  41. IMPLEMENT_AUTO_LIST( IBaseObjectAutoList );
  42. IMPLEMENT_CLIENTCLASS_DT(C_BaseObject, DT_BaseObject, CBaseObject)
  43. RecvPropInt(RECVINFO(m_iHealth)),
  44. RecvPropInt(RECVINFO(m_iMaxHealth)),
  45. RecvPropInt(RECVINFO(m_bHasSapper)),
  46. RecvPropInt(RECVINFO(m_iObjectType)),
  47. RecvPropBool(RECVINFO(m_bBuilding)),
  48. RecvPropBool(RECVINFO(m_bPlacing)),
  49. RecvPropBool(RECVINFO(m_bCarried)),
  50. RecvPropBool(RECVINFO(m_bCarryDeploy)),
  51. RecvPropBool(RECVINFO(m_bMiniBuilding)),
  52. RecvPropFloat(RECVINFO(m_flPercentageConstructed)),
  53. RecvPropInt(RECVINFO(m_fObjectFlags)),
  54. RecvPropEHandle(RECVINFO(m_hBuiltOnEntity)),
  55. RecvPropInt( RECVINFO( m_bDisabled ) ),
  56. RecvPropEHandle( RECVINFO( m_hBuilder ) ),
  57. RecvPropVector( RECVINFO( m_vecBuildMaxs ) ),
  58. RecvPropVector( RECVINFO( m_vecBuildMins ) ),
  59. RecvPropInt( RECVINFO( m_iDesiredBuildRotations ) ),
  60. RecvPropInt( RECVINFO( m_bServerOverridePlacement ) ),
  61. RecvPropInt( RECVINFO(m_iUpgradeLevel) ),
  62. RecvPropInt( RECVINFO(m_iUpgradeMetal) ),
  63. RecvPropInt( RECVINFO(m_iUpgradeMetalRequired) ),
  64. RecvPropInt( RECVINFO(m_iHighestUpgradeLevel) ),
  65. RecvPropInt( RECVINFO(m_iObjectMode) ),
  66. RecvPropBool( RECVINFO( m_bDisposableBuilding ) ),
  67. RecvPropBool( RECVINFO( m_bWasMapPlaced ) ),
  68. RecvPropBool( RECVINFO( m_bPlasmaDisable ) ),
  69. END_RECV_TABLE()
  70. ConVar cl_obj_test_building_damage( "cl_obj_test_building_damage", "-1", FCVAR_CHEAT, "debug building damage", true, -1, true, BUILDING_DAMAGE_LEVEL_CRITICAL );
  71. //-----------------------------------------------------------------------------
  72. // Purpose:
  73. //-----------------------------------------------------------------------------
  74. C_BaseObject::C_BaseObject( )
  75. {
  76. m_YawPreviewState = YAW_PREVIEW_OFF;
  77. m_bBuilding = false;
  78. m_bPlacing = false;
  79. m_flPercentageConstructed = 0;
  80. m_fObjectFlags = 0;
  81. m_iOldUpgradeLevel = 0;
  82. m_flCurrentBuildRotation = 0;
  83. m_damageLevel = BUILDING_DAMAGE_LEVEL_NONE;
  84. m_iLastPlacementPosValid = -1;
  85. m_iObjectMode = 0;
  86. m_bCarryDeploy = false;
  87. m_bOldCarryDeploy = false;
  88. m_bMiniBuilding = false;
  89. m_bDisposableBuilding = false;
  90. m_vecBuildForward = vec3_origin;
  91. m_flBuildDistance = 0.0f;
  92. m_flInvisibilityPercent = 0.f;
  93. m_bWasMapPlaced = false;
  94. m_hDamageEffects = NULL;
  95. m_bPlasmaDisable = false;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose:
  99. //-----------------------------------------------------------------------------
  100. C_BaseObject::~C_BaseObject( void )
  101. {
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. void C_BaseObject::Spawn( void )
  107. {
  108. BaseClass::Spawn();
  109. m_bServerOverridePlacement = true; // assume valid at the start
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose:
  113. //-----------------------------------------------------------------------------
  114. void C_BaseObject::UpdateOnRemove( void )
  115. {
  116. StopAnimGeneratedSounds();
  117. DestroyBoneAttachments();
  118. CTFHudSpectatorExtras *pSpectatorExtras = GET_HUDELEMENT( CTFHudSpectatorExtras );
  119. if ( pSpectatorExtras )
  120. {
  121. pSpectatorExtras->RemoveEntity( entindex() );
  122. }
  123. BaseClass::UpdateOnRemove();
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose:
  127. //-----------------------------------------------------------------------------
  128. void C_BaseObject::PreDataUpdate( DataUpdateType_t updateType )
  129. {
  130. BaseClass::PreDataUpdate( updateType );
  131. m_iOldHealth = m_iHealth;
  132. m_hOldOwner = GetOwner();
  133. m_bWasActive = ShouldBeActive();
  134. m_bWasBuilding = m_bBuilding;
  135. m_bOldDisabled = m_bDisabled;
  136. m_bWasPlacing = m_bPlacing;
  137. m_nObjectOldSequence = GetSequence();
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose:
  141. //-----------------------------------------------------------------------------
  142. void C_BaseObject::OnDataChanged( DataUpdateType_t updateType )
  143. {
  144. if (updateType == DATA_UPDATE_CREATED)
  145. {
  146. CreateBuildPoints();
  147. // NVNT if the local player created this send a created effect
  148. if(IsOwnedByLocalPlayer() &&haptics)
  149. {
  150. haptics->ProcessHapticEvent(3, "Game", "Build", GetClassname());
  151. }
  152. }
  153. BaseClass::OnDataChanged( updateType );
  154. // did we just pick up the object?
  155. if ( !m_bWasPlacing && m_bPlacing )
  156. {
  157. m_iLastPlacementPosValid = -1;
  158. }
  159. // Did we just finish building?
  160. if ( m_bWasBuilding && !m_bBuilding )
  161. {
  162. FinishedBuilding();
  163. }
  164. else if ( !m_bWasBuilding && m_bBuilding )
  165. {
  166. ResetClientsideFrame();
  167. }
  168. // Did we just go active?
  169. bool bShouldBeActive = ShouldBeActive();
  170. if ( !m_bWasActive && bShouldBeActive )
  171. {
  172. OnGoActive();
  173. }
  174. else if ( m_bWasActive && !bShouldBeActive )
  175. {
  176. OnGoInactive();
  177. }
  178. if ( m_bDisabled != m_bOldDisabled )
  179. {
  180. if ( m_bDisabled )
  181. {
  182. OnStartDisabled();
  183. }
  184. else
  185. {
  186. OnEndDisabled();
  187. }
  188. }
  189. if ( !IsBuilding() && m_iHealth != m_iOldHealth )
  190. {
  191. // recalc our damage particle state
  192. BuildingDamageLevel_t damageLevel = CalculateDamageLevel();
  193. if ( damageLevel != m_damageLevel )
  194. {
  195. UpdateDamageEffects( damageLevel );
  196. m_damageLevel = damageLevel;
  197. }
  198. }
  199. if ( m_bCarryDeploy != m_bOldCarryDeploy )
  200. {
  201. m_bOldCarryDeploy = m_bCarryDeploy;
  202. if ( !m_bCarryDeploy )
  203. {
  204. // Update our damage effects when we're done redeploying.
  205. UpdateDamageEffects( CalculateDamageLevel() );
  206. }
  207. }
  208. if ( m_iHealth > m_iOldHealth && m_iHealth == m_iMaxHealth )
  209. {
  210. // If we were just fully healed, remove all decals
  211. RemoveAllDecals();
  212. }
  213. if ( GetOwner() == C_TFPlayer::GetLocalTFPlayer() )
  214. {
  215. IGameEvent *event = gameeventmanager->CreateEvent( "building_info_changed" );
  216. if ( event )
  217. {
  218. event->SetInt( "building_type", GetType() );
  219. event->SetInt( "object_mode", GetObjectMode() );
  220. gameeventmanager->FireEventClientSide( event );
  221. }
  222. }
  223. if ( IsPlacing() && GetSequence() != m_nObjectOldSequence )
  224. {
  225. // Ignore server sequences while placing
  226. OnPlacementStateChanged( m_iLastPlacementPosValid > 0 );
  227. }
  228. if ( m_iOldUpgradeLevel != m_iUpgradeLevel )
  229. {
  230. UpgradeLevelChanged();
  231. m_iOldUpgradeLevel = m_iUpgradeLevel;
  232. }
  233. // NVNT building status
  234. if(IsOwnedByLocalPlayer()) {
  235. if(m_bWasBuilding!=m_bBuilding) {
  236. if(m_bBuilding && haptics) {
  237. haptics->ProcessHapticEvent(3, "Game", "Building", GetClassname());
  238. }
  239. }
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. //-----------------------------------------------------------------------------
  245. void C_BaseObject::SetDormant( bool bDormant )
  246. {
  247. BaseClass::SetDormant( bDormant );
  248. //ENTITY_PANEL_ACTIVATE( "analyzed_object", !bDormant );
  249. }
  250. #define TF_OBJ_BODYGROUPTURNON 1
  251. #define TF_OBJ_BODYGROUPTURNOFF 0
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : origin -
  255. // angles -
  256. // event -
  257. // *options -
  258. //-----------------------------------------------------------------------------
  259. void C_BaseObject::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
  260. {
  261. switch ( event )
  262. {
  263. default:
  264. {
  265. BaseClass::FireEvent( origin, angles, event, options );
  266. }
  267. break;
  268. case TF_OBJ_PLAYBUILDSOUND:
  269. {
  270. EmitSound( options );
  271. }
  272. break;
  273. case TF_OBJ_ENABLEBODYGROUP:
  274. {
  275. int index_ = FindBodygroupByName( options );
  276. if ( index_ >= 0 )
  277. {
  278. SetBodygroup( index_, TF_OBJ_BODYGROUPTURNON );
  279. }
  280. }
  281. break;
  282. case TF_OBJ_DISABLEBODYGROUP:
  283. {
  284. int index_ = FindBodygroupByName( options );
  285. if ( index_ >= 0 )
  286. {
  287. SetBodygroup( index_, TF_OBJ_BODYGROUPTURNOFF );
  288. }
  289. }
  290. break;
  291. case TF_OBJ_ENABLEALLBODYGROUPS:
  292. case TF_OBJ_DISABLEALLBODYGROUPS:
  293. {
  294. // Start at 1, because body 0 is the main .mdl body...
  295. // Is this the way we want to do this?
  296. int count = GetNumBodyGroups();
  297. for ( int i = 1; i < count; i++ )
  298. {
  299. int subpartcount = GetBodygroupCount( i );
  300. if ( subpartcount == 2 )
  301. {
  302. SetBodygroup( i,
  303. ( event == TF_OBJ_ENABLEALLBODYGROUPS ) ?
  304. TF_OBJ_BODYGROUPTURNON : TF_OBJ_BODYGROUPTURNOFF );
  305. }
  306. else
  307. {
  308. DevMsg( "TF_OBJ_ENABLE/DISABLEBODY GROUP: %s has a group with %i subparts, should be exactly 2\n",
  309. GetClassname(), subpartcount );
  310. }
  311. }
  312. }
  313. break;
  314. }
  315. }
  316. const char* C_BaseObject::GetStatusName() const
  317. {
  318. return GetObjectInfo( GetType() )->m_AltModes[GetObjectMode()].pszStatusName;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: placement state has changed, update the model
  322. //-----------------------------------------------------------------------------
  323. void C_BaseObject::OnPlacementStateChanged( bool bValidPlacement )
  324. {
  325. if ( bValidPlacement )
  326. {
  327. // NVNT if the local player placed this send a created effect
  328. if(IsOwnedByLocalPlayer()&&haptics)
  329. {
  330. haptics->ProcessHapticEvent(3, "Game", "Placed", GetClassname());
  331. }
  332. SetActivity( ACT_OBJ_PLACING );
  333. }
  334. else
  335. {
  336. SetActivity( ACT_OBJ_IDLE );
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose:
  341. //-----------------------------------------------------------------------------
  342. void C_BaseObject::Simulate( void )
  343. {
  344. if ( IsPlacing() && !MustBeBuiltOnAttachmentPoint() )
  345. {
  346. int iValidPlacement = ( IsPlacementPosValid() && ServerValidPlacement() ) ? 1 : 0;
  347. if ( m_iLastPlacementPosValid != iValidPlacement )
  348. {
  349. m_iLastPlacementPosValid = iValidPlacement;
  350. OnPlacementStateChanged( m_iLastPlacementPosValid > 0 );
  351. }
  352. // We figure out our own placement pos, but we still leave it to the server to
  353. // do collision with other entities and nobuild triggers, so that sets the
  354. // placement animation
  355. SetLocalOrigin( m_vecBuildOrigin );
  356. InvalidateBoneCache();
  357. // Clear out our origin and rotation interpolation history
  358. // so we don't pop when we teleport in the actual position from the server
  359. CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
  360. interpolator.ClearHistory();
  361. CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator();
  362. rotInterpolator.ClearHistory();
  363. }
  364. else if ( !IsPlacing() && !IsCarried() && m_iLastPlacementPosValid == 0 )
  365. {
  366. // HACK HACK: This sentry has been placed, but was placed on the server before the client updated
  367. // from the carry position to see that was a valid placement.
  368. // It missed its chance to set the correct activity, so we're doing it now.
  369. SetActivity( ACT_OBJ_RUNNING );
  370. // Check if the activity was valid because it might have still been using the older placement model
  371. if ( GetActivity() != ACT_INVALID )
  372. {
  373. // Remember to retest our placement, but don't keep forcing the running activity
  374. m_iLastPlacementPosValid = -1;
  375. }
  376. }
  377. BaseClass::Simulate();
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose: Return false if the server is telling us we can't place right now
  381. // could be due to placing in a nobuild or respawn room
  382. //-----------------------------------------------------------------------------
  383. bool C_BaseObject::ServerValidPlacement( void )
  384. {
  385. return m_bServerOverridePlacement;
  386. }
  387. bool C_BaseObject::WasLastPlacementPosValid( void )
  388. {
  389. if ( MustBeBuiltOnAttachmentPoint() )
  390. {
  391. return ( !IsEffectActive(EF_NODRAW) );
  392. }
  393. return ( m_iLastPlacementPosValid > 0 );
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose:
  397. //-----------------------------------------------------------------------------
  398. int C_BaseObject::DrawModel( int flags )
  399. {
  400. int drawn;
  401. // If we're a brush-built, map-defined object chain up to baseentity draw
  402. if ( modelinfo->GetModelType( GetModel() ) == mod_brush )
  403. {
  404. drawn = CBaseEntity::DrawModel(flags);
  405. }
  406. else
  407. {
  408. drawn = BaseClass::DrawModel(flags);
  409. }
  410. HighlightBuildPoints( flags );
  411. return drawn;
  412. }
  413. float C_BaseObject::GetReversesBuildingConstructionSpeed( void )
  414. {
  415. if ( HasSapper() )
  416. {
  417. C_ObjectSapper *pSapper = dynamic_cast< C_ObjectSapper* >( FirstMoveChild() );
  418. if ( pSapper )
  419. {
  420. return pSapper->GetReversesBuildingConstructionSpeed();
  421. }
  422. }
  423. return 0.0f;
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose:
  427. //-----------------------------------------------------------------------------
  428. void C_BaseObject::HighlightBuildPoints( int flags )
  429. {
  430. C_TFPlayer *pLocal = C_TFPlayer::GetLocalTFPlayer();
  431. if ( !pLocal )
  432. return;
  433. if ( !GetNumBuildPoints() || !InLocalTeam() )
  434. return;
  435. C_TFWeaponBuilder *pBuilderWpn = dynamic_cast< C_TFWeaponBuilder * >( pLocal->GetActiveWeaponForSelection() );
  436. if ( !pBuilderWpn )
  437. return;
  438. if ( !pBuilderWpn->IsPlacingObject() )
  439. return;
  440. C_BaseObject *pPlacementObj = pBuilderWpn->GetPlacementModel();
  441. if ( !pPlacementObj || pPlacementObj == this )
  442. return;
  443. // Near enough?
  444. if ( (GetAbsOrigin() - pLocal->GetAbsOrigin()).LengthSqr() < MAX_VISIBLE_BUILDPOINT_DISTANCE )
  445. {
  446. bool bRestoreModel = false;
  447. Vector vecPrevAbsOrigin = pPlacementObj->GetAbsOrigin();
  448. QAngle vecPrevAbsAngles = pPlacementObj->GetAbsAngles();
  449. Vector orgColor;
  450. render->GetColorModulation( orgColor.Base() );
  451. float orgBlend = render->GetBlend();
  452. bool bSameTeam = ( pPlacementObj->GetTeamNumber() == GetTeamNumber() );
  453. if ( pPlacementObj->IsHostileUpgrade() && bSameTeam )
  454. {
  455. // Don't hilight hostile upgrades on friendly objects
  456. return;
  457. }
  458. else if ( !bSameTeam )
  459. {
  460. // Don't hilight upgrades on enemy objects
  461. return;
  462. }
  463. // Any empty buildpoints?
  464. for ( int i = 0; i < GetNumBuildPoints(); i++ )
  465. {
  466. // Can this object build on this point?
  467. if ( CanBuildObjectOnBuildPoint( i, pPlacementObj->GetType() ) )
  468. {
  469. Vector vecBPOrigin;
  470. QAngle vecBPAngles;
  471. if ( GetBuildPoint(i, vecBPOrigin, vecBPAngles) )
  472. {
  473. pPlacementObj->InvalidateBoneCaches();
  474. Vector color( 0, 255, 0 );
  475. render->SetColorModulation( color.Base() );
  476. float frac = fmod( gpGlobals->curtime, 3 );
  477. frac *= 2 * M_PI;
  478. frac = cos( frac );
  479. render->SetBlend( (175 + (int)( frac * 75.0f )) / 255.0 );
  480. // FIXME: This truly sucks! The bone cache should use
  481. // render location for this computation instead of directly accessing AbsAngles
  482. // Necessary for bone cache computations to work
  483. pPlacementObj->SetAbsOrigin( vecBPOrigin );
  484. pPlacementObj->SetAbsAngles( vecBPAngles );
  485. modelrender->DrawModel(
  486. flags,
  487. pPlacementObj,
  488. pPlacementObj->GetModelInstance(),
  489. pPlacementObj->index,
  490. pPlacementObj->GetModel(),
  491. vecBPOrigin,
  492. vecBPAngles,
  493. pPlacementObj->m_nSkin,
  494. pPlacementObj->m_nBody,
  495. pPlacementObj->m_nHitboxSet
  496. );
  497. bRestoreModel = true;
  498. }
  499. }
  500. }
  501. if ( bRestoreModel )
  502. {
  503. pPlacementObj->SetAbsOrigin(vecPrevAbsOrigin);
  504. pPlacementObj->SetAbsAngles(vecPrevAbsAngles);
  505. pPlacementObj->InvalidateBoneCaches();
  506. render->SetColorModulation( orgColor.Base() );
  507. render->SetBlend( orgBlend );
  508. }
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Builder preview...
  513. //-----------------------------------------------------------------------------
  514. void C_BaseObject::ActivateYawPreview( bool enable )
  515. {
  516. m_YawPreviewState = enable ? YAW_PREVIEW_ON : YAW_PREVIEW_WAITING_FOR_UPDATE;
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose:
  520. //-----------------------------------------------------------------------------
  521. void C_BaseObject::PreviewYaw( float yaw )
  522. {
  523. m_fYawPreview = yaw;
  524. }
  525. //-----------------------------------------------------------------------------
  526. // Purpose:
  527. //-----------------------------------------------------------------------------
  528. bool C_BaseObject::IsPreviewingYaw() const
  529. {
  530. return m_YawPreviewState != YAW_PREVIEW_OFF;
  531. }
  532. //-----------------------------------------------------------------------------
  533. // Purpose:
  534. //-----------------------------------------------------------------------------
  535. BuildingDamageLevel_t C_BaseObject::CalculateDamageLevel( void )
  536. {
  537. float flPercentHealth = (float)m_iHealth / (float)m_iMaxHealth;
  538. BuildingDamageLevel_t damageLevel = BUILDING_DAMAGE_LEVEL_NONE;
  539. if ( flPercentHealth < 0.25 )
  540. {
  541. damageLevel = BUILDING_DAMAGE_LEVEL_CRITICAL;
  542. }
  543. else if ( flPercentHealth < 0.45 )
  544. {
  545. damageLevel = BUILDING_DAMAGE_LEVEL_HEAVY;
  546. }
  547. else if ( flPercentHealth < 0.65 )
  548. {
  549. damageLevel = BUILDING_DAMAGE_LEVEL_MEDIUM;
  550. }
  551. else if ( flPercentHealth < 0.85 )
  552. {
  553. damageLevel = BUILDING_DAMAGE_LEVEL_LIGHT;
  554. }
  555. if ( cl_obj_test_building_damage.GetInt() >= 0 )
  556. {
  557. damageLevel = (BuildingDamageLevel_t)cl_obj_test_building_damage.GetInt();
  558. }
  559. return damageLevel;
  560. }
  561. /*
  562. //-----------------------------------------------------------------------------
  563. // Purpose:
  564. //-----------------------------------------------------------------------------
  565. void C_BaseObject::Release( void )
  566. {
  567. // Remove any reticles on this entity
  568. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  569. if ( pPlayer )
  570. {
  571. pPlayer->Remove_Target( this );
  572. }
  573. BaseClass::Release();
  574. }
  575. */
  576. //-----------------------------------------------------------------------------
  577. // Ownership:
  578. //-----------------------------------------------------------------------------
  579. C_TFPlayer *C_BaseObject::GetOwner()
  580. {
  581. return m_hBuilder;
  582. }
  583. //-----------------------------------------------------------------------------
  584. // Purpose:
  585. //-----------------------------------------------------------------------------
  586. bool C_BaseObject::IsOwnedByLocalPlayer() const
  587. {
  588. if ( !m_hBuilder )
  589. return false;
  590. return ( m_hBuilder == C_TFPlayer::GetLocalTFPlayer() );
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose: Add entity to visibile entities list
  594. //-----------------------------------------------------------------------------
  595. void C_BaseObject::AddEntity( void )
  596. {
  597. // If set to invisible, skip. Do this before resetting the entity pointer so it has
  598. // valid data to decide whether it's visible.
  599. if ( !ShouldDraw() )
  600. {
  601. return;
  602. }
  603. // Update the entity position
  604. //UpdatePosition();
  605. // Yaw preview
  606. if (m_YawPreviewState != YAW_PREVIEW_OFF)
  607. {
  608. // This piece of code makes it so we keep using the preview
  609. // until we get a network update which matches the update value
  610. if (m_YawPreviewState == YAW_PREVIEW_WAITING_FOR_UPDATE)
  611. {
  612. if (fmod( fabs(GetLocalAngles().y - m_fYawPreview), 360.0f) < 1.0f)
  613. {
  614. m_YawPreviewState = YAW_PREVIEW_OFF;
  615. }
  616. }
  617. if (GetLocalOrigin().y != m_fYawPreview)
  618. {
  619. SetLocalAnglesDim( Y_INDEX, m_fYawPreview );
  620. InvalidateBoneCache();
  621. }
  622. }
  623. // Create flashlight effects, etc.
  624. CreateLightEffects();
  625. }
  626. //-----------------------------------------------------------------------------
  627. // Purpose:
  628. //-----------------------------------------------------------------------------
  629. void C_BaseObject::Select( void )
  630. {
  631. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  632. pPlayer->SetSelectedObject( this );
  633. }
  634. void C_BaseObject::ResetClientsideFrame( void )
  635. {
  636. SetCycle( GetReversesBuildingConstructionSpeed() != 0.0f ? 1.0f : 0.0f );
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Sends client commands back to the server:
  640. //-----------------------------------------------------------------------------
  641. void C_BaseObject::SendClientCommand( const char *pCmd )
  642. {
  643. char szbuf[128];
  644. Q_snprintf( szbuf, sizeof( szbuf ), "objcmd %d %s", entindex(), pCmd );
  645. engine->ClientCmd(szbuf);
  646. }
  647. //-----------------------------------------------------------------------------
  648. // Purpose: Get a text description for the object target
  649. //-----------------------------------------------------------------------------
  650. const char *C_BaseObject::GetTargetDescription( void ) const
  651. {
  652. return GetStatusName();
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Get a text description for the object target (more verbose)
  656. //-----------------------------------------------------------------------------
  657. const char *C_BaseObject::GetIDString( void )
  658. {
  659. m_szIDString[0] = 0;
  660. RecalculateIDString();
  661. return m_szIDString;
  662. }
  663. //-----------------------------------------------------------------------------
  664. // It's a valid ID target when it's building
  665. //-----------------------------------------------------------------------------
  666. bool C_BaseObject::IsValidIDTarget( void )
  667. {
  668. return InSameTeam( C_TFPlayer::GetLocalTFPlayer() ) && m_bBuilding;
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose:
  672. //-----------------------------------------------------------------------------
  673. void C_BaseObject::RecalculateIDString( void )
  674. {
  675. // Subclasses may have filled this out with a string
  676. if ( !m_szIDString[0] )
  677. {
  678. Q_strncpy( m_szIDString, GetTargetDescription(), sizeof(m_szIDString) );
  679. }
  680. // Have I taken damage?
  681. if ( m_iHealth < m_iMaxHealth )
  682. {
  683. char szHealth[ MAX_ID_STRING ];
  684. if ( m_bBuilding )
  685. {
  686. Q_snprintf( szHealth, sizeof(szHealth), "\nConstruction at %.0f percent\nHealth at %.0f percent", (m_flPercentageConstructed * 100), ceil(((float)m_iHealth / (float)m_iMaxHealth) * 100) );
  687. }
  688. else
  689. {
  690. Q_snprintf( szHealth, sizeof(szHealth), "\nHealth at %.0f percent", ceil(((float)m_iHealth / (float)m_iMaxHealth) * 100) );
  691. }
  692. Q_strncat( m_szIDString, szHealth, sizeof(m_szIDString), COPY_ALL_CHARACTERS );
  693. }
  694. }
  695. //-----------------------------------------------------------------------------
  696. // Purpose: Player has waved his crosshair over this entity. Display appropriate hints.
  697. //-----------------------------------------------------------------------------
  698. void C_BaseObject::DisplayHintTo( C_BasePlayer *pPlayer )
  699. {
  700. bool bHintPlayed = false;
  701. C_TFPlayer *pTFPlayer = ToTFPlayer(pPlayer);
  702. if ( InSameTeam( pPlayer ) )
  703. {
  704. // We're looking at a friendly object.
  705. if ( HasSapper() )
  706. {
  707. bHintPlayed = pPlayer->HintMessage( HINT_OBJECT_HAS_SAPPER, true, true );
  708. }
  709. if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
  710. {
  711. // I'm an engineer.
  712. // If I'm looking at a constructing object, let me know I can help build it (but not
  713. // if I built it myself, since I've already got that hint from the wrench).
  714. if ( !bHintPlayed && IsBuilding() && GetBuilder() != pTFPlayer )
  715. {
  716. bHintPlayed = pPlayer->HintMessage( HINT_ENGINEER_USE_WRENCH_ONOTHER, false, true );
  717. }
  718. // If it's damaged, I can repair it
  719. if ( !bHintPlayed && !IsBuilding() && GetHealth() < GetMaxHealth() )
  720. {
  721. bHintPlayed = pPlayer->HintMessage( HINT_ENGINEER_REPAIR_OBJECT, false, true );
  722. }
  723. }
  724. }
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose:
  728. //-----------------------------------------------------------------------------
  729. void C_BaseObject::GetGlowEffectColor( float *r, float *g, float *b )
  730. {
  731. if ( TFGameRules() )
  732. {
  733. TFGameRules()->GetTeamGlowColor( GetTeamNumber(), *r, *g, *b );
  734. }
  735. else
  736. {
  737. *r = 0.76f;
  738. *g = 0.76f;
  739. *b = 0.76f;
  740. }
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose: Does this object have a sapper on it
  744. //-----------------------------------------------------------------------------
  745. bool C_BaseObject::HasSapper( void )
  746. {
  747. return m_bHasSapper;
  748. }
  749. //-----------------------------------------------------------------------------
  750. // Purpose:
  751. //-----------------------------------------------------------------------------
  752. bool C_BaseObject::IsPlasmaDisabled( void )
  753. {
  754. return m_bPlasmaDisable;
  755. }
  756. void C_BaseObject::OnStartDisabled()
  757. {
  758. }
  759. void C_BaseObject::OnEndDisabled()
  760. {
  761. }
  762. //-----------------------------------------------------------------------------
  763. // Purpose:
  764. //-----------------------------------------------------------------------------
  765. void C_BaseObject::GetTargetIDString( OUT_Z_BYTECAP( iMaxLenInBytes ) wchar_t *sIDString, int iMaxLenInBytes, bool bSpectator )
  766. {
  767. Assert( iMaxLenInBytes >= sizeof(sIDString[0]) );
  768. sIDString[0] = '\0';
  769. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  770. if ( !pLocalPlayer )
  771. return;
  772. if ( pLocalPlayer->InSameDisguisedTeam( this ) || pLocalPlayer->IsPlayerClass( TF_CLASS_SPY ) || bSpectator )
  773. {
  774. wchar_t wszBuilderName[ MAX_PLAYER_NAME_LENGTH ];
  775. const char *pszStatusName = GetStatusName();
  776. const wchar_t *wszObjectName = g_pVGuiLocalize->Find( pszStatusName );
  777. bool bHasMode = false;
  778. const char *printFormatString = "#TF_playerid_object";
  779. if ( IsMiniBuilding() && !IsDisposableBuilding() )
  780. {
  781. printFormatString = "#TF_playerid_object_mini";
  782. }
  783. const wchar_t *wszModeName = L"";
  784. const CObjectInfo* pObjectInfo = GetObjectInfo( GetType() );
  785. if ( pObjectInfo && (pObjectInfo->m_iNumAltModes > 0) )
  786. {
  787. const char *pszModeName = pObjectInfo->m_AltModes[GetObjectMode()].pszModeName;
  788. wszModeName = g_pVGuiLocalize->Find( pszModeName );
  789. printFormatString = "TF_playerid_object_mode";
  790. bHasMode = true;
  791. }
  792. if ( !wszObjectName )
  793. {
  794. wszObjectName = L"";
  795. }
  796. C_BasePlayer *pBuilder = GetOwner();
  797. if ( pBuilder )
  798. {
  799. g_pVGuiLocalize->ConvertANSIToUnicode( pBuilder->GetPlayerName(), wszBuilderName, sizeof(wszBuilderName) );
  800. }
  801. else
  802. {
  803. wszBuilderName[0] = '\0';
  804. }
  805. // building or live, show health
  806. wchar_t * localizedString = g_pVGuiLocalize->Find( printFormatString );
  807. if ( localizedString )
  808. {
  809. if ( bHasMode )
  810. {
  811. g_pVGuiLocalize->ConstructString( sIDString, iMaxLenInBytes, localizedString,
  812. 3, wszObjectName, wszBuilderName, wszModeName );
  813. }
  814. else
  815. {
  816. g_pVGuiLocalize->ConstructString( sIDString, iMaxLenInBytes, localizedString,
  817. 2, wszObjectName, wszBuilderName );
  818. }
  819. }
  820. }
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose:
  824. //-----------------------------------------------------------------------------
  825. void C_BaseObject::GetTargetIDDataString( OUT_Z_BYTECAP(iMaxLenInBytes) wchar_t *sDataString, int iMaxLenInBytes )
  826. {
  827. Assert( iMaxLenInBytes >= sizeof(sDataString[0]) );
  828. sDataString[0] = '\0';
  829. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  830. if ( !pLocalPlayer )
  831. return;
  832. // Sentryguns have models for each level, so we don't show it in their target ID.
  833. bool bShowLevel = ( GetType() != OBJ_SENTRYGUN );
  834. wchar_t wszLevel[32];
  835. if ( bShowLevel )
  836. {
  837. _snwprintf( wszLevel, ARRAYSIZE(wszLevel) - 1, L"%d", m_iUpgradeLevel );
  838. wszLevel[ ARRAYSIZE(wszLevel)-1 ] = '\0';
  839. }
  840. if ( m_iUpgradeLevel >= 3 )
  841. {
  842. if ( bShowLevel )
  843. {
  844. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_level"),
  845. 1,
  846. wszLevel );
  847. }
  848. return;
  849. }
  850. wchar_t wszBuilderName[ MAX_PLAYER_NAME_LENGTH ];
  851. wchar_t wszObjectName[ 32 ];
  852. wchar_t wszUpgradeProgress[ 32 ];
  853. g_pVGuiLocalize->ConvertANSIToUnicode( GetStatusName(), wszObjectName, sizeof(wszObjectName) );
  854. C_BasePlayer *pBuilder = GetOwner();
  855. if ( pBuilder )
  856. {
  857. g_pVGuiLocalize->ConvertANSIToUnicode( pBuilder->GetPlayerName(), wszBuilderName, sizeof(wszBuilderName) );
  858. }
  859. else
  860. {
  861. wszBuilderName[0] = '\0';
  862. }
  863. // level 1 and 2 show upgrade progress
  864. if ( !IsMiniBuilding() && !IsDisposableBuilding() )
  865. {
  866. _snwprintf( wszUpgradeProgress, ARRAYSIZE(wszUpgradeProgress) - 1, L"%d / %d", m_iUpgradeMetal, GetUpgradeMetalRequired() );
  867. wszUpgradeProgress[ ARRAYSIZE(wszUpgradeProgress)-1 ] = '\0';
  868. if ( bShowLevel )
  869. {
  870. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_upgrading_level"),
  871. 2,
  872. wszLevel,
  873. wszUpgradeProgress );
  874. }
  875. else
  876. {
  877. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_upgrading"),
  878. 1,
  879. wszUpgradeProgress );
  880. }
  881. }
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose:
  885. //-----------------------------------------------------------------------------
  886. int C_BaseObject::GetDisplayPriority( void )
  887. {
  888. return GetObjectInfo( GetType() )->m_iDisplayPriority;
  889. }
  890. //-----------------------------------------------------------------------------
  891. // Purpose:
  892. //-----------------------------------------------------------------------------
  893. const char *C_BaseObject::GetHudStatusIcon( void )
  894. {
  895. return GetObjectInfo( GetType() )->m_pHudStatusIcon;
  896. }
  897. ConVar cl_obj_fake_alert( "cl_obj_fake_alert", "0", 0, "", true, BUILDING_HUD_ALERT_NONE, true, MAX_BUILDING_HUD_ALERT_LEVEL-1 );
  898. //-----------------------------------------------------------------------------
  899. // Purpose:
  900. //-----------------------------------------------------------------------------
  901. BuildingHudAlert_t C_BaseObject::GetBuildingAlertLevel( void )
  902. {
  903. float flHealthPercent = GetHealth() / GetMaxHealth();
  904. BuildingHudAlert_t alertLevel = BUILDING_HUD_ALERT_NONE;
  905. if ( HasSapper() )
  906. {
  907. alertLevel = BUILDING_HUD_ALERT_SAPPER;
  908. }
  909. else if ( !IsBuilding() && flHealthPercent < 0.33 )
  910. {
  911. alertLevel = BUILDING_HUD_ALERT_VERY_LOW_HEALTH;
  912. }
  913. else if ( !IsBuilding() && flHealthPercent < 0.66 )
  914. {
  915. alertLevel = BUILDING_HUD_ALERT_LOW_HEALTH;
  916. }
  917. BuildingHudAlert_t iFakeAlert = (BuildingHudAlert_t)cl_obj_fake_alert.GetInt();
  918. if ( iFakeAlert > BUILDING_HUD_ALERT_NONE &&
  919. iFakeAlert < MAX_BUILDING_HUD_ALERT_LEVEL )
  920. {
  921. alertLevel = iFakeAlert;
  922. }
  923. return alertLevel;
  924. }
  925. //-----------------------------------------------------------------------------
  926. // Purpose:
  927. //-----------------------------------------------------------------------------
  928. ShadowType_t C_BaseObject::ShadowCastType( void )
  929. {
  930. if ( GetInvisibilityLevel() == 1.f )
  931. return SHADOWS_NONE;
  932. return BaseClass::ShadowCastType();
  933. }
  934. //-----------------------------------------------------------------------------
  935. // Purpose:
  936. //-----------------------------------------------------------------------------
  937. float C_BaseObject::GetInvisibilityLevel( void )
  938. {
  939. #ifdef STAGING_ONLY
  940. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  941. C_TFPlayer *pOwner = GetOwner();
  942. if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) && pLocalPlayer != pOwner )
  943. return 1.f;
  944. #endif // STAGING_ONLY
  945. return m_flInvisibilityPercent;
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Purpose:
  949. //-----------------------------------------------------------------------------
  950. void C_BaseObject::SetInvisibilityLevel( float flValue )
  951. {
  952. m_flPrevInvisibilityPercent = m_flInvisibilityPercent;
  953. m_flInvisibilityPercent = clamp( flValue, 0.f, 1.f );
  954. }
  955. //-----------------------------------------------------------------------------
  956. // Purpose: find the anim events that may have started sounds, and stop them.
  957. //-----------------------------------------------------------------------------
  958. void C_BaseObject::StopAnimGeneratedSounds( void )
  959. {
  960. MDLCACHE_CRITICAL_SECTION();
  961. CStudioHdr *pStudioHdr = GetModelPtr();
  962. if ( !pStudioHdr )
  963. return;
  964. mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( GetSequence() );
  965. if ( seqdesc.numevents == 0 )
  966. return;
  967. float flCurrentCycle = GetCycle();
  968. mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
  969. for (int i = 0; i < (int)seqdesc.numevents; i++)
  970. {
  971. if ( pevent[i].cycle < flCurrentCycle )
  972. {
  973. if ( pevent[i].event == CL_EVENT_SOUND || pevent[i].event == AE_CL_PLAYSOUND )
  974. {
  975. StopSound( entindex(), pevent[i].options );
  976. }
  977. }
  978. }
  979. }
  980. //============================================================================================================
  981. // POWER PROXY
  982. //============================================================================================================
  983. class CObjectPowerProxy : public CResultProxy
  984. {
  985. public:
  986. bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  987. void OnBind( void *pC_BaseEntity );
  988. private:
  989. CFloatInput m_Factor;
  990. };
  991. bool CObjectPowerProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  992. {
  993. if (!CResultProxy::Init( pMaterial, pKeyValues ))
  994. return false;
  995. if (!m_Factor.Init( pMaterial, pKeyValues, "scale", 1 ))
  996. return false;
  997. return true;
  998. }
  999. void CObjectPowerProxy::OnBind( void *pRenderable )
  1000. {
  1001. // Find the view angle between the player and this entity....
  1002. IClientRenderable *pRend = (IClientRenderable *)pRenderable;
  1003. C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
  1004. C_BaseObject *pObject = dynamic_cast<C_BaseObject*>(pEntity);
  1005. if (!pObject)
  1006. return;
  1007. SetFloatResult( m_Factor.GetFloat() );
  1008. if ( ToolsEnabled() )
  1009. {
  1010. ToolFramework_RecordMaterialParams( GetMaterial() );
  1011. }
  1012. }
  1013. EXPOSE_INTERFACE( CObjectPowerProxy, IMaterialProxy, "ObjectPower" IMATERIAL_PROXY_INTERFACE_VERSION );
  1014. //-----------------------------------------------------------------------------
  1015. // Control screen
  1016. //-----------------------------------------------------------------------------
  1017. class CBasicControlPanel : public CObjectControlPanel
  1018. {
  1019. DECLARE_CLASS( CBasicControlPanel, CObjectControlPanel );
  1020. public:
  1021. CBasicControlPanel( vgui::Panel *parent, const char *panelName );
  1022. };
  1023. DECLARE_VGUI_SCREEN_FACTORY( CBasicControlPanel, "basic_control_panel" );
  1024. //-----------------------------------------------------------------------------
  1025. // Constructor:
  1026. //-----------------------------------------------------------------------------
  1027. CBasicControlPanel::CBasicControlPanel( vgui::Panel *parent, const char *panelName )
  1028. : BaseClass( parent, "CBasicControlPanel" )
  1029. {
  1030. }
  1031. //-----------------------------------------------------------------------------
  1032. // Purpose: Used for spy invisiblity material
  1033. //-----------------------------------------------------------------------------
  1034. class CBuildingInvisProxy : public CBaseInvisMaterialProxy
  1035. {
  1036. public:
  1037. virtual void OnBind( C_BaseEntity *pBaseEntity ) OVERRIDE;
  1038. };
  1039. //-----------------------------------------------------------------------------
  1040. // Purpose:
  1041. // Input :
  1042. //-----------------------------------------------------------------------------
  1043. void CBuildingInvisProxy::OnBind( C_BaseEntity *pBaseEntity )
  1044. {
  1045. if ( !m_pPercentInvisible )
  1046. return;
  1047. if ( !pBaseEntity->IsBaseObject() )
  1048. return;
  1049. C_BaseObject *pObject = static_cast< C_BaseObject* >( pBaseEntity );
  1050. if ( !pObject )
  1051. return;
  1052. CTFPlayer *pOwner = ToTFPlayer( pObject->GetOwner() );
  1053. if ( !pOwner )
  1054. {
  1055. m_pPercentInvisible->SetFloatValue( 0.0f );
  1056. return;
  1057. }
  1058. m_pPercentInvisible->SetFloatValue( pObject->GetInvisibilityLevel() );
  1059. }
  1060. EXPOSE_INTERFACE( CBuildingInvisProxy, IMaterialProxy, "building_invis" IMATERIAL_PROXY_INTERFACE_VERSION );