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>
This commit is contained in:
2025-11-29 20:17:20 +09:00
parent 5d3cd64a25
commit dd97ddec92
11602 changed files with 1446576 additions and 0 deletions

View File

@@ -0,0 +1,726 @@
#include "stdafx.h"
#include "CharRespawnMgr.h"
#include <Utility/Math/Math.h>
#include <Utility/DelimitedFile.h>
#include <Utility/Setup/ServerSetup.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/Camp.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <GameTime/GameTimeMgr.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Packet/PacketStruct/CharAttackPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/WrapPacket.h>
const char* CCharRespawnMgr::ms_szRespawnScriptFileName = "./Script/Game/RespawnScript.txt";
CCharRespawnMgr& CCharRespawnMgr::GetInstance()
{
static CCharRespawnMgr ms_this;
return ms_this;
}
CCharRespawnMgr::CCharRespawnMgr()
{
}
CCharRespawnMgr::~CCharRespawnMgr()
{
m_ProtoTypeList.clear();
m_AllZoneTownList.clear();
}
bool CCharRespawnMgr::LoadRespawnFromFile(const char* szFileName)
{
int nLineCount = 0;
char strTemp[MAX_PATH];
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
RespawnProtoType tempProtoType;
Position tempRespawnPos;
m_ProtoTypeList.clear();
m_AllZoneTownList.clear();
// 매크로에 로그 코드 삽입을 잊지 말 것.
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
#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; \
}
if (!DelimitedFile.Open(szFileName ? szFileName : ms_szRespawnScriptFileName))
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szFileName ? szFileName : ms_szRespawnScriptFileName);
return false;
}
while (DelimitedFile.ReadLine())
{
++nLineCount;
// 순서가 바뀌면 곤란하다니깐~!!! (버럭!)
tempProtoType.m_dwGID = 0;
READ_STRING("TownID", strTemp, MAX_PATH);
tempProtoType.m_dwTownID = Math::Convert::Atoi(strTemp);
READ_DATA("Nation", tempProtoType.m_cNation);
READ_DATA("Zone", tempProtoType.m_cZone);
int iRespawnPosCount = 0;
READ_DATA("RespawnPosCount", iRespawnPosCount);
if (CServerSetup::GetInstance().GetServerZone() == tempProtoType.m_cZone)
{
for (int i=0; i<iRespawnPosCount; ++i)
{
READ_DATA("RespawnPosX", tempRespawnPos.m_fPointX);
READ_DATA("RespawnPosY", tempRespawnPos.m_fPointY);
READ_DATA("RespawnPosZ", tempRespawnPos.m_fPointZ);
tempProtoType.m_RespawnPosList.push_back(tempRespawnPos);
}
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(tempProtoType.m_dwTownID);
if (itr == m_ProtoTypeList.end())
{
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
// 마을일 경우 모든 존의 마을 리스폰 정보 맵에 집어 넣는다.
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
m_AllZoneTownList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
}
}
else
{
ERRLOG1(g_Log, "리스폰 스크립트에 마을 혹은 성의 ID가 같은 것이 두개이상 존재합니다. TownID : %d", tempProtoType.m_dwTownID);
return false;
}
RespawnCharMap::iterator CharItr = m_CharList.find(tempProtoType.m_dwTownID);
if (CharItr == m_CharList.end())
{
RespawnCharList tempRespawnCharList;
tempRespawnCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempRespawnCharList));
}
}
else
{
// 마을일 경우 모든 존의 마을 리스폰 정보 맵에 집어 넣는다.
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
for (int i=0; i<iRespawnPosCount; ++i)
{
READ_DATA("RespawnPosX", tempRespawnPos.m_fPointX);
READ_DATA("RespawnPosY", tempRespawnPos.m_fPointY);
READ_DATA("RespawnPosZ", tempRespawnPos.m_fPointZ);
tempProtoType.m_RespawnPosList.push_back(tempRespawnPos);
}
RespawnProtoTypeMap::iterator itr = m_AllZoneTownList.find(tempProtoType.m_dwTownID);
if (itr == m_AllZoneTownList.end())
{
m_AllZoneTownList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
}
else
{
ERRLOG1(g_Log, "리스폰 스크립트에 마을 혹은 성의 ID가 같은 것이 두개이상 존재합니다. TownID : %d", tempProtoType.m_dwTownID);
return false;
}
}
}
tempProtoType.m_RespawnPosList.clear();
}
return true;
}
bool CCharRespawnMgr::AddCampRespawnPoint(unsigned long dwCampID, unsigned long dwGID, const Position& RespawnPos)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwCampID);
if (itr != m_ProtoTypeList.end())
{
ERRLOG1(g_Log, "CampID : 0x%08x 이미 존재하는 리스폰 포인트(길드 요새) 입니다.", dwCampID);
return false;
}
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(dwGID);
if (lpGuild)
{
RespawnProtoType tempProtoType;
tempProtoType.m_dwTownID = dwCampID;
tempProtoType.m_dwGID = dwGID;
tempProtoType.m_cNation = lpGuild->GetNation();
tempProtoType.m_cZone = CServerSetup::GetInstance().GetServerZone();
tempProtoType.m_RespawnPosList.push_back(RespawnPos);
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
RespawnCharList tempCharList;
tempCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempCharList));
return true;
}
return false;
}
bool CCharRespawnMgr::DeleteCampRespawnPoint(unsigned long dwCampID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwCampID);
if (itr != m_ProtoTypeList.end())
{
m_ProtoTypeList.erase(itr);
return true;
}
ERRLOG1(g_Log, "CampID : 0x%08x 존재하지 않는 리스폰 포인트(길드 요새) 입니다.", dwCampID);
return false;
}
bool CCharRespawnMgr::AddRealmStatueRespawnPoint(unsigned long dwIndex, unsigned char cNation, const Position& RespawnPos)
{
unsigned long dwStatueID = Castle::STATUE_BIT | (dwIndex << 16);
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwStatueID);
if (itr != m_ProtoTypeList.end())
{
ERRLOG2(g_Log, "Statue ID : 0x%08x (0x%08x) 이미 존재하는 리스폰 포인트(다크 카나번 석상) 입니다.", dwStatueID, dwIndex);
return false;
}
RespawnProtoType tempProtoType;
tempProtoType.m_dwTownID = dwStatueID;
tempProtoType.m_dwGID = 0;
tempProtoType.m_cNation = cNation;
tempProtoType.m_cZone = CServerSetup::GetInstance().GetServerZone();
tempProtoType.m_RespawnPosList.push_back(RespawnPos);
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
RespawnCharList tempCharList;
tempCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempCharList));
return true;
}
bool CCharRespawnMgr::DeleteRealmStatueRespawnPoint(unsigned long dwIndex)
{
unsigned long dwStatueID = Castle::STATUE_BIT | (dwIndex << 16);
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwStatueID);
if (itr != m_ProtoTypeList.end())
{
m_ProtoTypeList.erase(itr);
return true;
}
ERRLOG2(g_Log, "Statue ID : 0x%08x (0x%08x) 존재하지 않는 리스폰 포인트(다크 카나번 석상) 입니다.", dwStatueID, dwIndex);
return false;
}
bool CCharRespawnMgr::SetCastleRespawnPointNation(unsigned long dwCastleNameID, unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
RespawnProtoTypeMap::iterator end = m_ProtoTypeList.end();
bool bRetFound = false;
while (itr != end)
{
RespawnProtoType& protoType = itr->second;
if (0 != (Castle::CASTLE_BIT & protoType.m_dwTownID))
{
unsigned long dwMask = dwCastleNameID << Castle::CASTLE_NAME_BIT_SHIFT;
if (dwMask == (dwMask & protoType.m_dwTownID))
{
if (0 == (Castle::SIEGE_BIT & protoType.m_dwTownID))
{
// 수성
bRetFound = true;
protoType.m_cNation = cNation;
}
else
{
// 공성
switch (cNation)
{
case Creature::KARTERANT:
protoType.m_cNation = Creature::MERKADIA;
break;
case Creature::MERKADIA:
protoType.m_cNation = Creature::KARTERANT;
break;
case Creature::ALMIGHTY_PIRATE:
case Creature::STATELESS:
default:
protoType.m_cNation = Creature::STATELESS;
break;
}
}
}
}
++itr;
}
if (false == bRetFound)
{
ERRLOG1(g_Log, "CastleNameID : %d 존재하지 않는 리스폰 포인트(성) 입니다.", dwCastleNameID);
}
return bRetFound;
}
/*
void CCharRespawnMgr::DelCharacter(unsigned long dwTownID, unsigned long dwCID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
RespawnCharMap::iterator it = m_CharList.find(dwTownID);
if (it != m_CharList.end())
{
RespawnCharList& tempCharList = it->second;
RespawnCharList::iterator CharListItr = tempCharList.begin();
while (CharListItr != tempCharList.end())
{
RespawnCharInfo& tempCharInfo = *CharListItr;
if(tempCharInfo.m_dwCID == dwCID)
{
CharListItr = tempCharList.erase(CharListItr);
return;
}
++CharListItr;
}
}
}
}
*/
CCharRespawnMgr::eReturn CCharRespawnMgr::AddCharacter(unsigned long dwTownID, unsigned long dwCID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (dwTownID & Castle::ABTOWN_BIT))
{
return RET_TOWN;
}
if (0 != (dwTownID & Castle::TOWN_BIT))
{
return RET_TOWN;
}
RespawnCharMap::iterator it = m_CharList.find(dwTownID);
if (it != m_CharList.end())
{
unsigned long dwRemainSec = 0;
RespawnCharList& tempCharList = it->second;
if (0 == tempCharList.size())
{
dwRemainSec = static_cast<unsigned long>(RESPAWN_DELAY_SEC);
}
else
{
RespawnCharList::reverse_iterator CharInfoItr = tempCharList.rbegin();
RespawnCharInfo& tempCharInfo = *CharInfoItr;
dwRemainSec = static_cast<unsigned long>(tempCharInfo.m_dwRemainSec + RESPAWN_DELAY_SEC);
}
tempCharList.push_back(RespawnCharInfo(dwCID, dwRemainSec));
return RET_TRUE;
}
++itr;
}
return RET_FALSE;
}
void CCharRespawnMgr::ProcessRespawn()
{
RespawnCharMap::iterator itr = m_CharList.begin();
while (itr != m_CharList.end())
{
if (0 != (itr->first & Castle::TOWN_BIT))
{
// 마을은 그냥 넘어간다.
++itr;
continue;
}
RespawnCharList& tempCharList = itr->second;
if (tempCharList.size() > 0)
{
RespawnCharList::iterator CharListItr = tempCharList.begin();
while (CharListItr != tempCharList.end())
{
RespawnCharInfo& tempCharInfo = *CharListItr;
if (0 < tempCharInfo.m_dwRemainSec)
{
--tempCharInfo.m_dwRemainSec;
// 2000초(약 30분). 이 숫자를 넘으면 로그를 찍는다
if (2000 < tempCharInfo.m_dwRemainSec)
{
ERRLOG2(g_Log, "CID:0x%08x/RemainSec:%u/리스폰 시간 이상",
tempCharInfo.m_dwCID, tempCharInfo.m_dwRemainSec);
}
}
else if (0 == tempCharInfo.m_dwRemainSec)
{
// 리스폰 시키고, 하나를 빼낸다.
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(tempCharInfo.m_dwCID);
if (lpCharacter)
{
if(lpCharacter->GetRespawnTownID() == 0)
{
CharListItr = tempCharList.erase(CharListItr);
continue;
}
RespawnProtoTypeMap::iterator ProtoTypeItr = m_ProtoTypeList.find(itr->first);
if (ProtoTypeItr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = ProtoTypeItr->second;
size_t iIndex = Math::Random::ComplexRandom(static_cast<int>(tempProtoType.m_RespawnPosList.size()));
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
lpCharacter->Respawn(CCharRespawnMgr::RST_FORTRESS, resapwnPos);
if (0 != lpCharacter->GetPID())
{
// 파티원 리스폰을 알린다.
GameClientSendPacket::SendCharDeadToParty(lpCharacter, 0, PktDeadInfo::RESPAWN);
}
}
else
{
Position resapwnPos = GetDefaultRespawnPos(lpCharacter->GetNation());
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
lpCharacter->Respawn(CCharRespawnMgr::RST_FORTRESS, resapwnPos);
if (0 != lpCharacter->GetPID())
{
// 파티원 리스폰을 알린다.
GameClientSendPacket::SendCharDeadToParty(lpCharacter, 0, PktDeadInfo::RESPAWN);
}
}
CharListItr = tempCharList.erase(CharListItr);
continue;
}
}
++CharListItr;
}
}
++itr;
}
}
const Position CCharRespawnMgr::GetDefaultRespawnPos(unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
while (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
int iRespawnPosCount = static_cast<int>( tempProtoType.m_RespawnPosList.size() );
int iIndex = Math::Random::SimpleRandom(GetTickCount(), iRespawnPosCount);
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
return resapwnPos;
}
}
++itr;
}
ERRLOG2(g_Log, "존의 기본 리스폰 위치를 찾을수 없습니다. Zone : %d, Nation : %d",
CServerSetup::GetInstance().GetServerZone(), cNation);
return Position(0, 0, 0);
}
const Position CCharRespawnMgr::GetTownRespawnPos(unsigned char cZone, unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_AllZoneTownList.begin();
while (itr != m_AllZoneTownList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (tempProtoType.m_cZone == cZone)
{
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
int iRespawnPosCount = static_cast<int>( tempProtoType.m_RespawnPosList.size() );
int iIndex = Math::Random::SimpleRandom(GetTickCount(), iRespawnPosCount);
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
return resapwnPos;
}
}
++itr;
}
ERRLOG2(g_Log, "존의 기본 리스폰 위치를 찾을수 없습니다. Zone : %d, Nation : %d", cZone, cNation);
return Position(0, 0, 0);
}
const Position CCharRespawnMgr::GetRespawnPos(unsigned long dwTownID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
size_t iIndex = Math::Random::ComplexRandom(static_cast<int>(tempProtoType.m_RespawnPosList.size()));
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
return resapwnPos;
}
ERRLOG2(g_Log, "존의 해당 리스폰 위치를 찾을수 없습니다. Zone : %d, dwTownID : %d",
CServerSetup::GetInstance().GetServerZone(), dwTownID);
return Position(0, 0, 0);
}
bool CCharRespawnMgr::SendRespawnInfo(unsigned long dwCID)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID : 0x%08x 리스폰 하려는 캐릭터가 존재하지 않습니다.", dwCID);
return false;
}
const unsigned short BufferSize = sizeof(PktRsInfoAck) + sizeof(RespawnArea) * PktRsInfoAck::MAX_RESPAWN_POINT;
char szBuffer[BufferSize];
unsigned short wBufferSize = 0;
unsigned char cRespawnAreaNum = 0; // 리스폰 포인트 수
unsigned char cNation = lpCharacter->GetNation();
RespawnArea* lpRespawnArea = reinterpret_cast<RespawnArea*>(szBuffer + sizeof(PktRsInfoAck));
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(lpCharacter->GetGID());
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
while (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (tempProtoType.m_dwTownID & Castle::ABTOWN_BIT))
{
if( lpCharacter->GetAbilityValue(Skill::Type::AB_RESPAWN_EX))
{
// 자신의 국가가 속한 마을이라면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
// 자신의 국가가 속한 마을이라면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
else if (lpGuild && Guild::JOIN_WAIT != lpGuild->GetTitle(dwCID) && 0 != (tempProtoType.m_dwTownID & Castle::CAMP_BIT))
{
// 진지라면 자기 길드의 곳이라면 패킷에 추가한다.
if (lpGuild->GetGID() == tempProtoType.m_dwGID)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::CASTLE_BIT))
{
// 자신과 같은 국적 수성/공성 리스폰 포인트라면 패킷에 추가한다.
if (cNation == tempProtoType.m_cNation)
{
// 공성측 리스폰 포인트는 공성 시간에만 활성화 된다.
bool bAddRespawnPoint = true;
if (0 != (tempProtoType.m_dwTownID & Castle::SIEGE_BIT))
{
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
bAddRespawnPoint = false;
}
}
if (bAddRespawnPoint)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::STATUE_BIT))
{
// 다크 카나번 석상이라면 자신의 국가와 일치(종족과 일치)하면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
++itr;
}
// edith 2009.07.21 워프나 포탈 사용시 현재 좌표갱신하기 (불법이동 방지 검출 피하기 위해서)
if(lpCharacter)
lpCharacter->SetCurrentPos(lpRespawnArea->m_RespawnPos.fPointX, lpRespawnArea->m_RespawnPos.fPointY, lpRespawnArea->m_RespawnPos.fPointZ);
PktRsInfoAck* lpRsInfoAck = reinterpret_cast<PktRsInfoAck*>(szBuffer);
lpRsInfoAck->m_dwCharID = dwCID;
lpRsInfoAck->m_cRsAreaNum = cRespawnAreaNum;
lpRsInfoAck->m_wSize = wBufferSize;
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
return SendStream.WrapCompress(szBuffer, sizeof(PktRsInfoAck) + wBufferSize, CmdCharRespawnInfo, 0, 0);
}
return false;
}
bool CCharRespawnMgr::SendRespawnAreaInfo(unsigned long dwCID, unsigned long dwTownID, bool bCount)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID : 0x%08x 리스폰 하려는 캐릭터가 존재하지 않습니다.", dwCID);
return false;
}
RespawnProtoTypeMap::iterator RespawnItr = m_ProtoTypeList.find(dwTownID);
if (RespawnItr == m_ProtoTypeList.end())
{
ERRLOG1(g_Log, "TownID : 0x%08x 리스폰 지점이 존재하지 않습니다.", dwTownID);
return false;
}
RespawnCharMap::iterator itr = m_CharList.find(dwTownID);
if (itr != m_CharList.end())
{
RespawnCharList& tempCharList = itr->second;
PktRsAreaInfoAck pktRsAreaInfoAck;
pktRsAreaInfoAck.m_dwCharID = dwCID;
pktRsAreaInfoAck.m_dwTownID = dwTownID;
pktRsAreaInfoAck.m_bCount = bCount;
pktRsAreaInfoAck.m_nWaitOrder = static_cast<unsigned short>(tempCharList.size());
unsigned long dwSec = RESPAWN_DELAY_SEC;
if (0 != tempCharList.size())
{
RespawnCharList::reverse_iterator CharInfoItr = tempCharList.rbegin();
RespawnCharInfo& tempCharInfo = *CharInfoItr;
dwSec += tempCharInfo.m_dwRemainSec;
}
// 시간 계산
pktRsAreaInfoAck.m_cRemainHour = static_cast<unsigned char>(dwSec / SEC_PER_HOUR);
pktRsAreaInfoAck.m_cRemainMin = static_cast<unsigned char>((dwSec / SEC_PER_MIN) / MIN_PER_HOUR);
pktRsAreaInfoAck.m_cRemainSec = static_cast<unsigned char>((dwSec % SEC_PER_HOUR) % SEC_PER_MIN);
char* szPacket = reinterpret_cast<char *>(&pktRsAreaInfoAck);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktRsAreaInfoAck), CmdCharRespawnAreaInfo, 0, 0))
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
return lpDispatch->GetSendStream().PutBuffer(szPacket, sizeof(PktRsAreaInfoAck), CmdCharRespawnAreaInfo);
}
}
}
ERRLOG1(g_Log, "dwTownID : 0x%08x 해당 마을(성, 길드 요새, 다크 카나번 석상)이 리스폰 포인트로 존재하지 않습니다.", dwTownID);
return false;
}

View File

@@ -0,0 +1,110 @@
#ifndef _CHARACTER_RESPAWN_MANAGER_H_
#define _CHARACTER_RESPAWN_MANAGER_H_
#pragma once
#include <Creature/CreatureStructure.h>
class CCharRespawnMgr
{
public:
enum Const
{
RESPAWN_DELAY_SEC = 5,
SEC_PER_HOUR = 3600,
SEC_PER_MIN = 60,
MIN_PER_HOUR = 60
};
enum eReturn
{
RET_FALSE = 0,
RET_TRUE = 1,
RET_TOWN = 2
};
enum RespawnType
{
RST_TOWN = 0, // 죽거나 포탈 이용으로 마을에서 부활 (일반적인 리스폰)
RST_FORTRESS = 1, // 죽거나 포탈 이용으로 요새나 성 및 다크 카나번 석상에서 부활
RST_RESURRECTION = 2, // 레저렉션으로 부활
RST_BATTLE = 3 // 배틀 그라운드나 배틀 로한에서 리스폰 시
};
struct RespawnProtoType
{
unsigned long m_dwTownID; // 마을, 길드 요새, 성 ID, 다크 카나번 석상 ID(0x800x0000)
unsigned long m_dwGID; // 길드 요새
unsigned char m_cNation; // 국적 (휴먼, 아칸, 제3세력)
unsigned char m_cZone; // 존
std::vector<Position> m_RespawnPosList; // 리스폰 위치
};
struct RespawnCharInfo
{
unsigned long m_dwCID;
unsigned long m_dwRemainSec;
RespawnCharInfo(unsigned long dwCID, unsigned long dwSec)
: m_dwCID(dwCID), m_dwRemainSec(dwSec) { }
};
typedef std::list<RespawnCharInfo> RespawnCharList;
typedef std::map<unsigned long, RespawnProtoType> RespawnProtoTypeMap;
typedef std::map<unsigned long, RespawnCharList> RespawnCharMap;
~CCharRespawnMgr();
static CCharRespawnMgr& GetInstance();
// ----------------------------------------------------------------------------
// 리스폰 스크립트 (RespawnScript.txt)
// 스크립트의 0x40001000 은 Castle::CASTLE_BIT | (성의 NameID << Castle::CASTLE_NAME_BIT_SHIFT) 입니다.
// 이후에 성 정보를 DB중계 서버로부터 받아와서 Map 정보에 제대로된 CastleID 가 key 로 들어갑니다.
bool LoadRespawnFromFile(const char* szFileName = 0);
// 요새 리스폰 포인트 사용
bool AddCampRespawnPoint(unsigned long dwCampID, unsigned long dwGID, const Position& RespawnPos);
bool DeleteCampRespawnPoint(unsigned long dwCampID);
// 다크 카나번 석상 리스폰 포인트 사용
// TownID 로 0x80010000 처럼 Castle::STATUE_BIT | (석상 Index) << 16) 입니다.
bool AddRealmStatueRespawnPoint(unsigned long dwIndex, unsigned char cNation, const Position& RespawnPos);
bool DeleteRealmStatueRespawnPoint(unsigned long dwIndex);
// 성 리스폰 포인트 사용
// 수성측 성 리스폰 포인트 키 : CASTLE_BIT | (NameID << Castle::CASTLE_NAME_BIT_SHIFT)
// 공성측 임시 리스폰 포인트 키 : CASTLE_BIT | SIEGE_BIT | (NameID << Castle::CASTLE_NAME_BIT_SHIFT) | ID(갯수별)
bool SetCastleRespawnPointNation(unsigned long dwCastleNameID, unsigned char cNation);
eReturn AddCharacter(unsigned long dwTownID, unsigned long dwCID);
// void DelCharacter(unsigned long dwTownID, unsigned long dwCID);
void ProcessRespawn();
const Position GetDefaultRespawnPos(unsigned char cNation);
const Position GetRespawnPos(unsigned long dwTownID);
const Position GetTownRespawnPos(unsigned char cZone, unsigned char cNation);
// ----------------------------------------------------------------------------
// Send 함수
bool SendRespawnInfo(unsigned long dwCID);
bool SendRespawnAreaInfo(unsigned long dwCID, unsigned long dwTownID, bool bCount=false);
private:
CCharRespawnMgr();
RespawnProtoTypeMap m_ProtoTypeList; // 현재 존의 리스폰 포인트 정보
RespawnProtoTypeMap m_AllZoneTownList; // 전 존의 마을 리스폰 포인트 정보
RespawnCharMap m_CharList;
static const char* ms_szRespawnScriptFileName;
};
#endif

View File

