Restructure repository to include all source folders

Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-29 20:17:20 +09:00
parent 5d3cd64a25
commit dd97ddec92
11602 changed files with 1446576 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
#include "stdafx.h"
#include <map>
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include <Utility/DelimitedFile.h>
#include <Creature/CreatureManager.h>
#include <Creature/Monster/Monster.h>
#include "DuelCellManager.h"
CDuelCellManager CDuelCellManager::ms_this;
CCell* CDuelCellManager::CreateCell(unsigned long dwCID)
{
CCell* lpCell = new CCell();
if(NULL == lpCell)
return NULL;
lpCell->Initialize(0, 0);
m_CellData.insert(std::make_pair(dwCID, lpCell)).second;
return lpCell;
}
bool CDuelCellManager::DestroyCell(unsigned long dwCID)
{
DuelCellMap::iterator itr = m_CellData.find(dwCID);
if(itr == m_CellData.end())
{
return false;
}
delete itr->second;
m_CellData.erase(dwCID);
return true;
}
// -------------------------------------------------------------------------------------
// 로그용 함수들
struct CheckInfo
{
unsigned long m_dwCellID;
CheckInfo(unsigned long CellID) : m_dwCellID(CellID) { }
};
typedef std::map<unsigned long, CheckInfo> CIDInCell;
typedef std::multimap<unsigned long, CheckInfo> DUPCID;
class CheckMap
{
public:
CheckMap(CIDInCell& Map) : m_Map(Map) { }
bool operator() (CAggresiveCreature* lpCreature) { m_Map.erase(lpCreature->GetCID()); return true; }
protected:
CIDInCell& m_Map;
};
bool CDuelCellManager::CheckCellAggresiveCreatures()
{
CIDInCell AggresiveCreatureMap;
DUPCID DuplicateCID;
unsigned long dwCID = 0;
for(DuelCellMap::iterator itr = m_CellData.begin(); itr != m_CellData.end(); ++itr)
{
CCell* lpCell = itr->second;
unsigned long CellID = itr->first;
CCharacter* lpCharacter = lpCell->GetFirstCharacter();
while(NULL != lpCharacter)
{
dwCID = lpCharacter->GetCID();
if(!AggresiveCreatureMap.insert(CIDInCell::value_type(dwCID, CheckInfo(CellID))).second)
{
DuplicateCID.insert(DUPCID::value_type(dwCID, CheckInfo(CellID)));
}
lpCharacter = lpCell->GetNextCharacter();
}
}
CheckMap check(AggresiveCreatureMap);
CCreatureManager::GetInstance().ProcessAllCharacter(check);
CCreatureManager::GetInstance().ProcessAllMonster(check);
FILE* fFile = fopen("CellLog.txt", "wt");
if(NULL == fFile) { return false; }
if(DuplicateCID.empty())
{
fprintf(fFile, "셀 끼리는 중복되는 CID가 없습니다.");
}
else
{
for(DUPCID::iterator itr = DuplicateCID.begin(); itr != DuplicateCID.end(); ++itr)
{
fprintf(fFile, "셀 안에서 중복되는 CID:0x%08x - CellID:%d", itr->first,
itr->second.m_dwCellID);
}
}
if(AggresiveCreatureMap.empty())
{
fprintf(fFile, "크리쳐 맵에 있는 아이디 뿐입니다.");
}
else
{
for(CIDInCell::iterator itr = AggresiveCreatureMap.begin(); itr != AggresiveCreatureMap.end(); ++itr)
{
fprintf(fFile, "크리쳐 맵에 없는 CID:0x%08x - CellID:%d", itr->first,
itr->second.m_dwCellID);
}
}
fclose(fFile);
return true;
}
bool CDuelCellManager::CheckCellStatus(void)
{
// 셀 상태를 출력하는 부분, 이전 셀에서 주위 셀 뺐음.
FILE* fFile = fopen("CellStat.txt", "wt");
if(NULL == fFile)
{
return false;
}
fprintf(fFile, "X,Z,인원수,아이템 수\r\n");
for(DuelCellMap::iterator itr = m_CellData.begin(); itr != m_CellData.end(); ++itr)
{
CCell* lpCell = itr->second;
unsigned long CellID = itr->first;
fprintf(fFile, "%4d\t%4d\t%4d\r\n",
CellID, lpCell->GetCharacterNum(), lpCell->GetItemNum());
}
fclose(fFile);
return true;
}

View File

@@ -0,0 +1,87 @@
#ifndef _DUELCELL_MANAGER_H_
#define _DUELCELL_MANAGER_H_
#pragma once
#include "../FieldMap/Cell.h"
#include <Pattern/Singleton.h>
class CDuelCellManager : public CSingleton<CDuelCellManager>
{
public:
~CDuelCellManager() { }
CCell* CreateCell(unsigned long dwCID);
bool DestroyCell(unsigned long dwCID);
CCell* GetCell(unsigned long dwCellID); // 셀 번호로 포인트 얻기
inline void LowerResolution(int nHighX, int nHighZ, int *nLowX, int *nLowZ);
inline void HigherResolution(int nLowX, int nLowZ, int *nHighX, int *nHighZ);
template<typename FnRefCell>
inline bool ProcessAllCell(FnRefCell fnRefCell)
{
if (0 == m_CellData.size()) return false;
for(DuelCellMap::iterator itr = m_CellData.begin(); itr != m_CellData.end(); ++itr)
{
fnRefCell(*itr->second);
}
return true;
}
template<typename FnRefCell, typename Arg1>
inline bool ProcessAllCell(FnRefCell fnRefCell, Arg1 arg1)
{
if (0 == m_CellData.size()) return false;
for(DuelCellMap::iterator itr = m_CellData.begin(); itr != m_CellData.end(); ++itr)
{
fnRefCell(*itr->second, arg1);
}
return true;
}
// --------------------------------------------------------------------------------------------
// 셀 통계 내는 함수들
bool CheckCellAggresiveCreatures(void); // 셀 내에 겹치는 CID가 있는지 검사.
bool CheckCellStatus(void); // 셀 내에 사용자 분포 출력.
private:
CDuelCellManager() { }
// --------------------------------------------------------------------------------------------
// member variable
typedef std::map<unsigned long, CCell*> DuelCellMap;
DuelCellMap m_CellData;
static CDuelCellManager ms_this;
};
inline CCell* CDuelCellManager::GetCell(unsigned long dwCellID)
{
DuelCellMap::iterator it = m_CellData.find(dwCellID);
return (it != m_CellData.end()) ? it->second : NULL;
}
inline void CDuelCellManager::LowerResolution(int nHighX, int nHighZ, int *nLowX, int *nLowZ)
{
*nLowX = nHighX >> CCell::CELL_RESOLUTION;
*nLowZ = nHighZ >> CCell::CELL_RESOLUTION;
}
inline void CDuelCellManager::HigherResolution(int nLowX, int nLowZ, int *nHighX, int *nHighZ) {
*nHighX = nLowX << CCell::CELL_RESOLUTION;
*nHighZ = nLowZ << CCell::CELL_RESOLUTION;
}
#endif+

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
#ifndef _CCELL_H_
#define _CCELL_H_
#pragma once
#pragma warning(disable:4800)
#include <list>
#include <algorithm>
#include <climits>
#include <boost/pool/pool_alloc.hpp>
#include <Creature/CreatureStructure.h>
#include <Network/Packet/PacketStruct/CharMovepacket.h>
#include <Network/Packet/PacketStruct/AddressPacket.h>
#include <Network/Packet/PacketStruct/CharAttackPacketStruct.h>
#include <Network/Packet/PacketStruct/CharItemPacketStruct.h>
// 전방 참조
class CParty;
class CCreature;
class CAggresiveCreature;
class CMonster;
class CCharacter;
class CSiegeObject;
class CBuffer;
struct PktBase;
struct PktMVEx;
namespace Item
{
class CItem;
};
class CCell
{
public:
enum ErrorCode
{
S_SUCCESS = 0,
S_AUTO_ROUTING = 1,
E_NOT_ENOUGH_MEMORY = 2,
E_NOT_OWNER_OF_ITEM = 3,
E_NOT_ITEM = 4,
E_CREATE_ITEM_FAIL = 5,
E_NOT_EXIST_GUILD = 6,
E_GOLD_OVERFLOW = 7
};
enum Const
{
CONNECT_NUM = 9,
CHECK_TIME = 100, // 아이템이 사라져야 하는지 체크하는 간격 (10초)
ITEM_RIGHT_TIME = 1600, // 플레이어가 드랍한 아이템의 경우 소유권은 10초, 다시 2분 30초 후엔 사라진다.
ITEM_LIFE_TIME = 1800, // 맵에 아이템이 남아있는 시간 (3분)
NO_OWNER_TIME = 1500, // 아이템에 소유권이 지정되어 있지 않은 시간 (2분 30초)
BROADCASTING_TIME = 10, // 기존 브로드 캐스팅 타임 (1초)
MONEY_BIT = 0x80000000,
TYPE_CHECK_BIT = 0xF0000000,
ITEM_PROTOTYPE_ID_BIT = 0x0000FFFF,
MAX_MONEY_AMOUNT = 0x0FFFFFFF
};
enum TurnConst
{
TURN_OF_CHARACTER = 0,
TURN_OF_SIEGE_OBJECT = 1,
TURN_OF_MONSTER = 2,
TURN_END = 3
};
enum Weather
{
WEATHER_FINE = 0, // 맑음
WEATHER_RAIN = 1, // 비
WEATHER_SNOW = 2, // 눈
MAX_WEATHER = 3
};
enum ItemAutoRouting
{
NONE = 0, // 루팅 하지 않는다.
PARTY = 1, // 파티원에게 루팅
GUILD = 2, // 길드원에게 루팅
};
enum ConnectCell
{
NO = 0,
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4,
UPPERLEFT = 5,
UPPERRIGHT = 6,
LOWERLEFT = 7,
LOWERRIGHT = 8
};
enum CellSize
{
CELL_SIZE_DEFAULT = 128,
CELL_SIZE_BATTLE_SERVER = 70,
CELL_DISTANCE = 32,
CELL_RESOLUTION = 5
};
static unsigned char ms_CellSize;
struct IDField
{
enum
{
MAX_UID = UCHAR_MAX
};
unsigned long dwUID; // 유니크 ID (생성된 순서에 따라 차례대로...)
unsigned short wMapIndex; // 가상 맵의 경우 그 인덱스
unsigned char cCellX; // 셀 인덱스 X
unsigned char cCellZ; // 셀 인덱스 Z
};
struct ItemInfo
{
Position m_Pos; // 월드맵 상의 위치
union
{
unsigned __int64 m_nUniqueID; // 유니크 ID
IDField m_Field;
} UID;
Item::CItem* m_lpItem; // 아이템
unsigned long m_dwGold; // 돈
unsigned long m_dwOwnerID; // 소유권
unsigned short m_wPulse; // 소멸까지 남은 펄스
unsigned char m_cAutoRouting; // 오토루팅될 아이템인가? (ItemAutoRouting enum 참고)
ItemInfo();
void MakeFieldObject(FieldObject& fieldObject);
};
typedef std::list<CMonster*, boost::fast_pool_allocator<CMonster*> > MonsterList;
typedef std::list<CCharacter*, boost::fast_pool_allocator<CCharacter*> > CharacterList;
typedef std::list<ItemInfo, boost::fast_pool_allocator<ItemInfo> > ItemList;
typedef std::list<CSiegeObject*, boost::fast_pool_allocator<CSiegeObject*> > SiegeObjectList;
//--------------------------------------------------------------------------------------------
// 초기화
CCell();
~CCell();
void Initialize(unsigned char cIndexX, unsigned char cIndexZ); // 셀 좌표를 초기화하고, 소트된 링크를 생성한다.
void SetMapIndex(unsigned short wMapIndex) { m_wMapIndex = wMapIndex; }
unsigned short GetMapIndex() { return m_wMapIndex; }
//--------------------------------------------------------------------------------------------
// interface
CCell* GetConnectCell(unsigned int Dir);
void SetConnectCell(unsigned int Dir, CCell* lpConnectedCell);
bool IsNearCell(CCell* lpNearCell);
//--------------------------------------------------------------------------------------------
// 셀 정보
unsigned char GetIndexX() const { return m_cIndexX; }
unsigned char GetIndexZ() const { return m_cIndexZ; }
size_t GetMonsterNum() const { return m_lstMonster.size(); }
size_t GetCharacterNum() const { return m_lstCharacter.size(); }
size_t GetItemNum() const { return m_lstItem.size(); }
size_t GetSiegeObjectNum() const { return m_lstSiegeObject.size(); }
size_t GetNearCellCharacterNum() const;
//--------------------------------------------------------------------------------------------
// 셀에 크리쳐 추가 및 삭제, 얻어 오기.
void SetCreature(unsigned long dwCID, CCreature* lpCreature, CCell* lpLastLogoutCell = 0);
void DeleteCreature(unsigned long dwCID, CCell* lpPrepareLoginCell = 0);
CCreature* GetCreature(unsigned long dwCID);
CAggresiveCreature* GetFirstAggresiveCreature();
CAggresiveCreature* GetNextAggresiveCreature();
void KillAll(CCharacter* lpAttacker);
//--------------------------------------------------------------------------------------------
// 몬스터 관련 처리
bool IsMonster() const { return !(m_lstMonster.empty()); }
CMonster* GetFirstMonster();
CMonster* GetNextMonster() { return (++m_MonsterIt != m_lstMonster.end()) ? *m_MonsterIt : NULL; }
void LogoutAllMonster() { m_lstMonster.clear(); }
//--------------------------------------------------------------------------------------------
// 공성 오브젝트 관련 처리
bool IsSiegeObject() { return !(m_lstSiegeObject.empty()); }
CSiegeObject* GetFirstAirShip();
CSiegeObject* GetNextAirShip();
CSiegeObject* GetFirstSiegeObject();
CSiegeObject* GetNextSiegeObject();
void UpgradeByEmblem(unsigned long dwCID);
void DegradeByEmblem();
bool IsDetectionCell() { return ( 0 != m_dwCastleEmblemCID ); }
void DetectionAttack(CSiegeObject* lpEmblem, CAggresiveCreature* lpTargetCreature);
bool SendStealthInfo(CCharacter& character, bool bUseStealth);
//--------------------------------------------------------------------------------------------
// 캐릭터 관련 처리
bool IsCharacter() { return !(m_lstCharacter.empty()); }
CCharacter* GetFirstCharacter();
CCharacter* GetNextCharacter() { return (++m_CharacterIt != m_lstCharacter.end()) ? *m_CharacterIt : NULL; }
template<typename FnProcess>
inline void ProcessAllMonster(FnProcess fnProcess)
{
std::for_each(m_lstMonster.begin(), m_lstMonster.end(), fnProcess);
}
template<typename FnProcess>
inline void ProcessAllCharacter(FnProcess fnProcess)
{
std::for_each(m_lstCharacter.begin(), m_lstCharacter.end(), fnProcess);
}
void RespawnAllCharacter(unsigned char cExceptNation);
//--------------------------------------------------------------------------------------------
// 아이템 관련 처리
bool IsItem(void) { return !(m_lstItem.empty()); }
void SetItem(const Position& Pos, Item::CItem* lpItem, unsigned long dwGold,
unsigned long dwOwnerID, unsigned char cAutoRouting, CCell::ItemInfo& cellItemInfo);
CCell::ErrorCode GetItem(unsigned long dwCreatureID, unsigned __int64 nItemInfoID,
Item::CItem** lppItem, unsigned long& dwMoney_Out);
void CheckDeleteItem(void);
void DeleteAllItem(void);
//--------------------------------------------------------------------------------------------
// 셀 관련 브로드 캐스팅
void PrepareBroadCast();
void BroadCast(unsigned long dwCurrentPulse);
// 만든 패킷을, Wrap하지 말고 보낸다(내부에서 Wrap함. srvState나 usError는 넣을수 없다.)
// 패킷 순서가 바뀔 수 있으므로 주의한다.
void SendAllNearCellCharacter(const PktBase* lpPktBase, unsigned short usLength, unsigned char cCMD);
void SendAllCharacter(const PktBase* lpPktBase, unsigned short usLength, unsigned char cCMD);
void SendNowAllNearCellCharacter(const char* szData, unsigned short usLength, unsigned char cCMD);
void SendNowAllCharacter(const char* szData, unsigned short usLength, unsigned char cCMD);
void SendAttackInfo(unsigned long AttackerID_In, const AtType &AtType_In,
unsigned char DefenserNum_In, DefenserNode* lpNode_In);
void SendCastObjectInfo(unsigned long SenderID, unsigned long ReceiverID, CastObject& CastObject_In);
private:
//--------------------------------------------------------------------------------------------
// 패킷 전송 관련 (내부에서만 호출하는 함수들)
void SendPullDownInfo(unsigned long dwOwnerID, ItemInfo& itemInfo);
void SendPickUpInfo(unsigned long dwCreatureID, unsigned __int64 nItemInfoID);
void SendCellLogin(CAggresiveCreature* lpAggresiveCreature, CCell* lpLastLogoutCell);
void SendCellLogout(CAggresiveCreature* lpAggresiveCreature, CCell* lpPrepareLoginCell);
// 버퍼를 전부 Release한다.
static void ReleaseAllBuffer(CBuffer*& lpBuffer);
//--------------------------------------------------------------------------------------------
// 데이터
CCell* m_lpConnectCell[CONNECT_NUM]; // 주변 연결 리스트. 자기 자신도 포함한다.
CCell* m_lpSortedConnectedCell[CONNECT_NUM]; // 주변 연결 리스트를 포인터순으로 소팅한 배열.
CharacterList m_lstCharacter;
MonsterList m_lstMonster;
ItemList m_lstItem;
SiegeObjectList m_lstSiegeObject; // 단지 리스트를 가지고 있을 뿐, 모든 공성 오브젝트는 CSiegeObjectMgr 이 관리한다.
MonsterList::iterator m_MonsterIt; // 범용적... 한 녀석이 다 쓰기 전에 다른 녀석이 달라고 하면 곤란합니다.
CharacterList::iterator m_CharacterIt; // 범용적... 한 녀석이 다 쓰기 전에 다른 녀석이 달라고 하면 곤란합니다.
SiegeObjectList::iterator m_SiegeObjectIt; // 범용적... 한 녀석이 다 쓰기 전에 다른 녀석이 달라고 하면 곤란합니다.
CBuffer* m_lpBroadcast2ndBuffer; // 브로드캐스트 2nd버퍼(새 브로드캐스트 버퍼, 캐릭터 및 아이템용)
unsigned long m_dwCastleEmblemCID; // 성 상징물의 디텍션 범위에 있는 셀이면 상징물의 CID를 가지고 있다.
unsigned short m_wMapIndex; // 맵 인덱스
unsigned char m_cIndexX; // 맵 X좌표
unsigned char m_cIndexZ; // 맵 Z좌표
unsigned char m_cTurnOfGetAggresiveCreature;
CCell::Weather m_eWeather; // 현재 날씨
};
#endif

View File

