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.
374 lines
13 KiB
374 lines
13 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
|
||
|
#include "tf_gcmessages.h"
|
||
|
#include "tf_item_inventory.h"
|
||
|
#include "tf_player.h"
|
||
|
#include "tf_duel_summary.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
|
||
|
#include "gc_clientsystem.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
struct duel_minigame_data_t
|
||
|
{
|
||
|
CSteamID m_steamIDInitiator;
|
||
|
CSteamID m_steamIDTarget;
|
||
|
uint16 m_usScoreInitiator;
|
||
|
uint16 m_usScoreTarget;
|
||
|
int m_iPlayerClass;
|
||
|
bool operator==( const duel_minigame_data_t &rhs ) const
|
||
|
{
|
||
|
return m_steamIDInitiator == rhs.m_steamIDInitiator && m_steamIDTarget == rhs.m_steamIDTarget;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CUtlVector< duel_minigame_data_t > g_duels;
|
||
|
|
||
|
static duel_minigame_data_t *FindDuelBySteamID( const CSteamID &steamID )
|
||
|
{
|
||
|
FOR_EACH_VEC( g_duels, i )
|
||
|
{
|
||
|
duel_minigame_data_t &duel = g_duels[i];
|
||
|
if ( duel.m_steamIDInitiator == steamID || duel.m_steamIDTarget == steamID )
|
||
|
{
|
||
|
return &duel;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void SpeakConceptBySteamID( const CSteamID &steamID, int iConcept, const CSteamID &steamIDInitiator, const CSteamID &steamIDTarget )
|
||
|
{
|
||
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerBySteamID( steamID ) );
|
||
|
if ( pPlayer != NULL)
|
||
|
{
|
||
|
CTFPlayer *pPlayerInitiator = ToTFPlayer( GetPlayerBySteamID( steamIDInitiator ) );
|
||
|
CTFPlayer *pPlayerTarget = ToTFPlayer( GetPlayerBySteamID( steamIDTarget ) );
|
||
|
char pModifiers[256] = "";
|
||
|
if ( pPlayerInitiator && pPlayerTarget )
|
||
|
{
|
||
|
Q_snprintf( pModifiers, sizeof(pModifiers), "duelinitiatorclass:%s,dueltargetclass:%s",
|
||
|
g_aPlayerClassNames_NonLocalized[ pPlayerInitiator->m_Shared.InCond( TF_COND_DISGUISED ) ? pPlayerInitiator->m_Shared.GetDisguiseClass() : pPlayerInitiator->GetPlayerClass()->GetClassIndex() ],
|
||
|
g_aPlayerClassNames_NonLocalized[ pPlayerTarget->m_Shared.InCond( TF_COND_DISGUISED ) ? pPlayerTarget->m_Shared.GetDisguiseClass() : pPlayerTarget->GetPlayerClass()->GetClassIndex() ]
|
||
|
);
|
||
|
}
|
||
|
pPlayer->SpeakConceptIfAllowed( iConcept, pModifiers );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void RemoveDuel( duel_minigame_data_t *pDuel )
|
||
|
{
|
||
|
g_duels.FindAndFastRemove( *pDuel );
|
||
|
}
|
||
|
|
||
|
static void SendDuelResults( duel_minigame_data_t &duel, const CSteamID &steamIDWinner, eDuelEndReason eReason )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGC_Duel_Results_t> msg( k_EMsgGC_Duel_Results );
|
||
|
msg.Body().m_ulInitiatorSteamID = duel.m_steamIDInitiator.ConvertToUint64();
|
||
|
msg.Body().m_ulTargetSteamID = duel.m_steamIDTarget.ConvertToUint64();
|
||
|
msg.Body().m_ulWinnerSteamID = steamIDWinner.ConvertToUint64();
|
||
|
msg.Body().m_usScoreInitiator = duel.m_usScoreInitiator;
|
||
|
msg.Body().m_usScoreTarget = duel.m_usScoreTarget;
|
||
|
msg.Body().m_usEndReason = eReason;
|
||
|
GCClientSystem()->BSendMessage( msg );
|
||
|
}
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
kDuelScoreType_Kill,
|
||
|
kDuelScoreType_Assist,
|
||
|
kMaxDuelScoreTypes,
|
||
|
} eDuelScoreType;
|
||
|
|
||
|
static int kDuelScoreTypes[kMaxDuelScoreTypes] = { 1, 1 };
|
||
|
|
||
|
static void UpdateDuelScore( CTFPlayer *pKiller, CTFPlayer *pVictim, eDuelScoreType scoreType )
|
||
|
{
|
||
|
CSteamID steamIDKiller;
|
||
|
CSteamID steamIDVictim;
|
||
|
if ( pKiller->GetSteamID( &steamIDKiller ) == false || pVictim->GetSteamID( &steamIDVictim ) == false )
|
||
|
return;
|
||
|
|
||
|
int iScoreIncrement = kDuelScoreTypes[ scoreType ];
|
||
|
|
||
|
FOR_EACH_VEC( g_duels, i )
|
||
|
{
|
||
|
duel_minigame_data_t &duel = g_duels[i];
|
||
|
if ( ( duel.m_steamIDInitiator == steamIDKiller && duel.m_steamIDTarget == steamIDVictim ) ||
|
||
|
( duel.m_steamIDInitiator == steamIDVictim && duel.m_steamIDTarget == steamIDKiller ) )
|
||
|
{
|
||
|
// if we have a class restriction...
|
||
|
bool bCountScore = true;
|
||
|
if ( duel.m_iPlayerClass >= TF_FIRST_NORMAL_CLASS && duel.m_iPlayerClass < TF_LAST_NORMAL_CLASS )
|
||
|
{
|
||
|
bCountScore = ( pKiller->GetPlayerClass() != NULL && duel.m_iPlayerClass == pKiller->GetPlayerClass()->GetClassIndex() &&
|
||
|
pVictim->GetPlayerClass() != NULL && duel.m_iPlayerClass == pVictim->GetPlayerClass()->GetClassIndex() );
|
||
|
}
|
||
|
|
||
|
if ( bCountScore )
|
||
|
{
|
||
|
// send appropriate event to all clients
|
||
|
IGameEvent * event = gameeventmanager->CreateEvent( "duel_status" );
|
||
|
if ( event )
|
||
|
{
|
||
|
event->SetInt( "killer", pKiller->GetUserID() );
|
||
|
event->SetInt( "score_type", scoreType );
|
||
|
if ( steamIDKiller == duel.m_steamIDInitiator )
|
||
|
{
|
||
|
event->SetInt( "initiator", pKiller->GetUserID() );
|
||
|
event->SetInt( "target", pVictim->GetUserID() );
|
||
|
duel.m_usScoreInitiator += iScoreIncrement;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
event->SetInt( "initiator", pVictim->GetUserID() );
|
||
|
event->SetInt( "target", pKiller->GetUserID() );
|
||
|
duel.m_usScoreTarget += iScoreIncrement;
|
||
|
}
|
||
|
event->SetInt( "initiator_score", duel.m_usScoreInitiator );
|
||
|
event->SetInt( "target_score", duel.m_usScoreTarget );
|
||
|
gameeventmanager->FireEvent( event );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Duel request
|
||
|
*/
|
||
|
class CGC_GameServer_Duel_Request : public GCSDK::CGCClientJob
|
||
|
{
|
||
|
public:
|
||
|
CGC_GameServer_Duel_Request( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||
|
|
||
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGC_Duel_Request_t> msg( pNetPacket );
|
||
|
SpeakConceptBySteamID( msg.Body().m_ulInitiatorSteamID, MP_CONCEPT_DUEL_REQUEST, msg.Body().m_ulInitiatorSteamID, msg.Body().m_ulTargetSteamID );
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_Duel_Request, "CGC_GameServer_Duel_Request", k_EMsgGC_Duel_Request, GCSDK::k_EServerTypeGCClient );
|
||
|
|
||
|
/**
|
||
|
* Duel response
|
||
|
*/
|
||
|
class CGC_GameServer_Duel_Response : public GCSDK::CGCClientJob
|
||
|
{
|
||
|
public:
|
||
|
CGC_GameServer_Duel_Response( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||
|
|
||
|
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGC_Duel_Response_t> msg( pNetPacket );
|
||
|
|
||
|
// make sure we're still allowed to start a duel because the
|
||
|
// game state may have changed since the duel request was sent
|
||
|
if ( TFGameRules() && !TFGameRules()->CanInitiateDuels() )
|
||
|
{
|
||
|
// if they accepted the duel somehow, we need to cancel it with the GC
|
||
|
if ( msg.Body().m_bAccepted )
|
||
|
{
|
||
|
GCSDK::CGCMsg<MsgGC_Duel_Results_t> msgDuelResults( k_EMsgGC_Duel_Results );
|
||
|
msgDuelResults.Body().m_ulInitiatorSteamID = msg.Body().m_ulInitiatorSteamID;
|
||
|
msgDuelResults.Body().m_ulTargetSteamID = msg.Body().m_ulTargetSteamID;
|
||
|
msgDuelResults.Body().m_ulWinnerSteamID = 0;
|
||
|
msgDuelResults.Body().m_usScoreInitiator = 0;
|
||
|
msgDuelResults.Body().m_usScoreTarget = 0;
|
||
|
msgDuelResults.Body().m_usEndReason = kDuelEndReason_Cancelled;
|
||
|
GCClientSystem()->BSendMessage( msgDuelResults );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// duel was rejected
|
||
|
if ( msg.Body().m_bAccepted == false )
|
||
|
{
|
||
|
SpeakConceptBySteamID( msg.Body().m_ulInitiatorSteamID, MP_CONCEPT_DUEL_REJECTED, msg.Body().m_ulInitiatorSteamID, msg.Body().m_ulTargetSteamID );
|
||
|
SpeakConceptBySteamID( msg.Body().m_ulTargetSteamID, MP_CONCEPT_DUEL_TARGET_REJECT, msg.Body().m_ulInitiatorSteamID, msg.Body().m_ulTargetSteamID );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// duel was accepted
|
||
|
SpeakConceptBySteamID( msg.Body().m_ulInitiatorSteamID, MP_CONCEPT_DUEL_ACCEPTED, msg.Body().m_ulInitiatorSteamID, msg.Body().m_ulTargetSteamID );
|
||
|
SpeakConceptBySteamID( msg.Body().m_ulTargetSteamID, MP_CONCEPT_DUEL_TARGET_ACCEPT, msg.Body().m_ulInitiatorSteamID, msg.Body().m_ulTargetSteamID );
|
||
|
int idx = g_duels.AddToTail();
|
||
|
duel_minigame_data_t &duel = g_duels[idx];
|
||
|
memset( &duel, 0, sizeof(duel) );
|
||
|
duel.m_steamIDInitiator = msg.Body().m_ulInitiatorSteamID;
|
||
|
duel.m_steamIDTarget = msg.Body().m_ulTargetSteamID;
|
||
|
duel.m_iPlayerClass = msg.Body().m_usAsPlayerClass;
|
||
|
|
||
|
if ( duel.m_iPlayerClass >= TF_FIRST_NORMAL_CLASS && duel.m_iPlayerClass < TF_LAST_NORMAL_CLASS )
|
||
|
{
|
||
|
CTFPlayer *pPlayer_Initiator = ToTFPlayer( GetPlayerBySteamID( msg.Body().m_ulInitiatorSteamID ) );
|
||
|
CTFPlayer *pPlayer_Target = ToTFPlayer( GetPlayerBySteamID( msg.Body().m_ulTargetSteamID ) );
|
||
|
if ( pPlayer_Initiator && ( pPlayer_Initiator->GetPlayerClass() == NULL || pPlayer_Initiator->GetPlayerClass()->GetClassIndex() != duel.m_iPlayerClass ) )
|
||
|
{
|
||
|
pPlayer_Initiator->SetDesiredPlayerClassIndex( duel.m_iPlayerClass );
|
||
|
pPlayer_Initiator->ForceRespawn();
|
||
|
}
|
||
|
if ( pPlayer_Target && ( pPlayer_Target->GetPlayerClass() == NULL || pPlayer_Target->GetPlayerClass()->GetClassIndex() != duel.m_iPlayerClass ) )
|
||
|
{
|
||
|
pPlayer_Target->SetDesiredPlayerClassIndex( duel.m_iPlayerClass );
|
||
|
pPlayer_Target->ForceRespawn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_Duel_Response, "CGC_GameServer_Duel_Response", k_EMsgGC_Duel_Response, GCSDK::k_EServerTypeGCClient );
|
||
|
|
||
|
bool DuelMiniGame_IsInDuel( CTFPlayer *pPlayer )
|
||
|
{
|
||
|
CSteamID steamID;
|
||
|
if ( pPlayer->GetSteamID( &steamID ) == false )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
duel_minigame_data_t *pDuel = FindDuelBySteamID( steamID );
|
||
|
return pDuel != NULL;
|
||
|
}
|
||
|
|
||
|
int DuelMiniGame_GetRequiredPlayerClass( CTFPlayer *pPlayer )
|
||
|
{
|
||
|
CSteamID steamID;
|
||
|
if ( pPlayer->GetSteamID( &steamID ) == false )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
duel_minigame_data_t *pDuel = FindDuelBySteamID( steamID );
|
||
|
return pDuel != NULL ? pDuel->m_iPlayerClass : TF_CLASS_UNDEFINED;
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_NotifyKill( CTFPlayer *pKiller, CTFPlayer *pVictim )
|
||
|
{
|
||
|
UpdateDuelScore( pKiller, pVictim, kDuelScoreType_Kill );
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_NotifyAssist( CTFPlayer *pAssister, CTFPlayer *pVictim )
|
||
|
{
|
||
|
UpdateDuelScore( pAssister, pVictim, kDuelScoreType_Assist );
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_NotifyPlayerChangedTeam( CTFPlayer *pPlayer, int iNewTeam, bool bInitiatedByPlayer )
|
||
|
{
|
||
|
CSteamID steamIDPlayerWhoChangedTeams;
|
||
|
if ( pPlayer->GetSteamID( &steamIDPlayerWhoChangedTeams ) == false )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
duel_minigame_data_t *pDuel = FindDuelBySteamID( steamIDPlayerWhoChangedTeams );
|
||
|
if ( pDuel == NULL )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
CSteamID steamIDOpponent;
|
||
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||
|
{
|
||
|
CTFPlayer *pOpponent = ToTFPlayer( UTIL_PlayerByIndex( i ) );
|
||
|
if ( pOpponent == NULL || pOpponent == pPlayer )
|
||
|
continue;
|
||
|
if ( pOpponent->GetSteamID( &steamIDOpponent ) == false )
|
||
|
continue;
|
||
|
if ( steamIDOpponent == pDuel->m_steamIDInitiator || steamIDOpponent == pDuel->m_steamIDTarget )
|
||
|
{
|
||
|
// player is disconnecting?
|
||
|
if ( iNewTeam == TEAM_UNASSIGNED )
|
||
|
{
|
||
|
SendDuelResults( *pDuel, steamIDOpponent, kDuelEndReason_PlayerDisconnected );
|
||
|
RemoveDuel( pDuel );
|
||
|
}
|
||
|
// found the opponent, if they are on the same team or the player is on spectator...
|
||
|
else if ( iNewTeam == TEAM_SPECTATOR ||
|
||
|
iNewTeam == pOpponent->GetTeamNumber() )
|
||
|
{
|
||
|
SendDuelResults( *pDuel, steamIDOpponent, bInitiatedByPlayer ? kDuelEndReason_PlayerSwappedTeams : kDuelEndReason_PlayerForceSwappedTeams );
|
||
|
RemoveDuel( pDuel );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_NotifyPlayerDisconnect( CTFPlayer *pPlayer, bool bKicked )
|
||
|
{
|
||
|
CSteamID steamIDPlayerWhoChangedTeams;
|
||
|
if ( pPlayer->GetSteamID( &steamIDPlayerWhoChangedTeams ) == false )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
duel_minigame_data_t *pDuel = FindDuelBySteamID( steamIDPlayerWhoChangedTeams );
|
||
|
if ( pDuel == NULL )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
CSteamID steamIDOpponent;
|
||
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||
|
{
|
||
|
CTFPlayer *pOpponent = ToTFPlayer( UTIL_PlayerByIndex( i ) );
|
||
|
if ( pOpponent == NULL || pOpponent == pPlayer )
|
||
|
continue;
|
||
|
if ( pOpponent->GetSteamID( &steamIDOpponent ) == false )
|
||
|
continue;
|
||
|
if ( steamIDOpponent == pDuel->m_steamIDInitiator || steamIDOpponent == pDuel->m_steamIDTarget )
|
||
|
{
|
||
|
SendDuelResults( *pDuel, steamIDOpponent, bKicked ? kDuelEndReason_PlayerKicked : kDuelEndReason_PlayerDisconnected );
|
||
|
RemoveDuel( pDuel );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_AssignWinners()
|
||
|
{
|
||
|
// send a message to the GC for each duel and remove the duels afterwards
|
||
|
FOR_EACH_VEC( g_duels, i )
|
||
|
{
|
||
|
duel_minigame_data_t &duel = g_duels[i];
|
||
|
if ( duel.m_usScoreInitiator == 0 && duel.m_usScoreTarget == 0 )
|
||
|
{
|
||
|
SendDuelResults( duel, duel.m_usScoreInitiator > duel.m_usScoreTarget ? duel.m_steamIDInitiator : duel.m_steamIDTarget, kDuelEndReason_ScoreTiedAtZero );
|
||
|
}
|
||
|
else if ( duel.m_usScoreInitiator == duel.m_usScoreTarget )
|
||
|
{
|
||
|
SendDuelResults( duel, duel.m_usScoreInitiator > duel.m_usScoreTarget ? duel.m_steamIDInitiator : duel.m_steamIDTarget, kDuelEndReason_ScoreTied );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SendDuelResults( duel, duel.m_usScoreInitiator > duel.m_usScoreTarget ? duel.m_steamIDInitiator : duel.m_steamIDTarget, kDuelEndReason_DuelOver );
|
||
|
}
|
||
|
}
|
||
|
g_duels.RemoveAll();
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_Stop()
|
||
|
{
|
||
|
DuelMiniGame_AssignWinners();
|
||
|
}
|
||
|
|
||
|
void DuelMiniGame_LevelShutdown()
|
||
|
{
|
||
|
// send a message to the GC for each duel and remove the duels afterwards
|
||
|
FOR_EACH_VEC( g_duels, i )
|
||
|
{
|
||
|
duel_minigame_data_t &duel = g_duels[i];
|
||
|
SendDuelResults( duel, duel.m_usScoreInitiator > duel.m_usScoreTarget ? duel.m_steamIDInitiator : duel.m_steamIDTarget, kDuelEndReason_LevelShutdown );
|
||
|
}
|
||
|
g_duels.RemoveAll();
|
||
|
}
|