Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

373 lines
13 KiB

//========= 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();
}