@@ -0,0 +1,568 @@
#include "stdafx.h"
#include "Cell.h"
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Utility/Compress/MiniLZO/miniLZOWrapper.h>
#include <Creature/Creature.h>
#include <Creature/AggresiveCreature.h>
#include <Creature/Character/Character.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Monster/VirtualMonsterMgr.h>
#include <Creature/Siege/SiegeObject.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharItemPacket.h>
#include <Network/Packet/PacketStruct/CharBroadCastPacket.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
__declspec(thread) static char g_BroadcastBuffer[PktMaxLen];
void CCell::ReleaseAllBuffer(CBuffer*& lpBuffer)
{
// 일전에 전송했던 buffer를 전부 Release한다.
CBuffer* lpPos = lpBuffer;
CBuffer* lpDel = lpBuffer;
while (0 != lpPos)
{
lpDel = lpPos;
lpPos = lpPos->next();
SAFE_RELEASE_BUFFER(lpDel);
}
lpBuffer = 0;
}
void CCell::PrepareBroadCast()
{
// 일전에 전송했던 buffer를 전부 Release한다.
ReleaseAllBuffer(m_lpBroadcast2ndBuffer);
if (m_lstCharacter.empty() && m_lstMonster.empty())
{
// 이 셀에는 몬스터도, 캐릭터도 없다. 처리하지 않는다.
return;
}
// 캐릭터 데이터를 생성한다.
const int MAX_BROADCAST_BUFFER_SIZE = 14 * 1024;
CBufferFactory& bufferFactory = CCellManager::GetInstance().GetBufferFactory();
// 캐릭터 데이터를 생성한다.
CharacterList::iterator char_pos = m_lstCharacter.begin();
CharacterList::iterator char_end = m_lstCharacter.end();
CBuffer* lpBroadcastBuffer = CREATE_BUFFER(bufferFactory, MAX_BROADCAST_BUFFER_SIZE);
for (; char_pos != char_end && 0 != lpBroadcastBuffer; )
{
CCharacter* lpCharacter = *char_pos;
//if (0 != lpCharacter && !lpCharacter->IsRideArms() &&
// 우선 병기에 타고 있어도 보낸다.
if (0 != lpCharacter &&
lpCharacter->IsOperationFlagSet(CCharacter::CHAR_INFO_LOADED))
{
Broadcast2nd::CSerializeCharacterData& charData =
lpCharacter->GetSerializeData();
if (lpBroadcastBuffer->push(
charData.GetDeltaBroadcastData(),
charData.GetDeltaBroadcastDataLen()))
{
// 데이터 저장 성공
++char_pos;
}
else
{
// 버퍼가 꽉 찬 경우, 버퍼를 비워 주고, 계속 진행한다.
lpBroadcastBuffer->next(m_lpBroadcast2ndBuffer);
m_lpBroadcast2ndBuffer = lpBroadcastBuffer;
lpBroadcastBuffer = CREATE_BUFFER(bufferFactory, MAX_BROADCAST_BUFFER_SIZE);
}
}
else
{
// 캐릭터가 복사 대상이 아닌 경우이다.
++char_pos;
}
}
// 몬스터 데이터를 생성한다.
MonsterList::iterator mon_pos = m_lstMonster.begin();
MonsterList::iterator mon_end = m_lstMonster.end();
for (; mon_pos != mon_end && 0 != lpBroadcastBuffer; )
{
CMonster* lpMonster = *mon_pos;
if (0 != lpMonster)
{
Broadcast2nd::CSerializeMonsterData& monsterData = lpMonster->GetSerializeData();
if (lpBroadcastBuffer->push(
monsterData.GetDeltaBroadcastData(),
monsterData.GetDeltaBroadcastDataLen()))
{
// 데이터 저장 성공
++mon_pos;
}
else
{
// 버퍼가 꽉 찬 경우, 버퍼를 비워 주고, 계속 진행한다.
lpBroadcastBuffer->next(m_lpBroadcast2ndBuffer);
m_lpBroadcast2ndBuffer = lpBroadcastBuffer;
lpBroadcastBuffer = CREATE_BUFFER(bufferFactory, MAX_BROADCAST_BUFFER_SIZE);
}
}
else
{
++mon_pos;
}
}
if (0 != lpBroadcastBuffer)
{
if(0 < lpBroadcastBuffer->length())
{
lpBroadcastBuffer->next(m_lpBroadcast2ndBuffer);
m_lpBroadcast2ndBuffer = lpBroadcastBuffer;
lpBroadcastBuffer = 0;
}
else
{
SAFE_RELEASE_BUFFER(lpBroadcastBuffer);
}
}
}
void SendBroadcastPacket(CCell& sendCell,
char* szBroadcastBuffer,
unsigned short usPacketLength,
unsigned char cBroadcastType,
unsigned char cDataType,
unsigned long dwCurrentPulse)
{
// 패킷 헤더 작성
Broadcast2nd::PktBroadcast* lpBroadcast =
reinterpret_cast<Broadcast2nd::PktBroadcast*>(szBroadcastBuffer);
lpBroadcast->m_dwCurrentPulse = dwCurrentPulse;
lpBroadcast->m_cBroadcastType = cBroadcastType;
lpBroadcast->m_cDataType = cDataType;
sendCell.SendAllCharacter(lpBroadcast, usPacketLength, CmdCellBroadCast2nd);
}
void CCell::BroadCast(unsigned long dwCurrentPulse)
{
if (!m_lstCharacter.empty())
{
// 본 셀에 보낼 캐릭터가 있는 경우에만 전송
// 현재 셀의 캐릭터에게, 주변 셀의 캐릭터 정보를 전부 보낸다.
// 버퍼 데이터를 가능한 많이 모아서 압축한 다음 한번에 많이 보낸다.
CCell** lppCellPos = m_lpConnectCell;
CCell** lppCellEnd = m_lpConnectCell + CONNECT_NUM;
char* szBufferPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
char* szBufferEnd = g_BroadcastBuffer + sizeof(g_BroadcastBuffer);
CCell* lpCell = 0;
CBuffer* lpBuffer = 0;
unsigned long dwCompressed = PktMaxLen;
unsigned short usPacketLength = 0;
for (; lppCellPos != lppCellEnd; ++lppCellPos)
{
lpCell = *lppCellPos;
if (0 != lpCell)
{
lpBuffer = lpCell->m_lpBroadcast2ndBuffer;
while(0 != lpBuffer)
{
size_t nBufferLen = lpBuffer->length();
if (szBufferPos + nBufferLen < szBufferEnd)
{
// 버퍼가 아직 비어 있다.
memcpy(szBufferPos, lpBuffer->rd_ptr(), nBufferLen);
szBufferPos += nBufferLen;
lpBuffer = lpBuffer->next();
}
else
{
SendBroadcastPacket(*this, g_BroadcastBuffer,
static_cast<unsigned short>(szBufferPos - g_BroadcastBuffer),
Broadcast2nd::PktBroadcast::BROADCAST,
Broadcast2nd::PktBroadcast::CHAR_DATA, dwCurrentPulse);
// 버퍼가 가득 찼다. 일단 전송하고, 버퍼 위치를 처음으로 돌린다.
szBufferPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
}
}
}
}
// 최종으로 보낸다. (보낼 데이터 길이가 없더라도 보내야 한다)
SendBroadcastPacket(*this, g_BroadcastBuffer,
static_cast<unsigned short>(szBufferPos - g_BroadcastBuffer),
Broadcast2nd::PktBroadcast::BROADCAST_END,
Broadcast2nd::PktBroadcast::CHAR_DATA, dwCurrentPulse);
}
}
void SendCharInfoToOthers(CCharacter& character,
CCell** lppSendCell, const int nMaxSendCell)
{
// 우선 병기에 타고 있어도 보낸다.
//if (!character.IsRideArms())
{
const unsigned short MAX_PKT_DATA = sizeof(Broadcast2nd::PktBroadcast) +
Broadcast2nd::CSerializeCharacterData::MAX_CHARACTER_DATA * 2;
char szPacketBuffer[MAX_PKT_DATA];
Broadcast2nd::PktBroadcast* lpBroadcast =
reinterpret_cast<Broadcast2nd::PktBroadcast*>(szPacketBuffer);
Broadcast2nd::CSerializeCharacterData& characterData = character.GetSerializeData();
unsigned short usCharacterDataLen = characterData.GetBroadcastDataLen();
// 좌표가 바뀌었기 때문에, 데이터를 갱신해서 보낸다 (Delta는 갱신하지 않는다)
characterData.PrepareBroadcastData(character);
lpBroadcast->m_dwCurrentPulse = 0;
lpBroadcast->m_cBroadcastType = Broadcast2nd::PktBroadcast::LOGIN;
lpBroadcast->m_cDataType = Broadcast2nd::PktBroadcast::CHAR_DATA;
memcpy(lpBroadcast + 1, characterData.GetBroadcastData(), usCharacterDataLen);
CCell** lppSendPos = lppSendCell;
CCell** lppSendEnd = lppSendCell + nMaxSendCell;
for (; lppSendPos != lppSendEnd; ++lppSendPos)
{
CCell* lpSendPos = *lppSendPos;
if (0 != lpSendPos)
{
lpSendPos->SendAllCharacter(lpBroadcast,
sizeof(Broadcast2nd::PktBroadcast) + usCharacterDataLen,
CmdCellBroadCast2nd);
}
}
}
}
//Interface/////////////////////////////////////////////////////////////////////////////////////
//
// 캐릭터 셀 로그인
//
// Parameter :
// 1st : 크리쳐 주소
// 2st : 마지막으로 로그아웃한 셀 (로그인, 리젠, 순간이동등의 경우는 0)
//
// Do :
// 셀안으로 캐릭터가 로그인했음을 주변 셀에 알려 준다.
//
// Return :
//
///////////////////////////////////////////////////////////////////////////////////////////////
void CCell::SendCellLogin(CAggresiveCreature* lpAggresiveCreature, CCell* lpLastLogoutCell)
{
if (0 != lpAggresiveCreature)
{
unsigned long dwCID = lpAggresiveCreature->GetCID();
// 현재 셀과, 마지막으로 로그아웃한 셀과의 차집합을 구한다.
// lpSendCell에는 내가 내 캐릭터 데이터를 보내고,
// 나에게 캐릭터 데이터를 보내야 할 녀석들이 들어 있다.
CCell* lpSendCell[CONNECT_NUM];
CCell** lppSendPos = 0;
CCell** lppSendEnd = 0;
CCell* lpSendPos = 0;
if (0 != lpLastLogoutCell)
{
std::fill_n(lpSendCell, int(CONNECT_NUM), reinterpret_cast<CCell*>(0));
std::set_difference(
m_lpSortedConnectedCell,
m_lpSortedConnectedCell + CONNECT_NUM,
lpLastLogoutCell->m_lpSortedConnectedCell,
lpLastLogoutCell->m_lpSortedConnectedCell + CONNECT_NUM,
lpSendCell);
}
else
{
std::copy(m_lpSortedConnectedCell, m_lpSortedConnectedCell + CONNECT_NUM, lpSendCell);
}
switch(Creature::GetCreatureType(dwCID))
{
case Creature::CT_PC:
{
CCharacter* lpCharacter = static_cast<CCharacter*>(lpAggresiveCreature);
// 내 정보를 다른 캐릭터들에게 보내는 루틴이다.
SendCharInfoToOthers(*lpCharacter, lpSendCell, CONNECT_NUM);
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (0 != lpDispatch)
{
// 주변의 캐릭터/몬스터 정보를 나에게 보낸다.
char* szDataPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
char* szDataEnd = g_BroadcastBuffer + PktMaxLen - 1024;
// 헤더 기록
Broadcast2nd::PktBroadcast* lpBroadcast =
reinterpret_cast<Broadcast2nd::PktBroadcast*>(g_BroadcastBuffer);
lpBroadcast->m_dwCurrentPulse = 0;
lpBroadcast->m_cBroadcastType = Broadcast2nd::PktBroadcast::LOGIN;
lpBroadcast->m_cDataType = Broadcast2nd::PktBroadcast::CHAR_DATA;
// 루프를 돈다.
lppSendPos = lpSendCell;
lppSendEnd = lpSendCell + CONNECT_NUM;
for (; lppSendPos != lppSendEnd; ++lppSendPos)
{
lpSendPos = *lppSendPos;
if (0 != lpSendPos)
{
// 데이터를 모아서 보낸다.
CCell::CharacterList::iterator char_pos = lpSendPos->m_lstCharacter.begin();
CCell::CharacterList::iterator char_end = lpSendPos->m_lstCharacter.end();
for (; char_pos != char_end; )
{
CCharacter* lpOtherCharacter = *char_pos;
Broadcast2nd::CSerializeCharacterData& characterData =
lpOtherCharacter->GetSerializeData();
unsigned short usCharacterDataLen =
characterData.GetBroadcastDataLen();
// 병기에 타고 있어서 보내지 않으면 문제가 생김(By Minbobo)
/*if (lpOtherCharacter->IsRideArms())
{
// 병기에 타고 있으면 보내지 않음.
++char_pos;
}*/
if (szDataPos + usCharacterDataLen < szDataEnd)
{
// 버퍼가 아직 남아 있으면, 버퍼링. 아니면 전송.
memcpy(szDataPos, characterData.GetBroadcastData(), usCharacterDataLen);
szDataPos += usCharacterDataLen;
++char_pos;
}
else
{
// 패킷을 보낸다.
lpDispatch->GetSendStream().WrapCompress(g_BroadcastBuffer,
static_cast<unsigned short>(szDataPos - g_BroadcastBuffer),
CmdCellBroadCast2nd, 0, 0);
// 버퍼 위치 초기화
szDataPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
}
}
// 데이터를 모아서 보낸다.
CCell::MonsterList::iterator mon_pos = lpSendPos->m_lstMonster.begin();
CCell::MonsterList::iterator mon_end = lpSendPos->m_lstMonster.end();
for (; mon_pos != mon_end; )
{
CMonster* lpMonster = *mon_pos;
Broadcast2nd::CSerializeMonsterData& monsterData =
lpMonster->GetSerializeData();
unsigned short usMonsterData = monsterData.GetBroadcastDataLen();
if (szDataPos + usMonsterData < szDataEnd)
{
// 버퍼가 아직 남아 있으면, 버퍼링. 아니면 전송.
memcpy(szDataPos, monsterData.GetBroadcastData(), usMonsterData);
szDataPos += usMonsterData;
++mon_pos;
}
else
{
// 패킷을 보낸다.
lpDispatch->GetSendStream().WrapCompress(g_BroadcastBuffer,
static_cast<unsigned short>(szDataPos - g_BroadcastBuffer),
CmdCellBroadCast2nd, 0, 0);
// 버퍼 위치 초기화
szDataPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
}
}
}
}
// 데이터가 있으면
if (g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast) < szDataPos)
{
// 패킷을 보낸다.
lpDispatch->GetSendStream().WrapCompress(g_BroadcastBuffer,
static_cast<unsigned short>(szDataPos - g_BroadcastBuffer),
CmdCellBroadCast2nd, 0, 0);
}
// 주변 셀의 아이템 데이터를 모아서 내 캐릭터에 보낸다.
// 이때, 버퍼 길이를 초과하지 않도록 한다. 셀당 최대치는 14k정도로 한다.
szDataPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
lpBroadcast->m_dwCurrentPulse = 0;
lpBroadcast->m_cBroadcastType = Broadcast2nd::PktBroadcast::LOGIN;
lpBroadcast->m_cDataType = Broadcast2nd::PktBroadcast::ITEM_DATA;
// 루프를 돈다.
lppSendPos = lpSendCell;
lppSendEnd = lpSendCell + CONNECT_NUM;
for (; lppSendPos != lppSendEnd; ++lppSendPos)
{
lpSendPos = *lppSendPos;
if (0 != lpSendPos)
{
// 데이터를 모아서 보낸다.
CCell::ItemList::iterator item_pos = lpSendPos->m_lstItem.begin();
CCell::ItemList::iterator item_end = lpSendPos->m_lstItem.end();
for (; item_pos != item_end; )
{
ItemInfo& itemInfo = (*item_pos);
// 버퍼가 아직 남아 있으면 버퍼링, 아니면 전송
if (szDataPos + sizeof(FieldObject) < szDataEnd)
{
itemInfo.MakeFieldObject(*reinterpret_cast<FieldObject*>(szDataPos));
szDataPos += sizeof(FieldObject);
++item_pos;
}
else
{
// 패킷을 보낸다.
lpDispatch->GetSendStream().WrapCompress(g_BroadcastBuffer,
static_cast<unsigned short>(szDataPos - g_BroadcastBuffer),
CmdCellBroadCast2nd, 0, 0);
// 버퍼 위치 초기화
szDataPos = g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast);
}
}
}
}
// 데이터가 있으면
if (szDataPos != g_BroadcastBuffer + sizeof(Broadcast2nd::PktBroadcast))
{
// 패킷을 보낸다.
lpDispatch->GetSendStream().WrapCompress(g_BroadcastBuffer,
static_cast<unsigned short>(szDataPos - g_BroadcastBuffer),
CmdCellBroadCast2nd, 0, 0);
}
}
}
break;
case Creature::CT_MONSTER:
case Creature::CT_SUMMON:
case Creature::CT_STRUCT:
{
CMonster* lpMonster = static_cast<CMonster*>(lpAggresiveCreature);
// 자신의 정보를 주변 캐릭터들에게 뿌린다.
const unsigned short MAX_PKT_DATA = sizeof(Broadcast2nd::PktBroadcast) +
Broadcast2nd::CSerializeMonsterData::MAX_MONSTER_DATA * 2;
char szPacketBuffer[MAX_PKT_DATA];
Broadcast2nd::PktBroadcast* lpBroadcast =
reinterpret_cast<Broadcast2nd::PktBroadcast*>(szPacketBuffer);
Broadcast2nd::CSerializeMonsterData& monsterData = lpMonster->GetSerializeData();
unsigned short usMonsterDataLen = monsterData.GetBroadcastDataLen();
// 좌표가 바뀌었기 때문에, 데이터를 갱신해서 보낸다 (Delta는 갱신하지 않는다)
monsterData.PrepareBroadcastData(*lpMonster);
lpBroadcast->m_dwCurrentPulse = 0;
lpBroadcast->m_cBroadcastType = Broadcast2nd::PktBroadcast::LOGIN;
lpBroadcast->m_cDataType = Broadcast2nd::PktBroadcast::CHAR_DATA;
memcpy(lpBroadcast + 1, monsterData.GetBroadcastData(), usMonsterDataLen);
lppSendPos = lpSendCell;
lppSendEnd = lpSendCell + CONNECT_NUM;
for (; lppSendPos != lppSendEnd; ++lppSendPos)
{
CCell* lpSendPos = *lppSendPos;
if (0 != lpSendPos)
{
lpSendPos->SendAllCharacter(lpBroadcast,
sizeof(Broadcast2nd::PktBroadcast) + usMonsterDataLen,
CmdCellBroadCast2nd);
}
}
}
break;
}
}
}
//Interface/////////////////////////////////////////////////////////////////////////////////////
//
// 캐릭터 셀 로그 아웃
//
// Parameter :
// 1st : 크리쳐 주소
// 2st : 로그아웃한 후 로그인하려는 셀 (로그아웃 등으로 없을 경우에는 0을 넣는다.)
//
// Do :
// 주변 셀에 내가 로그아웃한다고 알린다.
// 보이지 않아야 될 캐릭터들을 전부 지우게 한다.
//
// Return :
//
///////////////////////////////////////////////////////////////////////////////////////////////
void CCell::SendCellLogout(CAggresiveCreature* lpAggresiveCreature, CCell* lpPrepareLoginCell)
{
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
#ifndef _CELL_MANAGER_H_
#define _CELL_MANAGER_H_
#pragma once
#include <vector>
#include <Creature/CreatureStructure.h>
#include <Stream/Buffer/BufferFactory.h>
class CCell;
namespace VirtualArea
{
class CBGServerMgr;
}
class CBuffer;
class CCharacter;
class CCellManager
{
public:
static CCellManager& GetInstance();
void Destroy();
bool CheckPositionInZone(Position Pos);
void Load(void);
bool LoadComplete(void) { return m_bLoadComplete; }
bool SummonMonster(int nKID, Position Pos, CCharacter* lpMaster);
bool AdminSummonMonster(int nKID, Position Pos);
// Normal 상태에서 wMoving의 확률로 Moving. (디폴트는 20%)
// 이동을 20프로에서 80프로로 변경 2008.07.10
void SetMoving(bool bMoving, unsigned short wMoving = 20) { m_bMoving = bMoving; m_wNumMoving = wMoving; }
void SetAvoid(bool bAvoid) { m_bAvoid = bAvoid; }
bool IsMoving(void) const { return m_bMoving; }
bool IsAvoid(void) const { return m_bAvoid; }
unsigned short GetMovingNum(void) const { return m_wNumMoving; }
// 아이템 아이디로 셀 포인터 얻기
CCell* GetCell(unsigned __int64 nItemID);
// 셀 번호로 셀 포인터 얻기
CCell* GetCell(unsigned short wMapIndex, unsigned char cCellX, unsigned char cCellZ);
// 월드 좌표로 셀 포인터 얻기
CCell* GetCell(unsigned short wMapIndex, unsigned long dwPosX, unsigned long dwPosY, unsigned long dwPosZ);
CCell* GetCell(unsigned short wMapIndex, POS& Pos);
void LowerResolution(int nHighX, int nHighZ, int *nLowX, int *nLowZ);
void HigherResolution(int nLowX, int nLowZ, int *nHighX, int *nHighZ);
bool IsCampCreateArea(Position Pos);
bool IsSafetyZone(Position Pos);
CBufferFactory& GetBufferFactory() { return m_BufferFactory; }
template<typename FnRefCell>
inline bool ProcessAllCell(FnRefCell fnRefCell)
{
if (0 != m_CellData)
{
CCell* lpCellPos = m_CellData;
CCell* lpCellEnd = m_CellData + CCell::ms_CellSize * CCell::ms_CellSize;
for (; lpCellPos != lpCellEnd; ++lpCellPos)
{
fnRefCell(*lpCellPos);
}
return true;
}
return false;
}
template<typename FnRefCell, typename Arg1>
inline bool ProcessAllCell(FnRefCell fnRefCell, Arg1 arg1)
{
if (0 != m_CellData)
{
CCell* lpCellPos = m_CellData;
CCell* lpCellEnd = m_CellData + CCell::ms_CellSize * CCell::ms_CellSize;
for (; lpCellPos != lpCellEnd; ++lpCellPos)
{
fnRefCell(*lpCellPos, arg1);
}
return true;
}
return false;
}
// --------------------------------------------------------------------------------------------
// 셀 통계 내는 함수들
bool CheckCellAggresiveCreatures(void); // 셀 내에 겹치는 CID가 있는지 검사
bool CheckCellStatus(void); // 셀 내에 사용자 분포 출력
private:
enum
{
BGM_TEXTURE_SIZE = 128,
SECTOR_SIZE = 31508
};
// 클라이언트에서 사용중인 BGM트리거를 수정하였습니다. (같은 리소스 사용을 위해...)
struct SafetyZoneInfo
{
enum EventKey
{
EK_BGM_ONCE_SAFE = 0,
EK_BGM_LOOP_SAFE = 1,
EK_BGM_LOOP_UNSAFE = 2,
EK_BGM_ONCE_UNSAFE = 3,
EK_NOTBGM_SAFE = 4,
EK_BGM_TURN_AMB_SAFE = 5,
EK_BGM_TURN_AMB_UNSAFE = 6,
EK_CAMP_UNCREATE = 9,
EK_ESF_SAFE = 10,
EK_ESF_UNSAFE = 11
};
unsigned long m_dwSectorX;
unsigned long m_dwSectorY;
char m_szFilename[MAX_PATH];
unsigned long m_dwEventNum;
std::vector<unsigned long> m_vecEventKey;
std::vector<unsigned long> m_vecBGMColorKey;
unsigned long m_aryColorTable[BGM_TEXTURE_SIZE][BGM_TEXTURE_SIZE];
};
CCellManager();
~CCellManager();
bool InitAI(void);
bool CreateCell(void);
bool SetWeather(const char* szFileName);
bool LoginMonster(const char* szFileName, unsigned short wMapIndex = 0);
bool LoadSafetyZone(const char* szFileName);
private:
CCell* m_CellData;
unsigned char* m_WeatherRate;
unsigned short m_wNumMoving;
unsigned short m_usSummonCount;
CPoolBufferFactory m_BufferFactory;
std::vector<SafetyZoneInfo *> m_vecSafetyZone;
bool m_bMoving;
bool m_bAvoid;
bool m_bLoadComplete;
bool m_bPadding[1];
friend class VirtualArea::CBGServerMgr; // LoginMonster() 호출을 위해...
};
#endif

View File

@@ -0,0 +1,764 @@
#include "stdafx.h"
#include "MineralVeinMgr.h"
#include <Utility/DelimitedFile.h>
#include <Utility/Math/Math.h>
#include <Utility/Compress/MiniLZO/MiniLZOWrapper.h>
#include <Utility/Resource/EnsureCleanup.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
#define ENCODEHEADER(Start_In, Length_In, PageNum_In, PageVer_In) CXORCrypt::GetInstance().EncodeHeader((Start_In), (Length_In), (PageNum_In), (PageVer_In))
#define DECODEHEADER(Start_In, Length_In, PageNum_In, PageVer_In) CXORCrypt::GetInstance().DecodeHeader((Start_In), (Length_In), (PageNum_In), (PageVer_In))
#define COMPRESS(In, In_len, Out, Out_len) CMiniLZO::Compress((In), (In_len), (Out), (Out_len))
#define DECOMPRESS(In, In_len, Out, Out_len) CMiniLZO::Decompress((In), (In_len), (Out), (Out_len))
#include <Network/XORCrypt/XORCrypt.h>
// 클라이언트에서도 쓰이므로 include를 명시한다.
#include <Log/ServerLog.h>
#include "GMMemory.h"
CMineralVeinMgr& CMineralVeinMgr::GetInstance()
{
static CMineralVeinMgr ms_this;
return ms_this;
}
CMineralVeinMgr::CMineralVeinMgr()
{
Initialize();
}
CMineralVeinMgr::~CMineralVeinMgr()
{
Destroy();
}
bool CMineralVeinMgr::Initialize()
{
std::fill_n( m_dwColorTable[0], int(COLOR_IMAGE_SIZE * COLOR_IMAGE_SIZE), 0);
return true;
}
void CMineralVeinMgr::Destroy()
{
MineralVeinMap::iterator itr = m_mapMineralVein.begin();
MineralVeinMap::iterator end = m_mapMineralVein.end();
while ( itr != end )
{
if ( itr->second )
{
delete itr->second;
itr->second = NULL;
}
++itr;
}
m_mapMineralVein.clear();
}
bool CMineralVeinMgr::LoadMineralVeinsFromFiles(const char* szTextFile, const char* szImageFile)
{
Destroy() ;
// 텍스트 파일 체크
if ( !szTextFile )
{
ERRLOG0(g_Log, "광물 배치 스크립트 읽기 실패 : txt 파일이 존재하지 않습니다.");
return false;
}
// 이미지 파일명 체크
char szTempImageFile[ MAX_PATH ];
if ( !szImageFile )
{
strcpy(szTempImageFile, szTextFile);
if ( strtok(szTempImageFile, ".") )
{
strcat(szTempImageFile, ".tga");
FILE* fp = fopen(szTempImageFile, "rb");
if ( !fp )
{
ERRLOG1(g_Log, "광물 배치 스크립트 읽기 실패 : 이미지 파일 %s 가 존재하지 않습니다.", szTempImageFile);
return false;
}
fclose(fp);
}
else
{
ERRLOG1(g_Log, "광물 배치 스크립트 읽기 실패 : 파일명이 이상합니다. - %s", szTextFile);
return false;
}
}
else
{
strcpy(szTempImageFile, szImageFile);
}
char szTempBuf[ MAX_PATH ];
unsigned long dwTempValue;
int iLineCount = 0;
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
// 매크로에 로그 코드 삽입을 잊지 말 것.
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
#define READ_DATA(ColumnName, Argument) \
if (!DelimitedFile.ReadData(Argument)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", iLineCount, #ColumnName); \
return false; \
}
#define READ_STRING(ColumnName, Buffer, BufferSize) \
if (!DelimitedFile.ReadString(Buffer, BufferSize)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", iLineCount, #ColumnName); \
return false; \
}
// 텍스트 파일 로드
if (!DelimitedFile.Open(szTextFile))
{
ERRLOG1(g_Log, "광물 배치 스크립트 읽기 실패 : %s 파일이 존재하지 않습니다.", szTextFile);
return false;
}
MineralVeinInfo* pCurrentVeinInfo = NULL;
while ( DelimitedFile.ReadLine2nd() )
{
++iLineCount;
READ_STRING("TYPE", szTempBuf, MAX_TYPE_STRING_LEN);
if(stricmp(szTempBuf, "\r\n") == 0)
continue;
if (!stricmp(szTempBuf, "VEIN_COLOR"))
{
READ_STRING("VEIN_COLOR_VALUE", szTempBuf, MAX_HEXA_COLOR_LEN);
dwTempValue = Math::Convert::Atoi(szTempBuf);
MineralVeinMap::iterator itr = m_mapMineralVein.find(dwTempValue);
if (itr != m_mapMineralVein.end())
{
ERRLOG1(g_Log, "VeinColor : 0x%08x 중복되는 광맥 ID 가 존재합니다.", dwTempValue);
return false;
}
else
{
MineralVeinInfo* pVeinInfo = new MineralVeinInfo;
pVeinInfo->m_dwVeinColor = dwTempValue;
m_mapMineralVein.insert(std::make_pair(dwTempValue, pVeinInfo)).second ;
pCurrentVeinInfo = pVeinInfo;
}
}
else if (!stricmp(szTempBuf, "VEIN_NAME"))
{
if ( pCurrentVeinInfo )
{
READ_STRING("VEIN_NAME_VALUE", szTempBuf, MineralVeinInfo::MAX_VEIN_NAME_LEN);
strncpy(pCurrentVeinInfo->m_szVeinName, szTempBuf, strlen(szTempBuf)-1);
pCurrentVeinInfo->m_szVeinName[MineralVeinInfo::MAX_VEIN_NAME_LEN - 1] = '\0';
}
}
else if (!stricmp(szTempBuf, "FERTILITY"))
{
if ( pCurrentVeinInfo )
{
READ_DATA("FERTILITY_VALUE", pCurrentVeinInfo->m_dwMaxFertility);
}
}
else if (!stricmp(szTempBuf, "MINERAL"))
{
if ( pCurrentVeinInfo )
{
MineralInfo mineralInfo;
READ_DATA("MINERAL_ID", mineralInfo.m_dwMineralID);
READ_DATA("MIN_VALUE", mineralInfo.m_cMin);
READ_DATA("MAX_VALUE", mineralInfo.m_cMax);
pCurrentVeinInfo->m_lstMineralInfo.push_back(mineralInfo);
}
}
else
{
ERRLOG1(g_Log, "광물 배치 스크립트 로드 실패 : 존재하지 않는 타입이 있습니다. Type - %s", szTempBuf);
return false;
}
}
// 이미지 파일 로드
FILE* pTgaFile = fopen(szTempImageFile, "rb");
if ( !pTgaFile )
{
ERRLOG1(g_Log, "광물 배치 스크립트 읽기 실패 : 이미지 파일 %s 가 존재하지 않습니다.", szTempImageFile);
return false;
}
sTargaHeader tgaHeader;
bool bFlipVertical;
int iWidth, iHeight, iScanLineSize;
if (!fread(&tgaHeader, sizeof(sTargaHeader), 1, pTgaFile))
{
ERRLOG1(g_Log, "광물 배치 스크립트 읽기 실패 : TGA 파일(%s) 헤더 읽기 실패", szTempImageFile);
return false;
}
bFlipVertical = ((tgaHeader.ImageDescriptor & 0x20) != 0 );
tgaHeader.ImageDescriptor = tgaHeader.ImageDescriptor & 0xF;
iWidth = tgaHeader.Width;
iHeight = tgaHeader.Height;
iScanLineSize = iWidth * (tgaHeader.PixelSize / 8);
if ( bFlipVertical )
{
for (int i=0; i<iHeight; ++i)
{
fread(m_dwColorTable[i], iScanLineSize, 1, pTgaFile);
}
}
else
{
for (int i=iHeight-1; i>=0; --i)
{
fread(m_dwColorTable[i], iScanLineSize, 1, pTgaFile);
}
}
fclose( pTgaFile );
return true;
}
bool CMineralVeinMgr::SaveMineralVeinsToText(const char* szScriptFile)
{
if ( m_mapMineralVein.empty() )
{
return false;
}
size_t tempSize = 0;
unsigned long dwVeinCount = static_cast<unsigned long>(m_mapMineralVein.size());
MineralVeinMap::iterator itr = m_mapMineralVein.begin();
MineralVeinMap::iterator end = m_mapMineralVein.end();
FILE* fp = fopen(szScriptFile, "wt");
while (itr != end)
{
MineralVeinInfo* pVeinInfo = itr->second;
if ( pVeinInfo )
{
fprintf(fp, "VEIN_COLOR 0x%08x \n", pVeinInfo->m_dwVeinColor);
fprintf(fp, "VEIN_NAME %s \n", pVeinInfo->m_szVeinName);
fprintf(fp, "FERTILITY %d \n", pVeinInfo->m_dwMaxFertility);
// 광물 정보 저장
MineralInfoList::iterator listItr = pVeinInfo->m_lstMineralInfo.begin();
MineralInfoList::iterator listEnd = pVeinInfo->m_lstMineralInfo.end();
while ( listItr != listEnd )
{
const MineralInfo& mineralInfo = (*listItr);
fprintf(fp, "MINERAL %d %d %d \n", mineralInfo.m_dwMineralID, mineralInfo.m_cMin, mineralInfo.m_cMax);
++listItr;
}
fprintf(fp, "\n");
}
++itr;
}
fclose(fp);
// 이미지 파일명 체크
char szTempImageFile[ MAX_PATH ];
strcpy(szTempImageFile, szScriptFile);
if ( strtok(szTempImageFile, ".") )
{
strcat(szTempImageFile, ".tga");
}
sTargaHeader tgaHeader;
ZeroMemory(&tgaHeader, sizeof(sTargaHeader));
tgaHeader.ImageType = 2;
tgaHeader.Width = COLOR_IMAGE_SIZE;
tgaHeader.Height = COLOR_IMAGE_SIZE;
tgaHeader.PixelSize = 32;
tgaHeader.ImageDescriptor = 1;
bool bFlipVertical;
int iWidth, iHeight, iScanLineSize;
bFlipVertical = ((tgaHeader.ImageDescriptor & 0x20) != 0 );
tgaHeader.ImageDescriptor = tgaHeader.ImageDescriptor & 0xF;
iWidth = tgaHeader.Width;
iHeight = tgaHeader.Height;
iScanLineSize = iWidth * (tgaHeader.PixelSize / 8);
fp = fopen(szTempImageFile, "wb");
fwrite(&tgaHeader, sizeof(sTargaHeader), 1, fp);
if ( bFlipVertical )
{
for (int i=0; i<iHeight; ++i)
{
fwrite(m_dwColorTable[i], iScanLineSize, 1, fp);
}
}
else
{
for (int i=iHeight-1; i>=0; --i)
{
fwrite(m_dwColorTable[i], iScanLineSize, 1, fp);
}
}
fclose(fp);
return true;
}
bool CMineralVeinMgr::SaveMineralVeinsToBinary(const char* szBinaryFile, const char* szTrashFile)
{
if ( m_mapMineralVein.empty() )
{
return false;
}
HANDLE hFile = CreateFile(szBinaryFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) { return false; }
CEnsureCloseHandle writeFile(hFile);
size_t tempSize = 0;
unsigned long dwVeinCount = static_cast<unsigned long>(m_mapMineralVein.size());
MineralVeinMap::iterator itr = m_mapMineralVein.begin();
MineralVeinMap::iterator end = m_mapMineralVein.end();
while (itr != end)
{
MineralVeinInfo* pVeinInfo = itr->second;
if ( pVeinInfo )
{
tempSize += pVeinInfo->m_lstMineralInfo.size() * sizeof(MineralInfo) + sizeof(unsigned long);
}
++itr;
}
tempSize += m_mapMineralVein.size() * sizeof(MineralVeinInfo) + sizeof(unsigned long);
tempSize += COLOR_IMAGE_SIZE * COLOR_IMAGE_SIZE * sizeof(unsigned long);
const size_t MAX_SCRIPT_FILE_SIZE = tempSize;
char *pInputBuffer = new char[MAX_SCRIPT_FILE_SIZE];
char *pOutputBuffer = new char[MAX_SCRIPT_FILE_SIZE];
CEnsureDeleteArray<char> input(pInputBuffer);
CEnsureDeleteArray<char> output(pOutputBuffer);
if (0 == pInputBuffer || 0 == pOutputBuffer)
{
return false;
}
char *InputStartPointer = pInputBuffer;
char *OutputStartPointer = pOutputBuffer;
unsigned long dwInputBufferSize = 0;
unsigned long dwOutputBufferSize = 0;
// MineralVeinInfo 갯수 저장
memcpy(pInputBuffer, &dwVeinCount, sizeof(unsigned long));
dwInputBufferSize += sizeof(unsigned long);
pInputBuffer += sizeof(unsigned long);
// Image 저장
for (int i=0; i<COLOR_IMAGE_SIZE; ++i)
{
memcpy(pInputBuffer, m_dwColorTable[i], sizeof(unsigned long) * COLOR_IMAGE_SIZE);
dwInputBufferSize += sizeof(unsigned long) * COLOR_IMAGE_SIZE;
pInputBuffer += sizeof(unsigned long) * COLOR_IMAGE_SIZE;
}
unsigned long dwMineralCount;
itr = m_mapMineralVein.begin();
end = m_mapMineralVein.end();
while ( itr != end )
{
MineralVeinInfo* pVeinInfo = itr->second;
if ( pVeinInfo )
{
// 광물수 저장
dwMineralCount = static_cast<unsigned long>( pVeinInfo->m_lstMineralInfo.size() );
memcpy(pInputBuffer, &dwMineralCount, sizeof(unsigned long));
dwInputBufferSize += sizeof(unsigned long);
pInputBuffer += sizeof(unsigned long);
// Vein 정보 저장
memcpy(pInputBuffer, pVeinInfo, 56); //sizeof(MineralVeinInfo));
dwInputBufferSize += 56; //sizeof(MineralVeinInfo);
pInputBuffer += 56; //sizeof(MineralVeinInfo);
// 광물 정보 저장
MineralInfoList::iterator listItr = pVeinInfo->m_lstMineralInfo.begin();
MineralInfoList::iterator listEnd = pVeinInfo->m_lstMineralInfo.end();
while ( listItr != listEnd )
{
const MineralInfo& mineralInfo = (*listItr);
memcpy(pInputBuffer, &mineralInfo, sizeof(MineralInfo));
dwInputBufferSize += sizeof(MineralInfo);
pInputBuffer += sizeof(MineralInfo);
++listItr;
}
}
++itr;
}
ENCODEHEADER(InputStartPointer, dwInputBufferSize, 0, 3);
COMPRESS(InputStartPointer, dwInputBufferSize, pOutputBuffer, &dwOutputBufferSize);
// 파일에 쓰기
unsigned long dwWritten = 0;
// 쓰레기(더미) 자료
HANDLE hTrashFile = CreateFile(szTrashFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hTrashFile == INVALID_HANDLE_VALUE)
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szTrashFile);
return false;
}
CEnsureCloseHandle trashFile(hTrashFile);
unsigned long dwRead = 0;
unsigned long dwFileHighSize = 0;
unsigned long dwFileSize = GetFileSize(hTrashFile, &dwFileHighSize);
char* lpAllocated = new char[dwFileSize];
if (NULL == lpAllocated)
{
ERRLOG0(g_Log, "메모리 할당에 실패하였습니다.");
return false;
}
CEnsureDeleteArray<char> allocated(lpAllocated);
if (false == ReadFile(hTrashFile, lpAllocated, dwFileSize, &dwRead, NULL))
{
ERRLOG0(g_Log, "쓰레기 파일을 읽을 수 없습니다.");
return false;
}
WriteFile(hFile, &dwFileSize, sizeof(unsigned long), &dwWritten, 0);
WriteFile(hFile, lpAllocated, dwFileSize, &dwWritten, 0);
// 올바른 자료
WriteFile(hFile, &dwInputBufferSize, sizeof(unsigned long), &dwWritten, 0);
WriteFile(hFile, pOutputBuffer, dwOutputBufferSize, &dwWritten, 0);
return true;
}
bool CMineralVeinMgr::LoadMineralVeinsFromBinary(const char* szBinaryFile)
{
Destroy() ;
HANDLE hFile = CreateFile(szBinaryFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) { return false; }
CEnsureCloseHandle readFile(hFile);
unsigned long dwRead = 0;
unsigned long dwFileHighSize = 0;
unsigned long dwFileSize = GetFileSize(hFile, &dwFileHighSize);
char* lpAllocated = new char[dwFileSize];
if (NULL == lpAllocated)
{
return false;
}
CEnsureDeleteArray<char> allocated(lpAllocated);
if (!ReadFile(hFile, lpAllocated, dwFileSize, &dwRead, NULL))
{
return false;
}
unsigned long dwHeaderSize = sizeof(unsigned long) + *reinterpret_cast<unsigned long*>(lpAllocated) + sizeof(unsigned long);
unsigned long dwBufferSize = *reinterpret_cast<unsigned long*>(lpAllocated + dwHeaderSize - sizeof(unsigned long));
char* lpBuffer = new char[dwBufferSize];
if (NULL == lpBuffer)
{
return false;
}
CEnsureDeleteArray<char> buffer(lpBuffer);
char* lpBufferStartPointer = lpBuffer;
DECOMPRESS(lpAllocated + dwHeaderSize, dwFileSize - dwHeaderSize, lpBuffer, &dwBufferSize);
DECODEHEADER(lpBuffer, dwBufferSize, 0, 3);
// MineralVein 갯수 얻어오기
unsigned long dwVeinCount = *reinterpret_cast<unsigned long*>(lpBuffer);
lpBuffer += sizeof(unsigned long);
// Image 읽기
for (int h=0; h<COLOR_IMAGE_SIZE; ++h)
{
memcpy(m_dwColorTable[h], lpBuffer, sizeof(unsigned long) * COLOR_IMAGE_SIZE);
lpBuffer += sizeof(unsigned long) * COLOR_IMAGE_SIZE;
}
for (int i=0; i< (int)dwVeinCount; ++i)
{
// 광물 갯수 읽기
unsigned long dwMineralCount = *reinterpret_cast<unsigned long*>(lpBuffer);
lpBuffer += sizeof(unsigned long);
MineralVeinInfo* lpVeinInfo = new MineralVeinInfo;
// 광맥 정보 얻기
MineralVeinInfo* lpTempVeinInfo = reinterpret_cast<MineralVeinInfo*>(lpBuffer);
lpBuffer += 56; // std::list doesnt have a static size between VS versions
if ( lpVeinInfo && lpTempVeinInfo )
{
// 광맥 정보 셋팅
lpVeinInfo->m_dwVeinColor = lpTempVeinInfo->m_dwVeinColor;
strcpy(lpVeinInfo->m_szVeinName, lpTempVeinInfo->m_szVeinName);
lpVeinInfo->m_dwMaxFertility = lpTempVeinInfo->m_dwMaxFertility;
lpVeinInfo->m_dwNowFertility = lpTempVeinInfo->m_dwMaxFertility;
for (int j=0; j<(int)dwMineralCount; ++j)
{
// 광물 정보 얻어와서 셋팅
MineralInfo* lpMineralInfo = reinterpret_cast<MineralInfo*>(lpBuffer);
dwBufferSize -= sizeof(MineralInfo);
lpBuffer += sizeof(MineralInfo);
lpVeinInfo->m_lstMineralInfo.push_back(*lpMineralInfo);
}
// 광맥 추가
m_mapMineralVein.insert(std::make_pair(lpVeinInfo->m_dwVeinColor, lpVeinInfo)).second;
}
else
{
ERRLOG0(g_Log, "광물 배치 스크립트 로드 실패 : 읽어온 정보 혹은 new 로 생성한 포인터가 NULL 입니다.");
return false;
}
}
return true;
}
MineralVeinInfo* CMineralVeinMgr::GetMineralVein(unsigned long dwVeinColor) const
{
if ( 0 != dwVeinColor )
{
MineralVeinMap::const_iterator itr = m_mapMineralVein.find(dwVeinColor);
if ( itr != m_mapMineralVein.end() )
{
return itr->second;
}
}
return NULL;
}
MineralVeinInfo* CMineralVeinMgr::GetMineralVein(float fX, float fZ) const
{
int iIdxX = static_cast<int>( (fX - 315) * 512 / 3465.f ) ;
int iIdxZ = static_cast<int>( 512 - (fZ - 315) * 512 / 3465.f ) ;
unsigned long dwVeinColor = m_dwColorTable[ iIdxZ ][ iIdxX ];
return GetMineralVein(dwVeinColor);
}
unsigned long CMineralVeinMgr::GetVeinColor(float fX, float fZ) const
{
int iIdxX = static_cast<int>( (fX - 315) * 512 / 3465.f ) ;
int iIdxZ = static_cast<int>( 512 - (fZ - 315) * 512 / 3465.f ) ;
return m_dwColorTable[ iIdxZ ][ iIdxX ];
}
const char* CMineralVeinMgr::GetVeinName(float fX, float fZ) const
{
MineralVeinInfo* pVeinInfo = GetMineralVein(fX, fZ);
if (pVeinInfo)
{
return pVeinInfo->m_szVeinName;
}
return NULL;
}
int CMineralVeinMgr::GetVeinNum() const
{
return static_cast<int>( m_mapMineralVein.size() );
}
void CMineralVeinMgr::InitializeMiningCampNum()
{
m_mapCampNum.clear();
}
void CMineralVeinMgr::CalculateMiningCampNum(float fX, float fZ)
{
MineralVeinInfo* lpVeinInfo = GetMineralVein(fX, fZ);
if ( lpVeinInfo )
{
VeinToCampNumMap::iterator itr = m_mapCampNum.find( lpVeinInfo->m_dwVeinColor );
if ( itr != m_mapCampNum.end() )
{
++(itr->second);
}
else
{
m_mapCampNum.insert( std::make_pair(lpVeinInfo->m_dwVeinColor, 1) ).second;
}
}
}
int CMineralVeinMgr::GetMiningCampNum(unsigned long dwVeinColor)
{
VeinToCampNumMap::iterator itr = m_mapCampNum.find( dwVeinColor );
if ( itr != m_mapCampNum.end() )
{
return itr->second;
}
return 0;
}
void CMineralVeinMgr::CalculateFertility(unsigned long dwProcessType)
{
if(PktProcessMining::TEMPORARY_PROCESS != dwProcessType)
return;
MineralVeinMap::iterator itr = m_mapMineralVein.begin();
MineralVeinMap::iterator end = m_mapMineralVein.end();
while ( itr != end )
{
MineralVeinInfo* lpVeinInfo = itr->second;
if ( lpVeinInfo )
{
VeinToCampNumMap::iterator pos = m_mapCampNum.find( itr->first );
if ( pos != m_mapCampNum.end() )
{
// 지력은 해당지역에서 마이닝을 하고 있는 캠프가 몇개냐에 따라서
// 해당 캠프 수만큼 준다.
if ( (unsigned long)pos->second > lpVeinInfo->m_dwNowFertility )
{
lpVeinInfo->m_dwNowFertility = 0;
}
else
{
lpVeinInfo->m_dwNowFertility -= pos->second;
}
}
else if (lpVeinInfo->m_dwNowFertility < lpVeinInfo->m_dwMaxFertility)
{
unsigned long dwAddValue = static_cast<unsigned long>(ceil(lpVeinInfo->m_dwMaxFertility * 0.1f));
if (dwAddValue + lpVeinInfo->m_dwNowFertility > lpVeinInfo->m_dwMaxFertility)
{
lpVeinInfo->m_dwNowFertility = lpVeinInfo->m_dwMaxFertility;
}
else
{
lpVeinInfo->m_dwNowFertility += dwAddValue;
}
}
}
++itr;
}
}
void CMineralVeinMgr::SerializeOutFertility(unsigned long* lpData_Out, unsigned short& wSize)
{
MineralVeinMap::iterator itr = m_mapMineralVein.begin();
MineralVeinMap::iterator end = m_mapMineralVein.end();
while ( itr != end )
{
MineralVeinInfo* lpVeinInfo = itr->second;
if ( lpVeinInfo )
{
*lpData_Out = lpVeinInfo->m_dwVeinColor;
++lpData_Out;
wSize += sizeof(unsigned long);
*lpData_Out = lpVeinInfo->m_dwNowFertility;
++lpData_Out;
wSize += sizeof(unsigned long);
}
++itr;
}
}
// DB 에서 얻어온 현재 지력값을 설정하는 함수
bool CMineralVeinMgr::SetNowFertility(unsigned short wNum, unsigned long* szDataFromDB)
{
unsigned long dwVeinColor, dwNowFertility;
for (int i=0; i<wNum; ++i)
{
dwVeinColor = *szDataFromDB;
++szDataFromDB;
dwNowFertility = *szDataFromDB;
++szDataFromDB;
SetNowFertility(dwVeinColor, dwNowFertility);
}
return true;
}
bool CMineralVeinMgr::SetNowFertility(unsigned long dwVeinColor, unsigned long dwFertility)
{
MineralVeinMap::iterator pos = m_mapMineralVein.find(dwVeinColor);
if (pos != m_mapMineralVein.end())
{
MineralVeinInfo* lpVeinInfo = pos->second;
lpVeinInfo->m_dwNowFertility = dwFertility;
return true;
}
return false;
}

View File

@@ -0,0 +1,102 @@
#ifndef __MINERAL_VEIN_MANAGER_H__
#define __MINERAL_VEIN_MANAGER_H__
#pragma once
#define g_MinveralVeinMgr CMineralVeinMgr::GetInstance()
#include <list>
#include <map>
// 광물 정보 구조체
struct MineralInfo
{
unsigned short m_dwMineralID; // 광물 Item ID
unsigned char m_cMin; // 광물 추출 Min
unsigned char m_cMax; // 광물 추출 Max
};
typedef std::list<MineralInfo> MineralInfoList;
// 광맥 정보 구조체
struct MineralVeinInfo
{
enum Length
{
MAX_VEIN_NAME_LEN = 32
};
char m_szVeinName[ MAX_VEIN_NAME_LEN ]; // 광맥 이름
unsigned long m_dwVeinColor; // 광맥의 색상 ID
unsigned long m_dwMaxFertility; // 광맥의 최대 지력
unsigned long m_dwNowFertility; // 광맥의 현재 지력 (DB 에서 얻어오는 값)
MineralInfoList m_lstMineralInfo; // 광물 리스트
};
typedef std::map<unsigned long, MineralVeinInfo*> MineralVeinMap;
// TGA 파일 헤더
#pragma pack (1)
struct sTargaHeader {
unsigned char IDLength, ColorMapType, ImageType ;
unsigned short ColorMapOrigin, ColorMapSize ;
unsigned char ColorMapEntrySize ;
unsigned short XOrigin, YOrigin, Width, Height ;
unsigned char PixelSize ;
unsigned char ImageDescriptor ;
};
#pragma pack (8)
// 광맥 관리 클래스
class CMineralVeinMgr
{
public:
enum Const
{
MAX_TYPE_STRING_LEN = 32,
MAX_HEXA_COLOR_LEN = 10,
COLOR_IMAGE_SIZE = 512
};
static CMineralVeinMgr& GetInstance();
bool Initialize();
void Destroy();
bool LoadMineralVeinsFromFiles(const char* szTextFile, const char* szImageFile = 0);
bool SaveMineralVeinsToText(const char* szScriptFile);
bool SaveMineralVeinsToBinary(const char* szBinaryFile, const char* szTrashFile = 0);
bool LoadMineralVeinsFromBinary(const char* szBinaryFile);
MineralVeinInfo* GetMineralVein(unsigned long dwVeinColor) const;
MineralVeinInfo* GetMineralVein(float fX, float fZ) const;
unsigned long GetVeinColor(float fX, float fZ) const;
const char* GetVeinName(float fX, float fZ) const;
int GetVeinNum() const;
// 지력 프로세스 함수
void InitializeMiningCampNum(); // 지력당 채굴기 수를 0 으로 리셋
void CalculateMiningCampNum(float fX, float fZ); // 채굴기 위치에 해당하는 지력의 요새 수를 증가해주는 함수
int GetMiningCampNum(unsigned long dwVeinColor); // 현재 지역의 채굴중인 채굴기 수를 리턴
void CalculateFertility(unsigned long dwProcessType); // 지력 업데이트
void SerializeOutFertility(unsigned long* lpData_Out, unsigned short& wSize);
// DB 에서 얻어온 현재 지력값을 설정하는 함수
bool SetNowFertility(unsigned short wNum, unsigned long* szDataFromDB);
bool SetNowFertility(unsigned long dwVeinColor, unsigned long dwFertility);
private:
typedef std::map<unsigned long, int> VeinToCampNumMap; // < VeinColor, MiningCampNum >
CMineralVeinMgr();
~CMineralVeinMgr();
MineralVeinMap m_mapMineralVein;
VeinToCampNumMap m_mapCampNum;
unsigned long m_dwColorTable[ COLOR_IMAGE_SIZE ][ COLOR_IMAGE_SIZE ];
};
#endif // __MINERAL_VEIN_MANAGER_H__

View File

@@ -0,0 +1,336 @@
#include "stdafx.h"
#include "VirtualArea.h"
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Creature/Monster/VirtualMonsterMgr.h>
using namespace VirtualArea;
CVirtualArea::CVirtualArea(const VirtualArea::ProtoType* lpProtoType, unsigned short wMapIndex)
: m_CellData(NULL), m_dwVID(0), m_wMapIndex(wMapIndex), m_pVirtualMonsterMgr(NULL)
{
m_CharacterList.clear();
m_SpectatorList.clear();
if (lpProtoType)
{
m_dwVID = lpProtoType->m_dwVID;
CreateCell(lpProtoType->m_wWidth, lpProtoType->m_wHeight, wMapIndex);
}
}
CVirtualArea::~CVirtualArea()
{
if (NULL != m_CellData)
{
delete [] m_CellData;
m_CellData = NULL;
}
m_CharacterList.clear();
m_SpectatorList.clear();
if (m_pVirtualMonsterMgr)
{
delete m_pVirtualMonsterMgr;
m_pVirtualMonsterMgr = NULL;
}
}
bool CVirtualArea::CreateCell(unsigned short wWidth, unsigned short wHeight, unsigned short wMapIndex)
{
int nX = 0;
int nZ = 0;
m_CellData = new CCell[wWidth * wHeight];
if (NULL == m_CellData)
{
ERRLOG0(g_Log, "셀을 할당하는데 실패했습니다.");
return false;
}
// Make Cell Link
for (nZ = 0; nZ < wHeight; ++nZ)
{
for (nX = 0; nX < wWidth; ++nX)
{
// UP, UpperLeft, UpperRight
if (nZ > 0)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::UP, &m_CellData[nX + (nZ - 1) * wWidth]);
if (nX > 0)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::UPPERLEFT, &m_CellData[nX - 1 + (nZ - 1) * wWidth]);
}
if (nX < wWidth - 1)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::UPPERRIGHT, &m_CellData[nX + 1 + (nZ - 1) * wWidth]);
}
}
// Down, DnLeft, DnRight
if (nZ < wWidth - 1)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::DOWN, &m_CellData[nX + (nZ + 1) * wWidth]);
if (nX > 0)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::LOWERLEFT, &m_CellData[nX - 1 + (nZ + 1) * wWidth]);
}
if (nX < wWidth - 1)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::LOWERRIGHT, &m_CellData[nX + 1 + (nZ + 1) * wWidth]);
}
}
// Left
if (nX > 0)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::LEFT, &m_CellData[nX - 1 + nZ * wWidth]);
}
// Right
if (nX < wWidth - 1)
{
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::RIGHT, &m_CellData[nX + 1 + nZ * wWidth]);
}
// itself
m_CellData[nX + nZ * wWidth].SetConnectCell(CCell::NO, &m_CellData[nX + nZ * wWidth]);
}
}
// 2004 / 06 / 14 : sparrowhawk changed.
// 링크를 생성한 후 초기화를 해야 한다.
for (nZ = 0; nZ < wHeight; ++nZ)
{
for (nX = 0; nX < wWidth; ++nX)
{
m_CellData[nX + nZ * wWidth].Initialize(nX, nZ);
m_CellData[nX + nZ * wWidth].SetMapIndex(wMapIndex);
}
}
return true;
}
void CVirtualArea::CreateVirtualMonsterManager()
{
if (m_pVirtualMonsterMgr)
{
delete m_pVirtualMonsterMgr;
m_pVirtualMonsterMgr = NULL;
}
m_pVirtualMonsterMgr = new CVirtualMonsterMgr();
}
void CVirtualArea::ProcessAllMonster()
{
if (m_pVirtualMonsterMgr)
{
m_pVirtualMonsterMgr->ProcessAllMonster();
}
}
void CVirtualArea::ProcessMonsterRegenHPAndMP()
{
if (m_pVirtualMonsterMgr)
{
m_pVirtualMonsterMgr->ProcessMonsterRegenHPAndMP();
}
}
void CVirtualArea::ProcessSummonMonsterDead()
{
if (m_pVirtualMonsterMgr)
{
m_pVirtualMonsterMgr->ProcessSummonMonsterDead();
}
}
void CVirtualArea::ProcessDeleteItem()
{
if (NULL == m_CellData)
{
return;
}
CCell* lpCellPastEnd = m_CellData + GetWidth() * GetHeight();
for (CCell* lpCell = m_CellData; lpCell != lpCellPastEnd; ++lpCell)
{
lpCell->CheckDeleteItem();
}
}
bool CVirtualArea::ProcessAllCellPrepareBroadCast()
{
if (NULL == m_CellData)
{
return false;
}
CCell* lpCellPastEnd = m_CellData + GetWidth() * GetHeight();
for (CCell* lpCell = m_CellData; lpCell != lpCellPastEnd; ++lpCell)
{
lpCell->PrepareBroadCast();
}
return true;
}
bool CVirtualArea::ProcessAllCellBroadCast(unsigned long dwCurrentPulse)
{
if (NULL == m_CellData)
{
return false;
}
CCell* lpCellPastEnd = m_CellData + GetWidth() * GetHeight();
for (CCell* lpCell = m_CellData; lpCell != lpCellPastEnd; ++lpCell)
{
lpCell->BroadCast(dwCurrentPulse);
}
return true;
}
unsigned short CVirtualArea::GetStartX()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 0;
}
return pProtoType->m_wStartX;
}
unsigned short CVirtualArea::GetStartZ()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 0;
}
return pProtoType->m_wStartZ;
}
unsigned short CVirtualArea::GetWidth()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 0;
}
return pProtoType->m_wWidth;
}
unsigned short CVirtualArea::GetHeight()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 0;
}
return pProtoType->m_wHeight;
}
unsigned char CVirtualArea::GetVirtualZone()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 0;
}
return pProtoType->m_cZone;
}
Position CVirtualArea::GetStartPosition(unsigned char cNation)
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return Position(0, 0, 0);
}
if (cNation >= CClass::MAX_RACE)
{
ERRLOG1(g_Log, "VirtualArea StartPos 얻기 실패!! Nation : %d", cNation);
return Position(0, 0, 0);
}
return pProtoType->m_StartPos[cNation];
}
Position CVirtualArea::GetRespawnPosition(unsigned char cNation, int nIndex)
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return Position(0, 0, 0);
}
if (cNation >= CClass::MAX_RACE || nIndex < 0 || nIndex >= VirtualArea::MAX_VIRTUAL_AREA_RESPAWN_POINT)
{
ERRLOG3(g_Log, "VirtualArea RespawnPos 얻기 실패!! Nation : %d, Index : %d, MAX_VIRTUAL_AREA_RESPAWN_POINT : %d",
cNation, nIndex, VirtualArea::MAX_VIRTUAL_AREA_RESPAWN_POINT);
return Position(0, 0, 0);
}
return pProtoType->m_RespawnPos[cNation][nIndex];
}
unsigned char CVirtualArea::GetMaxRespawnPos()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return 1;
}
return pProtoType->m_cMaxRespawnPos;
}
const char* CVirtualArea::GetMapTypeName()
{
const VirtualArea::ProtoType* pProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType(m_dwVID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "VID : 0x%x VirtualAreaProtoType 을 얻기 실패!!", m_dwVID);
return "에러야!!";
}
return pProtoType->m_szMapType;
}

