summaryrefslogtreecommitdiff
path: root/game/client/tf/c_obj_sentrygun.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/c_obj_sentrygun.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf/c_obj_sentrygun.cpp')
-rw-r--r--game/client/tf/c_obj_sentrygun.cpp761
1 files changed, 761 insertions, 0 deletions
diff --git a/game/client/tf/c_obj_sentrygun.cpp b/game/client/tf/c_obj_sentrygun.cpp
new file mode 100644
index 0000000..3cc421c
--- /dev/null
+++ b/game/client/tf/c_obj_sentrygun.cpp
@@ -0,0 +1,761 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client's CObjectSentrygun
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "c_tf_player.h"
+#include "vgui_bitmapbutton.h"
+#include "vgui/ILocalize.h"
+#include "tf_fx_muzzleflash.h"
+#include "eventlist.h"
+#include "hintsystem.h"
+#include <vgui_controls/ProgressBar.h>
+#include "igameevents.h"
+
+#include "c_obj_sentrygun.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+static void RecvProxy_BooleanToShieldLevel( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ // convert old boolean "m_bShielded" to uint32 "m_nShieldLevel"
+ *(uint32*)pOut = ( pData->m_Value.m_Int != 0 ) ? 1 : 0;
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
+
+BEGIN_NETWORK_TABLE( C_TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
+END_NETWORK_TABLE()
+
+BEGIN_NETWORK_TABLE_NOBASE( C_ObjectSentrygun, DT_SentrygunLocalData )
+ RecvPropInt( RECVINFO(m_iKills) ),
+ RecvPropInt( RECVINFO(m_iAssists) ),
+END_NETWORK_TABLE()
+
+IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygun, DT_ObjectSentrygun, CObjectSentrygun)
+ RecvPropInt( RECVINFO(m_iAmmoShells) ),
+ RecvPropInt( RECVINFO(m_iAmmoRockets) ),
+ RecvPropInt( RECVINFO(m_iState) ),
+ RecvPropBool( RECVINFO(m_bPlayerControlled) ),
+ RecvPropInt( RECVINFO(m_nShieldLevel) ),
+ RecvPropInt( RECVINFO_NAME(m_nShieldLevel, m_bShielded), 0, RecvProxy_BooleanToShieldLevel ), // for demo compatibility only
+ RecvPropEHandle( RECVINFO( m_hEnemy ) ),
+ RecvPropEHandle( RECVINFO( m_hAutoAimTarget ) ),
+ RecvPropDataTable( "SentrygunLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_SentrygunLocalData ) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_ObjectSentrygun::C_ObjectSentrygun()
+{
+ m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
+ m_bPlayerControlled = false;
+ m_bOldPlayerControlled = false;
+ m_nShieldLevel = SHIELD_NONE;
+ m_nOldShieldLevel = SHIELD_NONE;
+ m_hLaserBeamEffect = NULL;
+ m_pTempShield = NULL;
+ m_bNearMiss = false;
+ m_flNextNearMissCheck = 0.f;
+
+ m_iOldModelIndex = 0;
+ m_bOldCarried = false;
+ m_bRecreateShield = false;
+ m_bRecreateLaserBeam = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::UpdateOnRemove( void )
+{
+ DestroyLaserBeam();
+ DestroyShield();
+ DestroySiren();
+
+ BaseClass::UpdateOnRemove();
+}
+
+
+void C_ObjectSentrygun::GetAmmoCount( int &iShells, int &iMaxShells, int &iRockets, int & iMaxRockets )
+{
+ iShells = m_iAmmoShells;
+ iMaxShells = m_iMaxAmmoShells;
+ iRockets = m_iAmmoRockets;
+ iMaxRockets = SENTRYGUN_MAX_ROCKETS;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::UpgradeLevelChanged()
+{
+ switch( m_iUpgradeLevel )
+ {
+ case 1:
+ {
+ VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_1, m_vecViewOffset );
+ m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
+ break;
+ }
+ case 2:
+ {
+ VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_2, m_vecViewOffset );
+ m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_2;
+ break;
+ }
+ case 3:
+ {
+ VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_3, m_vecViewOffset );
+ m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_3;
+ break;
+ }
+ default:
+ {
+ Assert( 0 );
+ break;
+ }
+ }
+
+ CreateLaserBeam();
+
+ // Because the bounding box size changes when upgrading, force the shadow to be reprojected using the new bounds
+ g_pClientShadowMgr->AddToDirtyShadowList( this, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnPreDataChanged( updateType );
+
+ m_iOldBodygroups = GetBody();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ // intercept bodygroup sets from the server
+ // we aren't clientsideanimating, but we don't want the server setting our
+ // bodygroup while we are placing
+ if ( m_iOldBodygroups != GetBody() )
+ {
+ if ( IsPlacing() )
+ {
+ m_nBody = m_iOldBodygroups;
+ }
+ }
+
+ if ( GetModelIndex() != m_iOldModelIndex )
+ {
+ m_iOldModelIndex = GetModelIndex();
+
+ if ( IsMiniBuilding() )
+ {
+ CStudioHdr *pStudiohdr = GetModelPtr();
+ int bodyGroup = FindBodygroupByName( "mini_sentry_light" );
+ if ( bodyGroup < pStudiohdr->numbodyparts() )
+ {
+ mstudiobodyparts_t *pbodypart = pStudiohdr->pBodypart( bodyGroup );
+ if ( pbodypart->base > 0 )
+ {
+ SetBodygroup( bodyGroup, 1 );
+ }
+ }
+ }
+ }
+
+ if ( m_bPlayerControlled != m_bOldPlayerControlled || m_bRecreateLaserBeam )
+ {
+ if ( m_bPlayerControlled )
+ {
+ CreateLaserBeam();
+ }
+ else
+ {
+ DestroyLaserBeam();
+ }
+ m_bOldPlayerControlled = m_bPlayerControlled;
+ m_bRecreateLaserBeam = false;
+ }
+
+ if ( m_nShieldLevel != m_nOldShieldLevel || m_bRecreateShield )
+ {
+ if ( m_nShieldLevel > 0 )
+ {
+ CreateShield();
+ }
+ else
+ {
+ DestroyShield();
+ }
+ m_nOldShieldLevel = m_nShieldLevel;
+ m_bRecreateShield = false;
+ }
+
+ if ( IsCarried() != m_bOldCarried )
+ {
+ m_bOldCarried = IsCarried();
+ if ( IsCarried() )
+ {
+ DestroySiren();
+ }
+ }
+
+ if ( ShouldBeActive() && !IsDisabled() && IsMiniBuilding() && !m_hSirenEffect )
+ {
+ CreateSiren();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnGoActive( void )
+{
+ CreateSiren();
+
+ BaseClass::OnGoActive();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnGoInactive( void )
+{
+ DestroySiren();
+
+ BaseClass::OnGoInactive();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnStartDisabled( void )
+{
+ DestroySiren();
+
+ BaseClass::OnStartDisabled();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnEndDisabled( void )
+{
+ CreateSiren();
+
+ BaseClass::OnEndDisabled();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::CreateLaserBeam( void )
+{
+ if ( !m_bPlayerControlled )
+ return;
+
+ DestroyLaserBeam();
+
+ int iAttachment = LookupAttachment( "laser_origin" );
+ m_hLaserBeamEffect = ParticleProp()->Create( "laser_sight_beam", PATTACH_POINT_FOLLOW, iAttachment );
+ if ( m_hLaserBeamEffect )
+ {
+ m_hLaserBeamEffect->SetSortOrigin( m_hLaserBeamEffect->GetRenderOrigin() );
+ }
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ if ( m_hLaserBeamEffect )
+ {
+ if ( GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ m_hLaserBeamEffect->SetControlPoint( 2, Vector( 0, 0, 255 ) );
+ }
+ else
+ {
+ m_hLaserBeamEffect->SetControlPoint( 2, Vector( 255, 0, 0 ) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::DestroyLaserBeam( void )
+{
+ if ( m_hLaserBeamEffect )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ ParticleProp()->StopEmissionAndDestroyImmediately( m_hLaserBeamEffect );
+ m_hLaserBeamEffect = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::SetDormant( bool bDormant )
+{
+ if ( IsDormant() && !bDormant )
+ {
+ // Make sure our shield is where we are. We may have moved since last seen.
+ if ( m_pTempShield )
+ {
+ m_bRecreateShield = true;
+ m_bRecreateLaserBeam = true;
+ }
+ }
+
+ BaseClass::SetDormant( bDormant );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::CreateShield( void )
+{
+ DestroyShield();
+
+ model_t *pModel = (model_t *) engine->LoadModel( "models/buildables/sentry_shield.mdl" );
+ m_pTempShield = tempents->SpawnTempModel( pModel, GetAbsOrigin(), GetAbsAngles(), Vector(0, 0, 0), 1, FTENT_NEVERDIE );
+ if ( m_pTempShield )
+ {
+ m_pTempShield->ChangeTeam( GetTeamNumber() );
+ m_pTempShield->m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
+ //m_pTempShield->m_nRenderFX = kRenderFxDistort;
+ }
+
+ m_hShieldEffect = ParticleProp()->Create( "turret_shield", PATTACH_ABSORIGIN_FOLLOW, 0, Vector( 0,0,30) );
+ if ( !m_hShieldEffect )
+ return;
+ if ( GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ m_hShieldEffect->SetControlPoint( 1, Vector(50,150,255) );
+ }
+ else
+ {
+ m_hShieldEffect->SetControlPoint( 1, Vector(255,50,50) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::DestroyShield( void )
+{
+ if ( m_pTempShield )
+ {
+ m_pTempShield->flags = FTENT_FADEOUT;
+ m_pTempShield->die = gpGlobals->curtime;
+ m_pTempShield->fadeSpeed = 1.0f;
+ m_pTempShield = NULL;
+ }
+
+ if ( m_hShieldEffect )
+ {
+ ParticleProp()->StopEmission( m_hShieldEffect );
+ m_hShieldEffect = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::CreateSiren( void )
+{
+ if ( !IsMiniBuilding() )
+ return;
+
+ if ( IsCarried() )
+ return;
+
+ if ( m_hSirenEffect )
+ return;
+
+ const char* flashlightName = "cart_flashinglight";
+ if ( GetTeamNumber() == TF_TEAM_RED )
+ {
+ flashlightName = "cart_flashinglight_red";
+ }
+ m_hSirenEffect = ParticleProp()->Create( flashlightName, PATTACH_POINT_FOLLOW, "siren" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::DestroySiren( void )
+{
+ if ( m_hSirenEffect )
+ {
+ ParticleProp()->StopEmission( m_hSirenEffect );
+ m_hSirenEffect = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::ClientThink( void )
+{
+ if ( m_hLaserBeamEffect && m_hEnemy && GetBuilder() )
+ {
+ QAngle vecAngles;
+ Vector vecMuzzleOrigin;
+ int iAttachment = 0;
+ switch ( GetUpgradeLevel() )
+ {
+ case 1:
+ iAttachment = LookupAttachment( "muzzle" );
+ break;
+ case 2:
+ iAttachment = LookupAttachment( "muzzle_l" );
+ break;
+ case 3:
+ iAttachment = LookupAttachment( "rocket_l" );
+ break;
+ }
+ GetAttachment( iAttachment, vecMuzzleOrigin, vecAngles );
+
+ Vector vForward;
+ AngleVectors( vecAngles, &vForward );
+
+ Vector vEnd = m_hEnemy->WorldSpaceCenter();
+ if ( m_hAutoAimTarget )
+ {
+ vEnd = m_hAutoAimTarget->GetAbsOrigin() + m_hAutoAimTarget->GetClassEyeHeight()*0.75f;
+ }
+
+ trace_t trace;
+ CTraceFilterIgnoreTeammatesAndTeamObjects filter( GetBuilder(), COLLISION_GROUP_NONE, GetBuilder()->GetTeamNumber() );
+ UTIL_TraceLine( vecMuzzleOrigin, vEnd, MASK_SOLID, &filter, &trace );
+
+ Vector vecInterpBeamPos;
+ InterpolateVector( gpGlobals->frametime * 25.f, m_vecLaserBeamPos, trace.endpos, vecInterpBeamPos );
+
+ m_hLaserBeamEffect->SetControlPoint( 1, vecInterpBeamPos );
+ m_vecLaserBeamPos = vecInterpBeamPos;
+
+ // Perform a near-miss check.
+ // This works pretty well as a threat indicator for the arrow, let's try it for our laser.
+ if ( gpGlobals->curtime > m_flNextNearMissCheck )
+ {
+// CheckNearMiss( vecMuzzleOrigin, m_hEnemy->GetAbsOrigin() );
+ m_flNextNearMissCheck = gpGlobals->curtime + 0.2f;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::CheckNearMiss( Vector vecStart, Vector vecEnd )
+{
+ // Check against the local player. If the laser sweeps near him, play the near miss sound...
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ // Can't hear near miss sounds from friendly guns.
+// if ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() )
+// return;
+
+ Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
+ Vector vecClosestPoint;
+ float dist;
+ CalcClosestPointOnLineSegment( vecPlayerPos, vecStart, vecEnd, vecClosestPoint, &dist );
+ dist = vecPlayerPos.DistTo( vecClosestPoint );
+ if ( dist > 120 )
+ {
+ StopSound( "Building_Sentrygun.ShaftLaserPass" );
+ return;
+ }
+
+ if ( !m_bNearMiss )
+ {
+ // We're good for a near miss!
+ float soundlen = 0;
+ EmitSound_t params;
+ params.m_flSoundTime = 0;
+ params.m_pSoundName = "Building_Sentrygun.ShaftLaserPass";
+ params.m_pflSoundDuration = &soundlen;
+ params.m_flVolume = 1.f - (dist / 120.f);
+ CSingleUserRecipientFilter localFilter( pLocalPlayer );
+ EmitSound( localFilter, pLocalPlayer->entindex(), params );
+
+ m_bNearMiss = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::DisplayHintTo( C_BasePlayer *pPlayer )
+{
+ bool bHintPlayed = false;
+
+ C_TFPlayer *pTFPlayer = ToTFPlayer(pPlayer);
+ if ( InSameTeam( pPlayer ) )
+ {
+ // We're looking at a friendly object.
+ if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ // If the sentrygun can be upgraded, and I can afford it, let me know
+ if ( GetHealth() == GetMaxHealth() && GetUpgradeLevel() < 3 )
+ {
+ if ( pTFPlayer->GetBuildResources() >= SENTRYGUN_UPGRADE_COST )
+ {
+ bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_UPGRADE_SENTRYGUN, false, true );
+ }
+ else
+ {
+ bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_METAL_TO_UPGRADE, false, true );
+ }
+ }
+ }
+ }
+
+ if ( !bHintPlayed )
+ {
+ BaseClass::DisplayHintTo( pPlayer );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *C_ObjectSentrygun::GetHudStatusIcon( void )
+{
+ const char *pszResult;
+
+ switch( m_iUpgradeLevel )
+ {
+ case 1:
+ default:
+ pszResult = "obj_status_sentrygun_1";
+ break;
+ case 2:
+ pszResult = "obj_status_sentrygun_2";
+ break;
+ case 3:
+ pszResult = "obj_status_sentrygun_3";
+ break;
+ }
+
+ return pszResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BuildingHudAlert_t C_ObjectSentrygun::GetBuildingAlertLevel( void )
+{
+ BuildingHudAlert_t baseAlertLevel = BaseClass::GetBuildingAlertLevel();
+
+ // Just warn on low shells.
+
+ float flShellPercent = (float)m_iAmmoShells / (float)m_iMaxAmmoShells;
+
+ BuildingHudAlert_t alertLevel = BUILDING_HUD_ALERT_NONE;
+
+ if ( !IsCarried() )
+ {
+ if ( !IsBuilding() && flShellPercent < 0.25 )
+ {
+ alertLevel = BUILDING_HUD_ALERT_VERY_LOW_AMMO;
+ }
+ else if ( !IsBuilding() && flShellPercent < 0.50 )
+ {
+ alertLevel = BUILDING_HUD_ALERT_LOW_AMMO;
+ }
+ }
+
+ return MAX( baseAlertLevel, alertLevel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: During placement, only use the smaller bbox for shadow calc, don't include the range bodygroup
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
+{
+ if ( IsPlacing() )
+ {
+ mins = CollisionProp()->OBBMins();
+ maxs = CollisionProp()->OBBMaxs();
+
+ // HACK: The collision prop bounding box doesn't quite cover the blueprint model, so we bloat it a little
+ Vector bbBloat( 10.0f, 10.0f, 0.0f );
+ mins -= bbBloat;
+ maxs += bbBloat;
+ }
+ else
+ {
+ BaseClass::GetShadowRenderBounds( mins, maxs, shadowType );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Re-calc our damage particles when we get a new model
+//-----------------------------------------------------------------------------
+CStudioHdr *C_ObjectSentrygun::OnNewModel( void )
+{
+ CStudioHdr *hdr = BaseClass::OnNewModel();
+
+ UpdateDamageEffects( m_damageLevel );
+
+ // Reset Bodygroups
+ for ( int i = GetNumBodyGroups()-1; i >= 0; i-- )
+ {
+ SetBodygroup( i, 0 );
+ }
+
+ m_iPlacementBodygroup = FindBodygroupByName( "sentry1_range" );
+ m_iPlacementBodygroup_Mini = FindBodygroupByName( "sentry1_range_mini" );
+
+ return hdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Damage level has changed, update our effects
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
+{
+ if ( m_hDamageEffects )
+ {
+ m_hDamageEffects->StopEmission( false, false );
+ m_hDamageEffects = NULL;
+ }
+
+ const char *pszEffect = "";
+
+ switch( damageLevel )
+ {
+ case BUILDING_DAMAGE_LEVEL_LIGHT:
+ pszEffect = "sentrydamage_1";
+ break;
+ case BUILDING_DAMAGE_LEVEL_MEDIUM:
+ pszEffect = "sentrydamage_2";
+ break;
+ case BUILDING_DAMAGE_LEVEL_HEAVY:
+ pszEffect = "sentrydamage_3";
+ break;
+ case BUILDING_DAMAGE_LEVEL_CRITICAL:
+ pszEffect = "sentrydamage_4";
+ break;
+
+ default:
+ break;
+ }
+
+ if ( Q_strlen(pszEffect) > 0 )
+ {
+ switch( m_iUpgradeLevel )
+ {
+ case 1:
+ case 2:
+ m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "build_point_0" );
+ break;
+
+ case 3:
+ m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "sentrydamage" );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: placement state has changed, update the model
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnPlacementStateChanged( bool bValidPlacement )
+{
+ if ( bValidPlacement && ( m_iPlacementBodygroup >= 0 ) && ( m_iPlacementBodygroup_Mini >= 0 ) )
+ {
+ if ( IsMiniBuilding() )
+ {
+ SetBodygroup( m_iPlacementBodygroup, 0 );
+ SetBodygroup( m_iPlacementBodygroup_Mini, 1 );
+ }
+ else
+ {
+ SetBodygroup( m_iPlacementBodygroup, 1 );
+ SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
+ }
+ }
+ else
+ {
+ SetBodygroup( m_iPlacementBodygroup, 0 );
+ SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
+ }
+
+ BaseClass::OnPlacementStateChanged( bValidPlacement );
+}
+
+void C_ObjectSentrygun::DebugDamageParticles( void )
+{
+ Msg( "Health %d\n", GetHealth() );
+
+ BuildingDamageLevel_t damageLevel = CalculateDamageLevel();
+ Msg( "Damage Level %d\n", (int)damageLevel );
+
+ if ( m_hDamageEffects )
+ {
+ Msg( "m_hDamageEffects is valid\n" );
+ }
+ else
+ {
+ Msg( "m_hDamageEffects is NULL\n" );
+ }
+
+ // print all particles owned by particleprop
+ ParticleProp()->DebugPrintEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
+{
+ BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
+
+ if ( !IsMiniBuilding() )
+ return;
+
+ if ( IsBuilding() || IsPlacing() )
+ return;
+
+
+ //Vector position;
+ //for ( int i=0; i<8; ++i )
+ //{
+ // matrix3x4_t &transform = GetBoneForWrite( i );
+ // MatrixGetColumn( transform, 3, position );
+ // MatrixSetColumn( Vector(0,0,-4) + position, 3, transform );
+ //}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char* C_ObjectSentrygun::GetStatusName() const
+{
+ if ( IsDisposableBuilding() )
+ {
+ return "#TF_Object_Sentry_Disp";
+ }
+
+ return "#TF_Object_Sentry";
+}
+
+