Team Fortress 2 Source Code as on 22/4/2020
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.

1819 lines
58 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_item_inventory.h"
  8. #include "econ_entity_creation.h"
  9. #include "tf_item_system.h"
  10. #include "vgui/ILocalize.h"
  11. #include "tier3/tier3.h"
  12. #ifdef CLIENT_DLL
  13. #include "c_tf_player.h"
  14. #include "item_pickup_panel.h"
  15. #include "KeyValues.h"
  16. #include "filesystem.h"
  17. #include "character_info_panel.h"
  18. #include "ienginevgui.h"
  19. #include "c_tf_gamestats.h"
  20. #include "econ_notifications.h"
  21. #include "achievementmgr.h"
  22. #include "baseachievement.h"
  23. #include "achievements_tf.h"
  24. #include "econ/econ_item_preset.h"
  25. #include "tf_shared_content_manager.h"
  26. #include "c_playerresource.h"
  27. #include "quest_log_panel.h"
  28. #include "backpack_panel.h"
  29. #include "materialsystem/itexture.h"
  30. #else
  31. #include "tf_player.h"
  32. #endif
  33. #include "gc_clientsystem.h"
  34. #include "econ_game_account_client.h"
  35. #include "gcsdk/gcclientsdk.h"
  36. #include "econ_gcmessages.h"
  37. #include "tf_gamerules.h"
  38. #include "tf_gcmessages.h"
  39. #include "econ_item.h"
  40. #include "game_item_schema.h"
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43. using namespace GCSDK;
  44. #ifdef CLIENT_DLL
  45. //-----------------------------------------------------------------------------
  46. CEconNotification_HasNewItems::CEconNotification_HasNewItems() : CEconNotification()
  47. {
  48. m_bHasTriggered = false;
  49. m_flExpireTime = -1.0f; // Does not initially expire
  50. m_bShowInGame = false;
  51. // Check to see if any items are not drops, if so show in game
  52. // do not show in game if the new items are only drops
  53. CPlayerInventory *pLocalInv = InventoryManager()->GetLocalInventory();
  54. if ( pLocalInv )
  55. {
  56. // Go through the root inventory and find any items that are in the "found" position
  57. int iCount = pLocalInv->GetItemCount();
  58. for ( int i = 0; i < iCount; i++ )
  59. {
  60. CEconItemView *pTmp = pLocalInv->GetItem(i);
  61. if ( !pTmp )
  62. continue;
  63. if ( pTmp->GetStaticData()->IsHidden() )
  64. continue;
  65. uint32 iPosition = pTmp->GetInventoryPosition();
  66. if ( IsUnacknowledged(iPosition) == false )
  67. continue;
  68. if ( InventoryManager()->GetBackpackPositionFromBackend(iPosition) != 0 )
  69. continue;
  70. // Now make sure we haven't got a clientside saved ack for this item.
  71. if ( InventoryManager()->HasBeenAckedByClient( pTmp ) )
  72. continue;
  73. // If item is not a drop we want to show the notification otherwise they'll get the notification on death
  74. int iFoundMethod = GetUnacknowledgedReason( iPosition );
  75. if ( iFoundMethod > UNACK_ITEM_DROPPED )
  76. {
  77. m_bShowInGame = true;
  78. break;
  79. }
  80. }
  81. }
  82. }
  83. CEconNotification_HasNewItems::~CEconNotification_HasNewItems()
  84. {
  85. if ( !m_bHasTriggered )
  86. {
  87. m_bHasTriggered = true;
  88. TFInventoryManager()->ShowItemsPickedUp( true, true, true );
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. CEconNotification_HasNewItemsOnKill::CEconNotification_HasNewItemsOnKill( int iVictimID )
  93. {
  94. m_bHasTriggered = false;
  95. SetLifetime( 3.0f );
  96. SetText( "#TF_EnemyDroppedItem" );
  97. wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH];
  98. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iVictimID ), wszPlayerName, sizeof( wszPlayerName ) );
  99. AddStringToken( "victim", wszPlayerName );
  100. m_bShowInGame = true;
  101. }
  102. /*static*/ bool CEconNotification_HasNewItemsOnKill::HasUnacknowledgedItems ()
  103. {
  104. // Check to see if any items are not drops, if so show in game
  105. // do not show in game if the new items are only drops
  106. CPlayerInventory *pLocalInv = InventoryManager()->GetLocalInventory();
  107. if ( pLocalInv )
  108. {
  109. // Go through the root inventory and find any items that are in the "found" position
  110. int iCount = pLocalInv->GetItemCount();
  111. for ( int i = 0; i < iCount; i++ )
  112. {
  113. CEconItemView *pTmp = pLocalInv->GetItem( i );
  114. if ( !pTmp )
  115. continue;
  116. if ( pTmp->GetStaticData()->IsHidden() )
  117. continue;
  118. uint32 iPosition = pTmp->GetInventoryPosition();
  119. if ( IsUnacknowledged( iPosition ) == false )
  120. continue;
  121. if ( InventoryManager()->GetBackpackPositionFromBackend( iPosition ) != 0 )
  122. continue;
  123. // Now make sure we haven't got a clientside saved ack for this item.
  124. if ( InventoryManager()->HasBeenAckedByClient( pTmp ) )
  125. continue;
  126. // If item is not a drop we want to show the notification otherwise they'll get the notification on death
  127. int iFoundMethod = GetUnacknowledgedReason( iPosition );
  128. if ( iFoundMethod > UNACK_ITEM_DROPPED )
  129. {
  130. return true;
  131. }
  132. }
  133. }
  134. return false;
  135. }
  136. #endif
  137. //-----------------------------------------------------------------------------
  138. // Purpose:
  139. //-----------------------------------------------------------------------------
  140. bool AreSlotsConsideredIdentical( EEquipType_t eEquipType, int iBaseSlot, int iTestSlot )
  141. {
  142. if ( eEquipType == EQUIP_TYPE_CLASS )
  143. {
  144. if ( iBaseSlot == LOADOUT_POSITION_MISC )
  145. {
  146. return IsMiscSlot( iTestSlot );
  147. }
  148. if ( iBaseSlot == LOADOUT_POSITION_BUILDING )
  149. {
  150. return IsBuildingSlot( iTestSlot );
  151. }
  152. if ( iBaseSlot == LOADOUT_POSITION_TAUNT )
  153. {
  154. return IsTauntSlot( iTestSlot );
  155. }
  156. }
  157. else if ( eEquipType == EQUIP_TYPE_ACCOUNT )
  158. {
  159. if ( iBaseSlot == ACCOUNT_LOADOUT_POSITION_ACCOUNT1 )
  160. {
  161. return IsQuestSlot( iTestSlot );
  162. }
  163. }
  164. return iBaseSlot == iTestSlot;
  165. }
  166. //-----------------------------------------------------------------------------
  167. CTFInventoryManager g_TFInventoryManager;
  168. CInventoryManager *InventoryManager( void )
  169. {
  170. return &g_TFInventoryManager;
  171. }
  172. CTFInventoryManager *TFInventoryManager( void )
  173. {
  174. return &g_TFInventoryManager;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose:
  178. //-----------------------------------------------------------------------------
  179. CTFInventoryManager::CTFInventoryManager( void )
  180. {
  181. }
  182. CTFInventoryManager::~CTFInventoryManager( void )
  183. {
  184. m_pBaseLoadoutItems.PurgeAndDeleteElements();
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. //-----------------------------------------------------------------------------
  189. void CTFInventoryManager::PostInit( void )
  190. {
  191. BaseClass::PostInit();
  192. GenerateBaseItems();
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: Generate & store the base item details for each class & loadout slot
  196. //-----------------------------------------------------------------------------
  197. void CTFInventoryManager::GenerateBaseItems( void )
  198. {
  199. // Purge our lists and make new
  200. m_pBaseLoadoutItems.PurgeAndDeleteElements();
  201. // Load a base top level invalid item
  202. {
  203. m_pDefaultItem = new CEconItemView;
  204. m_pDefaultItem->Invalidate();
  205. }
  206. //
  207. const CEconItemSchema::BaseItemDefinitionMap_t& mapItems = GetItemSchema()->GetBaseItemDefinitionMap();
  208. int iStart = 0;
  209. for ( int it = iStart; it != mapItems.InvalidIndex(); it = mapItems.NextInorder( it ) )
  210. {
  211. CEconItemView *pItem = new CEconItemView;
  212. pItem->Init( mapItems[it]->GetDefinitionIndex(), AE_USE_SCRIPT_VALUE, AE_USE_SCRIPT_VALUE, false );
  213. m_pBaseLoadoutItems.AddToTail( pItem );
  214. }
  215. }
  216. #ifdef CLIENT_DLL
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. bool CTFInventoryManager::EquipItemInLoadout( int iClass, int iSlot, itemid_t iItemID )
  221. {
  222. if ( !steamapicontext || !steamapicontext->SteamUser() )
  223. return false;
  224. // If they pass in a INVALID_ITEM_ID item id, we're just clearing the loadout slot
  225. if ( iItemID == INVALID_ITEM_ID )
  226. return m_LocalInventory.ClearLoadoutSlot( iClass, iSlot );
  227. CEconItemView *pItem = m_LocalInventory.GetInventoryItemByItemID( iItemID );
  228. if ( !pItem )
  229. return false;
  230. // We check for validity on the GC when we equip items, but we can't really trust anyone
  231. // and so we check here as well.
  232. if ( !AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItem->GetStaticData()->GetLoadoutSlot(iClass), iSlot ) )
  233. {
  234. return false;
  235. }
  236. if ( !pItem->GetStaticData()->CanBeUsedByClass( iClass ) )
  237. {
  238. return false;
  239. }
  240. // Equip the new item
  241. UpdateInventoryEquippedState( &m_LocalInventory, iItemID, iClass, iSlot );
  242. // TODO: Prediction
  243. // Item has been moved, so update our loadout.
  244. //m_LoadoutItems[iClass][iSlot] = iItemID;
  245. return true;
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose: Fills out pList with all inventory items that could fit into the specified loadout slot for a given class
  249. //-----------------------------------------------------------------------------
  250. int CTFInventoryManager::GetAllUsableItemsForSlot( int iClass, int iSlot, CUtlVector<CEconItemView*> *pList )
  251. {
  252. bool bIsAccountIndex = iClass == GEconItemSchema().GetAccountIndex();
  253. if ( bIsAccountIndex )
  254. {
  255. Assert( IsQuestSlot( iSlot ) );
  256. }
  257. else
  258. {
  259. Assert( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_CLASS_COUNT );
  260. Assert( iSlot >= -1 && iSlot < CLASS_LOADOUT_POSITION_COUNT );
  261. }
  262. int iCount = m_LocalInventory.GetItemCount();
  263. for ( int i = 0; i < iCount; i++ )
  264. {
  265. CEconItemView *pItem = m_LocalInventory.GetItem(i);
  266. CTFItemDefinition *pItemData = pItem->GetStaticData();
  267. if ( bIsAccountIndex != ( pItemData->GetEquipType() == EEquipType_t::EQUIP_TYPE_ACCOUNT ) )
  268. continue;
  269. if ( !bIsAccountIndex && !pItemData->CanBeUsedByClass(iClass) )
  270. continue;
  271. // Passing in iSlot of -1 finds all items usable by the class
  272. if ( iSlot >= 0 && pItem->GetStaticData()->GetLoadoutSlot( iClass ) != iSlot )
  273. continue;
  274. // Ignore unpack'd items
  275. if ( IsUnacknowledged( pItem->GetInventoryPosition() ) )
  276. continue;
  277. pList->AddToTail( m_LocalInventory.GetItem(i) );
  278. }
  279. return pList->Count();
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose: Fills out pList with all quest item in the local inventory
  283. //-----------------------------------------------------------------------------
  284. int CTFInventoryManager::GetAllQuestItems( CUtlVector<CEconItemView*> *pList )
  285. {
  286. if ( !pList )
  287. return 0;
  288. pList->RemoveAll();
  289. for ( int i = 0 ; i < m_LocalInventory.GetItemCount() ; ++i )
  290. {
  291. CEconItemView *pItem = m_LocalInventory.GetItem( i );
  292. // Cheapest test
  293. if ( pItem->GetItemDefinition()->GetQuestDef() == NULL )
  294. continue;
  295. pList->AddToTail( pItem );
  296. }
  297. return pList->Count();
  298. }
  299. #endif // CLIENT_DLL
  300. //-----------------------------------------------------------------------------
  301. // Purpose:
  302. //-----------------------------------------------------------------------------
  303. CEconItemView *CTFInventoryManager::GetItemInLoadoutForClass( int iClass, int iSlot, CSteamID *pID )
  304. {
  305. #ifdef CLIENT_DLL
  306. if ( !pID )
  307. {
  308. // If they didn't specify a steamID, use the local player
  309. if ( !steamapicontext || !steamapicontext->SteamUser() )
  310. return NULL;
  311. CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
  312. pID = &localSteamID;
  313. }
  314. #endif
  315. CTFPlayerInventory *pInv = GetInventoryForPlayer( *pID );
  316. if ( !pInv )
  317. return GetBaseItemForClass( iClass, iSlot );
  318. return pInv->GetItemInLoadout( iClass, iSlot );
  319. }
  320. CEconItemView *CTFInventoryManager::GetItemInLoadoutForAccount( int iSlot, CSteamID *pID )
  321. {
  322. #ifdef CLIENT_DLL
  323. if ( !pID )
  324. {
  325. // If they didn't specify a steamID, use the local player
  326. if ( !steamapicontext || !steamapicontext->SteamUser() )
  327. return NULL;
  328. CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
  329. pID = &localSteamID;
  330. }
  331. #endif
  332. CTFPlayerInventory *pInv = GetInventoryForPlayer( *pID );
  333. if ( !pInv )
  334. return NULL;
  335. return pInv->GetItemInLoadout( GEconItemSchema().GetAccountIndex(), iSlot );
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose:
  339. //-----------------------------------------------------------------------------
  340. CTFPlayerInventory *CTFInventoryManager::GetInventoryForPlayer( const CSteamID &playerID )
  341. {
  342. for ( int i = 0; i < m_pInventories.Count(); i++ )
  343. {
  344. if ( m_pInventories[i].pInventory->GetOwner() != playerID )
  345. continue;
  346. return assert_cast<CTFPlayerInventory*>( m_pInventories[i].pInventory );
  347. }
  348. return NULL;
  349. }
  350. #ifdef CLIENT_DLL
  351. //-----------------------------------------------------------------------------
  352. // Purpose:
  353. //-----------------------------------------------------------------------------
  354. int CTFInventoryManager::GetNumItemPickedUpItems( void )
  355. {
  356. int iResult = 0;
  357. int iCount = m_LocalInventory.GetItemCount();
  358. for ( int i = 0; i < iCount; i++ )
  359. {
  360. if ( IsUnacknowledged( m_LocalInventory.GetItem(i)->GetInventoryPosition() ) && !m_LocalInventory.GetItem(i)->GetStaticData()->IsHidden() )
  361. {
  362. ++iResult;
  363. }
  364. }
  365. return iResult;
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. bool CTFInventoryManager::ShowItemsPickedUp( bool bForce, bool bReturnToGame, bool bNoPanel )
  371. {
  372. // don't show new items in training, unless forced to do so
  373. // i.e. purchased something or traded...
  374. if ( bForce == false && TFGameRules() && ( TFGameRules()->IsInTraining() || TFGameRules()->IsCompetitiveMode() ) )
  375. {
  376. return false;
  377. }
  378. // Don't bring it up if we're already browsing something in the gameUI
  379. vgui::VPANEL gameuiPanel = enginevgui->GetPanel( PANEL_GAMEUIDLL );
  380. if ( !bForce && vgui::ipanel()->IsVisible( gameuiPanel ) )
  381. return false;
  382. CUtlVector<CEconItemView*> aItemsFound;
  383. // Go through the root inventory and find any items that are in the "found" position
  384. int iCount = m_LocalInventory.GetItemCount();
  385. for ( int i = 0; i < iCount; i++ )
  386. {
  387. CEconItemView *pTmp = m_LocalInventory.GetItem(i);
  388. if ( !pTmp )
  389. continue;
  390. if ( pTmp->GetStaticData()->IsHidden() )
  391. continue;
  392. uint32 iPosition = pTmp->GetInventoryPosition();
  393. if ( IsUnacknowledged(iPosition) == false )
  394. continue;
  395. if ( GetBackpackPositionFromBackend(iPosition) != 0 )
  396. continue;
  397. // Now make sure we haven't got a clientside saved ack for this item.
  398. // This makes sure we don't show multiple pickups for items that we've found,
  399. // but haven't been able to move out of unack'd position due to the GC being unavailable.
  400. if ( HasBeenAckedByClient( pTmp ) )
  401. continue;
  402. aItemsFound.AddToTail( pTmp );
  403. }
  404. if ( !aItemsFound.Count() )
  405. return CheckForRoomAndForceDiscard();
  406. // We're not forcing the player to make room yet. Just show the pickup panel.
  407. NotificationQueue_Remove( &CEconNotification_HasNewItems::IsNotificationType );
  408. CItemPickupPanel *pItemPanel = bNoPanel ? NULL : EconUI()->OpenItemPickupPanel();
  409. if ( pItemPanel )
  410. {
  411. pItemPanel->SetReturnToGame( bReturnToGame );
  412. }
  413. // Only acknowledge items if there is no panel
  414. // Panel will make calls to acknowledge items itself
  415. for ( int i = 0; i < aItemsFound.Count(); i++ )
  416. {
  417. if ( pItemPanel )
  418. {
  419. pItemPanel->AddItem( aItemsFound[i] );
  420. }
  421. else
  422. {
  423. AcknowledgeItem( aItemsFound[i] );
  424. }
  425. }
  426. if ( pItemPanel )
  427. {
  428. pItemPanel->MoveToFront();
  429. }
  430. else
  431. {
  432. SaveAckFile();
  433. }
  434. aItemsFound.Purge();
  435. return true;
  436. }
  437. //-----------------------------------------------------------------------------
  438. void CTFInventoryManager::AcknowledgeItem( CEconItemView *pItem, bool bMoveToBackpack /* = true */ )
  439. {
  440. SetAckedByClient( pItem );
  441. int iMethod = GetUnacknowledgedReason( pItem->GetInventoryPosition() ) - 1;
  442. if ( iMethod >= ARRAYSIZE( g_pszItemPickupMethodStringsUnloc ) || iMethod < 0 )
  443. iMethod = 0;
  444. EconUI()->Gamestats_ItemTransaction( IE_ITEM_RECEIVED, pItem, g_pszItemPickupMethodStringsUnloc[iMethod] );
  445. if ( iMethod+1 == UNACK_ITEM_PREVIEW_ITEM_PURCHASED )
  446. {
  447. // If we found a purchased preview item, we want to refresh the store view to remove the discount indicator.
  448. CStorePage* pStorePage = dynamic_cast<CStorePage*>( EconUI()->GetStorePanel()->GetActivePage() );
  449. if ( pStorePage )
  450. {
  451. pStorePage->UpdateModelPanels();
  452. }
  453. }
  454. // Move it to the first empty backpack position.
  455. if ( bMoveToBackpack )
  456. {
  457. SetItemBackpackPosition( pItem, 0, false, true );
  458. }
  459. }
  460. void CTFInventoryManager::Update( float frametime )
  461. {
  462. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  463. m_LocalInventory.UpdateWeaponSkinRequest();
  464. BaseClass::Update( frametime );
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose:
  468. //-----------------------------------------------------------------------------
  469. void CTFInventoryManager::ShowItemsCrafted( CUtlVector<itemid_t> *vecCraftedIndices )
  470. {
  471. CUtlVector<CEconItemView*> aItemsFound;
  472. FOR_EACH_VEC( *vecCraftedIndices, i )
  473. {
  474. CEconItemView *pItem = m_LocalInventory.GetInventoryItemByItemID( vecCraftedIndices->Element(i) );
  475. if ( pItem )
  476. {
  477. // Now make sure we haven't got a clientside saved ack for this item.
  478. // This makes sure we don't show multiple pickups for items that we've found,
  479. // but haven't been able to move out of unack'd position due to the GC being unavailable.
  480. if ( !HasBeenAckedByClient( pItem ) )
  481. {
  482. aItemsFound.AddToTail( pItem );
  483. }
  484. }
  485. }
  486. if ( !aItemsFound.Count() )
  487. return;
  488. NotificationQueue_Remove( &CEconNotification_HasNewItems::IsNotificationType );
  489. CItemPickupPanel *pItemPanel = EconUI()->OpenItemPickupPanel();
  490. for ( int i = 0; i < aItemsFound.Count(); i++ )
  491. {
  492. pItemPanel->AddItem( aItemsFound[i] );
  493. SetAckedByClient( aItemsFound[i] );
  494. #ifdef CLIENT_DLL
  495. EconUI()->Gamestats_ItemTransaction( IE_ITEM_RECEIVED, aItemsFound[i], "crafted" );
  496. #endif
  497. // Then move it to the first empty backpack position
  498. SetItemBackpackPosition( aItemsFound[i], 0, false, true );
  499. }
  500. SaveAckFile();
  501. pItemPanel->MoveToFront();
  502. aItemsFound.Purge();
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Purpose:
  506. //-----------------------------------------------------------------------------
  507. bool CTFInventoryManager::CheckForRoomAndForceDiscard( void )
  508. {
  509. // Go through the inventory and attempt to move any items outside the backpack into valid positions.
  510. // Remember the first item that we failed to move, so we can force a discard later.
  511. CEconItemView *pItem = NULL;
  512. const int iMaxItems = m_LocalInventory.GetMaxItemCount();
  513. int iCount = m_LocalInventory.GetItemCount();
  514. for ( int i = 0; i < iCount; i++ )
  515. {
  516. CEconItemView *pTmp = m_LocalInventory.GetItem(i);
  517. if ( !pTmp )
  518. continue;
  519. if ( pTmp->GetStaticData()->IsHidden() )
  520. continue;
  521. uint32 iPosition = pTmp->GetInventoryPosition();
  522. if ( IsUnacknowledged(iPosition) || GetBackpackPositionFromBackend(iPosition) > iMaxItems )
  523. {
  524. if ( !SetItemBackpackPosition( pTmp, 0, false, false ) )
  525. {
  526. pItem = pTmp;
  527. break;
  528. }
  529. }
  530. }
  531. // If we're not over the limit, we're done.
  532. if ( ( iCount - m_iPredictedDiscards ) <= iMaxItems )
  533. return false;
  534. if ( !pItem )
  535. return false;
  536. // We're forcing the player to make room for items he's found. Bring up that panel with the first item over the limit.
  537. CItemDiscardPanel *pDiscardPanel = EconUI()->OpenItemDiscardPanel();
  538. pDiscardPanel->SetItem( pItem );
  539. return true;
  540. }
  541. #endif
  542. bool CTFInventoryManager::SlotContainsBaseItems( EEquipType_t eType, int iSlot )
  543. {
  544. Assert( eType != EEquipType_t::EQUIP_TYPE_INVALID );
  545. if ( eType == EEquipType_t::EQUIP_TYPE_ACCOUNT )
  546. {
  547. return !IsQuestSlot( iSlot );
  548. }
  549. // Passtime gun
  550. if ( (iSlot == LOADOUT_POSITION_UTILITY) && TFGameRules() && TFGameRules()->IsPasstimeMode() )
  551. {
  552. return true;
  553. }
  554. // Halloween spellbook
  555. if ( iSlot == LOADOUT_POSITION_ACTION )
  556. {
  557. if ( TFGameRules() && TFGameRules()->IsUsingSpells() )
  558. return true;
  559. if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() )
  560. return true;
  561. }
  562. #ifdef STAGING_ONLY
  563. return ( ( iSlot < LOADOUT_POSITION_HEAD && iSlot != LOADOUT_POSITION_UTILITY && !IsTauntSlot( iSlot ) ) // Allow utility slots to be empty
  564. || iSlot == LOADOUT_POSITION_PDA3 );
  565. #else // STAGING_ONLY
  566. // Normal game
  567. return iSlot < LOADOUT_POSITION_HEAD;
  568. #endif // #else
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Purpose: Returns the item data for the base item in the loadout slot for a given class
  572. //-----------------------------------------------------------------------------
  573. CEconItemView *CTFInventoryManager::GetBaseItemForClass( int iClass, int iSlot )
  574. {
  575. // There is no base account item
  576. if ( iClass == GEconItemSchema().GetAccountIndex() )
  577. return m_pDefaultItem;
  578. if ( !HushAsserts() )
  579. {
  580. AssertMsg( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_CLASS_COUNT, "Invalid TF_CLASS_: %d", iClass );
  581. }
  582. Assert( iSlot >= 0 && iSlot < CLASS_LOADOUT_POSITION_COUNT );
  583. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_CLASS_COUNT || iSlot < 0 || iSlot >= CLASS_LOADOUT_POSITION_COUNT )
  584. return m_pDefaultItem;
  585. // Halloween spellbook
  586. if ( iSlot == LOADOUT_POSITION_ACTION )
  587. {
  588. CUtlVector< item_definition_index_t > stockActionItemDefIndices;
  589. static CSchemaItemDefHandle pItemDef_SpellBook( "TF_WEAPON_SPELLBOOK" );
  590. if ( TFGameRules() && TFGameRules()->IsUsingSpells() && pItemDef_SpellBook )
  591. {
  592. stockActionItemDefIndices.AddToTail( pItemDef_SpellBook->GetDefinitionIndex() );
  593. }
  594. static CSchemaItemDefHandle pItemDef_GrapplingHook( "TF_WEAPON_GRAPPLINGHOOK" );
  595. if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() && pItemDef_GrapplingHook )
  596. {
  597. stockActionItemDefIndices.AddToTail( pItemDef_GrapplingHook->GetDefinitionIndex() );
  598. }
  599. static CSchemaItemDefHandle pItemDef_MvMCanteen( "Default Power Up Canteen (MvM)" );
  600. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pItemDef_MvMCanteen )
  601. {
  602. stockActionItemDefIndices.AddToTail( pItemDef_MvMCanteen->GetDefinitionIndex() );
  603. }
  604. // Traverse List
  605. for ( CEconItemView *pActionItem : m_pBaseLoadoutItems )
  606. {
  607. for ( item_definition_index_t defIndex : stockActionItemDefIndices )
  608. {
  609. if ( pActionItem->GetItemDefIndex() == defIndex )
  610. return pActionItem;
  611. }
  612. }
  613. }
  614. if ( iSlot >= LOADOUT_POSITION_HEAD )
  615. return m_pDefaultItem;
  616. // Traverse List
  617. FOR_EACH_VEC( m_pBaseLoadoutItems, iItem )
  618. {
  619. if ( m_pBaseLoadoutItems[iItem]->GetItemDefinition()->GetLoadoutSlot( iClass ) == iSlot )
  620. return m_pBaseLoadoutItems[iItem];
  621. }
  622. return m_pDefaultItem;
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Purpose: Fills out the vector with the sets that are currently active on the specified player & class
  626. //-----------------------------------------------------------------------------
  627. void CTFInventoryManager::GetActiveSets( CUtlVector<const CEconItemSetDefinition *> *pItemSets, CSteamID steamIDForPlayer, int iClass )
  628. {
  629. pItemSets->Purge();
  630. CEconItemSchema *pSchema = ItemSystem()->GetItemSchema();
  631. if ( !pSchema )
  632. return;
  633. // Loop through all the items we have equipped, and build a list of only set items
  634. // Accumulate a list of our equipped set items.
  635. CUtlVector<CEconItemView*> equippedSetItems;
  636. for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
  637. {
  638. CEconItemView *pItem = NULL;
  639. #ifdef GAME_DLL
  640. // On the server we need to look at what the player actually has equipped
  641. // because they might be on a tournament server that's using an item_whitelist
  642. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( steamIDForPlayer ) );
  643. if ( pPlayer && pPlayer->Inventory() )
  644. {
  645. pItem = pPlayer->GetEquippedItemForLoadoutSlot( i );
  646. }
  647. #else
  648. pItem = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i, &steamIDForPlayer );
  649. #endif
  650. if ( !pItem )
  651. continue;
  652. CEconItemDefinition* pData = pItem->GetStaticData();
  653. if ( !pData )
  654. continue;
  655. // Ignore items that don't have set bonuses.
  656. if ( !pData->GetItemSetDefinition() )
  657. continue;
  658. // Make sure this item isn't failing a Holiday restriction before giving out the set bonus!
  659. if ( pData->GetHolidayRestriction() )
  660. {
  661. int iHolidayRestriction = UTIL_GetHolidayForString( pData->GetHolidayRestriction() );
  662. if ( iHolidayRestriction != kHoliday_None && (!TFGameRules() || !TFGameRules()->IsHolidayActive( iHolidayRestriction )) )
  663. continue;
  664. }
  665. equippedSetItems.AddToTail( pItem );
  666. }
  667. // Find out which sets to apply.
  668. CUtlVector<const char*> testedSets;
  669. for ( int inv = 0; inv < equippedSetItems.Count(); inv++ )
  670. {
  671. CEconItemView *pItem = equippedSetItems[inv];
  672. if ( !pItem )
  673. continue;
  674. const CEconItemSetDefinition *pItemSet = pItem->GetStaticData()->GetItemSetDefinition();
  675. if ( !pItemSet )
  676. continue;
  677. if ( testedSets.HasElement( pItemSet->m_pszName ) )
  678. continue; // Don't try to apply set bonuses we have already tested.
  679. testedSets.AddToTail( pItemSet->m_pszName );
  680. // Count how much of this set we have equipped.
  681. int iSetItemsEquipped = 0;
  682. for ( int i=0; i<pItemSet->m_iItemDefs.Count(); i++ )
  683. {
  684. unsigned int iIndex = pItemSet->m_iItemDefs[i];
  685. for ( int j=0; j<equippedSetItems.Count(); j++ )
  686. {
  687. const CEconItemView *pTestItem = equippedSetItems[j];
  688. const item_definition_index_t unEquippedItemSetDefIndex = pTestItem->GetItemDefinition()->GetSetItemRemap();
  689. if ( iIndex == unEquippedItemSetDefIndex )
  690. {
  691. iSetItemsEquipped++;
  692. break;
  693. }
  694. }
  695. }
  696. // Note: this logic will break if we ever have sets that have misc-slot items where we can have multiple items
  697. // of the same type with different remaps equipped.
  698. if ( iSetItemsEquipped == pItemSet->m_iItemDefs.Count() )
  699. {
  700. // The entire set is equipped.
  701. pItemSets->AddToTail( pItemSet );
  702. }
  703. }
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose: We're generating a base item. We need to add the game-specific keys to the criteria so that it'll find the right base item.
  707. //-----------------------------------------------------------------------------
  708. void CTFInventoryManager::AddBaseItemCriteria( baseitemcriteria_t *pCriteria, CItemSelectionCriteria *pSelectionCriteria )
  709. {
  710. pSelectionCriteria->BAddCondition( "used_by_classes", k_EOperator_Subkey_Contains, ItemSystem()->GetItemSchema()->GetClassUsabilityStrings()[pCriteria->iClass], true );
  711. pSelectionCriteria->BAddCondition( "item_slot", k_EOperator_String_EQ, ItemSystem()->GetItemSchema()->GetLoadoutStrings( EEquipType_t::EQUIP_TYPE_CLASS )[pCriteria->iSlot], true );
  712. }
  713. //=======================================================================================================================
  714. // TF PLAYER INVENTORY
  715. //=======================================================================================================================
  716. // Inventory Less function.
  717. // Used to sort the inventory items into their positions.
  718. class CTFInventoryListLess
  719. {
  720. public:
  721. bool Less( const CEconItemView &src1, const CEconItemView &src2, void *pCtx )
  722. {
  723. if ( src1.GetInventoryPosition() > src2.GetInventoryPosition() )
  724. return true;
  725. return false;
  726. }
  727. };
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. //-----------------------------------------------------------------------------
  731. CTFPlayerInventory::CTFPlayerInventory()
  732. {
  733. m_aInventoryItems.SetLessContext( this );
  734. #ifdef CLIENT_DLL
  735. for ( int i = 0; i < TF_TEAM_COUNT; ++i )
  736. m_CachedBaseTextureLowRes[ i ].SetLessFunc( DefLessFunc( itemid_t ) );
  737. #endif
  738. memset( m_LoadoutItems, LOADOUT_SLOT_USE_BASE_ITEM, sizeof( m_LoadoutItems ) );
  739. memset( m_AccountLoadoutItems, LOADOUT_SLOT_USE_BASE_ITEM, sizeof( m_AccountLoadoutItems ) );
  740. ClearClassLoadoutChangeTracking();
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose:
  744. //-----------------------------------------------------------------------------
  745. CTFPlayerInventory::~CTFPlayerInventory()
  746. {
  747. #ifdef CLIENT_DLL
  748. for ( int iTeam = 0; iTeam < TF_TEAM_COUNT; ++iTeam )
  749. {
  750. FOR_EACH_MAP_FAST( m_CachedBaseTextureLowRes[ iTeam ], i )
  751. m_CachedBaseTextureLowRes[ iTeam ][ i ]->Release();
  752. m_CachedBaseTextureLowRes[ iTeam ].RemoveAll();
  753. }
  754. #endif
  755. }
  756. #ifdef CLIENT_DLL
  757. //-----------------------------------------------------------------------------
  758. // Purpose:
  759. //-----------------------------------------------------------------------------
  760. void CTFPlayerInventory::CheckSaxtonMaskAchievement( const CEconItem *pEconItem )
  761. {
  762. if ( pEconItem )
  763. {
  764. if ( pEconItem->GetDefinitionIndex() == 277 && pEconItem->GetOrigin() == kEconItemOrigin_Crafted ) // Saxton mask is item index 277
  765. {
  766. g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HALLOWEEN_CRAFT_SAXTON_MASK );
  767. }
  768. }
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose:
  772. //-----------------------------------------------------------------------------
  773. void CTFPlayerInventory::UpdateCachedServerLoadoutItems()
  774. {
  775. V_memcpy( m_CachedServerLoadoutItems, m_LoadoutItems, sizeof( itemid_t ) * ARRAYSIZE( m_CachedServerLoadoutItems ) * ARRAYSIZE( m_CachedServerLoadoutItems[0] ) );
  776. }
  777. #endif // CLIENT_DLL
  778. //-----------------------------------------------------------------------------
  779. // Purpose:
  780. //-----------------------------------------------------------------------------
  781. void CTFPlayerInventory::SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
  782. {
  783. BaseClass::SOUpdated( steamIDOwner, pObject, eEvent );
  784. #ifdef CLIENT_DLL
  785. if ( pObject->GetTypeID() != CEconItem::k_nTypeID )
  786. return;
  787. // Clear out any predicted backpack slots when items move into them
  788. CEconItem *pEconItem = (CEconItem *)pObject;
  789. if ( eEvent == eSOCacheEvent_Incremental )
  790. {
  791. EconUI()->GetBackpackPanel()->MarkItemIDDirty( pEconItem->GetItemID() );
  792. }
  793. int iBackpackPos = TFInventoryManager()->GetBackpackPositionFromBackend( pEconItem->GetInventoryToken() );
  794. TFInventoryManager()->PredictedBackpackPosFilled( iBackpackPos );
  795. #endif // CLIENT_DLL
  796. }
  797. void CTFPlayerInventory::OnHasNewItems()
  798. {
  799. BaseClass::OnHasNewItems();
  800. #ifdef CLIENT_DLL
  801. if ( TFGameRules() && TFGameRules()->IsInTraining() )
  802. return;
  803. NotificationQueue_Remove( &CEconNotification_HasNewItems::IsNotificationType );
  804. CEconNotification_HasNewItems *pNotification = new CEconNotification_HasNewItems();
  805. pNotification->SetText( "TF_HasNewItems" );
  806. pNotification->SetLifetime( 7.0f );
  807. NotificationQueue_Add( pNotification );
  808. #endif
  809. }
  810. void CTFPlayerInventory::OnHasNewQuest()
  811. {
  812. #ifdef CLIENT_DLL
  813. #endif
  814. }
  815. #ifdef _DEBUG
  816. #ifdef CLIENT_DLL
  817. CON_COMMAND( cl_newitem_test, "Tests the new item ui notification." )
  818. {
  819. if ( steamapicontext == NULL || steamapicontext->SteamUser() == NULL )
  820. return;
  821. CEconNotification_HasNewItems *pNotification = new CEconNotification_HasNewItems();
  822. pNotification->SetText( "TF_HasNewItems" );
  823. pNotification->SetLifetime( 7.0f );
  824. NotificationQueue_Add( pNotification );
  825. }
  826. #endif
  827. #endif // _DEBUG
  828. //-----------------------------------------------------------------------------
  829. // Purpose:
  830. //-----------------------------------------------------------------------------
  831. void CTFPlayerInventory::ValidateInventoryPositions( void )
  832. {
  833. BaseClass::ValidateInventoryPositions();
  834. #ifdef CLIENT_DLL
  835. bool bHasNewItems = false;
  836. const int iMaxItems = GetMaxItemCount();
  837. // First, check for duplicate positions
  838. int iCount = m_aInventoryItems.Count();
  839. for ( int i = iCount-1; i >= 0; i-- )
  840. {
  841. CEconItemView *pEconItemView = &m_aInventoryItems[i];
  842. CheckSaxtonMaskAchievement( pEconItemView->GetSOCData() );
  843. uint32 iPosition = pEconItemView->GetInventoryPosition();
  844. // Waiting to be acknowledged?
  845. if ( IsUnacknowledged(iPosition) )
  846. {
  847. if ( !pEconItemView->GetStaticData()->IsHidden() )
  848. {
  849. bHasNewItems = true;
  850. }
  851. continue;
  852. }
  853. bool bInvalidSlot = false;
  854. if ( !IsNewPositionFormat(iPosition) )
  855. {
  856. ConvertOldFormatInventoryToNew();
  857. break;
  858. }
  859. // Inside the backpack?
  860. if ( i < (iCount-1) )
  861. {
  862. // We're not in an invalid slot yet. But if we're in the same position as another item, we should be moved too.
  863. int iPos1 = TFInventoryManager()->GetBackpackPositionFromBackend(iPosition);
  864. int iPos2 = TFInventoryManager()->GetBackpackPositionFromBackend(m_aInventoryItems[i+1].GetInventoryPosition());
  865. if ( iPos1 == iPos2 )
  866. {
  867. Warning("WARNING: Found item in a duplicate backpack position. Moving to the backpack end.\n" );
  868. bInvalidSlot = true;
  869. }
  870. }
  871. // Make sure it's not outside the backpack extents
  872. if ( !bInvalidSlot )
  873. {
  874. bInvalidSlot = (TFInventoryManager()->GetBackpackPositionFromBackend(iPosition) > iMaxItems);
  875. }
  876. if ( bInvalidSlot )
  877. {
  878. // The item is NOT hidden and is in an invalid slot. Move it back to the backpack.
  879. if ( pEconItemView->GetItemDefinition() && !pEconItemView->GetItemDefinition()->IsHidden() && !TFInventoryManager()->SetItemBackpackPosition( pEconItemView, 0, true ) )
  880. {
  881. // We failed to move it to the backpack, because the player has no room.
  882. // Force them to "refind" the item, which will make them throw something out.
  883. TFInventoryManager()->UpdateInventoryPosition( this, pEconItemView->GetItemID(), GetUnacknowledgedPositionFor(UNACK_ITEM_DROPPED) );
  884. }
  885. }
  886. // Make sure it isn't equipped by any invalid classes.
  887. for ( int j = TF_FIRST_NORMAL_CLASS; j <= TF_LAST_NORMAL_CLASS; j++ )
  888. {
  889. if ( !pEconItemView->GetStaticData()->CanBeUsedByClass( j ) &&
  890. pEconItemView->IsEquippedForClass( j ) )
  891. {
  892. // Unequip this item from this class.
  893. InventoryManager()->UpdateInventoryEquippedState( this, INVALID_ITEM_ID, j, pEconItemView->GetEquippedPositionForClass( j ) );
  894. }
  895. }
  896. }
  897. if ( bHasNewItems )
  898. {
  899. OnHasNewItems();
  900. }
  901. #endif
  902. }
  903. #ifdef CLIENT_DLL
  904. //-----------------------------------------------------------------------------
  905. // Purpose:
  906. //-----------------------------------------------------------------------------
  907. void CTFPlayerInventory::ConvertOldFormatInventoryToNew( void )
  908. {
  909. uint32 iBackpackPos = 1;
  910. // Loop through all items in the inventory. Move them all to the backpack, and in order.
  911. int iCount = m_aInventoryItems.Count();
  912. for ( int i = 0; i < iCount; i++ )
  913. {
  914. uint32 iPosition = m_aInventoryItems[i].GetInventoryPosition();
  915. // Waiting to be acknowledged?
  916. if ( IsUnacknowledged(iPosition) )
  917. continue;
  918. TFInventoryManager()->SetItemBackpackPosition( &m_aInventoryItems[i], iBackpackPos, true );
  919. iBackpackPos++;
  920. }
  921. }
  922. #endif
  923. //-----------------------------------------------------------------------------
  924. // Purpose: Creates a script item and associates it with this econ item
  925. //-----------------------------------------------------------------------------
  926. void CTFPlayerInventory::SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
  927. {
  928. BaseClass::SOCreated( steamIDOwner, pObject, eEvent );
  929. if ( pObject->GetTypeID() != CEconItem::k_nTypeID )
  930. return;
  931. CEconItem *pEconItem = (CEconItem *)pObject;
  932. // CEconItem *pEconItem = assert_cast<CEconItem*>( pObject );
  933. #ifdef CLIENT_DLL
  934. if ( InventoryManager()->GetLocalInventory() == this && GetOwner() == steamIDOwner )
  935. {
  936. if ( pObject->GetTypeID() == CEconItem::k_nTypeID )
  937. {
  938. CheckSaxtonMaskAchievement( (CEconItem*)pObject );
  939. }
  940. }
  941. // CSteamID ownerSteamID( pEconItem->GetAccountID(), GetUniverse(), k_EAccountTypeIndividual );
  942. // if ( ownerSteamID == ClientSteamContext().GetLocalPlayerSteamID() )
  943. // {
  944. // CheckSaxtonMaskAchievement( pEconItem );
  945. // }
  946. static CSchemaItemDefHandle pItemDef_HardyLaurel( "The Hardy Laurel" );
  947. if ( pEconItem->GetItemDefinition() == pItemDef_HardyLaurel )
  948. {
  949. if ( TFSharedContentManager() )
  950. {
  951. TFSharedContentManager()->OfferSharedVision( TF_VISION_FILTER_ROME, pEconItem->GetAccountID() );
  952. }
  953. }
  954. #else
  955. // Summer 2015 Operation Pass so players can display the coin
  956. //tagES remove this when we have a coin equip slot
  957. static CSchemaItemDefHandle pItemDef_Summer2015Operation( "Activated Summer 2015 Operation Pass" );
  958. if ( pEconItem->GetItemDefinition() == pItemDef_Summer2015Operation )
  959. {
  960. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( GetOwner() ) );
  961. if ( pPlayer )
  962. {
  963. pPlayer->SetCampaignMedalActive( CAMPAIGN_MEDAL_SUMMER2015 );
  964. }
  965. }
  966. // Invasion Community Update Pass so players can display the coin
  967. //tagES remove this when we have a coin equip slot
  968. static CSchemaItemDefHandle pItemDef_InvasionPass( "Activated Invasion Pass" );
  969. if ( pEconItem->GetItemDefinition() == pItemDef_InvasionPass )
  970. {
  971. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( GetOwner() ) );
  972. if ( pPlayer )
  973. {
  974. pPlayer->SetCampaignMedalActive( CAMPAIGN_MEDAL_INVASION );
  975. }
  976. }
  977. // Halloween Pass so players can display the coin
  978. //tagES remove this when we have a coin equip slot
  979. static CSchemaItemDefHandle pItemDef_HalloweenPass( "Activated Halloween Pass" );
  980. if ( pEconItem->GetItemDefinition() == pItemDef_HalloweenPass )
  981. {
  982. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( GetOwner() ) );
  983. if ( pPlayer )
  984. {
  985. pPlayer->SetCampaignMedalActive( CAMPAIGN_MEDAL_HALLOWEEN );
  986. }
  987. }
  988. // Winter2016 Pass so players can display the stamp
  989. //tagES remove this when we have a coin equip slot
  990. static CSchemaItemDefHandle pItemDef_Winter2016Pass( "Activated Operation Tough Break Pass" );
  991. if ( pEconItem->GetItemDefinition() == pItemDef_Winter2016Pass )
  992. {
  993. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( GetOwner() ) );
  994. if ( pPlayer )
  995. {
  996. pPlayer->SetCampaignMedalActive( CAMPAIGN_MEDAL_WINTER2016 );
  997. }
  998. }
  999. #endif
  1000. }
  1001. //-----------------------------------------------------------------------------
  1002. // Purpose:
  1003. //-----------------------------------------------------------------------------
  1004. void CTFPlayerInventory::DumpInventoryToConsole( bool bRoot )
  1005. {
  1006. if ( bRoot )
  1007. {
  1008. Msg("========================================\n");
  1009. #ifdef CLIENT_DLL
  1010. Msg("(CLIENT) Inventory:\n");
  1011. #else
  1012. Msg("(SERVER) Inventory for account (%d):\n", m_OwnerID.GetAccountID() );
  1013. #endif
  1014. Msg(" Version: %llu:\n", m_pSOCache ? m_pSOCache->GetVersion() : -1 );
  1015. }
  1016. int iCount = m_aInventoryItems.Count();
  1017. Msg(" Num items: %d\n", iCount );
  1018. for ( int i = 0; i < iCount; i++ )
  1019. {
  1020. Msg(" %s (ID %llu) at backpack slot %d\n", m_aInventoryItems[i].GetStaticData()->GetDefinitionName(), m_aInventoryItems[i].GetItemID(), TFInventoryManager()->GetBackpackPositionFromBackend( m_aInventoryItems[i].GetInventoryPosition() ) );
  1021. int iEquipped = 0;
  1022. for ( equipped_class_t eq = TF_FIRST_NORMAL_CLASS; eq < TF_LAST_NORMAL_CLASS; eq++ )
  1023. {
  1024. if ( m_aInventoryItems[i].IsEquippedForClass( eq ) )
  1025. {
  1026. if ( iEquipped == 0 )
  1027. {
  1028. iEquipped++;
  1029. Msg(" -> EQUIPPED: ");
  1030. }
  1031. Msg("%s ", g_aPlayerClassNames_NonLocalized[eq] );
  1032. }
  1033. }
  1034. if ( iEquipped )
  1035. {
  1036. Msg("\n");
  1037. }
  1038. }
  1039. }
  1040. //-----------------------------------------------------------------------------
  1041. // Purpose:
  1042. //-----------------------------------------------------------------------------
  1043. void CTFPlayerInventory::ClearClassLoadoutChangeTracking( void )
  1044. {
  1045. memset(m_bLoadoutChanged,0, sizeof(m_bLoadoutChanged));
  1046. #ifdef CLIENT_DLL
  1047. UpdateCachedServerLoadoutItems();
  1048. #endif // CLIENT_DLL
  1049. }
  1050. #ifdef CLIENT_DLL
  1051. //-----------------------------------------------------------------------------
  1052. // Purpose: Find the low res
  1053. //-----------------------------------------------------------------------------
  1054. ITexture *CTFPlayerInventory::GetWeaponSkinBaseLowRes( itemid_t nItemId, int iTeam ) const
  1055. {
  1056. Assert( iTeam >= 0 && iTeam < TF_TEAM_COUNT );
  1057. int index = m_CachedBaseTextureLowRes[ iTeam ].Find( nItemId );
  1058. if ( index == m_CachedBaseTextureLowRes[ iTeam ].InvalidIndex() )
  1059. return NULL;
  1060. return m_CachedBaseTextureLowRes[ iTeam ][ index ];
  1061. }
  1062. #endif
  1063. //-----------------------------------------------------------------------------
  1064. // Purpose: Find the first item in the local user's inventory with the given def index
  1065. //-----------------------------------------------------------------------------
  1066. CEconItemView *CTFPlayerInventory::GetFirstItemOfItemDef( item_definition_index_t nDefIndex, CPlayerInventory* pInventory )
  1067. {
  1068. #ifdef CLIENT_DLL
  1069. if ( pInventory == NULL )
  1070. {
  1071. pInventory = InventoryManager()->GetLocalInventory();
  1072. }
  1073. #endif
  1074. if ( !pInventory )
  1075. return NULL;
  1076. for ( int i = 0; i < pInventory->GetItemCount(); i++ )
  1077. {
  1078. CEconItemView *pItem = pInventory->GetItem(i);
  1079. if ( pItem->GetItemDefIndex() == nDefIndex )
  1080. {
  1081. return pItem;
  1082. }
  1083. }
  1084. return NULL;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. // Purpose: Returns the item in the specified loadout slot for a given class
  1088. //-----------------------------------------------------------------------------
  1089. CEconItemView *CTFPlayerInventory::GetItemInLoadout( int iClass, int iSlot )
  1090. {
  1091. if ( iSlot < 0 || iSlot >= CLASS_LOADOUT_POSITION_COUNT )
  1092. return NULL;
  1093. if ( iClass == GEconItemSchema().GetAccountIndex() )
  1094. {
  1095. return GetInventoryItemByItemID( m_AccountLoadoutItems[ iSlot ] );
  1096. }
  1097. else
  1098. {
  1099. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS )
  1100. return NULL;
  1101. // If we don't have an item in the loadout at that slot, we return the base item
  1102. if ( m_LoadoutItems[iClass][iSlot] != LOADOUT_SLOT_USE_BASE_ITEM )
  1103. {
  1104. CEconItemView *pItem = GetInventoryItemByItemID( m_LoadoutItems[iClass][iSlot] );
  1105. // To protect against users lying to the backend about the position of their items,
  1106. // we need to validate their position on the server when we retrieve them.
  1107. if ( pItem && AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItem->GetStaticData()->GetLoadoutSlot( iClass ), iSlot ) )
  1108. return pItem;
  1109. }
  1110. }
  1111. return TFInventoryManager()->GetBaseItemForClass( iClass, iSlot );
  1112. }
  1113. #ifdef CLIENT_DLL
  1114. CEconItemView *CTFPlayerInventory::GetCacheServerItemInLoadout( int iClass, int iSlot )
  1115. {
  1116. if ( iSlot < 0 || iSlot >= CLASS_LOADOUT_POSITION_COUNT )
  1117. return NULL;
  1118. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS )
  1119. return NULL;
  1120. // If we don't have an item in the loadout at that slot, we return the base item
  1121. if ( m_CachedServerLoadoutItems[iClass][iSlot] != LOADOUT_SLOT_USE_BASE_ITEM )
  1122. {
  1123. CEconItemView *pItem = GetInventoryItemByItemID( m_CachedServerLoadoutItems[iClass][iSlot] );
  1124. // To protect against users lying to the backend about the position of their items,
  1125. // we need to validate their position on the server when we retrieve them.
  1126. if ( pItem && AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItem->GetStaticData()->GetLoadoutSlot( iClass ), iSlot ) )
  1127. return pItem;
  1128. }
  1129. return TFInventoryManager()->GetBaseItemForClass( iClass, iSlot );
  1130. }
  1131. #endif // CLIENT_DLL
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose:
  1134. //-----------------------------------------------------------------------------
  1135. static CEconGameAccountClient *GetSOCacheGameAccountClient( CGCClientSharedObjectCache *pSOCache )
  1136. {
  1137. if ( !pSOCache )
  1138. return NULL;
  1139. return pSOCache->GetSingleton<CEconGameAccountClient>();
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose:
  1143. //-----------------------------------------------------------------------------
  1144. int CTFPlayerInventory::GetPreviewItemDef( void ) const
  1145. {
  1146. CEconGameAccountClient *pGameAccountClient = GetSOCacheGameAccountClient( m_pSOCache );
  1147. if ( !pGameAccountClient )
  1148. return 0;
  1149. return pGameAccountClient->Obj().preview_item_def();
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Purpose:
  1153. //-----------------------------------------------------------------------------
  1154. bool CTFPlayerInventory::CanPurchaseItems( int iItemCount ) const
  1155. {
  1156. // If we're not a free trial account, we fall back to our default logic of "do
  1157. // we have enough empty slots?".
  1158. CEconGameAccountClient *pGameAccountClient = GetSOCacheGameAccountClient( m_pSOCache );
  1159. if ( !pGameAccountClient || !pGameAccountClient->Obj().trial_account() )
  1160. return BaseClass::CanPurchaseItems( iItemCount );
  1161. // We're a free trial account, so when we purchase these items, our inventory
  1162. // will actually expand. We check to make sure that we have room for these
  1163. // items against what will be our new maximum backpack size, not our current
  1164. // backpack limit.
  1165. int iNewItemCount = GetItemCount() + iItemCount,
  1166. iAfterPurchaseMaxItemCount = DEFAULT_NUM_BACKPACK_SLOTS
  1167. + (pGameAccountClient ? pGameAccountClient->Obj().additional_backpack_slots() : 0);
  1168. return iNewItemCount <= iAfterPurchaseMaxItemCount;
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. // Purpose:
  1172. //-----------------------------------------------------------------------------
  1173. int CTFPlayerInventory::GetMaxItemCount( void ) const
  1174. {
  1175. int iMaxItems = DEFAULT_NUM_BACKPACK_SLOTS;
  1176. CEconGameAccountClient *pGameAccountClient = GetSOCacheGameAccountClient( m_pSOCache );
  1177. if ( pGameAccountClient )
  1178. {
  1179. if ( pGameAccountClient->Obj().trial_account() )
  1180. {
  1181. iMaxItems = DEFAULT_NUM_BACKPACK_SLOTS_FREE_TRIAL_ACCOUNT;
  1182. }
  1183. iMaxItems += pGameAccountClient->Obj().additional_backpack_slots();
  1184. }
  1185. return MIN( iMaxItems, MAX_NUM_BACKPACK_SLOTS );
  1186. }
  1187. #ifdef CLIENT_DLL
  1188. //-----------------------------------------------------------------------------
  1189. // Purpose: Removes any item in a loadout slot. If the slot has a base item,
  1190. // the player essentially returns to using that item.
  1191. //-----------------------------------------------------------------------------
  1192. bool CTFPlayerInventory::ClearLoadoutSlot( int iClass, int iSlot )
  1193. {
  1194. if ( iSlot < 0 || iSlot >= CLASS_LOADOUT_POSITION_COUNT )
  1195. return false;
  1196. if ( iClass == GEconItemSchema().GetAccountIndex() )
  1197. {
  1198. if ( m_AccountLoadoutItems[iSlot] == LOADOUT_SLOT_USE_BASE_ITEM )
  1199. return false;
  1200. }
  1201. else
  1202. {
  1203. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS )
  1204. return false;
  1205. if ( m_LoadoutItems[iClass][iSlot] == LOADOUT_SLOT_USE_BASE_ITEM )
  1206. return false;
  1207. }
  1208. CEconItemView *pItemInSlot = GetItemInLoadout( iClass, iSlot );
  1209. if ( !pItemInSlot )
  1210. return false;
  1211. InventoryManager()->UpdateInventoryEquippedState( this, INVALID_ITEM_ID, iClass, iSlot );
  1212. // TODO: Prediction
  1213. // It's been moved to the backpack, so clear out loadout entry
  1214. //m_LoadoutItems[iClass][iSlot] = LOADOUT_SLOT_USE_BASE_ITEM;
  1215. return true;
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Purpose: Update weapon skin request list
  1219. //-----------------------------------------------------------------------------
  1220. void CTFPlayerInventory::UpdateWeaponSkinRequest()
  1221. {
  1222. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1223. FOR_EACH_VEC_BACK( m_vecWeaponSkinRequestList, i )
  1224. {
  1225. SkinRequest_t &req = m_vecWeaponSkinRequestList[i];
  1226. CEconItemView *pItem = GetInventoryItemByItemID( req.m_nID );
  1227. Assert( pItem );
  1228. if ( !pItem )
  1229. {
  1230. m_vecWeaponSkinRequestList.Remove( i );
  1231. continue;
  1232. }
  1233. // Have we loaded this one yet? If not, do it here.
  1234. if ( mdlcache->IsErrorModel( req.m_hModel ) )
  1235. {
  1236. const char *pszModelName = pItem->GetPlayerDisplayModel( 0, 0 );
  1237. if ( pszModelName )
  1238. {
  1239. MDLHandle_t hItemMDL = mdlcache->FindMDL( pszModelName );
  1240. if ( mdlcache->IsErrorModel( hItemMDL ) )
  1241. {
  1242. AssertMsg( 0, "failed to find %s from mdlcache", pszModelName );
  1243. m_vecWeaponSkinRequestList.Remove( i );
  1244. continue;
  1245. }
  1246. req.m_hModel = hItemMDL;
  1247. }
  1248. else
  1249. {
  1250. AssertMsg( 0, "failed to precache weapon skin because there's no model" );
  1251. m_vecWeaponSkinRequestList.Remove( i );
  1252. }
  1253. }
  1254. // Draw!
  1255. {
  1256. CMDL mdl;
  1257. mdl.SetMDL( req.m_hModel );
  1258. mdl.m_pProxyData = static_cast<IClientRenderable*>( pItem );
  1259. pItem->SetWeaponSkinUseLowRes( true );
  1260. int nRestoreTeam = pItem->GetTeamNumber();
  1261. pItem->SetTeamNumber( req.m_nTeam );
  1262. matrix3x4_t matIdentity;
  1263. SetIdentityMatrix( matIdentity );
  1264. IMaterial *pOverrideMaterial = pItem->GetMaterialOverride( pItem->GetTeamNumber() );
  1265. if ( pOverrideMaterial )
  1266. modelrender->ForcedMaterialOverride( pOverrideMaterial );
  1267. mdl.Draw( matIdentity );
  1268. if ( pOverrideMaterial )
  1269. modelrender->ForcedMaterialOverride( NULL );
  1270. pItem->SetTeamNumber( nRestoreTeam );
  1271. pItem->SetWeaponSkinUseLowRes( false );
  1272. }
  1273. // Don't remove until it's complete.
  1274. if ( pItem->GetWeaponSkinBase() )
  1275. {
  1276. Assert( pItem->GetWeaponSkinBaseCompositor() == NULL );
  1277. ITexture* pTex = pItem->GetWeaponSkinBase();
  1278. if ( pTex )
  1279. {
  1280. pTex->AddRef(); // We need to hold a ref to the texture.
  1281. m_CachedBaseTextureLowRes[ req.m_nTeam ].Insert( req.m_nID, pTex );
  1282. pItem->SetWeaponSkinBase( NULL ); // Clean up.
  1283. }
  1284. // We do RED, then BLUE. Then drop it out of the list.
  1285. if ( req.m_nTeam == TF_TEAM_BLUE )
  1286. {
  1287. Assert( !mdlcache->IsErrorModel( req.m_hModel ) );
  1288. mdlcache->Release( req.m_hModel );
  1289. m_vecWeaponSkinRequestList.Remove( i );
  1290. }
  1291. else
  1292. {
  1293. Assert( req.m_nTeam == TF_TEAM_RED );
  1294. req.m_nTeam = TF_TEAM_BLUE;
  1295. }
  1296. }
  1297. // Only do one of these per frame to avoid hitching.
  1298. break;
  1299. }
  1300. }
  1301. #endif // CLIENT_DLL
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose:
  1304. //-----------------------------------------------------------------------------
  1305. void CTFPlayerInventory::ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile )
  1306. {
  1307. Assert( pItem );
  1308. BaseClass::ItemHasBeenUpdated( pItem, bUpdateAckFile, bWriteAckFile );
  1309. const CEconItem *pEconItem = pItem->GetSOCData();
  1310. #ifdef CLIENT_DLL
  1311. static CSchemaAttributeDefHandle pAttrib_WeaponAllowInspect( "weapon_allow_inspect" );
  1312. bool bLocalInv = InventoryManager()->GetLocalInventory() == this;
  1313. #endif // CLIENT_DLL
  1314. for ( equipped_class_t iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
  1315. {
  1316. equipped_slot_t unSlot = pEconItem ? pEconItem->GetEquippedPositionForClass( iClass ) : INVALID_EQUIPPED_SLOT;
  1317. Assert( GetInventoryItemByItemID( pItem->GetItemID() ) );
  1318. bool bThisClassLoadoutChanged = UpdateEquipStateForClass( pItem->GetItemID(), unSlot, m_LoadoutItems[iClass], ARRAYSIZE( m_LoadoutItems[iClass] ) );
  1319. if ( bThisClassLoadoutChanged )
  1320. {
  1321. m_bLoadoutChanged[iClass] = true;
  1322. #ifdef CLIENT_DLL
  1323. // if we can inspect this item, it has unique skin.
  1324. // draw it once per team to tell the system to generate unique skin
  1325. float flInspect = 0;
  1326. if ( bLocalInv && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttrib_WeaponAllowInspect, &flInspect ) )
  1327. {
  1328. SkinRequest_t req = { TF_TEAM_RED, pItem->GetItemID(), MDLHANDLE_INVALID };
  1329. m_vecWeaponSkinRequestList.AddToTail( req );
  1330. }
  1331. #endif // CLIENT_DLL
  1332. }
  1333. }
  1334. // Update account items as well
  1335. equipped_slot_t unSlot = pEconItem ? pEconItem->GetEquippedPositionForClass( GEconItemSchema().GetAccountIndex() ) : INVALID_EQUIPPED_SLOT;
  1336. UpdateEquipStateForClass( pItem->GetItemID(), unSlot, m_AccountLoadoutItems, ARRAYSIZE( m_AccountLoadoutItems ) );
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Purpose:
  1340. //-----------------------------------------------------------------------------
  1341. bool CTFPlayerInventory::UpdateEquipStateForClass( const itemid_t& itemID, equipped_slot_t nSlot, itemid_t *pLoadout, int nCount )
  1342. {
  1343. bool bThisClassLoadoutChanged = false;
  1344. // For each slot this item may have been equipped in, set to the base item, unless that slot
  1345. // is the current slot. (ie., if an item moves from slot 10 to slot 12, slot 10 will be set to
  1346. // base item, and slot 12 will be set to this item ID)
  1347. for ( int i = 0; i < nCount; i++ )
  1348. {
  1349. itemid_t& refLoadoutItemID = *( pLoadout + i );
  1350. if ( i == nSlot )
  1351. {
  1352. // This may be an invalid slot for the item, but CTFPlayerInventory::InventoryReceived()
  1353. // will have detected that and sent off a request already to move it. The response
  1354. // to that will clear this loadout slot.
  1355. refLoadoutItemID = itemID;
  1356. bThisClassLoadoutChanged = true;
  1357. }
  1358. else if ( refLoadoutItemID == itemID )
  1359. {
  1360. refLoadoutItemID = LOADOUT_SLOT_USE_BASE_ITEM;
  1361. bThisClassLoadoutChanged = true;
  1362. }
  1363. }
  1364. return bThisClassLoadoutChanged;
  1365. }
  1366. #ifdef CLIENT_DLL
  1367. //-----------------------------------------------------------------------------
  1368. // Purpose:
  1369. //-----------------------------------------------------------------------------
  1370. void CTFPlayerInventory::PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent )
  1371. {
  1372. BaseClass::PostSOUpdate( steamIDOwner, eEvent );
  1373. VerifyChangedLoadoutsAreValid();
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. // Purpose:
  1377. //-----------------------------------------------------------------------------
  1378. void CTFPlayerInventory::SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent )
  1379. {
  1380. BaseClass::SOCacheSubscribed( steamIDOwner, eEvent );
  1381. VerifyChangedLoadoutsAreValid();
  1382. UpdateCachedServerLoadoutItems();
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. // Purpose: Helper function to add a new item for a econ item
  1386. //-----------------------------------------------------------------------------
  1387. bool CTFPlayerInventory::AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems )
  1388. {
  1389. if ( BaseClass::AddEconItem( pItem, bUpdateAckFile, bWriteAckFile, bCheckForNewItems ) )
  1390. {
  1391. if ( bCheckForNewItems && InventoryManager()->GetLocalInventory() == this )
  1392. {
  1393. if ( IsUnacknowledged( pItem->GetInventoryToken() ) )
  1394. {
  1395. OnHasNewQuest();
  1396. }
  1397. }
  1398. return true;
  1399. }
  1400. return false;
  1401. }
  1402. //-----------------------------------------------------------------------------
  1403. // Purpose:
  1404. //-----------------------------------------------------------------------------
  1405. void CTFPlayerInventory::VerifyLoadoutItemsAreValid( int iClass )
  1406. {
  1407. // Important note: currently this function walks the loadout slots in order causing slots towards
  1408. // the beginning of the list to take priority over slots later in the list. This means that currently
  1409. // weapons take priority over hats, hats take priority over misc slots, etc. which is all well and good.
  1410. // If later we want the order in which slots claim their equip regions to change, we'll want to change
  1411. // the iteration order here and also change GenerateEquipRegionMaskUpToSlot(), which is used for
  1412. // filling out the UI.
  1413. equip_region_mask_t unCumulativeRegionMask = 0;
  1414. for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
  1415. {
  1416. CEconItemView *pEquippedItemView = GetItemInLoadout( iClass, i );
  1417. if ( !pEquippedItemView )
  1418. continue;
  1419. // Does this item use the same regions as some item that we already have equipped?
  1420. equip_region_mask_t unItemEquipMask = pEquippedItemView->GetItemDefinition()->GetEquipRegionMask();
  1421. if ( unItemEquipMask & unCumulativeRegionMask )
  1422. {
  1423. // Unequip this item. This will wind up calling into ::ItemHasBeenUpdated() once the
  1424. // unequip makes it to the GC and back.
  1425. InventoryManager()->UpdateInventoryEquippedState( this, INVALID_ITEM_ID, iClass, pEquippedItemView->GetEquippedPositionForClass( iClass ) );
  1426. }
  1427. else
  1428. {
  1429. unCumulativeRegionMask |= unItemEquipMask;
  1430. }
  1431. }
  1432. }
  1433. //-----------------------------------------------------------------------------
  1434. // Purpose:
  1435. //-----------------------------------------------------------------------------
  1436. void CTFPlayerInventory::VerifyChangedLoadoutsAreValid()
  1437. {
  1438. // We maybe changed equip state for an item and it's possible if we're sending bad messages and/or
  1439. // hacking state that we'll now have conflicting items equipped. Walk all of our items for this class
  1440. // to verify our state is good.
  1441. for ( equipped_class_t iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
  1442. {
  1443. if ( m_bLoadoutChanged[iClass] )
  1444. {
  1445. VerifyLoadoutItemsAreValid( iClass );
  1446. }
  1447. }
  1448. }
  1449. #endif // CLIENT_DLL
  1450. //-----------------------------------------------------------------------------
  1451. // Purpose:
  1452. //-----------------------------------------------------------------------------
  1453. void CTFPlayerInventory::ItemIsBeingRemoved( CEconItemView *pItem )
  1454. {
  1455. for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
  1456. {
  1457. if ( pItem->IsEquippedForClass( iClass ) )
  1458. {
  1459. for ( int iSlot = 0; iSlot < CLASS_LOADOUT_POSITION_COUNT; iSlot++ )
  1460. {
  1461. if ( m_LoadoutItems[iClass][iSlot] == pItem->GetItemID() )
  1462. {
  1463. m_LoadoutItems[iClass][iSlot] = LOADOUT_SLOT_USE_BASE_ITEM;
  1464. m_bLoadoutChanged[iClass] = true;
  1465. }
  1466. }
  1467. }
  1468. }
  1469. }
  1470. //=======================================================================================================================
  1471. // TF PLAYER INVENTORY
  1472. //=======================================================================================================================
  1473. #ifdef CLIENT_DLL
  1474. // WTF: Declaring this inline caused a compiler bug.
  1475. CTFPlayerInventory *CTFInventoryManager::GetLocalTFInventory( void )
  1476. {
  1477. return &m_LocalInventory;
  1478. }
  1479. #endif
  1480. #ifdef _DEBUG
  1481. #if defined(CLIENT_DLL)
  1482. CON_COMMAND_F( item_dumpinv_other, "Dumps the contents of a specified client inventory. Format: item_dumpinv_other <player index>", FCVAR_NONE )
  1483. {
  1484. if ( args.ArgC() > 1 )
  1485. {
  1486. C_TFPlayer *pOther = ToTFPlayer( UTIL_PlayerByIndex( atoi(args[1]) ) );
  1487. if ( pOther )
  1488. {
  1489. pOther->Inventory()->DumpInventoryToConsole( true );
  1490. return;
  1491. }
  1492. }
  1493. Msg("Couldn't find specified player.\nFormat: item_dumpinv_other <player index>\n");
  1494. }
  1495. #endif
  1496. #endif // _DEBUG
  1497. #ifdef _DEBUG
  1498. #if defined(CLIENT_DLL)
  1499. CON_COMMAND_F( item_dumpinv, "Dumps the contents of a specified client inventory. Format: item_dumpinv", FCVAR_CHEAT )
  1500. #else
  1501. CON_COMMAND_F( item_dumpinv_sv, "Dumps the contents of a specified server inventory. Format: item_dumpinv_sv", FCVAR_CHEAT )
  1502. #endif
  1503. {
  1504. #if defined(CLIENT_DLL)
  1505. CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
  1506. #else
  1507. CSteamID steamID;
  1508. CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
  1509. pPlayer->GetSteamID( &steamID );
  1510. #endif
  1511. CPlayerInventory *pInventory = TFInventoryManager()->GetInventoryForPlayer( steamID );
  1512. if ( !pInventory )
  1513. {
  1514. Msg("No inventory for that player.\n");
  1515. return;
  1516. }
  1517. pInventory->DumpInventoryToConsole( true );
  1518. }
  1519. #endif
  1520. #ifdef _DEBUG
  1521. #if defined(CLIENT_DLL)
  1522. CON_COMMAND_F( item_deleteunknowns, "Deletes all items in your inventory that we don't have static data for. Useful for removing items that have been removed from the backend.", FCVAR_CHEAT )
  1523. {
  1524. int iDeleted = TFInventoryManager()->DeleteUnknowns( InventoryManager()->GetLocalInventory() );
  1525. Msg("Deleted %d unknown items.\n", iDeleted);
  1526. }
  1527. #endif
  1528. #endif
  1529. #if defined( TF_CLIENT_DLL )
  1530. CON_COMMAND( load_itempreset, "Equip all items for a given preset on the player." )
  1531. {
  1532. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  1533. if ( !pPlayer )
  1534. return;
  1535. if ( args.ArgC() != 2 )
  1536. {
  1537. Msg( "Usage: \"load_itempreset <preset index>\" - <preset index> can be 0 to 3." );
  1538. return;
  1539. }
  1540. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1541. return;
  1542. equipped_class_t unClass = pPlayer->GetPlayerClass()->GetClassIndex();
  1543. equipped_preset_t unPreset = atoi( args[1] );
  1544. if ( TFInventoryManager()->LoadPreset( unClass, unPreset ) )
  1545. {
  1546. // Tell the GC to tell server that we should respawn if we're in a respawn room
  1547. extern ConVar tf_respawn_on_loadoutchanges;
  1548. if ( tf_respawn_on_loadoutchanges.GetBool() )
  1549. {
  1550. GCSDK::CGCMsg< ::MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
  1551. GCClientSystem()->BSendMessage( msg );
  1552. }
  1553. }
  1554. }
  1555. #endif // TF_CLIENT_DLL