View File

@@ -0,0 +1,89 @@
#ifndef _VIRTUAL_AREA_H_
#define _VIRTUAL_AREA_H_
#pragma once
#include <Map/FieldMap/Cell.h>
// 전방 참조
class CCharacter;
class CMonster;
class CVirtualMonsterMgr;
namespace VirtualArea
{
// 전방 참조
struct ProtoType;
class CVirtualAreaMgr;
class CVirtualArea
{
public:
virtual void Process() { }
virtual unsigned short Enter(CCharacter* lpCharacter, unsigned char cMoveType) { return 0; }
virtual bool Leave(CCharacter* lpCharacter) { return false; }
unsigned long GetVID() { return m_dwVID; }
CCell* GetCell(int nIndex) { return &m_CellData[nIndex]; }
unsigned short GetVirtualType() { return static_cast<unsigned short>(m_dwVID >> 16); }
unsigned short GetMapIndex() { return m_wMapIndex; }
void CreateVirtualMonsterManager();
CVirtualMonsterMgr* GetMonsterManager() { return m_pVirtualMonsterMgr; }
// 스크립트에서 읽은 프로토 타입의 값을 리턴해주는 함수들
unsigned short GetStartX();
unsigned short GetStartZ();
unsigned short GetWidth();
unsigned short GetHeight();
unsigned char GetVirtualZone();
Position GetStartPosition(unsigned char cNation);
Position GetRespawnPosition(unsigned char cNation, int nIndex);
unsigned char GetMaxRespawnPos();
const char* GetMapTypeName();
// Type 정의
typedef std::list<CCharacter* > CharacterList;
typedef std::list<CMonster* > MonsterList;
// 몬스터 처리
void ProcessAllMonster();
void ProcessMonsterRegenHPAndMP();
void ProcessSummonMonsterDead();
// Item 처리
void ProcessDeleteItem();
// Cell BroadCasting
bool ProcessAllCellPrepareBroadCast();
bool ProcessAllCellBroadCast(unsigned long dwCurrentPulse);
protected:
CVirtualArea(const VirtualArea::ProtoType* lpProtoType, unsigned short wMapIndex);
virtual ~CVirtualArea();
virtual unsigned short AddCharacter(CCharacter* lpCharacter) { return 0; }
virtual unsigned short AddSpectator(CCharacter* lpSpectator) { return 0; }
bool CreateCell(unsigned short wWidth, unsigned short wHeight, unsigned short wMapIndex);
CCell* m_CellData;
unsigned long m_dwVID;
unsigned short m_wMapIndex;
CharacterList m_CharacterList;
CharacterList m_SpectatorList;
CVirtualMonsterMgr* m_pVirtualMonsterMgr;
friend class CVirtualAreaMgr;
};
}
#endif // _VIRTUAL_AREA_H_

