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
472 lines
18 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Container that allows client & server access to data in player inventories & loadouts |
|
// |
|
//============================================================================= |
|
|
|
#ifndef ITEM_INVENTORY_H |
|
#define ITEM_INVENTORY_H |
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
#include "igamesystem.h" |
|
#include "econ_entity.h" |
|
#include "gamestringpool.h" |
|
#include "econ_item_view.h" |
|
#include "UtlSortVector.h" |
|
#include "econ_gcmessages.h" |
|
#include "gc_clientsystem.h" |
|
|
|
#if !defined(NO_STEAM) |
|
#include "steam/steam_api.h" |
|
#include "gcsdk/gcclientsdk.h" |
|
#endif // NO_STEAM |
|
|
|
|
|
class CPlayerInventory; |
|
class CEconItem; |
|
struct baseitemcriteria_t; |
|
class CEconItemViewHandle; |
|
#ifdef CLIENT_DLL |
|
class ITexture; |
|
#endif |
|
|
|
// Inventory Less function. |
|
// Used to sort the inventory items into their positions. |
|
class CInventoryListLess |
|
{ |
|
public: |
|
bool Less( const CEconItemView &src1, const CEconItemView &src2, void *pCtx ); |
|
}; |
|
|
|
// A class that wants notifications when an inventory is updated |
|
class IInventoryUpdateListener : public GCSDK::ISharedObjectListener |
|
{ |
|
public: |
|
virtual void InventoryUpdated( CPlayerInventory *pInventory ) = 0; |
|
|
|
virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); } |
|
virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } |
|
virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } |
|
virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); } |
|
virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); } |
|
virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); } |
|
virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); } |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: A single player's inventory. |
|
// On the client, the inventory manager contains an instance of this for the local player. |
|
// On the server, each player contains an instance of this. |
|
//----------------------------------------------------------------------------- |
|
class CPlayerInventory : public GCSDK::ISharedObjectListener |
|
{ |
|
DECLARE_CLASS_NOBASE( CPlayerInventory ); |
|
public: |
|
CPlayerInventory(); |
|
virtual ~CPlayerInventory(); |
|
|
|
void Clear(); |
|
|
|
// Returns true if this inventory has been filled out by Steam. |
|
bool RetrievedInventoryFromSteam( void ) { return m_bGotItemsFromSteam; } |
|
bool IsWaitingForSteam( void ) { return (m_iPendingRequests > 0); } |
|
|
|
// Inventory access |
|
CSteamID &GetOwner( void ) { return m_OwnerID; } |
|
int GetItemCount( void ) const { return m_aInventoryItems.Count(); } |
|
virtual bool CanPurchaseItems( int iItemCount ) const { return GetMaxItemCount() - GetItemCount() >= iItemCount; } |
|
virtual int GetMaxItemCount( void ) const { return DEFAULT_NUM_BACKPACK_SLOTS; } |
|
CEconItemView *GetItem( int i ) { return &m_aInventoryItems[i]; } |
|
|
|
virtual CEconItemView *GetItemInLoadout( int iClass, int iSlot ) { AssertMsg( 0, "Implement me!" ); return NULL; } |
|
|
|
// Get the item object cache data for the specified item |
|
CEconItem *GetSOCDataForItem( itemid_t iItemID ); |
|
GCSDK::CGCClientSharedObjectCache *GetSOC( void ) { return m_pSOCache; } |
|
|
|
// tells the GC systems to forget about this listener |
|
void RemoveListener( GCSDK::ISharedObjectListener *pListener ); |
|
|
|
// Finds the item in our inventory that matches the specified global index |
|
CEconItemView *GetInventoryItemByItemID( itemid_t iIndex, int *pIndex = NULL ); |
|
|
|
// Finds the item in our inventory that matches the specified global original id |
|
CEconItemView *GetInventoryItemByOriginalID( itemid_t iOriginalID, int *pIndex = NULL ); |
|
|
|
// Finds the item in our inventory in the specified position |
|
CEconItemView *GetItemByPosition( int iPosition, int *pIndex = NULL ); |
|
|
|
// Finds the first item in our backpack with match itemdef |
|
CEconItemView *FindFirstItembyItemDef( item_definition_index_t iItemDef ); |
|
|
|
// Used to reject items on the backend for inclusion into this inventory. |
|
// Mostly used for division of bags into different in-game inventories. |
|
virtual bool ItemShouldBeIncluded( int iItemPosition ) { return true; } |
|
|
|
// Debugging |
|
virtual void DumpInventoryToConsole( bool bRoot ); |
|
|
|
// Extracts the position that should be used to sort items in the inventory from the backend position. |
|
// Necessary if your inventory packs a bunch of info into the position instead of using it just as a position. |
|
virtual int ExtractInventorySortPosition( uint32 iBackendPosition ) { return iBackendPosition; } |
|
|
|
// Recipe access |
|
int GetRecipeCount( void ) const; |
|
const CEconCraftingRecipeDefinition *GetRecipeDef( int iIndex ); |
|
const CEconCraftingRecipeDefinition *GetRecipeDefByDefIndex( uint16 iDefIndex ); |
|
|
|
// Item previews |
|
virtual int GetPreviewItemDef( void ) const { return 0; }; |
|
|
|
// Access helpers |
|
virtual void SOClear(); |
|
|
|
virtual void NotifyHasNewItems() {} |
|
|
|
void AddItemHandle( CEconItemViewHandle* pHandle ); |
|
void RemoveItemHandle( CEconItemViewHandle* pHandle ); |
|
|
|
#ifdef CLIENT_DLL |
|
virtual ITexture *GetWeaponSkinBaseLowRes( itemid_t nItemId, int iTeam ) const { return NULL; } |
|
#endif |
|
|
|
|
|
protected: |
|
// Inventory updating, called by the Inventory Manager only. If you want an inventory updated, |
|
// use the SteamRequestX functions in CInventoryManager. |
|
void RequestInventory( CSteamID pSteamID ); |
|
void AddListener( GCSDK::ISharedObjectListener *pListener ); |
|
virtual bool AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems ); |
|
virtual void RemoveItem( itemid_t iItemID ); |
|
bool FilloutItemFromEconItem( CEconItemView *pScriptItem, CEconItem *pEconItem ); |
|
void SendInventoryUpdateEvent(); |
|
virtual void OnHasNewItems() {} |
|
virtual void OnItemChangedPosition( CEconItemView *pItem, uint32 iOldPos ) { return; } |
|
|
|
virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; |
|
virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } |
|
virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; |
|
virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } |
|
virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; |
|
virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; |
|
virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; |
|
|
|
void ResortInventory( void ) { m_aInventoryItems.RedoSort( true ); } |
|
virtual void ValidateInventoryPositions( void ); |
|
|
|
// Derived inventory hooks |
|
virtual void ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile ); |
|
virtual void ItemIsBeingRemoved( CEconItemView *pItem ) { return; } |
|
|
|
// Get the index for the item in our inventory utlvector |
|
int GetIndexForItem( CEconItemView *pItem ); |
|
|
|
void DirtyItemHandles(); |
|
|
|
protected: |
|
// The Steam Id of the player who owns this inventory |
|
CSteamID m_OwnerID; |
|
|
|
// The items the player has in his inventory, received from steam. |
|
CUtlSortVector<CEconItemView,CInventoryListLess> m_aInventoryItems; |
|
|
|
int m_iPendingRequests; |
|
bool m_bGotItemsFromSteam; |
|
|
|
GCSDK::CGCClientSharedObjectCache *m_pSOCache; |
|
|
|
CUtlVector<GCSDK::ISharedObjectListener *> m_vecListeners; |
|
|
|
CUtlVector< CEconItemViewHandle* > m_vecItemHandles; |
|
|
|
friend class CInventoryManager; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CInventoryManager : public CAutoGameSystemPerFrame |
|
{ |
|
DECLARE_CLASS_GAMEROOT( CInventoryManager, CAutoGameSystem ); |
|
public: |
|
CInventoryManager( void ); |
|
|
|
// Adds the inventory to the list of inventories that should be maintained. |
|
// This causes the game to load the items for the SteamID into this inventory. |
|
// NOTE: This fires off a request to Steam. The data will not be filled out immediately. |
|
void SteamRequestInventory( CPlayerInventory *pInventory, CSteamID pSteamID, IInventoryUpdateListener *pListener = NULL ); |
|
|
|
void PreInitGC(); |
|
void PostInitGC(); |
|
|
|
#ifdef CLIENT_DLL |
|
void DropItem( itemid_t iItemID ); |
|
int DeleteUnknowns( CPlayerInventory *pInventory ); |
|
#endif |
|
|
|
public: |
|
//----------------------------------------------------------------------- |
|
// IAutoServerSystem |
|
//----------------------------------------------------------------------- |
|
virtual bool Init( void ) OVERRIDE; |
|
virtual void PostInit( void ) OVERRIDE; |
|
virtual void Shutdown() OVERRIDE; |
|
virtual void LevelInitPreEntity( void ) OVERRIDE; |
|
virtual void LevelShutdownPostEntity( void ) OVERRIDE; |
|
|
|
#ifdef CLIENT_DLL |
|
// Gets called each frame |
|
virtual void Update( float frametime ) OVERRIDE; |
|
#endif |
|
|
|
void GameServerSteamAPIActivated(); |
|
|
|
virtual CPlayerInventory *GetInventoryForAccount( uint32 iAccountID ); |
|
|
|
// 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. |
|
virtual void AddBaseItemCriteria( baseitemcriteria_t *pCriteria, CItemSelectionCriteria *pSelectionCriteria ) { return; } |
|
|
|
#ifdef CLIENT_DLL |
|
// Must be implemented by derived class |
|
virtual bool EquipItemInLoadout( int iClass, int iSlot, itemid_t iItemID ) = 0; |
|
|
|
virtual CPlayerInventory *GeneratePlayerInventoryObject() const { return new CPlayerInventory; } |
|
|
|
//----------------------------------------------------------------------- |
|
// ITEM PRESETS |
|
//----------------------------------------------------------------------- |
|
|
|
// Is the given preset index valid? |
|
bool IsPresetIndexValid( equipped_preset_t unPreset ); |
|
|
|
// Equip all items for the given class and preset (all the work is done on the GC -- this just |
|
// sends the message up) |
|
bool LoadPreset( equipped_class_t unClass, equipped_preset_t unPreset ); |
|
|
|
//----------------------------------------------------------------------- |
|
// LOCAL INVENTORY |
|
// |
|
// On the client, we have a single inventory for the local player. Stored here, instead of in the |
|
// local player entity, because players need to access it while not being connected to a server. |
|
// Override GetLocalInventory() in your inventory manager and return your custom local inventory. |
|
//----------------------------------------------------------------------- |
|
virtual void UpdateLocalInventory( void ); |
|
virtual CPlayerInventory *GetLocalInventory( void ) { return NULL; } |
|
|
|
// The local inventory is used to track discards & responses to. We need to |
|
// make a decision about inventory space right after sending a delete request, |
|
// so we predict the request will work. |
|
void OnItemDeleted( CPlayerInventory *pInventory ) { if ( pInventory == GetLocalInventory() ) m_iPredictedDiscards--; } |
|
|
|
virtual void PersonaName_Precache( uint32 unAccountID ); |
|
virtual const char *PersonaName_Get( uint32 unAccountID ); |
|
virtual void PersonaName_Store( uint32 unAccountID, const char *pPersonaName ); |
|
|
|
static void SendGCConnectedEvent( void ); |
|
|
|
// Returns the item at the specified backpack position |
|
virtual CEconItemView *GetItemByBackpackPosition( int iBackpackPosition ); |
|
|
|
// Moves the item to the specified backpack position. If there's another item as that spot, it swaps positions with it. |
|
virtual void MoveItemToBackpackPosition( CEconItemView *pItem, int iBackpackPosition ); |
|
|
|
// Tries to set the item to the specified backpack position. Passing in 0 will find the first empty position. |
|
// FAILS if the backpack is full, or if that spot isn't clear. Returns false in that case. |
|
virtual bool SetItemBackpackPosition( CEconItemView *pItem, uint32 iPosition = 0, bool bForceUnequip = false, bool bAllowOverflow = false ); |
|
|
|
// Sort the backpack items by the specified type |
|
virtual void SortBackpackBy( uint32 iSortType ); |
|
void SortBackpackFinished( void ); |
|
bool IsInBackpackSort( void ) { return m_bInBackpackSort; } |
|
|
|
void PredictedBackpackPosFilled( int iBackpackPos ) { m_PredictedFilledSlots.FindAndRemove( iBackpackPos ); } |
|
|
|
// Tell the backend to move an item to a specified backend position |
|
virtual void UpdateInventoryPosition( CPlayerInventory *pInventory, uint64 ulItemID, uint32 unNewInventoryPos ); |
|
|
|
virtual void UpdateInventoryEquippedState( CPlayerInventory *pInventory, uint64 ulItemID, equipped_class_t unClass, equipped_slot_t unSlot ); |
|
|
|
|
|
//----------------------------------------------------------------------- |
|
// CLIENT PICKUP UI HANDLING |
|
//----------------------------------------------------------------------- |
|
|
|
// Get the number of items picked up |
|
virtual int GetNumItemPickedUpItems( void ) { return 0; } |
|
|
|
// Show the player a pickup screen with any items they've collected recently, if any |
|
virtual bool ShowItemsPickedUp( bool bForce = false, bool bReturnToGame = true, bool bNoPanel = false ); |
|
|
|
// Show the player a pickup screen with the items they've crafted |
|
virtual void ShowItemsCrafted( CUtlVector<itemid_t> *vecCraftedIndices ) { return; } |
|
|
|
// Force the player to discard an item to make room for a new item, if they have one. |
|
// Returns true if the discard panel has been brought up, and the player will be forced to discard an item. |
|
virtual bool CheckForRoomAndForceDiscard( void ); |
|
|
|
//----------------------------------------------------------------------- |
|
// CLIENT ITEM PICKUP ACKNOWLEDGEMENT FILES |
|
// |
|
// This system avoids showing multiple pickups for items that we've found, but haven't been |
|
// able to move out of unack'd position due to the GC being unavailable. We keep a list of |
|
// items we've ack'd in a client file, and don't re-show pickups for them. When a GC item |
|
// update tells us the item has moved out of the unack'd position, we remove it from our file. |
|
//----------------------------------------------------------------------- |
|
|
|
virtual void AcknowledgeItem ( CEconItemView *pItem, bool bMoveToBackpack = true ); // Client Acknowledges an item and moves it in to the backpack |
|
bool HasBeenAckedByClient( CEconItemView *pItem ); // Returns true if it's in our client file |
|
void SetAckedByClient( CEconItemView *pItem ); // Adds it to our client file |
|
void SetAckedByGC( CEconItemView *pItem, bool bSave ); // Removes it from our client file |
|
KeyValues *GetAckKeyForItem( CEconItemView *pItem ); |
|
void CleanAckFile( void ); |
|
void SaveAckFile( void ); |
|
|
|
private: |
|
void VerifyAckFileLoaded( void ); |
|
KeyValues *m_pkvItemClientAckFile; |
|
bool m_bClientAckDirty; |
|
|
|
private: |
|
// As we move items around in batches (on pickups usually) we need to know what slots will be filled |
|
// by items we've moved, and haven't received a response from Steam. |
|
CUtlVector<int> m_PredictedFilledSlots; |
|
#endif |
|
|
|
public: |
|
virtual int GetBackpackPositionFromBackend( uint32 iBackendPosition ) { return ExtractBackpackPositionFromBackend(iBackendPosition); } |
|
|
|
private: |
|
//----------------------------------------------------------------------- |
|
// Pending inventory requests |
|
struct pendingreq_t |
|
{ |
|
CPlayerInventory *pInventory; |
|
CSteamID pID; |
|
}; |
|
CUtlVector<pendingreq_t> m_hPendingInventoryRequests; |
|
void RemovePendingRequest( CSteamID *pSteamID ); |
|
|
|
protected: |
|
//----------------------------------------------------------------------- |
|
// Inventory registry |
|
void DeregisterInventory( CPlayerInventory *pInventory ); |
|
struct inventories_t |
|
{ |
|
CPlayerInventory *pInventory; |
|
IInventoryUpdateListener *pListener; |
|
}; |
|
CUtlVector<inventories_t> m_pInventories; |
|
|
|
friend class CPlayerInventory; |
|
|
|
inline bool IsValidPlayerClass( equipped_class_t unClass ); |
|
|
|
#ifdef CLIENT_DLL |
|
// Keep track of the number of items we've tried to discard, but haven't recieved responses on |
|
int m_iPredictedDiscards; |
|
|
|
typedef CUtlMap< uint32, CUtlString, int > tPersonaNamesByAccountID; |
|
tPersonaNamesByAccountID m_mapPersonaNamesCache; |
|
|
|
bool m_bInBackpackSort; |
|
|
|
float m_flNextLoadPresetChange; |
|
|
|
CMsgSetItemPositions m_msgPendingSetItemPositions; |
|
CMsgLookupMultipleAccountNames m_msgPendingLookupAccountNames; |
|
|
|
void OnPersonaStateChanged( PersonaStateChange_t *info ); |
|
CCallback< CInventoryManager, PersonaStateChange_t, false > m_sPersonaStateChangedCallback; |
|
CUtlMap< uint64, bool > m_personaNameRequests; |
|
|
|
#endif |
|
}; |
|
|
|
//================================================================================= |
|
// Implement these functions in your game code to create custom derived versions |
|
CInventoryManager *InventoryManager( void ); |
|
|
|
CBasePlayer *GetPlayerBySteamID( const CSteamID &steamID ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Maintains a handle to an CEconItemView within an inventory. When |
|
// the inventory gets updated and shuffles CEconItemViews around, this |
|
// handle automatically updates its pointer to point to the new |
|
// CEconItemView that has the same item_id |
|
//----------------------------------------------------------------------------- |
|
class CEconItemViewHandle |
|
{ |
|
public: |
|
CEconItemViewHandle() |
|
: m_pItem( NULL ) |
|
, m_pInv( NULL ) |
|
, m_bPointerDirty( false ) |
|
{} |
|
|
|
CEconItemViewHandle( CEconItemView* pItem ) |
|
: m_pItem( pItem ) |
|
, m_pInv( NULL ) |
|
, m_bPointerDirty( false ) |
|
{ |
|
SetItem( pItem ); |
|
} |
|
|
|
virtual ~CEconItemViewHandle() |
|
{ |
|
// Unregister us |
|
if ( m_pInv ) |
|
{ |
|
m_pInv->RemoveItemHandle( this ); |
|
} |
|
} |
|
|
|
void SetItem( CEconItemView* pItem ); |
|
|
|
operator CEconItemView *( void ) const |
|
{ |
|
return Get(); |
|
} |
|
|
|
CEconItemView* operator->( void ) const |
|
{ |
|
return Get(); |
|
} |
|
|
|
void ItemIsBeingDeleted( const CEconItemView* pItem ) |
|
{ |
|
m_bPointerDirty = true; |
|
|
|
// Inventory told us the item is going away |
|
if ( m_pItem == pItem ) |
|
{ |
|
m_pItem = NULL; |
|
} |
|
} |
|
|
|
void InventoryIsBeingDeleted() |
|
{ |
|
m_pInv = NULL; |
|
m_pItem = NULL; |
|
m_bPointerDirty = false; // So we dont keep trying to look up the item |
|
} |
|
|
|
void MarkDirty() |
|
{ |
|
m_bPointerDirty = true; |
|
} |
|
|
|
private: |
|
|
|
CEconItemView* Get() const; |
|
|
|
mutable bool m_bPointerDirty; // Used to mark when m_pItem is no longer valid |
|
CPlayerInventory *m_pInv; // Inventory the item belongs to. Used to look up new CEconItemView |
|
mutable CEconItemView* m_pItem; // The item. |
|
uint64 m_nItemID; // ID of the item |
|
CSteamID m_OwnerSteamID; // Steam ID of the item owner |
|
}; |
|
|
|
|
|
#endif // ITEM_INVENTORY_H
|
|
|