Files
Sun1602/Server/AgentServer/Zone/RoomInterface.cpp
T
2022-10-26 12:25:11 +08:00

1004 lines
31 KiB
C++

#include "stdafx.h"
//////////////////////////////////////////////////////////////////////////
#include <PacketStruct_AG.h>
#include <PacketStruct_CG.h>
#include <MapInfoParser.h>
#include <MapPartialLoadingParser.h>
#include <AgentParty/AgentPartyManager.h>
#include <AgentParty/AgentParty.h>
#include <Zone/RoomInterface.h>
#include <Zone/ZoneManager.h>
#include <Zone/LobbyRoom.h>
#include <Zone/PortalManager.h>
#include "BattleOpenTimeManager.h"
//_NA_20110630_BATTLE_ZONE_RENEWAL
#include "Party/PartyRoom.h"
RoomInterface::RoomInterface(void)
: m_pMasterUser(NULL)
{
SetRoomState(eROOMSTATE_DESTROY);
m_RelayUserHash.Initialize(MAX_PARTYMEMBER_NUM*2);
}
RoomInterface::~RoomInterface(void)
{
}
void
RoomInterface::ChangePolicy(PolicymentForRoom::POLICY_T newPolicySet)
{
RoomPolicyment.SetField(newPolicySet);
}
//void
//RoomInterface::SetMapCode(MAPCODE code)
//{
// m_MapCode = code;
//}
void
RoomInterface::On_create()
{
m_DestroyTimeout.SetTimer(MAX_DESTROY_TIMEOUT_TIME);
m_MasterEnterTimeout.SetTimer(MAX_DESTROY_TIMEOUT_TIME);
SetRoomState(eROOMSTATE_READY);
m_NumberOfExpectedPlayers = 0; //__NA000000_090622_MOB_ABILITY_CONTROL_CHANGE__
}
void
RoomInterface::On_destroy()
{
SetRoomState(eROOMSTATE_DESTROY);
//_NA_20110630_BATTLE_ZONE_RENEWAL
AgentPartyManager* const party_manager = AgentPartyManager::Instance();
const AgentParty* party =
static_cast<const AgentParty*>(party_manager->FindParty(GetLinkParty()));
if (party)
{
if (party->GetType() == ePARTY_ROOM)
{
party_manager->SetIsMissionStart(GetLinkParty(), false);
}
}
VestUserWithMaster(NULL);
util::SolarHashTable<User*>& relay_user = GetRelayHash();
util::SolarHashTable<User*>::iterator it = relay_user.begin(),
end = relay_user.end();
while (it != end)
{
it = relay_user.erase(it);
};
m_RelayUserHash.RemoveAll();
RoomPolicyment.Clear();
}
void
RoomInterface::On_readyuser(User* pUser)
{
// 릴레이 유저 삭제
RemoveRelayUser(pUser->GetPlayerKey());
//SetRoomState(eROOMSTATE_READY);
}
void
RoomInterface::On_enteruser(User* pUser)
{
ZoneInterface::On_enteruser(pUser);
m_DestroyTimeout.DisableCheckTime();
if(GetMaster() == pUser)
m_MasterEnterTimeout.DisableCheckTime();
SetRoomState(eROOMSTATE_GENERNAL);
// 방에 들어올때 파티관련 처리를 한다.
AgentPartyManager::Instance()->EnterZone(pUser, this);
PortalManager::OnEnteredRoom(pUser, this);
}
void
RoomInterface::On_leaveuser(User* pUser)
{
AgentPartyManager * const party_manager = AgentPartyManager::Instance();
//_NA_20110630_BATTLE_ZONE_RENEWAL
WORD party_key = pUser->GetPartyState().GetPartyKey();
if (pUser->GetPartyState().IsPartyMember())
{
BaseParty* party = const_cast<BaseParty*>(party_manager->FindParty(party_key));
if (party)
{
if (party->GetType() == ePARTY_ROOM)
{
if (GetUserNum() == 0)
{
SetRoomState(eROOMSTATE_DESTROY);
}
}
else
{
if (GetUserNum() == 0)
{
SetRoomState(eROOMSTATE_DESTROY);
}
else if(GetMaster() == pUser)
{
VestUserWithMaster(GetFirstUser());
}
}
party_manager->LeaveZone(pUser, this);
}
else
{
if (GetUserNum() == 0)
{
SetRoomState(eROOMSTATE_DESTROY);
}
SUNLOG(eCRITICAL_LOG, __FUNCTION__" : Invalid party state : %d", party_key);
}
}
else
{
if (GetUserNum() == 0)
{
SetRoomState(eROOMSTATE_DESTROY);
}
}
}
void
RoomInterface::VestUserWithMaster(User* pExpectedMaster)
{
if(GetMaster())
GetMaster()->SetMaster(FALSE);
if(GetMaster() && pExpectedMaster)
{
// 방장 위임 절차
MSG_AG_ZONE_MASTER_CHANGED_CMD msg;
msg.m_dwKey = pExpectedMaster->GetUserKey();
msg.m_RoomKey = GetKey();
SendToLinkedServer(&msg, sizeof(msg));
MSG_CG_ZONE_MASTER_CHANGED_BRD bmsg;
bmsg.m_dwMasterPlayerKey = pExpectedMaster->GetPlayerKey();
SendToAll(&bmsg, sizeof(bmsg));
// 파티장도 바꾼다.
// 로비에서 헌팅, 미션룸으로 떠날 때도 방장이 바뀌고 있기 때문에 그 부분이 수정되기 전에는 주석을 막아둔다.
//PartyManager::Instance()->ChangeMaster(GetMaster()->GetPartyState().GetPartyKey(), GetMaster()->GetZoneKey(), pExpectedMaster->GetUserKey());
}
m_pMasterUser = pExpectedMaster;
if(pExpectedMaster)
{
pExpectedMaster->SetReady(FALSE);
pExpectedMaster->SetMaster(TRUE);
}
}
BOOL
RoomInterface::Process()
{
BOOL rt = m_DestroyTimeout.IsExpiredManual(FALSE);
if(GetRoomState() == eROOMSTATE_DESTROY || rt)
{
if(rt)
{
SUNLOG(eCRITICAL_LOG, "Room(%u,type:%u,code:%u) Destroy For Timeout",
GetKey(), GetType(), GetMapCode());
}
return FALSE;
}
;{
util::SolarHashTable<User*>& relay_user = GetRelayHash();
util::SolarHashTable<User *>::iterator it = relay_user.begin(),
end = relay_user.end();
while (it != end)
{
User* const user = (*it);
if (user->IsExpired())
{
it = relay_user.erase(it);
SUNLOG(eCRITICAL_LOG,
_T("User(U:%u,C:%u,room:%u,type:%u,code:%u) Expired For Timeout"),
user->GetUserGUID(), user->GetSelectedCharGuid(),
GetKey(), GetType(), GetMapCode());
continue;
};
++it;
};
};
if(m_MasterEnterTimeout.IsExpired())
{
if(GetUserNum() != 0)
VestUserWithMaster(GetFirstUser());
}
return TRUE;
}
void
RoomInterface::RelayUser(RoomInterface* pPreviousRoom)
{
const ZONE_USER_LIST& rList = pPreviousRoom->GetUserList();
FOREACH_CONTAINER(const ZONE_USER_LIST::value_type& rNode, rList, ZONE_USER_LIST)
{
User* const pUser = rNode.second;
pUser->StartTimer(MAX_RELAY_TIMEOUT_TIME);
AddRelayUser(pUser, pUser->GetPlayerKey());
}
//__NA000000_090622_MOB_ABILITY_CONTROL_CHANGE__
BYTE number_of_players = BYTE(m_RelayUserHash.GetDataNum());
SetNumberOfExpectedPlayers(number_of_players);
}
RC::eROOM_RESULT
RoomInterface::Migrate(KEYTYPE key, RoomInterface* pPreviousRoom, ServerSession* pServerSession)
{
SetKey(key);
SetMapCode(pPreviousRoom->GetMapCode());
const sMAPINFO* pInfo = MapInfoParser::Instance()->FindMapInfo(pPreviousRoom->GetMapCode());
ASSERT(pInfo);
if(pPreviousRoom->GetLimitMaxUserNum() > pInfo->byMaxUserNum)
SetLimitMaxUserNum(pInfo->byMaxUserNum);
else
SetLimitMaxUserNum(pPreviousRoom->GetLimitMaxUserNum());
SetZonePublic(pPreviousRoom->GetZonePublic());
SetRoomTitle(pPreviousRoom->GetRoomTitle());
SetRoomPWD(pPreviousRoom->GetRoomPWD());
SetRoomInfo(pPreviousRoom->GetRoomInfo());
VestUserWithMaster(pPreviousRoom->GetMaster());
SetLinkedServer(pServerSession);
// 초기화
SetReusedKey(FALSE);
SetReusedServerSession(FALSE);
return RC::RC_ROOM_SUCCESS;
}
void
RoomInterface::Create(User* user, KEYTYPE key, MAPCODE map_code, eZONEPUBLIC zone_public,
TCHAR* room_title, TCHAR* room_password, BASE_ROOMINFO& IN base_room_info,
ServerSession* server_session)
{
SetKey(key);
SetMapCode(map_code);
const sMAPINFO* map_info = GetMapInfo();
if (!FlowControl::FCAssert(map_info))
{
SUNLOG(eCRITICAL_LOG, __FUNCTION__" : Map info is null : %d", map_code);
return;
}
SetLimitMaxUserNum(map_info->byMaxUserNum);
// ZoneType == 13 && IndunKind == 6 인 자유전투실,전장은 제외한다. (최대인원이 20)
if ( (map_info->byMKind != eZONETYPE_INSTANCE && map_info->byMType != INDUN_KIND_CHAOS_ZONE) && map_info->byMaxUserNum > 10)
{
SUNLOG(eCRITICAL_LOG, __FUNCTION__" : User count is over : %d", map_info->byMaxUserNum);
}
SetZonePublic(zone_public);
SetRoomTitle(room_title);
SetRoomPWD(room_password);
SetRoomInfo(&base_room_info);
//_NA_20110630_BATTLE_ZONE_RENEWAL
if (user)
{
WORD party_key = user->GetPartyState().GetPartyKey();
const BaseParty* party = AgentPartyManager::Instance()->FindParty(party_key);
if (party)
{
if (party->GetType() != ePARTY_ROOM)
{
VestUserWithMaster(user);
}
}
}
SetLinkedServer(server_session);
// 초기화
SetReusedKey(FALSE);
SetReusedServerSession(FALSE);
}
//{__NA1279_1281_090209_TIMELINE_QUEST_BASED_MISSION__
RC::eROOM_RESULT
RoomInterface::MigratePortalUserFirstEnter(KEYTYPE key,
const RoomArgumentForRoomSetup& rRoomArgument,
User* pUser, ServerSession* pServerSession)
{
// (WAVERIX) (090217) (NOTE) 코드 중복되고 있다. 통일 시킬 필요 있다.
SetKey(key);
const sMAPINFO* pMapInfo = rRoomArgument.m_pMapInfo;
SetMapCode(pMapInfo->MapCode);
SetLimitMaxUserNum(pMapInfo->byMaxUserNum);
SetZonePublic(rRoomArgument.RoomExportLevel);
SetRoomTitle("portal");
SetRoomPWD(rRoomArgument.Password);
SetRoomInfo(const_cast<BASE_ROOMINFO*>(&rRoomArgument.m_RoomInfo));
VestUserWithMaster(pUser);
SetLinkedServer(pServerSession);
// 초기화
SetReusedKey(FALSE);
SetReusedServerSession(FALSE);
return RC::RC_ROOM_SUCCESS;
}
//}
RC::eROOM_RESULT
RoomInterface::CanCreate(User* pUser, MAPCODE mapCode, eZONETYPE eZoneType, eZONEPUBLIC ePublic,
BASE_ROOMINFO& IN baseRoomInfo, BOOL needCheckOpenTime)
{
if(GetRoomState() != eROOMSTATE_DESTROY)
return RC::RC_ROOM_INVALIDPREVLOBBY;
const sMAPINFO* pInfo = MapInfoParser::Instance()->FindMapInfo(mapCode);
if(pInfo == 0)
return RC::RC_ROOM_INVALID_MAPCODE;
if(pInfo->GetZoneType4SharedCode() != eZoneType)
return RC::RC_ROOM_INVALID_MAPCODE;
MAP_PARTIAL_LOADING* pLoadingInfo = 0;
{
pLoadingInfo = MapPartialLoadingParser::Instance()->FindMapInfo(pInfo->MapKind);
if(pLoadingInfo && !pLoadingInfo->m_bLoading)
return RC::RC_ROOM_INVALID_MAPCODE;
};
#ifdef __NA_000000_20070528_TAIWAN_GUILD_PVP
// PC방인지 체크
if(baseRoomInfo.IsSpecialType(eROOM_SPECIAL_PCBANG))
{
if(!pUser->CanApplyPCBangBenefit(TRUE))
return RC::RC_ROOM_ISNOT_PCBANG_USER;
}
// 슈퍼방장 아이템을 소유하고 있는지 체크
if(baseRoomInfo.IsSpecialType(eROOM_SPECIAL_ITEM))
{
if(!pUser->GetItemOwnFlag().FindItemInfo(eITEMTYPE_SUPERMASTER))
return RC::RC_ROOM_NOT_HAVE_SUPER_MASTER_ITEM;
}
#else
// PC방인지 체크
if(baseRoomInfo.m_SpecialType == eROOM_SPECIAL_PCBANG_SUPER_ROOM)
{
if(!pUser->CanApplyPCBangBenefit(TRUE))
return RC::RC_ROOM_ISNOT_PCBANG_USER;
}
// 슈퍼방장 아이템을 소유하고 있는지 체크
else if(baseRoomInfo.m_SpecialType == eROOM_SPECIAL_ITEM1_SUPER_ROOM)
{
if(!pUser->GetItemOwnFlag().FindItemInfo(eITEMTYPE_SUPERMASTER))
return RC::RC_ROOM_NOT_HAVE_SUPER_MASTER_ITEM;
}
#endif
// (WAVERIX) (090522) (CHANGES)
// 제대로 된 레벨값이 들어 왔는지 체크
BOOLEAN isValidLimitLevel = baseRoomInfo.m_MinLV <= baseRoomInfo.m_MaxLV &&
pInfo->minLV <= baseRoomInfo.m_MinLV && baseRoomInfo.m_MaxLV <= pInfo->maxLV;
if(isValidLimitLevel == 0)
return RC::RC_ROOM_INVALID_LIMITLEVEL;
LEVELTYPE level = pUser->GetSelectedCharLV();
isValidLimitLevel = baseRoomInfo.m_MinLV <= level &&
level <= baseRoomInfo.m_MaxLV;
if(isValidLimitLevel == 0)
return RC::RC_ROOM_INVALID_LIMITLEVEL;
// 제대로 된 클래스제한이 이루어 졌는지 체크
for(int i = 0; i < eCHAR_TYPE_MAX - 1; ++i)
{
if((pInfo->byClass & (eCLASS_BERSERKER << i)) == 0)
if(baseRoomInfo.m_ClassLimit & (eCLASS_BERSERKER << i))
return RC::RC_ROOM_INVALID_LIMITCLASS;
}
// 입장시 시간 체크를 한다
if(needCheckOpenTime && pInfo->wTimeLimit != 0)
{
if(!(g_BattleOpenTimeMng.IsOpenDungeon(pInfo->wTimeLimit)))
return RC::RC_ROOM_NOT_OPEN_TIME;
}
return RC::RC_ROOM_SUCCESS;
}
RC::eROOM_RESULT
RoomInterface::IsRecommendRoom(User* pUser)
{
if(GetRoomState() != eROOMSTATE_GENERNAL)
return RC::RC_ROOM_INVALIDPREVLOBBY;
RC::eROOM_RESULT rt = CanJoin(pUser, eZONEPUBLIC_PUBLIC, NULL);
if(RC::RC_ROOM_SUCCESS == rt)
{
if(RecommendLevel(pUser->GetSelectedCharLV()))
return RC::RC_ROOM_SUCCESS_FOR_RECOMMEND_JOIN;
else
return RC::RC_ROOM_SUCCESS_FOR_GENERAL_JOIN;
}
return rt;
}
BOOL
RoomInterface::CheckLimitUser(User* pUser)
{
// 최대인원 제한
#ifdef _NA_008334_20150608_SONNENSCHEIN
// 최대인원이 0명이면 인원제한없음
if (GetLimitMaxUserNum() == 0) {
return TRUE;
}
else if(GetCurUserNum() >= GetLimitMaxUserNum()) {
return FALSE;
}
#else
if(GetCurUserNum() >= GetLimitMaxUserNum())
return FALSE;
#endif //_NA_008334_20150608_SONNENSCHEIN
// (WAVERIX) (090522) (NOTE) 최대 인원 제한 검사는 여기서 수행하지 말자. 과거 로직의 경험에
//의하면, 이것 저것 호출하는 과정 및 시점에 따라 달라질 수 있기 때문이다.
return TRUE;
}
RC::eROOM_RESULT
RoomInterface::_CanJoinAll(User* pUser, eZONEPUBLIC roomPublic, TCHAR* pszRoomPWD)
{
if(GetRoomState() != eROOMSTATE_GENERNAL)
return RC::RC_ROOM_INVALIDPREVLOBBY;
if(NULL == FindRelayUser(pUser->GetPlayerKey()))
{
if(!CheckLimitUser(pUser))
return RC::RC_ROOM_FULL_ROOM;
}
if(GetZonePublic() != roomPublic)
return RC::RC_ROOM_INVALID_ROOM_TYPE;
if(roomPublic == eZONEPUBLIC_PRIVATE)
{
if(0 != _tcsncmp(m_pszRoomPWD, pszRoomPWD, MAX_ROOMPASSWORD_LENGTH))
return RC::RC_ROOM_NOTEQUAL_PASSWORD;
}
if(!ValidCharClass((eCHAR_TYPE)pUser->GetSelectedCharClass()))
return RC::RC_ROOM_INVALID_LIMITCLASS_FOR_ME;
if(!ValidLevel(pUser->GetSelectedCharLV()))
return RC::RC_ROOM_INVALID_LIMITLEVEL_FOR_ME;
#if defined(__NA_000000_20070528_TAIWAN_GUILD_PVP) && defined(__TW_0_20080521_GUILD_PVP_BUG_FIX) // 길드전이 헌팅룸에만 속하지 않을수 있으므로 전체룸 체크에서
if(IsSpecialType(eROOM_SPECIAL_GUILD_PVP))
{
RC::eROOM_RESULT rcResult = CanJoinGuildPVP(pUser);
if(rcResult != RC::RC_ROOM_SUCCESS)
return rcResult;
}
#endif
return RC::RC_ROOM_SUCCESS;
}
BOOL RoomInterface::CanShowAtRoomList(User* user, eZONEPUBLIC room_type, TCHAR* room_password)
{
//_NA_000038_20100316_VIEW_TO_JOINABLE_ROOM_IN_LOBBY
// (CHANGES)(20100318)(hukim)(REQ_0038) 여기서 방에 입장 가능 여부를 설정한다.
// 다른 타입의 방이 추가 되더라도 여기서 공통으로 검사하여 설정한다.
// 변경을 최소화 하기 위해 이전 로직 플로우와 플로우 자체는 동일하고 추가 정보를
// 더하는 형식으로 구현했다.
CheckMoveZonePolicy* const check_move_zone_policy =
MoveZonePolicy<MZTF_VILLAGE_TO_LOBBY>::AlignInfo(
user, eZONETYPE_VILLAGE, eZONETYPE_LOBBY, user->GetZoneKey(), GetKey());
const RoomArgument room_argument(room_type, ((room_type == eZONEPUBLIC_PRIVATE) ? room_password : NULL));
m_RoomInfo.can_join_room_ =
(g_ZoneManager.IsMoveableZone(check_move_zone_policy, room_argument) == RC::RC_ROOM_SUCCESS);
return TRUE; // by __NA_0_20080328_ROOMLIST_ALL_VIEW
}
static BOOL
CanJoinZoneType(const sMAPINFO* pMapInfo, eZONETYPE enteringZoneType, MAPCODE enteringMapCode)
{
const sMAPINFO* pEnterMapInfo = MapInfoParser::Instance()->FindMapInfo(enteringMapCode);
switch (enteringZoneType)
{
case eZONETYPE_MISSION:
for (WORD i = 0 ; i < sMAPINFO::MAX_MISSION_MAP_NUM_V1_3; ++i)
{
if (pMapInfo->byMissionMapVec[i] == pEnterMapInfo->MapKind) {
return true;
};
};
break;
case eZONETYPE_PVP:
for (WORD i = 0 ; i < sMAPINFO::MAX_PVP_MAP_NUM_V1_3; ++i)
{
if (pMapInfo->wPVPMapVec[i] == pEnterMapInfo->MapKind) {
return true;
};
};
break;
case eZONETYPE_CHUNTING:
for (WORD i = 0 ; i < sMAPINFO::MAX_CHUNTING_MAP_NUM_V1_3; ++i)
{
if (pMapInfo->wCHuntingMapVec[i] == pEnterMapInfo->MapKind) {
return true;
};
};
// TODO: warnings, custom code returns success if request information can't find.
break;
case eZONETYPE_INSTANCE:
return true;
case eZONETYPE_HUNTING:
default:
SUNLOG(eFULL_LOG, "ValidJoinMap:들어 올 수 없는 구간");
return false;
}
return false;
}
BOOL
RoomInterface::ValidJoinMap(User* pUser)
{
// (WAVERIX) (090521) (CHANGES) 검사 조건 변경
// (NOTE) 해당 함수가 호출되는 것은 이것 저것 제약을 거친 뒤이다. 따라서 상태보단
// 타입 검증만으로 해결할 수 있다.
eZONE_STATE userZoneState = pUser->GetZoneState();
MAPCODE userMapCode = pUser->GetSelectedCharMapCode();
BOOLEAN isAcceptable = userZoneState == ePRS_AT_VILLAGE;
if(isAcceptable == 0)
{
isAcceptable = userZoneState == ePRS_AT_FIELD &&
userMapCode == HardCode::MAPCODE_for_TUTORIAL_FIELD;
}
if(isAcceptable == 0)
return TRUE;
const sMAPINFO* pFromMapInfo = MapInfoParser::Instance()->FindMapInfo(userMapCode);
eZONETYPE toZoneType = GetType();
MAPCODE toMapCode = GetMapCode();
if(toZoneType == eZONETYPE_LOBBY)
toZoneType = static_cast<LobbyRoom*>(this)->GetLobbyType();
return CanJoinZoneType(pFromMapInfo, toZoneType, toMapCode);
}
BOOL RoomInterface::ValidCharClass(eCHAR_TYPE type)
{
switch(type)
{
case eCHAR_BERSERKER:
{
if(!(m_RoomInfo.m_ClassLimit & eCLASS_BERSERKER))
return FALSE;
}
break;
case eCHAR_DRAGON:
{
if(!(m_RoomInfo.m_ClassLimit & eCLASS_DRAGON))
return FALSE;
}
break;
case eCHAR_SHADOW:
{
if(!(m_RoomInfo.m_ClassLimit & eCLASS_SHADOW))
return FALSE;
}
break;
case eCHAR_VALKYRIE:
{
if(!(m_RoomInfo.m_ClassLimit & eCLASS_VALKYRIE))
return FALSE;
}
break;
case eCHAR_ELEMENTALIST:
{
if(!(m_RoomInfo.m_ClassLimit & eCLASS_ELEMENTALIST))
return FALSE;
}
break;
case eCHAR_MYSTIC: // _NA_004965_20120613_NEW_CHARACTER_MYSTIC
{
if (!(m_RoomInfo.m_ClassLimit & eCLASS_MYSTIC))
return FALSE;
}
break;
case eCHAR_HELLROID: //_NA_000000_20130812_NEW_CHARACTER_HELLROID
{
if (!(m_RoomInfo.m_ClassLimit & eCLASS_HELLROID))
return FALSE;
}
break;
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
case eCHAR_WITCHBLADE:
{
if (!(m_RoomInfo.m_ClassLimit & eCLASS_WITCHBLADE))
return FALSE;
}
break;
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
}
return TRUE;
}
BOOL
RoomInterface::ValidLevel(LEVELTYPE level)
{
if(level < m_RoomInfo.m_MinLV || m_RoomInfo.m_MaxLV < level)
{
return FALSE;
}
return TRUE;
}
LEVELTYPE
RoomInterface::GetAvgLevel()
{
const ZONE_USER_LIST& rList = GetUserList();
if(rList.empty())
return 0;
// (WAVERIX) (080726) (NOTE) 생각컨데... interface에 매칭되는 결과값에 대한 평가는 현 함수
// 내부에서 판단하기 힘들다. 이런 것들이 작업자에 의해서 정해지고, 관찰자들은 나중에서야
// 필요해서 확인하는 형태가 되곤 한다. 뭔가 좀더 나아지는 방법은 없을까?
DWORD sumV = 0;
FOREACH_CONTAINER(const ZONE_USER_LIST::value_type& rNode, rList, ZONE_USER_LIST)
{
User* const pUser = rNode.second;
sumV += pUser->GetSelectedCharLV();
// 설마, 합산이 오버플로 되지는 않겠지...? 65536/130 = 504...
// 이후 레벨업 한계치 + RoomInterface(...최대 100?) ... 될 가능성은 희박하네...
}
const DWORD userCount = DWORD(rList.size());
const LEVELTYPE average = LEVELTYPE(sumV / userCount);
return average;
}
void
RoomInterface::GetCHuntingScore(BYTE& byAvgWinPercent, DWORD& totalCount)
{
const ZONE_USER_LIST& rList = GetUserList();
if(rList.empty())
return;
DWORD totalCountCache = 0;
FLOAT averageByWinPercent = .0f;
FOREACH_CONTAINER(const ZONE_USER_LIST::value_type& rNode, rList, ZONE_USER_LIST)
{
User* const pUser = rNode.second;
const DWORD cHuntingTotalCount = pUser->GetSelectedCharCHuntingTotCount();
if(!cHuntingTotalCount)
continue;
const FLOAT cHuntingVCount = (FLOAT)pUser->GetSelectedCharCHuntingVCount();
averageByWinPercent += cHuntingVCount / cHuntingTotalCount;
totalCountCache += cHuntingTotalCount;
}
const DWORD userCount = (DWORD)rList.size();
averageByWinPercent = (averageByWinPercent * 100 / userCount);
ASSERT(averageByWinPercent <= 100.f); // add-in (WAVERIX)(080726)
byAvgWinPercent = (BYTE)averageByWinPercent;
totalCount = totalCountCache;
}
User*
RoomInterface::GetNextPriorityMaster(RoomInterface* pRoom, DWORD curMasterKey,
eROOM_SPECIAL_TYPE* OUT pRoomSpecialTypeByUser)
{ // 자동 선택을 해야 하는 경우 최적의 다음 파티장을 찾는다.
// 수퍼방 개념이 적용되는 배틀존, 로비등등은 PC방 유저가 1순위, 2순위 수퍼방장 Item소지자,
// 그도 아니면 일반 유저...
const DWORD ApplyRoom = (1 << eZONETYPE_MISSION) | (1 << eZONETYPE_INSTANCE);
*pRoomSpecialTypeByUser = eROOM_SPECIAL_NORMAL;
BOOLEAN bCanApply = FALSE;
eZONETYPE zoneType = pRoom->GetType();
if(zoneType == eZONETYPE_LOBBY)
{
LobbyRoom* pLobbyRoom = static_cast<LobbyRoom*>(pRoom);
zoneType = pLobbyRoom->GetLobbyType();
}
bCanApply = !!(ApplyRoom & (1 << zoneType));
if(bCanApply == 0)
{
return 0; //
}
const ZONE_USER_LIST& rList = pRoom->GetUserList();
if(rList.empty())
return 0;
// _NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
User* const current_master_user = FindUser(curMasterKey);
if (current_master_user == NULL)
{
return 0;
}
User* pBestUser = 0;
eROOM_SPECIAL_TYPE bBestUserGrade = eROOM_SPECIAL_TYPE(0);
FOREACH_CONTAINER(const ZONE_USER_LIST::value_type& rNode, rList, ZONE_USER_LIST)
{
User* pUser = rNode.second;
DWORD playerKey = pUser->GetPlayerKey();
if(playerKey == curMasterKey)
continue;
// _NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
// 전장에서는 같은존에 2개의 파티가 존재하므로 파티키로 구분해서 다음 파티원을 찾는다.
if (current_master_user->GetPartyState().GetPartyKey() != pUser->GetPartyState().GetPartyKey())
continue;
if(pUser->CanApplyPCBangBenefit(TRUE))
{
pBestUser = pUser;
bBestUserGrade = eROOM_SPECIAL_PCBANG_SUPER_ROOM;
#ifdef __NA_000000_20070528_TAIWAN_GUILD_PVP
if(pUser->GetItemOwnFlag().FindItemInfo(eITEMTYPE_SUPERMASTER))
bBestUserGrade = eROOM_SPECIAL_ITEM1_SUPER_ROOM;
#endif
break;
}
if(pUser->GetItemOwnFlag().FindItemInfo(eITEMTYPE_SUPERMASTER))
{
if(bBestUserGrade == 0)
pBestUser = pUser;
bBestUserGrade = eROOM_SPECIAL_ITEM1_SUPER_ROOM;
}
if(bBestUserGrade != eROOM_SPECIAL_ITEM1_SUPER_ROOM)
pBestUser = pUser;
}
*pRoomSpecialTypeByUser = bBestUserGrade;
return pBestUser;
}
User*
RoomInterface::ChangeSpecialType(BOOLEAN isEnterEvent, User* pMaster)
{ // assert "BattleZone Method"
BASE_ROOMINFO* pBaseRoomInfo = GetRoomInfo();
#ifdef __NA_000000_20070528_TAIWAN_GUILD_PVP
BOOLEAN isApplySuper = !!pBaseRoomInfo->IsSpecialType(eROOM_SPECIAL_SUPER);
BOOLEAN isSuperRoomPc = !!pBaseRoomInfo->IsSpecialType(eROOM_SPECIAL_PCBANG_SUPER_ROOM);
BOOLEAN isSuperRoomItem = !!pBaseRoomInfo->IsSpecialType(eROOM_SPECIAL_ITEM1_SUPER_ROOM);
#else
BOOLEAN isApplySuper = pBaseRoomInfo->m_SpecialType != eROOM_SPECIAL_NORMAL;
BOOLEAN isSuperRoomPc = pBaseRoomInfo->m_SpecialType == eROOM_SPECIAL_PCBANG_SUPER_ROOM;
BOOLEAN isSuperRoomItem = pBaseRoomInfo->m_SpecialType == eROOM_SPECIAL_ITEM1_SUPER_ROOM;
#endif
eROOM_SPECIAL_TYPE specialType = eROOM_SPECIAL_TYPE(0);
User* pPoleUser = 0;
if(isEnterEvent)
{ // assert(isEnterEvent && pMaster && IsMaster(pMaster))
// assert(called by Zone->EnterUser)
//if(!FlowControl::FCAssert(isApplySuper == 0))
// return;
pPoleUser = pMaster;
// 나중에 입장한 유저(파티장)가 PC방 유저이거나 슈퍼방장 아이템 소유했을 경우
isSuperRoomPc = !!pPoleUser->CanApplyPCBangBenefit(TRUE);
isSuperRoomItem = !!pPoleUser->GetItemOwnFlag().FindItemInfo(eITEMTYPE_SUPERMASTER);
isApplySuper = isSuperRoomPc || isSuperRoomItem;
if(isApplySuper != 0)
{
#ifdef __NA_000000_20070528_TAIWAN_GUILD_PVP
if(isSuperRoomPc)
specialType = eROOM_SPECIAL_PCBANG_SUPER_ROOM;
if(isSuperRoomItem)
specialType = eROOM_SPECIAL_ITEM1_SUPER_ROOM;
#else
specialType = isSuperRoomPc ? eROOM_SPECIAL_PCBANG_SUPER_ROOM
: eROOM_SPECIAL_ITEM1_SUPER_ROOM;
#endif
}
pBaseRoomInfo->m_SpecialType = specialType != 0 ? specialType : eROOM_SPECIAL_NORMAL;
}
else
{
DWORD currentMasterKey = pMaster ? pMaster->GetPlayerKey() : 0;
pPoleUser = GetNextPriorityMaster(this, currentMasterKey, &specialType);
if(pPoleUser == 0)
return 0;
pBaseRoomInfo->m_SpecialType = specialType;
//#ifdef __NA_000000_20070528_TAIWAN_GUILD_PVP
// if(isSuperRoomPc)
// pBaseRoomInfo->m_SpecialType &= ~eROOM_SPECIAL_PCBANG_SUPER_ROOM;
// if(isSuperRoomItem)
// pBaseRoomInfo->m_SpecialType &= ~eROOM_SPECIAL_ITEM1_SUPER_ROOM;
//#else
// pBaseRoomInfo->m_SpecialType = eROOM_SPECIAL_NORMAL;
//#endif
}
// changed! - assert(pPoleUser)
{
MSG_AG_ZONE_CHANGE_SPECIALTYPE_CMD msg;
msg.m_RoomKey = GetKey();
msg.m_SpecialType = pBaseRoomInfo->m_SpecialType;
pPoleUser->SendToLinkedServer(&msg, sizeof(msg));
};
{
MSG_CG_ZONE_CHANGE_SPECIALTYPE_CMD msg;
msg.m_SpecialType = pBaseRoomInfo->m_SpecialType;
SendToAll(&msg, sizeof(msg));
};
return pPoleUser;
//////////////////////////////////////////////////////////////////////////
}
#if defined(__NA_000000_20070528_TAIWAN_GUILD_PVP) && defined(__TW_0_20080521_GUILD_PVP_BUG_FIX) //대만 길드전 체크는 헌팅룸에만 속하지 않을수 있으므로.
RC::eROOM_RESULT
RoomInterface::CanJoinGuildPVP(User* pUser)
{
GUILDGUID guildGuid = pUser->GetSelectedCharGuildGuid();
// 길드 소속 체크
if(guildGuid == 0)
return RC::RC_ROOM_CANNOT_CREATE_ROOM_FOR_NO_GUILD;
// 방장과 같은 길드인지 체크
if(User* pMaster = GetMaster())
{
if(pMaster->GetSelectedCharGuildGuid() != guildGuid)
return RC::RC_ROOM_CANNOT_JOIN_ROOM_FOR_DIFF_GUILD;
}
// 입장권 수량 체크
const sMAPINFO* pInfo = MapInfoParser::Instance()->FindMapInfo(GetMapCode());
if(pUser->GetItemOwnFlag().GetItemNum(eITEMTYPE_GUILD_PVP_ENTRANCE) < pInfo->EntranceItemCount)
return RC::RC_ROOM_SHORT_ENTRANCE_CARD_OF_GUILD_PVP;
return RC::RC_ROOM_SUCCESS;
}
#endif
bool
RoomInterface::ExceptionProcessByLobbyMember(const MSG_AG_ZONE_CAN_CREATE_ROOM_ACK* check_msg,
User* request_user)
{
typedef MSG_AG_ZONE_CAN_CREATE_ROOM_ACK Packet;
const Packet::CheckArgMembers& check_member = check_msg->m_CheckArgMembers;
const Packet::MemberInfo* it_src = check_member.members;
const Packet::MemberInfo* it_end = &check_member.members[_countof(check_member.members)];
for(; it_src != it_end; ++it_src)
{
if(it_src->objectKey == 0)
break;
}
it_end = it_src;
it_src = check_member.members;
Packet::CheckArgMembers member_result;
ZeroMemory(&member_result, sizeof(member_result));
Packet::MemberInfo* it_dest = member_result.members;
const ZoneInterface::ZONE_USER_LIST& user_list = GetUserList();
DWORD search_number = 0, result_number = 0, valid_number = 0;
BOOLEAN requester_notified = FALSE;
MSG_CG_ZONE_MISSION_CREATE_NAK nak_msg;
for (; it_src != it_end; ++it_src, ++it_dest)
{
it_dest->objectKey = it_src->objectKey;
ZoneInterface::ZONE_USER_LIST::const_iterator find_it = \
user_list.find(it_src->objectKey);
User* user = (find_it != user_list.end()) ? find_it->second : 0;
if (user == 0)
{
it_dest->status = check_msg->eStatus_PartialFail;
}
else
{
it_dest->status = it_src->status;
++result_number;
}
++search_number;
if (it_dest->status == check_msg->eStatus_PartialOk)
{
if(user->IsBeginTransaction())
it_dest->status = check_msg->eStatus_Transaction;
else
++valid_number;
}
if (it_dest->status != check_msg->eStatus_PartialOk)
{
switch (it_dest->status)
{
case Packet::eStatus_PartialFail:
nak_msg.m_dwErrorCode = RC::RC_ROOM_INVALID_PLAYER_STATE;
break;
case Packet::eStatus_Transaction:
nak_msg.m_dwErrorCode = RC::RC_ROOM_ALREADY_DOING_TRANSACTION;
break;
case Packet::eStatus_LevelViolation:
nak_msg.m_dwErrorCode = RC::RC_ROOM_INVALID_LIMITLEVEL_FOR_ME;
break;
case Packet::eStatus_RequireQuestClear:
nak_msg.m_dwErrorCode = RC::RC_ROOM_PREVIOUS_QUEST_CLEAR;
break;
case Packet::eStatus_RequireMissionClear:
nak_msg.m_dwErrorCode = RC::RC_ROOM_NEED2_CLEAR_PRE_MISSION;
break;
}
if (user)
{
//개별적으로 사유를 보낸다.
requester_notified = user == request_user;
user->SendPacket(&nak_msg, sizeof(nak_msg));
}
}
}
if (search_number == 0 || search_number != result_number || result_number != valid_number)
{
if (requester_notified == FALSE)
{
nak_msg.m_dwErrorCode = RC::RC_ROOM_CANNOT_JOIN_SOMEONE_PARTY_MEMBER;
request_user->SendPacket(&nak_msg, sizeof(nak_msg));
}
return false;
}
return true;
}