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

553 lines
16 KiB
C++

#include "StdAfx.h"
#include ".\gamezone.h"
#include ".\Map.h"
#include ".\MapAllocator.h"
#include ".\GameField.h"
#include ".\Player.h"
#include <MapInfoParser.h>
#include ".\ResrcManager.h"
#include "MovementChecker.h"
#include "GameServerEx.h"
#include "Player.h"
#include "TriggerManagerEx.h"
#include "GameBattleResult.h" //_NA_0_20091118_GAMEROOM_BATTLE_RESULT
GameZone::GameZone(void)
: m_MapCode(0)
, m_Key(0)
, m_NumberOfExpectedPlayers(0) //__NA000000_090622_MOB_ABILITY_CONTROL_CHANGE__
, m_NumberOfExpectedPlayersPrev(0)
, m_bCreateTrigger(FALSE)
, m_pBattleResult(NULL) //_NA_0_20091118_GAMEROOM_BATTLE_RESULT
{
}
GameZone::~GameZone(void)
{
//_NA_0_20091118_GAMEROOM_BATTLE_RESULT
SAFE_DELETE(m_pBattleResult);
}
VOID
GameZone::Release()
{
// 자식클래스에서 먼저 자료를 처리하기 위해
OnDestroy();
m_NumberOfExpectedPlayers = 0;
m_NumberOfExpectedPlayersPrev = 0;
Player* pPlayer = NULL;
_PLAYER_HASH::iterator it = m_PlayerHash.begin(), end = m_PlayerHash.end();
while(it != end)
{
pPlayer = it->second;
if (obj_util::IsValid(pPlayer, __FUNCTION__, __LINE__))
{
pPlayer->SetField(NULL);
pPlayer->SetGameZone(ePRS_NOT_AT_ZONE, NULL, 0);
}
++it;
}
m_PlayerHash.clear();
_GAMEFIELD_HASH::iterator it2 = m_GameFieldHash.begin(), end2 = m_GameFieldHash.end();
while(it2 != end2)
{
(it2->second)->Release();
GameField::FREE(it2->second);
++it2;
}
m_GameFieldHash.clear();
}
void
GameZone::Init(ZONEKEY zoneKey, MAPCODE mapCode, eZONE_RULESET::eRules rules)
{
if(m_PlayerHash.empty() == false)
SUNLOG(eCRITICAL_LOG, "[Map:%u],초기화시PlayerNum[%u] != 0", mapCode, m_PlayerHash.size());
// 룸키 셋팅
SetKey(zoneKey);
SetMapCode(mapCode);
m_pMAPINFO = const_cast<sMAPINFO*>(MapInfoParser::Instance()->FindMapInfo(mapCode));
ASSERT(m_pMAPINFO);
const eZONE_RULESET& rRulesetSrc = m_pMAPINFO->Ruleset;
eZONE_RULESET& rRuleset = const_cast<eZONE_RULESET&>(Ruleset);
if(rules == rRuleset.ZONE_RULE_EMPTY)
rRuleset.rules = rRulesetSrc.rules;
else
rRuleset.rules = rules;
m_NumberOfExpectedPlayers = 0;
m_NumberOfExpectedPlayersPrev = 0;
}
void
GameZone::OnCreate()
{
if(FALSE == FlowControl::FCAssert(m_pMAPINFO))
return;
// (WAVERIX) 재 작성 이유? 별 의미는 없다. 생각 정리 차원에서 무심코... 이건 병이다.
for(INT idx=0 ; idx<sMAPINFO::MAX_FIELD_NUM ; ++idx)
{
const MAPCODE mcMapCode = m_pMAPINFO->FCode[idx];
if(mcMapCode == 0)
continue;
const FIELDCODE fcWMOCode = m_pMAPINFO->FWMOCode[idx];
const FIELDCODE fcWUTCode = m_pMAPINFO->FWUTCode[idx];
ASSERT(0 != fcWMOCode);
FieldInfo* const pFieldInfo = g_ResrcMgr.FindFieldInfo(mcMapCode, fcWMOCode);
UnitRegenInfoManager* const pRegenInfo = g_ResrcMgr.FindRegenInfoMgr(mcMapCode, fcWUTCode);
UnitGroupInfoManager* const pGroupInfo = g_ResrcMgr.FindGroupInfoMgr(mcMapCode, fcWUTCode);
GameField* const pGameField = GameField::ALLOC();
bool bPreMapLoading = true, bPreRegen = true, bViewport = false;
const eZONETYPE zoneType = GetZoneType();
switch(zoneType)
{
case eZONETYPE_VILLAGE: case eZONETYPE_FIELD:
case eZONETYPE_DOMINATION_FIELD:
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
case eZONETYPE_SPA_FIELD:
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
bViewport = false;
break;
case eZONETYPE_MISSION: case eZONETYPE_CHUNTING:
bPreRegen = false; // 리젠은 유저가 섹터로 들어왔을 때 한다.
bPreMapLoading = false; // 최초의 유저가 입장한 후에 맵을 로딩한다.
case eZONETYPE_INSTANCE:
bPreRegen = false; // 리젠은 유저가 섹터로 들어왔을 때 한다.
bPreMapLoading = false; // 최초의 유저가 입장한 후에 맵을 로딩한다.
break;
}
if(!pFieldInfo)
{
SUNLOG(eCRITICAL_LOG,
"[GameZone::OnCreate()] pFieldInfo is NULL, FCode = %d, WMOCode = %d",
m_pMAPINFO->FCode[idx], m_pMAPINFO->FWMOCode[idx]);
}
pGameField->Init(this, pFieldInfo, m_pMAPINFO->FGCode[idx],
pRegenInfo, pGroupInfo,
bPreMapLoading, bViewport, bPreRegen);
m_GameFieldHash.insert(_GAMEFIELD_HASH::value_type(pGameField->GetFieldCode(), pGameField));
}
}
//__NA000000_090622_MOB_ABILITY_CONTROL_CHANGE__
BOOLEAN
GameZone::UpdateNumberOfExpectedPlayers(WORD* number_of_active_players)
{
// UpdateAllMonster발생시 사용되는... Get & Replace
if(m_NumberOfExpectedPlayers != m_NumberOfExpectedPlayersPrev)
{
m_NumberOfExpectedPlayersPrev = m_NumberOfExpectedPlayers;
*number_of_active_players = m_NumberOfExpectedPlayers;
return true;
}
*number_of_active_players = m_NumberOfExpectedPlayers;
return false;
}
VOID
GameZone::Update(DWORD dwDeltaTick)
{
GameField* pGameField = NULL;
_GAMEFIELD_HASH::iterator it = m_GameFieldHash.begin(), end = m_GameFieldHash.end();
while(it != end)
{
(it->second)->Update(dwDeltaTick);
++it;
}
}
VOID
GameZone::Leave(Player* pPlayer)
{
if(!pPlayer->GetField())
{
SUNLOG(eCRITICAL_LOG, "[GameZone::Leave] Field is NULL!");
return;
}
pPlayer->GetField()->LeaveField(pPlayer);
// TODO(WAVERIX) (081218) (WARNING) 메모리 누수가 발생할 가능성 있다.
// 하지만 플레이어는 이것 이후로도 많은 처리를 수행한다. 그곳에서 해제가 제대로 이뤄질 거라 생각되지만
// 꼭 확인할 것!
pPlayer->SetGameZone(ePRS_NOT_AT_ZONE, NULL, 0);
_PLAYER_HASH::iterator it = m_PlayerHash.find(pPlayer->GetObjectKey());
if(it == m_PlayerHash.end())
{
SUNLOG(eCRITICAL_LOG, "[Map:%u][%s]LeaveZone에러", GetMapCode(), pPlayer->GetCharName());
return; //__NA000000_080324_GAMEZONE_IRREGULAR_CODE_FIX__ 매크로 제거
}
m_PlayerHash.erase(it);
}
// 미션에서 사용
VOID
GameZone::Warp(Player* pPlayer, MAPCODE fieldCode, WzID areaID, WzVector* pwvStartPos)
{
GameZone* pEnterZone = NULL;
MAPCODE EnterFieldCode = 0;
eZONE_STATE eRoomState = pPlayer->GetGameZone(pEnterZone, EnterFieldCode);
ASSERT((eRoomState & ePRS_AT_ZONE));
if(pEnterZone)
pEnterZone->LeaveForWarp(pPlayer);
else
{
SUNLOG(eCRITICAL_LOG, "Warp()존이없다.[%u][%u]", eRoomState, EnterFieldCode);
}
pPlayer->SetGameZone(ePRS_BEFORE_ENTER_ZONE, this, fieldCode, areaID, pwvStartPos);
pPlayer->GetMovementChecker()->Reset();
}
VOID
GameZone::GetArea(GameField* pField, Player* pPlayer, MAPCODE fieldCode, WzID& areaID, WzVector& rwvStartPos)
{
pField->GetAreaRandomStartingVector(areaID, rwvStartPos);
}
BOOL
GameZone::Join(Player* pPlayer, MAPCODE fieldCode, WzID areaID, WzVector* pwvStartPos)
{
FlowControl flow;
FLOWCONTROL_START()
{
// start LOGIC_BLOCK
GameField* pField = FindGameField(fieldCode);
IfFailThenBreak(pField, 10);
BOOL bValdPos = FALSE;
WzVector wzVec = { 0, 0, 0 };
if(pwvStartPos && (pwvStartPos->x || pwvStartPos->y || pwvStartPos->z))
{
if(pField->IsMovableCoordinates(pwvStartPos))
bValdPos = TRUE;
}
if(bValdPos)
{
wzVec = *pwvStartPos;
}
else if(areaID != 0)
{
GetArea(pField, pPlayer, fieldCode, areaID, wzVec);
}
else
{
const BASE_PLAYERINFO* const pINFO = pPlayer->GetCharInfo();
if(pINFO->m_iRegion == GetMapCode())
{
wzVec.x = pINFO->m_sLocationX;
wzVec.y = pINFO->m_sLocationY;
wzVec.z = pINFO->m_sLocationZ;
}
}
// 좌표가 선택되지 않았으면 랜덤하게 구한다.
if(wzVec.x == 0 && wzVec.y == 0 && wzVec.z == 0)
{
wzVec = pField->GetRandomStartingVector();
}
else if(!pField->IsMovableCoordinates(&wzVec))
{
wzVec = pField->GetRandomStartingVector();
}
// 선택된 좌표의 유효성을 체크한다.
IfFailThenBreak(pField->IsMovableCoordinates(&wzVec), 30);
if(FALSE == flow.FCAssert(pField->EnterField(pPlayer, &wzVec)))
{
SetFailFc(pField->EnterField(pPlayer, &wzVec), 50);
break;
}
_PLAYER_HASH::const_iterator itFIND = m_PlayerHash.find(pPlayer->GetObjectKey());
if(itFIND != m_PlayerHash.end())
{
Player* pSyncFault = itFIND->second;
SUNLOG(eCRITICAL_LOG, "[Map:%u][%s]=[%s]EnterZone Error Occured",
GetMapCode(), pPlayer->GetCharName(), pSyncFault->GetCharName());
}
m_PlayerHash.insert(std::make_pair(pPlayer->GetObjectKey(), pPlayer));
// 필드를 들어올 때 섀도우면 소환체가 있는지 체크하고 있으면 해당 몬스터도 Enter시킨다.
// 이게 Player의 OnEnterField에 들어가지 못하고 여기 있는 이유는,
// 섀도우가 들어갈때 소환수도 소환되는데, 1. 섀도우 Enter ==> 소환수 생성 ==> 소환수 정보 클라 전달 ==> 섀도우 정보 클라 전달
// 이다. 그런데 클라 입장에서는 섀도우가 들어오기 전에 소환수가 먼저 들어왔으니
// 주인없는 소환수라 하여 처리 하지 않는다. 그래서 부득이 Enter 처리 완료 시점에서 처리한다.
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
// 온천은 소환수 재소환하지 않는다
if (GetZoneType() != eZONETYPE_SPA_FIELD) {
pPlayer->CallSummonMonster();
}
#else
pPlayer->CallSummonMonster();
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
return TRUE;
}
FLOWCONTROL_END;
switch(flow.GetErrorCode())
{
case 10:
SUNLOG(eCRITICAL_LOG, "[GameZone::Join] Can't Find Field (%u) OnEnterUser.", fieldCode);
break;
case 30:
SUNLOG(eCRITICAL_LOG, "[GameZone::Join] IsMovableCoordinates Failed!!");
break;
case 50: // assert
break;
default:
SUNLOG(eCRITICAL_LOG, _T("Unexpected Status %u"), flow.GetErrorCode());
break;
}
return FALSE;
}
// (f100623.1L) change an interface to support a multi-checksum checker
bool GameZone::CheckMapVersion(FIELDCODE field_code,
EngineFileChecksum* stored_checksums,
const EngineFileChecksum& inputed_checksums) const
{
GameField* game_field = const_cast<GameZone*>(this)->FindGameField(field_code);
if (game_field == NULL) {
return false;
}
return game_field->CheckMapVersion(stored_checksums, inputed_checksums);
}
/*
BOOL
GameZone::CheckMapVersion(FIELDCODE fieldCode, DWORD checksum, DWORD* OUT pCurCheckSum)
{
GameField* pField = FindGameField(fieldCode);
if(NULL == pField)
return FALSE;
return pField->CheckMapVersion(checksum, pCurCheckSum);
}
*/
GameField*
GameZone::GetEntryField() const
{
// GameField 자료구조 정리하자.
const FIELDCODE fieldCode = m_pMAPINFO->FCode[0];
assert(fieldCode);
_GAMEFIELD_HASH::const_iterator it = m_GameFieldHash.find(fieldCode);
return it != m_GameFieldHash.end() ? it->second : 0 ;
}
MAPCODE
GameZone::GetEntryFieldCode() const
{
return GetMAPINFO()->FCode[0];
}
DWORD
GameZone::GetEntryAreaID()
{
return GetMAPINFO()->wStartAreaID;
}
DWORD
GameZone::GetTotalMonsterNum(DWORD & OUT dwDeadNPCNum)
{
DWORD dwNPCNum = 0;
dwDeadNPCNum = 0;
GameField* pGameField = NULL;
_GAMEFIELD_HASH::iterator it = m_GameFieldHash.begin(), end = m_GameFieldHash.end();
while(it != end)
{
pGameField = it->second;
dwNPCNum += pGameField->GetNPCNum();
#ifdef _DEV_TAIYO_ADD_REGEN_TIME
dwDeadNPCNum += pGameField->GetDeadNPCNum();
#endif
++it;
}
return dwNPCNum;
}
VOID
GameZone::ChangeHandler(const eZONETYPE kindOfZone, const eINSTANCE_DUNGEON_KIND kindOfIndun)
{
switch(kindOfZone)
{
case eZONETYPE_VILLAGE:
Policyment.SetField
( Policyment.ENABLE_GROUP_VILLAGE
);
break;
case eZONETYPE_FIELD:
case eZONETYPE_TUTORIAL_FIELD:
Policyment.SetField
( Policyment.ENABLE_GROUP_HUNTING_FIELD
| Policyment.ENABLE_SAVE_LOCATION
);
break;
case eZONETYPE_DOMINATION_FIELD:
Policyment.SetField
( Policyment.ENABLE_GROUP_HUNTING_FIELD
| Policyment.ENABLE_DOMINATION_CONTROL // (WAVERIX) (100122) add rule
);
break;
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
case eZONETYPE_SPA_FIELD:
Policyment.SetField(Policyment.ENABLE_GROUP_SPAFIELD);
break;
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
// 하단의 타입들은 GameRoom에서 평가된다.
case eZONETYPE_LOBBY:
case eZONETYPE_CHUNTING:
case eZONETYPE_PVP:
case eZONETYPE_MISSION:
case eZONETYPE_INSTANCE:
default:
ASSERT(!"ChangeHandler Unexpected ZoneType");
Policyment.SetField(Policyment.DISABLE_ALL);
break;
}
}
#ifdef _NA_20081121_ABUMAN_NIJOT_EVENT
//현재 게임존에 있는 모든 유저가 같은 길드원인가?
bool
GameZone::IsAllSameGuildMember()
{
Player* pPlayer = NULL;
GUILDGUID firstGuildGuid = 0;
GUILDGUID curGuildGuid = 0;
bool bFirst = true;
_PLAYER_HASH::iterator it = m_PlayerHash.begin(), end = m_PlayerHash.end();
for(; it != end ; ++it)
{
pPlayer = it->second;
if (obj_util::IsValid(pPlayer, __FUNCTION__, __LINE__) == false)
{
continue;
}
//길드에 가입되어 있고
curGuildGuid = pPlayer->GetGuildGuid();
if(curGuildGuid == 0)
return false;
if(bFirst)
{
firstGuildGuid = curGuildGuid;
bFirst = false;
}
// 서로 다른 길드원이면
if(firstGuildGuid != curGuildGuid)
return false;
}
return true;
}
#endif
void GameZone::ResetTrigger()
{
DestroyTrigger();
CreateTrigger();
}
void GameZone::CreateTrigger()
{
if( FALSE == m_bCreateTrigger )
m_bCreateTrigger = TRUE;
else
return;
const _GAMEFIELD_HASH& rFields = GetFieldHash();
FOREACH_CONTAINER(const _GAMEFIELD_HASH::value_type& rField, rFields, _GAMEFIELD_HASH)
{
GameField* const pField = rField.second;
TriggerManagerEx* const pTriggerManager = pField->GetTriggerManager();
pTriggerManager->MAP_STATE = TriggerLastedState::Create();
pTriggerManager->MAP_STATE.Init();
}
}
void GameZone::DestroyTrigger()
{
if( FALSE != m_bCreateTrigger )
m_bCreateTrigger = FALSE;
else
return;
const _GAMEFIELD_HASH& rFields = GetFieldHash();
FOREACH_CONTAINER(const _GAMEFIELD_HASH::value_type& rField, rFields, _GAMEFIELD_HASH)
{
GameField* const pField = rField.second;
TriggerLastedStateDelegator& rDelegator = pField->GetTriggerManager()->MAP_STATE;
TriggerLastedState* pINFO = rDelegator.REFERENCE();
TriggerLastedState::Destroy(pINFO);
rDelegator = TriggerLastedStateDelegator();
}
}
BOOL GameZone::TriggerActive(WzID triggerID)
{
BOOL bSatisfied = TRUE;
const _GAMEFIELD_HASH& rFields = GetFieldHash();
FOREACH_CONTAINER(const _GAMEFIELD_HASH::value_type& rField, rFields, _GAMEFIELD_HASH)
{
GameField* const pField = rField.second;
TriggerManagerEx* const pTriggerManager = pField->GetTriggerManager();
if (FALSE == pTriggerManager->InsertActiveTrigger(triggerID) )
bSatisfied = FALSE;
}
return bSatisfied;
}
void GameZone::TriggerInactive(WzID triggerID)
{
const _GAMEFIELD_HASH& rFields = GetFieldHash();
FOREACH_CONTAINER(const _GAMEFIELD_HASH::value_type& rField, rFields, _GAMEFIELD_HASH)
{
GameField* const pField = rField.second;
TriggerManagerEx* const pTriggerManager = pField->GetTriggerManager();
pTriggerManager->RequestRemoveActiveTrigger(triggerID);
}
}
void GameZone::AllTriggerInactive()
{
const _GAMEFIELD_HASH& rFields = GetFieldHash();
FOREACH_CONTAINER(const _GAMEFIELD_HASH::value_type& rField, rFields, _GAMEFIELD_HASH)
{
GameField* const pField = rField.second;
TriggerManagerEx* const pTriggerManager = pField->GetTriggerManager();
pTriggerManager->RemoveAllActiveTrigger();
}
}