summaryrefslogtreecommitdiff
path: root/game/client/tf2/c_weapon_mortar.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/tf2/c_weapon_mortar.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf2/c_weapon_mortar.cpp')
-rw-r--r--game/client/tf2/c_weapon_mortar.cpp634
1 files changed, 634 insertions, 0 deletions
diff --git a/game/client/tf2/c_weapon_mortar.cpp b/game/client/tf2/c_weapon_mortar.cpp
new file mode 100644
index 0000000..ecae3b9
--- /dev/null
+++ b/game/client/tf2/c_weapon_mortar.cpp
@@ -0,0 +1,634 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client's CWeaponMortar class
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "hud.h"
+#include "hudelement.h"
+#include "in_buttons.h"
+#include "ground_line.h"
+#include "clientmode_tfnormal.h"
+#include "vgui_basepanel.h"
+#include "c_tf_basecombatweapon.h"
+#include "engine/IEngineSound.h"
+#include "iinput.h"
+#include "imessagechars.h"
+#include "c_weapon__stubs.h"
+
+//=============================================================================
+// Purpose: Hud Element for Mortar firing
+//=============================================================================
+class CHudMortar : public CHudElement, public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CHudMortar, vgui::Panel );
+
+public:
+ DECLARE_MULTIPLY_INHERITED();
+
+ CHudMortar( const char *pElementName );
+ virtual void Paint( void );
+
+ float m_flPower;
+ float m_flFiringPower;
+ float m_flFiringAccuracy;
+ float m_flReset;
+};
+
+DECLARE_HUDELEMENT( CHudMortar );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHudMortar::CHudMortar( const char *pElementName ) :
+ CHudElement( pElementName ), vgui::Panel( NULL, pElementName )
+{
+ m_flPower = 0;
+ m_flFiringPower = 0;
+ m_flFiringAccuracy = 0;
+ m_flReset = 0;
+ SetPaintBackgroundEnabled( false );
+ SetAutoDelete( false );
+ SetName( "mortar" );
+
+ SetHiddenBits( HIDEHUD_PLAYERDEAD );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudMortar::Paint()
+{
+ // Clear out markers
+ if ( m_flReset && m_flReset < gpGlobals->curtime )
+ {
+ m_flFiringPower = 0;
+ m_flFiringAccuracy = 0;
+ m_flReset = 0;
+ }
+
+ int w, h;
+ GetSize( w, h );
+
+ // Use an eighth of the height for each side
+ int iOffset = h / 8;
+ int iBarHeight = h - (2 * iOffset);
+ int iZero = (w / 5);
+ int iMarker = iZero + (m_flPower * (w - iZero));
+ int iPower = iZero + (m_flFiringPower * (w - iZero));
+ int iAccuracy = iZero + (m_flFiringAccuracy * (w - iZero));
+
+ // Shade the bar
+ int alpha = 205;
+ int colorAmt = 255;
+ int nSegs = 20;
+
+ int i;
+ for(i=0; i < nSegs; i++)
+ {
+ int left = iZero + (i*w) / nSegs;
+ int right = iZero + ((i+1)*w) / nSegs;
+
+ // Don't draw past the marker
+ if ( m_flFiringPower && right > iPower )
+ right = iPower;
+ else if ( !m_flFiringPower && right > iMarker )
+ right = iMarker;
+
+ vgui::surface()->DrawSetColor( ((i + 5) * colorAmt) / nSegs, 0, 0, alpha );
+ vgui::surface()->DrawFilledRect( left, iOffset, right, iBarHeight);
+ }
+
+ // Shade back from zero
+ nSegs = 10;
+ for(i=0; i < nSegs; i++)
+ {
+ int left = (i*iZero) / nSegs;
+ int right = ((i+1)*iZero) / nSegs;
+
+ if ( m_flFiringAccuracy && left < iAccuracy )
+ left = iAccuracy;
+ else if ( !m_flFiringAccuracy && left < iMarker )
+ left = iMarker;
+
+ vgui::surface()->DrawSetColor( ((nSegs - i) * colorAmt) / nSegs, 0, 0, alpha );
+ vgui::surface()->DrawFilledRect(left, iOffset, right, iBarHeight);
+ }
+
+ // Draw the zero marker
+ vgui::surface()->DrawSetColor(255,255,255,255);
+ vgui::surface()->DrawFilledRect(iZero-2, iOffset, iZero+2, iBarHeight);
+
+ // Draw the marker
+ vgui::surface()->DrawSetColor(255,255,255,255);
+ vgui::surface()->DrawFilledRect(iMarker-1, 0, iMarker+1, h);
+
+ // Draw the power mark if we've set one
+ if ( m_flFiringPower )
+ {
+ vgui::surface()->DrawSetColor(255,255,255,255);
+ vgui::surface()->DrawFilledRect(iPower-1, iOffset, iPower+1, iBarHeight);
+ }
+
+ // Draw the accuracy mark if we've set one
+ if ( m_flFiringAccuracy )
+ {
+ vgui::surface()->DrawSetColor(255,255,255,255);
+ vgui::surface()->DrawFilledRect(iAccuracy-1, iOffset, iAccuracy+1, iBarHeight);
+ }
+
+ // Draw box
+ vgui::surface()->DrawSetColor(255,255,255,255);
+ vgui::surface()->DrawOutlinedRect(0, iOffset, w, iBarHeight);
+}
+
+static ConVar g_CVMortarGroundLineUpdateInterval( "mortar_groundlineupdateinterval", "0.1", 0, "Number of seconds, mininum, between ground line position updates." );
+
+
+//=============================================================================
+// Purpose: Client version of CWeaponMortar
+//=============================================================================
+class C_WeaponMortar : public C_BaseTFCombatWeapon
+{
+public:
+ DECLARE_CLASS( C_WeaponMortar, C_BaseTFCombatWeapon );
+ DECLARE_CLIENTCLASS();
+ DECLARE_PREDICTABLE();
+
+ C_WeaponMortar();
+ ~C_WeaponMortar();
+
+ void FireMortar( void );
+
+ virtual void PreDataUpdate( DataUpdateType_t updateType );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void HandleInput( void );
+ virtual void Redraw();
+ virtual void OverrideMouseInput( float *x, float *y );
+
+ // Deploy / Holster
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+
+ // Mortar Data
+ bool m_bCarried;
+ bool m_bMortarReloading;
+ Vector m_vecMortarOrigin;
+ Vector m_vecMortarAngles;
+ float m_flPrevMortarServerYaw;
+ float m_flMortarYaw;
+ float m_flPrevMortarYaw;
+
+ // Input Handling
+ float m_flNextClick;
+ float m_flAccuracySpeed;
+ int m_iFiringState;
+ bool m_bRotating;
+
+ CGroundLine *m_pGroundLine;
+ CGroundLine *m_pDarkLine;
+ CHudMortar *m_pPowerBar;
+ IMaterial *m_pRotateIcon;
+
+ vgui::HFont m_hFontText;
+
+private:
+ C_WeaponMortar( const C_WeaponMortar & );
+
+ float m_flLastGroundlineUpdateTime;
+
+};
+
+STUB_WEAPON_CLASS_IMPLEMENT( weapon_mortar, C_WeaponMortar );
+
+IMPLEMENT_CLIENTCLASS_DT(C_WeaponMortar, DT_WeaponMortar, CWeaponMortar)
+ RecvPropInt( RECVINFO(m_bCarried) ),
+ RecvPropInt( RECVINFO(m_bMortarReloading) ),
+ RecvPropVector( RECVINFO(m_vecMortarOrigin) ),
+ RecvPropVector( RECVINFO(m_vecMortarAngles) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_WeaponMortar::C_WeaponMortar()
+{
+ m_bCarried = true;
+ m_bMortarReloading = false;
+ m_iFiringState = MORTAR_IDLE;
+ m_flNextClick = 0;
+ m_flAccuracySpeed = 0;
+ m_bRotating = false;
+ m_pGroundLine = NULL;
+ m_pDarkLine = NULL;
+ m_flMortarYaw = 0;
+ m_flPrevMortarYaw = -1;
+
+ m_pRotateIcon = materials->FindMaterial( "Hud/mortar/mortar_rotate", TEXTURE_GROUP_VGUI );
+ m_pRotateIcon->IncrementReferenceCount();
+
+ m_pPowerBar = GET_HUDELEMENT( CHudMortar );
+
+ m_flLastGroundlineUpdateTime = 0.0f;
+
+ m_hFontText = g_hFontTrebuchet24;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_WeaponMortar::~C_WeaponMortar()
+{
+ m_pPowerBar->SetParent( (vgui::Panel *)NULL );
+
+ if ( m_pRotateIcon != NULL )
+ {
+ m_pRotateIcon->DecrementReferenceCount();
+ m_pRotateIcon = NULL;
+ }
+
+ if ( m_pGroundLine )
+ {
+ delete m_pGroundLine;
+ }
+ if ( m_pDarkLine )
+ {
+ delete m_pDarkLine;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate(updateType);
+
+ m_flPrevMortarServerYaw = m_vecMortarAngles.y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged(updateType);
+
+ // If the mortar's being carried by some other player, don't make a ground line
+ if ( !IsCarriedByLocalPlayer() )
+ return;
+
+ // Draw power chart if the mortar is deployed
+ if ( !m_bCarried && !m_bMortarReloading )
+ {
+ vgui::Panel *pParent = GetClientModeNormal()->GetViewport();
+ int parentWidth, parentHeight;
+ pParent->GetSize(parentWidth, parentHeight);
+
+ int iWidth = 256;
+ int iHeight = 40;
+ int iX = (parentWidth - iWidth) / 2;
+ int iY = (parentHeight - 200);
+
+ // Only show the power bar if the mortar's the active weapon
+ if ( IsActiveByLocalPlayer() )
+ {
+ m_pPowerBar->SetBounds( iX, iY, iWidth, iHeight );
+ m_pPowerBar->SetParent(pParent);
+ }
+ else
+ {
+ m_pPowerBar->SetParent( (vgui::Panel *)NULL );
+ }
+
+ if ( !m_bRotating && m_flPrevMortarServerYaw != m_vecMortarAngles.y )
+ {
+ m_flMortarYaw = m_vecMortarAngles.y;
+ }
+
+ // Create the Ground lines
+ if ( !m_pGroundLine )
+ {
+ m_pGroundLine = new CGroundLine();
+ m_pGroundLine->Init( "player/support/mortarline" );
+ }
+ if ( !m_pDarkLine )
+ {
+ m_pDarkLine = new CGroundLine();
+ m_pDarkLine->Init( "player/support/mortarline" );
+ }
+ }
+ else
+ {
+ m_pPowerBar->SetParent( (vgui::Panel *)NULL );
+
+ if ( m_pGroundLine )
+ {
+ delete m_pGroundLine;
+ m_pGroundLine = NULL;
+ }
+ if ( m_pDarkLine )
+ {
+ delete m_pDarkLine;
+ m_pDarkLine = NULL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::HandleInput( void )
+{
+ // If it's being carried, ignore input
+ if ( m_bCarried )
+ return;
+ // If the player's dead, ignore input
+ C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer();
+ if ( pPlayer == NULL )
+ return;
+ if ( pPlayer->GetHealth() < 1 )
+ return;
+
+ // Ignore input if it's reloading
+ if ( m_bMortarReloading )
+ return;
+
+ // Secondary fire rotates the mortar
+ if ( gHUD.m_iKeyBits & IN_ATTACK2 )
+ {
+ if ( pPlayer->HasPowerup(POWERUP_EMP) )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "WeaponMortar.EMPed" );
+ return;
+ }
+ m_bRotating = true;
+
+ gHUD.m_iKeyBits &= ~IN_ATTACK2;
+
+ // Prevent firing while rotating
+ m_pPowerBar->SetParent( (vgui::Panel *)NULL );
+ return;
+ }
+ else
+ {
+ if ( m_bRotating )
+ {
+ // Bring up the firing bar again
+ vgui::Panel *pParent = GetClientModeNormal()->GetViewport();
+ m_pPowerBar->SetParent(pParent);
+ m_bRotating = false;
+ }
+ }
+
+ // Primary fire launches mortars
+ if (gHUD.m_iKeyBits & IN_ATTACK)
+ {
+ if ( pPlayer->HasPowerup(POWERUP_EMP) )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "WeaponMortar.EMPed" );
+ return;
+ }
+
+ if ( m_flNextClick <= gpGlobals->curtime )
+ {
+ // Play click animation
+ // SendWeaponAnim( ACT_SLAM_DETONATOR_DETONATE );
+
+ // Switch states
+ switch( m_iFiringState )
+ {
+ case MORTAR_IDLE:
+ m_iFiringState = MORTAR_CHARGING_POWER;
+ break;
+ case MORTAR_CHARGING_POWER:
+ m_pPowerBar->m_flFiringPower = m_pPowerBar->m_flPower;
+ m_iFiringState = MORTAR_CHARGING_ACCURACY;
+ break;
+ case MORTAR_CHARGING_ACCURACY:
+ m_pPowerBar->m_flFiringAccuracy = m_pPowerBar->m_flPower;
+ m_iFiringState = MORTAR_IDLE;
+
+ FireMortar();
+ break;
+ default:
+ break;
+ }
+
+ input->ClearInputButton( IN_ATTACK );
+ gHUD.m_iKeyBits &= ~IN_ATTACK;
+
+ m_flNextClick = gpGlobals->curtime + 0.05;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::Redraw()
+{
+ BaseClass::Redraw();
+
+ // If the player's dead, abort
+ C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer();
+ if ( pPlayer == NULL )
+ return;
+ if ( pPlayer->GetHealth() < 1 )
+ {
+ m_iFiringState = MORTAR_IDLE;
+ m_bCarried = true;
+ return;
+ }
+
+ // If it's reloading, tell the player
+ if ( m_bMortarReloading )
+ {
+ int width, height;
+ messagechars->GetStringLength( m_hFontText, &width, &height, "Mortar is reloading..." );
+ messagechars->DrawString( m_hFontText, (ScreenWidth() - width) / 2, YRES(350), 192, 192, 192, 255, "Mortar is reloading...", IMessageChars::MESSAGESTRINGID_NONE );
+ return;
+ }
+
+ // Handle power charging
+ switch( m_iFiringState )
+ {
+ case MORTAR_IDLE:
+ m_pPowerBar->m_flPower = 0;
+ break;
+ case MORTAR_CHARGING_POWER:
+ m_pPowerBar->m_flPower = MIN( m_pPowerBar->m_flPower + ( (1.0 / MORTAR_CHARGE_POWER_RATE) * gpGlobals->curtimeDelta), 1.0f);
+ m_pPowerBar->m_flFiringPower = 0;
+ m_pPowerBar->m_flFiringAccuracy = 0;
+ if ( m_pPowerBar->m_flPower >= 1.0 )
+ {
+ // Hit Max, start going down
+ m_pPowerBar->m_flFiringPower = m_pPowerBar->m_flPower;
+ m_iFiringState = MORTAR_CHARGING_ACCURACY;
+ m_flNextClick = gpGlobals->curtime + 0.25;
+ }
+ break;
+ case MORTAR_CHARGING_ACCURACY:
+ // Calculate accuracy speed
+ m_flAccuracySpeed = (1.0 / MORTAR_CHARGE_ACCURACY_RATE);
+ if ( m_pPowerBar->m_flFiringPower > 0.5 )
+ {
+ // Shots over halfway suffer an increased speed to the accuracy power, making accurate shots harder
+ float flAdjustedPower = (m_pPowerBar->m_flFiringPower - 0.5) * 3.0;
+ m_flAccuracySpeed += (m_pPowerBar->m_flFiringPower * flAdjustedPower);
+ }
+
+ m_pPowerBar->m_flPower = MAX( m_pPowerBar->m_flPower - ( m_flAccuracySpeed * gpGlobals->curtimeDelta), -0.25f);
+ if ( m_pPowerBar->m_flPower <= -0.25 )
+ {
+ // Hit Min, fire mortar
+ m_pPowerBar->m_flFiringAccuracy = m_pPowerBar->m_flPower;
+ m_iFiringState = MORTAR_IDLE;
+
+ FireMortar();
+ m_flNextClick = gpGlobals->curtime + 0.25;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Draw the rotate icon if the player's rotating the mortar
+ if ( m_bRotating )
+ {
+ vgui::Panel *pParent = GetClientModeNormal()->GetViewport();
+ int parentWidth, parentHeight;
+ pParent->GetSize(parentWidth, parentHeight);
+ int iWidth = 64;
+ int iHeight = 64;
+ int iX = (parentWidth - iWidth) / 2;
+ int iY = (parentHeight - 216);
+
+ IMesh* pMesh = materials->GetDynamicMesh( true, NULL, NULL, m_pRotateIcon );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,0,0 );
+ meshBuilder.Position3f( iX,iY,0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,1,0 );
+ meshBuilder.Position3f( iX+iWidth, iY, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,1,1 );
+ meshBuilder.Position3f( iX+iWidth, iY+iHeight, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3f( 1.0, 1.0, 1.0 );
+ meshBuilder.TexCoord2f( 0,0,1 );
+ meshBuilder.Position3f( iX, iY+iHeight, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+
+ // Update the ground line if it's moved
+ if ( !m_bCarried && (m_flPrevMortarYaw != m_flMortarYaw ) &&
+ gpGlobals->curtime > m_flLastGroundlineUpdateTime + g_CVMortarGroundLineUpdateInterval.GetFloat() )
+ {
+ // Create the Ground line start & end points
+ Vector vecForward;
+ AngleVectors( QAngle( 0, m_flMortarYaw, 0 ), &vecForward );
+ Vector vecStart = m_vecMortarOrigin + (vecForward * MORTAR_RANGE_MIN);
+
+ float flRange = MORTAR_RANGE_MAX_INITIAL;
+ if ( pPlayer->HasNamedTechnology( "mortar_range" ) )
+ flRange = MORTAR_RANGE_MAX_UPGRADED;
+ Vector vecEnd = m_vecMortarOrigin + (vecForward * flRange);
+
+ m_pDarkLine->SetParameters( m_vecMortarOrigin, vecStart, Vector( 0.1,0.1,0.1 ), Vector( 0.1,0.1,0.1 ), 0.5, 22 );
+ m_pGroundLine->SetParameters( vecStart, vecEnd, Vector(0,1,0), Vector(1,0,0), 0.5, 22 );
+
+ m_flPrevMortarYaw = m_flMortarYaw;
+
+ m_flLastGroundlineUpdateTime = gpGlobals->curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Capture mouse input for mortar rotation
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::OverrideMouseInput( float *x, float *y )
+{
+ if ( !m_bRotating )
+ return;
+
+ float flX = ( *x ) * 0.05f;
+
+ m_flMortarYaw = anglemod(m_flMortarYaw - flX);
+
+ *x = 0.0f;
+ *y = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Weapon's been deployed
+//-----------------------------------------------------------------------------
+bool C_WeaponMortar::Deploy( void )
+{
+ if ( m_pDarkLine )
+ {
+ m_pDarkLine->SetVisible( true );
+ }
+ if ( m_pGroundLine )
+ {
+ m_pGroundLine->SetVisible( true );
+ }
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Weapon's been holstered
+//-----------------------------------------------------------------------------
+bool C_WeaponMortar::Holster( C_BaseCombatWeapon *pSwitchingTo )
+{
+ if ( m_pDarkLine )
+ {
+ m_pDarkLine->SetVisible( false );
+ }
+ if ( m_pGroundLine )
+ {
+ m_pGroundLine->SetVisible( false );
+ }
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponMortar::FireMortar( void )
+{
+ // Clamp inaccuracy
+ float flTempAcc = m_pPowerBar->m_flFiringAccuracy;
+ if ( flTempAcc > 0.25 )
+ flTempAcc = 0.25;
+ else if ( flTempAcc < -0.25 )
+ flTempAcc = -0.25;
+
+ // HACKHACK: This is an amazingly bad way to do it. Replace this when the
+ // client DLL can insert commands into the usercmds
+ char szbuf[48];
+ Q_snprintf( szbuf, sizeof( szbuf ), "mortar %0.2f %0.2f %0.2f\n", m_pPowerBar->m_flFiringPower, flTempAcc, m_flMortarYaw );
+ engine->ClientCmd(szbuf);
+
+ // Tell the power bar to reset soon
+ m_pPowerBar->m_flReset = gpGlobals->curtime + 1.0;
+} \ No newline at end of file