You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1738 lines
56 KiB
1738 lines
56 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
|
|
#include "cbase.h" |
|
#include "crafting_panel.h" |
|
#include "vgui/ISurface.h" |
|
#include "vgui/ISystem.h" |
|
#include "c_tf_player.h" |
|
#include "gamestringpool.h" |
|
#include "iclientmode.h" |
|
#include "tf_item_inventory.h" |
|
#include "ienginevgui.h" |
|
#include <vgui/ILocalize.h> |
|
#include "vgui_controls/TextImage.h" |
|
#include "vgui_controls/CheckButton.h" |
|
#include "vgui_controls/ComboBox.h" |
|
#include <vgui_controls/TextEntry.h> |
|
#include "vgui/IInput.h" |
|
#include "gcsdk/gcclient.h" |
|
#include "gcsdk/gcclientjob.h" |
|
#include "character_info_panel.h" |
|
#include "charinfo_loadout_subpanel.h" |
|
#include "econ_item_system.h" |
|
#include "econ_item_constants.h" |
|
#include "tf_hud_notification_panel.h" |
|
#include "tf_hud_chat.h" |
|
#include "c_tf_gamestats.h" |
|
#include "confirm_dialog.h" |
|
#include "econ_notifications.h" |
|
#include "gc_clientsystem.h" |
|
#include "charinfo_loadout_subpanel.h" |
|
#include "item_selection_criteria.h" |
|
#include "rtime.h" |
|
#include "c_tf_freeaccount.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
ConVar tf_explanations_craftingpanel( "tf_explanations_craftingpanel", "0", FCVAR_ARCHIVE, "Whether the user has seen explanations for this panel." ); |
|
|
|
struct recipefilter_data_t |
|
{ |
|
const char *pszTooltipString; |
|
const char *pszButtonImage; |
|
const char *pszButtonImageMouseover; |
|
}; |
|
recipefilter_data_t g_RecipeFilters[NUM_RECIPE_CATEGORIES] = |
|
{ |
|
{ "#RecipeFilter_Crafting", "crafticon_crafting_items", "crafticon_crafting_items_over" }, // RECIPE_CATEGORY_CRAFTINGITEMS, |
|
{ "#RecipeFilter_CommonItems", "crafticon_common_items", "crafticon_common_items_over" }, // RECIPE_CATEGORY_COMMONITEMS, |
|
{ "#RecipeFilter_RareItems", "crafticon_rare_items", "crafticon_rare_items_over" }, // RECIPE_CATEGORY_RAREITEMS, |
|
{ "#RecipeFilter_Special", "crafticon_special_blueprints", "crafticon_special_blueprints_over" } // RECIPE_CATEGORY_SPECIAL, |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
wchar_t *LocalizeRecipeStringPiece( const char *pszString, wchar_t *pszConverted, int nConvertedSizeInBytes ) |
|
{ |
|
if ( !pszString ) |
|
return L""; |
|
|
|
if ( pszString[0] == '#' ) |
|
return g_pVGuiLocalize->Find( pszString ); |
|
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( pszString, pszConverted, nConvertedSizeInBytes ); |
|
return pszConverted; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void SetItemPanelToRecipe( CItemModelPanel *pPanel, const CEconCraftingRecipeDefinition *pRecipeDef, bool bShowName ) |
|
{ |
|
wchar_t wcTmpName[512]; |
|
wchar_t wcTmpDesc[512]; |
|
int iNegAttribsBegin = 0; |
|
|
|
if ( !pRecipeDef ) |
|
{ |
|
Q_wcsncpy( wcTmpName, g_pVGuiLocalize->Find( "#Craft_Recipe_Custom" ), sizeof( wcTmpName ) ); |
|
Q_wcsncpy( wcTmpDesc, g_pVGuiLocalize->Find( "#Craft_Recipe_CustomDesc" ), sizeof( wcTmpDesc ) ); |
|
iNegAttribsBegin = Q_wcslen( wcTmpDesc ); |
|
} |
|
else |
|
{ |
|
if ( bShowName ) |
|
{ |
|
wchar_t *pName_A = g_pVGuiLocalize->Find( pRecipeDef->GetName_A() ); |
|
g_pVGuiLocalize->ConstructString_safe( wcTmpName, g_pVGuiLocalize->Find( pRecipeDef->GetName() ), 1, pName_A ); |
|
} |
|
else |
|
{ |
|
wcTmpName[0] = '\0'; |
|
} |
|
|
|
wchar_t wcTmpA[32]; |
|
wchar_t wcTmpB[32]; |
|
wchar_t wcTmpC[32]; |
|
wchar_t wcTmp[512]; |
|
|
|
// Build the input string |
|
wchar_t *pInp_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_A(), wcTmpA, sizeof( wcTmpA ) ); |
|
wchar_t *pInp_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_B(), wcTmpB, sizeof( wcTmpB ) ); |
|
wchar_t *pInp_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_C(), wcTmpC, sizeof( wcTmpC ) ); |
|
g_pVGuiLocalize->ConstructString_safe( wcTmpDesc, g_pVGuiLocalize->Find( pRecipeDef->GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C ); |
|
iNegAttribsBegin = Q_wcslen(wcTmpDesc); |
|
|
|
// Build the output string |
|
wchar_t *pOut_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_A(), wcTmpA, sizeof( wcTmpA ) ); |
|
wchar_t *pOut_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_B(), wcTmpB, sizeof( wcTmpB ) ); |
|
wchar_t *pOut_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_C(), wcTmpC, sizeof( wcTmpC ) ); |
|
g_pVGuiLocalize->ConstructString_safe( wcTmp, g_pVGuiLocalize->Find( pRecipeDef->GetDescOutputs() ), 3, pOut_A, pOut_B, pOut_C ); |
|
|
|
// Concatenate, and mark the text changes |
|
V_wcscat_safe( wcTmpDesc, L"\n" ); |
|
V_wcscat_safe( wcTmpDesc, wcTmp ); |
|
} |
|
|
|
pPanel->SetAttribOnly( !bShowName ); |
|
pPanel->SetTextYPos( 0 ); |
|
pPanel->SetItem( NULL ); |
|
pPanel->SetNoItemText( wcTmpName, wcTmpDesc, iNegAttribsBegin ); |
|
pPanel->InvalidateLayout(true); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void PositionMouseOverPanelForRecipe( vgui::Panel *pScissorPanel, vgui::Panel *pRecipePanel, vgui::ScrollableEditablePanel *pRecipeScroller, CItemModelPanel *pMouseOverItemPanel ) |
|
{ |
|
int x,y; |
|
vgui::ipanel()->GetAbsPos( pRecipePanel->GetVPanel(), x, y ); |
|
int xs,ys; |
|
vgui::ipanel()->GetAbsPos( pMouseOverItemPanel->GetParent()->GetVPanel(), xs, ys ); |
|
x -= xs; |
|
y -= ys; |
|
|
|
int iXPos = (x + (pRecipePanel->GetWide() * 0.5)) - (pMouseOverItemPanel->GetWide() * 0.5); |
|
int iYPos = (y + pRecipePanel->GetTall()); |
|
|
|
// Make sure the popup stays onscreen. |
|
if ( iXPos < 0 ) |
|
{ |
|
iXPos = 0; |
|
} |
|
else if ( (iXPos + pMouseOverItemPanel->GetWide()) > pMouseOverItemPanel->GetParent()->GetWide() ) |
|
{ |
|
iXPos = pMouseOverItemPanel->GetParent()->GetWide() - pMouseOverItemPanel->GetWide(); |
|
} |
|
|
|
if ( iYPos < 0 ) |
|
{ |
|
iYPos = 0; |
|
} |
|
else if ( (iYPos + pMouseOverItemPanel->GetTall() + YRES(32)) > pMouseOverItemPanel->GetParent()->GetTall() ) |
|
{ |
|
// Move it up above our item |
|
iYPos = y - pMouseOverItemPanel->GetTall() - YRES(4); |
|
} |
|
|
|
pMouseOverItemPanel->SetPos( iXPos, iYPos ); |
|
pMouseOverItemPanel->SetVisible( true ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CCraftingPanel::CCraftingPanel( vgui::Panel *parent, const char *panelName ) : CBaseLoadoutPanel( parent, panelName ) |
|
{ |
|
m_pRecipeListContainer = new vgui::EditablePanel( this, "recipecontainer" ); |
|
m_pRecipeListContainerScroller = new vgui::ScrollableEditablePanel( this, m_pRecipeListContainer, "recipecontainerscroller" ); |
|
m_pSelectedRecipeContainer = new vgui::EditablePanel( this, "selectedrecipecontainer" ); |
|
m_pRecipeButtonsKV = NULL; |
|
m_pRecipeFilterButtonsKV = NULL; |
|
m_bEventLogging = false; |
|
m_iCraftingAttempts = 0; |
|
m_iRecipeCategoryFilter = RECIPE_CATEGORY_CRAFTINGITEMS; |
|
m_iCurrentlySelectedRecipe = -1; |
|
CleanupPostCraft( true ); |
|
|
|
m_pToolTip = new CTFTextToolTip( this ); |
|
m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" ); |
|
m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false ); |
|
m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false ); |
|
m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel ); |
|
m_pToolTip->SetTooltipDelay( 0 ); |
|
|
|
m_pSelectionPanel = NULL; |
|
m_iSelectingForSlot = 0; |
|
|
|
m_pCraftButton = NULL; |
|
m_pUpgradeButton = NULL; |
|
m_pFreeAccountLabel = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CCraftingPanel::~CCraftingPanel( void ) |
|
{ |
|
if ( m_pRecipeButtonsKV ) |
|
{ |
|
m_pRecipeButtonsKV->deleteThis(); |
|
m_pRecipeButtonsKV = NULL; |
|
} |
|
if ( m_pRecipeFilterButtonsKV ) |
|
{ |
|
m_pRecipeFilterButtonsKV->deleteThis(); |
|
m_pRecipeFilterButtonsKV = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) |
|
{ |
|
LoadControlSettings( GetResFile() ); |
|
|
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
m_pRecipeListContainerScroller->GetScrollbar()->SetAutohideButtons( true ); |
|
m_pCraftButton = dynamic_cast<CExButton*>( m_pSelectedRecipeContainer->FindChildByName("CraftButton") ); |
|
if ( m_pCraftButton ) |
|
{ |
|
m_pCraftButton->AddActionSignalTarget( this ); |
|
} |
|
m_pUpgradeButton = dynamic_cast<CExButton*>( m_pSelectedRecipeContainer->FindChildByName("UpgradeButton") ); |
|
if ( m_pUpgradeButton ) |
|
{ |
|
m_pUpgradeButton->AddActionSignalTarget( this ); |
|
} |
|
m_pFreeAccountLabel = dynamic_cast<CExLabel*>( m_pSelectedRecipeContainer->FindChildByName("FreeAccountLabel") ); |
|
|
|
CreateRecipeFilterButtons(); |
|
UpdateRecipeFilter(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::ApplySettings( KeyValues *inResourceData ) |
|
{ |
|
BaseClass::ApplySettings( inResourceData ); |
|
|
|
KeyValues *pItemKV = inResourceData->FindKey( "recipebuttons_kv" ); |
|
if ( pItemKV ) |
|
{ |
|
if ( m_pRecipeButtonsKV ) |
|
{ |
|
m_pRecipeButtonsKV->deleteThis(); |
|
} |
|
m_pRecipeButtonsKV = new KeyValues("recipebuttons_kv"); |
|
pItemKV->CopySubkeys( m_pRecipeButtonsKV ); |
|
} |
|
|
|
KeyValues *pButtonKV = inResourceData->FindKey( "recipefilterbuttons_kv" ); |
|
if ( pButtonKV ) |
|
{ |
|
if ( m_pRecipeFilterButtonsKV ) |
|
{ |
|
m_pRecipeFilterButtonsKV->deleteThis(); |
|
} |
|
m_pRecipeFilterButtonsKV = new KeyValues("recipefilterbuttons_kv"); |
|
pButtonKV->CopySubkeys( m_pRecipeFilterButtonsKV ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::PerformLayout( void ) |
|
{ |
|
BaseClass::PerformLayout(); |
|
|
|
// Need to lay these out before we start making item panels inside them |
|
m_pRecipeListContainer->InvalidateLayout( true ); |
|
m_pRecipeListContainerScroller->InvalidateLayout( true ); |
|
|
|
// Position the recipe filters |
|
FOR_EACH_VEC( m_pRecipeFilterButtons, i ) |
|
{ |
|
if ( m_pRecipeFilterButtonsKV ) |
|
{ |
|
m_pRecipeFilterButtons[i]->ApplySettings( m_pRecipeFilterButtonsKV ); |
|
m_pRecipeFilterButtons[i]->InvalidateLayout(); |
|
} |
|
|
|
int iButtonW, iButtonH; |
|
m_pRecipeFilterButtons[i]->GetSize( iButtonW, iButtonH ); |
|
|
|
int iXPos = (GetWide() * 0.5) + m_iFilterOffcenterX + ((iButtonW + m_iFilterDeltaX) * i); |
|
int iYPos = m_iFilterYPos;// + ((iButtonH + m_iFilterDeltaY) * i); |
|
m_pRecipeFilterButtons[i]->SetPos( iXPos, iYPos ); |
|
} |
|
|
|
// Position the recipe buttons |
|
for ( int i = 0; i < m_pRecipeButtons.Count(); i++ ) |
|
{ |
|
if ( m_pRecipeButtonsKV ) |
|
{ |
|
m_pRecipeButtons[i]->ApplySettings( m_pRecipeButtonsKV ); |
|
m_pRecipeButtons[i]->InvalidateLayout(); |
|
} |
|
|
|
int iYDelta = m_pRecipeButtons[0]->GetTall() + YRES(2); |
|
|
|
// Once we've setup our first item, we know how large to make the container |
|
if ( i == 0 ) |
|
{ |
|
m_pRecipeListContainer->SetSize( m_pRecipeListContainer->GetWide(), iYDelta * m_pRecipeButtons.Count() ); |
|
} |
|
|
|
int x,y; |
|
m_pRecipeButtons[i]->GetPos( x,y ); |
|
m_pRecipeButtons[i]->SetPos( x, (iYDelta * i) ); |
|
} |
|
|
|
// Now that the container has been sized, tell the scroller to re-evaluate |
|
m_pRecipeListContainerScroller->InvalidateLayout(); |
|
m_pRecipeListContainerScroller->GetScrollbar()->InvalidateLayout(); |
|
|
|
// Then position all our item panels |
|
for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) |
|
{ |
|
PositionItemPanel( m_pItemModelPanels[i], i ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::CreateRecipeFilterButtons( void ) |
|
{ |
|
for ( int i = 0; i < NUM_RECIPE_CATEGORIES; i++ ) |
|
{ |
|
if ( m_pRecipeFilterButtons.Count() <= i ) |
|
{ |
|
CImageButton *pNewButton = new CImageButton( this, g_RecipeFilters[i].pszTooltipString ); |
|
m_pRecipeFilterButtons.AddToTail( pNewButton ); |
|
} |
|
|
|
m_pRecipeFilterButtons[i]->SetInactiveImage( g_RecipeFilters[i].pszButtonImage ); |
|
m_pRecipeFilterButtons[i]->SetActiveImage( g_RecipeFilters[i].pszButtonImageMouseover ); |
|
m_pRecipeFilterButtons[i]->SetTooltip( m_pToolTip, g_RecipeFilters[i].pszTooltipString ); |
|
const char *pszCommand = VarArgs("selectfilter%d", i ); |
|
m_pRecipeFilterButtons[i]->SetCommand( pszCommand ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::UpdateRecipeFilter( void ) |
|
{ |
|
int iMatchingRecipes = 0; |
|
m_iCurrentlySelectedRecipe = -1; |
|
m_iCurrentRecipeTotalInputs = 0; |
|
m_iCurrentRecipeTotalOutputs = 0; |
|
|
|
FOR_EACH_VEC( m_pRecipeFilterButtons, i ) |
|
{ |
|
bool bForceDepressed = ( i == m_iRecipeCategoryFilter ); |
|
m_pRecipeFilterButtons[i]->ForceDepressed( bForceDepressed ); |
|
} |
|
|
|
// Loop through the known recipes, and see which ones match our category filter |
|
for ( int i = 0; i < TFInventoryManager()->GetLocalTFInventory()->GetRecipeCount(); i++ ) |
|
{ |
|
const CEconCraftingRecipeDefinition *pRecipeDef = TFInventoryManager()->GetLocalTFInventory()->GetRecipeDef(i); |
|
if ( !pRecipeDef ) |
|
continue; |
|
|
|
if ( pRecipeDef->IsDisabled() ) |
|
continue; |
|
|
|
if ( pRecipeDef->GetCategory() != m_iRecipeCategoryFilter ) |
|
continue; |
|
|
|
wchar_t wTemp[256]; |
|
wchar_t *pName_A = g_pVGuiLocalize->Find( pRecipeDef->GetName_A() ); |
|
g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find( pRecipeDef->GetName() ), 1, pName_A ); |
|
SetButtonToRecipe( iMatchingRecipes, pRecipeDef->GetDefinitionIndex(), wTemp ); |
|
|
|
iMatchingRecipes++; |
|
} |
|
|
|
// Add a "Custom" option to the bottom of the Special recipe list |
|
if ( m_iRecipeCategoryFilter == RECIPE_CATEGORY_SPECIAL ) |
|
{ |
|
SetButtonToRecipe( iMatchingRecipes, RECIPE_CUSTOM, g_pVGuiLocalize->Find("#Craft_Recipe_Custom") ); |
|
iMatchingRecipes++; |
|
} |
|
|
|
// Delete excess buttons |
|
for ( int i = m_pRecipeButtons.Count() - 1; i >= iMatchingRecipes; i-- ) |
|
{ |
|
m_pRecipeButtons[i]->MarkForDeletion(); |
|
m_pRecipeButtons.Remove( i ); |
|
} |
|
|
|
// Move the scrollbar to the top |
|
m_pRecipeListContainerScroller->GetScrollbar()->SetValue( 0 ); |
|
|
|
UpdateSelectedRecipe( true ); |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnCancelSelection( void ) |
|
{ |
|
if ( m_pSelectionPanel ) |
|
{ |
|
m_pSelectionPanel->SetVisible( false ); |
|
} |
|
|
|
CloseCraftingStatusDialog(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnSelectionReturned( KeyValues *data ) |
|
{ |
|
if ( data ) |
|
{ |
|
uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID ); |
|
if ( ulIndex == INVALID_ITEM_ID ) |
|
{ |
|
// should this be INVALID_ITEM_ID? |
|
m_InputItems[m_iSelectingForSlot] = 0; |
|
} |
|
else |
|
{ |
|
m_InputItems[m_iSelectingForSlot] = ulIndex; |
|
} |
|
|
|
UpdateModelPanels(); |
|
UpdateCraftButton(); |
|
} |
|
|
|
// It'll have deleted itself, so we don't need to clean it up |
|
OnCancelSelection(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory ) |
|
{ |
|
if ( bVisible ) |
|
{ |
|
if ( m_pSelectionPanel ) |
|
{ |
|
m_pSelectionPanel->SetVisible( false ); |
|
} |
|
|
|
memset( m_InputItems, 0, sizeof(m_InputItems) ); |
|
memset( m_ItemPanelCriteria, 0, sizeof(m_ItemPanelCriteria) ); |
|
m_iCurrentlySelectedRecipe = -1; |
|
m_iCurrentRecipeTotalInputs = 0; |
|
m_iCurrentRecipeTotalOutputs = 0; |
|
UpdateRecipeFilter(); |
|
|
|
if ( !m_bEventLogging ) |
|
{ |
|
m_bEventLogging = true; |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_ENTERED ); |
|
} |
|
} |
|
else |
|
{ |
|
CloseCraftingStatusDialog(); |
|
vgui::ivgui()->RemoveTickSignal( GetVPanel() ); |
|
} |
|
|
|
BaseClass::OnShowPanel( bVisible, bReturningFromArmory ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnClosing() |
|
{ |
|
if ( m_bEventLogging ) |
|
{ |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_EXITED ); |
|
m_bEventLogging = false; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex ) |
|
{ |
|
int iCenter = 0; |
|
int iButtonX, iButtonY, iXPos, iYPos; |
|
|
|
if ( IsInputItemPanel(iIndex) ) |
|
{ |
|
iButtonX = (iIndex % CRAFTING_SLOTS_INPUT_COLUMNS); |
|
iButtonY = (iIndex / CRAFTING_SLOTS_INPUT_COLUMNS); |
|
iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX); |
|
iYPos = m_iItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); |
|
} |
|
else |
|
{ |
|
int iButtonIndex = iIndex - CRAFTING_SLOTS_INPUTPANELS; |
|
iButtonX = (iButtonIndex % CRAFTING_SLOTS_OUTPUT_COLUMNS); |
|
iButtonY = (iButtonIndex / CRAFTING_SLOTS_OUTPUT_COLUMNS); |
|
iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX); |
|
iYPos = m_iOutputItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); |
|
} |
|
|
|
m_pItemModelPanels[iIndex]->SetPos( iXPos, iYPos ); |
|
return; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::UpdateRecipeItems( bool bClearInputItems ) |
|
{ |
|
if ( bClearInputItems ) |
|
{ |
|
memset( m_InputItems, 0, sizeof(m_InputItems) ); |
|
} |
|
|
|
memset( m_ItemPanelCriteria, 0, sizeof(m_ItemPanelCriteria) ); |
|
m_iCurrentRecipeTotalInputs = 0; |
|
m_iCurrentRecipeTotalOutputs = 0; |
|
|
|
if ( m_iCurrentlySelectedRecipe == -1 ) |
|
return; |
|
|
|
/* |
|
// Build lists of items divided by class & loadout slot, so recipes can quickly test themselves |
|
CUtlVector<CEconItem*> vecAllItems; |
|
CUtlVector<CEconItem*> vecItemsByClass[ LOADOUT_COUNT ]; |
|
CUtlVector<CEconItem*> vecItemsBySlot[ LOADOUT_POSITION_COUNT ]; |
|
|
|
for ( int i = 1; i <= TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount(); i++ ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i); |
|
if ( pItemData && pItemData->IsValid() ) |
|
{ |
|
CEconItem *pSOCData = pItemData->GetSOCData(); |
|
vecAllItems.AddToTail( pSOCData ); |
|
|
|
CTFItemDefinition *pItemDef = pItemData->GetStaticData(); |
|
|
|
// Put it in class lists for any class that can use it. Use the zeroth list as all-class items. |
|
if ( pItemDef->CanBeUsedByAllClasses() ) |
|
{ |
|
vecItemsByClass[0].AddToTail( pSOCData ); |
|
} |
|
for (int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) |
|
{ |
|
if ( pItemDef->CanBeUsedByClass(iClass) ) |
|
{ |
|
vecItemsByClass[iClass].AddToTail( pSOCData ); |
|
} |
|
} |
|
|
|
// Put it in the slot lists for any slot that it can be equipped in |
|
for (int iSlot = 0; iSlot < LOADOUT_POSITION_COUNT; iSlot++ ) |
|
{ |
|
if ( pItemDef->CanBePlacedInSlot( iSlot ) ) |
|
{ |
|
vecItemsBySlot[iSlot].AddToTail( pSOCData ); |
|
} |
|
} |
|
} |
|
} |
|
*/ |
|
|
|
// Find the items needed for the specified recipe |
|
if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) |
|
{ |
|
// Custom recipe. Show all open buttons, and let them put anything in there. |
|
m_iCurrentRecipeTotalInputs = CRAFTING_SLOTS_INPUTPANELS; |
|
m_iCurrentRecipeTotalOutputs = 0; |
|
|
|
FOR_EACH_VEC( m_pItemModelPanels, i ) |
|
{ |
|
m_pItemModelPanels[i]->SetNoItemText( "" ); |
|
} |
|
} |
|
else |
|
{ |
|
const CTFCraftingRecipeDefinition *pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); |
|
if ( pRecipeDef ) |
|
{ |
|
m_iCurrentRecipeTotalInputs = pRecipeDef->GetTotalInputItemsRequired(); |
|
m_iCurrentRecipeTotalOutputs = pRecipeDef->GetTotalOutputItems(); |
|
|
|
CUtlVector<itemid_t> vecItemsUsed; |
|
|
|
// Set the text in each of the item panels |
|
const CUtlVector<CItemSelectionCriteria> *vecInputCriteria; |
|
vecInputCriteria = pRecipeDef->GetInputItems(); |
|
CUtlVector<uint32> vecInputDupes; |
|
vecInputDupes = pRecipeDef->GetInputItemDupeCounts(); |
|
|
|
int iModelPanel = 0; |
|
FOR_EACH_VEC( *vecInputCriteria, i ) |
|
{ |
|
const char *pszNoItemText = GetItemTextForCriteria( &(*vecInputCriteria)[i] ); |
|
|
|
int iNumPanels = vecInputDupes[i] ? vecInputDupes[i] : 1; |
|
for ( int iPanel = 0; iPanel < iNumPanels; iPanel++ ) |
|
{ |
|
m_ItemPanelCriteria[iModelPanel] = &(*vecInputCriteria)[i]; |
|
if ( m_pItemModelPanels[iModelPanel] ) |
|
{ |
|
m_pItemModelPanels[iModelPanel]->SetNoItemText( pszNoItemText ); |
|
} |
|
iModelPanel++; |
|
} |
|
} |
|
|
|
// Set the output items as well |
|
CUtlVector<CItemSelectionCriteria> vecOutputCriteria; |
|
vecOutputCriteria = pRecipeDef->GetOutputItems(); |
|
FOR_EACH_VEC( vecOutputCriteria, i ) |
|
{ |
|
int iOutputPanel = CRAFTING_SLOTS_INPUTPANELS + i; |
|
|
|
CEconItemDefinition *pDef = GetItemDefFromCriteria( &vecOutputCriteria[i] ); |
|
if ( pDef ) |
|
{ |
|
//m_pItemModelPanels[iOutputPanel]->SetNoItemText( pszNoItemText ); |
|
CEconItemView *pItemData = new CEconItemView(); |
|
pItemData->Init( pDef->GetDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); |
|
if ( m_pItemModelPanels[iOutputPanel] ) |
|
{ |
|
m_pItemModelPanels[iOutputPanel]->SetItem( pItemData ); |
|
} |
|
delete pItemData; |
|
continue; |
|
} |
|
|
|
// If we didn't manage to extract an output, just use the recipe output string |
|
wchar_t wcTmpA[32]; |
|
wchar_t wcTmpB[32]; |
|
wchar_t wcTmpC[32]; |
|
wchar_t wcTmp[512]; |
|
wchar_t *pOut_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_A(), wcTmpA, sizeof( wcTmpA ) ); |
|
wchar_t *pOut_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_B(), wcTmpB, sizeof( wcTmpB ) ); |
|
wcTmp[0] = '\0'; |
|
V_wcscat_safe( wcTmp, pOut_A ); |
|
V_wcscat_safe( wcTmp, L" " ); |
|
V_wcscat_safe( wcTmp, pOut_B ); |
|
if ( Q_strnicmp( pRecipeDef->GetDescOutputs(), "#RDO_ABC", 8 ) == 0 ) |
|
{ |
|
wchar_t *pOut_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_C(), wcTmpC, sizeof( wcTmpC ) ); |
|
V_wcscat_safe( wcTmp, L" " ); |
|
V_wcscat_safe( wcTmp, pOut_C ); |
|
} |
|
|
|
if ( m_pItemModelPanels[iOutputPanel] ) |
|
{ |
|
m_pItemModelPanels[iOutputPanel]->SetItem( NULL ); |
|
m_pItemModelPanels[iOutputPanel]->SetNoItemText( wcTmp ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Now check to see if they've got the right items in there |
|
UpdateCraftButton(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::UpdateCraftButton( void ) |
|
{ |
|
if ( m_iCurrentlySelectedRecipe == -1 ) |
|
return; |
|
|
|
bool bAllowedToUse = true; |
|
|
|
const CEconCraftingRecipeDefinition *pRecipeDef = NULL; |
|
if ( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ) |
|
{ |
|
pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); |
|
if ( !pRecipeDef ) |
|
return; |
|
|
|
bAllowedToUse = ( !IsFreeTrialAccount() || !pRecipeDef->IsPremiumAccountOnly() ); |
|
} |
|
|
|
if ( m_pCraftButton ) |
|
{ |
|
m_pCraftButton->SetVisible( bAllowedToUse ); |
|
} |
|
if ( m_pUpgradeButton ) |
|
{ |
|
m_pUpgradeButton->SetVisible( !bAllowedToUse ); |
|
} |
|
if ( m_pFreeAccountLabel ) |
|
{ |
|
m_pFreeAccountLabel->SetVisible( !bAllowedToUse ); |
|
} |
|
|
|
if ( !bAllowedToUse ) |
|
return; |
|
|
|
bool bCraftButtonActive = false; |
|
if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) |
|
{ |
|
// Need at least one item in a slot |
|
for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); |
|
if ( pItemData ) |
|
{ |
|
bCraftButtonActive = true; |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
CUtlVector<CEconItem*> vecAllItems; |
|
for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); |
|
if ( pItemData ) |
|
{ |
|
vecAllItems.AddToTail( pItemData->GetSOCData() ); |
|
} |
|
} |
|
|
|
bCraftButtonActive = pRecipeDef->ItemListMatchesInputs( &vecAllItems, NULL, false, NULL ); |
|
} |
|
|
|
if ( m_pCraftButton ) |
|
{ |
|
m_pCraftButton->SetEnabled( bCraftButtonActive ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CCraftingPanel::GetItemTextForCriteria( const CItemSelectionCriteria *pCriteria ) |
|
{ |
|
// Otherwise, look at the first condition, and see if we can determine what the item is |
|
const char *pszVal = pCriteria->GetValueForFirstConditionOfType( k_EOperator_String_EQ ); |
|
if ( pszVal && pszVal[0] ) |
|
{ |
|
// Is it a loadout slot? |
|
int iSlot = StringFieldToInt( pszVal, ItemSystem()->GetItemSchema()->GetLoadoutStrings( EEquipType_t::EQUIP_TYPE_CLASS ), true ); |
|
if ( iSlot != -1 ) |
|
return ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[iSlot]; |
|
|
|
// Is it a craft material type? |
|
if ( V_stricmp( pszVal, "weapon" ) == 0 ) |
|
{ |
|
return "#RI_W"; |
|
} |
|
else if ( V_stricmp( pszVal, "hat" ) == 0 ) |
|
{ |
|
return "#RI_Hg"; |
|
} |
|
else if ( V_stricmp( pszVal, "craft_token" ) == 0 ) |
|
{ |
|
return "#RI_T"; |
|
} |
|
else if ( V_stricmp( pszVal, "class_token" ) == 0 ) |
|
{ |
|
return "#CI_T_C"; |
|
} |
|
else if ( V_stricmp( pszVal, "slot_token" ) == 0 ) |
|
{ |
|
return "#CI_T_S"; |
|
} |
|
|
|
// Is it an item name? |
|
CEconItemDefinition *pDef = ItemSystem()->GetItemSchema()->GetItemDefinitionByName(pszVal); |
|
if ( pDef ) |
|
return pDef->GetItemBaseName(); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CEconItemDefinition *CCraftingPanel::GetItemDefFromCriteria( const CItemSelectionCriteria *pCriteria ) |
|
{ |
|
// Otherwise, look at the first condition, and see if we can determine what the item is |
|
const char *pszVal = pCriteria->GetValueForFirstConditionOfType( k_EOperator_String_EQ ); |
|
if ( pszVal && pszVal[0] ) |
|
return ItemSystem()->GetItemSchema()->GetItemDefinitionByName(pszVal); |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::AddNewItemPanel( int iPanelIndex ) |
|
{ |
|
BaseClass::AddNewItemPanel( iPanelIndex ); |
|
|
|
// Move the model panels to our selected recipe container |
|
m_pItemModelPanels[iPanelIndex]->SetParent( m_pSelectedRecipeContainer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::UpdateModelPanels( void ) |
|
{ |
|
BaseClass::UpdateModelPanels(); |
|
|
|
for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) |
|
{ |
|
if ( IsInputItemPanel(i) ) |
|
{ |
|
if ( m_InputItems[i] != 0 ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); |
|
m_pItemModelPanels[i]->SetItem( pItemData ); |
|
m_pItemModelPanels[i]->SetVisible( true ); |
|
m_pItemModelPanels[i]->SetShowEquipped( true ); |
|
SetBorderForItem( m_pItemModelPanels[i], false ); |
|
} |
|
else |
|
{ |
|
m_pItemModelPanels[i]->SetItem( NULL ); |
|
|
|
// Always show the number of slots that the recipe uses |
|
bool bVisible = (m_iCurrentRecipeTotalInputs > i); |
|
m_pItemModelPanels[i]->SetVisible( bVisible ); |
|
} |
|
} |
|
else |
|
{ |
|
bool bVisible = ((m_iCurrentRecipeTotalOutputs + CRAFTING_SLOTS_INPUTPANELS) > i); |
|
m_pItemModelPanels[i]->SetVisible( bVisible ); |
|
} |
|
} |
|
|
|
vgui::Panel *pLabel = m_pSelectedRecipeContainer->FindChildByName("OutputLabel"); |
|
if ( pLabel ) |
|
{ |
|
pLabel->SetVisible( m_iCurrentRecipeTotalOutputs > 0 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::SetButtonToRecipe( int iButton, int iDefIndex, wchar_t *pszText ) |
|
{ |
|
// Re-use existing buttons, or make new ones if we need more |
|
CRecipeButton *pRecipeButton = NULL; |
|
if ( iButton < m_pRecipeButtons.Count() ) |
|
{ |
|
pRecipeButton = m_pRecipeButtons[iButton]; |
|
} |
|
else |
|
{ |
|
pRecipeButton = new CRecipeButton( m_pRecipeListContainer, "selectrecipe", "", this, "selectrecipe" ); |
|
if ( m_pRecipeButtonsKV ) |
|
{ |
|
pRecipeButton->ApplySettings( m_pRecipeButtonsKV ); |
|
} |
|
pRecipeButton->MakeReadyForUse(); |
|
m_pRecipeButtons.AddToTail( pRecipeButton ); |
|
} |
|
|
|
const char *pszCommand = VarArgs("selectrecipe%d", iDefIndex ); |
|
pRecipeButton->SetCommand( pszCommand ); |
|
pRecipeButton->SetText( pszText ); |
|
pRecipeButton->SetDefIndex( iDefIndex ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::UpdateSelectedRecipe( bool bClearInputItems ) |
|
{ |
|
for ( int i = 0; i < m_pRecipeButtons.Count(); i++ ) |
|
{ |
|
bool bSelected = m_pRecipeButtons[i]->m_iRecipeDefIndex == m_iCurrentlySelectedRecipe; |
|
m_pRecipeButtons[i]->ForceDepressed( bSelected ); |
|
m_pRecipeButtons[i]->RecalculateDepressedState(); |
|
|
|
if ( bSelected ) |
|
{ |
|
wchar_t wszText[1024]; |
|
m_pRecipeButtons[i]->GetText( wszText, ARRAYSIZE( wszText ) ); |
|
m_pSelectedRecipeContainer->SetDialogVariable( "recipetitle", wszText ); |
|
|
|
if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) |
|
{ |
|
m_pSelectedRecipeContainer->SetDialogVariable( "recipeinputstring", g_pVGuiLocalize->Find("#Craft_Recipe_CustomDesc") ); |
|
} |
|
else |
|
{ |
|
const CTFCraftingRecipeDefinition *pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); |
|
if ( pRecipeDef ) |
|
{ |
|
// Build the input string |
|
wchar_t wcTmpA[32]; |
|
wchar_t wcTmpB[32]; |
|
wchar_t wcTmpC[32]; |
|
wchar_t wcTmpDesc[512]; |
|
wchar_t *pInp_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_A(), wcTmpA, sizeof( wcTmpA ) ); |
|
wchar_t *pInp_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_B(), wcTmpB, sizeof( wcTmpB ) ); |
|
wchar_t *pInp_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_C(), wcTmpC, sizeof( wcTmpC ) ); |
|
g_pVGuiLocalize->ConstructString_safe( wcTmpDesc, g_pVGuiLocalize->Find( pRecipeDef->GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C ); |
|
m_pSelectedRecipeContainer->SetDialogVariable( "recipeinputstring", wcTmpDesc ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
m_pSelectedRecipeContainer->SetVisible( m_iCurrentlySelectedRecipe != -1 ); |
|
|
|
UpdateRecipeItems( bClearInputItems ); |
|
UpdateModelPanels(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnCommand( const char *command ) |
|
{ |
|
if ( !Q_strnicmp( command, "selectrecipe", 12 ) ) |
|
{ |
|
const char *pszNum = command+12; |
|
if ( pszNum && pszNum[0] ) |
|
{ |
|
m_iCurrentlySelectedRecipe = atoi(pszNum); |
|
UpdateSelectedRecipe( true ); |
|
} |
|
|
|
return; |
|
} |
|
if ( !Q_strnicmp( command, "selectfilter", 12 ) ) |
|
{ |
|
const char *pszNum = command+12; |
|
if ( pszNum && pszNum[0] ) |
|
{ |
|
m_iRecipeCategoryFilter = (recipecategories_t)atoi(pszNum); |
|
UpdateRecipeFilter(); |
|
} |
|
|
|
return; |
|
} |
|
else if ( !Q_strnicmp( command, "back", 4 ) ) |
|
{ |
|
PostMessage( GetParent(), new KeyValues("CraftingClosed") ); |
|
return; |
|
} |
|
else if ( !Q_strnicmp( command, "craft", 5 ) ) |
|
{ |
|
if ( CheckForUntradableItems() ) |
|
{ |
|
Craft(); |
|
} |
|
return; |
|
} |
|
else if ( !Q_stricmp( command, "upgrade" ) ) |
|
{ |
|
EconUI()->CloseEconUI(); |
|
EconUI()->OpenStorePanel( STOREPANEL_SHOW_UPGRADESTEPS, false ); |
|
return; |
|
} |
|
else if ( !Q_stricmp( command, "reloadscheme" ) ) |
|
{ |
|
InvalidateLayout( true, true ); |
|
} |
|
|
|
BaseClass::OnCommand( command ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnRecipePanelEntered( vgui::Panel *panel ) |
|
{ |
|
CRecipeButton *pRecipePanel = dynamic_cast < CRecipeButton * > ( panel ); |
|
|
|
if ( pRecipePanel && IsVisible() && !IsIgnoringItemPanelEnters() ) |
|
{ |
|
const CEconCraftingRecipeDefinition *pRecipeDef = NULL; |
|
if ( pRecipePanel->m_iRecipeDefIndex != RECIPE_CUSTOM ) |
|
{ |
|
pRecipeDef = TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( pRecipePanel->m_iRecipeDefIndex ); |
|
} |
|
|
|
SetItemPanelToRecipe( GetMouseOverPanel(), pRecipeDef, false ); |
|
PositionMouseOverPanelForRecipe( this, pRecipePanel, m_pRecipeListContainerScroller, GetMouseOverPanel() ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnRecipePanelExited( vgui::Panel *panel ) |
|
{ |
|
GetMouseOverPanel()->SetAttribOnly( false ); |
|
GetMouseOverPanel()->SetTextYPos( YRES(20) ); |
|
GetMouseOverPanel()->SetVisible( false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CCraftingPanel::GetItemPanelIndex( CItemModelPanel *pItemPanel ) |
|
{ |
|
for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) |
|
{ |
|
if ( m_pItemModelPanels[i] == pItemPanel ) |
|
return i; |
|
} |
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnItemPanelMousePressed( vgui::Panel *panel ) |
|
{ |
|
CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); |
|
|
|
if ( pItemPanel && IsVisible() && !pItemPanel->IsGreyedOut() ) |
|
{ |
|
int iPos = GetItemPanelIndex(pItemPanel); |
|
if ( IsInputItemPanel(iPos) ) |
|
{ |
|
m_iSelectingForSlot = iPos; |
|
|
|
// Create it the first time around |
|
if ( !m_pSelectionPanel ) |
|
{ |
|
m_pSelectionPanel = new CCraftingItemSelectionPanel( this ); |
|
} |
|
|
|
if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) |
|
{ |
|
m_pSelectionPanel->UpdateOnShow( NULL, true, m_InputItems, ARRAYSIZE(m_InputItems) ); |
|
} |
|
else |
|
{ |
|
// Clicked on an item in the crafting area. Open up the selection panel. |
|
m_pSelectionPanel->UpdateOnShow( m_ItemPanelCriteria[iPos], false, m_InputItems, ARRAYSIZE(m_InputItems) ); |
|
} |
|
|
|
m_pSelectionPanel->ShowDuplicateCounts( true ); |
|
m_pSelectionPanel->ShowPanel( 0, true ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
static void ConfirmCraft( bool bConfirmed, void* pContext ) |
|
{ |
|
CCraftingPanel *pCraftingPanel = ( CCraftingPanel* )pContext; |
|
if ( bConfirmed ) |
|
{ |
|
pCraftingPanel->Craft(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CCraftingPanel::CheckForUntradableItems( void ) |
|
{ |
|
bool bHasUntradable = false; |
|
for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) |
|
{ |
|
if ( m_InputItems[i] != 0 ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); |
|
if ( pItemData->IsTradable() == false ) |
|
{ |
|
bHasUntradable = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( bHasUntradable ) |
|
{ |
|
CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#Craft_Untradable_Title", "#Craft_Untradable_Text", "#GameUI_OK", "#Cancel", &ConfirmCraft ); |
|
pDialog->SetContext( this ); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::Craft( void ) |
|
{ |
|
// Build our list of items that we're trying to craft |
|
++m_iCraftingAttempts; |
|
CUtlVector<itemid_t> vecCraftingItems; |
|
for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) |
|
{ |
|
if ( m_InputItems[i] != 0 ) |
|
{ |
|
CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_ATTEMPT, pItemData, m_iCraftingAttempts ); |
|
vecCraftingItems.AddToTail( m_InputItems[i] ); |
|
} |
|
} |
|
|
|
if ( !vecCraftingItems.Count() ) |
|
return; |
|
|
|
GCSDK::CGCMsg<MsgGCCraft_t> msg( k_EMsgGCCraft ); |
|
msg.Body().m_nRecipeDefIndex = m_iCurrentlySelectedRecipe; |
|
msg.Body().m_nItemCount = vecCraftingItems.Count(); |
|
for ( int i = 0; i < vecCraftingItems.Count(); i++ ) |
|
{ |
|
msg.AddUint64Data( vecCraftingItems[i] ); |
|
} |
|
GCClientSystem()->BSendMessage( msg ); |
|
|
|
OpenCraftingStatusDialog( this, "#CraftUpdate_Start", true, false, false ); |
|
|
|
// Start ticking so we can give up waiting if we don't get a response from the GC |
|
// We use the VGUI time, because we may not be in a game at all. |
|
m_flAbortCraftingAt = vgui::system()->GetCurrentTime() + 10; |
|
m_bWaitingForCraftItems = false; |
|
m_iRecipeIndexTried = m_iCurrentlySelectedRecipe; |
|
|
|
vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnCraftResponse( EGCMsgResponse eResponse, CUtlVector<uint64> *vecCraftedIndices, int iRecipeUsed ) |
|
{ |
|
switch ( eResponse ) |
|
{ |
|
case k_EGCMsgResponseNoMatch: |
|
{ |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_NO_RECIPE_MATCH, NULL, m_iCraftingAttempts ); |
|
CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); |
|
OpenCraftingStatusDialog( this, "#CraftUpdate_NoMatch", false, true, false ); |
|
} |
|
break; |
|
|
|
case k_EGCMsgResponseDenied: |
|
{ |
|
// Craft denied. |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_FAILURE, NULL, m_iCraftingAttempts ); |
|
CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); |
|
OpenCraftingStatusDialog( this, "#CraftUpdate_Denied", false, true, false ); |
|
} |
|
break; |
|
|
|
// We've got the list of items crafted. We save off the item list until our item cache has all the items. |
|
case k_EGCMsgResponseOK: |
|
{ |
|
// Start ticking, and wait until the cache contains all the items in the list. |
|
m_bWaitingForCraftItems = true; |
|
m_vecNewlyCraftedItems = *vecCraftedIndices; |
|
|
|
if ( iRecipeUsed != m_iRecipeIndexTried && iRecipeUsed != -1 ) |
|
{ |
|
m_iNewRecipeIndex = iRecipeUsed; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
{ |
|
// Craft failed in some way. |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_FAILURE, NULL, m_iCraftingAttempts ); |
|
OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false ); |
|
CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::ShowCraftFinish( void ) |
|
{ |
|
TFInventoryManager()->ShowItemsCrafted( &m_vecNewlyCraftedItems ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::OnTick( void ) |
|
{ |
|
BaseClass::OnTick(); |
|
|
|
if ( IsVisible() ) |
|
{ |
|
if ( m_flAbortCraftingAt ) |
|
{ |
|
if ( m_flAbortCraftingAt < vgui::system()->GetCurrentTime() ) |
|
{ |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_TIMEOUT, NULL, m_iCraftingAttempts ); |
|
CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); |
|
OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false ); |
|
return; |
|
} |
|
} |
|
|
|
if ( m_bWaitingForCraftItems ) |
|
{ |
|
// If all the items in our newly crafted list are in the cache, we can show the pickup. |
|
FOR_EACH_VEC_BACK( m_vecNewlyCraftedItems, i ) |
|
{ |
|
CEconItemView* pNewItem = InventoryManager()->GetLocalInventory()->GetInventoryItemByItemID( m_vecNewlyCraftedItems[i] ); |
|
if ( pNewItem == NULL ) |
|
return; |
|
C_CTF_GameStats.Event_Crafting( IE_CRAFTING_SUCCESS, pNewItem, m_iCraftingAttempts ); |
|
} |
|
|
|
m_bWaitingForCraftItems = false; |
|
|
|
// We have all the new items, show the pickup |
|
OpenCraftingStatusDialog( this, "#CraftUpdate_Success", false, true, true ); |
|
CleanupPostCraft( true ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingPanel::CleanupPostCraft( bool bClearInputItems ) |
|
{ |
|
m_flAbortCraftingAt = 0; |
|
m_bWaitingForCraftItems = false; |
|
|
|
UpdateSelectedRecipe( bClearInputItems ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
ConVar *CCraftingPanel::GetExplanationConVar( void ) |
|
{ |
|
return &tf_explanations_craftingpanel; |
|
} |
|
|
|
//================================================================================================================================ |
|
// NOT CONNECTED TO STEAM WARNING DIALOG |
|
//================================================================================================================================ |
|
static vgui::DHANDLE<CCraftingStatusDialog> g_CraftingStatusPanel; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CCraftingStatusDialog::CCraftingStatusDialog( vgui::Panel *pParent, const char *pElementName ) : BaseClass( pParent, "CraftingStatusDialog" ) |
|
{ |
|
m_pRecipePanel = vgui::SETUP_PANEL( new CItemModelPanel( this, "RecipeItemModelPanel" ) ); |
|
m_bShowNewRecipe = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingStatusDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
if ( m_bShowNewRecipe ) |
|
{ |
|
LoadControlSettings( "resource/UI/NewRecipeFoundDialog.res" ); |
|
} |
|
else |
|
{ |
|
LoadControlSettings( "resource/UI/CraftingStatusDialog.res" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingStatusDialog::OnCommand( const char *command ) |
|
{ |
|
bool bClose = false; |
|
|
|
if ( !Q_stricmp( command, "close" ) ) |
|
{ |
|
// If we were a success, show the player their new crafted items |
|
if ( m_bShowOnExit ) |
|
{ |
|
if ( EconUI()->GetCraftingPanel() ) |
|
{ |
|
EconUI()->GetCraftingPanel()->ShowCraftFinish(); |
|
} |
|
|
|
m_bShowOnExit = false; |
|
} |
|
|
|
bClose = true; |
|
} |
|
else if ( !Q_stricmp( command, "forceclose" ) ) |
|
{ |
|
bClose = true; |
|
} |
|
|
|
if ( bClose ) |
|
{ |
|
m_bShowOnExit = false; |
|
TFModalStack()->PopModal( this ); |
|
SetVisible( false ); |
|
MarkForDeletion(); |
|
|
|
EconUI()->SetPreventClosure( false ); |
|
return; |
|
} |
|
|
|
BaseClass::OnCommand( command ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingStatusDialog::OnTick( void ) |
|
{ |
|
if ( !m_bAnimateEllipses || !IsVisible() ) |
|
{ |
|
vgui::ivgui()->RemoveTickSignal( GetVPanel() ); |
|
} |
|
else |
|
{ |
|
m_iNumEllipses = ((m_iNumEllipses+1) % 4); |
|
} |
|
|
|
switch ( m_iNumEllipses ) |
|
{ |
|
case 3: SetDialogVariable( "ellipses", L"..." ); break; |
|
case 2: SetDialogVariable( "ellipses", L".." ); break; |
|
case 1: SetDialogVariable( "ellipses", L"." ); break; |
|
default: SetDialogVariable( "ellipses", L"" ); break; |
|
} |
|
|
|
BaseClass::OnTick(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingStatusDialog::UpdateSchemeForVersion( bool bRecipe ) |
|
{ |
|
m_bShowNewRecipe = bRecipe; |
|
InvalidateLayout( false, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCraftingStatusDialog::ShowStatusUpdate( bool bAnimateEllipses, bool bAllowClose, bool bShowOnExit ) |
|
{ |
|
m_bShowNewRecipe = false; |
|
|
|
CExButton *pButton = dynamic_cast<CExButton*>( FindChildByName("CloseButton") ); |
|
if ( pButton ) |
|
{ |
|
pButton->SetVisible( bAllowClose ); |
|
pButton->SetEnabled( bAllowClose ); |
|
} |
|
|
|
m_bAnimateEllipses = bAnimateEllipses; |
|
if ( m_bAnimateEllipses ) |
|
{ |
|
vgui::ivgui()->AddTickSignal( GetVPanel(), 500 ); |
|
SetDialogVariable( "ellipses", L"" ); |
|
m_iNumEllipses = 0; |
|
} |
|
else |
|
{ |
|
vgui::ivgui()->RemoveTickSignal( GetVPanel() ); |
|
SetDialogVariable( "ellipses", L"" ); |
|
} |
|
|
|
m_bShowOnExit = bShowOnExit; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void SetupCraftingStatusDialog( vgui::Panel *pParent ) |
|
{ |
|
if (!g_CraftingStatusPanel.Get()) |
|
{ |
|
g_CraftingStatusPanel = vgui::SETUP_PANEL( new CCraftingStatusDialog( pParent, NULL ) ); |
|
} |
|
g_CraftingStatusPanel->SetVisible( true ); |
|
g_CraftingStatusPanel->MakePopup(); |
|
g_CraftingStatusPanel->MoveToFront(); |
|
g_CraftingStatusPanel->SetKeyBoardInputEnabled(true); |
|
g_CraftingStatusPanel->SetMouseInputEnabled(true); |
|
TFModalStack()->PushModal( g_CraftingStatusPanel ); |
|
|
|
EconUI()->SetPreventClosure( true ); |
|
} |
|
|
|
CCraftingStatusDialog *OpenCraftingStatusDialog( vgui::Panel *pParent, const char *pszText, bool bAnimateEllipses, bool bAllowClose, bool bShowOnExit ) |
|
{ |
|
SetupCraftingStatusDialog( pParent ); |
|
g_CraftingStatusPanel->UpdateSchemeForVersion( false ); |
|
g_CraftingStatusPanel->SetDialogVariable( "updatetext", g_pVGuiLocalize->Find( pszText ) ); |
|
g_CraftingStatusPanel->ShowStatusUpdate( bAnimateEllipses, bAllowClose, bShowOnExit ); |
|
return g_CraftingStatusPanel; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CloseCraftingStatusDialog( void ) |
|
{ |
|
if ( g_CraftingStatusPanel ) |
|
{ |
|
g_CraftingStatusPanel->OnCommand( "forceclose" ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive the craft response |
|
//----------------------------------------------------------------------------- |
|
class CGCCraftResponse : public GCSDK::CGCClientJob |
|
{ |
|
public: |
|
CGCCraftResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
GCSDK::CGCMsg<MsgGCStandardResponse_t> msg( pNetPacket ); |
|
|
|
CUtlVector<uint64> vecCraftedIndices; |
|
uint16 iItems = 0; |
|
if ( !msg.BReadUint16Data( &iItems ) ) |
|
return true; |
|
vecCraftedIndices.SetSize( iItems ); |
|
for ( int i = 0; i < iItems; i++ ) |
|
{ |
|
if( !msg.BReadUint64Data( &vecCraftedIndices[i] ) ) |
|
return true; |
|
} |
|
|
|
if ( EconUI()->GetCraftingPanel() ) |
|
{ |
|
EconUI()->GetCraftingPanel()->OnCraftResponse( (EGCMsgResponse)msg.Body().m_eResponse, &vecCraftedIndices, msg.Body().m_nResponseIndex ); |
|
} |
|
|
|
//Msg("RECEIVED CGCCraftResponse: %d\n", msg.Body().m_eResponse ); |
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CGCCraftResponse, "CGCCraftResponse", k_EMsgGCCraftResponse, GCSDK::k_EServerTypeGCClient ); |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive the Golden Wrench broadcast message |
|
//----------------------------------------------------------------------------- |
|
class CGCGoldenWrenchBroadcast : public GCSDK::CGCClientJob |
|
{ |
|
public: |
|
CGCGoldenWrenchBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
GCSDK::CProtoBufMsg<CMsgTFGoldenWrenchBroadcast> msg( pNetPacket ); |
|
|
|
// @todo Tom Bui: should we display this in some other manner? This gets covered up by the crafting panel. |
|
CHudNotificationPanel *pNotifyPanel = GET_HUDELEMENT( CHudNotificationPanel ); |
|
if ( pNotifyPanel ) |
|
{ |
|
bool bDeleted = msg.Body().deleted(); |
|
wchar_t szPlayerName[1024]; |
|
g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().user_name().c_str(), szPlayerName, sizeof(szPlayerName) ); |
|
wchar_t szWrenchNumber[16]=L""; |
|
_snwprintf( szWrenchNumber, ARRAYSIZE( szWrenchNumber ), L"%i", msg.Body().wrench_number() ); |
|
wchar_t szNotification[1024]=L""; |
|
g_pVGuiLocalize->ConstructString_safe( szNotification, |
|
g_pVGuiLocalize->Find( bDeleted ? "#TF_HUD_Event_GoldenWrench_D": "#TF_HUD_Event_GoldenWrench_C" ), |
|
2, szPlayerName, szWrenchNumber ); |
|
pNotifyPanel->SetupNotifyCustom( szNotification, HUD_NOTIFY_GOLDEN_WRENCH, 10.0f ); |
|
|
|
// echo to chat |
|
CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); |
|
if ( pHUDChat ) |
|
{ |
|
char szAnsi[1024]; |
|
g_pVGuiLocalize->ConvertUnicodeToANSI( szNotification, szAnsi, sizeof(szAnsi) ); |
|
|
|
pHUDChat->Printf( CHAT_FILTER_NONE, "%s", szAnsi ); |
|
} |
|
|
|
// play a sound |
|
vgui::surface()->PlaySound( bDeleted ? "vo/announcer_failure.mp3" : "vo/announcer_success.mp3" ); |
|
} |
|
|
|
//Msg("RECEIVED CGCCraftResponse: %d\n", msg.Body().m_eResponse ); |
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CGCGoldenWrenchBroadcast, "CGCGoldenWrenchBroadcast", k_EMsgGCGoldenWrenchBroadcast, GCSDK::k_EServerTypeGCClient ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive the Saxxy broadcast message |
|
//----------------------------------------------------------------------------- |
|
class CGSaxxyBroadcast : public GCSDK::CGCClientJob |
|
{ |
|
public: |
|
CGSaxxyBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
GCSDK::CProtoBufMsg<CMsgTFSaxxyBroadcast> msg( pNetPacket ); |
|
|
|
CEconNotification *pNotification = new CEconNotification(); |
|
pNotification->SetText( "#TF_Event_Saxxy_Deleted" ); |
|
pNotification->SetLifetime( 30.0f ); |
|
|
|
{ |
|
// Who deleted this? |
|
wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ]; |
|
g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().has_user_name() ? msg.Body().user_name().c_str() : NULL, wszPlayerName, sizeof( wszPlayerName ) ); |
|
pNotification->AddStringToken( "owner", wszPlayerName ); |
|
|
|
// What category was the Saxxy for? |
|
char szCategory[MAX_ATTRIBUTE_DESCRIPTION_LENGTH]; |
|
Q_snprintf( szCategory, sizeof( szCategory ), "Replay_Contest_Category%d", msg.Body().category_number() ); |
|
|
|
pNotification->AddStringToken( "category", g_pVGuiLocalize->Find( szCategory ) ); |
|
} |
|
|
|
NotificationQueue_Add( pNotification ); |
|
|
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CGSaxxyBroadcast, "CGSaxxyBroadcast", k_EMsgGCSaxxyBroadcast, GCSDK::k_EServerTypeGCClient ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive any generic item deletion notification |
|
//----------------------------------------------------------------------------- |
|
class CClientItemBroadcastNotificationJob : public GCSDK::CGCClientJob |
|
{ |
|
public: |
|
CClientItemBroadcastNotificationJob( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
GCSDK::CProtoBufMsg<CMsgGCTFSpecificItemBroadcast> msg( pNetPacket ); |
|
|
|
CEconNotification *pNotification = new CEconNotification(); |
|
pNotification->SetText( msg.Body().was_destruction() ? "#TF_Event_Item_Deleted" : "#TF_Event_Item_Created" ); |
|
pNotification->SetLifetime( 30.0f ); |
|
|
|
// Who deleted this? |
|
wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ]; |
|
g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().has_user_name() ? msg.Body().user_name().c_str() : NULL, wszPlayerName, sizeof( wszPlayerName ) ); |
|
pNotification->AddStringToken( "owner", wszPlayerName ); |
|
|
|
// What type of item was this? |
|
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( msg.Body().item_def_index() ); |
|
if ( pItemDef ) |
|
{ |
|
pNotification->AddStringToken( "item_name", g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) ); |
|
|
|
NotificationQueue_Add( pNotification ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CClientItemBroadcastNotificationJob, "CClientItemBroadcastNotificationJob", k_EMsgGCTFSpecificItemBroadcast, GCSDK::k_EServerTypeGCClient ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive the Saxxy Awarded broadcast message |
|
//----------------------------------------------------------------------------- |
|
class CGSaxxyAwardedBroadcast : public GCSDK::CGCClientJob |
|
{ |
|
private: |
|
// embedded notification for custom trigger |
|
class CSaxxyAwardedNotification : public CEconNotification |
|
{ |
|
public: |
|
CSaxxyAwardedNotification() |
|
{ |
|
SetSoundFilename( "vo/announcer_success.mp3" ); |
|
} |
|
|
|
virtual EType NotificationType() { return eType_Trigger; } |
|
|
|
virtual void Trigger() |
|
{ |
|
if ( steamapicontext && steamapicontext->SteamFriends() ) |
|
{ |
|
steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/saxxyawards/winners.php" ); |
|
} |
|
MarkForDeletion(); |
|
} |
|
}; |
|
|
|
public: |
|
|
|
CGSaxxyAwardedBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
GCSDK::CProtoBufMsg< CMsgSaxxyAwarded > msg( pNetPacket ); |
|
|
|
CEconNotification *pNotification = new CSaxxyAwardedNotification(); |
|
pNotification->SetText( "#TF_Event_Saxxy_Awarded" ); |
|
pNotification->SetLifetime( 30.0f ); |
|
|
|
{ |
|
// Winners |
|
CFmtStr1024 strWinners; |
|
for ( int i = 0; i < msg.Body().winner_names_size(); ++i ) |
|
{ |
|
strWinners.Append( msg.Body().winner_names( i ).c_str() ); |
|
if ( i + 1 < msg.Body().winner_names_size() ) |
|
{ |
|
strWinners.Append( "\n" ); |
|
} |
|
} |
|
wchar_t wszPlayerNames[ 1024 ]; |
|
g_pVGuiLocalize->ConvertANSIToUnicode( strWinners.Access(), wszPlayerNames, sizeof( wszPlayerNames ) ); |
|
pNotification->AddStringToken( "winners", wszPlayerNames ); |
|
|
|
// year |
|
CRTime cTime; |
|
cTime.SetToCurrentTime(); |
|
cTime.SetToGMT( false ); |
|
locchar_t wszYear[10]; |
|
loc_sprintf_safe( wszYear, LOCCHAR( "%04u" ), cTime.GetYear() ); |
|
pNotification->AddStringToken( "year", wszYear ); |
|
|
|
// What category was the Saxxy for? |
|
char szCategory[MAX_ATTRIBUTE_DESCRIPTION_LENGTH]; |
|
Q_snprintf( szCategory, sizeof( szCategory ), "Replay_Contest_Category%d", msg.Body().category() ); |
|
pNotification->AddStringToken( "category", g_pVGuiLocalize->Find( szCategory ) ); |
|
} |
|
|
|
NotificationQueue_Add( pNotification ); |
|
|
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CGSaxxyAwardedBroadcast, "CGSaxxyAwardedBroadcast", k_EMsgGCSaxxy_Awarded, GCSDK::k_EServerTypeGCClient ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: GC Msg handler to receive a generic system broadcast message |
|
//----------------------------------------------------------------------------- |
|
class CGCSystemMessageBroadcast : public GCSDK::CGCClientJob |
|
{ |
|
public: |
|
CGCSystemMessageBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
|
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
{ |
|
CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); |
|
if ( !pHUDChat ) |
|
return false; |
|
|
|
GCSDK::CProtoBufMsg<CMsgSystemBroadcast> msg( pNetPacket ); |
|
|
|
// retrieve the text |
|
const char *pchMessage = msg.Body().message().c_str(); |
|
wchar_t *pwMessage = g_pVGuiLocalize->Find( pchMessage ); |
|
wchar_t wszConvertedText[2048] = L""; |
|
if ( pwMessage == NULL ) |
|
{ |
|
g_pVGuiLocalize->ConvertANSIToUnicode( pchMessage, wszConvertedText, sizeof( wszConvertedText ) ); |
|
pwMessage = wszConvertedText; |
|
} |
|
|
|
Color color( 0xff, 0xcc, 0x33, 255 ); |
|
KeyValuesAD keyValues( "System Message" ); |
|
keyValues->SetWString( "message", pwMessage ); |
|
keyValues->SetColor( "custom_color", color ); |
|
|
|
// print to chat log |
|
wchar_t wszLocalizedString[2048] = L""; |
|
g_pVGuiLocalize->ConstructString_safe( wszLocalizedString, "#Notification_System_Message", keyValues ); |
|
pHUDChat->SetCustomColor( color ); |
|
pHUDChat->Printf( CHAT_FILTER_NONE, "%ls", wszLocalizedString ); |
|
|
|
// send to notification |
|
CEconNotification* pNotification = new CEconNotification(); |
|
pNotification->SetText( "#Notification_System_Message" ); |
|
pNotification->SetKeyValues( keyValues ); |
|
pNotification->SetLifetime( 30.0f ); |
|
pNotification->SetSoundFilename( "ui/system_message_alert.wav" ); |
|
NotificationQueue_Add( pNotification ); |
|
|
|
return true; |
|
} |
|
|
|
}; |
|
|
|
GC_REG_JOB( GCSDK::CGCClient, CGCSystemMessageBroadcast, "CGCSystemMessageBroadcast", k_EMsgGCSystemMessage, GCSDK::k_EServerTypeGCClient );
|
|
|