@@ -0,0 +1,485 @@
#include "stdafx.h"
#include <Item/Container/ItemContainer.h>
#include <Item/Container/ContainerConstant.h>
#include <Item/Container/ExchangeContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Log/GameLog.h>
#include <Log/CharacterLog.h>
#include <Log/LogStruct.h>
#include <Utility/Setup/ServerSetup.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap//VirtualArea/VirtualArea.h>
#include <Map/FieldMap//VirtualArea/VirtualAreaMgr.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Skill/SkillMgr.h>
#include <Skill/SkillTable.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Creature/CreatureManager.h>
#include <Creature/NPC/NPC.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/Siege/CampShop.h>
#include <Community/Guild/GuildMgr.h>
#include <Community/Guild/Guild.h>
#include "Character.h"
CCharacter::CCharacter(unsigned long dwCID, unsigned long dwSessionID)
: CAggresiveCreature(dwCID),
m_dwUID(0), m_dwSessionID(dwSessionID),
m_nLogoutCount(LOGOUT_COUNT), m_nDBUpdateCount(DBUPDATE_COUNT), m_cConsumeMPCount(0),
m_lpGameClientDispatch(NULL), m_lpSummonee(NULL),
m_cOperationFlags(0), m_cHandPos(0), m_cRidePos(0), m_friendList(dwCID, &CXRefFriends::GetInstance()), m_banList(dwCID, &CXRefBans::GetInstance()),
m_dwLastUpdateExTime(0), m_cMoveUpdateExCount(0), m_dwRideArmsCID(0), m_dwRespawnSpeed(RESPAWN_PULSE),
m_dwProtectGateCID(0), m_dwLastShoutTime(0),
m_dwLastSendPartyAttackInfoTime(0), m_bChatBan(false),
m_cAccountNation(0), // WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
m_cNameChangeCount(0),
m_cGuildWarFlag(Creature::WAR_OFF),
m_cRealmWarFlag(Creature::WAR_OFF),
m_dwExchangeID(0),
m_cGMModelFlag(0),
m_cRealmPoint(0),
m_dwPlayTime(0),
m_lPremiumTime(0),
m_cGuildSafe(-1),
m_iAbilityPoint(0),
m_iUseAbilityPoint(0),
m_iAdminAbilityPoint(0),
m_bRealmWarBuffer(FALSE),
m_bDead(FALSE),
m_eLastDeadType(DEAD_BY_NONE),
m_dwRespawnTownID(0)
{
m_StartTime = CTime::GetCurrentTime();
m_StartPremiumTime = CTime::GetCurrentTime();
memset(&m_FightInfo, 0, sizeof(CharacterFightInfo));
memset(&m_DBData, 0, sizeof(m_DBData));
memset(&m_CharacterStatus, 0, sizeof(m_CharacterStatus));
memset(m_szAccountName, 0, sizeof(char) * CHAR_INFOST::MAX_ACCOUNT_LEN);
memset(m_AbilityValue, 0, sizeof(m_AbilityValue));
std::fill_n(m_wHistoryQuest, int(Quest::MAX_HISTORY_QUEST), 0);
std::fill_n(m_cUsingMastery, int(MAX_USING_MASTERY), char(Skill::NONE_MASTERY));
std::fill_n(m_bPadding, int(PAD_BYTE), 0x65);
}
CCharacter::~CCharacter()
{
for(int nCount = 0; nCount < PAD_BYTE; ++nCount)
{
if (0x65 != (unsigned char)m_bPadding[nCount])
{
SERLOG1(g_Log, "CID:0x%08x 캐릭터의 패딩 바이트가 바뀌었습니다. "
"버퍼 오버플로우가 일어났을 수 있습니다.", m_dwCID);
break;
}
}
std::fill_n(m_bPadding, int(PAD_BYTE), 0x67);
SetDispatcher(NULL);
m_SpellMgr.GetAffectedInfo().ClearAll();
}
bool CCharacter::Initialize(CGameClientDispatch* lpGameClientDispatch)
{
SetDispatcher(lpGameClientDispatch);
if (!m_Inventory.Initialize(m_dwCID,
ContainerConstant::INVENTORY_WIDTH,
ContainerConstant::INVENTORY_HEIGHT,
ContainerConstant::MAX_INVENTORY_TAB))
{
return false;
}
if (!m_Equipments.Initialize(this,
Item::EquipmentPos::MAX_EQUPMENT_POS))
{
return false;
}
if (!m_ExtraSpace.Initialize(m_dwCID,
Item::ExtraSpacePos::MAX_EXTRA_SPACE_NUM))
{
return false;
}
if (!m_Exchange.Initialize(this,
ContainerConstant::EXCHANGE_WIDTH,
ContainerConstant::EXCHANGE_HEIGHT))
{
return false;
}
if (!m_Deposit.Initialize(this,
ContainerConstant::DEPOSIT_WIDTH,
ContainerConstant::DEPOSIT_HEIGHT,
ContainerConstant::MAX_DEPOSIT_TAB))
{
return false;
}
if (!m_Stall.Initialize(m_dwCID,
ContainerConstant::STALL_WIDTH,
ContainerConstant::STALL_HEIGHT))
{
return false;
}
if (!m_TempInven.Initialize(this, Item::MAX_TEMP_INVEN_ITEM_NUM))
{
return false;
}
return true;
}
void CCharacter::SetDispatcher(CGameClientDispatch* lpGameClientDispatch)
{
DETLOG5(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p에서 -> 0x%p로 바꿉니다.",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch, lpGameClientDispatch);
if (NULL != m_lpGameClientDispatch)
{
m_lpGameClientDispatch->SetCharacter(NULL);
if (false == IsOperationFlagSet(CHAR_ZONE_MOVED))
{
m_lpGameClientDispatch->Disconnect();
}
}
m_lpGameClientDispatch = lpGameClientDispatch;
if (NULL != m_lpGameClientDispatch)
{
m_bLogout = false;
m_dwUID = m_lpGameClientDispatch->GetUID();
m_lpGameClientDispatch->SetCharacter(this);
}
else
{
if (false == m_bLogout)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(m_Stall.GetOtherOwner());
// 노점상 마무리
if (0 != strcmp(m_Stall.GetStallName(), ""))
{
m_Stall.Close();
m_Stall.SendCharStallOpen("");
}
if (0 != m_Stall.GetOtherOwner())
{
if (Creature::CT_PC == Creature::GetCreatureType(m_Stall.GetOtherOwner()))
{
if (NULL != lpCharacter)
{
lpCharacter->GetStall().Leave(this);
lpCharacter->GetStall().SendCharStallEnter(m_dwCID, 0);
}
}
else
{
CCampShop* lpShop =
reinterpret_cast<CCampShop*>(CCreatureManager::GetInstance().GetSiegeObject(m_Stall.GetOtherOwner()));
if (NULL != lpShop)
{
lpShop->GetContainer().Leave(this);
lpShop->GetContainer().SendCharStallEnter(m_dwCID, 0);
}
}
}
m_bLogout = true;
CCreatureManager::GetInstance().EnqueueLogout(this);
}
}
}
bool CCharacter::GetCharacterView(CHAR_VIEW& charView)
{
charView.CID = m_dwCID;
memcpy(charView.Name, m_DBData.m_Info.Name, CHAR_INFOST::MAX_NAME_LEN); // 캐릭터 이름
charView.Sex = m_DBData.m_Info.Sex; // 캐릭터 성
charView.Hair = m_DBData.m_Info.Hair; // 캐릭터 머리 모양
charView.Face = m_DBData.m_Info.Face; // 캐릭터 얼굴 모양
charView.Race = m_DBData.m_Info.Race; // 캐릭터 종족
charView.Class = m_DBData.m_Info.Class; // 캐릭터 클래스
charView.Fame = m_DBData.m_Info.Fame; // 명성
charView.Mileage = m_DBData.m_Info.Mileage; // 마일리지
charView.GID = m_DBData.m_Info.GID; // 캐릭터 길드
charView.PID = m_DBData.m_Info.PID; // 캐릭터 파티
charView.Level = m_DBData.m_Info.Level; // 캐릭터 레벨
m_Equipments.GetEquipmentView(charView.Equip); // 장비(겉보기)
return true;
}
bool CCharacter::BindPositionToNPC(const unsigned long dwNPCID)
{
/*
GAMELOG::ERRType eReturnCode = 0;
CNPC* pNPC = CCreatureManager::GetInstance().GetNPC(dwNPCID);
if (NULL != pNPC&& true == pNPC->IsBindable() &&
pNPC->GetCurrentPos().GetDistance(m_CurrentPos) < CNPC::MAX_CONTACT_RANGE)
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
if (pNPC->GetZone() == nZone)
{
m_DBData.m_Pos.SavePoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.SavePoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.SavePoint.fPointZ = m_CurrentPos.m_fPointZ;
}
}
else
{
eReturnCode = 1;
}
// BindPosition로그를 남긴다.
GAMELOG::LogCharBindPos(*this, dwNPCID, eReturnCode);
return (0 == eReturnCode);
*/
return true;
}
bool CCharacter::ControlOption(const RejectOption Reject, bool bLogin)
{
m_RejectOption = Reject;
// TODO : 키 세팅 저장
return true;
}
unsigned char CCharacter::GetNation(void) const
{
CGuild* lpGuild = CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
if (Guild::JOIN_WAIT != lpGuild->GetTitle(m_dwCID))
{
return lpGuild->GetNation();
}
}
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
return m_cAccountNation;
/*
switch (m_DBData.m_Info.Race)
{
case CClass::HUMAN: return Creature::KARTERANT;
case CClass::AKHAN: return Creature::MERKADIA;
}
ERRLOG2(g_Log, "CID:0x%08x 캐릭터의 종족 설정이 이상하여 제3국가로 사용합니다. 종족:%d",
m_dwCID, m_DBData.m_Info.Race);
return Creature::ALMIGHTY_PIRATE;
*/
}
void CCharacter::SetPID(unsigned long dwPID)
{
m_DBData.m_Info.PID = dwPID;
}
void CCharacter::SetGID(unsigned long dwGID)
{
m_DBData.m_Info.GID = dwGID;
}
ChState CCharacter::GetState(void)
{
ChState state;
state.m_wIP = m_DBData.m_Info.IP;
state.m_wSTR = m_DBData.m_Info.STR;
state.m_wDEX = m_DBData.m_Info.DEX;
state.m_wCON = m_DBData.m_Info.CON;
state.m_wINT = m_DBData.m_Info.INT;
state.m_wWIS = m_DBData.m_Info.WIS;
return state;
}
void CCharacter::SetFame(unsigned long dwFame)
{
char cOldEliteBonus = GetEliteBonus();
m_DBData.m_Info.Fame = dwFame;
char cNewEliteBonus = GetEliteBonus();
if (cNewEliteBonus != cOldEliteBonus)
{
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharEliteBonus(
m_lpGameClientDispatch->GetSendStream(), cNewEliteBonus);
}
}
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, m_DBData.m_Info.Fame, Guild::TYPE_FAME);
}
// 퀘스트 트리거 체크
CheckTrigger(Quest::TRIGGER_FAME, dwFame, GetCurrentPos(), 1);
}
bool CCharacter::SetDuelOpponent(CCharacter* lpCharacter)
{
const POS DuelPos1 = { 3099, 1137, 3215 }; // 듀얼 스타트 위치
const POS DuelPos2 = { 3156, 1137, 3209 };
if (CServerSetup::GetInstance().GetDuelModeCheck())
{
CCell* lpDuelCell = NULL;
unsigned long Cell_ID = 0;
if (NULL == lpCharacter)
{
// 듀열 셀
Cell_ID = m_FightInfo.m_dwCellID;
lpDuelCell = CDuelCellManager::GetInstance().GetCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 듀얼 셀 얻기에 실패했습니다.", GetCID());
return false;
}
// 듀얼 셀에서 제거
lpDuelCell->DeleteCreature(m_dwCID);
if (0 == lpDuelCell->GetCharacterNum())
{
// 셀 제거
CDuelCellManager::GetInstance().DestroyCell(Cell_ID);
}
// 정보 설정
m_FightInfo.m_pDuelOpponent = lpCharacter;
m_FightInfo.m_dwCellID = 0;
// 이전 셀로 복귀
m_CellPos.MoveTo(m_CurrentPos);
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 현재 셀 없음.", GetCID());
return false;
}
m_CellPos.m_lpCell->SetCreature(m_dwCID, this);
GameClientSendPacket::SendCharBindPosition(*this, 0, 1,
m_DBData.m_Pos.LastPoint, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS DuelPos = {0,};
Cell_ID = lpCharacter->m_FightInfo.m_dwCellID;
if (0 == Cell_ID)
{
Cell_ID = GetCID();
lpDuelCell = CDuelCellManager::GetInstance().CreateCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG0(g_Log, "듀얼 셀 생성에 실패했습니다.");
return false;
}
DuelPos = DuelPos1;
}
else
{
lpDuelCell = CDuelCellManager::GetInstance().GetCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG0(g_Log, "듀얼 셀 생성에 실패했습니다.");
return false;
}
DuelPos = DuelPos2;
}
// 이전 셀에서 제거
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 현재 셀 없음.", GetCID());
return false;
}
m_CellPos.m_lpCell->DeleteCreature(GetCID());
m_CellPos.m_lpCell = NULL;
// 듀얼 셀로 추가
lpDuelCell->SetCreature(m_dwCID, this);
GameClientSendPacket::SendCharBindPosition(*this, 0, 1, DuelPos, 100, 0);
// 정보 설정
m_FightInfo.m_pDuelOpponent = lpCharacter;
m_FightInfo.m_dwCellID = Cell_ID;
}
}
else
{
m_FightInfo.m_pDuelOpponent = lpCharacter;
}
return true;
}
void CCharacter::SetGuildWarFlag(unsigned char cFlag)
{
if (cFlag >= Creature::WAR_OFF && cFlag <= Creature::WAR_INSTANCE)
{
m_cGuildWarFlag = cFlag;
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, m_cGuildWarFlag, Guild::TYPE_WARFLAG);
}
}
}

View File

@@ -0,0 +1,818 @@
#ifndef _CCHARACTER_H_
#define _CCHARACTER_H_
#include <Item/Container/ItemContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Item/Container/ExchangeContainer.h>
#include <Item/Container/DepositContainer.h>
#include <Item/Container/StallContainer.h>
#include <Item/Container/TempInvenContainer.h>
#include <Item/Container/InventoryContainer.h>
#include <Network/Packet/PacketStruct/CharLoginOutPacketStruct.h>
#include <Network/Packet/PacketStruct/CharItemPacketStruct.h>
#include <Network/Packet/PacketStruct/CharCommunityPacketStruct.h>
#include <Network/Packet/PacketStruct/CharStatusPacketStruct.h>
#include <Network/Packet/PacketStruct/CharConfigPacketStruct.h>
#include <Creature/AggresiveCreature.h>
#include <Creature/CreatureManager.h>
#include <Community/FriendList.h>
#include <Community/BanList.h>
#include <Quest/QuestStruct.h>
#include <Utility/Setup/ServerSetup.h>
#include "CharacterClass.h"
#include <Network/Broadcast/SerializeCharacterData.h>
#include <atltime.h>
#include <GameTime/GameTimeMgr.h>
#include <GameTime/GameTimeConstants.h>
// 전방 참조
class CChatPacket;
class CGameClientDispatch;
struct GiveItemInfo;
namespace Broadcast2nd
{
class CSerializeCharacterData;
};
namespace Item
{
class CItem;
class CEquipment;
class CUseItem;
struct ItemPos;
};
namespace BattleInclination
{
void SetCharData(CCreature& creature, BattleInclination::CharData& charData, bool bSetRideArmsInfo = false);
void SetRelationData(CCreature& ownerCreature, CCreature& targetCreature, BattleInclination::RelationData& relationData);
};
namespace RealmSkill
{
void RealmInchantAdd(CCharacter* lpCharacter);
void RealmInchantRemove(CCharacter* lpCharacter);
};
class CCharacter : public CAggresiveCreature
{
public:
// DB중계에서 테이블로 읽어서 TempInven으로 주어진 아이템의 CreationID를 저장한다.
typedef std::vector<unsigned long> GivenItemList;
enum Const
{
LOGOUT_PULSE = 5,
LOGOUT_COUNT = 5,
DBUPDATE_PULSE = 10,
DBUPDATE_COUNT = 180,
BATTLE_GROUND_PULSE = 600,
RESPAWN_PULSE = 100, // 10 초
MELEE_ATTACK_RANGE = 10, // 캐릭터의 근접 공격 제한 거리
RANGE_ATTACK_RANGE = 50 // 캐릭터의 원거리 공격 제한 거리
};
enum OperationFlag
{
CHAR_INFO_LOADED = ( 1 << 0),
CHAR_ZONE_MOVED = ( 1 << 1),
MOVEZONE_PROCESSED = ( 1 << 2),
SERVERZONE_PROCESSED = ( 1 << 3)
};
enum DURABILITY_DECRESE_PERCENTAGE
{
// edith 아이템의 내구도 랜덤값
WEAPON_DURABILITY_DECRESE_PERCENTAGE = 100, // 200
ARMOUR_DURABILITY_DECRESE_PERCENTAGE = 25, // 50
SHIELD_DURABILITY_DECRESE_PERCENTAGE = 10, // 20
STARTAXDURABILITY = 10, // 최대 내구도 감소폭 시작값
DOWNMAXDURABILITY = 10 // 최대 내구도 감소폭
};
enum DURABILITY_DECRESE_PERSENT
{
DURABILITY_DECREASE_PERSENT_BY_MONSTER = 20,
DURABILITY_DECREASE_PERSENT_BY_SUICIDE = 3
};
enum LAST_DEAD_TYPE
{
DEAD_BY_NONE,
DEAD_BY_SUICIDE,
DEAD_BY_MONSTER,
DEAD_BY_CHARACTER
};
enum RESPAWN_HP_MP_RECOVERY_PERCENTAGE
{
TOWN_RECOVERY_PERCENTAGE = 25,
FORTRESS_RECOVERY_PERCENTAGE = 50,
RESURRECTION_RECOVERY_PERCENTAGE = 75
};
bool Initialize(CGameClientDispatch* lpGameClientDispatch); // 초기화
// --------------------------------------------------------------------------------------------
// 캐릭터 로그인시 처리될 부분들 --------------------------------------------------------------
void PrepareLogin(void);
bool Login(void); // 캐릭터 정보가 캐릭터 테이블에 로드됨(로그인함)
bool IsLogined(void) { return IsOperationFlagSet(CHAR_INFO_LOADED); }
// --------------------------------------------------------------------------------------------
// 캐릭터 로그아웃시 처리될 부분들 ------------------------------------------------------------
inline void ResetLogoutCount(void) { m_nLogoutCount = LOGOUT_COUNT; }
inline bool StillAlive(void) { return --m_nLogoutCount >= 0; }
inline void SetDBUpdateCount(LONG nDBUpdateCount) { m_nDBUpdateCount = nDBUpdateCount; }
// 캐릭터를 맵에서 로그아웃시키고, 데이터를 DB로 보낸다.
bool Logout(DBUpdateData::UpdateType eUpdateType = DBUpdateData::LOGOUT);
// --------------------------------------------------------------------------------------------
// 캐릭터 데이터 업데이트 - CharacterDataUpdate.cpp -------------------------------------------
bool GetCharacterInfo(char* pBuffer, int* nBufferSize_InOut, unsigned short* lpUpdateLen);
bool SetCharacterInfo(char* pBuffer, unsigned short usUpdateLen[DBUpdateData::MAX_UPDATE_DB]);
bool DBUpdate(DBUpdateData::UpdateType eUpdateType);
bool DBUpdateForce(DBUpdateData::UpdateType eUpdateType)
{
m_nDBUpdateCount = -1;
return DBUpdate(DBUpdateData::UPDATE);
}
bool MoveZoneProcess(unsigned long dwServerID);
bool ItemDump(char* pBuffer, int* nBufferSize_InOut) const;
// --------------------------------------------------------------------------------------------
// 아이템, 돈 이동 및 사용 관련 메소드 - CharacterItem.cpp ------------------------------------
inline unsigned long GetGold(void) const { return m_DBData.m_Info.Gold; }
unsigned long GetGold(unsigned char cPos);
bool AddGold(unsigned long dwGold, bool bNotice);
bool DeductGold(unsigned long dwGold, bool bNotice);
bool MoveGold(unsigned long dwGold, unsigned char cSrcPos,
unsigned char cDstPos, unsigned short& usError); // 돈 옮기기
bool DeductMileage(unsigned long dwMileage, bool bNotice);
bool MoveItem(const TakeType takeType, bool bChk = true); // 아이템 이동 및 스택
bool SwapItem(const TakeType SrcType, const TakeType DstType); // 아이템 위치 스왑
Item::CItem* SplitItem(const TakeType takeType); // 아이템 개수 나누기
bool Pickup(Item::CItem* lpItem, Item::ItemPos dstPos); // 아이템 집기
Item::CItem* Drop(Item::ItemPos SrcPos, unsigned char cNum); // 아이템 버리기
// 아이템을 인벤토리에 넣어준다. (자리가 없으면 임시 인벤토리에 넣는다.)
bool GiveItem(Item::CItem* lpItem);
bool TestItem(Item::CItem* lpItem); // 아이템을 넣을 수 있는지 체크한다.
bool UseLottery(unsigned short usItemID);
bool UseCashItem(unsigned long dwSender, unsigned long dwReceiver, Item::ItemPos itemPos, unsigned short wCashType, unsigned short& wError);
bool UseStartKit(unsigned short wObjectType, unsigned short& wError);
bool CheckItemRuneSkill(Item::ItemPos SrcPos, Item::ItemPos DstPos, bool bChk); // 아이템 이동시 룬 속성 체크
const Broadcast2nd::CNetworkEquipGrade GetNetworkEquipGrade(void); // 이펙트를 위한 장비 그레이드 정보
// --------------------------------------------------------------------------------------------
// 캐릭터 인벤토리 관련(아이템 Get, Set, Reset) - CharacterInventoryItem.cpp ------------------
public:
Item::CItem* GetItem(Item::ItemPos SrcPos);
bool SetItem(Item::ItemPos SrcPos, Item::CItem* lpItemBase);
bool RemoveItem(Item::ItemPos SrcPos);
// --------------------------------------------------------------------------------------------
// 아이템 팔고 사고 수리하는 메서드 - CharacterTradeItem.cpp ----------------------------------
unsigned long RepairItem(const unsigned long dwNPCID, Item::ItemPos itemPos, unsigned short& wError);
unsigned long RepairAllItem(const unsigned long dwNPCID, unsigned short& wError);
Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError);
// --------------------------------------------------------------------------------------------
// 스테이터스 관련 메소드 - CharacterStatus.cpp -----------------------------------------------
// 능력치 및 스테이터스 데이터를 DBData에서 읽어서 재계산. HP, MP가 Full이 된다.
bool CalculateStatusData(bool bFullHPandMP);
bool CalculateAbility(const SKILL& skill); // 어빌리티의 정보를 갱신한다.
bool ChangeClass(unsigned char cClassType); // CClass 참조
bool IncrementExp(unsigned long dwExp); // 레벨업 상황이 되면 레벨업한다.
bool GetHuntingExp(CAggresiveCreature* lpDeadCreature, unsigned long dwExp, unsigned char cMemberNum);
unsigned short AddState(unsigned char Type_In, ChState& ChState_Out);
bool StateRedistribution(ChState& State); // 스탯을 해당 클래스의 처음 상태로 돌리는 함수
bool StatusRetrain(ChState& State, Item::ItemPos InvenPos); // 스탯을 일정량 돌려받는 처리를 하는 함수 (망각의 돌 사용)
bool ChangeWeaponAndShield(unsigned char cSelect); // 무기 바꾸기
bool ChangeRide(unsigned char cSelect); // 말 타기 내리기
bool CalculateEquipDurability(unsigned short wAttackType);
unsigned char GetCurrentDurability(unsigned char cEquipmentIndex);
bool CalculateAllEquipDurability(unsigned char cDecreasePersent);
// HP와 MP를 Regen한다. 추가 값이 0인 경우는 기본 값으로 Regen한다.
virtual bool RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue);
// Admin 명령으로 레벨을 다운 시킬때 사용하는 함수 (레벨 1의 초기 클래스로 만들어 버린다.)
bool InitLevel1Char(unsigned char cClassType);
bool CheckRenameWarrant(Item::ItemPos InvenPos, bool bItemAccept);
// --------------------------------------------------------------------------------------------
// 전투 관련 메소드 - CharacterFight.cpp ------------------------------------------------------
virtual const int CalculateFixLevelGap(CAggresiveCreature *pDefender);
void Casting(AtType attackType, AtNode& attackNode);
bool AttackCID(AtType attackType, AtNode& attackNode, unsigned short& wError);
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenderMPHeal);
bool AttackUsingBow(unsigned short wType);
bool UseAmmo(Item::CUseItem* pUseItem);
virtual bool Dead(CAggresiveCreature* pOffencer);
bool Respawn(unsigned char cType = 0, const Position& Pos = Position::Position());
bool AutoRespawn(void); // 배틀 그라운드에서 초렙 존의 위치로 자동 리스폰
bool IsPeaceMode(void);
EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* lpResult = NULL);
void SendAttackedToParty(unsigned long dwAttackerID, AtType atType,
unsigned short wDamage, unsigned char cDefenserJudge, unsigned short wMPHeal);
char GetEliteBonus(void);
char GetCaste(void);
void CalculateFame(CCharacter* lpWinnerCharacter);
// --------------------------------------------------------------------------------------------
// 스킬 관련 메소드 - CharacterSkill.cpp ------------------------------------------------------
unsigned short ReadSkill(SKILLSLOT SkillSlot, unsigned short wSkillID, unsigned short wSkillLockCount);
bool RedistributionSkill(void);
bool CalculateMaxSkillSlot(void);
bool SkillCreate(Item::CUseItem* pUseItem);
bool SkillErase(unsigned char Index_In, Item::ItemPos InvenPos);
bool SkillLock(unsigned char Index_In);
bool SkillUnLock(unsigned char Index_In, Item::ItemPos* InvenPos, bool b_SkillFifthUnlock = false);
bool SkillFifthLock(unsigned short wSKilID);
bool SkillFifthUnlock(unsigned short wSKilID);
virtual bool HasSkill(unsigned short usSkillType, unsigned char cLockCount, unsigned char cLevel);
short GetSkillLockCount(unsigned short usSkillType);
short GetSkillLevel(unsigned short usSkillType);
short GetSkillSlotIndex(unsigned short usSkillType);
bool AddWorldWeaponEnchant(CAggresiveCreature* lpWeapon, unsigned char cNation);
bool ClearWorldWeaponEnchant(void);
void CheckSkillVaild(void);
bool AbilityCreate(Item::CUseItem* pUseItem);
bool AbilityErase(unsigned char Index_In, Item::ItemPos InvenPos);
// --------------------------------------------------------------------------------------------
// 퀵슬롯 관련 메소드 - CharacterQuickSlot.cpp ------------------------------------------------
void UpdateQuickSlotSkill(SKILLSLOT Slot);
bool MoveQuickSlot(const TakeType takeType, const unsigned short usSkillID, unsigned char cLockCount, unsigned char cSkillLevel);
// --------------------------------------------------------------------------------------------
// 캐릭터 컨트롤 관련 메소드 - CharacterControl.cpp -------------------------------------------
bool DropItem(unsigned short usProtoTypeID, unsigned char cNum);
bool MovePos(Position Pos, char cZone, const bool bSitDown);
bool MoveZone(POS NewPos, char cZone, char Channel);
bool Kill(CAggresiveCreature* lpAttacker);
bool NotifyInfo(unsigned long dwAdminCID);
bool DuelInit(unsigned char cCmd);
// 길드전, 국가전에 해당하는 존으로 이동
void MoveToGuildWarZone();
void MoveToRealmWarZone();
// --------------------------------------------------------------------------------------------
// 캐릭터 퀘스트 관련 메소드 - CharacterQuest.cpp ---------------------------------------------
bool CheckQuest(Quest::QuestNode* lpQuestNode, unsigned short& wError);
bool HasQuest(unsigned short wQuestID);
bool GiveQuest(Quest::QuestNode* lpQuestNode);
bool StartPhase(unsigned short wQuestID, unsigned char cPhase);
bool CancelQuest(unsigned short wQuestID, bool bFinished = false);
void CheckTrigger(unsigned char cTriggerKind, unsigned long dwReferenceID, Position Pos, short wCount);
unsigned short OperateTrigger(unsigned short wQuestID, unsigned char cPhase, unsigned char cTrigger,
unsigned char cCount, Position Pos, unsigned char cType = 0);
void PendingQuest(Quest::TriggerMsg* pTriggerMsg);
//----------------------------------------------------------------------------------------------
// 친구 정보 업데이트
void FriendInfoUpdate(unsigned long dwUID, unsigned long dwCID, unsigned long dwGID, unsigned short wClass, char cLevel, unsigned long dwServerID);
private:
Quest::ErrorCode ExecuteEvent(Quest::ExecutingQuest executingQuest, const Quest::TriggerNode* triggerNode, Position Pos);
void EventEnd(unsigned short wQuestID, bool bSave);
void EventAward(unsigned long dwExp, unsigned long dwGold, unsigned long dwFame, unsigned long dwMileage);
public:
// 스킬 관련 //
void SkillClear();
bool ClearGarbage(vector<Item::ItemGarbage>& vecItemGarbage, unsigned char cCmd);
bool HasExecutingQuest(unsigned short wQuestID);
bool HasHistoryQuest(unsigned short wQuestID);
bool InsertHistoryQuest(unsigned short wQuestID);
bool DeleteHistoryQuest(unsigned short wQuestID);
virtual void CalculateStatusByQuest(void);
// --------------------------------------------------------------------------------------------
// 세션 관련 메소드 -------------------------------------------------------------------------
CGameClientDispatch* GetDispatcher(void) { return m_lpGameClientDispatch; }
void SetDispatcher(CGameClientDispatch* lpGameClientDispatch);
// --------------------------------------------------------------------------------------------
// 기타 메소드 -------------------------------------------------------------------------------
bool GetCharacterView(CHAR_VIEW& charView);
// TODO : AggresiveCreature로 올리고 GetName으로 수정합시다. (Creature로 올려도 좋고...)
const char* GetCharacterName() const { return m_DBData.m_Info.Name; }
void SetCharacterName(const char* szName)
{
strncpy(m_DBData.m_Info.Name, szName, CHAR_INFOST::MAX_NAME_LEN);
m_DBData.m_Info.Name[CHAR_INFOST::MAX_NAME_LEN - 1] = 0;
}
const char* GetAccountName() const { return m_szAccountName; }
void SetAccountName(const char* szAccountName)
{
strncpy(m_szAccountName, szAccountName, CHAR_INFOST::MAX_ACCOUNT_LEN);
m_szAccountName[CHAR_INFOST::MAX_ACCOUNT_LEN - 1] = 0;
}
bool BindPositionToNPC(const unsigned long dwNPCID); // 리스폰 위치를 NPC위치에 바인딩
bool ControlOption(const RejectOption Reject, bool bLogin); // 각종 옵션 조정
void UpgradeRespawnSpeedByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep);
void DegradeRespawnSpeedByEmblem();
virtual unsigned long GetUID(void) const { return m_dwUID; }
char GetConsumeMPCount(void) { return m_cConsumeMPCount; }
unsigned short GetClass(void) { return m_DBData.m_Info.Class; }
unsigned char GetLevel(void) { return m_DBData.m_Info.Level; }
CClass::RaceType GetRace(void) { return static_cast<CClass::RaceType>(m_DBData.m_Info.Race); }
unsigned char GetNation(void) const;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
void SetNation(unsigned char cNation) { m_cAccountNation = cNation; }
unsigned long GetPID(void) const { return m_DBData.m_Info.PID; }
void SetPID(unsigned long dwPID);
unsigned long GetGID(void) const { return m_DBData.m_Info.GID; }
void SetGID(unsigned long dwGID);
unsigned char GetHair(void) { return m_DBData.m_Info.Hair; }
unsigned char GetFace(void) { return m_DBData.m_Info.Face; }
unsigned char GetSex(void) { return m_DBData.m_Info.Sex; } // 0 : 남자, 1 : 여자
unsigned char GetHand(void) { return m_cHandPos; }
unsigned char GetRide(void) { return m_cRidePos; }
ChState GetState(void);
const SKILL& GetSkill(void) { return m_DBData.m_Skill; }
const unsigned long GetFame(void) { return m_DBData.m_Info.Fame; }
void SetFame(unsigned long dwFame);
const unsigned long GetMileage(void) { return m_DBData.m_Info.Mileage; }
void SetMileage(unsigned long dwMileage) { m_DBData.m_Info.Mileage = dwMileage; }
bool IsAdmin(void) const { return (0 < m_DBData.m_cAdminLevel); }
unsigned char GetAdminLevel() const { return m_DBData.m_cAdminLevel; }
void SetAdminLevel(unsigned char cAdminLevel) { m_DBData.m_cAdminLevel = cAdminLevel; }
bool IsRideArms(void) const { return (0 != m_dwRideArmsCID); }
unsigned long GetRideArmsCID(void) const { return m_dwRideArmsCID; }
void Ride(unsigned long dwArmsID) { m_dwRideArmsCID = dwArmsID; }
void GetOff(void) { m_dwRideArmsCID = 0; }
void SetProtectGateCID(unsigned long dwCID) { m_dwProtectGateCID = dwCID; }
const RejectOption& GetRejectOption(void) { return m_RejectOption; }
CAggresiveCreature* GetDuelOpponent(void) { return m_FightInfo.m_pDuelOpponent; }
bool SetDuelOpponent(CCharacter* pCharacter);
CMonster* GetSummonee(void) { return m_lpSummonee; }
void SetSummonee(CMonster* lpSummonee) { m_lpSummonee = lpSummonee; }
bool CanShout(void) const;
void Shouted(void);
unsigned long GetLastTime() const { return m_dwLastTime; }
CFriendList& GetFriendList(void) { return m_friendList; }
CBanList& GetBanList(void) { return m_banList; }
inline void SaveToDBData();
inline Item::CItemContainer* GetItemContainer(unsigned char cPos);
Item::CInventoryContainer& GetInventory(void) { return m_Inventory; }
Item::CEquipmentsContainer& GetEquipments(void) { return m_Equipments; }
Item::CListContainer& GetExtra(void) { return m_ExtraSpace; }
Item::CExchangeContainer& GetExchange(void) { return m_Exchange; }
Item::CDepositContainer& GetDeposit(void) { return m_Deposit; }
Item::CCharacterShopContainer& GetStall(void) { return m_Stall; }
Item::CTempInvenContainer& GetTempInven(void) { return m_TempInven; }
// DB중계서버의 요청으로 아이템을 임시인벤에 넣어준다.
bool GiveItemByDBAgentRequest(GiveItemInfo& giveItemInfo);
inline unsigned short* GetHistoryQuest(void) { return m_wHistoryQuest; }
inline Quest::ExecutingQuest* GetExecutingQuest(void) { return m_ExecutingQuest; }
const FightStatus& GetEtcTypeStatus(Creature::StatusType eType) { return m_EtcTypeStatus[eType]; }
Broadcast2nd::CSerializeCharacterData& GetSerializeData(void) { return m_SerializeCharacterData; }
friend class Skill::CFunctions;
void SetOperationFlag(OperationFlag eOperationFlag) { m_cOperationFlags |= eOperationFlag; }
void ResetOperationFlag(OperationFlag eOperationFlag) { m_cOperationFlags &= ~eOperationFlag; }
bool IsOperationFlagSet(OperationFlag eOperationFlag) { return 0 != (m_cOperationFlags & eOperationFlag); }
unsigned long GetLastMoveUpdateTime(void) const { return m_dwLastUpdateExTime; }
void SetLastMoveUpdateTime(unsigned long dwLastUpdateTime) { m_dwLastUpdateExTime = dwLastUpdateTime; }
unsigned char GetMoveUpdateCount(void) const { return m_cMoveUpdateExCount; }
void IncrementMoveUpdateCount(void) { ++m_cMoveUpdateExCount; }
bool IsChatBan(void) const { return m_bChatBan; }
void SetChatBan(bool bBan) { m_bChatBan = bBan; }
void SetGMModelFlag(unsigned char cGMModelFlag) { m_cGMModelFlag = cGMModelFlag; }
unsigned char GetGMModelFlag() { return m_cGMModelFlag; }
unsigned char GetNameChangeCount() const { return m_cNameChangeCount; }
void SetNameChangeCount(unsigned char cNameChangeCount) { m_cNameChangeCount = cNameChangeCount; }
unsigned char GetRealmWarFlag() const { return m_cRealmWarFlag; }
unsigned char GetGuildWarFlag() const { return m_cGuildWarFlag; }
void SetRealmWarFlag(unsigned char cFlag) { m_cRealmWarFlag = cFlag; }
void SetGuildWarFlag(unsigned char cFlag);
unsigned char GetTacticsFlag() const { return m_cTactics; }
void SetTacticsFlag(unsigned char cTactics) { m_cTactics = cTactics; }
// 국가 전쟁 공헌훈장 포인트.
virtual unsigned char GetRealmPoint() { return m_cRealmPoint; }
void SetRealmPoint(unsigned char cRealmPoint) { m_cRealmPoint = cRealmPoint; }
int GetMagicChancePoint()
{
// 매찬 포인트 계상하는곳
int iMCPoint = 0;
// edtih 2009.06.20 룬에의한 매직찬스값 추가
iMCPoint = (int)m_EquipStatus.m_fLuckResistRate;
// if(GetRealmPoint() > 0)
// iMCPoint = GetRealmPoint()*5; // 랠름포인트는 1개당 5퍼센트씩
// 매찬에 스틸핸드가 미치는 영향.
if(GetEnchantInfo().GetFlag(Skill::SpellID::StealHand))
{
// 3-0부터 1래벨당 1퍼센트씩 매찬기능 추가
int iLevel = GetEnchantLevel(Skill::SpellID::StealHand)-12;
if(iLevel > 0)
iMCPoint += iLevel;
}
return iMCPoint;
}
void SetPlayTime(unsigned int dwPlayTime)
{
m_StartTime = CTime::GetCurrentTime();
m_dwPlayTime = dwPlayTime;
}
unsigned int GetPlayTime() { return m_dwPlayTime; }
void SetPremiumService(long lPremiumTime, int iPremiumType)
{
// 프리미엄 타임 남은 시간
m_StartPremiumTime = CTime::GetCurrentTime();
m_lPremiumTime = lPremiumTime;
m_iPremiumType = iPremiumType;
}
long GetPremiumTime() { return m_lPremiumTime; }
int GetPremiumType() { return m_iPremiumType; }
float GetPremiumPt()
{
if(m_iPremiumType == 1)
return 0.5f;
else if(m_iPremiumType == 2)
return 0.3f;
return 0.0f;
}
bool CheckPremiumTime()
{
if(m_lPremiumTime == 0)
return false;
CTime nowTime = CTime::GetCurrentTime();
CTimeSpan tm(m_StartPremiumTime.GetDay(), m_StartPremiumTime.GetHour(), m_StartPremiumTime.GetMinute(), m_StartPremiumTime.GetSecond());
CTime tmPrev = nowTime-tm;
int iTime = (tmPrev.GetHour()*60)+tmPrev.GetMinute();
//
iTime = m_lPremiumTime-iTime;
if(iTime <= 0)
{
m_iPremiumType = 0;
m_lPremiumTime = 0;
return false;
}
return true;
}
float GetAwardPer()
{
float fEventAdd = 0.0f;
if (CGameTimeMgr::GetInstance().GetCurrentEventTime() == GameTime::EVENT_All20)
fEventAdd = 0.2f;
else if (CGameTimeMgr::GetInstance().GetCurrentEventTime() == GameTime::EVENT_All50)
fEventAdd = 0.5f;
if(GameRYL::CHINA != CServerSetup::GetInstance().GetNationType())
return 1.0f+fEventAdd;
if(m_dwPlayTime == 0)
return 1.0f+fEventAdd;
CTime nowTime = CTime::GetCurrentTime();
CTimeSpan tm(m_StartTime.GetDay(), m_StartTime.GetHour(), m_StartTime.GetMinute(), m_StartTime.GetSecond());
CTime tmPrev = nowTime-tm;
int iTime = (tmPrev.GetHour()*60)+tmPrev.GetMinute();
iTime += m_dwPlayTime;
/*
// 테스트를 위해 플레이타음을 약간 바꿔준다.
if(iTime <= 2)
{
return 1.0f;
}
else if(2 < iTime && iTime <= 4)
{
return 0.5f;
}
*/
if(iTime <= 180)
{
return 1.0f;
}
else if(180 < iTime && iTime <= 300)
{
return 0.5f;
}
return 0.0f;
}
// 교환 관련 메소드.
unsigned long GetExchangeID() const { return m_dwExchangeID; }
void SetExchangeID(unsigned long dwExchangeID) { m_dwExchangeID = dwExchangeID; }
SPELL GetSpell() { return m_DBData.m_Spell; }
void SetSpell(SPELL spell) { m_DBData.m_Spell = spell; }
void SaveSpell(BOOL bDead = FALSE);
// 길드 관련 메소드
void SetGuildSafe(char cGuildSafe) { m_cGuildSafe = cGuildSafe; }
char GetGuildSafe() { return m_cGuildSafe; }
void SetDead(BOOL bDead) { m_bDead = bDead; }
BOOL IsDead() { return m_bDead; }
void SetRealmWarBuffer(BOOL bEnable) { m_bRealmWarBuffer = bEnable; }
BOOL IsRealmWarBuffer() { return m_bRealmWarBuffer; }
// 리스폰 타운 정보 아이디
void SetRespawnTownID(unsigned long dwTownID) { m_dwRespawnTownID = dwTownID; }
unsigned long GetRespawnTownID() const { return m_dwRespawnTownID; }
void SetAdminAbilityPoint(int point) { m_iAdminAbilityPoint = point; }
int GetAbilityPoint() { return m_iAbilityPoint; }
int GetUseAbilityPoint() { return m_iUseAbilityPoint; }
void UpdateUseAbilityPoint();
unsigned short GetAbilityValue(int nAbilityType)
{
if(nAbilityType >= Skill::Type::MAX_ABILITY_TYPE )
return 0;
return m_AbilityValue[nAbilityType];
}
CCharacter(unsigned long dwCID, unsigned long dwSessionID);
virtual ~CCharacter();
protected:
enum
{
MAX_USING_MASTERY = 2, // 동시에 최대로 적용 가능한 마스터리수
PAD_BYTE = 3 // byte alignment 를 맞추기 위한 배열의 크기
};
CharacterFightInfo m_FightInfo; // 듀얼 정보
// PeaceModeInfo m_PeaceMode; // 반전 모드 정보
Item::CInventoryContainer m_Inventory; // 인벤토리
Item::CEquipmentsContainer m_Equipments; // 장비
Item::CListContainer m_ExtraSpace; // 추가 공간
Item::CExchangeContainer m_Exchange; // 교환창
Item::CDepositContainer m_Deposit; // 창고
Item::CCharacterShopContainer m_Stall; // 노점상
Item::CTempInvenContainer m_TempInven; // 임시 인벤토리
GivenItemList m_GivenItemList; // DB중계로부터 받은 아이템들의 CreationID
CFriendList m_friendList; // 친구 리스트
CBanList m_banList; // 거부 리스트
unsigned short m_wHistoryQuest[Quest::MAX_HISTORY_QUEST]; // 수행 완료한 퀘스트
Quest::ExecutingQuest m_ExecutingQuest[Quest::MAX_EXECUTING_QUEST]; // 수행중인 퀘스트
CharacterDBData m_DBData; // DB에서 읽어 오는 데이터
RejectOption m_RejectOption; // 각종 거부 옵션
FightStatus m_EtcTypeStatus[Creature::MAX_STATUS_TYPE]; // 특수한 경우에 사용되는 스탯들
unsigned long m_dwUID; // 유저 ID
unsigned long m_dwSessionID; // 세션 ID - DB에 업데이트할 때 쓰인다.
unsigned long m_dwLastShoutTime; // 마지막으로 외친 시간
unsigned long m_dwLastSendPartyAttackInfoTime; // 마지막으로 파티원에게 공격당했다는 정보를 보낸 시간
int m_nLogoutCount; // 로그아웃시까지 남은 카운트 수 저장 (0 이하가 될 수 있으므로 반드시 signed로!)
int m_nDBUpdateCount; // DB Update때까지 남은 카운트 수 저장 (0 이하가 될 수 있으므로 반드시 signed로!)
unsigned long m_dwExchangeID; // 교환할 유저 ID.
unsigned char m_cGMModelFlag; // GM 모델로 변경 체크.
CGameClientDispatch* m_lpGameClientDispatch;
CMonster* m_lpSummonee;
unsigned long m_dwLastUpdateExTime; // 마지막 이동 업데이트 시간
unsigned long m_dwRideArmsCID; // 타고 있는 공성관련 병기의 CID
unsigned long m_dwProtectGateCID; // 성문막기를 하고 있는 성문의 CID
unsigned long m_dwRespawnSpeed; // 리스폰 속도 (pulse 사용)
LAST_DEAD_TYPE m_eLastDeadType; // 마지막에 어떻게 죽었는지 체크
Broadcast2nd::CSerializeCharacterData m_SerializeCharacterData; // 캐릭터 데이터 보관 및 변경된 데이터 보관
char m_szAccountName[CHAR_INFOST::MAX_ACCOUNT_LEN];
char m_cUsingMastery[MAX_USING_MASTERY]; // 적용중인 마스터리 종류
char m_cConsumeMPCount; // MP 소모까지 남은 카운트 수 저장
char m_cOperationFlags; // 각종 처리가 완료되었는지를 검사하는 루틴
char m_cHandPos; // HandPos
char m_cRidePos; // Ride Pos
char m_cMoveUpdateExCount; // 이동 업데이트 Count
bool m_bChatBan; // 채팅 금지
unsigned char m_cNameChangeCount; // 이름 바꿀 수 있는 회수
unsigned char m_cGuildWarFlag; // 길드 전쟁 참여 플래그
unsigned char m_cRealmWarFlag; // 국가 전챙 참여 플래그
unsigned char m_cTactics; // 용병 플레그.
char m_cGuildSafe; // edith 2008.03.15 길드 입출금 관련 플래스
unsigned char m_cRealmPoint; // 국가 전쟁 공헌훈장 포인트.
unsigned int m_dwPlayTime; // 중국판을 위한 게임 연속 플레이시간.
CTime m_StartTime;
long m_lPremiumTime; // 프리미엄 서비스
int m_iPremiumType;
CTime m_StartPremiumTime;
BOOL m_bDead;
BOOL m_bRealmWarBuffer;
unsigned long m_dwRespawnTownID;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
unsigned char m_cAccountNation; // 계정 국적
char m_bPadding[PAD_BYTE]; // 4Byte Alignment를 맞추기 위한 도구
// edith 2009.10.22 어빌리티 작업중..
int m_iAbilityPoint;
int m_iUseAbilityPoint;
int m_iAdminAbilityPoint; // 어드민이 임시적으로 주는 포인트 (기본 0, 값은 저장되지 않음)
// edith 2009.11.10 어빌리티 능력치 제어
unsigned short m_AbilityValue[Skill::Type::MAX_ABILITY_TYPE];
};
inline void CCharacter::SaveToDBData()
{
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp;
m_DBData.m_Info.Level = m_CreatureStatus.m_nLevel;
m_DBData.m_Info.HP = m_CreatureStatus.m_nNowHP;
m_DBData.m_Info.MP = m_CreatureStatus.m_nNowMP;
m_DBData.m_Pos.LastPoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.LastPoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.LastPoint.fPointZ = m_CurrentPos.m_fPointZ;
}
inline Item::CItemContainer* CCharacter::GetItemContainer(unsigned char cPos)
{
switch(cPos)
{
case TakeType::TS_EQUIP: return &m_Equipments;
case TakeType::TS_INVEN: return &m_Inventory;
case TakeType::TS_TEMP: return &m_ExtraSpace;
case TakeType::TS_EXTRA: return &m_ExtraSpace;
case TakeType::TS_EXCHANGE: return &m_Exchange;
case TakeType::TS_DEPOSIT: return &m_Deposit;
case TakeType::TS_STALL: return &m_Stall;
case TakeType::TS_TEMPINVEN: return &m_TempInven;
}
return NULL;
}
#endif

