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>
385 lines
14 KiB
C++
385 lines
14 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <Log/ServerLog.h>
|
|
#include <Utility/Math/Math.h>
|
|
#include <Utility/Compress/MiniLZO/MiniLZOWrapper.h>
|
|
#include <Utility/Resource/EnsureCleanup.h>
|
|
#include <Network/XORCrypt/XORCrypt.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) CMiniLZOCompress::Compress((In), (In_len), (Out), (Out_len))
|
|
#define DECOMPRESS(In, In_len, Out, Out_len) CMiniLZOCompress::Decompress((In), (In_len), (Out), (Out_len))
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include "MonsterMgr.h"
|
|
|
|
CMonsterMgr CMonsterMgr::ms_this;
|
|
|
|
#ifdef _TEST_SERVER_
|
|
const char* CMonsterMgr::m_szMonsterScriptFileName = "TestMonsterProtoType.txt";
|
|
#else
|
|
const char* CMonsterMgr::m_szMonsterScriptFileName = "MonsterProtoType.txt";
|
|
#endif
|
|
|
|
|
|
CMonsterMgr::CMonsterMgr(void)
|
|
: m_nMonsterNum(0), m_ProtoTypeArray(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
CMonsterMgr::~CMonsterMgr(void)
|
|
{
|
|
ClearProtoType();
|
|
}
|
|
|
|
void CMonsterMgr::ClearProtoType()
|
|
{
|
|
if(NULL != m_ProtoTypeArray)
|
|
{
|
|
delete [] m_ProtoTypeArray;
|
|
}
|
|
}
|
|
|
|
bool CMonsterMgr::LoadMonstersFromFile(const char* szFileName)
|
|
{
|
|
int nIndex = 0;
|
|
int nLineCount = 0;
|
|
char strTemp[MAX_PATH];
|
|
|
|
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
|
|
std::vector<MonsterProtoType> monsterProtoTypeVector;
|
|
|
|
monsterProtoTypeVector.reserve(1000);
|
|
MonsterProtoType tempProtoType;
|
|
|
|
// 매크로에 로그 코드 삽입을 잊지 말 것.
|
|
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
|
|
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
|
|
#define READ_DATA(ColumnName, Argument) \
|
|
if (!DelimitedFile.ReadData(Argument)) { \
|
|
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
|
|
return false; \
|
|
}
|
|
|
|
#define READ_STRING(ColumnName, Buffer, BufferSize) \
|
|
if (!DelimitedFile.ReadString(Buffer, BufferSize)) { \
|
|
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %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, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
|
|
return false; \
|
|
} \
|
|
Argument = (!strcmp(strTemp, "O")) ? true : false;
|
|
|
|
|
|
if (!DelimitedFile.Open(szFileName ? szFileName : m_szMonsterScriptFileName))
|
|
{
|
|
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szFileName ? szFileName : m_szMonsterScriptFileName);
|
|
return false;
|
|
}
|
|
|
|
while (DelimitedFile.ReadLine())
|
|
{
|
|
++nLineCount;
|
|
|
|
// 순서가 바뀌면 곤란하다니깐~!!! (버럭!)
|
|
READ_DATA("KID", tempProtoType.m_MonsterInfo.m_dwKID);
|
|
|
|
READ_STRING("이름", tempProtoType.m_MonsterInfo.m_strName, MonsterInfo::MAX_NAME_LENGTH);
|
|
READ_STRING("모델링 형태 플래그", tempProtoType.m_MonsterInfo.m_strModelingFlag, MonsterInfo::MAX_MODELING_FLAG_LENGTH);
|
|
|
|
READ_STRING("스킬패턴", strTemp, MAX_PATH);
|
|
tempProtoType.m_MonsterInfo.m_nSkillPattern = MonsterInfo::GetMonsterPattern(strTemp);
|
|
|
|
// 모션 정보
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_wAction = MonsterInfo::Z3D_CA_WALK;
|
|
READ_DATA("걷기프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_dwFrame);
|
|
|
|
READ_DATA("걷기거리", tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_fVelocity);
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_fVelocity /= 100.0f;
|
|
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_wAction = MonsterInfo::Z3D_CA_RUN;
|
|
READ_DATA("달리기프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_dwFrame);
|
|
|
|
READ_DATA("달리기거리", tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_fVelocity);
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_fVelocity /= 100.0f;
|
|
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[2].m_wAction = MonsterInfo::Z3D_CA_ATTACK;
|
|
READ_DATA("공격프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[2].m_dwFrame);
|
|
|
|
tempProtoType.m_MonsterInfo.m_MonsterMotions[3].m_wAction = MonsterInfo::Z3D_CA_CASTING;
|
|
READ_DATA("스킬프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[3].m_dwFrame);
|
|
|
|
READ_DATA_ARRAY("타격박스", tempProtoType.m_MonsterInfo.m_fHitBox, MonsterInfo::MAX_HITBOX_NUM);
|
|
READ_DATA("공격거리", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nAttackRange);
|
|
READ_DATA("공격각도", tempProtoType.m_MonsterInfo.m_fAttackAngle);
|
|
|
|
// 기본 정보
|
|
READ_DATA("기본 경험점", tempProtoType.m_CreatureStatus.m_nExp);
|
|
READ_DATA("레벨", tempProtoType.m_CreatureStatus.m_nLevel);
|
|
READ_DATA("최소데미지", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMinDamage);
|
|
READ_DATA("최대데미지", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMaxDamage);
|
|
READ_DATA("공격보정", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nOffenceRevision);
|
|
READ_DATA("방어력", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nDefence);
|
|
READ_DATA("방어보정", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nDefenceRevision);
|
|
READ_DATA("블록율", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nBlockingPercentage);
|
|
READ_DATA("DRC", tempProtoType.m_CreatureStatus.m_StatusInfo.m_fDRC);
|
|
READ_DATA("크리티컬확률", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nCriticalPercentage);
|
|
READ_DATA("크리티컬타입", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nCriticalType);
|
|
READ_DATA("저항력", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMagicResistance);
|
|
READ_DATA_ARRAY("공격속성", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nWeaponAttributeLevel, StatusInfo::MAX_ATTRIBUTE_NUM);
|
|
READ_DATA_ARRAY("속성저항", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nAttributeResistance, StatusInfo::MAX_ATTRIBUTE_NUM);
|
|
READ_DATA("공격속도", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nAttackSpeed);
|
|
READ_DATA("이동속도", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMoveSpeed);
|
|
READ_DATA("HP Max", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMaxHP);
|
|
READ_DATA("MP Max", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMaxMP);
|
|
READ_DATA("HP 회복량", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nHPRegenAmount);
|
|
READ_DATA("MP 회복량", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMPRegenAmount);
|
|
|
|
// 기타
|
|
READ_DATA("사이즈", tempProtoType.m_MonsterInfo.m_fSize);
|
|
READ_DATA("스킬레벨", tempProtoType.m_MonsterInfo.m_nSkillLevel);
|
|
|
|
READ_DATA_BOOL("스텔스타입여부", tempProtoType.m_MonsterInfo.m_bStealth);
|
|
READ_DATA_BOOL("선제공격여부", tempProtoType.m_MonsterInfo.m_bFirstAttack);
|
|
READ_DATA_BOOL("도망여부", tempProtoType.m_MonsterInfo.m_bEscape);
|
|
READ_DATA_BOOL("귀환여부", tempProtoType.m_MonsterInfo.m_bReturnPosition);
|
|
|
|
READ_DATA("리스폰타임", tempProtoType.m_MonsterInfo.m_dwRespawnTime);
|
|
READ_DATA_ARRAY("오리지날아이템", tempProtoType.m_MonsterInfo.m_AwardItem, MonsterInfo::MAX_ORIGINAL_ITEM_NUM);
|
|
READ_DATA_ARRAY("아이템드랍률", tempProtoType.m_MonsterInfo.m_nDropRate, MonsterInfo::MAX_AWARD_KIND);
|
|
|
|
monsterProtoTypeVector.push_back(tempProtoType);
|
|
}
|
|
|
|
std::sort(monsterProtoTypeVector.begin(), monsterProtoTypeVector.end());
|
|
|
|
for(std::vector<MonsterProtoType>::iterator itr = monsterProtoTypeVector.begin();
|
|
itr != monsterProtoTypeVector.end() - 1; ++itr)
|
|
{
|
|
if(itr->m_MonsterInfo.m_dwKID == (itr+1)->m_MonsterInfo.m_dwKID)
|
|
{
|
|
ERRLOG1(g_Log, "겹치는 몬스터 종류 ID가 있습니다. 종류ID:%d", itr->m_MonsterInfo.m_dwKID);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
m_nMonsterNum = monsterProtoTypeVector.size();
|
|
m_ProtoTypeArray = new MonsterProtoType[m_nMonsterNum];
|
|
if(NULL == m_ProtoTypeArray)
|
|
{
|
|
ERRLOG0(g_Log, "몬스터 스크립트 초기화 실패 : 메모리 부족");
|
|
return false;
|
|
}
|
|
|
|
std::copy(monsterProtoTypeVector.begin(), monsterProtoTypeVector.end(), m_ProtoTypeArray);
|
|
return true;
|
|
}
|
|
|
|
|
|
const CMonsterMgr::MonsterProtoType* CMonsterMgr::GetMonsterProtoType(DWORD dwKID)
|
|
{
|
|
MonsterProtoType* lpFirst = m_ProtoTypeArray;
|
|
MonsterProtoType* lpLast = m_ProtoTypeArray + m_nMonsterNum;
|
|
MonsterProtoType* lpMid = NULL;
|
|
|
|
size_t nCount = m_nMonsterNum;
|
|
size_t nCount2 = 0;
|
|
|
|
for (; 0 < nCount; )
|
|
{
|
|
nCount2 = nCount / 2;
|
|
lpMid = lpFirst + nCount2;
|
|
|
|
if(lpMid->m_MonsterInfo.m_dwKID < dwKID)
|
|
{
|
|
lpFirst = ++lpMid, nCount -= nCount2 + 1;
|
|
}
|
|
else
|
|
{
|
|
nCount = nCount2;
|
|
}
|
|
}
|
|
|
|
return (lpFirst != lpLast && !(dwKID < lpFirst->m_MonsterInfo.m_dwKID)) ? lpFirst : NULL;
|
|
}
|
|
|
|
|
|
const CMonsterMgr::MonsterProtoType* CMonsterMgr::GetMonsterProtoType(char* szName)
|
|
{
|
|
for (size_t nIndex = 0; nIndex < m_nMonsterNum; nIndex++)
|
|
{
|
|
if (0 == strncmp(szName, m_ProtoTypeArray[nIndex].m_MonsterInfo.m_strName, MonsterInfo::MAX_NAME_LENGTH))
|
|
{
|
|
return m_ProtoTypeArray + nIndex;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool CMonsterMgr::LoadMonstersFromBinary(const char* szFileNameBinary)
|
|
{
|
|
HANDLE hFile = CreateFile((0 == szFileNameBinary) ? m_szMonsterScriptFileName : szFileNameBinary,
|
|
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) { return false; }
|
|
|
|
CEnsureCloseHandle readFile(hFile);
|
|
|
|
DWORD dwRead = 0;
|
|
DWORD dwFileHighSize = 0;
|
|
DWORD 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;
|
|
}
|
|
|
|
MonsterProtoType* lpProtoType = 0;
|
|
MonsterInfo* lpMonsterInfo = 0;
|
|
CreatureStatus* lpCreatureStatus = 0;
|
|
|
|
DWORD dwStructSize = 0;
|
|
DWORD dwHeaderSize = sizeof(DWORD) + *reinterpret_cast<DWORD*>(lpAllocated) + sizeof(DWORD);
|
|
DWORD dwBufferSize = *reinterpret_cast<DWORD*>(lpAllocated + dwHeaderSize - sizeof(DWORD));
|
|
|
|
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);
|
|
|
|
m_nMonsterNum = dwBufferSize / sizeof(MonsterProtoType);
|
|
m_ProtoTypeArray = new MonsterProtoType[m_nMonsterNum];
|
|
if(NULL == m_ProtoTypeArray)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for(size_t nIndex = 0; nIndex < m_nMonsterNum; ++nIndex)
|
|
{
|
|
lpMonsterInfo = reinterpret_cast<MonsterInfo*>(lpBuffer);
|
|
lpCreatureStatus = reinterpret_cast<CreatureStatus*>(lpBuffer + sizeof(MonsterInfo));
|
|
|
|
m_ProtoTypeArray[nIndex].m_MonsterInfo = MonsterInfo(*lpMonsterInfo);
|
|
dwBufferSize -= sizeof(MonsterInfo);
|
|
lpBuffer += sizeof(MonsterInfo);
|
|
|
|
m_ProtoTypeArray[nIndex].m_CreatureStatus = CreatureStatus(*lpCreatureStatus);
|
|
dwBufferSize -= sizeof(CreatureStatus);
|
|
lpBuffer += sizeof(CreatureStatus);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CMonsterMgr::SaveMonstersToBinary(const char* szFileNameBinary, const char* szTrashFile)
|
|
{
|
|
if(0 == m_ProtoTypeArray)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HANDLE hFile = CreateFile((0 == szFileNameBinary) ? m_szMonsterScriptFileName : szFileNameBinary,
|
|
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) { return false; }
|
|
|
|
CEnsureCloseHandle writeFile(hFile);
|
|
|
|
const size_t MAX_SCRIPT_FILE_SIZE = m_nMonsterNum * sizeof(MonsterProtoType);
|
|
|
|
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;
|
|
|
|
DWORD dwInputBufferSize = 0;
|
|
DWORD dwOutputBufferSize = 0;
|
|
|
|
for(size_t nCount = 0; nCount < m_nMonsterNum; ++nCount)
|
|
{
|
|
memcpy(pInputBuffer, &m_ProtoTypeArray[nCount].m_MonsterInfo, sizeof(MonsterInfo));
|
|
dwInputBufferSize += sizeof(MonsterInfo);
|
|
pInputBuffer += sizeof(MonsterInfo);
|
|
|
|
memcpy(pInputBuffer, &m_ProtoTypeArray[nCount].m_CreatureStatus, sizeof(CreatureStatus));
|
|
dwInputBufferSize += sizeof(CreatureStatus);
|
|
pInputBuffer += sizeof(CreatureStatus);
|
|
}
|
|
|
|
ENCODEHEADER(InputStartPointer, dwInputBufferSize, 0, 3);
|
|
COMPRESS(InputStartPointer, dwInputBufferSize, pOutputBuffer, &dwOutputBufferSize);
|
|
|
|
DWORD dwWritten = 0;
|
|
|
|
// 쓰레기(더미) 자료
|
|
HANDLE hTrashFile = CreateFile(szTrashFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hTrashFile == INVALID_HANDLE_VALUE) { return false; }
|
|
|
|
CEnsureCloseHandle trashFile(hTrashFile);
|
|
|
|
DWORD dwRead = 0;
|
|
DWORD dwFileHighSize = 0;
|
|
DWORD dwFileSize = GetFileSize(hTrashFile, &dwFileHighSize);
|
|
|
|
char* lpAllocated = new char[dwFileSize];
|
|
if(NULL == lpAllocated)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CEnsureDeleteArray<char> allocated(lpAllocated);
|
|
|
|
if (!ReadFile(hTrashFile, lpAllocated, dwFileSize, &dwRead, NULL))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 쓰레기(더미) 자료
|
|
WriteFile(hFile, &dwFileSize, sizeof(DWORD), &dwWritten, 0);
|
|
WriteFile(hFile, lpAllocated, dwFileSize, &dwWritten, 0);
|
|
|
|
// 올바른 자료
|
|
WriteFile(hFile, &dwInputBufferSize, sizeof(DWORD), &dwWritten, 0);
|
|
WriteFile(hFile, pOutputBuffer, dwOutputBufferSize, &dwWritten, 0);
|
|
return true;
|
|
}
|