#include "stdafx.h" #include "MonsterRespawnRegionExtractor.h" #include #include #include #include #include //#include "Util.h" #include "TerrainSeamlessWorldInfoForClient.h" #include "math.h" #include bool cMonsterRespawnRegionExtractor::sEventMonster::add(int id) { std::vector::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 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 minMonsterRegionList; std::map 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 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::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& 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(®ionNum, 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 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 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::iterator it_em = m_eventMonsterList.begin(); for (; it_em != m_eventMonsterList.end(); ++it_em) { int eventId = it_em->second.m_eventId; std::vector::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::iterator it_er = m_eventRegionList.find(eventId); if (it_er != m_eventRegionList.end()) { std::vector::iterator it_r = it_er->second.m_regionList.begin(); /// monster region list std::map::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::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 &monsterRegionList, int groupLength) { std::map::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 &monsterRegion) { struct sAverageSize { void getAverage(std::vector 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::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); } } } }