View File

@@ -0,0 +1,670 @@
#include "stdafx.h"
#include <Creature/Character/CharacterStructure.h>
#include <Network/Packet/PacketStruct/CharStatusPacketStruct.h>
#include "CharacterClass.h"
#include "GMMemory.h"
// 홀수 레벨일 때 증가량을 기준으로 작성.
CClass ClassTable[CClass::MAX_CLASS] =
{
CClass(),
CClass(CClass::Fighter, CClass::Fighter, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::STR, 1, CClass::CON, 1, false),
CClass(CClass::Rogue, CClass::Rogue, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::DEX, 1, CClass::STR, 1, false),
CClass(CClass::Mage, CClass::Mage, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::INT, 1, CClass::DEX, 1, false),
CClass(CClass::Acolyte, CClass::Acolyte, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::WIS, 1, CClass::CON, 1, false),
CClass(CClass::Defender, CClass::Fighter, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::STR, 2, CClass::CON, 1, true),
CClass(CClass::Warrior, CClass::Fighter, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::STR, 2, CClass::CON, 1, false),
CClass(CClass::Assassin, CClass::Rogue, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::DEX, 2, CClass::STR, 1, false),
CClass(CClass::Archer, CClass::Rogue, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::DEX, 2, CClass::STR, 1, true),
CClass(CClass::Sorcerer, CClass::Mage, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::INT, 2, CClass::DEX, 1, false),
CClass(CClass::Enchanter, CClass::Mage, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::INT, 2, CClass::DEX, 1, true),
CClass(CClass::Priest, CClass::Acolyte, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::WIS, 2, CClass::CON, 1, true),
CClass(CClass::Cleric, CClass::Acolyte, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::WIS, 2, CClass::CON, 1, false),
CClass(),
CClass(),
CClass(),
CClass(),
CClass(CClass::Combatant, CClass::Combatant, CClass::DEFAULT_CLASS, CClass::AKHAN, CClass::STR, 1, CClass::NONE_STAT, 0, false),
CClass(CClass::Officiator, CClass::Officiator, CClass::DEFAULT_CLASS, CClass::AKHAN, CClass::DEX, 1, CClass::NONE_STAT, 0, false),
CClass(CClass::Templar, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::STR, 2, CClass::CON, 1, true),
CClass(CClass::Attacker, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::STR, 2, CClass::CON, 1, false),
CClass(CClass::Gunner, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::DEX, 2, CClass::STR, 1, true),
CClass(CClass::RuneOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::INT, 2, CClass::DEX, 1, false),
CClass(CClass::LifeOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::WIS, 2, CClass::DEX, 1, false),
CClass(CClass::ShadowOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::DEX, 2, CClass::STR, 1, false)
};
CClass::CClass(const JobType eJobType, const JobType ePrevJobType, const JobLevel eJobLevel, const RaceType eRace,
const CClass::StatusType eType1, const unsigned char cIncrement1,
const CClass::StatusType eType2, const unsigned char cIncrement2,
const bool bLevelSwap)
: m_eJobType(eJobType), m_ePrevJobType(ePrevJobType), m_eJobLevel(eJobLevel),
m_eRace(eRace), m_bLevelSwap(bLevelSwap)
{
m_eIncrementType[0] = eType1; m_eIncrementType[1] = eType2;
m_cIncrementValue[0] = cIncrement1; m_cIncrementValue[1] = cIncrement2;
}
CClass::CClass()
: m_eJobType(NONE_JOB), m_ePrevJobType(NONE_JOB),
m_eJobLevel(DEFAULT_CLASS), m_eRace(MAX_RACE), m_bLevelSwap(false)
{
m_eIncrementType[0] = NONE_STAT; m_eIncrementType[1] = NONE_STAT;
m_cIncrementValue[0] = 0; m_cIncrementValue[1] = 0;
}
void CClass::LevelUp(CHAR_INFOST* InfoSt)
{
unsigned char cIndex1 = m_bLevelSwap ? ((1 == InfoSt->Level % 2) ? 0 : 1) : 0;
unsigned char cIndex2 = m_bLevelSwap ? ((1 == InfoSt->Level % 2) ? 1 : 0) : 1;
IncrementByType(InfoSt, 0, cIndex1);
IncrementByType(InfoSt, 1, cIndex2);
if (AKHAN == GetRace(static_cast<unsigned char>(InfoSt->Class)))
{
if (JOB_CHANGE_1ST == GetJobLevel(static_cast<unsigned char>(InfoSt->Class)))
{
InfoSt->IP += 2;
}
}
else
{
InfoSt->IP += 2;
}
}
unsigned char CClass::GetRequiredIP(unsigned short usClass, unsigned char cType)
{
if (usClass >= MAX_CLASS || cType >= MAX_TYPE) { return 0xFF; }
if (ClassTable[usClass].m_eJobLevel == JOB_CHANGE_1ST)
{
return (ClassTable[usClass].m_eIncrementType[0] == cType ||
ClassTable[usClass].m_eIncrementType[1] == cType) ? 2 : 1;
}
for (int nClassIndex = 0; nClassIndex < MAX_CLASS; nClassIndex++)
{
if (ClassTable[nClassIndex].m_eJobType == usClass)
{
if (ClassTable[nClassIndex].m_eIncrementType[0] == cType ||
ClassTable[nClassIndex].m_eIncrementType[1] == cType)
{
return 2;
}
}
}
return 1;
}
bool CClass::JobChange(CharacterDBData* DBData, unsigned char cClassType)
{
if (DBData->m_Info.Level < 10) { return false; }
switch (m_eJobLevel)
{
case DEFAULT_CLASS: return DowngradeClass(DBData, cClassType);
case JOB_CHANGE_1ST: return UpgradeClass(DBData, cClassType);
}
return false;
}
// edith 2008.02.18 아칸전직시 스탯을 주는걸 인간과 동일, 아칸도 랩업당 +2의 스킬포인트주어지고 전직시에 주지 않는다.
bool CClass::UpgradeClass(CharacterDBData* DBData, unsigned char cClassType)
{
if (DBData->m_Info.Class != ClassTable[cClassType].m_ePrevJobType ||
ClassTable[DBData->m_Info.Class].m_eJobLevel != DEFAULT_CLASS)
{
return false;
}
// 환원되는 IP 계산을 위한 임시 변수들
unsigned short StatusA = 0;
unsigned short StatusB = 0;
unsigned short StatusC = 0;
switch (cClassType)
{
// 인간
case Defender:
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Defender;
break;
case Warrior:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.STR - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Warrior;
break;
case Assassin:
DBData->m_Info.DEX += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.DEX - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.STR - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Assassin;
break;
case Archer:
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Archer;
break;
case Sorcerer:
DBData->m_Info.INT += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.INT - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Sorcerer;
break;
case Enchanter:
DBData->m_Info.INT += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.INT - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Enchanter;
break;
case Priest:
DBData->m_Info.WIS += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.WIS - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.STR + DBData->m_Info.DEX + DBData->m_Info.INT - 60;
DBData->m_Info.Class = Priest;
break;
case Cleric:
DBData->m_Info.WIS += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.WIS - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.DEX + DBData->m_Info.INT - 60;
DBData->m_Info.Class = Cleric;
break;
// 아칸
case Templar:
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Templar;
break;
case Attacker:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
DBData->m_Info.CON += DBData->m_Info.Level - 1;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Attacker;
break;
case Gunner:
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f);
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Gunner;
break;
case RuneOff:
DBData->m_Info.INT += (DBData->m_Info.Level - 1) * 2;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.INT - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = RuneOff;
break;
case LifeOff:
DBData->m_Info.WIS += (DBData->m_Info.Level - 1) * 2;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.WIS - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.INT - 60;
DBData->m_Info.Class = LifeOff;
break;
case ShadowOff:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
DBData->m_Info.DEX += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.DEX - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.STR - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
/*
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f);
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
*/
DBData->m_Info.Class = ShadowOff;
break;
default:
return false;
}
DBData->m_Info.IP += 10 + (DBData->m_Info.Level - 1) * 2 - ((StatusA + StatusB) * 2 + StatusC) - DBData->m_Info.IP;
return true;
}
bool CClass::DowngradeClass(CharacterDBData* DBData, unsigned char cClassType)
{
// 현재는 거너만 가능하다.
if (Gunner != DBData->m_Info.Class) { return false; }
if (cClassType != ClassTable[DBData->m_Info.Class].m_ePrevJobType ||
ClassTable[DBData->m_Info.Class].m_eJobLevel != JOB_CHANGE_1ST)
{
return false;
}
// 레벨이 10% 감소한다.
DBData->m_Info.Level -= static_cast<char>(DBData->m_Info.Level * 0.1f);
DBData->m_Info.Level = max(DBData->m_Info.Level, 10);
DBData->m_Info.Exp = 0;
// 명성치가 0이 된다.
DBData->m_Info.Fame = 0;
// 스탯이 초기화된다.
switch (cClassType)
{
case Combatant:
DBData->m_Info.STR = 20 + DBData->m_Info.Level - 1;
DBData->m_Info.DEX = 20;
DBData->m_Info.CON = 20;
DBData->m_Info.INT = 20;
DBData->m_Info.WIS = 20;
DBData->m_Info.Class = Combatant;
DBData->m_Info.IP = 10;
break;
default: return false;
}
// 스킬, 퀵슬롯 초기화
DBData->m_Skill = SKILL::SKILL();
DBData->m_Quick = QUICK::QUICK();
return true;
}
bool CClass::InitializeClass(CharacterDBData* DBData, unsigned char cClassType)
{
if (ClassTable[cClassType].m_eJobLevel != DEFAULT_CLASS)
{
return false;
}
// 레벨이 10% 감소한다.
DBData->m_Info.Level = 1;
DBData->m_Info.Exp = 0;
// 명성치가 0이 된다.
DBData->m_Info.Fame = 0;
// 스탯이 초기화된다.
DBData->m_Info.STR = 20;
DBData->m_Info.DEX = 20;
DBData->m_Info.CON = 20;
DBData->m_Info.INT = 20;
DBData->m_Info.WIS = 20;
DBData->m_Info.IP = 10;
DBData->m_Info.Class = cClassType;
// 스킬, 퀵슬롯 초기화
DBData->m_Skill = SKILL::SKILL();
DBData->m_Quick = QUICK::QUICK();
return true;
}
bool CClass::CheckState(ChState& State, unsigned char cLevel)
{
// unsigned short이면 해킹으로 인해 65535가 되면 검출할수 있는 방법이 없다. int로 바꾸자.
// unsigned short wTIP = State.m_wIP;
int wTIP = State.m_wIP;
switch (m_eJobType)
{
case Fighter:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Rogue:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Mage:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Acolyte:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1)) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Defender:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Warrior:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Assassin:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Archer:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Sorcerer:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Enchanter:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Priest:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Cleric:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1) * 2) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Combatant:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > 10) { return false; }
break;
case Officiator:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > 10) { return false; }
break;
case Templar:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Attacker:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Gunner:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case RuneOff:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case LifeOff:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1) * 2) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case ShadowOff:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
/*
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
*/
break;
}
return true;
}
bool CClass::CheckMinState(ChState& State, unsigned char cLevel)
{
if (State.m_wSTR < GetMinState(STR, cLevel)) { return false; }
if (State.m_wDEX < GetMinState(DEX, cLevel)) { return false; }
if (State.m_wCON < GetMinState(CON, cLevel)) { return false; }
if (State.m_wINT < GetMinState(INT, cLevel)) { return false; }
if (State.m_wWIS < GetMinState(WIS, cLevel)) { return false; }
return true;
}
unsigned short CClass::GetMinState(StatusType eType, unsigned char cLevel)
{
// 스탯 기본값
unsigned short wResult = 20;
cLevel = cLevel - 1;
if (eType == m_eIncrementType[0] || eType == m_eIncrementType[1])
{
// 레벨에 관계없이 똑같이 성장하는 경우
if (false == m_bLevelSwap)
{
if (eType == m_eIncrementType[0])
{
wResult += cLevel * m_cIncrementValue[0];
}
if (eType == m_eIncrementType[1])
{
wResult += cLevel * m_cIncrementValue[1];
}
}
// 레벨에 따라 스탯이 번갈아가며 성장하는 경우
else
{
if (eType == m_eIncrementType[0])
{
wResult += static_cast<unsigned short>(cLevel * 1.5f);
}
if (eType == m_eIncrementType[1])
{
wResult += static_cast<unsigned short>(cLevel * 1.5f + 0.5f);
}
}
}
return wResult;
}
CClass::JobLevel CClass::GetJobLevel(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return DEFAULT_CLASS; }
return ClassTable[cClass].m_eJobLevel;
}
unsigned char CClass::GetPreviousJob(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return NONE_JOB; }
return ClassTable[cClass].m_ePrevJobType;
}
CClass::RaceType CClass::GetRace(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return MAX_RACE; }
return ClassTable[cClass].m_eRace;
}

