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.

761 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client's CObjectSentrygun
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_tf_player.h"
  9. #include "vgui_bitmapbutton.h"
  10. #include "vgui/ILocalize.h"
  11. #include "tf_fx_muzzleflash.h"
  12. #include "eventlist.h"
  13. #include "hintsystem.h"
  14. #include <vgui_controls/ProgressBar.h>
  15. #include "igameevents.h"
  16. #include "c_obj_sentrygun.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. using namespace vgui;
  20. static void RecvProxy_BooleanToShieldLevel( const CRecvProxyData *pData, void *pStruct, void *pOut )
  21. {
  22. // convert old boolean "m_bShielded" to uint32 "m_nShieldLevel"
  23. *(uint32*)pOut = ( pData->m_Value.m_Int != 0 ) ? 1 : 0;
  24. }
  25. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
  26. BEGIN_NETWORK_TABLE( C_TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
  27. END_NETWORK_TABLE()
  28. BEGIN_NETWORK_TABLE_NOBASE( C_ObjectSentrygun, DT_SentrygunLocalData )
  29. RecvPropInt( RECVINFO(m_iKills) ),
  30. RecvPropInt( RECVINFO(m_iAssists) ),
  31. END_NETWORK_TABLE()
  32. IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygun, DT_ObjectSentrygun, CObjectSentrygun)
  33. RecvPropInt( RECVINFO(m_iAmmoShells) ),
  34. RecvPropInt( RECVINFO(m_iAmmoRockets) ),
  35. RecvPropInt( RECVINFO(m_iState) ),
  36. RecvPropBool( RECVINFO(m_bPlayerControlled) ),
  37. RecvPropInt( RECVINFO(m_nShieldLevel) ),
  38. RecvPropInt( RECVINFO_NAME(m_nShieldLevel, m_bShielded), 0, RecvProxy_BooleanToShieldLevel ), // for demo compatibility only
  39. RecvPropEHandle( RECVINFO( m_hEnemy ) ),
  40. RecvPropEHandle( RECVINFO( m_hAutoAimTarget ) ),
  41. RecvPropDataTable( "SentrygunLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_SentrygunLocalData ) ),
  42. END_RECV_TABLE()
  43. //-----------------------------------------------------------------------------
  44. // Purpose:
  45. //-----------------------------------------------------------------------------
  46. C_ObjectSentrygun::C_ObjectSentrygun()
  47. {
  48. m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
  49. m_bPlayerControlled = false;
  50. m_bOldPlayerControlled = false;
  51. m_nShieldLevel = SHIELD_NONE;
  52. m_nOldShieldLevel = SHIELD_NONE;
  53. m_hLaserBeamEffect = NULL;
  54. m_pTempShield = NULL;
  55. m_bNearMiss = false;
  56. m_flNextNearMissCheck = 0.f;
  57. m_iOldModelIndex = 0;
  58. m_bOldCarried = false;
  59. m_bRecreateShield = false;
  60. m_bRecreateLaserBeam = false;
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Purpose:
  64. //-----------------------------------------------------------------------------
  65. void C_ObjectSentrygun::UpdateOnRemove( void )
  66. {
  67. DestroyLaserBeam();
  68. DestroyShield();
  69. DestroySiren();
  70. BaseClass::UpdateOnRemove();
  71. }
  72. void C_ObjectSentrygun::GetAmmoCount( int &iShells, int &iMaxShells, int &iRockets, int & iMaxRockets )
  73. {
  74. iShells = m_iAmmoShells;
  75. iMaxShells = m_iMaxAmmoShells;
  76. iRockets = m_iAmmoRockets;
  77. iMaxRockets = SENTRYGUN_MAX_ROCKETS;
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. //-----------------------------------------------------------------------------
  82. void C_ObjectSentrygun::UpgradeLevelChanged()
  83. {
  84. switch( m_iUpgradeLevel )
  85. {
  86. case 1:
  87. {
  88. VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_1, m_vecViewOffset );
  89. m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
  90. break;
  91. }
  92. case 2:
  93. {
  94. VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_2, m_vecViewOffset );
  95. m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_2;
  96. break;
  97. }
  98. case 3:
  99. {
  100. VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_3, m_vecViewOffset );
  101. m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_3;
  102. break;
  103. }
  104. default:
  105. {
  106. Assert( 0 );
  107. break;
  108. }
  109. }
  110. CreateLaserBeam();
  111. // Because the bounding box size changes when upgrading, force the shadow to be reprojected using the new bounds
  112. g_pClientShadowMgr->AddToDirtyShadowList( this, true );
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. //-----------------------------------------------------------------------------
  117. void C_ObjectSentrygun::OnPreDataChanged( DataUpdateType_t updateType )
  118. {
  119. BaseClass::OnPreDataChanged( updateType );
  120. m_iOldBodygroups = GetBody();
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose:
  124. //-----------------------------------------------------------------------------
  125. void C_ObjectSentrygun::OnDataChanged( DataUpdateType_t updateType )
  126. {
  127. BaseClass::OnDataChanged( updateType );
  128. // intercept bodygroup sets from the server
  129. // we aren't clientsideanimating, but we don't want the server setting our
  130. // bodygroup while we are placing
  131. if ( m_iOldBodygroups != GetBody() )
  132. {
  133. if ( IsPlacing() )
  134. {
  135. m_nBody = m_iOldBodygroups;
  136. }
  137. }
  138. if ( GetModelIndex() != m_iOldModelIndex )
  139. {
  140. m_iOldModelIndex = GetModelIndex();
  141. if ( IsMiniBuilding() )
  142. {
  143. CStudioHdr *pStudiohdr = GetModelPtr();
  144. int bodyGroup = FindBodygroupByName( "mini_sentry_light" );
  145. if ( bodyGroup < pStudiohdr->numbodyparts() )
  146. {
  147. mstudiobodyparts_t *pbodypart = pStudiohdr->pBodypart( bodyGroup );
  148. if ( pbodypart->base > 0 )
  149. {
  150. SetBodygroup( bodyGroup, 1 );
  151. }
  152. }
  153. }
  154. }
  155. if ( m_bPlayerControlled != m_bOldPlayerControlled || m_bRecreateLaserBeam )
  156. {
  157. if ( m_bPlayerControlled )
  158. {
  159. CreateLaserBeam();
  160. }
  161. else
  162. {
  163. DestroyLaserBeam();
  164. }
  165. m_bOldPlayerControlled = m_bPlayerControlled;
  166. m_bRecreateLaserBeam = false;
  167. }
  168. if ( m_nShieldLevel != m_nOldShieldLevel || m_bRecreateShield )
  169. {
  170. if ( m_nShieldLevel > 0 )
  171. {
  172. CreateShield();
  173. }
  174. else
  175. {
  176. DestroyShield();
  177. }
  178. m_nOldShieldLevel = m_nShieldLevel;
  179. m_bRecreateShield = false;
  180. }
  181. if ( IsCarried() != m_bOldCarried )
  182. {
  183. m_bOldCarried = IsCarried();
  184. if ( IsCarried() )
  185. {
  186. DestroySiren();
  187. }
  188. }
  189. if ( ShouldBeActive() && !IsDisabled() && IsMiniBuilding() && !m_hSirenEffect )
  190. {
  191. CreateSiren();
  192. }
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. //-----------------------------------------------------------------------------
  197. void C_ObjectSentrygun::OnGoActive( void )
  198. {
  199. CreateSiren();
  200. BaseClass::OnGoActive();
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. //-----------------------------------------------------------------------------
  205. void C_ObjectSentrygun::OnGoInactive( void )
  206. {
  207. DestroySiren();
  208. BaseClass::OnGoInactive();
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. //-----------------------------------------------------------------------------
  213. void C_ObjectSentrygun::OnStartDisabled( void )
  214. {
  215. DestroySiren();
  216. BaseClass::OnStartDisabled();
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose:
  220. //-----------------------------------------------------------------------------
  221. void C_ObjectSentrygun::OnEndDisabled( void )
  222. {
  223. CreateSiren();
  224. BaseClass::OnEndDisabled();
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose:
  228. //-----------------------------------------------------------------------------
  229. void C_ObjectSentrygun::CreateLaserBeam( void )
  230. {
  231. if ( !m_bPlayerControlled )
  232. return;
  233. DestroyLaserBeam();
  234. int iAttachment = LookupAttachment( "laser_origin" );
  235. m_hLaserBeamEffect = ParticleProp()->Create( "laser_sight_beam", PATTACH_POINT_FOLLOW, iAttachment );
  236. if ( m_hLaserBeamEffect )
  237. {
  238. m_hLaserBeamEffect->SetSortOrigin( m_hLaserBeamEffect->GetRenderOrigin() );
  239. }
  240. SetNextClientThink( CLIENT_THINK_ALWAYS );
  241. if ( m_hLaserBeamEffect )
  242. {
  243. if ( GetTeamNumber() == TF_TEAM_BLUE )
  244. {
  245. m_hLaserBeamEffect->SetControlPoint( 2, Vector( 0, 0, 255 ) );
  246. }
  247. else
  248. {
  249. m_hLaserBeamEffect->SetControlPoint( 2, Vector( 255, 0, 0 ) );
  250. }
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose:
  255. //-----------------------------------------------------------------------------
  256. void C_ObjectSentrygun::DestroyLaserBeam( void )
  257. {
  258. if ( m_hLaserBeamEffect )
  259. {
  260. SetNextClientThink( CLIENT_THINK_NEVER );
  261. ParticleProp()->StopEmissionAndDestroyImmediately( m_hLaserBeamEffect );
  262. m_hLaserBeamEffect = NULL;
  263. }
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. //-----------------------------------------------------------------------------
  268. void C_ObjectSentrygun::SetDormant( bool bDormant )
  269. {
  270. if ( IsDormant() && !bDormant )
  271. {
  272. // Make sure our shield is where we are. We may have moved since last seen.
  273. if ( m_pTempShield )
  274. {
  275. m_bRecreateShield = true;
  276. m_bRecreateLaserBeam = true;
  277. }
  278. }
  279. BaseClass::SetDormant( bDormant );
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose:
  283. //-----------------------------------------------------------------------------
  284. void C_ObjectSentrygun::CreateShield( void )
  285. {
  286. DestroyShield();
  287. model_t *pModel = (model_t *) engine->LoadModel( "models/buildables/sentry_shield.mdl" );
  288. m_pTempShield = tempents->SpawnTempModel( pModel, GetAbsOrigin(), GetAbsAngles(), Vector(0, 0, 0), 1, FTENT_NEVERDIE );
  289. if ( m_pTempShield )
  290. {
  291. m_pTempShield->ChangeTeam( GetTeamNumber() );
  292. m_pTempShield->m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
  293. //m_pTempShield->m_nRenderFX = kRenderFxDistort;
  294. }
  295. m_hShieldEffect = ParticleProp()->Create( "turret_shield", PATTACH_ABSORIGIN_FOLLOW, 0, Vector( 0,0,30) );
  296. if ( !m_hShieldEffect )
  297. return;
  298. if ( GetTeamNumber() == TF_TEAM_BLUE )
  299. {
  300. m_hShieldEffect->SetControlPoint( 1, Vector(50,150,255) );
  301. }
  302. else
  303. {
  304. m_hShieldEffect->SetControlPoint( 1, Vector(255,50,50) );
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. void C_ObjectSentrygun::DestroyShield( void )
  311. {
  312. if ( m_pTempShield )
  313. {
  314. m_pTempShield->flags = FTENT_FADEOUT;
  315. m_pTempShield->die = gpGlobals->curtime;
  316. m_pTempShield->fadeSpeed = 1.0f;
  317. m_pTempShield = NULL;
  318. }
  319. if ( m_hShieldEffect )
  320. {
  321. ParticleProp()->StopEmission( m_hShieldEffect );
  322. m_hShieldEffect = NULL;
  323. }
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. void C_ObjectSentrygun::CreateSiren( void )
  329. {
  330. if ( !IsMiniBuilding() )
  331. return;
  332. if ( IsCarried() )
  333. return;
  334. if ( m_hSirenEffect )
  335. return;
  336. const char* flashlightName = "cart_flashinglight";
  337. if ( GetTeamNumber() == TF_TEAM_RED )
  338. {
  339. flashlightName = "cart_flashinglight_red";
  340. }
  341. m_hSirenEffect = ParticleProp()->Create( flashlightName, PATTACH_POINT_FOLLOW, "siren" );
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Purpose:
  345. //-----------------------------------------------------------------------------
  346. void C_ObjectSentrygun::DestroySiren( void )
  347. {
  348. if ( m_hSirenEffect )
  349. {
  350. ParticleProp()->StopEmission( m_hSirenEffect );
  351. m_hSirenEffect = NULL;
  352. }
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose:
  356. //-----------------------------------------------------------------------------
  357. void C_ObjectSentrygun::ClientThink( void )
  358. {
  359. if ( m_hLaserBeamEffect && m_hEnemy && GetBuilder() )
  360. {
  361. QAngle vecAngles;
  362. Vector vecMuzzleOrigin;
  363. int iAttachment = 0;
  364. switch ( GetUpgradeLevel() )
  365. {
  366. case 1:
  367. iAttachment = LookupAttachment( "muzzle" );
  368. break;
  369. case 2:
  370. iAttachment = LookupAttachment( "muzzle_l" );
  371. break;
  372. case 3:
  373. iAttachment = LookupAttachment( "rocket_l" );
  374. break;
  375. }
  376. GetAttachment( iAttachment, vecMuzzleOrigin, vecAngles );
  377. Vector vForward;
  378. AngleVectors( vecAngles, &vForward );
  379. Vector vEnd = m_hEnemy->WorldSpaceCenter();
  380. if ( m_hAutoAimTarget )
  381. {
  382. vEnd = m_hAutoAimTarget->GetAbsOrigin() + m_hAutoAimTarget->GetClassEyeHeight()*0.75f;
  383. }
  384. trace_t trace;
  385. CTraceFilterIgnoreTeammatesAndTeamObjects filter( GetBuilder(), COLLISION_GROUP_NONE, GetBuilder()->GetTeamNumber() );
  386. UTIL_TraceLine( vecMuzzleOrigin, vEnd, MASK_SOLID, &filter, &trace );
  387. Vector vecInterpBeamPos;
  388. InterpolateVector( gpGlobals->frametime * 25.f, m_vecLaserBeamPos, trace.endpos, vecInterpBeamPos );
  389. m_hLaserBeamEffect->SetControlPoint( 1, vecInterpBeamPos );
  390. m_vecLaserBeamPos = vecInterpBeamPos;
  391. // Perform a near-miss check.
  392. // This works pretty well as a threat indicator for the arrow, let's try it for our laser.
  393. if ( gpGlobals->curtime > m_flNextNearMissCheck )
  394. {
  395. // CheckNearMiss( vecMuzzleOrigin, m_hEnemy->GetAbsOrigin() );
  396. m_flNextNearMissCheck = gpGlobals->curtime + 0.2f;
  397. }
  398. }
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Purpose:
  402. //-----------------------------------------------------------------------------
  403. void C_ObjectSentrygun::CheckNearMiss( Vector vecStart, Vector vecEnd )
  404. {
  405. // Check against the local player. If the laser sweeps near him, play the near miss sound...
  406. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  407. if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
  408. return;
  409. // Can't hear near miss sounds from friendly guns.
  410. // if ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() )
  411. // return;
  412. Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
  413. Vector vecClosestPoint;
  414. float dist;
  415. CalcClosestPointOnLineSegment( vecPlayerPos, vecStart, vecEnd, vecClosestPoint, &dist );
  416. dist = vecPlayerPos.DistTo( vecClosestPoint );
  417. if ( dist > 120 )
  418. {
  419. StopSound( "Building_Sentrygun.ShaftLaserPass" );
  420. return;
  421. }
  422. if ( !m_bNearMiss )
  423. {
  424. // We're good for a near miss!
  425. float soundlen = 0;
  426. EmitSound_t params;
  427. params.m_flSoundTime = 0;
  428. params.m_pSoundName = "Building_Sentrygun.ShaftLaserPass";
  429. params.m_pflSoundDuration = &soundlen;
  430. params.m_flVolume = 1.f - (dist / 120.f);
  431. CSingleUserRecipientFilter localFilter( pLocalPlayer );
  432. EmitSound( localFilter, pLocalPlayer->entindex(), params );
  433. m_bNearMiss = true;
  434. }
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void C_ObjectSentrygun::DisplayHintTo( C_BasePlayer *pPlayer )
  440. {
  441. bool bHintPlayed = false;
  442. C_TFPlayer *pTFPlayer = ToTFPlayer(pPlayer);
  443. if ( InSameTeam( pPlayer ) )
  444. {
  445. // We're looking at a friendly object.
  446. if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
  447. {
  448. // If the sentrygun can be upgraded, and I can afford it, let me know
  449. if ( GetHealth() == GetMaxHealth() && GetUpgradeLevel() < 3 )
  450. {
  451. if ( pTFPlayer->GetBuildResources() >= SENTRYGUN_UPGRADE_COST )
  452. {
  453. bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_UPGRADE_SENTRYGUN, false, true );
  454. }
  455. else
  456. {
  457. bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_METAL_TO_UPGRADE, false, true );
  458. }
  459. }
  460. }
  461. }
  462. if ( !bHintPlayed )
  463. {
  464. BaseClass::DisplayHintTo( pPlayer );
  465. }
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Purpose:
  469. //-----------------------------------------------------------------------------
  470. const char *C_ObjectSentrygun::GetHudStatusIcon( void )
  471. {
  472. const char *pszResult;
  473. switch( m_iUpgradeLevel )
  474. {
  475. case 1:
  476. default:
  477. pszResult = "obj_status_sentrygun_1";
  478. break;
  479. case 2:
  480. pszResult = "obj_status_sentrygun_2";
  481. break;
  482. case 3:
  483. pszResult = "obj_status_sentrygun_3";
  484. break;
  485. }
  486. return pszResult;
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose:
  490. //-----------------------------------------------------------------------------
  491. BuildingHudAlert_t C_ObjectSentrygun::GetBuildingAlertLevel( void )
  492. {
  493. BuildingHudAlert_t baseAlertLevel = BaseClass::GetBuildingAlertLevel();
  494. // Just warn on low shells.
  495. float flShellPercent = (float)m_iAmmoShells / (float)m_iMaxAmmoShells;
  496. BuildingHudAlert_t alertLevel = BUILDING_HUD_ALERT_NONE;
  497. if ( !IsCarried() )
  498. {
  499. if ( !IsBuilding() && flShellPercent < 0.25 )
  500. {
  501. alertLevel = BUILDING_HUD_ALERT_VERY_LOW_AMMO;
  502. }
  503. else if ( !IsBuilding() && flShellPercent < 0.50 )
  504. {
  505. alertLevel = BUILDING_HUD_ALERT_LOW_AMMO;
  506. }
  507. }
  508. return MAX( baseAlertLevel, alertLevel );
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: During placement, only use the smaller bbox for shadow calc, don't include the range bodygroup
  512. //-----------------------------------------------------------------------------
  513. void C_ObjectSentrygun::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
  514. {
  515. if ( IsPlacing() )
  516. {
  517. mins = CollisionProp()->OBBMins();
  518. maxs = CollisionProp()->OBBMaxs();
  519. // HACK: The collision prop bounding box doesn't quite cover the blueprint model, so we bloat it a little
  520. Vector bbBloat( 10.0f, 10.0f, 0.0f );
  521. mins -= bbBloat;
  522. maxs += bbBloat;
  523. }
  524. else
  525. {
  526. BaseClass::GetShadowRenderBounds( mins, maxs, shadowType );
  527. }
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose: Re-calc our damage particles when we get a new model
  531. //-----------------------------------------------------------------------------
  532. CStudioHdr *C_ObjectSentrygun::OnNewModel( void )
  533. {
  534. CStudioHdr *hdr = BaseClass::OnNewModel();
  535. UpdateDamageEffects( m_damageLevel );
  536. // Reset Bodygroups
  537. for ( int i = GetNumBodyGroups()-1; i >= 0; i-- )
  538. {
  539. SetBodygroup( i, 0 );
  540. }
  541. m_iPlacementBodygroup = FindBodygroupByName( "sentry1_range" );
  542. m_iPlacementBodygroup_Mini = FindBodygroupByName( "sentry1_range_mini" );
  543. return hdr;
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose: Damage level has changed, update our effects
  547. //-----------------------------------------------------------------------------
  548. void C_ObjectSentrygun::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
  549. {
  550. if ( m_hDamageEffects )
  551. {
  552. m_hDamageEffects->StopEmission( false, false );
  553. m_hDamageEffects = NULL;
  554. }
  555. const char *pszEffect = "";
  556. switch( damageLevel )
  557. {
  558. case BUILDING_DAMAGE_LEVEL_LIGHT:
  559. pszEffect = "sentrydamage_1";
  560. break;
  561. case BUILDING_DAMAGE_LEVEL_MEDIUM:
  562. pszEffect = "sentrydamage_2";
  563. break;
  564. case BUILDING_DAMAGE_LEVEL_HEAVY:
  565. pszEffect = "sentrydamage_3";
  566. break;
  567. case BUILDING_DAMAGE_LEVEL_CRITICAL:
  568. pszEffect = "sentrydamage_4";
  569. break;
  570. default:
  571. break;
  572. }
  573. if ( Q_strlen(pszEffect) > 0 )
  574. {
  575. switch( m_iUpgradeLevel )
  576. {
  577. case 1:
  578. case 2:
  579. m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "build_point_0" );
  580. break;
  581. case 3:
  582. m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "sentrydamage" );
  583. break;
  584. }
  585. }
  586. }
  587. //-----------------------------------------------------------------------------
  588. // Purpose: placement state has changed, update the model
  589. //-----------------------------------------------------------------------------
  590. void C_ObjectSentrygun::OnPlacementStateChanged( bool bValidPlacement )
  591. {
  592. if ( bValidPlacement && ( m_iPlacementBodygroup >= 0 ) && ( m_iPlacementBodygroup_Mini >= 0 ) )
  593. {
  594. if ( IsMiniBuilding() )
  595. {
  596. SetBodygroup( m_iPlacementBodygroup, 0 );
  597. SetBodygroup( m_iPlacementBodygroup_Mini, 1 );
  598. }
  599. else
  600. {
  601. SetBodygroup( m_iPlacementBodygroup, 1 );
  602. SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
  603. }
  604. }
  605. else
  606. {
  607. SetBodygroup( m_iPlacementBodygroup, 0 );
  608. SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
  609. }
  610. BaseClass::OnPlacementStateChanged( bValidPlacement );
  611. }
  612. void C_ObjectSentrygun::DebugDamageParticles( void )
  613. {
  614. Msg( "Health %d\n", GetHealth() );
  615. BuildingDamageLevel_t damageLevel = CalculateDamageLevel();
  616. Msg( "Damage Level %d\n", (int)damageLevel );
  617. if ( m_hDamageEffects )
  618. {
  619. Msg( "m_hDamageEffects is valid\n" );
  620. }
  621. else
  622. {
  623. Msg( "m_hDamageEffects is NULL\n" );
  624. }
  625. // print all particles owned by particleprop
  626. ParticleProp()->DebugPrintEffects();
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose:
  630. //-----------------------------------------------------------------------------
  631. void C_ObjectSentrygun::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  632. {
  633. BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
  634. if ( !IsMiniBuilding() )
  635. return;
  636. if ( IsBuilding() || IsPlacing() )
  637. return;
  638. //Vector position;
  639. //for ( int i=0; i<8; ++i )
  640. //{
  641. // matrix3x4_t &transform = GetBoneForWrite( i );
  642. // MatrixGetColumn( transform, 3, position );
  643. // MatrixSetColumn( Vector(0,0,-4) + position, 3, transform );
  644. //}
  645. }
  646. //-----------------------------------------------------------------------------
  647. // Purpose:
  648. //-----------------------------------------------------------------------------
  649. const char* C_ObjectSentrygun::GetStatusName() const
  650. {
  651. if ( IsDisposableBuilding() )
  652. {
  653. return "#TF_Object_Sentry_Disp";
  654. }
  655. return "#TF_Object_Sentry";
  656. }