summaryrefslogtreecommitdiff
path: root/game/client/tf/workshop
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/workshop')
-rw-r--r--game/client/tf/workshop/item_import.cpp7631
-rw-r--r--game/client/tf/workshop/item_import.h317
-rw-r--r--game/client/tf/workshop/published_files.cpp1646
3 files changed, 9594 insertions, 0 deletions
diff --git a/game/client/tf/workshop/item_import.cpp b/game/client/tf/workshop/item_import.cpp
new file mode 100644
index 0000000..8f395ad
--- /dev/null
+++ b/game/client/tf/workshop/item_import.cpp
@@ -0,0 +1,7631 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+//
+// TODO:
+// * Bring in Jeff Hameluck's translucency support:
+// QC with any $translucent 1 VMT should have $mostlyopaque
+// * Add help icons with tooltips on valid asset constraints
+//
+// Maybe TODO:
+// * Add wireframe view to preview
+// * Fix issues with gesture support (no animation blending/easing, no weapon state logic)
+//
+//=============================================================================
+
+#include "cbase.h"
+
+#include "tier2/tier2.h"
+#include "tier2/p4helpers.h"
+#include "tier2/fileutils.h"
+#include "ienginevgui.h"
+#include "imageutils.h"
+#include "filesystem.h"
+#include "vgui_controls/FileOpenDialog.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/Label.h"
+#include "vgui_bitmapimage.h"
+#include "bitmap/bitmap.h"
+#include "itemtest/itemtest.h"
+
+#include "econ_controls.h"
+#include "econ_item_system.h"
+#include "tf_item_inventory.h"
+#include "c_tf_player.h"
+#include "confirm_dialog.h"
+#include "steampublishedfiles/publish_file_dialog.h"
+#include "vgui/store/v2/tf_store_preview_item2.h"
+#include "vgui/tf_playermodelpanel.h"
+#include "bone_setup.h"
+
+#include "scenefilecache/ISceneFileCache.h"
+
+#include "workshop/item_import.h"
+
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+
+#include "../public/zip_utils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+static const char *kClassFolders[TF_LAST_NORMAL_CLASS] = {
+ "", // TF_CLASS_UNDEFINED
+ "scout", // TF_CLASS_SCOUT
+ "sniper", // TF_CLASS_SNIPER,
+ "soldier", // TF_CLASS_SOLDIER,
+ "demo", // TF_CLASS_DEMOMAN,
+ "medic", // TF_CLASS_MEDIC,
+ "heavy", //TF_CLASS_HEAVYWEAPONS,
+ "pyro", // TF_CLASS_PYRO,
+ "spy", // TF_CLASS_SPY,
+ "engineer", // TF_CLASS_ENGINEER,
+};
+static const char *kClassFolderMulticlass = "all_class";
+
+static const int NUM_IMPORT_LODS = 3;
+static const int NUM_IMPORT_MATERIALS_PER_TEAM = 2;
+static const int MAX_MATERIAL_COUNT = NUM_IMPORT_MATERIALS_PER_TEAM * 2;
+static const int MAX_TEXT_EDIT_SIZE = 8192;
+static const int MAX_TAUNT_DURATION = 30.f;
+
+static const int kStartWorkshopItemIndex = 30000;
+
+static const char *kBodygroupArray[] =
+{
+ "backpack",
+ "dogtags",
+ "grenades",
+ "hands",
+ "hat",
+ "head",
+ "headphones",
+ "shoes",
+ "shoes_socks",
+};
+
+extern IFileSystem *g_pFullFileSystem;
+
+// This should be a constant since it's in the game directory hierarchy now
+//static ConVar tf_steam_workshop_dir( "tf_steam_workshop_dir", "workshop", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Directory used to hold import sessions for the Steam Workshop." );
+static const char *kSteamWorkshopDir = "%s/import_source";
+static const char *kWorkshopSchemaFile = "scripts/items/unencrypted/_items_workshop.txt";
+static const char *kWorkshopSoundScriptFile = "scripts/game_sounds_taunt_workshop.txt";
+
+static ConVar tf_steam_workshop_import_icon_path( "tf_steam_workshop_import_icon_path", "", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Default location to load backpack icons from" );
+static ConVar tf_steam_workshop_import_model_path( "tf_steam_workshop_import_model_path", "", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Default location to load models from" );
+static ConVar tf_steam_workshop_import_material_path( "tf_steam_workshop_import_material_path", "", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Default location to load materials from" );
+
+#ifdef STAGING_ONLY
+static ConVar tf_steam_workshop_max_taunt_duration( "tf_steam_workshop_max_taunt_duration", "-1" );
+#endif // STAGING_ONLY
+
+// Item data tokens
+static const char *kItemName = "name";
+static const char *kWorkshopName = "import_name";
+static const char *kTFEnglishName = "tf_english_name";
+static const char *kItemPrefab = "prefab";
+static const char *kItemIcon = "icon";
+static const char *kItemPaintable = "paintable%d";
+static const char *kAnimationLoopable = "loopable";
+static const char *kClassQC = "Classes/%s/QC";
+static const char *kClassQCI = "Classes/%s/QCI";
+static const char *kClassLODN = "Classes/%s/LOD%d";
+static const char *kClassLODNFile = "Classes/%s/LOD%d/file";
+static const char *kClassAnimation = "Classes/%s/Animation";
+static const char *kClassAnimationSourceFile = "Classes/%s/Animation/source_file";
+static const char *kClassAnimationVCDFile = "Classes/%s/Animation/vcd_file";
+static const char *kClassAnimationDuration = "Classes/%s/Animation/taunt_duration";
+static const char *kMaterialN = "Materials/Material%d";
+static const char *kMaterialSkinN = "Materials/Skins/%s/Material%d";
+static const char *kMaterialSkinNFile = "Materials/Skins/%s/Material%d/%s_texture_file";
+static const char *kMaterialSkinNVMT = "Materials/Skins/%s/Material%d/vmt";
+static const char *kItemSchema = "ItemSchema";
+static const char *kBuildOutput = "output";
+static const char *kSkinType = "skin_type";
+static const char *kEquipRegion = "equip_region";
+static const char *kBodygroup = "bodygroup";
+static const char *kPaintDefIndex = "paint_def_index";
+static const char *kWorkshopID = "workshop_id";
+
+// The default triangle budget for items
+static const int kDefaultTriangleBudget[] = { 1400, 1000, 700 };
+COMPILE_TIME_ASSERT( ARRAYSIZE( kDefaultTriangleBudget ) == NUM_IMPORT_LODS );
+
+static const int kDefaultBoneBudget = 5;
+
+static const char *kBuildResultMessages[] = {
+ "",
+ "#TF_ImportFile_BuildFailedNoSDK",
+ "#TF_ImportFile_BuildFailedNoName",
+ "#TF_ImportFile_BuildFailedNoType",
+ "#TF_ImportFile_BuildFailedNoModels",
+ "#TF_ImportFile_BuildFailedNumLODMismatch",
+ "#TF_ImportFile_BuildFailedNoMaterials",
+ "#TF_ImportFile_BuildFailedNumMaterialMismatch",
+ "#TF_ImportFile_BuildFailedNoBackpackIcon",
+ "#TF_ImportFile_BuildFailedBadName",
+ "TF English Name already exists in item schema.",
+ "#TF_ImportFile_BuildFailedBadType",
+ "#TF_ImportFile_BuildFailedBadModel",
+ "#TF_ImportFile_BuildFailedBadMaterialType",
+ "#TF_ImportFile_BuildFailedBadMaterial",
+ "#TF_ImportFile_BuildFailedMaterialMissingShader",
+ "#TF_ImportFile_BuildFailedMaterialMissingCloak",
+ "#TF_ImportFile_BuildFailedMaterialMissingBurning",
+ "#TF_ImportFile_BuildFailedMaterialMissingJarate",
+ "#TF_ImportFile_BuildFailedMaterialMissingPaintable",
+ "#TF_ImportFile_BuildFailedMissingModel",
+ "#TF_ImportFile_BuildFailedNeedMoreLOD",
+ "#TF_ImportFile_BuildFailedComplexModel",
+ "#TF_ImportFile_BuildFailedBadImage",
+ "#TF_ImportFile_BuildFailedCompile",
+ "#TF_ImportFile_BuildFailedNoWorkshopID",
+ "#TF_ImportFile_ImageUnsupportedFileType",
+ "#TF_ImportFile_ImageResolutionNotPowerOf2",
+ "#TF_ImportFile_ImageResolutionOverLimit",
+ "#TF_ImportFile_BuildFailedNoTaunts",
+ "#TF_ImportFile_BuildFailedBadVCD",
+ "#TF_ImportFile_BuildFailedVCDMissingEventSequence",
+ "#TF_ImportFile_BuildFailedVCDEventSequenceTooLong",
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kBuildResultMessages ) == CTFFileImportDialog::NUM_BUILD_RESULTS );
+
+static const char *kLoadResultMessages[ CTFFileImportDialog::NUM_LOAD_RESULTS ] = {
+ "",
+ "#TF_ImportFile_LoadFailedBadFile",
+ "#TF_ImportFile_LoadFailedBadFile",
+ "#TF_ImportFile_BuildFailedComplexModel",
+ "#TF_ImportFile_LoadFailedTooManyBones",
+ "#TF_ImportFile_LoadFailedBadFile",
+ "#TF_ImportFile_LoadFailedTooManyMaterials",
+ "#TF_ImportFile_LoadFailedMaterialCountMismatch",
+ "#TF_ImportFile_LoadFailedAnimationTooLong",
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kLoadResultMessages ) == CTFFileImportDialog::NUM_LOAD_RESULTS );
+
+static const char *kSaveResultMessages[ CTFFileImportDialog::NUM_SAVE_RESULTS ] = {
+ "",
+ "#TF_ImportFile_SaveFailedBadFile",
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kSaveResultMessages ) == CTFFileImportDialog::NUM_SAVE_RESULTS );
+
+static const char *kWarningMessages[ CTFFileImportDialog::NUM_WARNINGS ] =
+{
+ "TF_ImportFile_Warning_BaseAlphaMask",
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kWarningMessages ) == CTFFileImportDialog::NUM_WARNINGS );
+
+static const char *kLODLevels[] =
+{
+ "HIGH LOD",
+ "MEDIUM LOD",
+ "LOW LOD"
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kLODLevels ) == NUM_IMPORT_LODS );
+
+static const char *kPrefabs[] =
+{
+ "hat",
+ "misc",
+ "taunt"
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( kPrefabs ) == CTFFileImportDialog::PREFAB_COUNT );
+
+static const char* kDefaultPrefab = "hat";
+static const char* kDefaultEquipRegion = "hat";
+
+// Poses that your character can do in the item preview
+static const char *kModelPoses[] = {
+ "REF",
+ "STAND",
+ "CROUCH",
+ "RUN",
+};
+static const int kDefaultModelPoseIndex = 1;
+
+static const char *kModelActions[] = {
+ "taunt_laugh.vcd",
+ // The attack and reload animations don't work properly with the Heavy and Pyro
+ //"gesture_attack",
+ //"gesture_reload",
+};
+static const int kDefaultModelActionIndex = 0;
+
+static const char* s_pszMaterialFilePrefixes[] =
+{
+ "base",
+ "normal",
+ "phongexponent",
+ "selfillum"
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMaterialFilePrefixes ) == NUM_MATERIAL_TEXTURE_FILE_TYPE );
+
+// Each preview needs a unique model name so it actually gets reloaded if it changes
+static int s_nLastPreviewModel;
+
+static const char* kDefaultBumpmap = "models/player/shared/shared_normal";
+
+struct LightwarpInfo_t
+{
+ const char *pszName;
+ const char *pszPath;
+};
+
+static LightwarpInfo_t kLightwarps[] =
+{
+ { "pyro", "models/player/pyro/pyro_lightwarp" },
+ { "weapon", "models/lightwarps/weapon_lightwarp" },
+ { "robot", "models/lightwarps/robot_lightwarp" },
+ { "sentry1", "models/buildables/sentry1/sentry1_lightwarp" },
+ { "sentry2", "models/buildables/sentry2/sentry2_lightwarp" },
+ { "sentry3", "models/buildables/sentry3/sentry3_lightwarp" },
+ { "ambassador", "models/weapons/c_items/c_ambassador_lightwarp" },
+ { "jarate", "models/lightwarps/jarate_lightwarp" },
+};
+static const char *kLightwarpPath = "lightwarp_path";
+
+struct EnvmapInfo_t
+{
+ const char *pszName;
+ const char *pszPath;
+};
+
+static EnvmapInfo_t kEnvmaps[] =
+{
+ { "none", "" },
+ { "world cube map", "env_cubemap" },
+ { "saxxy gold", "effects/saxxy/saxxy_gold" },
+ { "pyro land goggles", "effects/pyrocube" }
+};
+static const char *kEnvmapPath = "envmap_path";
+
+struct EnvmapMaskInfo_t
+{
+ const char *pszDisplayName;
+ const char *pszVarName;
+};
+
+static EnvmapMaskInfo_t kEnvmapMasks[] =
+{
+ { "none", "" },
+ { "base alpha", "$basealphaenvmapmask" },
+ { "normal map alpha", "$normalmapalphaenvmapmask" }
+};
+static const char *kEnvmapMaskVarName = "envmapmask_varname";
+
+
+static float GetMaxTauntDuration()
+{
+#ifdef STAGING_ONLY
+ if ( tf_steam_workshop_max_taunt_duration.GetFloat() > 0 )
+ {
+ return tf_steam_workshop_max_taunt_duration.GetFloat();
+ }
+#endif // STAGING_ONLY
+ return MAX_TAUNT_DURATION;
+}
+
+
+static const char *GetWorkshopSoundScriptFile()
+{
+ static char szCorrectCaseFilePath[MAX_PATH];
+ char szItemWorkshopSoundScriptAbsPath[MAX_PATH];
+ if ( GenerateFullPath( kWorkshopSoundScriptFile, "MOD", szItemWorkshopSoundScriptAbsPath, ARRAYSIZE( szItemWorkshopSoundScriptAbsPath ) ) )
+ {
+ g_pFullFileSystem->GetCaseCorrectFullPath( szItemWorkshopSoundScriptAbsPath, szCorrectCaseFilePath );
+ }
+ else
+ {
+ Warning( "Failed to GenerateFullPath %s\n", kWorkshopSoundScriptFile );
+ return NULL;
+ }
+
+ return szCorrectCaseFilePath;
+}
+
+
+//-----------------------------------------------------------------------------
+static void SetMessageFileVariable( KeyValues *pData, const char *pszFilePath )
+{
+ if ( !pData )
+ return;
+
+ wchar_t unicodeFile[MAX_PATH];
+ g_pVGuiLocalize->ConvertANSIToUnicode( pszFilePath, unicodeFile, sizeof(unicodeFile) );
+ pData->SetWString( "file", unicodeFile );
+}
+
+//-----------------------------------------------------------------------------
+static void ShowMessageBoxWithFile( const char *pTitle, const char *pText, const char *pszFilePath)
+{
+ KeyValuesAD pData( "data" );
+ SetMessageFileVariable( pData, pszFilePath );
+ ShowMessageBox( pTitle, pText, pData );
+}
+
+static void SaveBrowsePath( ConVar &conVar, const char *pszFilePath )
+{
+ char pszDirPath[MAX_PATH];
+ V_ExtractFilePath( pszFilePath, pszDirPath, sizeof(pszDirPath) );
+ conVar.SetValue( pszDirPath );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Save build output for presenting to the user
+//-----------------------------------------------------------------------------
+class CBuildLog : public CItemLog
+{
+public:
+ CBuildLog() : CItemLog(), m_log( 0, 0, CUtlBuffer::TEXT_BUFFER ) { }
+
+ virtual void Log( ItemtestLogLevel_t nLogLevel, const char *pszMessage ) const
+ {
+ static const char *pszIgnoredMessages[] = {
+ "CDynamicFunction: "
+ };
+
+ // See if this is a message we want to ignore in user output
+ for ( int i = 0; i < ARRAYSIZE(pszIgnoredMessages); ++i )
+ {
+ if ( V_strncmp( pszMessage, pszIgnoredMessages[ i ], V_strlen( pszIgnoredMessages[ i ] ) ) == 0 )
+ return;
+ }
+
+ switch ( nLogLevel )
+ {
+ case kItemtest_Log_Info:
+ ::Msg( "%s", pszMessage );
+ break;
+ case kItemtest_Log_Warning:
+ ::Warning( "%s", pszMessage );
+ break;
+ case kItemtest_Log_Error:
+ ::Error( "%s", pszMessage );
+ break;
+ }
+ m_log.PutString( pszMessage );
+ }
+
+ const char *Get() { return (char*)m_log.Base(); }
+
+protected:
+ mutable CUtlBuffer m_log;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Import text edit dialog
+//-----------------------------------------------------------------------------
+class CTFFileImportTextEditDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CTFFileImportTextEditDialog, Frame );
+
+public:
+ CTFFileImportTextEditDialog( vgui::Panel *parent, const char *pszTitle, const char *pszCommand = NULL );
+
+ virtual ~CTFFileImportTextEditDialog() { }
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void OnCommand( const char *command );
+
+ void HideCancelButton() { m_bShowCancelButton = false; }
+
+ void SetText( const char *pszText );
+ bool GetText( char *pszText, int nMaxSize );
+
+protected:
+ bool m_bShowCancelButton;
+ CUtlString m_sTitle;
+ CUtlString m_sText;
+ CUtlString m_sCommand;
+ vgui::TextEntry *m_pTextEntry;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportTextEditDialog::CTFFileImportTextEditDialog( vgui::Panel *parent, const char *pszTitle, const char *pszCommand ) : Frame( parent, "ImportFileTextEditDialog" )
+, m_bShowCancelButton( true )
+, m_sTitle( pszTitle )
+, m_sText()
+, m_sCommand( pszCommand )
+, m_pTextEntry( NULL )
+{
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme(scheme);
+ SetProportional( true );
+ SetDeleteSelfOnClose( true );
+ SetTitle( "", false );
+ SetSizeable( false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportTextEditDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "Resource/UI/ImportFileTextEditDialog.res" );
+
+ vgui::Label *pLabel = FindControl<vgui::Label>( "TitleLabel" );
+ if ( pLabel )
+ {
+ pLabel->SetText( m_sTitle );
+ }
+
+ m_pTextEntry = FindControl<vgui::TextEntry>( "TextEntry" );
+ if ( m_pTextEntry )
+ {
+ m_pTextEntry->RequestFocus();
+ m_pTextEntry->SetMultiline( true );
+ m_pTextEntry->SetCatchEnterKey( true );
+ m_pTextEntry->SetVerticalScrollbar( true );
+ m_pTextEntry->SetText( m_sText );
+ }
+
+ if ( !m_bShowCancelButton )
+ {
+ vgui::Panel *pButton = FindControl<vgui::Panel>( "ButtonClose" );
+ if ( pButton )
+ {
+ pButton->SetVisible( false );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportTextEditDialog::OnCommand( const char *command )
+{
+ if ( V_stricmp( command, "Done" ) == 0 )
+ {
+ if ( m_sCommand.IsEmpty() )
+ {
+ OnCommand( "Close" );
+ }
+ else
+ {
+ GetParent()->OnCommand( m_sCommand );
+ }
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportTextEditDialog::SetText( const char *pszText )
+{
+ m_sText = pszText;
+
+ if ( m_pTextEntry )
+ {
+ m_pTextEntry->SetText( pszText );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportTextEditDialog::GetText( char *pszText, int nMaxSize )
+{
+ if ( m_pTextEntry )
+ {
+ m_pTextEntry->GetText( pszText, nMaxSize );
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Import material edit dialog
+//-----------------------------------------------------------------------------
+class CTFImportMaterialEditDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CTFImportMaterialEditDialog, Frame );
+
+public:
+ CTFImportMaterialEditDialog( vgui::Panel *parent, int nSkinIndex, int nMaterialIndex, KeyValues *pItemValues );
+
+ virtual ~CTFImportMaterialEditDialog() { }
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void OnCommand( const char *command );
+
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+ MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
+
+ void SetVMTKeyValues( const char *pszRedVMTText, const char *pszBlueVMTText );
+ KeyValues* GetVMTKeyValues( int nSkinIndex );
+
+ const char* GetBaseTextureFile( int nSkinIndex ) const { return m_strBaseTextureFile[nSkinIndex].String(); }
+ const char* GetNormalTextureFile() const { return m_strNormalTextureFile.String(); }
+ const char* GetPhongExponentTextureFile() const { return m_strPhongExponentTextureFile.String(); }
+ const char* GetSelfIllumTextureFile() const { return m_bSelfIllumEnabled ? m_strSelfIllumTextureFile.String() : ""; }
+
+private:
+ void OnCommandEditSkin( int nSkinIndex );
+ void OnCommandBrowseMaterial( MATERIAL_FILE_TYPE fileType );
+
+ void SetColorTintBase( int nSkinIndex, const char* pszColorTintBase );
+ void SetSelfIllumTint( int nSkinIndex, const char* pszSelfIllumTint );
+
+ void UpdateBackgroundTexture( int nSkinIndex );
+
+ void UpdateBaseTexture( const char *pszBaseTextureFile );
+ void UpdateNormalTexture( const char *pszNormalTextureFile );
+ void UpdatePhongExponentTexture( const char* pszPhongExponentTextureFile );
+ void UpdateSelfIllumTexture( const char *pszSelfIllumTextureFile );
+
+ void UpdateBaseMapAlphaPhongMask();
+ void UpdateRimMask();
+ void UpdateRimMaskDisplay( bool bEnable );
+ void UpdateHalfLambert();
+ void UpdateBlendTintByBaseAlpha();
+ void UpdateBlendTintColorOverBaseDisplay( bool bEnable );
+ void UpdateColorTintBase( int nSkinIndex, const char* pszColor );
+ void UpdateAdditive();
+ void UpdateTranslucent();
+ void UpdateAlphaTest();
+ void UpdateAlphaTestDiaplay( bool bEnable );
+ void UpdateSelfIllum();
+ void UpdateSelfIllumDisplay( bool bEnable );
+ void UpdateSelfIllumTint( int nSkinIndex, const char* pszColor );
+ void UpdateEnvmapDisplay( bool bEnable );
+
+ void UpdateUniqueLabels();
+
+ CUtlString m_sCommand;
+ KeyValuesAD m_VMTKeyValues;
+ int m_nSkinIndex;
+ int m_nMaterialIndex;
+ KeyValues *m_pItemValues;
+
+ // unique values per skin
+ CUtlString m_strBaseTextureFile[NUM_IMPORT_MATERIALS_PER_TEAM];
+ CUtlString m_strColorTintBase[NUM_IMPORT_MATERIALS_PER_TEAM];
+ CUtlString m_strSelfIllumTint[NUM_IMPORT_MATERIALS_PER_TEAM];
+
+ CUtlString m_strNormalTextureFile;
+ CUtlString m_strPhongExponentTextureFile;
+ bool m_bSelfIllumEnabled;
+ CUtlString m_strSelfIllumTextureFile;
+ MATERIAL_FILE_TYPE m_browseMaterialFileType;
+
+ vgui::Button *m_pRedTeamButton;
+ vgui::Button *m_pBlueTeamButton;
+
+ vgui::Label *m_pBaseTextureFileLabel;
+ vgui::Label *m_pNormalTextureFileLabel;
+ vgui::Label *m_pPhongExponentTextureFileLabel;
+ vgui::Label *m_pSelfIllumTextureFileLabel;
+
+ // Lighting
+ vgui::ComboBox *m_pLightwarpComboBox;
+ vgui::CheckButton *m_pBaseMapAlphaPhongMaskCheckButton;
+ vgui::TextEntry *m_pPhongExponentTextEntry;
+ vgui::TextEntry *m_pPhongBoostTextEntry;
+ vgui::TextEntry *m_pRimLightExponentTextEntry;
+ vgui::TextEntry *m_pRimLightBoostTextEntry;
+ vgui::CheckButton *m_pRimMaskCheckButton;
+ vgui::CheckButton *m_pHalfLambertCheckButton;
+
+ // Paint
+ vgui::CheckButton *m_pBlendTintByBaseAlphaCheckButton;
+ vgui::TextEntry *m_pBlendTintColorOverBaseTextEntry;
+ vgui::TextEntry *m_pColorTintBaseRedTextEntry;
+ vgui::TextEntry *m_pColorTintBaseGreenTextEntry;
+ vgui::TextEntry *m_pColorTintBaseBlueTextEntry;
+
+ // Translucent
+ vgui::CheckButton *m_pAdditiveCheckButton;
+ vgui::CheckButton *m_pTranslucentCheckButton;
+ vgui::CheckButton *m_pAlphaTestCheckButton;
+
+ // Cube map
+ vgui::ComboBox *m_pEnvmapComboBox;
+ vgui::ComboBox *m_pEnvmapAlphaMaskComboBox;
+ vgui::TextEntry *m_pCubemapTintRedTextEntry;
+ vgui::TextEntry *m_pCubemapTintGreenTextEntry;
+ vgui::TextEntry *m_pCubemapTintBlueTextEntry;
+
+ // Self Illum
+ vgui::CheckButton *m_pSelfIllumCheckButton;
+ vgui::TextEntry *m_pSelfIllumTintRedTextEntry;
+ vgui::TextEntry *m_pSelfIllumTintGreenTextEntry;
+ vgui::TextEntry *m_pSelfIllumTintBlueTextEntry;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFImportMaterialEditDialog::CTFImportMaterialEditDialog( vgui::Panel *parent, int nSkinIndex, int nMaterialIndex, KeyValues* pItemValues ) : Frame( parent, "ImportMaterialEditDialog" )
+ , m_sCommand( CFmtStr( "EditMaterialDone%d,%d", nSkinIndex, nMaterialIndex ) )
+ , m_VMTKeyValues( "VMT" )
+ , m_nSkinIndex( nSkinIndex )
+ , m_nMaterialIndex( nMaterialIndex )
+ , m_pItemValues( pItemValues )
+{
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme(scheme);
+ SetProportional( true );
+ SetDeleteSelfOnClose( true );
+ SetTitle( "", false );
+ SetSizeable( false );
+
+ bool bSetSharedTextures = false;
+ for ( int nSkin=0; nSkin<CItemUpload::Manifest()->GetNumMaterialSkins(); ++nSkin )
+ {
+ KeyValues* pMaterialKey = m_pItemValues->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkin ), m_nMaterialIndex ) );
+ if ( pMaterialKey )
+ {
+ m_strBaseTextureFile[nSkin] = pMaterialKey->GetString( CFmtStr( "%s_texture_file", s_pszMaterialFilePrefixes[MATERIAL_FILE_BASETEXTURE] ) );
+ if ( !bSetSharedTextures )
+ {
+ m_strNormalTextureFile = pMaterialKey->GetString( CFmtStr( "%s_texture_file", s_pszMaterialFilePrefixes[MATERIAL_FILE_NORMAL] ) );
+ m_strPhongExponentTextureFile = pMaterialKey->GetString( CFmtStr( "%s_texture_file", s_pszMaterialFilePrefixes[MATERIAL_FILE_PHONGEXPONENT] ) );
+ m_strSelfIllumTextureFile = pMaterialKey->GetString( CFmtStr( "%s_texture_file", s_pszMaterialFilePrefixes[MATERIAL_FILE_SELFILLUM] ) );
+ bSetSharedTextures = true;
+ }
+ }
+ }
+
+ m_pRedTeamButton = NULL;
+ m_pBlueTeamButton = NULL;
+ m_pBaseTextureFileLabel = NULL;
+ m_pNormalTextureFileLabel = NULL;
+ m_pPhongExponentTextureFileLabel = NULL;
+ m_bSelfIllumEnabled = false;
+ m_pSelfIllumTextureFileLabel = NULL;
+ m_pLightwarpComboBox = NULL;
+ m_pBaseMapAlphaPhongMaskCheckButton = NULL;
+ m_pPhongExponentTextEntry = NULL;
+ m_pPhongBoostTextEntry = NULL;
+ m_pRimLightExponentTextEntry = NULL;
+ m_pRimLightBoostTextEntry = NULL;
+ m_pRimMaskCheckButton = NULL;
+ m_pHalfLambertCheckButton = NULL;
+ m_pBlendTintByBaseAlphaCheckButton = NULL;
+ m_pBlendTintColorOverBaseTextEntry = NULL;
+ m_pColorTintBaseRedTextEntry = NULL;
+ m_pColorTintBaseGreenTextEntry = NULL;
+ m_pColorTintBaseBlueTextEntry = NULL;
+ m_pAdditiveCheckButton = NULL;
+ m_pTranslucentCheckButton = NULL;
+ m_pAlphaTestCheckButton = NULL;
+ m_pEnvmapComboBox = NULL;
+ m_pEnvmapAlphaMaskComboBox = NULL;
+ m_pCubemapTintRedTextEntry = NULL;
+ m_pCubemapTintGreenTextEntry = NULL;
+ m_pCubemapTintBlueTextEntry = NULL;
+ m_pSelfIllumCheckButton = NULL;
+ m_pSelfIllumTintRedTextEntry = NULL;
+ m_pSelfIllumTintGreenTextEntry = NULL;
+ m_pSelfIllumTintBlueTextEntry = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "Resource/UI/ImportMaterialEditDialog.res" );
+
+ bool bAllTeam = m_pItemValues->GetInt( kSkinType ) == 0;
+
+ if ( !bAllTeam )
+ {
+ m_pRedTeamButton = FindControl<vgui::Button>( "RedTeamButton" );
+ if ( m_pRedTeamButton )
+ {
+ m_pRedTeamButton->SetVisible( true );
+ m_pRedTeamButton->SetEnabled( true );
+ }
+
+ m_pBlueTeamButton = FindControl<vgui::Button>( "BlueTeamButton" );
+ if ( m_pBlueTeamButton )
+ {
+ m_pBlueTeamButton->SetVisible( true );
+ m_pBlueTeamButton->SetEnabled( true );
+ }
+
+ UpdateUniqueLabels();
+ }
+
+ UpdateBackgroundTexture( m_nSkinIndex );
+
+ vgui::Label *pTitleLabel = FindControl<vgui::Label>( "TitleLabel" );
+ if ( pTitleLabel )
+ {
+ pTitleLabel->SetText( CFmtStr( "#TF_ImportFile_EditVMT%d", m_nMaterialIndex ) );
+ }
+
+ m_pBaseTextureFileLabel = FindControl<vgui::Label>( "BaseTextureFileLabel" );
+ m_pNormalTextureFileLabel = FindControl< vgui::Label >( "NormalTextureFileLabel" );
+ m_pPhongExponentTextureFileLabel = FindControl< vgui::Label >( "PhongExponentTextureFileLabel" );
+ m_pSelfIllumTextureFileLabel = FindControl< vgui::Label >( "SelfIllumTextureFileLabel" );
+
+ m_pLightwarpComboBox = FindControl<vgui::ComboBox>( "LightwarpComboBox" );
+ if ( m_pLightwarpComboBox )
+ {
+ m_pLightwarpComboBox->RemoveAll();
+
+ KeyValuesAD pKeyValues( "data" );
+ for ( int i=0; i<ARRAYSIZE( kLightwarps ); ++i )
+ {
+ pKeyValues->SetString( kLightwarpPath, kLightwarps[i].pszPath );
+ m_pLightwarpComboBox->AddItem( kLightwarps[i].pszName, pKeyValues );
+ }
+
+ m_pLightwarpComboBox->AddActionSignalTarget( this );
+ }
+
+ // Lighting
+ m_pBaseMapAlphaPhongMaskCheckButton = FindControl<vgui::CheckButton>( "BaseMapAlphaPhongMaskCheckButton" );
+ if ( m_pBaseMapAlphaPhongMaskCheckButton )
+ {
+ m_pBaseMapAlphaPhongMaskCheckButton->AddActionSignalTarget( this );
+ }
+
+ m_pPhongExponentTextEntry = FindControl<vgui::TextEntry>( "PhongExponentTextEntry" );
+ if ( m_pPhongExponentTextEntry )
+ {
+ m_pPhongExponentTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pPhongBoostTextEntry = FindControl<vgui::TextEntry>( "PhongBoostTextEntry" );
+ if ( m_pPhongBoostTextEntry )
+ {
+ m_pPhongBoostTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pRimLightExponentTextEntry = FindControl<vgui::TextEntry>( "RimLightExponentTextEntry" );
+ if ( m_pRimLightExponentTextEntry )
+ {
+ m_pRimLightExponentTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pRimLightBoostTextEntry = FindControl<vgui::TextEntry>( "RimLightBoostTextEntry" );
+ if ( m_pRimLightBoostTextEntry )
+ {
+ m_pRimLightBoostTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pRimMaskCheckButton = FindControl<vgui::CheckButton>( "RimMaskCheckButton" );
+ if ( m_pRimMaskCheckButton )
+ {
+ m_pRimMaskCheckButton->AddActionSignalTarget( this );
+ m_pRimMaskCheckButton->SetCheckButtonCheckable( false );
+ }
+
+ m_pHalfLambertCheckButton = FindControl<vgui::CheckButton>( "HalfLambertCheckButton" );
+ if ( m_pHalfLambertCheckButton )
+ {
+ m_pHalfLambertCheckButton->AddActionSignalTarget( this );
+ }
+
+ // Paint
+ m_pBlendTintByBaseAlphaCheckButton = FindControl<vgui::CheckButton>( "BlendTintByBaseAlphaCheckButton" );
+ if ( m_pBlendTintByBaseAlphaCheckButton )
+ {
+ m_pBlendTintByBaseAlphaCheckButton->AddActionSignalTarget( this );
+ m_pBlendTintByBaseAlphaCheckButton->SetCheckButtonCheckable( false );
+ }
+
+ m_pBlendTintColorOverBaseTextEntry = FindControl<vgui::TextEntry>( "BlendTintColorOverBaseTextEntry" );
+ if ( m_pBlendTintColorOverBaseTextEntry )
+ {
+ m_pBlendTintColorOverBaseTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pColorTintBaseRedTextEntry = FindControl<vgui::TextEntry>( "ColorTintBaseRedTextEntry" );
+ if ( m_pColorTintBaseRedTextEntry )
+ {
+ m_pColorTintBaseRedTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pColorTintBaseGreenTextEntry = FindControl<vgui::TextEntry>( "ColorTintBaseGreenTextEntry" );
+ if ( m_pColorTintBaseGreenTextEntry )
+ {
+ m_pColorTintBaseGreenTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pColorTintBaseBlueTextEntry = FindControl<vgui::TextEntry>( "ColorTintBaseBlueTextEntry" );
+ if ( m_pColorTintBaseBlueTextEntry )
+ {
+ m_pColorTintBaseBlueTextEntry->AddActionSignalTarget( this );
+ }
+
+ // enable paintable UI
+ const char* pszPaintable = CFmtStr( kItemPaintable, m_nMaterialIndex );
+ if ( m_pItemValues->GetBool( pszPaintable ) )
+ {
+ m_pBlendTintByBaseAlphaCheckButton->SetEnabled( true );
+ m_pBlendTintByBaseAlphaCheckButton->SetCheckButtonCheckable( true );
+ m_pBlendTintColorOverBaseTextEntry->SetEnabled( true );
+ m_pColorTintBaseRedTextEntry->SetEnabled( true );
+ m_pColorTintBaseGreenTextEntry->SetEnabled( true );
+ m_pColorTintBaseBlueTextEntry->SetEnabled( true );
+
+ static const char* paintableUILabels[] =
+ {
+ "BlendTintByBaseAlphaLabel",
+ "BlendTintColorOverBaseLabel",
+ "ColorTintBaseLabel"
+ };
+
+ for ( int i=0; i<ARRAYSIZE( paintableUILabels ); ++i )
+ {
+ vgui::Label *pLabel = FindControl<vgui::Label>( paintableUILabels[i] );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( true );
+ }
+ }
+ }
+
+ // Translucent
+ m_pAdditiveCheckButton = FindControl<vgui::CheckButton>( "AdditiveCheckButton" );
+ if ( m_pAdditiveCheckButton )
+ {
+ m_pAdditiveCheckButton->AddActionSignalTarget( this );
+ }
+ m_pTranslucentCheckButton = FindControl<vgui::CheckButton>( "TranslucentCheckButton" );
+ if ( m_pTranslucentCheckButton )
+ {
+ m_pTranslucentCheckButton->AddActionSignalTarget( this );
+ }
+ m_pAlphaTestCheckButton = FindControl<vgui::CheckButton>( "AlphaTestCheckButton" );
+ if ( m_pAlphaTestCheckButton )
+ {
+ m_pAlphaTestCheckButton->AddActionSignalTarget( this );
+ m_pAlphaTestCheckButton->SetCheckButtonCheckable( false );
+ }
+
+ // Cube map
+ m_pEnvmapComboBox = FindControl<vgui::ComboBox>( "EnvmapComboBox" );
+ if ( m_pEnvmapComboBox )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ for ( int i=0; i<ARRAYSIZE(kEnvmaps); ++i )
+ {
+ pKeyValues->SetString( kEnvmapPath, kEnvmaps[i].pszPath );
+ m_pEnvmapComboBox->AddItem( kEnvmaps[i].pszName, pKeyValues );
+ }
+
+ m_pEnvmapComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pEnvmapAlphaMaskComboBox = FindControl<vgui::ComboBox>( "EnvmapAlphaMaskComboBox" );
+ if ( m_pEnvmapAlphaMaskComboBox )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ for ( int i=0; i<ARRAYSIZE(kEnvmapMasks); ++i )
+ {
+ pKeyValues->SetString( kEnvmapMaskVarName, kEnvmapMasks[i].pszVarName );
+ m_pEnvmapAlphaMaskComboBox->AddItem( kEnvmapMasks[i].pszDisplayName, pKeyValues );
+ }
+
+ m_pEnvmapAlphaMaskComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pCubemapTintRedTextEntry = FindControl<vgui::TextEntry>( "CubemapTintRedTextEntry" );
+ if ( m_pCubemapTintRedTextEntry )
+ {
+ m_pCubemapTintRedTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pCubemapTintGreenTextEntry = FindControl<vgui::TextEntry>( "CubemapTintGreenTextEntry" );
+ if ( m_pCubemapTintGreenTextEntry )
+ {
+ m_pCubemapTintGreenTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pCubemapTintBlueTextEntry = FindControl<vgui::TextEntry>( "CubemapTintBlueTextEntry" );
+ if ( m_pCubemapTintBlueTextEntry )
+ {
+ m_pCubemapTintBlueTextEntry->AddActionSignalTarget( this );
+ }
+
+ // Self Illum
+ m_pSelfIllumCheckButton = FindControl<vgui::CheckButton>( "SelfIllumCheckButton" );
+ if ( m_pSelfIllumCheckButton )
+ {
+ m_pSelfIllumCheckButton->AddActionSignalTarget( this );
+ }
+
+ m_pSelfIllumTintRedTextEntry = FindControl<vgui::TextEntry>( "SelfIllumTintRedTextEntry" );
+ if ( m_pSelfIllumTintRedTextEntry )
+ {
+ m_pSelfIllumTintRedTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pSelfIllumTintGreenTextEntry = FindControl<vgui::TextEntry>( "SelfIllumTintGreenTextEntry" );
+ if ( m_pSelfIllumTintGreenTextEntry )
+ {
+ m_pSelfIllumTintGreenTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pSelfIllumTintBlueTextEntry = FindControl<vgui::TextEntry>( "SelfIllumTintBlueTextEntry" );
+ if ( m_pSelfIllumTintBlueTextEntry )
+ {
+ m_pSelfIllumTintBlueTextEntry->AddActionSignalTarget( this );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::OnCommand( const char *command )
+{
+ if ( FStrEq( command, "Done" ) )
+ {
+ if ( m_sCommand.IsEmpty() )
+ {
+ OnCommand( "Close" );
+ }
+ else if ( GetParent() )
+ {
+ GetParent()->OnCommand( m_sCommand );
+ }
+ }
+ else if ( V_strncasecmp( command, "EditSkin", V_strlen( "EditSkin" ) ) == 0 )
+ {
+ int index = V_atoi( &command[ V_strlen( "EditSkin" ) ] );
+ OnCommandEditSkin( index );
+ }
+ else if ( FStrEq( command, "BrowseMaterial" ) )
+ {
+ OnCommandBrowseMaterial( MATERIAL_FILE_BASETEXTURE );
+ }
+ else if ( FStrEq( command, "BrowseNormalTexture" ) )
+ {
+ OnCommandBrowseMaterial( MATERIAL_FILE_NORMAL );
+ }
+ else if ( FStrEq( command, "BrowsePhongExponentTexture" ) )
+ {
+ OnCommandBrowseMaterial( MATERIAL_FILE_PHONGEXPONENT );
+ }
+ else if ( FStrEq( command, "BrowseSelfIllumTexture" ) )
+ {
+ OnCommandBrowseMaterial( MATERIAL_FILE_SELFILLUM );
+ }
+ else if ( FStrEq( command, "ClearNormalTexture" ) )
+ {
+ UpdateNormalTexture( "" );
+ }
+ else if ( FStrEq( command, "ClearPhongExponentTexture" ) )
+ {
+ UpdatePhongExponentTexture( "" );
+ }
+ else if ( FStrEq( command, "ClearSelfIllumTexture" ) )
+ {
+ UpdateSelfIllumTexture( "" );
+ }
+ else if ( FStrEq( command, "UpdateBaseMapAlphaPhongMask" ) )
+ {
+ UpdateBaseMapAlphaPhongMask();
+ }
+ else if ( FStrEq( command, "UpdateRimMask" ) )
+ {
+ UpdateRimMask();
+ }
+ else if ( FStrEq( command, "UpdateHalfLambert" ) )
+ {
+ UpdateHalfLambert();
+ }
+ else if ( FStrEq( command, "UpdateBlendTintByBaseAlpha" ) )
+ {
+ UpdateBlendTintByBaseAlpha();
+ }
+ else if ( FStrEq( command, "UpdateAdditive" ) )
+ {
+ UpdateAdditive();
+ }
+ else if ( FStrEq( command, "UpdateTranslucent" ) )
+ {
+ UpdateTranslucent();
+ }
+ else if ( FStrEq( command, "UpdateAlphaTest" ) )
+ {
+ UpdateAlphaTest();
+ }
+ else if ( FStrEq( command, "UpdateSelfIllum" ) )
+ {
+ UpdateSelfIllum();
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::OnCommandEditSkin( int nSkinIndex )
+{
+ if ( m_nSkinIndex == nSkinIndex )
+ {
+ return;
+ }
+
+ m_nSkinIndex = nSkinIndex;
+
+ UpdateBackgroundTexture( m_nSkinIndex );
+ UpdateBaseTexture( m_strBaseTextureFile[m_nSkinIndex] );
+ UpdateColorTintBase( m_nSkinIndex, m_strColorTintBase[m_nSkinIndex] );
+ UpdateSelfIllumTint( m_nSkinIndex, m_strSelfIllumTint[m_nSkinIndex] );
+ UpdateUniqueLabels();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::OnCommandBrowseMaterial( MATERIAL_FILE_TYPE fileType )
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectMaterial", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.tga,*.psd", "#TF_ImportFile_MaterialFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ const char *pszStartPath = tf_steam_workshop_import_material_path.GetString();
+ if ( pszStartPath && *pszStartPath )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+
+ m_browseMaterialFileType = fileType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CheckImageSize( const char *fullpath, MATERIAL_FILE_TYPE materialFileType, KeyValues *pMessageVariables )
+{
+ CAssetTF asset;
+ CTargetTGA image( &asset, NULL );
+ if ( !image.SetInputFile( fullpath ) )
+ {
+ SetMessageFileVariable( pMessageVariables, image.GetInputFile() );
+ return CTFFileImportDialog::BUILD_FAILED_IMAGEUNSUPPORTEDFILETYPE;
+ }
+
+ int nLimitWidth = 0;
+ int nLimitHeight = 0;
+
+ switch ( materialFileType )
+ {
+ case MATERIAL_FILE_BASETEXTURE:
+ {
+ // 512x512
+ nLimitWidth = nLimitHeight = 512;
+ }
+ break;
+ case MATERIAL_FILE_NORMAL:
+ {
+ // 512x512
+ nLimitWidth = nLimitHeight = 512;
+ }
+ break;
+ case MATERIAL_FILE_PHONGEXPONENT:
+ {
+ // 256x256
+ nLimitWidth = nLimitHeight = 256;
+ }
+ break;
+ case MATERIAL_FILE_SELFILLUM:
+ {
+ // 256x256
+ nLimitWidth = nLimitHeight = 256;
+ }
+ break;
+ }
+
+ if ( image.GetWidth() > nLimitWidth || image.GetHeight() > nLimitHeight )
+ {
+ SetMessageFileVariable( pMessageVariables, image.GetInputFile() );
+ pMessageVariables->SetInt( "width", nLimitWidth );
+ pMessageVariables->SetInt( "height", nLimitHeight );
+ return CTFFileImportDialog::BUILD_FAILED_IMAGERESOLUTIONOVERLIMIT;
+ }
+
+ if ( !IsPowerOfTwo( image.GetWidth() ) || !IsPowerOfTwo( image.GetHeight() ) )
+ {
+ SetMessageFileVariable( pMessageVariables, image.GetInputFile() );
+ return CTFFileImportDialog::BUILD_FAILED_IMAGERESOLUTIONNOTPOWEROF2;
+ }
+
+ return CTFFileImportDialog::BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::OnFileSelected( char const *fullpath )
+{
+ SaveBrowsePath( tf_steam_workshop_import_material_path, fullpath );
+
+ KeyValuesAD pKV("data");
+ CTFFileImportDialog::BUILD_RESULT result = CheckImageSize( fullpath, m_browseMaterialFileType, pKV );
+ if ( result != CTFFileImportDialog::BUILD_OKAY )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", kBuildResultMessages[result], pKV );
+ return;
+ }
+
+ switch ( m_browseMaterialFileType )
+ {
+ case MATERIAL_FILE_BASETEXTURE:
+ {
+ UpdateBaseTexture( fullpath );
+ }
+ break;
+ case MATERIAL_FILE_NORMAL:
+ {
+ UpdateNormalTexture( fullpath );
+ }
+ break;
+ case MATERIAL_FILE_PHONGEXPONENT:
+ {
+ UpdatePhongExponentTexture( fullpath );
+ }
+ break;
+ case MATERIAL_FILE_SELFILLUM:
+ {
+ UpdateSelfIllumTexture( fullpath );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr( "panel" ) );
+
+ vgui::ComboBox *pComboBox = dynamic_cast< vgui::ComboBox * >( pPanel );
+ if ( pComboBox )
+ {
+ if( pComboBox == m_pLightwarpComboBox )
+ {
+ KeyValues *pData = m_pLightwarpComboBox->GetActiveItemUserData();
+ m_VMTKeyValues->SetString( "$lightwarptexture", pData->GetString( kLightwarpPath ) );
+ }
+ else if ( pComboBox == m_pEnvmapComboBox )
+ {
+ KeyValues *pData = m_pEnvmapComboBox->GetActiveItemUserData();
+ const char* pszEnvmapPath = pData->GetString( kEnvmapPath );
+ m_VMTKeyValues->SetString( "$envmap", pszEnvmapPath );
+ UpdateEnvmapDisplay( !FStrEq( pszEnvmapPath, "" ) );
+ }
+ else if ( pComboBox == m_pEnvmapAlphaMaskComboBox )
+ {
+ KeyValues *pData = m_pEnvmapAlphaMaskComboBox->GetActiveItemUserData();
+ const char* pszEnvmapMaskVarName = pData->GetString( kEnvmapMaskVarName );
+ for ( int i=0; i<ARRAYSIZE(kEnvmapMasks); ++i )
+ {
+ const char* pszCurrentVarName = kEnvmapMasks[i].pszVarName;
+ if ( FStrEq( pszCurrentVarName, "" ) )
+ {
+ continue;
+ }
+
+ bool bSet = FStrEq( pszCurrentVarName, pszEnvmapMaskVarName );
+ m_VMTKeyValues->SetBool( pszCurrentVarName, bSet );
+ }
+
+ // pop a warning
+ static bool bWarned = false;
+ if ( FStrEq( pszEnvmapMaskVarName, "$basealphaenvmapmask" ) )
+ {
+ bool bHasNormal = !m_strNormalTextureFile.IsEmpty();
+ if ( !bWarned && bHasNormal )
+ {
+ bWarned = true;
+ ShowMessageBox( "#TF_ImportFile_Warning", kWarningMessages[ CTFFileImportDialog::WARNING_BASEALPHAMASK ] );
+ }
+ }
+ else
+ {
+ bWarned = false;
+ }
+ }
+
+ return;
+ }
+
+ vgui::TextEntry *pTextEntry = dynamic_cast< vgui::TextEntry * >( pPanel );
+ if ( pTextEntry )
+ {
+ if ( pTextEntry == m_pPhongExponentTextEntry )
+ {
+ m_VMTKeyValues->SetFloat( "$phongexponent", m_pPhongExponentTextEntry->GetValueAsFloat() );
+ }
+ else if ( pTextEntry == m_pPhongBoostTextEntry )
+ {
+ m_VMTKeyValues->SetFloat( "$phongboost", m_pPhongBoostTextEntry->GetValueAsFloat() );
+ }
+ else if ( pTextEntry == m_pRimLightExponentTextEntry )
+ {
+ m_VMTKeyValues->SetFloat( "$rimlightexponent", m_pRimLightExponentTextEntry->GetValueAsFloat() );
+ }
+ else if ( pTextEntry == m_pRimLightBoostTextEntry )
+ {
+ m_VMTKeyValues->SetFloat( "$rimlightboost", m_pRimLightBoostTextEntry->GetValueAsFloat() );
+ }
+ else if ( pTextEntry == m_pBlendTintColorOverBaseTextEntry )
+ {
+ float flClampedValue = clamp( m_pBlendTintColorOverBaseTextEntry->GetValueAsFloat(), 0.f, 1.f );
+ m_VMTKeyValues->SetFloat( "$blendtintcoloroverbase", flClampedValue );
+ }
+ else if ( pTextEntry == m_pColorTintBaseRedTextEntry ||
+ pTextEntry == m_pColorTintBaseGreenTextEntry ||
+ pTextEntry == m_pColorTintBaseBlueTextEntry )
+ {
+ const char* pszColor = CFmtStr( "{ %d %d %d }",
+ clamp( m_pColorTintBaseRedTextEntry->GetValueAsInt(), 0, 255 ),
+ clamp( m_pColorTintBaseGreenTextEntry->GetValueAsInt(), 0, 255 ),
+ clamp( m_pColorTintBaseBlueTextEntry->GetValueAsInt(), 0, 255 ) );
+ SetColorTintBase( m_nSkinIndex, pszColor );
+ }
+ else if ( pTextEntry == m_pCubemapTintRedTextEntry ||
+ pTextEntry == m_pCubemapTintGreenTextEntry ||
+ pTextEntry == m_pCubemapTintBlueTextEntry )
+ {
+ float flR = (float)clamp( m_pCubemapTintRedTextEntry->GetValueAsInt(), 0, 255 ) / 255.f;
+ float flG = (float)clamp( m_pCubemapTintGreenTextEntry->GetValueAsInt(), 0, 255 ) / 255.f;
+ float flB = (float)clamp( m_pCubemapTintBlueTextEntry->GetValueAsInt(), 0, 255 ) / 255.f;
+ const char* pszColor = CFmtStr( "[%f %f %f]", flR, flG, flB );
+ m_VMTKeyValues->SetString( "$envmaptint", pszColor );
+ }
+ else if ( pTextEntry == m_pSelfIllumTintRedTextEntry ||
+ pTextEntry == m_pSelfIllumTintGreenTextEntry ||
+ pTextEntry == m_pSelfIllumTintBlueTextEntry )
+ {
+ float flR = m_pSelfIllumTintRedTextEntry->GetValueAsFloat() / 255.f;
+ float flG = m_pSelfIllumTintGreenTextEntry->GetValueAsFloat() / 255.f;
+ float flB = m_pSelfIllumTintBlueTextEntry->GetValueAsFloat() / 255.f;
+ const char* pszColor = CFmtStr( "[%f %f %f]", flR, flG, flB );
+ SetSelfIllumTint( m_nSkinIndex, pszColor );
+ }
+
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::SetColorTintBase( int nSkinIndex, const char* pszColorTintBase )
+{
+ m_VMTKeyValues->SetString( "$colortint_base", pszColorTintBase );
+ m_VMTKeyValues->SetString( "$color2", pszColorTintBase );
+
+ m_strColorTintBase[nSkinIndex] = pszColorTintBase;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::SetSelfIllumTint( int nSkinIndex, const char* pszSelfIllumTint )
+{
+ m_VMTKeyValues->SetString( "$selfillumtint", pszSelfIllumTint );
+
+ m_strSelfIllumTint[nSkinIndex] = pszSelfIllumTint;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::SetVMTKeyValues( const char *pszRedVMTText, const char *pszBlueVMTText )
+{
+ m_VMTKeyValues->Clear();
+
+ // read the unique data
+ const char* pszVMTs[NUM_IMPORT_MATERIALS_PER_TEAM] = { pszRedVMTText, pszBlueVMTText };
+ for ( int i=0; i<ARRAYSIZE( pszVMTs ); ++i )
+ {
+ KeyValuesAD pKV( "VMT" );
+ if ( pKV->LoadFromBuffer( "VMT", pszVMTs[i] ) )
+ {
+ m_strColorTintBase[i] = pKV->GetString( "$colortint_base" );
+ m_strSelfIllumTint[i] = pKV->GetString( "$selfillumtint" );
+ }
+ }
+
+ const char* pszVMTText = m_nSkinIndex == 0 ? pszRedVMTText : pszBlueVMTText;
+ if ( m_VMTKeyValues->LoadFromBuffer( "VMT", pszVMTText ) )
+ {
+ // update the UI here
+ UpdateBaseTexture( m_strBaseTextureFile[m_nSkinIndex].String() );
+ UpdateNormalTexture( m_strNormalTextureFile.String() );
+ UpdatePhongExponentTexture( m_strPhongExponentTextureFile.String() );
+ UpdateSelfIllumTexture( m_strSelfIllumTextureFile.String() );
+
+ if ( m_pLightwarpComboBox )
+ {
+ const char* pszCurrentLightwarpPath = m_VMTKeyValues->GetString( "$lightwarptexture" );
+ for ( int i=0; i<ARRAYSIZE(kLightwarps); ++i )
+ {
+ if ( FStrEq( pszCurrentLightwarpPath, kLightwarps[i].pszPath ) )
+ {
+ m_pLightwarpComboBox->ActivateItemByRow( i );
+ break;
+ }
+ }
+ }
+
+ const char* pszFloatFormat = "%.3f";
+
+ // Lighting
+ if ( m_pBaseMapAlphaPhongMaskCheckButton )
+ {
+ m_pBaseMapAlphaPhongMaskCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$basemapalphaphongmask" ) );
+ }
+ if ( m_pPhongExponentTextEntry )
+ {
+ m_pPhongExponentTextEntry->SetText( CFmtStr( pszFloatFormat, m_VMTKeyValues->GetFloat( "$phongexponent" ) ) );
+ }
+ if ( m_pPhongBoostTextEntry )
+ {
+ m_pPhongBoostTextEntry->SetText( CFmtStr( pszFloatFormat, m_VMTKeyValues->GetFloat( "$phongboost" ) ) );
+ }
+ if ( m_pRimLightExponentTextEntry )
+ {
+ m_pRimLightExponentTextEntry->SetText( CFmtStr( pszFloatFormat, m_VMTKeyValues->GetFloat( "$rimlightexponent" ) ) );
+ }
+ if ( m_pRimLightBoostTextEntry )
+ {
+ m_pRimLightBoostTextEntry->SetText( CFmtStr( pszFloatFormat, m_VMTKeyValues->GetFloat( "$rimlightboost" ) ) );
+ }
+ if ( m_pRimMaskCheckButton )
+ {
+ UpdateRimMaskDisplay( !m_strPhongExponentTextureFile.IsEmpty() );
+ m_pRimMaskCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$rimmask" ) );
+ }
+ if ( m_pHalfLambertCheckButton )
+ {
+ m_pHalfLambertCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$halflambert" ) );
+ }
+
+ // Paint
+ if ( m_pBlendTintByBaseAlphaCheckButton )
+ {
+ m_pBlendTintByBaseAlphaCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$blendtintbybasealpha" ) );
+ }
+ if ( m_pBlendTintColorOverBaseTextEntry )
+ {
+ m_pBlendTintColorOverBaseTextEntry->SetText( CFmtStr( pszFloatFormat, m_VMTKeyValues->GetFloat( "$blendtintcoloroverbase" ) ) );
+ }
+
+ UpdateColorTintBase( m_nSkinIndex, m_VMTKeyValues->GetString( "$colortint_base" ) );
+
+ // Translucent
+ if ( m_pAdditiveCheckButton )
+ {
+ m_pAdditiveCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$additive" ) );
+ }
+ if ( m_pTranslucentCheckButton )
+ {
+ m_pTranslucentCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$translucent" ) );
+ }
+ if ( m_pAlphaTestCheckButton )
+ {
+ bool bTranslucent = m_pTranslucentCheckButton->IsSelected();
+ UpdateAlphaTestDiaplay( bTranslucent );
+ m_pAlphaTestCheckButton->SetSelected( m_VMTKeyValues->GetBool( "$alphatest" ) );
+ }
+
+ // Cube map
+ if ( m_pEnvmapComboBox )
+ {
+ const char *pszEnvmap = m_VMTKeyValues->GetString( "$envmap" );
+ for ( int i=0; i<m_pEnvmapComboBox->GetItemCount(); ++i )
+ {
+ KeyValues *pKey = m_pEnvmapComboBox->GetItemUserData( i );
+ const char *pszPath = pKey->GetString( kEnvmapPath );
+ if ( FStrEq( pszEnvmap, pszPath ) )
+ {
+ m_pEnvmapComboBox->ActivateItemByRow( i );
+ break;
+ }
+ }
+ }
+
+ if ( m_pEnvmapAlphaMaskComboBox )
+ {
+ int nRow = 0;
+ for ( int i=0; i<m_pEnvmapAlphaMaskComboBox->GetItemCount(); ++i )
+ {
+ KeyValues *pKey = m_pEnvmapAlphaMaskComboBox->GetItemUserData( i );
+ const char *pszMask = pKey->GetString( kEnvmapMaskVarName );
+ if ( pKey && m_VMTKeyValues->GetBool( pszMask ) )
+ {
+ nRow = i;
+ break;
+ }
+ }
+ m_pEnvmapAlphaMaskComboBox->ActivateItemByRow( nRow );
+ }
+
+ const char *pszStrippedEnvmapTintColor = m_VMTKeyValues->GetString( "$envmaptint" ) + 1;
+ float entmapTint[3];
+ UTIL_StringToFloatArray( entmapTint, ARRAYSIZE( entmapTint ), pszStrippedEnvmapTintColor );
+ if ( m_pCubemapTintRedTextEntry )
+ {
+ m_pCubemapTintRedTextEntry->SetText( CFmtStr( "%d", int( entmapTint[0] * 255.f ) ) );
+ }
+ if ( m_pCubemapTintGreenTextEntry )
+ {
+ m_pCubemapTintGreenTextEntry->SetText( CFmtStr( "%d", int( entmapTint[1] * 255.f ) ) );
+ }
+ if ( m_pCubemapTintBlueTextEntry )
+ {
+ m_pCubemapTintBlueTextEntry->SetText( CFmtStr( "%d", int( entmapTint[2] * 255.f ) ) );
+ }
+
+ // Self Illum
+ bool bSelfIllum = m_VMTKeyValues->GetBool( ">=DX90/$selfillum" );
+ if ( m_pSelfIllumCheckButton )
+ {
+ m_pSelfIllumCheckButton->SetSelected( bSelfIllum );
+ UpdateSelfIllumDisplay( bSelfIllum );
+ }
+
+ UpdateSelfIllumTint( m_nSkinIndex, m_VMTKeyValues->GetString( "$selfillumtint" ) );
+ }
+ else
+ {
+ Warning( "CTFImportMaterialEditDialog::SetKeyValues failed to LoadFromBuffer\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues* CTFImportMaterialEditDialog::GetVMTKeyValues( int nSkinIndex )
+{
+ //set unique values for the skin index here
+ m_VMTKeyValues->SetString( "$colortint_base", m_strColorTintBase[nSkinIndex] );
+ m_VMTKeyValues->SetString( "$color2", m_strColorTintBase[nSkinIndex] );
+ m_VMTKeyValues->SetString( "$selfillumtint", m_strSelfIllumTint[nSkinIndex] );
+
+ return m_VMTKeyValues;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateBackgroundTexture( int nSkinIndex )
+{
+ static const char* pszBGs[] = { "RedBG", "BlueBG" };
+ for ( int i=0; i<ARRAYSIZE(pszBGs); ++i )
+ {
+ vgui::ImagePanel *pBG = FindControl<vgui::ImagePanel>( pszBGs[i] );
+ if ( pBG )
+ {
+ bool bVisible = nSkinIndex == i;
+ pBG->SetVisible( bVisible );
+ pBG->SetEnabled( bVisible );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateBaseTexture( const char *pszBaseTextureFile )
+{
+ if ( m_pBaseTextureFileLabel && pszBaseTextureFile )
+ {
+ m_pBaseTextureFileLabel->InvalidateLayout( true, true );
+
+ if ( *pszBaseTextureFile )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszBaseTextureFile, file, sizeof(file) );
+ m_pBaseTextureFileLabel->SetText( file );
+ m_pBaseTextureFileLabel->SetFgColor( Color( 255, 255, 255, 255 ) );
+ }
+ else
+ {
+ m_pBaseTextureFileLabel->SetText( "#TF_PublishFile_NoFileSelected" );
+ m_pBaseTextureFileLabel->SetFgColor( Color( 255, 0, 0, 255 ) );
+ }
+
+ m_strBaseTextureFile[m_nSkinIndex] = pszBaseTextureFile;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateNormalTexture( const char *pszNormalTextureFile )
+{
+ if ( m_pNormalTextureFileLabel && pszNormalTextureFile )
+ {
+ if ( *pszNormalTextureFile )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszNormalTextureFile, file, sizeof(file) );
+ m_pNormalTextureFileLabel->SetText( file );
+ }
+ else
+ {
+ m_pNormalTextureFileLabel->SetText( "#TF_PublishFile_Optional" );
+ }
+
+ m_strNormalTextureFile = pszNormalTextureFile;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdatePhongExponentTexture( const char* pszPhongExponentTextureFile )
+{
+ if ( m_pPhongExponentTextureFileLabel && pszPhongExponentTextureFile )
+ {
+ if ( *pszPhongExponentTextureFile )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszPhongExponentTextureFile, file, sizeof(file) );
+ m_pPhongExponentTextureFileLabel->SetText( file );
+ }
+ else
+ {
+ m_pPhongExponentTextureFileLabel->SetText( "#TF_PublishFile_Optional" );
+ }
+
+ m_strPhongExponentTextureFile = pszPhongExponentTextureFile;
+
+ UpdateRimMaskDisplay( !m_strPhongExponentTextureFile.IsEmpty() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateSelfIllumTexture( const char *pszSelfIllumTextureFile )
+{
+ if ( m_pSelfIllumTextureFileLabel && pszSelfIllumTextureFile )
+ {
+ if ( *pszSelfIllumTextureFile )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszSelfIllumTextureFile, file, sizeof(file) );
+ m_pSelfIllumTextureFileLabel->SetText( file );
+ }
+ else
+ {
+ m_pSelfIllumTextureFileLabel->SetText( "#TF_PublishFile_Optional" );
+ }
+
+ m_strSelfIllumTextureFile = pszSelfIllumTextureFile;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateBaseMapAlphaPhongMask()
+{
+ if ( m_pBaseMapAlphaPhongMaskCheckButton )
+ {
+ m_VMTKeyValues->SetBool( "$basemapalphaphongmask", m_pBaseMapAlphaPhongMaskCheckButton->IsSelected() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateRimMask()
+{
+ if ( m_pRimMaskCheckButton )
+ {
+ m_VMTKeyValues->SetBool( "$rimmask", m_pRimMaskCheckButton->IsSelected() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateRimMaskDisplay( bool bEnable )
+{
+ if ( m_pRimMaskCheckButton )
+ {
+ m_pRimMaskCheckButton->SetEnabled( bEnable );
+ m_pRimMaskCheckButton->SetCheckButtonCheckable( bEnable );
+ }
+
+ vgui::Label* pLabel = FindControl< vgui::Label >( "RimMaskLabel" );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( bEnable );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateHalfLambert()
+{
+ if ( m_pHalfLambertCheckButton )
+ {
+ m_VMTKeyValues->SetBool( "$halflambert", m_pHalfLambertCheckButton->IsSelected() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateBlendTintByBaseAlpha()
+{
+ if ( m_pBlendTintByBaseAlphaCheckButton )
+ {
+ bool bSelected = m_pBlendTintByBaseAlphaCheckButton->IsSelected();
+ m_VMTKeyValues->SetBool( "$blendtintbybasealpha", bSelected );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateBlendTintColorOverBaseDisplay( bool bEnable )
+{
+ if ( m_pBlendTintColorOverBaseTextEntry )
+ {
+ if ( !bEnable )
+ {
+ m_VMTKeyValues->SetFloat( "$blendtintcoloroverbase", 0.f );
+ }
+ m_pBlendTintColorOverBaseTextEntry->SetText( CFmtStr( "%.3f", m_VMTKeyValues->GetFloat( "$blendtintcoloroverbase" ) ) );
+ m_pBlendTintColorOverBaseTextEntry->SetEnabled( bEnable );
+
+ vgui::Label *pLabel = FindControl<vgui::Label>( "BlendTintColorOverBaseLabel" );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( bEnable );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateColorTintBase( int nSkinIndex, const char* pszColor )
+{
+ m_strColorTintBase[nSkinIndex] = pszColor;
+
+ int colorTintBase[3];
+ // color string +2 to skip "{ " or "[ "
+ const char *pszStrippedColorTintBase = pszColor + 2;
+ UTIL_StringToIntArray( colorTintBase, 3, pszStrippedColorTintBase );
+ if ( m_pColorTintBaseRedTextEntry )
+ {
+ m_pColorTintBaseRedTextEntry->SetText( CFmtStr( "%d", colorTintBase[0] ) );
+ }
+ if ( m_pColorTintBaseGreenTextEntry )
+ {
+ m_pColorTintBaseGreenTextEntry->SetText( CFmtStr( "%d", colorTintBase[1] ) );
+ }
+ if ( m_pColorTintBaseBlueTextEntry )
+ {
+ m_pColorTintBaseBlueTextEntry->SetText( CFmtStr( "%d", colorTintBase[2] ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateAdditive()
+{
+ if ( m_pAdditiveCheckButton )
+ {
+ m_VMTKeyValues->SetBool( "$additive", m_pAdditiveCheckButton->IsSelected() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateTranslucent()
+{
+ if ( m_pTranslucentCheckButton )
+ {
+ bool bSelected = m_pTranslucentCheckButton->IsSelected();
+ UpdateAlphaTestDiaplay( bSelected );
+ m_VMTKeyValues->SetBool( "$translucent", bSelected );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateAlphaTest()
+{
+ if ( m_pAlphaTestCheckButton )
+ {
+ m_VMTKeyValues->SetBool( "$alphatest", m_pAlphaTestCheckButton->IsSelected() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateAlphaTestDiaplay( bool bEnable )
+{
+ if ( m_pAlphaTestCheckButton )
+ {
+ m_pAlphaTestCheckButton->SetEnabled( bEnable );
+ m_pAlphaTestCheckButton->SetCheckButtonCheckable( bEnable );
+ }
+
+ vgui::Label *pLabel = FindControl<vgui::Label>( "AlphaTestLabel" );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( bEnable );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateSelfIllum()
+{
+ if ( m_pSelfIllumCheckButton )
+ {
+ bool bSelected = m_pSelfIllumCheckButton->IsSelected();
+ m_VMTKeyValues->SetBool( ">=DX90/$selfillum", bSelected );
+ UpdateSelfIllumDisplay( bSelected );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateSelfIllumDisplay( bool bEnable )
+{
+ m_bSelfIllumEnabled = bEnable;
+
+ static const char* pszSelfIllumLabels[] =
+ {
+ "SelfIllumTintLabel",
+ "SelfIllumTextureLabel"
+ };
+
+ for ( int i=0; i<ARRAYSIZE(pszSelfIllumLabels); ++i )
+ {
+ vgui::Label *pLabel = FindControl<vgui::Label>( pszSelfIllumLabels[i] );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( bEnable );
+ }
+ }
+
+ static const char* pszSelfIllumButtons[] =
+ {
+ "SelfIllumTextureBrowse",
+ "SelfIllumTextureClear"
+ };
+
+ for ( int i=0; i<ARRAYSIZE(pszSelfIllumButtons); ++i )
+ {
+ vgui::Button *pButton = FindControl<vgui::Button>( pszSelfIllumButtons[i] );
+ if ( pButton )
+ {
+ pButton->SetEnabled( bEnable );
+ }
+ }
+
+ m_pSelfIllumTextureFileLabel->SetEnabled( bEnable );
+ m_pSelfIllumTintRedTextEntry->SetEnabled( bEnable );
+ m_pSelfIllumTintGreenTextEntry->SetEnabled( bEnable );
+ m_pSelfIllumTintBlueTextEntry->SetEnabled( bEnable );
+
+ // blend tint color over base is not compatible with selfillum. must turn it off
+ UpdateBlendTintColorOverBaseDisplay( !bEnable );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateSelfIllumTint( int nSkinIndex, const char* pszColor )
+{
+ m_strSelfIllumTint[nSkinIndex] = pszColor;
+
+ // +1 to skip [
+ const char *pszStrippedSelfIllumTintColor = pszColor + 1;
+ float selfIllumTint[3];
+ UTIL_StringToFloatArray( selfIllumTint, ARRAYSIZE( selfIllumTint ), pszStrippedSelfIllumTintColor );
+ if ( m_pSelfIllumTintRedTextEntry )
+ {
+ m_pSelfIllumTintRedTextEntry->SetText( CFmtStr( "%d", int( selfIllumTint[0] * 255.f ) ) );
+ }
+ if ( m_pSelfIllumTintGreenTextEntry )
+ {
+ m_pSelfIllumTintGreenTextEntry->SetText( CFmtStr( "%d", int( selfIllumTint[1] * 255.f ) ) );
+ }
+ if ( m_pSelfIllumTintBlueTextEntry )
+ {
+ m_pSelfIllumTintBlueTextEntry->SetText( CFmtStr( "%d", int( selfIllumTint[2] * 255.f ) ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateEnvmapDisplay( bool bEnable )
+{
+ static const char* pszEnvmapLabels[] =
+ {
+ "EnvmapAlphaMaskLabel",
+ "EnvmapTintLabel"
+ };
+
+ for ( int i=0; i<ARRAYSIZE(pszEnvmapLabels); ++i )
+ {
+ vgui::Label *pLabel = FindControl<vgui::Label>( pszEnvmapLabels[i] );
+ if ( pLabel )
+ {
+ pLabel->SetEnabled( bEnable );
+ }
+ }
+
+ m_pEnvmapAlphaMaskComboBox->SetEnabled( bEnable );
+ m_pCubemapTintRedTextEntry->SetEnabled( bEnable );
+ m_pCubemapTintGreenTextEntry->SetEnabled( bEnable );
+ m_pCubemapTintBlueTextEntry->SetEnabled( bEnable );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFImportMaterialEditDialog::UpdateUniqueLabels()
+{
+ static Color color[NUM_IMPORT_MATERIALS_PER_TEAM] = { Color( 189, 59, 59, 255 ), Color( 57, 92, 120, 255 ) };
+ static const char* pszLabelNames[] =
+ {
+ "BaseTextureLabel",
+ "ColorTintBaseLabel",
+ "SelfIllumTintLabel"
+ };
+
+ for ( int i=0; i<ARRAYSIZE( pszLabelNames ); ++i )
+ {
+ vgui::Label* pLabel = FindControl<vgui::Label>( pszLabelNames[i] );
+ if ( pLabel )
+ {
+ pLabel->InvalidateLayout( true, true );
+ pLabel->SetFgColor( color[m_nSkinIndex] );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Preview an item after building it
+//-----------------------------------------------------------------------------
+class CImportPreviewItemPanel : public CTFStorePreviewItemPanel2
+{
+ DECLARE_CLASS_SIMPLE( CImportPreviewItemPanel, CTFStorePreviewItemPanel2 );
+
+public:
+ CImportPreviewItemPanel( vgui::Panel *parent, KeyValues *pItemValues, int nSelectedClass );
+ virtual ~CImportPreviewItemPanel();
+
+ virtual void PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry = NULL ) OVERRIDE;
+
+protected:
+ void ResetHandles();
+
+ virtual void OnThink();
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void OnCommand( const char *command );
+ virtual void OnKeyCodeTyped( KeyCode code );
+ virtual void PerformLayout( void );
+ virtual void OnClose();
+
+ virtual void UpdatePlayerModelButtons();
+ void UpdateActivityUI();
+ void UpdateActivity();
+ void UpdateMaterialButtons();
+
+ virtual bool AllowUnusualPreview() const { return true; }
+
+ int GetSequence( const char *pszGesture = NULL );
+ void StartAction();
+ void StartGesture( const char *pszGesture );
+ void EndGestures();
+
+ bool ClassHasModels( int nClassIndex )
+ {
+ for ( int nModelIndex = 0; nModelIndex < NUM_IMPORT_LODS; ++nModelIndex )
+ {
+ if ( V_stricmp( m_pItemValues->GetString( CFmtStr( kClassLODNFile, kClassFolders[ nClassIndex ], nModelIndex ) ), "" ) != 0 )
+ return true;
+ }
+ return false;
+ }
+
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+ MESSAGE_FUNC_PARAMS( OnClassIconSelected, "ClassIconSelected", data );
+
+ void ResetCamera();
+
+ vgui::ComboBox *m_pLODComboBox;
+ vgui::ComboBox *m_pLoadoutComboBox;
+ vgui::ComboBox *m_pPoseComboBox;
+ vgui::ComboBox *m_pActionComboBox;
+ vgui::ComboBox *m_pEffectComboBox;
+ CUtlVector< vgui::Button* > m_pMaterialButtons;
+
+ KeyValues *m_pItemValues;
+ int m_nCurrentLOD;
+ loadout_positions_t m_nCurrentLoadout;
+ CUtlString m_sCurrentPose;
+ CUtlString m_sCurrentAction;
+ bool m_bIsVCDFileNameOnly;
+ float m_flGestureEndTime;
+ int m_nSelectedClass;
+
+ int m_nCacheModelDetail;
+ bool m_bIsTaunt;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CImportPreviewItemPanel::CImportPreviewItemPanel( vgui::Panel *parent, KeyValues *pItemValues, int nSelectedClass )
+: CTFStorePreviewItemPanel2( parent, "resource/ui/ImportPreviewItemPanel.res", "importpreviewitem", NULL )
+, m_pItemValues( pItemValues )
+, m_nCurrentLOD( 0 )
+, m_nCurrentLoadout( LOADOUT_POSITION_PRIMARY )
+, m_sCurrentPose( kModelPoses[ kDefaultModelPoseIndex ] )
+, m_bIsVCDFileNameOnly( true )
+, m_flGestureEndTime( 0.0f )
+, m_nSelectedClass( nSelectedClass )
+{
+ ResetHandles();
+
+ static ConVarRef r_rootlod( "r_rootlod" );
+ if ( r_rootlod.IsValid() )
+ {
+ m_nCacheModelDetail = r_rootlod.GetInt();
+ r_rootlod.SetValue( 0 );
+ }
+
+ m_bIsTaunt = FStrEq( m_pItemValues->GetString( kItemPrefab ), "taunt" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CImportPreviewItemPanel::~CImportPreviewItemPanel()
+{
+ g_PlayerPreviewEffect.Reset();
+
+ static ConVarRef r_rootlod( "r_rootlod" );
+ if ( r_rootlod.IsValid() )
+ {
+ r_rootlod.SetValue( m_nCacheModelDetail );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry )
+{
+ if ( m_pPlayerModelPanel && m_iState == PS_PLAYER )
+ {
+ // Preserve state when refreshing the preview
+ Vector vecSavedCameraOffset;
+ Vector vecSavedCameraPos;
+ QAngle angSavedCameraDir;
+ m_pPlayerModelPanel->GetCameraOffset( vecSavedCameraOffset );
+ m_pPlayerModelPanel->GetCameraPositionAndAngles( vecSavedCameraPos, angSavedCameraDir );
+
+ int iSavedClass = m_iCurrentClass;
+
+ BaseClass::PreviewItem( iClass, pItem, pEntry );
+
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "class", iSavedClass );
+ OnClassIconSelected( pKeyValues );
+
+ m_pPlayerModelPanel->ResetCameraPivot();
+ m_pPlayerModelPanel->SetCameraOffset( vecSavedCameraOffset );
+ m_pPlayerModelPanel->SetCameraPositionAndAngles( vecSavedCameraPos, angSavedCameraDir );
+ m_pPlayerModelPanel->SetForcedCameraPosition( true );
+
+ m_pPlayerModelPanel->SetLOD( m_nCurrentLOD );
+ }
+ else
+ {
+ BaseClass::PreviewItem( iClass, pItem, pEntry );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::ResetHandles()
+{
+ m_pLODComboBox = NULL;
+ m_pLoadoutComboBox = NULL;
+ m_pPoseComboBox = NULL;
+ m_pActionComboBox = NULL;
+ m_pEffectComboBox = NULL;
+ m_pMaterialButtons.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnThink()
+{
+ // We want to skip CTFStorePreviewItemPanel2::OnThink() because it closes when the mouse is clicked outside the dialog.
+ CTFStorePreviewItemPanelBase::OnThink();
+
+ // There's no event when the team color changes, but this is a super cheap call so we'll poll.
+ g_PlayerPreviewEffect.SetTeam( GetPreviewTeam() );
+
+ if ( m_flGestureEndTime && gpGlobals->curtime >= m_flGestureEndTime )
+ {
+ EndGestures();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ ResetHandles();
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // We don't use the scrollbar in this view
+ if ( m_pScrollBar )
+ {
+ m_pScrollBar->MarkForDeletion();
+ m_pScrollBar = NULL;
+ }
+
+ vgui::Button *pButton = FindControl<vgui::Button>( "AddToCartButton", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( false );
+ }
+
+ m_pLODComboBox = FindControl<vgui::ComboBox>( "LODComboBox", true );
+ if ( m_pLODComboBox )
+ {
+ m_pLODComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pLoadoutComboBox = FindControl<vgui::ComboBox>( "LoadoutComboBox", true );
+ if ( m_pLoadoutComboBox )
+ {
+ m_pLoadoutComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pPoseComboBox = FindControl<vgui::ComboBox>( "PoseComboBox", true );
+ if ( m_pPoseComboBox )
+ {
+ m_pPoseComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pActionComboBox = FindControl<vgui::ComboBox>( "ActionComboBox", true );
+ if ( m_pActionComboBox )
+ {
+ m_pActionComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pEffectComboBox = FindControl<vgui::ComboBox>( "EffectComboBox", true );
+ if ( m_pEffectComboBox )
+ {
+ m_pEffectComboBox->RemoveAll();
+
+ KeyValuesAD pKeyValues( "data" );
+ for ( int iEffect = 0; iEffect < C_TFPlayerPreviewEffect::NUM_PREVIEW_EFFECTS; ++iEffect )
+ {
+ pKeyValues->SetInt( "effect", iEffect );
+ int iRow = m_pEffectComboBox->AddItem( CFmtStr( "#TF_ImportPreview_Effect%d", iEffect ), pKeyValues );
+ if ( g_PlayerPreviewEffect.GetEffect() == iEffect )
+ {
+ m_pEffectComboBox->ActivateItemByRow( iRow );
+ }
+ }
+
+ m_pEffectComboBox->AddActionSignalTarget( this );
+ }
+
+ // Default to the character view, because that's the most valuable for preview here
+ const CTFItemDefinition *pItemData = m_item.GetItemDefinition();
+ if ( pItemData )
+ {
+ for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
+ {
+ if ( !pItemData->CanBeUsedByClass(iClass) )
+ continue;
+
+ KeyValuesAD pKeyValues( "data" );
+ pKeyValues->SetInt( "class", iClass );
+ OnClassIconSelected( pKeyValues );
+ break;
+ }
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonAction", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonEditQC", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ int nSkinType = m_pItemValues->GetInt( kSkinType );
+ m_pMaterialButtons.SetCount( MAX_MATERIAL_COUNT );
+ for ( int i = 0; i < MAX_MATERIAL_COUNT; ++i )
+ {
+ m_pMaterialButtons[i] = FindControl<vgui::Button>( CFmtStr( "ButtonEditMaterial%d", i ), true );
+ if ( m_pMaterialButtons[i] )
+ {
+ int nSkinIndex = i / NUM_IMPORT_MATERIALS_PER_TEAM;
+ int nMaterialIndex = i % NUM_IMPORT_MATERIALS_PER_TEAM;
+ const char* pszTeam;
+ if ( nSkinType == 0 )
+ {
+ pszTeam = "ALL";
+ }
+ else
+ {
+ pszTeam = nSkinIndex == 0 ? "RED" : "BLU";
+ }
+ m_pMaterialButtons[i]->SetText( CFmtStr( "Edit VMT%d %s", nMaterialIndex + 1, pszTeam ) );
+ m_pMaterialButtons[i]->AddActionSignalTarget( this );
+ }
+ }
+
+ // Fix up the fullscreen panel with our desired control set
+ if ( m_pFullscreenPanel )
+ {
+ pButton = m_pFullscreenPanel->FindControl<vgui::Button>( "RotateLeftButton", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( false );
+ }
+
+ pButton = m_pFullscreenPanel->FindControl<vgui::Button>( "RotateRightButton", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( false );
+ }
+
+ pButton = m_pFullscreenPanel->FindControl<vgui::Button>( "ZoomButton", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( false );
+ }
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonEditQCI", true );
+ if ( pButton )
+ {
+ pButton->SetVisible( m_bIsTaunt );
+ }
+
+ vgui::Label *pLabel = FindControl<vgui::Label>( "AdvancedLabel", true );
+ if ( pLabel )
+ {
+ pLabel->SetVisible( !m_bIsTaunt );
+ }
+ vgui::Panel *pPanel = FindControl<vgui::Panel>( "AdvancedFrame", true );
+ if ( pPanel )
+ {
+ pPanel->SetVisible( !m_bIsTaunt );
+ }
+
+ UpdateActivityUI();
+ UpdateMaterialButtons();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnCommand( const char *command )
+{
+ if ( V_strcasecmp( command, "show_explanations" ) == 0 )
+ {
+ CExplanationPopup *pPopup = dynamic_cast<CExplanationPopup*>( FindChildByName("StartExplanation") );
+ if ( pPopup )
+ {
+ pPopup->Popup();
+ }
+ }
+ else if ( V_strcasecmp( command, "action" ) == 0 )
+ {
+ StartAction();
+ }
+ else if ( V_strcasecmp( command, "BuildPreview" ) == 0 ||
+ V_strcasecmp( command, "EditQC" ) == 0 ||
+ V_strcasecmp( command, "EditQCI" ) == 0 ||
+ V_strncasecmp( command, "EditMaterial", V_strlen( "EditMaterial" ) ) == 0 )
+ {
+ // Dispatch directly to our parent because the base class tries to run the command through the console interpreter
+ GetParent()->OnCommand( command );
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_F5 )
+ {
+ OnCommand( "BuildPreview" );
+ return;
+ }
+
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::PerformLayout( void )
+{
+ CTFStorePreviewItemPanel2::PerformLayout();
+
+ PlaceControl( m_pDetailsViewChild, NULL, "UsedByLabel", m_iSmallVerticalBreakSize, true );
+ PlaceControl( m_pDetailsViewChild, "UsedByLabel", "UsedByTextLabel", m_iHorizontalBreakSize, false );
+ PlaceControl( m_pDetailsViewChild, "UsedByLabel", "SlotLabel", m_iSmallVerticalBreakSize, true );
+ PlaceControl( m_pDetailsViewChild, "SlotLabel", "SlotTextLabel", m_iHorizontalBreakSize, false );
+ PlaceControl( m_pDetailsViewChild, "SlotLabel", "AttributesLabel", m_iBigVerticalBreakSize, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnClose()
+{
+ GetParent()->OnCommand( "PreviewDone" );
+ MarkForDeletion();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::UpdatePlayerModelButtons()
+{
+ BaseClass::UpdatePlayerModelButtons();
+
+ UpdateActivityUI();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::UpdateActivityUI()
+{
+ if ( m_pLODComboBox )
+ {
+ m_pLODComboBox->RemoveAll();
+ m_pLODComboBox->SetText( "" );
+
+ Assert( m_nSelectedClass != TF_CLASS_UNDEFINED );
+ if ( m_iState == PS_PLAYER )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ int nDefaultRow = -1;
+ int nLastValidLOD = 0;
+ for ( int nLOD = 0; nLOD < NUM_IMPORT_LODS; ++nLOD )
+ {
+ KeyValues *pKey = m_pItemValues->FindKey( CFmtStr( kClassLODN, kClassFolders[ m_nSelectedClass ], nLOD ) );
+ if ( pKey && *pKey->GetString( "file" ) )
+ {
+ nLastValidLOD = nLOD;
+ }
+
+ pKeyValues->SetInt( "LOD", nLOD );
+ int nRow = m_pLODComboBox->AddItem( CFmtStr( "LOD%d", nLOD ), pKeyValues );
+ if ( m_nCurrentLOD == nLOD )
+ {
+ nDefaultRow = nRow;
+ }
+ }
+ Assert( nDefaultRow >= 0 );
+ m_pLODComboBox->ActivateItemByRow( nDefaultRow );
+ }
+ }
+
+ if ( m_pLoadoutComboBox )
+ {
+ m_pLoadoutComboBox->RemoveAll();
+ m_pLoadoutComboBox->SetText( "" );
+
+ if ( m_iState == PS_PLAYER )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ int nDefaultRow = -1;
+ loadout_positions_t nFirstLoadout = LOADOUT_POSITION_INVALID;
+ for ( int iLoadoutPosition = 0; iLoadoutPosition < CLASS_LOADOUT_POSITION_COUNT; ++iLoadoutPosition )
+ {
+ CEconItemView *pItem = TFInventoryManager()->GetBaseItemForClass( m_iCurrentClass, iLoadoutPosition );
+ if ( pItem && pItem->IsValid() )
+ {
+ const char *pszLabel = ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( pItem->GetItemDefinition()->GetEquipType() )[ iLoadoutPosition ];
+ pKeyValues->SetInt( "loadout", iLoadoutPosition );
+ int nRow = m_pLoadoutComboBox->AddItem( pszLabel, pKeyValues );
+ if ( iLoadoutPosition == m_nCurrentLoadout )
+ {
+ nDefaultRow = nRow;
+ }
+ if ( nFirstLoadout == LOADOUT_POSITION_INVALID )
+ {
+ nFirstLoadout = (loadout_positions_t)iLoadoutPosition;
+ }
+ }
+ }
+ Assert(nFirstLoadout != LOADOUT_POSITION_INVALID);
+
+ if (nDefaultRow < 0)
+ {
+ // Didn't find our current loadout position, pick the first one
+ nDefaultRow = 0;
+ m_nCurrentLoadout = nFirstLoadout;
+ }
+ m_pLoadoutComboBox->ActivateItemByRow( nDefaultRow );
+ }
+ }
+
+ if ( m_pPoseComboBox )
+ {
+ m_pPoseComboBox->RemoveAll();
+ m_pPoseComboBox->SetText( "" );
+
+ if ( m_iState == PS_PLAYER )
+ {
+ KeyValuesAD pKeyValues( "data" );
+ int nDefaultRow = -1;
+ for ( int iPose = 0; iPose < ARRAYSIZE(kModelPoses); ++iPose )
+ {
+ pKeyValues->SetString( "pose", kModelPoses[ iPose ] );
+ int nRow = m_pPoseComboBox->AddItem( CFmtStr( "#TF_ImportPreview_Pose%s", kModelPoses[ iPose ] ), pKeyValues );
+ if ( m_sCurrentPose == kModelPoses[ iPose ] )
+ {
+ nDefaultRow = nRow;
+ }
+ }
+ Assert( nDefaultRow >= 0 );
+ m_pPoseComboBox->ActivateItemByRow( nDefaultRow );
+ }
+ }
+
+ if ( m_pActionComboBox )
+ {
+ m_pActionComboBox->RemoveAll();
+ m_pActionComboBox->SetText( "" );
+
+ if ( m_iState == PS_PLAYER )
+ {
+ KeyValuesAD pKeyValues("data");
+ if ( m_bIsTaunt )
+ {
+ CTFFileImportDialog *pFileImportDialog = dynamic_cast<CTFFileImportDialog*>(GetParent());
+ if (pFileImportDialog)
+ {
+ KeyValuesAD pItemSchema(pFileImportDialog->BuildItemSchema("item_preview"));
+
+ const CUtlVector<const char *>&vecUsabilityStrings = ItemSystem()->GetItemSchema()->GetClassUsabilityStrings();
+ KeyValues *pTauntKey = pItemSchema->FindKey("taunt");
+ if (pTauntKey)
+ {
+ KeyValues *pCustomTauntPerClass = pTauntKey->FindKey("custom_taunt_scene_per_class");
+ if (pCustomTauntPerClass)
+ {
+ const char *pSceneName = pCustomTauntPerClass->GetString(vecUsabilityStrings[m_nSelectedClass]);
+ if (pSceneName && *pSceneName)
+ {
+ pKeyValues->SetString("action", pSceneName);
+ pKeyValues->SetBool("file_name_only", false);
+ m_pActionComboBox->AddItem("#TF_ImportPreview_Taunt", pKeyValues);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int iAction = 0; iAction < ARRAYSIZE(kModelActions); ++iAction)
+ {
+ pKeyValues->SetString("action", kModelActions[iAction]);
+ pKeyValues->SetBool("file_name_only", true);
+ m_pActionComboBox->AddItem(CFmtStr("#TF_ImportPreview_Action%d", iAction), pKeyValues);
+ }
+ }
+
+ Assert( m_pActionComboBox->GetItemCount() >= 0 );
+ m_pActionComboBox->ActivateItemByRow( 0 );
+
+ // init default value
+ KeyValues *pData = m_pActionComboBox->GetActiveItemUserData();
+ m_sCurrentAction = pData->GetString( "action" );
+ m_bIsVCDFileNameOnly = pData->GetBool( "file_name_only" );
+ }
+ }
+
+ UpdateActivity();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::UpdateActivity()
+{
+ if ( !m_pPlayerModelPanel || !m_pPlayerModelPanel->GetStudioHdr() || m_iState != PS_PLAYER )
+ {
+ return;
+ }
+
+ static CEconItemView tempItem;
+ if ( m_bIsTaunt && ClassHasModels( m_nSelectedClass ) )
+ {
+ // use preview item if specified for taunt
+ tempItem.Init( PREVIEW_ITEM_DEFINITION_INDEX, AE_UNIQUE, AE_USE_SCRIPT_VALUE, true );
+ }
+ else
+ {
+ // FIXME: Once we support weapons we'll need to check to see if we want to display the preview item here instead.
+ CEconItemView *pItem = TFInventoryManager()->GetBaseItemForClass( m_iCurrentClass, m_nCurrentLoadout );
+ Assert( pItem && pItem->IsValid() );
+ tempItem = *pItem;
+ }
+ m_pPlayerModelPanel->SwitchHeldItemTo( &tempItem );
+
+ int iSequence = GetSequence();
+ if ( iSequence >= 0 )
+ {
+ m_pPlayerModelPanel->SetSequence( iSequence );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CImportPreviewItemPanel::GetSequence( const char *pszGesture )
+{
+ if ( !m_pPlayerModelPanel || !m_pPlayerModelPanel->GetStudioHdr() )
+ {
+ return -1;
+ }
+
+ CEconItemView *pItem = m_pPlayerModelPanel->GetHeldItem();
+ if ( !pItem )
+ {
+ return -1;
+ }
+
+ int iAnimSlot = pItem->GetAnimationSlot();
+ if ( iAnimSlot < 0 )
+ {
+ iAnimSlot = pItem->GetItemDefinition()->GetLoadoutSlot( m_iCurrentClass );
+ }
+ if ( iAnimSlot < 0 )
+ {
+ return -1;
+ }
+
+ CStudioHdr studioHdr( m_pPlayerModelPanel->GetStudioHdr(), g_pMDLCache );
+
+ // Look for the bind pose by label since it's not an activity
+ if ( !pszGesture && V_strcasecmp( m_sCurrentPose.Get(), "ref" ) == 0 )
+ {
+ for ( int iSeq = 0; iSeq < studioHdr.GetNumSeq(); ++iSeq )
+ {
+ mstudioseqdesc_t &seqDesc = studioHdr.pSeqdesc( iSeq );
+ if ( V_strcasecmp( seqDesc.pszLabel(), m_sCurrentPose.Get() ) == 0 )
+ {
+ return iSeq;
+ }
+ }
+ return -1;
+ }
+
+ CUtlString sActivity;
+ const char *pszActivityOverride;
+ CUtlString sAnimSuffix = GetItemSchema()->GetWeaponTypeSubstrings()[ iAnimSlot ];
+ sAnimSuffix.ToUpper();
+
+ if ( pszGesture )
+ {
+ CUtlString sGesture = pszGesture;
+ sGesture.ToUpper();
+ sActivity.Format( "ACT_MP_%s_%s_%s", sGesture.Get(), m_sCurrentPose.Get(), sAnimSuffix.Get() );
+ }
+ else
+ {
+ sActivity.Format( "ACT_MP_%s_%s", m_sCurrentPose.Get(), sAnimSuffix.Get() );
+ }
+ pszActivityOverride = pItem->GetItemDefinition()->GetActivityOverride( GetPreviewTeam(), sActivity );
+ if ( pszActivityOverride )
+ {
+ sActivity = pszActivityOverride;
+ }
+ return m_pPlayerModelPanel->FindSequenceFromActivity( &studioHdr, sActivity.Get() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::UpdateMaterialButtons()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ return;
+
+ KeyValues* pModelKey = m_pItemValues->FindKey( CFmtStr( kClassLODN, kClassFolders[ m_nSelectedClass ], 0 ) );
+ if ( !pModelKey)
+ return;
+
+ int nSkinType = m_pItemValues->GetInt( kSkinType );
+ int nMaterialCount = pModelKey->GetInt( "materialCount" );
+ for ( int i=0; i<m_pMaterialButtons.Count(); ++i )
+ {
+ int nSkinIndex = i / NUM_IMPORT_MATERIALS_PER_TEAM;
+ int nMaterialIndex = i % NUM_IMPORT_MATERIALS_PER_TEAM;
+ bool bVisible = nSkinIndex <= nSkinType && nMaterialIndex < nMaterialCount;
+ m_pMaterialButtons[i]->SetVisible( bVisible );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::StartAction()
+{
+ const char *pszAction = m_sCurrentAction.String();
+ if ( !pszAction || !*pszAction )
+ {
+ return;
+ }
+
+ if ( !m_pPlayerModelPanel )
+ {
+ return;
+ }
+
+ if ( V_strncasecmp( pszAction, "gesture_", 8 ) == 0 )
+ {
+ pszAction += 8;
+ StartGesture( pszAction );
+ }
+ else
+ {
+ m_pPlayerModelPanel->PlayVCD( pszAction, NULL, false, m_bIsVCDFileNameOnly );
+ UpdateActivity();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::StartGesture( const char *pszGesture )
+{
+ int iSequence = GetSequence( pszGesture );
+ if ( iSequence >= 0 )
+ {
+ CStudioHdr studioHdr( m_pPlayerModelPanel->GetStudioHdr(), g_pMDLCache );
+
+ MDLSquenceLayer_t tmpSequenceLayers[1];
+ tmpSequenceLayers[0].m_nSequenceIndex = iSequence;
+ tmpSequenceLayers[0].m_flWeight = 1.0;
+ tmpSequenceLayers[0].m_bNoLoop = true;
+ tmpSequenceLayers[0].m_flCycleBeganAt = 0.0f;
+ m_pPlayerModelPanel->SetSequenceLayers( tmpSequenceLayers, 1 );
+
+ float flGestureDuration = Studio_Duration( &studioHdr, iSequence, NULL );
+ m_flGestureEndTime = gpGlobals->curtime + flGestureDuration;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::EndGestures()
+{
+ Assert(m_pPlayerModelPanel);
+ m_pPlayerModelPanel->SetSequenceLayers( NULL, 0 );
+ m_flGestureEndTime = 0.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr( "panel" ) );
+
+ vgui::ComboBox *pComboBox = dynamic_cast< vgui::ComboBox * >( pPanel );
+ if ( pComboBox )
+ {
+ if( pComboBox == m_pLODComboBox )
+ {
+ KeyValues *pData = pComboBox->GetActiveItemUserData();
+ int nLOD = pData->GetInt( "LOD" );
+ if ( nLOD != m_nCurrentLOD )
+ {
+ m_nCurrentLOD = nLOD;
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->SetLOD( m_nCurrentLOD );
+ }
+ }
+ }
+ else if( pComboBox == m_pLoadoutComboBox )
+ {
+ KeyValues *pData = pComboBox->GetActiveItemUserData();
+ loadout_positions_t nLoadout = (loadout_positions_t)pData->GetInt( "loadout" );
+ if ( nLoadout != m_nCurrentLoadout )
+ {
+ m_nCurrentLoadout = nLoadout;
+ UpdateActivity();
+ }
+ }
+ else if ( pComboBox == m_pPoseComboBox )
+ {
+ KeyValues *pData = pComboBox->GetActiveItemUserData();
+ const char *pszPose = pData->GetString( "pose" );
+ if ( V_strcmp( pszPose, m_sCurrentPose ) != 0 )
+ {
+ m_sCurrentPose = pszPose;
+ UpdateActivity();
+
+ // Changing pose resets the camera
+ ResetCamera();
+ }
+ }
+ else if ( pComboBox == m_pActionComboBox )
+ {
+ KeyValues *pData = pComboBox->GetActiveItemUserData();
+ m_sCurrentAction = pData->GetString( "action" );
+ m_bIsVCDFileNameOnly = pData->GetBool( "file_name_only" );
+ }
+ else if ( pComboBox == m_pEffectComboBox )
+ {
+ KeyValues *pData = pComboBox->GetActiveItemUserData();
+ g_PlayerPreviewEffect.SetEffect( (C_TFPlayerPreviewEffect::PREVIEW_EFFECT)pData->GetInt("effect") );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::ResetCamera()
+{
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->SetForcedCameraPosition( false );
+ m_pPlayerModelPanel->InvalidateLayout( true, true );
+ m_pPlayerModelPanel->SetForcedCameraPosition( true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CImportPreviewItemPanel::OnClassIconSelected( KeyValues *data )
+{
+ BaseClass::OnClassIconSelected( data );
+
+ m_nSelectedClass = data->GetInt( "class" );
+
+ // Clicking on a class icon resets the camera
+ ResetCamera();
+ UpdateMaterialButtons();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Import file dialog
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::CTFFileImportDialog( vgui::Panel *parent )
+ : Frame( parent, "ImportFileDialog" ),
+ m_tempQC( 0, 0, CUtlBuffer::TEXT_BUFFER )
+{
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme(scheme);
+ SetProportional( true );
+
+ m_nSelectedClass = TF_CLASS_UNDEFINED;
+ m_nFileOpenMode = FILE_OPEN_NONE;
+
+ m_pItemValues = new KeyValues( "ImportSession" );
+ m_pItemValues->UsesEscapeSequences( true );
+
+ m_pPreviewSchema = NULL;
+
+ m_pNameTextEntry = NULL;
+ m_pTypeComboBox = NULL;
+ m_pSwapVMTButton = NULL;
+ m_pSkinComboBox = NULL;
+ m_pWorkshopIDTextEntry = NULL;
+ m_pTFEnglishNameTextEntry = NULL;
+ m_pPerforceCheckButton = NULL;
+ m_pPartnerCheckButton = NULL;
+
+ m_pEquipRegionPanel = NULL;
+ m_pEquipRegionComboBox = NULL;
+
+ m_pIconImagePanel = NULL;
+
+ V_memset( m_pClassRadioButtons, 0, sizeof( m_pClassRadioButtons ) );
+ V_memset( m_pClassHighlights, 0, sizeof( m_pClassHighlights ) );
+
+ m_pBodygroupsPanel = NULL;
+
+ m_pLODsPanel = NULL;
+
+ m_pSkinsPanel = NULL;
+
+ m_pTauntInputPanel = NULL;
+ m_pAnimationSourceFile = NULL;
+ m_pAnimationVCDFile = NULL;
+ m_pAnimationDurationLabel = NULL;
+ m_pAnimationPropLabel = NULL;
+ m_pAnimationLoopCheckButton = NULL;
+ m_pAnimationLoopStartTextEntry = NULL;
+
+ m_pBuildButton = NULL;
+
+ m_pTextEditDialog = NULL;
+ m_pMaterialEditDialog = NULL;
+ m_pPreviewDialog = NULL;
+ m_pPlayerModelPanel = NULL;
+
+ // force sv_cheats 1 so we can run some client command
+ static ConVarRef sv_cheats("sv_cheats");
+ if ( sv_cheats.IsValid() )
+ {
+ m_bWasCheatOn = sv_cheats.GetBool();
+ sv_cheats.SetValue( true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::~CTFFileImportDialog()
+{
+ m_pItemValues->deleteThis();
+
+ if ( m_pPreviewSchema )
+ {
+ m_pPreviewSchema->deleteThis();
+ }
+
+ // restore sv_cheats value before coming into the tool
+ static ConVarRef sv_cheats("sv_cheats");
+ if ( sv_cheats.IsValid() )
+ {
+ sv_cheats.SetValue( m_bWasCheatOn );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ vgui::Button *pButton;
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "Resource/UI/ImportFileDialog.res" );
+
+ m_pNameTextEntry = FindControl<vgui::TextEntry>( "Name" );
+ if ( m_pNameTextEntry )
+ {
+ m_pNameTextEntry->AddActionSignalTarget( this );
+ }
+
+ m_pTypeComboBox = FindControl<vgui::ComboBox>( "TypeComboBox" );
+ if ( m_pTypeComboBox )
+ {
+ m_pTypeComboBox->RemoveAll();
+
+ KeyValuesAD pKeyValues( "data" );
+
+ // Add all the public prefabs
+ const CEconItemSchema::PrefabMap_t &prefabMap = ItemSystem()->GetItemSchema()->GetPrefabMap();
+ for ( int i = prefabMap.FirstInorder(); i != prefabMap.InvalidIndex(); i = prefabMap.NextInorder( i ) )
+ {
+ KeyValues *pPrefabKeyValues = prefabMap [ i ];
+ if ( pPrefabKeyValues->GetBool( "public_prefab" ) )
+ {
+ for ( int j=0; j<ARRAYSIZE(kPrefabs); ++j )
+ {
+ if ( V_strcmp( kPrefabs[j], pPrefabKeyValues->GetName() ) == 0 )
+ {
+ pKeyValues->SetString( kItemPrefab, pPrefabKeyValues->GetName() );
+ m_pTypeComboBox->AddItem( CFmtStr( "#TF_ItemPrefab_%s", pPrefabKeyValues->GetName() ), pKeyValues );
+ }
+ }
+ }
+ }
+
+ SetItemPrefab( kDefaultPrefab );
+
+ m_pTypeComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pEquipRegionPanel = FindControl<vgui::EditablePanel>( "EquipRegionPanel" );
+ m_pEquipRegionComboBox = FindControl<vgui::ComboBox>( "EquipRegionComboBox", true );
+ if ( m_pEquipRegionComboBox )
+ {
+ KeyValuesAD pKey( "data" );
+ const CEconItemSchema::EquipRegionsList_t& list = ItemSystem()->GetItemSchema()->GetEquipRegionsList();
+ CUtlStringList strEquipRegionNameList;
+
+ for ( int j=0; j<list.Count(); ++j )
+ {
+ const char* pszEquipRegion = list[j].m_sName.Get();
+ strEquipRegionNameList.CopyAndAddToTail( pszEquipRegion );
+ }
+ strEquipRegionNameList.Sort( strEquipRegionNameList.SortFunc );
+
+ for ( int j=0; j<strEquipRegionNameList.Count(); ++j )
+ {
+ const char* pszEquipRegion = strEquipRegionNameList[j];
+ pKey->SetString( kEquipRegion, pszEquipRegion );
+ m_pEquipRegionComboBox->AddItem( pszEquipRegion, pKey );
+ }
+
+ SetEquipRegion( kDefaultEquipRegion );
+
+ m_pEquipRegionComboBox->AddActionSignalTarget( this );
+ }
+
+ if ( vgui::Label* pLabel = FindControl<vgui::Label>( "WorkshopIDLabel" ) )
+ {
+ pLabel->SetVisible( p4 );
+ }
+
+ m_pWorkshopIDTextEntry = FindControl<vgui::TextEntry>( "WorkshopIDTextEntry" );
+ if ( m_pWorkshopIDTextEntry )
+ {
+ m_pWorkshopIDTextEntry->SetVisible( p4 );
+ }
+
+ if ( vgui::Label* pLabel = FindControl<vgui::Label>( "TFEnglishNameLabel" ) )
+ {
+ pLabel->SetVisible( p4 );
+ }
+
+ m_pTFEnglishNameTextEntry = FindControl<vgui::TextEntry>( "TFEnglishNameTextEntry" );
+ if ( m_pTFEnglishNameTextEntry )
+ {
+ m_pTFEnglishNameTextEntry->SetVisible( p4 );
+ }
+
+ m_pPerforceCheckButton = FindControl<vgui::CheckButton>( "PerforceCheckButton" );
+ if ( m_pPerforceCheckButton )
+ {
+ m_pPerforceCheckButton->SetVisible( p4 );
+ }
+
+ m_pPartnerCheckButton = FindControl<vgui::CheckButton>( "PartnerCheckButton" );
+ if ( m_pPartnerCheckButton )
+ {
+ m_pPartnerCheckButton->SetVisible( p4 );
+ }
+
+ m_pIconImagePanel = FindControl<vgui::ImagePanel>( "Icon", true );
+ if ( m_pIconImagePanel )
+ {
+ // Set black background, and center the 512x512 backpack icon
+ m_pIconImagePanel->SetFillColor( Color(0, 0, 0, 255) );
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonIconClear", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+ pButton = FindControl<vgui::Button>( "ButtonIconBrowse", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ m_pPaintableCheckButtons.SetCount( 2 );
+ for ( int i=0; i<m_pPaintableCheckButtons.Count(); ++i )
+ {
+ m_pPaintableCheckButtons[i] = FindControl<vgui::CheckButton>( CFmtStr( "Paintable%dCheckBox", i ), true );
+ if ( m_pPaintableCheckButtons[i] )
+ {
+ m_pPaintableCheckButtons[i]->AddActionSignalTarget( this );
+ }
+ }
+
+ for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
+ {
+ m_pClassHighlights[ i ] = FindControl<vgui::Panel>( CFmtStr( "ClassHighlight%d", i ), true );
+ m_pClassRadioButtons[i] = FindControl<vgui::RadioButton>( CFmtStr( "ButtonSelectClass%d", i ), true );
+ if ( m_pClassRadioButtons[i] )
+ m_pClassRadioButtons[i]->AddActionSignalTarget( this );
+ }
+
+ m_pBodygroupsPanel = FindControl<vgui::EditablePanel>( "BodygroupsPanel" );
+ m_pBodygroups.SetCount( ARRAYSIZE( kBodygroupArray ) );
+ for ( int i=0; i<m_pBodygroups.Count(); ++i )
+ {
+ m_pBodygroups[i] = FindControl<vgui::CheckButton>( CFmtStr( "Bodygroup%d", i ), true );
+ AssertMsg( m_pBodygroups[i], CFmtStr( "Missing Bodygroup%d CheckButton in ImportFileDialog.res", i ) );
+ if ( m_pBodygroups[i] )
+ {
+ m_pBodygroups[i]->AddActionSignalTarget( this );
+ m_pBodygroups[i]->SetSelected( true );
+ m_pBodygroups[i]->SetCheckButtonCheckable( false );
+ m_pBodygroups[i]->SetText( kBodygroupArray[i] );
+ }
+ }
+
+ m_pLODsPanel = FindControl<vgui::EditablePanel>( "LODsPanel" );
+ m_pLODPanels.SetCount( NUM_IMPORT_LODS );
+ m_pLODFiles.SetCount( NUM_IMPORT_LODS );
+ m_pLODDetails.SetCount( NUM_IMPORT_LODS );
+ for ( int i = 0; i < NUM_IMPORT_LODS; ++i )
+ {
+ m_pLODPanels[ i ] = FindControl<vgui::Panel>( CFmtStr( "LOD%dPanel", i ), true );
+ m_pLODFiles[ i ] = FindControl<vgui::Label>( CFmtStr( "LOD%dFile", i ), true );
+ m_pLODDetails[ i ] = FindControl<vgui::Label>( CFmtStr( "LOD%dDetails", i ), true );
+
+ pButton = FindControl<vgui::Button>( CFmtStr( "ButtonLOD%dBrowse", i ), true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( CFmtStr( "ButtonLOD%dClear", i ), true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonEditQC", true );
+ if (pButton)
+ {
+ pButton->AddActionSignalTarget(this);
+ }
+
+ m_pSkinsPanel = FindControl<vgui::EditablePanel>( "SkinsPanel" );
+
+ m_pSwapVMTButton = FindControl<vgui::Button>( "SwapVMTButton", true );
+ if ( m_pSwapVMTButton )
+ {
+ m_pSwapVMTButton->AddActionSignalTarget( this );
+ }
+
+ m_pSkinComboBox = FindControl<vgui::ComboBox>( "SkinComboBox", true );
+ if ( m_pSkinComboBox )
+ {
+ m_pSkinComboBox->RemoveAll();
+
+ KeyValuesAD pKeyValues( "data" );
+ for ( int j=0; j<NUM_IMPORT_MATERIALS_PER_TEAM; ++j )
+ {
+ pKeyValues->SetInt( kSkinType, j );
+ m_pSkinComboBox->AddItem( CFmtStr( "#TF_ItemSkinType_%d", j ), pKeyValues );
+ }
+
+ m_pSkinComboBox->ActivateItemByRow( 0 );
+
+ m_pSkinComboBox->AddActionSignalTarget( this );
+ }
+
+ m_pMaterialPanels.SetCount( MAX_MATERIAL_COUNT );
+ m_pMaterialLabels.SetCount( MAX_MATERIAL_COUNT );
+ m_pMaterialFiles.SetCount( MAX_MATERIAL_COUNT );
+ for ( int i = 0; i < MAX_MATERIAL_COUNT; ++i )
+ {
+ m_pMaterialPanels[ i ] = FindControl<vgui::Panel>( CFmtStr( "Material%dPanel", i ), true );
+ m_pMaterialLabels[ i ] = FindControl<vgui::Label>( CFmtStr( "Material%dLabel", i ), true );
+ m_pMaterialFiles[ i ] = FindControl<vgui::Label>( CFmtStr( "Material%dFile", i ), true );
+
+ pButton = FindControl<vgui::Button>( CFmtStr( "ButtonMaterial%dEdit", i ), true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( CFmtStr( "ButtonMaterial%dBrowse", i ), true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( CFmtStr( "ButtonMaterial%dClear", i ), true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+ }
+
+ m_pTauntInputPanel = FindControl<vgui::EditablePanel>( "TauntInputPanel" );
+ if ( m_pTauntInputPanel )
+ {
+ m_pAnimationSourceFile = FindControl<vgui::Label>( "AnimationSourceFile", true );
+ pButton = FindControl<vgui::Button>( "ButtonAnimationSourceBrowse", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonAnimationSourceClear", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ m_pAnimationVCDFile = FindControl<vgui::Label>( "AnimationVCDFile", true );
+ pButton = FindControl<vgui::Button>( "ButtonAnimationVCDBrowse", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonAnimationVCDClear", true );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ }
+
+ m_pAnimationDurationLabel = FindControl<vgui::Label>( "AnimDurationLabel", true );
+
+ m_pAnimationLoopCheckButton = FindControl<vgui::CheckButton>( "AnimationLoopCheckButton", true );
+ if ( m_pAnimationLoopCheckButton )
+ {
+ m_pAnimationLoopCheckButton->AddActionSignalTarget( this );
+ }
+
+ m_pAnimationLoopStartTextEntry = FindControl<vgui::TextEntry>( "AnimationLoopStartTextEntry", true );
+ if ( m_pAnimationLoopStartTextEntry )
+ {
+ m_pAnimationLoopStartTextEntry->AddActionSignalTarget( this );
+ }
+
+ pButton = FindControl<vgui::Button>( "ButtonEditQCI", true );
+ if (pButton)
+ {
+ pButton->AddActionSignalTarget(this);
+ }
+ }
+ m_pAnimationPropLabel = FindControl<vgui::Label>( "AnimationPropLabel" );
+
+ m_pBuildButton = FindControl<vgui::Button>( "ButtonBuild" );
+ if ( m_pBuildButton )
+ {
+ m_pBuildButton->SetEnabled( false );
+ }
+
+ m_pPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") );
+
+ OnOpen();
+
+ UpdateMaterialDisplay();
+ UpdateBodygroupsDisplay();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommand( const char *command )
+{
+ if ( V_stricmp( command, "Load" ) == 0 )
+ {
+ OnCommandLoad();
+ }
+ else if ( V_stricmp( command, "Save" ) == 0 )
+ {
+ OnCommandSave();
+ }
+ else if ( V_stricmp( command, "ClearIcon" ) == 0 )
+ {
+ SetItemIcon( "" );
+ }
+ else if ( V_stricmp( command, "BrowseIcon" ) == 0 )
+ {
+ OnCommandBrowseIcon();
+ }
+ else if ( V_stricmp( command, "UpdateBodygroup" ) == 0 )
+ {
+ OnCommandUpdateBodygroup();
+ }
+ else if ( V_strncasecmp( command, "UpdatePaintable", V_strlen( "UpdatePaintable" ) ) == 0 )
+ {
+ int nMaterialIndex = V_atoi( &command[ V_strlen( "UpdatePaintable" ) ] );
+ SetPaintable( m_pPaintableCheckButtons[nMaterialIndex]->IsSelected(), nMaterialIndex );
+ }
+ else if ( V_strncasecmp( command, "ClearLOD", V_strlen( "ClearLOD" ) ) == 0 )
+ {
+ int index = V_atoi( &command[ V_strlen( "ClearLOD" ) ] );
+
+ SetLOD( m_nSelectedClass, index, "" );
+ }
+ else if ( V_strncasecmp( command, "BrowseLOD", V_strlen( "BrowseLOD" ) ) == 0 )
+ {
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ ShowMessageBox( "#TF_ImportFile_SelectClassTitle", "#TF_ImportFile_SelectClass" );
+ return;
+ }
+
+ int index = V_atoi( &command[ V_strlen( "BrowseLOD" ) ] );
+
+ OnCommandBrowseLOD( index );
+ }
+ else if ( V_stricmp( command, "SwapVMT" ) == 0 )
+ {
+ OnCommandSwapVMT();
+ }
+ else if ( V_strncasecmp( command, "EditMaterialDone", V_strlen( "EditMaterialDone" ) ) == 0 )
+ {
+ int nSkinIndex, nMaterialIndex;
+ if ( sscanf( command + V_strlen( "EditMaterialDone" ), "%d,%d", &nSkinIndex, &nMaterialIndex ) == 2 )
+ {
+ OnCommandEditMaterialDone( nSkinIndex, nMaterialIndex );
+ }
+ }
+ else if ( V_strncasecmp( command, "EditMaterial", V_strlen( "EditMaterial" ) ) == 0 )
+ {
+ int nMaterialPanelIndex;
+ if ( sscanf( command + V_strlen( "EditMaterial" ), "%d", &nMaterialPanelIndex ) == 1 )
+ {
+ int nSkinIndex = nMaterialPanelIndex / NUM_IMPORT_MATERIALS_PER_TEAM;
+ int nMaterialIndex = nMaterialPanelIndex % NUM_IMPORT_MATERIALS_PER_TEAM;
+ OnCommandEditMaterial( nSkinIndex, nMaterialIndex );
+ }
+ }
+ else if ( V_stricmp( command, "UpdateAnimationLoopable" ) == 0 )
+ {
+ SetLoopableTaunt( IsLoopableTaunt(), GetAnimationLoopStartTime() );
+ }
+ else if ( V_stricmp( command, "ClearAnimationSource" ) == 0 )
+ {
+ SetAnimationSource( m_nSelectedClass, NULL );
+ }
+ else if ( V_stricmp( command, "ClearAnimationVCD" ) == 0 )
+ {
+ SetAnimationVCD( m_nSelectedClass, NULL );
+ }
+ else if ( V_stricmp( command, "BrowseAnimationSource" ) == 0 )
+ {
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ ShowMessageBox( "#TF_ImportFile_SelectClassTitle", "#TF_ImportFile_SelectClass" );
+ return;
+ }
+
+ OnCommandBrowseAnimationSource();
+ }
+ else if ( V_stricmp( command, "BrowseAnimationVCD" ) == 0 )
+ {
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ ShowMessageBox( "#TF_ImportFile_SelectClassTitle", "#TF_ImportFile_SelectClass" );
+ return;
+ }
+
+ OnCommandBrowseAnimationVCD();
+ }
+ else if ( V_stricmp( command, "EditQC" ) == 0 )
+ {
+ OnCommandEditQC();
+ }
+ else if ( V_stricmp( command, "EditQCI") == 0 )
+ {
+ OnCommandEditQCI();
+ }
+ else if ( V_stricmp( command, "EditQCDone" ) == 0 )
+ {
+ OnCommandEditQCDone();
+ }
+ else if ( V_stricmp( command, "EditQCIDone" ) == 0 )
+ {
+ OnCommandEditQCIDone();
+ }
+ else if ( V_stricmp( command, "BuildPreview" ) == 0 )
+ {
+ OnCommandBuild( BUILD_PREVIEW );
+ }
+ else if ( V_stricmp( command, "BuildVerify") == 0 )
+ {
+ OnCommandBuild( BUILD_VERIFY );
+ }
+ else if ( V_stricmp( command, "BuildFinal" ) == 0 )
+ {
+ OnCommandBuild( BUILD_FINAL );
+ }
+ else if ( V_stricmp( command, "PreviewDone" ) == 0 )
+ {
+ CleanupPreviewData();
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandLoad()
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectFile", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.txt,*.zip", "#TF_ImportFile_LoadSessionFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ char pszStartPath[ MAX_PATH ];
+ if ( g_pFullFileSystem->RelativePathToFullPath_safe( CFmtStr( kSteamWorkshopDir, GetWorkshopFolder() ), "GAME", pszStartPath ) )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = FILE_OPEN_LOAD;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandSave()
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectFile", vgui::FOD_SAVE );
+ pDialog->AddFilter( "*.txt", "#TF_ImportFile_SaveSessionFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ char pszStartPath[ MAX_PATH ];
+ if ( g_pFullFileSystem->RelativePathToFullPath_safe( CFmtStr( kSteamWorkshopDir, GetWorkshopFolder() ), "GAME", pszStartPath ) )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = FILE_OPEN_SAVE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandBrowseIcon()
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectImage", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.tga,*.psd", "#TF_ImportFile_IconFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ const char *pszStartPath = tf_steam_workshop_import_icon_path.GetString();
+ if ( pszStartPath && *pszStartPath )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = FILE_OPEN_ICON;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandBrowseLOD( int index )
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectModel", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.smd,*.dmx,*.fbx", "#TF_ImportFile_ModelFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ const char *pszStartPath = tf_steam_workshop_import_model_path.GetString();
+ if ( pszStartPath && *pszStartPath )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = static_cast<FileOpenMode>( FILE_OPEN_LOD0 + index );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandBrowseAnimationSource()
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectAnimationSource", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.smd,*.dmx,*.fbx", "#TF_ImportFile_AnimationSourceFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ const char *pszStartPath = tf_steam_workshop_import_model_path.GetString();
+ if ( pszStartPath && *pszStartPath )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = static_cast<FileOpenMode>( FILE_OPEN_ANIMATION_SOURCE );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandBrowseAnimationVCD()
+{
+ vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( NULL, "#TF_ImportFile_SelectAnimationVCD", vgui::FOD_OPEN );
+ pDialog->AddFilter( "*.vcd", "#TF_ImportFile_AnimationVCDFileType", true);
+ pDialog->AddActionSignalTarget( this );
+ const char *pszStartPath = tf_steam_workshop_import_model_path.GetString();
+ if ( pszStartPath && *pszStartPath )
+ {
+ pDialog->SetStartDirectory( pszStartPath );
+ }
+ pDialog->DoModal();
+ pDialog->Activate();
+ m_nFileOpenMode = static_cast<FileOpenMode>( FILE_OPEN_ANIMATION_VCD );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandSwapVMT()
+{
+ bool bSwapPaint = false;
+ for ( int nSkin=0; nSkin<CItemUpload::Manifest()->GetNumMaterialSkins(); ++nSkin )
+ {
+ KeyValues *pVMT0 = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkin ), 0 ) );
+ KeyValues *pVMT1 = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkin ), 1 ) );
+ if ( pVMT0 && pVMT1 )
+ {
+ KeyValuesAD pTemp( pVMT0->MakeCopy() );
+ pVMT0->Clear();
+ pVMT1->CopySubkeys( pVMT0 );
+ pVMT1->Clear();
+ pTemp->CopySubkeys( pVMT1 );
+
+ if ( !bSwapPaint )
+ {
+ bool bPaintable0 = IsPaintable( 0 );
+ bool bPaintable1 = IsPaintable( 1 );
+ SetPaintable( bPaintable1, 0 );
+ SetPaintable( bPaintable0, 1 );
+ bSwapPaint = true;
+ }
+ }
+ }
+
+ UpdateMaterialDisplay();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditMaterial( int nSkinIndex, int nMaterialIndex )
+{
+ CTFImportMaterialEditDialog *pDialog = new CTFImportMaterialEditDialog( this, nSkinIndex, nMaterialIndex, GetItemValues() );
+ pDialog->InvalidateLayout( true, true );
+
+ CUtlBuffer sRedMaterialText, sBlueMaterialText;
+ pDialog->SetVMTKeyValues( GetMaterialText( 0, nMaterialIndex, sRedMaterialText ), GetMaterialText( 1, nMaterialIndex, sBlueMaterialText ) );
+ pDialog->DoModal();
+ pDialog->Activate();
+
+ m_pMaterialEditDialog = pDialog;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditMaterialDone( int nSkinIndex, int nMaterialIndex )
+{
+ if ( !m_pMaterialEditDialog )
+ {
+ return;
+ }
+
+ CUtlStringList oldMaterials;
+ CUtlStringList oldBaseTextures;
+ CUtlStringList oldNormalTextures;
+ CUtlStringList oldPhongExponentTextures;
+ CUtlStringList oldSelfIllumTextures;
+
+ bool bAllTeam = m_pItemValues->GetInt( kSkinType ) == 0;
+ const int nNumSkin = bAllTeam ? 1 : CItemUpload::Manifest()->GetNumMaterialSkins();
+ for ( int nSkin=0; nSkin < nNumSkin; ++nSkin )
+ {
+ KeyValuesAD pKV( m_pMaterialEditDialog->GetVMTKeyValues( nSkin )->MakeCopy() );
+ BUILD_RESULT nResult = ValidateMaterialValues( pKV, nMaterialIndex );
+ if ( nResult != BUILD_OKAY )
+ {
+ ShowMessageBoxWithFile( "#TF_SteamWorkshop_Error", kBuildResultMessages[ nResult ], CFmtStr( "VMT%d", 1 + nMaterialIndex ) );
+ return;
+ }
+
+ CUtlBuffer sOldText;
+ GetMaterialText( nSkin, nMaterialIndex, sOldText );
+ oldMaterials.CopyAndAddToTail( sOldText.String() );
+ oldBaseTextures.CopyAndAddToTail( GetMaterialTextureFile( nSkin, nMaterialIndex, MATERIAL_FILE_BASETEXTURE ) );
+ oldNormalTextures.CopyAndAddToTail( GetMaterialTextureFile( nSkin, nMaterialIndex, MATERIAL_FILE_NORMAL ) );
+ oldPhongExponentTextures.CopyAndAddToTail( GetMaterialTextureFile( nSkin, nMaterialIndex, MATERIAL_FILE_PHONGEXPONENT ) );
+ oldSelfIllumTextures.CopyAndAddToTail( GetMaterialTextureFile( nSkin, nMaterialIndex, MATERIAL_FILE_SELFILLUM ) );
+
+ // set new material text
+ CUtlBuffer sNewText;
+ sNewText.SetBufferType( true, true );
+ pKV->RecursiveSaveToFile( sNewText, 0, false, true );
+ sNewText.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ SetMaterialText( nSkin, nMaterialIndex, sNewText.String() );
+
+ // set new textures
+ SetMaterial( nSkin, nMaterialIndex, m_pMaterialEditDialog->GetBaseTextureFile( nSkin ), MATERIAL_FILE_BASETEXTURE );
+ SetMaterial( nSkin, nMaterialIndex, m_pMaterialEditDialog->GetNormalTextureFile(), MATERIAL_FILE_NORMAL );
+ SetMaterial( nSkin, nMaterialIndex, m_pMaterialEditDialog->GetPhongExponentTextureFile(), MATERIAL_FILE_PHONGEXPONENT );
+ SetMaterial( nSkin, nMaterialIndex, m_pMaterialEditDialog->GetSelfIllumTextureFile(), MATERIAL_FILE_SELFILLUM );
+ }
+
+ if ( m_pPreviewDialog )
+ {
+ // Refresh the preview and see if everything builds correctly
+ if ( !OnCommandBuild( BUILD_PREVIEW ) )
+ {
+ for ( int nSkin=0; nSkin<nNumSkin; ++nSkin )
+ {
+ SetMaterialText( nSkin, nMaterialIndex, oldMaterials[nSkin] );
+ SetMaterial( nSkin, nMaterialIndex, oldBaseTextures[nSkin], MATERIAL_FILE_BASETEXTURE );
+ SetMaterial( nSkin, nMaterialIndex, oldNormalTextures[nSkin], MATERIAL_FILE_NORMAL );
+ SetMaterial( nSkin, nMaterialIndex, oldPhongExponentTextures[nSkin], MATERIAL_FILE_PHONGEXPONENT );
+ SetMaterial( nSkin, nMaterialIndex, oldSelfIllumTextures[nSkin], MATERIAL_FILE_SELFILLUM );
+ }
+ return;
+ }
+ }
+
+ m_pMaterialEditDialog->OnCommand( "Close" );
+ m_pMaterialEditDialog.Set(INVALID_PANEL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditQC()
+{
+ // The QC template is dependent on the item type, so make sure that's set first
+ if ( V_strcmp( GetItemPrefab(), "" ) == 0 )
+ {
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_BuildFailedNoType" );
+ return;
+ }
+
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ ShowMessageBox( "#TF_ImportFile_SelectClassTitle", "#TF_ImportFile_SelectClass" );
+ return;
+ }
+
+ CTFFileImportTextEditDialog *pDialog = new CTFFileImportTextEditDialog( this, "#TF_ImportFile_EditQC", "EditQCDone" );
+ pDialog->SetText( GetQCTemplate( m_nSelectedClass ) );
+ pDialog->DoModal();
+ pDialog->Activate();
+
+ m_pTextEditDialog = pDialog;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditQCI()
+{
+ // The QC template is dependent on the item type, so make sure that's set first
+ if ( V_strcmp(GetItemPrefab(), "") == 0 )
+ {
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_BuildFailedNoType" );
+ return;
+ }
+
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ ShowMessageBox( "#TF_ImportFile_SelectClassTitle", "#TF_ImportFile_SelectClass" );
+ return;
+ }
+
+ CTFFileImportTextEditDialog *pDialog = new CTFFileImportTextEditDialog(this, "#TF_ImportFile_EditQCI", "EditQCIDone");
+ pDialog->SetText( GetQCITemplate( m_nSelectedClass ) );
+ pDialog->DoModal();
+ pDialog->Activate();
+
+ m_pTextEditDialog = pDialog;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditQCDone()
+{
+ if ( !m_pTextEditDialog )
+ {
+ return;
+ }
+
+ char pszText[MAX_TEXT_EDIT_SIZE];
+ m_pTextEditDialog->GetText( pszText, sizeof(pszText) );
+
+ static const char *pszRequiredText[] = {
+ "<ITEMTEST_REPLACE_MDLABSPATH>",
+ "<ITEMTEST_REPLACE_LOD0>",
+ "<ITEMTEST_REPLACE_SKIN_OPTIONALBLOCK>",
+ };
+ for ( int i = 0; i < ARRAYSIZE(pszRequiredText); ++i )
+ {
+ if ( !V_strstr( pszText, pszRequiredText[i] ) )
+ {
+ KeyValuesAD pData( "Data" );
+ pData->SetString( "text", pszRequiredText[i] );
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_QCMissingText", pData );
+ return;
+ }
+ }
+
+ static const char *pszUnsupportedText[] =
+ {
+ "$keyvalues",
+ };
+ for ( int i = 0; i < ARRAYSIZE(pszUnsupportedText); ++i )
+ {
+ if ( V_strstr( pszText, pszUnsupportedText[i] ) )
+ {
+ KeyValuesAD pData( "Data" );
+ pData->SetString( "text", pszUnsupportedText[i] );
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_QCUnsupportedText", pData );
+ return;
+ }
+ }
+
+ const char* pszQCKeyName = CFmtStr( kClassQC, kClassFolders[ m_nSelectedClass ] );
+ CUtlString sOldText = GetItemValues()->GetString( pszQCKeyName );
+ GetItemValues()->SetString( pszQCKeyName, pszText );
+
+ if ( m_pPreviewDialog )
+ {
+ // Refresh the preview and see if everything builds correctly
+ if ( !OnCommandBuild( BUILD_PREVIEW ) )
+ {
+ GetItemValues()->SetString( pszQCKeyName, sOldText );
+ return;
+ }
+ }
+
+ m_pTextEditDialog->OnCommand( "Close" );
+ m_pTextEditDialog.Set(INVALID_PANEL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandEditQCIDone()
+{
+ if ( !m_pTextEditDialog )
+ {
+ return;
+ }
+
+ char pszText[MAX_TEXT_EDIT_SIZE];
+ m_pTextEditDialog->GetText( pszText, sizeof(pszText) );
+
+ const char* pszQCKeyName = CFmtStr( kClassQCI, kClassFolders[ m_nSelectedClass ] );
+ CUtlString sOldText = GetItemValues()->GetString( pszQCKeyName );
+ GetItemValues()->SetString( pszQCKeyName, pszText );
+
+ if ( m_pPreviewDialog )
+ {
+ // Refresh the preview and see if everything builds correctly
+ if ( !OnCommandBuild( BUILD_PREVIEW ) )
+ {
+ GetItemValues()->SetString( pszQCKeyName, sOldText );
+ return;
+ }
+ }
+
+ m_pTextEditDialog->OnCommand( "Close" );
+ m_pTextEditDialog.Set(INVALID_PANEL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::OnCommandBuild( BUILD_STAGE buildStage )
+{
+ KeyValuesAD pBuildMessageVariables( "data" );
+
+ // this can take a while, put up a waiting cursor
+ vgui::surface()->SetCursor( vgui::dc_hourglass );
+
+ // Set up Perforce integration
+ CItemUpload::SetP4( ShouldP4AddOrEdit() );
+ if ( CItemUpload::GetP4() )
+ {
+ CUtlString sChangeList( "imported - " );
+ sChangeList += GetItemName();
+
+ g_p4factory->SetOpenFileChangeList( NULL );
+ g_p4factory->SetOpenFileChangeList( sChangeList.String() );
+ }
+
+ BUILD_RESULT nResult = Build( buildStage, pBuildMessageVariables );
+
+ if ( CItemUpload::GetP4() )
+ {
+ g_p4factory->SetOpenFileChangeList( NULL );
+ }
+
+ // change the cursor back to normal
+ vgui::surface()->SetCursor( vgui::dc_user );
+
+ if ( nResult == BUILD_OKAY )
+ {
+ switch ( buildStage )
+ {
+ case BUILD_PREVIEW:
+ {
+ if ( !SetupPreviewData() )
+ {
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_PreviewFailed" );
+ return false;
+ }
+ m_pPreviewDialog->SetVisible( true );
+ }
+ break;
+ case BUILD_VERIFY:
+ {
+ SetDirty( false );
+ }
+ break;
+ case BUILD_FINAL:
+ {
+ const char *pszBuildOutput = V_GetFileName( GetItemValues()->GetString( kBuildOutput ) );
+ ShowMessageBoxWithFile( "#TF_ImportFile_ImportCompleteTitle", "#TF_ImportFile_ImportComplete", pszBuildOutput );
+ SetWorkshopData();
+ BaseClass::OnCommand( "Close" );
+ }
+ break;
+ default:
+ Assert( 0 );
+ }
+ return true;
+ }
+
+ // The build failed.
+ SetDirty( true );
+ if ( nResult == BUILD_FAILED_COMPILE )
+ {
+ CTFFileImportTextEditDialog *pDialog = new CTFFileImportTextEditDialog( this, "#TF_ImportFile_BuildFailed" );
+ pDialog->HideCancelButton();
+ pDialog->SetText( pBuildMessageVariables->GetString( "log" ) );
+ pDialog->DoModal();
+ pDialog->Activate();
+ }
+ else
+ {
+ ShowMessageBox( "#TF_ImportFile_BuildFailed", kBuildResultMessages[ nResult ], pBuildMessageVariables );
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnCommandUpdateBodygroup()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ KeyValuesAD pBodygroupKey( kBodygroup );
+ for ( int i=0; i<m_pBodygroups.Count(); ++i )
+ {
+ if ( m_pBodygroups[i] && !m_pBodygroups[i]->IsSelected() )
+ {
+ pBodygroupKey->SetBool( kBodygroupArray[i], true );
+ }
+ }
+ SetBodygroup( pBodygroupKey );
+
+ SetDirty( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnOpen()
+{
+ // Make sure our workshop directory exists
+ const char *pszWorkshopDir = CFmtStr( kSteamWorkshopDir, GetWorkshopFolder() );
+ g_pFullFileSystem->CreateDirHierarchy( pszWorkshopDir, "GAME" );
+
+ GetWorkshopData();
+
+ if ( !CheckSourceSDK() )
+ {
+ ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_BuildFailedNoSDK" );
+ }
+
+ CUtlString sName;
+ if ( CItemUpload::SanitizeName( GetItemName(), sName ) )
+ {
+ char pszSessionPath[ MAX_PATH ];
+ V_ComposeFileName( pszWorkshopDir, CFmtStr( "%s.txt", sName.Get() ), pszSessionPath, sizeof(pszSessionPath) );
+ CUtlString sFailedPath;
+ LOAD_RESULT nResult = Load( pszSessionPath, "GAME", sFailedPath );
+
+ // We ignore a general load failed since it's okay if the file is missing, but if some of the internal
+ // data is missing from a valid session we want to let the user know.
+ if ( nResult == LOAD_FAILED_BADMODEL || nResult == LOAD_FAILED_BADMATERIAL )
+ {
+ ShowMessageBoxWithFile( "#TF_ImportFile_LoadFailed", kLoadResultMessages[ nResult ], sFailedPath );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnClose()
+{
+ CleanupPreviewData();
+
+ CUtlString sName;
+ if ( CItemUpload::SanitizeName( GetItemName(), sName ) )
+ {
+ char pszSessionPath[ MAX_PATH ];
+ V_ComposeFileName( CFmtStr( kSteamWorkshopDir, GetWorkshopFolder() ), CFmtStr( "%s.txt", sName.Get() ), pszSessionPath, sizeof(pszSessionPath) );
+ SAVE_RESULT nResult = Save( pszSessionPath, "GAME" );
+ if ( nResult != SAVE_OKAY )
+ {
+ ShowMessageBoxWithFile( "#TF_ImportFile_SaveFailed", kSaveResultMessages[ nResult ], pszSessionPath );
+ }
+ }
+
+ BaseClass::OnClose();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr( "panel" ) );
+
+ vgui::ComboBox *pComboBox = dynamic_cast< vgui::ComboBox * >( pPanel );
+ if ( pComboBox )
+ {
+ if( pComboBox == m_pTypeComboBox )
+ {
+ KeyValues *pData = m_pTypeComboBox->GetActiveItemUserData();
+ SetItemPrefab( pData->GetString( kItemPrefab ) );
+ }
+ else if ( pComboBox == m_pSkinComboBox )
+ {
+ KeyValues *pData = m_pSkinComboBox->GetActiveItemUserData();
+ SetSkinType( pData->GetInt( kSkinType ) );
+ }
+ else if ( pComboBox == m_pEquipRegionComboBox )
+ {
+ KeyValues *pData = m_pEquipRegionComboBox->GetActiveItemUserData();
+ SetEquipRegion( pData->GetString( kEquipRegion ) );
+ }
+
+ return;
+ }
+
+ vgui::TextEntry *pTextEntry = dynamic_cast< vgui::TextEntry * >( pPanel );
+ if ( pTextEntry )
+ {
+ if ( pTextEntry == m_pNameTextEntry )
+ {
+ char name[256];
+ m_pNameTextEntry->GetText( name, sizeof(name) );
+ GetItemValues()->SetString( kItemName, name );
+ }
+ else if ( pTextEntry == m_pWorkshopIDTextEntry )
+ {
+ char szID[256];
+ m_pWorkshopIDTextEntry->GetText( szID, ARRAYSIZE( szID ) );
+ GetItemValues()->SetString( kWorkshopID, szID );
+ }
+ else if ( pTextEntry == m_pTFEnglishNameTextEntry )
+ {
+ char name[256];
+ m_pTFEnglishNameTextEntry->GetText( name, sizeof(name) );
+ GetItemValues()->SetString( kTFEnglishName, name );
+ }
+ else if ( pTextEntry == m_pAnimationLoopStartTextEntry )
+ {
+ const float flAnimationLoopStartTime = m_pAnimationLoopStartTextEntry->GetValueAsFloat();
+ GetItemValues()->SetFloat( kAnimationLoopable, flAnimationLoopStartTime );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnFileSelected( char const *fullpath )
+{
+ switch ( m_nFileOpenMode )
+ {
+ case FILE_OPEN_LOAD:
+ {
+ CUtlString sFailedPath;
+ LOAD_RESULT nResult = Load( fullpath, NULL, sFailedPath );
+ if ( nResult != LOAD_OKAY )
+ {
+ ShowMessageBoxWithFile( "#TF_ImportFile_LoadFailed", kLoadResultMessages[ nResult ], sFailedPath );
+ }
+ }
+ break;
+ case FILE_OPEN_SAVE:
+ {
+ SAVE_RESULT nResult = Save( fullpath, NULL );
+ if ( nResult != SAVE_OKAY )
+ {
+ ShowMessageBoxWithFile( "#TF_ImportFile_SaveFailed", kSaveResultMessages[ nResult ], fullpath );
+ }
+ }
+ break;
+ case FILE_OPEN_ICON:
+ {
+ SetItemIcon( fullpath );
+ SaveBrowsePath( tf_steam_workshop_import_icon_path, fullpath );
+ }
+ break;
+ case FILE_OPEN_LOD0:
+ case FILE_OPEN_LOD1:
+ case FILE_OPEN_LOD2:
+ {
+ KeyValuesAD pKV( "data" );
+ LOAD_RESULT result = SetLOD( m_nSelectedClass, (m_nFileOpenMode - FILE_OPEN_LOD0), fullpath, pKV );
+ if ( result != LOAD_OKAY )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", kLoadResultMessages[ result ], pKV );
+ }
+ SaveBrowsePath( tf_steam_workshop_import_model_path, fullpath );
+ }
+ break;
+ case FILE_OPEN_ANIMATION_SOURCE:
+ {
+ KeyValuesAD pKV( "data" );
+ LOAD_RESULT result = SetAnimationSource( m_nSelectedClass, fullpath, pKV );
+ if ( result != LOAD_OKAY )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", kLoadResultMessages[ result ], pKV );
+ }
+ SaveBrowsePath( tf_steam_workshop_import_model_path, fullpath );
+ }
+ break;
+ case FILE_OPEN_ANIMATION_VCD:
+ {
+ KeyValuesAD pKV( "data" );
+ LOAD_RESULT result = SetAnimationVCD( m_nSelectedClass, fullpath, pKV );
+ if ( result != LOAD_OKAY )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", kLoadResultMessages[ result ], pKV );
+ }
+ SaveBrowsePath( tf_steam_workshop_import_model_path, fullpath );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::OnRadioButtonChecked( Panel *panel )
+{
+ int i;
+
+ for ( i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
+ {
+ if ( panel == m_pClassRadioButtons[i] )
+ {
+ SelectClass( i );
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetItemName( const char *pszName )
+{
+ if ( m_pNameTextEntry )
+ {
+ m_pNameTextEntry->SetText( pszName );
+ }
+ GetItemValues()->SetString( kItemName, pszName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetItemName()
+{
+ const char *pszName = GetItemValues()->GetString( kItemName );
+
+ // Skip "The " if someone put it into the item name
+ const char *kSkip = "The ";
+ if ( V_strncasecmp(pszName, kSkip, V_strlen( kSkip ) ) == 0 )
+ {
+ pszName += V_strlen( kSkip );
+ }
+ return pszName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsValidPrefab( const char *pszPrefab )
+{
+ KeyValues *pPrefab = ItemSystem()->GetItemSchema()->FindDefinitionPrefabByName( GetItemPrefab() );
+ return pPrefab && pPrefab->GetBool( "public_prefab" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetItemPrefab( const char *pszPrefab )
+{
+ if ( m_pTypeComboBox )
+ {
+ ImportPrefab_t nOldPrefab = m_nPrefab;
+ bool bFound = false;
+ for ( int i = 0; i < m_pTypeComboBox->GetItemCount(); ++i )
+ {
+ KeyValues *pKeyValues = m_pTypeComboBox->GetItemUserData( m_pTypeComboBox->GetItemIDFromRow( i ) );
+ if ( V_strcasecmp( pszPrefab, pKeyValues->GetString( kItemPrefab ) ) == 0 )
+ {
+ bFound = true;
+ m_pTypeComboBox->ActivateItemByRow( i );
+ m_nPrefab = ImportPrefab_t(i);
+ break;
+ }
+ }
+
+ if ( ( nOldPrefab != PREFAB_TAUNT && m_nPrefab == PREFAB_TAUNT ) ||
+ ( nOldPrefab == PREFAB_TAUNT && m_nPrefab != PREFAB_TAUNT ) )
+ {
+ ClearLODs();
+ }
+
+ pszPrefab = bFound ? pszPrefab : "";
+ GetItemValues()->SetString( kItemPrefab, pszPrefab );
+ UpdateUIForPrefab( m_nPrefab );
+ }
+
+ SetDirty( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateUIForPrefab( ImportPrefab_t nPrefab )
+{
+ if ( nPrefab == PREFAB_TAUNT )
+ {
+ if ( m_pEquipRegionPanel )
+ {
+ m_pEquipRegionPanel->SetVisible( false );
+ }
+ if ( m_pBodygroupsPanel )
+ {
+ m_pBodygroupsPanel->SetVisible( false );
+ }
+ if ( m_pTauntInputPanel )
+ {
+ m_pTauntInputPanel->SetVisible( true );
+ }
+ if ( m_pAnimationPropLabel )
+ {
+ m_pAnimationPropLabel->SetVisible( true );
+ }
+
+ UpdateAnimationSourceDisplay();
+ UpdateAnimationVCDDisplay();
+ UpdateAnimDurationDisplay();
+ }
+ else
+ {
+ if ( m_pEquipRegionPanel )
+ {
+ m_pEquipRegionPanel->SetVisible( true );
+ }
+ if ( m_pBodygroupsPanel )
+ {
+ m_pBodygroupsPanel->SetVisible( true );
+ }
+ if ( m_pTauntInputPanel )
+ {
+ m_pTauntInputPanel->SetVisible( false );
+ }
+ if ( m_pAnimationPropLabel )
+ {
+ m_pAnimationPropLabel->SetVisible( false );
+ }
+
+ UpdateLODDisplay();
+ UpdateMaterialDisplay();
+ }
+
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetItemPrefab()
+{
+ return GetItemValues()->GetString( kItemPrefab );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::GetItemPrefabValue( const char *pszPrefab, const char *pszName, CUtlString& strOutput )
+{
+ extern void MergeDefinitionPrefab( KeyValues *pKVWriteItem, KeyValues *pKVSourceItem );
+
+ KeyValues *pPrefab = ItemSystem()->GetItemSchema()->FindDefinitionPrefabByName( pszPrefab );
+ if ( !pPrefab )
+ {
+ return false;
+ }
+
+ KeyValuesAD pKVUnpackedPrefab( "prefab" );
+ MergeDefinitionPrefab( pKVUnpackedPrefab, pPrefab );
+
+ strOutput = pKVUnpackedPrefab->GetString( pszName, NULL );
+ return !strOutput.IsEmpty();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SelectClass( int nClassIndex )
+{
+ m_nSelectedClass = nClassIndex;
+
+ if ( m_nPrefab == PREFAB_TAUNT )
+ {
+ UpdateAnimationSourceDisplay();
+ UpdateAnimationVCDDisplay();
+ UpdateAnimDurationDisplay();
+ UpdateLODDisplay();
+ UpdateMaterialDisplay();
+ }
+ else
+ {
+ UpdateBodygroupsDisplay();
+ UpdateLODDisplay();
+ UpdateMaterialDisplay();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetItemIcon( const char *pszFilePath )
+{
+ if ( pszFilePath && *pszFilePath && m_pIconImagePanel )
+ {
+ // clean up old image if there's one
+ m_pIconImagePanel->EvictImage();
+
+ Bitmap_t image;
+ ConversionErrorType nErrorCode = ImgUtl_LoadBitmap( pszFilePath, image );
+ if ( nErrorCode != CE_SUCCESS )
+ {
+ m_pIconImagePanel->SetImage( (vgui::IImage *)0 );
+ ShowMessageBoxWithFile( "#TF_ImportFile_LoadFailed", "#TF_ImportFile_LoadFailedBadFile", pszFilePath );
+ return;
+ }
+
+ if ( image.Width() != 512 || image.Height() != 512 )
+ {
+ ShowMessageBoxWithFile( "#TF_ImportFile_LoadFailed", "#TF_ImportFile_LoadFailedBadIconResolution", pszFilePath );
+ return;
+ }
+
+ if ( image.Format() != IMAGE_FORMAT_RGBA8888 )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", "#TF_ImportFile_LoadFailedImageNot32Bits" );
+ return;
+ }
+
+ int badPixelCount = 0;
+ static const int startSafeZoneY = 91;
+ static const int endSafeZoneY = 420;
+ for ( int y=0; y<512; ++y )
+ {
+ bool bSafeZone = y >= startSafeZoneY && y < endSafeZoneY;
+ // check if icon is outside the safezone
+ for ( int x=0; x<512; ++x )
+ {
+ RGBA8888_t* pPixel = (RGBA8888_t*)image.GetPixel( x, y );
+
+ if ( bSafeZone )
+ {
+ // in the safezone, check if background is black
+ if ( pPixel->a == 0 && ( pPixel->r || pPixel->g || pPixel->b ) )
+ {
+ badPixelCount++;
+ }
+ }
+ else
+ {
+ if ( pPixel->r || pPixel->g || pPixel->b || pPixel->a )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", "#TF_ImportFile_LoadFailedImageDoesNotFitInsideSafeZone" );
+ return;
+ }
+ }
+ }
+ }
+
+ static const int nTotalSafeZonePixels = 512 * ( endSafeZoneY - startSafeZoneY );
+ float flBadPercentage = (float)badPixelCount / nTotalSafeZonePixels;
+ if ( flBadPercentage > 0.05f )
+ {
+ ShowMessageBox( "#TF_ImportFile_LoadFailed", "#TF_ImportFile_LoadFailedBadIconBackground" );
+ return;
+ }
+
+ int wide, tall;
+ BitmapImage *pBitmapImage = new BitmapImage;
+ pBitmapImage->SetBitmap( image );
+ pBitmapImage->GetSize( wide, tall );
+
+ // The ImagePanel needs to know the scaling factor for the BitmapImage, to
+ // center it when it's going to be rendered, and then the BitmapImage needs
+ // to know how big it should be rendered.
+ float fScaleAmount = static_cast<float>( m_pIconImagePanel->GetWide() ) / wide;
+ m_pIconImagePanel->SetShouldCenterImage( true );
+ m_pIconImagePanel->SetShouldScaleImage( true );
+ m_pIconImagePanel->SetScaleAmount( fScaleAmount );
+ pBitmapImage->SetRenderSize( m_pIconImagePanel->GetWide(), static_cast<int>( tall * fScaleAmount ) );
+
+ m_pIconImagePanel->SetImage( pBitmapImage );
+ }
+
+ GetItemValues()->SetString( kItemIcon, pszFilePath );
+ SetDirty( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetItemIcon()
+{
+ return GetItemValues()->GetString( kItemIcon );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetPaintable( bool bPaintable, int nMaterialIndex )
+{
+ const char* pszPaintable = CFmtStr( kItemPaintable, nMaterialIndex );
+ GetItemValues()->SetBool( pszPaintable, bPaintable );
+
+ if ( m_pPaintableCheckButtons[nMaterialIndex] )
+ {
+ m_pPaintableCheckButtons[nMaterialIndex]->SetSelected( bPaintable );
+ }
+
+ // You should check out how paints look on the item
+ SetDirty( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsPaintable( int nMaterialIndex )
+{
+ const char* pszPaintable = CFmtStr( kItemPaintable, nMaterialIndex );
+ return GetItemValues()->GetBool( pszPaintable );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsAnyVMTPaintable()
+{
+ for ( int nMaterialIndex=0; nMaterialIndex<NUM_IMPORT_MATERIALS_PER_TEAM; ++nMaterialIndex )
+ {
+ if ( IsPaintable( nMaterialIndex ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetUserAnimationQCTemplate( int nSelectedClass, bool bPerforce /*= false*/ )
+{
+ Assert( m_nPrefab == PREFAB_TAUNT );
+
+ CUtlString strQCTemplateFile;
+ if ( bPerforce )
+ {
+ // load class_workshop_animations.qc here!
+ CAssetTF asset;
+ asset.SetName( "temp" );
+ asset.SetClass( GetClassFolder() );
+
+ asset.AddModel();
+ const char *pszClassFolder = kClassFolders[ nSelectedClass ];
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+ const char *pszCustomRelativeDir = CFmtStr( "%s/player/animations", GetWorkshopFolder() );
+ pQC->SetCustomRelativeDir( pszCustomRelativeDir );
+ // use class_workshop_animations as shipping QC
+ const char *pszCustomQCOutputName = CFmtStr( "%s_workshop_animations", pszClassFolder );
+ pQC->SetCustomOutputName( pszCustomQCOutputName );
+ pQC->SetQCITemplate( GetQCTemplate( nSelectedClass ) );
+
+ // add temp DMX to get QC path
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassAnimationSourceFile, pszClassFolder ) );
+ asset.AddTargetDMX( pszFilePath );
+
+ m_tempQC.Clear();
+ if ( pQC->GetOutputPath( strQCTemplateFile, 0 ) )
+ {
+ // if the QC was checked out, revert it first
+ char szCorrectCaseFilePath[MAX_PATH];
+ g_pFullFileSystem->GetCaseCorrectFullPath( strQCTemplateFile.String(), szCorrectCaseFilePath );
+ CP4AutoRevertFile revertFile( szCorrectCaseFilePath );
+
+ if ( g_pFullFileSystem->ReadFile( strQCTemplateFile, NULL, m_tempQC ) )
+ {
+ m_tempQC.PutString( "\n" );
+ m_tempQC.PutString( "$pushd \"../../../<QCI_RELATIVE_DIR>\"\n" );
+ m_tempQC.PutString( "$include \"../../../<QCI_RELATIVE_PATH>\"\n" );
+ m_tempQC.PutString( "$popd\n" );
+
+ return (char *)m_tempQC.Base();
+ }
+ }
+ }
+ else if ( GetItemPrefabValue( GetItemPrefab(), "qc_template", strQCTemplateFile ) )
+ {
+ m_tempQC.Clear();
+ if ( g_pFullFileSystem->ReadFile( strQCTemplateFile, "MOD", m_tempQC ) )
+ {
+ return (char *)m_tempQC.Base();
+ }
+ }
+
+ Warning( "Failed to load specified QC template '%s'.\n", strQCTemplateFile.String() );
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetQCTemplate( int nSelectedClass )
+{
+ const char *pszQCText = GetItemValues()->GetString( CFmtStr( kClassQC, kClassFolders[nSelectedClass] ) );
+ if ( V_strlen(pszQCText) == 0 )
+ {
+ CUtlString strQCTemplateFile;
+ if ( GetItemPrefabValue( m_nPrefab == PREFAB_TAUNT ? "misc" : GetItemPrefab(), "qc_template", strQCTemplateFile ) )
+ {
+ m_tempQC.Clear();
+ if ( g_pFullFileSystem->ReadFile( strQCTemplateFile, "MOD", m_tempQC ) )
+ {
+ return (char *)m_tempQC.Base();
+ }
+ else
+ {
+ Warning( "Failed to load specified QC template '%s'.\n", strQCTemplateFile.String() );
+ }
+ }
+
+ return CAssetTF().GetTargetQC()->GetQCTemplate();
+ }
+ return pszQCText;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetQCITemplate( int nSelectedClass )
+{
+ const char *pszQCText = GetItemValues()->GetString( CFmtStr( kClassQCI, kClassFolders[nSelectedClass] ) );
+ if ( V_strlen(pszQCText) == 0 )
+ {
+ CUtlString strQCITemplateFile = CItemUpload::Manifest()->GetQCITemplate();
+ CAssetTF asset;
+ m_tempQC.Clear();
+ if ( g_pFullFileSystem->ReadFile( strQCITemplateFile.String(), "MOD", m_tempQC ) )
+ {
+ return (char *)m_tempQC.Base();
+ }
+ else
+ {
+ Warning( "Failed to load specified QCI template '%s'.\n", strQCITemplateFile.String() );
+ }
+ }
+ return pszQCText;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::ShouldP4AddOrEdit() const
+{
+ return p4 && m_pPerforceCheckButton && m_pPerforceCheckButton->IsSelected();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsPartnerContent() const
+{
+ return m_pPartnerCheckButton && m_pPartnerCheckButton->IsVisible() && m_pPartnerCheckButton->IsSelected();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char* CTFFileImportDialog::GetWorkshopFolder() const
+{
+ return IsPartnerContent() ? "workshop_partner" : "workshop";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFFileImportDialog::GetCustomBones( int selectedClass, const char* pszFileName, CUtlStringList& strBoneList )
+{
+ strBoneList.PurgeAndDeleteElements();
+
+ CUtlBuffer hdrBuf;
+ const studiohdr_t *pItemStudioHdr = NULL;
+ if ( g_pFullFileSystem->ReadFile( pszFileName, NULL, hdrBuf ) )
+ {
+ pItemStudioHdr = reinterpret_cast< const studiohdr_t * >( hdrBuf.Base() );
+ }
+
+ if ( pItemStudioHdr == NULL )
+ {
+ return false;
+ }
+
+ if ( pItemStudioHdr )
+ {
+ m_pPlayerModelPanel->SetToPlayerClass( selectedClass, false );
+ const studiohdr_t* pClassStudioHdr = m_pPlayerModelPanel->GetStudioHdr();
+ for ( int iItemBone=0; iItemBone<pItemStudioHdr->numbones; ++iItemBone )
+ {
+ const char* pszItemBoneName = pItemStudioHdr->pBone( iItemBone )->pszName();
+ bool bCustomBone = true;
+ for ( int iClassBone=0; iClassBone<pClassStudioHdr->numbones; ++iClassBone )
+ {
+ const char* pszClassBoneName = pClassStudioHdr->pBone( iClassBone )->pszName();
+ if ( FStrEq( pszItemBoneName, pszClassBoneName ) )
+ {
+ bCustomBone = false;
+ break;
+ }
+ }
+
+ if ( bCustomBone )
+ {
+ strBoneList.CopyAndAddToTail( pszItemBoneName );
+ }
+ }
+ }
+
+ return strBoneList.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::ClearLODs()
+{
+ for ( int iClassIndex=TF_FIRST_NORMAL_CLASS; iClassIndex<TF_LAST_NORMAL_CLASS; ++iClassIndex )
+ {
+ for ( int iLOD=0; iLOD<NUM_IMPORT_LODS; ++iLOD )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ iClassIndex ], iLOD ) );
+ if ( pKey )
+ {
+ pKey->Clear();
+ }
+ }
+
+ if ( m_pClassHighlights[ iClassIndex ] )
+ {
+ m_pClassHighlights[ iClassIndex ]->SetVisible( false );
+ }
+ }
+
+ Assert( !AnyClassHasModels() );
+ ClearMaterials();
+
+ SetDirty( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::SetLOD( int selectedClass, int nModelIndex, const char *pszFilePath, KeyValues* pKV /*= NULL*/ )
+{
+ if ( selectedClass == TF_CLASS_UNDEFINED )
+ {
+ return LOAD_FAILED;
+ }
+
+ // Make sure we can load the model first
+ if ( *pszFilePath )
+ {
+ CAssetTF asset;
+ if ( asset.AddTargetDMX( pszFilePath ) < 0 )
+ {
+ return LOAD_FAILED_BADMODEL;
+ }
+
+ int nMaxTris = GetModelTriangleBudget( selectedClass, nModelIndex );
+ int nTriCount = asset.GetTargetDMX( 0 )->GetTriangleCount();
+ if ( nTriCount > nMaxTris )
+ {
+ pKV->SetInt( "count", nTriCount );
+ pKV->SetInt( "limit", nMaxTris );
+ SetMessageFileVariable( pKV, pszFilePath );
+
+ return LOAD_FAILED_COMPLEXMODEL;
+ }
+
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+ pQC->SetQCTemplate( GetQCTemplate( m_nSelectedClass ) );
+
+ // need to compile dmx to output mdl file to get studiohdr_t for bone name list
+ asset.SetName( "temp" );
+ asset.SetClass( kClassFolders[ selectedClass ] );
+ if ( asset.CompilePreview() )
+ {
+ const CUtlVector< CUtlString >& strBuiltFiles = asset.GetBuiltFiles();
+ CUtlString strMDLPath;
+ for ( int i=0; i<strBuiltFiles.Count(); ++i )
+ {
+ char pszExtention[16];
+ V_ExtractFileExtension( strBuiltFiles[i], pszExtention, ARRAYSIZE( pszExtention ) );
+ if ( FStrEq( pszExtention, "mdl" ) )
+ {
+ strMDLPath = strBuiltFiles[i];
+ break;
+ }
+ }
+
+ CUtlStringList strBoneList;
+ int nCustomBones = GetCustomBones( selectedClass, strMDLPath.String(), strBoneList );
+
+ // remove all the temp file
+ for ( int i=0; i<strBuiltFiles.Count(); ++i )
+ {
+ g_pFullFileSystem->RemoveFile( strBuiltFiles[i] );
+ }
+
+ const int nCustomBoneLimit = GetModelBoneBudget();
+ if ( nCustomBones > nCustomBoneLimit )
+ {
+ CUtlString strCustomBones = strBoneList[0];
+ for ( int i=1; i<nCustomBones; ++i )
+ {
+ strCustomBones += CFmtStr( ", %s", strBoneList[i] );
+ }
+ pKV->SetInt( "count", nCustomBones );
+ pKV->SetInt( "limit", nCustomBoneLimit );
+ pKV->SetString( "custom_bones", strCustomBones.String() );
+ SetMessageFileVariable( pKV, pszFilePath );
+
+ return LOAD_FAILED_TOOMANYBONES;
+ }
+ }
+
+ int nMaterialCount = asset.GetTargetVMTCount();
+ if ( nMaterialCount > NUM_IMPORT_MATERIALS_PER_TEAM )
+ {
+ pKV->SetInt( "count", nMaterialCount );
+ pKV->SetInt( "limit", NUM_IMPORT_MATERIALS_PER_TEAM );
+ SetMessageFileVariable( pKV, pszFilePath );
+ return LOAD_FAILED_TOOMANYMATERIALS;
+ }
+
+ // check if material count is not more than the higher LOD
+ if ( nModelIndex > 0 )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ selectedClass ], nModelIndex - 1 ) );
+ Assert( pKey );
+ if ( pKey )
+ {
+ int nHigherLODMaterialCount = pKey->GetInt( "materialCount" );
+ if ( nHigherLODMaterialCount < nMaterialCount )
+ {
+ SetMessageFileVariable( pKV, pszFilePath );
+ return LOAD_FAILED_MATERIALCOUNTMISMATCH;
+ }
+ }
+ }
+
+ for ( int i = 0; i < nMaterialCount; ++i )
+ {
+ CFmtStr sMaterialName( kMaterialN, i );
+ if ( !GetItemValues()->GetString( sMaterialName, NULL ) )
+ {
+ char pszMaterialId[MAX_PATH];
+ V_FileBase( asset.GetTargetVMT( i )->GetMaterialId(), pszMaterialId, sizeof(pszMaterialId) );
+ GetItemValues()->SetString( sMaterialName, pszMaterialId );
+ }
+ }
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ selectedClass ], nModelIndex ), true );
+ pKey->SetString( "file", pszFilePath );
+ pKey->SetInt( "polyCount", asset.GetTargetDMX( 0 )->GetPolyCount() );
+ pKey->SetInt( "triangleCount", nTriCount );
+ pKey->SetInt( "vertexCount", asset.GetTargetDMX( 0 )->GetVertexCount() );
+ pKey->SetInt( "materialCount", nMaterialCount );
+
+ // if we're already under the lowest budget, no need for more LODs
+ if ( nTriCount <= GetModelTriangleBudget( selectedClass, NUM_IMPORT_LODS - 1 ) )
+ {
+ for ( int i=nModelIndex+1; i<NUM_IMPORT_LODS; ++i )
+ {
+ KeyValues *pKeyLOD = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ selectedClass ], i ) );
+ if ( pKeyLOD )
+ {
+ pKeyLOD->Clear();
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i=nModelIndex; i<NUM_IMPORT_LODS; ++i )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ selectedClass ], i ) );
+ if ( pKey )
+ {
+ pKey->Clear();
+ }
+ }
+
+ if ( !AnyClassHasModels() )
+ {
+ ClearMaterials();
+ }
+ }
+
+ SetDirty( true );
+
+ if ( selectedClass == m_nSelectedClass )
+ {
+ UpdateLODDisplay();
+ }
+ UpdateMaterialDisplay();
+
+ // Show a highlight for classes that have models
+ if ( m_pClassHighlights[ selectedClass ] )
+ {
+ m_pClassHighlights[ selectedClass ]->SetVisible( ClassHasModels( selectedClass ) );
+ }
+
+ return LOAD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateLODDisplay()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ int nMinTriRequirement = GetModelTriangleBudget( m_nSelectedClass, NUM_IMPORT_LODS - 1 );
+ int nPreviousTriCount = INT_MAX;
+
+ for ( int i=0; i<NUM_IMPORT_LODS; ++i )
+ {
+ const char *pszFilePath = "";
+ int nTriCount = 0;
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ m_nSelectedClass ], i ) );
+ if ( pKey )
+ {
+ pszFilePath = pKey->GetString( "file" );
+ nTriCount = pKey->GetInt( "triangleCount" );
+ }
+
+ if ( nPreviousTriCount != 0 && nPreviousTriCount > nMinTriRequirement )
+ {
+ SetLODPanelEnable( true, i );
+ }
+ else
+ {
+ SetLODPanelEnable( false, i );
+ }
+
+ vgui::Label *pFileLabel = m_pLODFiles[ i ];
+ if ( pFileLabel )
+ {
+ if ( *pszFilePath )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszFilePath, file, sizeof(file) );
+ pFileLabel->SetText( file );
+ }
+ else
+ {
+ pFileLabel->SetText( "#TF_PublishFile_NoFileSelected" );
+ }
+ }
+
+ vgui::Label *pDetailsLabel = m_pLODDetails[ i ];
+ if ( pDetailsLabel )
+ {
+ if ( *pszFilePath )
+ {
+ wchar_t details[1024];
+ g_pVGuiLocalize->ConstructString_safe( details, "#TF_ImportFile_ModelDetails", pKey );
+ pDetailsLabel->SetText( details, true );
+ }
+ else
+ {
+ pDetailsLabel->SetText( "" );
+ }
+ }
+
+ nPreviousTriCount = nTriCount;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFFileImportDialog::GetModelTriangleBudget( int selectedClass, int nModelIndex )
+{
+ // Return the budget for this item type
+ if ( nModelIndex >= NUM_IMPORT_LODS )
+ {
+ Warning( "GetModelTriangleBudget: bad model index\n" );
+ return 0;
+ }
+
+ int nTriangleBudget = kDefaultTriangleBudget[nModelIndex];
+
+ CUtlString strTriangleBudget;
+ if ( GetItemPrefabValue( GetItemPrefab(), CFmtStr( "triangle_budget_lod%d", nModelIndex ), strTriangleBudget ) )
+ {
+ nTriangleBudget = V_atoi( strTriangleBudget.String() );
+ }
+
+ return nTriangleBudget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFFileImportDialog::GetModelBoneBudget()
+{
+ int nBoneBudget = kDefaultBoneBudget;
+ CUtlString strBoneBudget;
+ if ( GetItemPrefabValue( GetItemPrefab(), "custom_bone_budget", strBoneBudget ) )
+ {
+ nBoneBudget = V_atoi( strBoneBudget.String() );
+ }
+
+ return nBoneBudget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::SetMaterial( int nMaterialPanelIndex, const char* pszFilePath, MATERIAL_FILE_TYPE fileType )
+{
+ int selectedSkin = nMaterialPanelIndex / NUM_IMPORT_MATERIALS_PER_TEAM;
+ int nMaterialIndex = nMaterialPanelIndex % NUM_IMPORT_MATERIALS_PER_TEAM;
+
+ return SetMaterial( selectedSkin, nMaterialIndex, pszFilePath, fileType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::SetMaterial( int selectedSkin, int nMaterialIndex, const char *pszFilePath, MATERIAL_FILE_TYPE fileType )
+{
+ if ( *pszFilePath )
+ {
+ CAssetTF asset;
+ CTargetTGA image( &asset, NULL );
+ if ( !image.SetInputFile( pszFilePath ) )
+ {
+ return false;
+ }
+
+ const char *pszMaterialFilePrefix = s_pszMaterialFilePrefixes[fileType];
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( selectedSkin ), nMaterialIndex ), true );
+ pKey->SetString( CFmtStr( "%s_texture_file", pszMaterialFilePrefix ), pszFilePath );
+ pKey->SetInt( CFmtStr( "%s_texture_width", pszMaterialFilePrefix ), image.GetWidth() );
+ pKey->SetInt( CFmtStr( "%s_texture_height", pszMaterialFilePrefix ), image.GetHeight() );
+ pKey->SetInt( CFmtStr( "%s_texture_channels", pszMaterialFilePrefix ), image.GetChannelCount() );
+ pKey->SetBool( CFmtStr( "%s_texture_alpha", pszMaterialFilePrefix ), image.HasAlpha() );
+ }
+ else
+ {
+ static const char* s_removeKeyNames[] =
+ {
+ "%s_texture_file",
+ "%s_texture_width",
+ "%s_texture_height",
+ "%s_texture_channels",
+ "%s_texture_alpha"
+ };
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( selectedSkin ), nMaterialIndex ) );
+ if ( pKey )
+ {
+ const char *pszMaterialFilePrefix = s_pszMaterialFilePrefixes[fileType];
+ for ( int i=0; i<ARRAYSIZE( s_removeKeyNames ); ++i )
+ {
+ KeyValues *pSubKey = pKey->FindKey( CFmtStr( s_removeKeyNames[i], pszMaterialFilePrefix ) );
+ if ( pSubKey )
+ {
+ pKey->RemoveSubKey( pSubKey );
+ }
+ }
+ }
+ }
+
+ SetDirty( true );
+
+ UpdateMaterialDisplay( selectedSkin, nMaterialIndex );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char* CTFFileImportDialog::GetMaterialTextureFile( int selectedSkin, int nMaterialIndex, MATERIAL_FILE_TYPE fileType )
+{
+ KeyValues* pMaterialKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( selectedSkin ), nMaterialIndex ) );
+ if ( pMaterialKey )
+ {
+ const char *pszMaterialFilePrefix = s_pszMaterialFilePrefixes[fileType];
+ return pMaterialKey->GetString( CFmtStr( "%s_texture_file", pszMaterialFilePrefix ) );
+ }
+
+ return "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CUtlString CTFFileImportDialog::GetMaterialName( int selectedSkin, int nMaterialIndex )
+{
+ const char* pszFilePath = GetMaterialTextureFile( selectedSkin, nMaterialIndex, MATERIAL_FILE_BASETEXTURE );
+ if ( FStrEq( pszFilePath, "" ) )
+ {
+ return "";
+ }
+
+ // Turn the texture name into vmt name
+ CUtlString strMaterialName = V_GetFileName( pszFilePath );
+ strMaterialName += ".vmt";
+
+ return strMaterialName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::ClearMaterials()
+{
+ KeyValues *pKeyParent = GetItemValues()->FindKey( "Materials" );
+ if ( pKeyParent )
+ {
+ for ( int nMaterialIndex = 0; nMaterialIndex < NUM_IMPORT_MATERIALS_PER_TEAM; ++nMaterialIndex )
+ {
+ KeyValues *pKey = pKeyParent->FindKey( CFmtStr( "Material%d", nMaterialIndex ) );
+ if ( pKey )
+ {
+ pKeyParent->RemoveSubKey( pKey );
+ pKey->deleteThis();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::ClearMaterial( int nSkinIndex, int nMaterialIndex )
+{
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex ), nMaterialIndex ) );
+ if ( pKey )
+ {
+ pKey->Clear();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateMaterialDisplay()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ int nModelMaterials = 0;
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ m_nSelectedClass ], 0 ) );
+ if ( pKey )
+ {
+ nModelMaterials = pKey->GetInt( "materialCount" );
+ }
+
+ if ( m_pSwapVMTButton )
+ {
+ m_pSwapVMTButton->SetVisible( nModelMaterials > 1 );
+ }
+
+ int nSkinType = GetItemValues()->GetInt( kSkinType );
+ for ( int nMaterialPaneIndex = 0; nMaterialPaneIndex < MAX_MATERIAL_COUNT; ++nMaterialPaneIndex )
+ {
+ vgui::Panel *pMaterialPanel = m_pMaterialPanels[ nMaterialPaneIndex ];
+ if (!pMaterialPanel)
+ {
+ continue;
+ }
+
+ int nSkinIndex = nMaterialPaneIndex / NUM_IMPORT_MATERIALS_PER_TEAM;
+ int nMaterialIndex = nMaterialPaneIndex % NUM_IMPORT_MATERIALS_PER_TEAM;
+ bool bWasVisible = pMaterialPanel->IsVisible();
+ bool bVisible = nSkinIndex <= nSkinType && nMaterialIndex < nModelMaterials;
+ pMaterialPanel->SetVisible( bVisible );
+
+ if ( bVisible )
+ {
+ if ( !bWasVisible )
+ {
+ pMaterialPanel->InvalidateLayout( true, true );
+ }
+ UpdateMaterialDisplay( nSkinIndex, nMaterialIndex );
+ }
+ }
+
+ // update paintable buttons
+ for ( int nMaterialIndex=0; nMaterialIndex<m_pPaintableCheckButtons.Count(); ++nMaterialIndex )
+ {
+ bool bVisible = nMaterialIndex < nModelMaterials;
+ if ( m_pPaintableCheckButtons[nMaterialIndex] )
+ {
+ m_pPaintableCheckButtons[nMaterialIndex]->SetVisible( bVisible );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateMaterialDisplay( int nSkinIndex, int nMaterialIndex )
+{
+ int nMaterialPanelIndex = nSkinIndex * NUM_IMPORT_MATERIALS_PER_TEAM + nMaterialIndex;
+ if ( nMaterialPanelIndex >= MAX_MATERIAL_COUNT )
+ {
+ // nSkinIndex could be 2 for Preview Image
+ return;
+ }
+
+ vgui::Label *pLabel = m_pMaterialLabels[ nMaterialPanelIndex ];
+ if ( pLabel )
+ {
+ const char* pszInputName = "Input";
+ if ( GetItemValues()->GetInt( kSkinType ) )
+ {
+ pszInputName = ( nSkinIndex == 0 ) ? "Red Input" : "Blue Input";
+ }
+
+ pLabel->SetText( CFmtStr( "%s: \"%s\"", pszInputName, GetItemValues()->GetString( CFmtStr( kMaterialN, nMaterialIndex ) ) ) );
+ }
+
+ pLabel = m_pMaterialFiles[ nMaterialPanelIndex ];
+ if ( pLabel )
+ {
+ const char *pszFilePath = GetMaterialTextureFile( nSkinIndex, nMaterialIndex, MATERIAL_FILE_BASETEXTURE );
+ if ( *pszFilePath )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszFilePath, file, sizeof(file) );
+ pLabel->SetText( file );
+ pLabel->SetFgColor( Color( 255, 255, 255, 255 ) );
+ }
+ else
+ {
+ pLabel->SetText( "#TF_PublishFile_NoFileSelected" );
+ pLabel->SetFgColor( Color( 255, 0, 0, 255 ) );
+ }
+ }
+}
+
+const char *CTFFileImportDialog::GetMaterialText( int nSkinIndex, int nMaterialIndex, CUtlBuffer &sMaterialText )
+{
+ sMaterialText.Clear();
+ sMaterialText.SetBufferType( true, true );
+
+ if ( !sMaterialText.TellPut() )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex ), nMaterialIndex ) );
+ if ( pKey )
+ {
+ KeyValuesAD pVMTKey( "VMT" );
+ if ( pVMTKey->LoadFromBuffer( "VMT", pKey->GetString( "vmt" ) ) )
+ {
+ if ( ValidateMaterialValues( pVMTKey, nMaterialIndex ) == BUILD_OKAY )
+ {
+ sMaterialText.PutString( pKey->GetString( "vmt" ) );
+ }
+ else
+ {
+ // HACK! We had proxies bug that will invalidate the material
+ // Try to update the proxies from the template to see if this fixes the issue
+ KeyValues *pCurrentProxies = pVMTKey->FindKey( "Proxies" );
+ if ( pCurrentProxies )
+ {
+ KeyValuesAD pKVTemplate( "Template" );
+ if ( pKVTemplate->LoadFromFile( g_pFullFileSystem, "materials/models/player/items/templates/standard.vmt", "GAME" ) )
+ {
+ KeyValues *pTemplateProxiesKey = pKVTemplate->FindKey( "Proxies" );
+ if ( pTemplateProxiesKey )
+ {
+ // clear old proxies and copy a new one
+ pCurrentProxies->Clear();
+ pCurrentProxies->RecursiveMergeKeyValues( pTemplateProxiesKey );
+
+ // try again
+ if ( ValidateMaterialValues( pVMTKey, nMaterialIndex ) == BUILD_OKAY )
+ {
+ sMaterialText.SetBufferType( true, true );
+ pVMTKey->RecursiveSaveToFile( sMaterialText, 0, false, true );
+ sMaterialText.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !sMaterialText.TellPut() )
+ {
+ g_pFullFileSystem->ReadFile( "materials/models/player/items/templates/standard.vmt", "GAME", sMaterialText );
+ }
+
+ if ( !sMaterialText.TellPut() )
+ {
+ return NULL;
+ }
+ return sMaterialText.String();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::SetMaterialText( int nSkinIndex, int nMaterialIndex, const char* pszMaterialText )
+{
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kMaterialSkinN, CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex ), nMaterialIndex ), true );
+ if ( pKey )
+ {
+ pKey->SetString( "vmt", pszMaterialText );
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::ValidateMaterialValues( KeyValues *pKV, int nMaterialIndex )
+{
+ static const char *MATERIAL_SHADER = "VertexlitGeneric";
+ static const int CONDITION_PAINTABLE = 0x01;
+ static const int CONDITION_NOTPAINTABLE = 0x02;
+ static const int MAX_MATERIAL_VARIABLES = 8;
+ static struct MaterialCheck {
+ BUILD_RESULT nErrorResult;
+ int nCondition;
+ const char *vecVariables[MAX_MATERIAL_VARIABLES];
+ } sVecMaterialChecks[] = {
+ {
+ BUILD_FAILED_MATERIALMISSINGCLOAK, 0,
+ {
+ "$cloakPassEnabled", "Proxies/invis"
+ }
+ },
+ {
+ BUILD_FAILED_MATERIALMISSINGBURNING, 0,
+ {
+ "$detail", "$detailscale", "$detailblendfactor", "$detailblendmode", "Proxies/AnimatedTexture", "Proxies/BurnLevel"
+ }
+ },
+ {
+ BUILD_FAILED_MATERIALMISSINGJARATE, CONDITION_NOTPAINTABLE,
+ {
+ "$yellow", "Proxies/YellowLevel", "Proxies/Equals"
+ }
+ },
+ {
+ BUILD_FAILED_MATERIALMISSINGJARATE, CONDITION_PAINTABLE,
+ {
+ "$yellow", "Proxies/YellowLevel", "Proxies/Multiply"
+ }
+ },
+ {
+ BUILD_FAILED_MATERIALMISSINGPAINTABLE, CONDITION_PAINTABLE,
+ {
+ "$blendtintbybasealpha", "$blendtintcoloroverbase", "$colortint_base", "$color2", "$colortint_tmp",
+ "Proxies/ItemTintColor", "Proxies/SelectFirstIfNonZero"
+ }
+ },
+ };
+
+ if ( V_strcasecmp( pKV->GetName(), MATERIAL_SHADER ) != 0 )
+ {
+ return BUILD_FAILED_MATERIALMISSINGSHADER;
+ }
+
+ for ( int iCheck = 0; iCheck < ARRAYSIZE(sVecMaterialChecks); ++iCheck )
+ {
+ const MaterialCheck &check = sVecMaterialChecks[ iCheck ];
+
+ bool bIsPaintable = IsPaintable( nMaterialIndex );
+
+ if ( ( check.nCondition & CONDITION_PAINTABLE ) && !bIsPaintable )
+ {
+ continue;
+ }
+
+ if ( ( check.nCondition & CONDITION_NOTPAINTABLE ) && bIsPaintable )
+ {
+ continue;
+ }
+
+ for ( int i = 0; i < ARRAYSIZE(check.vecVariables) && check.vecVariables[i]; ++i )
+ {
+ if ( !pKV->FindKey( check.vecVariables[i] ) )
+ {
+ return check.nErrorResult;
+ }
+ }
+ }
+
+ // Ugh, terrible hack...
+ // If a KeyValues key doesn't have a value or subkeys, it won't get written out.
+ // Unfortunately we need Proxies/invis to be written out as an empty set,
+ // so it needs to have _something_ in it to force it to be written to disk.
+ const char *kSubkeyHack = "hack_not_written_to_disk";
+ KeyValues *pSubKey = pKV->FindKey( "Proxies/invis" );
+ Assert(pSubKey);
+ if ( !pSubKey->FindKey( kSubkeyHack ) )
+ {
+ pSubKey->AddSubKey( new KeyValues( kSubkeyHack ) );
+ }
+
+ return BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemoveUnnecessaryParametersFromVMT( KeyValues *pKV, int nMaterialIndex )
+{
+ RemoveLightParameters( pKV, nMaterialIndex );
+ RemovePaintParameters( pKV, nMaterialIndex );
+ RemoveTranslucentParameters( pKV );
+ RemoveCubeMapParameters( pKV );
+ RemoveSelfIllumParameters( pKV );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemoveLightParameters( KeyValues *pKV, int nMaterialIndex )
+{
+ static const char *vecLightingKeys[] =
+ {
+ "$basemapalphaphongmask", "$halflambert"
+ };
+ for ( int i=0; i<ARRAYSIZE(vecLightingKeys); ++i )
+ {
+ const char *pszLightKeyName = vecLightingKeys[i];
+ if ( !pKV->GetBool( pszLightKeyName ) )
+ {
+ KeyValues *pSubKey = pKV->FindKey( pszLightKeyName );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+ }
+
+ if ( FStrEq( GetMaterialTextureFile( 0, nMaterialIndex, MATERIAL_FILE_PHONGEXPONENT ), "" ) )
+ {
+ KeyValues* pSubKey = pKV->FindKey( "$rimmask" );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemovePaintParameters( KeyValues *pKV, int nMaterialIndex )
+{
+ if ( IsPaintable( nMaterialIndex ) )
+ {
+ static const char *vecNonPaintableProxyKeys[] =
+ {
+ "Equals"
+ };
+
+ // Now remove proxies
+ KeyValues *pProxyKV = pKV->FindKey( "Proxies" );
+ if ( pProxyKV )
+ {
+ for ( int iProxyKey = 0; iProxyKey < ARRAYSIZE(vecNonPaintableProxyKeys); ++iProxyKey )
+ {
+ KeyValues *pKey = pProxyKV->FindKey( vecNonPaintableProxyKeys[ iProxyKey ] );
+ if ( pKey )
+ {
+ pProxyKV->RemoveSubKey( pKey );
+ pKey->deleteThis();
+ }
+ }
+ }
+
+ return;
+ }
+
+ static const char *vecPaintableKeys[] =
+ {
+ "$blendtintbybasealpha", "$blendtintcoloroverbase", "$colortint_base", "$colortint_tmp"
+ };
+ static const char *vecPaintableProxyKeys[] =
+ {
+ "ItemTintColor", "SelectFirstIfNonZero", "Multiply"
+ };
+
+ // First remove all the variables
+ for ( int iKey = 0; iKey < ARRAYSIZE(vecPaintableKeys); ++iKey )
+ {
+ KeyValues *pKey = pKV->FindKey( vecPaintableKeys[ iKey ] );
+ if ( pKey )
+ {
+ pKV->RemoveSubKey( pKey );
+ pKey->deleteThis();
+ }
+ }
+
+ // Now remove proxies
+ KeyValues *pProxyKV = pKV->FindKey( "Proxies" );
+ if ( pProxyKV )
+ {
+ for ( int iProxyKey = 0; iProxyKey < ARRAYSIZE(vecPaintableProxyKeys); ++iProxyKey )
+ {
+ KeyValues *pKey = pProxyKV->FindKey( vecPaintableProxyKeys[ iProxyKey ] );
+ if ( pKey )
+ {
+ pProxyKV->RemoveSubKey( pKey );
+ pKey->deleteThis();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemoveTranslucentParameters( KeyValues *pKV )
+{
+ if ( !pKV->GetBool( "$additive" ) )
+ {
+ KeyValues *pSubKey = pKV->FindKey( "$additive" );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+
+ if ( !pKV->GetBool( "$translucent" ) )
+ {
+ KeyValues *pSubKey = pKV->FindKey( "$translucent" );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+
+ pSubKey = pKV->FindKey( "$alphatest" );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemoveCubeMapParameters( KeyValues *pKV )
+{
+ static const char* vecEnvmapKeys[] =
+ {
+ "$envmap",
+ "$basealphaenvmapmask",
+ "$normalmapalphaenvmapmask",
+ "$envmaptint"
+ };
+
+ const char* pszEnvmapName = pKV->GetString( "$envmap" );
+ if ( FStrEq( pszEnvmapName, "" ) )
+ {
+ for ( int i=0; i<ARRAYSIZE(vecEnvmapKeys); ++i )
+ {
+ const char* pszEnvmapKey = vecEnvmapKeys[i];
+ KeyValues *pSubKey = pKV->FindKey( pszEnvmapKey );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::RemoveSelfIllumParameters( KeyValues *pKV )
+{
+ static const char* vecSelfIllumKeys[] =
+ {
+ "$selfillum",
+ "$selfillumtint",
+ "$selfillummask"
+ };
+
+ bool bSelfIllum = false;
+ KeyValues *pDX9Key = pKV->FindKey( ">=DX90" );
+ if ( pDX9Key )
+ {
+ bSelfIllum = pDX9Key->GetBool( "$selfillum" );
+ }
+
+ if ( !bSelfIllum )
+ {
+ for ( int i=0; i<ARRAYSIZE( vecSelfIllumKeys ); ++i )
+ {
+ KeyValues *pSubKey = pKV->FindKey( vecSelfIllumKeys[i] );
+ if ( pSubKey )
+ {
+ pKV->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateBodygroupsDisplay()
+{
+ KeyValues *pKey = GetItemValues()->FindKey( kBodygroup );
+ for ( int i=0; i<ARRAYSIZE( kBodygroupArray ); ++i )
+ {
+ const char* pszBodygroupName = kBodygroupArray[i];
+ vgui::CheckButton* pButton = m_pBodygroups[i];
+ if ( pButton )
+ {
+ pButton->SetCheckButtonCheckable( true );
+ pButton->SetSelected( !pKey || !pKey->GetBool( pszBodygroupName ) );
+
+ if ( m_nSelectedClass != TF_CLASS_UNDEFINED )
+ {
+ m_pPlayerModelPanel->SetToPlayerClass( m_nSelectedClass, false );
+ const studiohdr_t* pMDL = m_pPlayerModelPanel->GetStudioHdr();
+
+ bool bEnabled = false;
+ for ( int iBodygroup=0; iBodygroup<pMDL->numbodyparts; ++iBodygroup )
+ {
+ const char* pszValidBodygroup = pMDL->pBodypart( iBodygroup )->pszName();
+ if ( FStrEq( pszValidBodygroup, pszBodygroupName ) )
+ {
+ bEnabled = true;
+ break;
+ }
+ }
+
+ pButton->SetEnabled( bEnabled );
+ }
+ pButton->SetCheckButtonCheckable( pButton->IsEnabled() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetBodygroup( KeyValues* pBodygroupKey )
+{
+ if ( !pBodygroupKey )
+ {
+ return;
+ }
+
+ KeyValues *pKey = GetItemValues()->FindKey( kBodygroup, true );
+ if ( pKey )
+ {
+ pKey->Clear();
+ for ( int i=0; i<ARRAYSIZE( kBodygroupArray ); ++i )
+ {
+ const char* pszBodygroup = kBodygroupArray[i];
+ if ( pBodygroupKey->GetBool( pszBodygroup ) )
+ {
+ pKey->SetBool( pszBodygroup, true );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::SetAnimationSource( int selectedClass, const char *pszFilePath, KeyValues* pKV /*= NULL*/ )
+{
+ if ( selectedClass == TF_CLASS_UNDEFINED )
+ {
+ SetMessageFileVariable( pKV, pszFilePath );
+ return LOAD_FAILED;
+ }
+
+ // Make sure we can load the model first
+ if ( pszFilePath && *pszFilePath )
+ {
+ CAssetTF asset;
+
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+ pQC->SetQCTemplate( GetUserAnimationQCTemplate( selectedClass ) );
+ pQC->SetQCITemplate( GetQCTemplate( selectedClass ) );
+
+ if ( asset.AddTargetDMX( pszFilePath ) < 0 )
+ {
+ SetMessageFileVariable( pKV, pszFilePath );
+ return LOAD_FAILED_BADMODEL;
+ }
+
+ CSmartPtr< CTargetDMX > pDMX = pQC->GetTargetDMX( 0 );
+ float flFrameRate;
+ int nFrameCount;
+ float flAnimDuration = 0.f;
+ if ( pDMX.IsValid() && pDMX->GetAnimationFrameInfo( flFrameRate, nFrameCount ) )
+ {
+ flAnimDuration = (float)nFrameCount/flFrameRate;
+ if ( flAnimDuration > GetMaxTauntDuration() )
+ {
+ if ( pKV )
+ {
+ pKV->SetFloat( "current_anim_duration", flAnimDuration );
+ pKV->SetInt( "max_anim_duration", GetMaxTauntDuration() );
+ }
+ SetMessageFileVariable( pKV, pszFilePath );
+
+ return LOAD_FAIL_ANIMATIONTOOLONG;
+ }
+ }
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ selectedClass ] ), true );
+ pKey->SetString( "source_file", pszFilePath );
+
+ SetAnimationDuration( selectedClass, flAnimDuration );
+ }
+ else
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ selectedClass ] ) );
+ if ( pKey )
+ {
+ KeyValues *pVCDKey = pKey->FindKey( "source_file" );
+ if ( pVCDKey )
+ {
+ pKey->RemoveSubKey( pVCDKey );
+ pVCDKey->deleteThis();
+ }
+ }
+ }
+
+ SetDirty( true );
+
+ if ( selectedClass == m_nSelectedClass )
+ {
+ UpdateAnimationSourceDisplay();
+ }
+
+ // Show a highlight for classes that have models
+ if ( m_pClassHighlights[ selectedClass ] )
+ {
+ m_pClassHighlights[ selectedClass ]->SetVisible( ClassHasTauntSources( selectedClass ) );
+ }
+
+ return LOAD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::SetAnimationVCD( int selectedClass, const char *pszFilePath, KeyValues* pKV /*= NULL*/ )
+{
+ if ( selectedClass == TF_CLASS_UNDEFINED )
+ {
+ SetMessageFileVariable( pKV, pszFilePath );
+ return LOAD_FAILED;
+ }
+
+ if ( pszFilePath && *pszFilePath )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ selectedClass ] ), true );
+ pKey->SetString( "vcd_file", pszFilePath );
+ }
+ else
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ selectedClass ] ) );
+ if ( pKey )
+ {
+ KeyValues *pVCDKey = pKey->FindKey( "vcd_file" );
+ if ( pVCDKey )
+ {
+ pKey->RemoveSubKey( pVCDKey );
+ pVCDKey->deleteThis();
+ }
+ }
+ }
+
+ SetDirty( true );
+
+ if ( selectedClass == m_nSelectedClass )
+ {
+ UpdateAnimationVCDDisplay();
+ }
+
+ // Show a highlight for classes that have models
+ if ( m_pClassHighlights[ selectedClass ] )
+ {
+ m_pClassHighlights[ selectedClass ]->SetVisible( ClassHasTauntSources( selectedClass ) );
+ }
+
+ return LOAD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetAnimationDuration( int selectedClass, float flDuration )
+{
+ if ( selectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ float flClampedDuration = clamp( flDuration, 0.f, GetMaxTauntDuration() );
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ selectedClass ] ) );
+ if ( pKey )
+ {
+ pKey->SetFloat( "taunt_duration", flClampedDuration );
+ }
+
+ UpdateAnimDurationDisplay();
+}
+
+
+extern CChoreoScene *LoadSceneForModel( const char *filename, IChoreoEventCallback *pCallback, float *flSceneEndTime );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::VerifyVCD( const CAssetTF &asset )
+{
+ if ( m_nPrefab != PREFAB_TAUNT )
+ return BUILD_OKAY;
+
+ // force reload scene cache
+ scenefilecache->Reload();
+ CUtlVector< int > vcdFileIndices;
+ const CUtlVector< CUtlString > &builtFiles = asset.GetBuiltFiles();
+ const CUtlVector< CUtlString > &relPathBuiltFiles = asset.GetRelativePathBuiltFiles();
+ for ( int iFile=0; iFile<builtFiles.Count(); ++iFile )
+ {
+ char pszExtention[16];
+ V_ExtractFileExtension( builtFiles[iFile].String(), pszExtention, ARRAYSIZE( pszExtention ) );
+ if ( !FStrEq( pszExtention, "vcd" ) )
+ {
+ continue;
+ }
+
+ const char *pszFullPath = builtFiles[iFile].String();
+ const char *pszRelPath = V_stristr( pszFullPath, "scenes" );
+ if ( !pszRelPath )
+ {
+ return BUILD_FAILED_BAD_VCD_FILE;
+ }
+
+ float flSceneEndTime = -1.f;
+
+ CChoreoScene *pScene = LoadSceneForModel( pszRelPath, NULL, &flSceneEndTime );
+ if ( pScene )
+ {
+ CChoreoEvent *pEventSequence = NULL;
+ for ( int iEvent=0; iEvent<pScene->GetNumEvents() && !pEventSequence; ++iEvent )
+ {
+ CChoreoEvent *pEvent = pScene->GetEvent( iEvent );
+ if ( pEvent && pEvent->GetType() == CChoreoEvent::EVENTTYPE::SEQUENCE )
+ {
+ pEventSequence = pEvent;
+ }
+ }
+
+ if ( !pEventSequence )
+ {
+ delete pScene;
+ return BUILD_FAILED_VCD_MISSING_EVENT_SEQUENCE;
+ }
+
+ if ( pEventSequence->GetEndTime() > GetMaxTauntDuration() )
+ {
+ delete pScene;
+ return BUILD_FAILED_VCD_EVENT_SEQUENCE_TOO_LONG;
+ }
+
+ char szFileName[MAX_PATH];
+ V_FileBase( pszRelPath, szFileName, sizeof( szFileName ) );
+
+ pEventSequence->SetParameters( szFileName );
+ pScene->SaveToFile( pszFullPath );
+
+ vcdFileIndices.AddToTail( iFile );
+
+ delete pScene;
+ }
+ }
+
+ const char *pszArchiveName = asset.GetArchivePath();
+ if ( pszArchiveName && *pszArchiveName )
+ {
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pszArchiveName, NULL, buf ) )
+ {
+ // failed to parse zip file
+ return BUILD_FAILED_BAD_VCD_FILE;
+ }
+
+ IZip *pZip = IZip::CreateZip();
+ if ( pZip )
+ {
+ pZip->ParseFromBuffer( buf.Base(), buf.Size() );
+ for ( int i=0; i<vcdFileIndices.Count(); ++i )
+ {
+ int vcdIndex = vcdFileIndices[i];
+ const char *pszFullPath = builtFiles[vcdIndex].String();
+ const char *pszRelPath = relPathBuiltFiles[vcdIndex].String();
+ pZip->AddFileToZip( pszRelPath, pszFullPath );
+ }
+
+ pZip->SaveToBuffer( buf );
+ IZip::ReleaseZip( pZip );
+
+ g_pFullFileSystem->WriteFile( pszArchiveName, NULL, buf );
+ }
+ else
+ {
+ // failed to parse zip file
+ return BUILD_FAILED_BAD_VCD_FILE;
+ }
+ }
+
+ return BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateAnimationSourceDisplay()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ if ( m_pAnimationSourceFile )
+ {
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassAnimationSourceFile, kClassFolders[ m_nSelectedClass ] ) );
+ if ( pszFilePath && *pszFilePath )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszFilePath, file, sizeof(file) );
+ m_pAnimationSourceFile->SetText( file );
+ }
+ else
+ {
+ m_pAnimationSourceFile->SetText( "#TF_PublishFile_NoFileSelected" );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateAnimationVCDDisplay()
+{
+ if ( m_nSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ return;
+ }
+
+ if ( m_pAnimationVCDFile )
+ {
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassAnimationVCDFile, kClassFolders[ m_nSelectedClass ] ) );
+ if ( pszFilePath && *pszFilePath )
+ {
+ char file[MAX_PATH];
+ V_FileBase( pszFilePath, file, sizeof(file) );
+ m_pAnimationVCDFile->SetText( file );
+ }
+ else
+ {
+ m_pAnimationVCDFile->SetText( "#TF_PublishFile_NoFileSelected" );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::UpdateAnimDurationDisplay()
+{
+ if ( m_pAnimationDurationLabel )
+ {
+ float flDuration = GetItemValues()->GetFloat( CFmtStr( kClassAnimationDuration, kClassFolders[ m_nSelectedClass ] ) );
+ wchar_t wszTemp[256];
+ wchar_t wszCount[10];
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%.4f", flDuration );
+ g_pVGuiLocalize->ConstructString_safe( wszTemp, g_pVGuiLocalize->Find("#TF_ImportFile_AnimationDuration"), 1, wszCount );
+ m_pAnimationDurationLabel->SetText( wszTemp );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetDirty( bool bDirty )
+{
+ if ( m_pBuildButton )
+ {
+ m_pBuildButton->SetEnabled( !bDirty );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static int FindSuffix( const char *pszString, const char *pszSuffix )
+{
+ int nStringLen = V_strlen(pszString);
+ int nSuffixLen = V_strlen(pszSuffix);
+ int nSuffixOffset = nStringLen - nSuffixLen;
+ if ( nSuffixOffset >= 0 && V_strcasecmp( (pszString + nSuffixOffset), pszSuffix ) == 0 )
+ {
+ return nSuffixOffset;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static bool FindTexture( const char *pszStrippedPath, const char *pszSuffix, CUtlString &sOutputPath )
+{
+ static const char *vecTextureExtensions[] = {
+ ".tga",
+ ".psd"
+ };
+
+ for ( int i = 0; i < ARRAYSIZE(vecTextureExtensions); ++i )
+ {
+ char pszFullPath[MAX_PATH];
+ V_snprintf(pszFullPath, sizeof(pszFullPath), "%s%s%s", pszStrippedPath, pszSuffix, vecTextureExtensions[ i ]);
+ if ( CItemUpload::FileExists( pszFullPath ) )
+ {
+ sOutputPath = pszFullPath;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SetLoopableTaunt( bool bLoopable, float flLoopStartTime )
+{
+ GetItemValues()->SetFloat( kAnimationLoopable, flLoopStartTime );
+
+ if ( m_pAnimationLoopCheckButton )
+ {
+ m_pAnimationLoopCheckButton->SetSelected( bLoopable );
+ }
+
+ if ( m_pAnimationLoopStartTextEntry )
+ {
+ m_pAnimationLoopStartTextEntry->SetEnabled( bLoopable );
+ m_pAnimationLoopStartTextEntry->SetText( CFmtStr( "%f", bLoopable ? flLoopStartTime : -1.f ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsLoopableTaunt() const
+{
+ return m_pAnimationLoopCheckButton && m_pAnimationLoopCheckButton->IsSelected();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFFileImportDialog::GetAnimationLoopStartTime() const
+{
+ if ( m_pAnimationLoopStartTextEntry )
+ {
+ return m_pAnimationLoopStartTextEntry->GetValueAsFloat();
+ }
+
+ return 0.f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::AddTauntToAsset( CAssetTF &asset, int nClassIndex, bool bIsMulticlass, BUILD_STAGE buildStage, KeyValues *pItemData, KeyValues *pBuildMessageVariables )
+{
+ const uint nPathFlags = (CTargetBase::PATH_FLAG_ALL & ~CTargetBase::PATH_FLAG_ABSOLUTE) | CTargetBase::PATH_FLAG_ZIP;
+ const CUtlVector<const char *>&vecUsabilityStrings = ItemSystem()->GetItemSchema()->GetClassUsabilityStrings();
+
+ bool bPreview = buildStage == BUILD_PREVIEW;
+
+ asset.AddModel();
+
+ const char *pszClassFolder = kClassFolders[ nClassIndex ];
+
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+
+ asset.ExcludeFileExtension( "qc" );
+ asset.ExcludeFileExtension( "mdl" );
+
+ // Are we checking out files to perforce?
+ bool bPerforce = !bPreview && ShouldP4AddOrEdit();
+
+ pQC->GetCustomKeyValues()->SetString( "<CLASS_NAME>", pszClassFolder );
+ pQC->SetQCTemplate( GetUserAnimationQCTemplate( nClassIndex, bPerforce ) );
+ pQC->SetQCITemplate( GetQCITemplate( nClassIndex ) );
+
+ const char *pszVCDFile = GetItemValues()->GetString( CFmtStr( kClassAnimationVCDFile, pszClassFolder ) );
+ CSmartPtr< CTargetVCD > pVCD = pQC->GetTargetVCD();
+ pVCD->SetInputFile( pszVCDFile );
+ const char *pszCustomRelativeDir = CFmtStr( "%s/player/%s/low", GetWorkshopFolder(), vecUsabilityStrings[nClassIndex] );
+ pVCD->SetCustomRelativeDir( pszCustomRelativeDir );
+ if ( bPreview )
+ {
+ pVCD->SetCustomModPath( "custom/workshop" );
+ }
+
+ // Dir for MDL
+ pszCustomRelativeDir = bPerforce ? CFmtStr( "%s/player/animations", GetWorkshopFolder() ) : "player";
+ const char *pszAnimMDLMiddleName = bPerforce ? "workshop" : "user"; // use class_user_animations as temp MDL and class_workshop_animations as shipping MDL
+ const char *pszCustomMDLOutputName = CFmtStr( "%s_%s_animations", pszClassFolder, pszAnimMDLMiddleName );
+ CSmartPtr< CTargetMDL > pMDL = asset.GetTargetMDL();
+ pMDL->SetCustomRelativeDir( pszCustomRelativeDir );
+ pMDL->SetCustomOutputName( pszCustomMDLOutputName );
+ if ( bPreview )
+ {
+ pMDL->SetCustomModPath( "custom/workshop" );
+ }
+
+ // Dir for QC
+ pszCustomRelativeDir = bPerforce ? CFmtStr( "%s/player/animations", GetWorkshopFolder() ) : CFmtStr( "player/%s", pszClassFolder );
+ const char *pszAnimQCMiddleName = bPerforce ? "workshop" : "preview"; // use class_preview_animations as temp QC and class_workshop_animations as shipping QC
+ const char *pszCustomQCOutputName = CFmtStr( "%s_%s_animations", pszClassFolder, pszAnimQCMiddleName );
+ pQC->SetCustomRelativeDir( pszCustomRelativeDir );
+ pQC->SetCustomOutputName( pszCustomQCOutputName );
+ pQC->SetIgnoreP4( !bPerforce );
+
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassAnimationSourceFile, pszClassFolder ) );
+ if ( pszFilePath && *pszFilePath )
+ {
+ int nLOD = asset.AddTargetDMX( pszFilePath );
+ if ( nLOD < 0 )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pszFilePath );
+ return BUILD_FAILED_BADMODEL;
+ }
+
+ CSmartPtr< CTargetDMX > pTargetDMX = asset.GetTargetDMX( nLOD );
+
+ if ( bIsMulticlass )
+ {
+ pTargetDMX->SetNameSuffix( CFmtStr( "_%s", pszClassFolder ) );
+ }
+
+ // Update the item data for the path inside the asset archive
+ CUtlString sOutputPath;
+ pTargetDMX->GetOutputPath( sOutputPath, 0, nPathFlags );
+ pItemData->SetString( CFmtStr( kClassAnimationSourceFile, pszClassFolder ), sOutputPath );
+
+ CUtlString sSequenceName;
+ pTargetDMX->GetOutputPath( sSequenceName, 0, CTargetBase::PATH_FLAG_FILE );
+
+ float flFrameRate;
+ int nFrameCount;
+ pTargetDMX->GetAnimationFrameInfo( flFrameRate, nFrameCount );
+ float flTotalAnimTime = (float)nFrameCount / flFrameRate;
+
+ // set custom keys for DMX
+ pTargetDMX->GetCustomKeyValues()->SetString( "<ITEMTEST_SEQUENCE_NAME>", sSequenceName.String() );
+ pTargetDMX->GetCustomKeyValues()->SetString( "<CLASS_NAME>", pszClassFolder );
+ pTargetDMX->GetCustomKeyValues()->SetFloat( "<MAX_SEQUENCE_LENGTH>", flTotalAnimTime );
+
+ if ( IsLoopableTaunt() )
+ {
+ float flStartTime = Clamp( GetAnimationLoopStartTime(), 0.f, flTotalAnimTime );
+ pTargetDMX->SetAnimationLoopStartTime( flStartTime );
+ }
+
+ pTargetDMX->SetSoundScriptFilePath( kWorkshopSoundScriptFile );
+
+ if ( bPreview )
+ {
+ pTargetDMX->SetCustomModPath( "custom/workshop" );
+ }
+ }
+
+ // check out files before compiling (INTERNAL ONLY)
+ if ( p4 )
+ {
+ CUtlString sMDLAbsPath;
+ pMDL->GetOutputPath( sMDLAbsPath, 0 );
+ char szCorrectCaseFilePath[MAX_PATH];
+ g_pFullFileSystem->GetCaseCorrectFullPath( sMDLAbsPath.String(), szCorrectCaseFilePath );
+ CP4AutoEditAddFile editMDL( szCorrectCaseFilePath, "binary" );
+
+ // check out class_workshop_animation.qc to submit the final workshop
+ if ( bPerforce )
+ {
+ CUtlString sQCAbsPath;
+ pQC->GetOutputPath( sQCAbsPath, 0 );
+ g_pFullFileSystem->GetCaseCorrectFullPath( sQCAbsPath.String(), szCorrectCaseFilePath );
+ CP4AutoEditAddFile editQC( szCorrectCaseFilePath );
+ }
+
+ const char *pszSoundScriptFile = GetWorkshopSoundScriptFile();
+ if ( pszSoundScriptFile )
+ {
+ CP4AutoEditAddFile editSoundScript( pszSoundScriptFile );
+ }
+ }
+
+ return BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::AddModelToAsset( CAssetTF &asset, int nClassIndex, bool bIsMulticlass, BUILD_STAGE buildStage, KeyValues *pItemData, KeyValues *pBuildMessageVariables )
+{
+ const uint nPathFlags = (CTargetBase::PATH_FLAG_ALL & ~CTargetBase::PATH_FLAG_ABSOLUTE) | CTargetBase::PATH_FLAG_ZIP;
+ bool bPreview = buildStage == BUILD_PREVIEW;
+
+ asset.AddModel();
+
+ const char *pszClassFolder = kClassFolders[ nClassIndex ];
+
+ if ( bIsMulticlass )
+ {
+ asset.GetTargetMDL()->SetNameSuffix( CFmtStr( "_%s", pszClassFolder ) );
+ }
+
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+ pQC->SetQCTemplate( GetQCTemplate( nClassIndex ) );
+
+ int nLastSpecifiedTriCount = 0;
+ int nLastSpecifiedLOD = 0;
+ for ( int nModelIndex = 0; nModelIndex < NUM_IMPORT_LODS; ++nModelIndex )
+ {
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassLODNFile, pszClassFolder, nModelIndex ) );
+ if ( *pszFilePath )
+ {
+ int nLOD = asset.AddTargetDMX( pszFilePath );
+ if ( nLOD < 0 )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pszFilePath );
+ return BUILD_FAILED_BADMODEL;
+ }
+
+ CSmartPtr< CTargetDMX > pTargetDMX = asset.GetTargetDMX( nLOD );
+
+ const int nMaxTris = GetModelTriangleBudget( nClassIndex, nModelIndex );
+ const int nTriCount = pTargetDMX->GetTriangleCount();
+ if ( !bPreview && nTriCount > nMaxTris )
+ {
+ pBuildMessageVariables->SetInt( "count", nTriCount );
+ pBuildMessageVariables->SetInt( "limit", nMaxTris );
+ SetMessageFileVariable( pBuildMessageVariables, pszFilePath );
+ return BUILD_FAILED_COMPLEXMODEL;
+ }
+
+ // Update the item data for the path inside the asset archive
+ CUtlString sOutputPath;
+ pTargetDMX->GetOutputPath( sOutputPath, 0, nPathFlags );
+ pItemData->SetString( CFmtStr( kClassLODNFile, pszClassFolder, nModelIndex ), sOutputPath );
+
+ nLastSpecifiedLOD = nModelIndex;
+ nLastSpecifiedTriCount = nTriCount;
+ }
+ else if ( !bPreview && nModelIndex == 0 )
+ {
+ // We need LOD0...
+ pBuildMessageVariables->SetString( "class", pszClassFolder );
+ return BUILD_FAILED_MISSINGMODEL;
+ }
+ } // for each LOD
+
+ if ( !bPreview && nLastSpecifiedTriCount > GetModelTriangleBudget( nClassIndex, NUM_IMPORT_LODS - 1 ) )
+ {
+ pBuildMessageVariables->SetString( "lod", kLODLevels[ nLastSpecifiedLOD + 1 ] );
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassLODNFile, pszClassFolder, nLastSpecifiedLOD ) );
+ SetMessageFileVariable( pBuildMessageVariables, pszFilePath );
+ return BUILD_FAILED_NEEDMORELOD;
+ }
+
+ return BUILD_OKAY;
+}
+
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::AddMaterialsToAsset( CAssetTF &asset, KeyValues *pItemData, KeyValues *pBuildMessageVariables )
+{
+ const uint nPathFlags = (CTargetBase::PATH_FLAG_ALL & ~CTargetBase::PATH_FLAG_ABSOLUTE) | CTargetBase::PATH_FLAG_ZIP;
+ int nValidVMTIndex = 0;
+ CUtlVector< CUtlString > vecMaterialFiles;
+ for ( int nVMT = 0; nVMT < asset.GetTargetVMTCount(); nVMT++ )
+ {
+ CTargetVMT *pVMT = asset.GetTargetVMT( nVMT );
+ if ( pVMT )
+ {
+ if ( pVMT->GetDuplicate() )
+ {
+ continue;
+ }
+
+ if ( pVMT->GetMaterialType() == kInvalidMaterialType )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pVMT->GetMaterialId() );
+ return BUILD_FAILED_BADMATERIALTYPE;
+ }
+
+ for ( int nSkinIndex = 0; nSkinIndex < CItemUpload::Manifest()->GetNumMaterialSkins(); ++nSkinIndex )
+ {
+ CUtlString strMaterialName = GetMaterialName( nSkinIndex, nValidVMTIndex );
+ if ( strMaterialName.IsEmpty() )
+ {
+ continue;
+ }
+
+ KeyValuesAD pKV( "VMT" );
+ CUtlBuffer sMaterialText;
+ if ( !GetMaterialText( nSkinIndex, nValidVMTIndex, sMaterialText ) ||
+ !pKV->LoadFromBuffer( pVMT->GetMaterialId(), sMaterialText ) )
+ {
+ if ( !strMaterialName.IsEmpty() )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, strMaterialName );
+ }
+ else
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pVMT->GetMaterialId() );
+ }
+ return BUILD_FAILED_BADMATERIAL;
+ }
+
+ BUILD_RESULT nMaterialResult = ValidateMaterialValues( pKV, nValidVMTIndex );
+ if ( nMaterialResult != BUILD_OKAY )
+ {
+ if ( !strMaterialName.IsEmpty() )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, strMaterialName );
+ }
+ else
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pVMT->GetMaterialId() );
+ }
+ return nMaterialResult;
+ }
+
+ RemoveUnnecessaryParametersFromVMT( pKV, nValidVMTIndex );
+ pVMT->SetVMTKV( pKV, nSkinIndex );
+
+ bool bSelfIllum = pKV->GetBool( ">=DX90/$selfillum" );
+ bool bBaseAlphaMask = pKV->GetBool( "$basealphaenvmapmask" );
+ Assert( CItemUpload::Manifest()->GetNumTextureTypes() == NUM_MATERIAL_TEXTURE_FILE_TYPE );
+ for ( int nTextureType = 0; nTextureType < CItemUpload::Manifest()->GetNumTextureTypes(); ++nTextureType )
+ {
+ MATERIAL_FILE_TYPE materialFileType = (MATERIAL_FILE_TYPE)nTextureType;
+ const char *pszTexturePath = GetMaterialTextureFile( nSkinIndex, nValidVMTIndex, materialFileType );
+ if ( V_strlen( pszTexturePath ) == 0 )
+ {
+ continue;
+ }
+
+ BUILD_RESULT checkImageResult = CheckImageSize( pszTexturePath, materialFileType, pBuildMessageVariables );
+ if ( checkImageResult != BUILD_OKAY )
+ {
+ return checkImageResult;
+ }
+
+ const char *pszTextureType = CItemUpload::Manifest()->GetTextureType( materialFileType );
+ if ( bBaseAlphaMask && FStrEq( pszTextureType, "_normal" ) )
+ {
+ Assert( materialFileType == MATERIAL_FILE_NORMAL );
+ // Base alpha mask will not work in materials using a normal map, use normal map alpha mask instead
+ continue;
+ }
+ else if ( !bSelfIllum && FStrEq( pszTextureType, "_illum" ) )
+ {
+ Assert( materialFileType == MATERIAL_FILE_SELFILLUM );
+ // no need to output illum if self illum is not set
+ continue;
+ }
+
+ if ( !pVMT->SetTargetVTF( pszTextureType, pszTexturePath, nSkinIndex ) )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pszTexturePath );
+ return BUILD_FAILED_BADIMAGE;
+ }
+ }
+
+ for ( int iVTF=0; iVTF<pVMT->GetNumTargetVTFS( nSkinIndex ); ++iVTF )
+ {
+ // Update the item data for the path inside the asset archive
+ CUtlString sOutputPath;
+ CTargetVTF *pVTF = pVMT->GetTargetVTF( iVTF, nSkinIndex );
+ if ( pVTF )
+ {
+ pVTF->GetTargetTGA()->GetOutputPath( sOutputPath, 0, nPathFlags );
+ sOutputPath = asset.CheckRedundantOutputFilePath( pVTF->GetTargetTGA()->GetInputFile().String(), "", sOutputPath.String() );
+ pItemData->SetString( CFmtStr( kMaterialSkinNFile, CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex ), nValidVMTIndex, s_pszMaterialFilePrefixes[iVTF] ), sOutputPath );
+ }
+ }
+ }
+
+ ++nValidVMTIndex;
+ }
+ }
+
+ return BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::CheckSourceSDK()
+{
+ CUtlString sDummy;
+ return CItemUpload::GetContentDir( sDummy ) && CItemUpload::GetBinDirectory( sDummy );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::BUILD_RESULT CTFFileImportDialog::Build( BUILD_STAGE buildStage, KeyValues *pBuildMessageVariables )
+{
+ const uint nPathFlags = (CTargetBase::PATH_FLAG_ALL & ~CTargetBase::PATH_FLAG_ABSOLUTE) | CTargetBase::PATH_FLAG_ZIP;
+ bool bPreview = buildStage == BUILD_PREVIEW;
+
+ if ( !CheckSourceSDK() )
+ {
+ return BUILD_FAILED_NOSDK;
+ }
+
+ // check for build errors
+ if ( !bPreview )
+ {
+ if ( V_strcmp( GetItemName(), "" ) == 0 )
+ {
+ return BUILD_FAILED_NONAME;
+ }
+
+ if ( V_strcmp( GetItemPrefab(), "" ) == 0 )
+ {
+ return BUILD_FAILED_NOTYPE;
+ }
+
+ if ( !IsValidPrefab( GetItemPrefab() ) )
+ {
+ return BUILD_FAILED_BADTYPE;
+ }
+
+ if ( m_nPrefab == PREFAB_TAUNT )
+ {
+ if ( !AnyClassHasTauntSources() )
+ {
+ return BUILD_FAILED_NO_TAUNT_SOURCES;
+ }
+ }
+ else
+ {
+ if ( !AnyClassHasModels() )
+ {
+ return BUILD_FAILED_NOMODELS;
+ }
+
+ if ( !AreClassesLODCountMatch() )
+ {
+ return BUILD_FAILED_LODCOUNTMISMATCH;
+ }
+
+ if ( !AreClassesMaterialCountMatch() )
+ {
+ return BUILD_FAILED_MATERIALCOUNTMISMATCH;
+ }
+
+ if ( !DidSpecifyAllMaterials() )
+ {
+ return BUILD_FAILED_NOMATERIALS;
+ }
+ }
+
+ if ( V_strcmp( GetItemIcon(), "" ) == 0 )
+ {
+ return BUILD_FAILED_NOBACKPACKICON;
+ }
+ }
+
+ bool bPartnerContent = IsPartnerContent();
+ // override the manifest data for partner content
+ CItemUpload::Manifest()->SetItemDirectoryOverride( bPartnerContent ? "workshop_partner/player/items/" : "" );
+ CItemUpload::Manifest()->SetAnimationDirectoryOverride( bPartnerContent ? "workshop_partner/player/animations/" : "" );
+ CItemUpload::Manifest()->SetIconDirectoryOverride( bPartnerContent ? "backpack/workshop_partner/player/items/" : "" );
+
+ CAssetTF asset;
+ CBuildLog buildLog;
+
+ asset.SetItemLog( &buildLog );
+
+ CUtlString sName = GetItemName();
+ if ( bPreview )
+ {
+ sName = "item_preview";
+ }
+
+ // get rid of leading/trailing white spaces
+ char szTempName[MAX_PATH];
+ V_strcpy_safe( szTempName, sName.String() );
+ Q_StripPrecedingAndTrailingWhitespace( szTempName );
+ sName = szTempName;
+
+ if ( !asset.SetName( sName ) )
+ {
+ return BUILD_FAILED_BADNAME;
+ }
+ asset.SetClass( GetClassFolder() );
+
+ KeyValuesAD pSessionData( BuildSessionData( sName ) );
+ KeyValues *pItemData = pSessionData->FindKey( GetItemValues()->GetName() );
+ Assert( pItemData != NULL );
+
+ const char *pszIcon = GetItemIcon();
+ if ( V_strlen( pszIcon ) > 0 )
+ {
+ int nNumIconTypes = CItemUpload::Manifest()->GetNumIconTypes();
+ if ( nNumIconTypes > 0 )
+ {
+ for ( int nIcon = 0; nIcon < nNumIconTypes; ++nIcon )
+ {
+ if ( !asset.SetTargetIcon( nIcon, pszIcon ) )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pszIcon );
+ return BUILD_FAILED_BADIMAGE;
+ }
+ }
+
+ // Get the filename from the last (largest) icon type
+ CSmartPtr< CTargetIcon > pTargetIcon = asset.GetTargetIcon( nNumIconTypes - 1 );
+ CUtlString sOutputPath;
+ pTargetIcon->GetTargetVTF( 0 )->GetTargetTGA()->GetOutputPath( sOutputPath, 0, nPathFlags );
+ pItemData->SetString( kItemIcon, sOutputPath );
+ }
+ }
+
+ // clean up "blue" materials if needed
+ if ( GetItemValues()->GetInt( kSkinType ) == 0 )
+ {
+ for ( int nMaterialIndex = 0; nMaterialIndex<NUM_IMPORT_MATERIALS_PER_TEAM; ++nMaterialIndex )
+ {
+ ClearMaterial( 1, nMaterialIndex );
+ }
+ }
+
+ const char *pszSoundScriptFile = GetWorkshopSoundScriptFile();
+ if ( pszSoundScriptFile )
+ {
+ CP4AutoRevertFile revert( pszSoundScriptFile );
+ }
+
+ // check and prepare models/taunt sources for compiling
+ bool bIsMulticlass = IsMulticlass();
+ asset.RemoveModels();
+ BUILD_RESULT buildResult;
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( ClassHasTauntSources( nClassIndex ) )
+ {
+ buildResult = AddTauntToAsset( asset, nClassIndex, bIsMulticlass, buildStage, pItemData, pBuildMessageVariables );
+ if ( buildResult != BUILD_OKAY )
+ return buildResult;
+
+ // should we add prop?
+ if ( ClassHasModels( nClassIndex ) )
+ {
+ buildResult = AddModelToAsset( asset, nClassIndex, bIsMulticlass, buildStage, pItemData, pBuildMessageVariables );
+ if ( buildResult != BUILD_OKAY )
+ return buildResult;
+ }
+ }
+ else if ( ClassHasModels( nClassIndex ) )
+ {
+ buildResult = AddModelToAsset( asset, nClassIndex, bIsMulticlass, buildStage, pItemData, pBuildMessageVariables );
+ if ( buildResult != BUILD_OKAY )
+ return buildResult;
+ }
+ } // for each class
+
+ buildResult = AddMaterialsToAsset( asset, pItemData, pBuildMessageVariables );
+ if ( buildResult != BUILD_OKAY )
+ return buildResult;
+
+ // setworkshop ID
+ if ( !bPreview )
+ {
+ if ( p4 )
+ {
+ if ( pItemData->GetUint64( kWorkshopID ) == 0 )
+ {
+ return BUILD_FAILED_NO_WORKSHOP_ID;
+ }
+ }
+
+ if ( CItemUpload::GetP4() )
+ {
+ KeyValues *pItemSchema = pSessionData->FindKey( CFmtStr( "%s/%s", GetItemValues()->GetName(), kItemSchema ) );
+ if ( pItemSchema )
+ {
+ item_definition_index_t defIndex = AddKeyValuesToItemWorkshopSchema( pItemSchema );
+ if ( !IsTFEnglishNameValid( defIndex ) )
+ {
+ return BUILD_FAILED_BADTFENGLISHNAME;
+ }
+ }
+ }
+ }
+
+ asset.SetAdditionalManifestData( pSessionData );
+
+ if ( m_nPrefab == PREFAB_TAUNT )
+ {
+ asset.SetBuildScenesImage( true );
+
+ if ( bPreview )
+ {
+ asset.SetCustomModPath( "custom/workshop" );
+ }
+ }
+
+ if ( bPreview )
+ {
+ if ( !asset.CompilePreview() )
+ {
+ pBuildMessageVariables->SetString( "log", buildLog.Get() );
+ return BUILD_FAILED_COMPILE;
+ }
+
+ buildResult = VerifyVCD( asset );
+ if ( buildResult != BUILD_OKAY )
+ {
+ return buildResult;
+ }
+
+ SavePreviewData( asset );
+ }
+ else
+ {
+ CUtlString sAssetName;
+ char pszSessionPath[ MAX_PATH ];
+ char pszArchivePath[ MAX_PATH ];
+
+ asset.GetName( sAssetName );
+ const char *pszWorkshopDir = CFmtStr( kSteamWorkshopDir, GetWorkshopFolder() );
+ if ( !g_pFullFileSystem->RelativePathToFullPath_safe( pszWorkshopDir, "GAME", pszSessionPath ) )
+ {
+ SetMessageFileVariable( pBuildMessageVariables, pszWorkshopDir );
+ return BUILD_FAILED_COMPILE;
+ }
+ V_ComposeFileName( pszSessionPath, CFmtStr( "%s.zip", sAssetName.Get() ), pszArchivePath, sizeof(pszArchivePath) );
+ asset.SetArchivePath( pszArchivePath );
+
+ if ( !asset.Compile() )
+ {
+ pBuildMessageVariables->SetString( "log", buildLog.Get() );
+ return BUILD_FAILED_COMPILE;
+ }
+
+ buildResult = VerifyVCD( asset );
+ if ( buildResult != BUILD_OKAY )
+ {
+ return buildResult;
+ }
+
+ GetItemValues()->SetString( kBuildOutput, pszArchivePath );
+ }
+
+ return BUILD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *CTFFileImportDialog::BuildSessionData( const char *pszItemName )
+{
+ KeyValues *pData = new KeyValues( "Data" );
+ KeyValues *pItemData = GetItemValues()->MakeCopy();
+ pItemData->AddSubKey( BuildItemSchema( pszItemName ) );
+ pData->AddSubKey( pItemData );
+ return pData;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *CTFFileImportDialog::BuildItemSchema( const char *pszItemName )
+{
+ const uint nPathFlags = (CTargetBase::PATH_FLAG_ALL & ~CTargetBase::PATH_FLAG_ABSOLUTE);
+
+ KeyValues *pData = new KeyValues( kItemSchema );
+ KeyValues *pKey;
+ int nClassIndex;
+
+ pData->UsesEscapeSequences( true );
+
+ // Get the sanitized name for item schema tokens
+ CUtlString sName;
+ CAssetTF asset;
+ asset.SetName( pszItemName );
+ asset.GetName( sName );
+ asset.SetClass( GetClassFolder() );
+
+ const char* pszTFEnglishName = GetItemValues()->GetString( kTFEnglishName );
+
+ pData->SetString( kWorkshopName, GetItemValues()->GetString( kItemName ) );
+ pData->SetString( "name", pszTFEnglishName ); // TODO: Change this when we add a ship panel to fill in name from tf_english.txt
+
+ const char *pszForcePrefab = "no_craft";
+ pData->SetString( "prefab", CFmtStr( "%s %s", pszForcePrefab, GetItemPrefab() ) );
+ pData->SetString( "item_name", CFmtStr( "#TF_%s", sName.Get() ) );
+ pData->SetString( "item_description", CFmtStr( "#TF_%s_Desc", sName.Get() ) );
+
+ if ( m_nPrefab != PREFAB_TAUNT )
+ {
+ pData->SetString( "equip_region", GetItemValues()->GetString( kEquipRegion ) );
+ }
+
+ const char *kThe = "The ";
+ if ( V_strncasecmp( pszTFEnglishName, kThe, V_strlen( kThe ) ) == 0 )
+ {
+ pData->SetBool( "propername", true );
+ }
+
+ if ( m_nPrefab != PREFAB_TAUNT )
+ {
+ KeyValues *pItemBodygroups = GetItemValues()->FindKey( kBodygroup );
+ if ( pItemBodygroups )
+ {
+ CUtlVector<int> bodygroupIDs;
+ for ( int i=0; i<ARRAYSIZE( kBodygroupArray ); ++i )
+ {
+ if ( pItemBodygroups->GetBool( kBodygroupArray[i] ) )
+ {
+ bodygroupIDs.AddToTail(i);
+ }
+ }
+
+ if ( bodygroupIDs.Count() > 0 )
+ {
+ KeyValues *pSchemaVisualsKey = pData->FindKey( "visuals", true );
+ if ( pSchemaVisualsKey )
+ {
+ KeyValues *pSchemaBodygroupsKey = pSchemaVisualsKey->FindKey( "player_bodygroups", true );
+ if ( pSchemaBodygroupsKey )
+ {
+ for ( int i=0; i<bodygroupIDs.Count(); ++i )
+ {
+ int nBodygroupIndex = bodygroupIDs[i];
+ pSchemaBodygroupsKey->SetBool( kBodygroupArray[nBodygroupIndex], true );
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( IsLoopableTaunt() )
+ {
+ KeyValues *pAttributesKey = pData->FindKey( "attributes", true );
+ if ( pAttributesKey )
+ {
+ KeyValues *pLoopAttrKey = pAttributesKey->FindKey( "taunt is press and hold", true );
+ if ( pLoopAttrKey )
+ {
+ pLoopAttrKey->SetString( "attribute_class", "enable_misc2_holdtaunt" );
+ pLoopAttrKey->SetInt( "value", 1 );
+ }
+ }
+ }
+
+ const char *pszIcon = GetItemIcon();
+ if ( V_strlen( pszIcon ) > 0 )
+ {
+ if ( asset.SetTargetIcon( 0, pszIcon ) )
+ {
+ CSmartPtr< CTargetIcon > pTargetIcon = asset.GetTargetIcon( 0 );
+ CUtlString sOutputPath;
+ if ( pTargetIcon->GetOutputPath( sOutputPath, 0, nPathFlags ) )
+ {
+ const char *pszMaterialsPrefix = "materials/";
+ char *pszOutputPath = sOutputPath.GetForModify();
+ V_FixSlashes( pszOutputPath, '/' );
+ if ( V_strncmp( pszOutputPath, pszMaterialsPrefix, V_strlen( pszMaterialsPrefix ) ) == 0 )
+ {
+ pszOutputPath += V_strlen( pszMaterialsPrefix );
+ }
+ V_StripExtension( pszOutputPath, pszOutputPath, V_strlen( pszOutputPath ) + 1 );
+
+ pData->SetString( "image_inventory", pszOutputPath );
+ }
+ else
+ {
+ Warning( "Couldn't get output path for icon %s", pszIcon );
+ }
+ }
+ else
+ {
+ Warning( "Couldn't load icon %s", pszIcon );
+ }
+ }
+
+ // always add can_craft_count even if the item is not craftable in case we want to make it craftable in the future
+ pKey = pData->FindKey( "capabilities", true );
+ pKey->SetInt( "can_craft_count", 1 );
+
+ if ( IsAnyVMTPaintable() )
+ {
+ pKey->SetBool( "paintable", true );
+ }
+
+ const CUtlVector<const char *>&vecUsabilityStrings = ItemSystem()->GetItemSchema()->GetClassUsabilityStrings();
+ if ( m_nPrefab != PREFAB_TAUNT )
+ {
+ bool bIsMulticlass = IsMulticlass();
+ asset.RemoveModels();
+ for ( nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( !ClassHasModels( nClassIndex ) )
+ continue;
+
+ asset.AddModel();
+
+ if ( bIsMulticlass )
+ {
+ asset.GetTargetMDL()->SetNameSuffix( CFmtStr( "_%s", kClassFolders[ nClassIndex ] ) );
+ }
+
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassLODNFile, kClassFolders[ nClassIndex ], 0 ) );
+ if ( *pszFilePath )
+ {
+ int nLOD = asset.AddTargetDMX( pszFilePath );
+ if ( nLOD < 0 )
+ {
+ Warning( "Couldn't load model %s", pszFilePath );
+ continue;
+ }
+
+ // Update the item data for the path inside the asset archive
+ CUtlString sOutputPath;
+ if ( asset.GetTargetMDL()->GetOutputPath( sOutputPath, 0, nPathFlags ) )
+ {
+ sOutputPath.FixSlashes( '/' );
+
+ if ( bIsMulticlass )
+ {
+ pKey = pData->FindKey( "model_player_per_class", true );
+ const char* pszClassName = vecUsabilityStrings[ nClassIndex ];
+ pKey->SetString( pszClassName, sOutputPath );
+ }
+ else
+ {
+ pData->SetString( "model_player", sOutputPath );
+ }
+ }
+ else
+ {
+ Warning( "Couldn't get output path for model %s", pszFilePath );
+ }
+ }
+ }
+ }
+
+ KeyValues *pUsedByClass = pData->FindKey( "used_by_classes", true );
+ for ( nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( !ClassHasModels( nClassIndex ) && !ClassHasTauntSources( nClassIndex ) )
+ continue;
+
+ pUsedByClass->SetBool( vecUsabilityStrings[ nClassIndex ], true );
+ }
+
+ // add "taunt" block here
+ if ( m_nPrefab == PREFAB_TAUNT )
+ {
+ KeyValues *pTauntKey = pData->FindKey( "taunt", true );
+ KeyValues *pCustomTauntPerClass = pTauntKey->FindKey( "custom_taunt_scene_per_class", true );
+
+ CSmartPtr< CTargetQC > pQC = asset.GetTargetQC();
+ CSmartPtr< CTargetVCD > pVCD = pQC->GetTargetVCD();
+
+ bool bIsMulticlass = IsMulticlass();
+
+ for ( nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ const char *pszClass = vecUsabilityStrings[nClassIndex];
+ if ( pUsedByClass->GetBool( pszClass ) )
+ {
+ pVCD->SetCustomRelativeDir( CFmtStr( "%s/player/%s/low", GetWorkshopFolder(), pszClass ) );
+ CUtlString sTauntOutputPath;
+ pVCD->GetOutputPath( sTauntOutputPath, 0, nPathFlags );
+ sTauntOutputPath.FixSlashes( '/' );
+ pCustomTauntPerClass->SetString( pszClass, sTauntOutputPath.String() );
+
+ // should we add prop?
+ if ( !ClassHasModels( nClassIndex ) )
+ continue;
+
+ asset.AddModel();
+
+ if ( bIsMulticlass )
+ {
+ asset.GetTargetMDL()->SetNameSuffix( CFmtStr( "_%s", kClassFolders[ nClassIndex ] ) );
+ }
+
+ const char *pszFilePath = GetItemValues()->GetString( CFmtStr( kClassLODNFile, kClassFolders[ nClassIndex ], 0 ) );
+ if ( *pszFilePath )
+ {
+ int nLOD = asset.AddTargetDMX( pszFilePath );
+ if ( nLOD < 0 )
+ {
+ Warning( "Couldn't load model %s", pszFilePath );
+ continue;
+ }
+
+ // Update the item data for the path inside the asset archive
+ CUtlString sPropOutputPath;
+ if ( asset.GetTargetMDL()->GetOutputPath( sPropOutputPath, 0, nPathFlags ) )
+ {
+ sPropOutputPath.FixSlashes( '/' );
+
+ KeyValues *pCustomPropPerClass = pTauntKey->FindKey( "custom_taunt_prop_per_class", true );
+ pCustomPropPerClass->SetString( pszClass, sPropOutputPath.String() );
+ }
+ else
+ {
+ Warning( "Couldn't get output path for model %s", pszFilePath );
+ }
+ }
+ }
+ }
+ }
+
+ return pData;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::Load( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath )
+{
+ const char *pszExtension = V_GetFileExtension( pszFilePath );
+ if ( V_strcasecmp( pszExtension, "txt" ) == 0 )
+ {
+ return LoadTxt( pszFilePath, pathID, sFailedPath );
+ }
+ if ( V_strcasecmp( pszExtension, "zip" ) == 0 )
+ {
+ return LoadZip( pszFilePath, pathID, sFailedPath );
+ }
+ sFailedPath = pszFilePath;
+ return LOAD_FAILED;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::LoadTxt( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath )
+{
+ char pszBasePath[MAX_PATH];
+ char pszLoadPath[MAX_PATH];
+
+ V_ExtractFilePath( pszFilePath, pszBasePath, sizeof(pszBasePath) );
+
+ KeyValuesAD pData( "Data" );
+ pData->UsesEscapeSequences( true );
+
+ if ( !pData->LoadFromFile( g_pFullFileSystem, pszFilePath, pathID ) )
+ {
+ sFailedPath = pszFilePath;
+ return LOAD_FAILED;
+ }
+
+ KeyValues *pItemData = pData->FindKey( GetItemValues()->GetName() );
+ if ( !pItemData )
+ {
+ sFailedPath = pszFilePath;
+ return LOAD_FAILED;
+ }
+
+ SetItemName( pItemData->GetString( kItemName) );
+ SetItemPrefab( pItemData->GetString( kItemPrefab ) );
+ SetSkinType( pItemData->GetInt( kSkinType ) );
+ SetEquipRegion( pItemData->GetString( kEquipRegion ) );
+ SetWorkshopID( pItemData->GetString( kWorkshopID ) );
+ SetTFEnglishName( pItemData->GetString( kTFEnglishName ) );
+
+ KeyValues *pBodygroup = pItemData->FindKey( kBodygroup );
+ if ( pBodygroup )
+ {
+ SetBodygroup( pBodygroup );
+ }
+
+ const char *pszIconFile = pItemData->GetString( kItemIcon );
+ if ( *pszIconFile && !V_IsAbsolutePath( pszIconFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszIconFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszIconFile = pszLoadPath;
+ }
+ SetItemIcon( pszIconFile );
+
+ for ( int nMaterialIndex=0; nMaterialIndex<NUM_IMPORT_MATERIALS_PER_TEAM; ++nMaterialIndex )
+ {
+ const char* pszPaintable = CFmtStr( kItemPaintable, nMaterialIndex );
+ SetPaintable( pItemData->GetBool( pszPaintable ), nMaterialIndex );
+ }
+
+ bool bSelected = false;
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ const char* pszQCKeyName = CFmtStr( kClassQC, kClassFolders[ nClassIndex ] );
+ GetItemValues()->SetString( pszQCKeyName, pItemData->GetString( pszQCKeyName ) );
+
+ for ( int nModelIndex = 0; nModelIndex < NUM_IMPORT_LODS; ++nModelIndex )
+ {
+ const char *pszModelFile = pItemData->GetString( CFmtStr( kClassLODNFile, kClassFolders[ nClassIndex ], nModelIndex ) );
+ if ( *pszModelFile )
+ {
+ if ( !V_IsAbsolutePath( pszModelFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszModelFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszModelFile = pszLoadPath;
+ }
+
+ // set default selected class
+ if ( !bSelected )
+ {
+ m_nSelectedClass = nClassIndex;
+
+ if ( m_pClassRadioButtons[ nClassIndex ] )
+ {
+ m_pClassRadioButtons[ nClassIndex ]->SetSelected( true );
+ bSelected = true;
+ }
+ }
+ }
+
+ LOAD_RESULT result = SetLOD( nClassIndex, nModelIndex, pszModelFile );
+ if ( result != LOAD_OKAY )
+ {
+ sFailedPath = pszModelFile;
+ return result;
+ }
+ }
+ }
+
+ const float flAnimationLoopStartTime = pItemData->GetFloat( kAnimationLoopable, -1.f );
+ SetLoopableTaunt( flAnimationLoopStartTime >= 0.f, flAnimationLoopStartTime );
+
+ // Load animation files
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ const char* pszQCIKeyName = CFmtStr( kClassQCI, kClassFolders[ nClassIndex ] );
+ GetItemValues()->SetString( pszQCIKeyName, pItemData->GetString( pszQCIKeyName ) );
+
+ const char *pszSourceFile = pItemData->GetString( CFmtStr( kClassAnimationSourceFile, kClassFolders[ nClassIndex ] ) );
+ if ( *pszSourceFile )
+ {
+ if ( !V_IsAbsolutePath( pszSourceFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszSourceFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszSourceFile = pszLoadPath;
+ }
+
+ // set default selected class
+ if ( !bSelected )
+ {
+ m_nSelectedClass = nClassIndex;
+
+ if ( m_pClassRadioButtons[ nClassIndex ] )
+ {
+ m_pClassRadioButtons[ nClassIndex ]->SetSelected( true );
+ bSelected = true;
+ }
+ }
+ }
+
+ LOAD_RESULT result = SetAnimationSource( nClassIndex, pszSourceFile );
+ if ( result != LOAD_OKAY )
+ {
+ sFailedPath = pszSourceFile;
+ return result;
+ }
+
+ const char *pszVCDFile = pItemData->GetString( CFmtStr( kClassAnimationVCDFile, kClassFolders[ nClassIndex ] ) );
+ if ( *pszVCDFile )
+ {
+ if ( !V_IsAbsolutePath( pszVCDFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszVCDFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszVCDFile = pszLoadPath;
+ }
+ }
+
+ result = SetAnimationVCD( nClassIndex, pszVCDFile );
+ if ( result != LOAD_OKAY )
+ {
+ sFailedPath = pszVCDFile;
+ return result;
+ }
+ }
+
+ Assert( CItemUpload::Manifest()->GetNumTextureTypes() == NUM_MATERIAL_TEXTURE_FILE_TYPE );
+ for ( int nSkinIndex = 0; nSkinIndex < CItemUpload::Manifest()->GetNumMaterialSkins(); ++nSkinIndex )
+ {
+ for ( int nMaterialIndex = 0; nMaterialIndex < NUM_IMPORT_MATERIALS_PER_TEAM; ++nMaterialIndex )
+ {
+ const char *pszMaterialSkin = CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex );
+ const char *pszMaterialVMT = pItemData->GetString( CFmtStr( kMaterialSkinNVMT, pszMaterialSkin, nMaterialIndex ) );
+ const char* pszMaterialFile = pItemData->GetString( CFmtStr( kMaterialSkinNFile, pszMaterialSkin, nMaterialIndex, s_pszMaterialFilePrefixes[MATERIAL_FILE_BASETEXTURE] ) );
+ if ( *pszMaterialFile && !V_IsAbsolutePath( pszMaterialFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszMaterialFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszMaterialFile = pszLoadPath;
+ }
+
+ CUtlBuffer sMaterialText;
+ if ( FStrEq( pszMaterialVMT, "" ) )
+ {
+ sMaterialText.SetBufferType( true, true );
+ g_pFullFileSystem->ReadFile( pszMaterialFile, NULL, sMaterialText );
+ pszMaterialVMT = sMaterialText.String();
+ }
+
+ if ( !SetMaterialText( nSkinIndex, nMaterialIndex, pszMaterialVMT ) )
+ {
+ sFailedPath = pszMaterialFile;
+ return LOAD_FAILED_BADMATERIAL;
+ }
+
+ for ( int nTextureType = 0; nTextureType < CItemUpload::Manifest()->GetNumTextureTypes(); ++nTextureType )
+ {
+ const char *pszMaterialTextureFile = pItemData->GetString( CFmtStr( kMaterialSkinNFile, pszMaterialSkin, nMaterialIndex, s_pszMaterialFilePrefixes[nTextureType] ) );
+ if ( *pszMaterialTextureFile && !V_IsAbsolutePath( pszMaterialTextureFile) )
+ {
+ V_ComposeFileName( pszBasePath, pszMaterialTextureFile, pszLoadPath, sizeof(pszLoadPath) );
+ pszMaterialTextureFile = pszLoadPath;
+ }
+
+ if ( !SetMaterial( nSkinIndex, nMaterialIndex, pszMaterialTextureFile, (MATERIAL_FILE_TYPE)nTextureType ) )
+ {
+ sFailedPath = pszMaterialTextureFile;
+ return LOAD_FAILED_BADMATERIAL;
+ }
+ }
+ }
+ }
+
+ return LOAD_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::LOAD_RESULT CTFFileImportDialog::LoadZip( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath )
+{
+ // Unzip the file into the temporary directory
+ char pszZipName[MAX_PATH];
+ V_FileBase( pszFilePath, pszZipName, sizeof(pszZipName) );
+
+ char pszTempPath[MAX_PATH];
+ V_StripExtension( pszFilePath, pszTempPath, sizeof(pszTempPath) );
+
+ if ( ShouldP4AddOrEdit() )
+ {
+ char szCorrectCaseFilePath[MAX_PATH];
+ g_pFullFileSystem->GetCaseCorrectFullPath( pszFilePath, szCorrectCaseFilePath );
+ CP4AutoEditFile a( szCorrectCaseFilePath );
+ }
+
+ if ( !g_pFullFileSystem->UnzipFile( pszFilePath, pathID, pszTempPath ) )
+ {
+ sFailedPath = pszFilePath;
+ return LOAD_FAILED;
+ }
+
+ char pszManifest[MAX_PATH];
+ V_ComposeFileName( pszTempPath, "manifest.txt", pszManifest, sizeof(pszManifest) );
+
+ return LoadTxt( pszManifest, pathID, sFailedPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFileImportDialog::SAVE_RESULT CTFFileImportDialog::Save( const char *pszFilePath, const char *pathID )
+{
+ KeyValuesAD pData( BuildSessionData( GetItemName() ) );
+
+ CUtlBuffer sNewText;
+ sNewText.SetBufferType( true, true );
+ pData->RecursiveSaveToFile( sNewText, 0, false, true );
+
+ if ( !pData->SaveToFile( g_pFullFileSystem, pszFilePath, pathID ) )
+ {
+ return SAVE_FAILED;
+ }
+ return SAVE_OKAY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::SavePreviewData( CAssetTF &asset )
+{
+ KeyValues *pSessionData = asset.GetAdditionalManifestData();
+
+ if ( m_pPreviewSchema )
+ {
+ m_pPreviewSchema->deleteThis();
+ m_pPreviewSchema = NULL;
+ }
+
+ KeyValues *pItemSchema = pSessionData->FindKey( CFmtStr( "%s/%s", GetItemValues()->GetName(), kItemSchema ) );
+ if ( pItemSchema )
+ {
+ m_pPreviewSchema = pItemSchema->MakeCopy();
+ }
+ Assert(m_pPreviewSchema);
+
+ // Save the files that were built so we can remove them later
+ m_vecPreviewFiles = asset.GetBuiltFiles();
+ m_vecCustomModFiles = asset.GetModFiles();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::SetupPreviewData()
+{
+ KeyValuesAD pPreviewSchema( m_pPreviewSchema->MakeCopy() );
+ pPreviewSchema->SetString( "item_name", pPreviewSchema->GetString( "name" ) );
+
+ if ( !ItemSystem()->GetItemSchema()->SetupPreviewItemDefinition( pPreviewSchema ) )
+ {
+ return false;
+ }
+
+ // must call this to flush old model and force load new model with the same name
+ engine->ExecuteClientCmd( "r_flushlod" );
+ engine->ExecuteClientCmd( "mat_reloadallmaterials" );
+ engine->ExecuteClientCmd( "cl_soundemitter_flush" );
+
+ if ( !m_pPreviewDialog )
+ {
+ m_pPreviewDialog = new CImportPreviewItemPanel( this, GetItemValues(), m_nSelectedClass );
+ m_pPreviewDialog->MakePopup( false );
+ }
+
+ CEconItemView itemData;
+ itemData.Init( PREVIEW_ITEM_DEFINITION_INDEX, AE_UNIQUE, AE_USE_SCRIPT_VALUE, true );
+ itemData.SetClientItemFlags( kEconItemFlagClient_Preview );
+ m_pPreviewDialog->PreviewItem( 0, &itemData );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::CleanupPreviewData()
+{
+ m_pPreviewDialog.Set( INVALID_PANEL );
+
+ FOR_EACH_VEC( m_vecPreviewFiles, i )
+ {
+ const char *pszFileName = m_vecPreviewFiles[ i ].String();
+ if ( p4 && V_strstr( pszFileName, "_user_animations.mdl" ) )
+ {
+ char szCorrectCaseFilePath[MAX_PATH];
+ g_pFullFileSystem->GetCaseCorrectFullPath( pszFileName, szCorrectCaseFilePath );
+ CP4AutoRevertFile revert( szCorrectCaseFilePath );
+ }
+ else
+ {
+ g_pFullFileSystem->RemoveFile( pszFileName );
+ }
+ }
+ m_vecPreviewFiles.RemoveAll();
+
+ FOR_EACH_VEC( m_vecCustomModFiles, i )
+ {
+ g_pFullFileSystem->RemoveFile( m_vecCustomModFiles[ i ] );
+ }
+ m_vecCustomModFiles.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::IsMulticlass()
+{
+ int nClasses = 0;
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( ClassHasModels( nClassIndex ) || ClassHasTauntSources( nClassIndex ) )
+ ++nClasses;
+ }
+ return (nClasses > 1);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFFileImportDialog::GetClassFolder()
+{
+ const char *pszClassName = NULL;
+
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( ClassHasModels( nClassIndex ) || ClassHasTauntSources( nClassIndex ) )
+ {
+ if ( pszClassName )
+ {
+ pszClassName = kClassFolderMulticlass;
+ }
+ else
+ {
+ pszClassName = kClassFolders[ nClassIndex ];
+ }
+ }
+ }
+
+ return pszClassName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::AnyClassHasModels()
+{
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( ClassHasModels( nClassIndex ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::ClassHasModels( int nClassIndex )
+{
+ for ( int nModelIndex = 0; nModelIndex < NUM_IMPORT_LODS; ++nModelIndex )
+ {
+ if ( V_stricmp( GetItemValues()->GetString( CFmtStr( kClassLODNFile, kClassFolders[ nClassIndex ], nModelIndex ) ), "" ) != 0 )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::DidSpecifyAllMaterials()
+{
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ nClassIndex ], 0 ) );
+ if ( pKey )
+ {
+ int nSkinType = GetItemValues()->GetInt( kSkinType );
+ int nMaterialCount = pKey->GetInt( "materialCount" );
+ for ( int nSkinIndex=0; nSkinIndex<=nSkinType; ++nSkinIndex )
+ {
+ for ( int nMaterialIndex=0; nMaterialIndex<nMaterialCount; ++nMaterialIndex )
+ {
+ const char *pszMaterialFile = GetItemValues()->GetString( CFmtStr( kMaterialSkinNFile, CItemUpload::Manifest()->GetMaterialSkin( nSkinIndex ), nMaterialIndex, s_pszMaterialFilePrefixes[MATERIAL_FILE_BASETEXTURE] ) );
+ if ( FStrEq( pszMaterialFile, "" ) )
+ {
+ return false;
+ }
+ }
+ }
+
+ // only need to check materials for one class because we already check AreClassesMaterialCountMatch
+ break;
+ }
+ } // for each LOD
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::AreClassesLODCountMatch()
+{
+ int nCurrentLODCount = 0;
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ int nNumClassLOD = 0;
+ for ( int nLOD=0; nLOD<NUM_IMPORT_LODS; ++nLOD )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ nClassIndex ], nLOD ) );
+ if ( pKey )
+ {
+ if ( !FStrEq( pKey->GetString( "file" ), "" ) )
+ {
+ nNumClassLOD++;
+ }
+ }
+ } // for each LOD
+
+ if ( nCurrentLODCount == 0 )
+ {
+ nCurrentLODCount = nNumClassLOD;
+ }
+ else if ( nNumClassLOD != 0 && nCurrentLODCount != nNumClassLOD )
+ {
+ return false;
+ }
+ } // for each class
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::AreClassesMaterialCountMatch()
+{
+ for ( int nLOD=0; nLOD<NUM_IMPORT_LODS; ++nLOD )
+ {
+ int nCurrentMaterialCount = -1;
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassLODN, kClassFolders[ nClassIndex ], nLOD ) );
+ if ( pKey )
+ {
+ if ( !FStrEq( pKey->GetString( "file" ), "" ) )
+ {
+ int nMaterialCount = pKey->GetInt( "materialCount" );
+ if ( nCurrentMaterialCount == -1 )
+ {
+ nCurrentMaterialCount = nMaterialCount;
+ }
+ else if ( nCurrentMaterialCount != nMaterialCount )
+ {
+ return false;
+ }
+ }
+ }
+ } // for each class
+ } // for each LOD
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::AnyClassHasTauntSources()
+{
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; ++nClassIndex )
+ {
+ if ( ClassHasTauntSources( nClassIndex ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFFileImportDialog::ClassHasTauntSources( int nClassIndex )
+{
+ if ( nClassIndex == TF_CLASS_UNDEFINED )
+ return false;
+
+ KeyValues *pKey = GetItemValues()->FindKey( CFmtStr( kClassAnimation, kClassFolders[ nClassIndex ] ) );
+ if ( pKey )
+ {
+ const char *pszSource = pKey->GetString( "source_file" );
+ if ( pszSource && *pszSource )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFileImportDialog::GetWorkshopData()
+{
+ CFilePublishDialog *pPublishDialog = dynamic_cast< CFilePublishDialog * >( GetParent() );
+ if ( !pPublishDialog )
+ return;
+
+ vgui::TextEntry *pTextEntry = pPublishDialog->FindControl<vgui::TextEntry>( "FileTitle" );
+ if ( pTextEntry )
+ {
+ char pszName[1024];
+ pTextEntry->GetText( pszName, sizeof(pszName) );
+ SetItemName( pszName );
+ }
+}
+
+void CTFFileImportDialog::SetWorkshopData()
+{
+ CFilePublishDialog *pPublishDialog = dynamic_cast< CFilePublishDialog * >( GetParent() );
+ if ( !pPublishDialog )
+ return;
+
+ pPublishDialog->SetFile( GetItemValues()->GetString( kBuildOutput ), true );
+
+ vgui::TextEntry *pTextEntry = pPublishDialog->FindControl<vgui::TextEntry>( "FileTitle" );
+ if ( pTextEntry )
+ {
+ pTextEntry->SetText( GetItemValues()->GetString( kItemName ) );
+ }
+
+ vgui::EditablePanel* pClassUsagePanel = pPublishDialog->FindControl<vgui::EditablePanel>( "ClassUsagePanel" );
+ if ( pClassUsagePanel )
+ {
+ for ( int nClassIndex = TF_FIRST_NORMAL_CLASS; nClassIndex < TF_LAST_NORMAL_CLASS; nClassIndex++ )
+ {
+ SetChildButtonSelected( pClassUsagePanel, VarArgs( "ClassCheckBox%d", nClassIndex ), ClassHasModels( nClassIndex ) );
+ }
+ }
+}
+
+
+void CTFFileImportDialog::SetLODPanelEnable( bool bEnable, int nModelIndex )
+{
+ vgui::Panel *pLODPanel = m_pLODPanels[nModelIndex];
+ if ( pLODPanel )
+ {
+ for ( int i=0; i<pLODPanel->GetChildCount(); ++i )
+ {
+ pLODPanel->GetChild(i)->SetEnabled( bEnable );
+ }
+ }
+}
+
+
+void CTFFileImportDialog::SetSkinType( int nSkinType )
+{
+ if ( m_pSkinComboBox && nSkinType < m_pSkinComboBox->GetItemCount() )
+ {
+ m_pSkinComboBox->ActivateItemByRow( nSkinType );
+ }
+ GetItemValues()->SetInt( kSkinType, nSkinType );
+
+ UpdateMaterialDisplay();
+
+ SetDirty( true );
+}
+
+void CTFFileImportDialog::SetEquipRegion( const char* pszEquipRegion )
+{
+ if ( m_pEquipRegionComboBox )
+ {
+ bool bFound = false;
+ for ( int i = 0; i < m_pEquipRegionComboBox->GetItemCount(); ++i )
+ {
+ KeyValues *pKeyValues = m_pEquipRegionComboBox->GetItemUserData( m_pEquipRegionComboBox->GetItemIDFromRow( i ) );
+ if ( V_strcasecmp( pszEquipRegion, pKeyValues->GetString( kEquipRegion ) ) == 0 )
+ {
+ bFound = true;
+ m_pEquipRegionComboBox->ActivateItemByRow( i );
+ break;
+ }
+ }
+
+ GetItemValues()->SetString( kEquipRegion, bFound ? pszEquipRegion : "" );
+ }
+
+ SetDirty( true );
+}
+
+void CTFFileImportDialog::SetWorkshopID( const char* pszWorkshopID )
+{
+ if ( m_pWorkshopIDTextEntry->IsVisible() )
+ {
+ GetItemValues()->SetString( kWorkshopID, pszWorkshopID );
+ m_pWorkshopIDTextEntry->SetText( GetItemValues()->GetString( kWorkshopID ) );
+ }
+ SetDirty( true );
+}
+
+bool CTFFileImportDialog::IsTFEnglishNameValid( item_definition_index_t defIndex )
+{
+ // check if the name already exist in item schema
+ if ( m_pTFEnglishNameTextEntry->IsVisible() )
+ {
+ if ( defIndex == INVALID_ITEM_DEF_INDEX )
+ {
+ return false;
+ }
+
+ const char* pszTFEnglishName = GetItemValues()->GetString( kTFEnglishName );
+ const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = ItemSystem()->GetItemSchema()->GetItemDefinitionMap();
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ CEconItemDefinition *pData = mapItemDefs[i];
+ const char *pszExistingItemName = pData->GetDefinitionName();
+ item_definition_index_t existingItemDefIndex = pData->GetDefinitionIndex();
+ if ( existingItemDefIndex != 0 && existingItemDefIndex != defIndex && FStrEq( pszExistingItemName, pszTFEnglishName ) )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void CTFFileImportDialog::SetTFEnglishName( const char* pszTFEnglishName )
+{
+ if ( m_pTFEnglishNameTextEntry->IsVisible() )
+ {
+ GetItemValues()->SetString( kTFEnglishName, pszTFEnglishName );
+ m_pTFEnglishNameTextEntry->SetText( GetItemValues()->GetString( kTFEnglishName ) );
+ }
+ SetDirty( true );
+}
+
+//-----------------------------------------------------------------------------
+item_definition_index_t CTFFileImportDialog::AddKeyValuesToItemWorkshopSchema( KeyValues *pKV )
+{
+ char szItemWorkshopAbsPath[MAX_PATH];
+ if ( !GenerateFullPath( kWorkshopSchemaFile, "MOD", szItemWorkshopAbsPath, ARRAYSIZE( szItemWorkshopAbsPath ) ) )
+ {
+ Warning( "Failed to GenerateFullPath %s\n", kWorkshopSchemaFile );
+ return INVALID_ITEM_DEF_INDEX;
+ }
+ char szCorrectCaseFilePath[MAX_PATH];
+ g_pFullFileSystem->GetCaseCorrectFullPath( szItemWorkshopAbsPath, szCorrectCaseFilePath );
+ CP4AutoEditAddFile a( szCorrectCaseFilePath );
+
+ KeyValuesAD pWorkshopSchema( "WorkshopSchema" );
+
+ // This file should force a cache refresh of this specific file otherwise we won't write stuff out.
+ if ( !pWorkshopSchema->LoadFromFile( g_pFullFileSystem, kWorkshopSchemaFile, "MOD", true ) )
+ {
+ Warning( "Failed to load %s\n", kWorkshopSchemaFile );
+ return INVALID_ITEM_DEF_INDEX;
+ }
+
+ int nNewItemIndex = kStartWorkshopItemIndex;
+ const char* pszNewItemName = pKV->GetString( kWorkshopName );
+ KeyValues *pNewItemKey = NULL;
+ KeyValues *pLastKey = NULL;
+
+ KeyValues *pItemKey = pWorkshopSchema;
+ while ( pItemKey )
+ {
+ const char* pszItemName = pItemKey->GetString( kWorkshopName );
+ nNewItemIndex = atoi( pItemKey->GetName() );
+ if ( FStrEq( pszItemName, pszNewItemName ) )
+ {
+ pNewItemKey = pItemKey;
+ break;
+ }
+
+ pLastKey = pItemKey;
+ pItemKey = pItemKey->GetNextKey();
+ }
+
+ if ( !pNewItemKey )
+ {
+ pNewItemKey = pKV->MakeCopy();
+ pNewItemKey->SetName( CFmtStr( "%d", nNewItemIndex + 1 ) );
+ pLastKey->SetNextKey( pNewItemKey );
+ }
+ else
+ {
+ KeyValuesAD temp( pNewItemKey->MakeCopy() );
+ pKV->CopySubkeys( pNewItemKey );
+ pNewItemKey->RecursiveMergeKeyValues( temp );
+ }
+
+ KeyValues* pPaymentKey = pNewItemKey->FindKey( "payment_rules/0", true );
+ pPaymentKey->SetFloat( "workshop_revenue_share", 0.25f );
+ pPaymentKey->SetString( "target", GetItemValues()->GetString( kWorkshopID ) );
+ pPaymentKey->SetString( "payment_rule_for_itemdef", pNewItemKey->GetName() );
+
+ pItemKey = pWorkshopSchema;
+ CUtlBuffer buffer;
+ while ( pItemKey )
+ {
+ pItemKey->RecursiveSaveToFile( buffer, 2, false, true );
+ pItemKey = pItemKey->GetNextKey();
+ }
+
+ if ( !g_pFullFileSystem->WriteFile( szItemWorkshopAbsPath, NULL, buffer ) )
+ {
+ Warning( "Failed to save new item to %s", szItemWorkshopAbsPath );
+ }
+
+ item_definition_index_t defIndex = atoi( pNewItemKey->GetName() );
+ return defIndex;
+}
diff --git a/game/client/tf/workshop/item_import.h b/game/client/tf/workshop/item_import.h
new file mode 100644
index 0000000..27ea77c
--- /dev/null
+++ b/game/client/tf/workshop/item_import.h
@@ -0,0 +1,317 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "vgui_controls/frame.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/RadioButton.h"
+#include "vgui_bitmappanel.h"
+
+#include "tf_shareddefs.h"
+
+class CTFFileImportTextEditDialog;
+class CTFImportMaterialEditDialog;
+class CImportPreviewItemPanel;
+class CTFPlayerModelPanel;
+
+enum MATERIAL_FILE_TYPE
+{
+ MATERIAL_FILE_BASETEXTURE,
+ MATERIAL_FILE_NORMAL,
+ MATERIAL_FILE_PHONGEXPONENT,
+ MATERIAL_FILE_SELFILLUM,
+ NUM_MATERIAL_TEXTURE_FILE_TYPE
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Import file dialog
+//-----------------------------------------------------------------------------
+class CTFFileImportDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CTFFileImportDialog, Frame );
+
+public:
+ enum BUILD_RESULT {
+ BUILD_OKAY,
+ BUILD_FAILED_NOSDK,
+ BUILD_FAILED_NONAME,
+ BUILD_FAILED_NOTYPE,
+ BUILD_FAILED_NOMODELS,
+ BUILD_FAILED_LODCOUNTMISMATCH,
+ BUILD_FAILED_NOMATERIALS,
+ BUILD_FAILED_MATERIALCOUNTMISMATCH,
+ BUILD_FAILED_NOBACKPACKICON,
+ BUILD_FAILED_BADNAME,
+ BUILD_FAILED_BADTFENGLISHNAME,
+ BUILD_FAILED_BADTYPE,
+ BUILD_FAILED_BADMODEL,
+ BUILD_FAILED_BADMATERIALTYPE,
+ BUILD_FAILED_BADMATERIAL,
+ BUILD_FAILED_MATERIALMISSINGSHADER,
+ BUILD_FAILED_MATERIALMISSINGCLOAK,
+ BUILD_FAILED_MATERIALMISSINGBURNING,
+ BUILD_FAILED_MATERIALMISSINGJARATE,
+ BUILD_FAILED_MATERIALMISSINGPAINTABLE,
+ BUILD_FAILED_MISSINGMODEL,
+ BUILD_FAILED_NEEDMORELOD,
+ BUILD_FAILED_COMPLEXMODEL,
+ BUILD_FAILED_BADIMAGE,
+ BUILD_FAILED_COMPILE,
+ BUILD_FAILED_NO_WORKSHOP_ID,
+ BUILD_FAILED_IMAGEUNSUPPORTEDFILETYPE,
+ BUILD_FAILED_IMAGERESOLUTIONNOTPOWEROF2,
+ BUILD_FAILED_IMAGERESOLUTIONOVERLIMIT,
+ BUILD_FAILED_NO_TAUNT_SOURCES,
+ BUILD_FAILED_BAD_VCD_FILE,
+ BUILD_FAILED_VCD_MISSING_EVENT_SEQUENCE,
+ BUILD_FAILED_VCD_EVENT_SEQUENCE_TOO_LONG,
+
+ NUM_BUILD_RESULTS
+ };
+ enum LOAD_RESULT {
+ LOAD_OKAY,
+ LOAD_FAILED,
+ LOAD_FAILED_BADMODEL,
+ LOAD_FAILED_COMPLEXMODEL,
+ LOAD_FAILED_TOOMANYBONES,
+ LOAD_FAILED_BADMATERIAL,
+ LOAD_FAILED_TOOMANYMATERIALS,
+ LOAD_FAILED_MATERIALCOUNTMISMATCH,
+ LOAD_FAIL_ANIMATIONTOOLONG,
+ NUM_LOAD_RESULTS
+ };
+ enum SAVE_RESULT {
+ SAVE_OKAY,
+ SAVE_FAILED,
+ NUM_SAVE_RESULTS
+ };
+
+ enum BUILD_STAGE
+ {
+ BUILD_PREVIEW,
+ BUILD_VERIFY,
+ BUILD_FINAL
+ };
+
+ enum WARNING
+ {
+ WARNING_BASEALPHAMASK,
+ NUM_WARNINGS
+ };
+
+ enum ImportPrefab_t
+ {
+ PREFAB_HAT = 0,
+ PREFAB_MISC,
+ PREFAB_TAUNT,
+
+ PREFAB_COUNT
+ };
+
+public:
+ CTFFileImportDialog( vgui::Panel *parent );
+
+ virtual ~CTFFileImportDialog();
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void OnCommand( const char *command );
+ void OnCommandLoad();
+ void OnCommandSave();
+ void OnCommandBrowseIcon();
+ void OnCommandBrowseLOD( int index );
+ void OnCommandBrowseAnimationSource();
+ void OnCommandBrowseAnimationVCD();
+ void OnCommandSwapVMT();
+ void OnCommandEditMaterial( int nSkinIndex, int nMaterialIndex );
+ void OnCommandEditMaterialDone( int nSkinIndex, int nMaterialIndex );
+ void OnCommandEditQC();
+ void OnCommandEditQCI();
+ void OnCommandEditQCDone();
+ void OnCommandEditQCIDone();
+ bool OnCommandBuild( BUILD_STAGE buildStage );
+ void OnCommandUpdateBodygroup();
+
+ void OnOpen();
+
+ virtual void OnClose();
+
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+
+ MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
+
+ MESSAGE_FUNC_PTR( OnRadioButtonChecked, "RadioButtonChecked", panel );
+
+ KeyValues *GetItemValues() { return m_pItemValues; }
+
+ void SetItemName( const char *pszName );
+ const char *GetItemName();
+ bool IsValidPrefab( const char *pszPrefab );
+ void SetItemPrefab( const char *pszPrefab );
+ const char *GetItemPrefab();
+ bool GetItemPrefabValue( const char *pszPrefab, const char *pszName, CUtlString& strOutput );
+ void SelectClass( int nClassIndex );
+ void SetItemIcon( const char *pszFilePath );
+ const char *GetItemIcon();
+ void SetPaintable( bool bPaintable, int nMaterialIndex );
+ bool IsPaintable( int nMaterialIndex );
+ bool IsAnyVMTPaintable();
+ const char *GetUserAnimationQCTemplate( int nSelectedClass, bool bPerforce = false );
+ const char *GetQCTemplate( int nSelectedClass );
+ const char *GetQCITemplate( int nSelectedClass );
+ void ClearLODs();
+ LOAD_RESULT SetLOD( int selectedClass, int nModelIndex, const char *pszFilePath, KeyValues* pKV = NULL );
+ void UpdateLODDisplay();
+ int GetModelTriangleBudget( int selectedClass, int nModelIndex );
+ int GetModelBoneBudget();
+ bool SetMaterial( int nMaterialPanelIndex, const char* pszFilePath, MATERIAL_FILE_TYPE fileType );
+ bool SetMaterial( int selectedSkin, int nMaterialIndex, const char *pszFilePath, MATERIAL_FILE_TYPE fileType );
+ const char* GetMaterialTextureFile( int selectedSkin, int nMaterialIndex, MATERIAL_FILE_TYPE fileType );
+ CUtlString GetMaterialName( int selectedSkin, int nMaterialIndex );
+ void ClearMaterials();
+ void ClearMaterial( int nSkinIndex, int nMaterialIndex );
+ void UpdateMaterialDisplay();
+ void UpdateMaterialDisplay( int nSkinIndex, int nMaterialIndex );
+ const char *GetMaterialText( int nSkinIndex, int nMaterialIndex, CUtlBuffer &sMaterialText );
+ bool SetMaterialText( int nSkinIndex, int nMaterialIndex, const char* pszMaterialText );
+ BUILD_RESULT ValidateMaterialValues( KeyValues *pKV, int nMaterialIndex );
+ void RemoveUnnecessaryParametersFromVMT( KeyValues *pKV, int nMaterialIndex );
+ void RemoveLightParameters( KeyValues *pKV, int nMaterialIndex );
+ void RemovePaintParameters( KeyValues *pKV, int nMaterialIndex );
+ void RemoveTranslucentParameters( KeyValues *pKV );
+ void RemoveCubeMapParameters( KeyValues *pKV );
+ void RemoveSelfIllumParameters( KeyValues *pKV );
+
+ void UpdateBodygroupsDisplay();
+ void SetBodygroup( KeyValues* pBodygroupKey );
+
+ LOAD_RESULT SetAnimationSource( int selectedClass, const char *pszFilePath, KeyValues* pKV = NULL );
+ LOAD_RESULT SetAnimationVCD( int selectedClass, const char *pszFilePath, KeyValues* pKV = NULL );
+ void SetAnimationDuration( int selectedClass, float flDuration );
+ BUILD_RESULT VerifyVCD( const CAssetTF &asset );
+ void UpdateAnimationSourceDisplay();
+ void UpdateAnimationVCDDisplay();
+ void UpdateAnimDurationDisplay();
+
+ void SetDirty( bool bDirty );
+
+ void SetLoopableTaunt( bool bLoopable, float flLoopStartTime );
+ bool IsLoopableTaunt() const;
+ float GetAnimationLoopStartTime() const;
+
+ BUILD_RESULT AddTauntToAsset( CAssetTF &asset, int nClassIndex, bool bIsMulticlass, BUILD_STAGE buildStage, KeyValues *pItemData, KeyValues *pBuildMessageVariables );
+ BUILD_RESULT AddModelToAsset( CAssetTF &asset, int nClassIndex, bool bIsMulticlass, BUILD_STAGE buildStage, KeyValues *pItemData, KeyValues *pBuildMessageVariables );
+ BUILD_RESULT AddMaterialsToAsset( CAssetTF &asset, KeyValues *pItemData, KeyValues *pBuildMessageVariables );
+ bool CheckSourceSDK();
+ BUILD_RESULT Build( BUILD_STAGE buildStage, KeyValues *pBuildMessageVariables );
+ KeyValues *BuildSessionData( const char *pszItemName );
+ KeyValues *BuildItemSchema( const char *pszItemName );
+ LOAD_RESULT Load( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath );
+ LOAD_RESULT LoadTxt( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath );
+ LOAD_RESULT LoadZip( const char *pszFilePath, const char *pathID, CUtlString &sFailedPath );
+ SAVE_RESULT Save( const char *pszFilePath, const char *pathID );
+
+ void SavePreviewData( CAssetTF &asset );
+ bool SetupPreviewData();
+ void CleanupPreviewData();
+
+protected:
+ bool IsMulticlass();
+ const char *GetClassFolder();
+ bool AnyClassHasModels();
+ bool ClassHasModels( int nClassIndex );
+ bool DidSpecifyAllMaterials();
+ bool AreClassesLODCountMatch();
+ bool AreClassesMaterialCountMatch();
+ bool AnyClassHasTauntSources();
+ bool ClassHasTauntSources( int nClassIndex );
+
+ void GetWorkshopData();
+ void SetWorkshopData();
+
+private:
+ bool ShouldP4AddOrEdit() const;
+ bool IsPartnerContent() const;
+ const char* GetWorkshopFolder() const;
+
+ int GetCustomBones( int selectedClass, const char* pszFileName, CUtlStringList& strBoneList );
+ void SetLODPanelEnable( bool bEnable, int nModelIndex );
+ void SetSkinType( int nSkinType );
+ void SetEquipRegion( const char* pszEquipRegion );
+ void SetWorkshopID( const char* pszWorkshopID );
+ bool IsTFEnglishNameValid( item_definition_index_t defIndex );
+ void SetTFEnglishName( const char* pszTFEnglishName );
+ void UpdateUIForPrefab( ImportPrefab_t nPrefab );
+ item_definition_index_t AddKeyValuesToItemWorkshopSchema( KeyValues *pKV );
+
+ int m_nSelectedClass;
+ ImportPrefab_t m_nPrefab;
+
+ enum FileOpenMode {
+ FILE_OPEN_NONE,
+ FILE_OPEN_LOAD,
+ FILE_OPEN_SAVE,
+ FILE_OPEN_ICON,
+ FILE_OPEN_LOD0,
+ FILE_OPEN_LOD1,
+ FILE_OPEN_LOD2,
+ FILE_OPEN_ANIMATION_SOURCE,
+ FILE_OPEN_ANIMATION_VCD,
+ } m_nFileOpenMode;
+
+ KeyValues *m_pItemValues;
+ KeyValues *m_pPreviewSchema;
+ CUtlVector< CUtlString > m_vecPreviewFiles;
+ CUtlVector< CUtlString > m_vecCustomModFiles;
+ CUtlBuffer m_tempQC;
+
+ vgui::TextEntry *m_pNameTextEntry;
+ vgui::ComboBox *m_pTypeComboBox;
+ vgui::Button *m_pSwapVMTButton;
+ vgui::ComboBox *m_pSkinComboBox;
+ vgui::TextEntry *m_pWorkshopIDTextEntry;
+ vgui::TextEntry *m_pTFEnglishNameTextEntry;
+ vgui::CheckButton *m_pPerforceCheckButton;
+ vgui::CheckButton *m_pPartnerCheckButton;
+
+ vgui::EditablePanel *m_pEquipRegionPanel;
+ vgui::ComboBox *m_pEquipRegionComboBox;
+
+ vgui::ImagePanel *m_pIconImagePanel;
+ CUtlVector< vgui::CheckButton* > m_pPaintableCheckButtons;
+
+ vgui::RadioButton *m_pClassRadioButtons[TF_LAST_NORMAL_CLASS];
+ vgui::Panel *m_pClassHighlights[TF_LAST_NORMAL_CLASS];
+
+ vgui::EditablePanel *m_pBodygroupsPanel;
+ CUtlVector< vgui::CheckButton* > m_pBodygroups;
+
+ vgui::EditablePanel *m_pLODsPanel;
+ CUtlVector< vgui::Panel* > m_pLODPanels;
+ CUtlVector< vgui::Label* > m_pLODFiles;
+ CUtlVector< vgui::Label* > m_pLODDetails;
+
+ vgui::EditablePanel *m_pSkinsPanel;
+ CUtlVector< vgui::Panel* > m_pMaterialPanels;
+ CUtlVector< vgui::Label* > m_pMaterialLabels;
+ CUtlVector< vgui::Label* > m_pMaterialFiles;
+
+ vgui::EditablePanel *m_pTauntInputPanel;
+ vgui::Label *m_pAnimationSourceFile;
+ vgui::Label *m_pAnimationVCDFile;
+ vgui::Label *m_pAnimationDurationLabel;
+ vgui::Label *m_pAnimationPropLabel;
+ vgui::CheckButton *m_pAnimationLoopCheckButton;
+ vgui::TextEntry *m_pAnimationLoopStartTextEntry;
+
+ vgui::Button *m_pBuildButton;
+ vgui::DHANDLE<CTFFileImportTextEditDialog> m_pTextEditDialog;
+ vgui::DHANDLE<CTFImportMaterialEditDialog> m_pMaterialEditDialog;
+ vgui::DHANDLE<CImportPreviewItemPanel> m_pPreviewDialog;
+ CTFPlayerModelPanel *m_pPlayerModelPanel;
+
+ bool m_bWasCheatOn;
+};
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;
+}
+