View File

@@ -0,0 +1,120 @@
#ifndef _CHARACTER_CLASS_H_
#define _CHARACTER_CLASS_H_
#include <DB/DBDefine.h>
// 전방 참조
struct CharacterDBData;
struct ChState;
class CClass
{
public:
enum JobType
{
NONE_JOB = 0,
// 인간 기본클래스
Fighter = 1, Rogue = 2,
Mage = 3, Acolyte = 4,
// 인간 1st클래스
Defender = 5, Warrior = 6,
Assassin = 7, Archer = 8,
Sorcerer = 9, Enchanter = 10,
Priest = 11, Cleric = 12,
// 아칸 기본클래스
Combatant = 17, Officiator = 18,
// 아칸 1st클래스
Templar = 19, Attacker = 20,
Gunner = 21, RuneOff = 22,
LifeOff = 23, ShadowOff = 24,
MAX_CLASS = 25
};
enum StatusType
{
NONE_STAT = 0,
STR = 1,
DEX = 2,
CON = 3,
INT = 4,
WIS = 5,
MAX_TYPE = 6
};
enum JobLevel
{
NONE_CLASS = 0,
DEFAULT_CLASS = 1,
JOB_CHANGE_1ST = 2
};
enum RaceType
{
HUMAN = 0,
AKHAN = 1,
MAX_RACE = 2
};
CClass(const JobType eJobType, const JobType ePrevJobType, const JobLevel eJobLevel, const RaceType eRace,
const StatusType eType1, const unsigned char cIncrement1,
const StatusType eType2, const unsigned char cIncrement2,
const bool bLevelSwap);
CClass();
void LevelUp(CHAR_INFOST* InfoSt);
bool JobChange(CharacterDBData* DBData, unsigned char cClassType);
bool UpgradeClass(CharacterDBData* DBData, unsigned char cClassType);
bool DowngradeClass(CharacterDBData* DBData, unsigned char cClassType);
bool InitializeClass(CharacterDBData* DBData, unsigned char cClassType); // 레벨 1의 기본 클래스로 만든다.
bool CheckState(ChState& State, unsigned char cLevel);
bool CheckMinState(ChState& State, unsigned char cLevel);
unsigned short GetMinState(StatusType eType, unsigned char cLevel);
static JobLevel GetJobLevel(unsigned char cClass);
static unsigned char GetPreviousJob(unsigned char cClass);
static unsigned char GetRequiredIP(unsigned short usClass, unsigned char cType);
static RaceType GetRace(unsigned char cClass);
protected:
StatusType m_eIncrementType[2]; // 성장시 추가 능력치 타입
unsigned char m_cIncrementValue[2]; // 성장시 추가 능력치 값
JobType m_eJobType; // 현재 직업
JobType m_ePrevJobType; // 바로 하위 클래스의 직업
JobLevel m_eJobLevel; // 현재 직업의 레벨
RaceType m_eRace; // 소속 종족
bool m_bLevelSwap; // 레벨에 따라 스탯이 번갈아가면서 성장함 (짝수 2/1 성장이면 홀수 1/2 성장)
inline void IncrementByType(CHAR_INFOST* InfoSt,
unsigned char cTypeIndex, unsigned char cIncrementIndex);
};
extern CClass ClassTable[CClass::MAX_CLASS];
inline void CClass::IncrementByType(CHAR_INFOST* InfoSt,
unsigned char cTypeIndex, unsigned char cIncrementIndex)
{
switch(m_eIncrementType[cTypeIndex]) {
case STR: InfoSt->STR += m_cIncrementValue[cIncrementIndex]; break;
case DEX: InfoSt->DEX += m_cIncrementValue[cIncrementIndex]; break;
case CON: InfoSt->CON += m_cIncrementValue[cIncrementIndex]; break;
case INT: InfoSt->INT += m_cIncrementValue[cIncrementIndex]; break;
case WIS: InfoSt->WIS += m_cIncrementValue[cIncrementIndex]; break;
}
}
#endif

View File

@@ -0,0 +1,29 @@
#include "stdafx.h"
#include "Character.h"
#include <Creature/CreatureManager.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Packet/ChatPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <mmsystem.h>
bool CCharacter::CanShout() const
{
const long MAX_SHOUT_DELAY = 15000;
unsigned long dwCurrentTime = timeGetTime();
return (0 == m_dwLastShoutTime || MAX_SHOUT_DELAY < dwCurrentTime - m_dwLastShoutTime);
}
void CCharacter::Shouted()
{
m_dwLastShoutTime = timeGetTime();
if(0 == m_dwLastShoutTime)
{
++m_dwLastShoutTime;
}
}

View File

@@ -0,0 +1,315 @@
#include "stdafx.h"
#include "Character.h"
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/GameClient/SendCharLoginOut.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Network/Packet/ChatPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharStatusPacket.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/Cell.h>
#include <Map/FieldMap/CellManager.h>
#include <Community/Party/Party.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Log/CharacterLog.h>
#include <Utility/Math/Math.h>
#include <utility/Setup/ServerSetup.h>
bool CCharacter::DropItem(unsigned short usProtoTypeID, unsigned char cNum)
{
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(usProtoTypeID);
if (NULL != lpItem)
{
lpItem->SetNumOrDurability(min(cNum, lpItem->GetMaxNumOrDurability()));
// edith 2009.08.19 DropItem 어드민 명령어로 장비 드랍시 시즌레코드 수정.
Item::CEquipment* lpEquip = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquip && lpEquip->GetSeasonRecord() == 0)
lpEquip->SetNewEquip();
if (false == GiveItem(lpItem))
{
ERRLOG2(g_Log, "CID:%10u %d종류의 아이템을 떨구지 못했습니다.",
GetCID(), usProtoTypeID);
DELETE_ITEM(lpItem);
return false;
}
// GievItem 으로 스택된 경우
if (lpItem->IsSet(Item::DetailData::STACKABLE) && 0 == lpItem->GetNumOrDurability())
{
DELETE_ITEM(lpItem);
}
}
return true;
}
bool CCharacter::MovePos(Position Pos, char cZone, const bool bSitDown)
{
if (0 != CCellManager::GetInstance().GetCell(m_CellPos.m_wMapIndex, Pos.PositionToPOS()))
{
DETLOG5(g_Log, "CID:%10u 워프 명령을 사용하였습니다. NewPos : %.1f, %.1f, %.1f, Zone : %d",
m_dwCID, Pos.m_fPointX, Pos.m_fPointY, Pos.m_fPointZ, cZone);
// edith 2009.07.21 워프나 포탈 사용시 현재 좌표갱신하기 (불법이동 방지 검출 피하기 위해서)
m_CurrentPos.m_fPointX = Pos.m_fPointX;
m_CurrentPos.m_fPointZ = Pos.m_fPointZ;
return GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, Pos, cZone, 0);
}
return false;
}
bool CCharacter::MoveZone(POS NewPos, char cZone, char Channel)
{
if (IsOperationFlagSet(CCharacter::MOVEZONE_PROCESSED))
{
return true;
}
// BG_TODO : 배틀 그라운드 전용 서버군 작업시 추가해야 한다.
/*
if (cZone == SERVER_ID::ZONE3)
{
SYSTEMTIME systemTime;
GetSystemTime(&systemTime);
// 배틀 그라운드 휴식 시간동안에는 진입을 할수 없다.
if ((systemTime.wMinute >= CCreatureManager::STATUE_REST_TIME_1ST_START && systemTime.wMinute <= CCreatureManager::STATUE_REST_TIME_1ST_END) ||
(systemTime.wMinute >= CCreatureManager::STATUE_REST_TIME_2ND_START && systemTime.wMinute <= CCreatureManager::STATUE_REST_TIME_2ND_END))
{
return false;
}
}
*/
if (NULL != m_lpGameClientDispatch)
{
CDBRequest DBRequest(*m_lpGameClientDispatch, 600);
if (DBRequest.IsValid())
{
if (GameClientSendPacket::SendMoveZoneToDBAgent(DBRequest.GetSendStream(),
NewPos, DBRequest.GetRequestKey(), GetUID(), cZone, Channel))
{
char* szPeaceMode = (true == IsPeaceMode()) ? "평화모드" : "전쟁모드";
DETLOG5(g_Log, "UID:%d/CID:%10u(0x%p)/RequestKey:%d/DispatchPointer:0x%p 존 이동 명령을 사용하였습니다.",
m_dwUID, m_dwCID, this, DBRequest.GetRequestKey(), m_lpGameClientDispatch);
DETLOG8(g_Log, "UID:%d/CID:%10u/ 존이동 당시의 정보입니다. NewPos : (%.1f, %.1f, %.1f), Zone : %d, Channel : %d (%s)",
m_dwUID, m_dwCID, NewPos.fPointX, NewPos.fPointY, NewPos.fPointZ, cZone, Channel, szPeaceMode);
GAMELOG::LogZoneMove(*this, cZone, Channel, 0);
SetOperationFlag(CCharacter::MOVEZONE_PROCESSED);
m_lpGameClientDispatch->PushRequestKey(DBRequest.GetRequestKey());
return true;
}
DBRequest.CancelRequest();
}
}
return false;
}
// 길드전, 국가전에 해당하는 존으로 이동
void CCharacter::MoveToGuildWarZone()
{
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(SERVER_ID::CAPITAL, GetNation());
if (CServerSetup::GetInstance().GetServerZone() == SERVER_ID::CAPITAL)
{
MoveTo(targetPos, false);
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, pos, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
MoveZone(pos, SERVER_ID::CAPITAL, -1);
}
}
void CCharacter::MoveToRealmWarZone()
{
const int iLevel = GetLevel();
int iZone = SERVER_ID::STONE_WAR1;
// edith 2009.06.13 고렙쟁과 저렙쟁 구분
if(iLevel <= 60)
iZone = SERVER_ID::STONE_WAR1;
else //if(iLevel <= 80)
iZone = SERVER_ID::STONE_WAR2;
// else
// iZone = SERVER_ID::STONE_WAR3;
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(iZone, GetNation());
if (CServerSetup::GetInstance().GetServerZone() == iZone)
{
MoveTo(targetPos, false);
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, pos, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
MoveZone(pos, iZone, -1);
}
}
bool CCharacter::Kill(CAggresiveCreature* lpAttacker)
{
if (true == Dead(NULL))
{
AtType Type = {0,};
Type.m_wType = 1;
const unsigned short wDamage = m_CreatureStatus.m_nNowHP;
m_CreatureStatus.m_nNowHP = 0;
// 공격자가 NULL 이면 운영자가 다른 존에 있는 캐릭터를 운영자 명령으로 죽인 경우이다.
// 이럴때는 자기가 죽인것으로 처리한다.
if (NULL == lpAttacker) lpAttacker = this;
if (NULL != m_lpGameClientDispatch)
{
return GameClientSendPacket::SendCharAttacked(m_lpGameClientDispatch->GetSendStream(),
lpAttacker, this, Type, 0, wDamage, 0, 0, PktBase::NO_SERVER_ERR);
}
return true;
}
return false;
}
bool CCharacter::NotifyInfo(unsigned long dwAdminCID)
{
char szMessage[PktChat::PktChatMaxSize] = "";
int nLen = _snprintf(szMessage, PktChat::PktChatMaxSize,
"Name:%s, Class:%d, HP:%3d%%, MP:%3d%%, Zone:%d, X:%.1f, Y:%.1f, Z:%.1f, "
"MinD:%d, MaxD:%d, HitRate:%d, Evade:%d, Armor:%d",
GetCharacterName(), m_DBData.m_Info.Class,
m_CreatureStatus.m_nNowHP * 100 / m_CreatureStatus.m_StatusInfo.m_nMaxHP,
m_CreatureStatus.m_nNowMP * 100 / m_CreatureStatus.m_StatusInfo.m_nMaxMP,
CServerSetup::GetInstance().GetServerZone(), m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ,
m_CreatureStatus.m_StatusInfo.m_lMinDamage, m_CreatureStatus.m_StatusInfo.m_lMaxDamage,
m_CreatureStatus.m_StatusInfo.m_wHitRate,
m_CreatureStatus.m_StatusInfo.m_wEvade,
m_CreatureStatus.m_StatusInfo.m_wArmor);
if (0 < nLen)
{
szMessage[PktChat::PktChatMaxSize - 1] = 0;
// 채팅 서버로 정보를 보낸다.
GET_SINGLE_DISPATCH(lpChatDispatch, CChatDispatch,
CChatDispatch::GetDispatchTable());
if (0 != lpChatDispatch)
{
const Position& pos = GetCurrentPos();
char strAdminCID[CHAR_INFOST::MAX_NAME_LEN] = "";
strcpy(strAdminCID, "0x");
char* strHexPos = (strAdminCID + 2);
Math::Convert::Hex32ToStr(strHexPos, dwAdminCID);
CChatRequestPacket chatReqPacket(szMessage,
PktChat::NOTIFY_CHAR_INFO, 0, GetUID(), GetCID(),
static_cast<unsigned short>(pos.m_fPointX),
static_cast<unsigned short>(pos.m_fPointY),
static_cast<unsigned short>(pos.m_fPointZ),
strAdminCID, 1);
if (chatReqPacket.IsValid())
{
return lpChatDispatch->GetSendStream().PutBuffer(
chatReqPacket.GetCompressedPacket(),
chatReqPacket.GetCompressedSize(), CmdCharChat);
}
}
return true;
}
return false;
}
bool CCharacter::DuelInit(unsigned char cCmd)
{
// 듀얼중이라면 듀얼 취소
CCharacter* lpDuelOpponent = static_cast<CCharacter*>(GetDuelOpponent());
if (NULL != lpDuelOpponent)
{
CGameClientDispatch* lpOpponentDispatch = lpDuelOpponent->GetDispatcher();
if (NULL != lpOpponentDispatch)
{
GameClientSendPacket::SendCharDuelCmd(lpOpponentDispatch->GetSendStream(), m_dwCID,
lpDuelOpponent->GetCID(), cCmd, PktDuC::NO_SERVER_ERR);
}
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharDuelCmd(m_lpGameClientDispatch->GetSendStream(), m_dwCID,
lpDuelOpponent->GetCID(), cCmd, PktDuC::NO_SERVER_ERR);
}
lpDuelOpponent->SetDuelOpponent(NULL);
SetDuelOpponent(NULL);
}
// 팀배틀에서 빠짐
if (NULL != m_pParty)
{
if (NULL != reinterpret_cast<CCharacterParty* >(m_pParty)->GetHostileParty())
{
if (PktDuC::DUC_CANCEL == cCmd ||
0 == reinterpret_cast<CCharacterParty* >(m_pParty)->DropMember(this, static_cast<PktDuC::DuelCmd>(cCmd)))
{
reinterpret_cast<CCharacterParty* >(m_pParty)->GetHostileParty()->EndTeamBattle();
reinterpret_cast<CCharacterParty* >(m_pParty)->EndTeamBattle();
}
}
}
return true;
}

View File

