diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 98cd048bd83..d9b6bb01d48 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -400,13 +400,13 @@ end -- Used on cyclopedia store summary local function insertPlayerTransactionSummary(player, offer) - local offerId = offer.id - if offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_SKILL then - offerId = offerId - HIRELING_STORAGE.SKILL - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT then - offerId = offerId - HIRELING_STORAGE.OUTFIT + local id = offer.id + if offer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE then + id = offer.itemtype + elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS then + id = offer.blessid end - player:createTransactionSummary(offer.type, math.max(1, offer.count), offer.itemtype or 0, offerId, offer.blessid or 0) + player:createTransactionSummary(offer.type, math.max(1, offer.count or 1), id) end function parseBuyStoreOffer(playerId, msg) @@ -460,9 +460,7 @@ function parseBuyStoreOffer(playerId, msg) -- Handled errors are thrown to indicate that the purchase has failed; -- Handled errors have a code index and unhandled errors do not local pcallOk, pcallError = pcall(function() - if offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM then - GameStore.processItemPurchase(player, offer.itemtype, offer.count or 1, offer.movable, offer.setOwner) - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE then + if offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM or offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE then GameStore.processItemPurchase(player, offer.itemtype, offer.count or 1, offer.movable, offer.setOwner) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS then GameStore.processInstantRewardAccess(player, offer.count) @@ -476,11 +474,9 @@ function parseBuyStoreOffer(playerId, msg) GameStore.processPremiumPurchase(player, offer.id) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_STACKABLE then GameStore.processStackablePurchase(player, offer.itemtype, offer.count, offer.name, offer.movable, offer.setOwner) - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE then + elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE or offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_BED then GameStore.processHouseRelatedPurchase(player, offer) - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT then - GameStore.processOutfitPurchase(player, offer.sexId, offer.addon) - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then + elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then GameStore.processOutfitPurchase(player, offer.sexId, offer.addon) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then GameStore.processMountPurchase(player, offer.id) @@ -514,8 +510,6 @@ function parseBuyStoreOffer(playerId, msg) GameStore.processHirelingSkillPurchase(player, offer) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT then GameStore.processHirelingOutfitPurchase(player, offer) - elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_BED then - GameStore.processHouseRelatedPurchase(player, offer) else -- This should never happen by our convention, but just in case the guarding condition is messed up... error({ code = 0, message = "This offer is unavailable [2]" }) @@ -533,7 +527,9 @@ function parseBuyStoreOffer(playerId, msg) return queueSendStoreAlertToUser(alertMessage, 500, playerId) end - insertPlayerTransactionSummary(player, offer) + if table.contains({ GameStore.OfferTypes.OFFER_TYPE_HOUSE, GameStore.OfferTypes.OFFER_TYPE_EXPBOOST, GameStore.OfferTypes.OFFER_TYPE_PREYBONUS, GameStore.OfferTypes.OFFER_TYPE_BLESSINGS, GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS, GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS }, offer.type) then + insertPlayerTransactionSummary(player, offer) + end local configure = useOfferConfigure(offer.type) if configure ~= GameStore.ConfigureOffers.SHOW_CONFIGURE then if not player:makeCoinTransaction(offer) then diff --git a/src/creatures/players/cyclopedia/player_cyclopedia.cpp b/src/creatures/players/cyclopedia/player_cyclopedia.cpp index 2f281d797bd..3e05c403f57 100644 --- a/src/creatures/players/cyclopedia/player_cyclopedia.cpp +++ b/src/creatures/players/cyclopedia/player_cyclopedia.cpp @@ -86,8 +86,21 @@ void PlayerCyclopedia::loadDeathHistory() { g_logger().debug("Checking and updating death history of player {} took {} milliseconds.", m_player.getName(), bm_check.duration()); } -void PlayerCyclopedia::updateStoreSummary(uint8_t type, uint16_t count, uint8_t itemType, uint8_t offerId, uint8_t blessId) { - updateAmount(type, count); +void PlayerCyclopedia::updateStoreSummary(uint8_t type, uint16_t amount, uint16_t id) { + switch (type) { + case Summary_t::HOUSE_ITEMS: + case Summary_t::BLESSINGS: + insertValue(type, amount, id); + break; + case Summary_t::ALL_BLESSINGS: + for (int i = 1; i < 8; ++i) { + insertValue(static_cast(Summary_t::BLESSINGS), amount, i); + } + break; + default: + updateAmount(type, amount); + break; + } } uint16_t PlayerCyclopedia::getAmount(uint8_t type) { @@ -116,37 +129,28 @@ void PlayerCyclopedia::insertPvpKillOnHistory(std::string cause, uint32_t timest m_pvpKillsHistory.emplace_back(std::move(cause), timestamp, status); } -std::map PlayerCyclopedia::getBlessings() const { -} -void insertBlessings(uint8_t bless, uint32_t timestamp) { -} - -std::vector PlayerCyclopedia::getHirelingJobs() const { -} -void insertHirelingJobs(uint8_t job, uint32_t timestamp) { -} - -std::vector PlayerCyclopedia::getHirelingOutfits() const { -} -void insertHirelingOutfits(uint8_t outfit, uint32_t timestamp) { +Summary PlayerCyclopedia::getSummary() { + return { getAmount(Summary_t::BOOSTS), getAmount(Summary_t::PREY_CARDS), getAmount(Summary_t::INSTANT_REWARDS), getAmount(Summary_t::HIRELINGS) }; } -std::map PlayerCyclopedia::getHouseItems() const { - auto type = static_cast(Summary_t::HOUSE_ITEMS); - auto kvItem = m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(type)); - - std::map items = {}; - for (const auto &itemId : kvItem->keys()) { - auto kv = kvItem->scoped(itemId)->get("amount"); - auto amount = static_cast(kv ? kv->getNumber() : 0); - items[std::stoull(itemId)] += amount; +std::map PlayerCyclopedia::getResult(uint8_t type) const { + auto kvScope = m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(type)); + std::map result = {}; + for (const auto &id : kvScope->keys()) { + auto kv = kvScope->scoped(id)->get("amount"); + result[std::stoull(id)] = static_cast(kv ? kv->getNumber() : 0); } - - return items; + return result; } -void PlayerCyclopedia::insertHouseItems(uint32_t itemId, uint16_t amount) { - auto type = static_cast(Summary_t::HOUSE_ITEMS); - auto oldAmount = static_cast(m_houseItems[itemId]); - m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(type))->scoped(fmt::format("{}", itemId))->set("amount", oldAmount + amount); +void PlayerCyclopedia::insertValue(uint8_t type, uint16_t amount, uint16_t id) { + auto kv = m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(type)); + auto oldAmount = 0; + auto result = getResult(type); + auto it = result.find(id); + if (it != result.end()) { + oldAmount = it->second; + } + kv->scoped(fmt::format("{}", id))->set("amount", oldAmount + amount); + g_logger().info("type: {}, old: {}, amount: {}, newamount: {}, id: {}", type, oldAmount, amount, oldAmount + amount, id); } diff --git a/src/creatures/players/cyclopedia/player_cyclopedia.hpp b/src/creatures/players/cyclopedia/player_cyclopedia.hpp index 3ed6d3f6de0..63d44730023 100644 --- a/src/creatures/players/cyclopedia/player_cyclopedia.hpp +++ b/src/creatures/players/cyclopedia/player_cyclopedia.hpp @@ -20,11 +20,10 @@ struct Summary { uint16_t m_xpBoosts = 0; uint16_t m_preyWildcards = 0; uint16_t m_instantRewards = 0; - uint16_t m_charmsExpansions = 0; uint16_t m_hirelings = 0; - Summary(uint16_t mXpBoosts, uint16_t mPreyWildcards, uint16_t mInstantRewards, uint16_t mCharmsExpansions, uint16_t mHirelings) : - m_xpBoosts(mXpBoosts), m_preyWildcards(mPreyWildcards), m_instantRewards(mInstantRewards), m_charmsExpansions(mCharmsExpansions), m_hirelings(mHirelings) { } + Summary(uint16_t mXpBoosts, uint16_t mPreyWildcards, uint16_t mInstantRewards, uint16_t mHirelings) : + m_xpBoosts(mXpBoosts), m_preyWildcards(mPreyWildcards), m_instantRewards(mInstantRewards), m_hirelings(mHirelings) { } }; class PlayerCyclopedia { @@ -36,7 +35,7 @@ class PlayerCyclopedia { void loadRecentKills(); void loadDeathHistory(); - void updateStoreSummary(uint8_t type, uint16_t count = 1, uint8_t itemType = 0, uint8_t offerId = 0, uint8_t blessId = 0); + void updateStoreSummary(uint8_t type, uint16_t amount = 1, uint16_t id = 0); uint16_t getAmount(uint8_t type); void updateAmount(uint8_t type, uint16_t toAddPoints); @@ -46,27 +45,13 @@ class PlayerCyclopedia { [[nodiscard]] std::vector getPvpKillsHistory() const; void insertPvpKillOnHistory(std::string cause, uint32_t timestamp, uint8_t status); - const Summary &getSummary() { - return Summary(getAmount(Summary_t::BOOSTS), getAmount(Summary_t::PREY_WILDCARDS), getAmount(Summary_t::INSTANT_REWARDS), getAmount(Summary_t::CHARM_EXPANSIONS), getAmount(Summary_t::HIRELINGS)); - } - [[nodiscard]] std::map getBlessings() const; - void insertBlessings(uint8_t bless, uint32_t timestamp); + Summary getSummary(); - [[nodiscard]] std::vector getHirelingJobs() const; - void insertHirelingJobs(uint8_t job, uint32_t timestamp); - - [[nodiscard]] std::vector getHirelingOutfits() const; - void insertHirelingOutfits(uint8_t outfit, uint32_t timestamp); - - [[nodiscard]] std::map getHouseItems() const; - void insertHouseItems(uint32_t itemId, uint16_t amount); + [[nodiscard]] std::map getResult(uint8_t type) const; + void insertValue(uint8_t type, uint16_t amount = 1, uint16_t id = 0); private: std::vector m_deathHistory; std::vector m_pvpKillsHistory; - std::map m_blessings; - std::vector m_hirelingJobs; - std::vector m_hirelingOutfits; - std::map m_houseItems; Player &m_player; }; diff --git a/src/enums/player_cyclopedia.hpp b/src/enums/player_cyclopedia.hpp index 2526b628bde..a31329160b6 100644 --- a/src/enums/player_cyclopedia.hpp +++ b/src/enums/player_cyclopedia.hpp @@ -38,13 +38,13 @@ enum CyclopediaTitle_t : uint8_t { }; enum Summary_t : uint8_t { - BOOSTS = 1, - PREY_SLOTS, - PREY_WILDCARDS, - INSTANT_REWARDS, - CHARM_EXPANSIONS, - HIRELINGS, - HIRELING_JOBS, - HIRELING_OUTFITS, - HOUSE_ITEMS, + HOUSE_ITEMS = 9, + BOOSTS = 10, + PREY_CARDS = 12, + BLESSINGS = 14, + ALL_BLESSINGS = 17, + INSTANT_REWARDS = 18, + HIRELINGS = 20, + HIRELING_JOBS = 23, + HIRELING_OUTFITS = 24, }; diff --git a/src/game/game.cpp b/src/game/game.cpp index 9980c7dcec1..d1e144f7a48 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -361,15 +361,33 @@ Game::Game() { }; m_summaryCategories = { + { static_cast(Summary_t::HOUSE_ITEMS), "house-items" }, { static_cast(Summary_t::BOOSTS), "xp-boosts" }, - { static_cast(Summary_t::PREY_SLOTS), "prey-slots" }, - { static_cast(Summary_t::PREY_WILDCARDS), "prey-wildcards" }, + { static_cast(Summary_t::PREY_CARDS), "prey-cards" }, + { static_cast(Summary_t::BLESSINGS), "blessings" }, { static_cast(Summary_t::INSTANT_REWARDS), "instant-rewards" }, - { static_cast(Summary_t::CHARM_EXPANSIONS), "charm-expansions" }, { static_cast(Summary_t::HIRELINGS), "hirelings" }, { static_cast(Summary_t::HIRELING_JOBS), "hirelings-jobs" }, { static_cast(Summary_t::HIRELING_OUTFITS), "hireling-outfits" }, - { static_cast(Summary_t::HOUSE_ITEMS), "house-items" }, + }; + + m_hirelingSkills = { + { 1001, "banker" }, + { 1002, "cooker" }, + { 1003, "steward" }, + { 1004, "trader" } + }; + + m_hirelingOutfits = { + { 2001, "banker" }, + { 2002, "cooker" }, + { 2003, "steward" }, + { 2004, "trader" }, + { 2005, "servant" }, + { 2006, "hydra" }, + { 2007, "ferumbras" }, + { 2008, "bonelord" }, + { 2009, "dragon" }, }; } @@ -10632,3 +10650,11 @@ Title Game::getTitleByName(const std::string &name) { } return {}; } + +std::unordered_map Game::getHirelingSkills() { + return m_hirelingSkills; +} + +std::unordered_map Game::getHirelingOutfits() { + return m_hirelingOutfits; +} diff --git a/src/game/game.hpp b/src/game/game.hpp index b35a4dfb61d..6cb13520a06 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -736,6 +736,9 @@ class Game { return m_summaryCategories[type]; } + std::unordered_map getHirelingSkills(); + std::unordered_map getHirelingOutfits(); + private: std::map m_achievements; std::map m_achievementsNameToId; @@ -747,6 +750,8 @@ class Game { std::unordered_map m_highscoreCategoriesNames; std::unordered_map m_summaryCategories; + std::unordered_map m_hirelingSkills; + std::unordered_map m_hirelingOutfits; std::map forgeMonsterEventIds; std::unordered_set fiendishMonsters; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index ff3328c9ebb..934a0df1dba 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -4356,7 +4356,7 @@ int PlayerFunctions::luaPlayerSetCurrentTitle(lua_State* L) { } int PlayerFunctions::luaPlayerCreateTransactionSummary(lua_State* L) { - // player:createTransactionSummary(type, count[, itemType = 0[, offerId = 0[, blessId = 0]]]) + // player:createTransactionSummary(type, amount[, id = 0]) const auto &player = getUserdataShared(L, 1); if (!player) { reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); @@ -4369,12 +4369,11 @@ int PlayerFunctions::luaPlayerCreateTransactionSummary(lua_State* L) { return 1; } - auto count = getNumber(L, 3, 1); - auto itemType = getNumber(L, 4, 0); - auto offerId = getNumber(L, 5, 0); - auto blessId = getNumber(L, 6, 0); + auto amount = getNumber(L, 3, 1); + auto id = getNumber(L, 4, 0); - player->cyclopedia()->updateStoreSummary(type, count, itemType, offerId, blessId); + g_logger().info("type: {}, amount: {}, id: {}", type, amount, id); + player->cyclopedia()->updateStoreSummary(type, amount, id); pushBoolean(L, true); return 1; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 6fdbc421512..5330568ab53 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3942,9 +3942,11 @@ void ProtocolGame::sendCyclopediaCharacterStoreSummary() { // getBlessingsObtained auto blessings = player->getBlessingNames(); msg.addByte(static_cast(blessings.size())); - for (const auto &it : blessings) { - msg.addString(it.second, "ProtocolGame::sendCyclopediaCharacterStoreSummary - blessing.name"); - msg.addByte(static_cast((player->cyclopedia()->getBlessings())[it.first])); + auto amountsByBlessing = player->cyclopedia()->getResult(static_cast(Summary_t::BLESSINGS)); + for (const auto &bless_it : blessings) { + msg.addString(bless_it.second, "ProtocolGame::sendCyclopediaCharacterStoreSummary - blessing.name"); + msg.addByte(static_cast(amountsByBlessing[bless_it.first])); + g_logger().info("bless id: {}, name: {}, found(count): {}", bless_it.first, bless_it.second, amountsByBlessing[bless_it.first]); } uint8_t preySlotsUnlocked = 0; @@ -3959,25 +3961,39 @@ void ProtocolGame::sendCyclopediaCharacterStoreSummary() { preySlotsUnlocked++; } msg.addByte(preySlotsUnlocked); // getPreySlotById + getTaskHuntingSlotById - + msg.addByte(cyclopediaSummary.m_preyWildcards); // getPreyCardsObtained msg.addByte(cyclopediaSummary.m_instantRewards); // getRewardCollectionObtained msg.addByte(player->hasCharmExpansion() ? 0x01 : 0x00); msg.addByte(cyclopediaSummary.m_hirelings); // getHirelingsObtained - auto jobs = player->cyclopedia()->getHirelingJobs(); - msg.addByte(jobs.size()); - for (const auto job_it : jobs) { - msg.addByte(job_it); - } - - auto outfits = player->cyclopedia()->getHirelingOutfits(); - msg.addByte(outfits.size()); - for (const auto outfit_it : outfits) { - msg.addByte(outfit_it); - } + std::vector m_hSkills; + for (const auto &it : g_game().getHirelingSkills()) { + if (player->kv()->scoped("hireling-skills")->get(it.second)) { + m_hSkills.emplace_back(it.first); + g_logger().info("skill id: {}, name: {}", it.first, it.second); + } + } + msg.addByte(m_hSkills.size()); + for (const auto &id : m_hSkills) { + msg.addByte(id); + } + + // std::vector m_hOutfits; + // for (const auto &it : g_game().getHirelingOutfits()) { + // if (player->kv()->scoped("hireling-outfits")->get(it.second)) { + // m_hOutfits.emplace_back(it.first); + // g_logger().info("outfit id: {}, name: {}", it.first, it.second); + // } + // } + // msg.addByte(m_hOutfits.size()); + // for (const auto &id : m_hOutfits) { + // msg.add(id); + // // msg.addByte(id); + // } + msg.addByte(0x00); - auto houseItems = player->cyclopedia()->getHouseItems(); + auto houseItems = player->cyclopedia()->getResult(static_cast(Summary_t::HOUSE_ITEMS)); msg.add(houseItems.size()); for (const auto &hItem_it : houseItems) { const ItemType &it = Item::items[hItem_it.first];