View File

@@ -0,0 +1,89 @@
#ifndef __VIRTUAL_AREA_CONSTANTS_H__
#define __VIRTUAL_AREA_CONSTANTS_H__
#pragma once
// 헤더 파일 빼기
#include <Creature/CreatureStructure.h>
#include <Creature/Character/CharacterClass.h>
namespace VirtualArea
{
enum VirtualAreaType
{
// MapIndex 에 들어가는 Flag 와 VID(VirtualAreaID) 에 사용한다.
BGSERVERMAP = 0x8000,
DUELMAP = 0x4000,
DUNGEON = 0x2000
};
enum MapType
{
FRAG = 0, // 스코어 경쟁 게임 방
STATUE = 1, // 석상 점령 게임 방
MAX_MAP_TYPE
};
enum MoveType
{
TYPE_PLAYER = 0, // 게임 참전자
TYPE_SPECTATOR = 1, // 게임 관전자
MAX_ENTER_TYPE
};
enum Status
{
START_WAITING = 0, // GameStart() 를 호출하기 전까지...
GAME_PLAYING = 1, // 게임 진행 상태
GAME_RESTING = 2, // 게임이 끝나고 휴식 상태
MOVEZONE_WAITING = 3, // 게임이 끝나고, 존 이동될때까지의 상태
MAX_STATUS_TYPE = 4
};
enum Score
{
FRAG_SCORE = 1,
HOSTILITY_STATUE_SCORE = 0,
NEUTRALITY_STATUE_SCORE = 0,
FRIENDLY_STATUE_SCORE = 2,
FRIENDLY_LOADING_STATUE_SCORE = 1
};
enum Const
{
MAX_MAP_TYPE_NAME = 32,
MAX_FILE_NAME = 128,
MAX_VIRTUAL_AREA_RESPAWN_POINT = 3,
MAX_LOBBY_RESPAWN_POS = 2,
MILLISEC_PER_MINUTE = 60000, // 1분 (60000 ms)
MILEAGE_PER_MINUTE_FOR_WIN = 20, // 1분 공헌메달 보상 수치 (이긴 경우)
MILEAGE_PER_MINUTE_FOR_DRAW = 10, // 1분 공헌메달 보상 수치 (비긴 경우)
MOVEZONE_WAIT_TIME = 10000, // 10초 후 존이동
};
// 초기값
const unsigned char DefaultMaxCharacterNumOfNation[MAX_MAP_TYPE] = { 60, 120 }; // 60명, 120명
const unsigned short DefaultTargetScore[MAX_MAP_TYPE] = { 500, 6 }; // 500점, 6점
const unsigned char DefaultLimitMin[MAX_MAP_TYPE] = { 25, 50 }; // 25분, 50분
const unsigned char DefaultRestMin[MAX_MAP_TYPE] = { 5, 10 }; // 5분, 10분
// BATTLE_SERVER 대기존 리스폰 좌표
const POS RespawnPos[CClass::MAX_RACE][MAX_LOBBY_RESPAWN_POS] =
{ // Human
{ { 2165, 1135, 1005 }, { 1727, 1135, 1005 } },
// Akhan
{ { 2119, 1132, 1841 }, { 1683, 1132, 1841 } }
};
static const char* ms_szVirtualAreaScriptFileName = "./Script/Game/VirtualAreaScript.txt";
}
#endif // __VIRTUAL_AREA_CONSTANTS_H__