@@ -0,0 +1,328 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include "CharacterClass.h"
#include "CharacterCreate.h"
bool CharCreate::CheckCharCreateName(const char *Name_In, bool HanCheck_In)
{
const unsigned short MIN_CHAR_NAME = 4;
const unsigned short MAX_CHAR_NAME = 20;
if(Name_In == NULL)
{
return false;
}
// 길이 제한
size_t Len = strlen(Name_In);
if(Len < MIN_CHAR_NAME || Len > MAX_CHAR_NAME)
{
return false;
}
LPBYTE CheckName = (LPBYTE)Name_In;
if(true == HanCheck_In)
{
// 제한 문자 검사
int ACount = 0;
for(unsigned short LCount = 0; LCount < Len; LCount++)
{
if((CheckName[LCount] & 0x80) == 0x80)
{
// 2Byte 문자 체크
if(CheckName[LCount + 1] == NULL)
{
return false;
}
// 허용 범위 체크 (한글)
if(CheckName[LCount] < 0xB0 || CheckName[LCount] > 0xC9)
{
return false;
}
if(CheckName[LCount + 1] < 0xA1 || CheckName[LCount + 1] > 0xFE)
{
return false;
}
// 한글 부분 불 허용
for(ACount = 0; ACount < ALLOW_HAN_NUM; ACount++)
{
if(MAKEWORD(CheckName[LCount + 1], CheckName[LCount]) == AllowHans[ACount])
{
break;
}
}
if(ACount != ALLOW_HAN_NUM)
{
return false;
}
LCount += 1;
continue;
}
else
{
// 영문자 허용
if((CheckName[LCount] >= 'A' && CheckName[LCount] <= 'Z') ||
(CheckName[LCount] >= 'a' && CheckName[LCount] <= 'z'))
{
continue;
}
// 숫자 허용
if(CheckName[LCount] >= '0' && CheckName[LCount] <= '9')
{
continue;
}
// 특수 기호 부분 허용
for(ACount = 0; ACount < ALLOW_LETTER_NUM; ACount++)
{
if(CheckName[LCount] == AllowLetters[ACount])
{
break;
}
}
if(ACount == ALLOW_LETTER_NUM)
{
return false;
}
}
}
}
else
{
if(!strcmp(Name_In, ""))
{
return false;
}
/////////////////////////////////////////////////////////////
// 영문, 숫자, 특수기호만 입력가능.
// 제한 문자 검사
int ACount = 0;
for(unsigned short LCount = 0; LCount < Len; LCount++)
{
// 영문자 허용
if((CheckName[LCount] >= 'A' && CheckName[LCount] <= 'Z') ||
(CheckName[LCount] >= 'a' && CheckName[LCount] <= 'z'))
{
continue;
}
// 숫자 허용
if(CheckName[LCount] >= '0' && CheckName[LCount] <= '9')
{
continue;
}
// 특수 기호 부분 허용
for(ACount = 0; ACount < ALLOW_LETTER_NUM; ACount++)
{
if(CheckName[LCount] == AllowLetters[ACount])
{
break;
}
}
if(ACount == ALLOW_LETTER_NUM)
{
return false;
}
}
// 특수기호 사용못함
for(int LCount = 0; LCount < DISALLOW_LETTER_NUM; LCount++)
{
if(_tcschr(Name_In, DisAllowLetters[LCount]) != NULL)
{
return false;
}
}
}
return true;
}
bool CharCreate::CheckCharCreateData(CHAR_CREATE &CharCreate_In)
{
if (CharCreate_In.Race == CClass::HUMAN)
{
if(CharCreate_In.Equip[Item::EquipmentPos::SHIRT] < 201 ||
CharCreate_In.Equip[Item::EquipmentPos::SHIRT] > 209)
{
return false;
}
if(601 != CharCreate_In.Equip[Item::EquipmentPos::BOOTS])
{
return false;
}
if(CClass::Fighter == CharCreate_In.Class)
{ // 전사
if(701 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Rogue == CharCreate_In.Class)
{ // 로그
if(1601 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Mage == CharCreate_In.Class)
{ // 메이지
if(1501 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Acolyte == CharCreate_In.Class)
{ // 어콜라이트
if(801 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
if(CharCreate_In.STR < 20 || CharCreate_In.CON < 20 ||
CharCreate_In.DEX < 20 || CharCreate_In.INT < 20 || CharCreate_In.WIS < 20)
{
return false;
}
if(CharCreate_In.STR + CharCreate_In.CON +
CharCreate_In.DEX + CharCreate_In.INT + CharCreate_In.WIS > 105)
{
return false;
}
}
else if (CharCreate_In.Race == CClass::AKHAN)
{
if(CClass::Combatant == CharCreate_In.Class)
{ // 컨배턴트
if(5401 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON])
{
return false;
}
if(5101 != CharCreate_In.Equip[Item::EquipmentPos::BODY])
{
return false;
}
if(5201 != CharCreate_In.Equip[Item::EquipmentPos::PELVIS])
{
return false;
}
}
else if(CClass::Officiator == CharCreate_In.Class)
{ // 오피세이터
if(5801 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON])
{
return false;
}
if(5110 != CharCreate_In.Equip[Item::EquipmentPos::BODY])
{
return false;
}
if(5210 != CharCreate_In.Equip[Item::EquipmentPos::PELVIS])
{
return false;
}
}
if(CharCreate_In.STR < 20 || CharCreate_In.CON < 20 ||
CharCreate_In.DEX < 20 || CharCreate_In.INT < 20 || CharCreate_In.WIS < 20)
{
return false;
}
if(CharCreate_In.STR + CharCreate_In.CON + CharCreate_In.DEX +
CharCreate_In.INT + CharCreate_In.WIS > 105)
{
return false;
}
}
else
{
return false;
}
return true;
}
unsigned long CharCreate::GetDefaultStartGold(void)
{
return START_GOLD;
}
POS CharCreate::GetDefaultCharacterPos(unsigned long dwRace, unsigned long dwRacePlayerNum)
{
if (true == CServerSetup::GetInstance().IsBattleAuthServer() ||
true == CServerSetup::GetInstance().IsBattleGameServer())
{
POS StartPos = BGServerStartPos[dwRace][Math::Random::ComplexRandom(MAX_LOBBY_RESPAWN_POS)];
StartPos.fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
StartPos.fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
return StartPos;
}
// 통합존의 생성 위치는 하나이다.
unsigned long dwIndex = 0;
if (CClass::MAX_RACE <= dwRace)
{
dwRace = 0;
}
for (; dwIndex < MAX_START_POS_NUM; ++dwIndex)
{
if (dwRacePlayerNum < StartPointVariation[dwIndex])
{
if (0 < dwIndex)
{
dwIndex = Math::Random::ComplexRandom(dwIndex + 1);
}
break;
}
}
POS startPos = StartPosNum[dwRace][dwIndex];
startPos.fPointX += static_cast<float>(Math::Random::ComplexRandom(14)) - 7.0f;
startPos.fPointZ += static_cast<float>(Math::Random::ComplexRandom(14)) - 7.0f;
return startPos;
}
inline bool operator == (const POS& lhs, const POS& rhs)
{
return (lhs.fPointX == rhs.fPointX) && (lhs.fPointY == rhs.fPointY) && (lhs.fPointZ == rhs.fPointZ);
}
inline bool operator != (const POS& lhs, const POS& rhs)
{
return !(lhs == rhs);
}

View File

@@ -0,0 +1,105 @@
#include "CharacterClass.h"
namespace CharCreate
{
enum Const
{
START_GOLD = 0,
MAX_START_POS_NUM = 6,
MAX_LOBBY_RESPAWN_POS = 2
};
// 파트 1용 리젠포인트 갔다 써라.
const POS StartPosNum[CClass::MAX_RACE][MAX_START_POS_NUM] = {
// 휴먼
{
{ 2740, 23, 606 },
{ 2738, 24, 647 },
{ 2801, 22, 644 },
{ 1442, 12, 1973 },
{ 1464, 12, 2064 },
{ 1419, 12, 2065 }
},
// 아칸
{
{ 2446, 33, 3208 },
{ 2370, 32, 3233 },
{ 2324, 32, 3244 },
{ 2059, 65, 1663 },
{ 2014, 65, 1648 },
{ 2009, 65, 1690 }
}
};
/*
const POS StartPosNum[CClass::MAX_RACE][MAX_START_POS_NUM] = {
// 휴먼
{
{ 2812, 15, 2498 },
{ 2817, 15, 2471 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 }
},
// 아칸
{
{ 2812, 15, 2498 },
{ 2834, 15, 2482 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 }
}
};
*/
const POS BGServerStartPos[CClass::MAX_RACE][MAX_LOBBY_RESPAWN_POS] = {
// Human
{
{ 2165, 1135, 1005 },
{ 1727, 1135, 1005 }
},
// Akhan
{
{ 2119, 1132, 1841 },
{ 1683, 1132, 1841 }
}
};
const unsigned long StartPointVariation[MAX_START_POS_NUM] = {
500, 1000, 1500, 2000, 2500, 0xFFFFFFFF
};
const unsigned short ALLOW_HAN_NUM = 39;
const unsigned short AllowHans[ALLOW_HAN_NUM] = {
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', ''
};
const unsigned short ALLOW_LETTER_NUM = 2;
const char AllowLetters[ALLOW_LETTER_NUM] = {
'-', '_'
};
const unsigned short DISALLOW_LETTER_NUM = 9;
const char DisAllowLetters[DISALLOW_LETTER_NUM] = {
' ', '\'', '\"', '#', '~', '!', '@', '[', ']'
};
bool CheckCharCreateName(const char *Name_In, bool HanCheck_In);
bool CheckCharCreateData(CHAR_CREATE &CharCreate_In);
unsigned long GetDefaultStartGold(void);
POS GetDefaultCharacterPos(unsigned long dwRace, unsigned long dwRacePlayerNum);
};

View File

@@ -0,0 +1,356 @@
#include "stdafx.h"
#include "Character.h"
#include <Community/Party/Party.h>
#include <Community/Guild/GuildConstants.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Utility/Math/Math.h>
#include <Log/LogCommands.h>
#include <Log/CharacterLog.h>
#include <Item/Container/ItemContainer.h>
#include <Item/ItemFactory.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharLoginOutPacket.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <RylChatServer/ChatGameServerDispatch.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
bool CCharacter::DBUpdate(DBUpdateData::UpdateType eUpdateType)
{
if(!IsOperationFlagSet(CHAR_INFO_LOADED) || (m_bLogout && DBUpdateData::LOGOUT != eUpdateType))
{
return false;
}
if(--m_nDBUpdateCount > 0)
{
return false;
}
m_nDBUpdateCount = DBUPDATE_COUNT;
int nTotalSize = sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE;
char szCharInfo[sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE];
char* lpCharacterInfo = szCharInfo + sizeof(PktDBUpdate);
PktDBUpdate* lpPktDBUpdate = reinterpret_cast<PktDBUpdate*>(szCharInfo);
memset(lpPktDBUpdate, 0, sizeof(PktDBUpdate));
unsigned short usLogError = 0;
unsigned char cLogCMD = (DBUpdateData::LOGOUT == eUpdateType) ?
GAMELOG::CMD::CHAR_LOGOUT : GAMELOG::CMD::CHAR_DBUPDATE;
unsigned char cAdmin = (true == IsAdmin()) ? 1 : 0;
if(!GetCharacterInfo(lpCharacterInfo, &nTotalSize, lpPktDBUpdate->m_usUpdate))
{
nTotalSize = 0; usLogError = 1;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : 데이터를 복사해 올 수 없습니다.", m_dwCID);
}
else
{
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 == lpDBAgentDispatch)
{
usLogError = 2;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : DBAgentDispatch 를 얻을 수 없습니다.", m_dwCID);
}
else
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 창고 데이터 업데이트(창고가 열려 있으면 업데이트한다.)
// 순서 주의! DB에 Update할 때는 창고 업데이트 후 캐릭터를 업데이트한다.
if(!m_Deposit.DBUpdate(AgentSendStream))
{
ERRLOG1(g_Log, "CID:0x%08x 창고 업데이트 실패", m_dwCID);
}
lpPktDBUpdate->m_dlItemSerial = Item::CItemFactory::GetInstance().GetItemUID();
lpPktDBUpdate->m_dwSessionID = m_dwSessionID;
lpPktDBUpdate->m_dwUserID = m_dwUID;
lpPktDBUpdate->m_dwCharID = m_dwCID;
lpPktDBUpdate->m_TypeCode = eUpdateType;
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_Address.S_un.S_addr = 0;
lpPktDBUpdate->m_cAdminLevel = 0;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
lpPktDBUpdate->m_cAccountNation = 0;
lpPktDBUpdate->m_cNameChangeCount = 0;
lpPktDBUpdate->m_cGuildWarFlag = 0;
lpPktDBUpdate->m_cRealmWarFlag = 0;
lpPktDBUpdate->m_cRealmPoint = 0;
lpPktDBUpdate->m_cTacticsFlag = 0;
if(!AgentSendStream.WrapCompress(reinterpret_cast<char*>(lpPktDBUpdate),
static_cast<unsigned short>(sizeof(PktDBUpdate) + nTotalSize), CmdDBUpdateData, 0, 0))
{
usLogError = 3;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : WrapCompress를 실패했습니다.", m_dwCID);
}
}
}
if(DBUpdateData::LOGOUT == eUpdateType || 0 != usLogError)
{
SOCKADDR_IN remoteAddr;
if (0 != m_lpGameClientDispatch)
{
remoteAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
}
else
{
memset(&remoteAddr, 0, sizeof(SOCKADDR_IN));
}
GAMELOG::LogCharLoginOut(m_dwUID, this, &remoteAddr, lpCharacterInfo, nTotalSize,
lpPktDBUpdate->m_usUpdate, cLogCMD, usLogError);
}
LOG_INOUT(
char szExp[64];
const char* szUpdateType = "Unknown";
switch (eUpdateType)
{
case DBUpdateData::LOGIN: szUpdateType = "Login"; break;
case DBUpdateData::LOGOUT: szUpdateType = "Logout"; break;
case DBUpdateData::UPDATE: szUpdateType = "Update"; break;
case DBUpdateData::ADMIN_LOGIN: szUpdateType = "AdminLogin"; break;
case DBUpdateData::ZONEMOVE: szUpdateType = "ZoneMove"; break;
}
unsigned long dwDispatchUID = (NULL != m_lpGameClientDispatch) ?
m_lpGameClientDispatch->GetUID() : 0;
Math::Convert::Hex64ToStr(szExp, m_DBData.m_Info.Exp);
DETLOG8(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchUID:%d 의 캐릭터 정보를 DBAgent에 업데이트합니다. "
" 업데이트 타입은 %s입니다. 몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%s)",
m_dwUID, m_dwCID, this, dwDispatchUID, szUpdateType, m_DBData.m_Info.Name,
m_DBData.m_Info.Level, szExp)
);
return true;
}
bool CCharacter::GetCharacterInfo(char* pBuffer, int* nBufferSize_InOut, unsigned short* lpUpdateLen)
{
unsigned long dwSize = 0;
unsigned short usTotalSize = 0;
if(!IsOperationFlagSet(CHAR_INFO_LOADED))
{
ERRLOG1(g_Log, "CID:0x%08x 캐릭터 정보가 세팅되지 않은 상태에서 캐릭터 정보를 얻으려 하였습니다.",
m_dwCID);
return false;
}
SaveToDBData();
for (int nCount = 0; nCount < DBUpdateData::MAX_UPDATE_DB; ++nCount)
{
switch (nCount)
{
case DBUpdateData::STATUS_UPDATE:
{
*reinterpret_cast<CHAR_INFOST*>(pBuffer) = m_DBData.m_Info;
lpUpdateLen[nCount] = sizeof(CHAR_INFOST);
}
break;
case DBUpdateData::POSITION_UPDATE:
*reinterpret_cast<CHAR_POS*>(pBuffer) = m_DBData.m_Pos;
lpUpdateLen[nCount] = sizeof(CHAR_POS);
break;
case DBUpdateData::SKILL_UPDATE:
*reinterpret_cast<SKILL*>(pBuffer) = m_DBData.m_Skill;
lpUpdateLen[nCount] = sizeof(SKILL);
break;
case DBUpdateData::QUICKSLOT_UPDATE:
*reinterpret_cast<QUICK*>(pBuffer) = m_DBData.m_Quick;
lpUpdateLen[nCount] = sizeof(QUICK);
break;
case DBUpdateData::SPELL_UPDATE:
*reinterpret_cast<SPELL*>(pBuffer) = m_DBData.m_Spell;
lpUpdateLen[nCount] = sizeof(SPELL);
break;
case DBUpdateData::ITEM_EQUIP_UPDATE:
dwSize = *nBufferSize_InOut; m_Equipments.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_INVEN_UPDATE:
dwSize = *nBufferSize_InOut; m_Inventory.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_EXTRA_UPDATE:
dwSize = *nBufferSize_InOut; m_ExtraSpace.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_EXCHANGE_UPDATE:
dwSize = *nBufferSize_InOut; m_Exchange.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_TEMPINVEN_UPDATE:
dwSize = *nBufferSize_InOut; m_TempInven.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
}
pBuffer += lpUpdateLen[nCount];
usTotalSize += lpUpdateLen[nCount];
*nBufferSize_InOut -= dwSize;
}
*nBufferSize_InOut = usTotalSize;
return true;
}
bool CCharacter::SetCharacterInfo(char* pBuffer, unsigned short usUpdateLen[DBUpdateData::MAX_UPDATE_DB])
{
for (int nCount = 0; nCount < DBUpdateData::MAX_UPDATE_DB; ++nCount)
{
unsigned long dwUpdateLen = usUpdateLen[nCount];
switch (nCount)
{
case DBUpdateData::STATUS_UPDATE: m_DBData.m_Info = *reinterpret_cast<CHAR_INFOST*>(pBuffer); break;
case DBUpdateData::POSITION_UPDATE: m_DBData.m_Pos = *reinterpret_cast<CHAR_POS*>(pBuffer); break;
case DBUpdateData::SKILL_UPDATE: m_DBData.m_Skill = *reinterpret_cast<SKILL*>(pBuffer); break;
case DBUpdateData::QUICKSLOT_UPDATE: m_DBData.m_Quick = *reinterpret_cast<QUICK*>(pBuffer); break;
case DBUpdateData::SPELL_UPDATE: m_DBData.m_Spell = *reinterpret_cast<SPELL*>(pBuffer); break;
case DBUpdateData::ITEM_EQUIP_UPDATE: m_Equipments.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_INVEN_UPDATE: m_Inventory.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_EXTRA_UPDATE: m_ExtraSpace.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_EXCHANGE_UPDATE: m_Exchange.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_TEMPINVEN_UPDATE: m_TempInven.SerializeIn(pBuffer, dwUpdateLen); break;
}
pBuffer += dwUpdateLen;
}
m_CreatureStatus.Init(m_DBData.m_Info);
m_CurrentPos.m_fPointX = m_DBData.m_Pos.LastPoint.fPointX;
m_CurrentPos.m_fPointY = m_DBData.m_Pos.LastPoint.fPointY;
m_CurrentPos.m_fPointZ = m_DBData.m_Pos.LastPoint.fPointZ;
return CalculateStatusData(false);
}
bool CCharacter::MoveZoneProcess(unsigned long dwServerID)
{
// 파티에 존이동했음을 보냄
if (0 != m_pParty)
{
CCharacterParty* lpParty = static_cast<CCharacterParty*>(m_pParty);
lpParty->SendPartyMemberDataToDBAgent(m_dwCID, 0, 0, dwServerID, 0,
GetCharacterName(), PktDD::SCmdMoveZonePartyMem);
}
// 친구 리스트에 존이동 메세지를 보낸다 //
FriendInfoUpdate(GetUID(), GetCID(), GetGID(), GetClass(), GetLevel(), dwServerID);
m_bLogout = true;
SetOperationFlag(CHAR_ZONE_MOVED);
return Logout(DBUpdateData::ZONEMOVE);
}
void CCharacter::FriendInfoUpdate(unsigned long dwUID, unsigned long dwCID, unsigned long dwGID, unsigned short wClass,
char cLevel, unsigned long dwServerID)
{
// 친구 리스트에 업데이트 메세지를 보낸다 //
GET_SINGLE_DISPATCH(lpChatDispatch, CChatDispatch, CChatDispatch::GetDispatchTable());
if(lpChatDispatch)
{
char* lpBuffer = lpChatDispatch->GetSendStream().GetBuffer(sizeof(PktFriendDB));
if(lpBuffer)
{
PktFriendDB* lpPktFriendDB = reinterpret_cast<PktFriendDB*>(lpBuffer);
lpPktFriendDB->m_dwOwnerUID = dwUID;
lpPktFriendDB->m_dwOwnerCID = dwCID;
lpPktFriendDB->m_dwReferenceUID = 0;
lpPktFriendDB->m_dwReferenceCID = 0;
lpPktFriendDB->m_dwData = 0;
lpPktFriendDB->m_cCmd = PktFriendDB::FRIEND_INFO_UPDATE;
lpPktFriendDB->m_dwGID = dwGID;
lpPktFriendDB->m_wClass = wClass;
lpPktFriendDB->m_cLevel = cLevel;
lpPktFriendDB->m_dwServerID = dwServerID;
lpChatDispatch->GetSendStream().WrapCrypt(sizeof(PktFriendDB), CmdFriendDB, 0, 0);
}
}
}
bool CCharacter::ItemDump(char* pBuffer, int* nBufferSize_InOut) const
{
using namespace GAMELOG;
sItemDump* lpItemDump = reinterpret_cast<sItemDump*>(pBuffer);
char* lpItems = reinterpret_cast<char*>(&lpItemDump[1]);
std::fill_n(lpItemDump->m_usDataSize, int(sItemDump::MAX_DUMP), 0);
unsigned long dwSize = 0;
unsigned short usTotalSize = sizeof(sItemDump);
for (int nCount = 0; nCount < sItemDump::MAX_DUMP; ++nCount)
{
switch (nCount)
{
case sItemDump::EQUIP_DUMP:
dwSize = *nBufferSize_InOut;
m_Equipments.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::INVEN_DUMP:
dwSize = *nBufferSize_InOut;
m_Inventory.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::EXTRA_DUMP:
dwSize = *nBufferSize_InOut;
m_ExtraSpace.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::EXCHANGE_DUMP:
dwSize = *nBufferSize_InOut;
m_Exchange.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
}
lpItems += lpItemDump->m_usDataSize[nCount];
usTotalSize += lpItemDump->m_usDataSize[nCount];
*nBufferSize_InOut -= dwSize;
}
*nBufferSize_InOut = usTotalSize;
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
#include "stdafx.h"
#include <Item/Container/ItemContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Item/Container/ExchangeContainer.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include "Character.h"
Item::CItem* CCharacter::GetItem(Item::ItemPos SrcPos)
{
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.GetItem(SrcPos);
case TakeType::TS_INVEN: return m_Inventory.GetItem(SrcPos);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.GetItem(SrcPos);
case TakeType::TS_EXCHANGE: return m_Exchange.GetItem(SrcPos);
case TakeType::TS_DEPOSIT: return m_Deposit.GetItem(SrcPos);
case TakeType::TS_STALL: return m_Stall.GetItem(SrcPos);
case TakeType::TS_TEMPINVEN: return m_TempInven.GetItem(SrcPos);
}
return NULL;
}
bool CCharacter::RemoveItem(Item::ItemPos SrcPos)
{
Item::CItem* lpItem = GetItem(SrcPos);
if (NULL == lpItem)
{
ERRLOG3(g_Log, "CID:0x%08x 삭제할 아이템이 존재하지 않습니다. SrcPos:(%d/%d)",
m_dwCID, SrcPos.m_cPos, SrcPos.m_cIndex);
return false;
}
// 노점상에서의 삭제가 아닌데 노점상 정보가 남아 있을 시... (잘못하면 서버가 죽을 수 있습니다.)
if (TakeType::TS_STALL != SrcPos.m_cPos && TakeType::TS_STALL == lpItem->GetRealPos().m_cPos)
{
// RemoveItem() 으로 RealPos 가 바뀌므로 미리 저장해두고, 그 값으로 SendRemoveItem 에 보내준다. by Vincent
Item::ItemPos realPos = lpItem->GetRealPos();
m_Stall.RemoveItem(lpItem->GetRealPos());
unsigned char cNum = (lpItem->IsSet(Item::DetailData::STACKABLE)) ? lpItem->GetNumOrDurability() : 1;
m_Stall.SendRemoveItem(TakeType::TakeType(realPos, realPos, cNum), PktStRI::SC_CANCEL, "");
}
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.RemoveItem(SrcPos);
case TakeType::TS_INVEN: return m_Inventory.RemoveItem(SrcPos);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.RemoveItem(SrcPos);
case TakeType::TS_EXCHANGE: return m_Exchange.RemoveItem(SrcPos);
case TakeType::TS_DEPOSIT: return m_Deposit.RemoveItem(SrcPos);
case TakeType::TS_STALL:
{
bool bResult = m_Stall.RemoveItem(SrcPos);
if (true == bResult)
{
unsigned char cNum = (lpItem->IsSet(Item::DetailData::STACKABLE)) ? lpItem->GetNumOrDurability() : 1;
m_Stall.SendRemoveItem(TakeType::TakeType(SrcPos, SrcPos, cNum), PktStRI::SC_CANCEL, "");
}
return bResult;
}
case TakeType::TS_TEMPINVEN: return m_TempInven.RemoveItem(SrcPos);
}
return true;
}
bool CCharacter::SetItem(Item::ItemPos SrcPos, Item::CItem* lpItem)
{
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.SetItem(SrcPos, lpItem);
case TakeType::TS_INVEN: return m_Inventory.SetItem(SrcPos, lpItem);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.SetItem(SrcPos, lpItem);
case TakeType::TS_EXCHANGE: return m_Exchange.SetItem(SrcPos, lpItem);
case TakeType::TS_DEPOSIT: return m_Deposit.SetItem(SrcPos, lpItem);
case TakeType::TS_STALL: return m_Stall.SetItem(SrcPos, lpItem);
case TakeType::TS_TEMPINVEN: return m_TempInven.SetItem(SrcPos, lpItem);
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,911 @@
#include "stdafx.h"
#include <Log/LogCommands.h>
#include <Log/CharacterLog.h>
#include <Utility/Math/Math.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/PacketStruct/CharAttackPacket.h>
#include <Network/Packet/PacketStruct/CharItemPacket.h>
#include <Network/Packet/PacketStruct/CharStatusPacket.h>
#include <Network/Packet/PacketStruct/ServerLogPacket.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentPacketParse.h>
#include <Network/Dispatch/DBAgent/RegularAgentDispatch.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharLoginOut.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Dispatch/GameClient/SendCharQuest.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameLog/SendLogPacket.h>
#include <Network/Dispatch/DBAgent/CastlePacketParse.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Map/FieldMap/Cell.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Map/FieldMap/CellManager.h>
#include <Item/ItemFactory.h>
#include <Item/Container/ExchangeContainer.h>
#include <Skill/SkillTable.h>
#include <Skill/Spell/SpellUtil.h>
#include <Skill/Spell/SpellTable.h>
#include <Skill/Spell/GlobalSpellMgr.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Character/ExpTable.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/Camp.h>
#include <Creature/Siege/CastleGate.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <Community/Guild/GuildMgr.h>
#include <Community/Guild/Guild.h>
#include <Community/Party/PartyMgr.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
#include "Character.h"
#include "CharacterCreate.h"
#include "SphereTree/CharSphereTree.h"
// forward decl.
void CheckDuplicatedItem(CCharacter& character);
void SendItemDuplicatedLog(CSendStream& SendStream, unsigned __int64 dwItemSerial,
Item::CItemOwnerInfo& itemOwnerInfo, unsigned long dwItemQty);
void CCharacter::PrepareLogin()
{
// HP/MP세팅
m_CreatureStatus.m_nNowHP = m_DBData.m_Info.HP;
m_CreatureStatus.m_nNowMP = m_DBData.m_Info.MP;
// 복사 아이템 검사
CheckDuplicatedItem(*this);
// edith 2009.02.07 석상전 위치 내의 지역에서 로그인하는거 방지
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 좌표 검사
if (!CCellManager::GetInstance().CheckPositionInZone(m_CurrentPos))
{
if (CServerSetup::GetInstance().IsBattleGameServer())
{
m_CurrentPos = CharCreate::BGServerStartPos[GetRace()][Math::Random::ComplexRandom(CharCreate::MAX_LOBBY_RESPAWN_POS)];
m_CurrentPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
m_CurrentPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
}
else
{
m_CurrentPos = CCharRespawnMgr::GetInstance().GetDefaultRespawnPos(GetNation());
}
m_DBData.m_Pos.LastPoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.LastPoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.LastPoint.fPointZ = m_CurrentPos.m_fPointZ;
}
}
// 브로드캐스팅 데이터 준비
m_SerializeCharacterData.PrepareData(*this);
m_SerializeCharacterData.ClearDeltaData();
SetOperationFlag(CHAR_INFO_LOADED);
}
bool CCharacter::Login()
{
if (NULL == m_lpGameClientDispatch)
{
return false;
}
CSendStream& SendStream = m_lpGameClientDispatch->GetSendStream();
/*
#ifndef NO_GAMEGUARD
// 로그인 하기전에 게임가드 처리
// edith 2009.08.11 게임가드 테스트
if (NULL != m_lpGameClientDispatch)// && 0 == GetAdminLevel())
{
// edith 2009.08.11 게임가드 2.5 업그레이드
// if (false == m_lpGameClientDispatch->IsAuth())
// {
// ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드를 보내지않아 연결을 끊습니다.", GetCID());
// m_lpGameClientDispatch->Disconnect();
// return false;
// }
GG_AUTH_DATA* lpAuthData = NULL;
if (false == m_lpGameClientDispatch->GetAuthQuery(&lpAuthData))
{
ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드(2) 체크에 실패하여 연결을 끊습니다.", GetCID());
m_lpGameClientDispatch->Disconnect();
return false;
}
GameClientSendPacket::SendCSAuth(SendStream,
GetCID(), m_lpGameClientDispatch->GetAuthCode(), lpAuthData, PktBase::NO_SERVER_ERR);
}
#endif
*/
// DELETE_ME : 더이상 클라이언트가 반전 모드로의 변경은 불가능하다. 개념만 남아있을 뿐. (2005-05-31 by 로딘)
/*
// 전쟁 모드 제한
if (SERVER_ID::ZONE12 == CServerSetup::GetInstance().GetServerZone() ||
SERVER_ID::CAPITAL == CServerSetup::GetInstance().GetServerZone())
{
m_PeaceMode.m_bPeace = true;
}
else
{
m_PeaceMode.m_bPeace = false;
}
SetPeaceMode(m_PeaceMode, false);
GameClientSendPacket::SendCharPeaceMode(SendStream, m_dwCID, 0, m_PeaceMode.m_bPeace, 0);
*/
// 배틀 그라운드 서버군에서 레벨은 40으로 고정
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
// 레벨은 40으로 고정
while (static_cast<unsigned long>(m_CreatureStatus.m_nLevel) < 40)
{
if (false == IncrementExp(static_cast<unsigned long>(
EXP::ExpTable[m_CreatureStatus.m_nLevel - 1])))
{
break;
}
}
}
// Cell 일 설정되지 않은 상태이므로 MoveTo 함수를 한번 호출해준다.
MoveTo(GetCurrentPos(), false);
// edith 2009.06.13 16번 존 / 17번 존에서 무적시간 조정
int InvincibleTime = 30;
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
InvincibleTime = 15;
char cZone = CServerSetup::GetInstance().GetServerZone();
// edith 2009.06.13 석상전에서 로그인할 경우 스타팅 포인트에서 리스폰되어서 시작
// 위치를 이동시키려면 MovePos 즉 워프를 이용해서 해당좌표로 강제이동시킨다.
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(cZone, GetNation());
MovePos(targetPos, cZone, false);
// 죽은 상태면 리스폰 시킨다.
if (0 == m_CreatureStatus.m_nNowHP)
{
Respawn();
}
}
// 로그인시 30초간 무적
Skill::CAddSpell<CInvincibleSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, this,
Skill::SpellType::MAGICAL_SPELL, Skill::SpellID::Invincible, 1, InvincibleTime))(this);
// 월드 웨폰이 존재하면 월드 웨폰 인챈트 적용
CCamp* lpWorldWeapon = CSiegeObjectMgr::GetInstance().GetWorldWeapon();
if (lpWorldWeapon)
{
AddWorldWeaponEnchant(reinterpret_cast<CAggresiveCreature*>(lpWorldWeapon), lpWorldWeapon->GetNation());
}
// DB에서 스펠을 읽어와 다시 걸어준다.
const SPELL spell = GetSpell();
GetSpellMgr().SetSpell(spell);
// 길드 정보를 준다.
CGuild* lpGuild = 0;
if (0 != GetGID() && 0 != (lpGuild = CGuildMgr::GetInstance().GetGuild(GetGID())))
{
unsigned short wError = PktBase::NO_SERVER_ERR;
unsigned char cTitle = lpGuild->GetTitle(m_dwCID);
if (Guild::NONE == cTitle)
{
wError = PktBase::SERVER_ERROR;
}
GameClientSendPacket::SendCharMyGuildInfo(SendStream,
lpGuild->GetGold(), lpGuild->GetRight(), cTitle, wError);
}
// 성 정보 전송
GameClientSendPacket::SendCharCastleInfo(SendStream);
// 게임 시간 정보 전송
GameClientSendPacket::SendCharGameTimeInfo(SendStream);
// 길드 요새 정보 전송
GameClientSendPacket::SendCharCampInfo(SendStream);
// CASTLE_TODO : 성이 길드 소유가 아니므로 일단 막아둔다.
// 리스폰 속도 향상 적용
// if (0 != GetGID())
// {
// Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastleByGID(GetGID());
// if (lpCastle)
// {
// CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
// if (lpEmblem && lpEmblem->GetUpgradeStep() > 0)
// {
// UpgradeRespawnSpeedByEmblem(lpEmblem->GetUpgradeType(), lpEmblem->GetUpgradeStep());
// }
// }
// }
// 엘리트 보너스 정보 전송
GameClientSendPacket::SendCharEliteBonus(SendStream, GetEliteBonus());
// 거부 옵션 정보 전송
GameClientSendPacket::SendCharControlOption(SendStream, m_dwCID, m_RejectOption);
// 어빌리티 포인트 업데이트
UpdateUseAbilityPoint();
// edith 2008.06.03 공헌훈장 포인트효과
// 컨텐츠 : 다크 카나번 국가 전쟁
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
// 국가전쟁 공헌훈장 포인트 효과.
if (CGameTimeMgr::GetInstance().IsRealmWarTime() &&
(SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3))
{
RealmSkill::RealmInchantAdd(this);
}
}
// Admin 캐릭터에는 예외처리 (QA 쪽 요청).
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
if(GetAdminLevel())
{
goto lb_move;
}
}
/*
if (true == CServerSetup::GetInstance().UseContents(GameRYL::NEWZONE_ZONE9))
{
if(GetAdminLevel())
{
goto lb_move;
}
}
*/
// 길드전, 국가전 참여 캐릭터는 해당 시간에 로그인시 존 이동 처리를 해준다.
if (CGameTimeMgr::GetInstance().IsGuildWarTime() &&
(GetGuildWarFlag() == Creature::WAR_ON || GetGuildWarFlag() == Creature::WAR_INSTANCE))
{
MoveToGuildWarZone();
}
// 공성전 중 로그인할 경우 공성측은 리스폰 지역으로 이동시킨다.
// edith 2008.07.17 캐피탈존에 로그인했는데 공성시간이면 리스폰지역으로 이동함.
if(CServerSetup::GetInstance().GetServerZone() == SERVER_ID::CAPITAL)
{
if(CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
using namespace DBAgentPacketParse;
SiegeMovePos(this);
}
}
// 컨텐츠 : 다크 카나번 국가 전쟁
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
if ((CGameTimeMgr::GetInstance().IsRealmWarReadyTime() || CGameTimeMgr::GetInstance().IsRealmWarTime()) &&
(GetRealmWarFlag() == Creature::WAR_ON || GetRealmWarFlag() == Creature::WAR_INSTANCE) &&
(SERVER_ID::STONE_WAR1 < CServerSetup::GetInstance().GetServerZone() || CServerSetup::GetInstance().GetServerZone() > SERVER_ID::STONE_WAR3) )
{
// 랠름전 시간인데 전투참가가 되어있고.하지만 16번이 아니니 강제로 16번으로 이동.
MoveToRealmWarZone();
}
if (CGameTimeMgr::GetInstance().IsRealmWarTime() &&
GetRealmWarFlag() == Creature::WAR_OFF &&
(SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3))
{
// edith 2008.07.08 쟁에 참여를 안했는데 16번 존이고.. 랠름전 시간이면 강제 참여
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
GameClientSendPacket::SendWarOnOff(lpDBAgentDispatch->GetSendStream(), GetCID(), GameTime::REALM, Creature::WAR_INSTANCE, 0);
}
// 전쟁시간에 로그인했는데 얼래 내가 석상전 맵에서 로그인을 했어.. 이르면 리스폰위치로 강제이동해..
// edith 2008.07.08 전쟁에 참가안한 사람이지만 16번존에 존재하면 리스폰위치로 강제 이동시킨다.
// MoveToRealmWarZone();
}
lb_move:
// edith 2008.06.03 석상 인첸트효과
// 다크 카나번에 로그인시
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 석상 정보를 보내준다.
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo(SendStream);
bool bAddRealmStatueEnchant = false;
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
// 국가 전쟁 시간이 아니라면, 석상 인챈트 효과를 걸어준다.
if (!CGameTimeMgr::GetInstance().IsRealmWarTime())
{
bAddRealmStatueEnchant = true;
}
}
/*
// 공선전용 맵과 석상전용 맵이 따로기 때문에 해당 로직 불필요
if (true == CServerSetup::GetInstance().UseContents(GameRYL::SIEGE))
{
// 공성전 컨텐츠 포함시 공성전 시간도 아니라면, 석상 인챈트 효과를 걸어준다.
if (CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
bAddRealmStatueEnchant = false;
}
}
*/
/*
// 석상 인챈트 효과를 적용할 수 있는 상황이라면....
if (bAddRealmStatueEnchant)
{
// edith 2008.06.03 석상전이 끝날때 11시간 버프를 걸어준다.
CCreatureManager::GetInstance().AddRealmStatueEnchant(this);
}
*/
}
// 신규존 로그인시
else if (CServerSetup::GetInstance().GetServerZone() == SERVER_ID::ZONE5)
{
// 생명축출기 정보를 보내준다.
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo(SendStream);
}
}
LOG_INOUT(
const int MAX_LOG_BUFFER = 1024;
char szBuffer[MAX_LOG_BUFFER];
unsigned long dwDispatchUID = m_lpGameClientDispatch->GetUID();
const SOCKADDR_IN& sockAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
_snprintf(szBuffer, MAX_LOG_BUFFER - 1,
"UID:%d/CID:0x%08x(0x%p)/DispatchUID:%d/DispatchPointer:0x%p 로그인에 성공하였습니다. "
"몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%016I64u) IP:%15s:%5u",
m_dwUID, m_dwCID, this, dwDispatchUID, m_lpGameClientDispatch,
m_DBData.m_Info.Name, m_DBData.m_Info.Level, m_DBData.m_Info.Exp,
inet_ntoa(sockAddr.sin_addr), ntohs(sockAddr.sin_port));
szBuffer[MAX_LOG_BUFFER - 1] = 0;
DETLOG0(g_Log, szBuffer);
);
return true;
}
bool CCharacter::Logout(DBUpdateData::UpdateType eUpdateType)
{
// 배틀그라운드의 경우 (죽은 상태라면) 리스폰 큐에서 제거
if (SERVER_ID::ZONE3 == CServerSetup::GetInstance().GetServerZone())
{
CCreatureManager::GetInstance().PopRespawnQueue(this);
}
// 죽었는데 리스폰 안하고 로그아웃하면
// 내구도가 안깍이는 버그가 있어서 로그아웃할때 부활한 상태가 아니면 내구도를 깍아버린다.
if(IsDead())
{
switch(m_eLastDeadType)
{
case DEAD_BY_NONE: // 올 수 없음
case DEAD_BY_CHARACTER: // 캐릭터에 의해 사망
break;
case DEAD_BY_SUICIDE: // 자살
// 자살시 내구도 감소
CalculateAllEquipDurability(DURABILITY_DECREASE_PERSENT_BY_SUICIDE);
break;
case DEAD_BY_MONSTER: // 몬스터에 의해 사망
if (!CServerSetup::GetInstance().GetDeathPenaltyEvent())
{
unsigned char cDecreasePersent = 0;
if(m_AbilityValue[Skill::Type::AB_ENDUR_SHILD] != 0)
cDecreasePersent = DURABILITY_DECREASE_PERSENT_BY_MONSTER*m_AbilityValue[Skill::Type::AB_ENDUR_SHILD]/100;
// 몬스터에게 죽었을 때 내구도 감소
CalculateAllEquipDurability(DURABILITY_DECREASE_PERSENT_BY_MONSTER-cDecreasePersent);
}
break;
}
}
// 월드 웨폰 인챈트를 제거한다.
ClearWorldWeaponEnchant();
// 성문을 막고 있던 중이었다면... 성문 막기를 취소한다.
if (0 != m_dwProtectGateCID)
{
CCastleGate* lpGate = reinterpret_cast<CCastleGate*>( CSiegeObjectMgr::GetInstance().GetSiegeObject(m_dwProtectGateCID) );
if (lpGate)
{
lpGate->DeleteProtectGate(this);
// 클라이언트에게 전송
CGameClientDispatch* lpDispatch = GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharCastleCmd(lpDispatch->GetSendStream(), lpGate->GetCastleID(),
lpGate->GetCID(), 0, 0,
PktCastleCmd::CASTLE_GATE_PROTECT_CANCEL,
PktCastleCmd::NO_SERVER_ERR);
}
}
}
// 셀 로그아웃
if (NULL != m_CellPos.m_lpCell)
{
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
m_CellPos.m_lpCell = NULL;
}
// 채팅 서버로 로그아웃을 던진다.
m_SerializeCharacterData.SendChatLogout(*this);
// BG_TODO : 듀얼이나, 던젼의 경우에는.. 다시 처리해야 할지도 모른다.
// VirtualArea 에 있었다면, VirtualArea 의 캐릭터리스트에서 제거
if (0 != GetMapIndex())
{
VirtualArea::CVirtualAreaMgr::GetInstance().LeaveVirtualArea(this);
}
// 소환수가 있다면 없앤다.
if (NULL != m_lpSummonee)
{
m_lpSummonee->Dead(NULL);
}
// 거래중이라면 거래 취소
CCharacter* lpExchangeCharacter = m_Exchange.GetExchangeCharacter();
if (NULL != lpExchangeCharacter)
{
m_Exchange.ExchangeOK(false);
CGameClientDispatch* lpExchangerDispatch = lpExchangeCharacter->GetDispatcher();
if (NULL != lpExchangerDispatch)
{
GameClientSendPacket::SendCharExchangeCmd(lpExchangerDispatch->GetSendStream(),
m_dwCID, lpExchangeCharacter->GetCID(), PktExC::EXC_QUIT, PktExC::NO_SERVER_ERR);
}
}
// 듀얼 초기화
DuelInit(PktDuC::DUC_LOGOUT);
// 파티찾기 리스트에서 삭제
CPartyMgr::GetInstance().DeleteFindPartyList(m_dwCID);
// 파티 주문 제거.
if (0 != GetPID())
{
// 파티 로그아웃
if (NULL != m_pParty)
{
m_pParty->PrepareLogout(m_dwCID);
}
else
{
ERRLOG3(g_Log, "CID:0x%08x 파티에 속한 녀석의 파티 포인터가 NULL입니다. PID:0x%08x, PartyPointer:0x%08x",
m_dwCID, m_DBData.m_Info.PID, m_pParty);
}
}
// 캐릭터에 현제 걸려있는 스펠들을 DB에 저장한다.
// 죽은게 아니면 스펠을 다시 읽지 않는다.
// 죽은직후 스펠을 저장한후 스펠을 삭제하기 때문에 여기서 다시 스펠을 저장하면
// 스펠이 사라진다.
if(!IsDead())
{
const SPELL spell = GetSpellMgr().GetAffectedInfo().GetSpellInfo();
SetSpell(spell);
}
// 환전소 기능을 사용해서 임시 객체가 남아있다면 삭제
CRegularAgentDispatch::GetTempCharacterMgr().EraseChar(m_dwCID);
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 퀘스트 정보 저장
GameClientSendPacket::SendCharQuestInfo(AgentSendStream, this);
// 환경 설정 정보 저장
GameClientSendPacket::SendConfigInfoDB(AgentSendStream, this);
}
// 병기에 탑승중이라면 내린것으로 간주
if (IsRideArms())
{
CSiegeObject* lpArms = CSiegeObjectMgr::GetInstance().GetSiegeObject(m_dwRideArmsCID);
if (lpArms)
{
// 수송선 조종사가 나간경우
if (Siege::RIDER_FOR_OWNER == lpArms->IsRider(m_dwCID))
{
lpArms->AllGetOff();
}
else
{
m_dwRideArmsCID = 0;
lpArms->GetOff(m_dwCID);
}
}
}
int nTotalSize = sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE;
char szCharBuffer[sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE];
char* lpCharacterInfo = szCharBuffer + sizeof(PktDBUpdate);
PktDBUpdate* lpPktDBUpdate = reinterpret_cast<PktDBUpdate*>(szCharBuffer);
memset(lpPktDBUpdate, 0, sizeof(PktDBUpdate));
unsigned short usError = 0;
unsigned char cAdmin = (true == IsAdmin()) ? 1 : 0;
bool bCharacterUpdate = GetCharacterInfo(lpCharacterInfo, &nTotalSize, lpPktDBUpdate->m_usUpdate);
if (!bCharacterUpdate)
{
nTotalSize = 0;
usError = 1;
std::fill_n(lpPktDBUpdate->m_usUpdate, unsigned short(DBUpdateData::MAX_UPDATE_DB), 0);
ERRLOG1(g_Log, "CID:0x%08x 데이터를 복사해 올 수 없습니다. DB에 업데이트 할 수 없습니다.", m_dwCID);
}
else if (0 != lpDBAgentDispatch)
{
if (IsOperationFlagSet(CHAR_INFO_LOADED))
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 창고 데이터 업데이트(창고가 열려 있으면 업데이트한다.)
// 순서 주의! DB에 Update할 때는 창고 업데이트 후, 캐릭터를 업데이트한다.
if (!m_Deposit.DBUpdate(AgentSendStream))
{
ERRLOG1(g_Log, "CID:0x%08x 창고 업데이트 실패", m_dwCID);
}
// 로그아웃 정보 DBAgent에 보내기
lpPktDBUpdate->m_dlItemSerial = Item::CItemFactory::GetInstance().GetItemUID();
lpPktDBUpdate->m_dwSessionID = m_dwSessionID;
lpPktDBUpdate->m_dwUserID = m_dwUID;
lpPktDBUpdate->m_dwCharID = m_dwCID;
lpPktDBUpdate->m_TypeCode = eUpdateType;
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_cAdminFlag = GetGMModelFlag();
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_Address.S_un.S_addr = 0;
lpPktDBUpdate->m_cAdminLevel = 0;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
//lpPktDBUpdate->m_cPadding = 0;
lpPktDBUpdate->m_cAccountNation = 0;
lpPktDBUpdate->m_cNameChangeCount = 0;
lpPktDBUpdate->m_cGuildWarFlag = 0;
lpPktDBUpdate->m_cRealmWarFlag = 0;
lpPktDBUpdate->m_cRealmPoint = 0;
lpPktDBUpdate->m_cTacticsFlag = 0;
lpPktDBUpdate->m_PlayTime = 0;
lpPktDBUpdate->m_PremiumTime = 0;
lpPktDBUpdate->m_PremiumType = 0;
if (AgentSendStream.WrapCompress(reinterpret_cast<char*>(lpPktDBUpdate),
static_cast<unsigned short>(sizeof(PktDBUpdate) + nTotalSize), CmdDBUpdateData, 0, 0))
{
LOG_INOUT(
char szExp[64];
Math::Convert::Hex64ToStr(szExp, m_DBData.m_Info.Exp);
DETLOG7(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p 의 캐릭터 정보를 DBAgent에 업데이트합니다. 유저 로그아웃을 처리합니다."
" 로그아웃에 성공하였습니다. 몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%s)",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch, m_DBData.m_Info.Name, m_DBData.m_Info.Level, szExp)
);
}
else
{
ERRLOG1(g_Log, "CID:0x%08x DB에 캐릭터 업데이트를 할 수 없습니다. 전송에 실패했습니다.", m_dwCID);
}
}
else
{
// 데이터가 세팅되지 않았으니, 정상 로그아웃은 아니다.
DBAgentPacketParse::SendAbnormalLogout(
m_dwUID, m_dwCID, m_dwSessionID, 0, m_lpGameClientDispatch);
}
}
else
{
ERRLOG2(g_Log, "CID:0x%08x/AgentSession:0x%p/ DBUpdate failed.",
m_dwCID, lpDBAgentDispatch);
}
SOCKADDR_IN remoteAddr;
if (0 != m_lpGameClientDispatch)
{
remoteAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
}
else
{
memset(&remoteAddr, 0, sizeof(SOCKADDR_IN));
}
// 게임로그에 캐릭터 로그아웃 남기기
GAMELOG::LogCharLoginOut(m_dwUID, this, &remoteAddr, lpCharacterInfo, nTotalSize,
lpPktDBUpdate->m_usUpdate, GAMELOG::CMD::CHAR_LOGOUT, usError);
// 로그 서버에 로그아웃 보내기.
// SendLogPacket::CharLogout(*this);
LOG_INOUT(DETLOG4(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p 다음 유저의 로그아웃을 처리합니다. 다음 유저를 제거합니다.",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch));
return CCreatureManager::GetInstance().DeleteCreature(m_dwCID);
}
void CheckDuplicatedItem(CCharacter& character)
{
using namespace Item;
CItemOwnerInfo itemOwnerInfo(
character.GetCharacterName(), character.GetUID(), character.GetCID());
CItemOwnerInfo* lpDuplicateOwner = 0;
CItemFactory& itemFactory = Item::CItemFactory::GetInstance();
// first : 소유자 정보 / second : 복사 아이템 Qty
typedef std::pair<CItemOwnerInfo*, unsigned long> DuplicateInfo;
typedef std::multimap<unsigned __int64, DuplicateInfo, std::less<unsigned __int64>,
boost::fast_pool_allocator<std::pair<unsigned __int64, DuplicateInfo> > > DuplicatedMap;
DuplicatedMap duplicatedItemMap;
// 복사 데이터가 있으면 DB중계로 송신하기 위해서..
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch,
CDBAgentDispatch::GetDispatchTable());
// 아이템 컨테이너들을 배열에 넣는다.
const int MAX_CONTAINER_SET = 7;
CItemContainer* containerSet[MAX_CONTAINER_SET] =
{
&character.GetInventory(),
&character.GetEquipments(),
&character.GetExtra(),
&character.GetExchange(),
&character.GetDeposit(),
&character.GetStall(),
&character.GetTempInven(),
};
CItemContainer** lppContainerPos = containerSet;
CItemContainer** lppContainerEnd = containerSet + MAX_CONTAINER_SET;
for(; lppContainerPos != lppContainerEnd; ++lppContainerPos)
{
CItemContainer& itemContainer = **lppContainerPos;
CItemContainer::iterator pos = itemContainer.begin();
CItemContainer::iterator end = itemContainer.end();
for(; pos != end; ++pos)
{
CItem* lpItem = *pos;
if (0 != lpItem)
{
unsigned __int64 dwItemSerial = lpItem->GetUID();
lpDuplicateOwner = itemFactory.AddItemMap(dwItemSerial, itemOwnerInfo);
if (0 != lpDuplicateOwner)
{
// 복사 발견. 일단 나와 이녀석의 시리얼 및 정보를 리스트 등으로 기억해두었다가
// 개수를 세어서 나중에 한번에 보낸다.
// 내 아이템 정보 : dwItemSerial, itemOwnerInfo
// 상대 아이템 정보 : dwItemSerial, lpDuplicateOwner
std::pair<DuplicatedMap::iterator, DuplicatedMap::iterator> result =
duplicatedItemMap.equal_range(dwItemSerial);
bool bAddedQty = false;
bool bAddedDuplicateOwner = false;
for(;result.first != result.second; ++result.first)
{
unsigned __int64 dwItemUID = result.first->first;
// first : 소유자 정보 / second : 복사 아이템 Qty
DuplicateInfo& duplicateInfo = result.first->second;
CItemOwnerInfo& duplicateOwnerInfo = *duplicateInfo.first;
if (duplicateOwnerInfo.GetCID() == itemOwnerInfo.GetCID())
{
// 소유자가 같은 아이템이다. Qty를 증가시켜준다.
// Qty는 복제된 아이템을 '내가' 몇개나 가지고 있는지를 나타낸다.
++duplicateInfo.second;
bAddedQty = true;
}
else if (duplicateOwnerInfo.GetCID() == lpDuplicateOwner->GetCID())
{
// 이전에 가지고 있던 사람이 내가 아니라 다른 사람이다.
bAddedDuplicateOwner = true;
}
}
if (!bAddedQty)
{
// 숫자가 추가되지 않았으면, 항목에 새로 추가.
duplicatedItemMap.insert(result.second, DuplicatedMap::value_type(dwItemSerial,
DuplicateInfo(&itemOwnerInfo, (itemOwnerInfo.GetCID() == lpDuplicateOwner->GetCID()) ? 2 : 1)));
}
if (!bAddedDuplicateOwner && lpDuplicateOwner->GetCID() != itemOwnerInfo.GetCID())
{
duplicatedItemMap.insert(result.second,
DuplicatedMap::value_type(dwItemSerial, DuplicateInfo(lpDuplicateOwner, 1)));
}
}
}
}
}
DuplicatedMap::iterator pos = duplicatedItemMap.begin();
DuplicatedMap::iterator end = duplicatedItemMap.end();
if (0 != lpDBAgentDispatch)
{
// 전송
for(; pos != end; ++pos)
{
SendItemDuplicatedLog(lpDBAgentDispatch->GetSendStream(),
pos->first, *pos->second.first, pos->second.second);
}
}
else
{
// 로그를 남긴다.
for(; pos != end; ++pos)
{
DuplicateInfo& dupInfo = pos->second;
ERRLOG5(g_Log, "UID:%10u / CID:%10u / CharName:%s / ItemSerial:0x%I64X / Qty:%u / 아이템 복사가 발견되었습니다",
dupInfo.first->GetUID(), dupInfo.first->GetCID(), dupInfo.first->GetName(), pos->first, dupInfo.second);
}
}
}
void SendItemDuplicatedLog(CSendStream& SendStream, unsigned __int64 dwItemSerial,
Item::CItemOwnerInfo& itemOwnerInfo, unsigned long dwItemQty)
{
PktItemDuplicated* lpPktItemDuplicated =
reinterpret_cast<PktItemDuplicated*>(SendStream.GetBuffer(sizeof(PktItemDuplicated)));
if (0 != lpPktItemDuplicated)
{
lpPktItemDuplicated->m_cLogCmd = PktServerLog::ITEM_DUPLICATED_LOG;
strncpy(lpPktItemDuplicated->m_szName, itemOwnerInfo.GetName(), PktItemDuplicated::MAX_NAME - 1);
lpPktItemDuplicated->m_szName[PktItemDuplicated::MAX_NAME - 1] = 0;
lpPktItemDuplicated->m_dwItemSerial = dwItemSerial;
lpPktItemDuplicated->m_dwUID = itemOwnerInfo.GetUID();
lpPktItemDuplicated->m_dwCID = itemOwnerInfo.GetCID();
lpPktItemDuplicated->m_dwQty = dwItemQty;
SendStream.WrapHeader(sizeof(PktItemDuplicated), CmdServerLog, 0, 0);
}
}
void RealmSkill::RealmInchantAdd(CCharacter* lpCharacter)
{
// edith 2010.01.02 공헌 훈장의 개념이 바뀌면서 공헌훈장 버프를 제외한다.
return;
// edith 2008.05.28 공헌 훈장별 버프 설정
// 공헌 훈장 포인트 세팅 //
unsigned char cLevel[2][6] =
{
0, 1, 1, 2, 2, 3,
0, 0, 1, 1, 2, 2
};
if(!lpCharacter)
{
return;
}
unsigned char cPoint = lpCharacter->GetRealmPoint();
if(!cPoint)
{
return;
}
// HP 효과.
if(cLevel[0][cPoint])
{
Skill::CAddSpell<CRealmHPSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, lpCharacter,
Skill::SpellType::REALM_SPELL, Skill::SpellID::RealmHP, cLevel[0][cPoint], CSpell::INFINITE_DURATION))(lpCharacter);
}
// MP 효과.
if(cLevel[1][cPoint])
{
Skill::CAddSpell<CRealmMPSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, lpCharacter,
Skill::SpellType::REALM_SPELL, Skill::SpellID::RealmMP, cLevel[1][cPoint], CSpell::INFINITE_DURATION))(lpCharacter);
}
}
void RealmSkill::RealmInchantRemove(CCharacter* lpCharacter)
{
if(!lpCharacter)
return;
// HP 효과.
if(lpCharacter->GetSpellMgr().GetAffectedInfo().GetSpell(Skill::SpellID::RealmHP))
{
lpCharacter->GetSpellMgr().GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::RealmHP);
}
// MP 효과.
if(lpCharacter->GetSpellMgr().GetAffectedInfo().GetSpell(Skill::SpellID::RealmMP))
{
lpCharacter->GetSpellMgr().GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::RealmMP);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Skill/SkillMgr.h>
#include "Character.h"
void CCharacter::UpdateQuickSlotSkill(SKILLSLOT Slot)
{
for (int nQSlot = 0; nQSlot < QUICK::MAX_QUICK_NUM; ++nQSlot)
{
QUICKSLOT& QuickSlot = m_DBData.m_Quick.Slots[nQSlot];
if (QUICKSLOT::SKILL == QuickSlot.nType
&& QuickSlot.wID == Slot.SKILLINFO.wSkill)
{
QuickSlot.nSkillLevel = Slot.SKILLINFO.cSkillLevel;
QuickSlot.nSkillLockCount = Slot.SKILLINFO.cLockCount;
}
}
}
bool CCharacter::MoveQuickSlot(const TakeType takeType, const unsigned short usSkillID, unsigned char cLockCount, unsigned char cSkillLevel)
{
bool bResult = 0;
if (QUICK::MAX_QUICK_NUM <= takeType.m_dstPos.m_cIndex)
{
ERRLOG3(g_Log, "CID:%10u 퀵슬롯 이동 이상. 퀵슬롯 인덱스 위치가 이상합니다."
"Dst(%2d,%2d)", m_dwCID, takeType.m_dstPos.m_cPos, takeType.m_dstPos.m_cIndex);
return false;
}
switch (takeType.m_srcPos.m_cPos)
{
case TakeType::TS_INVEN:
case TakeType::TS_TEMP:
{
Item::CItem* pItem = GetItem(takeType.m_srcPos);
if (NULL == pItem)
{
ERRLOG3(g_Log, "CID:%10u 퀵슬롯 이동 이상. 아이템을 얻어올 수 없습니다. Src(%2d,%2d)",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
}
else if (pItem->IsSet(Item::DetailData::QUICKSLOT_IN))
{
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::ITEM, 0, 0, pItem->GetPrototypeID());
return true;
}
} break;
case TakeType::TS_SSLOT:
{
if (SKILL::MAX_SLOT_NUM <= takeType.m_srcPos.m_cIndex)
{
ERRLOG3(g_Log, "CID:%10u 옮기려는 스킬 위치가 이상합니다. Src(%2d,%2d)",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
}
else if (TakeType::TS_QSLOT == takeType.m_dstPos.m_cPos)
{
const Skill::ProtoType* lpProtoType = CSkillMgr::GetInstance().GetSkillProtoType(usSkillID);
if (NULL == lpProtoType)
{
ERRLOG2(g_Log, "CID:%10u 옮기려는 스킬의 아이디가 존재하지 않습니다. ID:0x%04x", m_dwCID, usSkillID);
return false;
}
unsigned char cSkillLockCount = 0;
unsigned char cSkillCurLevel = 1;
// 액션 스크립트이면
if(lpProtoType->m_eSkillType == Skill::Type::ACTION)
{
cSkillLockCount = cLockCount;
cSkillCurLevel = cSkillLevel;
}
else
{
SKILLSLOT SkillSlot = m_DBData.m_Skill.SSlot[takeType.m_srcPos.m_cIndex];
if (false == lpProtoType->m_bIsClassSkill)
{
cSkillLockCount = SkillSlot.SKILLINFO.cLockCount;
cSkillCurLevel = SkillSlot.SKILLINFO.cSkillLevel;
}
}
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::SKILL, cSkillLockCount, cSkillCurLevel, usSkillID);
return true;
}
} break;
case TakeType::TS_QSLOT:
{
if (QUICK::MAX_QUICK_NUM <= takeType.m_srcPos.m_cIndex)
{
ERRLOG2(g_Log, "CID:%10u 옮기려는 퀵슬롯 위치가 이상합니다. 스킬 위치 %d",
m_dwCID, takeType.m_srcPos.m_cIndex);
}
else
{
if (TakeType::TS_QSLOT == takeType.m_dstPos.m_cPos)
{
std::swap(m_DBData.m_Quick.Slots[takeType.m_srcPos.m_cIndex],
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex]);
}
else
{
m_DBData.m_Quick.Slots[takeType.m_srcPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::NONE, 0, 0, 0);
}
return true;
}
} break;
}
return false;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
#include "stdafx.h"
#include "CharacterStructure.h"
#include "GMMemory.h"
CharacterFightInfo::CharacterFightInfo()
: m_pDuelOpponent(NULL)
{
}

