//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "c_basefourwheelvehicle.h" #include "tf_movedata.h" #include "ObjectControlPanel.h" #include #include #include "vehicle_mortar_shared.h" #include "vgui_rotation_slider.h" #include #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(pObj) ); C_VehicleMortar *pRam = static_cast(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(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); }