diff options
Diffstat (limited to 'game/server/tf/minigames')
| -rw-r--r-- | game/server/tf/minigames/tf_duel.cpp | 373 | ||||
| -rw-r--r-- | game/server/tf/minigames/tf_duel.h | 23 |
2 files changed, 396 insertions, 0 deletions
diff --git a/game/server/tf/minigames/tf_duel.cpp b/game/server/tf/minigames/tf_duel.cpp new file mode 100644 index 0000000..a3fed6e --- /dev/null +++ b/game/server/tf/minigames/tf_duel.cpp @@ -0,0 +1,373 @@ +//========= 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(); +} diff --git a/game/server/tf/minigames/tf_duel.h b/game/server/tf/minigames/tf_duel.h new file mode 100644 index 0000000..f3ea7b8 --- /dev/null +++ b/game/server/tf/minigames/tf_duel.h @@ -0,0 +1,23 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Duel MiniGame +// +// $NoKeywords: $ +//============================================================================= + +#ifndef TF_DUEL_H +#define TF_DUEL_H + +class CTFPlayer; + +bool DuelMiniGame_IsInDuel( CTFPlayer *pPlayer ); +int DuelMiniGame_GetRequiredPlayerClass( CTFPlayer *pPlayer ); +void DuelMiniGame_NotifyKill( CTFPlayer *pKiller, CTFPlayer *pVictim ); +void DuelMiniGame_NotifyAssist( CTFPlayer *pAssister, CTFPlayer *pVictim ); +void DuelMiniGame_NotifyPlayerChangedTeam( CTFPlayer *pPlayer, int iNewTeam, bool bInitiatedByPlayer ); +void DuelMiniGame_NotifyPlayerDisconnect( CTFPlayer *pPlayer, bool bKicked = false ); +void DuelMiniGame_AssignWinners(); +void DuelMiniGame_Stop(); +void DuelMiniGame_LevelShutdown(); + +#endif // TF_DUEL_H |