View File

@@ -0,0 +1,50 @@
#ifndef _CHARACTER_STRUCTURE_H_
#define _CHARACTER_STRUCTURE_H_
#include <Creature/CreatureStructure.h>
// 전방 참조
class CCharacter;
// -----------------------------------------------------------------------
// DB 에서 처음에 읽어 오는 데이터들. 중간중간 저장해서 DB에 갱신함.
// 되도록이면 게임 처리중에는 건드리지 않는다. ( 레벨업 등등을 제외하고 ... )
struct CharacterDBData
{
CHAR_INFOST m_Info; // 외모, 이름, 능력치, hp,mp 등 기본 정보
CHAR_POS m_Pos; // 종료시 위치 정보, 저장된 위치 정보 ( Respawn 장소 )
SKILL m_Skill;
QUICK m_Quick;
SPELL m_Spell;
unsigned char m_cAdminLevel;
};
typedef CharacterDBData* LPCharacterDBData;
// 유저 접속 정보. 유저의 IP를 넣음.
struct ConnectInfo
{
SOCKADDR_IN m_siAgentHost; // agent UDP address of host
SOCKADDR_IN m_siPrivateHost; // private UDP address of host
SOCKADDR_IN m_siPublicHost; // public UDP address of host
};
typedef ConnectInfo* LPConnectInfo;
// 캐릭터끼리의 전투 정보 (듀얼, 전투)
struct CharacterFightInfo
{
CCharacter* m_pDuelOpponent;
POS m_Pos;
unsigned long m_dwCellID;
__int64 m_nRestoreExp; // 스킬 리저렉션을 통해 부활할 경우 일정량의 경험치가 복구될 수 있다.
CharacterFightInfo();
};
#endif

View File

