diff --git a/data/scripts/actions/items/wheel_scrolls.lua b/data/scripts/actions/items/wheel_scrolls.lua index 61aaa6fe..083c6e80 100644 --- a/data/scripts/actions/items/wheel_scrolls.lua +++ b/data/scripts/actions/items/wheel_scrolls.lua @@ -15,13 +15,11 @@ function scroll.onUse(player, item, fromPosition, target, toPosition, isHotkey) end local scrollData = promotionScrolls[item:getId()] - local scrollKV = player:kv():scoped("wheel-of-destiny"):scoped("scrolls") - if scrollKV:get(scrollData.name) then + if not player:wheelUnlockScroll(scrollData.name) then player:sendTextMessage(MESSAGE_LOOK, "You have already deciphered this scroll.") return true end - scrollKV:set(scrollData.name, true) player:sendTextMessage(MESSAGE_LOOK, "You have gained " .. scrollData.points .. " promotion points for the Wheel of Destiny by deciphering the " .. scrollData.itemName .. ".") item:remove(1) return true diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 9d348591..fce25215 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -33,6 +33,8 @@ #include "server/network/message/networkmessage.hpp" #include "server/network/protocol/protocolgame.hpp" +static PlayerWheelGem emptyGem = {}; + std::array m_resistance = { 0 }; const static std::vector wheelGemBasicSlot1Allowed = { @@ -313,12 +315,6 @@ namespace { return 0; } - struct PromotionScroll { - uint16_t itemId; - std::string name; - uint8_t extraPoints; - }; - std::vector WheelOfDestinyPromotionScrolls = { { 43946, "abridged", 3 }, { 43947, "basic", 5 }, @@ -860,36 +856,7 @@ uint16_t PlayerWheel::getUnusedPoints() const { return 0; } - const auto vocationBaseId = m_player.getVocation()->getBaseId(); - const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); - - for (const auto &modPosition : modsBasicPosition) { - const auto pos = static_cast(modPosition); - uint8_t grade = 0; - auto gradeKV = gemsGradeKV(WheelFragmentType_t::Lesser, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = static_cast(gradeKV->get()); - } - - totalPoints += grade == 3 ? 1 : 0; - } - - if (modsSupremeIt != modsSupremePositionByVocation.end()) { - for (const auto &modPosition : modsSupremeIt->second.get()) { - const auto pos = static_cast(modPosition); - uint8_t grade = 0; - auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = gradeKV->get(); - } - - totalPoints += grade == 3 ? 1 : 0; - } - } else { - g_logger().error("[{}] supreme modifications not found for vocation base id: {}", std::source_location::current().function_name(), vocationBaseId); - } + totalPoints += m_modsMaxGrade; for (uint8_t i = WheelSlots_t::SLOT_FIRST; i <= WheelSlots_t::SLOT_LAST; ++i) { totalPoints -= getPointsBySlotType(static_cast(i)); @@ -1007,22 +974,8 @@ bool PlayerWheel::handleBeamMasteryCooldown(const std::shared_ptr &playe } void PlayerWheel::addPromotionScrolls(NetworkMessage &msg) const { - std::vector unlockedScrolls; - - for (const auto &[itemId, name, extraPoints] : WheelOfDestinyPromotionScrolls) { - const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); - if (!scrollKv) { - continue; - } - - const auto scrollOpt = scrollKv->get(name); - if (scrollOpt && scrollOpt->get()) { - unlockedScrolls.emplace_back(itemId); - } - } - - msg.add(unlockedScrolls.size()); - for (const auto &itemId : unlockedScrolls) { + msg.add(m_unlockedScrolls.size()); + for (const auto &[itemId, name, extraPoints] : m_unlockedScrolls) { msg.add(itemId); } } @@ -1036,13 +989,7 @@ std::shared_ptr PlayerWheel::gemsGradeKV(WheelFragmentType_t type, uint8_t p } uint8_t PlayerWheel::getGemGrade(WheelFragmentType_t type, uint8_t pos) const { - uint8_t grade = 0; - const auto gradeKV = gemsGradeKV(type, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = static_cast(gradeKV->get()); - } - return grade; + return type == WheelFragmentType_t::Lesser ? m_basicGrades[pos] : m_supremeGrades[pos]; } std::vector PlayerWheel::getRevealedGems() const { @@ -1057,7 +1004,7 @@ std::vector PlayerWheel::getRevealedGems() const { sortedUnlockedGemGUIDs.emplace_back(uuid); } - std::sort(sortedUnlockedGemGUIDs.begin(), sortedUnlockedGemGUIDs.end(), [](const std::string &a, const std::string &b) { + std::ranges::sort(sortedUnlockedGemGUIDs, [](const std::string &a, const std::string &b) { if (std::ranges::all_of(a, ::isdigit) && std::ranges::all_of(b, ::isdigit)) { return std::stoull(a) < std::stoull(b); } else { @@ -1067,7 +1014,7 @@ std::vector PlayerWheel::getRevealedGems() const { for (const auto &uuid : sortedUnlockedGemGUIDs) { auto gem = PlayerWheelGem::load(gemsKV(), uuid); - if (gem.uuid.empty()) { + if (!gem) { continue; } unlockedGems.emplace_back(gem); @@ -1078,20 +1025,12 @@ std::vector PlayerWheel::getRevealedGems() const { std::vector PlayerWheel::getActiveGems() const { std::vector activeGems; for (const auto &affinity : magic_enum::enum_values()) { - std::string key(magic_enum::enum_name(affinity)); - auto uuidKV = gemsKV()->scoped("active")->get(key); - if (!uuidKV.has_value()) { - continue; - } + const auto &gem = m_activeGems[static_cast(affinity)]; - auto uuid = uuidKV->get(); - if (uuid.empty()) { - continue; - } - auto gem = PlayerWheelGem::load(gemsKV(), uuid); - if (gem.uuid.empty()) { + if (!gem) { continue; } + activeGems.emplace_back(gem); } return activeGems; @@ -1133,7 +1072,7 @@ uint64_t PlayerWheel::getGemRevealCost(WheelGemQuality_t quality) { return static_cast(g_configManager().getNumber(key)); } -void PlayerWheel::revealGem(WheelGemQuality_t quality) const { +void PlayerWheel::revealGem(WheelGemQuality_t quality) { uint16_t gemId = m_player.getVocation()->getWheelGemId(quality); if (gemId == 0) { g_logger().error("[{}] Failed to get gem id for quality {} and vocation {}", __FUNCTION__, fmt::underlying(quality), m_player.getVocation()->getVocName()); @@ -1168,32 +1107,39 @@ void PlayerWheel::revealGem(WheelGemQuality_t quality) const { gem.supremeModifier = supremeModifiers[uniform_random(0, supremeModifiers.size() - 1)]; } g_logger().debug("[{}] {}", __FUNCTION__, gem.toString()); - gem.save(gemsKV()); + m_revealedGems.emplace_back(gem); + + std::ranges::sort(m_revealedGems, [](const auto &gem1, const auto &gem2) { + if (std::ranges::all_of(gem1.uuid, ::isdigit) && std::ranges::all_of(gem2.uuid, ::isdigit)) { + return std::stoull(gem1.uuid) < std::stoull(gem2.uuid); + } else { + return gem1.uuid < gem2.uuid; + } + }); + sendOpenWheelWindow(m_player.getID()); } -PlayerWheelGem PlayerWheel::getGem(uint16_t index) const { - auto gems = getRevealedGems(); - if (gems.size() <= index) { - g_logger().error("[{}] Player {} trying to get gem with index {} but has only {} gems", __FUNCTION__, m_player.getName(), index, gems.size()); - return {}; +PlayerWheelGem &PlayerWheel::getGem(uint16_t index) { + auto revealedGemsSize = m_revealedGems.size(); + if (revealedGemsSize <= index) { + g_logger().error("[{}] Player {} trying to get gem with index {} but has only {} gems", __FUNCTION__, m_player.getName(), index, revealedGemsSize); + return emptyGem; } - return gems[index]; + return m_revealedGems[index]; } -PlayerWheelGem PlayerWheel::getGem(const std::string &uuid) const { - auto gem = PlayerWheelGem::load(gemsKV(), uuid); - if (gem.uuid.empty()) { - g_logger().error("[{}] Failed to load gem with uuid {}", __FUNCTION__, uuid); - return {}; - } - return gem; +PlayerWheelGem &PlayerWheel::getGem(const std::string &uuid) { + auto it = std::ranges::find_if(m_revealedGems, [&uuid](const auto &gem) { + return gem.uuid == uuid; + }); + + return it != m_revealedGems.end() ? *it : emptyGem; } uint16_t PlayerWheel::getGemIndex(const std::string &uuid) const { - const auto gems = getRevealedGems(); - for (uint16_t i = 0; i < gems.size(); ++i) { - if (gems[i].uuid == uuid) { + for (uint16_t i = 0; i < m_revealedGems.size(); ++i) { + if (m_revealedGems[i].uuid == uuid) { return i; } } @@ -1201,16 +1147,17 @@ uint16_t PlayerWheel::getGemIndex(const std::string &uuid) const { return 0xFF; } -void PlayerWheel::destroyGem(uint16_t index) const { - const auto gem = getGem(index); +void PlayerWheel::destroyGem(uint16_t index) { + const auto &gem = getGem(index); + if (!gem) { + return; + } + if (gem.locked) { g_logger().error("[{}] Player {} destroyed locked gem with index {}", std::source_location::current().function_name(), m_player.getName(), index); return; } - const auto &backpack = m_player.getInventoryItem(CONST_SLOT_BACKPACK); - const auto &mainBackpack = backpack ? backpack->getContainer() : nullptr; - uint8_t lesserFragments = 0; uint8_t greaterFragments = 0; @@ -1248,7 +1195,8 @@ void PlayerWheel::destroyGem(uint16_t index) const { g_logger().debug("[{}] Player {} destroyed a gem and received {} greater fragments", std::source_location::current().function_name(), m_player.getName(), greaterFragments); } - gem.remove(gemsKV()); + m_destroyedGems.emplace_back(gem); + m_revealedGems.erase(m_revealedGems.begin() + index); const auto totalLesserFragment = m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT) + m_player.getStashItemCount(ITEM_LESSER_FRAGMENT); const auto totalGreaterFragment = m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT) + m_player.getStashItemCount(ITEM_GREATER_FRAGMENT); @@ -1259,8 +1207,12 @@ void PlayerWheel::destroyGem(uint16_t index) const { sendOpenWheelWindow(m_player.getID()); } -void PlayerWheel::switchGemDomain(uint16_t index) const { - auto gem = getGem(index); +void PlayerWheel::switchGemDomain(uint16_t index) { + auto &gem = getGem(index); + if (!gem) { + return; + } + if (gem.locked) { g_logger().error("[{}] Player {} trying to destroy locked gem with index {}", __FUNCTION__, m_player.getName(), index); return; @@ -1273,20 +1225,21 @@ void PlayerWheel::switchGemDomain(uint16_t index) const { auto gemAffinity = convertWheelGemAffinityToDomain(static_cast(gem.affinity)); gem.affinity = static_cast(gemAffinity); - gem.save(gemsKV()); sendOpenWheelWindow(m_player.getID()); } -void PlayerWheel::toggleGemLock(uint16_t index) const { - auto gem = getGem(index); +void PlayerWheel::toggleGemLock(uint16_t index) { + auto &gem = getGem(index); + if (!gem) { + return; + } gem.locked = !gem.locked; - gem.save(gemsKV()); sendOpenWheelWindow(m_player.getID()); } -void PlayerWheel::setActiveGem(WheelGemAffinity_t affinity, uint16_t index) const { - auto gem = getGem(index); - if (gem.uuid.empty()) { +void PlayerWheel::setActiveGem(WheelGemAffinity_t affinity, uint16_t index) { + auto &gem = getGem(index); + if (!gem) { g_logger().error("[{}] Failed to load gem with index {}", __FUNCTION__, index); return; } @@ -1294,13 +1247,11 @@ void PlayerWheel::setActiveGem(WheelGemAffinity_t affinity, uint16_t index) cons g_logger().error("[{}] Gem with index {} has affinity {} but trying to set it to {}", __FUNCTION__, index, fmt::underlying(gem.affinity), fmt::underlying(affinity)); return; } - const std::string key(magic_enum::enum_name(affinity)); - gemsKV()->scoped("active")->set(key, gem.uuid); + m_activeGems[static_cast(affinity)] = gem; } -void PlayerWheel::removeActiveGem(WheelGemAffinity_t affinity) const { - const std::string key(magic_enum::enum_name(affinity)); - gemsKV()->scoped("active")->remove(key); +void PlayerWheel::removeActiveGem(WheelGemAffinity_t affinity) { + m_activeGems[static_cast(affinity)] = emptyGem; } void PlayerWheel::addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points) { @@ -1374,10 +1325,9 @@ void PlayerWheel::addGems(NetworkMessage &msg) const { msg.add(getGemIndex(gem.uuid)); } - const auto revealedGems = getRevealedGems(); - msg.add(revealedGems.size()); + msg.add(m_revealedGems.size()); uint16_t index = 0; - for (const auto &gem : revealedGems) { + for (const auto &gem : m_revealedGems) { g_logger().debug("[{}] Adding revealed gem: {}", __FUNCTION__, gem.toString()); msg.add(index++); msg.addByte(gem.locked); @@ -1395,16 +1345,11 @@ void PlayerWheel::addGems(NetworkMessage &msg) const { void PlayerWheel::addGradeModifiers(NetworkMessage &msg) const { msg.addByte(0x2E); // Modifiers for all Vocations + for (const auto &modPosition : modsBasicPosition) { const auto pos = static_cast(modPosition); msg.addByte(pos); - uint8_t grade = 0; - auto gradeKV = gemsGradeKV(WheelFragmentType_t::Lesser, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = static_cast(gradeKV->get()); - } - msg.addByte(grade); + msg.addByte(m_basicGrades[pos]); } msg.addByte(0x17); // Modifiers for specific per Vocations @@ -1416,13 +1361,7 @@ void PlayerWheel::addGradeModifiers(NetworkMessage &msg) const { for (const auto &modPosition : modsSupremeIt->second.get()) { const auto pos = static_cast(modPosition); msg.addByte(pos); - uint8_t grade = 0; - auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = gradeKV->get(); - } - msg.addByte(grade); + msg.addByte(m_supremeGrades[pos]); } } else { g_logger().error("[{}] vocation base id: {}", std::source_location::current().function_name(), m_player.getVocation()->getBaseId()); @@ -1435,9 +1374,10 @@ void PlayerWheel::improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos) uint8_t quantity = 0; uint8_t grade = 0; - auto gradeKV = gemsGradeKV(fragmentType, pos)->get("grade"); - if (gradeKV.has_value()) { - grade = gradeKV->get(); + if (fragmentType == WheelFragmentType_t::Lesser) { + grade = m_basicGrades[pos]; + } else { + grade = m_supremeGrades[pos]; } ++grade; @@ -1476,7 +1416,14 @@ void PlayerWheel::improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos) return; } - gemsGradeKV(fragmentType, pos)->set("grade", grade); + if (fragmentType == WheelFragmentType_t::Lesser) { + m_basicGrades[pos] = grade; + } else { + m_supremeGrades[pos] = grade; + } + + m_modsMaxGrade += grade == 3 ? 1 : 0; + loadPlayerBonusData(); sendOpenWheelWindow(m_player.getID()); } @@ -1682,9 +1629,186 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { // Player's bonus data is loaded, initialized, registered, and the function logs loadPlayerBonusData(); + sendOpenWheelWindow(m_player.getID()); + g_logger().debug("Player: {} is saved the all slots info in: {} milliseconds", m_player.getName(), bm_saveSlot.duration()); } +void PlayerWheel::loadActiveGems() { + for (const auto &affinity : magic_enum::enum_values()) { + std::string key(magic_enum::enum_name(affinity)); + auto uuidKV = gemsKV()->scoped("active")->get(key); + if (!uuidKV.has_value()) { + continue; + } + + auto uuid = uuidKV->get(); + if (uuid.empty()) { + continue; + } + + auto it = std::ranges::find_if(m_revealedGems, [&uuid](const auto &gem) { + return gem.uuid == uuid; + }); + + if (it == m_revealedGems.end()) { + continue; + } + + m_activeGems[static_cast(affinity)] = *it; + } +} + +void PlayerWheel::saveActiveGems() const { + for (const auto &affinity : magic_enum::enum_values()) { + const std::string key(magic_enum::enum_name(affinity)); + const auto &gem = m_activeGems[static_cast(affinity)]; + if (gem) { + gemsKV()->scoped("active")->set(key, gem.uuid); + } else { + gemsKV()->scoped("active")->remove(key); + } + } +} + +void PlayerWheel::loadRevealedGems() { + const auto unlockedGemUUIDs = gemsKV()->scoped("revealed")->keys(); + if (unlockedGemUUIDs.empty()) { + return; + } + + std::vector sortedUnlockedGemGUIDs; + for (const auto &uuid : unlockedGemUUIDs) { + sortedUnlockedGemGUIDs.emplace_back(uuid); + } + + std::ranges::sort(sortedUnlockedGemGUIDs, [](const std::string &a, const std::string &b) { + if (std::ranges::all_of(a, ::isdigit) && std::ranges::all_of(b, ::isdigit)) { + return std::stoull(a) < std::stoull(b); + } else { + return a < b; + } + }); + + for (const auto &uuid : sortedUnlockedGemGUIDs) { + auto gem = PlayerWheelGem::load(gemsKV(), uuid); + if (!gem) { + continue; + } + m_revealedGems.emplace_back(gem); + } +} + +void PlayerWheel::saveRevealedGems() const { + for (const auto &gem : m_destroyedGems) { + gem.remove(gemsKV()); + } + + for (const auto &gem : m_revealedGems) { + gem.save(gemsKV()); + } +} + +bool PlayerWheel::scrollAcquired(const std::string &scrollName) { + auto it = std::ranges::find_if(m_unlockedScrolls, [&scrollName](const PromotionScroll &promotionScroll) { + return scrollName == promotionScroll.name; + }); + + return it != m_unlockedScrolls.end(); +} + +bool PlayerWheel::unlockScroll(const std::string &scrollName) { + if (scrollAcquired(scrollName)) { + return false; + } + + auto it = std::ranges::find_if(WheelOfDestinyPromotionScrolls, [&scrollName](const auto &scroll) { + return scroll.name == scrollName; + }); + + if (it != WheelOfDestinyPromotionScrolls.end()) { + m_unlockedScrolls.emplace_back(*it); + return true; + } + + return false; +} + +void PlayerWheel::loadKVScrolls() { + const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); + if (!scrollKv) { + return; + } + + for (const auto &[itemId, name, extraPoints] : WheelOfDestinyPromotionScrolls) { + const auto scrollValue = scrollKv->get(name); + if (scrollValue && scrollValue->get()) { + m_unlockedScrolls.emplace_back(itemId, name, extraPoints); + } + } +} + +void PlayerWheel::saveKVScrolls() const { + const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); + if (!scrollKv) { + return; + } + + for (const auto &[itemId, name, extraPoints] : m_unlockedScrolls) { + scrollKv->set(name, true); + } +} + +void PlayerWheel::loadKVModGrades() { + for (const auto &modPosition : modsBasicPosition) { + const auto pos = static_cast(modPosition); + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Lesser, pos)->get("grade"); + + if (gradeKV.has_value()) { + uint8_t grade = gradeKV->get(); + m_basicGrades[pos] = grade; + m_modsMaxGrade += grade == 3 ? 1 : 0; + } + } + + const auto vocationBaseId = m_player.getVocation()->getBaseId(); + const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); + if (modsSupremeIt != modsSupremePositionByVocation.end()) { + for (const auto &modPosition : modsSupremeIt->second.get()) { + const auto pos = static_cast(modPosition); + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); + + if (gradeKV.has_value()) { + uint8_t grade = gradeKV->get(); + m_supremeGrades[pos] = grade; + m_modsMaxGrade += grade == 3 ? 1 : 0; + } + } + } +} + +void PlayerWheel::saveKVModGrades() const { + for (const auto &modPosition : modsBasicPosition) { + const auto pos = static_cast(modPosition); + uint8_t grade = m_basicGrades[pos]; + if (grade > 0) { + gemsGradeKV(WheelFragmentType_t::Lesser, pos)->set("grade", grade); + } + } + + const auto vocationBaseId = m_player.getVocation()->getBaseId(); + const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); + if (modsSupremeIt != modsSupremePositionByVocation.end()) { + for (const auto &modPosition : modsSupremeIt->second.get()) { + const auto pos = static_cast(modPosition); + uint8_t grade = m_supremeGrades[pos]; + if (grade > 0) { + gemsGradeKV(WheelFragmentType_t::Greater, pos)->set("grade", grade); + } + } + } +} + /* * Functions for load and save player database informations */ @@ -1748,20 +1872,12 @@ uint16_t PlayerWheel::getExtraPoints() const { } uint16_t totalBonus = 0; - for (const auto &[itemId, name, extraPoints] : WheelOfDestinyPromotionScrolls) { + for (const auto &[itemId, name, extraPoints] : m_unlockedScrolls) { if (itemId == 0) { continue; } - const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); - if (!scrollKv) { - continue; - } - - const auto scrollKV = scrollKv->get(name); - if (scrollKV && scrollKV->get()) { - totalBonus += extraPoints; - } + totalBonus += extraPoints; } return totalBonus; @@ -2564,24 +2680,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons } } - const auto vocationBaseId = m_player.getVocation()->getBaseId(); - const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); - - if (modsSupremeIt != modsSupremePositionByVocation.end()) { - for (const auto &modPosition : modsSupremeIt->second.get()) { - const auto pos = static_cast(modPosition); - uint8_t grade = 0; - auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); - - if (gradeKV.has_value()) { - grade = gradeKV->get(); - } - - totalPoints += grade == 3 ? 1 : 0; - } - } else { - g_logger().error("[{}] supreme modifications not found for vocation base id: {}", std::source_location::current().function_name(), vocationBaseId); - } + totalPoints += m_modsMaxGrade; if (totalPoints >= static_cast(WheelStagePointsEnum_t::THREE)) { return WheelStageEnum_t::THREE; @@ -3822,4 +3921,4 @@ PlayerWheelGem PlayerWheelGem::deserialize(const std::string &uuid, const ValueW static_cast(map["basicModifier2"]->get()), static_cast(map["supremeModifier"]->get()) }; -} +} \ No newline at end of file diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index 055715f6..e1960dc0 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -41,16 +41,20 @@ enum skills_t : int8_t; enum Vocation_t : uint16_t; struct PlayerWheelGem { - std::string uuid; - bool locked; - WheelGemAffinity_t affinity; - WheelGemQuality_t quality; - WheelGemBasicModifier_t basicModifier1; - WheelGemBasicModifier_t basicModifier2; - WheelGemSupremeModifier_t supremeModifier; + std::string uuid = {}; + bool locked = false; + WheelGemAffinity_t affinity = {}; + WheelGemQuality_t quality = {}; + WheelGemBasicModifier_t basicModifier1 = {}; + WheelGemBasicModifier_t basicModifier2 = {}; + WheelGemSupremeModifier_t supremeModifier = {}; std::string toString() const; + explicit operator bool() const { + return !uuid.empty(); + } + void save(const std::shared_ptr &kv) const; void remove(const std::shared_ptr &kv) const; @@ -63,10 +67,29 @@ struct PlayerWheelGem { static PlayerWheelGem deserialize(const std::string &uuid, const ValueWrapper &val); }; +struct PromotionScroll { + uint16_t itemId; + std::string name; + uint8_t extraPoints; +}; + class PlayerWheel { public: explicit PlayerWheel(Player &initPlayer); + void loadActiveGems(); + void saveActiveGems() const; + void loadRevealedGems(); + void saveRevealedGems() const; + + bool scrollAcquired(const std::string &scrollName); + bool unlockScroll(const std::string &scrollName); + void loadKVScrolls(); + void saveKVScrolls() const; + + void loadKVModGrades(); + void saveKVModGrades() const; + /* * Functions for load and save player database informations */ @@ -392,15 +415,15 @@ class PlayerWheel { * @return The calculated mitigation value. */ float calculateMitigation() const; - PlayerWheelGem getGem(uint16_t index) const; - PlayerWheelGem getGem(const std::string &uuid) const; + PlayerWheelGem &getGem(uint16_t index); + PlayerWheelGem &getGem(const std::string &uuid); uint16_t getGemIndex(const std::string &uuid) const; - void revealGem(WheelGemQuality_t quality) const; - void destroyGem(uint16_t index) const; - void switchGemDomain(uint16_t index) const; - void toggleGemLock(uint16_t index) const; - void setActiveGem(WheelGemAffinity_t affinity, uint16_t index) const; - void removeActiveGem(WheelGemAffinity_t affinity) const; + void revealGem(WheelGemQuality_t quality); + void destroyGem(uint16_t index); + void switchGemDomain(uint16_t index); + void toggleGemLock(uint16_t index); + void setActiveGem(WheelGemAffinity_t affinity, uint16_t index); + void removeActiveGem(WheelGemAffinity_t affinity); void addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points); void resetRevelationBonus(); void addSpellBonus(const std::string &spellName, const WheelSpells::Bonus &bonus); @@ -429,6 +452,10 @@ class PlayerWheel { PlayerWheelMethodsBonusData m_playerBonusData; std::unique_ptr m_modifierContext; + uint8_t m_modsMaxGrade = {}; + std::array m_basicGrades = { 0 }; + std::array m_supremeGrades = { 0 }; + std::array(WheelStage_t::STAGE_COUNT)> m_stages = { 0 }; std::array(WheelOnThink_t::TOTAL_COUNT)> m_onThink = { 0 }; std::array(WheelStat_t::TOTAL_COUNT)> m_stats = { 0 }; @@ -442,4 +469,10 @@ class PlayerWheel { std::vector m_learnedSpellsSelected; std::unordered_map m_spellsBonuses; std::unordered_set m_beamMasterySpells; -}; + + std::vector m_unlockedScrolls; + + std::array m_activeGems; + std::vector m_revealedGems; + std::vector m_destroyedGems; +}; \ No newline at end of file diff --git a/src/game/game.cpp b/src/game/game.cpp index 45ee9413..38a2cadb 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -9919,8 +9919,8 @@ void Game::playerSaveWheel(uint32_t playerId, NetworkMessage &msg) { return; } - player->wheel()->saveSlotPointsOnPressSaveButton(msg); player->updateUIExhausted(); + player->wheel()->saveSlotPointsOnPressSaveButton(msg); } void Game::playerWheelGemAction(uint32_t playerId, NetworkMessage &msg) { diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index f49829ba..2b52c854 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -129,7 +129,7 @@ bool SaveManager::doSavePlayer(std::shared_ptr player) { } bool SaveManager::savePlayer(std::shared_ptr player) { - if (player->isOnline()) { + if (player->isOnline() && g_game().getGameState() != GAME_STATE_SHUTDOWN) { schedulePlayer(player); return true; } diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 46b11bd4..f1165047 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -997,6 +997,10 @@ void IOLoginDataLoad::loadPlayerInitializeSystem(const std::shared_ptr & // Wheel loading player->wheel()->loadDBPlayerSlotPointsOnLogin(); + player->wheel()->loadRevealedGems(); + player->wheel()->loadActiveGems(); + player->wheel()->loadKVModGrades(); + player->wheel()->loadKVScrolls(); player->wheel()->initializePlayerData(); player->achiev()->loadUnlockedAchievements(); diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 54af4353..8f7691c7 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -274,6 +274,11 @@ bool IOLoginData::savePlayerGuard(const std::shared_ptr &player) { throw DatabaseException("[PlayerWheel::saveDBPlayerSlotPointsOnLogout] - Failed to save player wheel info: " + player->getName()); } + player->wheel()->saveRevealedGems(); + player->wheel()->saveActiveGems(); + player->wheel()->saveKVModGrades(); + player->wheel()->saveKVScrolls(); + if (!IOLoginDataSave::savePlayerStorage(player)) { throw DatabaseException("[IOLoginDataSave::savePlayerStorage] - Failed to save player storage: " + player->getName()); } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index ac7475f4..5ac00417 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -327,6 +327,7 @@ void PlayerFunctions::init(lua_State* L) { Lua::registerMethod(L, "Player", "getWheelSpellAdditionalArea", PlayerFunctions::luaPlayerGetWheelSpellAdditionalArea); Lua::registerMethod(L, "Player", "getWheelSpellAdditionalTarget", PlayerFunctions::luaPlayerGetWheelSpellAdditionalTarget); Lua::registerMethod(L, "Player", "getWheelSpellAdditionalDuration", PlayerFunctions::luaPlayerGetWheelSpellAdditionalDuration); + Lua::registerMethod(L, "Player", "wheelUnlockScroll", PlayerFunctions::luaPlayerWheelUnlockScroll); // Forge Functions Lua::registerMethod(L, "Player", "openForge", PlayerFunctions::luaPlayerOpenForge); @@ -4511,6 +4512,26 @@ int PlayerFunctions::luaPlayerGetWheelSpellAdditionalDuration(lua_State* L) { return 1; } +int PlayerFunctions::luaPlayerWheelUnlockScroll(lua_State* L) { + // player:wheelUnlockScroll(scrollName) + const auto &player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + Lua::pushBoolean(L, false); + return 0; + } + + const auto scrollName = Lua::getString(L, 2); + if (scrollName.empty()) { + Lua::reportErrorFunc("Scroll name is empty"); + Lua::pushBoolean(L, false); + return 0; + } + + lua_pushboolean(L, player->wheel()->unlockScroll(scrollName)); + return 1; +} + int PlayerFunctions::luaPlayerUpdateConcoction(lua_State* L) { // player:updateConcoction(itemid, timeLeft) const auto &player = Lua::getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 02330612..339a36b1 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -307,6 +307,7 @@ class PlayerFunctions { static int luaPlayerGetWheelSpellAdditionalArea(lua_State* L); static int luaPlayerGetWheelSpellAdditionalTarget(lua_State* L); static int luaPlayerGetWheelSpellAdditionalDuration(lua_State* L); + static int luaPlayerWheelUnlockScroll(lua_State* L); static int luaPlayerOpenForge(lua_State* L); static int luaPlayerCloseForge(lua_State* L);