View File

@@ -0,0 +1,444 @@
#include "stdafx.h"
#include "VirtualAreaMgr.h"
#include <Utility/Math/Math.h>
#include <Utility/DelimitedFile.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/BGServer/BGServerMgr.h>
#include <Map/FieldMap/VirtualArea/Duel/DuelMgr.h>
#include <Map/FieldMap/VirtualArea/Dungeon/DungeonMgr.h>
#include <Creature/Character/Character.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
using namespace VirtualArea;
CVirtualAreaMgr& CVirtualAreaMgr::GetInstance()
{
static CVirtualAreaMgr ms_this;
return ms_this;
};
CVirtualAreaMgr::CVirtualAreaMgr()
: m_BGServerMgr(CBGServerMgr::GetInstance()),
m_DuelMgr(CDuelMgr::GetInstance()),
m_DungeonMgr(CDungeonMgr::GetInstance()),
m_VirtualAreaProtoTypeArray(NULL), m_VirtualAreaProtoTypeNum(0)
{
}
CVirtualAreaMgr::~CVirtualAreaMgr()
{
DestroyVirtualAreaProtoTypeArray();
}
// Script 파일 로드
bool CVirtualAreaMgr::LoadVirtualAreaProtoType(const char* szFileName)
{
int nIndex = 0;
int nLineCount = 0;
char strTemp[MAX_PATH];
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
std::vector<VirtualArea::ProtoType> virtualAreaProtoTypeVector;
virtualAreaProtoTypeVector.reserve(10);
VirtualArea::ProtoType tempProtoType;
// 매크로에 로그 코드 삽입을 잊지 말 것.
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
#define READ_DATA(ColumnName, Argument) \
if (!DelimitedFile.ReadData(Argument)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_STRING(ColumnName, Buffer, BufferSize) \
if (!DelimitedFile.ReadString(Buffer, BufferSize)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_POSITION(ColumnName, Pos) \
if (!DelimitedFile.ReadData(Pos.m_fPointX)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
} \
if (!DelimitedFile.ReadData(Pos.m_fPointY)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
} \
if (!DelimitedFile.ReadData(Pos.m_fPointZ)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_DATA_ARRAY(ColumnName, Argument, ArgumentNum) \
for (nIndex=0; nIndex < ArgumentNum; ++nIndex) { \
READ_DATA(ColumnName, Argument[nIndex]); \
}
#define READ_DATA_BOOL(ColumnName, Argument) \
if (!DelimitedFile.ReadString(strTemp, MAX_PATH)) { \
ERRLOG2(g_Log, "Virtual Area 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
} \
Argument = (!strcmp(strTemp, "O")) ? true : false;
if (!DelimitedFile.Open(szFileName ? szFileName : ms_szVirtualAreaScriptFileName))
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szFileName ? szFileName : ms_szVirtualAreaScriptFileName);
return false;
}
while (DelimitedFile.ReadLine())
{
++nLineCount;
// 순서가 바뀌면 곤란하다니깐~!!! (버럭!)
READ_STRING("VID", strTemp, MAX_PATH);
tempProtoType.m_dwVID = Math::Convert::Atoi(strTemp);
READ_STRING("MapType", tempProtoType.m_szMapType, VirtualArea::MAX_MAP_TYPE_NAME);
tempProtoType.m_cMapType = m_MapTypeMatching.m_matchMap.find(tempProtoType.m_szMapType)->second;
READ_DATA("Zone", tempProtoType.m_cZone);
READ_DATA("StartX", tempProtoType.m_wStartX);
READ_DATA("StartZ", tempProtoType.m_wStartZ);
READ_DATA("Width", tempProtoType.m_wWidth);
READ_DATA("Height", tempProtoType.m_wHeight);
READ_STRING("ArrangementFile", strTemp, MAX_PATH);
if (0 == stricmp(strTemp, "N/A"))
{
tempProtoType.m_szArrangementFileName[0] = '\0';
}
else
{
strcpy(tempProtoType.m_szArrangementFileName, strTemp);
}
READ_POSITION("StartPosition(HUMAN)", tempProtoType.m_StartPos[CClass::HUMAN]);
READ_POSITION("StartPosition(AKHAN)", tempProtoType.m_StartPos[CClass::AKHAN]);
tempProtoType.m_cMaxRespawnPos = 1;
READ_POSITION("RespawnPos1(HUMAN)", tempProtoType.m_RespawnPos[CClass::HUMAN][0]);
READ_POSITION("RespawnPos2(HUMAN)", tempProtoType.m_RespawnPos[CClass::HUMAN][1]);
READ_POSITION("RespawnPos3(HUMAN)", tempProtoType.m_RespawnPos[CClass::HUMAN][2]);
READ_POSITION("RespawnPos1(AKHAN)", tempProtoType.m_RespawnPos[CClass::AKHAN][0]);
READ_POSITION("RespawnPos2(AKHAN)", tempProtoType.m_RespawnPos[CClass::AKHAN][1]);
READ_POSITION("RespawnPos3(AKHAN)", tempProtoType.m_RespawnPos[CClass::AKHAN][2]);
for (int i=1; i<VirtualArea::MAX_VIRTUAL_AREA_RESPAWN_POINT; ++i)
{
if (tempProtoType.m_RespawnPos[CClass::HUMAN][i].m_fPointX != 0 &&
tempProtoType.m_RespawnPos[CClass::HUMAN][i].m_fPointZ != 0)
{
++tempProtoType.m_cMaxRespawnPos;
}
}
virtualAreaProtoTypeVector.push_back(tempProtoType);
}
std::sort(virtualAreaProtoTypeVector.begin(), virtualAreaProtoTypeVector.end());
for (std::vector<VirtualArea::ProtoType>::iterator itr = virtualAreaProtoTypeVector.begin();
itr != virtualAreaProtoTypeVector.end() - 1; ++itr)
{
if (itr->m_dwVID == (itr+1)->m_dwVID)
{
ERRLOG1(g_Log, "겹치는 VirtualArea ID가 있습니다. VID:%d", itr->m_dwVID);
return false;
}
}
m_VirtualAreaProtoTypeNum = virtualAreaProtoTypeVector.size();
m_VirtualAreaProtoTypeArray = new VirtualArea::ProtoType[m_VirtualAreaProtoTypeNum];
if (NULL == m_VirtualAreaProtoTypeArray)
{
ERRLOG0(g_Log, "VirtualArea 스크립트 초기화 실패 : 메모리 부족");
return false;
}
std::copy(virtualAreaProtoTypeVector.begin(), virtualAreaProtoTypeVector.end(), m_VirtualAreaProtoTypeArray);
return true;
}
void CVirtualAreaMgr::DestroyVirtualAreaProtoTypeArray()
{
m_VirtualAreaProtoTypeNum = 0;
if (0 != m_VirtualAreaProtoTypeArray)
{
delete [] m_VirtualAreaProtoTypeArray;
m_VirtualAreaProtoTypeArray = 0;
}
}
CVirtualArea* CVirtualAreaMgr::GetVirtualArea(unsigned short wMapIndex)
{
if (VirtualArea::BGSERVERMAP == (wMapIndex & VirtualArea::BGSERVERMAP))
{
return m_BGServerMgr.GetVirtualArea(wMapIndex);
}
else if (VirtualArea::DUELMAP == (wMapIndex & VirtualArea::DUELMAP))
{
}
else if (VirtualArea::DUNGEON == (wMapIndex & VirtualArea::DUNGEON))
{
}
return NULL;
}
class CFindProtoTypeFromVID : public std::unary_function<VirtualArea::ProtoType, bool>
{
public:
explicit CFindProtoTypeFromVID(unsigned long dwVID)
: m_dwVID(dwVID)
{ }
bool operator() (VirtualArea::ProtoType& protoType)
{
return (m_dwVID == protoType.m_dwVID);
}
private:
const unsigned long m_dwVID;
};
const VirtualArea::ProtoType* CVirtualAreaMgr::GetVirtualAreaProtoType(unsigned long dwVID)
{
VirtualArea::ProtoType* lpProtoType = NULL;
if (0 != dwVID)
{
CFindProtoTypeFromVID findVID(dwVID);
lpProtoType = std::find_if(&m_VirtualAreaProtoTypeArray[0], &m_VirtualAreaProtoTypeArray[m_VirtualAreaProtoTypeNum], findVID);
}
return lpProtoType;
}
const VirtualArea::ProtoType* CVirtualAreaMgr::GetVirtualAreaProtoType(char* szMapType)
{
for (size_t nIndex = 0; nIndex < m_VirtualAreaProtoTypeNum; nIndex++)
{
if (0 == strncmp(szMapType, m_VirtualAreaProtoTypeArray[nIndex].m_szMapType, VirtualArea::MAX_MAP_TYPE_NAME))
{
return m_VirtualAreaProtoTypeArray + nIndex;
}
}
return NULL;
}
bool CVirtualAreaMgr::EnterVirtualArea(CCharacter* lpCharacter, unsigned short wMapIndex, unsigned char cMoveType)
{
if (NULL == lpCharacter)
{
return false;
}
CVirtualArea* lpVirtualArea = GetVirtualArea(wMapIndex);
if (NULL == lpVirtualArea)
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 존재하지 않는 가상 존(MapIndex : %d)으로 이동하려고 합니다.", lpCharacter->GetCID(), wMapIndex);
return false;
}
if (VirtualArea::BGSERVERMAP == (wMapIndex & VirtualArea::BGSERVERMAP))
{
return m_BGServerMgr.Enter(lpCharacter, wMapIndex, cMoveType);
}
else if (VirtualArea::DUELMAP == (wMapIndex & VirtualArea::DUELMAP))
{
}
else if (VirtualArea::DUNGEON == (wMapIndex & VirtualArea::DUNGEON))
{
}
return false;
}
bool CVirtualAreaMgr::LeaveVirtualArea(CCharacter* lpCharacter)
{
if (NULL == lpCharacter)
{
return false;
}
unsigned short wCurrentMapIndex = lpCharacter->GetMapIndex();
CVirtualArea* lpVirtualArea = GetVirtualArea(wCurrentMapIndex);
if (NULL == lpVirtualArea)
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 존재하지 않는 가상 존(MapIndex : %d)에서 나가려고 합니다.", lpCharacter->GetCID(), wCurrentMapIndex);
return false;
}
if (VirtualArea::BGSERVERMAP == (wCurrentMapIndex & VirtualArea::BGSERVERMAP))
{
return m_BGServerMgr.Leave(lpCharacter);
}
else if (VirtualArea::DUELMAP == (wCurrentMapIndex & VirtualArea::DUELMAP))
{
}
else if (VirtualArea::DUNGEON == (wCurrentMapIndex & VirtualArea::DUNGEON))
{
}
return false;
}
void CVirtualAreaMgr::ProcessAllVirtualArea()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.Process();
}
else
{
// m_DeulMgr.Process();
// m_DungeonMgr.Process();
}
}
void CVirtualAreaMgr::ProcessAllMonster()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessAllMonster();
}
else
{
// m_DeulMgr.ProcessAllMonster();
// m_DungeonMgr.ProcessAllMonster();
}
}
void CVirtualAreaMgr::ProcessMonsterRegenHPAndMP()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessMonsterRegenHPAndMP();
}
else
{
// m_DeulMgr.ProcessMonsterRegenHPAndMP();
// m_DungeonMgr.ProcessMonsterRegenHPAndMP();
}
}
void CVirtualAreaMgr::ProcessSummonMonsterDead()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessSummonMonsterDead();
}
else
{
// m_DuelMgr.ProcessSummonMonsterDead();
// m_DungeonMgr.ProcessSummonMonsterDead();
}
}
bool CVirtualAreaMgr::ProcessAllCellPrepareBroadCast()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessAllCellPrepareBroadCast();
}
else
{
// m_DuelMgr.ProcessAllCellPrepareBroadCast();
// m_DungeonMgr.ProcessAllCellPrepareBroadCast();
}
return true;
}
bool CVirtualAreaMgr::ProcessAllCellBroadCast(unsigned long dwCurrentPulse)
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessAllCellBroadCast(dwCurrentPulse);
}
else
{
// m_DuelMgr.ProcessAllCellBroadCast();
// m_DungeonMgr.ProcessAllCellBroadCast();
}
return true;
}
void CVirtualAreaMgr::ProcessDeleteItem()
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
m_BGServerMgr.ProcessDeleteItem();
}
else
{
// m_DuelMgr.ProcessDeleteItem();
// m_DungeonMgr.ProcessDeleteItem();
}
}
// --------------------------------------------------------------------------------------------
// BattleGround Server 관련 함수
bool CVirtualAreaMgr::CreateBGServer() // Battle Ground Server Map 과 Cell 생성
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
return m_BGServerMgr.CreateBGServerMap();
}
return false;
}
bool CVirtualAreaMgr::SendBGServerMapList(CCharacter* lpCharacter)
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
return m_BGServerMgr.SendMapList(lpCharacter);
}
return false;
}
bool CVirtualAreaMgr::SendBGServerResultList(CCharacter* lpCharacter)
{
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
return m_BGServerMgr.SendResultList(lpCharacter);
}
return false;
}

View File