@@ -0,0 +1,337 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Creature/NPC/NPC.h>
#include <Creature/CreatureManager.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Log/ItemLog.h>
#include "Character.h"
unsigned long CCharacter::RepairItem(const unsigned long dwNPCID, Item::ItemPos itemPos, unsigned short& wError)
{
CCreature* lpCreature = CCreatureManager::GetInstance().GetCreature(dwNPCID);
if (NULL == lpCreature)
{
ERRLOG2(g_Log, "CID:%10u 수리해줄 크리쳐가 존재하지 않습니다. 수리 실패. CID:%10u", m_dwCID, dwNPCID);
wError = PktRpI::FAIL_NOT_CREATURE;
return 0;
}
if (Creature::CT_NPC == Creature::GetCreatureType(dwNPCID))
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
CNPC* lpNPC = reinterpret_cast<CNPC*>(lpCreature);
if (lpNPC->GetZone() != nZone)
{
ERRLOG3(g_Log, "CID:%10u NPC의 존 번호가 다릅니다. Server Zone : %d, NPC Zone : %d", m_dwCID, nZone, lpNPC->GetZone());
wError = PktRpI::FAIL_NOT_NPCZONE;
return 0;
}
}
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem)
{
ERRLOG3(g_Log, "CID:%10u 아이템이 %d Pos, %d Index에 없습니다", m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
wError = PktRpI::FAIL_NOT_POSITEM;
return 0;
}
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
ERRLOG1(g_Log, "CID:%10u 스택 아이템이어서 수리할 수 없습니다", m_dwCID);
wError = PktRpI::FAIL_NOT_STACKABLE;
return 0;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG1(g_Log, "CID:%10u 장비 아이템이 아니어서 수리할 수 없습니다", m_dwCID);
wError = PktRpI::FAIL_NOT_EQUIP;
return 0;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// ERRLOG4(g_Log, "CID:%10u 장비가 이미 최대 내구도에 도달해서, 수리할 수 없습니다."
// " 종류ID:%d, 현재 내구도:%d, 최대 내구도:%d", m_dwCID, lpItem->GetPrototypeID(),
// lpItem->GetNumOrDurability(), lpItem->GetMaxNumOrDurability());
wError = PktRpI::FAIL_FULL_DRUA;
return 0;
}
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
unsigned long dwGold = lpCreature->RepairItem(lpEquipment, m_DBData.m_Info.Gold);
if (0 != dwGold)
{
CalculateStatusData(false);
}
else
{
wError = PktRpAI::FAIL_NOT_REPAIR;
}
return dwGold;
}
unsigned long CCharacter::RepairAllItem(const unsigned long dwNPCID, unsigned short& wError)
{
CCreature* lpCreature = CCreatureManager::GetInstance().GetCreature(dwNPCID);
if (NULL == lpCreature)
{
ERRLOG2(g_Log, "CID:%10u 수리해줄 크리쳐가 존재하지 않습니다. 수리 실패. CID:%10u", m_dwCID, dwNPCID);
wError = PktRpAI::FAIL_NOT_CREATURE;
return 0;
}
if (Creature::CT_NPC == Creature::GetCreatureType(dwNPCID))
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
CNPC* lpNPC = reinterpret_cast<CNPC*>(lpCreature);
if (lpNPC->GetZone() != nZone)
{
ERRLOG3(g_Log, "CID:%10u NPC의 존 번호가 다릅니다. Server Zone : %d, NPC Zone : %d", m_dwCID, nZone, lpNPC->GetZone());
wError = PktRpAI::FAIL_NOT_NPCZONE;
return 0;
}
}
unsigned long dwRepairGold = 0;
Item::ItemPos itemPos;
itemPos.m_cPos = TakeType::TS_EQUIP;
// 1단계 검사
for (unsigned char cIndex = 0; cIndex < Item::EquipmentPos::MAX_EQUPMENT_POS; ++cIndex)
{
if (GetRace() == CClass::AKHAN &&
(Item::EquipmentPos::SHIRT == cIndex ||
Item::EquipmentPos::TUNIC == cIndex ||
Item::EquipmentPos::SHIELD_HAND2 == cIndex ||
Item::EquipmentPos::WEAPON_HAND2 < cIndex))
{
continue;
}
itemPos.m_cIndex = cIndex;
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem) { continue; }
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
// 화살/볼트의 경우 수리 불가
continue;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG3(g_Log, "CID:%10u 수리하는 아이템이 장비 아이템이 아닙니다. (%d Pos, %d Index)",
m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
continue;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// 풀내구의 장비는 스킵
continue;
}
// 골드를 계산한다.
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquipment)
dwRepairGold += lpEquipment->GetRepairPrice();
}
// 돈이 실제보다 많이들면 에러를 리턴한다.
if (dwRepairGold > GetGold())
{
wError = PktRpAI::NOT_ENOUGH_GOLD;
return 0;
}
dwRepairGold = 0;
// 2단계 실제 수리
for (unsigned char cIndex = 0; cIndex < Item::EquipmentPos::MAX_EQUPMENT_POS; ++cIndex)
{
if (GetRace() == CClass::AKHAN &&
(Item::EquipmentPos::SHIRT == cIndex ||
Item::EquipmentPos::TUNIC == cIndex ||
Item::EquipmentPos::SHIELD_HAND2 == cIndex ||
Item::EquipmentPos::WEAPON_HAND2 < cIndex))
{
continue;
}
itemPos.m_cIndex = cIndex;
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem) { continue; }
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
// 화살/볼트의 경우 수리 불가
continue;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG3(g_Log, "CID:%10u 수리하는 아이템이 장비 아이템이 아닙니다. (%d Pos, %d Index)",
m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
continue;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// 풀내구의 장비는 스킵
continue;
}
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquipment)
dwRepairGold += lpCreature->RepairItem(lpEquipment, m_DBData.m_Info.Gold);
}
if (0 != dwRepairGold)
{
CalculateStatusData(false);
}
else
{
wError = PktRpAI::FAIL_NOT_REPAIR;
}
return dwRepairGold;
}
Item::CItem* CCharacter::SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError)
{
Item::CItem* lpItem = m_Stall.GetItem(takeType.m_srcPos);
if (NULL == lpItem) { return NULL; }
unsigned long dwCustomerCID = lpCustomer->GetCID();
unsigned long dwCurrentGold = lpCustomer->GetGold();
dwPrice = lpItem->GetBuyPrice() * takeType.m_cNum;
if (dwPrice > dwCurrentGold)
{
ERRLOG2(g_Log, "노점상 오류 : 돈이 부족합니다. 가격:%d, 소지금:%d", dwPrice, dwCurrentGold);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
// 가질수 있는 최대 소지금 초과
if (GetGold() > ULONG_MAX - dwPrice)
{
ERRLOG2(g_Log, "노점상 오류 : 거래후 Gold가 최대 소지금을 초과합니다. 가격:%d, 소지금:%d", dwPrice, GetGold());
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_GOLD_OVERFLOW);
return NULL;
}
if (false == lpCustomer->GetInventory().TestItem(takeType.m_dstPos, lpItem->GetPrototypeID(), takeType.m_cNum))
{
Item::CItemContainer* lpItemContainer = lpCustomer->GetItemContainer(takeType.m_dstPos.m_cPos);
if (NULL != lpItemContainer)
{
lpItemContainer->DumpItemInfo();
}
else
{
ERRLOG1(g_Log, "CID:%10u 아이템 덤프를 출력할 수 없습니다.", dwCustomerCID);
}
ERRLOG4(g_Log, "CID:%10u 아이템 종류:%d를 (%2d:%2d)에 아이템 넣기 실패.",
dwCustomerCID, lpItem->GetPrototypeID(), takeType.m_dstPos.m_cPos, takeType.m_dstPos.m_cIndex);
return NULL;
}
bool bStackable = lpItem->IsSet(Item::DetailData::STACKABLE);
unsigned char cNumOrDurability = lpItem->GetNumOrDurability();
Item::ItemPos ItemPos = lpItem->GetPos();
// 스택 가능한 아이템인 경우, 개수제한 확인.
if (!(bStackable && (cNumOrDurability < takeType.m_cNum)))
{
// 스택이 불가능하거나, 전부 파는 경우에는 아이템 제거.
if (!bStackable || (bStackable && (takeType.m_cNum == lpItem->GetNumOrDurability())))
{
if (false == m_Stall.RemoveItem(takeType.m_srcPos))
{
ERRLOG3(g_Log, "CID:%10u 아이템을 (%2d, %4x)위치로부터 지우는 데 실패했습니다.",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
else
{
RemoveItem(ItemPos);
m_Stall.SendRemoveItem(takeType, PktStRI::SC_CANCEL, lpCustomer->GetCharacterName());
}
}
else
{
lpItem->SetNumOrDurability(cNumOrDurability - takeType.m_cNum);
m_Stall.SendRemoveItem(takeType, PktStRI::SC_CANCEL, lpCustomer->GetCharacterName());
unsigned short usProtoTypeID = lpItem->GetPrototypeID();
lpItem = Item::CItemFactory::GetInstance().CreateItem(usProtoTypeID);
if (NULL == lpItem)
{
ERRLOG1(g_Log, "노점상 오류 : 아이템 생성 실패. ProtoTypeID : %d", usProtoTypeID);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
lpItem->SetNumOrDurability(takeType.m_cNum);
}
AddGold(dwPrice, false);
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharTradeItem(m_lpGameClientDispatch->GetSendStream(), this, lpCustomer->GetCID(),
Item::ItemPos(), NULL, ItemPos, takeType.m_cNum, 0);
}
}
else
{
ERRLOG4(g_Log, "노점상 오류 : (%2d, %4x)의 아이템 개수 : %d개 팔려는 아이템 개수 : %d개",
takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex, cNumOrDurability, takeType.m_cNum);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::NO_SERVER_ERR);
return lpItem;
}

View File

@@ -0,0 +1,336 @@
#ifndef _EXP_TABLE_H_
#define _EXP_TABLE_H_
#include <Utility/Setup/ServerSetup.h>
namespace EXP
{
static unsigned char GetUsingMaxLevel(void)
{
if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_100))
{
return 100;
}
else if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_90))
{
return 90;
}
else
{
return 80;
}
}
const __int64 ExpTable[Creature::PC_MAX_LEVEL] = {
200 ,
300 ,
422 ,
571 ,
752 ,
974 ,
1244 ,
1574 ,
1976 ,
2467 ,
3066 ,
3796 ,
4687 ,
5775 ,
7101 ,
8719 ,
10693 ,
13102 ,
16040 ,
19625 ,
23999 ,
29335 ,
35844 ,
43786 ,
53475 ,
65296 ,
79717 ,
97310 ,
118774 ,
144961 ,
176908 ,
215884 ,
263434 ,
321446 ,
392220 ,
478564 ,
583905 ,
712420 ,
869208 ,
1060490 ,
1293854 ,
1578557 ,
1925896 ,
2349649 ,
2866628 ,
3497342 ,
4266813 ,
5205568 ,
6350849 ,
7748092 ,
9452728 ,
11532384 ,
14069565 ,
17164925 ,
20941264 ,
25548398 ,
31169102 ,
38026360 ,
46392216 ,
56598559 ,
69050298 ,
84241420 ,
102774588 ,
125385054 ,
152969821 ,
186623238 ,
227680407 ,
277770152 ,
338879641 ,
413433218 ,
504388583 ,
615354127 ,
750732091 ,
915893207 ,
1117389768 ,
1363215573 ,
1663123055 ,
2029010183 ,
2475392479 ,
3019978881 ,
3684374291 ,
4494936691 ,
5483822818 ,
6690263894 ,
8162122007 ,
9957788905 ,
12148502520 ,
14821173130 ,
18081831275 ,
22059834211 ,
26912997794 ,
32833857365 ,
40057306041 ,
48869913426 ,
59621294436 ,
72737979268 ,
88740334762 ,
108263208466 ,
132081114385 ,
132081114385 // 100레벨 부터 이해 못해-_-
};
const unsigned long ExpCapTable[Creature::PC_MAX_LEVEL] = {
58,
62,
67,
72,
82,
86,
96,
202,
221,
240,
259,
278,
298,
326,
355,
384,
413,
442,
480,
518,
566,
605,
653,
710,
768,
826,
893,
970,
1046,
1133,
1219,
1315,
1421,
1536,
1661,
1795,
1939,
2093,
2266,
2438,
2640,
2851,
3082,
3322,
3590,
3878,
4186,
4522,
4886,
5280,
5702,
6163,
6653,
7181,
7757,
8381,
9053,
9773,
10560,
11405,
12317,
13306,
14371,
15514,
16762,
18096,
19546,
21110,
22800,
24624,
26602,
28723,
31027,
33504,
36192,
39082,
42211,
45590,
49238,
53174,
57427,
62026,
66989,
72346,
78134,
84384,
91133,
98419,
106301,
114806,
123984,
133910,
144624,
156192,
168682,
182179,
196752,
212496,
229498,
247853
};
const float ExpConvertTable[Creature::PC_MAX_LEVEL] =
{
1.2f,
0.652173913f,
0.395833333f,
0.221052632f,
0.147058824f,
0.107142857f,
0.080434783f,
0.06056338f,
0.04952381f,
0.064356436f,
0.051724138f,
0.044887781f,
0.038745387f,
0.033426184f,
0.031149302f,
0.029661017f,
0.027516779f,
0.027027027f,
0.025438596f,
0.023050847f,
0.021866667f,
0.020726496f,
0.020138889f,
0.019400856f,
0.018979834f,
0.019104478f,
0.019107744f,
0.019010043f,
0.019446154f,
0.018932528f,
0.018588137f,
0.018548387f,
0.018669076f,
0.019495352f,
0.020523708f,
0.021702486f,
0.023075468f,
0.024573379f,
0.025590848f,
0.026882439f,
0.028578261f,
0.03020531f,
0.032458698f,
0.034889868f,
0.037479936f,
0.040498899f,
0.044010767f,
0.047801858f,
0.052198744f,
0.056302083f,
0.060962822f,
0.066287215f,
0.072166196f,
0.078949329f,
0.086399724f,
0.094809356f,
0.104353011f,
0.115183537f,
0.122776247f,
0.131411711f,
0.141578839f,
0.153099017f,
0.166192541f,
0.181208054f,
0.197992027f,
0.216885605f,
0.238397966f,
0.262553292f,
0.289851868f,
0.308373111f,
0.330063006f,
0.35528795f,
0.384373361f,
0.417348834f,
0.454948957f,
0.497478302f,
0.545998585f,
0.600566859f,
0.662433912f,
0.701894612f,
0.749179452f,
0.804414873f,
0.868368606f,
0.941942784f,
1.025947304f,
1.1215602f,
1.230182368f,
1.353346121f,
1.492902584f,
1.209087319f,
1.071556113f,
1.001289906f,
0.968735572f,
0.960942686f,
0.971115208f,
0.995614754f,
1.032601272f,
1.081251392f,
1.141193513f,
1.198466357f // 100레벨 부터 이해 못해-_-
};
};
#endif

View File

@@ -0,0 +1,775 @@
#include "CharSphereTree.h"
#include <cassert>
#include <Creature/Character/Character.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
CCharSphereTree& CCharSphereTree::GetInstance()
{
static CCharSphereTree ms_this( SphereConst::MAX_SPHERE_NODE,
SphereConst::DEFAULT_ROOT_SIZE,
SphereConst::DEFAULT_LEAF_SIZE,
SphereConst::DEFAULT_GRAVY );
return ms_this;
}
CCharSphereNode::CCharSphereNode() :
m_dwCID(0),
m_pCharacter(NULL),
m_pParent(NULL),
m_pChildren(NULL),
m_pPrevSibling(NULL),
m_pNextSibling(NULL),
m_ppRecomuteFifo(NULL),
m_ppIntegrateFifo(NULL),
m_iChildCount(0),
m_dwFlag(0),
m_fBindingDistance2(0),
m_pLink(NULL),
m_pTree(NULL)
{
}
CCharSphereNode::CCharSphereNode( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink ) :
CSphere(Pos, fRadius),
m_dwCID(0),
m_pCharacter(NULL),
m_pParent(NULL),
m_pChildren(NULL),
m_pPrevSibling(NULL),
m_pNextSibling(NULL),
m_ppRecomuteFifo(NULL),
m_ppIntegrateFifo(NULL),
m_iChildCount(0),
m_dwFlag(0),
m_fBindingDistance2(0),
m_pLink(pLink),
m_pTree(pTree)
{
}
void
CCharSphereNode::Init( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink )
{
m_pTree = pTree;
m_Center = Pos;
SetRadius( fRadius );
m_pLink = pLink;
}
void
CCharSphereNode::AddChild( CCharSphereNode* pChild )
{
CCharSphereNode* pOldChild = m_pChildren;
m_pChildren = pChild;
pChild->SetNextSibling( pOldChild );
pChild->SetPrevSibling( NULL );
pChild->SetParent( this );
if ( pOldChild )
{
pOldChild->SetPrevSibling( pChild );
}
++m_iChildCount;
float fDist = Distance2( pChild );
float fRadius = sqrtf( fDist ) + pChild->GetRadius();
assert( fRadius <= GetRadius() );
}
void
CCharSphereNode::DeleteChild( CCharSphereNode* pChild )
{
assert( m_iChildCount );
assert( m_pChildren );
#ifdef _DEBUG
// pChild 가 현재 노드의 자식인지 체크
CCharSphereNode* pChildren = m_pChildren;
bool bFound = false;
while ( pChildren )
{
if ( pChildren == pChild )
{
assert( !bFound );
bFound = true;
break;
}
pChildren = pChildren->GetNextSibling();
}
assert( bFound );
#endif
// Prev 와 Next Sibling 다시 설정
CCharSphereNode* pPrev = pChild->GetPrevSibling();
if ( pPrev )
{
CCharSphereNode* pNext = pChild->GetNextSibling();
pPrev->SetNextSibling( pNext );
if ( pNext ) pNext->SetPrevSibling( pPrev );
}
else
{
CCharSphereNode* pNext = pChild->GetNextSibling();
m_pChildren = pNext;
if ( m_pChildren ) m_pChildren->SetPrevSibling( NULL );
}
--m_iChildCount;
if ( 0 == m_iChildCount && HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
m_pTree->DeleteSphere( this );
}
}
void
CCharSphereNode::Unlink()
{
// Recompute & Integrate Fifo 에서 제거한다.
if ( m_ppRecomuteFifo )
{
*m_ppRecomuteFifo = NULL;
m_ppRecomuteFifo = NULL;
}
if ( m_ppIntegrateFifo )
{
*m_ppIntegrateFifo = NULL;
m_ppIntegrateFifo = NULL;
}
if ( m_pParent )
{
m_pParent->DeleteChild( this );
}
assert( !m_pChildren );
m_pParent = NULL;
}
void
CCharSphereNode::NewPos( const Position& Pos )
{
// 새로운 좌표 설정
m_Center = Pos;
// 부모가 있고, 재구성되어야 할 플래그가 없다면, 재구성을 할지를 판단한다.
if ( m_pParent && !HasSphereNodeFlag( SNF_INTEGRATE ) )
{
// 부모와의 거리의 제곱값을 계산한다.
float fDistance2 = Distance2( m_pParent );
// 거리가 바인딩 거리를 벗어난 경우
if ( fDistance2 >= m_fBindingDistance2 )
{
// 재계산 플래그가 없다면, 부모의 크기가 재계산되어야 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
// 부모와의 연결을 끊고, 해당 Root 노드에 연결한다.
Unlink();
m_pTree->AddIntegrate( this );
}
}
}
void
CCharSphereNode::NewPos( float fX, float fY, float fZ )
{
NewPos( Position(fX, fY, fZ) );
}
void
CCharSphereNode::NewPosRadius( const Position& Pos, float fRadius )
{
// 새로운 좌표 설정
m_Center = Pos;
// 부모가 있고, 재구성되어야 할 플래그가 없다면, 재구성을 할지를 판단한다.
if ( m_pParent && !HasSphereNodeFlag( SNF_INTEGRATE ) )
{
// 반지름이 변경 되었는지를 체크해서 변경되었다면,
// 반지름을 다시 설정하고, 바인딩 거리를 재계산한다.
if ( fRadius != GetRadius() )
{
SetRadius( fRadius );
ComputeBindingDistance( m_pParent );
}
// 부모와의 거리의 제곱값을 계산한다.
float fDistance2 = Distance2( m_pParent );
// 거리가 바인딩 거리를 벗어난 경우
if ( fDistance2 >= m_fBindingDistance2 )
{
// 재계산 플래그가 없다면, 부모의 크기가 재계산되어야 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
// 부모와의 연결을 끊고, 해당 Root 노드에 연결한다.
Unlink();
m_pTree->AddIntegrate( this );
}
// 자식이 부모의 바인딩 거리안에 있을 경우
else
{
// 부모의 바인딩 거리를 줄일수 있을수도 있기 때문에, 재계산을 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
}
}
}
void
CCharSphereNode::NewPosRadius( float fX, float fY, float fZ, float fRadius )
{
NewPosRadius( Position(fX, fY, fZ), fRadius );
}
float
CCharSphereNode::Distance2( const CCharSphereNode* pNode )
{
return static_cast<float>( m_Center.GetSquaredDistance( pNode->GetCenter() ) );
}
void
CCharSphereNode::ComputeBindingDistance( const CCharSphereNode* pParent )
{
m_fBindingDistance2 = pParent->GetRadius() - GetRadius();
if ( m_fBindingDistance2 <= 0 ) m_fBindingDistance2 = 0;
else m_fBindingDistance2 *= m_fBindingDistance2;
}
bool
CCharSphereNode::Recompute( float fGravy )
{
if ( !m_pChildren ) return true; // 제거 요망
if ( HasSphereNodeFlag( SNF_ROOT_NODE ) ) return false; // Root 는 재계산하지 않는다.
Position sumPos;
int iCount = 0;
// 자식들의 위치를 모두 더해서 평균을 구해서,
// 새로운 중심 좌표를 계산해 낸다.
CCharSphereNode* pChild = m_pChildren;
while ( pChild )
{
sumPos.m_fPointX += pChild->GetCenter().m_fPointX;
sumPos.m_fPointY += pChild->GetCenter().m_fPointY;
sumPos.m_fPointZ += pChild->GetCenter().m_fPointZ;
++iCount;
pChild = pChild->GetNextSibling();
}
if ( iCount )
{
float fRecip = 1.0f / float(iCount);
sumPos.m_fPointX *= fRecip;
sumPos.m_fPointY *= fRecip;
sumPos.m_fPointZ *= fRecip;
Position oldCenter = m_Center;
m_Center = sumPos; // 새로 구해준 중심 좌표로 설정
float fMaxRadius = 0;
// 자식들이 포함되는가 확인한다.
pChild = m_pChildren;
while ( pChild )
{
float fDistance2 = Distance2( pChild );
float fRadius = sqrtf( fDistance2 ) + pChild->GetRadius();
if ( fRadius > fMaxRadius )
{
fMaxRadius = fRadius;
if ( (fMaxRadius + fGravy) >= GetRadius() )
{
m_Center = oldCenter;
ClearShpereNodeFlag( SNF_RECOMPUTE );
return false;
}
}
pChild = pChild->GetNextSibling();
}
// 더 커진 반지름으로 재 설정
fMaxRadius += fGravy;
SetRadius( fMaxRadius );
// 자식들의 바인딩 거리를 재 계산
pChild = m_pChildren;
while ( pChild )
{
pChild->ComputeBindingDistance( this );
pChild = pChild->GetNextSibling();
}
}
ClearShpereNodeFlag( SNF_RECOMPUTE );
return false;
}
CCharSphereTree::CCharSphereTree( int iMaxSphereNode, float fRootSize, float fLeafSize, float fGravy ) :
m_fMaxRootSize(fRootSize), m_fMaxLeafSize(fLeafSize), m_fSuperSphereGravy(fGravy)
{
iMaxSphereNode *= 4;
m_Recompute = new CCharSphereNodeFifo( iMaxSphereNode );
m_Integrate = new CCharSphereNodeFifo( iMaxSphereNode );
// Root Tree 생성
Position rootPos;
m_pRoot = new CCharSphereNode();
m_pRoot->Init( this, rootPos, 65535, NULL );
m_pRoot->SetSphereNodeFlag( eSphereNodeFlag(SNF_ROOT_NODE | SNF_SUPERSPHERE | SNF_ROOT_TREE) );
// Leaf Tree 생성
m_pLeaf = new CCharSphereNode();
m_pLeaf->Init( this, rootPos, 16384, NULL );
m_pLeaf->SetSphereNodeFlag( eSphereNodeFlag(SNF_ROOT_NODE | SNF_SUPERSPHERE | SNF_LEAF_TREE) );
}
CCharSphereTree::~CCharSphereTree()
{
DeleteAllChildSphere( m_pRoot );
DeleteAllChildSphere( m_pLeaf );
SAFE_DELETE( m_pRoot );
SAFE_DELETE( m_pLeaf );
SAFE_DELETE( m_Recompute );
SAFE_DELETE( m_Integrate );
m_CharSphereMap.clear();
}
void
CCharSphereTree::Process()
{
// 재계산 되어야 할 목록에 있는 노드를 재계산한다.
// 만약 leaf node 가 부모를 벗어났다면, 부모가 재 구성되거나, 자식이 없다면, 제거되어야 한다.
if ( m_Recompute->GetCount() > 0 )
{
bool bKill;
CCharSphereNode* pTempNode = NULL;
int iCount = m_Recompute->GetCount();
for (int i=0; i<iCount; ++i)
{
pTempNode = m_Recompute->Pop();
if ( !pTempNode ) continue;
pTempNode->SetRecomputeFifo( NULL );
bKill = pTempNode->Recompute( m_fSuperSphereGravy );
if ( bKill )
{
DeleteSphere( pTempNode );
}
}
}
// 재구성 되어야 할 목록에 있는 노드를 재구성한다.
if ( m_Integrate->GetCount() > 0 )
{
CCharSphereNode* pTempNode = NULL;
int iCount = m_Integrate->GetCount();
for (int i=0; i<iCount; ++i)
{
pTempNode = m_Integrate->Pop();
if ( !pTempNode ) continue;
pTempNode->SetIntegrateFifo( NULL );
if ( pTempNode->HasSphereNodeFlag( SNF_ROOT_TREE ) )
Integrate( pTempNode, m_pRoot, m_fMaxRootSize ); // integrate this one single dude against the root node.
else
Integrate( pTempNode, m_pLeaf, m_fMaxLeafSize ); // integrate this one single dude against the root node.
}
}
}
bool
CCharSphereTree::AddCharacter( unsigned long dwCID, CCharacter* pCharacter, const Position& Pos )
{
CCharSphereNode* pNode = AddSphere( Pos, SphereConst::CHAR_RADIUS, NULL );
if ( pNode )
{
pNode->SetCID( dwCID );
pNode->SetCharacter( pCharacter );
pNode->SetSphereNodeFlag( SNF_ACTOR_NODE );
return m_CharSphereMap.insert( std::make_pair(dwCID, pNode) ).second;
}
return false;
}
bool
CCharSphereTree::DeleteCharacter( unsigned long dwCID )
{
CharSphereMap::iterator itr = m_CharSphereMap.find( dwCID );
if ( itr != m_CharSphereMap.end() )
{
CCharSphereNode* pNode = itr->second;
if ( pNode )
{
DeleteSphere( pNode );
}
m_CharSphereMap.erase( itr );
return true;
}
return false;
}
void
CCharSphereTree::MoveTo( unsigned long dwCID, const Position& NewPos )
{
CharSphereMap::iterator itr = m_CharSphereMap.find( dwCID );
if ( itr != m_CharSphereMap.end() )
{
CCharSphereNode* pNode = itr->second;
if ( pNode && pNode->GetCenter() != NewPos )
{
pNode->NewPos( NewPos );
}
}
}
CCharSphereNode*
CCharSphereTree::AddSphere( const Position& Pos, float fRadius, void* pLink, unsigned long dwFlag )
{
// 새로운 노드를 만든다.
CCharSphereNode* pNode = new CCharSphereNode();
assert( pNode );
if ( pNode )
{
// Root 와 Leaf 중에 한쪽의 멤버로 붙여주고, 재 구성 리스트에 넣는다.
if ( dwFlag & SNF_ROOT_TREE )
{
pNode->Init( this, Pos, fRadius, pLink );
pNode->SetSphereNodeFlag( SNF_ROOT_TREE );
AddIntegrate( pNode );
}
else
{
pNode->Init( this, Pos, fRadius, pLink );
pNode->SetSphereNodeFlag( SNF_LEAF_TREE );
AddIntegrate( pNode );
}
}
return pNode;
}
void
CCharSphereTree::DeleteSphere( CCharSphereNode* pNode )
{
// Root node 는 사용자가 지워준다.
if ( pNode->HasSphereNodeFlag( SNF_ROOT_NODE ) ) return;
// Leaf node tree 의 supersphere 는 링크된 root node tree 의 node 를 지워준다.
if ( pNode->HasSphereNodeFlag( SNF_SUPERSPHERE ) && pNode->HasSphereNodeFlag( SNF_LEAF_TREE) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNode->GetLink();
DeleteSphere( pLink );
}
pNode->Unlink();
SAFE_DELETE( pNode );
}
void
CCharSphereTree::DeleteAllChildSphere( CCharSphereNode* pRootNode )
{
CCharSphereNode* pTempNode = NULL;
CCharSphereNode* pChild = pRootNode->GetChildren();
while ( pChild )
{
if ( pChild->GetChildCount() )
{
DeleteAllChildSphere( pChild );
}
pTempNode = pChild;
pChild = pChild->GetNextSibling();
SAFE_DELETE( pTempNode );
}
}
void
CCharSphereTree::AddRecompute( CCharSphereNode* pNode )
{
if ( !pNode->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
if ( pNode->GetChildCount() )
{
pNode->SetSphereNodeFlag( SNF_RECOMPUTE );
CCharSphereNode** ppFifo = m_Recompute->Push( pNode );
pNode->SetRecomputeFifo( ppFifo );
}
else
{
DeleteSphere( pNode );
}
}
}
void
CCharSphereTree::AddIntegrate( CCharSphereNode* pNode )
{
if ( pNode->HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
m_pRoot->AddChild( pNode );
}
else
{
m_pLeaf->AddChild( pNode );
}
pNode->SetSphereNodeFlag( SNF_INTEGRATE );
CCharSphereNode** ppFifo = m_Integrate->Push( pNode );
pNode->SetIntegrateFifo( ppFifo );
}
void
CCharSphereTree::Integrate( CCharSphereNode* pNode, CCharSphereNode* pSuperSphere, float fNodeSize )
{
// 재구성 단계 첫번째로 pNode 와 가장 가까이 있는 SuperSphere 를 찾는다.
CCharSphereNode* pSearch = pSuperSphere->GetChildren();
CCharSphereNode* pNearestNode1st = NULL; // pNode 를 완전히 둘러쌀수 있는 SuperSphere
CCharSphereNode* pNearestNode2nd = NULL; // pNode 를 추가하기 위해 약간 크기를 증가해야 하는 SuperSphere
float fNearDist1st2 = 1e9;
float fNearDist2nd2 = 1e9;
while ( pSearch )
{
if ( !pSearch->HasSphereNodeFlag( SNF_ROOT_NODE ) &&
pSearch->HasSphereNodeFlag( SNF_SUPERSPHERE ) &&
pSearch->GetChildCount() )
{
float fDistance2 = pNode->Distance2( pSearch );
if ( pNearestNode1st )
{
// 찾아 놓은 완전히 포함하는 SuperSphere 보다 더 가까운 녀석을 찾은 경우
if ( fDistance2 < fNearDist1st2 )
{
float fDist = sqrtf( fDistance2 ) + pNode->GetRadius();
if ( fDist <= pSearch->GetRadius() )
{
pNearestNode1st = pSearch;
fNearDist1st2 = fDistance2;
}
}
}
else
{
float fDist = sqrtf( fDistance2 ) + pNode->GetRadius() - pSearch->GetRadius();
if ( fDist < fNearDist2nd2 )
{
if ( fDist < 0 )
{
// 완전히 포함하는 SuperSphere 를 찾은 경우
pNearestNode1st = pSearch;
fNearDist1st2 = fDistance2;
}
else
{
// 완전히 포함하지는 못하지만, 반지름을 키워서 포함할 수 있는
// 가능성이 있는 SuperSphere 를 찾은 경우
pNearestNode2nd = pSearch;
fNearDist2nd2 = fDist;
}
}
}
}
pSearch = pSearch->GetNextSibling();
}
// pNode 를 완전히 포함할 수 있는 SuperSphere 를 찾은 경우
if ( pNearestNode1st )
{
// pNode 를 pNearestNode1st 의 자식으로 붙여주면 된다.
pNode->Unlink();
pNearestNode1st->AddChild( pNode );
pNearestNode1st->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pNearestNode1st );
// 만약 leaf node 의 supersphere 라면, root tree 의 link node 또한 좌표와 크기를 갱신해준다.
if ( pNearestNode1st->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNearestNode1st->GetLink();
pLink->NewPosRadius( pNearestNode1st->GetCenter(), pNearestNode1st->GetRadius() );
}
}
else
{
bool bCreateNewSphere = true;
// 크기를 약간 키우면 pNode 를 포함할 수 있을만한 SuperSphere 를 찾은 경우
if ( pNearestNode2nd )
{
float fNewSize = fNearDist2nd2 + pNearestNode2nd->GetRadius() + m_fSuperSphereGravy;
if ( fNewSize <= fNodeSize )
{
pNode->Unlink();
pNearestNode2nd->SetRadius( fNewSize );
pNearestNode2nd->AddChild( pNode );
pNearestNode2nd->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pNearestNode2nd );
if ( pNearestNode2nd->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNearestNode2nd->GetLink();
pLink->NewPosRadius( pNearestNode2nd->GetCenter(), pNearestNode2nd->GetRadius() );
}
bCreateNewSphere = false;
}
}
// 새로운 SuperSphere 를 만들어야 하는 경우
if ( bCreateNewSphere )
{
assert( pSuperSphere->HasSphereNodeFlag( SNF_ROOT_NODE ) );
pNode->Unlink();
CCharSphereNode* pParent = new CCharSphereNode();
assert( pParent );
pParent->Init( this, pNode->GetCenter(), pNode->GetRadius() + m_fSuperSphereGravy, NULL );
if ( pSuperSphere->HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
pParent->SetSphereNodeFlag( eSphereNodeFlag(SNF_SUPERSPHERE | SNF_ROOT_TREE) );
}
else
{
pParent->SetSphereNodeFlag( eSphereNodeFlag(SNF_SUPERSPHERE | SNF_LEAF_TREE) );
}
pParent->AddChild( pNode );
pSuperSphere->AddChild( pParent );
pParent->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pParent );
if ( pParent->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
// root node 에 link 를 생성해야 한다.
CCharSphereNode* pLink = AddSphere( pParent->GetCenter(), pParent->GetRadius(), pParent, SNF_ROOT_TREE );
pParent->SetLink( pLink );
}
}
}
pNode->ClearShpereNodeFlag( SNF_INTEGRATE );
}
void
CCharSphereNode::RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack )
{
float fDist = static_cast<float>( m_Center.GetDistance( centerPos ) );
if ( (fDist - fDistance) > GetRadius() ) return;
if ( HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
CCharSphereNode* pNode = m_pChildren;
while ( pNode )
{
pNode->RangeTest( centerPos, fDistance, pCallBack );
pNode = pNode->GetNextSibling();
}
}
else
{
if ( HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) GetLink();
if ( pLink )
{
pLink->RangeTest( centerPos, fDistance, pCallBack ) ;
}
}
else
{
pCallBack->RangeTestCallBack( centerPos, fDistance, this );
}
}
}
void
CCharSphereTree::RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack )
{
m_pRoot->RangeTest( centerPos, fDistance, pCallBack );
}
void
CCharSphereNode::SendToRange( const Position& centerPos, float fDistance, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In )
{
float fDist = static_cast<float>( m_Center.GetDistance( centerPos ) );
if ( (fDist - fDistance) > GetRadius() ) return;
if ( HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
CCharSphereNode* pNode = m_pChildren;
while ( pNode )
{
pNode->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In );
pNode = pNode->GetNextSibling();
}
}
else
{
if ( HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) GetLink();
if ( pLink )
{
pLink->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In ) ;
}
}
else
{
// Packet 전송
if (NULL != m_pCharacter)
{
CGameClientDispatch* lpDispatcher = m_pCharacter->GetDispatcher();
if (NULL != lpDispatcher)
{
lpDispatcher->GetSendStream().PutBuffer( szPacket, dwPacketSize, cCMD_In );
}
}
}
}
}
void
CCharSphereTree::SendToRange( const Position& centerPos, float fDistance, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In )
{
m_pRoot->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In );
}

