Files
Client/Server/AdminTool/AdminToolLibrary/character/ModifyCharacter.cpp
LGram16 dd97ddec92 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>
2025-11-29 20:17:20 +09:00

611 lines
17 KiB
C++

#include "stdafx.h"
#include "ModifyCharacter.h"
#include <Network/Dispatch/Dispatch.h>
#include <Network/Protocol/Ryl_AdminMgrProtocol.h>
#include <Network/Packet/PacketStruct/CharStatusPacket.h>
#include <Network/Packet/PacketStruct/CharQuestPacket.h>
#include <Skill/SkillMgr.h>
#include <Item/ItemStructure.h>
#include <Log/ServerLog.h>
#include <Quest/QuestMgr.h>
#include <Creature/Character/CharacterClass.h>
#include <Creature/CreatureStructure.h>
CModifyCharacter::CModifyCharacter()
: CCharacter(0, 0)
, m_dwServerGroup(0)
, m_byChangedInfo(0)
, m_bScheduleClose(false)
, m_bSave(false)
, m_bIsOwnCopyItem(false)
{
Initialize(NULL);
ZeroMemory(&m_ExtraData, sizeof(CHAR_EXTRA_DATA));
m_ModifyCharItemSerialInfo.clear();
m_OverlapSerialInfo.clear();
m_cOldServerGroupID = UnifiedConst::Part2Selectable;
}
// 이녀석은 사용하지 말자 Initialize <- 이녀석 캐릭터 생성후 딱~~ 1번만 사용하자
// 그렇지 않으면 메모리 샌다 ㅡ.ㅡ;
void CModifyCharacter::Reset()
{
SetUID(0);
SetCID(0);
m_dwServerGroup = 0;
m_byChangedInfo = 0;
m_bScheduleClose = false;
ZeroMemory(&m_DBData, sizeof(CharacterDBData));
Initialize(NULL);
}
unsigned short CModifyCharacter::GetMaxSkillPoint()
{
// CalculateStatusData가 선행되어야 함.
return m_CreatureStatus.m_StatusInfo.m_wSkillPoint;
}
// 스킬 추가 및 스킬 레벨 변경 (이미 습득한 스킬을 추가 요청한다면 레벨 수정으로 인식)
unsigned char CModifyCharacter::AppendSkill(unsigned short wSkillID, char cLevel, char cLockCount)
{
m_DBData.m_Skill.wSkillNum = m_DBData.m_Skill.GetSkillNum(); // 현재 스킬수 재 계산
const Skill::ProtoType* lpSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(wSkillID);
short sSlotIndex = GetSkillSlotIndex(wSkillID);
bool bIsNewSkill = false; // 새 스킬 습득 여부
if(-1 == sSlotIndex) bIsNewSkill = true;
if(NULL == lpSkillProtoType)
{
ERRLOG1(g_Log, "스킬 프로토 타입 얻기 실패. 스킬ID: 0x%04x", wSkillID);
return PktAdminMgr::PktSkillEdit::FAILED_TO_GET_SKILLPROTOTYPE;
}
if(CSkillMgr::MAX_SKILL_LOCKCOUNT <= cLockCount)
{
ERRLOG1(g_Log, "최대 스킬 락 카운트를 초과하였습니다. 락 카운트: %c", cLockCount);
return PktAdminMgr::PktSkillEdit::OVERED_MAX_LOCKCOUNT;
}
if(CSkillMgr::MAX_SKILL_LEVEL < cLevel)
{
ERRLOG1(g_Log, "최대 스킬 레벨을 초과하였습니다. 스킬 레벨: %c", cLevel);
return PktAdminMgr::PktSkillEdit::OVERED_MAX_SKILL_LEVEL;
}
if(lpSkillProtoType[0].m_usSkill_ID != lpSkillProtoType[cLockCount].m_usSkill_ID)
{
ERRLOG2(g_Log, "스킬 ID가 다릅니다. 0x%04x : 0x%04x", lpSkillProtoType[0].m_usSkill_ID,
lpSkillProtoType[cLockCount].m_usSkill_ID);
return PktAdminMgr::PktSkillEdit::FAILED;
}
// 최대 스킬 레벨을 추가할 경우 (다음 단계로 넘어가는 경우)
if(cLevel == CSkillMgr::MAX_SKILL_LEVEL)
{
// 최종 단계 스킬이 아닌경우 다음 단계 0레벨 스킬로 습득
if(cLockCount < CSkillMgr::MAX_SKILL_LOCKCOUNT - 1) // 아직 0~3 단계까지만 사용중
{
cLevel = 0;
++cLockCount;
}
}
// 새로운 스킬 습득
if(bIsNewSkill && (m_DBData.m_Skill.wSlotNum < SKILL::MAX_SLOT_NUM))
{
sSlotIndex = m_DBData.m_Skill.wSlotNum; // 새 슬롯 얻기
}
else if(m_DBData.m_Skill.wSlotNum == SKILL::MAX_SLOT_NUM)
{
ERRLOG0(g_Log, "이미 사용할 수 있는 최대 슬롯을 사용중입니다.");
return PktAdminMgr::PktSkillEdit::NOT_ENOUGH_SKILLSLOT;
}
SKILLSLOT TempSkillSlot;
TempSkillSlot.SKILLINFO.wSkill = wSkillID;
TempSkillSlot.SKILLINFO.cSkillLevel = cLevel;
TempSkillSlot.SKILLINFO.cLockCount = cLockCount;
unsigned short cResult = ReadSkill(TempSkillSlot, wSkillID, cLockCount);
if(PktBase::NO_SERVER_ERR == cResult) // 사용 가능한 스킬임
{
SKILL& Skill = m_DBData.m_Skill;
SKILLSLOT& SkillSlot = Skill.SSlot[sSlotIndex];
ChkEmptySlot(Skill); // 빈 슬롯을 가지고 있는지 체크
unsigned short BeforePoint =
(SkillSlot.SKILLINFO.cLockCount * CSkillMgr::MAX_SKILL_LOCKCOUNT) + SkillSlot.SKILLINFO.cSkillLevel;
unsigned short AfterPoint =
(cLockCount * CSkillMgr::MAX_SKILL_LOCKCOUNT) + cLevel;
// 기존 스킬 레벨 변경
if((!bIsNewSkill) && (GetMaxSkillPoint() >= ((GetSkillPoint() - BeforePoint) + AfterPoint)))
{
SkillSlot.SKILLINFO.wSkill = wSkillID;
SkillSlot.SKILLINFO.cSkillLevel = cLevel;
SkillSlot.SKILLINFO.cLockCount = cLockCount;
}
// 새로운 스킬 추가
else if(bIsNewSkill && (GetMaxSkillPoint() >= (GetSkillPoint() + AfterPoint)))
{
SkillSlot.SKILLINFO.wSkill = wSkillID;
SkillSlot.SKILLINFO.cSkillLevel = cLevel;
SkillSlot.SKILLINFO.cLockCount = cLockCount;
++m_DBData.m_Skill.wSlotNum; // 사용중인 슬롯수 + 1
}
UpdateQuickSlotSkill(SkillSlot);
m_DBData.m_Skill.wSkillNum = m_DBData.m_Skill.GetSkillNum();
}
else
{
switch(cResult)
{
case PktSk::FAIL_NOT_CURRENT_CLASS:
return PktAdminMgr::PktSkillEdit::NOT_CURRENT_CLASS;
case PktSk::FAIL_NOT_ENOUGH_STATUS:
return PktAdminMgr::PktSkillEdit::NOT_ENOUGH_STAT;
case PktSk::FAIL_MAX_LEVEL:
return PktAdminMgr::PktSkillEdit::OVERED_MAX_SKILL_LEVEL;
case PktSk::FAIL_NOT_CURRENT_LOCK_COUNT:
return PktAdminMgr::PktSkillEdit::FAILED;
default:
return PktAdminMgr::PktSkillEdit::FAILED;
}
}
return PktAdminMgr::PktSkillEdit::SUCCESS;
}
// 스킬 삭제 (스킬 포인트 조정 및 해당 스킬 슬롯 정보 초기화)
unsigned char CModifyCharacter::DeleteSkill(unsigned short wSkillID, char cLevel, char cLockCount)
{
m_DBData.m_Skill.wSkillNum = m_DBData.m_Skill.GetSkillNum(); // 현재 스킬수 재 계산
short sDelSlotIndex = GetSkillSlotIndex(wSkillID);
if(-1 != sDelSlotIndex)
{
SKILL& Skill = m_DBData.m_Skill;
SKILLSLOT& DelSlot = Skill.SSlot[sDelSlotIndex];
ChkEmptySlot(Skill); // 빈 슬롯을 가지고 있는지 체크
const Skill::ProtoType* lpSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(wSkillID);
if(NULL == lpSkillProtoType)
{
ERRLOG1(g_Log, "스킬 프로토 타입 얻기 실패. 스킬ID: 0x%04x", wSkillID);
return PktAdminMgr::PktSkillEdit::FAILED_TO_GET_SKILLPROTOTYPE;
}
if(DelSlot.SKILLINFO.wSkill != wSkillID)
{
ERRLOG3(g_Log, "삭제 요청 받은 스킬과 삭제 할 슬롯의 스킬이 일치하지 않습니다."
"슬롯번호: %d , 삭제할 슬롯의 스킬ID: 0x%04x, 삭제 요청 한 스킬ID: 0x%04x",
sDelSlotIndex, DelSlot.SKILLINFO.wSkill, wSkillID);
return PktAdminMgr::PktSkillEdit::FAILED;
}
if(true == lpSkillProtoType->m_bIsClassSkill)
{
ERRLOG0(g_Log, "삭제하려는 스킬이 클래스 스킬입니다.");
return PktAdminMgr::PktSkillEdit::FAILED;
}
// 스킬 슬롯 중간이 비면 한칸씩 앞으로 당겨옴 (중간에 비는 슬롯이 없도록!)
unsigned short sLastSlotIndex = Skill.wSlotNum - 1;
for(unsigned short sSlotIndex = sDelSlotIndex; sSlotIndex < sLastSlotIndex; ++sSlotIndex)
{
Skill.SSlot[sSlotIndex] = Skill.SSlot[sSlotIndex + 1];
}
Skill.SSlot[sLastSlotIndex].SKILLINFO.wSkill = 0;
Skill.SSlot[sLastSlotIndex].SKILLINFO.cSkillLevel = 0;
Skill.SSlot[sLastSlotIndex].SKILLINFO.cLockCount = 0;
Skill.wSkillNum -=
(DelSlot.SKILLINFO.cLockCount * CSkillMgr::MAX_SKILL_LEVEL) + DelSlot.SKILLINFO.cSkillLevel;
Skill.wSlotNum -= 1;
UpdateQuickSlotSkill(DelSlot);
return PktAdminMgr::PktSkillEdit::SUCCESS;
}
return PktAdminMgr::PktSkillEdit::FAILED;
}
bool CModifyCharacter::ChkEmptySlot(SKILL Skill)
{
CString strEmptySlotIndex;
for(unsigned short sSlotIndex = 0; sSlotIndex < Skill.wSlotNum; ++sSlotIndex)
{
if(0 == Skill.SSlot[sSlotIndex].SKILLINFO.wSkill)
{
strEmptySlotIndex.AppendFormat("%d ", sSlotIndex);
}
}
if(!strEmptySlotIndex.IsEmpty())
{
ERRLOG3(g_Log, "스킬 슬롯에 빈슬롯이 있습니다. CID: %u, 슬롯수: %d, 빈슬롯: %s",
GetCID(), Skill.wSlotNum, strEmptySlotIndex);
return true;
}
return false;
}
// 걍 날림 스킬레벨가져오기..
// Return :
// 존재하는 스킬이면 현재 내레벨.. 같은계열의 하위스킬일경우 MaxSkillLevel을...
// 같은 계열스킬중 상위계열이면 0을 리턴..
short CModifyCharacter::GetSkillLevelEX(unsigned short usSkillType, char cLockCount)
{
const Skill::ProtoType* lpSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(usSkillType);
if(NULL != lpSkillProtoType)
{
int nMaxSlotNum = m_DBData.m_Skill.wSlotNum;
if(SKILL::MAX_SLOT_NUM >= nMaxSlotNum)
{
for( int nSlot = 0; nSlot < nMaxSlotNum; ++ nSlot )
{
const SKILLSLOT& SkillSlot = m_DBData.m_Skill.SSlot[ nSlot ];
if( SkillSlot.SKILLINFO.wSkill == usSkillType )
{
if( SkillSlot.SKILLINFO.cLockCount == cLockCount )
{
return SkillSlot.SKILLINFO.cSkillLevel;
}
else if( SkillSlot.SKILLINFO.cLockCount > cLockCount )
{
return SKILL::MAX_SKILL_LEVEL;
}
else
{
return 0;
}
}
}
}
}
return -1;
}
bool CModifyCharacter::SetGold(unsigned long dwGold)
{
if(dwGold > ULONG_MAX)
{
return false;
}
m_DBData.m_Info.Gold = dwGold;
return true;
}
// 캐릭터를 잡고 있는 관리자의 Session을 리턴~~
CSession* CModifyCharacter::GetCheckSession( unsigned long dwUID )
{
if( m_dwUID == dwUID )
{
if( NULL != m_lpPacketDispatch )
{
CSession& lpSession = m_lpPacketDispatch->GetSession( );
//if( NULL != lpSession ) return lpSession;
return &lpSession;
}
}
return NULL;
}
// 캐릭터 정보가 변경됬음을 기억해놓자..
void CModifyCharacter::OnChangedInfo( int iMask )
{
m_byChangedInfo |= iMask;
}
// 캐릭터 변경정보를 지운다..
void CModifyCharacter::OffChangedInfo( int iMask )
{
m_byChangedInfo &= iMask;
}
// 캐릭터의 Status
void CModifyCharacter::GetModifyCharStatus(PktAdminMgr::CHAR_STATUS_ST& StatusST)
{
StatusST.m_cFace = m_DBData.m_Info.Face;
StatusST.m_cHair = m_DBData.m_Info.Hair;
StatusST.m_cLevel = m_DBData.m_Info.Level;
StatusST.m_cRace = m_DBData.m_Info.Race;
StatusST.m_cSex = m_DBData.m_Info.Sex;
StatusST.m_dwExp = m_DBData.m_Info.Exp;
StatusST.m_dwFame = m_DBData.m_Info.Fame;
StatusST.m_dwMileage = m_DBData.m_Info.Mileage;
StatusST.m_nClass = m_DBData.m_Info.Class;
StatusST.m_nCON = m_DBData.m_Info.CON;
StatusST.m_nDEX = m_DBData.m_Info.DEX;
StatusST.m_nINT = m_DBData.m_Info.INT;
StatusST.m_nIP = m_DBData.m_Info.IP;
StatusST.m_nSTR = m_DBData.m_Info.STR;
StatusST.m_nWIS = m_DBData.m_Info.WIS;
StatusST.m_cChance = m_DBData.m_Info.Chance;
}
// 캐릭터 Status변경..
bool CModifyCharacter::UpdataStatus(PktAdminMgr::CHAR_STATUS_ST& StatusST)
{
CHAR_INFOST OrgCharInfo;
CopyMemory(&OrgCharInfo, &m_DBData.m_Info, sizeof(CHAR_INFOST));
m_DBData.m_Info.Sex = StatusST.m_cSex;
m_DBData.m_Info.Hair = StatusST.m_cHair;
m_DBData.m_Info.Face = StatusST.m_cFace;
m_DBData.m_Info.Race = StatusST.m_cRace;
m_DBData.m_Info.Class = StatusST.m_nClass;
m_DBData.m_Info.Fame = StatusST.m_dwFame;
m_DBData.m_Info.Mileage = StatusST.m_dwMileage;
m_DBData.m_Info.Level = StatusST.m_cLevel;
m_DBData.m_Info.Exp = StatusST.m_dwExp;
m_DBData.m_Info.IP = StatusST.m_nIP;
m_DBData.m_Info.STR = StatusST.m_nSTR;
m_DBData.m_Info.CON = StatusST.m_nCON;
m_DBData.m_Info.DEX = StatusST.m_nDEX;
m_DBData.m_Info.INT = StatusST.m_nINT;
m_DBData.m_Info.WIS = StatusST.m_nWIS;
m_DBData.m_Info.Chance = StatusST.m_cChance;
m_CreatureStatus.m_nExp = StatusST.m_dwExp;
m_CreatureStatus.m_nLevel = StatusST.m_cLevel;
// Status 계산식을 넣자
if(!CalculateStatusData(false))
{
// 잘못된 Status값
CopyMemory(&m_DBData.m_Info, &OrgCharInfo, sizeof(CHAR_INFOST));
return false;
}
return true;
}
// 캐릭터가 소지하고 있는 아이템을 UID로 검색하여 리턴한다..
// 해당 아이템이 없으면 NULL
Item::CItem* CModifyCharacter::UIDbyItem(unsigned __int64 ItemUID, unsigned char cTakeType)
{
Item::CItemContainer::iterator Itr;
if(cTakeType == TakeType::TS_INVEN) // 인벤토리 아이템
{
Item::CArrayContainer& ArrayContainer = GetInventory();
Itr = ArrayContainer.begin();
for(;Itr != ArrayContainer.end(); ++Itr)
{
if(NULL != (*Itr))
{
if((*Itr)->GetUID() == ItemUID) return (*Itr);
}
}
}
else if( cTakeType == TakeType::TS_EQUIP ) // 장비창 아이템
{
Item::CEquipmentsContainer& EquipContainer = GetEquipments( );
Itr = EquipContainer.begin( );
for(;Itr != EquipContainer.end(); ++Itr )
{
if(NULL != (*Itr))
{
if((*Itr)->GetUID() == ItemUID) return (*Itr);
}
}
}
else if(cTakeType == TakeType::TS_DEPOSIT) // 창고 아이템
{
Item::CDepositContainer& DepositContainer = GetDeposit();
for (unsigned char cTab = 0; cTab < DepositContainer.GetMaxTabNum(); ++cTab)
{
Item::CItemContainer* DepositTabContainer = DepositContainer.GetTab(cTab);
Itr = DepositTabContainer->begin();
for(; Itr != DepositTabContainer->end( ); ++Itr )
{
if( NULL != ( *Itr ) )
{
if( ( *Itr )->GetUID( ) == ItemUID ) return ( *Itr );
}
}
}
}
return NULL;
}
void CModifyCharacter::ChangeName(char* szName)
{
strncpy(m_DBData.m_Info.Name, szName, CHAR_INFOST::MAX_NAME_LEN);
}
// 캐릭터를 편집중인 관리자 계정을 저장한다.
void CModifyCharacter::SetModifyAdmin(char* Account, int Length)
{
strncpy(m_szModifyAdmin, Account, Length);
}
void CModifyCharacter::ParseQuestData()
{
Quest::ExecutingQuest* aryExecutingQuest = GetExecutingQuest();
unsigned short* aryHistoryQuest = GetHistoryQuest();
// 수행중인 퀘스트 목록
int nIndex = 0;
PktQuestDB::ExecutingQuest* lpExecuteQuestPos =
reinterpret_cast<PktQuestDB::ExecutingQuest*>(m_ExtraData.m_Quest.Data);
PktQuestDB::ExecutingQuest* lpExecuteQuestEnd =
reinterpret_cast<PktQuestDB::ExecutingQuest*>(m_ExtraData.m_Quest.Data) +
(m_ExtraData.m_Quest.dwSize / sizeof(PktQuestDB::ExecutingQuest));
for(; lpExecuteQuestPos != lpExecuteQuestEnd; ++lpExecuteQuestPos)
{
Quest::QuestNode* lpQuest = CQuestMgr::GetInstance().GetQuestNode(lpExecuteQuestPos->m_wQuestID);
if (0 != lpQuest && false == lpQuest->CheckNationDependent(GetUserNation()))
{
aryExecutingQuest[nIndex] = Quest::ExecutingQuest(lpExecuteQuestPos->m_wQuestID,
lpExecuteQuestPos->m_cPhase, lpExecuteQuestPos->m_cTriggerCount);
++nIndex;
}
}
nIndex = 0;
// 완료 퀘스트 목록
unsigned short* lpHistoryQuestPos =
reinterpret_cast<unsigned short*>(m_ExtraData.m_History.Data);
unsigned short* lpHistoryQuestEnd =
reinterpret_cast<unsigned short*>(m_ExtraData.m_History.Data) +
(m_ExtraData.m_History.dwSize / sizeof(unsigned short));
for (; lpHistoryQuestPos != lpHistoryQuestEnd; ++lpHistoryQuestPos)
{
Quest::QuestNode* lpQuest = CQuestMgr::GetInstance().GetQuestNode(*lpHistoryQuestPos);
if (0 != lpQuest && false == lpQuest->CheckNationDependent(GetUserNation()))
{
aryHistoryQuest[nIndex] = *lpHistoryQuestPos;
++nIndex;
}
}
// 퀘스트에 의해 받는 영향을 계산
CalculateStatusData(false);
}
void CModifyCharacter::SetQuestData()
{
const int MAX_BUFFER =
sizeof(PktQuestDB) + PktQuestDB::MAX_EXECUTING_QUEST * sizeof(PktQuestDB::ExecutingQuest) +
PktQuestDB::MAX_HISTORY_QUEST * sizeof(unsigned short);
char szBuffer[MAX_BUFFER];
PktQuestDB* lpPktQuestDB = reinterpret_cast<PktQuestDB*>(szBuffer);
lpPktQuestDB->m_dwUID = GetUID();
lpPktQuestDB->m_dwCID = GetCID();
lpPktQuestDB->m_wExecuteQuestSize = 0;
lpPktQuestDB->m_wHistoryQuestSize = 0;
Quest::ExecutingQuest* ExecutingQuest = GetExecutingQuest();
unsigned short *wHistoryQuest = GetHistoryQuest();
int nIndex = 0;
for (nIndex = 0; nIndex < PktQuestDB::MAX_EXECUTING_QUEST; ++nIndex)
{
if (NULL == ExecutingQuest[nIndex].m_QuestNode)
{
break;
}
PktQuestDB::ExecutingQuest* ExecuteQuest =
reinterpret_cast<PktQuestDB::ExecutingQuest*>
(szBuffer + sizeof(PktQuestDB) + lpPktQuestDB->m_wExecuteQuestSize);
ExecuteQuest->m_wQuestID = ExecutingQuest[nIndex].m_QuestNode->m_wQuestID;
ExecuteQuest->m_cPhase = ExecutingQuest[nIndex].m_cPhase;
memcpy(ExecuteQuest->m_cTriggerCount, ExecutingQuest[nIndex].m_cTriggerCount,
sizeof(unsigned char) * PktQuestDB::MAX_TRIGGER);
lpPktQuestDB->m_wExecuteQuestSize += sizeof(PktQuestDB::ExecutingQuest);;
}
for (nIndex = 0; nIndex < PktQuestDB::MAX_HISTORY_QUEST; nIndex++)
{
if (0 == wHistoryQuest[nIndex])
{
break;
}
unsigned short* wHistoryQuestForPkt =
reinterpret_cast<unsigned short *>(szBuffer + sizeof(PktQuestDB) +
lpPktQuestDB->m_wExecuteQuestSize + lpPktQuestDB->m_wHistoryQuestSize);
*wHistoryQuestForPkt = wHistoryQuest[nIndex];
lpPktQuestDB->m_wHistoryQuestSize += sizeof(unsigned short);
}
memset(&m_ExtraData.m_Quest, 0, sizeof(QUEST));
m_ExtraData.m_Quest.dwSize = lpPktQuestDB->m_wExecuteQuestSize;
memcpy(m_ExtraData.m_Quest.Data, lpPktQuestDB + 1, lpPktQuestDB->m_wExecuteQuestSize);
memset(&m_ExtraData.m_History, 0, sizeof(HISTORY));
m_ExtraData.m_History.dwSize = lpPktQuestDB->m_wHistoryQuestSize;
memcpy(m_ExtraData.m_History.Data, reinterpret_cast<char*>(lpPktQuestDB + 1) + lpPktQuestDB->m_wExecuteQuestSize,
lpPktQuestDB->m_wHistoryQuestSize);
}
void CModifyCharacter::RevisionQuestNation()
{
using namespace Creature;
// 양국체제에선 무조건 휴먼은 카르테란트, 아칸은 메르카디아! (퀘스트 파싱할때 필요)
switch(GetRace())
{
case CClass::RaceType::HUMAN:
m_cQuestNation = Creature::KARTERANT;
break;
case CClass::RaceType::AKHAN:
m_cQuestNation = Creature::MERKADIA;
break;
default:
m_cQuestNation = Creature::MAX_NATION;
break;
}
}
// class CModifyItem Down_Cast ----------------------------------------------------------------------------------------------------------------------------------
void CModifyItem::SetUID(unsigned __int64 UID)
{
m_ItemData.m_dwUID = UID;
}
void CModifyItem::SetPrototoypID(unsigned short PrototypeID)
{
m_ItemData.m_usProtoTypeID = PrototypeID;
}