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.
870 lines
24 KiB
870 lines
24 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "c_basefourwheelvehicle.h"
|
||
|
#include "tf_movedata.h"
|
||
|
#include "ObjectControlPanel.h"
|
||
|
#include <vgui_controls/Label.h>
|
||
|
#include <vgui_controls/Button.h>
|
||
|
#include "vehicle_mortar_shared.h"
|
||
|
#include "vgui_rotation_slider.h"
|
||
|
#include <vgui/ISurface.h>
|
||
|
#include "vgui_basepanel.h"
|
||
|
#include "ground_line.h"
|
||
|
#include "hud_minimap.h"
|
||
|
#include "vgui_bitmapimage.h"
|
||
|
#include "iusesmortarpanel.h"
|
||
|
|
||
|
// How long it waits after you've changed the mortar's yaw to draw using the server's value.
|
||
|
#define CLIENT_MORTAR_YAW_COUNTDOWN 0.5
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class C_VehicleMortar : public C_BaseTFFourWheelVehicle, public IUsesMortarPanel
|
||
|
{
|
||
|
DECLARE_CLASS( C_VehicleMortar, C_BaseTFFourWheelVehicle );
|
||
|
public:
|
||
|
DECLARE_CLIENTCLASS();
|
||
|
|
||
|
C_VehicleMortar();
|
||
|
|
||
|
virtual void ReceiveMessage( int classID, bf_read &msg );
|
||
|
|
||
|
// Fire off a mortar.
|
||
|
void FireMortar();
|
||
|
|
||
|
virtual void ClientThink();
|
||
|
|
||
|
// C_BaseEntity overrides.
|
||
|
public:
|
||
|
virtual void GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS]);
|
||
|
|
||
|
// IUsesMortarPanel
|
||
|
public:
|
||
|
// Get the data from this mortar needed by the panel
|
||
|
virtual void GetMortarData( float *flClientMortarYaw, bool *bAllowedToFire, float *flPower, float *flFiringPower, float *flFiringAccuracy, int *iFiringState );
|
||
|
virtual void SendYawCommand( void );
|
||
|
virtual void ForceClientYawCountdown( float flTime );
|
||
|
virtual void ClickFire( void );
|
||
|
|
||
|
public:
|
||
|
// Mortar firing info.
|
||
|
int m_iFiringState; // One of the MORTAR_ defines.
|
||
|
bool m_bMortarReloading;
|
||
|
float m_flPower;
|
||
|
bool m_bAllowedToFire;
|
||
|
|
||
|
// Parameters for the next shot.
|
||
|
float m_flFiringPower;
|
||
|
float m_flFiringAccuracy;
|
||
|
|
||
|
float m_flMortarYaw; // What direction the mortar is aimed in.
|
||
|
float m_flMortarPitch;
|
||
|
|
||
|
// This is what is used on the client to draw the ground line and orient the mortar.
|
||
|
// It is usually copied right over from m_flClientMortarYaw (which comes from the server),
|
||
|
// but this is also used when rotating the mortar so you can see the line move smoothly.
|
||
|
float m_flClientMortarYaw;
|
||
|
|
||
|
// This is set to about 1/4 seconds when you rotate the mortar line so you use the client's
|
||
|
// (smooth, non-lagged) yaw changes instead of the server's.
|
||
|
float m_flForceClientYawCountdown;
|
||
|
|
||
|
private:
|
||
|
C_VehicleMortar( const C_VehicleMortar & ); // not defined, not accessible
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_VehicleMortar, DT_VehicleMortar, CVehicleMortar)
|
||
|
RecvPropFloat( RECVINFO( m_flMortarYaw ) ),
|
||
|
RecvPropFloat( RECVINFO( m_flMortarPitch ) ),
|
||
|
RecvPropBool( RECVINFO( m_bAllowedToFire ) )
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
C_VehicleMortar::C_VehicleMortar()
|
||
|
{
|
||
|
m_iFiringState = MORTAR_IDLE;
|
||
|
m_bMortarReloading = false;
|
||
|
m_flPower = 0;
|
||
|
|
||
|
m_flMortarYaw = 0;
|
||
|
m_flClientMortarYaw = 0;
|
||
|
m_flMortarPitch = 0;
|
||
|
m_flForceClientYawCountdown = 0;
|
||
|
m_bAllowedToFire = true;
|
||
|
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_VehicleMortar::ReceiveMessage( int classID, bf_read &msg )
|
||
|
{
|
||
|
if ( classID != GetClientClass()->m_ClassID )
|
||
|
{
|
||
|
// message is for subclass
|
||
|
BaseClass::ReceiveMessage( classID, msg );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::ClickFire()
|
||
|
{
|
||
|
switch( m_iFiringState )
|
||
|
{
|
||
|
case MORTAR_IDLE:
|
||
|
m_iFiringState = MORTAR_CHARGING_POWER;
|
||
|
break;
|
||
|
|
||
|
case MORTAR_CHARGING_POWER:
|
||
|
m_flFiringPower = m_flPower;
|
||
|
m_iFiringState = MORTAR_CHARGING_ACCURACY;
|
||
|
break;
|
||
|
|
||
|
case MORTAR_CHARGING_ACCURACY:
|
||
|
m_flFiringAccuracy = m_flPower;
|
||
|
m_iFiringState = MORTAR_IDLE;
|
||
|
|
||
|
FireMortar();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::FireMortar()
|
||
|
{
|
||
|
char cmd[512];
|
||
|
Q_snprintf( cmd, sizeof( cmd ), "FireMortar %.2f %.2f", m_flFiringPower, m_flFiringAccuracy );
|
||
|
SendClientCommand( cmd );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::SendYawCommand( void )
|
||
|
{
|
||
|
char szbuf[48];
|
||
|
Q_snprintf( szbuf, sizeof( szbuf ), "MortarYaw %0.2f\n", m_flClientMortarYaw );
|
||
|
SendClientCommand( szbuf );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::ForceClientYawCountdown( float flTime )
|
||
|
{
|
||
|
m_flForceClientYawCountdown = flTime;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::GetMortarData( float *flClientMortarYaw, bool *bAllowedToFire, float *flPower, float *flFiringPower, float *flFiringAccuracy, int *iFiringState )
|
||
|
{
|
||
|
*flClientMortarYaw = m_flClientMortarYaw;
|
||
|
*bAllowedToFire = m_bAllowedToFire;
|
||
|
*flPower = m_flPower;
|
||
|
*flFiringPower = m_flFiringPower;
|
||
|
*flFiringAccuracy = m_flFiringAccuracy;
|
||
|
*iFiringState = m_iFiringState;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_VehicleMortar::ClientThink()
|
||
|
{
|
||
|
m_flForceClientYawCountdown -= gpGlobals->frametime;
|
||
|
if ( m_flForceClientYawCountdown <= 0 )
|
||
|
{
|
||
|
m_flClientMortarYaw = m_flMortarYaw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_VehicleMortar::GetBoneControllers( float controllers[MAXSTUDIOBONECTRLS])
|
||
|
{
|
||
|
BaseClass::GetBoneControllers( controllers);
|
||
|
|
||
|
controllers[0] = anglemod( m_flClientMortarYaw ) / 360.0;
|
||
|
controllers[1] = anglemod( m_flMortarPitch ) / 360.0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CMortarMinimapPanel
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CMortarMinimapPanel::CMortarMinimapPanel( vgui::Panel *pParent, const char *pElementName )
|
||
|
: CMinimapPanel( pElementName )
|
||
|
{
|
||
|
SetParent( pParent );
|
||
|
|
||
|
m_bMouseDown = false;
|
||
|
m_bFireButtonDown = false;
|
||
|
m_LastX = m_LastY = -1;
|
||
|
|
||
|
m_nTextureId = vgui::surface()->CreateNewTextureID();
|
||
|
vgui::surface()->DrawSetTextureFile( m_nTextureId, "hud/minimap/mortar_slider", true, false );
|
||
|
|
||
|
m_nTextureId_CantFire = vgui::surface()->CreateNewTextureID();
|
||
|
vgui::surface()->DrawSetTextureFile( m_nTextureId_CantFire, "hud/minimap/mortar_slider_cantfire", true, false );
|
||
|
}
|
||
|
|
||
|
|
||
|
CMortarMinimapPanel::~CMortarMinimapPanel()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CMortarMinimapPanel::InitMortarMinimap( C_BaseEntity *pMortar )
|
||
|
{
|
||
|
BaseClass::Init( NULL );
|
||
|
|
||
|
m_hMortar = pMortar;
|
||
|
|
||
|
m_MortarButtonUp.Init( GetVPanel(), "hud/minimap/icon_mortarbutton_up" );
|
||
|
m_MortarButtonDown.Init( GetVPanel(), "hud/minimap/icon_mortarbutton_dn" );
|
||
|
m_MortarButtonCantFire.Init( GetVPanel(), "hud/minimap/icon_mortarbutton_cantfire" );
|
||
|
|
||
|
m_MortarDirectionImage.Init( GetVPanel(), "hud/minimap/icon_player_arrow" );
|
||
|
m_MortarDirectionImage.SetColor( Color( 0, 255, 0, 255 ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
C_BaseEntity *CMortarMinimapPanel::GetMortar() const
|
||
|
{
|
||
|
return (C_BaseEntity*)m_hMortar;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CMortarMinimapPanel::Paint()
|
||
|
{
|
||
|
BaseClass::Paint();
|
||
|
|
||
|
C_BaseEntity *pMortar = GetMortar();
|
||
|
IUsesMortarPanel *pMortarInterface = dynamic_cast< IUsesMortarPanel* >( pMortar );
|
||
|
if ( pMortar && pMortarInterface )
|
||
|
{
|
||
|
float flClientMortarYaw, flPower, flFiringPower, flFiringAccuracy;
|
||
|
int iFiringState;
|
||
|
bool bAllowedToFire;
|
||
|
pMortarInterface->GetMortarData( &flClientMortarYaw, &bAllowedToFire, &flPower, &flFiringPower, &flFiringAccuracy, &iFiringState );
|
||
|
|
||
|
float yaw = flClientMortarYaw + 90;
|
||
|
|
||
|
float x, y;
|
||
|
if ( WorldToMinimap( MINIMAP_CLAMP, pMortar->GetAbsOrigin(), x, y ) )
|
||
|
{
|
||
|
int size = 20;
|
||
|
|
||
|
BitmapImage *pImage = &m_MortarButtonCantFire;
|
||
|
if ( bAllowedToFire )
|
||
|
{
|
||
|
if ( m_bFireButtonDown )
|
||
|
pImage = &m_MortarButtonDown;
|
||
|
else
|
||
|
pImage = &m_MortarButtonUp;
|
||
|
}
|
||
|
|
||
|
pImage->DoPaint( x-size/2, y-size/2, size, size, yaw );
|
||
|
|
||
|
size = 40;
|
||
|
m_MortarDirectionImage.DoPaint( x-size/2, y-size/2, size, size, pMortar->GetAbsAngles()[YAW] + 90 );
|
||
|
|
||
|
|
||
|
// Draw the power bar.
|
||
|
float flAngle = pMortar->GetAbsAngles()[YAW] + flClientMortarYaw;
|
||
|
Vector vForward( -sin( DEG2RAD( flAngle ) ), cos( DEG2RAD( flAngle ) ), 0 );
|
||
|
Vector vRight( vForward.y, -vForward.x, 0 );
|
||
|
|
||
|
Vector vStartPoint = pMortar->GetAbsOrigin();
|
||
|
Vector vEndPoint = vStartPoint + vForward * MORTAR_RANGE_MAX_INITIAL;
|
||
|
Vector vInaccuracy = vEndPoint + vRight * (MORTAR_RANGE_MAX_INITIAL * MORTAR_INACCURACY_MAX_INITIAL);
|
||
|
|
||
|
Vector2D vStart2D, vEnd2D, vInaccuracy2D;
|
||
|
WorldToMinimap( MINIMAP_ALWAYS_ACCEPT, vStartPoint, vStart2D.x, vStart2D.y );
|
||
|
WorldToMinimap( MINIMAP_ALWAYS_ACCEPT, vEndPoint, vEnd2D.x, vEnd2D.y );
|
||
|
WorldToMinimap( MINIMAP_ALWAYS_ACCEPT, vInaccuracy, vInaccuracy2D.x, vInaccuracy2D.y );
|
||
|
|
||
|
Vector2D vDir = vEnd2D - vStart2D;
|
||
|
Vector2DNormalize( vDir );
|
||
|
|
||
|
|
||
|
// These variables control the look.
|
||
|
float flLength = (vEnd2D - vStart2D).Length();
|
||
|
float flZeroT = 1.0f / 5;
|
||
|
float flZero = flLength * flZeroT;
|
||
|
|
||
|
float flFirePower = MAX( flPower, flFiringPower );
|
||
|
|
||
|
float flStartFatness = 2;
|
||
|
float flEndFatness = flStartFatness;
|
||
|
|
||
|
float flScalePower = flFiringAccuracy;
|
||
|
if ( iFiringState != MORTAR_IDLE )
|
||
|
flScalePower = flPower;
|
||
|
|
||
|
Vector2D vInaccuracyDir = vInaccuracy2D - vEnd2D;
|
||
|
flEndFatness *= vInaccuracyDir.Length() * flScalePower * 0.4;
|
||
|
flEndFatness = MAX( fabs( flEndFatness ), flStartFatness );
|
||
|
|
||
|
Vector2D vPerp( vDir.y, -vDir.x );
|
||
|
Vector2DNormalize( vPerp );
|
||
|
|
||
|
Vector2D vStartPerp = vPerp * flStartFatness;
|
||
|
Vector2D vEndPerp = vPerp * flEndFatness;
|
||
|
|
||
|
|
||
|
// Draw the red-black power bars.
|
||
|
vgui::ISurface *pSurface = vgui::surface();
|
||
|
if ( bAllowedToFire )
|
||
|
pSurface->DrawSetTexture( m_nTextureId );
|
||
|
else
|
||
|
pSurface->DrawSetTexture( m_nTextureId_CantFire );
|
||
|
|
||
|
Vector2D vZeroPerp;
|
||
|
Vector2DLerp( vStartPerp, vEndPerp, flZero / flLength, vZeroPerp );
|
||
|
|
||
|
// Draw a black->red bar from zero to our current power.
|
||
|
float flFirePowerDistance = RemapVal( flFirePower, 0, 1, flZero, flLength );
|
||
|
Vector2D vPowerPerp;
|
||
|
Vector2DLerp( vStartPerp, vEndPerp, RemapVal( flFirePowerDistance, flZero, flLength, flZeroT, 1 ), vPowerPerp );
|
||
|
|
||
|
vgui::Vertex_t verts[4];
|
||
|
verts[0].Init( vStart2D + vDir * flZero - vZeroPerp );
|
||
|
verts[1].Init( verts[0].m_Position + vZeroPerp * 2 );
|
||
|
|
||
|
verts[2].Init( vStart2D + vDir * flFirePowerDistance + vPowerPerp, Vector2D( flFirePower, 0 ) );
|
||
|
verts[3] = verts[2];
|
||
|
verts[3].m_Position -= vPowerPerp * 2;
|
||
|
|
||
|
pSurface->DrawSetColor( 255, 255, 255, 255 );
|
||
|
pSurface->DrawTexturedPolygon( 4, verts );
|
||
|
|
||
|
|
||
|
// Draw the power slider.
|
||
|
pSurface->DrawSetTexture( -1 );
|
||
|
|
||
|
|
||
|
vgui::Vertex_t line[2];
|
||
|
line[0].Init( vStart2D + vDir * flFirePowerDistance - vPowerPerp );
|
||
|
line[1].Init( line[0].m_Position + vPowerPerp * 2 );
|
||
|
pSurface->DrawTexturedLine( line[0], line[1] );
|
||
|
|
||
|
|
||
|
|
||
|
// Draw a white outline.
|
||
|
pSurface->DrawSetColor( 255, 255, 255, 255 );
|
||
|
vgui::Vertex_t pts[4] =
|
||
|
{
|
||
|
vgui::Vertex_t( vStart2D - vStartPerp ),
|
||
|
vgui::Vertex_t( vStart2D + vStartPerp ),
|
||
|
vgui::Vertex_t( vEnd2D + vEndPerp ),
|
||
|
vgui::Vertex_t( vEnd2D - vEndPerp )
|
||
|
};
|
||
|
pSurface->DrawTexturedPolyLine( pts, 4 );
|
||
|
|
||
|
|
||
|
// Draw the zero line.
|
||
|
line[0].Init( vStart2D + vDir * flZero - vZeroPerp );
|
||
|
line[1].Init( line[0].m_Position + vZeroPerp * 2 );
|
||
|
pSurface->DrawTexturedLine( line[0], line[1] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CMortarMinimapPanel::OnMousePressed( vgui::MouseCode code )
|
||
|
{
|
||
|
BaseClass::OnMousePressed( code );
|
||
|
|
||
|
if (code != vgui::MOUSE_LEFT)
|
||
|
return;
|
||
|
|
||
|
C_BaseEntity *pMortar = GetMortar();
|
||
|
IUsesMortarPanel *pMortarInterface = dynamic_cast< IUsesMortarPanel* >( pMortar );
|
||
|
if ( !pMortar || !pMortarInterface )
|
||
|
return;
|
||
|
|
||
|
float flClientMortarYaw, flPower, flFiringPower, flFiringAccuracy;
|
||
|
int iFiringState;
|
||
|
bool bAllowedToFire;
|
||
|
pMortarInterface->GetMortarData( &flClientMortarYaw, &bAllowedToFire, &flPower, &flFiringPower, &flFiringAccuracy, &iFiringState );
|
||
|
|
||
|
// See if they clicked the "fire" button.
|
||
|
float x, y;
|
||
|
if ( WorldToMinimap( MINIMAP_ALWAYS_ACCEPT, pMortar->GetAbsOrigin(), x, y ) &&
|
||
|
(Vector2D( x, y ) - Vector2D( m_LastX, m_LastY )).Length() <= 8 )
|
||
|
{
|
||
|
if ( bAllowedToFire )
|
||
|
{
|
||
|
// Treat it like they clicked the fire button.
|
||
|
m_bFireButtonDown = true;
|
||
|
pMortarInterface->ClickFire();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pMortarInterface->ForceClientYawCountdown( 5000000 );
|
||
|
}
|
||
|
|
||
|
m_bMouseDown = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CMortarMinimapPanel::OnCursorMoved( int x, int y )
|
||
|
{
|
||
|
m_LastX = x;
|
||
|
m_LastY = y;
|
||
|
|
||
|
if ( !m_bMouseDown || m_bFireButtonDown )
|
||
|
return;
|
||
|
|
||
|
C_BaseEntity *pMortar = GetMortar();
|
||
|
IUsesMortarPanel *pMortarInterface = dynamic_cast< IUsesMortarPanel* >( pMortar );
|
||
|
if ( !pMortar || !pMortarInterface )
|
||
|
return;
|
||
|
|
||
|
float flClientMortarYaw, flPower, flFiringPower, flFiringAccuracy;
|
||
|
int iFiringState;
|
||
|
bool bAllowedToFire;
|
||
|
pMortarInterface->GetMortarData( &flClientMortarYaw, &bAllowedToFire, &flPower, &flFiringPower, &flFiringAccuracy, &iFiringState );
|
||
|
|
||
|
float mortarX, mortarY;
|
||
|
if ( WorldToMinimap( MINIMAP_ALWAYS_ACCEPT, pMortar->GetAbsOrigin(), mortarX, mortarY ) )
|
||
|
{
|
||
|
float flAngle = atan2( x - mortarX, mortarY - y );
|
||
|
flClientMortarYaw = -anglemod( flAngle * 180.0f / M_PI + pMortar->GetAbsAngles()[YAW] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CMortarMinimapPanel::OnMouseReleased( vgui::MouseCode code )
|
||
|
{
|
||
|
BaseClass::OnMouseReleased( code );
|
||
|
|
||
|
if ( code != vgui::MOUSE_LEFT )
|
||
|
return;
|
||
|
|
||
|
m_bMouseDown = false;
|
||
|
|
||
|
if ( m_bFireButtonDown )
|
||
|
{
|
||
|
m_bFireButtonDown = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
C_BaseEntity *pMortar = GetMortar();
|
||
|
IUsesMortarPanel *pMortarInterface = dynamic_cast< IUsesMortarPanel* >( pMortar );
|
||
|
if ( pMortar && pMortarInterface )
|
||
|
{
|
||
|
pMortarInterface->SendYawCommand();
|
||
|
pMortarInterface->ForceClientYawCountdown( CLIENT_MORTAR_YAW_COUNTDOWN );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Control screen
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CVehicleMortarControlPanel : public CObjectControlPanel
|
||
|
{
|
||
|
DECLARE_CLASS( CVehicleMortarControlPanel, CObjectControlPanel );
|
||
|
|
||
|
public:
|
||
|
|
||
|
CVehicleMortarControlPanel( vgui::Panel *parent, const char *panelName );
|
||
|
virtual ~CVehicleMortarControlPanel();
|
||
|
|
||
|
virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
|
||
|
virtual void OnCommand( const char *command );
|
||
|
C_VehicleMortar* GetMortar() const;
|
||
|
|
||
|
|
||
|
protected:
|
||
|
|
||
|
virtual vgui::Panel* TickCurrentPanel();
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
void OnTickMainPanel();
|
||
|
void OnTickDeployPanel( float flDeployTime );
|
||
|
void OnTickGunnerPanel();
|
||
|
|
||
|
void GetInRam( void );
|
||
|
|
||
|
void StartDeploying();
|
||
|
void StopDismantling();
|
||
|
bool IsDeploying() const;
|
||
|
bool IsUndeploying() const;
|
||
|
bool IsDeployed() const;
|
||
|
|
||
|
private:
|
||
|
vgui::Label *m_pDriverLabel;
|
||
|
vgui::Label *m_pPassengerLabel;
|
||
|
vgui::Button *m_pOccupyButton;
|
||
|
vgui::Button *m_pCancelDeployButton;
|
||
|
|
||
|
vgui::Label *m_pDeployMessageLabel; // says "Deployed in" or "Undeployed in"
|
||
|
vgui::Label *m_pDeployTimeLabel; // says "N seconds"
|
||
|
|
||
|
vgui::EditablePanel *m_pDeployPanel;
|
||
|
vgui::EditablePanel *m_pGunnerPanel;
|
||
|
|
||
|
CMortarMinimapPanel *m_pMinimapPanel;
|
||
|
|
||
|
vgui::Label *m_pReloadingLabel;
|
||
|
};
|
||
|
|
||
|
|
||
|
DECLARE_VGUI_SCREEN_FACTORY( CVehicleMortarControlPanel, "vehicle_mortar_control_panel" );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Constructor:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CVehicleMortarControlPanel::CVehicleMortarControlPanel( vgui::Panel *parent, const char *panelName )
|
||
|
: BaseClass( parent, "CVehicleMortarControlPanel" )
|
||
|
{
|
||
|
m_pDeployPanel = new CCommandChainingPanel( this, "DeployPanel" );
|
||
|
m_pDeployPanel->SetZPos( -1 );
|
||
|
|
||
|
m_pGunnerPanel = new CCommandChainingPanel( this, "GunnerPanel" );
|
||
|
m_pGunnerPanel->SetZPos( -1 );
|
||
|
|
||
|
m_pMinimapPanel = new CMortarMinimapPanel( m_pGunnerPanel, "MinimapPanel" );
|
||
|
m_pMinimapPanel->SetZPos( 10 );
|
||
|
m_pMinimapPanel->Init( NULL );
|
||
|
}
|
||
|
|
||
|
|
||
|
CVehicleMortarControlPanel::~CVehicleMortarControlPanel()
|
||
|
{
|
||
|
delete m_pMinimapPanel;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Initialization
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CVehicleMortarControlPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
|
||
|
{
|
||
|
m_pDriverLabel = new vgui::Label( this, "DriverReadout", "" );
|
||
|
m_pPassengerLabel = new vgui::Label( this, "PassengerReadout", "" );
|
||
|
m_pOccupyButton = new vgui::Button( this, "OccupyButton", "Occupy" );
|
||
|
|
||
|
m_pDeployMessageLabel = new vgui::Label( m_pDeployPanel, "DeployMessage", "" );
|
||
|
m_pDeployTimeLabel = new vgui::Label( m_pDeployPanel, "DeployTime", "" );
|
||
|
m_pCancelDeployButton = new vgui::Button( m_pDeployPanel, "CancelDeployButton", "" );
|
||
|
|
||
|
m_pReloadingLabel = new vgui::Label( m_pGunnerPanel, "ReloadingLabel", "" );
|
||
|
|
||
|
// Make sure all named panels are created up above because BaseClass::Init initializes them
|
||
|
// all from their keyvalues.
|
||
|
if ( !BaseClass::Init( pKeyValues, pInitData ) )
|
||
|
return false;
|
||
|
|
||
|
// Init subpanels.
|
||
|
int x, y, w, h;
|
||
|
GetBounds( x, y, w, h );
|
||
|
|
||
|
m_pMinimapPanel->LevelInit( engine->GetLevelName() );
|
||
|
m_pMinimapPanel->SetVisible( true );
|
||
|
m_pMinimapPanel->InitMortarMinimap( GetMortar() );
|
||
|
|
||
|
m_pDeployPanel->SetBounds( x, y, w, h );
|
||
|
m_pDeployPanel->SetVisible( false );
|
||
|
|
||
|
m_pGunnerPanel->SetBounds( x, y, w, h );
|
||
|
m_pGunnerPanel->SetVisible( false );
|
||
|
|
||
|
m_pReloadingLabel->SetVisible( false );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Frame-based update
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
vgui::Panel* CVehicleMortarControlPanel::TickCurrentPanel()
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( !pMortar )
|
||
|
return BaseClass::TickCurrentPanel();
|
||
|
|
||
|
if ( IsUndeploying() )
|
||
|
{
|
||
|
OnTickDeployPanel( pMortar->GetDeployFinishTime() );
|
||
|
return m_pDeployPanel;
|
||
|
}
|
||
|
else if ( IsDeploying() )
|
||
|
{
|
||
|
OnTickDeployPanel( pMortar->GetDeployFinishTime() );
|
||
|
return m_pDeployPanel;
|
||
|
}
|
||
|
else if ( IsDeployed() )
|
||
|
{
|
||
|
OnTickGunnerPanel();
|
||
|
return m_pGunnerPanel;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
OnTickMainPanel();
|
||
|
return BaseClass::TickCurrentPanel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
C_VehicleMortar* CVehicleMortarControlPanel::GetMortar() const
|
||
|
{
|
||
|
return dynamic_cast< C_VehicleMortar* >( GetOwningObject() );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CVehicleMortarControlPanel::OnTickMainPanel()
|
||
|
{
|
||
|
C_BaseObject *pObj = GetOwningObject();
|
||
|
if (!pObj)
|
||
|
return;
|
||
|
|
||
|
ShowOwnerLabel( true );
|
||
|
ShowHealthLabel( true );
|
||
|
|
||
|
Assert( dynamic_cast<C_VehicleMortar*>(pObj) );
|
||
|
C_VehicleMortar *pRam = static_cast<C_VehicleMortar*>(pObj);
|
||
|
|
||
|
char buf[256];
|
||
|
// Update the currently manned player label
|
||
|
if ( pRam->GetDriverPlayer() )
|
||
|
{
|
||
|
Q_snprintf( buf, sizeof( buf ), "Driven by %s", pRam->GetDriverPlayer()->GetPlayerName() );
|
||
|
m_pDriverLabel->SetText( buf );
|
||
|
m_pDriverLabel->SetVisible( true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pDriverLabel->SetVisible( false );
|
||
|
}
|
||
|
|
||
|
int nPassengerCount = pRam->GetPassengerCount();
|
||
|
int nMaxPassengerCount = pRam->GetMaxPassengerCount();
|
||
|
|
||
|
Q_snprintf( buf, sizeof( buf ), "Passengers %d/%d", nPassengerCount >= 1 ? nPassengerCount - 1 : 0, nMaxPassengerCount - 1 );
|
||
|
m_pPassengerLabel->SetText( buf );
|
||
|
|
||
|
// Update the get in button
|
||
|
if ( pRam->IsPlayerInVehicle( C_BaseTFPlayer::GetLocalPlayer() ) )
|
||
|
{
|
||
|
m_pOccupyButton->SetEnabled( false );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( pRam->GetOwner() == C_BaseTFPlayer::GetLocalPlayer() )
|
||
|
{
|
||
|
if (nPassengerCount == nMaxPassengerCount)
|
||
|
{
|
||
|
// Owners can boot other players to get in
|
||
|
C_BaseTFPlayer *pPlayer = static_cast<C_BaseTFPlayer*>(pRam->GetPassenger( VEHICLE_ROLE_DRIVER ));
|
||
|
Q_snprintf( buf, sizeof( buf ), "Get In (Ejecting %s)", pPlayer->GetPlayerName() );
|
||
|
m_pDriverLabel->SetText( buf );
|
||
|
m_pOccupyButton->SetEnabled( true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pOccupyButton->SetText( "Get In" );
|
||
|
m_pOccupyButton->SetEnabled( true );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pOccupyButton->SetText( "Get In" );
|
||
|
m_pOccupyButton->SetEnabled( pRam->GetPassengerCount() < pRam->GetMaxPassengerCount() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CVehicleMortarControlPanel::OnTickDeployPanel( float flDeployTime )
|
||
|
{
|
||
|
ShowDismantleButton( false );
|
||
|
|
||
|
// Update the countdown.
|
||
|
int nSec = (int)(flDeployTime - gpGlobals->curtime + 0.5f);
|
||
|
if (nSec < 0)
|
||
|
nSec = 0;
|
||
|
|
||
|
char buf[256];
|
||
|
int nLen = Q_snprintf( buf, sizeof( buf ), "%d second", nSec );
|
||
|
if (nSec != 1)
|
||
|
{
|
||
|
buf[nLen] = 's';
|
||
|
++nLen;
|
||
|
buf[nLen] = 0;
|
||
|
}
|
||
|
|
||
|
m_pDeployTimeLabel->SetText( buf );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CVehicleMortarControlPanel::OnTickGunnerPanel()
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( !pMortar )
|
||
|
return;
|
||
|
|
||
|
ShowOwnerLabel( false );
|
||
|
ShowHealthLabel( false );
|
||
|
|
||
|
m_pMinimapPanel->Repaint();
|
||
|
|
||
|
// If it's reloading, tell the player
|
||
|
m_pReloadingLabel->SetVisible( pMortar->m_bMortarReloading );
|
||
|
if ( pMortar->m_bMortarReloading )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float flAccuracySpeed = (1.0 / MORTAR_CHARGE_ACCURACY_RATE);
|
||
|
|
||
|
// Handle power charging
|
||
|
switch( pMortar->m_iFiringState )
|
||
|
{
|
||
|
case MORTAR_IDLE:
|
||
|
pMortar->m_flPower = 0;
|
||
|
break;
|
||
|
|
||
|
case MORTAR_CHARGING_POWER:
|
||
|
pMortar->m_flPower = MIN( pMortar->m_flPower + ( (1.0 / MORTAR_CHARGE_POWER_RATE) * gpGlobals->frametime), 1 );
|
||
|
pMortar->m_flFiringPower = 0;
|
||
|
pMortar->m_flFiringAccuracy = 0;
|
||
|
if ( pMortar->m_flPower >= 1.0 )
|
||
|
{
|
||
|
// Hit Max, start going down
|
||
|
pMortar->m_flFiringPower = pMortar->m_flPower;
|
||
|
pMortar->m_iFiringState = MORTAR_CHARGING_ACCURACY;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MORTAR_CHARGING_ACCURACY:
|
||
|
// Calculate accuracy speed
|
||
|
if ( pMortar->m_flFiringPower > 0.5 )
|
||
|
{
|
||
|
// Shots over halfway suffer an increased speed to the accuracy power, making accurate shots harder
|
||
|
float flAdjustedPower = (pMortar->m_flFiringPower - 0.5) * 3.0;
|
||
|
flAccuracySpeed += (pMortar->m_flFiringPower * flAdjustedPower);
|
||
|
}
|
||
|
|
||
|
pMortar->m_flPower = MAX( pMortar->m_flPower - ( flAccuracySpeed * gpGlobals->frametime), -0.25f);
|
||
|
if ( pMortar->m_flPower <= -0.25 )
|
||
|
{
|
||
|
// Hit Min, fire mortar
|
||
|
pMortar->m_flFiringAccuracy = pMortar->m_flPower;
|
||
|
pMortar->m_iFiringState = MORTAR_IDLE;
|
||
|
|
||
|
pMortar->FireMortar();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Handle clicking on the Occupy button
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CVehicleMortarControlPanel::GetInRam( void )
|
||
|
{
|
||
|
SendToServerObject( "toggle_use" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Starts/stops deploying
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
bool CVehicleMortarControlPanel::IsDeploying() const
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( !pMortar )
|
||
|
return false;
|
||
|
|
||
|
return !IsDeployed() && pMortar->GetDeployFinishTime() > 0.0f;
|
||
|
}
|
||
|
|
||
|
bool CVehicleMortarControlPanel::IsUndeploying() const
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( !pMortar )
|
||
|
return false;
|
||
|
|
||
|
return IsDeployed() && pMortar->GetDeployFinishTime() > 0.0f;
|
||
|
}
|
||
|
|
||
|
bool CVehicleMortarControlPanel::IsDeployed() const
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( pMortar )
|
||
|
return pMortar->GetVehicleModeDeploy() == VEHICLE_MODE_DEPLOYED;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Button click handlers
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CVehicleMortarControlPanel::OnCommand( const char *command )
|
||
|
{
|
||
|
C_VehicleMortar *pMortar = GetMortar();
|
||
|
if ( !pMortar )
|
||
|
return;
|
||
|
|
||
|
if (!Q_strnicmp(command, "Occupy", 7))
|
||
|
{
|
||
|
GetInRam();
|
||
|
return;
|
||
|
}
|
||
|
else if ( !Q_stricmp( command, "Deploy" ) )
|
||
|
{
|
||
|
m_pDeployMessageLabel->SetText( "Deployed in" );
|
||
|
m_pCancelDeployButton->SetVisible( true );
|
||
|
|
||
|
SendToServerObject( "Deploy" ); // Tell the server.
|
||
|
}
|
||
|
else if ( !Q_stricmp( command, "CancelDeploy" ) )
|
||
|
{
|
||
|
SendToServerObject( command );
|
||
|
}
|
||
|
else if ( !Q_stricmp( command, "Undeploy" ) )
|
||
|
{
|
||
|
m_pDeployMessageLabel->SetText( "Undeployed in" );
|
||
|
m_pCancelDeployButton->SetVisible( false );
|
||
|
|
||
|
SendToServerObject( "Undeploy" ); // Tell the server.
|
||
|
}
|
||
|
else if ( !Q_stricmp( command, "FireMortar" ) )
|
||
|
{
|
||
|
pMortar->ClickFire();
|
||
|
}
|
||
|
|
||
|
BaseClass::OnCommand(command);
|
||
|
}
|
||
|
|