Files
Sun1602/Server/GameServer/PVPManager.cpp
T
2022-10-26 12:25:11 +08:00

679 lines
18 KiB
C++

#include "stdafx.h"
#include ".\PVPManager.h"
#include <PacketStruct_AG.h>
#include ".\Player.h"
#include ".\PVPInfo.h"
// _NA_0_20091118_GAMEROOM_BATTLE_RESULT
#include "PVPBattleResult.h"
#include "GamePVPRoom.h"
//==================================================================================================
// max wait interval until key press event after enter signal
const DWORD PVPManager::kWaitIntervalUntilKeyPressAfterEntered = 2 * 60 * 1000;
PVPManager::PVPManager(void)
: pvp_room_(NULL)
, inter_mode_(eInterMode_None)
, additional_pvp_info_(NULL)
{
pvp_player_infos_.Clear();
}
PVPManager::~PVPManager(void)
{
}
void
PVPManager::Release()
{
Init(NULL, NULL);
}
void
PVPManager::Init(ADDITIONAL_PVPINFO* additional_pvp_info, GamePVPRoom* pvp_room)
{
pvp_room_ = pvp_room;
additional_pvp_info_ = additional_pvp_info;
inter_mode_ = this->eInterMode_None;
pvp_player_infos_.Clear();
update_timer_.Stop();
}
void
PVPManager::StartPVP()
{
update_timer_.SetTimer(kDefaultTimerForUpdater);//1 * 1000
inter_mode_ = eInterMode_WaitKeyPressEvent;
mode_timer_.SetTimer(kWaitIntervalUntilKeyPressAfterEntered, true, false);
DISPMSG("PVP开始\n");
//_NA_0_20091118_GAMEROOM_BATTLE_RESULT
GameBattleResult* pResult = pvp_room_->GetGameBattleResult();
if (pResult)
{
pResult->Clear();
}
}
bool
PVPManager::IsOpponentDieAll()
{
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_)
{
continue;
}
assert(pvp_node->player_ != NULL);
const PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
if (pvp_info.GetPVPState() != ePVP_USER_DIE)
{
return false;
}
}
return true;
}
bool
PVPManager::IsDrawCheck(Player* player)
{
const PVPInfo& pvp_info = player->GetPVPInfo();
if (pvp_info.GetPVPState() == ePVP_USER_ENTERED) {
return true;
}
return false;
}
void
PVPManager::EnterPlayer(Player* player)
{
if (inter_mode_ == this->eInterMode_WaitClose)
{
return;
}
const DWORD player_key = player->GetObjectKey();
const int find_index = pvp_player_infos_.FindPvPControlIndex(player_key);
if (find_index == pvp_player_infos_.kNotFoundIndex)
{
SUNLOG(
eCRITICAL_LOG,
__FUNCTION__" : Can't find reserved pvp member : room key : %d, user key : %d",
pvp_room_->GetKey(),
player->GetUserKey()
);
return;
}
PvPInfoContainer::Node* const pvp_node = &pvp_player_infos_.nodes_[find_index];
pvp_node->ChangeToEntered(player);
PVPInfo& pvp_info = player->GetPVPInfo();
pvp_info.Clear();
pvp_info.SetPVPState(ePVP_USER_ENTERED);
pvp_info.SetTeam(pvp_node->team_no_);
MSG_CG_PVP_INFO_CMD msg_info_cmd;
pvp_player_infos_.MakeMessage(&msg_info_cmd, pvp_node);
player->SendPacket(&msg_info_cmd, msg_info_cmd.GetSize());
MSG_CG_PVP_INFO_BRD bmsg;
bmsg.m_dwPlayerKey = player_key;
bmsg.m_Team = pvp_node->team_no_;
SendToAll(&bmsg, sizeof(bmsg), player_key);
DISPMSG("玩家进入PVP\n");
}
void
PVPManager::LeavePlayer(Player* player)
{
const DWORD player_key = player->GetObjectKey();
const int find_index = pvp_player_infos_.FindPvPControlIndex(player_key);
if (find_index == pvp_player_infos_.kNotFoundIndex)
{
SUNLOG(
eCRITICAL_LOG,
__FUNCTION__" : Can't find pvp member : room key : %d, user key : %d",
pvp_room_->GetKey(), player->GetUserKey());
return;
}
PvPInfoContainer::Node* const pvp_node = &pvp_player_infos_.nodes_[find_index];
pvp_node->ChangeToClosed();
PVPInfo& pvp_info = player->GetPVPInfo();
//?? ? ?? ?? ??? ??? ?? Draw??, Die?? ??
if (pvp_info.GetPVPState() == ePVP_USER_MATCH)
{
if (!IsOpponentDieAll())
{
pvp_info.IncDie();
}
}
else if (IsDrawCheck(player))
{
pvp_info.IncDraw();
}
//??? ??
pvp_info.CalcPoint();
pvp_info.SetPVPState(ePVP_USER_NONE);
DISPMSG("PVP玩家退出\n");
}
RC::ePVP_RESULT
PVPManager::PressKeyPlayer(Player* key_pressed_player)
{
if (inter_mode_ == this->eInterMode_WaitClose)
{
return RC::RC_PVP_INVALID_USER_STATE;
}
PVPInfo& pvp_info = key_pressed_player->GetPVPInfo();
if (pvp_info.GetPVPState() != ePVP_USER_ENTERED)
{
return RC::RC_PVP_INVALID_USER_STATE;
}
pvp_info.SetPVPState(ePVP_USER_KEYPRESSED);
DISPMSG("PVP玩家Key\n");
return RC::RC_PVP_SUCCESS;
}
bool
PVPManager::CanChangeClearForActionMode()
{
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
//need separated condition check \
//so that the current state is processed on a detached update.
//if the entered_ is equal to false, will be expelled call by UpdateWaitPressKeyTimeoutEvent
if (pvp_node->closed_)
{
continue;
}
if (pvp_node->entered_ == false)
{
return false;
}
assert(pvp_node->player_ != NULL);
const PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
if (pvp_info.GetPVPState() != ePVP_USER_KEYPRESSED)
{
return false;
}
}
return true;
}
void
PVPManager::UpdateWaitPressKeyTimeoutEvent()
{
MSG_CG_PVP_END_BRD send_msg;
MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = \
MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase();
msg_brd.Init(&send_msg, sizeof(send_msg));
uint16_t* const user_begin = msg_brd.GetUsersStartPtr();
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_)
{
pvp_node->ChangeToClosed();
continue;
}
assert(pvp_node->player_ != NULL);
const PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
if (pvp_info.GetPVPState() == ePVP_USER_KEYPRESSED)
{
continue;
};
DWORD check_bound = msg_brd.AddUser(user_begin, pvp_node->player_key_);
__TOUCHED(check_bound);
}
if (msg_brd.number_of_users)
{
g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize());
}
}
void
PVPManager::UpdateEachNodeUpdateEvent()
{
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->closed_ != false)
{
continue;
}
assert(pvp_node->player_ != NULL);
Player* player = pvp_node->player_;
PVPInfo& pvp_info = player->GetPVPInfo();
if (pvp_info.IsExpired())
{
ePVP_USER_STATE pvp_user_state = pvp_info.GetPVPState();
switch (pvp_user_state)
{
case ePVP_USER_MATCHLESS: //case of change 'clear for action' to 'go in battle'
OnStartMatchMode(player);
OnStartMatchModeBRD(player);
break;
case ePVP_USER_DIE:
OnRevival(player); //resurrection
OnStartMatchlessMode(player); //start 'clear for action'
break;
default:
assert(!"意想不到的pvp状态");
break;
}
}
}
}
void
PVPManager::Update(DWORD delta_tick)
{
if (inter_mode_ == this->eInterMode_WaitClose) {
return;
}
bool need_update = update_timer_.IsExpired() != false;
if (need_update == false) {
return;
}
switch (inter_mode_)
{
case this->eInterMode_WaitKeyPressEvent:
{
bool need_to_change = mode_timer_.IsExpired() != false; // wait timeout in a room
if (need_to_change) {
UpdateWaitPressKeyTimeoutEvent();
return;
}
need_to_change = CanChangeClearForActionMode();
if (need_to_change) {
StartClearForActionModeAll();
}
}
break;
case this->eInterMode_ClearForAction:
{
bool need_to_change = mode_timer_.IsExpired() != false;
if (need_to_change) {
inter_mode_ = this->eInterMode_ClearForActionBusyCheck;
// no timer // mode_timer_.Configure(mode_timer_.LOOP, ...);
}
if (need_to_change == false) {
break;
}
}
// (NOTE) no break, same as eInterMode_ClearForActionBusyCheck, except timer
case this->eInterMode_ClearForActionBusyCheck:
if (IsContinuePVP()) {
StartGoIntoBattleModeAll();
}
else {
// no control, custom logic has active event \
// which is automatic leave process by the client
ChangeToWaitCloseMode();
}
break;
case this->eInterMode_GoIntoBattle:
// no control, change to each node update logic
UpdateEachNodeUpdateEvent();
break;
case this->eInterMode_WaitClose:
// no control, custom logic has active event \
// which is automatic leave process by the client
break;
}
}
// ?? ???
void
PVPManager::StartClearForActionModeAll()
{
inter_mode_ = this->eInterMode_ClearForAction;
mode_timer_.SetTimer(PVP_MATCHLESSS_DELAY_TIME, true, false);
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
pvp_info.StopTimer();
pvp_info.SetPVPState(ePVP_USER_MATCHLESS);
DISPMSG("PVP[%u] : MSG_CG_PVP_MATCHLESS_MODE_START_CMD\n", pvp_node->player_key_);
}
MSG_CG_PVP_MATCHLESS_MODE_START_CMD msg_cmd;
SendToAll(&msg_cmd, sizeof(msg_cmd), 0);
}
// ?? ???
void
PVPManager::StartGoIntoBattleModeAll()
{
inter_mode_ = this->eInterMode_GoIntoBattle;
mode_timer_.Stop();
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
pvp_info.StopTimer();
pvp_info.SetPVPState(ePVP_USER_MATCH);
DISPMSG("PVP[%u] : MSG_CG_PVP_MATCH_MODE_FIRST_START_CMD\n", pvp_node->player_->GetObjectKey());
GAMELOG->WritePVPStart(pvp_node->player_);
}
MSG_CG_PVP_MATCH_MODE_FIRST_START_CMD msg_cmd;
SendToAll(&msg_cmd, sizeof(msg_cmd), 0);
}
// ??? ?? ???
void
PVPManager::OnStartMatchlessMode(Player* player)
{
PVPInfo& pvp_info = player->GetPVPInfo();
pvp_info.StartTimer(PVP_MATCHLESSS_DELAY_TIME);
pvp_info.SetPVPState(ePVP_USER_MATCHLESS);
MSG_CG_PVP_MATCHLESS_MODE_START_CMD msg;
player->SendPacket(&msg, sizeof(msg));
DISPMSG("PVP[%u] : MSG_CG_PVP_MATCHLESS_MODE_START_CMD\n", player->GetObjectKey());
}
// ??? ?? ???
void
PVPManager::OnStartMatchMode(Player* player)
{
PVPInfo& pvp_info = player->GetPVPInfo();
pvp_info.StopTimer();
pvp_info.SetPVPState(ePVP_USER_MATCH);
MSG_CG_PVP_MATCH_MODE_START_CMD msg;
player->SendPacket(&msg, sizeof(msg));
DISPMSG("PVP[%u] : MSG_CG_PVP_MATCH_MODE_START_CMD\n", player->GetObjectKey());
}
void
PVPManager::OnStartMatchModeBRD(Player* except_player)
{
MSG_CG_PVP_MATCH_MODE_START_BRD msg;
msg.m_dwPlayerKey = except_player->GetObjectKey();
SendToAll(&msg, sizeof(msg), except_player->GetObjectKey());
DISPMSG("MSG_CG_PVP_MATCH_MODE_START_BRD\n");
}
void
PVPManager::OnRevival(Player* player)
{
if (player->IsDead()) {
player->OnResurrection(0, 1.f, 1.f);
}
}
// (NOTE) old comment '@@@ OPT ???', no means
// this function must be called in a 'clear for action' state
bool
PVPManager::IsContinuePVP()
{
int team1 = 0, team2 = 0;
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
pvp_node->team_no_ == 1 ? ++team1 : ++team2;
}
if (additional_pvp_info_->m_Mode == ePVP_TEAM_MODE)
{ // determine end condition whether any team is empty
if (team1 == 0 || team2 == 0) {
return false;
}
}
else
{ // over than 2, use field 'team2'
if (team2 <= 1) {
return false;
}
}
return true;
}
bool
PVPManager::IsEndPVP()
{
int max_kill = 0;
// ???
switch (additional_pvp_info_->m_Rule)
{
#if !defined(_GLOBAL) || !defined(_RUSSIA)
case ePVP_RULE_10: max_kill = 10; break;
case ePVP_RULE_30: max_kill = 30; break;
#else
case ePVP_RULE_10: max_kill = 1; break;
case ePVP_RULE_30: max_kill = 10; break;
#endif
case ePVP_RULE_INFINITY:
return false;
}
if (additional_pvp_info_->m_Mode == ePVP_TEAM_MODE)
{
int sum_of_team_score1 = 0, sum_of_team_score2 = 0;
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
const PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
int number_of_kills = pvp_info.GetTotalKillNum();
if (pvp_node->team_no_ == 1) {
sum_of_team_score1 += number_of_kills;
}
else {
sum_of_team_score2 += number_of_kills;
}
if (max_kill <= sum_of_team_score1 || max_kill <= sum_of_team_score2) {
return true;
}
}
DISPMSG("Max:%u,Cur1:%u,Cur2:%u\n", max_kill, sum_of_team_score1, sum_of_team_score2);
}
else
{
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
const PVPInfo& pvp_info = pvp_node->player_->GetPVPInfo();
int number_of_kills = pvp_info.GetTotalKillNum();
if (max_kill <= number_of_kills) {
return true;
}
else {
DISPMSG("Max:%u,Cur:%u\n", max_kill, number_of_kills);
}
}
}
return false;
}
// TODO(old comment)
// 1. ??? ? ?? ??
// 2. ??? ??? ? ???
void
PVPManager::OnPVPResult(Character* killer_character, Player* died_player)
{
// ??? ???.
Player* killer = NULL;
if (killer_character && (killer = killer_character->GetPlayerOwner()) != NULL) {
killer = pvp_player_infos_.FindPvPPlayer(killer->GetObjectKey());
}
if (inter_mode_ == this->eInterMode_WaitClose) {
return OnRevival(died_player);
}
if (killer)
{
// 会增加
PVPInfo& killer_pvp_info = killer->GetPVPInfo();
killer_pvp_info.IncKill();
DWORD score = killer_pvp_info.CalcScore(killer->GetLevel(), died_player->GetLevel());
//
MSG_CG_PVP_SCORE_CMD msg_score;
msg_score.m_Score = score;
killer->SendPacket(&msg_score, sizeof(msg_score));
// 捕获箱+周边扩展
MSG_CG_PVP_RESULT_BRD msg_result;
msg_result.m_KillerKey = killer->GetObjectKey();
msg_result.m_DierKey = died_player->GetObjectKey();
SendToAll(&msg_result, sizeof(msg_result));
}
// die可以增加
PVPInfo& died_pvp_info = died_player->GetPVPInfo();
died_pvp_info.IncDie();
died_pvp_info.SetPVPState(ePVP_USER_DIE);
died_pvp_info.StartTimer(PVP_REVIVAL_DELAY_TIME);
// PVP是否退出
if (IsEndPVP())
{
ChangeToWaitCloseMode();
MSG_CG_PVP_END_BRD msg_end;
SendToAll(&msg_end, sizeof(msg_end));
DISPMSG("END_PVP\n");
}
//PVP结果日志
if (killer)
{
GAMELOG->WritePVPKillUser(killer, died_player, true);
GAMELOG->WritePVPKillUser(died_player, killer, false);
}
}
void
PVPManager::SendToAll(MSG_BASE_FORWARD* send_msg, WORD size, DWORD except_object_key)
{
MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = \
MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase();
msg_brd.Init(send_msg, size);
uint16_t* const user_begin = msg_brd.GetUsersStartPtr();
// no bound check, because of a limits of the pvp room is MAX_PARTYNUM
const bool skip_check_key = except_object_key == 0;
const int number_of_members = pvp_player_infos_.number_of_members_;
for (int index = 0; index < number_of_members; ++index)
{
const PvPInfoContainer::Node* pvp_node = &pvp_player_infos_.nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
if ((skip_check_key || except_object_key != pvp_node->player_key_) == false) {
continue;
}
DWORD check_bound = msg_brd.AddUser(user_begin, pvp_node->player_key_);
__TOUCHED(check_bound);
}
if (msg_brd.number_of_users) {
g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize());
}
}
//==================================================================================================
static void PvPRuleCompileTimeChecker()
{ // UNUSED, only test function
MSG_AG_ZONE_PVP_INFO_CMD msg_cmd;
PvPInfoContainer pvp_player_info = { 0, };
BOOST_STATIC_ASSERT(_countof(msg_cmd.m_Info) == _countof(pvp_player_info.nodes_) &&
_countof(msg_cmd.m_Info) == pvp_player_info.kNotFoundIndex &&
pvp_player_info.kNotFoundIndex == MAX_PARTYMEMBER_NUM);
};
bool
PvPInfoContainer::AddPvPControl(DWORD player_key, BYTE team_no)
{
int find_index = FindPvPControlIndex(player_key);
if (find_index != kNotFoundIndex)
{
return false;
}
Node* node = &nodes_[number_of_members_];
node->player_key_ = player_key;
node->team_no_ = team_no;
++number_of_members_;
return true;
}
// make other player info message for the enter player
bool
PvPInfoContainer::MakeMessage(MSG_CG_PVP_INFO_CMD* msg_cmd, const Node* enter_player_node)
{
// make broadcast all member information message with my info
// 给我发送队员信息.
msg_cmd->m_Team = enter_player_node->team_no_;
msg_cmd->m_Count = 0;
BOOST_STATIC_ASSERT(_countof(nodes_) == _countof(msg_cmd->m_Info));
for (int index = 0, result_index = 0; index < number_of_members_; ++index)
{
const Node* pvp_node = &nodes_[index];
if (pvp_node->entered_ == false || pvp_node->closed_) {
continue;
}
assert(pvp_node->player_ != NULL);
MSG_CG_PVP_INFO_CMD::PVP_TEAM_INFO* team_info = &msg_cmd->m_Info[msg_cmd->m_Count];
team_info->m_dwPlayerKey = pvp_node->player_key_;
team_info->m_Team = pvp_node->team_no_;
++msg_cmd->m_Count;
}
return true;
};