diff options
Diffstat (limited to 'game/client/game_controls')
37 files changed, 10863 insertions, 0 deletions
diff --git a/game/client/game_controls/ClientScoreBoardDialog.cpp b/game/client/game_controls/ClientScoreBoardDialog.cpp new file mode 100644 index 0000000..29865b0 --- /dev/null +++ b/game/client/game_controls/ClientScoreBoardDialog.cpp @@ -0,0 +1,580 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "cbase.h" +#include <stdio.h> + +#include <cdll_client_int.h> +#include <cdll_util.h> +#include <globalvars_base.h> +#include <igameresources.h> +#include "IGameUIFuncs.h" // for key bindings +#include "inputsystem/iinputsystem.h" +#include "clientscoreboarddialog.h" +#include <voice_status.h> + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vstdlib/IKeyValuesSystem.h> + +#include <KeyValues.h> +#include <vgui_controls/ImageList.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/SectionedListPanel.h> + +#include <game/client/iviewport.h> +#include <igameresources.h> + +#include "vgui_avatarimage.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +bool AvatarIndexLessFunc( const int &lhs, const int &rhs ) +{ + return lhs < rhs; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClientScoreBoardDialog::CClientScoreBoardDialog(IViewPort *pViewPort) : EditablePanel( NULL, PANEL_SCOREBOARD ) +{ + m_iPlayerIndexSymbol = KeyValuesSystem()->GetSymbolForString("playerIndex"); + m_nCloseKey = BUTTON_CODE_INVALID; + + //memset(s_VoiceImage, 0x0, sizeof( s_VoiceImage )); + TrackerImage = 0; + m_pViewPort = pViewPort; + + // initialize dialog + SetProportional(true); + SetKeyBoardInputEnabled(false); + SetMouseInputEnabled(false); + + // set the scheme before any child control is created + SetScheme("ClientScheme"); + + m_pPlayerList = new SectionedListPanel(this, "PlayerList"); + m_pPlayerList->SetVerticalScrollbar(false); + + LoadControlSettings("Resource/UI/ScoreBoard.res"); + m_iDesiredHeight = GetTall(); + m_pPlayerList->SetVisible( false ); // hide this until we load the images in applyschemesettings + + m_HLTVSpectators = 0; + m_ReplaySpectators = 0; + + // update scoreboard instantly if on of these events occure + ListenForGameEvent( "hltv_status" ); + ListenForGameEvent( "server_spawn" ); + + m_pImageList = NULL; + + m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) ); + m_mapAvatarsToImageList.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClientScoreBoardDialog::~CClientScoreBoardDialog() +{ + if ( NULL != m_pImageList ) + { + delete m_pImageList; + m_pImageList = NULL; + } +} + +//----------------------------------------------------------------------------- +// Call every frame +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::OnThink() +{ + BaseClass::OnThink(); + + // NOTE: this is necessary because of the way input works. + // If a key down message is sent to vgui, then it will get the key up message + // Sometimes the scoreboard is activated by other vgui menus, + // sometimes by console commands. In the case where it's activated by + // other vgui menus, we lose the key up message because this panel + // doesn't accept keyboard input. It *can't* accept keyboard input + // because another feature of the dialog is that if it's triggered + // from within the game, you should be able to still run around while + // the scoreboard is up. That feature is impossible if this panel accepts input. + // because if a vgui panel is up that accepts input, it prevents the engine from + // receiving that input. So, I'm stuck with a polling solution. + // + // Close key is set to non-invalid when something other than a keybind + // brings the scoreboard up, and it's set to invalid as soon as the + // dialog becomes hidden. + if ( m_nCloseKey != BUTTON_CODE_INVALID ) + { + if ( !g_pInputSystem->IsButtonDown( m_nCloseKey ) ) + { + m_nCloseKey = BUTTON_CODE_INVALID; + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, false ); + GetClientVoiceMgr()->StopSquelchMode(); + } + } +} + +//----------------------------------------------------------------------------- +// Called by vgui panels that activate the client scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::OnPollHideCode( int code ) +{ + m_nCloseKey = (ButtonCode_t)code; +} + +//----------------------------------------------------------------------------- +// Purpose: clears everything in the scoreboard and all it's state +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::Reset() +{ + // clear + m_pPlayerList->DeleteAllItems(); + m_pPlayerList->RemoveAllSections(); + + m_iSectionId = 0; + m_fNextUpdateTime = 0; + // add all the sections + InitScoreboardSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds all the team sections to the scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::InitScoreboardSections() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: sets up screen +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + if ( m_pImageList ) + delete m_pImageList; + m_pImageList = new ImageList( false ); + + m_mapAvatarsToImageList.RemoveAll(); + + PostApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Does dialog-specific customization after applying scheme settings. +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::PostApplySchemeSettings( vgui::IScheme *pScheme ) +{ + // resize the images to our resolution + for (int i = 0; i < m_pImageList->GetImageCount(); i++ ) + { + int wide, tall; + m_pImageList->GetImage(i)->GetSize(wide, tall); + m_pImageList->GetImage(i)->SetSize(scheme()->GetProportionalScaledValueEx( GetScheme(),wide), scheme()->GetProportionalScaledValueEx( GetScheme(),tall)); + } + + m_pPlayerList->SetImageList( m_pImageList, false ); + m_pPlayerList->SetVisible( true ); + + // light up scoreboard a bit + SetBgColor( Color( 0,0,0,0) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::ShowPanel(bool bShow) +{ + // Catch the case where we call ShowPanel before ApplySchemeSettings, eg when + // going from windowed <-> fullscreen + if ( m_pImageList == NULL ) + { + InvalidateLayout( true, true ); + } + + if ( !bShow ) + { + m_nCloseKey = BUTTON_CODE_INVALID; + } + + if ( BaseClass::IsVisible() == bShow ) + return; + + if ( bShow ) + { + Reset(); + Update(); + SetVisible( true ); + MoveToFront(); + } + else + { + BaseClass::SetVisible( false ); + SetMouseInputEnabled( false ); + SetKeyBoardInputEnabled( false ); + } +} + +void CClientScoreBoardDialog::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type, "hltv_status") == 0 ) + { + // spectators = clients - proxies + m_HLTVSpectators = event->GetInt( "clients" ); + m_HLTVSpectators -= event->GetInt( "proxies" ); + } + else if ( Q_strcmp(type, "server_spawn") == 0 ) + { + // We'll post the message ourselves instead of using SetControlString() + // so we don't try to translate the hostname. + const char *hostname = event->GetString( "hostname" ); + Panel *control = FindChildByName( "ServerName" ); + if ( control ) + { + PostMessage( control, new KeyValues( "SetText", "text", hostname ) ); + control->MoveToFront(); + } + } + + if( IsVisible() ) + Update(); + +} + +bool CClientScoreBoardDialog::NeedsUpdate( void ) +{ + return (m_fNextUpdateTime < gpGlobals->curtime); +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate the internal scoreboard data +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::Update( void ) +{ + // Set the title + + // Reset(); + m_pPlayerList->DeleteAllItems(); + + FillScoreBoard(); + + // grow the scoreboard to fit all the players + int wide, tall; + m_pPlayerList->GetContentSize(wide, tall); + tall += GetAdditionalHeight(); + wide = GetWide(); + if (m_iDesiredHeight < tall) + { + SetSize(wide, tall); + m_pPlayerList->SetSize(wide, tall); + } + else + { + SetSize(wide, m_iDesiredHeight); + m_pPlayerList->SetSize(wide, m_iDesiredHeight); + } + + MoveToCenterOfScreen(); + + // update every second + m_fNextUpdateTime = gpGlobals->curtime + 1.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Sort all the teams +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdateTeamInfo() +{ +// TODO: work out a sorting algorithm for team display for TF2 +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdatePlayerInfo() +{ + m_iSectionId = 0; // 0'th row is a header + int selectedRow = -1; + + // walk all the players and make sure they're in the scoreboard + for ( int i = 1; i <= gpGlobals->maxClients; ++i ) + { + IGameResources *gr = GameResources(); + + if ( gr && gr->IsConnected( i ) ) + { + // add the player to the list + KeyValues *playerData = new KeyValues("data"); + GetPlayerScoreInfo( i, playerData ); + UpdatePlayerAvatar( i, playerData ); + + const char *oldName = playerData->GetString("name",""); + char newName[MAX_PLAYER_NAME_LENGTH]; + + UTIL_MakeSafeName( oldName, newName, MAX_PLAYER_NAME_LENGTH ); + + playerData->SetString("name", newName); + + int itemID = FindItemIDForPlayerIndex( i ); + int sectionID = gr->GetTeam( i ); + + if ( gr->IsLocalPlayer( i ) ) + { + selectedRow = itemID; + } + if (itemID == -1) + { + // add a new row + itemID = m_pPlayerList->AddItem( sectionID, playerData ); + } + else + { + // modify the current row + m_pPlayerList->ModifyItem( itemID, sectionID, playerData ); + } + + // set the row color based on the players team + m_pPlayerList->SetItemFgColor( itemID, gr->GetTeamColor( sectionID ) ); + + playerData->deleteThis(); + } + else + { + // remove the player + int itemID = FindItemIDForPlayerIndex( i ); + if (itemID != -1) + { + m_pPlayerList->RemoveItem(itemID); + } + } + } + + if ( selectedRow != -1 ) + { + m_pPlayerList->SetSelectedItem(selectedRow); + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds the top header of the scoreboars +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::AddHeader() +{ + // add the top header + m_pPlayerList->AddSection(m_iSectionId, ""); + m_pPlayerList->SetSectionAlwaysVisible(m_iSectionId); + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#PlayerName", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "#PlayerScore", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "#PlayerDeath", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "#PlayerPing", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new section to the scoreboard (i.e the team header) +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::AddSection(int teamType, int teamNumber) +{ + if ( teamType == TYPE_TEAM ) + { + IGameResources *gr = GameResources(); + + if ( !gr ) + return; + + // setup the team name + wchar_t *teamName = g_pVGuiLocalize->Find( gr->GetTeamName(teamNumber) ); + wchar_t name[64]; + wchar_t string1[1024]; + + if (!teamName) + { + g_pVGuiLocalize->ConvertANSIToUnicode(gr->GetTeamName(teamNumber), name, sizeof(name)); + teamName = name; + } + + g_pVGuiLocalize->ConstructString_safe( string1, g_pVGuiLocalize->Find("#Player"), 2, teamName ); + + m_pPlayerList->AddSection(m_iSectionId, "", StaticPlayerSortFunc); + + // Avatars are always displayed at 32x32 regardless of resolution + if ( ShowAvatars() ) + { + m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth ); + } + + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", string1, 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) ); + } + else if ( teamType == TYPE_SPECTATORS ) + { + m_pPlayerList->AddSection(m_iSectionId, ""); + + // Avatars are always displayed at 32x32 regardless of resolution + if ( ShowAvatars() ) + { + m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth ); + } + m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#Spectators", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth ); + m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used for sorting players +//----------------------------------------------------------------------------- +bool CClientScoreBoardDialog::StaticPlayerSortFunc(vgui::SectionedListPanel *list, int itemID1, int itemID2) +{ + KeyValues *it1 = list->GetItemData(itemID1); + KeyValues *it2 = list->GetItemData(itemID2); + Assert(it1 && it2); + + // first compare frags + int v1 = it1->GetInt("frags"); + int v2 = it2->GetInt("frags"); + if (v1 > v2) + return true; + else if (v1 < v2) + return false; + + // next compare deaths + v1 = it1->GetInt("deaths"); + v2 = it2->GetInt("deaths"); + if (v1 > v2) + return false; + else if (v1 < v2) + return true; + + // the same, so compare itemID's (as a sentinel value to get deterministic sorts) + return itemID1 < itemID2; +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new row to the scoreboard, from the playerinfo structure +//----------------------------------------------------------------------------- +bool CClientScoreBoardDialog::GetPlayerScoreInfo(int playerIndex, KeyValues *kv) +{ + IGameResources *gr = GameResources(); + + if (!gr ) + return false; + + kv->SetInt("deaths", gr->GetDeaths( playerIndex ) ); + kv->SetInt("frags", gr->GetFrags( playerIndex ) ); + kv->SetInt("ping", gr->GetPing( playerIndex ) ) ; + kv->SetString("name", gr->GetPlayerName( playerIndex ) ); + kv->SetInt("playerIndex", playerIndex); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::UpdatePlayerAvatar( int playerIndex, KeyValues *kv ) +{ + // Update their avatar + if ( kv && ShowAvatars() && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() ) + { + player_info_t pi; + if ( engine->GetPlayerInfo( playerIndex, &pi ) ) + { + if ( pi.friendsID ) + { + CSteamID steamIDForPlayer( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual ); + + // See if we already have that avatar in our list + int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer ); + int iImageIndex; + if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() ) + { + CAvatarImage *pImage = new CAvatarImage(); + pImage->SetAvatarSteamID( steamIDForPlayer ); + pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling + iImageIndex = m_pImageList->AddImage( pImage ); + + m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex ); + } + else + { + iImageIndex = m_mapAvatarsToImageList[ iMapIndex ]; + } + + kv->SetInt( "avatar", iImageIndex ); + + CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex ); + pAvIm->UpdateFriendStatus(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: reload the player list on the scoreboard +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::FillScoreBoard() +{ + // update totals information + UpdateTeamInfo(); + + // update player info + UpdatePlayerInfo(); +} + +//----------------------------------------------------------------------------- +// Purpose: searches for the player in the scoreboard +//----------------------------------------------------------------------------- +int CClientScoreBoardDialog::FindItemIDForPlayerIndex(int playerIndex) +{ + for (int i = 0; i <= m_pPlayerList->GetHighestItemID(); i++) + { + if (m_pPlayerList->IsItemIDValid(i)) + { + KeyValues *kv = m_pPlayerList->GetItemData(i); + kv = kv->FindKey(m_iPlayerIndexSymbol); + if (kv && kv->GetInt() == playerIndex) + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::MoveLabelToFront(const char *textEntryName) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->MoveToFront(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Center the dialog on the screen. (vgui has this method on +// Frame, but we're an EditablePanel, need to roll our own.) +//----------------------------------------------------------------------------- +void CClientScoreBoardDialog::MoveToCenterOfScreen() +{ + int wx, wy, ww, wt; + surface()->GetWorkspaceBounds(wx, wy, ww, wt); + SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2); +} diff --git a/game/client/game_controls/IconPanel.cpp b/game/client/game_controls/IconPanel.cpp new file mode 100644 index 0000000..3688dae --- /dev/null +++ b/game/client/game_controls/IconPanel.cpp @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "IconPanel.h" +#include "KeyValues.h" + +DECLARE_BUILD_FACTORY( CIconPanel ); + +CIconPanel::CIconPanel( vgui::Panel *parent, const char *name ) : vgui::Panel( parent, name ) +{ + m_szIcon[0] = '\0'; + m_icon = NULL; + m_bScaleImage = false; +} + +void CIconPanel::ApplySettings( KeyValues *inResourceData ) +{ + Q_strncpy( m_szIcon, inResourceData->GetString( "icon", "" ), sizeof( m_szIcon ) ); + + m_icon = gHUD.GetIcon( m_szIcon ); + + m_bScaleImage = inResourceData->GetInt("scaleImage", 0); + + BaseClass::ApplySettings( inResourceData ); +} + +void CIconPanel::SetIcon( const char *szIcon ) +{ + Q_strncpy( m_szIcon, szIcon, sizeof(m_szIcon) ); + + m_icon = gHUD.GetIcon( m_szIcon ); +} + +void CIconPanel::Paint() +{ + BaseClass::Paint(); + + if ( m_icon ) + { + int x, y, w, h; + GetBounds( x, y, w, h ); + + if ( m_bScaleImage ) + { + m_icon->DrawSelf( 0, 0, w, h, m_IconColor ); + } + else + { + m_icon->DrawSelf( 0, 0, m_IconColor ); + } + } +} + +void CIconPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + if ( m_szIcon[0] != '\0' ) + { + m_icon = gHUD.GetIcon( m_szIcon ); + } + + SetFgColor( pScheme->GetColor( "FgColor", Color( 255, 255, 255, 255 ) ) ); +} diff --git a/game/client/game_controls/IconPanel.h b/game/client/game_controls/IconPanel.h new file mode 100644 index 0000000..f9dcb24 --- /dev/null +++ b/game/client/game_controls/IconPanel.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICONPANEL_H +#define ICONPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> + +using namespace vgui; + +class CIconPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CIconPanel, vgui::Panel ); + +public: + CIconPanel( vgui::Panel *parent, const char *name ); + + void Init( void ); + virtual void Paint(); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void SetIcon( const char *szIcon ); + void SetIconColor( Color cColor ) { m_IconColor = cColor; } + +private: + CHudTexture *m_icon; + char m_szIcon[128]; + + bool m_bScaleImage; + + CPanelAnimationVar( Color, m_IconColor, "iconColor", "255 255 255 255" ); +}; + +#endif //ICONPANEL_H
\ No newline at end of file diff --git a/game/client/game_controls/MapOverview.cpp b/game/client/game_controls/MapOverview.cpp new file mode 100644 index 0000000..0deb535 --- /dev/null +++ b/game/client/game_controls/MapOverview.cpp @@ -0,0 +1,1340 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: MapOverview.cpp: implementation of the CMapOverview class. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "mapoverview.h" +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <filesystem.h> +#include <KeyValues.h> +#include <convar.h> +#include "mathlib/mathlib.h" +#include <game/client/iviewport.h> +#include <igameresources.h> +#include "gamevars_shared.h" +#include "spectatorgui.h" +#include "c_playerresource.h" +#include "view.h" + +#include "clientmode.h" +#include <vgui_controls/AnimationController.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar overview_health( "overview_health", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's health in map overview.\n" ); +ConVar overview_names ( "overview_names", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's names in map overview.\n" ); +ConVar overview_tracks( "overview_tracks", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's tracks in map overview.\n" ); +ConVar overview_locked( "overview_locked", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Locks map angle, doesn't follow view angle.\n" ); +ConVar overview_alpha( "overview_alpha", "1.0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Overview map translucency.\n" ); + +IMapOverviewPanel *g_pMapOverview = NULL; // we assume only one overview is created + +static int AdjustValue( int curValue, int targetValue, int amount ) +{ + if ( curValue > targetValue ) + { + curValue -= amount; + + if ( curValue < targetValue ) + curValue = targetValue; + } + else if ( curValue < targetValue ) + { + curValue += amount; + + if ( curValue > targetValue ) + curValue = targetValue; + } + + return curValue; +} + +CON_COMMAND( overview_zoom, "Sets overview map zoom: <zoom> [<time>] [rel]" ) +{ + if ( !g_pMapOverview || args.ArgC() < 2 ) + return; + + float zoom = Q_atof( args[ 1 ] ); + + float time = 0; + + if ( args.ArgC() >= 3 ) + time = Q_atof( args[ 2 ] ); + + if ( args.ArgC() == 4 ) + zoom *= g_pMapOverview->GetZoom(); + + // We are going to store their zoom pick as the resultant overview size that it sees. This way, the value will remain + // correct even on a different map that has a different intrinsic zoom. + float desiredViewSize = 0.0f; + desiredViewSize = (zoom * OVERVIEW_MAP_SIZE * g_pMapOverview->GetFullZoom()) / g_pMapOverview->GetMapScale(); + g_pMapOverview->SetPlayerPreferredViewSize( desiredViewSize ); + + if( !g_pMapOverview->AllowConCommandsWhileAlive() ) + { + C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer(); + if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() ) + return;// Not allowed to execute commands while alive + else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM ) + return;// In the death cam spiral counts as alive + } + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( g_pMapOverview->GetAsPanel(), "zoom", zoom, 0.0, time, vgui::AnimationController::INTERPOLATOR_LINEAR ); +} + +CON_COMMAND( overview_mode, "Sets overview map mode off,small,large: <0|1|2>" ) +{ + if ( !g_pMapOverview ) + return; + + int mode; + + if ( args.ArgC() < 2 ) + { + // toggle modes + mode = g_pMapOverview->GetMode() + 1; + + if ( mode > CMapOverview::MAP_MODE_FULL ) + mode = CMapOverview::MAP_MODE_OFF; + } + else + { + // set specific mode + mode = Q_atoi( args[ 1 ] ); + } + + if( mode != CMapOverview::MAP_MODE_RADAR ) + g_pMapOverview->SetPlayerPreferredMode( mode ); + + if( !g_pMapOverview->AllowConCommandsWhileAlive() ) + { + C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer(); + if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() ) + return;// Not allowed to execute commands while alive + else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM ) + return;// In the death cam spiral counts as alive + } + + g_pMapOverview->SetMode( mode ); +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +using namespace vgui; + +CMapOverview::CMapOverview( const char *pElementName ) : BaseClass( NULL, pElementName ), CHudElement( pElementName ) +{ + SetParent( g_pClientMode->GetViewport()->GetVPanel() ); + + SetBounds( 0,0, 256, 256 ); + SetBgColor( Color( 0,0,0,100 ) ); + SetPaintBackgroundEnabled( true ); + ShowPanel( false ); + + // Make sure we actually have the font... + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + + m_hIconFont = pScheme->GetFont( "DefaultSmall" ); + + m_nMapTextureID = -1; + m_MapKeyValues = NULL; + + m_MapOrigin = Vector( 0, 0, 0 ); + m_fMapScale = 1.0f; + m_bFollowAngle = false; + SetMode( MAP_MODE_OFF ); + + m_fZoom = 3.0f; + m_MapCenter = Vector2D( 512, 512 ); + m_ViewOrigin = Vector2D( 512, 512 ); + m_fViewAngle = 0; + m_fTrailUpdateInterval = 1.0f; + + m_bShowNames = true; + m_bShowHealth = true; + m_bShowTrails = true; + + m_flChangeSpeed = 1000; + m_flIconSize = 64.0f; + + m_ObjectCounterID = 1; + + Reset(); + + Q_memset( m_Players, 0, sizeof(m_Players) ); + + InitTeamColorsAndIcons(); + + g_pMapOverview = this; // for cvars access etc +} + +void CMapOverview::Init( void ) +{ + // register for events as client listener + ListenForGameEvent( "game_newmap" ); + ListenForGameEvent( "round_start" ); + ListenForGameEvent( "player_connect_client" ); + ListenForGameEvent( "player_info" ); + ListenForGameEvent( "player_team" ); + ListenForGameEvent( "player_spawn" ); + ListenForGameEvent( "player_death" ); + ListenForGameEvent( "player_disconnect" ); +} + +void CMapOverview::InitTeamColorsAndIcons() +{ + Q_memset( m_TeamIcons, 0, sizeof(m_TeamIcons) ); + Q_memset( m_TeamColors, 0, sizeof(m_TeamColors) ); + Q_memset( m_ObjectIcons, 0, sizeof(m_ObjectIcons) ); + + m_TextureIDs.RemoveAll(); +} + +int CMapOverview::AddIconTexture(const char *filename) +{ + int index = m_TextureIDs.Find( filename ); + + if ( m_TextureIDs.IsValidIndex( index ) ) + { + // already known, return texture ID + return m_TextureIDs.Element(index); + } + + index = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( index , filename, true, false); + + m_TextureIDs.Insert( filename, index ); + + return index; +} + +void CMapOverview::ApplySchemeSettings(vgui::IScheme *scheme) +{ + BaseClass::ApplySchemeSettings( scheme ); + + SetBgColor( Color( 0,0,0,100 ) ); + SetPaintBackgroundEnabled( true ); +} + +CMapOverview::~CMapOverview() +{ + if ( m_MapKeyValues ) + m_MapKeyValues->deleteThis(); + + g_pMapOverview = NULL; + + //TODO release Textures ? clear lists +} + +void CMapOverview::UpdatePlayers() +{ + if ( !g_PR ) + return; + + // first disable all players health + for ( int i=0; i<MAX_PLAYERS; i++ ) + { + m_Players[i].health = 0; + m_Players[i].team = TEAM_SPECTATOR; + } + + for ( int i = 1; i<= gpGlobals->maxClients; i++) + { + // update from global player resources + if ( g_PR && g_PR->IsConnected(i) ) + { + MapPlayer_t *player = &m_Players[i-1]; + + player->health = g_PR->GetHealth( i ); + + if ( !g_PR->IsAlive( i ) ) + { + player->health = 0; + } + + if ( player->team != g_PR->GetTeam( i ) ) + { + player->team = g_PR->GetTeam( i ); + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + } + + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + // don't update if player is dormant + if ( pPlayer->IsDormant() ) + continue; + + // update position of active players in our PVS + Vector position = pPlayer->EyePosition(); + QAngle angles = pPlayer->EyeAngles(); + + SetPlayerPositions( i-1, position, angles ); + } +} + +void CMapOverview::UpdatePlayerTrails() +{ + if ( m_fNextTrailUpdate > m_fWorldTime ) + return; + + m_fNextTrailUpdate = m_fWorldTime + 1.0f; // update once a second + + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *p = &m_Players[i]; + + // no trails for spectators or dead players + if ( (p->team <= TEAM_SPECTATOR) || (p->health <= 0) ) + { + continue; + } + + // move old trail points + for ( int j=MAX_TRAIL_LENGTH-1; j>0; j--) + { + p->trail[j]=p->trail[j-1]; + } + + p->trail[0] = WorldToMap ( p->position ); + } +} + +void CMapOverview::UpdateFollowEntity() +{ + if ( m_nFollowEntity != 0 ) + { + C_BaseEntity *ent = ClientEntityList().GetEnt( m_nFollowEntity ); + + if ( ent ) + { + Vector position = MainViewOrigin(); // Use MainViewOrigin so SourceTV works in 3rd person + QAngle angle = ent->EyeAngles(); + + if ( m_nFollowEntity <= MAX_PLAYERS ) + { + SetPlayerPositions( m_nFollowEntity-1, position, angle ); + } + + SetCenter( WorldToMap(position) ); + SetAngle( angle[YAW] ); + } + } + else + { + SetCenter( Vector2D(OVERVIEW_MAP_SIZE/2,OVERVIEW_MAP_SIZE/2) ); + SetAngle( 0 ); + } +} + +void CMapOverview::Paint() +{ + UpdateSizeAndPosition(); + + UpdateFollowEntity(); + + UpdateObjects(); + + UpdatePlayers(); + + UpdatePlayerTrails(); + + DrawMapTexture(); + + DrawMapPlayerTrails(); + + DrawObjects(); + + DrawMapPlayers(); + + DrawCamera(); + + BaseClass::Paint(); +} + +bool CMapOverview::CanPlayerBeSeen(MapPlayer_t *player) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !localPlayer || !player ) + return false; + + // don't draw ourself + if ( localPlayer->GetUserID() == (player->userid) ) + return false; + + // Invalid guy. + if( player->position == Vector(0,0,0) ) + return false; + + // if local player is on spectator team, he can see everyone + if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR ) + return true; + + // we never track unassigned or real spectators + if ( player->team <= TEAM_SPECTATOR ) + return false; + + // if observer is an active player, check mp_forcecamera: + + if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE ) + return false; + + if ( mp_forcecamera.GetInt() == OBS_ALLOW_TEAM ) + { + // true if both players are on the same team + return (localPlayer->GetTeamNumber() == player->team ); + } + + // by default we can see all players + return true; +} + +/// allows mods to restrict health +/// Note: index is 0-based +bool CMapOverview::CanPlayerHealthBeSeen(MapPlayer_t *player) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !localPlayer ) + return false; + + // real spectators can see everything + if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR ) + return true; + + if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL ) + { + // if forcecamera is on, only show health for teammates + return ( localPlayer->GetTeamNumber() == player->team ); + } + + return true; +} + +// usually name rule is same as health rule +bool CMapOverview::CanPlayerNameBeSeen(MapPlayer_t *player) +{ + return CanPlayerHealthBeSeen( player ); +} + +void CMapOverview::SetPlayerPositions(int index, const Vector &position, const QAngle &angle) +{ + MapPlayer_t *p = &m_Players[index]; + + p->angle = angle; + p->position = position; +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides the buy menu +//----------------------------------------------------------------------------- +void CMapOverview::ShowPanel(bool bShow) +{ + SetVisible( bShow ); +} + +void CMapOverview::OnThink( void ) +{ + if ( NeedsUpdate() ) + { + Update(); + m_fNextUpdateTime = gpGlobals->curtime + 0.2f; // update 5 times a second + } +} + +bool CMapOverview::NeedsUpdate( void ) +{ + return m_fNextUpdateTime < gpGlobals->curtime; +} + +void CMapOverview::Update( void ) +{ + // update settings + m_bShowNames = overview_names.GetBool() && ( GetMode() != MAP_MODE_RADAR ); + m_bShowHealth = overview_health.GetBool() && ( GetMode() != MAP_MODE_RADAR ); + m_bFollowAngle = ( GetMode() != MAP_MODE_RADAR && !overview_locked.GetBool() ) || ( GetMode() == MAP_MODE_RADAR && !IsRadarLocked() ); + m_fTrailUpdateInterval = overview_tracks.GetInt() && ( GetMode() != MAP_MODE_RADAR ); + + m_fWorldTime = gpGlobals->curtime; + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer ) + return; + + int specmode = GetSpectatorMode(); + + if ( specmode == OBS_MODE_IN_EYE || specmode == OBS_MODE_CHASE ) + { + // follow target + SetFollowEntity( GetSpectatorTarget() ); + } + else + { + // follow ourself otherwise + SetFollowEntity( pPlayer->entindex() ); + } +} + +void CMapOverview::Reset( void ) +{ + m_fNextUpdateTime = 0; +} + +void CMapOverview::SetData(KeyValues *data) +{ + m_fZoom = data->GetFloat( "zoom", m_fZoom ); + m_nFollowEntity = data->GetInt( "entity", m_nFollowEntity ); +} + + +CMapOverview::MapPlayer_t* CMapOverview::GetPlayerByUserID( int userID ) +{ + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *player = &m_Players[i]; + + if ( player->userid == userID ) + return player; + } + + return NULL; +} + +bool CMapOverview::IsInPanel(Vector2D &pos) +{ + int x,y,w,t; + + GetBounds( x,y,w,t ); + + return ( pos.x >= 0 && pos.x < w && pos.y >= 0 && pos.y < t ); +} + +void CMapOverview::DrawMapTexture() +{ + // now draw a box around the outside of this panel + int x0, y0, x1, y1; + int wide, tall; + + GetSize(wide, tall); + x0 = 0; y0 = 0; x1 = wide - 2; y1 = tall - 2 ; + + if ( m_nMapTextureID < 0 ) + return; + + Vertex_t points[4] = + { + Vertex_t( MapToPanel ( Vector2D(0,0) ), Vector2D(0,0) ), + Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,0) ), Vector2D(1,0) ), + Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,OVERVIEW_MAP_SIZE-1) ), Vector2D(1,1) ), + Vertex_t( MapToPanel ( Vector2D(0,OVERVIEW_MAP_SIZE-1) ), Vector2D(0,1) ) + }; + + int alpha = 255.0f * overview_alpha.GetFloat(); clamp( alpha, 1, 255 ); + + surface()->DrawSetColor( 255,255,255, alpha ); + surface()->DrawSetTexture( m_nMapTextureID ); + surface()->DrawTexturedPolygon( 4, points ); +} + +void CMapOverview::DrawMapPlayerTrails() +{ + if ( m_fTrailUpdateInterval <= 0 ) + return; // turned off + + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *player = &m_Players[i]; + + if ( !CanPlayerBeSeen(player) ) + continue; + + player->trail[0] = WorldToMap ( player->position ); + + for ( int iTrail=0; iTrail<(MAX_TRAIL_LENGTH-1); iTrail++) + { + if ( player->trail[iTrail +1].x == 0 && player->trail[iTrail +1].y == 0 ) + break; + + Vector2D pos1 = MapToPanel( player->trail[iTrail] ); + Vector2D pos2 = MapToPanel( player->trail[iTrail +1] ); + + int intensity = 255 - float(255.0f * iTrail ) / MAX_TRAIL_LENGTH; + + Vector2D dist = pos1 - pos2; + + // don't draw too long lines, player probably teleported + if ( dist.LengthSqr() < (128*128) ) + { + surface()->DrawSetColor( player->color[0], player->color[1], player->color[2], intensity ); + surface()->DrawLine( pos1.x, pos1.y, pos2.x, pos2.y ); + } + } + } +} + +void CMapOverview::DrawObjects( ) +{ + surface()->DrawSetTextFont( m_hIconFont ); + + for (int i=0; i<m_Objects.Count(); i++) + { + MapObject_t *obj = &m_Objects[i]; + + const char *text = NULL; + + if ( Q_strlen(obj->name) > 0 ) + text = obj->name; + + float flAngle = obj->angle[YAW]; + + if ( obj->flags & MAP_OBJECT_ALIGN_TO_MAP && m_bRotateMap ) + { + if ( m_bRotateMap ) + flAngle = 90; + else + flAngle = 0; + } + + MapObject_t tempObj = *obj; + tempObj.angle[YAW] = flAngle; + tempObj.text = text; + tempObj.statusColor = obj->color; + + // draw icon + if ( !DrawIcon( &tempObj ) ) + continue; + } +} + +bool CMapOverview::DrawIcon( MapObject_t *obj ) +{ + int textureID = obj->icon; + Vector pos = obj->position; + float scale = obj->size; + float angle = obj->angle[YAW]; + const char *text = obj->text; + Color *textColor = &obj->color; + float status = obj->status; + Color *statusColor = &obj->statusColor; + + Vector offset; offset.z = 0; + + Vector2D pospanel = WorldToMap( pos ); + pospanel = MapToPanel( pospanel ); + + if ( !IsInPanel( pospanel ) ) + return false; // player is not within overview panel + + offset.x = -scale; offset.y = scale; + VectorYawRotate( offset, angle, offset ); + Vector2D pos1 = WorldToMap( pos + offset ); + + offset.x = scale; offset.y = scale; + VectorYawRotate( offset, angle, offset ); + Vector2D pos2 = WorldToMap( pos + offset ); + + offset.x = scale; offset.y = -scale; + VectorYawRotate( offset, angle, offset ); + Vector2D pos3 = WorldToMap( pos + offset ); + + offset.x = -scale; offset.y = -scale; + VectorYawRotate( offset, angle, offset ); + Vector2D pos4 = WorldToMap( pos + offset ); + + Vertex_t points[4] = + { + Vertex_t( MapToPanel ( pos1 ), Vector2D(0,0) ), + Vertex_t( MapToPanel ( pos2 ), Vector2D(1,0) ), + Vertex_t( MapToPanel ( pos3 ), Vector2D(1,1) ), + Vertex_t( MapToPanel ( pos4 ), Vector2D(0,1) ) + }; + + surface()->DrawSetColor( 255, 255, 255, 255 ); + surface()->DrawSetTexture( textureID ); + surface()->DrawTexturedPolygon( 4, points ); + + int d = GetPixelOffset( scale); + + pospanel.y += d + 4; + + if ( status >=0.0f && status <= 1.0f && statusColor ) + { + // health bar is 50x3 pixels + surface()->DrawSetColor( 0,0,0,255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 ); + + int length = (float)(d*2)*status; + surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 ); + + pospanel.y += 3; + } + + if ( text && textColor ) + { + wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ]; + + g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) ); + + int wide, tall; + surface()->GetTextSize( m_hIconFont, iconText, wide, tall ); + + int x = pospanel.x-(wide/2); + int y = pospanel.y; + + // draw black shadow text + surface()->DrawSetTextColor( 0, 0, 0, 255 ); + surface()->DrawSetTextPos( x+1, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + + // draw name in color + surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + } + + return true; +} + +int CMapOverview::GetPixelOffset( float height ) +{ + Vector2D pos2 = WorldToMap( Vector( height,0,0) ); + pos2 = MapToPanel( pos2 ); + + Vector2D pos3 = WorldToMap( Vector(0,0,0) ); + pos3 = MapToPanel( pos3 ); + + int a = pos2.y-pos3.y; + int b = pos2.x-pos3.x; + + return (int)sqrt((float)(a*a+b*b)); // number of panel pixels for "scale" units in world +} + +void CMapOverview::DrawMapPlayers() +{ + surface()->DrawSetTextFont( m_hIconFont ); + + Color colorGreen( 0, 255, 0, 255 ); // health bar color + + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *player = &m_Players[i]; + + if ( !CanPlayerBeSeen( player ) ) + continue; + + // don't draw dead players / spectators + if ( player->health <= 0 ) + continue; + + float status = -1; + const char *name = NULL; + + if ( m_bShowNames && CanPlayerNameBeSeen( player ) ) + name = player->name; + + if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) ) + status = player->health/100.0f; + + // convert from PlayerObject_t + MapObject_t tempObj; + memset( &tempObj, 0, sizeof(MapObject_t) ); + tempObj.icon = player->icon; + tempObj.position = player->position; + tempObj.size = m_flIconSize; + tempObj.angle = player->angle; + tempObj.text = name; + tempObj.color = player->color; + tempObj.status = status; + tempObj.statusColor = colorGreen; + + DrawIcon( &tempObj ); + } +} + +Vector2D CMapOverview::WorldToMap( const Vector &worldpos ) +{ + Vector2D offset( worldpos.x - m_MapOrigin.x, worldpos.y - m_MapOrigin.y); + + offset.x /= m_fMapScale; + offset.y /= -m_fMapScale; + + return offset; +} + +float CMapOverview::GetViewAngle( void ) +{ + float viewAngle = m_fViewAngle - 90.0f; + + if ( !m_bFollowAngle ) + { + // We don't use fViewAngle. We just show straight at all times. + if ( m_bRotateMap ) + viewAngle = 90.0f; + else + viewAngle = 0.0f; + } + + return viewAngle; +} + +Vector2D CMapOverview::MapToPanel( const Vector2D &mappos ) +{ + int pwidth, pheight; + Vector2D panelpos; + float viewAngle = GetViewAngle(); + + GetSize(pwidth, pheight); + + Vector offset; + offset.x = mappos.x - m_MapCenter.x; + offset.y = mappos.y - m_MapCenter.y; + offset.z = 0; + + VectorYawRotate( offset, viewAngle, offset ); + + // find the actual zoom from the animationvar m_fZoom and the map zoom scale + float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE; + + offset.x *= fScale; + offset.y *= fScale; + + panelpos.x = (pwidth * 0.5f) + (pheight * offset.x); + panelpos.y = (pheight * 0.5f) + (pheight * offset.y); + + return panelpos; +} + +void CMapOverview::SetTime( float time ) +{ + m_fWorldTime = time; +} + +void CMapOverview::SetMap(const char * levelname) +{ + // Reset players and objects, even if the map is the same as the previous one + m_Objects.RemoveAll(); + + m_fNextTrailUpdate = 0;// Set to 0 for immediate update. Our WorldTime var hasn't been updated to 0 for the new map yet + m_fWorldTime = 0;// In release, we occasionally race and get this bug again if we gt a paint before an update. Reset this before the old value gets in to the timer. + // Please note, UpdatePlayerTrails comes from PAINT, not UPDATE. + + InitTeamColorsAndIcons(); + + // load new KeyValues + if ( m_MapKeyValues && Q_strcmp( levelname, m_MapKeyValues->GetName() ) == 0 ) + { + return; // map didn't change + } + + if ( m_MapKeyValues ) + m_MapKeyValues->deleteThis(); + + m_MapKeyValues = new KeyValues( levelname ); + + char tempfile[MAX_PATH]; + Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", levelname ); + + if ( !m_MapKeyValues->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) ) + { + DevMsg( 1, "Error! CMapOverview::SetMap: couldn't load file %s.\n", tempfile ); + m_nMapTextureID = -1; + m_MapOrigin.x = 0; + m_MapOrigin.y = 0; + m_fMapScale = 1; + m_bRotateMap = false; + m_fFullZoom = 1; + return; + } + + // TODO release old texture ? + + m_nMapTextureID = surface()->CreateNewTextureID(); + + //if we have not uploaded yet, lets go ahead and do so + surface()->DrawSetTextureFile( m_nMapTextureID, m_MapKeyValues->GetString("material"), true, false); + + int wide, tall; + + surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall ); + + if ( wide != tall ) + { + DevMsg( 1, "Error! CMapOverview::SetMap: map image must be a square.\n" ); + m_nMapTextureID = -1; + return; + } + + m_MapOrigin.x = m_MapKeyValues->GetInt("pos_x"); + m_MapOrigin.y = m_MapKeyValues->GetInt("pos_y"); + m_fMapScale = m_MapKeyValues->GetFloat("scale", 1.0f); + m_bRotateMap = m_MapKeyValues->GetInt("rotate")!=0; + m_fFullZoom = m_MapKeyValues->GetFloat("zoom", 1.0f ); +} + +void CMapOverview::ResetRound() +{ + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *p = &m_Players[i]; + + if ( p->team > TEAM_SPECTATOR ) + { + p->health = 100; + } + + Q_memset( p->trail, 0, sizeof(p->trail) ); + + p->position = Vector( 0, 0, 0 ); + } + + m_Objects.RemoveAll(); +} + +void CMapOverview::OnMousePressed( MouseCode code ) +{ + +} + +void CMapOverview::DrawCamera() +{ + // draw a red center point + surface()->DrawSetColor( 255,0,0,255 ); + Vector2D center = MapToPanel( m_ViewOrigin ); + surface()->DrawFilledRect( center.x-2, center.y-2, center.x+2, center.y+2); +} + +void CMapOverview::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type, "game_newmap") == 0 ) + { + SetMap( event->GetString("mapname") ); + ResetRound(); + } + + else if ( Q_strcmp(type, "round_start") == 0 ) + { + ResetRound(); + } + + else if ( Q_strcmp(type,"player_connect_client") == 0 ) + { + int index = event->GetInt("index"); // = entity index - 1 + + if ( index < 0 || index >= MAX_PLAYERS ) + return; + + MapPlayer_t *player = &m_Players[index]; + + player->index = index; + player->userid = event->GetInt("userid"); + Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) ); + + // Reset settings + Q_memset( player->trail, 0, sizeof(player->trail) ); + player->team = TEAM_UNASSIGNED; + player->health = 0; + } + + else if ( Q_strcmp(type,"player_info") == 0 ) + { + int index = event->GetInt("index"); // = entity index - 1 + + if ( index < 0 || index >= MAX_PLAYERS ) + return; + + MapPlayer_t *player = &m_Players[index]; + + player->index = index; + player->userid = event->GetInt("userid"); + Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) ); + } + + else if ( Q_strcmp(type,"player_team") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + player->team = event->GetInt("team"); + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + + else if ( Q_strcmp(type,"player_death") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + player->health = 0; + Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails + } + + else if ( Q_strcmp(type,"player_spawn") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + player->health = 100; + Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails + } + + else if ( Q_strcmp(type,"player_disconnect") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + Q_memset( player, 0, sizeof(MapPlayer_t) ); // clear player field + } +} + +void CMapOverview::SetMode(int mode) +{ + m_flChangeSpeed = 0; // change size instantly + + if ( mode == MAP_MODE_OFF ) + { + ShowPanel( false ); + + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapOff" ); + } + else if ( mode == MAP_MODE_INSET ) + { + if( m_nMapTextureID == -1 ) + { + SetMode( MAP_MODE_OFF ); + return; + } + + if ( m_nMode != MAP_MODE_OFF ) + m_flChangeSpeed = 1000; // zoom effect + + C_BasePlayer *pPlayer = CBasePlayer::GetLocalPlayer(); + + if ( pPlayer ) + SetFollowEntity( pPlayer->entindex() ); + + ShowPanel( true ); + + if ( mode != m_nMode && RunHudAnimations() ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToSmall" ); + } + } + else if ( mode == MAP_MODE_FULL ) + { + if( m_nMapTextureID == -1 ) + { + SetMode( MAP_MODE_OFF ); + return; + } + + if ( m_nMode != MAP_MODE_OFF ) + m_flChangeSpeed = 1000; // zoom effect + + SetFollowEntity( 0 ); + + ShowPanel( true ); + + if ( mode != m_nMode && RunHudAnimations() ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToLarge" ); + } + } + + // finally set mode + m_nMode = mode; + + UpdateSizeAndPosition(); +} + +bool CMapOverview::ShouldDraw( void ) +{ + return ( m_nMode != MAP_MODE_OFF ) && CHudElement::ShouldDraw(); +} + +void CMapOverview::UpdateSizeAndPosition() +{ + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + int iScreenWide, iScreenTall; + GetHudSize( iScreenWide, iScreenTall ); + + int iTopBarHeight = g_pSpectatorGUI->GetTopBarHeight(); + int iBottomBarHeight = g_pSpectatorGUI->GetBottomBarHeight(); + + iScreenTall -= ( iTopBarHeight + iBottomBarHeight ); + + int x,y,w,h; + GetBounds( x,y,w,h ); + + if ( y < iTopBarHeight ) + y = iTopBarHeight; + + SetBounds( x,y,w,MIN(h,iScreenTall) ); + } +} + +void CMapOverview::SetCenter(const Vector2D &mappos) +{ + int width, height; + + GetSize( width, height); + + m_ViewOrigin = mappos; + m_MapCenter = mappos; + + float fTwiceZoom = m_fZoom * m_fFullZoom * 2; + + width = height = OVERVIEW_MAP_SIZE / (fTwiceZoom); + + if( GetMode() != MAP_MODE_RADAR ) + { + if ( m_MapCenter.x < width ) + m_MapCenter.x = width; + + if ( m_MapCenter.x > (OVERVIEW_MAP_SIZE-width) ) + m_MapCenter.x = (OVERVIEW_MAP_SIZE-width); + + if ( m_MapCenter.y < height ) + m_MapCenter.y = height; + + if ( m_MapCenter.y > (OVERVIEW_MAP_SIZE-height) ) + m_MapCenter.y = (OVERVIEW_MAP_SIZE-height); + + //center if in full map mode + if ( m_fZoom <= 1.0 ) + { + m_MapCenter.x = OVERVIEW_MAP_SIZE/2; + m_MapCenter.y = OVERVIEW_MAP_SIZE/2; + } + } + +} + +void CMapOverview::SetFollowAngle(bool state) +{ + m_bFollowAngle = state; +} + +void CMapOverview::SetFollowEntity(int entindex) +{ + m_nFollowEntity = entindex; +} + +float CMapOverview::GetZoom( void ) +{ + return m_fZoom; +} + +int CMapOverview::GetMode( void ) +{ + return m_nMode; +} + +void CMapOverview::SetAngle(float angle) +{ + m_fViewAngle = angle; +} + +void CMapOverview::ShowPlayerNames(bool state) +{ + m_bShowNames = state; +} + + +void CMapOverview::ShowPlayerHealth(bool state) +{ + m_bShowHealth = state; +} + +void CMapOverview::ShowPlayerTracks(float seconds) +{ + m_fTrailUpdateInterval = seconds; +} + +bool CMapOverview::SetTeamColor(int team, Color color) +{ + if ( team < 0 || team>= MAX_TEAMS ) + return false; + + m_TeamColors[team] = color; + + return true; +} + +CMapOverview::MapObject_t* CMapOverview::FindObjectByID(int objectID) +{ + for ( int i = 0; i < m_Objects.Count(); i++ ) + { + if ( m_Objects[i].objectID == objectID ) + return &m_Objects[i]; + } + + return NULL; +} + +int CMapOverview::AddObject( const char *icon, int entity, float timeToLive ) +{ + MapObject_t obj; Q_memset( &obj, 0, sizeof(obj) ); + + obj.objectID = m_ObjectCounterID++; + obj.index = entity; + obj.icon = AddIconTexture( icon ); + obj.size = m_flIconSize; + obj.status = -1; + + if ( timeToLive > 0 ) + obj.endtime = gpGlobals->curtime + timeToLive; + else + obj.endtime = -1; + + m_Objects.AddToTail( obj ); + + return obj.objectID; +} + +void CMapOverview::SetObjectText( int objectID, const char *text, Color color ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + if ( text ) + { + Q_strncpy( obj->name, text, sizeof(obj->name) ); + } + else + { + Q_memset( obj->name, 0, sizeof(obj->name) ); + } + + obj->color = color; +} + +void CMapOverview::SetObjectStatus( int objectID, float status, Color color ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + obj->status = status; + obj->statusColor = color; +} + +void CMapOverview::SetObjectIcon( int objectID, const char *icon, float size ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + obj->icon = AddIconTexture( icon ); + obj->size = size; +} + +void CMapOverview::SetObjectPosition( int objectID, const Vector &position, const QAngle &angle ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + obj->angle = angle; + obj->position = position; +} + +void CMapOverview::AddObjectFlags( int objectID, int flags ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + obj->flags |= flags; +} + +void CMapOverview::SetObjectFlags( int objectID, int flags ) +{ + MapObject_t* obj = FindObjectByID( objectID ); + + if ( !obj ) + return; + + obj->flags = flags; +} + +void CMapOverview::RemoveObjectByIndex( int index ) +{ + for ( int i = 0; i < m_Objects.Count(); i++ ) + { + if ( m_Objects[i].index == index ) + { + m_Objects.Remove( i ); + return; + } + } +} + +void CMapOverview::RemoveObject( int objectID ) +{ + for ( int i = 0; i < m_Objects.Count(); i++ ) + { + if ( m_Objects[i].objectID == objectID ) + { + m_Objects.Remove( i ); + return; + } + } +} + +void CMapOverview::UpdateObjects() +{ + for ( int i = 0; i < m_Objects.Count(); i++ ) + { + MapObject_t *obj = &m_Objects[i]; + + if ( obj->endtime > 0 && obj->endtime < gpGlobals->curtime ) + { + m_Objects.Remove( i ); + i--; + continue; + } + + if ( obj->index <= 0 ) + continue; + + C_BaseEntity *entity = ClientEntityList().GetEnt( obj->index ); + + if ( !entity ) + continue; + + obj->position = entity->GetAbsOrigin(); + obj->angle = entity->GetAbsAngles(); + } +}
\ No newline at end of file diff --git a/game/client/game_controls/NavProgress.cpp b/game/client/game_controls/NavProgress.cpp new file mode 100644 index 0000000..8aef8c4 --- /dev/null +++ b/game/client/game_controls/NavProgress.cpp @@ -0,0 +1,137 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "NavProgress.h" + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include <KeyValues.h> +#include <convar.h> + +#include <vgui_controls/Label.h> + +#include <game/client/iviewport.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//-------------------------------------------------------------------------------------------------------------- +CNavProgress::CNavProgress( IViewPort *pViewPort ) : Frame( NULL, PANEL_NAV_PROGRESS ) +{ + // initialize dialog + m_pViewPort = pViewPort; + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMoveable(false); + SetSizeable(false); + SetProportional(true); + + // hide the system buttons + SetTitleBarVisible( false ); + + m_pTitle = new Label( this, "TitleLabel", "" ); + m_pText = new Label( this, "TextLabel", "" ); + + m_pProgressBarBorder = new Panel( this, "ProgressBarBorder" ); + m_pProgressBar = new Panel( this, "ProgressBar" ); + m_pProgressBarSizer = new Panel( this, "ProgressBarSizer" ); + + LoadControlSettings("Resource/UI/NavProgress.res"); + + Reset(); +} + +//-------------------------------------------------------------------------------------------------------------- +CNavProgress::~CNavProgress() +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetPaintBackgroundType( 2 ); + + m_pProgressBarSizer->SetVisible( false ); + + m_pProgressBarBorder->SetBorder( pScheme->GetBorder( "ButtonDepressedBorder" ) ); + m_pProgressBarBorder->SetBgColor( Color( 0, 0, 0, 0 ) ); + + m_pProgressBar->SetBorder( pScheme->GetBorder( "ButtonBorder" ) ); + m_pProgressBar->SetBgColor( pScheme->GetColor( "ProgressBar.FgColor", Color( 0, 0, 0, 0 ) ) ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_numTicks ) + { + int w = m_pProgressBarSizer->GetWide(); + w = w * m_currentTick / m_numTicks; + m_pProgressBar->SetWide( w ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::Init( const char *title, int numTicks, int startTick ) +{ + m_pText->SetText( title ); + + m_numTicks = MAX( 1, numTicks ); // non-zero, since we'll divide by this + m_currentTick = MAX( 0, MIN( m_numTicks, startTick ) ); + + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::SetData(KeyValues *data) +{ + Init( data->GetString( "msg" ), + data->GetInt( "total" ), + data->GetInt( "current" ) ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::ShowPanel( bool bShow ) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + m_pViewPort->ShowBackGround( bShow ); + + if ( bShow ) + { + Activate(); + SetMouseInputEnabled( true ); + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::Reset( void ) +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void CNavProgress::Update( void ) +{ +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/game_controls/NavProgress.h b/game/client/game_controls/NavProgress.h new file mode 100644 index 0000000..412ed29 --- /dev/null +++ b/game/client/game_controls/NavProgress.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef NAVPROGRESS_H +#define NAVPROGRESS_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/ProgressBar.h> + +#include <game/client/iviewport.h> + +class CNavProgress : public vgui::Frame, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CNavProgress, vgui::Frame ); + +public: + CNavProgress(IViewPort *pViewPort); + virtual ~CNavProgress(); + + virtual const char *GetName( void ) { return PANEL_NAV_PROGRESS; } + virtual void SetData(KeyValues *data); + virtual void Reset(); + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_NONE; } + +public: + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PerformLayout(); + void Init( const char *title, int numTicks, int currentTick ); + +protected: + IViewPort *m_pViewPort; + + int m_numTicks; + int m_currentTick; + + vgui::Label * m_pTitle; + vgui::Label * m_pText; + vgui::Panel * m_pProgressBarBorder; + vgui::Panel * m_pProgressBar; + vgui::Panel * m_pProgressBarSizer; +}; + +#endif // NAVPROGRESS_H diff --git a/game/client/game_controls/SpectatorGUI.cpp b/game/client/game_controls/SpectatorGUI.cpp new file mode 100644 index 0000000..41b33a3 --- /dev/null +++ b/game/client/game_controls/SpectatorGUI.cpp @@ -0,0 +1,887 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "inputsystem/iinputsystem.h" +#include "input.h" +#include <cdll_client_int.h> +#include <globalvars_base.h> +#include <cdll_util.h> +#include <KeyValues.h> + +#include "spectatorgui.h" + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui/IPanel.h> +#include <vgui_controls/ImageList.h> +#include <vgui_controls/MenuItem.h> +#include <vgui_controls/TextImage.h> + +#include <stdio.h> // _snprintf define + +#include <game/client/iviewport.h> +#include "commandmenu.h" +#include "hltvcamera.h" +#if defined( REPLAY_ENABLED ) +#include "replay/replaycamera.h" +#endif + +#include <vgui_controls/TextEntry.h> +#include <vgui_controls/Panel.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/Menu.h> +#include "IGameUIFuncs.h" // for key bindings +#include <imapoverview.h> +#include <shareddefs.h> +#include <igameresources.h> + +#ifdef TF_CLIENT_DLL +#include "tf_gamerules.h" +void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ); +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifndef _XBOX +extern IGameUIFuncs *gameuifuncs; // for key binding details +#endif + +// void DuckMessage(const char *str); // from vgui_teamfortressviewport.cpp + +ConVar spec_scoreboard( "spec_scoreboard", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + +CSpectatorGUI *g_pSpectatorGUI = NULL; + + +// NB disconnect between localization text and observer mode enums +static const char *s_SpectatorModes[] = +{ + "#Spec_Mode0", // OBS_MODE_NONE = 0, + "#Spec_Mode1", // OBS_MODE_DEATHCAM, + "", // OBS_MODE_FREEZECAM, + "#Spec_Mode2", // OBS_MODE_FIXED, + "#Spec_Mode3", // OBS_MODE_IN_EYE, + "#Spec_Mode4", // OBS_MODE_CHASE, + "#Spec_Mode_POI", // OBS_MODE_POI, PASSTIME + "#Spec_Mode5", // OBS_MODE_ROAMING, +}; + +using namespace vgui; + +ConVar cl_spec_mode( + "cl_spec_mode", + "1", + FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_SERVER_CAN_EXECUTE, + "spectator mode" ); + + + +//----------------------------------------------------------------------------- +// Purpose: left and right buttons pointing buttons +//----------------------------------------------------------------------------- +class CSpecButton : public Button +{ +public: + CSpecButton(Panel *parent, const char *panelName): Button(parent, panelName, "") {} + +private: + void ApplySchemeSettings(vgui::IScheme *pScheme) + { + Button::ApplySchemeSettings(pScheme); + SetFont(pScheme->GetFont("Marlett", IsProportional()) ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSpectatorMenu::CSpectatorMenu( IViewPort *pViewPort ) : Frame( NULL, PANEL_SPECMENU ) +{ + m_iDuckKey = BUTTON_CODE_INVALID; + + m_pViewPort = pViewPort; + + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( true ); + SetTitleBarVisible( false ); // don't draw a title bar + SetMoveable( false ); + SetSizeable( false ); + SetProportional(true); + + SetScheme("ClientScheme"); + + m_pPlayerList = new ComboBox(this, "playercombo", 10 , false); + HFont hFallbackFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmallFallBack", false ); + if ( INVALID_FONT != hFallbackFont ) + { + m_pPlayerList->SetUseFallbackFont( true, hFallbackFont ); + } + + m_pViewOptions = new ComboBox(this, "viewcombo", 10 , false ); + m_pConfigSettings = new ComboBox(this, "settingscombo", 10 , false ); + + m_pLeftButton = new CSpecButton( this, "specprev"); + m_pLeftButton->SetText("3"); + m_pRightButton = new CSpecButton( this, "specnext"); + m_pRightButton->SetText("4"); + + m_pPlayerList->SetText(""); + m_pViewOptions->SetText("#Spec_Modes"); + m_pConfigSettings->SetText("#Spec_Options"); + + m_pPlayerList->SetOpenDirection( Menu::UP ); + m_pViewOptions->SetOpenDirection( Menu::UP ); + m_pConfigSettings->SetOpenDirection( Menu::UP ); + + // create view config menu + CommandMenu * menu = new CommandMenu(m_pConfigSettings, "spectatormenu", gViewPortInterface); + menu->LoadFromFile( "Resource/spectatormenu.res" ); + m_pConfigSettings->SetMenu( menu ); // attach menu to combo box + + // create view mode menu + menu = new CommandMenu(m_pViewOptions, "spectatormodes", gViewPortInterface); + menu->LoadFromFile("Resource/spectatormodes.res"); + m_pViewOptions->SetMenu( menu ); // attach menu to combo box + + LoadControlSettings( "Resource/UI/BottomSpectator.res" ); + + ListenForGameEvent( "spec_target_updated" ); +} + +void CSpectatorMenu::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + // need to MakeReadyForUse() on the menus so we can set their bg color before they are displayed + m_pConfigSettings->GetMenu()->MakeReadyForUse(); + m_pViewOptions->GetMenu()->MakeReadyForUse(); + m_pPlayerList->GetMenu()->MakeReadyForUse(); + + if ( g_pSpectatorGUI ) + { + m_pConfigSettings->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() ); + m_pViewOptions->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() ); + m_pPlayerList->GetMenu()->SetBgColor( g_pSpectatorGUI->GetBlackBarColor() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: makes the GUI fill the screen +//----------------------------------------------------------------------------- +void CSpectatorMenu::PerformLayout() +{ + int w,h; + GetHudSize(w, h); + + // fill the screen + SetSize(w,GetTall()); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles changes to combo boxes +//----------------------------------------------------------------------------- +void CSpectatorMenu::OnTextChanged(KeyValues *data) +{ + Panel *panel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") ); + + vgui::ComboBox *box = dynamic_cast<vgui::ComboBox *>( panel ); + + if( box == m_pConfigSettings) // don't change the text in the config setting combo + { + m_pConfigSettings->SetText("#Spec_Options"); + } + else if ( box == m_pPlayerList ) + { + KeyValues *kv = box->GetActiveItemUserData(); + if ( kv && GameResources() ) + { + const char *player = kv->GetString("player"); + + int currentPlayerNum = GetSpectatorTarget(); + const char *currentPlayerName = GameResources()->GetPlayerName( currentPlayerNum ); + + if ( !FStrEq( currentPlayerName, player ) ) + { + char command[128]; + Q_snprintf( command, sizeof(command), "spec_player \"%s\"", player ); + engine->ClientCmd( command ); + } + } + } +} + +void CSpectatorMenu::OnCommand( const char *command ) +{ + if (!stricmp(command, "specnext") ) + { + engine->ClientCmd("spec_next"); + } + else if (!stricmp(command, "specprev") ) + { + engine->ClientCmd("spec_prev"); + } +} + +void CSpectatorMenu::FireGameEvent( IGameEvent * event ) +{ + const char *pEventName = event->GetName(); + + if ( Q_strcmp( "spec_target_updated", pEventName ) == 0 ) + { + IGameResources *gr = GameResources(); + if ( !gr ) + return; + + // make sure the player combo box is up to date + int playernum = GetSpectatorTarget(); + if ( playernum < 1 || playernum > MAX_PLAYERS ) + return; + + const char *selectedPlayerName = gr->GetPlayerName( playernum ); + const char *currentPlayerName = ""; + KeyValues *kv = m_pPlayerList->GetActiveItemUserData(); + if ( kv ) + { + currentPlayerName = kv->GetString( "player" ); + } + if ( !FStrEq( currentPlayerName, selectedPlayerName ) ) + { + for ( int i=0; i<m_pPlayerList->GetItemCount(); ++i ) + { + KeyValues *pKv = m_pPlayerList->GetItemUserData( i ); + if ( pKv && FStrEq( pKv->GetString( "player" ), selectedPlayerName ) ) + { + m_pPlayerList->ActivateItemByRow( i ); + break; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: when duck is pressed it hides the active part of the GUI +//----------------------------------------------------------------------------- +void CSpectatorMenu::OnKeyCodePressed(KeyCode code) +{ + if ( code == m_iDuckKey ) + { + // hide if DUCK is pressed again + m_pViewPort->ShowPanel( this, false ); + } +} + +void CSpectatorMenu::ShowPanel(bool bShow) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + if ( bShow ) + { + // Force relayout in case Steam Controller stuff has changed. + InvalidateLayout( true, true ); + Activate(); + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( true ); + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + SetKeyBoardInputEnabled( false ); + } + + bool bIsEnabled = true; + + if ( engine->IsHLTV() && HLTVCamera()->IsPVSLocked() ) + { + // when watching HLTV or Replay with a locked PVS, some elements are disabled + bIsEnabled = false; + } + + m_pLeftButton->SetVisible( bIsEnabled ); + m_pRightButton->SetVisible( bIsEnabled ); + m_pPlayerList->SetVisible( bIsEnabled ); + m_pViewOptions->SetVisible( bIsEnabled ); +} + +void CSpectatorMenu::Update( void ) +{ + IGameResources *gr = GameResources(); + + Reset(); + + if ( m_iDuckKey == BUTTON_CODE_INVALID ) + { + m_iDuckKey = gameuifuncs->GetButtonCodeForBind( "duck" ); + } + + if ( !gr ) + return; + + int iPlayerIndex; + for ( iPlayerIndex = 1 ; iPlayerIndex <= gpGlobals->maxClients; iPlayerIndex++ ) + { + + // does this slot in the array have a name? + if ( !gr->IsConnected( iPlayerIndex ) ) + continue; + + if ( gr->IsLocalPlayer( iPlayerIndex ) ) + continue; + + if ( !gr->IsAlive( iPlayerIndex ) ) + continue; + + wchar_t playerText[ 80 ], playerName[ 64 ], *team, teamText[ 64 ]; + char localizeTeamName[64]; + char szPlayerIndex[16]; + g_pVGuiLocalize->ConvertANSIToUnicode( UTIL_SafeName( gr->GetPlayerName(iPlayerIndex) ), playerName, sizeof( playerName ) ); + const char * teamname = gr->GetTeamName( gr->GetTeam(iPlayerIndex) ); + if ( teamname ) + { + Q_snprintf( localizeTeamName, sizeof( localizeTeamName ), "#%s", teamname ); + team=g_pVGuiLocalize->Find( localizeTeamName ); + + if ( !team ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( teamname , teamText, sizeof( teamText ) ); + team = teamText; + } + + g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem_Team" ), 2, playerName, team ); + } + else + { + g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem" ), 1, playerName ); + } + + Q_snprintf( szPlayerIndex, sizeof( szPlayerIndex ), "%d", iPlayerIndex ); + + KeyValues *kv = new KeyValues( "UserData", "player", gr->GetPlayerName( iPlayerIndex ), "index", szPlayerIndex ); + m_pPlayerList->AddItem( playerText, kv ); + kv->deleteThis(); + } + + // make sure the player combo box is up to date + int playernum = GetSpectatorTarget(); + const char *selectedPlayerName = gr->GetPlayerName( playernum ); + for ( iPlayerIndex=0; iPlayerIndex<m_pPlayerList->GetItemCount(); ++iPlayerIndex ) + { + KeyValues *kv = m_pPlayerList->GetItemUserData( iPlayerIndex ); + if ( kv && FStrEq( kv->GetString( "player" ), selectedPlayerName ) ) + { + m_pPlayerList->ActivateItemByRow( iPlayerIndex ); + break; + } + } + + //============================================================================= + // HPE_BEGIN: + // [pfreese] make sure the view mode combo box is up to date - the spectator + // mode can be changed multiple ways + //============================================================================= + + int specmode = GetSpectatorMode(); + m_pViewOptions->SetText(s_SpectatorModes[specmode]); + + //============================================================================= + // HPE_END + //============================================================================= +} + +//----------------------------------------------------------------------------- +// main spectator panel + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSpectatorGUI::CSpectatorGUI(IViewPort *pViewPort) : EditablePanel( NULL, PANEL_SPECGUI ) +{ +// m_bHelpShown = false; +// m_bInsetVisible = false; +// m_iDuckKey = KEY_NONE; + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + m_bSpecScoreboard = false; + + m_pViewPort = pViewPort; + g_pSpectatorGUI = this; + + // initialize dialog + SetVisible(false); + SetProportional(true); + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMouseInputEnabled( false ); + SetKeyBoardInputEnabled( false ); + + m_pTopBar = new Panel( this, "topbar" ); + m_pBottomBarBlank = new Panel( this, "bottombarblank" ); + + // m_pBannerImage = new ImagePanel( m_pTopBar, NULL ); + m_pPlayerLabel = new Label( this, "playerlabel", "" ); + m_pPlayerLabel->SetVisible( false ); + TextImage *image = m_pPlayerLabel->GetTextImage(); + if ( image ) + { + HFont hFallbackFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmallFallBack", false ); + if ( INVALID_FONT != hFallbackFont ) + { + image->SetUseFallbackFont( true, hFallbackFont ); + } + } + + SetPaintBorderEnabled(false); + SetPaintBackgroundEnabled(false); + + // m_pBannerImage->SetVisible(false); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSpectatorGUI::~CSpectatorGUI() +{ + g_pSpectatorGUI = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the colour of the top and bottom bars +//----------------------------------------------------------------------------- +void CSpectatorGUI::ApplySchemeSettings(IScheme *pScheme) +{ + KeyValues *pConditions = NULL; + +#ifdef TF_CLIENT_DLL + if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) + { + pConditions = new KeyValues( "conditions" ); + AddSubKeyNamed( pConditions, "if_mvm" ); + } +#endif + + LoadControlSettings( GetResFile(), NULL, NULL, pConditions ); + + if ( pConditions ) + { + pConditions->deleteThis(); + } + + m_pBottomBarBlank->SetVisible( true ); + m_pTopBar->SetVisible( true ); + + BaseClass::ApplySchemeSettings( pScheme ); + SetBgColor(Color( 0,0,0,0 ) ); // make the background transparent + m_pTopBar->SetBgColor(GetBlackBarColor()); + m_pBottomBarBlank->SetBgColor(GetBlackBarColor()); + // m_pBottomBar->SetBgColor(Color( 0,0,0,0 )); + SetPaintBorderEnabled(false); + + SetBorder( NULL ); + +#ifdef CSTRIKE_DLL + SetZPos(80); // guarantee it shows above the scope +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: makes the GUI fill the screen +//----------------------------------------------------------------------------- +void CSpectatorGUI::PerformLayout() +{ + int w,h,x,y; + GetHudSize(w, h); + + // fill the screen + SetBounds(0,0,w,h); + + // stretch the bottom bar across the screen + m_pBottomBarBlank->GetPos(x,y); + m_pBottomBarBlank->SetSize( w, h - y ); +} + +//----------------------------------------------------------------------------- +// Purpose: checks spec_scoreboard cvar to see if the scoreboard should be displayed +//----------------------------------------------------------------------------- +void CSpectatorGUI::OnThink() +{ + BaseClass::OnThink(); + + if ( IsVisible() ) + { + if ( m_bSpecScoreboard != spec_scoreboard.GetBool() ) + { + if ( !spec_scoreboard.GetBool() || !gViewPortInterface->GetActivePanel() ) + { + m_bSpecScoreboard = spec_scoreboard.GetBool(); + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, m_bSpecScoreboard ); + } + } + +#ifdef TF_CLIENT_DLL + if ( TFGameRules() && TFGameRules()->ShowMatchSummary() ) + { + SetVisible( false ); + } +#endif + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the image to display for the banner in the top right corner +//----------------------------------------------------------------------------- +void CSpectatorGUI::SetLogoImage(const char *image) +{ + if ( m_pBannerImage ) + { + m_pBannerImage->SetImage( scheme()->GetImage(image, false) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CSpectatorGUI::SetLabelText(const char *textEntryName, const char *text) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetText(text); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CSpectatorGUI::SetLabelText(const char *textEntryName, wchar_t *text) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetText(text); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CSpectatorGUI::MoveLabelToFront(const char *textEntryName) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->MoveToFront(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides the buy menu +//----------------------------------------------------------------------------- +void CSpectatorGUI::ShowPanel(bool bShow) +{ + if ( bShow && !IsVisible() ) + { + InvalidateLayout( true, true ); + m_bSpecScoreboard = false; + } + + SetVisible( bShow ); + + if ( !bShow && m_bSpecScoreboard ) + { + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, false ); + } +} + +bool CSpectatorGUI::ShouldShowPlayerLabel( int specmode ) +{ + return ( (specmode == OBS_MODE_IN_EYE) || (specmode == OBS_MODE_CHASE) ); +} +//----------------------------------------------------------------------------- +// Purpose: Updates the gui, rearranges elements +//----------------------------------------------------------------------------- +void CSpectatorGUI::Update() +{ + int wide, tall; + int bx, by, bwide, btall; + + GetHudSize(wide, tall); + m_pTopBar->GetBounds( bx, by, bwide, btall ); + + IGameResources *gr = GameResources(); + int specmode = GetSpectatorMode(); + int playernum = GetSpectatorTarget(); + + IViewPortPanel *overview = gViewPortInterface->FindPanelByName( PANEL_OVERVIEW ); + + if ( overview && overview->IsVisible() ) + { + int mx, my, mwide, mtall; + + VPANEL p = overview->GetVPanel(); + vgui::ipanel()->GetPos( p, mx, my ); + vgui::ipanel()->GetSize( p, mwide, mtall ); + + if ( my < btall ) + { + // reduce to bar + m_pTopBar->SetSize( wide - (mx + mwide), btall ); + m_pTopBar->SetPos( (mx + mwide), 0 ); + } + else + { + // full top bar + m_pTopBar->SetSize( wide , btall ); + m_pTopBar->SetPos( 0, 0 ); + } + } + else + { + // full top bar + m_pTopBar->SetSize( wide , btall ); // change width, keep height + m_pTopBar->SetPos( 0, 0 ); + } + + m_pPlayerLabel->SetVisible( ShouldShowPlayerLabel(specmode) ); + + // update player name filed, text & color + + if ( playernum > 0 && playernum <= gpGlobals->maxClients && gr ) + { + Color c = gr->GetTeamColor( gr->GetTeam(playernum) ); // Player's team color + + m_pPlayerLabel->SetFgColor( c ); + + wchar_t playerText[ 80 ], playerName[ 64 ], health[ 10 ]; + V_wcsncpy( playerText, L"Unable to find #Spec_PlayerItem*", sizeof( playerText ) ); + memset( playerName, 0x0, sizeof( playerName ) ); + + g_pVGuiLocalize->ConvertANSIToUnicode( UTIL_SafeName(gr->GetPlayerName( playernum )), playerName, sizeof( playerName ) ); + int iHealth = gr->GetHealth( playernum ); + if ( iHealth > 0 && gr->IsAlive(playernum) ) + { + _snwprintf( health, ARRAYSIZE( health ), L"%i", iHealth ); + g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem_Team" ), 2, playerName, health ); + } + else + { + g_pVGuiLocalize->ConstructString_safe( playerText, g_pVGuiLocalize->Find( "#Spec_PlayerItem" ), 1, playerName ); + } + + m_pPlayerLabel->SetText( playerText ); + } + else + { + m_pPlayerLabel->SetText( L"" ); + } + + // update extra info field + wchar_t szEtxraInfo[1024]; + wchar_t szTitleLabel[1024]; + char tempstr[128]; + + if ( engine->IsHLTV() ) + { + // set spectator number and HLTV title + Q_snprintf(tempstr,sizeof(tempstr),"Spectators : %d", HLTVCamera()->GetNumSpectators() ); + g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,szEtxraInfo,sizeof(szEtxraInfo)); + + Q_strncpy( tempstr, HLTVCamera()->GetTitleText(), sizeof(tempstr) ); + g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,szTitleLabel,sizeof(szTitleLabel)); + } + else + { + // otherwise show map name + Q_FileBase( engine->GetLevelName(), tempstr, sizeof(tempstr) ); + + wchar_t wMapName[64]; + g_pVGuiLocalize->ConvertANSIToUnicode(tempstr,wMapName,sizeof(wMapName)); + g_pVGuiLocalize->ConstructString_safe( szEtxraInfo, g_pVGuiLocalize->Find("#Spec_Map" ),1, wMapName ); + + g_pVGuiLocalize->ConvertANSIToUnicode( "" ,szTitleLabel,sizeof(szTitleLabel)); + } + + SetLabelText("extrainfo", szEtxraInfo ); + SetLabelText("titlelabel", szTitleLabel ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the res file we should use (depends on if we're in Steam Controller mode) +//----------------------------------------------------------------------------- +const char * CSpectatorGUI::GetResFile( void ) +{ + if ( ::input->IsSteamControllerActive() ) + { + return "Resource/UI/Spectator_SC.res"; + } + else + { + return "Resource/UI/Spectator.res"; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the timer label if one exists +//----------------------------------------------------------------------------- +void CSpectatorGUI::UpdateTimer() +{ + wchar_t szText[ 63 ]; + + int timer = 0; + + V_swprintf_safe ( szText, L"%d:%02d\n", (timer / 60), (timer % 60) ); + + SetLabelText("timerlabel", szText ); +} + +static void ForwardSpecCmdToServer( const CCommand &args ) +{ + if ( engine->IsPlayingDemo() ) + return; + + if ( args.ArgC() == 1 ) + { + // just forward the command without parameters + engine->ServerCmd( args[ 0 ] ); + } + else if ( args.ArgC() == 2 ) + { + // forward the command with parameter + // XXX(JohnS): Whyyyyy + char command[128]; + Q_snprintf( command, sizeof(command), "%s \"%s\"", args[ 0 ], args[ 1 ] ); + engine->ServerCmd( command ); + } +} + +CON_COMMAND_F( spec_next, "Spectate next player", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer || !pPlayer->IsObserver() ) + return; + + if ( engine->IsHLTV() ) + { + // handle the command clientside + if ( !HLTVCamera()->IsPVSLocked() ) + { + HLTVCamera()->SpecNextPlayer( false ); + } + } + else + { + ForwardSpecCmdToServer( args ); + } +} + +CON_COMMAND_F( spec_prev, "Spectate previous player", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer || !pPlayer->IsObserver() ) + return; + + if ( engine->IsHLTV() ) + { + // handle the command clientside + if ( !HLTVCamera()->IsPVSLocked() ) + { + HLTVCamera()->SpecNextPlayer( true ); + } + } + else + { + ForwardSpecCmdToServer( args ); + } +} + +CON_COMMAND_F( spec_mode, "Set spectator mode", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer || !pPlayer->IsObserver() ) + return; + + if ( engine->IsHLTV() ) + { + if ( HLTVCamera()->IsPVSLocked() ) + { + // in locked mode we can only switch between first and 3rd person + HLTVCamera()->ToggleChaseAsFirstPerson(); + } + else + { + // we can choose any mode, not loked to PVS + int mode; + + if ( args.ArgC() == 2 ) + { + // set specifc mode + mode = Q_atoi( args[1] ); + } + else + { + // set next mode + mode = HLTVCamera()->GetMode()+1; + + if ( mode > LAST_PLAYER_OBSERVERMODE ) + mode = OBS_MODE_IN_EYE; + else if ( mode == OBS_MODE_POI ) // PASSTIME skip POI mode since hltv doesn't have the entity data required to make it work + mode = OBS_MODE_ROAMING; + } + + // handle the command clientside + HLTVCamera()->SetMode( mode ); + } + + // turn off auto director once user tried to change view settings + HLTVCamera()->SetAutoDirector( false ); + } + else + { + // we spectate on a game server, forward command + ForwardSpecCmdToServer( args ); + } +} + +CON_COMMAND_F( spec_player, "Spectate player by partial name, steamid, or userid", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer || !pPlayer->IsObserver() ) + return; + + if ( args.ArgC() != 2 ) + { + ConMsg( "Usage: spec_player { steamid | #userid | partial name match }\n" ); + return; + } + + if ( engine->IsHLTV() ) + { + // we can only switch primary spectator targets is PVS isnt locked by auto-director + if ( !HLTVCamera()->IsPVSLocked() ) + { + HLTVCamera()->SpecPlayerByPredicate( args[1] ); + } + } + else + { + ForwardSpecCmdToServer( args ); + } +} + + + diff --git a/game/client/game_controls/basemodel_panel.cpp b/game/client/game_controls/basemodel_panel.cpp new file mode 100644 index 0000000..1e7e170 --- /dev/null +++ b/game/client/game_controls/basemodel_panel.cpp @@ -0,0 +1,885 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "basemodel_panel.h" +#include "activitylist.h" +#include "animation.h" +#include "vgui/IInput.h" +#include "matsys_controls/manipulator.h" +#include "bone_setup.h" + +using namespace vgui; +extern float GetAutoPlayTime( void ); +DECLARE_BUILD_FACTORY( CBaseModelPanel ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseModelPanel::CBaseModelPanel( vgui::Panel *pParent, const char *pName ) + : BaseClass( pParent, pName ) + , m_nActiveSequence( ACT_INVALID ) + , m_flActiveSequenceDuration( 0.f ) +{ + m_bForcePos = false; + m_bMousePressed = false; + m_bAllowRotation = false; + m_bAllowPitch = false; + m_bAllowFullManipulation = false; + m_bApplyManipulators = false; + m_bForcedCameraPosition = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseModelPanel::~CBaseModelPanel() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Load in the model portion of the panel's resource file. +//----------------------------------------------------------------------------- +void CBaseModelPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + // Set whether we render to texture + m_bRenderToTexture = inResourceData->GetBool( "render_texture", true ); + m_bUseParticle = inResourceData->GetBool( "use_particle", false ); + + // Grab and set the camera FOV. + float flFOV = GetCameraFOV(); + m_BMPResData.m_flFOV = inResourceData->GetInt( "fov", flFOV ); + SetCameraFOV( m_BMPResData.m_flFOV ); + + // Do we allow rotation on these panels. + m_bAllowRotation = inResourceData->GetBool( "allow_rot", false ); + m_bAllowPitch = inResourceData->GetBool( "allow_pitch", false ); + + // Do we allow full manipulation on these panels. + m_bAllowFullManipulation = inResourceData->GetBool( "allow_manip", false ); + + // Parse our resource file and apply all necessary updates to the MDL. + for ( KeyValues *pData = inResourceData->GetFirstSubKey() ; pData != NULL ; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "model" ) ) + { + ParseModelResInfo( pData ); + } + } + + SetMouseInputEnabled( m_bAllowFullManipulation || m_bAllowRotation || m_bAllowPitch ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::ParseModelResInfo( KeyValues *inResourceData ) +{ + m_bForcePos = ( inResourceData->GetInt( "force_pos", 0 ) == 1 ); + m_BMPResData.m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); + m_BMPResData.m_pszModelName_HWM = ReadAndAllocStringValue( inResourceData, "modelname_hwm" ); + m_BMPResData.m_pszVCD = ReadAndAllocStringValue( inResourceData, "vcd" ); + m_BMPResData.m_angModelPoseRot.Init( inResourceData->GetFloat( "angles_x", 0.0f ), inResourceData->GetFloat( "angles_y", 0.0f ), inResourceData->GetFloat( "angles_z", 0.0f ) ); + m_BMPResData.m_vecOriginOffset.Init( inResourceData->GetFloat( "origin_x", 110.0 ), inResourceData->GetFloat( "origin_y", 5.0 ), inResourceData->GetFloat( "origin_z", 5.0 ) ); + m_BMPResData.m_vecFramedOriginOffset.Init( inResourceData->GetFloat( "frame_origin_x", 110.0 ), inResourceData->GetFloat( "frame_origin_y", 5.0 ), inResourceData->GetFloat( "frame_origin_z", 5.0 ) ); + m_BMPResData.m_vecViewportOffset.Init(); + m_BMPResData.m_nSkin = inResourceData->GetInt( "skin", -1 ); + m_BMPResData.m_bUseSpotlight = ( inResourceData->GetInt( "spotlight", 0 ) == 1 ); + + m_angPlayer = m_BMPResData.m_angModelPoseRot; + m_vecPlayerPos = m_BMPResData.m_vecOriginOffset; + + for ( KeyValues *pData = inResourceData->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "animation" ) ) + { + ParseModelAnimInfo( pData ); + } + else if ( !Q_stricmp( pData->GetName(), "attached_model" ) ) + { + ParseModelAttachInfo( pData ); + } + } + + SetupModelDefaults(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::ParseModelAnimInfo( KeyValues *inResourceData ) +{ + if ( !inResourceData ) + return; + + int iAnim = m_BMPResData.m_aAnimations.AddToTail(); + if ( iAnim == m_BMPResData.m_aAnimations.InvalidIndex() ) + return; + + m_BMPResData.m_aAnimations[iAnim].m_pszName = ReadAndAllocStringValue( inResourceData, "name" ); + m_BMPResData.m_aAnimations[iAnim].m_pszSequence = ReadAndAllocStringValue( inResourceData, "sequence" ); + m_BMPResData.m_aAnimations[iAnim].m_pszActivity = ReadAndAllocStringValue( inResourceData, "activity" ); + m_BMPResData.m_aAnimations[iAnim].m_bDefault = inResourceData->GetBool( "default" ); + + for ( KeyValues *pAnimData = inResourceData->GetFirstSubKey(); pAnimData != NULL; pAnimData = pAnimData->GetNextKey() ) + { + if ( !Q_stricmp( pAnimData->GetName(), "pose_parameters" ) ) + { + m_BMPResData.m_aAnimations[iAnim].m_pPoseParameters = pAnimData->MakeCopy(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::ParseModelAttachInfo( KeyValues *inResourceData ) +{ + if ( !inResourceData ) + return; + + int iAttach = m_BMPResData.m_aAttachModels.AddToTail(); + if ( iAttach == m_BMPResData.m_aAttachModels.InvalidIndex() ) + return; + + m_BMPResData.m_aAttachModels[iAttach].m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); + m_BMPResData.m_aAttachModels[iAttach].m_nSkin = inResourceData->GetInt( "skin", -1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetupModelDefaults( void ) +{ + SetupModelAnimDefaults(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetupModelAnimDefaults( void ) +{ + // Set the move_x parameter so the run activity works + SetPoseParameterByName( "move_x", 1.0f ); + + // Verify that we have animations for this model. + int nAnimCount = m_BMPResData.m_aAnimations.Count(); + if ( nAnimCount == 0 ) + return; + + // Find the default animation if one exists. + int iIndex = FindDefaultAnim(); + if ( iIndex == -1 ) + return; + + SetModelAnim( iIndex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBaseModelPanel::FindDefaultAnim( void ) +{ + int iIndex = -1; + + int nAnimCount = m_BMPResData.m_aAnimations.Count(); + for ( int iAnim = 0; iAnim < nAnimCount; ++iAnim ) + { + if ( m_BMPResData.m_aAnimations[iAnim].m_bDefault ) + return iAnim; + } + + return iIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBaseModelPanel::FindAnimByName( const char *pszName ) +{ + int iIndex = -1; + if ( !pszName ) + return iIndex; + + int nAnimCount = m_BMPResData.m_aAnimations.Count(); + for ( int iAnim = 0; iAnim < nAnimCount; ++iAnim ) + { + if ( !Q_stricmp( m_BMPResData.m_aAnimations[iAnim].m_pszName, pszName ) ) + return iAnim; + } + + return iIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBaseModelPanel::FindSequenceFromActivity( CStudioHdr *pStudioHdr, const char *pszActivity ) +{ + if ( !pStudioHdr ) + return -1; + + for ( int iSeq = 0; iSeq < pStudioHdr->GetNumSeq(); ++iSeq ) + { + mstudioseqdesc_t &seqDesc = pStudioHdr->pSeqdesc( iSeq ); + if ( !V_stricmp( seqDesc.pszActivityName(), pszActivity ) ) + { + return iSeq; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetModelAnim( int iAnim ) +{ + int nAnimCount = m_BMPResData.m_aAnimations.Count(); + if ( nAnimCount == 0 || !m_BMPResData.m_aAnimations.IsValidIndex( iAnim ) ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + // Get the studio header of the root model. + studiohdr_t *pStudioHdr = m_RootMDL.m_MDL.GetStudioHdr(); + if ( !pStudioHdr ) + return; + + CStudioHdr studioHdr( pStudioHdr, g_pMDLCache ); + + // Do we have an activity or a sequence? + int iSequence = ACT_INVALID; + if ( m_BMPResData.m_aAnimations[iAnim].m_pszActivity && m_BMPResData.m_aAnimations[iAnim].m_pszActivity[0] ) + { + iSequence = FindSequenceFromActivity( &studioHdr, m_BMPResData.m_aAnimations[iAnim].m_pszActivity ); + } + else if ( m_BMPResData.m_aAnimations[iAnim].m_pszSequence && m_BMPResData.m_aAnimations[iAnim].m_pszSequence[0] ) + { + iSequence = LookupSequence( &studioHdr, m_BMPResData.m_aAnimations[iAnim].m_pszSequence ); + } + + if ( iSequence != ACT_INVALID ) + { + SetSequence( iSequence, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetMDL( MDLHandle_t handle, void *pProxyData ) +{ + MDLCACHE_CRITICAL_SECTION(); + studiohdr_t *pHdr = g_pMDLCache->GetStudioHdr( handle ); + + if ( pHdr ) + { + // SetMDL will cause the base CMdl code to set our localtoglobal indices if they aren't set. + // We set them up here so that they're left alone by that code. + CStudioHdr studioHdr( pHdr, g_pMDLCache ); + if (studioHdr.numflexcontrollers() > 0 && studioHdr.pFlexcontroller( LocalFlexController_t(0) )->localToGlobal == -1) + { + for (LocalFlexController_t i = LocalFlexController_t(0); i < studioHdr.numflexcontrollers(); i++) + { + int j = C_BaseFlex::AddGlobalFlexController( studioHdr.pFlexcontroller( i )->pszName() ); + studioHdr.pFlexcontroller( i )->localToGlobal = j; + } + } + } + else + { + handle = MDLHANDLE_INVALID; + } + + // Clear our current sequence + SetSequence( ACT_IDLE ); + + BaseClass::SetMDL( handle, pProxyData ); + + SetupModelDefaults(); + + // Need to invalidate the layout so the panel will adjust is LookAt for the new model. + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ) +{ + BaseClass::SetModelAnglesAndPosition( angRot, vecPos ); + + // Cache + m_vecPlayerPos = vecPos; + m_angPlayer = angRot; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::SetMDL( const char *pMDLName, void *pProxyData ) +{ + BaseClass::SetMDL( pMDLName, pProxyData ); + + // Need to invalidate the layout so the panel will adjust is LookAt for the new model. +// InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_bForcedCameraPosition ) + { + return; + } + + if ( m_bAllowFullManipulation ) + { + // Set this to true if you want to keep the current rotation when changing models or poses + const bool bPreserveManipulation = false; + + // Need to look at the target so we can rotate around it + const Vector kVecFocalPoint( 0.0f, 0.0f, 60.0f ); + ResetCameraPivot(); + SetCameraOffset( -(m_vecPlayerPos + kVecFocalPoint) ); + SetCameraPositionAndAngles( kVecFocalPoint, vec3_angle, !bPreserveManipulation ); + + // We want to move the player to the origin and facing the correct way, + // but don't clobber m_angPlayer and m_vecPlayerPos, so use BaseClass. + BaseClass::SetModelAnglesAndPosition( m_angPlayer, vec3_origin ); + + // Once a manual transform has been done we want to apply it + if ( m_bApplyManipulators ) + { + ApplyManipulation(); + } + else + { + SyncManipulation(); + } + return; + } + + if ( m_bForcePos ) + { + ResetCameraPivot(); + SetCameraOffset( Vector( 0.0f, 0.0f, 0.0f ) ); + SetCameraPositionAndAngles( vec3_origin, vec3_angle ); + SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); + } + + // Center and fill the frame with the model? + if ( m_bStartFramed ) + { + Vector vecBoundsMin, vecBoundsMax; + if ( GetBoundingBox( vecBoundsMin, vecBoundsMax ) ) + { + LookAtBounds( vecBoundsMin, vecBoundsMax ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnTick() +{ + // Cycle stuff gets handled in mdlpanel::OnTick, so we want to fix up + // what our sequence is before it gets called. + + // Check if we have a active sequence, and if it's expired and we need + // to run our default + if ( m_nActiveSequence != ACT_INVALID ) + { + float flElapsedTime = GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime; + if ( flElapsedTime >= m_flActiveSequenceDuration ) + { + m_nActiveSequence = ACT_INVALID; + m_flActiveSequenceDuration = 0.f; + + SetupModelDefaults(); + } + } + + BaseClass::OnTick(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnKeyCodePressed ( vgui::KeyCode code ) +{ + if ( m_bAllowFullManipulation ) + { + BaseClass::OnKeyCodePressed( code ); + return; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnKeyCodeReleased( vgui::KeyCode code ) +{ + if ( m_bAllowFullManipulation ) + { + BaseClass::OnKeyCodeReleased( code ); + return; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnMousePressed ( vgui::MouseCode code ) +{ + if ( m_bAllowFullManipulation ) + { + BaseClass::OnMousePressed( code ); + return; + } + + if ( !m_bAllowRotation && !m_bAllowPitch ) + return; + + RequestFocus(); + + EnableMouseCapture( true, code ); + + // Save where they clicked + input()->GetCursorPosition( m_nClickStartX, m_nClickStartY ); + + // Warp the mouse to the center of the screen + int width, height; + GetSize( width, height ); + int x = width / 2; + int y = height / 2; + + int xpos = x; + int ypos = y; + LocalToScreen( xpos, ypos ); + input()->SetCursorPos( xpos, ypos ); + + m_nManipStartX = xpos; + m_nManipStartY = ypos; + + m_bMousePressed = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnMouseReleased( vgui::MouseCode code ) +{ + if ( m_bAllowFullManipulation ) + { + BaseClass::OnMouseReleased( code ); + return; + } + + if ( !m_bAllowRotation && !m_bAllowPitch ) + return; + + EnableMouseCapture( false ); + m_bMousePressed = false; + + // Restore the cursor to where the clicked + input()->SetCursorPos( m_nClickStartX, m_nClickStartY ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnCursorMoved( int x, int y ) +{ + if ( m_bAllowFullManipulation ) + { + if ( m_pCurrentManip ) + { + m_bApplyManipulators = true; + } + BaseClass::OnCursorMoved( x, y ); + return; + } + + if ( !m_bAllowRotation && !m_bAllowPitch ) + return; + + if ( m_bMousePressed ) + { + WarpMouse( x, y ); + int xpos, ypos; + input()->GetCursorPos( xpos, ypos ); + + if ( m_bAllowRotation ) + { + // Only want the x delta. + float flDelta = xpos - m_nManipStartX; + + + // Apply the delta and rotate the player. + RotateYaw( flDelta ); + } + + if ( m_bAllowPitch ) + { + // Only want the y delta. + float flDelta = ypos - m_nManipStartY; + + + // Apply the delta and rotate the player. + RotatePitch( flDelta ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::RotateYaw( float flDelta ) +{ + m_angPlayer.y += flDelta; + if ( m_angPlayer.y > 360.0f ) + { + m_angPlayer.y = m_angPlayer.y - 360.0f; + } + else if ( m_angPlayer.y < -360.0f ) + { + m_angPlayer.y = m_angPlayer.y + 360.0f; + } + + SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::RotatePitch( float flDelta ) +{ + m_angPlayer.x += flDelta; + if ( m_angPlayer.x > m_flMaxPitch ) + { + m_angPlayer.x = m_flMaxPitch; + } + else if ( m_angPlayer.x < -m_flMaxPitch ) + { + m_angPlayer.x = -m_flMaxPitch; + } + + SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CBaseModelPanel::GetPlayerPos() const +{ + return m_vecPlayerPos; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +QAngle CBaseModelPanel::GetPlayerAngles() const +{ + return m_angPlayer; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::PlaySequence( const char *pszSequenceName ) +{ + CStudioHdr studioHDR( GetStudioHdr(), g_pMDLCache ); + int iSeq = ::LookupSequence( &studioHDR, pszSequenceName ); + if ( iSeq != ACT_INVALID ) + { + m_nActiveSequence = iSeq; + m_flActiveSequenceDuration = Studio_Duration( &studioHDR, iSeq, NULL ); + SetSequence( m_nActiveSequence, true ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseModelPanel::OnMouseWheeled( int delta ) +{ + if ( m_bAllowFullManipulation ) + { + BaseClass::OnMouseWheeled( delta ); + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the camera to a distance that allows the object to fill the model panel. +//----------------------------------------------------------------------------- +void CBaseModelPanel::LookAtBounds( const Vector &vecBoundsMin, const Vector &vecBoundsMax ) +{ + // Get the model space render bounds. + Vector vecMin = vecBoundsMin; + Vector vecMax = vecBoundsMax; + Vector vecCenter = ( vecMax + vecMin ) * 0.5f; + vecMin -= vecCenter; + vecMax -= vecCenter; + + // Get the bounds points and transform them by the desired model panel rotation. + Vector aBoundsPoints[8]; + aBoundsPoints[0].Init( vecMax.x, vecMax.y, vecMax.z ); + aBoundsPoints[1].Init( vecMin.x, vecMax.y, vecMax.z ); + aBoundsPoints[2].Init( vecMax.x, vecMin.y, vecMax.z ); + aBoundsPoints[3].Init( vecMin.x, vecMin.y, vecMax.z ); + aBoundsPoints[4].Init( vecMax.x, vecMax.y, vecMin.z ); + aBoundsPoints[5].Init( vecMin.x, vecMax.y, vecMin.z ); + aBoundsPoints[6].Init( vecMax.x, vecMin.y, vecMin.z ); + aBoundsPoints[7].Init( vecMin.x, vecMin.y, vecMin.z ); + + // Translated center point (offset from camera center). + Vector vecTranslateCenter = -vecCenter; + + // Build the rotation matrix. + matrix3x4_t matRotation; + AngleMatrix( m_BMPResData.m_angModelPoseRot, matRotation ); + + Vector aXFormPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + VectorTransform( aBoundsPoints[iPoint], matRotation, aXFormPoints[iPoint] ); + } + + Vector vecXFormCenter; + VectorTransform( -vecTranslateCenter, matRotation, vecXFormCenter ); + + int w, h; + GetSize( w, h ); + float flW = (float)w; + float flH = (float)h; + + float flFOVx = DEG2RAD( m_BMPResData.m_flFOV * 0.5f ); + float flFOVy = CalcFovY( ( m_BMPResData.m_flFOV * 0.5f ), flW/flH ); + flFOVy = DEG2RAD( flFOVy ); + + float flTanFOVx = tan( flFOVx ); + float flTanFOVy = tan( flFOVy ); + + // Find the max value of x, y, or z + Vector2D dist[8]; + float flDist = 0.0f; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + float flDistY = fabs( aXFormPoints[iPoint].y / flTanFOVx ) - aXFormPoints[iPoint].x; + float flDistZ = fabs( aXFormPoints[iPoint].z / flTanFOVy ) - aXFormPoints[iPoint].x; + dist[iPoint].x = flDistY; + dist[iPoint].y = flDistZ; + float flTestDist = MAX( flDistZ, flDistY ); + flDist = MAX( flDist, flTestDist ); + } + + // Screen space points. + Vector2D aScreenPoints[8]; + Vector aCameraPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + aCameraPoints[iPoint] = aXFormPoints[iPoint]; + aCameraPoints[iPoint].x += flDist; + + aScreenPoints[iPoint].x = aCameraPoints[iPoint].y / ( flTanFOVx * aCameraPoints[iPoint].x ); + aScreenPoints[iPoint].y = aCameraPoints[iPoint].z / ( flTanFOVy * aCameraPoints[iPoint].x ); + + aScreenPoints[iPoint].x = ( aScreenPoints[iPoint].x * 0.5f + 0.5f ) * flW; + aScreenPoints[iPoint].y = ( aScreenPoints[iPoint].y * 0.5f + 0.5f ) * flH; + } + + // Find the min/max and center of the 2D bounding box of the object. + Vector2D vecScreenMin( 99999.0f, 99999.0f ), vecScreenMax( -99999.0f, -99999.0f ); + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + vecScreenMin.x = MIN( vecScreenMin.x, aScreenPoints[iPoint].x ); + vecScreenMin.y = MIN( vecScreenMin.y, aScreenPoints[iPoint].y ); + vecScreenMax.x = MAX( vecScreenMax.x, aScreenPoints[iPoint].x ); + vecScreenMax.y = MAX( vecScreenMax.y, aScreenPoints[iPoint].y ); + } + + // Offset the model to the be the correct distance away from the camera. + Vector vecModelPos; + vecModelPos.x = flDist - vecXFormCenter.x; + vecModelPos.y = -vecXFormCenter.y; + vecModelPos.z = -vecXFormCenter.z; + SetModelAnglesAndPosition( m_BMPResData.m_angModelPoseRot, vecModelPos ); + m_vecPlayerPos = vecModelPos; + + // Back project to figure out the camera offset to center the model. + Vector2D vecPanelCenter( ( flW * 0.5f ), ( flH * 0.5f ) ); + Vector2D vecScreenCenter = ( vecScreenMax + vecScreenMin ) * 0.5f; + + Vector2D vecPanelCenterCamera, vecScreenCenterCamera; + vecPanelCenterCamera.x = ( ( vecPanelCenter.x / flW ) * 2.0f ) - 0.5f; + vecPanelCenterCamera.y = ( ( vecPanelCenter.y / flH ) * 2.0f ) - 0.5f; + vecPanelCenterCamera.x *= ( flTanFOVx * flDist ); + vecPanelCenterCamera.y *= ( flTanFOVy * flDist ); + vecScreenCenterCamera.x = ( ( vecScreenCenter.x / flW ) * 2.0f ) - 0.5f; + vecScreenCenterCamera.y = ( ( vecScreenCenter.y / flH ) * 2.0f ) - 0.5f; + vecScreenCenterCamera.x *= ( flTanFOVx * flDist ); + vecScreenCenterCamera.y *= ( flTanFOVy * flDist ); + + Vector2D vecCameraOffset( 0.0f, 0.0f ); + vecCameraOffset.x = vecPanelCenterCamera.x - vecScreenCenterCamera.x; + vecCameraOffset.y = vecPanelCenterCamera.y - vecScreenCenterCamera.y; + + // Clear the camera pivot and set position matrix. + ResetCameraPivot(); + if (m_bAllowRotation || m_bAllowPitch ) + { + vecCameraOffset.x = 0.0f; + } + SetCameraOffset( Vector( 0.0f, -vecCameraOffset.x, -vecCameraOffset.y ) ); + UpdateCameraTransform(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseModelPanel::particle_data_t::~particle_data_t() +{ + if ( m_pParticleSystem ) + { + delete m_pParticleSystem; + m_pParticleSystem = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocate particle data +//----------------------------------------------------------------------------- +void CBaseModelPanel::particle_data_t::UpdateControlPoints( CStudioHdr *pStudioHdr, matrix3x4_t *pWorldMatrix, const CUtlVector< int >& vecAttachments, int iDefaultBone /*= 0*/, const Vector& vecParticleOffset /*= vec3_origin*/ ) +{ + if ( m_pParticleSystem ) + { + // Update control points which is updating the position of the particles + matrix3x4_t matAttachToWorld; + Vector vecPosition, vecForward, vecRight, vecUp; + if ( vecAttachments.Count() ) + { + for ( int i = 0; i < vecAttachments.Count(); ++i ) + { + const mstudioattachment_t& attach = pStudioHdr->pAttachment( vecAttachments[i] ); + MatrixMultiply( pWorldMatrix[ attach.localbone ], attach.local, matAttachToWorld ); + + MatrixVectors( matAttachToWorld, &vecForward, &vecRight, &vecUp ); + MatrixPosition( matAttachToWorld, vecPosition ); + + m_pParticleSystem->SetControlPointOrientation( i, vecForward, vecRight, vecUp ); + m_pParticleSystem->SetControlPoint( i, vecPosition + vecParticleOffset ); + } + } + else + { + matAttachToWorld = pWorldMatrix[iDefaultBone]; + MatrixVectors( matAttachToWorld, &vecForward, &vecRight, &vecUp ); + MatrixPosition( matAttachToWorld, vecPosition ); + + m_pParticleSystem->SetControlPointOrientation( 0, vecForward, vecRight, vecUp ); + m_pParticleSystem->SetControlPoint( 0, vecPosition + vecParticleOffset ); + } + } + + m_bIsUpdateToDate = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocate particle data +//----------------------------------------------------------------------------- +CBaseModelPanel::particle_data_t *CBaseModelPanel::CreateParticleData( const char *pszParticleName ) +{ + Assert( m_bUseParticle ); + if ( !m_bUseParticle ) + return NULL; + + CParticleCollection *pParticle = g_pParticleSystemMgr->CreateParticleCollection( pszParticleName ); + if ( !pParticle ) + return NULL; + + particle_data_t *pData = new particle_data_t; + pData->m_bIsUpdateToDate = false; + pData->m_pParticleSystem = pParticle; + + m_particleList.AddToTail( pData ); + + return pData; +} + + +//----------------------------------------------------------------------------- +// Purpose: remove and delete particle data +//----------------------------------------------------------------------------- +bool CBaseModelPanel::SafeDeleteParticleData( particle_data_t **pData ) +{ + if ( !m_bUseParticle ) + return false; + + if ( *pData ) + { + FOR_EACH_VEC( m_particleList, i ) + { + if ( *pData == m_particleList[i] ) + { + delete *pData; + *pData = NULL; + m_particleList.FastRemove( i ); + return true; + } + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::PrePaint3D( IMatRenderContext *pRenderContext ) +{ + if ( !m_bUseParticle ) + return; + + // mark all effects need to be updated + FOR_EACH_VEC( m_particleList, i ) + { + m_particleList[i]->m_bIsUpdateToDate = false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseModelPanel::PostPaint3D( IMatRenderContext *pRenderContext ) +{ + if ( !m_bUseParticle ) + return; + + // This needs calling to reset various counters. + g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime ); + + // Render Particles + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity( ); + + FOR_EACH_VEC( m_particleList, i ) + { + if ( m_particleList[i]->m_bIsUpdateToDate ) + { + m_particleList[i]->m_pParticleSystem->Simulate( gpGlobals->frametime, false ); + m_particleList[i]->m_pParticleSystem->Render( pRenderContext ); + m_particleList[i]->m_bIsUpdateToDate = false; + } + } + + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PopMatrix(); +} + diff --git a/game/client/game_controls/basemodel_panel.h b/game/client/game_controls/basemodel_panel.h new file mode 100644 index 0000000..344b990 --- /dev/null +++ b/game/client/game_controls/basemodel_panel.h @@ -0,0 +1,254 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#ifndef BASEMODEL_PANEL_H +#define BASEMODEL_PANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "matsys_controls/mdlpanel.h" + +//----------------------------------------------------------------------------- +// Resource file data used in posing the model inside of the model panel. +//----------------------------------------------------------------------------- +struct BMPResAnimData_t +{ + const char *m_pszName; + const char *m_pszSequence; + const char *m_pszActivity; + KeyValues *m_pPoseParameters; + bool m_bDefault; + + BMPResAnimData_t() + { + m_pszName = NULL; + m_pszSequence = NULL; + m_pszActivity = NULL; + m_pPoseParameters = NULL; + m_bDefault = false; + } + + ~BMPResAnimData_t() + { + if ( m_pszName && m_pszName[0] ) + { + delete [] m_pszName; + m_pszName = NULL; + } + + if ( m_pszSequence && m_pszSequence[0] ) + { + delete [] m_pszSequence; + m_pszSequence = NULL; + } + + if ( m_pszActivity && m_pszActivity[0] ) + { + delete [] m_pszActivity; + m_pszActivity = NULL; + } + + if ( m_pPoseParameters ) + { + m_pPoseParameters->deleteThis(); + m_pPoseParameters = NULL; + } + } +}; + +struct BMPResAttachData_t +{ + const char *m_pszModelName; + int m_nSkin; + + BMPResAttachData_t() + { + m_pszModelName = NULL; + m_nSkin = 0; + } + + ~BMPResAttachData_t() + { + if ( m_pszModelName && m_pszModelName[0] ) + { + delete [] m_pszModelName; + m_pszModelName = NULL; + } + } +}; + +struct BMPResData_t +{ + float m_flFOV; + + const char *m_pszModelName; + const char *m_pszModelName_HWM; + const char *m_pszVCD; + QAngle m_angModelPoseRot; + Vector m_vecOriginOffset; + Vector m_vecFramedOriginOffset; + Vector2D m_vecViewportOffset; + int m_nSkin; + bool m_bUseSpotlight; + + CUtlVector<BMPResAnimData_t> m_aAnimations; + CUtlVector<BMPResAttachData_t> m_aAttachModels; + + BMPResData_t() + { + m_flFOV = 0.0f; + + m_pszModelName = NULL; + m_pszModelName_HWM = NULL; + m_pszVCD = NULL; + m_angModelPoseRot.Init(); + m_vecOriginOffset.Init(); + m_vecFramedOriginOffset.Init(); + m_vecViewportOffset.Init(); + m_nSkin = 0; + m_bUseSpotlight = false; + } + + ~BMPResData_t() + { + if ( m_pszModelName && m_pszModelName[0] ) + { + delete [] m_pszModelName; + m_pszModelName = NULL; + } + + if ( m_pszModelName_HWM && m_pszModelName_HWM[0] ) + { + delete [] m_pszModelName_HWM; + m_pszModelName_HWM = NULL; + } + + if ( m_pszVCD && m_pszVCD[0] ) + { + delete [] m_pszVCD; + m_pszVCD = NULL; + } + + m_aAnimations.Purge(); + m_aAttachModels.Purge(); + } +}; + +//----------------------------------------------------------------------------- +// Base Model Panel +// +// ...vgui::Panel |--> vgui +// +->vgui::EditablePanel | +// +->PotterWheelPanel |--> matsys_controls +// +->MDLPanel | +// +->BaseModelPanel |--> game_controls, client.dll +// +//----------------------------------------------------------------------------- +class CBaseModelPanel : public CMDLPanel +{ + DECLARE_CLASS_SIMPLE( CBaseModelPanel, CMDLPanel ); + +public: + + // Constructor, Destructor. + CBaseModelPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CBaseModelPanel(); + + // Overridden mdlpanel.h + virtual void SetMDL( MDLHandle_t handle, void *pProxyData = NULL ); + virtual void SetMDL( const char *pMDLName, void *pProxyData = NULL ); + virtual void SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ); + + // Overridden methods of vgui::Panel + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void PerformLayout(); + virtual void OnTick() OVERRIDE; + + // Animation. + int FindDefaultAnim( void ); + int FindAnimByName( const char *pszName ); + void SetModelAnim( int iAnim ); + + // Manipulation. + virtual void OnKeyCodePressed ( vgui::KeyCode code ); + virtual void OnKeyCodeReleased( vgui::KeyCode code ); + virtual void OnMousePressed ( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnCursorMoved( int x, int y ); + virtual void OnMouseWheeled( int delta ); + + studiohdr_t* GetStudioHdr( void ) { return m_RootMDL.m_MDL.GetStudioHdr(); } + void SetBody( unsigned int nBody ) { m_RootMDL.m_MDL.m_nBody = nBody; } + + void RotateYaw( float flDelta ); + void RotatePitch( float flDelta ); + + Vector GetPlayerPos() const; + QAngle GetPlayerAngles() const; + + void PlaySequence( const char *pszSequenceName ); + + void LookAtBounds( const Vector &vecBoundsMin, const Vector &vecBoundsMax ); + + // Set to true if external code has set a specific camera position that shouldn't be clobbered by layout + void SetForcedCameraPosition( bool bForcedCameraPosition ) { m_bForcedCameraPosition = bForcedCameraPosition; } + + int FindSequenceFromActivity( CStudioHdr *pStudioHdr, const char *pszActivity ); + +protected: + + // Resource file data. + void ParseModelResInfo( KeyValues *inResourceData ); + void ParseModelAnimInfo( KeyValues *inResourceData ); + void ParseModelAttachInfo( KeyValues *inResourceData ); + + void SetupModelDefaults( void ); + void SetupModelAnimDefaults( void ); + +public: + BMPResData_t m_BMPResData; // Base model panel data set in the .res file. + QAngle m_angPlayer; + Vector m_vecPlayerPos; + +protected: + bool m_bForcePos; + bool m_bMousePressed; + bool m_bAllowRotation; + bool m_bAllowPitch; + bool m_bAllowFullManipulation; + bool m_bApplyManipulators; + bool m_bForcedCameraPosition; + + int m_nActiveSequence; + float m_flActiveSequenceDuration; + + // VGUI script accessible variables. + CPanelAnimationVar( bool, m_bStartFramed, "start_framed", "0" ); + CPanelAnimationVar( bool, m_bDisableManipulation, "disable_manipulation", "0" ); + CPanelAnimationVar( bool, m_bUseParticle, "use_particle", "0" ); + CPanelAnimationVar( float, m_flMaxPitch, "max_pitch", "90" ); + + struct particle_data_t + { + ~particle_data_t(); + + void UpdateControlPoints( CStudioHdr *pStudioHdr, matrix3x4_t *pWorldMatrix, const CUtlVector< int >& vecAttachments, int iDefaultBone = 0, const Vector& vecParticleOffset = vec3_origin ); + + bool m_bIsUpdateToDate; + CParticleCollection *m_pParticleSystem; + }; + CUtlVector< particle_data_t* > m_particleList; + + + + particle_data_t *CreateParticleData( const char *pszParticleName ); + bool SafeDeleteParticleData( particle_data_t **pData ); + + virtual void PrePaint3D( IMatRenderContext *pRenderContext ) OVERRIDE; + virtual void PostPaint3D( IMatRenderContext *pRenderContext ) OVERRIDE; +}; + +#endif // BASEMODEL_PANEL_H
\ No newline at end of file diff --git a/game/client/game_controls/basemodelpanel.cpp b/game/client/game_controls/basemodelpanel.cpp new file mode 100644 index 0000000..2058873 --- /dev/null +++ b/game/client/game_controls/basemodelpanel.cpp @@ -0,0 +1,947 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include <KeyValues.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui/IScheme.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/EditablePanel.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui/ISurface.h> +#include <vgui/IImage.h> +#include <vgui_controls/Label.h> + +#include "materialsystem/imaterialsystem.h" +#include "engine/ivmodelinfo.h" + +#include "c_sceneentity.h" +#include "gamestringpool.h" +#include "model_types.h" +#include "view_shared.h" +#include "view.h" +#include "ivrenderview.h" +#include "iefx.h" +#include "dlight.h" +#include "activitylist.h" + +#include "basemodelpanel.h" + +bool UseHWMorphModels(); + + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CModelPanel ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CModelPanel::CModelPanel( vgui::Panel *pParent, const char *pName ) : vgui::EditablePanel( pParent, pName ) +{ + m_nFOV = 54; + m_hModel = NULL; + m_pModelInfo = NULL; + m_hScene = NULL; + m_iDefaultAnimation = 0; + m_bPanelDirty = true; + m_bStartFramed = false; + m_bAllowOffscreen = false; + + ListenForGameEvent( "game_newmap" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CModelPanel::~CModelPanel() +{ + if ( m_pModelInfo ) + { + delete m_pModelInfo; + m_pModelInfo = NULL; + } + + DeleteVCDData(); + DeleteModelData(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_nFOV = inResourceData->GetInt( "fov", 54 ); + m_bStartFramed = inResourceData->GetInt( "start_framed", false ); + m_bAllowOffscreen = inResourceData->GetInt( "allow_offscreen", false ); + + // do we have a valid "model" section in the .res file? + for ( KeyValues *pData = inResourceData->GetFirstSubKey() ; pData != NULL ; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "model" ) ) + { + ParseModelInfo( pData ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnCommand( const char *command ) +{ + if (!Q_strnicmp("animation", command, 9)) + { + UpdateModel(); + SetSequence( command + 9 + 1 ); + return; + } + + BaseClass::OnCommand(command); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ParseModelInfo( KeyValues *inResourceData ) +{ + // delete any current info + if ( m_pModelInfo ) + { + delete m_pModelInfo; + m_pModelInfo = NULL; + } + + m_pModelInfo = new CModelPanelModelInfo; + + if ( !m_pModelInfo ) + return; + + m_pModelInfo->m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); + m_pModelInfo->m_pszModelName_HWM = ReadAndAllocStringValue( inResourceData, "modelname_hwm" ); + m_pModelInfo->m_nSkin = inResourceData->GetInt( "skin", -1 ); + m_pModelInfo->m_vecAbsAngles.Init( inResourceData->GetFloat( "angles_x", 0.0 ), inResourceData->GetFloat( "angles_y", 0.0 ), inResourceData->GetFloat( "angles_z", 0.0 ) ); + m_pModelInfo->m_vecOriginOffset.Init( inResourceData->GetFloat( "origin_x", 110.0 ), inResourceData->GetFloat( "origin_y", 5.0 ), inResourceData->GetFloat( "origin_z", 5.0 ) ); + m_pModelInfo->m_vecFramedOriginOffset.Init( inResourceData->GetFloat( "frame_origin_x", 110.0 ), inResourceData->GetFloat( "frame_origin_y", 5.0 ), inResourceData->GetFloat( "frame_origin_z", 5.0 ) ); + m_pModelInfo->m_pszVCD = ReadAndAllocStringValue( inResourceData, "vcd" ); + m_pModelInfo->m_bUseSpotlight = ( inResourceData->GetInt( "spotlight", 0 ) == 1 ); + m_pModelInfo->m_vecViewportOffset.Init(); + + for ( KeyValues *pData = inResourceData->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "animation" ) ) + { + OnAddAnimation( pData ); + } + else if ( !Q_stricmp( pData->GetName(), "attached_model" ) ) + { + CModelPanelAttachedModelInfo *pAttachedModelInfo = new CModelPanelAttachedModelInfo; + + if ( pAttachedModelInfo ) + { + pAttachedModelInfo->m_pszModelName = ReadAndAllocStringValue( pData, "modelname" ); + pAttachedModelInfo->m_nSkin = pData->GetInt( "skin", -1 ); + + m_pModelInfo->m_AttachedModelsInfo.AddToTail( pAttachedModelInfo ); + } + } + } + + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnAddAnimation( KeyValues *pData ) +{ + if ( !pData ) + return; + + CModelPanelModelAnimation *pAnimation = new CModelPanelModelAnimation; + + if ( pAnimation ) + { + pAnimation->m_pszName = ReadAndAllocStringValue( pData, "name" ); + pAnimation->m_pszSequence = ReadAndAllocStringValue( pData, "sequence" ); + pAnimation->m_pszActivity = ReadAndAllocStringValue( pData, "activity" ); + pAnimation->m_bDefault = ( pData->GetInt( "default", 0 ) == 1 ); + + for ( KeyValues *pAnimData = pData->GetFirstSubKey(); pAnimData != NULL; pAnimData = pAnimData->GetNextKey() ) + { + if ( !Q_stricmp( pAnimData->GetName(), "pose_parameters" ) ) + { + pAnimation->m_pPoseParameters = pAnimData->MakeCopy(); + } + } + + m_pModelInfo->m_Animations.AddToTail( pAnimation ); + if ( pAnimation->m_bDefault ) + { + m_iDefaultAnimation = m_pModelInfo->m_Animations.Find( pAnimation ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::FireGameEvent( IGameEvent * event ) +{ + const char *type = event->GetName(); + + if ( Q_strcmp( type, "game_newmap" ) == 0 ) + { + // force the models to re-setup themselves + m_bPanelDirty = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetDefaultAnimation( const char *pszName ) +{ + if ( m_pModelInfo ) + { + for ( int i = 0; i < m_pModelInfo->m_Animations.Count(); i++ ) + { + if ( m_pModelInfo->m_Animations[i] && m_pModelInfo->m_Animations[i]->m_pszName ) + { + if ( !Q_stricmp( m_pModelInfo->m_Animations[i]->m_pszName, pszName ) ) + { + m_iDefaultAnimation = i; + return; + } + } + } + } + + Assert( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Replaces the current model with a new one, without changing the camera settings +//----------------------------------------------------------------------------- +void CModelPanel::SwapModel( const char *pszName, const char *pszAttached ) +{ + if ( !m_pModelInfo || !pszName || !pszName[0] ) + return; + + int len = Q_strlen( pszName ) + 1; + char *pAlloced = new char[ len ]; + Assert( pAlloced ); + Q_strncpy( pAlloced, pszName, len ); + m_pModelInfo->m_pszModelName = pAlloced; + + ClearAttachedModelInfos(); + + if ( pszAttached ) + { + CModelPanelAttachedModelInfo *pAttachedModelInfo = new CModelPanelAttachedModelInfo; + if ( pAttachedModelInfo ) + { + len = Q_strlen( pszAttached ) + 1; + pAlloced = new char[ len ]; + Assert( pAlloced ); + Q_strncpy( pAlloced, pszAttached, len ); + pAttachedModelInfo->m_pszModelName = pAlloced; + pAttachedModelInfo->m_nSkin = 0; + + m_pModelInfo->m_AttachedModelsInfo.AddToTail( pAttachedModelInfo ); + } + } + + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::DeleteVCDData( void ) +{ + if ( m_hScene.Get() ) + { + m_hScene->StopClientOnlyScene(); + + m_hScene->Remove(); + m_hScene = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetupVCD( void ) +{ + if ( !m_pModelInfo ) + return; + + DeleteVCDData(); + + C_SceneEntity *pEnt = new class C_SceneEntity; + + if ( !pEnt ) + return; + + if ( pEnt->InitializeAsClientEntity( "", RENDER_GROUP_OTHER ) == false ) + { + // we failed to initialize this entity so just return gracefully + pEnt->Remove(); + return; + } + + // setup the handle + m_hScene = pEnt; + + // setup the scene + pEnt->SetupClientOnlyScene( m_pModelInfo->m_pszVCD, m_hModel, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ClearAttachedModelInfos( void ) +{ + if ( m_pModelInfo ) + { + m_pModelInfo->m_AttachedModelsInfo.PurgeAndDeleteElements(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::DeleteModelData( void ) +{ + if ( m_hModel.Get() ) + { + m_hModel->Remove(); + m_hModel = NULL; + m_flFrameDistance = 0; + } + + for ( int i = 0 ; i < m_AttachedModels.Count() ; i++ ) + { + if ( m_AttachedModels[i].Get() ) + { + m_AttachedModels[i]->Remove(); + } + m_AttachedModels.Remove( i ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CModelPanel::GetModelName( void ) +{ + if ( !m_pModelInfo ) + return NULL; + + // check to see if we want to use a HWM model + if ( UseHWMorphModels() ) + { + // do we have a valid HWM model filename + if ( m_pModelInfo->m_pszModelName_HWM && ( Q_strlen( m_pModelInfo->m_pszModelName_HWM ) > 0 ) ) + { + // does the file exist + model_t *pModel = (model_t *)engine->LoadModel( m_pModelInfo->m_pszModelName_HWM ); + if ( pModel ) + { + return m_pModelInfo->m_pszModelName_HWM; + } + } + } + + return m_pModelInfo->m_pszModelName; +} + +void CModelPanel::SetBodyGroup( const char* pszBodyGroupName, int nGroup ) +{ + if ( !m_pModelInfo ) + return; + + if ( !m_hModel.Get() ) + return; + + int nBodyGroupNum = m_hModel->FindBodygroupByName( pszBodyGroupName ); + + if ( nBodyGroupNum == -1 ) + return; + + m_pModelInfo->m_mapBodygroupValues.InsertOrReplace( nBodyGroupNum, nGroup ); + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetupModel( void ) +{ + if ( !m_pModelInfo ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + // remove any current models we're using + DeleteModelData(); + + const char *pszModelName = GetModelName(); + if ( !pszModelName || !pszModelName[0] ) + return; + + // create the new model + CModelPanelModel *pEnt = new CModelPanelModel; + + if ( !pEnt ) + return; + + if ( pEnt->InitializeAsClientEntity( pszModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + // we failed to initialize this entity so just return gracefully + pEnt->Remove(); + return; + } + + // setup the handle + m_hModel = pEnt; + + pEnt->DontRecordInTools(); + pEnt->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + + if ( m_pModelInfo->m_nSkin >= 0 ) + { + pEnt->m_nSkin = m_pModelInfo->m_nSkin; + } + + FOR_EACH_MAP_FAST( m_pModelInfo->m_mapBodygroupValues, i ) + { + pEnt->SetBodygroup( m_pModelInfo->m_mapBodygroupValues.Key( i ), m_pModelInfo->m_mapBodygroupValues[ i ] ); + } + + // do we have any animation information? + if ( m_pModelInfo->m_Animations.Count() > 0 && m_pModelInfo->m_Animations.IsValidIndex( m_iDefaultAnimation ) ) + { + CModelPanelModelAnimation *pAnim = m_pModelInfo->m_Animations[ m_iDefaultAnimation ]; + int sequence = ACT_INVALID; + if ( pAnim->m_pszActivity && pAnim->m_pszActivity[0] ) + { + Activity activity = (Activity)ActivityList_IndexForName( pAnim->m_pszActivity ); + sequence = pEnt->SelectWeightedSequence( activity ); + } + else if ( pAnim->m_pszSequence && pAnim->m_pszSequence[0] ) + { + sequence = pEnt->LookupSequence( pAnim->m_pszSequence ); + } + if ( sequence != ACT_INVALID ) + { + pEnt->ResetSequence( sequence ); + pEnt->SetCycle( 0 ); + + if ( pAnim->m_pPoseParameters ) + { + for ( KeyValues *pData = pAnim->m_pPoseParameters->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + const char *pName = pData->GetName(); + float flValue = pData->GetFloat(); + + pEnt->SetPoseParameter( pName, flValue ); + } + } + + pEnt->m_flAnimTime = gpGlobals->curtime; + } + } + + // setup any attached models + for ( int i = 0 ; i < m_pModelInfo->m_AttachedModelsInfo.Count() ; i++ ) + { + CModelPanelAttachedModelInfo *pInfo = m_pModelInfo->m_AttachedModelsInfo[i]; + C_BaseAnimating *pTemp = new C_BaseAnimating; + + if ( pTemp ) + { + if ( pTemp->InitializeAsClientEntity( pInfo->m_pszModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + // we failed to initialize this model so just skip it + pTemp->Remove(); + continue; + } + + pTemp->DontRecordInTools(); + pTemp->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + pTemp->FollowEntity( m_hModel.Get() ); // attach to parent model + + if ( pInfo->m_nSkin >= 0 ) + { + pTemp->m_nSkin = pInfo->m_nSkin; + } + + pTemp->m_flAnimTime = gpGlobals->curtime; + m_AttachedModels.AddToTail( pTemp ); + } + } + + CalculateFrameDistance(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::InitCubeMaps() +{ + ITexture *pCubemapTexture; + + // Deal with the default cubemap + if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() ) + { + pCubemapTexture = materials->FindTexture( "editor/cubemap.hdr", NULL, true ); + m_DefaultHDREnvCubemap.Init( pCubemapTexture ); + } + else + { + pCubemapTexture = materials->FindTexture( "editor/cubemap", NULL, true ); + m_DefaultEnvCubemap.Init( pCubemapTexture ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: If the panel is marked as dirty, update it and mark it as clean +//----------------------------------------------------------------------------- +void CModelPanel::UpdateModel() +{ + if ( m_bPanelDirty ) + { + InitCubeMaps(); + + SetupModel(); + + // are we trying to play a VCD? + if ( Q_strlen( m_pModelInfo->m_pszVCD ) > 0 ) + { + SetupVCD(); + } + + m_bPanelDirty = false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::Paint() +{ + BaseClass::Paint(); + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pLocalPlayer || !m_pModelInfo ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + UpdateModel(); + + if ( !m_hModel.Get() ) + return; + + int i = 0; + int x, y, w, h; + + GetBounds( x, y, w, h ); + ParentLocalToScreen( x, y ); + + if ( !m_bAllowOffscreen && x < 0 ) + { + // prevent x from being pushed off the left side of the screen + // for modes like 1280 x 1024 (prevents model from being drawn in the panel) + x = 0; + } + + Vector vecExtraModelOffset( 0, 0, 0 ); + float flWidthRatio = ((float)w / (float)h ) / ( 4.0f / 3.0f ); + + // is this a player model? + if ( Q_strstr( GetModelName(), "models/player/" ) ) + { + // need to know if the ratio is not 4/3 + // HACK! HACK! to get our player models to appear the way they do in 4/3 if we're using other aspect ratios + if ( flWidthRatio > 1.05f ) + { + vecExtraModelOffset.Init( -60, 0, 0 ); + } + else if ( flWidthRatio < 0.95f ) + { + vecExtraModelOffset.Init( 15, 0, 0 ); + } + } + + m_hModel->SetAbsOrigin( m_pModelInfo->m_vecOriginOffset + vecExtraModelOffset ); + m_hModel->SetAbsAngles( QAngle( m_pModelInfo->m_vecAbsAngles.x, m_pModelInfo->m_vecAbsAngles.y, m_pModelInfo->m_vecAbsAngles.z ) ); + + // do we have a valid sequence? + if ( m_hModel->GetSequence() != -1 ) + { + m_hModel->FrameAdvance( gpGlobals->frametime ); + } + + CMatRenderContextPtr pRenderContext( materials ); + + // figure out what our viewport is right now + int viewportX, viewportY, viewportWidth, viewportHeight; + pRenderContext->GetViewport( viewportX, viewportY, viewportWidth, viewportHeight ); + + // Now draw it. + CViewSetup view; + view.x = x + m_pModelInfo->m_vecViewportOffset.x + viewportX; // we actually want to offset by the + view.y = y + m_pModelInfo->m_vecViewportOffset.y + viewportY; // viewport origin here because Push3DView expects global coords below + view.width = w; + view.height = h; + + view.m_bOrtho = false; + + // scale the FOV for aspect ratios other than 4/3 + view.fov = ScaleFOVByWidthRatio( m_nFOV, flWidthRatio ); + + view.origin = vec3_origin; + view.angles.Init(); + view.zNear = VIEW_NEARZ; + view.zFar = 1000; + + + + // Not supported by queued material system - doesn't appear to be necessary +// ITexture *pLocalCube = pRenderContext->GetLocalCubemap(); + + if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() ) + { + pRenderContext->BindLocalCubemap( m_DefaultHDREnvCubemap ); + } + else + { + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + } + + pRenderContext->SetLightingOrigin( vec3_origin ); + pRenderContext->SetAmbientLight( 0.4, 0.4, 0.4 ); + + static Vector white[6] = + { + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + }; + + g_pStudioRender->SetAmbientLightColors( white ); + g_pStudioRender->SetLocalLights( 0, NULL ); + + if ( m_pModelInfo->m_bUseSpotlight ) + { + Vector vecMins, vecMaxs; + m_hModel->GetRenderBounds( vecMins, vecMaxs ); + LightDesc_t spotLight( vec3_origin + Vector( 0, 0, 200 ), Vector( 1, 1, 1 ), m_hModel->GetAbsOrigin() + Vector( 0, 0, ( vecMaxs.z - vecMins.z ) * 0.75 ), 0.035, 0.873 ); + g_pStudioRender->SetLocalLights( 1, &spotLight ); + } + + Frustum dummyFrustum; + render->Push3DView( view, 0, NULL, dummyFrustum ); + + modelrender->SuppressEngineLighting( true ); + float color[3] = { 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( color ); + render->SetBlend( 1.0f ); + m_hModel->DrawModel( STUDIO_RENDER ); + + for ( i = 0 ; i < m_AttachedModels.Count() ; i++ ) + { + if ( m_AttachedModels[i].Get() ) + { + m_AttachedModels[i]->DrawModel( STUDIO_RENDER ); + } + } + + modelrender->SuppressEngineLighting( false ); + + render->PopView( dummyFrustum ); + + pRenderContext->BindLocalCubemap( NULL ); + + /* + vgui::surface()->DrawSetColor( Color(0,0,0,255) ); + vgui::surface()->DrawOutlinedRect( 0,0, GetWide(), GetTall() ); + */ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CModelPanel::FindAnimByName( const char *pszName ) +{ + // first try to find the sequence using pszName as the friendly name + for ( int iIndex = 0 ; iIndex < m_pModelInfo->m_Animations.Count() ; iIndex++ ) + { + CModelPanelModelAnimation *pAnimation = m_pModelInfo->m_Animations[ iIndex ]; + if ( FStrEq( pAnimation->m_pszName, pszName ) ) + return iIndex; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CModelPanel::SetSequence( const char *pszName ) +{ + bool bRetVal = false; + const char *pszAnim = NULL; + + MDLCACHE_CRITICAL_SECTION(); + + if ( m_pModelInfo ) + { + int iIndex = FindAnimByName(pszName); + if ( iIndex != -1 ) + { + pszAnim = m_pModelInfo->m_Animations[iIndex]->m_pszSequence; + } + else + { + // if not, just use the passed name as the sequence + pszAnim = pszName; + } + + if ( m_hModel.Get() ) + { + int sequence = m_hModel->LookupSequence( pszAnim ); + if ( sequence != ACT_INVALID ) + { + m_hModel->ResetSequence( sequence ); + m_hModel->SetCycle( 0 ); + + bRetVal = true; + } + } + } + + return bRetVal; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetSkin( int nSkin ) +{ + if ( m_pModelInfo ) + { + m_pModelInfo->m_nSkin = nSkin; + m_bPanelDirty = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnSetAnimation( KeyValues *data ) +{ + UpdateModel(); + + // If there's no model, these commands will be ignored. + Assert(m_hModel); + + if ( data ) + { + const char *pszAnimation = data->GetString( "animation", "" ); + const char *pszActivity = data->GetString( "activity", "" ); + if ( pszActivity && pszActivity[0] ) + { + if ( m_hModel ) + { + int iIndex = FindAnimByName(pszActivity); + if ( iIndex != -1 ) + { + pszActivity = m_pModelInfo->m_Animations[iIndex]->m_pszActivity; + } + + Activity activity = (Activity)ActivityList_IndexForName( pszActivity ); + int sequence = m_hModel->SelectWeightedSequence( activity ); + if ( sequence != ACT_INVALID ) + { + m_hModel->ResetSequence( sequence ); + m_hModel->SetCycle( 0 ); + } + } + } + else + { + SetSequence( pszAnimation ); + } + } +} + +void CModelPanel::CalculateFrameDistanceInternal( const model_t *pModel ) +{ + // Get the model space render bounds. + Vector vecMin, vecMax; + modelinfo->GetModelRenderBounds( pModel, vecMin, vecMax ); + Vector vecCenter = ( vecMax + vecMin ) * 0.5f; + vecMin -= vecCenter; + vecMax -= vecCenter; + + // Get the bounds points and transform them by the desired model panel rotation. + Vector aBoundsPoints[8]; + aBoundsPoints[0].Init( vecMax.x, vecMax.y, vecMax.z ); + aBoundsPoints[1].Init( vecMin.x, vecMax.y, vecMax.z ); + aBoundsPoints[2].Init( vecMax.x, vecMin.y, vecMax.z ); + aBoundsPoints[3].Init( vecMin.x, vecMin.y, vecMax.z ); + aBoundsPoints[4].Init( vecMax.x, vecMax.y, vecMin.z ); + aBoundsPoints[5].Init( vecMin.x, vecMax.y, vecMin.z ); + aBoundsPoints[6].Init( vecMax.x, vecMin.y, vecMin.z ); + aBoundsPoints[7].Init( vecMin.x, vecMin.y, vecMin.z ); + + // Translated center point (offset from camera center). + Vector vecTranslateCenter = -vecCenter; + + // Build the rotation matrix. + QAngle angPanelAngles( m_pModelInfo->m_vecAbsAngles.x, m_pModelInfo->m_vecAbsAngles.y, m_pModelInfo->m_vecAbsAngles.z ); + matrix3x4_t matRotation; + AngleMatrix( angPanelAngles, matRotation ); + + Vector aXFormPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + VectorTransform( aBoundsPoints[iPoint], matRotation, aXFormPoints[iPoint] ); + } + + Vector vecXFormCenter; + VectorTransform( -vecTranslateCenter, matRotation, vecXFormCenter ); + + int w, h; + GetSize( w, h ); + float flW = (float)w; + float flH = (float)h; + + float flFOVx = DEG2RAD( m_nFOV * 0.5f ); + float flFOVy = CalcFovY( ( m_nFOV * 0.5f ), flW/flH ); + flFOVy = DEG2RAD( flFOVy ); + + float flTanFOVx = tan( flFOVx ); + float flTanFOVy = tan( flFOVy ); + + // Find the max value of x, y, or z + float flDist = 0.0f; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + float flDistZ = fabs( aXFormPoints[iPoint].z / flTanFOVy - aXFormPoints[iPoint].x ); + float flDistY = fabs( aXFormPoints[iPoint].y / flTanFOVx - aXFormPoints[iPoint].x ); + float flTestDist = MAX( flDistZ, flDistY ); + flDist = MAX( flDist, flTestDist ); + } + + // Scale the object down by 10%. + flDist *= 1.10f; + + // Add the framing offset. + vecXFormCenter += m_pModelInfo->m_vecFramedOriginOffset; + + // Zoom to the frame distance + m_pModelInfo->m_vecOriginOffset.x = flDist - vecXFormCenter.x; + m_pModelInfo->m_vecOriginOffset.y = -vecXFormCenter.y; + m_pModelInfo->m_vecOriginOffset.z = -vecXFormCenter.z; + + // Screen space points. + Vector2D aScreenPoints[8]; + Vector aCameraPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + aCameraPoints[iPoint] = aXFormPoints[iPoint]; + aCameraPoints[iPoint].x += flDist; + + aScreenPoints[iPoint].x = aCameraPoints[iPoint].y / ( flTanFOVx * aCameraPoints[iPoint].x ); + aScreenPoints[iPoint].y = aCameraPoints[iPoint].z / ( flTanFOVy * aCameraPoints[iPoint].x ); + + aScreenPoints[iPoint].x = ( aScreenPoints[iPoint].x * 0.5f + 0.5f ) * flW; + aScreenPoints[iPoint].y = ( aScreenPoints[iPoint].y * 0.5f + 0.5f ) * flH; + } + + // Find the min/max and center of the 2D bounding box of the object. + Vector2D vecScreenMin( 99999.0f, 99999.0f ), vecScreenMax( -99999.0f, -99999.0f ); + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + vecScreenMin.x = MIN( vecScreenMin.x, aScreenPoints[iPoint].x ); + vecScreenMin.y = MIN( vecScreenMin.y, aScreenPoints[iPoint].y ); + vecScreenMax.x = MAX( vecScreenMax.x, aScreenPoints[iPoint].x ); + vecScreenMax.y = MAX( vecScreenMax.y, aScreenPoints[iPoint].y ); + } + + vecScreenMin.x = clamp( vecScreenMin.x, 0.0f, flW ); + vecScreenMin.y = clamp( vecScreenMin.y, 0.0f, flH ); + vecScreenMax.x = clamp( vecScreenMax.x, 0.0f, flW ); + vecScreenMax.y = clamp( vecScreenMax.y, 0.0f, flH ); + + // Offset the view port based on the calculated model 2D center and the center of the viewport. + Vector2D vecScreenCenter = ( vecScreenMax + vecScreenMin ) * 0.5f; + m_pModelInfo->m_vecViewportOffset.x = -( ( flW * 0.5f ) - vecScreenCenter.x ); + m_pModelInfo->m_vecViewportOffset.y = -( ( flH * 0.5f ) - vecScreenCenter.y ); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates the distance the camera should be at to frame the model on the screen. +//----------------------------------------------------------------------------- +void CModelPanel::CalculateFrameDistance( void ) +{ + m_flFrameDistance = 0; + if ( !m_hModel ) + return; + + // Compute a bounding radius for the model + const model_t *mod = modelinfo->GetModel( m_hModel->GetModelIndex() ); + if ( !mod ) + return; + + if ( m_bStartFramed ) + { + CalculateFrameDistanceInternal( mod ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the camera forward/backward along the current view angle to +// frame the model on the screen. +//----------------------------------------------------------------------------- +void CModelPanel::ZoomToFrameDistance( void ) +{ + if ( !m_flFrameDistance || !m_hModel ) + return; + + const model_t *mod = modelinfo->GetModel( m_hModel->GetModelIndex() ); + if ( !mod ) + return; + + // Move the model to the midpoint + Vector mins, maxs, vecModelCenter; + modelinfo->GetModelRenderBounds( mod, mins, maxs ); + VectorLerp( mins, maxs, 0.5f, vecModelCenter ); + + vecModelCenter += m_pModelInfo->m_vecFramedOriginOffset; + + // Zoom to the frame distance + m_pModelInfo->m_vecOriginOffset.x = m_flFrameDistance; + m_pModelInfo->m_vecOriginOffset.y = -vecModelCenter.y; + m_pModelInfo->m_vecOriginOffset.z = -vecModelCenter.z; +} + diff --git a/game/client/game_controls/basemodelpanel.h b/game/client/game_controls/basemodelpanel.h new file mode 100644 index 0000000..0e54306 --- /dev/null +++ b/game/client/game_controls/basemodelpanel.h @@ -0,0 +1,238 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BASEMODELPANEL_H +#define BASEMODELPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/IScheme.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/EditablePanel.h> +#include "GameEventListener.h" +#include "KeyValues.h" + +class C_SceneEntity; + + +class CModelPanelModel : public C_BaseFlex +{ +public: + CModelPanelModel(){} + DECLARE_CLASS( CModelPanelModel, C_BaseFlex ); + + virtual bool IsMenuModel() const{ return true; } +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CModelPanelModelAnimation +{ +public: + CModelPanelModelAnimation() + { + m_pszName = NULL; + m_pszSequence = NULL; + m_pszActivity = NULL; + m_pPoseParameters = NULL; + m_bDefault = false; + } + + ~CModelPanelModelAnimation() + { + if ( m_pszName && m_pszName[0] ) + { + delete [] m_pszName; + m_pszName = NULL; + } + + if ( m_pszSequence && m_pszSequence[0] ) + { + delete [] m_pszSequence; + m_pszSequence = NULL; + } + + if ( m_pszActivity && m_pszActivity[0] ) + { + delete [] m_pszActivity; + m_pszActivity = NULL; + } + + if ( m_pPoseParameters ) + { + m_pPoseParameters->deleteThis(); + m_pPoseParameters = NULL; + } + } + +public: + const char *m_pszName; + const char *m_pszSequence; + const char *m_pszActivity; + KeyValues *m_pPoseParameters; + bool m_bDefault; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CModelPanelAttachedModelInfo +{ +public: + CModelPanelAttachedModelInfo() + { + m_pszModelName = NULL; + m_nSkin = 0; + } + + ~CModelPanelAttachedModelInfo() + { + if ( m_pszModelName && m_pszModelName[0] ) + { + delete [] m_pszModelName; + m_pszModelName = NULL; + } + } + +public: + const char *m_pszModelName; + int m_nSkin; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CModelPanelModelInfo +{ +public: + CModelPanelModelInfo() + : m_mapBodygroupValues( DefLessFunc( int ) ) + { + m_pszModelName = NULL; + m_pszModelName_HWM = NULL; + m_nSkin = -1; + m_vecAbsAngles.Init(); + m_vecOriginOffset.Init(); + m_vecFramedOriginOffset.Init(); + m_bUseSpotlight = false; + } + + ~CModelPanelModelInfo() + { + if ( m_pszModelName && m_pszModelName[0] ) + { + delete [] m_pszModelName; + m_pszModelName = NULL; + } + + if ( m_pszModelName_HWM && m_pszModelName_HWM[0] ) + { + delete [] m_pszModelName_HWM; + m_pszModelName_HWM = NULL; + } + + if ( m_pszVCD && m_pszVCD[0] ) + { + delete [] m_pszVCD; + m_pszVCD = NULL; + } + + m_Animations.PurgeAndDeleteElements(); + m_AttachedModelsInfo.PurgeAndDeleteElements(); + } + +public: + const char *m_pszModelName; + const char *m_pszModelName_HWM; + int m_nSkin; + const char *m_pszVCD; + Vector m_vecAbsAngles; + Vector m_vecOriginOffset; + Vector2D m_vecViewportOffset; + Vector m_vecFramedOriginOffset; + bool m_bUseSpotlight; + CUtlMap< int, int > m_mapBodygroupValues; + + CUtlVector<CModelPanelModelAnimation*> m_Animations; + CUtlVector<CModelPanelAttachedModelInfo*> m_AttachedModelsInfo; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CModelPanel : public vgui::EditablePanel, public CGameEventListener +{ +public: + DECLARE_CLASS_SIMPLE( CModelPanel, vgui::EditablePanel ); + + CModelPanel( vgui::Panel *parent, const char *name ); + virtual ~CModelPanel(); + + virtual void Paint(); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void OnCommand( const char *command ) OVERRIDE; + virtual void DeleteVCDData( void ); + virtual void DeleteModelData( void ); + + virtual void SetFOV( int nFOV ){ m_nFOV = nFOV; } + virtual void SetPanelDirty( void ){ m_bPanelDirty = true; } + virtual bool SetSequence( const char *pszSequence ); + virtual void SetSkin( int nSkin ); + void SetBodyGroup( const char* pszBodyGroupName, int nGroup ); + + MESSAGE_FUNC_PARAMS( OnAddAnimation, "AddAnimation", data ); + MESSAGE_FUNC_PARAMS( OnSetAnimation, "SetAnimation", data ); + + void SetDefaultAnimation( const char *pszName ); + void SwapModel( const char *pszName, const char *pszAttached = NULL ); + + virtual void ParseModelInfo( KeyValues *inResourceData ); + + void ClearAttachedModelInfos( void ); + + void CalculateFrameDistance( void ); + void ZoomToFrameDistance( void ); + + void UpdateModel(); +public: // IGameEventListener: + virtual void FireGameEvent( IGameEvent * event ); + +protected: + virtual void SetupModel( void ); + virtual void SetupVCD( void ); + virtual const char *GetModelName( void ); + +private: + void InitCubeMaps(); + int FindAnimByName( const char *pszName ); + void CalculateFrameDistanceInternal( const model_t *pModel ); + +public: + int m_nFOV; + float m_flFrameDistance; + bool m_bStartFramed; + CModelPanelModelInfo *m_pModelInfo; + + CHandle<CModelPanelModel> m_hModel; + CUtlVector<CHandle<C_BaseAnimating> > m_AttachedModels; + + CHandle<C_SceneEntity> m_hScene; + +private: + bool m_bPanelDirty; + int m_iDefaultAnimation; + + bool m_bAllowOffscreen; + + CTextureReference m_DefaultEnvCubemap; + CTextureReference m_DefaultHDREnvCubemap; +}; + + +#endif // BASEMODELPANEL_H diff --git a/game/client/game_controls/baseviewport.cpp b/game/client/game_controls/baseviewport.cpp new file mode 100644 index 0000000..181b0d3 --- /dev/null +++ b/game/client/game_controls/baseviewport.cpp @@ -0,0 +1,768 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client DLL VGUI2 Viewport +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#pragma warning( disable : 4800 ) // disable forcing int to bool performance warning + +#include "cbase.h" +#include <cdll_client_int.h> +#include <cdll_util.h> +#include <globalvars_base.h> + +// VGUI panel includes +#include <vgui_controls/Panel.h> +#include <vgui_controls/AnimationController.h> +#include <vgui/ISurface.h> +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include <vgui/IVGui.h> +#include <vgui/ILocalize.h> +#include <vgui/IPanel.h> +#include <vgui_controls/Button.h> + +#include <igameresources.h> + +// sub dialogs +#include "clientscoreboarddialog.h" +#include "spectatorgui.h" +#include "teammenu.h" +#include "vguitextwindow.h" +#include "IGameUIFuncs.h" +#include "mapoverview.h" +#include "hud.h" +#include "NavProgress.h" +#include "commentary_modelviewer.h" + +// our definition +#include "baseviewport.h" +#include <filesystem.h> +#include <convar.h> +#include "ienginevgui.h" +#include "iclientmode.h" + +#include "tier0/etwprof.h" + +#if defined( REPLAY_ENABLED ) +#include "replay/ireplaysystem.h" +#include "replay/ienginereplay.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IViewPort *gViewPortInterface = NULL; + +vgui::Panel *g_lastPanel = NULL; // used for mouseover buttons, keeps track of the last active panel +vgui::Button *g_lastButton = NULL; // used for mouseover buttons, keeps track of the last active button +using namespace vgui; + +void hud_autoreloadscript_callback( IConVar *var, const char *pOldValue, float flOldValue ); + +ConVar hud_autoreloadscript("hud_autoreloadscript", "0", FCVAR_NONE, "Automatically reloads the animation script each time one is ran", hud_autoreloadscript_callback); + +void hud_autoreloadscript_callback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( g_pClientMode && g_pClientMode->GetViewportAnimationController() ) + { + g_pClientMode->GetViewportAnimationController()->SetAutoReloadScript( hud_autoreloadscript.GetBool() ); + } +} + +static ConVar cl_leveloverviewmarker( "cl_leveloverviewmarker", "0", FCVAR_CHEAT ); + +CON_COMMAND( showpanel, "Shows a viewport panel <name>" ) +{ + if ( !gViewPortInterface ) + return; + + if ( args.ArgC() != 2 ) + return; + + gViewPortInterface->ShowPanel( args[ 1 ], true ); +} + +CON_COMMAND( hidepanel, "Hides a viewport panel <name>" ) +{ + if ( !gViewPortInterface ) + return; + + if ( args.ArgC() != 2 ) + return; + + gViewPortInterface->ShowPanel( args[ 1 ], false ); +} + +/* global helper functions + +bool Helper_LoadFile( IBaseFileSystem *pFileSystem, const char *pFilename, CUtlVector<char> &buf ) +{ + FileHandle_t hFile = pFileSystem->Open( pFilename, "rt" ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + Warning( "Helper_LoadFile: missing %s\n", pFilename ); + return false; + } + + unsigned long len = pFileSystem->Size( hFile ); + buf.SetSize( len ); + pFileSystem->Read( buf.Base(), buf.Count(), hFile ); + pFileSystem->Close( hFile ); + + return true; +} */ + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseViewport::LoadHudAnimations( void ) +{ + const char *HUDANIMATION_MANIFEST_FILE = "scripts/hudanimations_manifest.txt"; + KeyValues *manifest = new KeyValues( HUDANIMATION_MANIFEST_FILE ); + if ( manifest->LoadFromFile( g_pFullFileSystem, HUDANIMATION_MANIFEST_FILE, "GAME" ) == false ) + { + manifest->deleteThis(); + return false; + } + + bool bClearScript = true; + + // Load each file defined in the text + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( !Q_stricmp( sub->GetName(), "file" ) ) + { + // Add it + if ( m_pAnimController->SetScriptFile( GetVPanel(), sub->GetString(), bClearScript ) == false ) + { + Assert( 0 ); + } + + bClearScript = false; + continue; + } + } + + manifest->deleteThis(); + return true; +} + +//================================================================ +CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport") +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + gViewPortInterface = this; + m_bInitialized = false; + + m_GameuiFuncs = NULL; + m_GameEventManager = NULL; + SetKeyBoardInputEnabled( false ); + SetMouseInputEnabled( false ); + +#ifndef _XBOX + m_pBackGround = NULL; +#endif + m_bHasParent = false; + m_pActivePanel = NULL; + m_pLastActivePanel = NULL; + g_lastPanel = NULL; + + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme"); + SetScheme(scheme); + SetProportional( true ); + + m_pAnimController = new vgui::AnimationController(this); + // create our animation controller + m_pAnimController->SetScheme(scheme); + m_pAnimController->SetProportional(true); + + // Attempt to load all hud animations + if ( LoadHudAnimations() == false ) + { + // Fall back to just the main + if ( m_pAnimController->SetScriptFile( GetVPanel(), "scripts/HudAnimations.txt", true ) == false ) + { + Assert(0); + } + } + + m_OldSize[ 0 ] = m_OldSize[ 1 ] = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates hud to handle the new screen size +//----------------------------------------------------------------------------- +void CBaseViewport::OnScreenSizeChanged(int iOldWide, int iOldTall) +{ + BaseClass::OnScreenSizeChanged(iOldWide, iOldTall); + + IViewPortPanel* pSpecGuiPanel = FindPanelByName(PANEL_SPECGUI); + bool bSpecGuiWasVisible = pSpecGuiPanel && pSpecGuiPanel->IsVisible(); + + // reload the script file, so the screen positions in it are correct for the new resolution + ReloadScheme( NULL ); + + // recreate all the default panels + RemoveAllPanels(); +#ifndef _XBOX + m_pBackGround = new CBackGroundPanel( NULL ); + m_pBackGround->SetZPos( -20 ); // send it to the back + m_pBackGround->SetVisible( false ); +#endif + CreateDefaultPanels(); +#ifndef _XBOX + vgui::ipanel()->MoveToBack( m_pBackGround->GetVPanel() ); // really send it to the back +#endif + + // hide all panels when reconnecting + ShowPanel( PANEL_ALL, false ); + + // re-enable the spectator gui if it was previously visible + if ( bSpecGuiWasVisible ) + { + ShowPanel( PANEL_SPECGUI, true ); + } +} + +void CBaseViewport::CreateDefaultPanels( void ) +{ +#ifndef _XBOX + AddNewPanel( CreatePanelByName( PANEL_SCOREBOARD ), "PANEL_SCOREBOARD" ); + AddNewPanel( CreatePanelByName( PANEL_INFO ), "PANEL_INFO" ); + AddNewPanel( CreatePanelByName( PANEL_SPECGUI ), "PANEL_SPECGUI" ); +#if !defined( TF_CLIENT_DLL ) + AddNewPanel( CreatePanelByName( PANEL_SPECMENU ), "PANEL_SPECMENU" ); + AddNewPanel( CreatePanelByName( PANEL_NAV_PROGRESS ), "PANEL_NAV_PROGRESS" ); +#endif // !TF_CLIENT_DLL +#endif // !_XBOX +} + +void CBaseViewport::UpdateAllPanels( void ) +{ + int count = m_Panels.Count(); + + for (int i=0; i< count; i++ ) + { + IViewPortPanel *p = m_Panels[i]; + + if ( p->IsVisible() ) + { + p->Update(); + } + } +} + +// Check if we have any visible panel (that's not the MainMenuOverride or the Scoreboard) +bool CBaseViewport::IsAnyPanelVisibleExceptScores() +{ + int count = m_Panels.Count(); + for ( int i = 0; i < count; i++ ) + { + IViewPortPanel *p = m_Panels[i]; + + if ( p->IsVisible() && Q_strcmp("MainMenuOverride", p->GetName()) && Q_strcmp("scores", p->GetName()) ) + { + return true; + } + } + + return false; +} + +bool CBaseViewport::IsPanelVisible( const char* panel ) +{ + int count = m_Panels.Count(); + + for ( int i = 0; i < count; i++ ) + { + IViewPortPanel *p = m_Panels[i]; + if ( p->IsVisible() ) + { + const char* panel_name = p->GetName(); + if ( !Q_strcmp( panel, panel_name ) ) + { + return true; + } + } + } + + return false; +} + +IViewPortPanel* CBaseViewport::CreatePanelByName(const char *szPanelName) +{ + IViewPortPanel* newpanel = NULL; + +#ifndef _XBOX + if ( Q_strcmp(PANEL_SCOREBOARD, szPanelName) == 0 ) + { + newpanel = new CClientScoreBoardDialog( this ); + } + else if ( Q_strcmp(PANEL_INFO, szPanelName) == 0 ) + { + newpanel = new CTextWindow( this ); + } +/* else if ( Q_strcmp(PANEL_OVERVIEW, szPanelName) == 0 ) + { + newpanel = new CMapOverview( this ); + } + */ + else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 ) + { + newpanel = new CTeamMenu( this ); + } + else if ( Q_strcmp(PANEL_SPECMENU, szPanelName) == 0 ) + { + newpanel = new CSpectatorMenu( this ); + } + else if ( Q_strcmp(PANEL_SPECGUI, szPanelName) == 0 ) + { + newpanel = new CSpectatorGUI( this ); + } +#if !defined( TF_CLIENT_DLL ) + else if ( Q_strcmp(PANEL_NAV_PROGRESS, szPanelName) == 0 ) + { + newpanel = new CNavProgress( this ); + } +#endif // TF_CLIENT_DLL +#endif + + if ( Q_strcmp(PANEL_COMMENTARY_MODELVIEWER, szPanelName) == 0 ) + { + newpanel = new CCommentaryModelViewer( this ); + } + + return newpanel; +} + + +bool CBaseViewport::AddNewPanel( IViewPortPanel* pPanel, char const *pchDebugName ) +{ + if ( !pPanel ) + { + DevMsg("CBaseViewport::AddNewPanel(%s): NULL panel.\n", pchDebugName ); + return false; + } + + // we created a new panel, initialize it + if ( FindPanelByName( pPanel->GetName() ) != NULL ) + { + DevMsg("CBaseViewport::AddNewPanel: panel with name '%s' already exists.\n", pPanel->GetName() ); + return false; + } + + m_Panels.AddToTail( pPanel ); + pPanel->SetParent( GetVPanel() ); + + return true; +} + +IViewPortPanel* CBaseViewport::FindPanelByName(const char *szPanelName) +{ + int count = m_Panels.Count(); + + for (int i=0; i< count; i++ ) + { + if ( Q_strcmp(m_Panels[i]->GetName(), szPanelName) == 0 ) + return m_Panels[i]; + } + + return NULL; +} + +void CBaseViewport::PostMessageToPanel( IViewPortPanel* pPanel, KeyValues *pKeyValues ) +{ + PostMessage( pPanel->GetVPanel(), pKeyValues ); +} + +void CBaseViewport::PostMessageToPanel( const char *pName, KeyValues *pKeyValues ) +{ + if ( Q_strcmp( pName, PANEL_ALL ) == 0 ) + { + for (int i=0; i< m_Panels.Count(); i++ ) + { + PostMessageToPanel( m_Panels[i], pKeyValues ); + } + + return; + } + + IViewPortPanel * panel = NULL; + + if ( Q_strcmp( pName, PANEL_ACTIVE ) == 0 ) + { + panel = m_pActivePanel; + } + else + { + panel = FindPanelByName( pName ); + } + + if ( !panel ) + return; + + PostMessageToPanel( panel, pKeyValues ); +} + + +void CBaseViewport::ShowPanel( const char *pName, bool state ) +{ + if ( Q_strcmp( pName, PANEL_ALL ) == 0 ) + { + for (int i=0; i< m_Panels.Count(); i++ ) + { + ShowPanel( m_Panels[i], state ); + } + + return; + } + + IViewPortPanel * panel = NULL; + + if ( Q_strcmp( pName, PANEL_ACTIVE ) == 0 ) + { + panel = m_pActivePanel; + } + else + { + panel = FindPanelByName( pName ); + } + + if ( !panel ) + return; + + ShowPanel( panel, state ); +} + +void CBaseViewport::ShowPanel( IViewPortPanel* pPanel, bool state ) +{ + if ( state ) + { + // if this is an 'active' panel, deactivate old active panel + if ( pPanel->HasInputElements() ) + { + // don't show input panels during normal demo playback +#if defined( REPLAY_ENABLED ) + if ( engine->IsPlayingDemo() && !engine->IsHLTV() && !g_pEngineClientReplay->IsPlayingReplayDemo() ) +#else + if ( engine->IsPlayingDemo() && !engine->IsHLTV() ) +#endif + return; + if ( (m_pActivePanel != NULL) && (m_pActivePanel != pPanel) && (m_pActivePanel->IsVisible()) ) + { + // store a pointer to the currently active panel + // so we can restore it later + m_pLastActivePanel = m_pActivePanel; + m_pActivePanel->ShowPanel( false ); + } + + m_pActivePanel = pPanel; + } + } + else + { + // if this is our current active panel + // update m_pActivePanel pointer + if ( m_pActivePanel == pPanel ) + { + m_pActivePanel = NULL; + } + + // restore the previous active panel if it exists + if( m_pLastActivePanel ) + { + m_pActivePanel = m_pLastActivePanel; + m_pLastActivePanel = NULL; + + m_pActivePanel->ShowPanel( true ); + } + } + + // just show/hide panel + pPanel->ShowPanel( state ); + + UpdateAllPanels(); // let other panels rearrange +} + +IViewPortPanel* CBaseViewport::GetActivePanel( void ) +{ + return m_pActivePanel; +} + +void CBaseViewport::RemoveAllPanels( void) +{ + g_lastPanel = NULL; + for ( int i=0; i < m_Panels.Count(); i++ ) + { + vgui::VPANEL vPanel = m_Panels[i]->GetVPanel(); + vgui::ipanel()->DeletePanel( vPanel ); + } +#ifndef _XBOX + if ( m_pBackGround ) + { + m_pBackGround->MarkForDeletion(); + m_pBackGround = NULL; + } +#endif + m_Panels.Purge(); + m_pActivePanel = NULL; + m_pLastActivePanel = NULL; +} + +CBaseViewport::~CBaseViewport() +{ + m_bInitialized = false; + +#ifndef _XBOX + if ( !m_bHasParent && m_pBackGround ) + { + m_pBackGround->MarkForDeletion(); + } + m_pBackGround = NULL; +#endif + RemoveAllPanels(); + + gameeventmanager->RemoveListener( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: called when the VGUI subsystem starts up +// Creates the sub panels and initialises them +//----------------------------------------------------------------------------- +void CBaseViewport::Start( IGameUIFuncs *pGameUIFuncs, IGameEventManager2 * pGameEventManager ) +{ + m_GameuiFuncs = pGameUIFuncs; + m_GameEventManager = pGameEventManager; +#ifndef _XBOX + m_pBackGround = new CBackGroundPanel( NULL ); + m_pBackGround->SetZPos( -20 ); // send it to the back + m_pBackGround->SetVisible( false ); +#endif + CreateDefaultPanels(); + + m_GameEventManager->AddListener( this, "game_newmap", false ); + + m_bInitialized = true; +} + +/* + +//----------------------------------------------------------------------------- +// Purpose: Updates the spectator panel with new player info +//----------------------------------------------------------------------------- +void CBaseViewport::UpdateSpectatorPanel() +{ + char bottomText[128]; + int player = -1; + const char *name; + Q_snprintf(bottomText,sizeof( bottomText ), "#Spec_Mode%d", m_pClientDllInterface->SpectatorMode() ); + + m_pClientDllInterface->CheckSettings(); + // check if we're locked onto a target, show the player's name + if ( (m_pClientDllInterface->SpectatorTarget() > 0) && (m_pClientDllInterface->SpectatorTarget() <= m_pClientDllInterface->GetMaxPlayers()) && (m_pClientDllInterface->SpectatorMode() != OBS_ROAMING) ) + { + player = m_pClientDllInterface->SpectatorTarget(); + } + + // special case in free map and inset off, don't show names + if ( ((m_pClientDllInterface->SpectatorMode() == OBS_MAP_FREE) && !m_pClientDllInterface->PipInsetOff()) || player == -1 ) + name = NULL; + else + name = m_pClientDllInterface->GetPlayerInfo(player).name; + + // create player & health string + if ( player && name ) + { + Q_strncpy( bottomText, name, sizeof( bottomText ) ); + } + char szMapName[64]; + Q_FileBase( const_cast<char *>(m_pClientDllInterface->GetLevelName()), szMapName ); + + m_pSpectatorGUI->Update(bottomText, player, m_pClientDllInterface->SpectatorMode(), m_pClientDllInterface->IsSpectateOnly(), m_pClientDllInterface->SpectatorNumber(), szMapName ); + m_pSpectatorGUI->UpdateSpectatorPlayerList(); +} */ + +// Return TRUE if the HUD's allowed to print text messages +bool CBaseViewport::AllowedToPrintText( void ) +{ + + /* int iId = GetCurrentMenuID(); + if ( iId == MENU_TEAM || iId == MENU_CLASS || iId == MENU_INTRO || iId == MENU_CLASSHELP ) + return false; */ + // TODO ask every aktive elemet if it allows to draw text while visible + + return ( m_pActivePanel == NULL); +} + +void CBaseViewport::OnThink() +{ + // Clear our active panel pointer if the panel has made + // itself invisible. Need this so we don't bring up dead panels + // if they are stored as the last active panel + if( m_pActivePanel && !m_pActivePanel->IsVisible() ) + { + if( m_pLastActivePanel ) + { + m_pActivePanel = m_pLastActivePanel; + ShowPanel( m_pActivePanel, true ); + m_pLastActivePanel = NULL; + } + else + m_pActivePanel = NULL; + } + + // TF does this in OnTick in TFViewport. This remains to preserve old + // behavior in other games +#if !defined( TF_CLIENT_DLL ) + m_pAnimController->UpdateAnimations( gpGlobals->curtime ); +#endif + + int count = m_Panels.Count(); + + for (int i=0; i< count; i++ ) + { + IViewPortPanel *panel = m_Panels[i]; + if ( panel->NeedsUpdate() && panel->IsVisible() ) + { + panel->Update(); + } + } + + int w, h; + vgui::ipanel()->GetSize( enginevgui->GetPanel( PANEL_CLIENTDLL ), w, h ); + + if ( m_OldSize[ 0 ] != w || m_OldSize[ 1 ] != h ) + { + m_OldSize[ 0 ] = w; + m_OldSize[ 1 ] = h; + g_pClientMode->Layout(); + } + + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the parent for each panel to use +//----------------------------------------------------------------------------- +void CBaseViewport::SetParent(vgui::VPANEL parent) +{ + EditablePanel::SetParent( parent ); + // force ourselves to be proportional - when we set our parent above, if our new + // parent happened to be non-proportional (such as the vgui root panel), we got + // slammed to be nonproportional + EditablePanel::SetProportional( true ); + +#ifndef _XBOX + m_pBackGround->SetParent( (vgui::VPANEL)parent ); +#endif + + // set proportionality on animation controller + m_pAnimController->SetProportional( true ); + + m_bHasParent = (parent != 0); +} + +//----------------------------------------------------------------------------- +// Purpose: called when the engine shows the base client VGUI panel (i.e when entering a new level or exiting GameUI ) +//----------------------------------------------------------------------------- +void CBaseViewport::ActivateClientUI() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: called when the engine hides the base client VGUI panel (i.e when the GameUI is comming up ) +//----------------------------------------------------------------------------- +void CBaseViewport::HideClientUI() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: passes death msgs to the scoreboard to display specially +//----------------------------------------------------------------------------- +void CBaseViewport::FireGameEvent( IGameEvent * event) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type, "game_newmap") == 0 ) + { + // hide all panels when reconnecting + ShowPanel( PANEL_ALL, false ); + + if ( engine->IsHLTV() ) + { + ShowPanel( PANEL_SPECGUI, true ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseViewport::ReloadScheme(const char *fromFile) +{ + CETWScope timer( "CBaseViewport::ReloadScheme" ); + + // See if scheme should change + + if ( fromFile != NULL ) + { + // "resource/ClientScheme.res" + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), fromFile, "HudScheme" ); + + SetScheme(scheme); + SetProportional( true ); + m_pAnimController->SetScheme(scheme); + } + + // Force a reload + if ( LoadHudAnimations() == false ) + { + // Fall back to just the main + if ( m_pAnimController->SetScriptFile( GetVPanel(), "scripts/HudAnimations.txt", true ) == false ) + { + Assert(0); + } + } + + SetProportional( true ); + + KeyValuesAD pConditions( "conditions" ); + g_pClientMode->ComputeVguiResConditions( pConditions ); + + // reload the .res file from disk + LoadControlSettings( "scripts/HudLayout.res", NULL, NULL, pConditions ); + + gHUD.RefreshHudTextures(); + + InvalidateLayout( true, true ); + + // reset the hud + gHUD.ResetHUD(); +} + +int CBaseViewport::GetDeathMessageStartHeight( void ) +{ + return YRES(2); +} + +void CBaseViewport::Paint() +{ + if ( cl_leveloverviewmarker.GetInt() > 0 ) + { + int size = cl_leveloverviewmarker.GetInt(); + // draw a 1024x1024 pixel box + vgui::surface()->DrawSetColor( 255, 0, 0, 255 ); + vgui::surface()->DrawLine( size, 0, size, size ); + vgui::surface()->DrawLine( 0, size, size, size ); + } +} diff --git a/game/client/game_controls/baseviewport.h b/game/client/game_controls/baseviewport.h new file mode 100644 index 0000000..fa8055d --- /dev/null +++ b/game/client/game_controls/baseviewport.h @@ -0,0 +1,163 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TEAMFORTRESSVIEWPORT_H +#define TEAMFORTRESSVIEWPORT_H + +// viewport interface for the rest of the dll +#include <game/client/iviewport.h> + +#include <utlqueue.h> // a vector based queue template to manage our VGUI menu queue +#include <vgui_controls/Frame.h> +#include "vguitextwindow.h" +#include "vgui/ISurface.h" +#include "commandmenu.h" +#include <igameevents.h> + +using namespace vgui; + +class IBaseFileSystem; +class IGameUIFuncs; +class IGameEventManager; + +//============================================================================== +class CBaseViewport : public vgui::EditablePanel, public IViewPort, public IGameEventListener2 +{ + DECLARE_CLASS_SIMPLE( CBaseViewport, vgui::EditablePanel ); + +public: + CBaseViewport(); + virtual ~CBaseViewport(); + + virtual IViewPortPanel* CreatePanelByName(const char *szPanelName); + virtual IViewPortPanel* FindPanelByName(const char *szPanelName); + virtual IViewPortPanel* GetActivePanel( void ); + virtual void RemoveAllPanels( void); + + virtual void ShowPanel( const char *pName, bool state ); + virtual void ShowPanel( IViewPortPanel* pPanel, bool state ); + virtual bool AddNewPanel( IViewPortPanel* pPanel, char const *pchDebugName ); + virtual void CreateDefaultPanels( void ); + virtual void UpdateAllPanels( void ); + virtual void PostMessageToPanel( const char *pName, KeyValues *pKeyValues ); + + virtual void Start( IGameUIFuncs *pGameUIFuncs, IGameEventManager2 *pGameEventManager ); + virtual void SetParent(vgui::VPANEL parent); + + virtual void ReloadScheme(const char *fromFile); + virtual void ActivateClientUI(); + virtual void HideClientUI(); + virtual bool AllowedToPrintText( void ); + +#ifndef _XBOX + virtual int GetViewPortScheme() { return m_pBackGround->GetScheme(); } + virtual VPANEL GetViewPortPanel() { return m_pBackGround->GetVParent(); } +#endif + virtual AnimationController *GetAnimationController() { return m_pAnimController; } + + virtual void ShowBackGround(bool bShow) + { +#ifndef _XBOX + m_pBackGround->SetVisible( bShow ); +#endif + } + + virtual int GetDeathMessageStartHeight( void ); + + // virtual void ChatInputPosition( int *x, int *y ); + + // Check if any panel other than the scoreboard is visible + virtual bool IsAnyPanelVisibleExceptScores(); + + // Walk through all the panels. Handler should be an object taking an IViewPortPanel* + template<typename THandler> void ForEachPanel( THandler handler ) + { + FOR_EACH_VEC( m_Panels, i ) + { + handler( m_Panels[i] ); + } + } + + // Check if the named panel is visible + virtual bool IsPanelVisible( const char* panel ); + +public: // IGameEventListener: + virtual void FireGameEvent( IGameEvent * event); + + +protected: + + bool LoadHudAnimations( void ); + +#ifndef _XBOX + class CBackGroundPanel : public vgui::Frame + { + private: + typedef vgui::Frame BaseClass; + public: + CBackGroundPanel( vgui::Panel *parent) : Frame( parent, "ViewPortBackGround" ) + { + SetScheme("ClientScheme"); + + SetTitleBarVisible( false ); + SetMoveable(false); + SetSizeable(false); + SetProportional(true); + } + private: + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + BaseClass::ApplySchemeSettings(pScheme); + SetBgColor(pScheme->GetColor("ViewportBG", Color( 0,0,0,0 ) )); + } + + virtual void PerformLayout() + { + int w,h; + GetHudSize(w, h); + + // fill the screen + SetBounds(0,0,w,h); + + BaseClass::PerformLayout(); + } + + virtual void OnMousePressed(MouseCode code) { }// don't respond to mouse clicks + virtual vgui::VPANEL IsWithinTraverse( int x, int y, bool traversePopups ) + { + return ( vgui::VPANEL )0; + } + + }; +#endif +protected: + + virtual void Paint(); + virtual void OnThink(); + virtual void OnScreenSizeChanged(int iOldWide, int iOldTall); + void PostMessageToPanel( IViewPortPanel* pPanel, KeyValues *pKeyValues ); + +protected: + IGameUIFuncs* m_GameuiFuncs; // for key binding details + IGameEventManager2* m_GameEventManager; +#ifndef _XBOX + CBackGroundPanel *m_pBackGround; +#endif + CUtlVector<IViewPortPanel*> m_Panels; + + bool m_bHasParent; // Used to track if child windows have parents or not. + bool m_bInitialized; + IViewPortPanel *m_pActivePanel; + IViewPortPanel *m_pLastActivePanel; + vgui::HCursor m_hCursorNone; + vgui::AnimationController *m_pAnimController; + int m_OldSize[2]; +}; + + +#endif diff --git a/game/client/game_controls/buymenu.cpp b/game/client/game_controls/buymenu.cpp new file mode 100644 index 0000000..7d6160a --- /dev/null +++ b/game/client/game_controls/buymenu.cpp @@ -0,0 +1,151 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "buymenu.h" + +#include "buysubmenu.h" +using namespace vgui; + +#include "mouseoverpanelbutton.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBuyMenu::CBuyMenu(IViewPort *pViewPort) : WizardPanel( NULL, PANEL_BUY ) +{ + SetScheme("ClientScheme"); + SetTitle( "#Cstrike_Buy_Menu", true); + + SetMoveable(false); + SetSizeable(false); + SetProportional(true); + + // hide the system buttons + SetTitleBarVisible( false ); + + SetAutoDelete( false ); // we reuse this panel, don't let WizardPanel delete us + + LoadControlSettings( "Resource/UI/BuyMenu.res" ); + ShowButtons( false ); + + m_pViewPort = pViewPort; + + m_pMainMenu = new CBuySubMenu( this, "mainmenu" ); + m_pMainMenu->LoadControlSettings( "Resource/UI/MainBuyMenu.res" ); + m_pMainMenu->SetVisible( false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBuyMenu::~CBuyMenu() +{ + if ( m_pMainMenu ) + m_pMainMenu->DeleteSubPanels(); //? +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides the buy menu +//----------------------------------------------------------------------------- +void CBuyMenu::ShowPanel(bool bShow) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + if ( bShow ) + { + Update(); + + Run( m_pMainMenu ); + + SetMouseInputEnabled( true ); + + engine->ClientCmd_Unrestricted( "gameui_preventescapetoshow\n" ); + } + else + { + engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" ); + + SetVisible( false ); + SetMouseInputEnabled( false ); + } + + m_pViewPort->ShowBackGround( bShow ); +} + + +void CBuyMenu::Update() +{ + //Don't need to do anything, but do need to implement this function as base is pure virtual +} +void CBuyMenu::OnClose() +{ + engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" ); + + BaseClass::OnClose(); + ResetHistory(); +} + +void CBuyMenu::OnKeyCodePressed( vgui::KeyCode code ) +{ + int nDir = 0; + + switch ( code ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + case STEAMCONTROLLER_DPAD_UP: + nDir = -1; + break; + + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + nDir = 1; + break; + } + + if ( nDir != 0 ) + { + Panel *pSubPanel = ( GetCurrentSubPanel() ? GetCurrentSubPanel() : m_pMainMenu ); + + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons; + VguiPanelGetSortedChildButtonList( pSubPanel, (void*)&vecSortedButtons, "&", 0 ); + + if ( VguiPanelNavigateSortedChildButtonList( (void*)&vecSortedButtons, nDir ) != -1 ) + { + // Handled! + return; + } + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + +void CBuyMenu::OnKeyCodeTyped( KeyCode code ) +{ + if ( code == KEY_ESCAPE ) + { + OnClose(); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } +} + diff --git a/game/client/game_controls/buymenu.h b/game/client/game_controls/buymenu.h new file mode 100644 index 0000000..9f87ad9 --- /dev/null +++ b/game/client/game_controls/buymenu.h @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUYMENU_H +#define BUYMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/WizardPanel.h> +#include <game/client/iviewport.h> +#include "vgui/KeyCode.h" + +class CBuySubMenu; + +namespace vgui +{ + class Panel; +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the class menu +//----------------------------------------------------------------------------- +class CBuyMenu : public vgui::WizardPanel, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CBuyMenu, vgui::WizardPanel ); + +public: + CBuyMenu(IViewPort *pViewPort); + ~CBuyMenu(); + + virtual const char *GetName( void ) { return PANEL_BUY; } + virtual void SetData(KeyValues *data) {}; + virtual void Reset() {}; + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_MENUCONTROLS; } + +public: + virtual void OnClose(); + +protected: + + CBuySubMenu *m_pMainMenu; + IViewPort *m_pViewPort; + + int m_iTeam; + int m_iClass; +}; + + +#endif // BUYMENU_H diff --git a/game/client/game_controls/buysubmenu.cpp b/game/client/game_controls/buysubmenu.cpp new file mode 100644 index 0000000..6bee2e5 --- /dev/null +++ b/game/client/game_controls/buysubmenu.cpp @@ -0,0 +1,171 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "buysubmenu.h" + +#include <KeyValues.h> +#include <vgui_controls/WizardPanel.h> +#include <filesystem.h> +#include <game/client/iviewport.h> +#include <cdll_client_int.h> + +#include "mouseoverpanelbutton.h" +// #include "cs_gamerules.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBuySubMenu::CBuySubMenu(vgui::Panel *parent, const char *name) : WizardSubPanel(parent, name) +{ + m_NextPanel = NULL; + m_pFirstButton = NULL; + SetProportional(true); + + m_pPanel = new EditablePanel( this, "ItemInfo" );// info window about these items + m_pPanel->SetProportional( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBuySubMenu::~CBuySubMenu() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: magic override to allow vgui to create mouse over buttons for us +//----------------------------------------------------------------------------- +Panel *CBuySubMenu::CreateControlByName( const char *controlName ) +{ + if( !Q_stricmp( "MouseOverPanelButton", controlName ) ) + { + MouseOverPanelButton *newButton = CreateNewMouseOverPanelButton( m_pPanel ); + + if( !m_pFirstButton ) + { + m_pFirstButton = newButton; + } + return newButton; + } + else + { + return BaseClass::CreateControlByName( controlName ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Make the first buttons page get displayed when the menu becomes visible +//----------------------------------------------------------------------------- +void CBuySubMenu::SetVisible( bool state ) +{ + BaseClass::SetVisible( state ); + + for( int i = 0; i< GetChildCount(); i++ ) // get all the buy buttons to performlayout + { + MouseOverPanelButton *buyButton = dynamic_cast<MouseOverPanelButton *>(GetChild(i)); + if ( buyButton ) + { + if( buyButton == m_pFirstButton && state == true ) + buyButton->ShowPage(); + else + buyButton->HidePage(); + + buyButton->InvalidateLayout(); + } + } +} + +CBuySubMenu* CBuySubMenu::CreateNewSubMenu() +{ + return new CBuySubMenu( this ); +} + +MouseOverPanelButton* CBuySubMenu::CreateNewMouseOverPanelButton(EditablePanel *panel) +{ + return new MouseOverPanelButton(this, NULL, panel); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the user picks a class +//----------------------------------------------------------------------------- +void CBuySubMenu::OnCommand( const char *command) +{ + if ( Q_strstr( command, ".res" ) ) // if its a .res file then its a new menu + { + int i; + // check the cache + for ( i = 0; i < m_SubMenus.Count(); i++ ) + { + if ( !Q_stricmp( m_SubMenus[i].filename, command ) ) + { + m_NextPanel = m_SubMenus[i].panel; + Assert( m_NextPanel ); + m_NextPanel->InvalidateLayout(); // force it to reset it prices + break; + } + } + + if ( i == m_SubMenus.Count() ) + { + // not there, add a new entry + SubMenuEntry_t newEntry; + memset( &newEntry, 0x0, sizeof( newEntry ) ); + + CBuySubMenu *newMenu = CreateNewSubMenu(); + newMenu->LoadControlSettings( command ); + m_NextPanel = newMenu; + Q_strncpy( newEntry.filename, command, sizeof( newEntry.filename ) ); + newEntry.panel = newMenu; + m_SubMenus.AddToTail( newEntry ); + } + + GetWizardPanel()->OnNextButton(); + } + else + { + GetWizardPanel()->Close(); + gViewPortInterface->ShowBackGround( false ); + + if ( Q_stricmp( command, "vguicancel" ) != 0 ) + engine->ClientCmd( command ); + + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Causes the panel to delete itself when it closes +//----------------------------------------------------------------------------- +void CBuySubMenu::DeleteSubPanels() +{ + if ( m_NextPanel ) + { + m_NextPanel->SetVisible( false ); + m_NextPanel = NULL; + } + + m_pFirstButton = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: return the next panel to show +//----------------------------------------------------------------------------- +vgui::WizardSubPanel *CBuySubMenu::GetNextSubPanel() +{ + return m_NextPanel; +} + + + + diff --git a/game/client/game_controls/buysubmenu.h b/game/client/game_controls/buysubmenu.h new file mode 100644 index 0000000..f3b8c25 --- /dev/null +++ b/game/client/game_controls/buysubmenu.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUYSUBMENU_H +#define BUYSUBMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/WizardSubPanel.h> +#include <vgui_controls/Button.h> +#include <utlvector.h> +#include "mouseoverpanelbutton.h" + +class CBuyMenu; + +//----------------------------------------------------------------------------- +// Purpose: Draws the class menu +//----------------------------------------------------------------------------- +class CBuySubMenu : public vgui::WizardSubPanel +{ +private: + DECLARE_CLASS_SIMPLE( CBuySubMenu, vgui::WizardSubPanel ); + +public: + CBuySubMenu(vgui::Panel *parent,const char *name = "BuySubMenu"); + ~CBuySubMenu(); + + virtual void SetVisible( bool state ); + virtual void DeleteSubPanels(); + +protected: + + // command callbacks + virtual void OnCommand( const char *command ); + virtual vgui::WizardSubPanel *GetNextSubPanel(); // this is the last menu in the list + virtual vgui::Panel *CreateControlByName(const char *controlName); + virtual CBuySubMenu* CreateNewSubMenu(); + virtual MouseOverPanelButton* CreateNewMouseOverPanelButton(vgui::EditablePanel *panel); + + typedef struct + { + char filename[_MAX_PATH]; + CBuySubMenu *panel; + } SubMenuEntry_t; + + vgui::EditablePanel *m_pPanel; + MouseOverPanelButton *m_pFirstButton; + + CUtlVector<SubMenuEntry_t> m_SubMenus; // a cache of buy submenus, so we don't need to construct them each time + + vgui::WizardSubPanel *m_NextPanel; +}; + +#endif // BUYSUBMENU_H diff --git a/game/client/game_controls/classmenu.cpp b/game/client/game_controls/classmenu.cpp new file mode 100644 index 0000000..d9e857c --- /dev/null +++ b/game/client/game_controls/classmenu.cpp @@ -0,0 +1,319 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include "cbase.h" +#include <stdio.h> + +#include <cdll_client_int.h> + +#include "classmenu.h" + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <KeyValues.h> +#include <vgui_controls/ImageList.h> +#include <filesystem.h> + +#include <vgui_controls/TextEntry.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/Panel.h> +#include "inputsystem/iinputsystem.h" + +#include "cdll_util.h" +#include "IGameUIFuncs.h" // for key bindings +#ifndef _XBOX +extern IGameUIFuncs *gameuifuncs; // for key binding details +#endif +#include <game/client/iviewport.h> + +#include <stdlib.h> // MAX_PATH define + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#ifdef TF_CLIENT_DLL +#define HUD_CLASSAUTOKILL_FLAGS ( FCVAR_CLIENTDLL | FCVAR_ARCHIVE | FCVAR_USERINFO ) +#else +#define HUD_CLASSAUTOKILL_FLAGS ( FCVAR_CLIENTDLL | FCVAR_ARCHIVE ) +#endif // !TF_CLIENT_DLL + +ConVar hud_classautokill( "hud_classautokill", "1", HUD_CLASSAUTOKILL_FLAGS, "Automatically kill player after choosing a new playerclass." ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClassMenu::CClassMenu(IViewPort *pViewPort) : Frame(NULL, PANEL_CLASS) +{ + m_pViewPort = pViewPort; + m_iScoreBoardKey = BUTTON_CODE_INVALID; // this is looked up in Activate() + m_iTeam = 0; + + // initialize dialog + SetTitle("", true); + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMoveable(false); + SetSizeable(false); + + // hide the system buttons + SetTitleBarVisible( false ); + SetProportional(true); + + // info window about this class + m_pPanel = new EditablePanel( this, "ClassInfo" ); + + LoadControlSettings( "Resource/UI/ClassMenu.res" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CClassMenu::CClassMenu(IViewPort *pViewPort, const char *panelName) : Frame(NULL, panelName) +{ + m_pViewPort = pViewPort; + m_iScoreBoardKey = BUTTON_CODE_INVALID; // this is looked up in Activate() + m_iTeam = 0; + + // initialize dialog + SetTitle("", true); + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMoveable(false); + SetSizeable(false); + + // hide the system buttons + SetTitleBarVisible( false ); + SetProportional(true); + + // info window about this class + m_pPanel = new EditablePanel( this, "ClassInfo" ); + + // Inheriting classes are responsible for calling LoadControlSettings()! +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CClassMenu::~CClassMenu() +{ +} + +MouseOverPanelButton* CClassMenu::CreateNewMouseOverPanelButton(EditablePanel *panel) +{ + return new MouseOverPanelButton(this, "MouseOverPanelButton", panel); +} + + +Panel *CClassMenu::CreateControlByName(const char *controlName) +{ + if( !Q_stricmp( "MouseOverPanelButton", controlName ) ) + { + MouseOverPanelButton *newButton = CreateNewMouseOverPanelButton( m_pPanel ); + + m_mouseoverButtons.AddToTail( newButton ); + return newButton; + } + else + { + return BaseClass::CreateControlByName( controlName ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClassMenu::Reset() +{ + for ( int i = 0 ; i < GetChildCount() ; ++i ) + { + // Hide the subpanel for the MouseOverPanelButtons + MouseOverPanelButton *pPanel = dynamic_cast<MouseOverPanelButton *>( GetChild( i ) ); + + if ( pPanel ) + { + pPanel->HidePage(); + } + } + + // Turn the first button back on again (so we have a default description shown) + Assert( m_mouseoverButtons.Count() ); + for ( int i=0; i<m_mouseoverButtons.Count(); ++i ) + { + if ( i == 0 ) + { + m_mouseoverButtons[i]->ShowPage(); // Show the first page + } + else + { + m_mouseoverButtons[i]->HidePage(); // Hide the rest + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the user picks a class +//----------------------------------------------------------------------------- +void CClassMenu::OnCommand( const char *command ) +{ + if ( Q_stricmp( command, "vguicancel" ) ) + { + engine->ClientCmd( const_cast<char *>( command ) ); + +#if !defined( CSTRIKE_DLL ) && !defined( TF_CLIENT_DLL ) + // They entered a command to change their class, kill them so they spawn with + // the new class right away + if ( hud_classautokill.GetBool() ) + { + engine->ClientCmd( "kill" ); + } +#endif // !CSTRIKE_DLL && !TF_CLIENT_DLL + } + + Close(); + + gViewPortInterface->ShowBackGround( false ); + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: shows the class menu +//----------------------------------------------------------------------------- +void CClassMenu::ShowPanel(bool bShow) +{ + if ( bShow ) + { + Activate(); + SetMouseInputEnabled( true ); + + // load a default class page + for ( int i=0; i<m_mouseoverButtons.Count(); ++i ) + { + if ( i == 0 ) + { + m_mouseoverButtons[i]->ShowPage(); // Show the first page + } + else + { + m_mouseoverButtons[i]->HidePage(); // Hide the rest + } + } + + if ( m_iScoreBoardKey == BUTTON_CODE_INVALID ) + { + m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" ); + } + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + } + + m_pViewPort->ShowBackGround( bShow ); +} + + +void CClassMenu::SetData(KeyValues *data) +{ + m_iTeam = data->GetInt( "team" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CClassMenu::SetLabelText(const char *textEntryName, const char *text) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetText(text); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the visibility of a button by name +//----------------------------------------------------------------------------- +void CClassMenu::SetVisibleButton(const char *textEntryName, bool state) +{ + Button *entry = dynamic_cast<Button *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetVisible(state); + } +} + +void CClassMenu::OnKeyCodePressed(KeyCode code) +{ + int nDir = 0; + + switch ( code ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case KEY_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + nDir = -1; + break; + + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + case KEY_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + nDir = 1; + break; + } + + if ( m_iScoreBoardKey != BUTTON_CODE_INVALID && m_iScoreBoardKey == code ) + { + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, true ); + gViewPortInterface->PostMessageToPanel( PANEL_SCOREBOARD, new KeyValues( "PollHideCode", "code", code ) ); + } + else if ( nDir != 0 ) + { + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons; + VguiPanelGetSortedChildButtonList( this, (void*)&vecSortedButtons, "&", 0 ); + + int nNewArmed = VguiPanelNavigateSortedChildButtonList( (void*)&vecSortedButtons, nDir ); + + if ( nNewArmed != -1 ) + { + // Handled! + if ( nNewArmed < m_mouseoverButtons.Count() ) + { + m_mouseoverButtons[ nNewArmed ]->OnCursorEntered(); + } + return; + } + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + + + + + diff --git a/game/client/game_controls/classmenu.h b/game/client/game_controls/classmenu.h new file mode 100644 index 0000000..43ae72e --- /dev/null +++ b/game/client/game_controls/classmenu.h @@ -0,0 +1,81 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CLASSMENU_H +#define CLASSMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/HTML.h> +#include <utlvector.h> +#include <vgui/ILocalize.h> +#include <vgui/KeyCode.h> +#include <game/client/iviewport.h> + +#include "mouseoverpanelbutton.h" + +namespace vgui +{ + class TextEntry; +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the class menu +//----------------------------------------------------------------------------- +class CClassMenu : public vgui::Frame, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CClassMenu, vgui::Frame ); + +public: + CClassMenu(IViewPort *pViewPort); + CClassMenu(IViewPort *pViewPort, const char *panelName ); + virtual ~CClassMenu(); + + virtual const char *GetName( void ) { return PANEL_CLASS; } + virtual void SetData(KeyValues *data); + virtual void Reset(); + virtual void Update() {}; + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_NONE; } + +protected: + + virtual vgui::Panel *CreateControlByName(const char *controlName); + virtual MouseOverPanelButton* CreateNewMouseOverPanelButton(vgui::EditablePanel *panel); + + //vgui2 overrides + virtual void OnKeyCodePressed(vgui::KeyCode code); + + // helper functions + void SetLabelText(const char *textEntryName, const char *text); + void SetVisibleButton(const char *textEntryName, bool state); + + // command callbacks + void OnCommand( const char *command ); + + IViewPort *m_pViewPort; + ButtonCode_t m_iScoreBoardKey; + int m_iTeam; + vgui::EditablePanel *m_pPanel; + + CUtlVector< MouseOverPanelButton * > m_mouseoverButtons; +}; + + +#endif // CLASSMENU_H diff --git a/game/client/game_controls/clientscoreboarddialog.h b/game/client/game_controls/clientscoreboarddialog.h new file mode 100644 index 0000000..896f0d7 --- /dev/null +++ b/game/client/game_controls/clientscoreboarddialog.h @@ -0,0 +1,129 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CLIENTSCOREBOARDDIALOG_H +#define CLIENTSCOREBOARDDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/EditablePanel.h> +#include <game/client/iviewport.h> +#include "GameEventListener.h" + +#define TYPE_NOTEAM 0 // NOTEAM must be zero :) +#define TYPE_TEAM 1 // a section for a single team +#define TYPE_PLAYERS 2 +#define TYPE_SPECTATORS 3 // a section for a spectator group +#define TYPE_BLANK 4 + + +//----------------------------------------------------------------------------- +// Purpose: Game ScoreBoard +//----------------------------------------------------------------------------- +class CClientScoreBoardDialog : public vgui::EditablePanel, public IViewPortPanel, public CGameEventListener +{ +private: + DECLARE_CLASS_SIMPLE( CClientScoreBoardDialog, vgui::EditablePanel ); + +protected: +// column widths at 640 + enum { NAME_WIDTH = 160, SCORE_WIDTH = 60, DEATH_WIDTH = 60, PING_WIDTH = 80, VOICE_WIDTH = 0, FRIENDS_WIDTH = 0 }; + // total = 340 + +public: + CClientScoreBoardDialog( IViewPort *pViewPort ); + ~CClientScoreBoardDialog(); + + virtual const char *GetName( void ) { return PANEL_SCOREBOARD; } + virtual void SetData(KeyValues *data) {}; + virtual void Reset(); + virtual void Update(); + virtual bool NeedsUpdate( void ); + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + virtual bool ShowAvatars() + { +#ifdef CSS_PERF_TEST + return false; +#endif + return IsPC(); + } + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + // IGameEventListener interface: + virtual void FireGameEvent( IGameEvent *event); + + virtual void UpdatePlayerAvatar( int playerIndex, KeyValues *kv ); + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_NONE; } + +protected: + MESSAGE_FUNC_INT( OnPollHideCode, "PollHideCode", code ); + + // functions to override + virtual bool GetPlayerScoreInfo(int playerIndex, KeyValues *outPlayerInfo); + virtual void InitScoreboardSections(); + virtual void UpdateTeamInfo(); + virtual void UpdatePlayerInfo(); + virtual void OnThink(); + virtual void AddHeader(); // add the start header of the scoreboard + virtual void AddSection(int teamType, int teamNumber); // add a new section header for a team + virtual int GetAdditionalHeight() { return 0; } + + // sorts players within a section + static bool StaticPlayerSortFunc(vgui::SectionedListPanel *list, int itemID1, int itemID2); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + virtual void PostApplySchemeSettings( vgui::IScheme *pScheme ); + + // finds the player in the scoreboard + int FindItemIDForPlayerIndex(int playerIndex); + + int m_iNumTeams; + + vgui::SectionedListPanel *m_pPlayerList; + int m_iSectionId; // the current section we are entering into + + int s_VoiceImage[5]; + int TrackerImage; + int m_HLTVSpectators; + int m_ReplaySpectators; + float m_fNextUpdateTime; + + void MoveLabelToFront(const char *textEntryName); + void MoveToCenterOfScreen(); + + vgui::ImageList *m_pImageList; + CUtlMap<CSteamID,int> m_mapAvatarsToImageList; + + CPanelAnimationVar( int, m_iAvatarWidth, "avatar_width", "34" ); // Avatar width doesn't scale with resolution + CPanelAnimationVarAliasType( int, m_iNameWidth, "name_width", "136", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iClassWidth, "class_width", "35", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iScoreWidth, "score_width", "35", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iDeathWidth, "death_width", "35", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iPingWidth, "ping_width", "23", "proportional_int" ); + +private: + int m_iPlayerIndexSymbol; + int m_iDesiredHeight; + IViewPort *m_pViewPort; + ButtonCode_t m_nCloseKey; + + + // methods + void FillScoreBoard(); +}; + + +#endif // CLIENTSCOREBOARDDIALOG_H diff --git a/game/client/game_controls/commandmenu.cpp b/game/client/game_controls/commandmenu.cpp new file mode 100644 index 0000000..f86b2f2 --- /dev/null +++ b/game/client/game_controls/commandmenu.cpp @@ -0,0 +1,361 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "cbase.h" +#include <cdll_client_int.h> +#include <cdll_util.h> +#include <globalvars_base.h> +#include <icvar.h> +#include <filesystem.h> + +#include "commandmenu.h" +#include "vgui_controls/MenuItem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +CommandMenu::CommandMenu( Panel *parent, const char *panelName, IViewPort * viewport) : Menu( parent, panelName ) +{ + if ( !viewport ) + return; + + m_ViewPort = viewport; + SetVisible( false ); + m_CurrentMenu = this; + + m_MenuKeys = NULL; +} + +bool CommandMenu::LoadFromFile( const char * fileName) // load menu from KeyValues +{ + KeyValues * kv = new KeyValues(fileName); + + if ( !kv->LoadFromFile( g_pFullFileSystem, fileName, "GAME" ) ) + return false; + + bool ret = LoadFromKeyValues( kv ); + + kv->deleteThis(); + return ret; +} + +CommandMenu::~CommandMenu() +{ + ClearMenu(); +} + +void CommandMenu::OnMessage(const KeyValues *params, VPANEL fromPanel) +{ + char text[255]; + bool bHandled = false; + + KeyValues *param1 = const_cast<KeyValues *>(params); + + // toggle attached cvar, if any + Q_strncpy( text, param1->GetString("toggle"), sizeof( text ) ); + + if ( text[0] ) + { + ConVarRef convar( text ); + if ( convar.IsValid() ) + { + // toggle cvar + + if ( convar.GetInt() ) + { + convar.SetValue( 0 ); + } + else + { + convar.SetValue( 1 ); + } + + UpdateMenu(); + } + else + { + Msg("CommandComboBox::OnMessage: cvar %s not found.\n", param1->GetString("typedata") ); + } + + bHandled = true; + } + + // execute attached command, if any + Q_strncpy( text, param1->GetString("command"), sizeof( text ) ); + if ( text[0] ) + { + engine->ClientCmd( text ); + bHandled = true; + } + + // fire custom message, if any + Q_strncpy( text, param1->GetString("custom"), sizeof( text ) ); + if ( text[0] ) + { + OnCustomItem( param1 ); // let derived class decide what to do + bHandled = true; + } + + if ( bHandled ) + { + PostMessage( GetParent(), new KeyValues("CommandMenuClosed") ); + } + + BaseClass::OnMessage( params, fromPanel ); +} + +void CommandMenu::StartNewSubMenu(KeyValues * params) +{ + CommandMenuItem menuitem; + menuitem.menu = m_CurrentMenu; + + Menu * menu = new Menu( this, params->GetString("name") ); // create new menu + + menuitem.itemnr = m_CurrentMenu->AddCascadingMenuItem( params->GetString("label"), this, menu, params ); // add to current menu as item + + m_MenuItems.AddToTail( menuitem ); // add to global list + + m_pMenuStack.Push( m_CurrentMenu ); // remember current menu + + m_CurrentMenu = menu; // continue adding items in new menu +} + +void CommandMenu::FinishSubMenu() +{ + m_CurrentMenu = m_pMenuStack.Top(); // get menu one level above + m_pMenuStack.Pop(); // remove it from stack +} + +void CommandMenu::AddMenuCommandItem(KeyValues * params) +{ + CommandMenuItem menuitem; // create new menuItem + menuitem.menu = m_CurrentMenu; // save the current menu context + menuitem.itemnr = m_CurrentMenu->AddMenuItem( params->GetString("label"), params->MakeCopy(), this, params ); // add it + m_MenuItems.AddToTail( menuitem ); // add to global list +} + +void CommandMenu::AddMenuToggleItem(KeyValues * params) +{ + CommandMenuItem menuitem; // create new menuItem + menuitem.menu = m_CurrentMenu; // save the current menu context + menuitem.itemnr = m_CurrentMenu->AddCheckableMenuItem( params->GetString("label"), params->MakeCopy(), this, params ); // add it + m_MenuItems.AddToTail( menuitem ); // add to global list +} + +void CommandMenu::AddMenuCustomItem(KeyValues * params) +{ + CommandMenuItem menuitem; // create new menuItem + menuitem.menu = m_CurrentMenu; // save the current menu context + menuitem.itemnr = AddCustomItem( params, m_CurrentMenu ); + m_MenuItems.AddToTail( menuitem ); // add to global list +} + +void CommandMenu::ClearMenu() +{ + SetVisible( false ); + m_pMenuStack.Clear(); + m_MenuItems.RemoveAll(); + // DeleteAllItems(); + MarkForDeletion(); + + if ( m_MenuKeys ) + { + m_MenuKeys->deleteThis(); + m_MenuKeys = NULL; + } + +} + +void CommandMenu::RebuildMenu() +{ + if ( !m_MenuKeys ) + return; + + m_pMenuStack.Clear(); + m_MenuItems.RemoveAll(); + DeleteAllItems(); + + LoadFromKeyValues( m_MenuKeys ); // and reload respecting new team, mapname etc. +} + +void CommandMenu::UpdateMenu() +{ + char text[255]; + + int num = m_MenuItems.Count(); + + for (int i=0; i < num; i++) + { + CommandMenuItem menuitem = m_MenuItems.Element(i); + KeyValues * keys = menuitem.menu->GetItemUserData( menuitem.itemnr ); + + if ( !keys ) + continue; + + // let custom menu items update themself + Q_strncpy( text, keys->GetString("custom"), sizeof(text) ); + + if ( text[0] ) + { + // let derived class modify the menu item + UpdateCustomItem( keys, menuitem.menu->GetMenuItem(menuitem.itemnr) ); + continue; + } + + // update toggle buttons + Q_strncpy( text, keys->GetString("toggle"), sizeof(text) ); + + if ( text[0] ) + { + // set toggle state equal to cvar state + ConVarRef convar( text ); + + if ( convar.IsValid() ) + { + menuitem.menu->SetMenuItemChecked( menuitem.itemnr, convar.GetBool() ); + } + } + } +} + +void CommandMenu::SetVisible(bool state) +{ + if ( state && !IsVisible() ) + { + UpdateMenu(); + } + + BaseClass::SetVisible( state ); +} + +bool CommandMenu::CheckRules(const char *rule, const char *ruledata) +{ + if ( !rule || !ruledata ) + { + return true; // no rule defined, show item + } + + if ( Q_strcmp( rule, "team") == 0 ) + { + // if team is same as specified in rule, show item + return ( Q_strcmp( m_CurrentTeam, ruledata ) == 0 ); + } + else if ( Q_strcmp( rule, "map") == 0 ) + { + // if team is same as specified in rule, show item + return ( Q_strcmp( m_CurrentMap, ruledata ) == 0 ); + } + + return true; +} + +KeyValues * CommandMenu::GetKeyValues() +{ + return m_MenuKeys; +} + +bool CommandMenu::LoadFromKeyValues( KeyValues * params ) +{ + if ( !params ) + return false; + + Q_snprintf( m_CurrentTeam, 4, "%i", GetLocalPlayerTeam() ); + + Q_FileBase( engine->GetLevelName(), m_CurrentMap, sizeof(m_CurrentMap) ); + + if ( params != m_MenuKeys ) + { + if ( m_MenuKeys ) + m_MenuKeys->deleteThis(); + + m_MenuKeys = params->MakeCopy(); // save keyvalues + } + + // iterate through all menu items + + KeyValues * subkey = m_MenuKeys->GetFirstSubKey(); + + while ( subkey ) + { + if ( subkey->GetDataType() == KeyValues::TYPE_NONE ) + { + if ( !LoadFromKeyValuesInternal( subkey, 0 ) ) // recursive call + return false; + } + + subkey = subkey->GetNextKey(); + } + + UpdateMenu(); + + return true; +} + +bool CommandMenu::LoadFromKeyValuesInternal(KeyValues * key, int depth) +{ + char text[255]; + KeyValues * subkey = NULL; + + if ( depth > 100 ) + { + Msg("CommandMenu::LoadFromKeyValueInternal: depth > 100.\n"); + return false; + } + + Q_strncpy( text, key->GetString("custom"), sizeof(text) ); // get type + + if ( text[0] ) + { + AddMenuCustomItem( key ); // do whatever custom item wants to + return true; + } + + if ( !CheckRules( key->GetString("rule"), key->GetString("ruledata") ) ) + { + return true; + } + + // rules OK add subkey + Q_strncpy( text, key->GetString("toggle"), sizeof(text) ); // get type + + if ( text[0] ) + { + AddMenuToggleItem( key ); + return true; + } + + Q_strncpy( text, key->GetString("command"), sizeof(text) ); // get type + + if ( text[0] ) + { + AddMenuCommandItem( key ); + return true; + } + + // not a command, nor a toggle. Must be a submenu: + + StartNewSubMenu( key ); // create submenu + + // iterate through all subkeys + + subkey = key->GetFirstSubKey(); + + while ( subkey ) + { + if ( subkey->GetDataType() == KeyValues::TYPE_NONE ) + { + LoadFromKeyValuesInternal( subkey, depth+1 ); // recursive call + } + + subkey = subkey->GetNextKey(); + } + + FinishSubMenu(); // go one level back + + return true; +}
\ No newline at end of file diff --git a/game/client/game_controls/commandmenu.h b/game/client/game_controls/commandmenu.h new file mode 100644 index 0000000..9f80049 --- /dev/null +++ b/game/client/game_controls/commandmenu.h @@ -0,0 +1,77 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + + +#ifndef COMMANDMENU_H +#define COMMANDMENU_H + +#include <vgui_controls/Menu.h> +#include <game/client/iviewport.h> +#include <filesystem.h> +#include "utlstack.h" +#include "utlvector.h" +#include <KeyValues.h> + +using namespace vgui; + +class CommandMenu : public Menu +{ +private: + DECLARE_CLASS_SIMPLE( CommandMenu, Menu ); + + typedef struct + { + Menu * menu; + int itemnr; + } CommandMenuItem; + + public: + CommandMenu( Panel *parent, const char *panelName, IViewPort * viewport ); + ~CommandMenu(); + + bool LoadFromFile(const char * fileName); // load menu from file (via KeyValues) + void UpdateMenu(); // call to update all menu items, check buttons etc + void RebuildMenu(); // rebuilds menu respecting changed game state (map, team etc) + void ClearMenu(); // destroy menu + + public: + // overwrite these in your derived class + // virtual CommandMenu * CommandMenu::Factory(Panel *parent, const char *panelName, IViewPort * viewport = NULL, IFileSystem * pFileSytem = NULL); // overwrite + virtual int AddCustomItem(KeyValues * params, Menu * menu) {return 0;} // return MenuItem nr + virtual void UpdateCustomItem(KeyValues * params, MenuItem * item ) {}; // maybe change your item + virtual void OnCustomItem(KeyValues * params) {}; // a custom item was pressed + virtual bool CheckRules(const char *rule, const char *ruledata); // check a menu item rule + virtual void SetVisible(bool state); + + // DON'T touch anything below ! + + protected: + + void OnMessage(const KeyValues *params, VPANEL fromPanel); + void StartNewSubMenu(KeyValues * params); + void FinishSubMenu(); + void AddMenuCommandItem(KeyValues * params); + void AddMenuCustomItem(KeyValues * params); + void AddMenuToggleItem(KeyValues * params); + bool LoadFromKeyValuesInternal(KeyValues * key, int depth); + bool LoadFromKeyValues( KeyValues * key); // + + KeyValues * GetKeyValues(); // returns keyValues for current menu or NULL + + + IViewPort * m_ViewPort; // viewport interface + Menu * m_CurrentMenu; // Current menu while building CommandComoboBox + char m_CurrentTeam[4]; + char m_CurrentMap[256]; + + KeyValues* m_MenuKeys; + + CUtlStack<vgui::Menu*>m_pMenuStack; + CUtlVector<CommandMenuItem>m_MenuItems; +}; + +#endif // COMMANDMENU_H
\ No newline at end of file diff --git a/game/client/game_controls/imagemouseoverbutton.h b/game/client/game_controls/imagemouseoverbutton.h new file mode 100644 index 0000000..9dee5d7 --- /dev/null +++ b/game/client/game_controls/imagemouseoverbutton.h @@ -0,0 +1,256 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMAGE_MOUSE_OVER_BUTTON_H +#define IMAGE_MOUSE_OVER_BUTTON_H + +#include "vgui/ISurface.h" +#include "vgui/IScheme.h" +#include "mouseoverpanelbutton.h" + +//=============================================== +// CImageMouseOverButton - used for class images +//=============================================== + +template <class T> +class CImageMouseOverButton : public MouseOverButton<T> +{ +private: + //DECLARE_CLASS_SIMPLE( CImageMouseOverButton, MouseOverButton ); + +public: + CImageMouseOverButton( vgui::Panel *parent, const char *panelName, T *templatePanel ); + + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnSizeChanged( int newWide, int newTall ); + + void RecalculateImageSizes( void ); + void SetActiveImage( const char *imagename ); + void SetInactiveImage( const char *imagename ); + void SetActiveImage( vgui::IImage *image ); + void SetInactiveImage( vgui::IImage *image ); + +public: + virtual void Paint(); + virtual void ShowPage( void ); + virtual void HidePage( void ); + +private: + vgui::IImage *m_pActiveImage; + char *m_pszActiveImageName; + + vgui::IImage *m_pInactiveImage; + char *m_pszInactiveImageName; + + bool m_bScaleImage; +}; + +template <class T> +CImageMouseOverButton<T>::CImageMouseOverButton( vgui::Panel *parent, const char *panelName, T *templatePanel ) : + MouseOverButton<T>( parent, panelName, templatePanel ) +{ + m_pszActiveImageName = NULL; + m_pszInactiveImageName = NULL; + + m_pActiveImage = NULL; + m_pInactiveImage = NULL; +} + +template <class T> +void CImageMouseOverButton<T>::ApplySettings( KeyValues *inResourceData ) +{ + m_bScaleImage = inResourceData->GetInt( "scaleImage", 0 ); + + // Active Image + delete [] m_pszActiveImageName; + m_pszActiveImageName = NULL; + + const char *activeImageName = inResourceData->GetString( "activeimage", "" ); + if ( *activeImageName ) + { + this->SetActiveImage( activeImageName ); + } + + // Inactive Image + delete [] m_pszInactiveImageName; + m_pszInactiveImageName = NULL; + + const char *inactiveImageName = inResourceData->GetString( "inactiveimage", "" ); + if ( *inactiveImageName ) + { + this->SetInactiveImage( inactiveImageName ); + } + + MouseOverButton<T>::ApplySettings( inResourceData ); + + this->InvalidateLayout( false, true ); // force applyschemesettings to run +} + +template <class T> +void CImageMouseOverButton<T>::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + MouseOverButton<T>::ApplySchemeSettings( pScheme ); + + if ( m_pszActiveImageName && strlen( m_pszActiveImageName ) > 0 ) + { + this->SetActiveImage( vgui::scheme()->GetImage( m_pszActiveImageName, m_bScaleImage ) ); + } + + if ( m_pszInactiveImageName && strlen( m_pszInactiveImageName ) > 0 ) + { + this->SetInactiveImage( vgui::scheme()->GetImage( m_pszInactiveImageName, m_bScaleImage ) ); + } + + vgui::IBorder *pBorder = pScheme->GetBorder( "NoBorder" ); + this->SetDefaultBorder( pBorder); + this->SetDepressedBorder( pBorder ); + this->SetKeyFocusBorder( pBorder ); + + Color blank(0,0,0,0); + this->SetDefaultColor( this->GetButtonFgColor(), blank ); + this->SetArmedColor( this->GetButtonArmedFgColor(), blank ); + this->SetDepressedColor( this->GetButtonDepressedFgColor(), blank ); +} + +template <class T> +void CImageMouseOverButton<T>::RecalculateImageSizes( void ) +{ + // Reset our images, which will force them to recalculate their size. + // Necessary for images shared with other scaling buttons. + this->SetActiveImage( m_pActiveImage ); + this->SetInactiveImage( m_pInactiveImage ); +} + +template <class T> +void CImageMouseOverButton<T>::SetActiveImage( const char *imagename ) +{ + int len = Q_strlen( imagename ) + 1; + m_pszActiveImageName = new char[ len ]; + Q_strncpy( m_pszActiveImageName, imagename, len ); +} + +template <class T> +void CImageMouseOverButton<T>::SetInactiveImage( const char *imagename ) +{ + int len = Q_strlen( imagename ) + 1; + m_pszInactiveImageName = new char[ len ]; + Q_strncpy( m_pszInactiveImageName, imagename, len ); +} + +template <class T> +void CImageMouseOverButton<T>::SetActiveImage( vgui::IImage *image ) +{ + m_pActiveImage = image; + + if ( m_pActiveImage ) + { + int wide, tall; + if ( m_bScaleImage ) + { + // scaling, force the image size to be our size + this->GetSize( wide, tall ); + m_pActiveImage->SetSize( wide, tall ); + } + else + { + // not scaling, so set our size to the image size + m_pActiveImage->GetSize( wide, tall ); + this->SetSize( wide, tall ); + } + } + + this->Repaint(); +} + +template <class T> +void CImageMouseOverButton<T>::SetInactiveImage( vgui::IImage *image ) +{ + m_pInactiveImage = image; + + if ( m_pInactiveImage ) + { + int wide, tall; + if ( m_bScaleImage) + { + // scaling, force the image size to be our size + this->GetSize( wide, tall ); + m_pInactiveImage->SetSize( wide, tall ); + } + else + { + // not scaling, so set our size to the image size + m_pInactiveImage->GetSize( wide, tall ); + this->SetSize( wide, tall ); + } + } + + this->Repaint(); +} + +template <class T> +void CImageMouseOverButton<T>::OnSizeChanged( int newWide, int newTall ) +{ + if ( m_bScaleImage ) + { + // scaling, force the image size to be our size + + if ( m_pActiveImage ) + m_pActiveImage->SetSize( newWide, newTall ); + + if ( m_pInactiveImage ) + m_pInactiveImage->SetSize( newWide, newTall ); + } + MouseOverButton<T>::OnSizeChanged( newWide, newTall ); +} + +template <class T> +void CImageMouseOverButton<T>::Paint() +{ + this->SetActiveImage( m_pActiveImage ); + this->SetInactiveImage( m_pInactiveImage ); + + if ( this->IsArmed() ) + { + // draw the active image + if ( m_pActiveImage ) + { + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + m_pActiveImage->SetPos( 0, 0 ); + m_pActiveImage->Paint(); + } + } + else + { + // draw the inactive image + if ( m_pInactiveImage ) + { + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + m_pInactiveImage->SetPos( 0, 0 ); + m_pInactiveImage->Paint(); + } + } + + MouseOverButton<T>::Paint(); +} + +template <class T> +void CImageMouseOverButton<T>::ShowPage( void ) +{ + MouseOverButton<T>::ShowPage(); + + // send message to parent that we triggered something + this->PostActionSignal( new KeyValues( "ShowPage", "page", this->GetName() ) ); +} + +template <class T> +void CImageMouseOverButton<T>::HidePage( void ) +{ + MouseOverButton<T>::HidePage(); +} + +#endif //IMAGE_MOUSE_OVER_BUTTON_H
\ No newline at end of file diff --git a/game/client/game_controls/intromenu.cpp b/game/client/game_controls/intromenu.cpp new file mode 100644 index 0000000..bf31a83 --- /dev/null +++ b/game/client/game_controls/intromenu.cpp @@ -0,0 +1,122 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "intromenu.h" +#include <networkstringtabledefs.h> +#include <cdll_client_int.h> +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include <KeyValues.h> +#include <convar.h> +#include <game/client/iviewport.h> +#include "spectatorgui.h" +#include "gamerules.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CIntroMenu::CIntroMenu( IViewPort *pViewPort ) : Frame( NULL, PANEL_INTRO ) +{ + // initialize dialog + m_pViewPort = pViewPort; + + m_pTitleLabel = NULL; + + // load the new scheme early!! + SetScheme( "ClientScheme" ); + SetMoveable( false ); + SetSizeable( false ); + SetProportional( true ); + + // hide the system buttons + SetTitleBarVisible( false ); + + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CIntroMenu::~CIntroMenu() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the color of the top and bottom bars +//----------------------------------------------------------------------------- +void CIntroMenu::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings("Resource/UI/IntroMenu.res"); + + m_pTitleLabel = dynamic_cast<Label *>( FindChildByName( "titlelabel" ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CIntroMenu::Reset( void ) +{ + Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CIntroMenu::Update( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CIntroMenu::OnCommand( const char *command ) +{ + if ( !Q_strcmp( command, "skip" ) ) + { + engine->ClientCmd( "intro_skip" ); + m_pViewPort->ShowPanel( this, false ); + } + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CIntroMenu::ShowPanel( bool bShow ) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + m_pViewPort->ShowBackGround( bShow ); + + if ( bShow ) + { + Activate(); + + if ( GameRules() ) + { + SetDialogVariable( "gamemode", g_pVGuiLocalize->Find( GameRules()->GetGameTypeName() ) ); + } + + SetMouseInputEnabled( true ); + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + } +} diff --git a/game/client/game_controls/intromenu.h b/game/client/game_controls/intromenu.h new file mode 100644 index 0000000..30e366a --- /dev/null +++ b/game/client/game_controls/intromenu.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef INTROMENU_H +#define INTROMENU_H +#ifdef _WIN32 +#pragma once +#endif + + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/Label.h> + +#include <game/client/iviewport.h> + +namespace vgui +{ + class TextEntry; +} + +class CIntroMenu : public vgui::Frame, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CIntroMenu, vgui::Frame ); + +public: + CIntroMenu( IViewPort *pViewPort ); + virtual ~CIntroMenu(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual const char *GetName( void ){ return PANEL_INTRO; } + virtual void SetData( KeyValues *data ){ return; } + virtual void Reset(); + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_IN_GAME_HUD; } + +protected: + // vgui overrides + virtual void OnCommand( const char *command ); + + IViewPort *m_pViewPort; + vgui::Label *m_pTitleLabel; +}; + +#endif // INTROMENU_H diff --git a/game/client/game_controls/mapoverview.h b/game/client/game_controls/mapoverview.h new file mode 100644 index 0000000..edaa2d1 --- /dev/null +++ b/game/client/game_controls/mapoverview.h @@ -0,0 +1,275 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: MiniMap.h: interface for the CMiniMap class. +// +// $NoKeywords: $ +//=============================================================================// + +#if !defined HLTVPANEL_H +#define HLTVPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> +#include <game/client/iviewport.h> +#include "mathlib/vector.h" +#include <igameevents.h> +#include <shareddefs.h> +#include <const.h> +#include "hudelement.h" + +class IMapOverviewPanel +{ +public: + virtual void SetMode( int mode ) = 0; + virtual int GetMode( void ) = 0; + virtual void FlashEntity( int entityID ) = 0; + virtual void SetPlayerPositions(int index, const Vector &position, const QAngle &angle) = 0; + virtual void SetVisible(bool state) = 0; + virtual float GetZoom( void ) = 0; + virtual vgui::Panel *GetAsPanel() = 0; + virtual bool AllowConCommandsWhileAlive() = 0; + virtual void SetPlayerPreferredMode( int mode ) = 0; + virtual void SetPlayerPreferredViewSize( float viewSize ) = 0; + virtual bool IsVisible() = 0; + virtual void GetBounds(int &x, int &y, int &wide, int &tall) = 0; + virtual float GetFullZoom( void ) = 0; + virtual float GetMapScale( void ) = 0; +}; + +#define MAX_TRAIL_LENGTH 30 +#define OVERVIEW_MAP_SIZE 1024 // an overview map is 1024x1024 pixels + +typedef bool ( *FnCustomMapOverviewObjectPaint )( int textureID, Vector pos, float scale, float angle, const char *text, Color *textColor, float status, Color *statusColor ); + + +class CMapOverview : public CHudElement, public vgui::Panel, public IMapOverviewPanel +{ + DECLARE_CLASS_SIMPLE( CMapOverview, vgui::Panel ); + +public: + + enum + { + MAP_MODE_OFF = 0, // Totally off + MAP_MODE_INSET, // A little map up in a corner + MAP_MODE_FULL, // Full screen, full map + MAP_MODE_RADAR // In game radar, extra functionality + }; + + CMapOverview( const char *pElementName ); + virtual ~CMapOverview(); + + virtual bool ShouldDraw( void ); + vgui::Panel *GetAsPanel(){ return this; } + virtual bool AllowConCommandsWhileAlive(){return true;} + virtual void SetPlayerPreferredMode( int mode ){} + virtual void SetPlayerPreferredViewSize( float viewSize ){}; + +protected: // private structures & types + + float GetViewAngle( void ); // The angle that determines the viewport twist from map texture to panel drawing. + + // list of game events the hLTV takes care of + + typedef struct { + int xpos; + int ypos; + } FootStep_t; + + typedef struct MapPlayer_s { + int index; // player's index + int userid; // user ID on server + int icon; // players texture icon ID + Color color; // players team color + char name[MAX_PLAYER_NAME_LENGTH]; + int team; // N,T,CT + int health; // 0..100, 7 bit + Vector position; // current x,y pos + QAngle angle; // view origin 0..360 + Vector2D trail[MAX_TRAIL_LENGTH]; // save 1 footstep each second for 1 minute + } MapPlayer_t; + + typedef struct MapObject_s { + int objectID; // unique object ID + int index; // entity index if any + int icon; // players texture icon ID + Color color; // players team color + char name[MAX_PLAYER_NAME_LENGTH]; // show text under icon + Vector position; // current x,y pos + QAngle angle; // view origin 0..360 + float endtime; // time stop showing object + float size; // object size + float status; // green status bar [0..1], -1 = disabled + Color statusColor; // color of status bar + int flags; // MAB_OBJECT_* + const char *text; // text to draw underneath the icon + } MapObject_t; + +#define MAP_OBJECT_ALIGN_TO_MAP (1<<0) + +public: // IViewPortPanel interface: + + virtual const char *GetName( void ) { return PANEL_OVERVIEW; } + virtual void SetData(KeyValues *data); + virtual void Reset(); + virtual void OnThink(); + virtual void Update(); + virtual bool NeedsUpdate( void ); + virtual bool HasInputElements( void ) { return false; } + virtual void ShowPanel( bool bShow ); + virtual void Init( void ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void GetBounds(int &x, int &y, int &wide, int &tall) { BaseClass::GetBounds(x, y, wide, tall); } + virtual void SetParent(vgui::VPANEL parent) { BaseClass::SetParent(parent); } + +public: // IGameEventListener + + virtual void FireGameEvent( IGameEvent *event); + +public: // VGUI overrides + + virtual void Paint(); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void ApplySchemeSettings(vgui::IScheme *scheme); + virtual void SetVisible(bool state){BaseClass::SetVisible(state);} + +public: + + virtual float GetZoom( void ); + virtual int GetMode( void ); + virtual float GetFullZoom( void ){ return m_fFullZoom; } + virtual float GetMapScale( void ){ return m_fMapScale; } + + // Player settings: + virtual void ShowPlayerNames(bool state); + virtual void ShowPlayerHealth(bool state); + virtual void ShowPlayerTracks(float seconds); + virtual void SetPlayerPositions(int index, const Vector &position, const QAngle &angle); + + // general settings: + virtual void SetMap(const char * map); + virtual void SetTime( float time ); + virtual void SetMode( int mode ); + virtual bool SetTeamColor(int team, Color color); + virtual void SetFollowAngle(bool state); + virtual void SetFollowEntity(int entindex); // 0 = off + virtual void SetCenter( const Vector2D &mappos); + virtual void SetAngle( float angle); + virtual Vector2D WorldToMap( const Vector &worldpos ); + + // Object settings + virtual int AddObject( const char *icon, int entity, float timeToLive ); // returns object ID, 0 = no entity, -1 = forever + virtual void SetObjectIcon( int objectID, const char *icon, float size ); // icon world size + virtual void SetObjectText( int objectID, const char *text, Color color ); // text under icon + virtual void SetObjectStatus( int objectID, float value, Color statusColor ); // status bar under icon + virtual void SetObjectPosition( int objectID, const Vector &position, const QAngle &angle ); // world pos/angles + virtual void AddObjectFlags( int objectID, int flags ); + virtual void SetObjectFlags( int objectID, int flags ); + virtual void RemoveObject( int objectID ); + virtual void RemoveObjectByIndex( int index ); + virtual void FlashEntity( int entityID ){} + + // rules that define if you can see a player on the overview or not + virtual bool CanPlayerBeSeen(MapPlayer_t *player); + + /// allows mods to restrict health + virtual bool CanPlayerHealthBeSeen(MapPlayer_t *player); + + /// allows mods to restrict names (e.g. CS when mp_playerid is non-zero) + virtual bool CanPlayerNameBeSeen(MapPlayer_t *player); + + virtual int GetIconNumberFromTeamNumber( int teamNumber ){return teamNumber;} + +protected: + + virtual void DrawCamera(); + virtual void DrawObjects(); + virtual void DrawMapTexture(); + virtual void DrawMapPlayers(); + virtual void DrawMapPlayerTrails(); + virtual void UpdatePlayerTrails(); + virtual void ResetRound(); + virtual void InitTeamColorsAndIcons(); + virtual void UpdateSizeAndPosition(); + virtual bool RunHudAnimations(){ return true; } + + bool IsInPanel(Vector2D &pos); + MapPlayer_t* GetPlayerByUserID( int userID ); + int AddIconTexture(const char *filename); + Vector2D MapToPanel( const Vector2D &mappos ); + int GetPixelOffset( float height ); + void UpdateFollowEntity(); + virtual void UpdatePlayers(); + void UpdateObjects(); // objects bound to entities + MapObject_t* FindObjectByID(int objectID); + virtual bool IsRadarLocked() {return false;} + + virtual bool DrawIcon( MapObject_t *obj ); + + /*virtual bool DrawIcon( int textureID, + int offscreenTextureID, + Vector pos, + float scale, + float angle, + int alpha = 255, + const char *text = NULL, + Color *textColor = NULL, + float status = -1, + Color *statusColor = NULL, + int objectType = OBJECT_TYPE_NORMAL );*/ + + int m_nMode; + Vector2D m_vPosition; + Vector2D m_vSize; + float m_flChangeSpeed; + float m_flIconSize; + + + IViewPort * m_pViewPort; + MapPlayer_t m_Players[MAX_PLAYERS]; + CUtlDict< int, int> m_TextureIDs; + CUtlVector<MapObject_t> m_Objects; + + Color m_TeamColors[MAX_TEAMS]; + int m_TeamIcons[MAX_TEAMS]; + int m_ObjectIcons[64]; + int m_ObjectCounterID; + vgui::HFont m_hIconFont; + + + bool m_bShowNames; + bool m_bShowTrails; + bool m_bShowHealth; + + int m_nMapTextureID; // texture id for current overview image + + KeyValues * m_MapKeyValues; // keyvalues describing overview parameters + + Vector m_MapOrigin; // read from KeyValues files + float m_fMapScale; // origin and scale used when screenshot was made + bool m_bRotateMap; // if true roatate map around 90 degress, so it fits better to 4:3 screen ratio + + int m_nFollowEntity;// entity number to follow, 0 = off + CPanelAnimationVar( float, m_fZoom, "zoom", "1.0" ); // current zoom n = overview panel shows 1/n^2 of whole map' + float m_fFullZoom; // best zoom factor for full map view (1.0 is map is a square) + Vector2D m_ViewOrigin; // map coordinates that are in the center of the pverview panel + Vector2D m_MapCenter; // map coordinates that are in the center of the pverview panel + + float m_fNextUpdateTime; + float m_fViewAngle; // rotation of overview map + float m_fWorldTime; // current world time + float m_fNextTrailUpdate; // next time to update player trails + float m_fTrailUpdateInterval; // if -1 don't show trails + bool m_bFollowAngle; // if true, map rotates with view angle + + +}; + +extern IMapOverviewPanel *g_pMapOverview; + +#endif // diff --git a/game/client/game_controls/mouseoverhtmlbutton.h b/game/client/game_controls/mouseoverhtmlbutton.h new file mode 100644 index 0000000..e380083 --- /dev/null +++ b/game/client/game_controls/mouseoverhtmlbutton.h @@ -0,0 +1,119 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MOUSEOVERHTMLBUTTON_H +#define MOUSEOVERHTMLBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Purpose: Triggers a new html page when the mouse goes over the button +//----------------------------------------------------------------------------- +class MouseOverHTMLButton : public vgui::Button +{ +public: + MouseOverHTMLButton(vgui::Panel *parent, const char *panelName, vgui::HTML *html, const char *page) : + Button( parent, panelName, "MouseOverHTMLButton") + { + m_pHTML = html; + m_iClass = 0; + m_iIndex = -1; + m_bAddShortCut = true; + if ( page ) + { + Q_strncpy( m_sPage, page, sizeof( m_sPage ) ); + } + else + { + memset(m_sPage, 0x0, sizeof( m_sPage ) ); + } + } + + void SetClass(int pClass, int index) { m_iClass = pClass; m_iIndex = index;} + int GetClass() { return m_iClass; } + + void SetAddHotKey( bool state ) { m_bAddShortCut = state; } + + void SetPage( const char *page ) + { + if ( page ) + { + Q_strncpy( m_sPage, page, sizeof( m_sPage ) ); + } + else + { + memset(m_sPage, 0x0, sizeof( m_sPage ) ); + } + } + + void SetHTML( vgui::HTML *html) + { + m_pHTML = html; + } + + +private: + + virtual void OnCursorEntered() + { + Button::OnCursorEntered(); + if ( m_pHTML && strlen(m_sPage) > 0 ) + { + m_pHTML->OpenURL(m_sPage); + } + } + + virtual void SetText(const char *text) + { + if ( m_iIndex != -1 ) + { + wchar_t newText[ 128 ]; + wchar_t localizeText[ 128 ]; + wchar_t *ansiLocal; + if ( text[0] == '#' && ( ansiLocal = g_pVGuiLocalize->Find( text ) ) ) + { + // wcsncpy will crash if ansiLocal is null... *sigh* + wcsncpy(localizeText, ansiLocal, sizeof(localizeText)/sizeof(wchar_t)); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( text, localizeText, sizeof( localizeText ) ); + } + + if ( m_bAddShortCut ) + { +#ifdef WIN32 + _snwprintf( newText, sizeof( newText )/ sizeof( wchar_t ), L"&%i %s", m_iIndex, localizeText); +#else + _snwprintf( newText, sizeof( newText )/ sizeof( wchar_t ), L"&%i %S", m_iIndex, localizeText); +#endif + + } + else + { + memcpy( newText, localizeText, sizeof( newText ) ); + } + + Button::SetText( newText ); + } + else + { + Button::SetText( text ); + } + } + + vgui::HTML *m_pHTML; + char m_sPage[ 255 ]; + int m_iClass; + int m_iIndex; + bool m_bAddShortCut; +}; + + +#endif // MOUSEOVERHTMLBUTTON_H diff --git a/game/client/game_controls/mouseoverpanelbutton.h b/game/client/game_controls/mouseoverpanelbutton.h new file mode 100644 index 0000000..9e4e245 --- /dev/null +++ b/game/client/game_controls/mouseoverpanelbutton.h @@ -0,0 +1,186 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MOUSEOVERPANELBUTTON_H +#define MOUSEOVERPANELBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/IScheme.h> +#include <vgui_controls/Button.h> +#include <vgui/KeyCode.h> +#include <filesystem.h> + +extern vgui::Panel *g_lastPanel; +extern vgui::Button *g_lastButton; + +//----------------------------------------------------------------------------- +// Purpose: Triggers a new panel when the mouse goes over the button +// +// the new panel has the same dimensions as the passed templatePanel and is of +// the same class. +// +// must at least inherit from vgui::EditablePanel to support LoadControlSettings +//----------------------------------------------------------------------------- +template <class T> +class MouseOverButton : public vgui::Button +{ +private: + DECLARE_CLASS_SIMPLE( MouseOverButton, vgui::Button ); + +public: + MouseOverButton(vgui::Panel *parent, const char *panelName, T *templatePanel ) : + Button( parent, panelName, "MouseOverButton") + { + m_pPanel = new T( parent, NULL ); + m_pPanel ->SetVisible( false ); + + // copy size&pos from template panel + int x,y,wide,tall; + templatePanel->GetBounds( x, y, wide, tall ); + m_pPanel->SetBounds( x, y, wide, tall ); + int px, py; + templatePanel->GetPinOffset( px, py ); + int rx, ry; + templatePanel->GetResizeOffset( rx, ry ); + // Apply pin settings from template, too + m_pPanel->SetAutoResize( templatePanel->GetPinCorner(), templatePanel->GetAutoResize(), px, py, rx, ry ); + + m_bPreserveArmedButtons = false; + m_bUpdateDefaultButtons = false; + } + + virtual void SetPreserveArmedButtons( bool bPreserve ){ m_bPreserveArmedButtons = bPreserve; } + virtual void SetUpdateDefaultButtons( bool bUpdate ){ m_bUpdateDefaultButtons = bUpdate; } + + virtual void ShowPage() + { + if( m_pPanel ) + { + m_pPanel->SetVisible( true ); + m_pPanel->MoveToFront(); + g_lastPanel = m_pPanel; + } + } + + virtual void HidePage() + { + if ( m_pPanel ) + { + m_pPanel->SetVisible( false ); + } + } + + const char *GetClassPage( const char *className ) + { + static char classPanel[ _MAX_PATH ]; + Q_snprintf( classPanel, sizeof( classPanel ), "classes/%s.res", className); + + if ( g_pFullFileSystem->FileExists( classPanel, IsX360() ? "MOD" : "GAME" ) ) + { + } + else if (g_pFullFileSystem->FileExists( "classes/default.res", IsX360() ? "MOD" : "GAME" ) ) + { + Q_snprintf ( classPanel, sizeof( classPanel ), "classes/default.res" ); + } + else + { + return NULL; + } + + return classPanel; + } + +#ifdef REFRESH_CLASSMENU_TOOL + + void RefreshClassPage( void ) + { + m_pPanel->LoadControlSettings( GetClassPage( GetName() ) ); + } + +#endif + + virtual void ApplySettings( KeyValues *resourceData ) + { + BaseClass::ApplySettings( resourceData ); + + // name, position etc of button is set, now load matching + // resource file for associated info panel: + m_pPanel->LoadControlSettings( GetClassPage( GetName() ) ); + } + + T *GetClassPanel( void ) { return m_pPanel; } + + virtual void OnCursorExited() + { + if ( !m_bPreserveArmedButtons ) + { + BaseClass::OnCursorExited(); + } + } + + virtual void OnCursorEntered() + { + BaseClass::OnCursorEntered(); + + if ( !IsEnabled() ) + return; + + // are we updating the default buttons? + if ( m_bUpdateDefaultButtons ) + { + SetAsDefaultButton( 1 ); + } + + // are we preserving the armed state (and need to turn off the old button)? + if ( m_bPreserveArmedButtons ) + { + if ( g_lastButton && g_lastButton != this ) + { + g_lastButton->SetArmed( false ); + } + + g_lastButton = this; + } + + // turn on our panel (if it isn't already) + if ( m_pPanel && ( !m_pPanel->IsVisible() ) ) + { + // turn off the previous panel + if ( g_lastPanel && g_lastPanel->IsVisible() ) + { + g_lastPanel->SetVisible( false ); + } + + ShowPage(); + } + } + + virtual void OnKeyCodeReleased( vgui::KeyCode code ) + { + BaseClass::OnKeyCodeReleased( code ); + + if ( m_bPreserveArmedButtons ) + { + if ( g_lastButton ) + { + g_lastButton->SetArmed( true ); + } + } + } + +private: + + T *m_pPanel; + bool m_bPreserveArmedButtons; + bool m_bUpdateDefaultButtons; +}; + +#define MouseOverPanelButton MouseOverButton<vgui::EditablePanel> + +#endif // MOUSEOVERPANELBUTTON_H diff --git a/game/client/game_controls/navigationpanel.cpp b/game/client/game_controls/navigationpanel.cpp new file mode 100644 index 0000000..0cf616d --- /dev/null +++ b/game/client/game_controls/navigationpanel.cpp @@ -0,0 +1,324 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "cbase.h" +#include "game_controls/navigationpanel.h" +#include "econ/econ_controls.h" +#include "vgui_controls/Frame.h" +#include "vgui/IInput.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- + +using namespace vgui; + +//----------------------------------------------------------------------------- + +DECLARE_BUILD_FACTORY( CNavigationPanel ); + +//----------------------------------------------------------------------------- + +class CNavButton : public CExImageButton +{ + DECLARE_CLASS_SIMPLE( CNavButton, CExImageButton ); +public: + CNavButton( Panel *parent, const char *name, const char *text = "", Panel *pActionSignalTarget = NULL, const char *cmd = NULL ) + : CExImageButton( parent, name, text, pActionSignalTarget, cmd ), m_iUserData( -1 ) {} + + CNavButton( Panel *parent, const char *name, const wchar_t *wszText = L"", Panel *pActionSignalTarget = NULL, const char *cmd = NULL ) + : CExImageButton( parent, name, wszText, pActionSignalTarget, cmd ) , m_iUserData( -1 ) {} + + virtual void ApplySettings( KeyValues *pInResourceData ) + { + BaseClass::ApplySettings( pInResourceData ); + + if ( m_iUserData < 0 ) + { + m_iUserData = pInResourceData->GetInt( "userdata", -1 ); AssertMsg( m_iUserData != -1, "Any messages sent for this button will have invalid user data!" ); + } + } + + int m_iUserData; +}; + +//----------------------------------------------------------------------------- + +CNavigationPanel::CNavigationPanel( Panel *pParent, const char *pName, bool bAddParentAsActionSignalTarget/*=true*/ ) +: BaseClass( pParent, pName ), + m_bAutoLayout( false ), + m_bAutoScale( true ), + m_bDisplayVertical( false ), + m_iSelectedButton( 0 ), +// m_nAlignment( ALIGN_CENTER ), + m_pKVButtonSettings( NULL ) +{ + if ( bAddParentAsActionSignalTarget && pParent ) + { + AddActionSignalTarget( pParent ); + } +} + +CNavigationPanel::~CNavigationPanel() +{ +} + +void CNavigationPanel::AddButton( int iUserData, const char *pTextToken ) +{ + const int i = m_vecButtons.Count(); + CNavButton *pNewButton = new CNavButton( this, CFmtStr( "button_%i", i ).Access(), pTextToken ); + pNewButton->m_iUserData = iUserData; + pNewButton->InvalidateLayout( true, false ); + pNewButton->SetCommand( CFmtStr( "select_%i", i ).Access() ); + m_vecButtons.AddToTail( pNewButton ); +} + +CExImageButton *CNavigationPanel::GetButton( int index ) +{ + return m_vecButtons[ index ]; +} + +void CNavigationPanel::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + /* + const char *pAlignment = pInResourceData->GetString( "align", NULL ); + if ( pAlignment ) + { + if ( !V_strnicmp( pAlignment, "west", 4 ) ) + { + m_nAlignment = ALIGN_WEST; + } + else if ( !V_strnicmp( pAlignment, "center", 6 ) ) + { + m_nAlignment = ALIGN_CENTER; + } + else if ( !V_strnicmp( pAlignment, "east", 4 ) ) + { + AssertMsg( 0, "This type of alignment is not supported." ); + } + } + */ + + m_bAutoLayout = pInResourceData->GetBool( "auto_layout", false ); + m_bAutoScale = pInResourceData->GetBool( "auto_scale", true ); + m_bDisplayVertical = pInResourceData->GetBool( "display_vertically", false ); + + KeyValues *pKVButtonSettings = pInResourceData->FindKey( "ButtonSettings" ); AssertMsg( pKVButtonSettings, "This is required" ); + if ( !pKVButtonSettings ) + { + AssertMsg( 0, "No button settings specified. CNavigationPanel is useless without this data." ); + return; + } + + // Cache this off for later + if ( m_pKVButtonSettings ) + { + m_pKVButtonSettings->deleteThis(); + m_pKVButtonSettings = NULL; + } + m_pKVButtonSettings = pKVButtonSettings->MakeCopy(); + + // Get individual button data and apply now + KeyValues *pKVButtons = pInResourceData->FindKey( "Buttons" ); + if ( !pKVButtons ) + return; + + // Go through each image description and create a button + if ( m_vecButtons.Count() ) + return; + + int i = 0; + FOR_EACH_SUBKEY( pKVButtons, pKVCurButton ) + { + CNavButton *pNewButton = new CNavButton( this, pKVCurButton->GetString( "FieldName", pKVCurButton->GetName() ), L"" ); + pNewButton->ApplySettings( pKVCurButton ); + pNewButton->InvalidateLayout( true, false ); + pNewButton->SetCommand( CFmtStr( "select_%i", i ).Access() ); + m_vecButtons.AddToTail( pNewButton ); + ++i; + } + + if ( m_vecButtons.IsValidIndex( m_iSelectedButtonDefault ) ) + { + UpdateButtonSelectionStates( m_iSelectedButtonDefault ); + m_vecButtons[ m_iSelectedButtonDefault ]->InvalidateLayout( true ); + + const int iUserData = m_vecButtons[ m_iSelectedButtonDefault ]->m_iUserData; + PostActionSignal( new KeyValues( "NavButtonSelected", "userdata", iUserData ) ); + } +} + +void CNavigationPanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} + +void CNavigationPanel::PerformLayout() +{ + if ( !m_vecButtons.Count() ) + return; + + if ( !m_pKVButtonSettings ) + return; + + BaseClass::PerformLayout(); + + // Get from settings + int nSettingWidth = m_pKVButtonSettings->GetInt( "wide" ); + int nSettingHeight = m_pKVButtonSettings->GetInt( "tall" ); + + // Button dimensions for setting positions + int nButtonWidth = nSettingWidth; + int nButtonHeight = nSettingHeight; + + if ( m_bAutoScale ) + { + // Get image size + int nImageW, nImageH; + m_vecButtons[0]->GetImage()->GetSize( nImageW, nImageH ); + + int nWidth, nHeight; + if ( m_bDisplayVertical ) + { + nWidth = GetWide(); + nHeight = nSettingHeight * nWidth / ( nSettingWidth > 0 ? nSettingWidth : 1 ); + } + else + { + nHeight = GetTall(); + nWidth = nSettingWidth * nHeight / ( nSettingHeight > 0 ? nSettingHeight : 1 ); + } + + // Update button dimensions to scaled versions + nButtonWidth = nWidth; + nButtonHeight = nHeight; + } + + FOR_EACH_VEC( m_vecButtons, i ) + { + if ( m_vecButtons[i] && m_pKVButtonSettings ) + { + // Apply generic settings + m_vecButtons[i]->ApplySettings( m_pKVButtonSettings ); + } + + if ( m_bAutoLayout ) + { + // Display buttons vertically or horizontally? + if ( m_bDisplayVertical ) + { + m_vecButtons[i]->SetPos( 0, i * ( nButtonHeight + m_nVerticalBuffer ) ); + } + else + { + const int nStartX = 0;//0.5f * ( GetWide() - NumButtons() * ( nButtonWidth + m_nHorizontalBuffer ) ); + m_vecButtons[i]->SetPos( nStartX + i * ( nButtonWidth + m_nHorizontalBuffer ), 0 ); + } + } + + if ( m_bAutoScale ) + { + m_vecButtons[i]->SetSize( nButtonWidth, nButtonHeight ); + m_vecButtons[i]->GetImage()->SetSize( nButtonWidth, nButtonHeight ); + } + + m_vecButtons[i]->SetVisible( true ); + } +} + +void CNavigationPanel::OnCommand( const char *pCommand ) +{ + if ( !V_strnicmp( pCommand, "select_", 7 ) ) + { + const int iButton = atoi( pCommand + 7 ); AssertMsg( m_vecButtons.IsValidIndex( iButton ), "Button index out of range!" ); + if ( !m_vecButtons.IsValidIndex( iButton ) ) + return; + + UpdateButtonSelectionStates( iButton ); + + const int iUserData = m_vecButtons[ iButton ]->m_iUserData; + PostActionSignal( new KeyValues( "NavButtonSelected", "userdata", iUserData ) ); + } + else + { + BaseClass::OnCommand( pCommand ); + } +} + +void CNavigationPanel::OnThink() +{ + BaseClass::OnThink(); + + // Make sure we only ever have one button in the selection state, since it's + // possible to do this if you select a button, then click and drag on another + // button, and release the mouse elsewhere. + if ( !vgui::input()->IsMouseDown( MOUSE_LEFT ) ) + { + UpdateButtonSelectionStates( m_iSelectedButton ); + } +} + + +void CNavigationPanel::UpdateButtonSelectionStates( int iButton ) +{ + if ( m_iSelectedButton != iButton ) + { + m_iSelectedButton = iButton; + } + + // Set the correct button as selected, all other buttons as not selected + for ( int i = 0; i < NumButtons(); ++i ) + { + CNavButton *pCurButton = m_vecButtons[ i ]; + if ( !pCurButton ) + continue; + + bool bShouldSelect = iButton == i; + pCurButton->SetSelected( bShouldSelect ); + pCurButton->InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- + +#ifdef _DEBUG + +class CNavPanelTest : public Frame +{ + DECLARE_CLASS_SIMPLE( CNavPanelTest, Frame ); + +public: + CNavPanelTest( vgui::Panel *pParent ) + : Frame( pParent, "NavPanelTest" ) + { + SetProportional( true ); + LoadControlSettings( "Resource/UI/NavigationPanelTest.res" ); + } +}; + +CON_COMMAND( open_navpanel_test, "" ) +{ + CNavPanelTest *pPanel = SETUP_PANEL( new CNavPanelTest( NULL ) ); + pPanel->SetVisible( true ); + pPanel->InvalidateLayout( false, true ); + + engine->ClientCmd_Unrestricted( "gameui_activate" ); + + const int nWidth = XRES( 300 ); + const int nHeight = YRES( 300 ); + + pPanel->SetBounds( + ( ScreenWidth() - nWidth ) / 2, + ( ScreenHeight() - nHeight ) / 2, + nWidth, + nHeight + ); + + pPanel->Activate(); +} + +#endif // _DEBUG diff --git a/game/client/game_controls/navigationpanel.h b/game/client/game_controls/navigationpanel.h new file mode 100644 index 0000000..528191b --- /dev/null +++ b/game/client/game_controls/navigationpanel.h @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#ifndef NAVIGATIONPANEL_H +#define NAVIGATIONPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- + +#include "vgui_controls/EditablePanel.h" + +//----------------------------------------------------------------------------- + +namespace vgui +{ + class Panel; + class ImagePanel; +}; +class CNavButton; +class CExImageButton; + +//----------------------------------------------------------------------------- + +// +// A generic panel containing a list of buttons which can be displayed vertically +// or horizontally. One button at a time can be selected, and messages are sent +// to the parent by default. +// +class CNavigationPanel : public vgui::EditablePanel +{ +private: + DECLARE_CLASS_SIMPLE( CNavigationPanel, vgui::EditablePanel ); + +public: + CNavigationPanel( vgui::Panel *pParent, const char *pName, bool bAddParentAsActionSignalTarget = true ); + virtual ~CNavigationPanel(); + + void AddButton( int iUserData, const char *pTextToken ); + int NumButtons() const { return m_vecButtons.Count(); } + CExImageButton *GetButton( int index ); + + void UpdateButtonSelectionStates( int iButton ); + +protected: + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pInResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *pCommand ); + virtual void OnThink(); + + CUtlVector< CNavButton * > m_vecButtons; + bool m_bAutoLayout; + bool m_bAutoScale; // Auto-scale buttons to proportionally match height (for horizontal display) or width (for vertical display) + bool m_bDisplayVertical; + int m_iSelectedButton; // The currently selected button + KeyValues *m_pKVButtonSettings; + + /* + enum Alignment_t + { + ALIGN_WEST, // left + ALIGN_CENTER, + }; + + Alignment_t m_nAlignment; + */ + + // For auto-layout mode only + CPanelAnimationVarAliasType( int, m_nHorizontalBuffer, "auto_layout_horizontal_buffer", "5", "proportional_xpos" ); + CPanelAnimationVarAliasType( int, m_nVerticalBuffer, "auto_layout_vertical_buffer", "5", "proportional_ypos" ); + CPanelAnimationVar( int, m_iSelectedButtonDefault, "selected_button_default", "-1" ); +}; + +//----------------------------------------------------------------------------- + +#endif // NAVIGATIONPANEL_H diff --git a/game/client/game_controls/slideshowpanel.cpp b/game/client/game_controls/slideshowpanel.cpp new file mode 100644 index 0000000..8267e7a --- /dev/null +++ b/game/client/game_controls/slideshowpanel.cpp @@ -0,0 +1,317 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "cbase.h" +#include "game_controls/slideshowpanel.h" +#include "vgui/IVGui.h" +#include "filesystem.h" + +//----------------------------------------------------------------------------- + +using namespace vgui; + +//----------------------------------------------------------------------------- + +DECLARE_BUILD_FACTORY( CCrossfadableImagePanel ); +DECLARE_BUILD_FACTORY( CSlideshowPanel ); + +//----------------------------------------------------------------------------- + +CCrossfadableImagePanel::CCrossfadableImagePanel( Panel* pParent, const char *pName ) +: EditablePanel( pParent, pName ), + m_iSrcImg( 0 ), + m_flBlend( 0.0f ), + m_flBlendTime( 0.0f ), + m_flStartBlendTime( 0.0f ), + m_bBlending( false ) +{ + m_pImages[ 0 ] = new ImagePanel( this, "Image0" ); + m_pImages[ 1 ] = new ImagePanel( this, "Image1" ); + + ivgui()->AddTickSignal( GetVPanel(), 10 ); +} + +CCrossfadableImagePanel::~CCrossfadableImagePanel() +{ + ivgui()->RemoveTickSignal( GetVPanel() ); +} + + +void CCrossfadableImagePanel::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); +} + +void CCrossfadableImagePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SrcImg()->SetTileImage( false ); + DstImg()->SetTileImage( false ); +} + +void CCrossfadableImagePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + for ( int i = 0; i < 2; ++i ) + { + m_pImages[ i ]->SetBounds( 0, 0, GetWide(), GetTall() ); + m_pImages[ i ]->SetVisible( true ); + } +} + +// Helper macro to perform a call on both images +#define CALL_FUNC_ON_BOTH_IMAGES( _call ) \ + AssertMsg( m_pImages[ 0 ], "m_pImages[ 0 ] is NULL!" ); \ + AssertMsg( m_pImages[ 1 ], "m_pImages[ 1 ] is NULL!" ); \ + m_pImages[0]->_call; \ + m_pImages[1]->_call; + +void CCrossfadableImagePanel::SetShouldScaleImage( bool bState ) +{ + CALL_FUNC_ON_BOTH_IMAGES( SetShouldScaleImage( bState ) ); +} + +void CCrossfadableImagePanel::SetScaleAmount( float flScale ) +{ + CALL_FUNC_ON_BOTH_IMAGES( SetScaleAmount( flScale ) ); +} + +void CCrossfadableImagePanel::SetFillColor( Color c ) +{ + CALL_FUNC_ON_BOTH_IMAGES( SetFillColor( c ) ); +} + +void CCrossfadableImagePanel::SetDrawColor( Color clrDrawColor ) +{ + CALL_FUNC_ON_BOTH_IMAGES( SetDrawColor( clrDrawColor ) ); +} + +void CCrossfadableImagePanel::InstallMouseHandler( Panel *pHandler ) +{ + CALL_FUNC_ON_BOTH_IMAGES( InstallMouseHandler( pHandler ) ); +} + +IImage *CCrossfadableImagePanel::GetImage() +{ + return SrcImg()->GetImage(); +} + +const char *CCrossfadableImagePanel::GetImageName() +{ + return SrcImg()->GetImageName(); +} + +float CCrossfadableImagePanel::GetScaleAmount() +{ + return SrcImg()->GetScaleAmount(); +} + +bool CCrossfadableImagePanel::GetShouldScaleImage() +{ + return SrcImg()->GetShouldScaleImage(); +} + +Color CCrossfadableImagePanel::GetFillColor() +{ + return SrcImg()->GetFillColor(); +} + +Color CCrossfadableImagePanel::GetDrawColor() +{ + return SrcImg()->GetDrawColor(); +} + +void CCrossfadableImagePanel::SetImage( IImage *pImage, float flBlendTime/*=0.0f*/ ) +{ + DstImg()->SetImage( pImage ); + SetupImageBlend( flBlendTime ); +} + +void CCrossfadableImagePanel::SetImage( const char *pImageName, float flBlendTime/*=0.0f*/ ) +{ + DstImg()->SetImage( pImageName ); + SetupImageBlend( flBlendTime ); +} + +void CCrossfadableImagePanel::SetupImageBlend( float flBlendTime ) +{ + m_bBlending = true; + m_flBlendTime = flBlendTime; + m_flStartBlendTime = gpGlobals->realtime; + m_flBlend = 0.0f; +} + +void CCrossfadableImagePanel::OnSizeChanged( int nWide, int nTall ) +{ + m_pImages[ 0 ]->SetSize( nWide, nTall ); + m_pImages[ 1 ]->SetSize( nWide, nTall ); +} + +void CCrossfadableImagePanel::OnTick() +{ + if ( m_bBlending ) + { + // Compute current blend value + if ( m_flBlendTime == 0.0f ) + { + m_flBlend = 1.0f; + } + else + { + float t = clamp( ( gpGlobals->realtime - m_flStartBlendTime ) / m_flBlendTime, 0.0f, 1.0f ); + m_flBlend = clamp( t * t * (3 - 2*t), 0.0f, 1.0f ); // S-curve + } + + // Set alpha channel on source image + Color clrDraw; + clrDraw = SrcImg()->GetDrawColor(); + clrDraw[ 3 ] = ( int )( 255 * ( 1 - m_flBlend ) ); + SrcImg()->SetDrawColor( clrDraw ); + + // Set alpha channel on destination image + clrDraw = DstImg()->GetDrawColor(); + clrDraw[ 3 ] = ( int )( 255 * m_flBlend ); + DstImg()->SetDrawColor( clrDraw ); + + // If we're done, don't blend next think + if ( m_flBlend >= 1.0f ) + { + m_bBlending = false; + m_iSrcImg = !m_iSrcImg; + m_flBlend = 0.0f; + } + } +} + +//----------------------------------------------------------------------------- + +CSlideshowPanel::CSlideshowPanel( Panel *pParent, const char *pName ) +: EditablePanel( pParent, pName ), + m_iCurImg( 0 ), + m_flInterval( 3.0f ), + m_flTransitionLength( 0.5f ) +{ + m_pImagePanel = new CCrossfadableImagePanel( this, "CrossfadableImage" ); + + // Setup the first transition time + UpdateNextTransitionTime(); + + // Add tick signal + ivgui()->AddTickSignal( GetVPanel(), 10 ); +} + +CSlideshowPanel::~CSlideshowPanel() +{ + // Remove tick signal + ivgui()->RemoveTickSignal( GetVPanel() ); +} + +void CSlideshowPanel::SetInterval( float flInterval ) +{ + m_flInterval = flInterval; + UpdateNextTransitionTime(); +} + +void CSlideshowPanel::SetTransitionTime( float flTransitionLength ) +{ + m_flTransitionLength = flTransitionLength; + UpdateNextTransitionTime(); +} + +void CSlideshowPanel::UpdateNextTransitionTime() +{ + m_flNextTransitionTime = gpGlobals->realtime + m_flInterval; +} + +void CSlideshowPanel::AddImage( const char *pImageName ) +{ + if ( pImageName && strlen( pImageName ) > 0 ) + { + AddImage( scheme()->GetImage( pImageName, m_pImagePanel->GetShouldScaleImage() ) ); + } +} + +void CSlideshowPanel::AddImage( IImage *pImage ) +{ + // Cache a pointer to the image + m_vecImages.AddToTail( pImage ); + + if ( m_vecImages.Count() == 1 ) + { + GetImagePanel()->SetImage( pImage ); + } +} + +void CSlideshowPanel::FillWithImages( const char *pBasePath ) +{ + int i = 0; + while ( 1 ) + { + CFmtStr fmtImagePath( "materials/vgui/%s%i.vmt", pBasePath, i ); + V_FixDoubleSlashes( fmtImagePath.Access() ); + if ( !g_pFullFileSystem->FileExists( fmtImagePath.Access() ) ) + break; + + fmtImagePath.sprintf( "%s%i.vmt", pBasePath, i ); + AddImage( fmtImagePath.Access() ); + + ++i; + } +} + +void CSlideshowPanel::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + int iDefaultImage = pInResourceData->GetInt( "default_index", 0 ); + + int i = 0; + while ( 1 ) + { + CFmtStr fmtImageKeyName( "image_%i", i ); + const char *pImagePath = pInResourceData->GetString( fmtImageKeyName.Access(), NULL ); + if ( !pImagePath ) + break; + + AddImage( pImagePath ); + + if ( iDefaultImage == i ) + { + GetImagePanel()->SetImage( pImagePath ); + } + + ++i; + } + + GetImagePanel()->SetSize( + XRES( pInResourceData->GetInt( "wide" ) ), + YRES( pInResourceData->GetInt( "tall" ) ) + ); + + GetImagePanel()->SetShouldScaleImage( pInResourceData->GetBool( "scaleImage" ) ); + GetImagePanel()->SetScaleAmount( pInResourceData->GetFloat( "scaleAmount" ) ); +} + +void CSlideshowPanel::OnSizeChanged( int nWide, int nTall ) +{ + GetImagePanel()->SetSize( nWide, nTall ); +} + +void CSlideshowPanel::OnTick() +{ + if ( GetImageCount() > 1 && gpGlobals->realtime >= m_flNextTransitionTime ) + { + // Iterate to next image + m_iCurImg = ( m_iCurImg + 1 ) % GetImageCount(); + + // Setup new image + GetImagePanel()->SetImage( m_vecImages[ m_iCurImg ], m_flTransitionLength ); + + // Set transition time to be the end of the blend + m_flNextTransitionTime = gpGlobals->realtime + m_flInterval; + } +} + diff --git a/game/client/game_controls/slideshowpanel.h b/game/client/game_controls/slideshowpanel.h new file mode 100644 index 0000000..498a44c --- /dev/null +++ b/game/client/game_controls/slideshowpanel.h @@ -0,0 +1,105 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#ifndef SLIDESHOWPANEL_H +#define SLIDESHOWPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/ProgressBar.h" +#include "vgui_controls/ImagePanel.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Allows fade from one image to another +//----------------------------------------------------------------------------- +class CCrossfadableImagePanel : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CCrossfadableImagePanel, EditablePanel ); +public: + CCrossfadableImagePanel( Panel* pParent, const char *pName ); + ~CCrossfadableImagePanel(); + + virtual void ApplySettings( KeyValues *pInResourceData ); + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void PerformLayout(); + + // A duplicate of the ImagePanel interface - the only difference being that + // here we have blend times for SetImage(). + void SetImage( IImage *pImage, float flBlendTime = 0.0f ); + void SetImage( const char *pImageName, float flBlendTime = 0.0f ); + + void SetShouldScaleImage( bool bState ); + void SetScaleAmount( float flScale ); + void SetDrawColor( Color clrDrawColor ); + + IImage *GetImage(); + const char *GetImageName(); + void SetFillColor( Color c ); + float GetScaleAmount(); + bool GetShouldScaleImage(); + Color GetFillColor(); + Color GetDrawColor(); + + virtual void InstallMouseHandler( Panel *pHandler ); + +private: + virtual void OnSizeChanged( int nWide, int nTall ); + virtual void OnTick(); + + inline ImagePanel *SrcImg() { return m_pImages[ m_iSrcImg ]; } + inline ImagePanel *DstImg() { return m_pImages[ !m_iSrcImg ]; } + + void SetupImageBlend( float flBlendTime ); + + float m_flStartBlendTime; // the gpGlobals->realtime when we started blending + float m_flBlendTime; // amount of time to blend from one image to the next one + float m_flBlend; // blend value in [0,1] where 0 maps to the source image, and 1 maps to the dest image + bool m_bBlending; // are we currently transitioning/blending from one image to another? + ImagePanel* m_pImages[2]; + int m_iSrcImg; // 0 or 1 +}; + +//----------------------------------------------------------------------------- +// Purpose: Displays a slideshow of images at a set interval +//----------------------------------------------------------------------------- +class CSlideshowPanel : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CSlideshowPanel, EditablePanel ); +public: + CSlideshowPanel( Panel *pParent, const char *pName ); + ~CSlideshowPanel(); + + // Pass in a vgui-relative path, like "training/screenshots/cp_dustbowl_", and + // the slideshow panel will add "0.vmt", "1.vmt" etc. until it can't find anymore images. + void FillWithImages( const char *pBasePath ); + + void AddImage( const char *pImageName ); + void AddImage( IImage *pImage ); + + void SetInterval( float flInterval ); + void SetTransitionTime( float flTransitionLength ); + + CCrossfadableImagePanel *GetImagePanel() { return m_pImagePanel; } + + int GetImageCount() const { return m_vecImages.Count(); } + +private: + virtual void ApplySettings( KeyValues *pInResourceData ); + virtual void OnSizeChanged( int nWide, int nTall ); + virtual void OnTick(); + void UpdateNextTransitionTime(); + + CCrossfadableImagePanel *m_pImagePanel; + CUtlVector< IImage * > m_vecImages; + float m_flNextTransitionTime; // gpGlobals->realtime of time when we should transition next + float m_flInterval; // Amount of time between blend begins to next image, in seconds + float m_flTransitionLength; // Length of each transition + int m_iCurImg; // Current image index +}; + +#endif // SLIDESHOWPANEL_H diff --git a/game/client/game_controls/spectatorgui.h b/game/client/game_controls/spectatorgui.h new file mode 100644 index 0000000..8e9169f --- /dev/null +++ b/game/client/game_controls/spectatorgui.h @@ -0,0 +1,158 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SPECTATORGUI_H +#define SPECTATORGUI_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/IScheme.h> +#include <vgui/KeyCode.h> +#include <vgui_controls/Frame.h> +#include <vgui_controls/EditablePanel.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/ComboBox.h> +#include <igameevents.h> +#include "GameEventListener.h" + +#include <game/client/iviewport.h> + +class KeyValues; + +namespace vgui +{ + class TextEntry; + class Button; + class Panel; + class ImagePanel; + class ComboBox; +} + +#define BLACK_BAR_COLOR Color(0, 0, 0, 196) + +class IBaseFileSystem; + +//----------------------------------------------------------------------------- +// Purpose: Spectator UI +//----------------------------------------------------------------------------- +class CSpectatorGUI : public vgui::EditablePanel, public IViewPortPanel +{ + DECLARE_CLASS_SIMPLE( CSpectatorGUI, vgui::EditablePanel ); + +public: + CSpectatorGUI( IViewPort *pViewPort ); + virtual ~CSpectatorGUI(); + + virtual const char *GetName( void ) { return PANEL_SPECGUI; } + virtual void SetData(KeyValues *data) {}; + virtual void Reset() {}; + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return false; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent(vgui::VPANEL parent) { BaseClass::SetParent(parent); } + virtual void OnThink(); + + virtual int GetTopBarHeight() { return m_pTopBar->GetTall(); } + virtual int GetBottomBarHeight() { return m_pBottomBarBlank->GetTall(); } + + virtual bool ShouldShowPlayerLabel( int specmode ); + + virtual Color GetBlackBarColor( void ) { return BLACK_BAR_COLOR; } + + virtual const char *GetResFile( void ); + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_SPECTATOR; } + +protected: + + void SetLabelText(const char *textEntryName, const char *text); + void SetLabelText(const char *textEntryName, wchar_t *text); + void MoveLabelToFront(const char *textEntryName); + void UpdateTimer(); + void SetLogoImage(const char *image); + +protected: + enum { INSET_OFFSET = 2 } ; + + // vgui overrides + virtual void PerformLayout(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); +// virtual void OnCommand( const char *command ); + + vgui::Panel *m_pTopBar; + vgui::Panel *m_pBottomBarBlank; + + vgui::ImagePanel *m_pBannerImage; + vgui::Label *m_pPlayerLabel; + + IViewPort *m_pViewPort; + + // bool m_bHelpShown; + // bool m_bInsetVisible; + bool m_bSpecScoreboard; +}; + + +//----------------------------------------------------------------------------- +// Purpose: the bottom bar panel, this is a separate panel because it +// wants mouse input and the main window doesn't +//---------------------------------------------------------------------------- +class CSpectatorMenu : public vgui::Frame, public IViewPortPanel, public CGameEventListener +{ + DECLARE_CLASS_SIMPLE( CSpectatorMenu, vgui::Frame ); + +public: + CSpectatorMenu( IViewPort *pViewPort ); + ~CSpectatorMenu() {} + + virtual const char *GetName( void ) { return PANEL_SPECMENU; } + virtual void SetData(KeyValues *data) {}; + virtual void Reset( void ) { m_pPlayerList->DeleteAllItems(); } + virtual void Update( void ); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + virtual void FireGameEvent( IGameEvent *event ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + virtual bool IsVisible() { return BaseClass::IsVisible(); } + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual void SetParent(vgui::VPANEL parent) { BaseClass::SetParent(parent); } + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_SPECTATOR; } + +private: + // VGUI2 overrides + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data ); + virtual void OnCommand( const char *command ); + virtual void OnKeyCodePressed(vgui::KeyCode code); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void PerformLayout(); + + void SetViewModeText( const char *text ) { m_pViewOptions->SetText( text ); } + void SetPlayerFgColor( Color c1 ) { m_pPlayerList->SetFgColor(c1); } + + vgui::ComboBox *m_pPlayerList; + vgui::ComboBox *m_pViewOptions; + vgui::ComboBox *m_pConfigSettings; + + vgui::Button *m_pLeftButton; + vgui::Button *m_pRightButton; + + IViewPort *m_pViewPort; + ButtonCode_t m_iDuckKey; +}; + +extern CSpectatorGUI * g_pSpectatorGUI; + +#endif // SPECTATORGUI_H diff --git a/game/client/game_controls/teammenu.cpp b/game/client/game_controls/teammenu.cpp new file mode 100644 index 0000000..e1adf32 --- /dev/null +++ b/game/client/game_controls/teammenu.cpp @@ -0,0 +1,447 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include <cdll_client_int.h> + +#include "teammenu.h" + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <KeyValues.h> +#include <vgui_controls/ImageList.h> +#include <filesystem.h> + +#include <vgui_controls/RichText.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/HTML.h> + +#include "IGameUIFuncs.h" // for key bindings +#include <igameresources.h> +#include <game/client/iviewport.h> +#include <stdlib.h> // MAX_PATH define +#include <stdio.h> +#include "byteswap.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IGameUIFuncs *gameuifuncs; // for key binding details + +using namespace vgui; + +void UpdateCursorState(); +// void DuckMessage(const char *str); + +// helper function +const char *GetStringTeamColor( int i ) +{ + switch( i ) + { + case 0: + return "team0"; + + case 1: + return "team1"; + + case 2: + return "team2"; + + case 3: + return "team3"; + + case 4: + default: + return "team4"; + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CTeamMenu::CTeamMenu(IViewPort *pViewPort) : Frame(NULL, PANEL_TEAM ) +{ + m_pViewPort = pViewPort; + m_iJumpKey = BUTTON_CODE_INVALID; // this is looked up in Activate() + m_iScoreBoardKey = BUTTON_CODE_INVALID; // this is looked up in Activate() + + // initialize dialog + SetTitle("", true); + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMoveable(false); + SetSizeable(false); + + // hide the system buttons + SetTitleBarVisible( false ); + SetProportional(true); + + // info window about this map + m_pMapInfo = new RichText( this, "MapInfo" ); + +#if defined( ENABLE_HTML_WINDOW ) + m_pMapInfoHTML = new HTML( this, "MapInfoHTML"); +#endif + + LoadControlSettings("Resource/UI/TeamMenu.res"); + InvalidateLayout(); + + m_szMapName[0] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CTeamMenu::~CTeamMenu() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: sets the text color of the map description field +//----------------------------------------------------------------------------- +void CTeamMenu::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + m_pMapInfo->SetFgColor( pScheme->GetColor("MapDescriptionText", Color(255, 255, 255, 0)) ); + + if ( *m_szMapName ) + { + LoadMapPage( m_szMapName ); // reload the map description to pick up the color + } +} + +//----------------------------------------------------------------------------- +// Purpose: makes the user choose the auto assign option +//----------------------------------------------------------------------------- +void CTeamMenu::AutoAssign() +{ + engine->ClientCmd("jointeam 0"); + OnClose(); +} + + +//----------------------------------------------------------------------------- +// Purpose: shows the team menu +//----------------------------------------------------------------------------- +void CTeamMenu::ShowPanel(bool bShow) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + if ( bShow ) + { + Activate(); + + SetMouseInputEnabled( true ); + + // get key bindings if shown + + if( m_iJumpKey == BUTTON_CODE_INVALID ) // you need to lookup the jump key AFTER the engine has loaded + { + m_iJumpKey = gameuifuncs->GetButtonCodeForBind( "jump" ); + } + + if ( m_iScoreBoardKey == BUTTON_CODE_INVALID ) + { + m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" ); + } + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + } + + m_pViewPort->ShowBackGround( bShow ); +} + + +//----------------------------------------------------------------------------- +// Purpose: updates the UI with a new map name and map html page, and sets up the team buttons +//----------------------------------------------------------------------------- +void CTeamMenu::Update() +{ + char mapname[MAX_MAP_NAME]; + + Q_FileBase( engine->GetLevelName(), mapname, sizeof(mapname) ); + + SetLabelText( "mapname", mapname ); + + LoadMapPage( mapname ); +} + +//----------------------------------------------------------------------------- +// Purpose: chooses and loads the text page to display that describes mapName map +//----------------------------------------------------------------------------- +void CTeamMenu::LoadMapPage( const char *mapName ) +{ + // Save off the map name so we can re-load the page in ApplySchemeSettings(). + Q_strncpy( m_szMapName, mapName, strlen( mapName ) + 1 ); + + char mapRES[ MAX_PATH ]; + + char uilanguage[ 64 ]; + uilanguage[0] = 0; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + + Q_snprintf( mapRES, sizeof( mapRES ), "resource/maphtml/%s_%s.html", mapName, uilanguage ); + + bool bFoundHTML = false; + + if ( !g_pFullFileSystem->FileExists( mapRES ) ) + { + // try english + Q_snprintf( mapRES, sizeof( mapRES ), "resource/maphtml/%s_english.html", mapName ); + } + else + { + bFoundHTML = true; + } + + if( bFoundHTML || g_pFullFileSystem->FileExists( mapRES ) ) + { + // it's a local HTML file + char localURL[ _MAX_PATH + 7 ]; + Q_strncpy( localURL, "file://", sizeof( localURL ) ); + + char pPathData[ _MAX_PATH ]; + g_pFullFileSystem->GetLocalPath( mapRES, pPathData, sizeof(pPathData) ); + Q_strncat( localURL, pPathData, sizeof( localURL ), COPY_ALL_CHARACTERS ); + + // force steam to dump a local copy + g_pFullFileSystem->GetLocalCopy( pPathData ); + + m_pMapInfo->SetVisible( false ); + +#if defined( ENABLE_HTML_WINDOW ) + m_pMapInfoHTML->SetVisible( true ); + m_pMapInfoHTML->OpenURL( localURL, NULL ); +#endif + InvalidateLayout(); + Repaint(); + + return; + } + else + { + m_pMapInfo->SetVisible( true ); + +#if defined( ENABLE_HTML_WINDOW ) + m_pMapInfoHTML->SetVisible( false ); +#endif + } + + Q_snprintf( mapRES, sizeof( mapRES ), "maps/%s.txt", mapName); + + // if no map specific description exists, load default text + if( !g_pFullFileSystem->FileExists( mapRES ) ) + { + if ( g_pFullFileSystem->FileExists( "maps/default.txt" ) ) + { + Q_snprintf ( mapRES, sizeof( mapRES ), "maps/default.txt"); + } + else + { + m_pMapInfo->SetText( "" ); + return; + } + } + + FileHandle_t f = g_pFullFileSystem->Open( mapRES, "r" ); + + // read into a memory block + int fileSize = g_pFullFileSystem->Size(f); + int dataSize = fileSize + sizeof( wchar_t ); + if ( dataSize % 2 ) + ++dataSize; + wchar_t *memBlock = (wchar_t *)malloc(dataSize); + memset( memBlock, 0x0, dataSize); + int bytesRead = g_pFullFileSystem->Read(memBlock, fileSize, f); + if ( bytesRead < fileSize ) + { + // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and + // return fewer bytes than we were expecting. + char *data = reinterpret_cast<char *>( memBlock ); + data[ bytesRead ] = 0; + data[ bytesRead+1 ] = 0; + } + +#ifndef WIN32 + if ( ((ucs2 *)memBlock)[0] == 0xFEFF ) + { + // convert the win32 ucs2 data to wchar_t + dataSize*=2;// need to *2 to account for ucs2 to wchar_t (4byte) growth + wchar_t *memBlockConverted = (wchar_t *)malloc(dataSize); + V_UCS2ToUnicode( (ucs2 *)memBlock, memBlockConverted, dataSize ); + free(memBlock); + memBlock = memBlockConverted; + } +#else + // null-terminate the stream (redundant, since we memset & then trimmed the transformed buffer already) + memBlock[dataSize / sizeof(wchar_t) - 1] = 0x0000; +#endif + // ensure little-endian unicode reads correctly on all platforms + CByteswap byteSwap; + byteSwap.SetTargetBigEndian( false ); + byteSwap.SwapBufferToTargetEndian( memBlock, memBlock, dataSize/sizeof(wchar_t) ); + + // check the first character, make sure this a little-endian unicode file + if ( memBlock[0] != 0xFEFF ) + { + // its a ascii char file + m_pMapInfo->SetText( reinterpret_cast<char *>( memBlock ) ); + } + else + { + m_pMapInfo->SetText( memBlock+1 ); + } + // go back to the top of the text buffer + m_pMapInfo->GotoTextStart(); + + g_pFullFileSystem->Close( f ); + free(memBlock); + + InvalidateLayout(); + Repaint(); +} + +/* +//----------------------------------------------------------------------------- +// Purpose: sets the text on and displays the team buttons +//----------------------------------------------------------------------------- +void CTeamMenu::MakeTeamButtons(void) +{ + int i = 0; + + for( i = 0; i< m_pTeamButtons.Count(); i++ ) + { + m_pTeamButtons[i]->SetVisible(false); + } + + i = 0; + + while( true ) + { + const char *teamname = GameResources()->GetTeamName( i ); + + if ( !teamname || !teamname[0] ) + return; // no more teams + + char buttonText[32]; + Q_snprintf( buttonText, sizeof(buttonText), "&%i %s", i +1, teamname ); + m_pTeamButtons[i]->SetText( buttonText ); + + m_pTeamButtons[i]->SetCommand( new KeyValues("TeamButton", "team", i ) ); + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_pTeamButtons[i]->SetArmedColor(pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)) , pScheme->GetColor("SelectionBG", Color(255, 255, 255, 0)) ); + m_pTeamButtons[i]->SetDepressedColor( pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)), pScheme->GetColor("ButtonArmedBgColor", Color(255, 255, 255, 0)) ); + m_pTeamButtons[i]->SetDefaultColor( pScheme->GetColor(GetStringTeamColor(i), Color(255, 255, 255, 255)), pScheme->GetColor("ButtonDepressedBgColor", Color(255, 255, 255, 0)) ); + m_pTeamButtons[i]->SetVisible(true); + + i++; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: When a team button is pressed it triggers this function to cause the player to join a team +//----------------------------------------------------------------------------- +void CTeamMenu::OnTeamButton( int team ) +{ + char cmd[64]; + if( team >= m_iNumTeams ) // its a special button + { + if( team == m_iNumTeams ) // first extra team is auto assign + { + Q_snprintf( cmd, sizeof( cmd ), "jointeam 5" ); + } + else // next is spectate + { + // DuckMessage( "#Spec_Duck" ); + gViewPortInterface->ShowBackGround( false ); + } + } + else + { + Q_snprintf( cmd, sizeof( cmd ), "jointeam %i", team + 1 ); + //g_iTeamNumber = team + 1; + } + + engine->ClientCmd(cmd); + SetVisible( false ); + OnClose(); +} */ + +//----------------------------------------------------------------------------- +// Purpose: Sets the text of a control by name +//----------------------------------------------------------------------------- +void CTeamMenu::SetLabelText(const char *textEntryName, const char *text) +{ + Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetText(text); + } +} + +void CTeamMenu::OnKeyCodePressed(KeyCode code) +{ + int nDir = 0; + + switch ( code ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case KEY_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + nDir = -1; + break; + + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + case KEY_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + nDir = 1; + break; + } + + if ( m_iScoreBoardKey != BUTTON_CODE_INVALID && m_iScoreBoardKey == code ) + { + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, true ); + gViewPortInterface->PostMessageToPanel( PANEL_SCOREBOARD, new KeyValues( "PollHideCode", "code", code ) ); + } + else if ( nDir != 0 ) + { + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons; + VguiPanelGetSortedChildButtonList( this, (void*)&vecSortedButtons, "&", 0 ); + + if ( VguiPanelNavigateSortedChildButtonList( (void*)&vecSortedButtons, nDir ) != -1 ) + { + // Handled! + return; + } + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} diff --git a/game/client/game_controls/teammenu.h b/game/client/game_controls/teammenu.h new file mode 100644 index 0000000..04ec004 --- /dev/null +++ b/game/client/game_controls/teammenu.h @@ -0,0 +1,88 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TEAMMENU_H +#define TEAMMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Button.h> + +#include <game/client/iviewport.h> + +#include <vgui/KeyCode.h> +#include <utlvector.h> + +namespace vgui +{ + class RichText; + class HTML; +} +class TeamFortressViewport; + + +//----------------------------------------------------------------------------- +// Purpose: Displays the team menu +//----------------------------------------------------------------------------- +class CTeamMenu : public vgui::Frame, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CTeamMenu, vgui::Frame ); + +public: + CTeamMenu(IViewPort *pViewPort); + virtual ~CTeamMenu(); + + virtual const char *GetName( void ) { return PANEL_TEAM; } + virtual void SetData(KeyValues *data) {}; + virtual void Reset() {}; + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_IN_GAME_HUD; } + +public: + + void AutoAssign(); + +protected: + + // int GetNumTeams() { return m_iNumTeams; } + + // VGUI2 overrides + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void OnKeyCodePressed(vgui::KeyCode code); + + // helper functions + virtual void SetLabelText(const char *textEntryName, const char *text); + virtual void LoadMapPage( const char *mapName ); + // virtual void MakeTeamButtons( void ); + + // command callbacks + // MESSAGE_FUNC_INT( OnTeamButton, "TeamButton", team ); + + IViewPort *m_pViewPort; + vgui::RichText *m_pMapInfo; + vgui::HTML *m_pMapInfoHTML; +// int m_iNumTeams; + ButtonCode_t m_iJumpKey; + ButtonCode_t m_iScoreBoardKey; + + char m_szMapName[ MAX_PATH ]; +}; + + +#endif // TEAMMENU_H diff --git a/game/client/game_controls/vguitextwindow.cpp b/game/client/game_controls/vguitextwindow.cpp new file mode 100644 index 0000000..4967d74 --- /dev/null +++ b/game/client/game_controls/vguitextwindow.cpp @@ -0,0 +1,436 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "vguitextwindow.h" +#include <networkstringtabledefs.h> +#include <cdll_client_int.h> +#include <clientmode_shared.h> + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include <KeyValues.h> +#include <convar.h> +#include <vgui_controls/ImageList.h> + +#include <vgui_controls/TextEntry.h> +#include <vgui_controls/Button.h> + +#include <game/client/iviewport.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; +extern INetworkStringTable *g_pStringTableInfoPanel; + +#define TEMP_HTML_FILE "textwindow_temp.html" + +ConVar cl_disablehtmlmotd( "cl_disablehtmlmotd", "0", FCVAR_ARCHIVE, "Disable HTML motds." ); + +//============================================================================= +// HPE_BEGIN: +// [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration +// of options. Passing a command string is dangerous and allowed a server network +// message to run arbitrary commands on the client. +//============================================================================= +CON_COMMAND( showinfo, "Shows a info panel: <type> <title> <message> [<command number>]" ) +{ + if ( !gViewPortInterface ) + return; + + if ( args.ArgC() < 4 ) + return; + + IViewPortPanel * panel = gViewPortInterface->FindPanelByName( PANEL_INFO ); + + if ( panel ) + { + KeyValues *kv = new KeyValues("data"); + kv->SetInt( "type", Q_atoi(args[ 1 ]) ); + kv->SetString( "title", args[ 2 ] ); + kv->SetString( "message", args[ 3 ] ); + + if ( args.ArgC() == 5 ) + kv->SetString( "command", args[ 4 ] ); + + panel->SetData( kv ); + + gViewPortInterface->ShowPanel( panel, true ); + + kv->deleteThis(); + } + else + { + Msg("Couldn't find info panel.\n" ); + } +} +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CTextWindow::CTextWindow(IViewPort *pViewPort) : Frame(NULL, PANEL_INFO ) +{ + // initialize dialog + m_pViewPort = pViewPort; + +// SetTitle("", true); + + m_szTitle[0] = '\0'; + m_szMessage[0] = '\0'; + m_szMessageFallback[0] = '\0'; + m_nExitCommand = TEXTWINDOW_CMD_NONE; + m_bShownURL = false; + m_bUnloadOnDismissal = false; + + // load the new scheme early!! + SetScheme("ClientScheme"); + SetMoveable(false); + SetSizeable(false); + SetProportional(true); + + // hide the system buttons + SetTitleBarVisible( false ); + + m_pTextMessage = new TextEntry( this, "TextMessage" ); + m_pHTMLMessage = new CMOTDHTML( this,"HTMLMessage" ); + m_pTitleLabel = new Label( this, "MessageTitle", "Message Title" ); + m_pOK = new Button(this, "ok", "#PropertyDialog_OK"); + + m_pOK->SetCommand("okay"); + m_pTextMessage->SetMultiline( true ); + m_nContentType = TYPE_TEXT; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTextWindow::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings("Resource/UI/TextWindow.res"); + + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CTextWindow::~CTextWindow() +{ + // remove temp file again + g_pFullFileSystem->RemoveFile( TEMP_HTML_FILE, "DEFAULT_WRITE_PATH" ); +} + +void CTextWindow::Reset( void ) +{ + //============================================================================= + // HPE_BEGIN: + // [Forrest] Replace strange hard-coded default message with hard-coded error message. + //============================================================================= + V_strcpy_safe( m_szTitle, "Error loading info message." ); + V_strcpy_safe( m_szMessage, "" ); + V_strcpy_safe( m_szMessageFallback, "" ); + //============================================================================= + // HPE_END + //============================================================================= + + m_nExitCommand = TEXTWINDOW_CMD_NONE; + m_nContentType = TYPE_TEXT; + m_bShownURL = false; + m_bUnloadOnDismissal = false; + Update(); +} + +void CTextWindow::ShowText( const char *text ) +{ + m_pTextMessage->SetVisible( true ); + m_pTextMessage->SetText( text ); + m_pTextMessage->GotoTextStart(); +} + +void CTextWindow::ShowURL( const char *URL, bool bAllowUserToDisable ) +{ + #ifdef _DEBUG + Msg( "CTextWindow::ShowURL( %s )\n", URL ); + #endif + + ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); + if ( ( bAllowUserToDisable && cl_disablehtmlmotd.GetBool() ) || !mode->IsHTMLInfoPanelAllowed() ) + { + Warning( "Blocking HTML info panel '%s'; Using plaintext instead.\n", URL ); + + // User has disabled HTML TextWindows. Show the fallback as text only. + if ( g_pStringTableInfoPanel ) + { + int index = g_pStringTableInfoPanel->FindStringIndex( "motd_text" ); + if ( index != ::INVALID_STRING_INDEX ) + { + int length = 0; + const char *data = (const char *)g_pStringTableInfoPanel->GetStringUserData( index, &length ); + if ( data && data[0] ) + { + m_pHTMLMessage->SetVisible( false ); + ShowText( data ); + } + } + } + return; + } + + m_pHTMLMessage->SetVisible( true ); + m_pHTMLMessage->OpenURL( URL, NULL ); + m_bShownURL = true; +} + +void CTextWindow::ShowIndex( const char *entry ) +{ + const char *data = NULL; + int length = 0; + + if ( NULL == g_pStringTableInfoPanel ) + return; + + int index = g_pStringTableInfoPanel->FindStringIndex( m_szMessage ); + + if ( index != ::INVALID_STRING_INDEX ) + data = (const char *)g_pStringTableInfoPanel->GetStringUserData( index, &length ); + + if ( !data || !data[0] ) + return; // nothing to show + + // is this a web URL ? + if ( !Q_strncmp( data, "http://", 7 ) || !Q_strncmp( data, "https://", 8 ) ) + { + ShowURL( data ); + return; + } + + // try to figure out if this is HTML or not + if ( data[0] != '<' ) + { + ShowText( data ); + return; + } + + // data is a HTML, we have to write to a file and then load the file + FileHandle_t hFile = g_pFullFileSystem->Open( TEMP_HTML_FILE, "wb", "DEFAULT_WRITE_PATH" ); + + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + return; + + g_pFullFileSystem->Write( data, length, hFile ); + g_pFullFileSystem->Close( hFile ); + + if ( g_pFullFileSystem->Size( TEMP_HTML_FILE ) != (unsigned int)length ) + return; // something went wrong while writing + + ShowFile( TEMP_HTML_FILE ); +} + +void CTextWindow::ShowFile( const char *filename ) +{ + if ( Q_stristr( filename, ".htm" ) || Q_stristr( filename, ".html" ) ) + { + // it's a local HTML file + char localURL[ _MAX_PATH + 7 ]; + Q_strncpy( localURL, "file://", sizeof( localURL ) ); + + char pPathData[ _MAX_PATH ]; + g_pFullFileSystem->GetLocalPath( filename, pPathData, sizeof(pPathData) ); + Q_strncat( localURL, pPathData, sizeof( localURL ), COPY_ALL_CHARACTERS ); + + ShowURL( localURL ); + } + else + { + // read from local text from file + FileHandle_t f = g_pFullFileSystem->Open( m_szMessage, "rb", "GAME" ); + + if ( !f ) + return; + + char buffer[2048]; + + int size = MIN( g_pFullFileSystem->Size( f ), sizeof(buffer)-1 ); // just allow 2KB + + g_pFullFileSystem->Read( buffer, size, f ); + g_pFullFileSystem->Close( f ); + + buffer[size]=0; //terminate string + + ShowText( buffer ); + } +} + +void CTextWindow::Update( void ) +{ + SetTitle( m_szTitle, false ); + + m_pTitleLabel->SetText( m_szTitle ); + + if ( m_pHTMLMessage ) + m_pHTMLMessage->SetVisible( false ); + m_pTextMessage->SetVisible( false ); + + if ( m_nContentType == TYPE_INDEX ) + { + ShowIndex( m_szMessage ); + } + else if ( m_nContentType == TYPE_URL ) + { + if ( !Q_strncmp( m_szMessage, "http://", 7 ) || !Q_strncmp( m_szMessage, "https://", 8 ) || !Q_stricmp( m_szMessage, "about:blank" ) ) + { + ShowURL( m_szMessage ); + } + else + { + // We should have trapped this at a higher level + Assert( !"URL protocol is missing or blocked" ); + } + } + else if ( m_nContentType == TYPE_FILE ) + { + ShowFile( m_szMessage ); + } + else if ( m_nContentType == TYPE_TEXT ) + { + ShowText( m_szMessage ); + } + else + { + DevMsg("CTextWindow::Update: unknown content type %i\n", m_nContentType ); + } +} + +void CTextWindow::OnCommand( const char *command ) +{ + if (!Q_strcmp(command, "okay")) + { + //============================================================================= + // HPE_BEGIN: + // [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration + // of options. Passing a command string is dangerous and allowed a server network + // message to run arbitrary commands on the client. + //============================================================================= + const char *pszCommand = NULL; + switch ( m_nExitCommand ) + { + case TEXTWINDOW_CMD_NONE: + break; + + case TEXTWINDOW_CMD_JOINGAME: + pszCommand = "joingame"; + break; + + case TEXTWINDOW_CMD_CHANGETEAM: + pszCommand = "changeteam"; + break; + + case TEXTWINDOW_CMD_IMPULSE101: + pszCommand = "impulse 101"; + break; + + case TEXTWINDOW_CMD_MAPINFO: + pszCommand = "mapinfo"; + break; + + case TEXTWINDOW_CMD_CLOSED_HTMLPAGE: + pszCommand = "closed_htmlpage"; + break; + + case TEXTWINDOW_CMD_CHOOSETEAM: + pszCommand = "chooseteam"; + break; + + default: + DevMsg("CTextWindow::OnCommand: unknown exit command value %i\n", m_nExitCommand ); + break; + } + + if ( pszCommand != NULL ) + { + engine->ClientCmd_Unrestricted( pszCommand ); + } + //============================================================================= + // HPE_END + //============================================================================= + + m_pViewPort->ShowPanel( this, false ); + } + + BaseClass::OnCommand(command); +} + +void CTextWindow::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_B || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B ) + { + OnCommand( "okay" ); + return; + } + + BaseClass::OnKeyCodePressed(code); +} + +void CTextWindow::SetData(KeyValues *data) +{ + SetData( data->GetInt( "type" ), data->GetString( "title" ), data->GetString( "msg" ), data->GetString( "msg_fallback" ), data->GetInt( "cmd" ), data->GetBool( "unload" ) ); +} + +void CTextWindow::SetData( int type, const char *title, const char *message, const char *message_fallback, int command, bool bUnload ) +{ + Q_strncpy( m_szTitle, title, sizeof( m_szTitle ) ); + Q_strncpy( m_szMessage, message, sizeof( m_szMessage ) ); + Q_strncpy( m_szMessageFallback, message_fallback, sizeof( m_szMessageFallback ) ); + + m_nExitCommand = command; + + m_nContentType = type; + m_bUnloadOnDismissal = bUnload; + + Update(); +} + +void CTextWindow::ShowPanel( bool bShow ) +{ + if ( BaseClass::IsVisible() == bShow ) + return; + + m_pViewPort->ShowBackGround( bShow ); + + if ( bShow ) + { + Activate(); + SetMouseInputEnabled( true ); + } + else + { + SetVisible( false ); + SetMouseInputEnabled( false ); + + if ( m_bUnloadOnDismissal && m_bShownURL && m_pHTMLMessage ) + { + m_pHTMLMessage->OpenURL( "about:blank", NULL ); + m_bShownURL = false; + } + } +} + +bool CTextWindow::CMOTDHTML::OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect ) +{ + if ( Q_strstr( url, "steam://" ) ) + return false; + + return BaseClass::OnStartRequest( url, target, pchPostData, bIsRedirect ); +} diff --git a/game/client/game_controls/vguitextwindow.h b/game/client/game_controls/vguitextwindow.h new file mode 100644 index 0000000..316cc04 --- /dev/null +++ b/game/client/game_controls/vguitextwindow.h @@ -0,0 +1,106 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VGUITEXTWINDOW_H +#define VGUITEXTWINDOW_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/HTML.h> + +#include <game/client/iviewport.h> +#include "shareddefs.h" + +namespace vgui +{ + class TextEntry; +} + +//----------------------------------------------------------------------------- +// Purpose: displays the MOTD +//----------------------------------------------------------------------------- + +class CTextWindow : public vgui::Frame, public IViewPortPanel +{ +private: + DECLARE_CLASS_SIMPLE( CTextWindow, vgui::Frame ); + +public: + CTextWindow(IViewPort *pViewPort); + virtual ~CTextWindow(); + + virtual const char *GetName( void ) { return PANEL_INFO; } + virtual void SetData(KeyValues *data); + virtual void Reset(); + virtual void Update(); + virtual bool NeedsUpdate( void ) { return false; } + virtual bool HasInputElements( void ) { return true; } + virtual void ShowPanel( bool bShow ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent( vgui::VPANEL parent ) { BaseClass::SetParent( parent ); } + +public: + + virtual void SetData( int type, const char *title, const char *message, const char *message_fallback, int command, bool bUnload ); + virtual void ShowFile( const char *filename ); + virtual void ShowText( const char *text ); + virtual void ShowURL( const char *URL, bool bAllowUserToDisable = true ); + virtual void ShowIndex( const char *entry ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual GameActionSet_t GetPreferredActionSet() { return GAME_ACTION_SET_IN_GAME_HUD; } + +protected: + // vgui overrides + virtual void OnCommand( const char *command ); + + void OnKeyCodePressed( vgui::KeyCode code ); + + IViewPort *m_pViewPort; + char m_szTitle[255]; + char m_szMessage[2048]; + char m_szMessageFallback[2048]; + //============================================================================= + // HPE_BEGIN: + // [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration + // of options. Passing a command string is dangerous and allowed a server network + // message to run arbitrary commands on the client. + //============================================================================= + int m_nExitCommand; + //============================================================================= + // HPE_END + //============================================================================= + int m_nContentType; + bool m_bShownURL; + bool m_bUnloadOnDismissal; + + vgui::TextEntry *m_pTextMessage; + + class CMOTDHTML : public vgui::HTML + { + private: + DECLARE_CLASS_SIMPLE( CMOTDHTML, vgui::HTML ); + + public: + CMOTDHTML( Panel *parent, const char *pchName ) : vgui::HTML( parent, pchName ) {} + virtual bool OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect ) OVERRIDE; + }; + CMOTDHTML *m_pHTMLMessage; + + vgui::Button *m_pOK; + vgui::Label *m_pTitleLabel; +}; + + +#endif // VGUITEXTWINDOW_H |