summaryrefslogtreecommitdiff
path: root/game/shared/econ/econ_item_inventory.h
blob: fd6e8b8b1e5649a4936807383d509121e0b74bee (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
//========= 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