summaryrefslogtreecommitdiff
path: root/game/client/tf/workshop/published_files.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/workshop/published_files.cpp')
-rw-r--r--game/client/tf/workshop/published_files.cpp1646
1 files changed, 1646 insertions, 0 deletions
diff --git a/game/client/tf/workshop/published_files.cpp b/game/client/tf/workshop/published_files.cpp
new file mode 100644
index 0000000..c8e0bfe
--- /dev/null
+++ b/game/client/tf/workshop/published_files.cpp
@@ -0,0 +1,1646 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+#include "cbase.h"
+
+#include "imageutils.h"
+#include "econ_controls.h"
+#include "econ/confirm_dialog.h"
+#include "game/client/iviewport.h"
+#include "ienginevgui.h"
+#include "tf_hud_mainmenuoverride.h"
+#include "vgui/ILocalize.h"
+#include "vgui/ISurface.h"
+#include "vgui/ISystem.h"
+#include "vgui_bitmappanel.h"
+#include <vgui_controls/FileOpenDialog.h>
+
+#ifdef WORKSHOP_IMPORT_ENABLED
+#include "itemtest/itemtest.h"
+#include "workshop/item_import.h"
+#endif
+
+#include "steampublishedfiles/publish_file_dialog.h"
+
+#include "tier1/checksum_crc.h"
+
+// for hud element
+#include "iclientmode.h"
+#include "tf_gamerules.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#define TF2_PREVIEW_IMAGE_HEIGHT 512
+#define TF2_PREVIEW_IMAGE_WIDTH 512
+
+#define COMMUNITY_DEV_HOST "http://localhost/"
+
+extern ConVar publish_file_last_dir;
+
+// milliseconds
+ConVar tf_steam_workshop_query_timeout( "tf_steam_workshop_query_timeout", "10", FCVAR_CLIENTDLL, "Time in seconds to allow communication with the Steam Workshop server." );
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility function
+//-----------------------------------------------------------------------------
+static void MakeModalAndBringToFront( vgui::EditablePanel *dialog )
+{
+ dialog->SetVisible( true );
+ if ( dialog->GetParent() == NULL )
+ {
+ dialog->MakePopup();
+ }
+ dialog->SetZPos( 10000 );
+ dialog->MoveToFront();
+ dialog->SetKeyBoardInputEnabled( true );
+ dialog->SetMouseInputEnabled( true );
+ TFModalStack()->PushModal( dialog );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: helper class that contains the list of published files for a user
+//-----------------------------------------------------------------------------
+class CPublishedFiles
+{
+public:
+ enum
+ {
+ kState_Initialized,
+ kState_PopulatingFileList,
+ kState_ErrorOccurred,
+ kState_DeletingFile,
+ kState_ErrorCannotDeleteFile,
+ kState_Timeout,
+ kState_Done,
+ };
+
+ CPublishedFiles();
+ ~CPublishedFiles();
+
+ bool QueryHasTimedOut( void );
+ void PopulateFileList( void );
+ bool EnumerateUserPublishedFiles( uint32 unPage );
+ void RefreshPublishedFileDetails( uint64 nPublishedFileID );
+ void DeletePublishedFile( uint64 nPublishedFileID );
+ void ViewPublishedFile( uint64 nPublishedFileID );
+ const SteamUGCDetails_t *GetPublishedFileDetails( uint64 nPublishedFileID ) const;
+
+ // Enumerate subscribed files
+ CCallResult<CPublishedFiles, SteamUGCQueryCompleted_t> m_callbackEnumeratePublishedFiles;
+ void Steam_OnEnumeratePublishedFiles( SteamUGCQueryCompleted_t *pResult, bool bError );
+
+ // Callback for deleting files
+ CCallResult<CPublishedFiles, RemoteStorageDeletePublishedFileResult_t> m_callbackDeletePublishedFile;
+ void Steam_OnDeletePublishedFile( RemoteStorageDeletePublishedFileResult_t *pResult, bool bError );
+
+ unsigned int m_nTotalFilesToQuery;
+ CUtlQueue< PublishedFileId_t > m_FilesToQuery;
+ CUtlMap< PublishedFileId_t, SteamUGCDetails_t > m_FileDetails;
+
+ long m_nFileQueryTime;
+ bool m_bQueryErrorOccurred;
+ uint32 m_state;
+ uint32 m_unEnumerateStartPage;
+ PublishedFileId_t m_nCurrentQueryPublishedFileID;
+};
+
+CPublishedFiles::CPublishedFiles()
+ : m_nTotalFilesToQuery( 0 )
+ , m_nFileQueryTime( 0 )
+ , m_bQueryErrorOccurred( false )
+ , m_state( kState_Initialized )
+ , m_unEnumerateStartPage( 0 )
+ , m_nCurrentQueryPublishedFileID( 0 )
+{
+ m_FileDetails.SetLessFunc( DefLessFunc( PublishedFileId_t ) );
+}
+
+CPublishedFiles::~CPublishedFiles()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine if our file query has timed out
+//-----------------------------------------------------------------------------
+bool CPublishedFiles::QueryHasTimedOut( void )
+{
+ return ( ( system()->GetTimeMillis() - m_nFileQueryTime ) > tf_steam_workshop_query_timeout.GetInt() * 1000 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::PopulateFileList( void )
+{
+ // Start the process of showing all of our published files
+ if ( EnumerateUserPublishedFiles( 1 ) )
+ {
+ m_unEnumerateStartPage = 1;
+
+ // Get our starting tick
+ m_nFileQueryTime = system()->GetTimeMillis();
+
+ m_state = kState_PopulatingFileList;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper for creating and sending a CreateQueryUserUGCRequest for this user's published files
+//-----------------------------------------------------------------------------
+bool CPublishedFiles::EnumerateUserPublishedFiles( uint32 unPage )
+{
+ ISteamUGC *pUGC = steamapicontext->SteamUGC();
+ ISteamUser *pUser = steamapicontext->SteamUser();
+ if ( pUGC && pUser )
+ {
+ AccountID_t nAccountID = pUser->GetSteamID().GetAccountID();
+
+ UGCQueryHandle_t ugcHandle = pUGC->CreateQueryUserUGCRequest( nAccountID,
+ k_EUserUGCList_Published,
+ k_EUGCMatchingUGCType_Items,
+ k_EUserUGCListSortOrder_CreationOrderDesc,
+ engine->GetAppID(), engine->GetAppID(), unPage );
+
+ // make sure we get the entire description and not a truncated version
+ pUGC->SetReturnLongDescription( ugcHandle, true );
+
+ SteamAPICall_t hSteamAPICall = pUGC->SendQueryUGCRequest( ugcHandle );
+ m_callbackEnumeratePublishedFiles.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnEnumeratePublishedFiles );
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::DeletePublishedFile( uint64 nPublishedFileID )
+{
+ m_state = kState_DeletingFile;
+ SteamAPICall_t hSteamAPICall = steamapicontext->SteamRemoteStorage()->DeletePublishedFile( nPublishedFileID );
+ m_callbackDeletePublishedFile.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnDeletePublishedFile );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const SteamUGCDetails_t *CPublishedFiles::GetPublishedFileDetails( uint64 nPublishedFileID ) const
+{
+ int idx = m_FileDetails.Find( nPublishedFileID );
+ if ( idx != m_FileDetails.InvalidIndex() )
+ {
+ return &m_FileDetails[idx];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::ViewPublishedFile( uint64 nPublishedFileID )
+{
+ EUniverse universe = GetUniverse();
+ switch ( universe )
+ {
+ case k_EUniversePublic:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ case k_EUniverseBeta:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ case k_EUniverseDev:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::RefreshPublishedFileDetails( uint64 nPublishedFileID )
+{
+ if ( m_state != kState_Done )
+ {
+ // Would confuse the refresh in progress
+ AssertMsg( m_state == kState_Done, "Shouldn't be refreshing file details while other operations are already happening" );
+ return;
+ }
+
+ ISteamUGC *pUGC = steamapicontext->SteamUGC();
+ Assert( pUGC );
+ if ( pUGC )
+ {
+ UGCQueryHandle_t ugcHandle = pUGC->CreateQueryUGCDetailsRequest( &nPublishedFileID, 1 );
+ SteamAPICall_t hSteamAPICall = pUGC->SendQueryUGCRequest( ugcHandle );
+ m_callbackEnumeratePublishedFiles.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnEnumeratePublishedFiles );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::Steam_OnDeletePublishedFile( RemoteStorageDeletePublishedFileResult_t *pResult, bool bError )
+{
+ if ( pResult && !bError )
+ {
+ if ( pResult->m_eResult != k_EResultOK )
+ {
+ m_state = kState_ErrorOccurred;
+ if ( pResult->m_eResult == k_EResultAccessDenied )
+ {
+ m_state = kState_ErrorCannotDeleteFile;
+ }
+ }
+ else
+ {
+ m_state = kState_Done;
+ m_FileDetails.Remove( pResult->m_nPublishedFileId );
+ }
+ }
+ else
+ {
+ m_state = kState_ErrorOccurred;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPublishedFiles::Steam_OnEnumeratePublishedFiles( SteamUGCQueryCompleted_t *pResult, bool bError )
+{
+ // Make sure we succeeded
+ if ( bError || pResult->m_eResult != k_EResultOK )
+ {
+ m_state = kState_ErrorOccurred;
+ return;
+ }
+
+ if ( m_state == kState_PopulatingFileList && m_unEnumerateStartPage == 1 )
+ {
+ // Start from scratch
+ m_FilesToQuery.Purge();
+ m_FileDetails.Purge();
+ }
+
+ const int nNumFiles = pResult->m_unNumResultsReturned;
+ for ( int i = 0; i < nNumFiles; i++ )
+ {
+ SteamUGCDetails_t sDetails = { 0 };
+ steamapicontext->SteamUGC()->GetQueryUGCResult( pResult->m_handle, i, &sDetails );
+ m_FileDetails.InsertOrReplace( sDetails.m_nPublishedFileId, sDetails );
+ }
+
+ if ( m_state == kState_Done )
+ {
+ // This was a one-off request from e.g. RefreshPublishedFileDetails
+ return;
+ }
+
+ if ( !nNumFiles || m_FileDetails.Count() >= pResult->m_unTotalMatchingResults )
+ {
+ m_state = kState_Done;
+ return;
+ }
+
+ EnumerateUserPublishedFiles( ++m_unEnumerateStartPage );
+}
+
+// Tags
+static const char *kClassTags[TF_LAST_NORMAL_CLASS] = {
+ "", // TF_CLASS_UNDEFINED
+ "Scout", // TF_CLASS_SCOUT
+ "Sniper", // TF_CLASS_SNIPER,
+ "Soldier", // TF_CLASS_SOLDIER,
+ "Demoman", // TF_CLASS_DEMOMAN,
+ "Medic", // TF_CLASS_MEDIC,
+ "Heavy", //TF_CLASS_HEAVYWEAPONS,
+ "Pyro", // TF_CLASS_PYRO,
+ "Spy", // TF_CLASS_SPY,
+ "Engineer", // TF_CLASS_ENGINEER,
+};
+
+struct TagPair_t
+{
+ const char *pCheckboxElementName;
+ const char *pTag;
+};
+static TagPair_t kOtherTags[] = {
+ { "TagCheckbox_Headgear", "Headgear" },
+ { "TagCheckbox_Weapon", "Weapon" },
+ { "TagCheckbox_Misc", "Misc" },
+ { "TagCheckbox_SoundDevice", "Sound Device" },
+ { "TagCheckbox_Halloween", "Halloween" },
+ { "TagCheckbox_Taunt", "Taunt" },
+ { "TagCheckbox_UnusualEffect", "Unusual Effect" },
+ { "TagCheckbox_Jungle", "Jungle" },
+};
+static uint32 kNumOtherTags = ARRAYSIZE( kOtherTags );
+
+// Map Tags
+static TagPair_t kMapTags[] = {
+ { "MapsCheckBox_CTF", "Capture the Flag" },
+ { "MapsCheckBox_CP", "Control Point" },
+ { "MapsCheckBox_Escort", "Payload" },
+ { "MapsCheckBox_EscortRace", "Payload Race" },
+ { "MapsCheckBox_Arena", "Arena" },
+ { "MapsCheckBox_Koth", "King of the Hill" },
+ { "MapsCheckBox_AttackDefense", "Attack / Defense" },
+ { "MapsCheckBox_SD", "Special Delivery" },
+ { "MapsCheckBox_RobotDestruction", "Robot Destruction" },
+ { "MapsCheckBox_MVM", "Mann vs. Machine" },
+ { "MapsCheckBox_Powerup", "Mannpower" },
+ { "MapsCheckBox_Medieval", "Medieval" },
+ { "MapsCheckBox_PassTime", "PASS Time" },
+ { "MapsCheckBox_Specialty", "Specialty" },
+ { "MapsCheckBox_Halloween", "Halloween" },
+ { "MapsCheckbox_Smissmas", "Smissmas" },
+ { "MapsCheckbox_Night", "Night" },
+ { "MapsCheckbox_Jungle", "Jungle" },
+};
+static uint32 kNumMapTags = ARRAYSIZE( kMapTags );
+
+static const char *kImportedTag = "Certified Compatible";
+
+//-----------------------------------------------------------------------------
+// Purpose: Publish file dialog
+//-----------------------------------------------------------------------------
+class CTFFilePublishDialog : public CFilePublishDialog
+{
+ DECLARE_CLASS_SIMPLE( CTFFilePublishDialog, CFilePublishDialog );
+public:
+ CTFFilePublishDialog( Panel *parent, const char *name, PublishedFileDetails_t *pDetails ) : CFilePublishDialog( parent, name, pDetails ), m_bImported(false) {}
+
+ virtual ErrorCode_t ValidateFile( const char *lpszFilename )
+ {
+ if( !g_pFullFileSystem->FileExists( lpszFilename ) )
+ return kFailedFileNotFound;
+
+ // TODO This is the nominal max of SteamUGC, but should be found dynamically
+ const uint32 kMaxFileSize = 400 * 1024 * 1024;
+ unsigned int unFileSize = g_pFullFileSystem->Size( lpszFilename );
+ if ( unFileSize == 0 || unFileSize > kMaxFileSize )
+ {
+ return kFailedFileTooLarge;
+ }
+ return kNoError;
+ }
+ virtual AppId_t GetTargetAppID( void ) { return engine->GetAppID(); }
+ virtual unsigned int DesiredPreviewHeight( void ) { return TF2_PREVIEW_IMAGE_HEIGHT; }
+ virtual unsigned int DesiredPreviewWidth( void ) { return TF2_PREVIEW_IMAGE_WIDTH; }
+ virtual bool BForceSquarePreviewImage( void ) { return true; }
+ virtual EWorkshopFileType WorkshipFileTypeForFile( const char *pszFileName ) {
+ const char *pExt = V_GetFileExtension( pszFileName );
+ if ( pExt && V_strcmp( pExt, "bsp" ) == 0 )
+ {
+ return k_EWorkshopFileTypeCommunity;
+ }
+
+ AssertMsg( pExt && V_strcmp( pExt, "zip" ) == 0, "Unrecognized file type, defaulting to microtransaction\n" );
+ return k_EWorkshopFileTypeMicrotransaction;
+ }
+ virtual const char *GetPreviewFileTypes( void ) { return "*.tga,*.jpg,*.png"; }
+ virtual const char *GetPreviewFileTypeDescriptions( void ) { return "#TF_SteamWorkshop_Images"; }
+ virtual const char *GetFileTypes( eFilterType_t eType = IMPORT_FILTER_NONE ) OVERRIDE
+ {
+ if ( eType == IMPORT_FILTER_MAP )
+ {
+ return "*.bsp";
+ }
+ return "*.zip";
+ }
+ virtual const char *GetFileTypeDescriptions( eFilterType_t eType = IMPORT_FILTER_NONE ) OVERRIDE
+ {
+ if ( eType == IMPORT_FILTER_MAP )
+ {
+ return "#TF_SteamWorkshop_AcceptableFilesMaps";
+ }
+ return "#TF_SteamWorkshop_AcceptableFiles";
+ }
+ virtual const char *GetResFile() const { return "Resource/UI/PublishFileDialog.res"; }
+
+ virtual void SetFile( const char *lpszFilename, bool bImported )
+ {
+ BaseClass::SetFile( lpszFilename, bImported );
+ SetTagsVisible( true, WorkshipFileTypeForFile( lpszFilename ) );
+
+ m_sFilePath = lpszFilename;
+
+ CUtlBuffer buffer;
+ g_pFullFileSystem->ReadFile( lpszFilename, NULL, buffer );
+ m_fileCRC = CRC32_ProcessSingleBuffer( buffer.Base(), buffer.Size() );
+
+ m_bImported = bImported;
+ }
+
+ void SetTagsVisible( bool visible, EWorkshopFileType eFileType = k_EWorkshopFileTypeCommunity )
+ {
+ uint32 i;
+ vgui::CheckButton *pButton;
+
+ vgui::Label *pLabel = FindControl<vgui::Label>( "TagsTitle", true );
+ if ( pLabel )
+ {
+ pLabel->SetVisible( visible );
+ }
+
+ if ( !visible )
+ {
+ // no tags visible, so hide everything
+ // class tags
+ for ( i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
+ {
+ pButton = FindControl<vgui::CheckButton>( VarArgs( "ClassCheckBox%d", i ), true );
+ if ( pButton )
+ {
+ pButton->SetVisible( visible );
+ }
+ }
+
+ // other tags
+ for ( i = 0; i < kNumOtherTags; ++i )
+ {
+ pButton = FindControl<vgui::CheckButton>( kOtherTags[i].pCheckboxElementName, true );
+ if ( pButton )
+ {
+ pButton->SetVisible( visible );
+ }
+ }
+
+ // map tags
+ for ( i = 0; i < kNumMapTags; ++i )
+ {
+ pButton = FindControl<vgui::CheckButton>( kMapTags[i].pCheckboxElementName, true );
+ if ( pButton )
+ {
+ pButton->SetVisible( visible );
+ }
+ }
+ }
+ else
+ {
+ bool bIsMap = ( eFileType == k_EWorkshopFileTypeCommunity );
+
+ // class tags
+ for ( i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
+ {
+ pButton = FindControl<vgui::CheckButton>( VarArgs( "ClassCheckBox%d", i ), true );
+ if ( pButton )
+ {
+ pButton->SetVisible( !bIsMap );
+
+ if ( bIsMap && pButton->IsSelected() )
+ {
+ pButton->SetSelected( false ); // reset this button if we're using the maps tags
+ }
+ }
+ }
+
+ // other tags
+ for ( i = 0; i < kNumOtherTags; ++i )
+ {
+ pButton = FindControl<vgui::CheckButton>( kOtherTags[i].pCheckboxElementName, true );
+ if ( pButton )
+ {
+ pButton->SetVisible( !bIsMap );
+
+ if ( bIsMap && pButton->IsSelected() )
+ {
+ pButton->SetSelected( false ); // reset this button if we're using the maps tags
+ }
+ }
+ }
+
+ // map tags
+ for ( i = 0; i < kNumMapTags; ++i )
+ {
+ pButton = FindControl<vgui::CheckButton>( kMapTags[i].pCheckboxElementName, true );
+ if ( pButton )
+ {
+ pButton->SetVisible( bIsMap );
+
+ if ( !bIsMap && pButton->IsSelected() )
+ {
+ pButton->SetSelected( false ); // reset this button if we're not using the maps tags
+ }
+ }
+ }
+ }
+ }
+
+ virtual void PopulateTags( SteamParamStringArray_t &strArray )
+ {
+ m_vecTags.RemoveAll();
+
+ // class tags
+ vgui::EditablePanel* pClassUsagePanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName( "ClassUsagePanel" ) );
+ if ( pClassUsagePanel )
+ {
+ for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
+ {
+ if ( IsChildButtonSelected( pClassUsagePanel, VarArgs("ClassCheckBox%d",i), false ) )
+ {
+ m_vecTags.AddToTail( kClassTags[i] );
+ }
+ }
+ }
+
+ // other tags
+ for ( uint32 i = 0; i < kNumOtherTags; ++i )
+ {
+ if ( IsChildButtonSelected( this, kOtherTags[i].pCheckboxElementName, true ) )
+ {
+ m_vecTags.AddToTail( kOtherTags[i].pTag );
+ }
+ }
+
+ // map tags
+ for ( uint32 i = 0; i < kNumMapTags; ++i )
+ {
+ if ( IsChildButtonSelected( this, kMapTags[i].pCheckboxElementName, true ) )
+ {
+ m_vecTags.AddToTail( kMapTags[i].pTag );
+ }
+ }
+
+ if ( m_bImported )
+ {
+ m_vecTags.AddToTail( kImportedTag );
+ }
+
+ strArray.m_ppStrings = m_vecTags.Base();
+ strArray.m_nNumStrings = m_vecTags.Count();
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ // Center it, keeping requested size
+ int x, y, ww, wt, wide, tall;
+ vgui::surface()->GetWorkspaceBounds( x, y, ww, wt );
+ GetSize(wide, tall);
+ SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2));
+ }
+
+ virtual bool PrepSteamCloudFilePath( const char *lpszFileName, CUtlString &steamCloudFileName )
+ {
+ char szShortName[ MAX_PATH ];
+ Q_FileBase( lpszFileName, szShortName, sizeof( szShortName ) );
+ const char *szExt = Q_GetFileExtension( lpszFileName );
+ Q_SetExtension( szShortName, CFmtStr( ".%s", szExt ).Access(), sizeof(szShortName ) );
+ steamCloudFileName.Format( "steamworkshop/tf2/%s", szShortName );
+ return true;
+ }
+
+ virtual void ErrorMessage( ErrorCode_t errorCode, KeyValues *pkvTokens )
+ {
+ switch ( errorCode )
+ {
+ case kFailedToPublishFile:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToPublishFile", "#GameUI_OK" );
+ break;
+ case kFailedToPrepareFile:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToPrepareFile", "#GameUI_OK" );
+ break;
+ case kFailedToUpdateFile:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToUpdateFile", "#GameUI_OK" );
+ break;
+ case kSteamCloudNotAvailable:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kSteamCloudNotAvailable", "#GameUI_OK" );
+ break;
+ case kSteamExceededCloudQuota:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kSteamExceededCloudQuota", "#GameUI_OK" );
+ break;
+ case kFailedToWriteToSteamCloud:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToWriteToSteamCloud", "#GameUI_OK" );
+ break;
+ case kFileNotFound:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFileNotFound", "#GameUI_OK" );
+ break;
+ case kNeedTitleAndDescription:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kNeedTitleAndDescription", "#GameUI_OK" );
+ break;
+ case kFailedFileValidation:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileValidation", "#GameUI_OK" );
+ break;
+ case kFailedFileTooLarge:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileTooLarge", "#GameUI_OK" );
+ break;
+ case kFailedFileNotFound:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileNotFound", "#GameUI_OK" );
+ break;
+ case kFailedUserModifiedFile:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedUserModifiedFile", "#GameUI_OK" );
+ break;
+ case kInvalidMapName:
+ ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kInvalidMapName", "#GameUI_OK" );
+ break;
+ default:
+ Assert( false ); // Unhandled enum value
+ break;
+ }
+ if ( pkvTokens )
+ {
+ pkvTokens->deleteThis();
+ }
+ }
+
+ void ApplySchemeSettings( vgui::IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ if ( !m_bAddingNewFile )
+ {
+ bool bIsMap = ( m_FileDetails.publishedFileDetails.m_eFileType == k_EWorkshopFileTypeCommunity );
+
+ CExImageButton *pImageButton = FindControl<CExImageButton>( "ButtonSourceCosmetics", true );
+ if ( pImageButton )
+ {
+ pImageButton->SetVisible( !bIsMap );
+ }
+
+ vgui::Button *pButton = FindControl<Button>( "ButtonSourceOther", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( !bIsMap );
+ }
+
+ pImageButton = FindControl<CExImageButton>( "ButtonSourceMaps", true );
+ if ( pImageButton )
+ {
+ pImageButton->SetVisible( bIsMap );
+ }
+ }
+ }
+
+protected:
+
+ virtual void PopulateEditFields( void )
+ {
+ BaseClass::PopulateEditFields();
+
+ // class tags
+ vgui::EditablePanel* pClassUsagePanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName( "ClassUsagePanel" ) );
+ if ( pClassUsagePanel )
+ {
+ for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
+ {
+ bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kClassTags[i] ) != 0;
+ SetChildButtonSelected( pClassUsagePanel, VarArgs("ClassCheckBox%d",i), bHasTag );
+ }
+ }
+
+ // other tags
+ for ( uint32 i = 0; i < kNumOtherTags; ++i )
+ {
+ bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kOtherTags[i].pTag ) != 0;
+ SetChildButtonSelected( this, kOtherTags[i].pCheckboxElementName, bHasTag, true );
+ }
+
+ // map tags
+ for ( uint32 i = 0; i < kNumMapTags; ++i )
+ {
+ bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kMapTags[i].pTag ) != 0;
+ SetChildButtonSelected( this, kMapTags[i].pCheckboxElementName, bHasTag, true );
+ }
+
+ if ( Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kImportedTag ) )
+ {
+ m_bImported = true;
+ }
+ else
+ {
+ m_bImported = false;
+ }
+
+ // Default the tags hidden until there is a file set
+ if ( !m_FileDetails.lpszFilename )
+ {
+ SetTagsVisible( false );
+ }
+ else
+ {
+ SetTagsVisible( true, m_FileDetails.publishedFileDetails.m_eFileType );
+ }
+
+#ifndef WORKSHOP_IMPORT_ENABLED
+ vgui::Button *pImportButton = FindControl<vgui::Button>( "ButtonSourceCosmetics" );
+ if ( pImportButton )
+ pImportButton->SetVisible( false );
+#endif
+ }
+
+ const char* GetStatusString( StatusCode_t statusCode )
+ {
+ switch ( statusCode )
+ {
+ case kPublishing:
+ return "#TF_PublishFile_Publishing";
+ break;
+ case kUpdating:
+ return "#TF_PublishFile_Updating";
+ break;
+ }
+ return "";
+ }
+
+ void ShowStatusWindow( StatusCode_t statusCode )
+ {
+ ShowWaitingDialog( new CGenericWaitingDialog( this ), GetStatusString( statusCode ), true, false, 0.0f );
+ }
+
+ void HideStatusWindow( void )
+ {
+ CloseWaitingDialog();
+ }
+
+ virtual void OnCommand( const char *command )
+ {
+ if ( V_stricmp( command, "MainFileCosmetics" ) == 0 )
+ {
+#ifdef WORKSHOP_IMPORT_ENABLED
+ if ( CItemUpload::InitManifest() )
+ {
+ CTFFileImportDialog *pImportDialog = new CTFFileImportDialog( this );
+ pImportDialog->SetDeleteSelfOnClose( true );
+ pImportDialog->SetSizeable( false );
+ MakeModalAndBringToFront( pImportDialog );
+ }
+ else
+ {
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_InvalidManifest" );
+ }
+#endif
+ }
+ else if ( V_stricmp( command, "Publish" ) == 0 || V_stricmp( command, "Update" ) == 0 )
+ {
+ if ( m_bImported )
+ {
+ CUtlBuffer buffer;
+ g_pFullFileSystem->ReadFile( m_sFilePath, NULL, buffer );
+ CRC32_t crc = CRC32_ProcessSingleBuffer( buffer.Base(), buffer.Size() );
+ if ( crc != m_fileCRC )
+ {
+ ErrorMessage( kFailedUserModifiedFile, NULL );
+ m_bImported = false;
+ return;
+ }
+ }
+
+ BaseClass::OnCommand( command );
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+ }
+
+private:
+ CUtlVector< const char* > m_vecTags;
+
+ CRC32_t m_fileCRC;
+ CUtlString m_sFilePath;
+ bool m_bImported;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CUGCFileRequestCache
+{
+public:
+ CUGCFileRequestCache()
+ {
+ m_mapPreviewFileRequests.SetLessFunc( DefLessFunc( UGCHandle_t ) );
+ }
+
+ ~CUGCFileRequestCache()
+ {
+ m_mapPreviewFileRequests.PurgeAndDeleteElements();
+ }
+
+ CUGCFileRequest *GetFileRequest( UGCHandle_t fileHandle )
+ {
+ int idx = m_mapPreviewFileRequests.Find( fileHandle );
+ if ( idx == m_mapPreviewFileRequests.InvalidIndex() )
+ {
+ idx = m_mapPreviewFileRequests.Insert( fileHandle, new CUGCFileRequest() );
+ }
+ return m_mapPreviewFileRequests[idx];
+ }
+
+private:
+ CUtlMap< UGCHandle_t, CUGCFileRequest* > m_mapPreviewFileRequests;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSteamWorkshopItemPanel : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CSteamWorkshopItemPanel, vgui::EditablePanel );
+public:
+ CSteamWorkshopItemPanel( vgui::Panel *parent, const char *panelName, CUGCFileRequestCache &previewFileCache )
+ : vgui::EditablePanel( parent, panelName )
+ , m_bSelected( false )
+ , m_bPreviewDownloadPending( false )
+ , m_pUGCPreviewFileRequest( NULL )
+ , m_previewFileCache( previewFileCache )
+
+ {
+ memset( &m_FileDetails, 0, sizeof( m_FileDetails ) );
+ m_pCroppedTextureImagePanel = new CBitmapPanel( this, "PreviewImage" );
+ m_pHighlightPanel = new vgui::EditablePanel( this, "HighlightPanel" );
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
+ }
+
+ void SetPublishedFileDetails( const SteamUGCDetails_t *pDetails )
+ {
+ if ( pDetails )
+ {
+ m_FileDetails = *pDetails;
+
+ SetDialogVariable( "title", m_FileDetails.m_rgchTitle );
+ DownloadPreviewImage();
+
+ SetVisible( true );
+ InvalidateLayout( true );
+ }
+ else
+ {
+ m_bPreviewDownloadPending = false;
+ SetVisible( false );
+ m_pUGCPreviewFileRequest = NULL;
+ }
+
+ m_bSelected = false;
+ UpdateBorder();
+ }
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+ LoadControlSettings( "Resource/UI/SteamWorkshopItem.res" );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+ }
+
+ virtual void OnTick()
+ {
+ BaseClass::OnTick();
+
+ if ( m_bPreviewDownloadPending && m_pUGCPreviewFileRequest )
+ {
+ UGCFileRequestStatus_t ugcStatus = m_pUGCPreviewFileRequest->Update();
+ switch ( ugcStatus )
+ {
+ case UGCFILEREQUEST_ERROR:
+ m_bPreviewDownloadPending = false;
+ break;
+
+ case UGCFILEREQUEST_FINISHED:
+ SetPreviewImage();
+ m_bPreviewDownloadPending = false;
+ break;
+
+ default:
+ // Working, continue to wait...
+ return;
+ break;
+ }
+ }
+ }
+
+ virtual void OnCursorEntered()
+ {
+ m_pHighlightPanel->SetVisible( true );
+ }
+
+ virtual void OnCursorExited()
+ {
+ UpdateBorder();
+ }
+
+ virtual void OnMousePressed(vgui::MouseCode code)
+ {
+ if ( code != MOUSE_LEFT )
+ return;
+
+ PostActionSignal( new KeyValues( "ItemPanelMousePressed" ) );
+ }
+
+ void UpdateBorder()
+ {
+ m_pHighlightPanel->SetVisible( m_bSelected );
+ }
+
+ void SetSelected( bool bSelected ) { m_bSelected = bSelected; }
+ bool GetSelected() const { return m_bSelected; }
+
+ uint64 GetPublishedFileID() const { return m_FileDetails.m_nPublishedFileId; }
+
+protected:
+ void DownloadPreviewImage()
+ {
+ m_pCroppedTextureImagePanel->SetImage( NULL );
+ m_pUGCPreviewFileRequest = m_previewFileCache.GetFileRequest( m_FileDetails.m_hPreviewFile );
+
+ switch ( m_pUGCPreviewFileRequest->GetStatus() )
+ {
+ case UGCFILEREQUEST_READY:
+ {
+ // Start off our download
+ char szTargetFilename[MAX_PATH];
+ V_snprintf( szTargetFilename, sizeof(szTargetFilename), "%llu_thumb.jpg", m_FileDetails.m_nPublishedFileId );
+ m_pUGCPreviewFileRequest->StartDownload( m_FileDetails.m_hPreviewFile, "downloads", szTargetFilename );
+ m_bPreviewDownloadPending = true;
+ }
+ break;
+
+ case UGCFILEREQUEST_DOWNLOADING:
+ case UGCFILEREQUEST_DOWNLOAD_WRITING:
+ {
+ // download already going for another preview panel
+ m_bPreviewDownloadPending = true;
+ }
+ break;
+
+ case UGCFILEREQUEST_FINISHED:
+ {
+ SetPreviewImage();
+ }
+ break;
+
+ default:
+ {
+ m_pCroppedTextureImagePanel->SetVisible( false );
+ }
+ break;
+ } // switch
+ }
+
+ void SetPreviewImage()
+ {
+ // Update our image preview
+ char szLocalFilename[MAX_PATH];
+ m_pUGCPreviewFileRequest->GetLocalFileName( szLocalFilename, sizeof(szLocalFilename) );
+ char szLocalPath[ _MAX_PATH ];
+ g_pFullFileSystem->GetLocalPath( szLocalFilename, szLocalPath, sizeof(szLocalPath) );
+ SetPreviewImage( szLocalPath );
+ }
+
+ void SetPreviewImage( const char *lpszFilename )
+ {
+ if ( lpszFilename == NULL )
+ return;
+
+ ConversionErrorType nErrorCode = ImgUtl_LoadBitmap( lpszFilename, m_imgSource );
+ if ( nErrorCode == CE_SUCCESS )
+ {
+ m_pCroppedTextureImagePanel->SetBitmap( m_imgSource );
+ m_pCroppedTextureImagePanel->SetVisible( true );
+ }
+ }
+
+ // data
+ bool m_bSelected;
+ bool m_bPreviewDownloadPending;
+ Bitmap_t m_imgSource; // original resolution and aspect
+ CBitmapPanel *m_pCroppedTextureImagePanel;
+ vgui::EditablePanel *m_pHighlightPanel;
+ CUGCFileRequest *m_pUGCPreviewFileRequest;
+ SteamUGCDetails_t m_FileDetails;
+ CUGCFileRequestCache &m_previewFileCache;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#define MAX_ITEMS_VIEWABLE 4
+
+class CSteamWorkshopDialog : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CSteamWorkshopDialog, vgui::EditablePanel );
+public:
+ CSteamWorkshopDialog( vgui::Panel *parent )
+ : vgui::EditablePanel( parent, "SteamWorkshopDialog" )
+ , m_lastPublishedFilesState( CPublishedFiles::kState_Initialized )
+ {
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme(scheme);
+ SetProportional( true );
+
+ m_pContainer = new vgui::EditablePanel( this, "Container" );
+ m_pItemsContainer = new vgui::EditablePanel( m_pContainer, "ItemsContainer" );
+
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
+
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ m_items[i] = new CSteamWorkshopItemPanel( m_pItemsContainer, VarArgs("SteamWorkshopItem%d", i ), m_previewFileCache );
+ m_items[i]->AddActionSignalTarget( this );
+ }
+ }
+
+ virtual ~CSteamWorkshopDialog()
+ {
+ }
+
+ virtual void OnTick( void )
+ {
+ BaseClass::OnTick();
+
+ if ( m_lastPublishedFilesState != m_publishedFiles.m_state )
+ {
+ CloseWaitingDialog();
+ switch ( m_publishedFiles.m_state )
+ {
+ case CPublishedFiles::kState_PopulatingFileList:
+ ShowWaitingDialog( new CGenericWaitingDialog( this ), "#TF_SteamWorkshop_PopulatingList", true, false, 30.0f );
+ break;
+ case CPublishedFiles::kState_ErrorOccurred:
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_ErrorText", "#GameUI_OK" );
+ break;
+ case CPublishedFiles::kState_DeletingFile:
+ ShowWaitingDialog( new CGenericWaitingDialog( this ), "#TF_SteamWorkshop_DeletingFile", true, false, 30.0f );
+ break;
+ case CPublishedFiles::kState_ErrorCannotDeleteFile:
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_CannotDeleteFile", "#GameUI_OK" );
+ break;
+ case CPublishedFiles::kState_Timeout:
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_Timeout", "#GameUI_OK" );
+ break;
+ case CPublishedFiles::kState_Done:
+ RefreshItemsUI();
+ break;
+ }
+ m_lastPublishedFilesState = m_publishedFiles.m_state;
+ }
+ }
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "Resource/ui/SteamWorkshopDialog.res" );
+
+ SetupButton( "LearnMoreButton" );
+ SetupButton( "LearnMore2Button" );
+ SetupButton( "BrowseButton" );
+ SetupButton( "PublishButton" );
+ SetupButton( "ViewPublishedButton" );
+ SetupButton( "LoadTestMapButton" );
+ SetupButton( "ViewLegalAgreementButton" );
+
+ SetupButton( "PrevPageButton" );
+ SetupButton( "NextPageButton" );
+ SetupButton( "ViewButton" );
+ SetupButton( "EditButton" );
+ SetupButton( "DeleteButton" );
+
+ SetupButton( "CancelButton" );
+
+ SetChildPanelEnabled( this, "LoadTestMapButton", !engine->IsInGame() || !FStrEq( engine->GetLevelName(), "maps/itemtest.bsp" ), true );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ // Center it, keeping requested size
+ int x, y, ww, wt, wide, tall;
+ vgui::surface()->GetWorkspaceBounds( x, y, ww, wt );
+ GetSize(wide, tall);
+ SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2));
+ }
+
+ virtual void Show()
+ {
+ TFModalStack()->PushModal( this );
+
+ // Make sure we're signed on
+ if ( !CheckSteamSignOn() )
+ {
+ Close();
+ return;
+ }
+
+ SetVisible( true );
+ MakePopup();
+ MoveToFront();
+ SetKeyBoardInputEnabled( true );
+ SetMouseInputEnabled( true );
+
+ // start download
+ m_publishedFiles.PopulateFileList();
+ }
+
+ virtual void OnCommand( const char *pCommand )
+ {
+ if ( FStrEq( pCommand, "cancel" ) )
+ {
+ Close();
+ }
+ else if ( FStrEq( pCommand, "publish" ) )
+ {
+ ShowPublishFileDialog();
+ }
+ else if ( FStrEq( pCommand, "learn_more" ) )
+ {
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/contribute/" );
+ }
+ else if ( FStrEq( pCommand, "view_files" ) )
+ {
+ EUniverse universe = GetUniverse();
+ uint64 ulSteamID = steamapicontext->SteamUser()->GetSteamID().ConvertToUint64();
+ switch ( universe )
+ {
+ case k_EUniversePublic:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/profiles/%llu/mysharedfiles/", ulSteamID ) );
+ break;
+ case k_EUniverseBeta:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/profiles/%llu/mysharedfiles/", ulSteamID ) );
+ break;
+ case k_EUniverseDev:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "profiles/%llu/mysharedfiles/", ulSteamID ) );
+ break;
+ }
+ }
+ else if ( FStrEq( pCommand, "view_legal_agreement" ) )
+ {
+ EUniverse universe = GetUniverse();
+ switch ( universe )
+ {
+ case k_EUniversePublic:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) );
+ break;
+ case k_EUniverseBeta:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) );
+ break;
+ case k_EUniverseDev:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) );
+ break;
+ }
+ }
+ else if ( FStrEq( pCommand, "browse" ) )
+ {
+ EUniverse universe = GetUniverse();
+ switch ( universe )
+ {
+ case k_EUniversePublic:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/workshop/browse?appid=%d", engine->GetAppID() ) );
+ break;
+ case k_EUniverseBeta:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/workshop/browse?appid=%d", engine->GetAppID() ) );
+ break;
+ case k_EUniverseDev:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "workshop/browse?appid=%d", engine->GetAppID() ) );
+ break;
+ }
+ }
+ else if ( FStrEq( pCommand, "itemtest" ) )
+ {
+ Close();
+ engine->ClientCmd_Unrestricted( "disconnect\nwait\nwait\n\nprogress_enable\nmap itemtest\n" );
+ }
+ else if ( FStrEq( pCommand, "prevpage" ) )
+ {
+ if ( m_unCurrentPage > 0 )
+ {
+ --m_unCurrentPage;
+ }
+ PopulatePublishedFilesUI();
+ }
+ else if ( FStrEq( pCommand, "nextpage" ) )
+ {
+ uint32 unNumPages = ceil( (float)m_publishedFiles.m_FileDetails.Count() / (float)MAX_ITEMS_VIEWABLE );
+ if ( m_unCurrentPage < unNumPages )
+ {
+ ++m_unCurrentPage;
+ PopulatePublishedFilesUI();
+ }
+ }
+ else if ( FStrEq( pCommand, "view" ) )
+ {
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ if ( m_items[i]->GetSelected() )
+ {
+ ViewPublishedFile( m_items[i]->GetPublishedFileID() );
+ break;
+ }
+ }
+ }
+ else if ( FStrEq( pCommand, "edit" ) )
+ {
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ if ( m_items[i]->GetSelected() )
+ {
+ EditPublishedFile( m_items[i]->GetPublishedFileID() );
+ break;
+ }
+ }
+ }
+ else if ( FStrEq( pCommand, "delete_item" ) )
+ {
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ if ( m_items[i]->GetSelected() )
+ {
+ DeletePublishedFile( m_items[i]->GetPublishedFileID() );
+ break;
+ }
+ }
+ }
+ else
+ {
+ BaseClass::OnCommand( pCommand );
+ }
+ }
+
+ virtual void OnKeyCodeTyped( vgui::KeyCode code )
+ {
+ if( code == KEY_ESCAPE )
+ {
+ OnCommand( "cancel" );
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+ }
+
+ virtual void OnKeyCodePressed( vgui::KeyCode code )
+ {
+ if( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
+ {
+ OnCommand( "cancel" );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+ }
+
+ MESSAGE_FUNC_PTR( OnItemPanelMousePressed, "ItemPanelMousePressed", panel )
+ {
+ CSteamWorkshopItemPanel *pItemPanel = static_cast< CSteamWorkshopItemPanel * >( panel );
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ m_items[i]->SetSelected( m_items[i] == pItemPanel );
+ m_items[i]->UpdateBorder();
+ }
+
+ // enable per item controls
+ SetChildPanelEnabled( m_pItemsContainer, "ViewButton", true );
+ SetChildPanelEnabled( m_pItemsContainer, "EditButton", true );
+ SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", true );
+ }
+
+ MESSAGE_FUNC_UINT64( OnChangedFile, "ChangedFile", nPublishedFileID )
+ {
+ m_publishedFiles.RefreshPublishedFileDetails( nPublishedFileID );
+ }
+
+protected:
+
+ bool CheckSteamSignOn()
+ {
+ // Make sure we are connected to steam, or they are going to be disappointed
+ if ( steamapicontext == NULL
+ || steamapicontext->SteamUtils() == NULL
+ || steamapicontext->SteamMatchmakingServers() == NULL
+ || steamapicontext->SteamUser() == NULL
+ || !steamapicontext->SteamUser()->BLoggedOn()
+ ) {
+ Warning( "Steam not properly initialized or connected.\n" );
+ ShowMessageBox( "#TF_MM_GenericFailure_Title", "#TF_MM_GenericFailure", "#GameUI_OK" );
+ return false;
+ }
+ return true;
+ }
+
+ void RefreshItemsUI()
+ {
+ SetChildPanelVisible( m_pContainer, "NoItemsContainer", m_publishedFiles.m_FileDetails.Count() == 0 );
+ SetChildPanelVisible( m_pContainer, "ItemsContainer", m_publishedFiles.m_FileDetails.Count() != 0 );
+ SetChildPanelVisible( m_pContainer, "LearnMore2Button", m_publishedFiles.m_FileDetails.Count() != 0 );
+ SetChildPanelVisible( m_pContainer, "BrowseButton", m_publishedFiles.m_FileDetails.Count() == 0 );
+
+ m_unCurrentPage = 0;
+ PopulatePublishedFilesUI();
+ }
+
+ void PopulatePublishedFilesUI()
+ {
+ if ( m_publishedFiles.m_FileDetails.Count() != 0 )
+ {
+ uint32 unIndex = m_unCurrentPage * MAX_ITEMS_VIEWABLE;
+ int nFileIdx = m_publishedFiles.m_FileDetails.FirstInorder();
+ for ( uint32 i = 0; i < unIndex && nFileIdx != m_publishedFiles.m_FileDetails.InvalidIndex(); ++i )
+ {
+ nFileIdx = m_publishedFiles.m_FileDetails.NextInorder( nFileIdx );
+ }
+ bool bSelected = false;
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ CSteamWorkshopItemPanel *pItemPanel = m_items[i];
+ if ( nFileIdx != m_publishedFiles.m_FileDetails.InvalidIndex() )
+ {
+ SteamUGCDetails_t &details = m_publishedFiles.m_FileDetails[ nFileIdx ];
+ pItemPanel->SetPublishedFileDetails( &details );
+ if ( !bSelected )
+ {
+ pItemPanel->SetSelected( true );
+ pItemPanel->UpdateBorder();
+ bSelected = true;
+ }
+ nFileIdx = m_publishedFiles.m_FileDetails.NextInorder( nFileIdx );
+ }
+ else
+ {
+ pItemPanel->SetPublishedFileDetails( NULL );
+ }
+ }
+
+ // paging
+ uint32 unNumPages = ceil( (float)m_publishedFiles.m_FileDetails.Count() / (float)MAX_ITEMS_VIEWABLE );
+ bool bMultiplePages = ( unNumPages > 1 );
+ if ( bMultiplePages )
+ {
+ m_pItemsContainer->SetDialogVariable( "page", CFmtStr( "%d/%d", m_unCurrentPage + 1, unNumPages ) );
+ }
+ else
+ {
+ m_pItemsContainer->SetDialogVariable( "page", "" );
+ }
+ SetChildPanelVisible( m_pItemsContainer, "NextPageButton", bMultiplePages );
+ SetChildPanelVisible( m_pItemsContainer, "PrevPageButton", bMultiplePages );
+ SetChildPanelEnabled( m_pItemsContainer, "NextPageButton", m_unCurrentPage < unNumPages - 1 );
+ SetChildPanelEnabled( m_pItemsContainer, "PrevPageButton", m_unCurrentPage > 0 );
+
+ // other controls
+ SetChildPanelEnabled( m_pItemsContainer, "ViewButton", bSelected );
+ SetChildPanelEnabled( m_pItemsContainer, "EditButton", bSelected );
+ SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", bSelected );
+ }
+ else
+ {
+ SetChildPanelEnabled( m_pItemsContainer, "ViewButton", false );
+ SetChildPanelEnabled( m_pItemsContainer, "EditButton", false );
+ SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", false );
+ }
+ }
+
+ void ShowPublishFileDialog()
+ {
+ CTFFilePublishDialog *pPublishDialog = new CTFFilePublishDialog( this, "PublishFileDialog", NULL );
+ pPublishDialog->AddActionSignalTarget( this );
+ pPublishDialog->SetDeleteSelfOnClose( true );
+ pPublishDialog->SetSizeable( false );
+ MakeModalAndBringToFront( pPublishDialog );
+ }
+
+ void ViewPublishedFile( uint64 nPublishedFileID )
+ {
+ EUniverse universe = GetUniverse();
+ switch ( universe )
+ {
+ case k_EUniversePublic:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ case k_EUniverseBeta:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ case k_EUniverseDev:
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "sharedfiles/filedetails/?id=%llu", nPublishedFileID ) );
+ break;
+ }
+ }
+
+ void EditPublishedFile( uint64 nPublishedFileID )
+ {
+ const SteamUGCDetails_t *pDetails = m_publishedFiles.GetPublishedFileDetails( nPublishedFileID );
+ if ( pDetails )
+ {
+
+ PublishedFileDetails_t fileDetails;
+ fileDetails.publishedFileDetails = *pDetails;
+ fileDetails.lpszFilename = pDetails->m_pchFileName;
+
+ CTFFilePublishDialog *pPublishDialog = new CTFFilePublishDialog( this, "PublishFileDialog", &fileDetails );
+ pPublishDialog->AddActionSignalTarget( this );
+ pPublishDialog->SetDeleteSelfOnClose( true );
+ pPublishDialog->SetSizeable( false );
+ MakeModalAndBringToFront( pPublishDialog );
+
+ if ( fileDetails.publishedFileDetails.m_eFileType == k_EWorkshopFileTypeMicrotransaction &&
+ !Q_strstr( fileDetails.publishedFileDetails.m_rgchTags, kImportedTag ) )
+ {
+ ShowMessageBox( "#TF_ImportFile_Warning", "#TF_ImportFile_NotCompatible" );
+ }
+ }
+ }
+
+ static void ConfirmDeletePublishedFile( bool bConfirmed, void *pContext )
+ {
+ if ( bConfirmed )
+ {
+ CSteamWorkshopDialog *pDialog = static_cast< CSteamWorkshopDialog* >( pContext );
+ uint64 nPublishedFileID = 0;
+ for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i )
+ {
+ if ( pDialog->m_items[i]->GetSelected() )
+ {
+ nPublishedFileID = pDialog->m_items[i]->GetPublishedFileID();
+ break;
+ }
+ }
+ if ( nPublishedFileID != 0 )
+ {
+ const SteamUGCDetails_t *pDetails = pDialog->m_publishedFiles.GetPublishedFileDetails( nPublishedFileID );
+ if ( pDetails )
+ {
+ pDialog->m_publishedFiles.DeletePublishedFile( nPublishedFileID );
+ }
+ }
+ }
+ }
+
+ void DeletePublishedFile( uint64 nPublishedFileID )
+ {
+ ShowConfirmDialog( "#TF_SteamWorkshop_DeleteConfirmTitle", "#TF_SteamWorkshop_DeleteConfirmText", "#GameUI_OK", "#GameuI_CancelBold", &ConfirmDeletePublishedFile, this, this );
+ }
+
+ void SetupButton( const char *pPanelName )
+ {
+ vgui::Panel *pPanel = m_pContainer->FindChildByName( pPanelName, true );
+ if ( pPanel )
+ {
+ pPanel->AddActionSignalTarget( this );
+ }
+ }
+
+ // called when the Cancel button is pressed
+ void Close()
+ {
+ SetVisible( false );
+ TFModalStack()->PopModal( this );
+ MarkForDeletion();
+ }
+
+ vgui::EditablePanel *m_pContainer;
+ vgui::EditablePanel *m_pItemsContainer;
+ CPublishedFiles m_publishedFiles;
+ uint32 m_lastPublishedFilesState;
+ uint32 m_unCurrentPage;
+ CSteamWorkshopItemPanel *m_items[MAX_ITEMS_VIEWABLE];
+ CUGCFileRequestCache m_previewFileCache;
+};
+static vgui::DHANDLE<CSteamWorkshopDialog> g_pSteamWorkshopDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose: Callback to open the game menus
+//-----------------------------------------------------------------------------
+static void CL_OpenSteamWorkshopDialog( const CCommand &args )
+{
+ if ( g_pSteamWorkshopDialog.Get() == NULL )
+ {
+ IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) );
+ g_pSteamWorkshopDialog = vgui::SETUP_PANEL( new CSteamWorkshopDialog( (CHudMainMenuOverride*)pMMOverride ) );
+ }
+ engine->ExecuteClientCmd( "gameui_activate" );
+ g_pSteamWorkshopDialog->Show();
+}
+
+// the console commands
+static ConCommand steamworkshopdialog( "OpenSteamWorkshopDialog", &CL_OpenSteamWorkshopDialog, "" );
+
+//-----------------------------------------------------------------------------
+
+class CItemTestHUDPanel : public CHudElement, public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CItemTestHUDPanel, vgui::EditablePanel );
+public:
+ CItemTestHUDPanel( const char *pElementName )
+ : CHudElement( pElementName )
+ , BaseClass( NULL, "ItemTestHUDPanel" )
+ {
+ vgui::Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+
+ SetHiddenBits( HIDEHUD_MISCSTATUS );
+ }
+
+ virtual ~CItemTestHUDPanel()
+ {
+
+ }
+
+ virtual bool ShouldDraw( void )
+ {
+ if ( !CHudElement::ShouldDraw() )
+ return false;
+
+ if ( !engine->IsInGame() )
+ return false;
+
+ if ( TFGameRules() && TFGameRules()->IsInItemTestingMode() )
+ return true;
+
+ return FStrEq( engine->GetLevelName(), "maps/itemtest.bsp" );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ if ( m_pBGPanel_Blue && m_pBGPanel_Red && C_TFPlayer::GetLocalTFPlayer() )
+ {
+ bool bRed = ( C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() == TF_TEAM_RED );
+ m_pBGPanel_Blue->SetVisible( !bRed );
+ m_pBGPanel_Red->SetVisible( bRed );
+ }
+ }
+
+ virtual void ApplySchemeSettings( vgui::IScheme *scheme )
+ {
+ if ( g_pFullFileSystem->FileExists( "resource/UI/ItemTestHUDPanel.res" ) )
+ {
+ LoadControlSettings( "resource/UI/ItemTestHUDPanel.res" );
+ }
+
+ BaseClass::ApplySchemeSettings( scheme );
+
+ m_pBGPanel_Blue = FindChildByName("Background_Blue");
+ m_pBGPanel_Red = FindChildByName("Background_Red");
+
+ if ( m_pBGPanel_Blue )
+ {
+ m_pBGPanel_Blue->SetVisible( true );
+ }
+ }
+
+ int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+ {
+ if ( !IsVisible() )
+ return 1; // key not handled
+
+ if ( !down )
+ return 1; // key not handled
+
+ switch ( keynum )
+ {
+ case KEY_F7:
+ {
+ engine->ClientCmd_Unrestricted( "itemtest" );
+ return 0;
+ }
+ break;
+ case KEY_F8:
+ {
+ engine->ClientCmd_Unrestricted( "itemtest_botcontrols" );
+ return 0;
+ }
+ break;
+ case KEY_F9:
+ {
+ InvalidateLayout( true, true );
+ return 0;
+ }
+ break;
+ }
+
+ return 1; // key not handled
+ }
+
+protected:
+ vgui::Panel *m_pBGPanel_Blue;
+ vgui::Panel *m_pBGPanel_Red;
+};
+
+DECLARE_HUDELEMENT( CItemTestHUDPanel );
+
+//-----------------------------------------------------------------------------
+
+// @return true if the item test HUD handled the input, false otherwise
+bool ItemTestHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ CItemTestHUDPanel *pItemTestHUDPanel = ( CItemTestHUDPanel * )GET_HUDELEMENT( CItemTestHUDPanel );
+ if ( pItemTestHUDPanel )
+ {
+ return pItemTestHUDPanel->HudElementKeyInput( down, keynum, pszCurrentBinding ) == 0;
+ }
+ return false;
+}
+