From 3ce8cf212991a9fbb1e24f83333a4c9bc882d879 Mon Sep 17 00:00:00 2001 From: dongresource Date: Sun, 18 Oct 2020 01:19:05 +0200 Subject: [PATCH] Cleaned up item drop logic. * Replaced bad exception logic with C-style error returns in ItemManager * Removed unnecessary instances of objects being passed by value * Fixed whitespace problems * Added new config options to the default config.ini * Updated tabledata reference --- config.ini | 11 ++++ src/ItemManager.cpp | 150 +++++++++++++++++++++++--------------------- src/ItemManager.hpp | 15 ++--- src/MobManager.cpp | 102 ++++++++++++++---------------- src/MobManager.hpp | 4 +- src/TableData.cpp | 20 +++--- src/main.cpp | 8 ++- tdata | 2 +- 8 files changed, 167 insertions(+), 145 deletions(-) diff --git a/config.ini b/config.ini index 44442a4..6b6c650 100644 --- a/config.ini +++ b/config.ini @@ -38,6 +38,8 @@ xdtdata=tdata/xdt.json mobdata=tdata/mobs.json # path json pathdata=tdata/paths.json +# drop json +dropdata=tdata/drops.json # gruntwork output (this is what you submit) gruntwork=tdata/gruntwork.json @@ -49,6 +51,15 @@ gruntwork=tdata/gruntwork.json # any number higher than 50 will disable commands accountlevel=1 +# should mobs drop event crates? +# 0 = no event +# 1 = Knishmas +# 2 = Halloween +# 3 = Easter +eventmode=0 +# percent chance of an event crate dropping each kill +eventcratechance=10 + # spawn coordinates (Z is height) # the supplied defaults are at Sector V (future) spawnx=632032 diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index 1c1e36b..d005f79 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -16,14 +16,11 @@ std::map ItemManager::Crates; /// pair Itemset, Rarity -> vector of pointers (map iterators) to records in ItemData std::map, std::vector, Item>::iterator>> ItemManager::CrateItems; -// buffer for error messages -char buffer[255]; - void ItemManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_ITEM, itemGMGiveHandler); - //this one is for gumballs + // this one is for gumballs REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_USE, itemUseHandler); // Bank REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler); @@ -809,11 +806,15 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) { return; // ignore the malformed packet sP_CL2FE_REQ_ITEM_CHEST_OPEN *chest = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf; - Player *player = PlayerManager::getPlayer(sock); + Player *plr = PlayerManager::getPlayer(sock); - if (player == nullptr) + // chest opening acknowledgement packet + INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); + resp.iSlotNum = chest->iSlotNum; + + if (plr == nullptr) return; - + // item giving packet const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); assert(resplen < CN_PACKET_BUFFER_SIZE - 8); @@ -827,75 +828,82 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) { memset(respbuf, 0, resplen); // maintain stats - reward->m_iCandy = player->money; - reward->m_iFusionMatter = player->fusionmatter; + reward->m_iCandy = plr->money; + reward->m_iFusionMatter = plr->fusionmatter; reward->iFatigue = 100; // prevents warning message reward->iFatigue_Level = 1; reward->iItemCnt = 1; // remember to update resplen if you change this - // item reward - if (chest->ChestItem.iType != 9) - std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl; - else - item->sItem = openCrate(chest->ChestItem.iID, player->PCStyle.iGender); - item->iSlotNum = chest->iSlotNum; item->eIL = chest->eIL; + // item reward + if (chest->ChestItem.iType != 9) { + std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl; + return; + } + + int itemSetId = -1, rarity = -1, ret = -1; + bool failing = false; + + // find the crate + if (Crates.find(chest->ChestItem.iID) == Crates.end()) { + std::cout << "[WARN] Crate " << chest->ChestItem.iID << " not found!" << std::endl; + failing = true; + } + Crate& crate = Crates[chest->ChestItem.iID]; + + if (!failing) + itemSetId = getItemSetId(crate, chest->ChestItem.iID); + if (itemSetId == -1) + failing = true; + + if (!failing) + rarity = getRarity(crate, itemSetId); + if (rarity == -1) + failing = true; + + if (!failing) + ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); + if (ret == -1) + failing = true; + + // if we failed to open a crate, at least give the player a gumball (suggested by Jade) + if (failing) { + item->sItem.iType = 7; + item->sItem.iID = 119 + (rand() % 3); + item->sItem.iOpt = 1; + } + // update player - player->Inven[chest->iSlotNum] = item->sItem; + plr->Inven[chest->iSlotNum] = item->sItem; // transmit item sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); - // chest opening acknowledgement packet - INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); - - resp.iSlotNum = chest->iSlotNum; - + // transmit chest opening acknowledgement packet std::cout << "opening chest..." << std::endl; sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC)); } -sItemBase ItemManager::openCrate(int crateId, int playerGender) { - sItemBase reward = {}; - try { - Crate crate = getCrate(crateId); - int itemSetId = getItemSetId(crate, crateId); - int rarity = getRarity(crate, itemSetId); - reward = getCrateItem(itemSetId, rarity, playerGender); - } - catch (const std::exception& err) { - std::cerr << "[WARN] An error has occured while trying to open a crate. Error description: \n" << err.what() << std::endl; - // if we failed to open a crate, at least give the player a gumball (suggested by Jade) - reward.iType = 7; - reward.iID = 119 + (rand() % 3); - reward.iOpt = 1; - } - - return reward; -} - -Crate ItemManager::getCrate(int crateId) { - if (Crates.find(crateId) == Crates.end()) - throwError(sprintf(buffer, "Crate %d was not found!", crateId)); - return Crates[crateId]; -} - -int ItemManager::getItemSetId(Crate crate, int crateId) { +int ItemManager::getItemSetId(Crate& crate, int crateId) { int itemSetsCount = crate.itemSets.size(); - if (itemSetsCount == 0) - throwError(sprintf(buffer, "Crate %d has no item sets assigned?!", crateId)); + if (itemSetsCount == 0) { + std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl; + return -1; + } // if crate points to multiple itemSets, choose a random one int itemSetIndex = rand() % itemSetsCount; return crate.itemSets[itemSetIndex]; } -int ItemManager::getRarity(Crate crate , int itemSetId) { +int ItemManager::getRarity(Crate& crate, int itemSetId) { // find rarity ratio - if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) - throwError(sprintf(buffer, "Rarity Ratio %d not found!", crate.rarityRatioId)); + if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) { + std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl; + return -1; + } std::vector rarityRatio = RarityRatios[crate.rarityRatioId]; @@ -905,7 +913,7 @@ int ItemManager::getRarity(Crate crate , int itemSetId) { * it is simpler to do here than to fix individually in the file */ - // remember that rarities start from 1 ! + // remember that rarities start from 1! for (int i = 0; i < rarityRatio.size(); i++){ if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end()) rarityRatio[i] = 0; @@ -915,9 +923,10 @@ int ItemManager::getRarity(Crate crate , int itemSetId) { for (int value : rarityRatio) total += value; - // if we didn't find any items, throw exception - if (total == 0) - throwError(sprintf(buffer, "Item Set %d has no items assigned?!", itemSetId)); + if (total == 0) { + std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl; + return -1; + } // now return a random rarity number int randomNum = rand() % total; @@ -925,17 +934,19 @@ int ItemManager::getRarity(Crate crate , int itemSetId) { int sum = 0; do { sum += rarityRatio[rarity]; - rarity++; + rarity++; } while (sum <= randomNum); - + return rarity; } -sItemBase ItemManager::getCrateItem(int itemSetId, int rarity, int playerGender) { +int ItemManager::getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) { std::pair key = std::make_pair(itemSetId, rarity); - if (CrateItems.find(key) == CrateItems.end()) - throwError(sprintf(buffer, "Item Set ID %d Rarity %d items have not been found", itemSetId, rarity)); + if (CrateItems.find(key) == CrateItems.end()) { + std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " items have not been found" << std::endl; + return -1; + } // only take into account items that have correct gender std::vector, Item>::iterator> items; @@ -948,20 +959,19 @@ sItemBase ItemManager::getCrateItem(int itemSetId, int rarity, int playerGender) items.push_back(crateitem); } - if (items.size() == 0) - throwError(sprintf(buffer, "Gender inequality! Set ID %d Rarity %d contains only %s items?!", itemSetId, rarity, playerGender==2 ? "boys" : "girls")); + if (items.size() == 0) { + std::cout << "[WARN] Gender inequality! Set ID " << itemSetId << " Rarity " << rarity << " contains only " + << (playerGender == 2 ? "boys" : "girls") << " items?!" << std::endl; + return -1; + } + auto item = items[rand() % items.size()]; - sItemBase result = {}; + result.iID = item->first.first; result.iType = item->first.second; result.iOpt = 1; - return result; -} - -// argument is here only so we can call sprintf in brackets -void ItemManager::throwError(int ignore) { - throw buffer; + return 0; } // TODO: use this in cleaned up ItemManager diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index 9d9a8cd..678a060 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -29,11 +29,11 @@ namespace ItemManager { extern std::map, Item> ItemData; // -> data extern std::map> VendorTables; extern std::map CrocPotTable; // level gap -> entry - extern std::map> RarityRatios; + extern std::map> RarityRatios; extern std::map Crates; // pair -> vector of pointers (map iterators) to records in ItemData (it looks a lot scarier than it is) - extern std::map, - std::vector, Item>::iterator>> CrateItems; + extern std::map, + std::vector, Item>::iterator>> CrateItems; void init(); @@ -56,12 +56,9 @@ namespace ItemManager { void chestOpenHandler(CNSocket* sock, CNPacketData* data); // crate opening logic with all helper functions - sItemBase openCrate(int crateId, int playerGender); - Crate getCrate(int crateId); - int getItemSetId(Crate crate, int crateId); - int getRarity(Crate crate, int itemSetId); - sItemBase getCrateItem(int itemSetId, int rarity, int playerGender); - void throwError(int ignore); + int getItemSetId(Crate& crate, int crateId); + int getRarity(Crate& crate, int itemSetId); + int getCrateItem(sItemBase& reward, int itemSetId, int rarity, int playerGender); int findFreeSlot(Player *plr); Item* getItemData(int32_t id, int32_t type); diff --git a/src/MobManager.cpp b/src/MobManager.cpp index af54a4c..979f7b4 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -11,9 +11,10 @@ #include std::map MobManager::Mobs; +std::queue MobManager::RemovalQueue; + std::map MobManager::MobDropChances; std::map MobManager::MobDrops; -std::queue MobManager::RemovalQueue; bool MobManager::simulateMobs; @@ -168,7 +169,7 @@ void MobManager::giveReward(CNSocket *sock, Mob* mob) { return; } // find correct mob drop - MobDrop drop = MobDrops[mob->dropType]; + MobDrop& drop = MobDrops[mob->dropType]; plr->money += drop.taros; // formula for scaling FM with player/mob level difference @@ -197,29 +198,27 @@ void MobManager::giveReward(CNSocket *sock, Mob* mob) { reward->iFatigue_Level = 1; reward->iItemCnt = 1; // remember to update resplen if you change this - int slot = ItemManager::findFreeSlot(plr); - - bool awardDrop = false; - MobDropChance chance; - // sanity check - if (MobDropChances.find(drop.dropChanceType) == MobDropChances.end()) - std::cout << "[WARN] Unknown Drop Chance Type: " << drop.dropChanceType << std::endl; - else { - chance = MobDropChances[drop.dropChanceType]; - awardDrop = (rand() % 1000 < chance.dropChance); - } + bool awardDrop = false; + MobDropChance *chance = nullptr; + // sanity check + if (MobDropChances.find(drop.dropChanceType) == MobDropChances.end()) { + std::cout << "[WARN] Unknown Drop Chance Type: " << drop.dropChanceType << std::endl; + return; // this also prevents holiday crate drops, but oh well + } else { + chance = &MobDropChances[drop.dropChanceType]; + awardDrop = (rand() % 1000 < chance->dropChance); + } // no drop if (slot == -1 || !awardDrop) { - // no room for an item, but you still get FM and taros reward->iItemCnt = 0; sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM)); } else { // item reward - item->sItem = getReward(&drop, &chance); + getReward(&item->sItem, &drop, chance); item->iSlotNum = slot; item->eIL = 1; // Inventory Location. 1 means player inventory. @@ -227,7 +226,6 @@ void MobManager::giveReward(CNSocket *sock, Mob* mob) { plr->Inven[slot] = item->sItem; sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); - } // event crates @@ -235,10 +233,9 @@ void MobManager::giveReward(CNSocket *sock, Mob* mob) { giveEventReward(sock, plr); } -sItemBase MobManager::getReward(MobDrop* drop, MobDropChance* chance) { - sItemBase reward = {}; - reward.iType = 9; - reward.iOpt = 1; +void MobManager::getReward(sItemBase *reward, MobDrop* drop, MobDropChance* chance) { + reward->iType = 9; + reward->iOpt = 1; int total = 0; for (int ratio : chance->cratesRatio) @@ -249,29 +246,26 @@ sItemBase MobManager::getReward(MobDrop* drop, MobDropChance* chance) { int i = 0; int sum = 0; do { - reward.iID = drop->crateIDs[i]; + reward->iID = drop->crateIDs[i]; sum += chance->cratesRatio[i]; i++; } while (sum<=randomNum); - return reward; } void MobManager::giveEventReward(CNSocket* sock, Player* player) { - // random drop chance if (rand() % 100 > settings::EVENTCRATECHANCE) return; - // no slot = no award + // no slot = no reward int slot = ItemManager::findFreeSlot(player); if (slot == -1) return; const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - // we know it's only one trailing struct, so we can skip full validation - uint8_t respbuf[resplen]; // not a variable length array, don't worry + uint8_t respbuf[resplen]; sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); @@ -289,20 +283,20 @@ void MobManager::giveEventReward(CNSocket* sock, Player* player) { // which crate to drop int crateId; - switch (settings::EVENTMODE) + switch (settings::EVENTMODE) { // knishmas - case 1: crateId = 1187; break; + case 1: crateId = 1187; break; // halloween case 2: crateId = 1181; break; // spring case 3: crateId = 1126; break; // what - default: + default: std::cout << "[WARN] Unknown event Id " << settings::EVENTMODE << std::endl; return; } - + item->sItem.iType = 9; item->sItem.iID = crateId; item->sItem.iOpt = 1; @@ -315,33 +309,33 @@ void MobManager::giveEventReward(CNSocket* sock, Player* player) { } int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) { - // cannot kill mobs multiple times; cannot harm retreating mobs - if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) { - return 0; // no damage - } + // cannot kill mobs multiple times; cannot harm retreating mobs + if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) { + return 0; // no damage + } - if (mob->state == MobState::ROAMING) { - assert(mob->target == nullptr); - mob->target = sock; - mob->state = MobState::COMBAT; - mob->nextMovement = getTime(); - mob->nextAttack = 0; + if (mob->state == MobState::ROAMING) { + assert(mob->target == nullptr); + mob->target = sock; + mob->state = MobState::COMBAT; + mob->nextMovement = getTime(); + mob->nextAttack = 0; - mob->roamX = mob->appearanceData.iX; - mob->roamY = mob->appearanceData.iY; - mob->roamZ = mob->appearanceData.iZ; - } + mob->roamX = mob->appearanceData.iX; + mob->roamY = mob->appearanceData.iY; + mob->roamZ = mob->appearanceData.iZ; + } - mob->appearanceData.iHP -= damage; + mob->appearanceData.iHP -= damage; - // wake up sleeping monster - // TODO: remove client-side bit somehow - mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ; + // wake up sleeping monster + // TODO: remove client-side bit somehow + mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ; - if (mob->appearanceData.iHP <= 0) - killMob(mob->target, mob); + if (mob->appearanceData.iHP <= 0) + killMob(mob->target, mob); - return damage; + return damage; } void MobManager::killMob(CNSocket *sock, Mob *mob) { @@ -349,14 +343,14 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) { mob->target = nullptr; mob->appearanceData.iConditionBitFlag = 0; mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step? - + // check for the edge case where hitting the mob did not aggro it if (sock != nullptr) { Player* plr = PlayerManager::getPlayer(sock); - + if (plr == nullptr) return; - + if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { giveReward(sock, mob); MissionManager::mobKilled(sock, mob->appearanceData.iNPCType); diff --git a/src/MobManager.hpp b/src/MobManager.hpp index a6e79eb..3049484 100644 --- a/src/MobManager.hpp +++ b/src/MobManager.hpp @@ -43,7 +43,7 @@ struct Mob : public BaseNPC { time_t nextAttack = 0; int roamX, roamY, roamZ; - //drop + // drop int dropType; // temporary; until we're sure what's what @@ -129,7 +129,7 @@ namespace MobManager { int hitMob(CNSocket *sock, Mob *mob, int damage); void killMob(CNSocket *sock, Mob *mob); void giveReward(CNSocket *sock, Mob *mob); - sItemBase getReward(MobDrop *drop, MobDropChance *chance); + void getReward(sItemBase *reward, MobDrop *drop, MobDropChance *chance); void giveEventReward(CNSocket* sock, Player* player); std::pair lerp(int, int, int, int, int); diff --git a/src/TableData.cpp b/src/TableData.cpp index bb4cbc8..33f3796 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -336,7 +336,7 @@ void TableData::loadDrops() { for (nlohmann::json::iterator _crates = drop["CrateIDs"].begin(); _crates != drop["CrateIDs"].end(); _crates++) { toAdd.crateIDs.push_back((int)_crates.value()); } - + toAdd.dropChanceType = (int)drop["DropChance"]; // Check if DropChance exists if (MobManager::MobDropChances.find(toAdd.dropChanceType) == MobManager::MobDropChances.end()) @@ -354,7 +354,9 @@ void TableData::loadDrops() { toAdd.boosts = (int)drop["Boosts"]; MobManager::MobDrops[(int)drop["DropType"]] = toAdd; } + std::cout << "[INFO] Loaded " << MobManager::MobDrops.size() << " Mob Drop Types"<< std::endl; + // Rarity Ratios nlohmann::json rarities = dropData["RarityRatios"]; for (nlohmann::json::iterator _rarity = rarities.begin(); _rarity != rarities.end(); _rarity++) { @@ -365,6 +367,7 @@ void TableData::loadDrops() { } ItemManager::RarityRatios[(int)rarity["Type"]] = toAdd; } + // Crates nlohmann::json crates = dropData["Crates"]; for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) { @@ -376,6 +379,7 @@ void TableData::loadDrops() { } ItemManager::Crates[(int)crate["Id"]] = toAdd; } + // Crate Items nlohmann::json items = dropData["Items"]; int itemCount = 0; @@ -383,28 +387,30 @@ void TableData::loadDrops() { auto item = _item.value(); std::pair itemSetkey = std::make_pair((int)item["ItemSet"], (int)item["Rarity"]); std::pair itemDataKey = std::make_pair((int)item["Id"], (int)item["Type"]); + if (ItemManager::ItemData.find(itemDataKey) == ItemManager::ItemData.end()) { char buff[255]; sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]); throw TableException(std::string(buff)); } + std::map, Item>::iterator toAdd = ItemManager::ItemData.find(itemDataKey); + // if item collection doesn't exist, start a new one if (ItemManager::CrateItems.find(itemSetkey) == ItemManager::CrateItems.end()) { std::vector, Item>::iterator> vector; vector.push_back(toAdd); ItemManager::CrateItems[itemSetkey] = vector; - } - // else add a new element to existing collection - else + } else // else add a new element to existing collection ItemManager::CrateItems[itemSetkey].push_back(toAdd); + itemCount++; } - std::cout << "[INFO] Loaded " << ItemManager::Crates.size() << " Crates containing " - <