@@ -0,0 +1,111 @@
#ifndef _VIRTUAL_AREA_MANAGER_H_
#define _VIRTUAL_AREA_MANAGER_H_
#pragma once
#include <vector>
#include <map>
#include <Map/FieldMap/VirtualArea/VirtualAreaStructure.h>
class CCell;
class CCharacter;
namespace VirtualArea
{
// 전방 참조
class CBGServerMgr;
class CDuelMgr;
class CDungeonMgr;
class CVirtualArea;
class CVirtualAreaMgr
{
public:
enum Const
{
VIRTUALAREA_PULSE = 20 // 2초에 한번씩 처리
};
static CVirtualAreaMgr& GetInstance();
// Script 파일 로드
bool LoadVirtualAreaProtoType(const char* szFileName = 0);
CVirtualArea* GetVirtualArea(unsigned short wMapIndex);
const VirtualArea::ProtoType* GetVirtualAreaProtoType(unsigned long dwVID);
const VirtualArea::ProtoType* GetVirtualAreaProtoType(char* szMapType);
bool EnterVirtualArea(CCharacter* lpCharacter, unsigned short wMapIndex, unsigned char cMoveType);
bool LeaveVirtualArea(CCharacter* lpCharacter);
void ProcessAllVirtualArea();
void ProcessAllMonster();
void ProcessMonsterRegenHPAndMP();
void ProcessSummonMonsterDead();
void ProcessDeleteItem();
// Cell BroadCasting
bool ProcessAllCellPrepareBroadCast();
bool ProcessAllCellBroadCast(unsigned long dwCurrentPulse);
// --------------------------------------------------------------------------------------------
// BattleGround Server 관련 함수
bool CreateBGServer(); // Battle Ground Server Map 과 Cell 생성
bool SendBGServerMapList(CCharacter* lpCharacter);
bool SendBGServerResultList(CCharacter* lpCharacter);
// --------------------------------------------------------------------------------------------
// Duel 관련 함수
// --------------------------------------------------------------------------------------------
// Dungeon 관련 함수
private:
CVirtualAreaMgr();
~CVirtualAreaMgr();
void DestroyVirtualAreaProtoTypeArray();
// --------------------------------------------------------------------------------------------
// member variable
CBGServerMgr& m_BGServerMgr;
CDuelMgr& m_DuelMgr;
CDungeonMgr& m_DungeonMgr;
VirtualArea::ProtoType* m_VirtualAreaProtoTypeArray;
size_t m_VirtualAreaProtoTypeNum;
const VirtualArea::MapTypeMatching m_MapTypeMatching;
// --------------------------------------------------------------------------------------------
// BattleGround Server 변수
// --------------------------------------------------------------------------------------------
// Duel 변수
// --------------------------------------------------------------------------------------------
// Dungeon 변수
};
}
#endif // _VIRTUAL_AREA_MANAGER_H_

View File

@@ -0,0 +1,60 @@
#include "stdafx.h"
#include "VirtualAreaStructure.h"
VirtualArea::MapTypeMatching::MapTypeMatching()
{
m_matchMap.clear();
m_matchMap.insert(make_pair("BG_FRAG", VirtualArea::FRAG));
m_matchMap.insert(make_pair("BG_STATUE", VirtualArea::STATUE));
m_matchMap.insert(make_pair("DUEL_FRAG", VirtualArea::FRAG));
m_matchMap.insert(make_pair("DUEL_STATUE", VirtualArea::STATUE));
}
VirtualArea::MapInfo::MapInfo()
: m_cMapType(0), m_cMaxCharNumOfNation(0), m_cRemainPlayMin(0),
m_cRemainRestMin(0), m_wTargetScore(0), m_cLimitMin(0), m_cRestMin(0)
{
std::fill_n(m_cCurrentCharNum, int(CClass::MAX_RACE), 0);
std::fill_n(m_wScore, int(CClass::MAX_RACE), 0);
m_PersonalInfoMap.clear();
}
VirtualArea::MapInfo::MapInfo(unsigned char cMapType)
: m_cMapType(cMapType), m_cMaxCharNumOfNation(DefaultMaxCharacterNumOfNation[cMapType]),
m_cRemainPlayMin(DefaultLimitMin[cMapType]), m_cRemainRestMin(DefaultRestMin[cMapType]),
m_wTargetScore(DefaultTargetScore[cMapType]),
m_cLimitMin(DefaultLimitMin[cMapType]), m_cRestMin(DefaultRestMin[cMapType])
{
std::fill_n(m_cCurrentCharNum, int(CClass::MAX_RACE), 0);
std::fill_n(m_wScore, int(CClass::MAX_RACE), 0);
m_PersonalInfoMap.clear();
}
void VirtualArea::MapInfo::Initialize()
{
m_cRemainPlayMin = m_cLimitMin;
m_cRemainRestMin = m_cRestMin;
std::fill_n(m_cCurrentCharNum, int(CClass::MAX_RACE), 0);
std::fill_n(m_wScore, int(CClass::MAX_RACE), 0);
m_PersonalInfoMap.clear();
}
VirtualArea::ResultInfo::ResultInfo()
{
Initialize();
}
void VirtualArea::ResultInfo::Initialize()
{
m_cWinRace = CClass::MAX_RACE;
std::fill_n(m_wScore, int(CClass::MAX_RACE), 0);
}

View File

@@ -0,0 +1,92 @@
#ifndef __VIRTUAL_AREA_STRUCTURE_H__
#define __VIRTUAL_AREA_STRUCTURE_H__
#pragma once
#include <map>
#include <Map/FieldMap/VirtualArea/VirtualAreaConstants.h>
using namespace std;
namespace VirtualArea
{
// VirtualArea Info
struct ProtoType
{
unsigned long m_dwVID;
unsigned char m_cMapType;
unsigned char m_cZone;
unsigned short m_wStartX;
unsigned short m_wStartZ;
unsigned short m_wWidth;
unsigned short m_wHeight;
unsigned char m_cMaxRespawnPos;
char m_szArrangementFileName[MAX_FILE_NAME];
char m_szMapType[MAX_MAP_TYPE_NAME];
Position m_StartPos[CClass::MAX_RACE];
Position m_RespawnPos[CClass::MAX_RACE][MAX_VIRTUAL_AREA_RESPAWN_POINT];
inline bool operator < (ProtoType& rhs)
{ return m_dwVID < rhs.m_dwVID; }
};
// 스크립트의 szMapType 과 cMapType 을 매칭시켜주기 위함
// BGServerMap 을 스크립트화 하지 않고, 하드코딩을 하기 때문에 매칭이 필요하다.
struct MapTypeMatching
{
std::map<std::string, unsigned char> m_matchMap;
MapTypeMatching();
};
// 방 정보
struct MapInfo
{
struct PersonalInfo
{
unsigned char m_cEnteringMin; // 들어온 시점의 남은 경기 시간
unsigned char m_cKill; // 내가 죽인 유저 수
unsigned char m_cKilled; // 내가 죽은 수
PersonalInfo(unsigned char cEnteringMin) : m_cEnteringMin(cEnteringMin), m_cKill(0), m_cKilled(0)
{
}
};
// 배틀 그라운드 하나의 방(가상 맵)이 가져야 하는 변수
unsigned char m_cMapType;
unsigned char m_cMaxCharNumOfNation; // 한 진영 최대 인원
unsigned char m_cRemainPlayMin; // 남은 경기 시간 (분 단위)
unsigned char m_cRemainRestMin; // 남은 쉬는 시간 (분 단위)
unsigned short m_wTargetScore;
unsigned char m_cCurrentCharNum[CClass::MAX_RACE];
unsigned short m_wScore[CClass::MAX_RACE];
unsigned char m_cLimitMin; // 시간 제한 (분 단위)
unsigned char m_cRestMin; // 쉬는 시간 (분 단위)
// 게임에 들어온 캐릭터의 개인 정보
typedef std::map<unsigned long, PersonalInfo> PersonalInfoMap;
PersonalInfoMap m_PersonalInfoMap;
MapInfo();
MapInfo(unsigned char cMapType);
void Initialize();
};
// 방의 결과 정보
struct ResultInfo
{
unsigned short m_wScore[CClass::MAX_RACE];
unsigned char m_cWinRace;
ResultInfo();
void Initialize();
};
}
#endif // __VIRTUAL_AREA_STRUCTURE_H__

View File

