Merge pull request #92 from gsemaj/crocpot

Implement Croc Pot item combining
Fix vehicle type override
This commit is contained in:
CakeLancelot 2020-09-20 13:40:36 -05:00 committed by GitHub
commit 77df7b7160
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 4 deletions

View File

@ -9,6 +9,7 @@
std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData; std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData;
std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables; std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables;
std::map<int32_t, CrocPotEntry> ItemManager::CrocPotTable;
void ItemManager::init() { void ItemManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);

View File

@ -5,11 +5,15 @@
struct Item { struct Item {
bool tradeable, sellable; bool tradeable, sellable;
int buyPrice, sellPrice, stackSize, level; // TODO: implement more as needed int buyPrice, sellPrice, stackSize, level, rarity; // TODO: implement more as needed
}; };
struct VendorListing { struct VendorListing {
int sort, type, iID; int sort, type, iID;
}; };
struct CrocPotEntry {
int multStats, multLooks;
float base, rd0, rd1, rd2, rd3;
};
namespace ItemManager { namespace ItemManager {
enum class SlotType { enum class SlotType {
@ -20,6 +24,7 @@ namespace ItemManager {
// hopefully this is fine since it's never modified after load // hopefully this is fine since it's never modified after load
extern std::map<std::pair<int32_t, int32_t>, Item> ItemData; // <id, type> -> data extern std::map<std::pair<int32_t, int32_t>, Item> ItemData; // <id, type> -> data
extern std::map<int32_t, std::vector<VendorListing>> VendorTables; extern std::map<int32_t, std::vector<VendorListing>> VendorTables;
extern std::map<int32_t, CrocPotEntry> CrocPotTable; // level gap -> entry
void init(); void init();

View File

@ -27,6 +27,7 @@ void NPCManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_SELL, npcVendorSell); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_SELL, npcVendorSell);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY, npcVendorBuyback); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY, npcVendorBuyback);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_BATTERY_BUY, npcVendorBuyBattery); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_BATTERY_BUY, npcVendorBuyBattery);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_COMBINATION, npcCombineItems);
} }
void NPCManager::addNPC(std::vector<Chunk*> viewableChunks, int32_t id) { void NPCManager::addNPC(std::vector<Chunk*> viewableChunks, int32_t id) {
@ -312,6 +313,95 @@ void NPCManager::npcVendorBuyBattery(CNSocket* sock, CNPacketData* data) {
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC)); sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC));
} }
void NPCManager::npcCombineItems(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_COMBINATION))
return; // malformed packet
sP_CL2FE_REQ_PC_ITEM_COMBINATION* req = (sP_CL2FE_REQ_PC_ITEM_COMBINATION*)data->buf;
Player* plr = PlayerManager::getPlayer(sock);
if (req->iCostumeItemSlot < 0 || req->iCostumeItemSlot >= AINVEN_COUNT || req->iStatItemSlot < 0 || req->iStatItemSlot >= AINVEN_COUNT) { // sanity check 1
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_COMBINATION_FAIL, failResp);
failResp.iCostumeItemSlot = req->iCostumeItemSlot;
failResp.iStatItemSlot = req->iStatItemSlot;
failResp.iErrorCode = 0;
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_ITEM_COMBINATION_FAIL, sizeof(sP_FE2CL_REP_PC_ITEM_COMBINATION_FAIL));
std::cout << "[WARN] Inventory slot(s) out of range (" << req->iStatItemSlot << " and " << req->iCostumeItemSlot << ")" << std::endl;
return;
}
sItemBase* itemStats = &plr->Inven[req->iStatItemSlot];
sItemBase* itemLooks = &plr->Inven[req->iCostumeItemSlot];
Item* itemStatsDat = ItemManager::getItemData(itemStats->iID, itemStats->iType);
Item* itemLooksDat = ItemManager::getItemData(itemLooks->iID, itemLooks->iType);
if (itemStatsDat == nullptr || itemLooksDat == nullptr
|| ItemManager::CrocPotTable.find(abs(itemStatsDat->level - itemLooksDat->level)) == ItemManager::CrocPotTable.end()) // sanity check 2
{
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_COMBINATION_FAIL, failResp);
failResp.iCostumeItemSlot = req->iCostumeItemSlot;
failResp.iStatItemSlot = req->iStatItemSlot;
failResp.iErrorCode = 0;
std::cout << "[WARN] Either item ids or croc pot value set not found" << std::endl;
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_ITEM_COMBINATION_FAIL, sizeof(sP_FE2CL_REP_PC_ITEM_COMBINATION_FAIL));
return;
}
CrocPotEntry* recipe = &ItemManager::CrocPotTable[abs(itemStatsDat->level - itemLooksDat->level)];
int cost = itemStatsDat->buyPrice * recipe->multStats + itemLooksDat->buyPrice * recipe->multLooks;
float successChance = recipe->base / 100.0f; // base success chance
// rarity gap multiplier
switch(abs(itemStatsDat->rarity - itemLooksDat->rarity))
{
case 0:
successChance *= recipe->rd0;
break;
case 1:
successChance *= recipe->rd1;
break;
case 2:
successChance *= recipe->rd2;
break;
case 3:
successChance *= recipe->rd3;
break;
default:
break;
}
float rolled = (rand() * 1.0f / RAND_MAX) * 100.0f; // success chance out of 100
//std::cout << rolled << " vs " << successChance << std::endl;
plr->money -= cost;
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_COMBINATION_SUCC, resp);
if (rolled < successChance) {
// success
resp.iSuccessFlag = 1;
// modify the looks item with the new stats and set the appearance through iOpt
itemLooks->iOpt = (int32_t)itemLooks->iID << 16;
itemLooks->iID = itemStats->iID;
// delete stats item
itemStats->iID = 0;
itemStats->iOpt = 0;
itemStats->iTimeLimit = 0;
itemStats->iType = 0;
}
else {
// failure; don't do anything?
resp.iSuccessFlag = 0;
}
resp.iCandy = plr->money;
resp.iNewItemSlot = req->iCostumeItemSlot;
resp.iStatItemSlot = req->iStatItemSlot;
resp.sNewItem = *itemLooks;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_ITEM_COMBINATION_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_COMBINATION_SUCC));
}
void NPCManager::npcBarkHandler(CNSocket* sock, CNPacketData* data) {} // stubbed for now void NPCManager::npcBarkHandler(CNSocket* sock, CNPacketData* data) {} // stubbed for now
void NPCManager::npcUnsummonHandler(CNSocket* sock, CNPacketData* data) { void NPCManager::npcUnsummonHandler(CNSocket* sock, CNPacketData* data) {

View File

@ -34,4 +34,5 @@ namespace NPCManager {
void npcVendorSell(CNSocket* sock, CNPacketData* data); void npcVendorSell(CNSocket* sock, CNPacketData* data);
void npcVendorBuyback(CNSocket* sock, CNPacketData* data); void npcVendorBuyback(CNSocket* sock, CNPacketData* data);
void npcVendorBuyBattery(CNSocket* sock, CNPacketData* data); void npcVendorBuyBattery(CNSocket* sock, CNPacketData* data);
void npcCombineItems(CNSocket* sock, CNPacketData* data);
} }

View File

@ -110,11 +110,12 @@ void TableData::init() {
nlohmann::json itemSet; nlohmann::json itemSet;
for (int i = 0; i < 12; i++) { for (int i = 0; i < 12; i++) {
itemSet = xdtData[setNames[i]]["m_pItemData"]; itemSet = xdtData[setNames[i]]["m_pItemData"];
for (nlohmann::json::iterator _item = itemSet.begin(); _item != itemSet.end(); _item++) { for (nlohmann::json::iterator _item = itemSet.begin(); _item != itemSet.end(); _item++) {
auto item = _item.value(); auto item = _item.value();
ItemManager::ItemData[std::pair<int32_t, int32_t>(item["m_iItemNumber"], i == 11 ? 9 : (i == 10 ? 7 : (int)item["m_iEquipLoc"]))] int typeOverride = getItemType(i); // used for special cases where iEquipLoc doesn't indicate item type
= { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"] }; ItemManager::ItemData[std::pair<int32_t, int32_t>(item["m_iItemNumber"], typeOverride != -1 ? typeOverride : (int)item["m_iEquipLoc"])]
= { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"],
i > 9 ? 1 : (int)item["m_iRarity"] };
} }
} }
@ -130,6 +131,17 @@ void TableData::init() {
} }
std::cout << "[INFO] Loaded " << ItemManager::VendorTables.size() << " vendor tables" << std::endl; std::cout << "[INFO] Loaded " << ItemManager::VendorTables.size() << " vendor tables" << std::endl;
// load crocpot entries
nlohmann::json crocs = xdtData["m_pCombiningTable"]["m_pCombiningData"];
for (nlohmann::json::iterator croc = crocs.begin(); croc != crocs.end(); croc++) {
CrocPotEntry crocEntry = { croc.value()["m_iStatConstant"], croc.value()["m_iLookConstant"], croc.value()["m_fLevelGapStandard"],
croc.value()["m_fSameGrade"], croc.value()["m_fOneGrade"], croc.value()["m_fTwoGrade"], croc.value()["m_fThreeGrade"] };
ItemManager::CrocPotTable[croc.value()["m_iLevelGap"]] = crocEntry;
}
std::cout << "[INFO] Loaded " << ItemManager::CrocPotTable.size() << " croc pot value sets" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed xdt.json file! Reason:" << err.what() << std::endl; std::cerr << "[WARN] Malformed xdt.json file! Reason:" << err.what() << std::endl;
@ -179,3 +191,25 @@ void TableData::cleanup() {
for (auto& pair : NPCManager::NPCs) for (auto& pair : NPCManager::NPCs)
delete pair.second; delete pair.second;
} }
/*
* Some item categories either don't possess iEquipLoc or use a different value for item type.
*/
int TableData::getItemType(int itemSet) {
int overriden;
switch (itemSet)
{
case 11: // Chest items don't have iEquipLoc and are type 9.
overriden = 9;
break;
case 10: // General items don't have iEquipLoc and are type 7.
overriden = 7;
break;
case 9: // Vehicles have iEquipLoc 8, but type 10.
overriden = 10;
break;
default:
overriden = -1;
}
return overriden;
}

View File

@ -4,4 +4,6 @@
namespace TableData { namespace TableData {
void init(); void init();
void cleanup(); void cleanup();
int getItemType(int);
} }