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>
922 lines
25 KiB
C++
922 lines
25 KiB
C++
// SkillConvert.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include <iostream>
|
|
#include <ctime>
|
|
#include <string>
|
|
#include <Log/ServerLog.h>
|
|
#include <DB/DBComponent.h>
|
|
#include <DB/GameDBComponent.h>
|
|
#include <Skill/SkillMgr.h>
|
|
#include <Utility/Math/Math.h>
|
|
#include <list>
|
|
|
|
using namespace std;
|
|
|
|
|
|
const int nHammerID = 0x8406;
|
|
const int nFirstAidID = 0x8405;
|
|
|
|
|
|
class CSkillConvert
|
|
{
|
|
public:
|
|
|
|
CSkillConvert() : m_fSrc(NULL), m_fConverted(NULL) { }
|
|
~CSkillConvert()
|
|
{
|
|
if(NULL != m_fSrc) { fclose(m_fSrc); m_fSrc = NULL; }
|
|
if(NULL != m_fConverted) { fclose(m_fConverted); m_fConverted = NULL; }
|
|
}
|
|
|
|
bool Initialize(char* szDBServerName, char* szDBName, char* szDBAccount, char* szDBPass);
|
|
bool Process();
|
|
void Test(const char* szFileName);
|
|
|
|
private:
|
|
|
|
bool ProcessSkill();
|
|
bool ProcessSkillBook();
|
|
bool ProcessSkillLevel();
|
|
|
|
// return : 업데이트 여부 - 업데이트시 True, 아니면 False
|
|
static bool ProcessEraseHammerOfLight(unsigned long dwCID, SKILL& Skill);
|
|
static int ProcessEraseHammerOfLightQuickSlot(unsigned long dwCID, QUICK& Quick);
|
|
|
|
// return : 업데이트한 아이템 갯수.
|
|
static int ProcessSkillBookConvert(unsigned long dwUID, unsigned long dwCID, const char* lpBuffer, int nSize);
|
|
static bool RecalculateSkill(unsigned long dwCID, SKILL& Skill);
|
|
static void EraseSkill(SKILL& Skill, unsigned short usErasePos);
|
|
|
|
// return : 업데이트 여부
|
|
bool AdjustSkillLevel(SKILL& skill);
|
|
bool AdjustQSlotSkillLevel(QUICK& quick);
|
|
|
|
CDBComponent m_ReadDB;
|
|
CDBComponent m_WriteDB;
|
|
|
|
FILE* m_fSrc;
|
|
FILE* m_fConverted;
|
|
};
|
|
|
|
|
|
void PrintUsage()
|
|
{
|
|
cout << "다음과 같이 입력하세요 : SkillConvert DB서버주소 DB이름 DB계정 DB패스워드" << endl
|
|
<< " 또는 : SkillConvert test testFileName" << endl << endl
|
|
|
|
<< " 테스트 파일의 포맷은 다음과 같은 포맷입니다. 엑셀로 작성한 후 cvs포맷으로 뽑으십시오" << endl
|
|
<< " CID,SkillNum,SlotNum,Skill_1,Skill_1_LockCount,Skill_1_Level,Skill_2,Skill_2_LockCount,Skill_2_Level,..." << endl;
|
|
}
|
|
|
|
|
|
void LogSkillToFile(FILE* fWriteFile, unsigned long dwCID, SKILL& Skill)
|
|
{
|
|
fprintf(fWriteFile, "%d,%d,%d,", dwCID, Skill.wSkillNum, Skill.wSlotNum);
|
|
|
|
for(int nSlot = 0; nSlot < SKILL::MAX_SLOT_NUM; ++nSlot)
|
|
{
|
|
if(0 != Skill.SSlot[nSlot].SKILLINFO.wSkill)
|
|
{
|
|
fprintf(fWriteFile, "0x%04x,%d,%d,", Skill.SSlot[nSlot].SKILLINFO.wSkill,
|
|
Skill.SSlot[nSlot].SKILLINFO.cLockCount,
|
|
Skill.SSlot[nSlot].SKILLINFO.cSkillLevel);
|
|
}
|
|
}
|
|
|
|
fprintf(fWriteFile, "\n");
|
|
}
|
|
|
|
|
|
int _tmain(int argc, _TCHAR* argv[])
|
|
{
|
|
CSkillConvert Convert;
|
|
|
|
if(argc == 5)
|
|
{
|
|
if(!Convert.Initialize(argv[1], argv[2], argv[3], argv[4]))
|
|
{
|
|
cout << "[DB연결 실패]" << endl;
|
|
return -1;
|
|
}
|
|
}
|
|
else if(argc == 3)
|
|
{
|
|
if(0 == strcmp("test", argv[1]))
|
|
{
|
|
Convert.Test(argv[2]);
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return 0;
|
|
}
|
|
|
|
cout << "[DB 연결 성공]" << endl;
|
|
|
|
time_t CurrentTime = time(NULL);
|
|
DETLOG1(g_Log, "현재시간 : %s", ctime(&CurrentTime));
|
|
|
|
Convert.Process();
|
|
|
|
CurrentTime = time(NULL);
|
|
DETLOG1(g_Log, "종료시간 : %s", ctime(&CurrentTime));
|
|
|
|
cout << "Press Enter Key" << endl;
|
|
|
|
string temp;
|
|
getline(cin, temp);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool CSkillConvert::Initialize(char* szDBServerName, char* szDBName, char* szDBAccount, char* szDBPass)
|
|
{
|
|
// DB 연결
|
|
if(!m_ReadDB.Connect(szDBServerName, szDBName, szDBAccount, szDBPass))
|
|
{
|
|
cout << "[ReadDB 연결 실패]" << endl;
|
|
return false;
|
|
}
|
|
|
|
if(!m_WriteDB.Connect(szDBServerName, szDBName, szDBAccount, szDBPass))
|
|
{
|
|
cout << "[WriteDB 연결 실패]" << endl;
|
|
return false;
|
|
}
|
|
|
|
m_fSrc = fopen("SrcSkillLog.txt", "wt");
|
|
|
|
if(NULL == m_fSrc)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_fConverted = fopen("ConvertedSkillLog.txt", "wt");
|
|
|
|
if(NULL == m_fConverted)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CSkillConvert::Process()
|
|
{
|
|
//ProcessSkill();
|
|
//ProcessSkillBook();
|
|
|
|
ProcessSkillLevel();
|
|
return true;
|
|
}
|
|
|
|
bool CSkillConvert::ProcessSkill()
|
|
{
|
|
char* szQuery = "select UID, Skill from CharSkill";
|
|
|
|
#pragma pack(1)
|
|
|
|
struct SkillData
|
|
{
|
|
unsigned long m_dwCID;
|
|
SKILL m_Skill;
|
|
};
|
|
|
|
#pragma pack()
|
|
|
|
if(!m_ReadDB.ExecuteQuery(szQuery))
|
|
{
|
|
cout << "[Skill 쿼리 실패] : " << m_ReadDB.GetErrorString() << endl;
|
|
return false;
|
|
}
|
|
|
|
const int MAX_ROWS = 10240;
|
|
int nGetRows = 0;
|
|
SkillData* skillData = new SkillData[MAX_ROWS];
|
|
memset(skillData, 0, sizeof(SkillData) * MAX_ROWS);
|
|
|
|
while(m_ReadDB.GetData((void**)skillData, sizeof(SkillData), MAX_ROWS, &nGetRows))
|
|
{
|
|
if(0 == nGetRows)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for(SkillData* lpSkillData = skillData;
|
|
0 < nGetRows; --nGetRows, ++lpSkillData)
|
|
{
|
|
const unsigned long dwCID = lpSkillData->m_dwCID;
|
|
SKILL& skill = lpSkillData->m_Skill;
|
|
SKILL oldSkill = lpSkillData->m_Skill;
|
|
|
|
bool bChanged = false;
|
|
|
|
if(ProcessEraseHammerOfLight(dwCID, skill))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
// 마지막에 해 줄 것
|
|
if(RecalculateSkill(dwCID, skill))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
if(bChanged)
|
|
{
|
|
LogSkillToFile(m_fSrc, dwCID, oldSkill);
|
|
LogSkillToFile(m_fConverted, dwCID, skill);
|
|
|
|
DBComponent::GameDB::UpdateCharSkill(m_WriteDB, dwCID, &skill);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] skillData;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CSkillConvert::ProcessEraseHammerOfLight(unsigned long dwCID, SKILL& Skill)
|
|
{
|
|
/*
|
|
|
|
리턴값. 고치는 데 성공하면, true.
|
|
|
|
2. SKILL이, 해머 오브 라이트가 있는지 본다.
|
|
해머 오브 라이트가 있는 경우.. 퍼스트 에이드가 있는지 확인한다
|
|
퍼스트 에이드가 있으면,
|
|
해머 오브 라이트보다 레벨이 높거나 같다 - 해머 오브 라이트를 지운다.
|
|
해머 오브 라이트보다 레벨이 낮다 - 퍼스트에이드를 지우고, 해머오브라이트를 퍼스트에이드로 바꾼다.
|
|
퍼스트 에이드가 없으면, 해머 오브 라이트를 퍼스트 에이드로 바꾼다.
|
|
|
|
*/
|
|
|
|
if(20 < Skill.wSlotNum)
|
|
{
|
|
ERRLOG2(g_Log, "CID:%10d : 스킬 슬롯 개수가 이상합니다. %d개입니다.", dwCID, Skill.wSlotNum);
|
|
Skill.wSlotNum = 20;
|
|
}
|
|
|
|
for(int nHammerPos = 0; nHammerPos < Skill.wSlotNum; ++nHammerPos)
|
|
{
|
|
// 해머오브라이트가 있는지 확인
|
|
if(nHammerID == Skill.SSlot[nHammerPos].SKILLINFO.wSkill)
|
|
{
|
|
// 퍼스트에이드가 있는지 확인
|
|
for(int nFirstAidPos = 0; nFirstAidPos < Skill.wSlotNum; ++nFirstAidPos)
|
|
{
|
|
if(nFirstAidID == Skill.SSlot[nFirstAidPos].SKILLINFO.wSkill)
|
|
{
|
|
const int nHammerLevel = Skill.SSlot[nHammerPos].SKILLINFO.cLockCount * CSkillMgr::MAX_SKILL_LEVEL + Skill.SSlot[nHammerPos].SKILLINFO.cSkillLevel;
|
|
const int nFirstAidLevel = Skill.SSlot[nFirstAidPos].SKILLINFO.cLockCount * CSkillMgr::MAX_SKILL_LEVEL + Skill.SSlot[nFirstAidPos].SKILLINFO.cSkillLevel;
|
|
|
|
if(nHammerLevel <= nFirstAidLevel)
|
|
{
|
|
// 퍼스트에이드가 해머오브라이트보다 레벨이 높거나 같다. - 해머오브라이트를 지운다
|
|
EraseSkill(Skill, nHammerPos);
|
|
}
|
|
else
|
|
{
|
|
// 퍼스트에이드가 해머오브라이트보다 레벨이 낮다 -
|
|
// 퍼스트에이드를 지우고, 해머오브라이트를 퍼스트에이드로 바꾼다.
|
|
Skill.SSlot[nHammerPos].SKILLINFO.wSkill = nFirstAidID;
|
|
EraseSkill(Skill, nFirstAidPos);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 퍼스트에이드가 없다.. -> 해머 오브 라이트를 퍼스트에이드로 바꾼다.
|
|
Skill.SSlot[nHammerPos].SKILLINFO.wSkill = nFirstAidID;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CSkillConvert::EraseSkill(SKILL& Skill, unsigned short usErasePos)
|
|
{
|
|
if(usErasePos < Skill.wSlotNum)
|
|
{
|
|
const int nEraseSkillLevel =
|
|
Skill.SSlot[usErasePos].SKILLINFO.cLockCount * CSkillMgr::MAX_SKILL_LEVEL +
|
|
Skill.SSlot[usErasePos].SKILLINFO.cSkillLevel;
|
|
|
|
int nEraseCount = usErasePos;
|
|
for (; nEraseCount < Skill.wSlotNum - 1; ++nEraseCount)
|
|
{
|
|
Skill.SSlot[nEraseCount] = Skill.SSlot[nEraseCount + 1];
|
|
}
|
|
|
|
Skill.SSlot[nEraseCount].SKILLINFO.wSkill = 0;
|
|
Skill.SSlot[nEraseCount].SKILLINFO.cLockCount = 0;
|
|
Skill.SSlot[nEraseCount].SKILLINFO.cSkillLevel = 0;
|
|
|
|
Skill.wSkillNum -= nEraseSkillLevel;
|
|
--Skill.wSlotNum;
|
|
}
|
|
}
|
|
|
|
bool CSkillConvert::RecalculateSkill(unsigned long dwCID, SKILL& Skill)
|
|
{
|
|
unsigned short usOldSkillNum = Skill.wSkillNum;
|
|
unsigned short usOldSlotNum = Skill.wSlotNum;
|
|
|
|
unsigned short usNewSkillNum = 0;
|
|
unsigned short usNewSlotNum = 0;
|
|
|
|
for(int nCount = 0; nCount < SKILL::MAX_SLOT_NUM; ++nCount)
|
|
{
|
|
if(0 == Skill.SSlot[nCount].SKILLINFO.wSkill)
|
|
{
|
|
for(; nCount < SKILL::MAX_SLOT_NUM; ++nCount)
|
|
{
|
|
Skill.SSlot[nCount].SKILLINFO.wSkill = 0;
|
|
Skill.SSlot[nCount].SKILLINFO.cLockCount = 0;
|
|
Skill.SSlot[nCount].SKILLINFO.cSkillLevel = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
usNewSkillNum += Skill.SSlot[nCount].SKILLINFO.cLockCount * (CSkillMgr::MAX_SKILL_LEVEL - 1) +
|
|
Skill.SSlot[nCount].SKILLINFO.cSkillLevel;
|
|
|
|
++usNewSlotNum;
|
|
}
|
|
}
|
|
|
|
if(usNewSkillNum != usOldSkillNum || usNewSlotNum != usOldSlotNum)
|
|
{
|
|
Skill.wSkillNum = usNewSkillNum;
|
|
Skill.wSlotNum = usNewSlotNum;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSkillConvert::ProcessSkillBook()
|
|
{
|
|
char Query[MAX_PATH] = "";
|
|
int Rows = 0;
|
|
|
|
STORE Store1 = {0,};
|
|
STORE Store2 = {0,};
|
|
|
|
EQUIP EquipData = {0,};
|
|
INVEN InvenData = {0,};
|
|
EXTRA ExtraData = {0,};
|
|
EXCHANGE Exchange = {0,};
|
|
|
|
typedef std::list<DWORD> UIDList;
|
|
UIDList m_UIDList;
|
|
|
|
DWORD UIDs[CDBSingleObject::MaxRowNum];
|
|
sprintf(Query, "select UID from UserInfo");
|
|
for(int StartRows = 0;; StartRows += Rows)
|
|
{
|
|
memset(UIDs, 0, sizeof(DWORD) * CDBSingleObject::MaxRowNum);
|
|
if(!m_ReadDB.Select(Query, (void**)&UIDs, sizeof(DWORD), StartRows, CDBSingleObject::MaxRowNum, &Rows))
|
|
{
|
|
ERRLOG0(g_Log, "UID 얻기 실패");
|
|
return false;
|
|
}
|
|
|
|
for(int Count = 0; Count < Rows; ++Count)
|
|
{
|
|
m_UIDList.push_back(UIDs[Count]);
|
|
}
|
|
|
|
if(Rows != CDBSingleObject::MaxRowNum)
|
|
break;
|
|
}
|
|
|
|
DETLOG1(g_Log, "리스트 유저 숫자 : %d", m_UIDList.size());
|
|
|
|
DWORD UserID = 0;
|
|
|
|
USER_INFO UserInfo = {0,};
|
|
DWORD CharID[3] = {0,};
|
|
|
|
SKILL Skill;
|
|
SKILL oldSkill;
|
|
|
|
QUICK Quick;
|
|
|
|
int nCurrentCount = 0;
|
|
int nTotalCount = m_UIDList.size();
|
|
|
|
for(UIDList::iterator itr = m_UIDList.begin(); itr != m_UIDList.end(); ++itr)
|
|
{
|
|
UserID = *itr;
|
|
|
|
memset(&UserInfo, 0, sizeof(USER_INFO));
|
|
memset(&CharID, 0, sizeof(DWORD) * 3);
|
|
|
|
if(!DBComponent::GameDB::GetUserInfo(m_ReadDB, UserID, &UserInfo))
|
|
continue;
|
|
|
|
CharID[0] = UserInfo.Char1;
|
|
CharID[1] = UserInfo.Char2;
|
|
CharID[2] = UserInfo.Char3;
|
|
|
|
memset(&Store1, 0, sizeof(STORE));
|
|
memset(&Store2, 0, sizeof(STORE));
|
|
|
|
for(int Count = 0; Count < 3; ++Count)
|
|
{
|
|
if(0 == CharID[Count])
|
|
continue;
|
|
|
|
memset(&EquipData, 0, sizeof(EQUIP));
|
|
memset(&InvenData, 0, sizeof(INVEN));
|
|
memset(&ExtraData, 0, sizeof(EXTRA));
|
|
memset(&Exchange, 0, sizeof(EXCHANGE));
|
|
|
|
// 장비
|
|
if(!DBComponent::GameDB::GetEquip(m_ReadDB, CharID[Count], &EquipData))
|
|
{
|
|
ERRLOG2(g_Log, "장비 읽기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, CharID[Count], EquipData.Data, EquipData.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateEquip(m_WriteDB, CharID[Count], &EquipData))
|
|
{
|
|
ERRLOG2(g_Log, "장비 쓰기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
}
|
|
|
|
// 인벤
|
|
if(!DBComponent::GameDB::GetInven(m_ReadDB, CharID[Count], &InvenData))
|
|
{
|
|
ERRLOG2(g_Log, "인벤 읽기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, CharID[Count], InvenData.Data, InvenData.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateInven(m_WriteDB, CharID[Count], &InvenData))
|
|
{
|
|
ERRLOG2(g_Log, "인벤 쓰기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
}
|
|
|
|
// 여분
|
|
if(!DBComponent::GameDB::GetExtra(m_ReadDB, CharID[Count], &ExtraData))
|
|
{
|
|
ERRLOG2(g_Log, "여분 읽기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, CharID[Count], ExtraData.Data, ExtraData.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateExtra(m_WriteDB, CharID[Count], &ExtraData))
|
|
{
|
|
ERRLOG2(g_Log, "여분 쓰기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
}
|
|
|
|
// 교환
|
|
if(!DBComponent::GameDB::GetExchange(m_ReadDB, CharID[Count], &Exchange))
|
|
{
|
|
ERRLOG2(g_Log, "교환 읽기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, CharID[Count], Exchange.Data, Exchange.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateExchange(m_WriteDB, CharID[Count], &Exchange))
|
|
{
|
|
ERRLOG2(g_Log, "교환 쓰기 실패 UID: %d CID: %d", UserID, CharID[Count]);
|
|
}
|
|
}
|
|
|
|
// 퀵슬롯
|
|
if(!DBComponent::GameDB::GetQuick(m_ReadDB, CharID[Count], &Quick))
|
|
{
|
|
ERRLOG2(g_Log, "퀵슬롯 읽기 실패 UID:%d, CID:%d", CharID[Count], UserID);
|
|
}
|
|
else
|
|
{
|
|
if(0 < ProcessEraseHammerOfLightQuickSlot(CharID[Count], Quick))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateQuick(m_WriteDB, CharID[Count], &Quick))
|
|
{
|
|
ERRLOG2(g_Log, "퀵슬롯 쓰기 실패 UID:%d, CID:%d", CharID[Count], UserID);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// 스킬
|
|
if(!DBComponent::GameDB::GetCharSkill(m_ReadDB, CharID[Count], &Skill))
|
|
{
|
|
ERRLOG2(g_Log, "스킬 읽기 실패 UID:%d, CID:%d", CharID[Count], UserID);
|
|
}
|
|
else
|
|
{
|
|
oldSkill = Skill;
|
|
|
|
bool bChanged = false;
|
|
|
|
if(ProcessEraseHammerOfLight(CharID[Count], Skill))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
// 마지막에 해 줄 것
|
|
if(RecalculateSkill(CharID[Count], Skill))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
if(bChanged)
|
|
{
|
|
LogSkillToFile(m_fSrc, CharID[Count], oldSkill);
|
|
LogSkillToFile(m_fConverted, CharID[Count], Skill);
|
|
|
|
if(!DBComponent::GameDB::UpdateCharSkill(m_WriteDB, CharID[Count], &Skill))
|
|
{
|
|
ERRLOG2(g_Log, "스킬 쓰기 실패 UID:%d, CID:%d", CharID[Count], UserID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 창고1
|
|
if(!DBComponent::GameDB::GetItemStore1(m_ReadDB, UserID, &Store1))
|
|
{
|
|
ERRLOG1(g_Log, "창고1 읽기 실패 UID: %d", UserID);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, 0, Store1.Data, Store1.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateItemStore1(m_WriteDB, UserID, &Store1))
|
|
{
|
|
ERRLOG1(g_Log, "창고1 쓰기 실패 UID: %d", UserID);
|
|
}
|
|
}
|
|
|
|
// 창고 2
|
|
if(!DBComponent::GameDB::GetItemStore2(m_ReadDB, UserID, &Store2))
|
|
{
|
|
ERRLOG1(g_Log, "창고2 읽기 실패 UID: %d", UserID);
|
|
}
|
|
else if(0 < ProcessSkillBookConvert(UserID, 0, Store2.Data, Store2.dwSize))
|
|
{
|
|
if(!DBComponent::GameDB::UpdateItemStore2(m_WriteDB, UserID, &Store2))
|
|
{
|
|
ERRLOG1(g_Log, "창고2 쓰기 실패 UID: %d", UserID);
|
|
}
|
|
}
|
|
|
|
cout << ".";
|
|
|
|
if(0 != (nCurrentCount % 10000))
|
|
{
|
|
cout << endl << nCurrentCount << "/" << nTotalCount << endl;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
int CSkillConvert::ProcessSkillBookConvert(unsigned long dwUID, unsigned long dwCID, const char* lpBuffer, int nSize)
|
|
{
|
|
const char* lpItemBuff = lpBuffer;
|
|
int nItemSize = nSize;
|
|
|
|
int nConvertNum = 0;
|
|
|
|
int nLoopCount = 0;
|
|
|
|
while(nItemSize > 0)
|
|
{
|
|
Item::ItemData* lpItem = (Item::ItemData*)lpItemBuff;
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// 해머오브라이트 계열 스킬북을 퍼스트에이드 계열 스킬북으로 바꿉니다.
|
|
switch(lpItem->m_usProtoTypeID)
|
|
{
|
|
case 3558: lpItem->m_usProtoTypeID = 3553; ++nConvertNum; break;
|
|
case 3559: lpItem->m_usProtoTypeID = 3554; ++nConvertNum; break;
|
|
case 3560: lpItem->m_usProtoTypeID = 3555; ++nConvertNum; break;
|
|
case 3561: lpItem->m_usProtoTypeID = 3556; ++nConvertNum; break;
|
|
case 3562: lpItem->m_usProtoTypeID = 3557; ++nConvertNum; break;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// 다음 아이템을 진행합니다.
|
|
|
|
lpItemBuff += lpItem->m_cItemSize;
|
|
nItemSize -= lpItem->m_cItemSize;
|
|
|
|
++nLoopCount;
|
|
|
|
if(10000 < ++nLoopCount)
|
|
{
|
|
// 뭔가 이상하다. break한다.
|
|
SERLOG2(g_Log, "UID:%u/CID:%u/ 이상한 아이템을 지니고 있습니다. 확인해 주세요", dwUID, dwCID);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return nConvertNum;
|
|
}
|
|
|
|
|
|
int CSkillConvert::ProcessEraseHammerOfLightQuickSlot(unsigned long dwCID, QUICK& Quick)
|
|
{
|
|
int nErased = 0;
|
|
|
|
for(int nCount = 0; nCount < QUICK::MAX_QUICK_NUM; ++nCount)
|
|
{
|
|
if(Quick.Slots[nCount].nType == QUICKSLOT::SKILL &&
|
|
(Quick.Slots[nCount].wID == nHammerID || Quick.Slots[nCount].wID == nFirstAidID))
|
|
{
|
|
Quick.Slots[nCount].wID = 0;
|
|
Quick.Slots[nCount].nType = QUICKSLOT::NONE;
|
|
Quick.Slots[nCount].nSkillLevel = 0;
|
|
Quick.Slots[nCount].nSkillLockCount = 0;
|
|
|
|
++nErased;
|
|
}
|
|
|
|
}
|
|
|
|
return nErased;
|
|
}
|
|
|
|
void CSkillConvert::Test(const char* szFileName)
|
|
{
|
|
const char* szDelimit = ",";
|
|
unsigned long dwCID = 0;
|
|
SKILL Skill;
|
|
|
|
FILE* fReadFile = fopen(szFileName, "rt");
|
|
if(NULL != fReadFile)
|
|
{
|
|
char szWriteFile[MAX_PATH];
|
|
|
|
_snprintf(szWriteFile, MAX_PATH, "Result%s", szFileName);
|
|
szWriteFile[MAX_PATH - 1] = 0;
|
|
|
|
FILE* fWriteFile = fopen(szWriteFile, "wt");
|
|
if(NULL != fWriteFile)
|
|
{
|
|
const int MAX_DATA_LEN = 4096;
|
|
char szData[MAX_DATA_LEN];
|
|
|
|
while(fgets(szData, MAX_DATA_LEN, fReadFile))
|
|
{
|
|
memset(&Skill, 0, sizeof(SKILL));
|
|
|
|
char* szBuffer = strtok(szData, szDelimit);
|
|
if(NULL == szBuffer)
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwCID = atoi(szBuffer);
|
|
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
if(NULL == szBuffer)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Skill.wSkillNum = atoi(szBuffer);
|
|
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
if(NULL == szBuffer)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Skill.wSlotNum = atoi(szBuffer);
|
|
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
|
|
int nSlot = 0;
|
|
while(NULL != szBuffer && nSlot < SKILL::MAX_SLOT_NUM)
|
|
{
|
|
Skill.SSlot[nSlot].SKILLINFO.wSkill = Math::Convert::Atos(szBuffer); // 스킬 ID
|
|
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
if(NULL == szBuffer)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Skill.SSlot[nSlot].SKILLINFO.cLockCount = Math::Convert::Atoc(szBuffer);
|
|
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
if(NULL == szBuffer)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Skill.SSlot[nSlot].SKILLINFO.cSkillLevel = Math::Convert::Atoc(szBuffer);
|
|
|
|
++nSlot;
|
|
szBuffer = strtok(NULL, szDelimit);
|
|
}
|
|
|
|
ProcessEraseHammerOfLight(dwCID, Skill);
|
|
RecalculateSkill(dwCID, Skill);
|
|
|
|
LogSkillToFile(fWriteFile, dwCID, Skill);
|
|
}
|
|
|
|
fclose(fWriteFile);
|
|
}
|
|
|
|
fclose(fReadFile);
|
|
}
|
|
}
|
|
|
|
|
|
bool CSkillConvert::ProcessSkillLevel()
|
|
{
|
|
char* szQuery = "select UID, Skill from CharSkill";
|
|
|
|
#pragma pack(1)
|
|
|
|
struct SkillData
|
|
{
|
|
unsigned long m_dwCID;
|
|
SKILL m_Skill;
|
|
};
|
|
|
|
#pragma pack()
|
|
|
|
if(!m_ReadDB.ExecuteQuery(szQuery))
|
|
{
|
|
cout << "[Skill 쿼리 실패] : " << m_ReadDB.GetErrorString() << endl;
|
|
return false;
|
|
}
|
|
|
|
const int MAX_ROWS = 10240;
|
|
int nGetRows = 0;
|
|
SkillData* skillData = new SkillData[MAX_ROWS];
|
|
memset(skillData, 0, sizeof(SkillData) * MAX_ROWS);
|
|
|
|
while(m_ReadDB.GetData((void**)skillData, sizeof(SkillData), MAX_ROWS, &nGetRows))
|
|
{
|
|
if(0 == nGetRows)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for(SkillData* lpSkillData = skillData;
|
|
0 < nGetRows; --nGetRows, ++lpSkillData)
|
|
{
|
|
const unsigned long dwCID = lpSkillData->m_dwCID;
|
|
|
|
SKILL& skill = lpSkillData->m_Skill;
|
|
SKILL oldSkill = lpSkillData->m_Skill;
|
|
|
|
bool bChanged = false;
|
|
|
|
if(AdjustSkillLevel(skill))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
// 스킬 레벨을 조정한다.
|
|
if(bChanged)
|
|
{
|
|
LogSkillToFile(m_fSrc, dwCID, oldSkill);
|
|
LogSkillToFile(m_fConverted, dwCID, skill);
|
|
|
|
DBComponent::GameDB::UpdateCharSkill(m_WriteDB, dwCID, &skill);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] skillData;
|
|
|
|
|
|
szQuery = "select UID, Quick from CharItem";
|
|
|
|
#pragma pack(1)
|
|
|
|
struct QSlotData
|
|
{
|
|
unsigned long m_dwCID;
|
|
QUICK m_Quick;
|
|
};
|
|
|
|
#pragma pack()
|
|
|
|
if(!m_ReadDB.ExecuteQuery(szQuery))
|
|
{
|
|
cout << "[Quick 쿼리 실패] : " << m_ReadDB.GetErrorString() << endl;
|
|
return false;
|
|
}
|
|
|
|
nGetRows = 0;
|
|
QSlotData* qslotdata = new QSlotData[MAX_ROWS];
|
|
memset(qslotdata, 0, sizeof(QSlotData) * MAX_ROWS);
|
|
|
|
while(m_ReadDB.GetData((void**)qslotdata, sizeof(QSlotData), MAX_ROWS, &nGetRows))
|
|
{
|
|
if(0 == nGetRows)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for(QSlotData* lpQSlotData = qslotdata;
|
|
0 < nGetRows; --nGetRows, ++lpQSlotData)
|
|
{
|
|
const unsigned long dwCID = lpQSlotData->m_dwCID;
|
|
|
|
QUICK& quick = lpQSlotData->m_Quick;
|
|
QUICK oldquick = lpQSlotData->m_Quick;
|
|
|
|
bool bChanged = false;
|
|
|
|
if(AdjustQSlotSkillLevel(quick))
|
|
{
|
|
bChanged = true;
|
|
}
|
|
|
|
// 스킬 레벨을 조정한다.
|
|
if(bChanged)
|
|
{
|
|
DBComponent::GameDB::UpdateQuick(m_WriteDB, dwCID, &quick);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] qslotdata;
|
|
return true;
|
|
}
|
|
|
|
bool CSkillConvert::AdjustSkillLevel(SKILL& skill)
|
|
{
|
|
int nChangedCount = 0;
|
|
|
|
for(int nCount = 0; nCount < SKILL::MAX_SLOT_NUM; ++nCount)
|
|
{
|
|
if(6 == skill.SSlot[nCount].SKILLINFO.cSkillLevel)
|
|
{
|
|
if(skill.SSlot[nCount].SKILLINFO.cLockCount < 3)
|
|
{
|
|
++skill.SSlot[nCount].SKILLINFO.cLockCount;
|
|
skill.SSlot[nCount].SKILLINFO.cSkillLevel = 0;
|
|
|
|
++nChangedCount;
|
|
}
|
|
else
|
|
{
|
|
skill.SSlot[nCount].SKILLINFO.cLockCount = 3;
|
|
skill.SSlot[nCount].SKILLINFO.cSkillLevel = 6;
|
|
|
|
++nChangedCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0 < nChangedCount);
|
|
}
|
|
|
|
bool CSkillConvert::AdjustQSlotSkillLevel(QUICK& quick)
|
|
{
|
|
int nChangedCount = 0;
|
|
|
|
for(int nCount = 0; nCount < QUICK::MAX_QUICK_NUM; ++nCount)
|
|
{
|
|
if(QUICKSLOT::SKILL == quick.Slots[nCount].nType &&
|
|
6 == quick.Slots[nCount].nSkillLevel)
|
|
{
|
|
if(quick.Slots[nCount].nSkillLockCount < 3)
|
|
{
|
|
++quick.Slots[nCount].nSkillLockCount;
|
|
quick.Slots[nCount].nSkillLevel = 0;
|
|
|
|
++nChangedCount;
|
|
}
|
|
else
|
|
{
|
|
quick.Slots[nCount].nSkillLockCount = 3;
|
|
quick.Slots[nCount].nSkillLevel = 6;
|
|
|
|
++nChangedCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0 < nChangedCount);
|
|
}
|