Files
Client/Server/RylServerProject/RylGameLibrary/Creature/Character/CharacterTradeItem.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

338 lines
10 KiB
C++

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