@@ -0,0 +1,873 @@
#include "stdafx.h"
#include <mmsystem.h>
#include <Utility/Math/Math.h>
#include <Creature/Character/Character.h>
#include <Creature/Monster/PatternMonster.h>
#include <Creature/Monster/VirtualMonsterMgr.h>
#include <Network/Dispatch/GameClient/SendCharBGServer.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/PacketStruct/BGServerPacket.h>
#include "BGServerMap.h"
using namespace VirtualArea;
CBGServerMap::CBGServerMap(const VirtualArea::ProtoType* lpProtoType, unsigned short wMapNumber)
: CVirtualArea(lpProtoType, VirtualArea::BGSERVERMAP | wMapNumber), m_MapInfo(lpProtoType->m_cMapType), m_cStatus(START_WAITING)
{
}
CBGServerMap::~CBGServerMap(void)
{
}
unsigned short CBGServerMap::Enter(CCharacter* lpCharacter, unsigned char cMoveType)
{
if (false == IsPlaying())
{
return PktBase::SERVER_ERROR;
}
switch (cMoveType)
{
case TYPE_PLAYER:
{
return AddCharacter(lpCharacter);
}
case TYPE_SPECTATOR:
{
return AddSpectator(lpCharacter);
}
}
return PktBase::SERVER_ERROR;
}
bool CBGServerMap::Leave(CCharacter* lpCharacter)
{
if (NULL == lpCharacter) return false;
char szNation[8];
if (lpCharacter->GetRace() == CClass::HUMAN) strcpy(szNation, "HUMAN");
else strcpy(szNation, "AKHAN");
CharacterList::iterator pos = std::find(m_CharacterList.begin(), m_CharacterList.end(), lpCharacter);
if (pos != m_CharacterList.end())
{
--m_MapInfo.m_cCurrentCharNum[ lpCharacter->GetRace() ];
m_CharacterList.erase(pos);
m_MapInfo.m_PersonalInfoMap.erase( lpCharacter->GetCID() );
// 로그
DETLOG5(g_Log, "Battle Server Log :: (Channel : %d, %s) - CID : 0x%08x 캐릭터(%s, %s)가 게임에서 나가셨습니다.",
(m_wMapIndex & ~VirtualArea::BGSERVERMAP), GetMapTypeName(),
lpCharacter->GetCID(), lpCharacter->GetCharacterName(), szNation);
return true;
}
pos = std::find(m_SpectatorList.begin(), m_SpectatorList.end(), lpCharacter);
if (pos != m_SpectatorList.end())
{
m_SpectatorList.erase(pos);
// 로그
DETLOG5(g_Log, "Battle Server Log :: (Channel : %d, %s) - CID : 0x%08x 캐릭터(%s, %s)가 게임에서 나가셨습니다.",
(m_wMapIndex & ~VirtualArea::BGSERVERMAP), GetMapTypeName(),
lpCharacter->GetCID(), lpCharacter->GetCharacterName(), szNation);
return true;
}
return false;
}
unsigned short CBGServerMap::AddCharacter(CCharacter* lpCharacter)
{
if (NULL == lpCharacter)
{
return PktBase::SERVER_ERROR;
}
if (m_MapInfo.m_cMaxCharNumOfNation <= m_MapInfo.m_cCurrentCharNum[ lpCharacter->GetRace() ])
{
return PktBGServerMoveZone::FAIL_FULL_MAP;
}
// 진입 인원 제한 걸기
if (m_MapInfo.m_cCurrentCharNum[ lpCharacter->GetRace() ] >= 10)
{
if ( (lpCharacter->GetRace() == CClass::HUMAN && m_MapInfo.m_cCurrentCharNum[CClass::HUMAN] >= m_MapInfo.m_cCurrentCharNum[CClass::AKHAN] * 1.5) ||
(lpCharacter->GetRace() == CClass::AKHAN && m_MapInfo.m_cCurrentCharNum[CClass::AKHAN] >= m_MapInfo.m_cCurrentCharNum[CClass::HUMAN] * 1.5) )
{
return PktBGServerMoveZone::FAIL_FIX_RATE;
}
}
++m_MapInfo.m_cCurrentCharNum[ lpCharacter->GetRace() ];
m_CharacterList.push_back(lpCharacter);
// 들어왔을때 남은 경기시간을 저장해둔다. (남은 경기시간에 따른 차등 점수를 주기위해서...)
MapInfo::PersonalInfo personalInfo(m_MapInfo.m_cRemainPlayMin);
m_MapInfo.m_PersonalInfoMap.insert( make_pair(lpCharacter->GetCID(), personalInfo) );
// 로그 남기기
char szNation[8];
if (lpCharacter->GetRace() == CClass::HUMAN)
{
strcpy(szNation, "HUMAN");
}
else
{
strcpy(szNation, "AKHAN");
}
DETLOG5(g_Log, "Battle Server Log :: (Channel : %d, %s) - CID : 0x%08x 캐릭터(%s, %s)가 Player 로 들어왔습니다.",
(m_wMapIndex & ~VirtualArea::BGSERVERMAP), GetMapTypeName(),
lpCharacter->GetCID(), lpCharacter->GetCharacterName(), szNation);
return PktBase::NO_SERVER_ERR;
}
unsigned short CBGServerMap::AddSpectator(CCharacter* lpSpectator)
{
if (NULL == lpSpectator)
{
return PktBase::SERVER_ERROR;
}
m_SpectatorList.push_back(lpSpectator);
// 로그 남기기
char szNation[8];
if (lpSpectator->GetRace() == CClass::HUMAN) strcpy(szNation, "HUMAN");
else strcpy(szNation, "AKHAN");
DETLOG5(g_Log, "Battle Server Log :: (Channel : %d, %s) - CID : 0x%08x 캐릭터(%s, %s)가 Spectator 로 들어왔습니다.",
(m_wMapIndex & ~VirtualArea::BGSERVERMAP), GetMapTypeName(),
lpSpectator->GetCID(), lpSpectator->GetCharacterName(), szNation);
return PktBase::NO_SERVER_ERR;
}
bool CBGServerMap::IsPlayer(CCharacter* lpCharacter)
{
if (NULL == lpCharacter) return false;
CharacterList::iterator pos = std::find(m_CharacterList.begin(), m_CharacterList.end(), lpCharacter);
if (pos != m_CharacterList.end()) return true;
return false;
}
bool CBGServerMap::IsSpectator(CCharacter* lpCharacter)
{
if (NULL == lpCharacter) return false;
CharacterList::iterator pos = std::find(m_SpectatorList.begin(), m_SpectatorList.end(), lpCharacter);
if (pos != m_SpectatorList.end()) return true;
return false;
}
// 모두 대기실로 이동
bool CBGServerMap::AllRespawn()
{
if (0 == m_CharacterList.size() && 0 == m_SpectatorList.size()) return true;
bool bResult = true;
CharacterList::iterator pos = m_CharacterList.begin();
CharacterList::iterator end = m_CharacterList.end();
while (pos != end)
{
if (NULL == (*pos)) continue;
CGameClientDispatch* lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
if (false == GameClientSendPacket::SendCharBGServerMoveZone(lpDispatch->GetSendStream(), SERVER_ID::BATTLE_SERVER, VirtualArea::TYPE_PLAYER, PktBase::NO_SERVER_ERR))
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 나가는데 실패하였습니다.", (*pos)->GetCID(), (*pos)->GetMapIndex());
bResult = false;
}
Position RespawnPos(VirtualArea::RespawnPos[ (*pos)->GetRace() ][ Math::Random::ComplexRandom(VirtualArea::MAX_LOBBY_RESPAWN_POS) ]);
RespawnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
RespawnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
(*pos)->SetMapIndex(0);
(*pos)->GetEnchantInfo().ResetFlag(Skill::SpellID::Hide);
(*pos)->MoveTo(RespawnPos, false);
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 나가는데 실패하였습니다.", (*pos)->GetCID(), (*pos)->GetMapIndex());
bResult = false;
}
++pos;
}
// 스펙테이터 처리
pos = m_SpectatorList.begin();
end = m_SpectatorList.end();
while (pos != end)
{
if (NULL == (*pos)) continue;
CGameClientDispatch* lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
if (false == GameClientSendPacket::SendCharBGServerMoveZone(lpDispatch->GetSendStream(), SERVER_ID::BATTLE_SERVER, VirtualArea::TYPE_PLAYER, PktBase::NO_SERVER_ERR))
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 나가는데 실패하였습니다.", (*pos)->GetCID(), (*pos)->GetMapIndex());
bResult = false;
}
Position RespawnPos(VirtualArea::RespawnPos[ (*pos)->GetRace() ][ Math::Random::ComplexRandom(VirtualArea::MAX_LOBBY_RESPAWN_POS) ]);
RespawnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
RespawnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
(*pos)->SetMapIndex(0);
(*pos)->GetEnchantInfo().ResetFlag(Skill::SpellID::Hide);
(*pos)->MoveTo(RespawnPos, false);
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 나가는데 실패하였습니다.", (*pos)->GetCID(), (*pos)->GetMapIndex());
bResult = false;
}
++pos;
}
// 리스트 클리어
m_CharacterList.clear();
m_SpectatorList.clear();
return bResult;
}
void CBGServerMap::Process()
{
unsigned long dwCurrentTime = timeGetTime();
int nMin = 0, nSec = 0;
if (m_dwRemainTime < dwCurrentTime) nMin = 0;
else
{
nMin = (m_dwRemainTime - dwCurrentTime) / MILLISEC_PER_MINUTE;
nSec = (m_dwRemainTime - dwCurrentTime) % MILLISEC_PER_MINUTE;
if (nSec > 0) ++nMin;
}
switch (m_cStatus)
{
// 아직 Start 되지 않은 상태라면 아무것도 하지 않는다.
case START_WAITING: return;
case GAME_PLAYING:
{
// 1분에 한번씩 남은 시간을 보내준다.
if (nMin < m_MapInfo.m_cRemainPlayMin)
{
// 남은 시간 갱신
m_MapInfo.m_cRemainPlayMin = static_cast<unsigned char>(nMin);
// 방 정보 전송
SendMapInfo();
}
// 제한 시간이 다 되었다면,
if (0 == m_MapInfo.m_cRemainPlayMin)
{
// 승패를 체크 및 결과 저장
m_ResultInfo.m_cWinRace = RuleCheck(true);
std::copy(&m_MapInfo.m_wScore[0], &m_MapInfo.m_wScore[CClass::MAX_RACE], &m_ResultInfo.m_wScore[0]);
// 결과 전송
// 이긴 종족, 혹은 무승부 캐릭터들에게 보상을 준다.
SendResultInfo();
}
else
{
m_ResultInfo.m_cWinRace = RuleCheck();
// 한 종족이 목표 점수를 달성했을 경우
if (CClass::MAX_RACE != m_ResultInfo.m_cWinRace)
{
// 결과 점수 저장
std::copy(&m_MapInfo.m_wScore[0], &m_MapInfo.m_wScore[CClass::MAX_RACE], &m_ResultInfo.m_wScore[0]);
// 결과 전송
// 이긴 종족의 캐릭터들에게 보상을 준다.
SendResultInfo();
}
}
break;
}
case GAME_RESTING:
{
// 남은 시간 갱신
m_MapInfo.m_cRemainRestMin = static_cast<unsigned char>(nMin);
// 쉬는 시간이 다 되었다면,
if (0 == m_MapInfo.m_cRemainRestMin)
{
GameStart();
}
break;
}
case MOVEZONE_WAITING:
{
// 존이동 시킬 시간이 다 되었다면,
if (m_dwRemainTime <= timeGetTime())
{
SetStatus(GAME_RESTING);
m_dwRemainTime = timeGetTime() + m_MapInfo.m_cRestMin * MILLISEC_PER_MINUTE;
// 캐릭터들을 존 이동 시킨다. (로비로 리스폰)
AllRespawn();
// Item을 모두 지운다.
DeleteAllItem();
// 방 정보 초기화
m_MapInfo.Initialize();
}
break;
}
}
}
bool CBGServerMap::GameStart() // 게임을 시작한다.
{
m_dwRemainTime = timeGetTime() + m_MapInfo.m_cLimitMin * MILLISEC_PER_MINUTE;
InitializeGameObject();
SetStatus(GAME_PLAYING);
return true;
}
bool CBGServerMap::InitializeGameObject()
{
m_CharacterList.clear();
m_SpectatorList.clear();
m_MapInfo.Initialize();
m_ResultInfo.Initialize();
if (STATUE == m_MapInfo.m_cMapType && NULL != GetMonsterManager())
{
// 모든 석상을 다 죽이고, 아무것도 소환하지 않는다.
CVirtualMonsterMgr::MonsterMap::iterator pos = m_pVirtualMonsterMgr->GetMonsterMap().begin();
CVirtualMonsterMgr::MonsterMap::iterator end = m_pVirtualMonsterMgr->GetMonsterMap().end();
while (pos != end)
{
CMonster* lpMonster = pos->second;
if (NULL == lpMonster) continue;
CStatue* lpStatue = lpMonster->DowncastToStatue();
if (NULL != lpStatue)
{
lpStatue->Rest();
}
++pos;
}
// 휴먼, 중립, 아칸 석상을 순서대로 소환한다.
enum { HUMAN_STATUE = 0, NUETRALITY_STATUE1 = 1, AKHAN_STATUE = 2, NUETRALITY_STATUE2 = 3, NUETRALITY_STATUE3 = 4, NONE = 100 };
DWORD dwKind, dwOldKind = NONE;
pos = m_pVirtualMonsterMgr->GetMonsterMap().begin();
end = m_pVirtualMonsterMgr->GetMonsterMap().end();
while (pos != end)
{
CMonster* lpMonster = pos->second;
if (NULL == lpMonster) continue;
CStatue* lpStatue = lpMonster->DowncastToStatue();
if (NULL != lpStatue)
{
// 석상 초기화 시키기
// !!주의!! 스크립트와 관련이 있다. 순서주의
// 종류를 얻어온다. (HUMAN_STATUE = 0, NUETRALITY_STATUE1 = 1, AKHAN_STATUE = 2, NUETRALITY_STATUE2 = 3, NUETRALITY_STATUE3 = 4)
dwKind = (lpStatue->GetCID() & ~(Creature::MONSTER_BIT | Creature::MONSTER_KIND_BIT)) >> 16;
if (dwOldKind != dwKind)
{
switch (dwKind)
{
case HUMAN_STATUE:
lpStatue = lpStatue->GetLinkStatue(MonsterInfo::BG_STATUE_HUMAN_COMPLETE1);
lpStatue->InitMonster(lpStatue->GetOriginalPos());
break;
case NUETRALITY_STATUE1:
case NUETRALITY_STATUE2:
case NUETRALITY_STATUE3:
lpStatue = lpStatue->GetLinkStatue(MonsterInfo::BG_STATUE_NEUTRALITY1);
lpStatue->InitMonster(lpStatue->GetOriginalPos());
break;
case AKHAN_STATUE:
lpStatue = lpStatue->GetLinkStatue(MonsterInfo::BG_STATUE_AKHAN_COMPLETE1);
lpStatue->InitMonster(lpStatue->GetOriginalPos());
break;
}
dwOldKind = dwKind;
}
}
++pos;
}
// 석상 점령전일때의 초기 점수 계산
CalculateScore();
// 로그 남기기
DETLOG4(g_Log, "Battle Server Log :: (Channel : %d, %s) - 석상전을 초기화 합니다. (현재 Score - HM: %d AK: %d)",
(m_wMapIndex & ~VirtualArea::BGSERVERMAP), GetMapTypeName(),
m_MapInfo.m_wScore[CClass::HUMAN], m_MapInfo.m_wScore[CClass::AKHAN]);
}
return true;
}
unsigned char CBGServerMap::RuleCheck(bool bTimeout) // 룰을 체크해서 이긴종족을 리턴
{
unsigned char cWinNation = CClass::MAX_RACE;
// 시간이 다 지났다면, 혹은 운영자 명령으로 게임을 중지 시킬경우
if (true == bTimeout)
{
if (m_MapInfo.m_wScore[CClass::HUMAN] > m_MapInfo.m_wScore[CClass::AKHAN])
{
cWinNation = CClass::HUMAN;
}
else if (m_MapInfo.m_wScore[CClass::HUMAN] < m_MapInfo.m_wScore[CClass::AKHAN])
{
cWinNation = CClass::AKHAN;
}
else
{
// 이경우의 CClass::MAX_RACE 은 무승부이다.
}
SetStatus(MOVEZONE_WAITING);
m_dwRemainTime = timeGetTime() + MOVEZONE_WAIT_TIME;
}
else
{
// 목표 점수에 도달했는가 체크
if (m_MapInfo.m_wScore[CClass::HUMAN] >= m_MapInfo.m_wTargetScore)
{
cWinNation = CClass::HUMAN;
SetStatus(MOVEZONE_WAITING);
m_dwRemainTime = timeGetTime() + MOVEZONE_WAIT_TIME;
}
else if (m_MapInfo.m_wScore[CClass::AKHAN] >= m_MapInfo.m_wTargetScore)
{
cWinNation = CClass::AKHAN;
SetStatus(MOVEZONE_WAITING);
m_dwRemainTime = timeGetTime() + MOVEZONE_WAIT_TIME;
}
// 이경우의 CClass::MAX_RACE 은 게임이 끝나지 않은 상태이다.
}
return cWinNation;
}
void CBGServerMap::AwardToWinner()
{
unsigned short wAwardBase, wRealPlayMin;
unsigned short wPlayMin = m_MapInfo.m_cLimitMin - m_MapInfo.m_cRemainPlayMin;
if (0 == wPlayMin) wPlayMin = 1;
CharacterList winnerList;
CharacterList::iterator pos;
CharacterList::iterator end;
if (CClass::MAX_RACE == m_ResultInfo.m_cWinRace)
{
wAwardBase = m_MapInfo.m_cLimitMin * MILEAGE_PER_MINUTE_FOR_DRAW;
pos = m_CharacterList.begin();
end = m_CharacterList.end();
}
else
{
wAwardBase = m_MapInfo.m_cLimitMin * MILEAGE_PER_MINUTE_FOR_WIN;
FindWinner(winnerList);
pos = winnerList.begin();
end = winnerList.end();
}
while (pos != end)
{
if (NULL != (*pos))
{
MapInfo::PersonalInfoMap::iterator which = m_MapInfo.m_PersonalInfoMap.find( (*pos)->GetCID() );
if (which != m_MapInfo.m_PersonalInfoMap.end())
{
wRealPlayMin = which->second.m_cEnteringMin - m_MapInfo.m_cRemainPlayMin;
unsigned short wAward = wAwardBase * wRealPlayMin / wPlayMin;
(*pos)->SetMileage((*pos)->GetMileage() + wAward);
CGameClientDispatch* lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharFameInfo(lpDispatch->GetSendStream(), (*pos),
"", "", 0, 0, PktFIAck::FAME_INFO, PktBase::NO_SERVER_ERR);
}
}
}
++pos;
}
}
void CBGServerMap::KillChar(unsigned long dwDeadCID, CCharacter* lpOffencer)
{
if (NULL == lpOffencer) return;
AddScore(lpOffencer->GetRace(), FRAG_SCORE);
// Kill 정보 업데이트
UpdateKillInfo(dwDeadCID, lpOffencer->GetCID());
// 방 정보 전송
SendMapInfo();
}
void CBGServerMap::UpdateKillInfo(unsigned long dwDeadCID, unsigned long dwKillerCID)
{
MapInfo::PersonalInfoMap::iterator pos = m_MapInfo.m_PersonalInfoMap.find( dwDeadCID );
if (pos != m_MapInfo.m_PersonalInfoMap.end()) ++pos->second.m_cKilled;
pos = m_MapInfo.m_PersonalInfoMap.find( dwKillerCID );
if (pos != m_MapInfo.m_PersonalInfoMap.end()) ++pos->second.m_cKill;
}
void CBGServerMap::AddScore(unsigned char cNation, short wScore)
{
if (cNation >= CClass::MAX_CLASS) return;
m_MapInfo.m_wScore[cNation] += wScore;
}
void CBGServerMap::CalculateScore() // 석상 점령전일때 점수 계산
{
if (STATUE == m_MapInfo.m_cMapType && NULL != GetMonsterManager())
{
std::fill_n(m_MapInfo.m_wScore, int(CClass::MAX_RACE), 0);
CVirtualMonsterMgr::MonsterMap::iterator pos = m_pVirtualMonsterMgr->GetMonsterMap().begin();
CVirtualMonsterMgr::MonsterMap::iterator end = m_pVirtualMonsterMgr->GetMonsterMap().end();
while (pos != end)
{
CMonster* lpMonster = pos->second;
if (NULL != lpMonster && lpMonster->GetCurrentState() != STATE_ID_DIE)
{
switch (lpMonster->GetCID() & Creature::MONSTER_KIND_BIT)
{
case MonsterInfo::BG_STATUE_HUMAN_LOADING1:
{
AddScore(CClass::HUMAN, FRIENDLY_LOADING_STATUE_SCORE);
break;
}
case MonsterInfo::BG_STATUE_HUMAN_COMPLETE1:
{
AddScore(CClass::HUMAN, FRIENDLY_STATUE_SCORE);
break;
}
case MonsterInfo::BG_STATUE_AKHAN_LOADING1:
{
AddScore(CClass::AKHAN, FRIENDLY_LOADING_STATUE_SCORE);
break;
}
case MonsterInfo::BG_STATUE_AKHAN_COMPLETE1:
{
AddScore(CClass::AKHAN, FRIENDLY_STATUE_SCORE);
break;
}
case MonsterInfo::BG_STATUE_NEUTRALITY1:
{
AddScore(CClass::AKHAN, NEUTRALITY_STATUE_SCORE);
AddScore(CClass::HUMAN, NEUTRALITY_STATUE_SCORE);
break;
}
}
}
++pos;
}
}
}
// 자기 방의 정보를 보내주는 함수
bool CBGServerMap::SendMapInfo()
{
if (0 == m_CharacterList.size() && 0 == m_SpectatorList.size()) return true;
const int MAX_BUFFER = sizeof(PktBGServerMapList) + sizeof(BGServerMapInfoNode);
char szBuffer[MAX_BUFFER];
PktBGServerMapList* lpPktBGSMLAck = reinterpret_cast<PktBGServerMapList *>(szBuffer);
BGServerMapInfoNode* lpMapInfoNode = reinterpret_cast<BGServerMapInfoNode *>(lpPktBGSMLAck + 1);
lpPktBGSMLAck->m_bAll = false;
lpPktBGSMLAck->m_cMapInfoNodeNum = 1;
lpMapInfoNode->m_bPlaying = IsPlaying();
lpMapInfoNode->m_cMapType = m_MapInfo.m_cMapType;
lpMapInfoNode->m_cMaxCharNumOfNation = m_MapInfo.m_cMaxCharNumOfNation;
lpMapInfoNode->m_wTargetScore = m_MapInfo.m_wTargetScore;
//lpMapInfoNode->m_cRemainMin = (IsPlaying() == true) ? m_MapInfo.m_cRemainPlayMin : m_MapInfo.m_cRemainRestMin;
lpMapInfoNode->m_cRemainMin = (IsPlaying() == true) ? m_MapInfo.m_cRemainPlayMin : 0;
lpMapInfoNode->m_cCurrentCharNum[CClass::HUMAN] = m_MapInfo.m_cCurrentCharNum[CClass::HUMAN];
lpMapInfoNode->m_cCurrentCharNum[CClass::AKHAN] = m_MapInfo.m_cCurrentCharNum[CClass::AKHAN];
lpMapInfoNode->m_wScore[CClass::HUMAN] = m_MapInfo.m_wScore[CClass::HUMAN];
lpMapInfoNode->m_wScore[CClass::AKHAN] = m_MapInfo.m_wScore[CClass::AKHAN];
// 캐릭터 처리
CharacterList::iterator pos = m_CharacterList.begin();
CharacterList::iterator end = m_CharacterList.end();
CGameClientDispatch* lpDispatch = NULL;
while (pos != end)
{
if (NULL == (*pos)) continue;
lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
SendStream.WrapCompress(szBuffer, sizeof(PktBGServerMapList) + sizeof(BGServerMapInfoNode), CmdBGServerMapList, 0, 0);
}
++pos;
}
// 스펙테이터 처리
pos = m_SpectatorList.begin();
end = m_SpectatorList.end();
while (pos != end)
{
if (NULL == (*pos)) continue;
lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
SendStream.WrapCompress(szBuffer, sizeof(PktBGServerMapList) + sizeof(BGServerMapInfoNode), CmdBGServerMapList, 0, 0);
}
++pos;
}
return true;
}
bool CBGServerMap::SendResultInfo()
{
const int MAX_BUFFER = sizeof(PktBGServerResultList) + sizeof(BGServerResultInfoNode);
char szBuffer[MAX_BUFFER];
PktBGServerResultList* lpPktBGSRLAck = reinterpret_cast<PktBGServerResultList *>(szBuffer);
BGServerResultInfoNode* lpResultInfoNode = reinterpret_cast<BGServerResultInfoNode *>(lpPktBGSRLAck + 1);
lpPktBGSRLAck->m_bAll = false;
lpPktBGSRLAck->m_cResultInfoNodeNum = 1;
lpResultInfoNode->m_cWinRace = m_ResultInfo.m_cWinRace;
lpResultInfoNode->m_wScore[CClass::HUMAN] = m_ResultInfo.m_wScore[CClass::HUMAN];
lpResultInfoNode->m_wScore[CClass::AKHAN] = m_ResultInfo.m_wScore[CClass::AKHAN];
// 캐릭터(플레이어) 처리
CharacterList::iterator pos = m_CharacterList.begin();
CharacterList::iterator end = m_CharacterList.end();
CGameClientDispatch* lpDispatch = NULL;
unsigned short wAwardBase;
if (CClass::MAX_RACE == m_ResultInfo.m_cWinRace) wAwardBase = m_MapInfo.m_cLimitMin * MILEAGE_PER_MINUTE_FOR_DRAW;
else wAwardBase = m_MapInfo.m_cLimitMin * MILEAGE_PER_MINUTE_FOR_WIN;
unsigned short wPlayMin = m_MapInfo.m_cLimitMin - m_MapInfo.m_cRemainPlayMin;
if (0 == wPlayMin) wPlayMin = 1;
while (pos != end)
{
if (NULL == (*pos)) continue;
lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
MapInfo::PersonalInfoMap::iterator which = m_MapInfo.m_PersonalInfoMap.find( (*pos)->GetCID() );
if (which != m_MapInfo.m_PersonalInfoMap.end())
{
lpResultInfoNode->m_cPlayMin = which->second.m_cEnteringMin - m_MapInfo.m_cRemainPlayMin;
lpResultInfoNode->m_cKill = which->second.m_cKill;
lpResultInfoNode->m_cKilled = which->second.m_cKilled;
RULLOG3(g_Log, "CID:0x%08x 이벤트로그 - %s, Kill : %d, Die : %d",
(*pos)->GetCharacterName(), which->second.m_cKill, which->second.m_cKilled);
// 무승부나 승리팀의 캐릭터인 경우 얻은 마일리지를 보내준다.
if (CClass::MAX_RACE == m_ResultInfo.m_cWinRace || (*pos)->GetRace() == m_ResultInfo.m_cWinRace)
{
unsigned short wAward = wAwardBase * lpResultInfoNode->m_cPlayMin / wPlayMin;
lpResultInfoNode->m_wAward = wAward;
(*pos)->SetMileage((*pos)->GetMileage() + wAward);
CGameClientDispatch* lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharFameInfo(lpDispatch->GetSendStream(), (*pos),
"", "", 0, 0, PktFIAck::FAME_INFO, PktBase::NO_SERVER_ERR);
}
}
else lpResultInfoNode->m_wAward = 0;
CSendStream& SendStream = lpDispatch->GetSendStream();
SendStream.WrapCompress(szBuffer, sizeof(PktBGServerResultList) + sizeof(BGServerResultInfoNode), CmdBGServerResultList, 0, 0);
}
}
++pos;
}
// 스펙테이터 처리
lpResultInfoNode->m_cPlayMin = 0;
lpResultInfoNode->m_wAward = 0;
lpResultInfoNode->m_cKill = 0;
lpResultInfoNode->m_cKilled = 0;
pos = m_SpectatorList.begin();
end = m_SpectatorList.end();
while (pos != end)
{
if (NULL == (*pos)) continue;
lpDispatch = (*pos)->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
SendStream.WrapCompress(szBuffer, sizeof(PktBGServerResultList) + sizeof(BGServerResultInfoNode), CmdBGServerResultList, 0, 0);
}
++pos;
}
return true;
}
void CBGServerMap::FindWinner(CharacterList& winnerList)
{
CharacterList::iterator pos = m_CharacterList.begin();
CharacterList::iterator end = m_CharacterList.end();
while (pos != end)
{
if (NULL != (*pos))
{
if (m_ResultInfo.m_cWinRace == (*pos)->GetRace())
{
winnerList.push_back((*pos));
}
}
++pos;
}
}
void CBGServerMap::DeleteAllItem()
{
if (NULL == m_CellData)
{
return;
}
CCell* lpCellPastEnd = m_CellData + GetWidth() * GetHeight();
for (CCell* lpCell = m_CellData; lpCell != lpCellPastEnd; ++lpCell)
{
lpCell->DeleteAllItem();
}
}
void CBGServerMap::ResetEnteringMin(unsigned char cMin)
{
if (m_MapInfo.m_PersonalInfoMap.empty()) return;
MapInfo::PersonalInfoMap::iterator pos = m_MapInfo.m_PersonalInfoMap.begin();
MapInfo::PersonalInfoMap::iterator end = m_MapInfo.m_PersonalInfoMap.end();
while (pos != end)
{
pos->second.m_cEnteringMin = cMin;
++pos;
}
}

View File

@@ -0,0 +1,84 @@
#ifndef _BATTLEGROUND_SERVER_MAP_H_
#define _BATTLEGROUND_SERVER_MAP_H_
#pragma once
#include <Map/FieldMap/VirtualArea/VirtualAreaConstants.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaStructure.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
class CCharacter;
class CMonster;
namespace VirtualArea
{
// 전방 참조
struct ProtoType;
class CBGServerMap : public CVirtualArea
{
public:
CBGServerMap(const VirtualArea::ProtoType* lpProtoType, unsigned short wMapNumber);
~CBGServerMap(void);
void Process();
unsigned short Enter(CCharacter* lpCharacter, unsigned char cMoveType);
bool Leave(CCharacter* lpCharacter);
bool AllRespawn(); // 모두 대기실로 이동
// 게임 룰 관련
bool GameStart(); // 게임을 시작한다.
unsigned char RuleCheck(bool bTimeout = false); // 룰을 체크해서 이긴종족을 리턴
void AwardToWinner();
void KillChar(unsigned long dwDeadCID, CCharacter* lpOffencer);
void UpdateKillInfo(unsigned long dwDeadCID, unsigned long dwKillerCID);
void AddScore(unsigned char cNation, short wScore);
void CalculateScore(); // 석상 점령전일때 점수 계산
void ResetEnteringMin(unsigned char cMin);
// Get/Set 함수
unsigned char GetMapType() { return m_MapInfo.m_cMapType; }
MapInfo& GetMapInfo() { return m_MapInfo; }
ResultInfo& GetResultInfo() { return m_ResultInfo; }
unsigned char GetStatus() { return m_cStatus; }
void SetStatus(unsigned char cStatus) { m_cStatus = cStatus; }
void SetRemainTime(unsigned long dwRemainTime) { m_dwRemainTime = dwRemainTime; }
bool IsPlaying() { return (GAME_PLAYING == m_cStatus); }
bool IsResting() { return (GAME_RESTING == m_cStatus); }
bool IsPlayer(CCharacter* lpCharacter);
bool IsSpectator(CCharacter* lpCharacter);
// 자기 방의 정보를 보내주는 함수
bool SendMapInfo();
bool SendResultInfo();
private:
bool InitializeGameObject();
void FindWinner(CharacterList& winnerList);
unsigned short AddCharacter(CCharacter* lpCharacter);
unsigned short AddSpectator(CCharacter* lpSpectator);
// 바닥에 떨어진 Item 모두 지우기
void DeleteAllItem();
// 게임 방 정보
MapInfo m_MapInfo;
ResultInfo m_ResultInfo;
unsigned char m_cStatus;
unsigned long m_dwRemainTime; // 시간에 관련된 모든 부분에서 남은 시간으로 사용
};
}
#endif // _BATTLEGROUND_SERVER_MAP_H_

