summaryrefslogtreecommitdiff
path: root/game/client/tf2/c_objectsentrygun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf2/c_objectsentrygun.cpp')
-rw-r--r--game/client/tf2/c_objectsentrygun.cpp558
1 files changed, 558 insertions, 0 deletions
diff --git a/game/client/tf2/c_objectsentrygun.cpp b/game/client/tf2/c_objectsentrygun.cpp
new file mode 100644
index 0000000..9d47a76
--- /dev/null
+++ b/game/client/tf2/c_objectsentrygun.cpp
@@ -0,0 +1,558 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client's CObjectSentrygun
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "bone_setup.h"
+#include "CommanderOverlay.h"
+#include "c_baseobject.h"
+#include "C_Obj_SentryGun.h"
+#include "tf_shareddefs.h"
+#include "c_basetfplayer.h"
+#include "ObjectControlPanel.h"
+#include <vgui_controls/Button.h>
+
+inline float UTIL_AngleMod(float a)
+{
+ return anglemod(a);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Base Sentrygun
+//-----------------------------------------------------------------------------
+BEGIN_RECV_TABLE_NOBASE(C_ObjectSentrygun, DT_SentrygunTeamOnlyVars)
+ RecvPropInt(RECVINFO( m_iAmmo )),
+END_RECV_TABLE()
+
+IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygun, DT_ObjectSentrygun, CObjectSentrygun)
+ RecvPropInt( RECVINFO( m_iBaseTurnRate ) ),
+ RecvPropEHandle(RECVINFO(m_hEnemy)),
+ RecvPropDataTable( "teamonly", 0, 0, &REFERENCE_RECV_TABLE( DT_SentrygunTeamOnlyVars )),
+ RecvPropInt(RECVINFO(m_bTurtled)),
+ RecvPropInt( RECVINFO( m_nAnimationParity ) ),
+ RecvPropInt( RECVINFO( m_nOrientationParity ) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_ObjectSentrygun::C_ObjectSentrygun()
+{
+ m_fBoneXRotator = 0;
+ m_fBoneYRotator = 0;
+ m_iAmmo = 0;
+ m_bTurtled = false;
+ m_flStartedTurtlingAt = 0;
+ m_flStartedUnTurtlingAt = 0;
+
+ SetViewOffset( Vector(0,0,22) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::SetDormant( bool bDormant )
+{
+ BaseClass::SetDormant( bDormant );
+ ENTITY_PANEL_ACTIVATE( "sentrygun", !bDormant );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_ObjectSentrygun::DrawModel( int flags )
+{
+ float flRealOriginZ = GetLocalOrigin().z;
+
+ // If we're turtling, slide the model into the ground
+ if ( m_bTurtled )
+ {
+ // How far down are we?
+ float flTime = MIN( gpGlobals->curtime - m_flStartedTurtlingAt, SENTRY_TURTLE_TIME );
+ float flPercent = 1 - (SENTRY_TURTLE_TIME - flTime) / SENTRY_TURTLE_TIME;
+
+ // FIXME: This is totally wrong!!!
+ Vector vNewOrigin = GetLocalOrigin();
+ vNewOrigin.z -= (CollisionProp()->OBBSize().z * flPercent);
+ SetLocalOrigin( vNewOrigin );
+ InvalidateBoneCache();
+ }
+ else if ( !m_bTurtled )
+ {
+ if ( m_flStartedUnTurtlingAt )
+ {
+ float flTime = MIN( gpGlobals->curtime - m_flStartedUnTurtlingAt, SENTRY_TURTLE_TIME );
+ float flPercent = (SENTRY_TURTLE_TIME - flTime) / SENTRY_TURTLE_TIME;
+
+ // FIXME: This is totally wrong!!!
+ Vector vNewOrigin = GetLocalOrigin();
+ vNewOrigin.z -= (CollisionProp()->OBBSize().z * flPercent);
+ SetLocalOrigin( vNewOrigin );
+ InvalidateBoneCache();
+
+ // Fully unturtled?
+ if ( flTime >= SENTRY_TURTLE_TIME )
+ {
+ m_flStartedUnTurtlingAt = 0;
+ }
+ }
+ }
+
+ int drawn = BaseClass::DrawModel( flags );
+ SetLocalOriginDim( Z_INDEX, flRealOriginZ );
+
+ return drawn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+
+ m_bLastTurtled = m_bTurtled;
+ m_nLastAnimationParity = m_nAnimationParity;
+ m_angPrevLocalAngles = GetLocalAngles();
+ m_nPrevOrientationParity = m_nOrientationParity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate( updateType );
+
+ if ( m_bLastTurtled != m_bTurtled )
+ {
+ if ( m_bTurtled )
+ {
+ m_flStartedTurtlingAt = gpGlobals->curtime;
+ m_flStartedUnTurtlingAt = 0;
+ }
+ else
+ {
+ m_flStartedUnTurtlingAt = gpGlobals->curtime;
+ m_flStartedTurtlingAt = 0;
+ }
+ }
+
+ if ( m_nLastAnimationParity != m_nAnimationParity )
+ {
+ SetCycle( 0.0f );
+ }
+
+ bool changed = false;
+ QAngle angleDiff;
+ angleDiff = ( GetAbsAngles() - m_angPrevLocalAngles );
+ for (int i = 0;i < 3; i++ )
+ {
+ angleDiff[i] = UTIL_AngleMod( angleDiff[ i ] );
+ }
+
+ if ( angleDiff.Length() > 0.1f )
+ {
+ changed = true;
+ }
+ if ( updateType == DATA_UPDATE_CREATED || changed )
+ {
+ // Orient it
+ m_vecCurAngles.y = UTIL_AngleMod( GetLocalAngles().y );
+ RecomputeOrientation();
+ }
+ else if ( m_nPrevOrientationParity != m_nOrientationParity )
+ {
+ if ( changed )
+ {
+ RecomputeOrientation();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ // Start thinking (Baseclass stops it)
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::FinishedBuilding( void )
+{
+ BaseClass::FinishedBuilding();
+
+ EmitSound( "ObjectSentrygun.Activate" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS])
+{
+ studiohdr_t *pModel = modelinfo->GetStudiomodel( GetModel() );
+
+ // When yaw preview is on,
+ if (!IsPreviewingYaw())
+ {
+ Studio_SetController(pModel, 0, m_fBoneXRotator, controllers[0]);
+ }
+ else
+ {
+ // Bone rotation == 0 here to make it exactly match the preview
+ Studio_SetController(pModel, 0, 0, controllers[0]);
+ }
+
+ Studio_SetController(pModel, 1, m_fBoneYRotator, controllers[1]);
+ Studio_SetController(pModel, 2, m_fBoneYRotator, controllers[2]);
+ Studio_SetController(pModel, 3, m_fBoneYRotator, controllers[3]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This is called to get the initial builder yaw...
+//-----------------------------------------------------------------------------
+float C_ObjectSentrygun::GetInitialBuilderYaw()
+{
+ // Take the current rotation into account
+ return GetAbsAngles().y + m_fBoneXRotator;
+}
+
+//-----------------------------------------------------------------------------
+// Called when a rotation happens
+//-----------------------------------------------------------------------------
+void C_ObjectSentrygun::RecomputeOrientation( )
+{
+ m_iRightBound = UTIL_AngleMod( m_vecCurAngles.y - 50);
+ m_iLeftBound = UTIL_AngleMod( m_vecCurAngles.y + 50);
+ if ( m_iRightBound > m_iLeftBound )
+ {
+ m_iRightBound = m_iLeftBound;
+ m_iLeftBound = UTIL_AngleMod( m_vecCurAngles.y - 50);
+ }
+
+ // Start it rotating
+ m_vecGoalAngles.y = m_iRightBound;;
+ m_vecGoalAngles.x = m_vecCurAngles.x = 0;
+
+ m_fBoneXRotator = 0.0f;
+ m_fBoneYRotator = 0.0f;
+
+ m_bTurningRight = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle movement of the turret
+//-----------------------------------------------------------------------------
+bool C_ObjectSentrygun::MoveTurret(void)
+{
+ bool bMoved = 0;
+
+ float turnrate = (float)(m_iBaseTurnRate) * 10.0f;
+ turnrate *= gpGlobals->frametime;
+
+ // any x movement?
+ if ( m_vecCurAngles.x != m_vecGoalAngles.x )
+ {
+ float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ;
+
+ m_vecCurAngles.x += 0.1 * (turnrate * 5) * flDir;
+
+ // if we started below the goal, and now we're past, peg to goal
+ if (flDir == 1)
+ {
+ if (m_vecCurAngles.x > m_vecGoalAngles.x)
+ m_vecCurAngles.x = m_vecGoalAngles.x;
+ }
+ else
+ {
+ if (m_vecCurAngles.x < m_vecGoalAngles.x)
+ m_vecCurAngles.x = m_vecGoalAngles.x;
+ }
+
+ m_fBoneYRotator = m_vecCurAngles.x;
+
+ bMoved = 1;
+ }
+
+ if ( m_vecCurAngles.y != m_vecGoalAngles.y )
+ {
+ float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ;
+ float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y);
+ bool bReversed = false;
+
+ if (flDist > 180)
+ {
+ flDist = 360 - flDist;
+ flDir = -flDir;
+ bReversed = true;
+ }
+
+ if (m_hEnemy == NULL )
+ {
+ if (flDist > 30)
+ {
+ if (m_fTurnRate < turnrate * 20)
+ {
+ m_fTurnRate += turnrate;
+ }
+ }
+ else
+ {
+ // Slow down
+ if ( m_fTurnRate > (turnrate * 5) )
+ m_fTurnRate -= turnrate;
+ }
+ }
+ else
+ {
+ // When tracking enemies, move faster and don't slow
+ if (flDist > 30)
+ {
+ if (m_fTurnRate < turnrate * 30)
+ {
+ m_fTurnRate += turnrate * 3;
+ }
+ }
+ }
+
+ m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir;
+
+ // if we passed over the goal, peg right to it now
+ if (flDir == -1)
+ {
+ if ( (bReversed == false && m_vecGoalAngles.y > m_vecCurAngles.y) || (bReversed == true && m_vecGoalAngles.y < m_vecCurAngles.y) )
+ m_vecCurAngles.y = m_vecGoalAngles.y;
+ }
+ else
+ {
+ if ( (bReversed == false && m_vecGoalAngles.y < m_vecCurAngles.y) || (bReversed == true && m_vecGoalAngles.y > m_vecCurAngles.y) )
+ m_vecCurAngles.y = m_vecGoalAngles.y;
+ }
+
+ if (m_vecCurAngles.y < 0)
+ m_vecCurAngles.y += 360;
+ else if (m_vecCurAngles.y >= 360)
+ m_vecCurAngles.y -= 360;
+
+ if (flDist < (0.05 * turnrate))
+ m_vecCurAngles.y = m_vecGoalAngles.y;
+
+ m_fBoneXRotator = m_vecCurAngles.y - UTIL_AngleMod( GetAbsAngles().y );
+
+ bMoved = 1;
+ }
+
+ if ( !bMoved || !m_fTurnRate )
+ m_fTurnRate = turnrate;
+
+ if ( bMoved )
+ {
+ NetworkStateChanged();
+ }
+
+ return bMoved;
+}
+
+void C_ObjectSentrygun::ClientThink( void )
+{
+ // Turtling sentryguns don't think
+ if ( IsTurtled() )
+ return;
+
+ if ( IsPlacing() || IsBuilding() )
+ return;
+
+
+ if ( m_hEnemy != NULL )
+ {
+ // Figure out where we're firing at
+ Vector vecMid = EyePosition();
+ Vector vecFireTarget = m_hEnemy->WorldSpaceCenter(); // + vecMid; // BodyTarget( vecMid );
+ Vector vecDirToEnemy = vecFireTarget - vecMid;
+ QAngle angToTarget;
+ VectorAngles(vecDirToEnemy, angToTarget);
+
+ angToTarget.y = UTIL_AngleMod( angToTarget.y );
+ if (angToTarget.x < -180)
+ angToTarget.x += 360;
+ if (angToTarget.x > 180)
+ angToTarget.x -= 360;
+
+ // now all numbers should be in [1...360]
+ // pin to turret limitations to [-50...50]
+ if (angToTarget.x > 50)
+ angToTarget.x = 50;
+ else if (angToTarget.x < -50)
+ angToTarget.x = -50;
+
+ m_vecGoalAngles.y = angToTarget.y;
+ m_vecGoalAngles.x = angToTarget.x;
+
+ MoveTurret();
+ return;
+ }
+
+ // Rotate
+ if ( !MoveTurret() )
+ {
+ // Play a sound occasionally
+ if ( random->RandomFloat(0, 1) < 0.02 )
+ {
+ EmitSound( "ObjectSentrygun.Idle" );
+ }
+
+ // Switch rotation direction
+ if (m_bTurningRight)
+ {
+ m_bTurningRight = false;
+ m_vecGoalAngles.y = m_iLeftBound;
+ }
+ else
+ {
+ m_bTurningRight = true;
+ m_vecGoalAngles.y = m_iRightBound;
+ }
+
+ // Randomly look up and down a bit
+ if ( random->RandomFloat(0, 1) < 0.3 )
+ {
+ m_vecGoalAngles.x = (int)random->RandomFloat(-10,10);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Control screen
+//-----------------------------------------------------------------------------
+class CSentrygunControlPanel : public CRotatingObjectControlPanel
+{
+ DECLARE_CLASS( CSentrygunControlPanel, CRotatingObjectControlPanel );
+
+public:
+ CSentrygunControlPanel( vgui::Panel *parent, const char *panelName );
+ virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
+ virtual void OnTick();
+ virtual void OnCommand( const char *command );
+
+ void AddAmmo( void );
+
+private:
+ vgui::Label *m_pAmmoLabel;
+};
+
+
+DECLARE_VGUI_SCREEN_FACTORY( CSentrygunControlPanel, "sentrygun_control_panel" );
+
+
+//-----------------------------------------------------------------------------
+// Constructor:
+//-----------------------------------------------------------------------------
+CSentrygunControlPanel::CSentrygunControlPanel( vgui::Panel *parent, const char *panelName )
+ : BaseClass( parent, "CSentrygunControlPanel" )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+bool CSentrygunControlPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
+{
+ m_pAmmoLabel = new vgui::Label( this, "AmmoReadout", "" );
+
+ if (!BaseClass::Init(pKeyValues, pInitData))
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Frame-based update
+//-----------------------------------------------------------------------------
+void CSentrygunControlPanel::OnTick()
+{
+ BaseClass::OnTick();
+
+ C_BaseObject *pObj = GetOwningObject();
+ if (!pObj)
+ return;
+
+ Assert( dynamic_cast<C_ObjectSentrygun*>(pObj) );
+ C_ObjectSentrygun *pSentrygun = static_cast<C_ObjectSentrygun*>(pObj);
+
+ char buf[256];
+ int iAmmo = pSentrygun->GetAmmoLeft();
+ if (iAmmo > 0)
+ {
+ Q_snprintf( buf, sizeof( buf ), "%d rounds left", iAmmo );
+ }
+ else
+ {
+ Q_snprintf( buf, sizeof( buf ), "OUT OF AMMO" );
+ }
+ m_pAmmoLabel->SetText( buf );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle ammo input to the sentrygun
+//-----------------------------------------------------------------------------
+void CSentrygunControlPanel::AddAmmo( void )
+{
+ C_BaseObject *pObj = GetOwningObject();
+ if (pObj)
+ {
+ pObj->SendClientCommand( "addammo" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Button click handlers
+//-----------------------------------------------------------------------------
+void CSentrygunControlPanel::OnCommand( const char *command )
+{
+ if (!Q_strnicmp(command, "AddAmmo", 7))
+ {
+ AddAmmo();
+ return;
+ }
+
+ BaseClass::OnCommand(command);
+}
+
+
+
+//======================================================================================================
+// SENTRYGUN TYPES
+//======================================================================================================
+// Purpose: Plasma sentrygun
+//-----------------------------------------------------------------------------
+IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygunPlasma, DT_ObjectSentrygunPlasma, CObjectSentrygunPlasma)
+END_RECV_TABLE()
+
+C_ObjectSentrygunPlasma::C_ObjectSentrygunPlasma()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Rocket launcher sentrygun
+//-----------------------------------------------------------------------------
+IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygunRocketlauncher, DT_ObjectSentrygunRocketlauncher, CObjectSentrygunRocketlauncher)
+END_RECV_TABLE()
+
+C_ObjectSentrygunRocketlauncher::C_ObjectSentrygunRocketlauncher()
+{
+}
+
+