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.

472 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Container that allows client & server access to data in player inventories & loadouts
  4. //
  5. //=============================================================================
  6. #ifndef ITEM_INVENTORY_H
  7. #define ITEM_INVENTORY_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "igamesystem.h"
  12. #include "econ_entity.h"
  13. #include "gamestringpool.h"
  14. #include "econ_item_view.h"
  15. #include "UtlSortVector.h"
  16. #include "econ_gcmessages.h"
  17. #include "gc_clientsystem.h"
  18. #if !defined(NO_STEAM)
  19. #include "steam/steam_api.h"
  20. #include "gcsdk/gcclientsdk.h"
  21. #endif // NO_STEAM
  22. class CPlayerInventory;
  23. class CEconItem;
  24. struct baseitemcriteria_t;
  25. class CEconItemViewHandle;
  26. #ifdef CLIENT_DLL
  27. class ITexture;
  28. #endif
  29. // Inventory Less function.
  30. // Used to sort the inventory items into their positions.
  31. class CInventoryListLess
  32. {
  33. public:
  34. bool Less( const CEconItemView &src1, const CEconItemView &src2, void *pCtx );
  35. };
  36. // A class that wants notifications when an inventory is updated
  37. class IInventoryUpdateListener : public GCSDK::ISharedObjectListener
  38. {
  39. public:
  40. virtual void InventoryUpdated( CPlayerInventory *pInventory ) = 0;
  41. virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
  42. virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  43. virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  44. virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
  45. virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
  46. virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
  47. virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
  48. };
  49. //-----------------------------------------------------------------------------
  50. // Purpose: A single player's inventory.
  51. // On the client, the inventory manager contains an instance of this for the local player.
  52. // On the server, each player contains an instance of this.
  53. //-----------------------------------------------------------------------------
  54. class CPlayerInventory : public GCSDK::ISharedObjectListener
  55. {
  56. DECLARE_CLASS_NOBASE( CPlayerInventory );
  57. public:
  58. CPlayerInventory();
  59. virtual ~CPlayerInventory();
  60. void Clear();
  61. // Returns true if this inventory has been filled out by Steam.
  62. bool RetrievedInventoryFromSteam( void ) { return m_bGotItemsFromSteam; }
  63. bool IsWaitingForSteam( void ) { return (m_iPendingRequests > 0); }
  64. // Inventory access
  65. CSteamID &GetOwner( void ) { return m_OwnerID; }
  66. int GetItemCount( void ) const { return m_aInventoryItems.Count(); }
  67. virtual bool CanPurchaseItems( int iItemCount ) const { return GetMaxItemCount() - GetItemCount() >= iItemCount; }
  68. virtual int GetMaxItemCount( void ) const { return DEFAULT_NUM_BACKPACK_SLOTS; }
  69. CEconItemView *GetItem( int i ) { return &m_aInventoryItems[i]; }
  70. virtual CEconItemView *GetItemInLoadout( int iClass, int iSlot ) { AssertMsg( 0, "Implement me!" ); return NULL; }
  71. // Get the item object cache data for the specified item
  72. CEconItem *GetSOCDataForItem( itemid_t iItemID );
  73. GCSDK::CGCClientSharedObjectCache *GetSOC( void ) { return m_pSOCache; }
  74. // tells the GC systems to forget about this listener
  75. void RemoveListener( GCSDK::ISharedObjectListener *pListener );
  76. // Finds the item in our inventory that matches the specified global index
  77. CEconItemView *GetInventoryItemByItemID( itemid_t iIndex, int *pIndex = NULL );
  78. // Finds the item in our inventory that matches the specified global original id
  79. CEconItemView *GetInventoryItemByOriginalID( itemid_t iOriginalID, int *pIndex = NULL );
  80. // Finds the item in our inventory in the specified position
  81. CEconItemView *GetItemByPosition( int iPosition, int *pIndex = NULL );
  82. // Finds the first item in our backpack with match itemdef
  83. CEconItemView *FindFirstItembyItemDef( item_definition_index_t iItemDef );
  84. // Used to reject items on the backend for inclusion into this inventory.
  85. // Mostly used for division of bags into different in-game inventories.
  86. virtual bool ItemShouldBeIncluded( int iItemPosition ) { return true; }
  87. // Debugging
  88. virtual void DumpInventoryToConsole( bool bRoot );
  89. // Extracts the position that should be used to sort items in the inventory from the backend position.
  90. // Necessary if your inventory packs a bunch of info into the position instead of using it just as a position.
  91. virtual int ExtractInventorySortPosition( uint32 iBackendPosition ) { return iBackendPosition; }
  92. // Recipe access
  93. int GetRecipeCount( void ) const;
  94. const CEconCraftingRecipeDefinition *GetRecipeDef( int iIndex );
  95. const CEconCraftingRecipeDefinition *GetRecipeDefByDefIndex( uint16 iDefIndex );
  96. // Item previews
  97. virtual int GetPreviewItemDef( void ) const { return 0; };
  98. // Access helpers
  99. virtual void SOClear();
  100. virtual void NotifyHasNewItems() {}
  101. void AddItemHandle( CEconItemViewHandle* pHandle );
  102. void RemoveItemHandle( CEconItemViewHandle* pHandle );
  103. #ifdef CLIENT_DLL
  104. virtual ITexture *GetWeaponSkinBaseLowRes( itemid_t nItemId, int iTeam ) const { return NULL; }
  105. #endif
  106. protected:
  107. // Inventory updating, called by the Inventory Manager only. If you want an inventory updated,
  108. // use the SteamRequestX functions in CInventoryManager.
  109. void RequestInventory( CSteamID pSteamID );
  110. void AddListener( GCSDK::ISharedObjectListener *pListener );
  111. virtual bool AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems );
  112. virtual void RemoveItem( itemid_t iItemID );
  113. bool FilloutItemFromEconItem( CEconItemView *pScriptItem, CEconItem *pEconItem );
  114. void SendInventoryUpdateEvent();
  115. virtual void OnHasNewItems() {}
  116. virtual void OnItemChangedPosition( CEconItemView *pItem, uint32 iOldPos ) { return; }
  117. virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  118. virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  119. virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  120. virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  121. virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  122. virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  123. virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  124. void ResortInventory( void ) { m_aInventoryItems.RedoSort( true ); }
  125. virtual void ValidateInventoryPositions( void );
  126. // Derived inventory hooks
  127. virtual void ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile );
  128. virtual void ItemIsBeingRemoved( CEconItemView *pItem ) { return; }
  129. // Get the index for the item in our inventory utlvector
  130. int GetIndexForItem( CEconItemView *pItem );
  131. void DirtyItemHandles();
  132. protected:
  133. // The Steam Id of the player who owns this inventory
  134. CSteamID m_OwnerID;
  135. // The items the player has in his inventory, received from steam.
  136. CUtlSortVector<CEconItemView,CInventoryListLess> m_aInventoryItems;
  137. int m_iPendingRequests;
  138. bool m_bGotItemsFromSteam;
  139. GCSDK::CGCClientSharedObjectCache *m_pSOCache;
  140. CUtlVector<GCSDK::ISharedObjectListener *> m_vecListeners;
  141. CUtlVector< CEconItemViewHandle* > m_vecItemHandles;
  142. friend class CInventoryManager;
  143. };
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. //-----------------------------------------------------------------------------
  147. class CInventoryManager : public CAutoGameSystemPerFrame
  148. {
  149. DECLARE_CLASS_GAMEROOT( CInventoryManager, CAutoGameSystem );
  150. public:
  151. CInventoryManager( void );
  152. // Adds the inventory to the list of inventories that should be maintained.
  153. // This causes the game to load the items for the SteamID into this inventory.
  154. // NOTE: This fires off a request to Steam. The data will not be filled out immediately.
  155. void SteamRequestInventory( CPlayerInventory *pInventory, CSteamID pSteamID, IInventoryUpdateListener *pListener = NULL );
  156. void PreInitGC();
  157. void PostInitGC();
  158. #ifdef CLIENT_DLL
  159. void DropItem( itemid_t iItemID );
  160. int DeleteUnknowns( CPlayerInventory *pInventory );
  161. #endif
  162. public:
  163. //-----------------------------------------------------------------------
  164. // IAutoServerSystem
  165. //-----------------------------------------------------------------------
  166. virtual bool Init( void ) OVERRIDE;
  167. virtual void PostInit( void ) OVERRIDE;
  168. virtual void Shutdown() OVERRIDE;
  169. virtual void LevelInitPreEntity( void ) OVERRIDE;
  170. virtual void LevelShutdownPostEntity( void ) OVERRIDE;
  171. #ifdef CLIENT_DLL
  172. // Gets called each frame
  173. virtual void Update( float frametime ) OVERRIDE;
  174. #endif
  175. void GameServerSteamAPIActivated();
  176. virtual CPlayerInventory *GetInventoryForAccount( uint32 iAccountID );
  177. // 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.
  178. virtual void AddBaseItemCriteria( baseitemcriteria_t *pCriteria, CItemSelectionCriteria *pSelectionCriteria ) { return; }
  179. #ifdef CLIENT_DLL
  180. // Must be implemented by derived class
  181. virtual bool EquipItemInLoadout( int iClass, int iSlot, itemid_t iItemID ) = 0;
  182. virtual CPlayerInventory *GeneratePlayerInventoryObject() const { return new CPlayerInventory; }
  183. //-----------------------------------------------------------------------
  184. // ITEM PRESETS
  185. //-----------------------------------------------------------------------
  186. // Is the given preset index valid?
  187. bool IsPresetIndexValid( equipped_preset_t unPreset );
  188. // Equip all items for the given class and preset (all the work is done on the GC -- this just
  189. // sends the message up)
  190. bool LoadPreset( equipped_class_t unClass, equipped_preset_t unPreset );
  191. //-----------------------------------------------------------------------
  192. // LOCAL INVENTORY
  193. //
  194. // On the client, we have a single inventory for the local player. Stored here, instead of in the
  195. // local player entity, because players need to access it while not being connected to a server.
  196. // Override GetLocalInventory() in your inventory manager and return your custom local inventory.
  197. //-----------------------------------------------------------------------
  198. virtual void UpdateLocalInventory( void );
  199. virtual CPlayerInventory *GetLocalInventory( void ) { return NULL; }
  200. // The local inventory is used to track discards & responses to. We need to
  201. // make a decision about inventory space right after sending a delete request,
  202. // so we predict the request will work.
  203. void OnItemDeleted( CPlayerInventory *pInventory ) { if ( pInventory == GetLocalInventory() ) m_iPredictedDiscards--; }
  204. virtual void PersonaName_Precache( uint32 unAccountID );
  205. virtual const char *PersonaName_Get( uint32 unAccountID );
  206. virtual void PersonaName_Store( uint32 unAccountID, const char *pPersonaName );
  207. static void SendGCConnectedEvent( void );
  208. // Returns the item at the specified backpack position
  209. virtual CEconItemView *GetItemByBackpackPosition( int iBackpackPosition );
  210. // Moves the item to the specified backpack position. If there's another item as that spot, it swaps positions with it.
  211. virtual void MoveItemToBackpackPosition( CEconItemView *pItem, int iBackpackPosition );
  212. // Tries to set the item to the specified backpack position. Passing in 0 will find the first empty position.
  213. // FAILS if the backpack is full, or if that spot isn't clear. Returns false in that case.
  214. virtual bool SetItemBackpackPosition( CEconItemView *pItem, uint32 iPosition = 0, bool bForceUnequip = false, bool bAllowOverflow = false );
  215. // Sort the backpack items by the specified type
  216. virtual void SortBackpackBy( uint32 iSortType );
  217. void SortBackpackFinished( void );
  218. bool IsInBackpackSort( void ) { return m_bInBackpackSort; }
  219. void PredictedBackpackPosFilled( int iBackpackPos ) { m_PredictedFilledSlots.FindAndRemove( iBackpackPos ); }
  220. // Tell the backend to move an item to a specified backend position
  221. virtual void UpdateInventoryPosition( CPlayerInventory *pInventory, uint64 ulItemID, uint32 unNewInventoryPos );
  222. virtual void UpdateInventoryEquippedState( CPlayerInventory *pInventory, uint64 ulItemID, equipped_class_t unClass, equipped_slot_t unSlot );
  223. //-----------------------------------------------------------------------
  224. // CLIENT PICKUP UI HANDLING
  225. //-----------------------------------------------------------------------
  226. // Get the number of items picked up
  227. virtual int GetNumItemPickedUpItems( void ) { return 0; }
  228. // Show the player a pickup screen with any items they've collected recently, if any
  229. virtual bool ShowItemsPickedUp( bool bForce = false, bool bReturnToGame = true, bool bNoPanel = false );
  230. // Show the player a pickup screen with the items they've crafted
  231. virtual void ShowItemsCrafted( CUtlVector<itemid_t> *vecCraftedIndices ) { return; }
  232. // Force the player to discard an item to make room for a new item, if they have one.
  233. // Returns true if the discard panel has been brought up, and the player will be forced to discard an item.
  234. virtual bool CheckForRoomAndForceDiscard( void );
  235. //-----------------------------------------------------------------------
  236. // CLIENT ITEM PICKUP ACKNOWLEDGEMENT FILES
  237. //
  238. // This system avoids showing multiple pickups for items that we've found, but haven't been
  239. // able to move out of unack'd position due to the GC being unavailable. We keep a list of
  240. // items we've ack'd in a client file, and don't re-show pickups for them. When a GC item
  241. // update tells us the item has moved out of the unack'd position, we remove it from our file.
  242. //-----------------------------------------------------------------------
  243. virtual void AcknowledgeItem ( CEconItemView *pItem, bool bMoveToBackpack = true ); // Client Acknowledges an item and moves it in to the backpack
  244. bool HasBeenAckedByClient( CEconItemView *pItem ); // Returns true if it's in our client file
  245. void SetAckedByClient( CEconItemView *pItem ); // Adds it to our client file
  246. void SetAckedByGC( CEconItemView *pItem, bool bSave ); // Removes it from our client file
  247. KeyValues *GetAckKeyForItem( CEconItemView *pItem );
  248. void CleanAckFile( void );
  249. void SaveAckFile( void );
  250. private:
  251. void VerifyAckFileLoaded( void );
  252. KeyValues *m_pkvItemClientAckFile;
  253. bool m_bClientAckDirty;
  254. private:
  255. // As we move items around in batches (on pickups usually) we need to know what slots will be filled
  256. // by items we've moved, and haven't received a response from Steam.
  257. CUtlVector<int> m_PredictedFilledSlots;
  258. #endif
  259. public:
  260. virtual int GetBackpackPositionFromBackend( uint32 iBackendPosition ) { return ExtractBackpackPositionFromBackend(iBackendPosition); }
  261. private:
  262. //-----------------------------------------------------------------------
  263. // Pending inventory requests
  264. struct pendingreq_t
  265. {
  266. CPlayerInventory *pInventory;
  267. CSteamID pID;
  268. };
  269. CUtlVector<pendingreq_t> m_hPendingInventoryRequests;
  270. void RemovePendingRequest( CSteamID *pSteamID );
  271. protected:
  272. //-----------------------------------------------------------------------
  273. // Inventory registry
  274. void DeregisterInventory( CPlayerInventory *pInventory );
  275. struct inventories_t
  276. {
  277. CPlayerInventory *pInventory;
  278. IInventoryUpdateListener *pListener;
  279. };
  280. CUtlVector<inventories_t> m_pInventories;
  281. friend class CPlayerInventory;
  282. inline bool IsValidPlayerClass( equipped_class_t unClass );
  283. #ifdef CLIENT_DLL
  284. // Keep track of the number of items we've tried to discard, but haven't recieved responses on
  285. int m_iPredictedDiscards;
  286. typedef CUtlMap< uint32, CUtlString, int > tPersonaNamesByAccountID;
  287. tPersonaNamesByAccountID m_mapPersonaNamesCache;
  288. bool m_bInBackpackSort;
  289. float m_flNextLoadPresetChange;
  290. CMsgSetItemPositions m_msgPendingSetItemPositions;
  291. CMsgLookupMultipleAccountNames m_msgPendingLookupAccountNames;
  292. void OnPersonaStateChanged( PersonaStateChange_t *info );
  293. CCallback< CInventoryManager, PersonaStateChange_t, false > m_sPersonaStateChangedCallback;
  294. CUtlMap< uint64, bool > m_personaNameRequests;
  295. #endif
  296. };
  297. //=================================================================================
  298. // Implement these functions in your game code to create custom derived versions
  299. CInventoryManager *InventoryManager( void );
  300. CBasePlayer *GetPlayerBySteamID( const CSteamID &steamID );
  301. //-----------------------------------------------------------------------------
  302. // Purpose: Maintains a handle to an CEconItemView within an inventory. When
  303. // the inventory gets updated and shuffles CEconItemViews around, this
  304. // handle automatically updates its pointer to point to the new
  305. // CEconItemView that has the same item_id
  306. //-----------------------------------------------------------------------------
  307. class CEconItemViewHandle
  308. {
  309. public:
  310. CEconItemViewHandle()
  311. : m_pItem( NULL )
  312. , m_pInv( NULL )
  313. , m_bPointerDirty( false )
  314. {}
  315. CEconItemViewHandle( CEconItemView* pItem )
  316. : m_pItem( pItem )
  317. , m_pInv( NULL )
  318. , m_bPointerDirty( false )
  319. {
  320. SetItem( pItem );
  321. }
  322. virtual ~CEconItemViewHandle()
  323. {
  324. // Unregister us
  325. if ( m_pInv )
  326. {
  327. m_pInv->RemoveItemHandle( this );
  328. }
  329. }
  330. void SetItem( CEconItemView* pItem );
  331. operator CEconItemView *( void ) const
  332. {
  333. return Get();
  334. }
  335. CEconItemView* operator->( void ) const
  336. {
  337. return Get();
  338. }
  339. void ItemIsBeingDeleted( const CEconItemView* pItem )
  340. {
  341. m_bPointerDirty = true;
  342. // Inventory told us the item is going away
  343. if ( m_pItem == pItem )
  344. {
  345. m_pItem = NULL;
  346. }
  347. }
  348. void InventoryIsBeingDeleted()
  349. {
  350. m_pInv = NULL;
  351. m_pItem = NULL;
  352. m_bPointerDirty = false; // So we dont keep trying to look up the item
  353. }
  354. void MarkDirty()
  355. {
  356. m_bPointerDirty = true;
  357. }
  358. private:
  359. CEconItemView* Get() const;
  360. mutable bool m_bPointerDirty; // Used to mark when m_pItem is no longer valid
  361. CPlayerInventory *m_pInv; // Inventory the item belongs to. Used to look up new CEconItemView
  362. mutable CEconItemView* m_pItem; // The item.
  363. uint64 m_nItemID; // ID of the item
  364. CSteamID m_OwnerSteamID; // Steam ID of the item owner
  365. };
  366. #endif // ITEM_INVENTORY_H