View File

@@ -0,0 +1,487 @@
#include "stdafx.h"
#include "BGServerMgr.h"
#include <Utility/Math/Math.h>
#include <Creature/Character/Character.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/PacketStruct/BGServerPacket.h>
#include <Network/Dispatch/GameClient/SendCharBGServer.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap/VirtualArea/BGServer/BGServerMap.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Skill/SkillTable.h>
#include <Skill/Spell/SpellTable.h>
#include <Skill/Spell/SpellUtil.h>
using namespace VirtualArea;
CBGServerMgr::CBGServerMgr()
{
}
CBGServerMgr::~CBGServerMgr()
{
DestroyBGServerMap();
}
CBGServerMgr& CBGServerMgr::GetInstance()
{
static CBGServerMgr ms_this;
return ms_this;
}
bool CBGServerMgr::CreateBGServerMap()
{
if (m_lstBGServerMap.size() != 0)
{
DestroyBGServerMap();
}
// Game Room 가상 맵 생성
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 1 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 2 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 3 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 4 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 5 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 6 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_FRAG"), 7 ) );
m_lstBGServerMap.push_back( new CBGServerMap( CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType("BG_STATUE"), 8 ) );
LoginAllMonster();
GameAllStart();
return true;
}
void CBGServerMgr::DestroyBGServerMap()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
for(; pos != end; ++pos)
{
if (NULL != (*pos))
{
delete (*pos);
(*pos) = NULL;
}
}
m_lstBGServerMap.clear();
}
bool CBGServerMgr::Enter(CCharacter* lpCharacter, unsigned short wMapIndex, unsigned char cMoveType)
{
if (NULL == lpCharacter) { return false; }
unsigned short wIndex = (wMapIndex & ~VirtualArea::BGSERVERMAP);
if (0 == wIndex || wIndex > m_lstBGServerMap.size()) { return false; }
CBGServerMap* lpBGServerMap = m_lstBGServerMap[wIndex - 1];
if (NULL == lpBGServerMap) { return false; }
unsigned short wError = lpBGServerMap->Enter(lpCharacter, cMoveType);
if (PktBase::NO_SERVER_ERR == wError)
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
if (false == GameClientSendPacket::SendCharBGServerMoveZone(lpDispatch->GetSendStream(), lpBGServerMap->GetVirtualZone(), cMoveType, wError))
{
lpBGServerMap->Leave(lpCharacter);
ERRLOG3(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 가상 존(MapIndex : %d)으로 이동하는데 실패하였습니다.",
lpCharacter->GetCID(), lpCharacter->GetMapIndex(), wMapIndex);
return false;
}
// 존 이동 (실제로는 MapIndex 와 좌표가 바뀔 뿐이다.)
lpCharacter->SetMapIndex(wMapIndex);
lpCharacter->MoveTo(lpBGServerMap->GetStartPosition(lpCharacter->GetRace()), false);
// HIDE 명령 처리
if (VirtualArea::TYPE_SPECTATOR == cMoveType)
{
lpCharacter->GetEnchantInfo().SetFlag(Skill::SpellID::Hide);
}
// 방 정보를 보내준다.
// (지금은 방의 모든 사람에게 다 보내주지만, 구경꾼이 들어올경우에는 그 구경꾼에게만 보내주도록 수정할수 있다.)
lpBGServerMap->SendMapInfo();
}
else
{
lpBGServerMap->Leave(lpCharacter);
ERRLOG3(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 가상 존(MapIndex : %d)으로 이동하는데 실패하였습니다.",
lpCharacter->GetCID(), lpCharacter->GetMapIndex(), wMapIndex);
return false;
}
}
else
{
if (PktBase::SERVER_ERROR == wError)
{
ERRLOG3(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 가상 존(MapIndex : %d)으로 이동하는데 실패하였습니다.",
lpCharacter->GetCID(), lpCharacter->GetMapIndex(), wMapIndex);
}
else
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
return GameClientSendPacket::SendCharBGServerMoveZone(lpDispatch->GetSendStream(),
lpBGServerMap->GetVirtualZone(), cMoveType, wError);
}
}
}
return true;
}
bool CBGServerMgr::Leave(CCharacter* lpCharacter)
{
if (NULL == lpCharacter) return false;
unsigned short wIndex = (lpCharacter->GetMapIndex() & ~VirtualArea::BGSERVERMAP);
if (0 == wIndex || wIndex > m_lstBGServerMap.size()) return false;
CBGServerMap* lpBGServerMap = m_lstBGServerMap[wIndex - 1];
if (NULL == lpBGServerMap) return false;
lpCharacter->SetMapIndex(0);
Position RespawnPos(VirtualArea::RespawnPos[ lpCharacter->GetRace() ][ Math::Random::ComplexRandom(VirtualArea::MAX_LOBBY_RESPAWN_POS) ]);
RespawnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
RespawnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
lpCharacter->MoveTo(RespawnPos, false);
lpCharacter->GetEnchantInfo().ResetFlag(Skill::SpellID::Hide);
lpBGServerMap->Leave(lpCharacter);
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
if (false == GameClientSendPacket::SendCharBGServerMoveZone(lpDispatch->GetSendStream(), SERVER_ID::BATTLE_SERVER, VirtualArea::TYPE_PLAYER, PktBase::NO_SERVER_ERR))
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터가 가상 존(MapIndex : %d)에서 나가는데 실패하였습니다.", lpCharacter->GetCID(), lpCharacter->GetMapIndex());
return false;
}
// 플레이어가 나간경우 방 정보를 보내준다.
if (lpBGServerMap->IsPlayer(lpCharacter))
{
lpBGServerMap->SendMapInfo();
}
}
return true;
}
void CBGServerMgr::Process()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
(*pos)->Process();
++pos;
}
static unsigned long s_dwLastTime = 0;
unsigned long dwCurrentTime = timeGetTime();
if (dwCurrentTime - s_dwLastTime > VirtualArea::MILLISEC_PER_MINUTE)
{
s_dwLastTime = dwCurrentTime;
SendMapListToAllCharacter();
}
}
CVirtualArea* CBGServerMgr::GetVirtualArea(unsigned short wMapIndex)
{
unsigned short wIndex = (wMapIndex & ~VirtualArea::BGSERVERMAP);
if (0 == wIndex || wIndex > m_lstBGServerMap.size())
return NULL;
return reinterpret_cast<CVirtualArea*>(m_lstBGServerMap[wIndex - 1]);
}
bool CBGServerMgr::SendMapList(CCharacter* lpCharacter)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
if (NULL == lpCharacter)
{
return false;
}
const int MAX_BUFFER = sizeof(PktBGServerMapList) + PktBGServerMapList::MAX_MAP_NUM * sizeof(BGServerMapInfoNode);
char szBuffer[MAX_BUFFER];
PktBGServerMapList* lpPktBGSMLAck = reinterpret_cast<PktBGServerMapList *>(szBuffer);
BGServerMapInfoNode* lpMapInfoNode = reinterpret_cast<BGServerMapInfoNode *>(lpPktBGSMLAck + 1);
lpPktBGSMLAck->m_bAll = true;
lpPktBGSMLAck->m_cMapInfoNodeNum = 0;
for (unsigned char cIndex = 0; cIndex < PktBGServerMapList::MAX_MAP_NUM && cIndex < m_lstBGServerMap.size();
++cIndex, ++lpMapInfoNode, ++lpPktBGSMLAck->m_cMapInfoNodeNum)
{
CBGServerMap* lpBGServerMap = m_lstBGServerMap[cIndex];
if (NULL != lpBGServerMap)
{
lpMapInfoNode->m_bPlaying = lpBGServerMap->IsPlaying();
lpMapInfoNode->m_cMapType = lpBGServerMap->GetMapInfo().m_cMapType;
lpMapInfoNode->m_cMaxCharNumOfNation = lpBGServerMap->GetMapInfo().m_cMaxCharNumOfNation;
lpMapInfoNode->m_wTargetScore = lpBGServerMap->GetMapInfo().m_wTargetScore;
lpMapInfoNode->m_cRemainMin = (lpBGServerMap->IsPlaying() == true) ? lpBGServerMap->GetMapInfo().m_cRemainPlayMin : lpBGServerMap->GetMapInfo().m_cRemainRestMin;
lpMapInfoNode->m_cCurrentCharNum[CClass::HUMAN] = lpBGServerMap->GetMapInfo().m_cCurrentCharNum[CClass::HUMAN];
lpMapInfoNode->m_cCurrentCharNum[CClass::AKHAN] = lpBGServerMap->GetMapInfo().m_cCurrentCharNum[CClass::AKHAN];
lpMapInfoNode->m_wScore[CClass::HUMAN] = lpBGServerMap->GetMapInfo().m_wScore[CClass::HUMAN];
lpMapInfoNode->m_wScore[CClass::AKHAN] = lpBGServerMap->GetMapInfo().m_wScore[CClass::AKHAN];
}
}
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
return lpDispatch->GetSendStream().WrapCompress(szBuffer, sizeof(PktBGServerMapList) +
lpPktBGSMLAck->m_cMapInfoNodeNum * sizeof(BGServerMapInfoNode), CmdBGServerMapList, 0, 0);
}
return true;
}
bool CBGServerMgr::SendResultList(CCharacter* lpCharacter)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
if (NULL == lpCharacter)
{
return false;
}
const int MAX_BUFFER = sizeof(PktBGServerResultList) + PktBGServerResultList::MAX_MAP_NUM * sizeof(BGServerResultInfoNode);
char szBuffer[MAX_BUFFER];
PktBGServerResultList* lpPktBGSRLAck = reinterpret_cast<PktBGServerResultList *>(szBuffer);
BGServerResultInfoNode* lpResultInfoNode = reinterpret_cast<BGServerResultInfoNode *>(lpPktBGSRLAck + 1);
lpPktBGSRLAck->m_bAll = true;
lpPktBGSRLAck->m_cResultInfoNodeNum = 0;
for (unsigned char cIndex = 0; cIndex < PktBGServerResultList::MAX_MAP_NUM && cIndex < m_lstBGServerMap.size();
++cIndex, ++lpResultInfoNode, ++lpPktBGSRLAck->m_cResultInfoNodeNum)
{
CBGServerMap* lpBGServerMap = m_lstBGServerMap[cIndex];
if (NULL != lpBGServerMap)
{
lpResultInfoNode->m_cWinRace = lpBGServerMap->GetResultInfo().m_cWinRace;
lpResultInfoNode->m_cPlayMin = 0;
lpResultInfoNode->m_wAward = 0;
lpResultInfoNode->m_wScore[CClass::HUMAN] = lpBGServerMap->GetResultInfo().m_wScore[CClass::HUMAN];
lpResultInfoNode->m_wScore[CClass::AKHAN] = lpBGServerMap->GetResultInfo().m_wScore[CClass::AKHAN];
}
}
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
return lpDispatch->GetSendStream().WrapCompress(szBuffer, sizeof(PktBGServerResultList) +
lpPktBGSRLAck->m_cResultInfoNodeNum * sizeof(BGServerResultInfoNode), CmdBGServerResultList, 0, 0);
}
return true;
}
bool CBGServerMgr::SendMapListToAllCharacter()
{
PERFORMANCE_CHECK(FunctionTimingCheck);
if (0 == CCreatureManager::GetInstance().GetCharacterNum()) return true;
const int MAX_BUFFER = sizeof(PktBGServerMapList) + PktBGServerMapList::MAX_MAP_NUM * sizeof(BGServerMapInfoNode);
char szSrcBuffer[MAX_BUFFER], szDstBuffer[MAX_BUFFER];
PktBGServerMapList* lpPktBGSMLAck = reinterpret_cast<PktBGServerMapList *>(szSrcBuffer);
BGServerMapInfoNode* lpMapInfoNode = reinterpret_cast<BGServerMapInfoNode *>(lpPktBGSMLAck + 1);
lpPktBGSMLAck->m_bAll = true;
lpPktBGSMLAck->m_cMapInfoNodeNum = 0;
for (unsigned char cIndex = 0; cIndex < PktBGServerMapList::MAX_MAP_NUM && cIndex < m_lstBGServerMap.size();
++cIndex, ++lpMapInfoNode, ++lpPktBGSMLAck->m_cMapInfoNodeNum)
{
CBGServerMap* lpBGServerMap = m_lstBGServerMap[cIndex];
if (NULL != lpBGServerMap)
{
lpMapInfoNode->m_bPlaying = lpBGServerMap->IsPlaying();
lpMapInfoNode->m_cMapType = lpBGServerMap->GetMapInfo().m_cMapType;
lpMapInfoNode->m_cMaxCharNumOfNation = lpBGServerMap->GetMapInfo().m_cMaxCharNumOfNation;
lpMapInfoNode->m_wTargetScore = lpBGServerMap->GetMapInfo().m_wTargetScore;
lpMapInfoNode->m_cRemainMin = (lpBGServerMap->IsPlaying() == true) ? lpBGServerMap->GetMapInfo().m_cRemainPlayMin : lpBGServerMap->GetMapInfo().m_cRemainRestMin;
lpMapInfoNode->m_cCurrentCharNum[CClass::HUMAN] = lpBGServerMap->GetMapInfo().m_cCurrentCharNum[CClass::HUMAN];
lpMapInfoNode->m_cCurrentCharNum[CClass::AKHAN] = lpBGServerMap->GetMapInfo().m_cCurrentCharNum[CClass::AKHAN];
lpMapInfoNode->m_wScore[CClass::HUMAN] = lpBGServerMap->GetMapInfo().m_wScore[CClass::HUMAN];
lpMapInfoNode->m_wScore[CClass::AKHAN] = lpBGServerMap->GetMapInfo().m_wScore[CClass::AKHAN];
}
}
unsigned long dwDstLength = MAX_BUFFER;
unsigned short wSrcLength = sizeof(PktBGServerMapList) + lpPktBGSMLAck->m_cMapInfoNodeNum * sizeof(BGServerMapInfoNode);
if (PacketWrap::WrapCompress(szDstBuffer, dwDstLength,
szSrcBuffer, wSrcLength, CmdBGServerMapList, 0, 0))
{
// 대기실에 있는 사람들에게만 방정보를 전송해준다.
CCreatureManager::GetInstance().SendAllCharacter(
szDstBuffer, dwDstLength, CmdBGServerMapList, false);
}
return true;
}
bool CBGServerMgr::LoginAllMonster()
{
for (size_t nIndex=0; nIndex<m_lstBGServerMap.size(); ++nIndex)
{
if (m_lstBGServerMap[nIndex])
{
const VirtualArea::ProtoType* lpProtoType = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualAreaProtoType( m_lstBGServerMap[nIndex]->GetVID() );
if (lpProtoType && '\0' != lpProtoType->m_szArrangementFileName[0])
{
m_lstBGServerMap[nIndex]->CreateVirtualMonsterManager();
unsigned short wMapIndex = static_cast<unsigned short>(VirtualArea::BGSERVERMAP | (nIndex + 1));
CCellManager::GetInstance().LoginMonster(lpProtoType->m_szArrangementFileName, wMapIndex);
}
}
}
return true;
}
void CBGServerMgr::GameAllStart()
{
std::for_each(m_lstBGServerMap.begin(), m_lstBGServerMap.end(), std::mem_fun<bool, CBGServerMap>(&CBGServerMap::GameStart));
}
void CBGServerMgr::ProcessAllMonster()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessAllMonster();
}
++pos;
}
}
void CBGServerMgr::ProcessMonsterRegenHPAndMP()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessMonsterRegenHPAndMP();
}
++pos;
}
}
void CBGServerMgr::ProcessSummonMonsterDead()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessSummonMonsterDead();
}
++pos;
}
}
void CBGServerMgr::ProcessDeleteItem()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessDeleteItem();
}
++pos;
}
}
bool CBGServerMgr::ProcessAllCellPrepareBroadCast()
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessAllCellPrepareBroadCast();
}
++pos;
}
return true;
}
bool CBGServerMgr::ProcessAllCellBroadCast(unsigned long dwCurrentPulse)
{
BGServerMapList::iterator pos = m_lstBGServerMap.begin();
BGServerMapList::iterator end = m_lstBGServerMap.end();
while (pos != end)
{
if (NULL != (*pos))
{
(*pos)->ProcessAllCellBroadCast(dwCurrentPulse);
}
++pos;
}
return true;
}

View File

@@ -0,0 +1,61 @@
#ifndef _BATTLEGROUND_SERVER_MANAGER_H_
#define _BATTLEGROUND_SERVER_MANAGER_H_
#pragma once
#include <vector>
// Àü¹æ ÂüÁ¶
class CCharacter;
namespace VirtualArea
{
// Àü¹æ ÂüÁ¶
class CVirtualArea;
class CBGServerMap;
class CBGServerMgr
{
public:
static CBGServerMgr& GetInstance();
bool CreateBGServerMap();
void Process();
bool Enter(CCharacter* lpCharacter, unsigned short wMapIndex, unsigned char cMoveType);
bool Leave(CCharacter* lpCharacter);
CVirtualArea* GetVirtualArea(unsigned short wMapIndex);
bool SendMapList(CCharacter* lpCharacter);
bool SendResultList(CCharacter* lpCharacter);
bool SendMapListToAllCharacter();
typedef std::vector<CBGServerMap* > BGServerMapList;
void ProcessAllMonster();
void ProcessMonsterRegenHPAndMP();
void ProcessSummonMonsterDead();
void ProcessDeleteItem();
// Cell BroadCasting
bool ProcessAllCellPrepareBroadCast();
bool ProcessAllCellBroadCast(unsigned long dwCurrentPulse);
private:
CBGServerMgr();
~CBGServerMgr();
void DestroyBGServerMap();
bool LoginAllMonster();
void GameAllStart();
BGServerMapList m_lstBGServerMap;
};
}
#endif // _BATTLEGROUND_SERVER_MANAGER_H_

View File

@@ -0,0 +1,13 @@
#ifndef _DUEL_MAP_H_
#define _DUEL_MAP_H_
#pragma once
namespace VirtualArea
{
class CDuelMap
{
};
}
#endif // _DUEL_MAP_H_

View File

@@ -0,0 +1,9 @@
#include "DuelMgr.h"
using namespace VirtualArea;
CDuelMgr& CDuelMgr::GetInstance()
{
static CDuelMgr ms_this;
return ms_this;
}

View File

@@ -0,0 +1,18 @@
#ifndef _DUEL_MANAGER_H_
#define _DUEL_MANAGER_H_
#pragma once
namespace VirtualArea
{
class CDuelMgr
{
public:
static CDuelMgr& GetInstance();
private:
};
}
#endif // _DUEL_MANAGER_H_

View File

@@ -0,0 +1,13 @@
#ifndef _DUNGEON_H_
#define _DUNGEON_H_
#pragma once
namespace VirtualArea
{
class CDungeon
{
};
}
#endif // _DUNGEON_H_

View File

@@ -0,0 +1,9 @@
#include "DungeonMgr.h"
using namespace VirtualArea;
CDungeonMgr& CDungeonMgr::GetInstance()
{
static CDungeonMgr ms_this;
return ms_this;
}

View File

@@ -0,0 +1,18 @@
#ifndef _DUNGEON_MANAGER_H_
#define _DUNGEON_MANAGER_H_
#pragma once
namespace VirtualArea
{
class CDungeonMgr
{
public:
static CDungeonMgr& GetInstance();
private:
};
}
#endif // _DUNGEON_MANAGER_H_