Files
Leviathan/Client/Game/game/Utility/MonsterRespawnRegionExtractor.cpp
T
2026-06-01 12:46:52 +02:00

674 lines
18 KiB
C++

#include "stdafx.h"
#include "MonsterRespawnRegionExtractor.h"
#include <iostream>
#include <fstream>
#include <windows.h>
#include <kfile/KFileManager.h>
#include <kfile/KStream.h>
//#include "Util.h"
#include "TerrainSeamlessWorldInfoForClient.h"
#include "math.h"
#include <float.h>
bool cMonsterRespawnRegionExtractor::sEventMonster::add(int id)
{
std::vector<int>::iterator it = m_monsterList.begin();
for (; it != m_monsterList.end(); ++it)
{
if (*it == id)
return false;
}
m_monsterList.push_back(id);
return true;
}
bool cMonsterRespawnRegionExtractor::sEventRegion::add(int regionIndex, SCRIPTFUNC_LIST const& scriptList, std::vector<KRect> const& regionList)
{
if (0 > regionIndex)
return false;
if (regionIndex >= (int)regionList.size())
return false;
m_eventId = getEventId(scriptList);
m_regionList.push_back(regionList[regionIndex]);
return m_eventId != -1;
}
int cMonsterRespawnRegionExtractor::sEventRegion::getEventId(SCRIPTFUNC_LIST const& scriptList)
{
size_t s, e;
SCRIPTFUNC_LIST::const_iterator it = scriptList.begin();
for (; it != scriptList.end(); ++it)
{
s = it->m_strFuncName.find("mob(");
if (s != std::string::npos)
{
s = it->m_strFuncName.find("(");
e = it->m_strFuncName.find(",");
if (e != std::string::npos)
{
std::string strEventId = it->m_strFuncName.substr(s+1, e-s-1);
return atoi(strEventId.c_str());
}
}
}
return -1;
}
/*
*/
cMonsterRespawnRegionExtractor::~cMonsterRespawnRegionExtractor()
{
m_eventMonsterList.clear();
m_eventRegionList.clear();
m_monsterRegionList.clear();
}
/*
*/
bool cMonsterRespawnRegionExtractor::extract()
{
if (!getSeamlessWorldInfo()) return false;
if (!extractLua()) return false;
if (!extractNfs()) return false;
if (!unifyData()) return false;
std::map<int, sMonsterRegion> minMonsterRegionList;
std::map<int, sMonsterRegion> maxMonsterRegionList;
grouping(minMonsterRegionList, 1500);
grouping(maxMonsterRegionList, 2000);
calcCovariance(minMonsterRegionList);
calcCovariance(maxMonsterRegionList);
if (!writeData("second/monsterMinRegion.lst", minMonsterRegionList)) return false;
if (!writeData("second/monsterMaxRegion.lst", maxMonsterRegionList)) return false;
minMonsterRegionList.clear();
maxMonsterRegionList.clear();
return true;
}
/*
*/
bool cMonsterRespawnRegionExtractor::extractNfs()
{
char filename[MAX_PATH];
int mapwidth = 13;
int mapheight = 10;
for (int y = 0; y < mapheight; ++y)
{
for (int x = 0; x < mapwidth; ++x)
{
KStream* pStream;
sprintf(filename, "m%03d_%03d.nfm", x, y);
pStream = KFileManager::Instance().CreateStreamFromResource(filename);
if (!pStream)
continue;
SAFE_DELETE(pStream);
sprintf(filename, "m%03d_%03d.nfs", x, y);
pStream = KFileManager::Instance().CreateStreamFromResource(filename);
if (!pStream)
continue;
NFS_LATESTHEADER headerScript;
if( sizeof(headerScript) != pStream->Read( &headerScript, sizeof(headerScript) ) )
{
SAFE_DELETE(pStream);
continue;
}
if (strcmp(headerScript.szSign, NFSFILE_SIGN) != 0)
{
SAFE_DELETE(pStream);
continue;
}
// 헤더를 다시 읽을 수 있도록 파일의 맨앞으로 이동
pStream->Seek( 0, KStream::seekSet );
switch( headerScript.dwVersion ) // 버전 체크
{
case c_dwNFSCurrentVer: LoadScriptFileForVer02(pStream, x, y); break;
case 1: LoadScriptFileForVer01(pStream, x, y); break;
}
SAFE_DELETE(pStream);
}
}
return true;
}
/*
*/
bool cMonsterRespawnRegionExtractor::extractLua()
{
ifstream ifs("second/monster_respawn.lua", ifstream::in);
if (!ifs.is_open())
{
return false;
}
int eventId = 0;
char buffer[MAX_PATH];
while (ifs.good())
{
ifs.getline(buffer, MAX_PATH);
if (strstr(buffer, "ID == "))
{
std::string str = assignString(buffer, "ID == ", "then");
eventId = atoi(str.c_str());
}
else if (strstr(buffer, "monster_ID") || strstr(buffer, "Raremob_ID") || strstr(buffer, "Raidmob_ID"))
{
if (0 == eventId)
continue;
sEventMonster eventMonster;
std::string str = assignString(buffer, "{", "}");
size_t s = 0;
while (1)
{
size_t found = str.find(",", s);
if (found == std::string::npos)
{
std::string strId = str.substr(s, str.length()-s);
int id = atoi(strId.c_str());
if (id)
eventMonster.add(id);
break;
}
else
{
std::string strId = str.substr(s, found-s);
int id = atoi(strId.c_str());
if (id)
eventMonster.add(id);
s = found+1;
}
}
eventMonster.m_eventId = eventId;
m_eventMonsterList.insert(std::make_pair(eventId, eventMonster));
eventId = 0;
}
}
ifs.close();
return true;
}
/*
*/
bool cMonsterRespawnRegionExtractor::writeData(char const* filename, std::map<int, sMonsterRegion> const& monsterRegionList)
{
KFileStream* pStream = new KFileStream(filename, KFileStream::wronly | KFileStream::truncate);
if (!pStream->IsValid())
{
SAFE_DELETE(pStream);
return false;
}
_oprint("write monster respawn data : %s\n\n", filename);
int count = m_monsterRegionList.size();
pStream->Write(&count, sizeof (count));
_oprint("count : %d\n", count);
int i = 0;
std::map<int, sMonsterRegion>::const_iterator it = monsterRegionList.begin();
for (; it != monsterRegionList.end(); ++it, ++i)
{
int monsterId = it->second.m_monsterId;
pStream->Write(&monsterId, sizeof (monsterId));
int num = it->second.m_regionList.size();
pStream->Write(&num, sizeof (num));
//_oprint("[%d] id = %d, num = %d\n", i, monsterId, num);
sMonsterRegion::cit_region it_r = it->second.m_regionList.begin();
for (; it_r != it->second.m_regionList.end(); ++it_r)
{
pStream->Write(&it_r->m_rect, sizeof (it_r->m_rect));
pStream->Write(&it_r->m_theta, sizeof (it_r->m_theta));
}
}
pStream->Close();
SAFE_DELETE(pStream);
return true;
}
/*
*/
bool cMonsterRespawnRegionExtractor::readData(char const* filename, std::map<int, sMonsterRegion>& monsterRegionList)
{
KStream* pStream = KFileManager::Instance().CreateStreamFromResource(filename);
if (!pStream)
return false;
// _oprint("write monster respawn data : %s\n\n", filename);
int count;
pStream->Read(&count, sizeof (count));
// _oprint("count : %d\n", count);
for (int i = 0; i < count; ++i)
{
sMonsterRegion monsterRegion;
int monsterId;
pStream->Read(&monsterId, sizeof (monsterId));
int regionNum;
pStream->Read(&regionNum, sizeof (regionNum));
monsterRegion.m_monsterId = monsterId;
//_oprint("[%d] id = %d, num = %d\n", i, monsterRegion.m_monsterId, regionNum);
for (int r = 0; r < regionNum; ++r)
{
sMonsterRegionRect mobRegion;
pStream->Read(&mobRegion.m_rect, sizeof (mobRegion.m_rect));
pStream->Read(&mobRegion.m_theta, sizeof (mobRegion.m_theta));
monsterRegion.m_regionList.push_back(mobRegion);
// _oprint("\tleft=%d, right=%d, top=%d, bottom=%d\n", rect.left, rect.right, rect.top, rect.bottom);
}
monsterRegionList.insert(std::make_pair(monsterRegion.m_monsterId, monsterRegion));
}
SAFE_DELETE(pStream);
return true;
}
/*
*/
std::string cMonsterRespawnRegionExtractor::assignString(char const* buffer, char* openStr, char* closeStr)
{
char const* open = strstr(buffer, openStr);
open += strlen(openStr);
char const* close = strstr(buffer, closeStr);
std::string str;
str.assign(open, close-open);
return str;
}
/*
*/
void cMonsterRespawnRegionExtractor::LoadScriptFileForVer02(KStream* pStream, int nMapX, int nMapY )
{
// 헤더 읽기
NFS_HEADER_V02 headerScript;
if( sizeof(headerScript) != pStream->Read( &headerScript, sizeof(headerScript) ) )
{
return ;
}
std::vector<KRect> regionList;
int nEventLocationCount;
int nEventLocationScriptCount;
int tileLength = (int)m_tileLength;
// 이벤트 영역 정보
{
// 이벤트 영역 갯수 읽기
pStream->Read( &nEventLocationCount, sizeof(nEventLocationCount) );
for( int n( 0 ); n < nEventLocationCount; ++n )
{
// 이벤트 영역 좌표 읽기
KRect rcLocation;
pStream->Read( &rcLocation.left, sizeof(rcLocation.left) );
pStream->Read( &rcLocation.top, sizeof(rcLocation.top) );
pStream->Read( &rcLocation.right, sizeof(rcLocation.right) );
pStream->Read( &rcLocation.bottom, sizeof(rcLocation.bottom) );
rcLocation.left += (nMapX * m_tileCountPerMap);
rcLocation.top += (nMapY * m_tileCountPerMap);
rcLocation.right += (nMapX * m_tileCountPerMap);
rcLocation.bottom += (nMapY * m_tileCountPerMap);
rcLocation.left *= tileLength;
rcLocation.right *= tileLength;
rcLocation.top *= tileLength;
rcLocation.bottom *= tileLength;
// 이벤트 영역 이름 읽기
std::string strEvLocName = CStringUtil::ReadString( pStream );
regionList.push_back(rcLocation);
}
}
// 이벤트 영역의 스크립트펑션 리스트 정보
{
// 스크립트펑션 리스트들의 갯수
pStream->Read( &nEventLocationScriptCount, sizeof(nEventLocationScriptCount) );
for( int n( 0 ); n < nEventLocationScriptCount; ++n )
{
// 스크립트 펑션 리스트 읽기
SCRIPTFUNC_LIST ScriptFuncList;
int nIndex = LoadEvLocScriptFuncListForV01( pStream, ScriptFuncList );
sEventRegion eventRegion;
if (eventRegion.add(nIndex, ScriptFuncList, regionList))
addEventRegion(eventRegion);//m_eventRegionList.insert(std::make_pair(eventRegion.m_eventId, eventRegion));
ScriptFuncList.clear();
}
}
regionList.clear();
}
/*
*/
void cMonsterRespawnRegionExtractor::LoadScriptFileForVer01(KStream* pStream, int nMapX, int nMapY )
{
// 헤더 읽기
NFS_HEADER_V01 headerScript;
if( sizeof(headerScript) != pStream->Read( &headerScript, sizeof(headerScript) ) )
{
return ;
}
std::vector<KRect> regionList;
// 이벤트 영역 정보
{
// 이벤트 영역 갯수 읽기
int nEventLocationCount;
pStream->Read( &nEventLocationCount, sizeof(nEventLocationCount) );
for( int n( 0 ); n < nEventLocationCount; ++n )
{
// 이벤트 영역 좌표 읽기
KRect rcLocation;
pStream->Read( &rcLocation.left, sizeof(rcLocation.left) );
pStream->Read( &rcLocation.top, sizeof(rcLocation.top) );
pStream->Read( &rcLocation.right, sizeof(rcLocation.right) );
pStream->Read( &rcLocation.bottom, sizeof(rcLocation.bottom) );
rcLocation.left += (nMapX * m_tileCountPerMap);
rcLocation.top += (nMapY * m_tileCountPerMap);
rcLocation.right += (nMapX * m_tileCountPerMap);
rcLocation.bottom += (nMapY * m_tileCountPerMap);
// 이벤트 영역 이름 읽기
std::string strEvLocName = CStringUtil::ReadString( pStream );
regionList.push_back(rcLocation);
}
}
// 이벤트 영역의 스크립트펑션 리스트 정보
{
// 스크립트펑션 리스트들의 갯수
int nEventLocationScriptCount = 0;
pStream->Read( &nEventLocationScriptCount, sizeof(nEventLocationScriptCount) );
for( int n( 0 ); n < nEventLocationScriptCount; ++n )
{
// 스크립트 펑션 리스트 읽기
SCRIPTFUNC_LIST ScriptFuncList;
int nIndex = LoadEvLocScriptFuncListForV01( pStream, ScriptFuncList );
sEventRegion eventRegion;
if (eventRegion.add(nIndex, ScriptFuncList, regionList))
addEventRegion(eventRegion);//m_eventRegionList.insert(std::make_pair(eventRegion.m_eventId, eventRegion));
ScriptFuncList.clear();
}
}
regionList.clear();
}
/*
*/
int cMonsterRespawnRegionExtractor::LoadEvLocScriptFuncListForV01( KStream* pStream, SCRIPTFUNC_LIST& rScriptFuncList )
{
// 인덱스 읽기
int nIndex = 0;
pStream->Read( &nIndex, sizeof(nIndex) );
// 스크립트 펑션 갯수 읽기
int nScriptFuncCount = 0;
pStream->Read( &nScriptFuncCount, sizeof(nScriptFuncCount) );
for( int n( 0 ); n < nScriptFuncCount; ++n )
{
// 트리거 읽기
int nTrigger = 0;
pStream->Read( &nTrigger, sizeof(nTrigger) );
// 펑션 이름 읽기
std::string strFuncName = CStringUtil::ReadString( pStream );
rScriptFuncList.push_back( ScriptFunction( SCRIPT_TRIGGER(nTrigger), strFuncName.c_str() ) );
}
return nIndex;
}
/*
*/
bool cMonsterRespawnRegionExtractor::getSeamlessWorldInfo()
{
CTerrainSeamlessWorldInfoForClient seamlessWorldInfo;
if( !seamlessWorldInfo.Initialize("TerrainSeamlessWorld.cfg", false))
return false;
m_segmentCountPerMap = seamlessWorldInfo.GetSegmentCountPerMap();
m_tileCountPerSegment = seamlessWorldInfo.GetTileCountPerSegment();
m_tileCountPerMap = m_segmentCountPerMap * m_tileCountPerSegment;
m_tileLength = seamlessWorldInfo.GetTileLength();
return true;
}
/*
*/
bool cMonsterRespawnRegionExtractor::unifyData()
{
/// event monster list
std::map<int, sEventMonster>::iterator it_em = m_eventMonsterList.begin();
for (; it_em != m_eventMonsterList.end(); ++it_em)
{
int eventId = it_em->second.m_eventId;
std::vector<int>::iterator it_m = it_em->second.m_monsterList.begin();
for (; it_m != it_em->second.m_monsterList.end(); ++it_m)
{
int monsterId = *it_m;
/// event region list
std::map<int, sEventRegion>::iterator it_er = m_eventRegionList.find(eventId);
if (it_er != m_eventRegionList.end())
{
std::vector<KRect>::iterator it_r = it_er->second.m_regionList.begin();
/// monster region list
std::map<int, sMonsterRegion>::iterator it_mr = m_monsterRegionList.find(monsterId);
if (it_mr != m_monsterRegionList.end())
{
for (; it_r != it_er->second.m_regionList.end(); ++it_r)
it_mr->second.m_regionList.push_back(*it_r);
}
else
{
sMonsterRegion mr;
mr.m_monsterId = monsterId;
for (; it_r != it_er->second.m_regionList.end(); ++it_r)
mr.m_regionList.push_back(sMonsterRegionRect(*it_r));
m_monsterRegionList.insert(std::make_pair(monsterId, mr));
}
}
}
}
return true;
}
/*
*/
void cMonsterRespawnRegionExtractor::addEventRegion(sEventRegion const& eventRegion)
{
std::map<int, sEventRegion>::iterator it = m_eventRegionList.find(eventRegion.m_eventId);
if (it == m_eventRegionList.end())
{
m_eventRegionList.insert(std::make_pair(eventRegion.m_eventId, eventRegion));
}
else
{
/// eventRegion.m_regionList는 꼭 한이다.
it->second.m_regionList.push_back(eventRegion.m_regionList[0]);
}
}
/*
*/
void cMonsterRespawnRegionExtractor::grouping(std::map<int,sMonsterRegion> &monsterRegionList, int groupLength)
{
std::map<int, sMonsterRegion>::iterator it = m_monsterRegionList.begin();
for (; it != m_monsterRegionList.end(); ++it)
{
sMonsterRegion monsterRegion;
monsterRegion.m_monsterId = it->second.m_monsterId;
monsterRegion.m_regionList = it->second.m_regionList;
sMonsterRegion::it_region it_first = monsterRegion.m_regionList.begin();
for (; it_first != monsterRegion.m_regionList.end(); ++it_first)
{
sMonsterRegion::it_region it_second = it_first + 1;
for (; it_second != monsterRegion.m_regionList.end(); )
{
int len = getRectLength(it_first->m_rect, it_second->m_rect);
if (len < groupLength)
{
KRect r = getGroupRect(it_first->m_rect, it_second->m_rect);
it_first->setRect(r);
it_first->addGroup(it_second->m_rect);
it_second = monsterRegion.m_regionList.erase(it_second);
}
else
{
++it_second;
}
}
}
monsterRegionList.insert(std::make_pair(monsterRegion.m_monsterId, monsterRegion));
}
}
/*
*/
int cMonsterRespawnRegionExtractor::getRectLength(KRect const& r1, KRect const& r2)
{
KPoint p1((r1.left + r1.right) >> 1, (r1.top + r1.bottom) >> 1);
KPoint p2((r2.left + r2.right) >> 1, (r2.top + r2.bottom) >> 1);
KPoint p = p2 - p1;
double _x = (double)p.x;
double _y = (double)p.y;
int len = (int)::sqrt(pow(_x, 2) + pow(_y, 2));
return len;
}
/*
*/
KRect cMonsterRespawnRegionExtractor::getGroupRect(KRect const& r1, KRect const& r2)
{
KRect r;
r.left = min(r1.left, r2.left);
r.right = max(r1.right, r2.right);
r.top = min(r1.top, r2.top);
r.bottom = max(r1.bottom, r2.bottom);
return r;
}
/*
*/
void cMonsterRespawnRegionExtractor::calcCovariance(std::map<int,sMonsterRegion> &monsterRegion)
{
struct sAverageSize
{
void getAverage(std::vector<KRect> const& rectList, KSize& size)
{
KSize ave(0, 0);
int n = rectList.size();
for (int i = 0; i < n; ++i)
{
ave.cx += rectList[i].GetWidth();
ave.cy += rectList[i].GetHeight();
}
ave.cx /= n;
ave.cy /= n;
size = ave;
}
};
sCorMatrix corMat;
int _x = 0;
int _y = 0;
std::map<int,sMonsterRegion>::iterator it_mr = monsterRegion.begin();
for (; it_mr != monsterRegion.end(); ++it_mr, ++_x)
{
sMonsterRegion::it_region it_r = it_mr->second.m_regionList.begin();
for (_y = 0; it_r != it_mr->second.m_regionList.end(); ++it_r, ++_y)
{
if (1 == it_r->m_groupRectList.size())
{
it_r->m_theta = 0.0f;
}
else
{
corMat.make(it_r->m_groupRectList);
float tanTheta, theta;
float deno = corMat.v[1][1] - corMat.v[0][0];
if (FLT_EPSILON >= fabs(deno))
tanTheta = 0.0f;
else
tanTheta = (2.0f * corMat.v[0][1])/deno;
theta = ::atanf(tanTheta) * 0.5f;
float _cos = ::cosf(theta);
float _sin = ::sinf(theta);
float _cos2 = pow(_cos, 2);
float _sin2 = pow(_sin, 2);
float _sincos = _sin * _cos;
float r_uu = (corMat.v[0][0] * _cos2) - (2.0f * corMat.v[0][1] * _sincos) + (corMat.v[1][1] * _sin2);
float r_vv = (corMat.v[0][0] * _sin2) - (2.0f * corMat.v[0][1] * _sincos) + (corMat.v[1][1] * _cos2);
float w, h;
if (1.0f >= fabs(r_uu)) w = 1.0f;
else w = ::sqrtf(r_uu);
if (1.0f >= fabs(r_vv)) h = 1.0f;
else h = ::sqrtf(r_vv);
// 공분산 상관관계를 구할 때, 지역의 영역크기는 고려가 안되었기 때문에, 평균 영역 크기를 더해준다.
KSize aveSize;
sAverageSize averageSize;
averageSize.getAverage(it_r->m_groupRectList, aveSize);
w += aveSize.cx;
h += aveSize.cy;
it_r->m_theta = theta;
it_r->reSize((int)w, (int)h);
}
}
}
}