View File

@@ -0,0 +1,273 @@
#ifndef __CHARACTER_SPHERE_TREE_H__
#define __CHARACTER_SPHERE_TREE_H__
#include "Sphere.h"
#include "CharSphereTreeConstants.h"
using namespace SphereConst;
#include <map>
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if ((p)) { delete (p); (p) = NULL; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if ((p)) { delete [] (p); (p) = NULL; } }
#endif
//======================================================================================
struct Position;
class CCharacter;
class CSphere;
class CCharSphereNode;
class CCharSphereTree;
class CCharSphereNodeFifo;
class ICharSphereTreeCallBack;
//======================================================================================
class ICharSphereTreeCallBack
{
public:
virtual void RangeTestCallBack( const Position& centerPos, float fDistance, CCharSphereNode* pNode )
{
}
private:
};
//======================================================================================
class CCharSphereNode : public CSphere
{
public:
CCharSphereNode();
CCharSphereNode( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink );
void Init( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink );
// ===================================================================
// Flag 함수
void SetSphereNodeFlag( eSphereNodeFlag flag ) { m_dwFlag |= flag; }
void ClearShpereNodeFlag( eSphereNodeFlag flag ) { m_dwFlag &= ~flag; }
bool HasSphereNodeFlag( eSphereNodeFlag flag ) const
{
if ( m_dwFlag & flag ) return true;
return false;
}
// ===================================================================
// 캐릭터 관련 함수
void SetCID( unsigned long dwCID ) { m_dwCID = dwCID; }
unsigned long GetCID() const { return m_dwCID; }
void SetCharacter(CCharacter* pCharacter) { m_pCharacter = pCharacter; }
CCharacter* GetCharacter() const { return m_pCharacter; }
// ===================================================================
// 부모 & 자식 함수
void SetParent( CCharSphereNode* pParent ) { m_pParent = pParent; }
CCharSphereNode* GetParent() const { return m_pParent; }
void AddChild( CCharSphereNode* pChild );
void DeleteChild( CCharSphereNode* pChild );
CCharSphereNode* GetChildren() const { return m_pChildren; }
int GetChildCount() const { return m_iChildCount; }
// ===================================================================
// 같은 레벨의 형제 함수
void SetPrevSibling( CCharSphereNode* pNode ) { m_pPrevSibling = pNode; }
void SetNextSibling( CCharSphereNode* pNode ) { m_pNextSibling = pNode; }
CCharSphereNode* GetPrevSibling() const { return m_pPrevSibling; }
CCharSphereNode* GetNextSibling() const { return m_pNextSibling; }
// ===================================================================
// Fifo 포인터 관리 함수
void SetRecomputeFifo( CCharSphereNode** ppFifo ) { m_ppRecomuteFifo = ppFifo; }
void SetIntegrateFifo( CCharSphereNode** ppFifo ) { m_ppIntegrateFifo = ppFifo; }
// ===================================================================
// 부모와 자식 모두의 연결을 끊는 함수 (실제로는 부모와만 연결을 끊는다.)
void Unlink();
// ===================================================================
// Root tree node 의 Link ( Leaf tree 의 Root 가 아닌 SuperSphere node 가 가진다 )
void SetLink( void* pLink ) { m_pLink = pLink; }
void* GetLink() const { return m_pLink; }
// ===================================================================
// 좌표 변경 함수
void NewPos( const Position& Pos );
void NewPos( float fX, float fY, float fZ );
void NewPosRadius( const Position& Pos, float fRadius );
void NewPosRadius( float fX, float fY, float fZ, float fRadius );
// ===================================================================
// 거리 및 바인딩거리 계산 함수
float Distance2( const CCharSphereNode* pNode );
void ComputeBindingDistance( const CCharSphereNode* pParent );
// ===================================================================
// 크기 재계산 함수 ( true 리턴시에는 제거해야함 )
bool Recompute( float fGravy );
// ===================================================================
// 범위 테스트용 함수
void RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack );
void SendToRange( const Position& centerPos, float fDistance, const char* szPacket,
unsigned long dwPacketSize, unsigned char cCMD_In );
protected:
private:
unsigned long m_dwCID;
CCharacter* m_pCharacter;
CCharSphereNode* m_pParent;
CCharSphereNode* m_pChildren;
CCharSphereNode* m_pPrevSibling;
CCharSphereNode* m_pNextSibling;
CCharSphereNode** m_ppRecomuteFifo; // Recompute Fifo 내의 위치 포인터
CCharSphereNode** m_ppIntegrateFifo; // Integrate Fifo 내의 위치 포인터
int m_iChildCount;
unsigned long m_dwFlag;
float m_fBindingDistance2;
void* m_pLink;
CCharSphereTree* m_pTree;
};
//======================================================================================
class CCharSphereTree
{
public:
CCharSphereTree( int iMaxSphereNode, float fRootSize, float fLeafSize, float fGravy );
virtual ~CCharSphereTree();
typedef std::map< unsigned long, CCharSphereNode* > CharSphereMap;
static CCharSphereTree& GetInstance();
// ===================================================================
// Tree 의 Node 를 재계산하고 재구성하는 처리 함수
void Process();
// ===================================================================
// 캐릭터 관련 함수
bool AddCharacter( unsigned long dwCID, CCharacter* pCharacter, const Position& Pos ) ;
bool DeleteCharacter( unsigned long dwCID );
void MoveTo( unsigned long dwCID, const Position& NewPos );
// ===================================================================
// CCharSphereNode 를 생성(캐릭터 제외)해서 재구성하는 함수 와 삭제하는 함수
CCharSphereNode* AddSphere( const Position& Pos, float fRadius, void* pLink, unsigned long dwFlag = SNF_LEAF_TREE );
void DeleteSphere( CCharSphereNode* pNode );
// ===================================================================
// 재계산, 재구성 리스트에 Node 추가 함수
void AddRecompute( CCharSphereNode* pNode );
void AddIntegrate( CCharSphereNode* pNode );
// ===================================================================
// Tree 를 재구성하는 함수
void Integrate( CCharSphereNode* pNode, CCharSphereNode* pSuperSphere, float fNodeSize );
// ===================================================================
// 범위 테스트용 함수
void RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack );
void SendToRange( const Position& centerPos, float fDistance, const char* szPacket,
unsigned long dwPacketSize, unsigned char cCMD_In );
protected:
// ===================================================================
// 모든 자식 Node 를 메모리 해제하는 함수
void DeleteAllChildSphere( CCharSphereNode* pRootNode );
private:
CCharSphereNode* m_pRoot;
CCharSphereNode* m_pLeaf;
CCharSphereNodeFifo* m_Recompute;
CCharSphereNodeFifo* m_Integrate;
float m_fMaxRootSize;
float m_fMaxLeafSize;
float m_fSuperSphereGravy;
CharSphereMap m_CharSphereMap;
};
//======================================================================================
class CCharSphereNodeFifo
{
public:
CCharSphereNodeFifo( int iSize )
{
m_iCount = 0;
m_iStackPointer = 0;
m_iBottom = 0;
m_iSize = iSize;
m_ppFifo = new CCharSphereNode *[ m_iSize ];
};
~CCharSphereNodeFifo()
{
SAFE_DELETE_ARRAY( m_ppFifo );
};
CCharSphereNode** Push( CCharSphereNode* pNode )
{
m_iCount++;
CCharSphereNode** ret = &m_ppFifo[ m_iStackPointer ];
m_ppFifo[ m_iStackPointer ] = pNode;
++m_iStackPointer;
if ( m_iStackPointer == m_iSize ) m_iStackPointer = 0;
return ret;
};
CCharSphereNode* Pop()
{
while ( m_iStackPointer != m_iBottom )
{
--m_iCount;
CCharSphereNode* ret = m_ppFifo[ m_iBottom ];
++m_iBottom;
if ( m_iBottom == m_iSize ) m_iBottom = 0;
if ( ret ) return ret;
}
return NULL;
}
bool Flush( CCharSphereNode* pNode )
{
if ( m_iStackPointer == m_iBottom ) return false;
int i = m_iBottom;
while ( i != m_iStackPointer )
{
if ( m_ppFifo[i] == pNode )
{
m_ppFifo[i] = NULL;
return true;
}
i++;
if ( i == m_iSize ) i = 0;
}
return false;
};
int GetCount(void) const { return m_iCount; }
private:
int m_iCount;
int m_iStackPointer;
int m_iBottom;
int m_iSize;
CCharSphereNode** m_ppFifo;
};
#endif __CHARACTER_SPHERE_TREE_H__

View File

@@ -0,0 +1,34 @@
#ifndef __SPHERE_TREE_CONST_H__
#define __SPHERE_TREE_CONST_H__
namespace SphereConst
{
enum eSphereNodeFlag
{
SNF_ROOT_NODE = (1<<0), // this is the root node
SNF_SUPERSPHERE = (1<<1), // this is a supersphere, allocated and deleted by us
SNF_ACTOR_NODE = (1<<2), // this is real actor node
SNF_ROOT_TREE = (1<<3), // member of the root tree
SNF_LEAF_TREE = (1<<4), // member of the leaf node tree
SNF_RECOMPUTE = (1<<5), // needs recomputed bounding sphere
SNF_INTEGRATE = (1<<6) // needs to be reintegrated into tree
};
enum eViewState
{
VS_INSIDE, // completely inside the frustum.
VS_PARTIAL, // partially inside and partially outside the frustum.
VS_OUTSIDE // completely outside the frustum
};
enum eConst
{
CHAR_RADIUS = 2, // 캐릭터 반경 2m
MAX_SPHERE_NODE = 2000, // MAX 2000 명까지
DEFAULT_ROOT_SIZE = 256, // Root node 의 사이즈
DEFAULT_LEAF_SIZE = 64, // Leaf node 의 사이즈
DEFAULT_GRAVY = 15 // 부모원과 자식원의 반지름 차
};
};
#endif __SPHERE_TREE_CONST_H__

View File

@@ -0,0 +1,214 @@
#ifndef __SPHERE_H__
#define __SPHERE_H__
#include <math.h>
#include <Creature/CreatureStructure.h>
//======================================================================================
// 3차원 좌표 구조체
/*
#ifndef EPSILON
#define EPSILON 0.001
#endif
struct sVector3
{
union
{
struct
{
float x;
float y;
float z;
};
float v[3];
};
sVector3()
{
x = 0;
y = 0;
z = 0;
}
sVector3( float fX, float fY, float fZ )
{
x = fX;
y = fY;
z = fZ;
}
float Length() const
{
return sqrtf( x*x + y*y + z*z );
}
float Distance( const sVector3& inPos ) const
{
sVector3 tempVector( inPos.x - x, inPos.y - y, inPos.z - z );
return tempVector.Length();
}
float Distance2( const sVector3& inPos ) const
{
return ( (inPos.x - x) * (inPos.x - x) + (inPos.y - y) * (inPos.y - y) + (inPos.z - z) * (inPos.z - z) );
}
bool operator == ( const sVector3& inPos ) const
{
if ( fabs(x - inPos.x) <= EPSILON && fabs(y - inPos.y) <= EPSILON && fabs(z - inPos.z) <= EPSILON )
{
return true;
}
return false;
}
bool operator != ( const sVector3& inPos ) const
{
if ( fabs(x - inPos.x) > EPSILON || fabs(y - inPos.y) > EPSILON || fabs(z - inPos.z) > EPSILON )
{
return true;
}
return false;
}
};
*/
//======================================================================================
// 기본 구 클래스
class CSphere
{
public:
CSphere();
CSphere( float fX, float fY, float fZ, float fRadius );
CSphere( const Position& inPos, float fRadius );
// ===============================================================
void Set( float fX, float fY, float fZ, float fRadius );
void Set( const Position& inPos, float fRadius );
void SetRadius( float fRadius );
float GetX() const { return m_Center.m_fPointX; }
float GetY() const { return m_Center.m_fPointY; }
float GetZ() const { return m_Center.m_fPointZ; }
const Position& GetCenter() const { return m_Center; }
float GetRadius() const { return m_fRadius; }
float GetRadius2() const { return m_fRadius2; }
// ===============================================================
// 3차원 좌표가 구안에 있는지 검사
// 구의 반지름 + fDistance 안에 있는지 검사를 한다.
bool InSphere( float fX, float fY, float fZ, float fDistance );
bool InSphere( const Position& inPos, float fDistance );
// 2차원 좌표가 구안에 있는지 검사
// 구의 반지름 + fDistance 안에 있는지 검사를 한다.
bool InSphereXY( float fX, float fY, float fDistance );
bool InSphereXY( const Position& inPos, float fDistance );
protected:
Position m_Center; // 구의 중심 좌표
private:
float m_fRadius; // 구의 반지름
float m_fRadius2; // 구의 반지름 제곲 값
};
inline
CSphere::CSphere() :
m_fRadius(0), m_fRadius2(0)
{
}
inline
CSphere::CSphere( float fX, float fY, float fZ, float fRadius ) :
m_fRadius(fRadius), m_fRadius2(fRadius * fRadius)
{
}
inline
CSphere::CSphere( const Position& inPos, float fRadius ) :
m_Center(inPos), m_fRadius(fRadius), m_fRadius2(fRadius * fRadius)
{
}
inline void
CSphere::Set( float fX, float fY, float fZ, float fRadius )
{
m_Center.m_fPointX = fX;
m_Center.m_fPointY = fY;
m_Center.m_fPointZ = fZ;
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline void
CSphere::Set( const Position& inPos, float fRadius )
{
m_Center = inPos;
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline void
CSphere::SetRadius( float fRadius )
{
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline bool
CSphere::InSphere( float fX, float fY, float fZ, float fDistance )
{
float dx = fX - m_Center.m_fPointX;
float dy = fY - m_Center.m_fPointY;
float dz = fZ - m_Center.m_fPointZ;
float dist = sqrtf( dx*dx + dy*dy + dz*dz );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphere( const Position& inPos, float fDistance )
{
float dx = inPos.m_fPointX - m_Center.m_fPointX;
float dy = inPos.m_fPointY - m_Center.m_fPointY;
float dz = inPos.m_fPointZ - m_Center.m_fPointZ;
float dist = sqrtf( dx*dx + dy*dy + dz*dz );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphereXY( float fX, float fY, float fDistance )
{
float dx = fX - m_Center.m_fPointX;
float dy = fY - m_Center.m_fPointY;
float dist = sqrtf( dx*dx + dy*dy );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphereXY( const Position& inPos, float fDistance )
{
float dx = inPos.m_fPointX - m_Center.m_fPointX;
float dy = inPos.m_fPointY - m_Center.m_fPointY;
float dist = sqrtf( dx*dx + dy*dy );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
#endif __SPHERE_H__

View File

@@ -0,0 +1,81 @@
#include "stdafx.h"
#include "TempCharacter.h"
CTempCharacter::CTempCharacter()
: m_dwUID(0), m_dwCID(0), m_nDataRequestCount(0), m_cGroup(-1), m_cFlag(0)
{
memset(&m_szCharacterName, 0, sizeof(char) * CHAR_INFOST::MAX_NAME_LEN);
memset(&m_CharInfoEX, 0, sizeof(CHAR_INFOEX));
memset(&m_Quest, 0, sizeof(QUEST));
memset(&m_History, 0, sizeof(HISTORY));
memset(&m_Config, 0, sizeof(CONFIG));
memset(&m_StoreInfo, 0, sizeof(STORE_INFO));
}
CTempCharacter::~CTempCharacter()
{
}
CTempCharacterMgr::~CTempCharacterMgr()
{
isMapCharList::iterator pos = m_mapTempChar.begin();
isMapCharList::iterator end = m_mapTempChar.end();
for(;pos != end; ++pos)
{
m_tempCharPool.destroy(pos->second);
}
m_mapTempChar.clear();
}
// CID/Group을 키로 하고, 일치하는 클래스가 있으면 가져온다. 없으면 생성한 후 리턴한다.
CTempCharacter* CTempCharacterMgr::GetCharacter(unsigned long dwBattleCID, unsigned char cGroup)
{
std::pair<isMapCharList::iterator, isMapCharList::iterator>
result = m_mapTempChar.equal_range(dwBattleCID);
CTempCharacter* lpCharacter = 0;
for(; result.first != result.second; ++result.first)
{
lpCharacter = result.first->second;
if(cGroup == lpCharacter->GetGroup())
{
return lpCharacter;
}
}
// 못찾았다. 하나 삽입한다.
lpCharacter = m_tempCharPool.construct();
if(0 != lpCharacter)
{
lpCharacter->SetGroup(cGroup);
m_mapTempChar.insert(result.first, std::make_pair(dwBattleCID, lpCharacter));
}
return lpCharacter;
}
// 캐릭터 로그아웃시 호출한다.
bool CTempCharacterMgr::EraseChar(unsigned long dwBattleCID)
{
std::pair<isMapCharList::iterator, isMapCharList::iterator>
result = m_mapTempChar.equal_range(dwBattleCID);
for(; result.first != result.second;)
{
m_tempCharPool.destroy(result.first->second);
m_mapTempChar.erase(result.first++);
}
return true;
}

View File

@@ -0,0 +1,105 @@
#ifndef _TEMP_CHARACTER_H_
#define _TEMP_CHARACTER_H_
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/object_pool.hpp>
#include <map>
#include <DB/DBdefine.h>
class CTempCharacterMgr;
// TODO : 환전소(배틀그라운드 서버군)용으로 제작된 클래스입니다.
// 운영툴에서 사용하고 있는 CModifyCharacter의 기능 일부를 차용해왔는데...
// 추후 CModifyCharacter가 이 클래스를 상속받아 약간 수정하면 좋을 듯 합니다.
class CTempCharacter
{
public:
enum Const
{
SLOT_INFO_SET = (1 << 0), // 정섭 UID/CID/이름 이 세팅되었는지 여부
CHAR_DATA_SET = (1 << 1), // 캐릭터 데이터가 세팅되었는지 여부
SLOT_INFO_REQ = (1 << 2), // 정섭 UID/CID/이름 을 요청하였음.
CHAR_DATA_REQ = (1 << 3) // 캐릭터 데이터를 요청하였음
};
CTempCharacter();
~CTempCharacter();
unsigned char GetGroup(void) { return m_cGroup; }
void SetGroup(unsigned char cGroup) { m_cGroup = cGroup; }
// 임시 객체이므로 아이디를 마음대로 설정할 수 있다. 다른 곳(상/하위 클래스)에서 사용하면 위험하다.
void SetUID(unsigned long dwUID) { m_dwUID = dwUID; }
void SetCID(unsigned long dwCID) { m_dwCID = dwCID; }
// 저장되는 UID/CID는 정섭 UID, 정섭 CID이다. (배틀로한과는 다르다!)
unsigned long GetUID() const { return m_dwUID; }
unsigned long GetCID() const { return m_dwCID; }
const char* GetCharacterName() const { return m_szCharacterName; }
void SetCharacterName(const char* szName)
{
strncpy(m_szCharacterName, szName, CHAR_INFOST::MAX_NAME_LEN);
m_szCharacterName[CHAR_INFOST::MAX_NAME_LEN] = 0;
}
CHAR_INFOEX& GetCharInfoEx() { return m_CharInfoEX; }
QUEST& GetQuest() { return m_Quest; }
HISTORY& GetHistory() { return m_History; }
CONFIG& GetConfig() { return m_Config; }
STORE_INFO& GetStoreInfo() { return m_StoreInfo; }
bool IsSetData(unsigned char cFlag) const { return 0 != (m_cFlag & cFlag); }
void SetData(unsigned char cFlag) { m_cFlag |= cFlag; }
void ResetData(unsigned char cFlag) { m_cFlag &= ~cFlag; }
void ClearData() { m_cFlag = 0; }
int AddDataRequestCount() { return ++m_nDataRequestCount; }
int ReleaseDataRequestCount() { return --m_nDataRequestCount; }
int GetDataRequestCount() { return m_nDataRequestCount; }
private:
unsigned long m_dwUID;
unsigned long m_dwCID;
int m_nDataRequestCount;
char m_szCharacterName[CHAR_INFOST::MAX_NAME_LEN];
CHAR_INFOEX m_CharInfoEX;
QUEST m_Quest;
HISTORY m_History;
CONFIG m_Config;
STORE_INFO m_StoreInfo;
unsigned char m_cGroup; // 속한 서버군
unsigned char m_cFlag;
};
class CTempCharacterMgr
{
public:
// Key : 배틀로한 캐릭터 CID / Value : 캐릭터 정보
typedef std::multimap<unsigned long, CTempCharacter*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CTempCharacter*> > > isMapCharList;
// CID/Group을 키로 하고, 일치하는 클래스가 있으면 가져온다. 없으면 생성한 후 리턴한다.
CTempCharacter* GetCharacter(unsigned long dwBattleCID, unsigned char cGroup);
// 캐릭터 로그아웃시 호출한다.
bool EraseChar(unsigned long dwBattleCID);
~CTempCharacterMgr();
private:
boost::object_pool<CTempCharacter> m_tempCharPool;
isMapCharList m_mapTempChar;
};
#endif