diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 7ddfb6d..9d421e2 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -22,6 +22,7 @@ void NPCManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE, npcVendorTable); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_BUY, npcVendorBuy); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_SELL, npcVendorSell); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY, npcVendorRestore); } void NPCManager::npcVendorBuy(CNSocket* sock, CNPacketData* data) { @@ -32,7 +33,7 @@ void NPCManager::npcVendorBuy(CNSocket* sock, CNPacketData* data) { Player *plr = PlayerManager::getPlayer(sock); if (!ItemManager::isItemRegistered(req->Item.iID, req->Item.iType)) { - std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found" << std::endl; + std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found (buy)" << std::endl; // NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side. INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, failResp); failResp.iErrorCode = 0; @@ -84,9 +85,20 @@ void NPCManager::npcVendorSell(CNSocket* sock, CNPacketData* data) { sItemBase* item = &plr->Inven[req->iInvenSlotNum]; + if (!ItemManager::isItemRegistered(item->iID, item->iType) || !ItemManager::getItemData(item->iID, item->iType).sellable) { // sanity + sellable check + std::cout << "[WARN] Item id " << item->iID << " with type " << item->iType << " not found (sell)" << std::endl; + INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, failResp); + failResp.iErrorCode = 0; + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL)); + return; + } + + sItemBase original; + memcpy(&original, item, sizeof(sItemBase)); + INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC, resp); - int sellValue = ItemManager::getItemData(item->iID, item->iType).sellPrice * req->iItemCnt; // TODO: lookup item price + int sellValue = ItemManager::getItemData(item->iID, item->iType).sellPrice * req->iItemCnt; // increment taros plr->money = plr->money + sellValue; @@ -100,11 +112,55 @@ void NPCManager::npcVendorSell(CNSocket* sock, CNPacketData* data) { // response parameters resp.iInvenSlotNum = req->iInvenSlotNum; resp.iCandy = plr->money; - resp.Item = *item; + resp.Item = original; // the item that gets sent to buyback + resp.ItemStay = *item; // the void item that gets put in the slot sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC)); } +void NPCManager::npcVendorRestore(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY)) + return; // malformed packet + + sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY* req = (sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + if (!ItemManager::isItemRegistered(req->Item.iID, req->Item.iType)) { + std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found (rebuy)" << std::endl; + // NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side. + INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, failResp); + failResp.iErrorCode = 0; + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL)); + return; + } + + int itemCost = ItemManager::getItemData(req->Item.iID, req->Item.iType).sellPrice; // sell price is used on rebuy + int slot = ItemManager::findFreeSlot(plr); + if (itemCost > plr->money || slot == -1) { + // NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side. + INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, failResp); + failResp.iErrorCode = 0; + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL)); + return; + } + + if (slot != req->iInvenSlotNum) { + // possible item stacking? + std::cout << "[WARN] Client and server disagree on bought item slot (" << req->iInvenSlotNum << " vs " << slot << ")" << std::endl; + } + + plr->money = plr->money - itemCost; + plr->Inven[slot] = req->Item; + + INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, resp); + // response parameters + resp.iCandy = plr->money; + resp.iInvenSlotNum = slot; + resp.Item = req->Item; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC)); +} + void NPCManager::npcVendorTable(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE)) return; // malformed packet diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index b65874d..c77d92b 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -28,6 +28,7 @@ namespace NPCManager { void npcVendorTable(CNSocket* sock, CNPacketData* data); void npcVendorBuy(CNSocket* sock, CNPacketData* data); void npcVendorSell(CNSocket* sock, CNPacketData* data); + void npcVendorRestore(CNSocket* sock, CNPacketData* data); void updatePlayerNPCS(CNSocket* sock, PlayerView& plr); } diff --git a/src/TableData.cpp b/src/TableData.cpp index 587e2f4..f3748c9 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -122,15 +122,15 @@ void TableData::init() { std::cout << "[INFO] Loaded mission-related data" << std::endl; // load all item data. i'm sorry. it has to be done - const char* setNames[10] = { "m_pBackItemTable", "m_pFaceItemTable", "m_pGlassItemTable", "m_pHatItemTable", + const char* setNames[12] = { "m_pBackItemTable", "m_pFaceItemTable", "m_pGlassItemTable", "m_pHatItemTable", "m_pHeadItemTable", "m_pPantsItemTable", "m_pShirtsItemTable", "m_pShoesItemTable", "m_pWeaponItemTable", - "m_pVehicleItemTable"}; + "m_pVehicleItemTable", "m_pGeneralItemTable", "m_pChestItemTable" }; nlohmann::json itemSet; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 12; i++) { itemSet = xdtData[setNames[i]]["m_pItemData"]; for (nlohmann::json::iterator item = itemSet.begin(); item != itemSet.end(); item++) - ItemManager::ItemData[std::pair(item.value()["m_iItemNumber"], item.value()["m_iEquipLoc"])] - = { item.value()["m_iTradeAble"] == 1, item.value()["m_iSellAble"] == 1, item.value()["m_iItemPrice"], item.value()["m_iItemSellPrice"], item.value()["m_iStackNumber"], item.value()["m_iMinReqLev"] }; + ItemManager::ItemData[std::pair(item.value()["m_iItemNumber"], i == 11 ? 9 : (i == 10 ? 7 : item.value()["m_iEquipLoc"]))] + = { item.value()["m_iTradeAble"] == 1, item.value()["m_iSellAble"] == 1, item.value()["m_iItemPrice"], item.value()["m_iItemSellPrice"], item.value()["m_iStackNumber"], i > 9 ? 0 : item.value()["m_iMinReqLev"] }; } std::cout << "[INFO] Loaded " << ItemManager::ItemData.size() << " items" << std::endl;