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