Files
Client/Server/RylServerProject/RylGameLibrary/Creature/Character/Character.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

485 lines
12 KiB
C++

#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);
}
}
}