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.
646 lines
20 KiB
646 lines
20 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================
|
||
|
#include "cbase.h"
|
||
|
|
||
|
#include "econ_trading.h"
|
||
|
|
||
|
// for messaging with the GC
|
||
|
#include "econ_gcmessages.h"
|
||
|
#include "econ_item_inventory.h"
|
||
|
#include "econ_gcmessages.h"
|
||
|
#include "econ_ui.h"
|
||
|
|
||
|
// other
|
||
|
#include "c_baseplayer.h"
|
||
|
#include "c_playerresource.h"
|
||
|
|
||
|
// UI
|
||
|
#include "confirm_dialog.h"
|
||
|
#include "econ_controls.h"
|
||
|
#include "vgui/ILocalize.h"
|
||
|
#include "econ_notifications.h"
|
||
|
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
#include "c_tf_gamestats.h"
|
||
|
#endif
|
||
|
|
||
|
#include "gc_clientsystem.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include <tier0/memdbgon.h>
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
const char *g_FriendRelationship[] =
|
||
|
{
|
||
|
"none"
|
||
|
"blocked",
|
||
|
"request_recipient",
|
||
|
"friend",
|
||
|
"request_initiator",
|
||
|
"ignored",
|
||
|
"ignored_friend"
|
||
|
};
|
||
|
|
||
|
const char *g_RejectedReasons[] =
|
||
|
{
|
||
|
"accepted",
|
||
|
"declined",
|
||
|
"vac_banned_initiator",
|
||
|
"vac_banned_target",
|
||
|
"target_already_trading",
|
||
|
"trading_disabled",
|
||
|
"user_not_logged_in"
|
||
|
};
|
||
|
|
||
|
const char *g_ClosedReasons[] =
|
||
|
{
|
||
|
"traded",
|
||
|
"canceled",
|
||
|
"error",
|
||
|
"does_not_own_items",
|
||
|
"untradable_items",
|
||
|
"no_items",
|
||
|
"trading_disabled"
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kShowTradeRequestsFrom_FriendsOnly = 1,
|
||
|
kShowTradeRequestsFrom_FriendsAndCurrentServer = 2,
|
||
|
kShowTradeRequestsFrom_Anyone = 3,
|
||
|
kShowTradeRequestsFrom_NoOne = 4,
|
||
|
};
|
||
|
|
||
|
ConVar cl_trading_show_requests_from( "cl_trading_show_requests_from", "3", FCVAR_ARCHIVE, "View trade requests from a certain group only." );
|
||
|
|
||
|
static bool sbTestingSelfTrade = false;
|
||
|
|
||
|
static int iTradeRequests = 0;
|
||
|
static int iTradeAttempts = 0;
|
||
|
static int iTradeOffers = 0;
|
||
|
static int iGiftsGiven = 0;
|
||
|
|
||
|
const uint32 kTradeRequestLifetime = 30.0f;
|
||
|
|
||
|
// waiting dialog
|
||
|
class CTradingWaitDialog : public CGenericWaitingDialog
|
||
|
{
|
||
|
public:
|
||
|
CTradingWaitDialog( const char *pText = "#TF_Trading_Timeout_Text", const wchar_t *pPlayerName = NULL )
|
||
|
: CGenericWaitingDialog( NULL )
|
||
|
, m_pText( pText )
|
||
|
, m_pKeyValues( NULL )
|
||
|
{
|
||
|
if ( pPlayerName != NULL && wcsicmp( pPlayerName, L"" ) != 0 )
|
||
|
{
|
||
|
m_pKeyValues = new KeyValues( "CTradingWaitDialog" );
|
||
|
m_pKeyValues->SetWString( "other_player", pPlayerName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual ~CTradingWaitDialog()
|
||
|
{
|
||
|
if ( m_pKeyValues != NULL )
|
||
|
{
|
||
|
m_pKeyValues->deleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
virtual void OnTimeout()
|
||
|
{
|
||
|
ShowMessageBox( "#TF_Trading_Timeout_Title", m_pText, m_pKeyValues, "#GameUI_OK" );
|
||
|
}
|
||
|
|
||
|
virtual void OnUserClose()
|
||
|
{
|
||
|
GCSDK::CGCMsg< MsgGCTrading_CancelSession_t > msg( k_EMsgGCTrading_CancelSession );
|
||
|
GCClientSystem()->BSendMessage( msg );
|
||
|
// @note not sure we need to wait for the GC here, but we will just in case...
|
||
|
ShowWaitingDialog( new CTradingWaitDialog(), "#TF_Trading_WaitingForCancel", true, false, 20.0f );
|
||
|
}
|
||
|
|
||
|
KeyValues *m_pKeyValues;
|
||
|
const char* m_pText;
|
||
|
};
|
||
|
|
||
|
// used by the waiting dialogs
|
||
|
static void TradeCompleteDialogClosed( bool bConfirmed, void *pContext )
|
||
|
{
|
||
|
if ( bConfirmed )
|
||
|
{
|
||
|
InventoryManager()->ShowItemsPickedUp( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// jobs
|
||
|
class CTFTradeRequestNotification : public CEconNotification
|
||
|
{
|
||
|
public:
|
||
|
CTFTradeRequestNotification( uint64 ulInitiatorSteamID, uint32 unTradeRequestID, const char* pPlayerName )
|
||
|
: CEconNotification()
|
||
|
, m_unTradeRequestID( unTradeRequestID )
|
||
|
{
|
||
|
SetSteamID( ulInitiatorSteamID );
|
||
|
g_pVGuiLocalize->ConvertANSIToUnicode( pPlayerName, m_wszPlayerName, sizeof(m_wszPlayerName) );
|
||
|
SetLifetime( kTradeRequestLifetime );
|
||
|
SetText( "#TF_Trading_JoinText" );
|
||
|
AddStringToken( "initiator", m_wszPlayerName );
|
||
|
}
|
||
|
|
||
|
virtual EType NotificationType() { return eType_AcceptDecline; }
|
||
|
|
||
|
/// XXX(JohnS): Dead code? Was always accept/decline AFAICT
|
||
|
virtual void Trigger()
|
||
|
{
|
||
|
CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#TF_Trading_JoinTitle", "#TF_Trading_JoinText", "#GameUI_OK", "#TF_Trading_JoinCancel", &ConfirmJoinTradeSession );
|
||
|
pDialog->SetContext( this );
|
||
|
pDialog->AddStringToken( "initiator", m_wszPlayerName );
|
||
|
// so we aren't deleted
|
||
|
SetIsInUse( true );
|
||
|
}
|
||
|
|
||
|
virtual void Accept()
|
||
|
{
|
||
|
ConfirmJoinTradeSession( true, this );
|
||
|
}
|
||
|
virtual void Decline()
|
||
|
{
|
||
|
ConfirmJoinTradeSession( false, this );
|
||
|
}
|
||
|
static void ConfirmJoinTradeSession( bool bConfirmed, void *pContext )
|
||
|
{
|
||
|
CTFTradeRequestNotification *pNotification = (CTFTradeRequestNotification*)pContext;
|
||
|
GCSDK::CGCMsg< MsgGCTrading_InitiateTradeResponse_t > msg( k_EMsgGCTrading_InitiateTradeResponse );
|
||
|
msg.Body().m_eResponse = bConfirmed ? k_EGCMsgInitiateTradeResponse_Accepted : k_EGCMsgInitiateTradeResponse_Declined;
|
||
|
msg.Body().m_unTradeRequestID = pNotification->m_unTradeRequestID;
|
||
|
GCClientSystem()->BSendMessage( msg );
|
||
|
if ( bConfirmed )
|
||
|
{
|
||
|
ShowWaitingDialog( new CTradingWaitDialog(), "#TF_Trading_WaitingForServer", true, false, kTradeRequestLifetime );
|
||
|
}
|
||
|
// now we can be deleted
|
||
|
pNotification->SetIsInUse( false );
|
||
|
pNotification->MarkForDeletion();
|
||
|
}
|
||
|
|
||
|
static bool IsTradingNotification( CEconNotification * pNotification )
|
||
|
{
|
||
|
return dynamic_cast< CTFTradeRequestNotification * >( pNotification ) != NULL;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
uint32 m_unTradeRequestID;
|
||
|
wchar_t m_wszPlayerName[MAX_PLAYER_NAME_LENGTH];
|
||
|
};
|
||
|
|
||
|
// request from party A (through GC) to start trading
|
||
|
class CGCTrading_InitiateTradeRequest : public GCSDK::CGCClientJob
|
||
|
{
|
||
|
public:
|
||
|
CGCTrading_InitiateTradeRequest( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||
|
|
||
|
void SendDeclinedMessage( uint32 unTradeRequestID )
|
||
|
{
|
||
|
GCSDK::CGCMsg< MsgGCTrading_InitiateTradeResponse_t > msgResponse( k_EMsgGCTrading_InitiateTradeResponse );
|
||
|
msgResponse.Body().m_eResponse = k_EGCMsgInitiateTradeResponse_Declined;
|
||
|
msgResponse.Body().m_unTradeRequestID = unTradeRequestID;
|
||
|
GCClientSystem()->BSendMessage( msgResponse );
|
||
|
}
|
||
|
|
||
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGCTrading_InitiateTradeRequest_t> msg( pNetPacket );
|
||
|
CUtlString playerName;
|
||
|
msg.BReadStr( &playerName );
|
||
|
|
||
|
if ( sbTestingSelfTrade )
|
||
|
{
|
||
|
CloseWaitingDialog();
|
||
|
}
|
||
|
else if ( msg.Body().m_ulOtherSteamID == Trading_GetLocalPlayerSteamID().ConvertToUint64() )
|
||
|
{
|
||
|
CloseWaitingDialog();
|
||
|
}
|
||
|
|
||
|
iTradeRequests++;
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_REQUEST_RECEIVED, msg.Body().m_ulOtherSteamID, iTradeRequests );
|
||
|
#endif
|
||
|
|
||
|
// auto-decline for ignored or blocked peoples
|
||
|
if ( steamapicontext == NULL || steamapicontext->SteamFriends() == NULL )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
CSteamID steamIDOther( msg.Body().m_ulOtherSteamID );
|
||
|
EFriendRelationship eRelationship = steamapicontext->SteamFriends()->GetFriendRelationship( steamIDOther );
|
||
|
switch ( eRelationship )
|
||
|
{
|
||
|
case k_EFriendRelationshipBlocked:
|
||
|
case k_EFriendRelationshipIgnored:
|
||
|
case k_EFriendRelationshipIgnoredFriend:
|
||
|
{
|
||
|
SendDeclinedMessage( msg.Body().m_unTradeRequestID );
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
} // switch
|
||
|
|
||
|
switch ( cl_trading_show_requests_from.GetInt() )
|
||
|
{
|
||
|
case kShowTradeRequestsFrom_FriendsOnly:
|
||
|
{
|
||
|
if ( eRelationship != k_EFriendRelationshipFriend )
|
||
|
{
|
||
|
SendDeclinedMessage( msg.Body().m_unTradeRequestID );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case kShowTradeRequestsFrom_FriendsAndCurrentServer:
|
||
|
{
|
||
|
if ( eRelationship == k_EFriendRelationshipFriend )
|
||
|
break;
|
||
|
bool bInCurrentGame = false;
|
||
|
if ( engine->IsInGame() )
|
||
|
{
|
||
|
// otherwise, test if they are in the current game
|
||
|
for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
|
||
|
{
|
||
|
if ( g_PR && g_PR->IsConnected( iPlayerIndex ) )
|
||
|
{
|
||
|
player_info_t pi;
|
||
|
if ( !engine->GetPlayerInfo( iPlayerIndex, &pi ) )
|
||
|
continue;
|
||
|
if ( !pi.friendsID )
|
||
|
continue;
|
||
|
|
||
|
CSteamID steamID( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
|
||
|
if ( steamID == steamIDOther )
|
||
|
{
|
||
|
bInCurrentGame = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( bInCurrentGame == false )
|
||
|
{
|
||
|
SendDeclinedMessage( msg.Body().m_unTradeRequestID );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case kShowTradeRequestsFrom_Anyone:
|
||
|
{
|
||
|
// nothing to check
|
||
|
}
|
||
|
break;
|
||
|
case kShowTradeRequestsFrom_NoOne:
|
||
|
{
|
||
|
SendDeclinedMessage( msg.Body().m_unTradeRequestID );
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NotificationQueue_Add( new CTFTradeRequestNotification( msg.Body().m_ulOtherSteamID, msg.Body().m_unTradeRequestID, playerName.Get() ) );
|
||
|
return true;
|
||
|
}
|
||
|
protected:
|
||
|
};
|
||
|
GC_REG_JOB( GCSDK::CGCClient, CGCTrading_InitiateTradeRequest, "CGCTrading_InitiateTradeRequest", k_EMsgGCTrading_InitiateTradeRequest, GCSDK::k_EServerTypeGCClient );
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#ifdef CLIENT_DLL
|
||
|
CON_COMMAND( cl_trading_test, "Tests the trade ui notification." )
|
||
|
{
|
||
|
if ( steamapicontext == NULL || steamapicontext->SteamUser() == NULL )
|
||
|
return;
|
||
|
|
||
|
CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
|
||
|
NotificationQueue_Add( new CTFTradeRequestNotification( steamID.ConvertToUint64(), steamID.ConvertToUint64(), "Biff" ) );
|
||
|
}
|
||
|
#endif
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
/**
|
||
|
* Remove notification that matches the trade request
|
||
|
*/
|
||
|
class CEconNotificationVisitor_RemoveTradeRequest : public CEconNotificationVisitor
|
||
|
{
|
||
|
public:
|
||
|
CEconNotificationVisitor_RemoveTradeRequest( uint32 unTradeRequestID ) : m_unTradeRequestID( unTradeRequestID ) {}
|
||
|
virtual void Visit( CEconNotification ¬ification )
|
||
|
{
|
||
|
if ( CTFTradeRequestNotification::IsTradingNotification( ¬ification ) )
|
||
|
{
|
||
|
CTFTradeRequestNotification &tradeNotification = dynamic_cast< CTFTradeRequestNotification& >( notification );
|
||
|
if ( tradeNotification.m_unTradeRequestID == m_unTradeRequestID )
|
||
|
{
|
||
|
tradeNotification.MarkForDeletion();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
private:
|
||
|
uint32 m_unTradeRequestID;
|
||
|
};
|
||
|
|
||
|
// GC notification of trade request status
|
||
|
static const char *g_pszTradeResponseDescLocKeys[] =
|
||
|
{
|
||
|
"#TF_Trading_BusyText", // k_EGCMsgInitiateTradeResponse_Accepted (should never be used!)
|
||
|
"#TF_Trading_DeclinedText", // k_EGCMsgInitiateTradeResponse_Declined
|
||
|
"#TF_Trading_VACBannedText", // k_EGCMsgInitiateTradeResponse_VAC_Banned_Initiator
|
||
|
"#TF_Trading_VACBanned2Text", // k_EGCMsgInitiateTradeResponse_VAC_Banned_Target
|
||
|
"#TF_Trading_BusyText", // k_EGCMsgInitiateTradeResponse_Target_Already_Trading
|
||
|
"#TF_Trading_DisabledText", // k_EGCMsgInitiateTradeResponse_Disabled
|
||
|
"#TF_Trading_NotLoggedIn", // k_EGCMsgInitiateTradeResponse_NotLoggedIn
|
||
|
"#TF_Trading_BusyText", // k_EGCMsgInitiateTradeResponse_Cancel (should never be used!)
|
||
|
"#TF_Trading_TooSoon", // k_EGCMsgInitiateTradeResponse_TooSoon
|
||
|
"#TF_Trading_TooSoonPenalty", // k_EGCMsgInitiateTradeResponse_TooSoonPenalty
|
||
|
"#TF_Trading_TradeBannedText", // (was k_EGCMsgInitiateTradeResponse_Free_Account_Initiator_DEPRECATED)
|
||
|
"#TF_Trading_TradeBanned2Text", // k_EGCMsgInitiateTradeResponse_Trade_Banned_Target
|
||
|
"#TF_Trading_FreeAccountInitiate", // k_EGCMsgInitiateTradeResponse_Free_Account_Initiator
|
||
|
"#TF_Trading_SharedAccountInitiate", // k_EGCMsgInitiateTradeResponse_Shared_Account_Initiator
|
||
|
"#TF_Trading_Service_Unavailable", // k_EGCMsgInitiateTradeResponse_Service_Unavailable
|
||
|
"#TF_Trading_YouBlockedThem", // k_EGCMsgInitiateTradeResponse_Target_Blocked
|
||
|
"#TF_Trading_NeedVerifiedEmail", // k_EGCMsgInitiateTradeResponse_NeedVerifiedEmail
|
||
|
"#TF_Trading_NeedSteamGuard", // k_EGCMsgInitiateTradeResponse_NeedSteamGuard
|
||
|
"#TF_Trading_SteamGuardDuration", // k_EGCMsgInitiateTradeResponse_SteamGuardDuration
|
||
|
"#TF_Trading_TheyCannotTrade", // k_EGCMsgInitiateTradeResponse_TheyCannotTrade
|
||
|
"#TF_Trading_PasswordChanged", // k_EGCMsgInitiateTradeResponse_Recent_Password_Reset = 20,
|
||
|
"#TF_Trading_NewDevice", // k_EGCMsgInitiateTradeResponse_Using_New_Device = 21,
|
||
|
"#TF_Trading_InvalidCookie" // k_EGCMsgInitiateTradeResponse_Sent_Invalid_Cookie = 22,
|
||
|
};
|
||
|
|
||
|
class CGCTrading_InitiateTradeResponse : public GCSDK::CGCClientJob
|
||
|
{
|
||
|
public:
|
||
|
CGCTrading_InitiateTradeResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||
|
{
|
||
|
// If this assertion fails it probably means you added a new value to EGCMsgInitiateTradeResponse
|
||
|
// but didn't add a string to the array that tracks the user-facing response strings.
|
||
|
Assert( ARRAYSIZE( g_pszTradeResponseDescLocKeys ) == k_EGCMsgInitiateTradeResponse_Count );
|
||
|
|
||
|
CloseWaitingDialog();
|
||
|
|
||
|
GCSDK::CGCMsg<MsgGCTrading_InitiateTradeResponse_t> msg( pNetPacket );
|
||
|
const uint32 eResponse = msg.Body().m_eResponse;
|
||
|
switch ( eResponse )
|
||
|
{
|
||
|
case k_EGCMsgInitiateTradeResponse_Accepted:
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_REQUEST_ACCEPTED, iTradeRequests, g_RejectedReasons[msg.Body().m_eResponse] );
|
||
|
#endif
|
||
|
ShowWaitingDialog( new CTradingWaitDialog(), "#TF_Trading_WaitingForStart", true, false, 30.0f );
|
||
|
return true; // !
|
||
|
|
||
|
case k_EGCMsgInitiateTradeResponse_Cancel:
|
||
|
{
|
||
|
CEconNotificationVisitor_RemoveTradeRequest visitor( msg.Body().m_unTradeRequestID );
|
||
|
NotificationQueue_Visit( visitor );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case k_EGCMsgInitiateTradeResponse_Declined:
|
||
|
case k_EGCMsgInitiateTradeResponse_VAC_Banned_Initiator:
|
||
|
case k_EGCMsgInitiateTradeResponse_VAC_Banned_Target:
|
||
|
case k_EGCMsgInitiateTradeResponse_Target_Already_Trading:
|
||
|
case k_EGCMsgInitiateTradeResponse_Disabled:
|
||
|
case k_EGCMsgInitiateTradeResponse_NotLoggedIn:
|
||
|
case k_EGCMsgInitiateTradeResponse_TooSoon:
|
||
|
case k_EGCMsgInitiateTradeResponse_TooSoonPenalty:
|
||
|
case k_EGCMsgInitiateTradeResponse_Trade_Banned_Initiator:
|
||
|
case k_EGCMsgInitiateTradeResponse_Trade_Banned_Target:
|
||
|
case k_EGCMsgInitiateTradeResponse_Shared_Account_Initiator:
|
||
|
case k_EGCMsgInitiateTradeResponse_Service_Unavailable:
|
||
|
case k_EGCMsgInitiateTradeResponse_Target_Blocked:
|
||
|
case k_EGCMsgInitiateTradeResponse_NeedVerifiedEmail:
|
||
|
case k_EGCMsgInitiateTradeResponse_NeedSteamGuard:
|
||
|
case k_EGCMsgInitiateTradeResponse_TheyCannotTrade:
|
||
|
case k_EGCMsgInitiateTradeResponse_Recent_Password_Reset:
|
||
|
case k_EGCMsgInitiateTradeResponse_Using_New_Device:
|
||
|
case k_EGCMsgInitiateTradeResponse_Sent_Invalid_Cookie:
|
||
|
|
||
|
ShowMessageBox( "#TF_Trading_StatusTitle", g_pszTradeResponseDescLocKeys[eResponse], "#GameUI_OK" );
|
||
|
break;
|
||
|
|
||
|
case k_EGCMsgInitiateTradeResponse_SteamGuardDuration:
|
||
|
{
|
||
|
|
||
|
KeyValuesAD kvTokens( "CTradingWaitDialog" );
|
||
|
kvTokens->SetWString( "days", L"15" ); // Ideally this would come from the GC, which would get the value from Steam
|
||
|
ShowMessageBox( "#TF_Trading_StatusTitle", g_pszTradeResponseDescLocKeys[eResponse], kvTokens, "#GameUI_OK" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_REQUEST_REJECTED, iTradeRequests, "unknown" );
|
||
|
#endif
|
||
|
ShowMessageBox( "#TF_Trading_StatusTitle", "#TF_Trading_BusyText", "#GameUI_OK" );
|
||
|
return true;
|
||
|
} // switch
|
||
|
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_REQUEST_REJECTED, iTradeRequests, g_RejectedReasons[msg.Body().m_eResponse] );
|
||
|
#endif
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
GC_REG_JOB( GCSDK::CGCClient, CGCTrading_InitiateTradeResponse, "CGCTrading_InitiateTradeResponse", k_EMsgGCTrading_InitiateTradeResponse, GCSDK::k_EServerTypeGCClient );
|
||
|
|
||
|
// start trading session
|
||
|
class CGCTrading_StartSession : public GCSDK::CGCClientJob
|
||
|
{
|
||
|
public:
|
||
|
CGCTrading_StartSession( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGCTrading_StartSession_t> msg( pNetPacket );
|
||
|
|
||
|
steamapicontext->SteamFriends()->ActivateGameOverlayToUser( "jointrade", msg.Body().m_ulSteamIDPartyB );
|
||
|
|
||
|
CloseWaitingDialog();
|
||
|
|
||
|
// remove all trading notifications
|
||
|
NotificationQueue_Remove( &CTFTradeRequestNotification::IsTradingNotification );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
GC_REG_JOB( GCSDK::CGCClient, CGCTrading_StartSession, "CGCTrading_StartSession", k_EMsgGCTrading_StartSession, GCSDK::k_EServerTypeGCClient );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// External interface
|
||
|
|
||
|
CSteamID Trading_GetLocalPlayerSteamID()
|
||
|
{
|
||
|
if ( steamapicontext && steamapicontext->SteamUser() )
|
||
|
{
|
||
|
return steamapicontext->SteamUser()->GetSteamID();
|
||
|
}
|
||
|
return CSteamID();
|
||
|
}
|
||
|
|
||
|
void Trading_RequestTrade( int iPlayerIdx )
|
||
|
{
|
||
|
CSteamID steamID;
|
||
|
C_BasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( iPlayerIdx ) );
|
||
|
if ( pPlayer && pPlayer->GetSteamID( &steamID ) )
|
||
|
{
|
||
|
Trading_RequestTrade( steamID );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Trading_RequestTrade( const CSteamID &steamID )
|
||
|
{
|
||
|
sbTestingSelfTrade = false;
|
||
|
GCSDK::CGCMsg< MsgGCTrading_InitiateTradeRequest_t > msg( k_EMsgGCTrading_InitiateTradeRequest );
|
||
|
msg.Body().m_ulOtherSteamID = steamID.ConvertToUint64();
|
||
|
bool bSent = GCClientSystem()->BSendMessage( msg );
|
||
|
if ( bSent )
|
||
|
{
|
||
|
iTradeRequests++;
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_REQUEST_SENT, msg.Body().m_ulOtherSteamID, iTradeRequests );
|
||
|
#endif
|
||
|
|
||
|
const char* pPlayerName = InventoryManager()->PersonaName_Get( steamID.GetAccountID() );
|
||
|
if ( pPlayerName != NULL && FStrEq( pPlayerName, "" ) == false )
|
||
|
{
|
||
|
wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH];
|
||
|
g_pVGuiLocalize->ConvertANSIToUnicode( pPlayerName, wszPlayerName, sizeof(wszPlayerName) );
|
||
|
CTradingWaitDialog *pDialog = new CTradingWaitDialog( "#TF_Trading_TimeoutPartyB_Named", wszPlayerName );
|
||
|
ShowWaitingDialog( pDialog, "#TF_Trading_WaitingForPartyB", true, true, 30.0f );
|
||
|
wchar_t wszConstructedString[1024];
|
||
|
g_pVGuiLocalize->ConstructString_safe( wszConstructedString, g_pVGuiLocalize->Find( "#TF_Trading_WaitingForPartyB_Named" ), 1, wszPlayerName );
|
||
|
pDialog->SetDialogVariable( "updatetext", wszConstructedString );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CTradingWaitDialog *pDialog = new CTradingWaitDialog( "#TF_Trading_TimeoutPartyB" );
|
||
|
ShowWaitingDialog( pDialog, "#TF_Trading_WaitingForPartyB", true, true, 30.0f );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* UniverseToCommunityURL( EUniverse universe )
|
||
|
{
|
||
|
switch( universe )
|
||
|
{
|
||
|
default: // return public if we don't have a better guess.
|
||
|
case k_EUniversePublic: return "https://steamcommunity.com";
|
||
|
case k_EUniverseBeta: return "https://beta.steamcommunity.com";
|
||
|
case k_EUniverseDev: return "https://localhost/community";
|
||
|
}
|
||
|
|
||
|
// Should never get here.
|
||
|
return UniverseToCommunityURL( k_EUniversePublic );
|
||
|
}
|
||
|
|
||
|
const char* GetCommunityURL()
|
||
|
{
|
||
|
if ( GetUniverse() == k_EUniverseInvalid )
|
||
|
{
|
||
|
Assert( !"calling GetCommunityURL when not connected. This is allowed, but will return public universe." );
|
||
|
return UniverseToCommunityURL( k_EUniversePublic );
|
||
|
}
|
||
|
|
||
|
return UniverseToCommunityURL( GetUniverse() );
|
||
|
}
|
||
|
|
||
|
void Trading_SendGift( const CSteamID& steamID, const CEconItemView& giftItem )
|
||
|
{
|
||
|
if ( !steamapicontext || !steamapicontext->SteamFriends() )
|
||
|
{
|
||
|
// TODO: Error dialog.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
C_CTF_GameStats.Event_Trading( IE_TRADING_ITEM_GIFTED, steamID.ConvertToUint64(), iGiftsGiven );
|
||
|
#endif
|
||
|
|
||
|
// Build up the steam URL and send it over.
|
||
|
// Should look like this: https://steamcommunity.com/trade/1/sendgift/?appid=&contextid=&assetid=&steamid_target=
|
||
|
|
||
|
steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage(
|
||
|
CFmtStrMax( "%s/trade/1/sendgift/?appid=%d&contextid=%d&assetid=%llu&steamid_target=%llu",
|
||
|
GetCommunityURL(),
|
||
|
engine->GetAppID(),
|
||
|
2, // k_EEconContextBackpack
|
||
|
giftItem.GetItemID(),
|
||
|
steamID.ConvertToUint64()
|
||
|
)
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
CON_COMMAND( cl_trade, "Trade with a person by player name" )
|
||
|
{
|
||
|
if ( args.ArgC() < 2 )
|
||
|
return;
|
||
|
|
||
|
if ( GetUniverse() == k_EUniverseInvalid )
|
||
|
return;
|
||
|
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
|
||
|
{
|
||
|
if( ( iPlayerIndex != iLocalPlayerIndex ) && ( g_PR->IsConnected( iPlayerIndex ) ) )
|
||
|
{
|
||
|
player_info_t pi;
|
||
|
if ( !engine->GetPlayerInfo( iPlayerIndex, &pi ) )
|
||
|
continue;
|
||
|
if ( !pi.friendsID )
|
||
|
continue;
|
||
|
|
||
|
if ( FStrEq( pi.name, args[1] ) == false )
|
||
|
continue;
|
||
|
|
||
|
CSteamID steamID( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
|
||
|
Trading_RequestTrade( steamID );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CON_COMMAND( cl_trade_steamid, "Trade with a person by steam id" )
|
||
|
{
|
||
|
if ( args.ArgC() < 2 )
|
||
|
return;
|
||
|
|
||
|
if ( GetUniverse() == k_EUniverseInvalid )
|
||
|
return;
|
||
|
|
||
|
const char *pInput = args[1];
|
||
|
if ( pInput[0] >= '0' && pInput[0] <= '9' )
|
||
|
{
|
||
|
CSteamID steamID;
|
||
|
steamID.SetFromString( pInput, GetUniverse() );
|
||
|
if ( steamID.IsValid() )
|
||
|
Trading_RequestTrade( steamID );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
CON_COMMAND( cl_trading_test_self_trade, "Test self-trading" )
|
||
|
{
|
||
|
Trading_RequestTrade( Trading_GetLocalPlayerSteamID() );
|
||
|
sbTestingSelfTrade = true;
|
||
|
}
|
||
|
#endif
|