diff --git a/data-canary/scripts/actions/objects/imbuement_shrine.lua b/data-canary/scripts/actions/objects/imbuement_shrine.lua index 71e3776f2f0..11d75150cf8 100644 --- a/data-canary/scripts/actions/objects/imbuement_shrine.lua +++ b/data-canary/scripts/actions/objects/imbuement_shrine.lua @@ -13,5 +13,7 @@ function imbuement.onUse(player, item, fromPosition, target, toPosition, isHotke return true end +imbuement:position({ x = 1943, y = 1340, z = 7 }, 25061) + imbuement:id(25060, 25061, 25103, 25104, 25202, 25174, 25175, 25182, 25183) imbuement:register() diff --git a/data-otservbr-global/lib/quests/soul_war.lua b/data-otservbr-global/lib/quests/soul_war.lua index f5b2d75f5ba..a6058bc4715 100644 --- a/data-otservbr-global/lib/quests/soul_war.lua +++ b/data-otservbr-global/lib/quests/soul_war.lua @@ -1455,7 +1455,7 @@ function Player:getSoulWarZoneMonster() return zoneMonsterName end -function Player:isInBoatSpot() +function Creature:isInBoatSpot() -- Get ebb and flow zone and check if player is in zone local zone = SoulWarQuest.ebbAndFlow.getZone() local tile = Tile(self:getPosition()) @@ -1464,11 +1464,11 @@ function Player:isInBoatSpot() groundId = tile:getGround():getId() end if zone and zone:isInZone(self:getPosition()) and tile and groundId == SoulWarQuest.ebbAndFlow.boatId then - logger.trace("Player {} is in boat spot", self:getName()) + logger.trace("Creature {} is in boat spot", self:getName()) return true end - logger.trace("Player {} is not in boat spot", self:getName()) + logger.trace("Creature {} is not in boat spot", self:getName()) return false end diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua index ce5c6737e02..bfc9000ca0f 100644 --- a/data-otservbr-global/npc/hireling.lua +++ b/data-otservbr-global/npc/hireling.lua @@ -420,11 +420,13 @@ function createHirelingType(HirelingName) local TOPIC_FOOD = { SKILL_CHOOSE = 1301, + SKILL_SURPRISE = 1302, } local GREETINGS = { BANK = "Alright! What can I do for you and your bank business, |PLAYERNAME|?", - FOOD = "Hmm, yes! A variety of fine food awaits! However, a small expense of 15000 gold is expected to make these delicious masterpieces happen. Shall I?", + FOOD = [[Hmm, yes! A variety of fine food awaits! However, a small expense of 15000 gold is expected to make these delicious masterpieces happen. + For 90000 gold I will also serve you a specific dish. Just tell me what it shall be: a {specific} meal or a little {surprise}.]], STASH = "Of course, here is your stash! Well-maintained and neatly sorted for your convenience!", } @@ -513,7 +515,7 @@ function createHirelingType(HirelingName) return message end - local function deliverFood(npc, creature, food_id) + local function deliverFood(npc, creature, food_id, cost) local playerId = creature:getId() local player = Player(creature) local itType = ItemType(food_id) @@ -523,8 +525,8 @@ function createHirelingType(HirelingName) npcHandler:say("Sorry, but you don't have enough capacity.", npc, creature) elseif not inbox or #inboxItems >= inbox:getMaxCapacity() then player:getPosition():sendMagicEffect(CONST_ME_POFF) - npcHandler:say("Sorry, you don't have enough room on your inbox", npc, creature) - elseif not player:removeMoneyBank(15000) then + npcHandler:say("Sorry, you don't have enough room on your inbox.", npc, creature) + elseif not player:removeMoneyBank(cost) then npcHandler:say("Sorry, you don't have enough money.", npc, creature) else local message = getDeliveredMessageByFoodId(food_id) @@ -534,38 +536,66 @@ function createHirelingType(HirelingName) npcHandler:setTopic(playerId, TOPIC.SERVICES) end - local function cookFood(npc, creature) + local function cookFood(npc, creature, specificRequest) local playerId = creature:getId() - local random = math.random(6) - if random == 6 then - -- ask for preferred skill + if specificRequest then + npcHandler:say("Very well. You may choose one of the following: {chilli con carniphila}, {svargrond salmon filet}, {carrion casserole}, {consecrated beef}, {roasted wyvern wings}, {carrot pie}, {tropical marinated tiger}, or {delicatessen salad}.", npc, creature) npcHandler:setTopic(playerId, TOPIC_FOOD.SKILL_CHOOSE) - npcHandler:say("Yay! I have the ingredients to make a skill boost dish. Would you rather like to boost your {magic}, {melee}, {shielding} or {distance} skill?", npc, creature) - else -- deliver the random generated index - deliverFood(npc, creature, HIRELING_FOODS_IDS[random]) + else + npcHandler:say("Alright, let me astonish you. Shall I?", npc, creature) + deliverFood(npc, creature, HIRELING_FOODS_IDS[math.random(#HIRELING_FOODS_IDS)], 15000) end end local function handleFoodActions(npc, creature, message) local playerId = creature:getId() + if npcHandler:getTopic(playerId) == TOPIC.FOOD then - if MsgContains(message, "yes") then - cookFood(npc, creature) + if MsgContains(message, "specific") then + npcHandler:setTopic(playerId, TOPIC_FOOD.SPECIFIC) + npcHandler:say("Which specific meal would you like? Choices are: {chilli con carniphila}, {svargrond salmon filet}, {carrion casserole}, {consecrated beef}, {roasted wyvern wings}, {carrot pie}, {tropical marinated tiger}, or {delicatessen salad}.", npc, creature) + elseif MsgContains(message, "surprise") then + local random = math.random(6) + if random == 6 then + npcHandler:setTopic(playerId, TOPIC_FOOD.SKILL_CHOOSE) + npcHandler:say("Yay! I have the ingredients to make a skill boost dish. Would you rather like to boost your {magic}, {melee}, {shielding}, or {distance} skill?", npc, creature) + else + deliverFood(npc, creature, HIRELING_FOODS_IDS[random], 15000) + end + elseif MsgContains(message, "yes") then + deliverFood(npc, creature, HIRELING_FOODS_IDS[math.random(#HIRELING_FOODS_IDS)], 15000) elseif MsgContains(message, "no") then npcHandler:setTopic(playerId, TOPIC.SERVICES) npcHandler:say("Alright then, ask me for other {services}, if you want.", npc, creature) end elseif npcHandler:getTopic(playerId) == TOPIC_FOOD.SKILL_CHOOSE then if MsgContains(message, "magic") then - deliverFood(npc, creature, HIRELING_FOODS_BOOST.MAGIC) + deliverFood(npc, creature, HIRELING_FOODS_BOOST.MAGIC, 15000) elseif MsgContains(message, "melee") then - deliverFood(npc, creature, HIRELING_FOODS_BOOST.MELEE) + deliverFood(npc, creature, HIRELING_FOODS_BOOST.MELEE, 15000) elseif MsgContains(message, "shielding") then - deliverFood(npc, creature, HIRELING_FOODS_BOOST.SHIELDING) + deliverFood(npc, creature, HIRELING_FOODS_BOOST.SHIELDING, 15000) elseif MsgContains(message, "distance") then - deliverFood(npc, creature, HIRELING_FOODS_BOOST.DISTANCE) + deliverFood(npc, creature, HIRELING_FOODS_BOOST.DISTANCE, 15000) + else + npcHandler:say("Sorry, but you must choose a valid skill class. Would you like to boost your {magic}, {melee}, {shielding}, or {distance} skill?", npc, creature) + end + elseif npcHandler:getTopic(playerId) == TOPIC_FOOD.SPECIFIC then + local specificFoodOptions = { + ["chilli con carniphila"] = 29412, + ["svargrond salmon filet"] = 29413, + ["carrion casserole"] = 29414, + ["consecrated beef"] = 29415, + ["roasted wyvern wings"] = 29408, + ["carrot pie"] = 29409, + ["tropical marinated tiger"] = 29410, + ["delicatessen salad"] = 29411, + } + + if specificFoodOptions[message:lower()] then + deliverFood(npc, creature, specificFoodOptions[message:lower()], 90000) else - npcHandler:say("Sorry, but you must choose a valid skill class. Would you like to boost your {magic}, {melee}, {shielding} or {distance} skill?", npc, creature) + npcHandler:say("I'm sorry, but that's not a valid food option. Please choose from: {chilli con carniphila}, {svargrond salmon filet}, {carrion casserole}, {consecrated beef}, {roasted wyvern wings}, {carrot pie}, {tropical marinated tiger}, or {delicatessen salad}.", npc, creature) end end end @@ -656,7 +686,7 @@ function createHirelingType(HirelingName) end elseif npcHandler:getTopic(playerId) == TOPIC.BANK then enableBankSystem[playerId] = true - elseif npcHandler:getTopic(playerId) == TOPIC.FOOD or npcHandler:getTopic(playerId) == TOPIC_FOOD.SKILL_CHOOSE then + elseif npcHandler:getTopic(playerId) == TOPIC.FOOD or npcHandler:getTopic(playerId) == TOPIC_FOOD.SKILL_CHOOSE or npcHandler:getTopic(playerId) == TOPIC_FOOD.SPECIFIC then handleFoodActions(npc, creature, message) elseif npcHandler:getTopic(playerId) == TOPIC.GOODS then -- Ensures players cannot access other shop categories diff --git a/data-otservbr-global/scripts/quests/soul_war/action-reward_soul_war.lua b/data-otservbr-global/scripts/quests/soul_war/action-reward_soul_war.lua index fae3fc59794..652a74af84b 100644 --- a/data-otservbr-global/scripts/quests/soul_war/action-reward_soul_war.lua +++ b/data-otservbr-global/scripts/quests/soul_war/action-reward_soul_war.lua @@ -26,35 +26,3 @@ end rewardSoulWar:position({ x = 33620, y = 31400, z = 10 }) rewardSoulWar:register() - -local phantasmalJadeMount = Action() - -function phantasmalJadeMount.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local soulWarQuest = player:soulWarQuestKV() - if soulWarQuest:get("panthasmal-jade-mount") then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You already have Phantasmal Jade mount!") - return true - end - - if table.contains({ 34072, 34073, 34074 }, item.itemid) then - if player:getItemCount(34072) >= 4 and player:getItemCount(34073) == 1 and player:getItemCount(34074) == 1 then - player:removeItem(34072, 4) - player:removeItem(34073, 1) - player:removeItem(34074, 1) - player:addMount(167) - player:addAchievement("You got Horse Power") - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations! You won Phantasmal Jade mount.") - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations! You won You got Horse Power achievement.") - player:getPosition():sendMagicEffect(CONST_ME_HOLYDAMAGE) - soulWarQuest:set("panthasmal-jade-mount", true) - else - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You don't have the necessary items!") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - end - end - - return true -end - -phantasmalJadeMount:id(34072, 34073, 34074) -phantasmalJadeMount:register() diff --git a/data-otservbr-global/scripts/quests/soul_war/globalevent-ebb_and_flow_change_maps.lua b/data-otservbr-global/scripts/quests/soul_war/globalevent-ebb_and_flow_change_maps.lua index 91273ac4cbd..e5166038db2 100644 --- a/data-otservbr-global/scripts/quests/soul_war/globalevent-ebb_and_flow_change_maps.lua +++ b/data-otservbr-global/scripts/quests/soul_war/globalevent-ebb_and_flow_change_maps.lua @@ -18,17 +18,25 @@ local function updateWaterPoolsSize() end local function loadMapEmpty() - if SoulWarQuest.ebbAndFlow.getZone():countPlayers() > 0 then - local players = SoulWarQuest.ebbAndFlow.getZone():getPlayers() - for _, player in ipairs(players) do - if player:getPosition().z == 8 then - if player:isInBoatSpot() then - local teleportPosition = player:getPosition() - teleportPosition.z = 9 - player:teleportTo(teleportPosition) - logger.trace("Teleporting player to down.") + local playersInZone = SoulWarQuest.ebbAndFlow.getZone():countPlayers() + local monstersInZone = SoulWarQuest.ebbAndFlow.getZone():countMonsters() + if playersInZone > 0 or monstersInZone > 0 then + local creatures = SoulWarQuest.ebbAndFlow.getZone():getCreatures() + for _, creature in ipairs(creatures) do + local creatureMaster = creature:getMaster() + local player = creature:getPlayer() + if creature:isPlayer() or (creature:isMonster() and creatureMaster and creatureMaster:getPlayer()) then + if creature:getPosition().z == 8 then + if creature:isInBoatSpot() then + local teleportPosition = creature:getPosition() + teleportPosition.z = 9 + creature:teleportTo(teleportPosition) + logger.trace("Teleporting player to down.") + end + if player then + player:sendCreatureAppear() + end end - player:sendCreatureAppear() end end end @@ -72,22 +80,30 @@ local function findNearestRoomPosition(playerPosition) end local function loadMapInundate() - if SoulWarQuest.ebbAndFlow.getZone():countPlayers() > 0 then - local players = SoulWarQuest.ebbAndFlow.getZone():getPlayers() - for _, player in ipairs(players) do - local playerPosition = player:getPosition() - if playerPosition.z == 9 then - if player:isInBoatSpot() then - local nearestCenterPosition = findNearestRoomPosition(playerPosition) - player:teleportTo(nearestCenterPosition) - logger.trace("Teleporting player to the near center position room and updating tile.") - else - player:teleportTo(SoulWarQuest.ebbAndFlow.waitPosition) - logger.trace("Teleporting player to wait position and updating tile.") + local playersInZone = SoulWarQuest.ebbAndFlow.getZone():countPlayers() + local monstersInZone = SoulWarQuest.ebbAndFlow.getZone():countMonsters() + if playersInZone > 0 or monstersInZone > 0 then + local creatures = SoulWarQuest.ebbAndFlow.getZone():getCreatures() + for _, creature in ipairs(creatures) do + local creatureMaster = creature:getMaster() + local player = creature:getPlayer() + if creature:isPlayer() or (creature:isMonster() and creatureMaster and creatureMaster:getPlayer()) then + local creaturePosition = creature:getPosition() + if creaturePosition.z == 9 then + if creature:isInBoatSpot() then + local nearestCenterPosition = findNearestRoomPosition(creaturePosition) + creature:teleportTo(nearestCenterPosition) + logger.trace("Teleporting player to the near center position room and updating tile.") + else + creature:teleportTo(SoulWarQuest.ebbAndFlow.waitPosition) + logger.trace("Teleporting player to wait position and updating tile.") + end + creaturePosition:sendMagicEffect(CONST_ME_TELEPORT) + end + if player then + player:sendCreatureAppear() end - playerPosition:sendMagicEffect(CONST_ME_TELEPORT) end - player:sendCreatureAppear() end end diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index d7f1eb786fd..8e96828ac74 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -315,7 +315,8 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, end -- Reward System - if toPosition.x == CONTAINER_POSITION then + local containerThing = tile and tile:getItemByType(ITEM_TYPE_CONTAINER) + if containerThing and toPosition.x == CONTAINER_POSITION then local containerId = toPosition.y - 64 local container = self:getContainerById(containerId) if not container then diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua index 40bc8e0b4b0..ae8a89d0e54 100644 --- a/data/libs/functions/boss_lever.lua +++ b/data/libs/functions/boss_lever.lua @@ -177,7 +177,7 @@ function BossLever:onUse(player) return true end - local isAccountNormal = creature:getAccountType() == ACCOUNT_TYPE_NORMAL + local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER if isAccountNormal and creature:getLevel() < self.requiredLevel then local message = "All players need to be level " .. self.requiredLevel .. " or higher." creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) diff --git a/data/libs/systems/blessing.lua b/data/libs/systems/blessing.lua index fee0e6fe8e8..f5403fbb07b 100644 --- a/data/libs/systems/blessing.lua +++ b/data/libs/systems/blessing.lua @@ -258,8 +258,9 @@ Blessings.BuyAllBlesses = function(player) local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or true local missingBless = player:getBlessings(nil, donthavefilter) local missingBlessAmt = #missingBless + (hasToF and 0 or 1) - local totalCost - for i, bless in ipairs(missingBless) do + local totalCost = 0 + + for _, bless in ipairs(missingBless) do totalCost = totalCost + Blessings.getBlessingCost(player:getLevel(), true, bless.id >= 7) end @@ -274,19 +275,19 @@ Blessings.BuyAllBlesses = function(player) end if player:removeMoneyBank(totalCost) then - metrics.addCounter("balance_decrease", remainsPrice, { + metrics.addCounter("balance_decrease", totalCost, { player = player:getName(), context = "blessings", }) - for i, v in ipairs(missingBless) do - player:addBlessing(v.id, 1) + for _, bless in ipairs(missingBless) do + player:addBlessing(bless.id, 1) end - player:sendCancelMessage("You received the remaining " .. missingBlessAmt .. " blesses for a total of " .. totalCost .. " gold.") + player:sendCancelMessage(string.format("You received the remaining %d blesses for a total of %d gold.", missingBlessAmt, totalCost)) player:getPosition():sendMagicEffect(CONST_ME_HOLYAREA) else - player:sendCancelMessage("You don't have enough money. You need " .. totalCost .. " to buy all blesses.", cid) + player:sendCancelMessage(string.format("You don't have enough money. You need %d to buy all blesses.", totalCost)) player:getPosition():sendMagicEffect(CONST_ME_POFF) end diff --git a/data/scripts/actions/items/usable_phantasmal_jade_items.lua b/data/scripts/actions/items/usable_phantasmal_jade_items.lua new file mode 100644 index 00000000000..8075125df25 --- /dev/null +++ b/data/scripts/actions/items/usable_phantasmal_jade_items.lua @@ -0,0 +1,45 @@ +local config = { + requiredItems = { + [34072] = { key = "spectral-horseshoes", count = 4 }, + [34073] = { key = "spectral-saddle", count = 1 }, + [34074] = { key = "spectral-horse-tac", count = 1 }, + }, + + mountId = 167, +} + +local usablePhantasmalJadeItems = Action() + +function usablePhantasmalJadeItems.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local itemInfo = config.requiredItems[item:getId()] + if not itemInfo then + return true + end + + if player:hasMount(config.mountId) then + return true + end + + local currentCount = (player:kv():get(itemInfo.key) or 0) + 1 + player:kv():set(itemInfo.key, currentCount) + player:getPosition():sendMagicEffect(CONST_ME_HOLYDAMAGE) + item:remove(1) + + for _, info in pairs(config.requiredItems) do + if (player:kv():get(info.key) or 0) < info.count then + return true + end + end + + player:addMount(config.mountId) + player:addAchievement("Natural Born Cowboy") + player:addAchievement("You got Horse Power") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Phantasmal jade is now yours!") + return true +end + +for itemId, _ in pairs(config.requiredItems) do + usablePhantasmalJadeItems:id(itemId) +end + +usablePhantasmalJadeItems:register() diff --git a/data/scripts/spells/support/sharpshooter.lua b/data/scripts/spells/support/sharpshooter.lua index 568fec86e00..e5111781562 100644 --- a/data/scripts/spells/support/sharpshooter.lua +++ b/data/scripts/spells/support/sharpshooter.lua @@ -4,9 +4,9 @@ local combat = Combat() combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_GREEN) combat:setParameter(COMBAT_PARAM_AGGRESSIVE, false) -local speed = Condition(CONDITION_HASTE) +local speed = Condition(CONDITION_PARALYZE) speed:setParameter(CONDITION_PARAM_TICKS, spellDuration) -speed:setFormula(0.7, 0, 0.7, 0) +speed:setFormula(0.7, 56, 0.7, 56) combat:addCondition(speed) local exhaustHealGroup = Condition(CONDITION_SPELLGROUPCOOLDOWN) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 34a28398e9e..6b7667a71a5 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -1066,7 +1066,7 @@ void Combat::setupChain(const std::shared_ptr &weapon) { } bool Combat::doCombatChain(const std::shared_ptr &caster, const std::shared_ptr &target, bool aggressive) const { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (!params.chainCallback) { return false; } @@ -1469,7 +1469,7 @@ void Combat::setRuneSpellName(const std::string &value) { std::vector>> Combat::pickChainTargets(const std::shared_ptr &caster, const CombatParams ¶ms, uint8_t chainDistance, uint8_t maxTargets, bool backtracking, bool aggressive, const std::shared_ptr &initialTarget /* = nullptr */) { Benchmark bm_pickChain; - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (!caster) { return {}; } @@ -1512,7 +1512,7 @@ std::vector>> Combat::pickChainTargets } if (closestSpectator) { - g_logger().trace("[{}] closestSpectator: {}", __METHOD_NAME__, closestSpectator->getName()); + g_logger().trace("[{}] closestSpectator: {}", __FUNCTION__, closestSpectator->getName()); bool found = false; for (auto &[pos, vec] : resultMap) { @@ -1531,7 +1531,7 @@ std::vector>> Combat::pickChainTargets continue; } if (backtracking) { - g_logger().debug("[{}] backtracking", __METHOD_NAME__); + g_logger().debug("[{}] backtracking", __FUNCTION__); targets.pop_back(); backtrackingAttempts--; continue; @@ -1539,7 +1539,7 @@ std::vector>> Combat::pickChainTargets break; } - g_logger().debug("[{}] resultMap: {} in {} ms", __METHOD_NAME__, resultMap.size(), bm_pickChain.duration()); + g_logger().debug("[{}] resultMap: {} in {} ms", __FUNCTION__, resultMap.size(), bm_pickChain.duration()); return resultMap; } @@ -2221,7 +2221,7 @@ void MagicField::onStepInField(const std::shared_ptr &creature) { } void Combat::applyExtensions(const std::shared_ptr &caster, const std::shared_ptr &target, CombatDamage &damage, const CombatParams ¶ms) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (damage.extension || !caster || damage.primary.type == COMBAT_HEALING) { return; } diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 327d97f3284..f522eac86a6 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -23,9 +23,6 @@ #include "lua/scripts/scripts.hpp" #include "lib/di/container.hpp" -std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyRegularBoost = { 0 }; -std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyUpgradedBoost = { 0 }; - Spells::Spells() = default; Spells::~Spells() = default; diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 5d9edb70dd6..56ff1a330de 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -10,9 +10,7 @@ #pragma once #include "lua/creature/actions.hpp" - -enum class WheelSpellBoost_t : uint8_t; -enum class WheelSpellGrade_t : uint8_t; +#include "creatures/players/wheel/wheel_definitions.hpp" class InstantSpell; class RuneSpell; @@ -256,6 +254,8 @@ class Spell : public BaseSpell { bool pzLocked = false; bool whellOfDestinyUpgraded = false; + std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyRegularBoost = { 0 }; + std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyUpgradedBoost = { 0 }; private: uint32_t mana = 0; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 2081b0572fc..32f3c598b81 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -35,7 +35,7 @@ Creature::~Creature() { } bool Creature::canSee(const Position &myPos, const Position &pos, int32_t viewRangeX, int32_t viewRangeY) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (myPos.z <= MAP_INIT_SURFACE_LAYER) { // we are on ground level or above (7 -> 0) // view is from 7 -> 0 @@ -95,7 +95,7 @@ int32_t Creature::getWalkSize() { } void Creature::onThink(uint32_t interval) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &followCreature = getFollowCreature(); const auto &master = getMaster(); @@ -168,7 +168,7 @@ void Creature::onCreatureWalk() { checkingWalkCreature = true; - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); g_dispatcher().addWalkEvent([self = getCreature(), this] { checkingWalkCreature = false; @@ -287,7 +287,7 @@ void Creature::stopEventWalk() { } void Creature::onCreatureAppear(const std::shared_ptr &creature, bool isLogin) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (creature.get() == this) { if (isLogin) { setLastPosition(getPosition()); @@ -296,7 +296,7 @@ void Creature::onCreatureAppear(const std::shared_ptr &creature, bool } void Creature::onRemoveCreature(const std::shared_ptr &creature, bool) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); onCreatureDisappear(creature, true); // Update player from monster target list (avoid memory usage after clean) @@ -307,7 +307,7 @@ void Creature::onRemoveCreature(const std::shared_ptr &creature, bool) } void Creature::onCreatureDisappear(const std::shared_ptr &creature, bool isLogout) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (getAttackedCreature() == creature) { setAttackedCreature(nullptr); onAttackedCreatureDisappear(isLogout); @@ -320,7 +320,7 @@ void Creature::onCreatureDisappear(const std::shared_ptr &creature, bo } void Creature::onChangeZone(ZoneType_t zone) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &attackedCreature = getAttackedCreature(); if (attackedCreature && zone == ZONE_PROTECTION) { onCreatureDisappear(attackedCreature, false); @@ -328,7 +328,7 @@ void Creature::onChangeZone(ZoneType_t zone) { } void Creature::onAttackedCreatureChangeZone(ZoneType_t zone) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (zone == ZONE_PROTECTION) { const auto &attackedCreature = getAttackedCreature(); if (attackedCreature) { @@ -338,7 +338,7 @@ void Creature::onAttackedCreatureChangeZone(ZoneType_t zone) { } void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (hasSummons()) { std::vector> despawnMonsterList; for (const auto &summon : getSummons()) { @@ -385,7 +385,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) { } void Creature::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (creature.get() == this) { lastStep = OTSYS_TIME(); lastStepCost = 1; @@ -447,7 +447,7 @@ void Creature::onCreatureMove(const std::shared_ptr &creature, const s } void Creature::onDeath() { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); bool lastHitUnjustified = false; bool mostDamageUnjustified = false; const auto &lastHitCreature = g_game().getCreatureByID(lastHitCreatureId); @@ -585,7 +585,7 @@ void Creature::onDeath() { } bool Creature::dropCorpse(const std::shared_ptr &lastHitCreature, const std::shared_ptr &mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (!lootDrop && getMonster()) { if (getMaster()) { // Scripting event onDeath @@ -882,7 +882,7 @@ void Creature::goToFollowCreature_async(std::function &&onComplete) { } void Creature::goToFollowCreature() { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &followCreature = getFollowCreature(); if (!followCreature) { return; @@ -937,7 +937,7 @@ bool Creature::canFollowMaster() const { } bool Creature::setFollowCreature(const std::shared_ptr &creature) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (creature) { if (getFollowCreature() == creature) { return true; @@ -1086,7 +1086,7 @@ void Creature::onAttackedCreatureDrainHealth(const std::shared_ptr &ta } void Creature::onAttackedCreatureKilled(const std::shared_ptr &target) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (target != getCreature()) { uint64_t gainExp = target->getGainedExperience(static_self_cast()); onGainExperience(gainExp, target); @@ -1094,7 +1094,7 @@ void Creature::onAttackedCreatureKilled(const std::shared_ptr &target) } bool Creature::deprecatedOnKilledCreature(const std::shared_ptr &target, bool lastHit) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &master = getMaster(); if (master) { master->deprecatedOnKilledCreature(target, lastHit); @@ -1109,7 +1109,7 @@ bool Creature::deprecatedOnKilledCreature(const std::shared_ptr &targe } void Creature::onGainExperience(uint64_t gainExp, const std::shared_ptr &target) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &master = getMaster(); if (gainExp == 0 || !master) { return; @@ -1140,7 +1140,7 @@ void Creature::onGainExperience(uint64_t gainExp, const std::shared_ptr &newMaster, bool reloadCreature /* = false*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); // Persists if this creature has ever been a summon this->summoned = true; const auto &oldMaster = getMaster(); @@ -1173,7 +1173,7 @@ bool Creature::setMaster(const std::shared_ptr &newMaster, bool reload } bool Creature::addCondition(const std::shared_ptr &condition, bool attackerPlayer /* = false*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (condition == nullptr) { return false; } @@ -1211,7 +1211,7 @@ bool Creature::addCombatCondition(const std::shared_ptr &condition, b } void Creature::removeCondition(ConditionType_t type) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); auto it = conditions.begin(), end = conditions.end(); while (it != end) { std::shared_ptr condition = *it; @@ -1229,7 +1229,7 @@ void Creature::removeCondition(ConditionType_t type) { } void Creature::removeCondition(ConditionType_t conditionType, ConditionId_t conditionId, bool force /* = false*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); auto it = conditions.begin(); const auto end = conditions.end(); while (it != end) { @@ -1292,7 +1292,7 @@ std::shared_ptr Creature::getCondition(ConditionType_t type) const { } std::shared_ptr Creature::getCondition(ConditionType_t type, ConditionId_t conditionId, uint32_t subId /* = 0*/) const { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); for (const auto &condition : conditions) { if (condition->getType() == type && condition->getId() == conditionId && condition->getSubId() == subId) { return condition; @@ -1312,7 +1312,7 @@ std::vector> Creature::getConditionsByType(ConditionT } void Creature::executeConditions(uint32_t interval) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); auto it = conditions.begin(), end = conditions.end(); while (it != end) { std::shared_ptr condition = *it; @@ -1331,7 +1331,7 @@ void Creature::executeConditions(uint32_t interval) { } bool Creature::hasCondition(ConditionType_t type, uint32_t subId /* = 0*/) const { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (isSuppress(type, false)) { return false; } @@ -1607,7 +1607,7 @@ ZoneType_t Creature::getZoneType() { } bool Creature::getPathTo(const Position &targetPos, std::vector &dirList, const FindPathParams &fpp) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (fpp.maxSearchDist != 0 || fpp.keepDistance) { return g_game().map.getPathMatchingCond(getCreature(), targetPos, dirList, FrozenPathingConditionCall(targetPos), fpp); } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index d838f8b96a0..3ef1d64e982 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2739,6 +2739,10 @@ uint16_t Player::getGrindingXpBoost() const { return grindingXpBoost; } +uint16_t Player::getDisplayGrindingXpBoost() const { + return std::clamp(grindingXpBoost * (baseXpGain / 100), 0, std::numeric_limits::max()); +} + void Player::setGrindingXpBoost(uint16_t value) { grindingXpBoost = std::min(std::numeric_limits::max(), value); } @@ -2747,6 +2751,10 @@ uint16_t Player::getXpBoostPercent() const { return xpBoostPercent; } +uint16_t Player::getDisplayXpBoostPercent() const { + return std::clamp(xpBoostPercent * (baseXpGain / 100), 0, std::numeric_limits::max()); +} + void Player::setXpBoostPercent(uint16_t percent) { xpBoostPercent = percent; } @@ -3517,7 +3525,7 @@ void Player::death(const std::shared_ptr &lastHitCreature) { } // Level loss - auto expLoss = static_cast(std::ceil((experience * deathLossPercent) / 100.)); + auto expLoss = static_cast(std::ceil(experience * deathLossPercent)); g_logger().debug("[{}] - experience lost {}", __FUNCTION__, expLoss); g_events().eventPlayerOnLoseExperience(static_self_cast(), expLoss); @@ -6323,9 +6331,9 @@ double Player::getLostPercent() const { g_logger().debug("[{}] - after promotion {}", __FUNCTION__, percentReduction); } - g_logger().debug("[{}] - total lost percent {}", __FUNCTION__, lossPercent - (lossPercent * percentReduction)); + g_logger().debug("[{}] - total lost percent {}", __FUNCTION__, (lossPercent * (1 - percentReduction)) / 100.); - return lossPercent - (lossPercent * percentReduction); + return (lossPercent * (1 - percentReduction)) / 100.; } [[nodiscard]] const std::string &Player::getGuildNick() const { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 03f248cfa2f..6ab3be4ca7a 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -972,8 +972,10 @@ class Player final : public Creature, public Cylinder, public Bankable { uint16_t getVoucherXpBoost() const; void setVoucherXpBoost(uint16_t value); uint16_t getGrindingXpBoost() const; + uint16_t getDisplayGrindingXpBoost() const; void setGrindingXpBoost(uint16_t value); uint16_t getXpBoostPercent() const; + uint16_t getDisplayXpBoostPercent() const; void setXpBoostPercent(uint16_t percent); uint16_t getStaminaXpBoost() const; void setStaminaXpBoost(uint16_t value); diff --git a/src/game/game.cpp b/src/game/game.cpp index 77a277c69ad..068ecad4a50 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1161,7 +1161,7 @@ bool Game::internalPlaceCreature(const std::shared_ptr &creature, cons } bool Game::placeCreature(const std::shared_ptr &creature, const Position &pos, bool extendedPos /*=false*/, bool forced /*= false*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (!internalPlaceCreature(creature, pos, extendedPos, forced)) { return false; } @@ -1188,7 +1188,7 @@ bool Game::placeCreature(const std::shared_ptr &creature, const Positi } bool Game::removeCreature(const std::shared_ptr &creature, bool isLogout /* = true*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (!creature || creature->isRemoved()) { return false; } @@ -1256,7 +1256,7 @@ bool Game::removeCreature(const std::shared_ptr &creature, bool isLogo } void Game::executeDeath(uint32_t creatureId) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); std::shared_ptr creature = getCreatureByID(creatureId); if (creature && !creature->isRemoved()) { afterCreatureZoneChange(creature, creature->getZones(), {}); @@ -1265,7 +1265,7 @@ void Game::executeDeath(uint32_t creatureId) { } void Game::playerTeleport(uint32_t playerId, const Position &newPosition) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player || !player->hasFlag(PlayerFlags_t::CanMapClickTeleport)) { return; @@ -1278,7 +1278,7 @@ void Game::playerTeleport(uint32_t playerId, const Position &newPosition) { } void Game::playerInspectItem(const std::shared_ptr &player, const Position &pos) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const std::shared_ptr &thing = internalGetThing(player, pos, 0, 0, STACKPOS_TOPDOWN_ITEM); if (!thing) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); @@ -1295,7 +1295,7 @@ void Game::playerInspectItem(const std::shared_ptr &player, const Positi } void Game::playerInspectItem(const std::shared_ptr &player, uint16_t itemId, uint8_t itemCount, bool cyclopedia) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); player->sendItemInspection(itemId, itemCount, nullptr, cyclopedia); } @@ -1349,7 +1349,7 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { } void Game::playerMoveThing(uint32_t playerId, const Position &fromPos, uint16_t itemId, uint8_t fromStackPos, const Position &toPos, uint8_t count) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player) { return; @@ -1434,7 +1434,7 @@ void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, } void Game::playerMoveCreature(const std::shared_ptr &player, const std::shared_ptr &movingCreature, const Position &movingCreatureOrigPos, const std::shared_ptr &toTile) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); g_dispatcher().addWalkEvent([=, this] { if (!player->canDoAction()) { @@ -1590,7 +1590,7 @@ ReturnValue Game::internalMoveCreature(const std::shared_ptr &creature } ReturnValue Game::internalMoveCreature(const std::shared_ptr &creature, const std::shared_ptr &toTile, uint32_t flags /*= 0*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (creature->hasCondition(CONDITION_ROOTED)) { return RETURNVALUE_NOTPOSSIBLE; } @@ -2009,7 +2009,7 @@ ReturnValue Game::checkMoveItemToCylinder(const std::shared_ptr &player, } ReturnValue Game::internalMoveItem(std::shared_ptr fromCylinder, std::shared_ptr toCylinder, int32_t index, const std::shared_ptr &item, uint32_t count, std::shared_ptr* movedItem, uint32_t flags /*= 0*/, const std::shared_ptr &actor /*=nullptr*/, const std::shared_ptr &tradeItem /* = nullptr*/, bool checkTile /* = true*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (fromCylinder == nullptr) { g_logger().error("[{}] fromCylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; @@ -2271,7 +2271,7 @@ ReturnValue Game::internalAddItem(std::shared_ptr toCylinder, const st } ReturnValue Game::internalAddItem(std::shared_ptr toCylinder, const std::shared_ptr &item, int32_t index, uint32_t flags, bool test, uint32_t &remainderCount) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (toCylinder == nullptr) { g_logger().error("[{}] fromCylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; @@ -2359,7 +2359,7 @@ ReturnValue Game::internalAddItem(std::shared_ptr toCylinder, const st ReturnValue Game::internalRemoveItem(const std::shared_ptr &items, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/, bool force /*= false*/) { auto item = items; - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (item == nullptr) { g_logger().debug("{} - Item is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; @@ -2439,7 +2439,7 @@ std::tuple Game::addItemBatch(const std::shared return std::make_tuple(ret, totalAdded, containersCreated); } - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = toCylinder->getPlayer(); bool dropping = false; auto setupDestination = [&]() -> std::shared_ptr { @@ -2519,7 +2519,7 @@ std::tuple Game::addItemBatch(const std::shared } std::tuple Game::createItemBatch(const std::shared_ptr &toCylinder, const std::vector> &itemCounts, uint32_t flags /* = 0 */, bool dropOnMap /* = true */, uint32_t autoContainerId /* = 0 */) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); std::vector> items; for (const auto &[itemId, count, subType] : itemCounts) { const auto &itemType = Item::items[itemId]; @@ -2553,7 +2553,7 @@ std::tuple Game::createItem(const std::shared_p } ReturnValue Game::internalPlayerAddItem(const std::shared_ptr &player, const std::shared_ptr &item, bool dropOnMap /*= true*/, Slots_t slot /*= CONST_SLOT_WHEREEVER*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); uint32_t remainderCount = 0; ReturnValue ret; if (slot == CONST_SLOT_WHEREEVER) { @@ -2585,7 +2585,7 @@ ReturnValue Game::internalPlayerAddItem(const std::shared_ptr &player, c } std::shared_ptr Game::findItemOfType(const std::shared_ptr &cylinder, uint16_t itemId, bool depthSearch /*= true*/, int32_t subType /*= -1*/) const { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (cylinder == nullptr) { g_logger().error("[{}] Cylinder is nullptr", __FUNCTION__); return nullptr; @@ -2751,7 +2751,7 @@ void Game::addMoney(const std::shared_ptr &cylinder, uint64_t money, u } std::shared_ptr Game::transformItem(std::shared_ptr item, uint16_t newId, int32_t newCount /*= -1*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount != 0))) { // chargeless item placed on map = infinite return item; } @@ -2889,7 +2889,7 @@ std::shared_ptr Game::transformItem(std::shared_ptr item, uint16_t n } ReturnValue Game::internalTeleport(const std::shared_ptr &thing, const Position &newPos, bool pushMove /* = true*/, uint32_t flags /*= 0*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (thing == nullptr) { g_logger().error("[{}] thing is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; @@ -3409,18 +3409,22 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = const auto &slotItem = player->getInventoryItem(slot); const auto &equipItem = searchForItem(backpack, it.id, hasTier, tier); ReturnValue ret = RETURNVALUE_NOERROR; + if (slotItem && slotItem->getID() == it.id && (!it.stackable || slotItem->getItemCount() == slotItem->getStackSize() || !equipItem)) { ret = internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); g_logger().debug("Item {} was unequipped", slotItem->getName()); } else if (equipItem) { // Shield slot item const auto &rightItem = player->getInventoryItem(CONST_SLOT_RIGHT); + // Check Ammo item if (it.weaponType == WEAPON_AMMO) { if (rightItem && rightItem->isQuiver()) { ret = internalMoveItem(equipItem->getParent(), rightItem->getContainer(), 0, equipItem, equipItem->getItemCount(), nullptr); } } else { + const auto &leftItem = player->getInventoryItem(CONST_SLOT_LEFT); + const int32_t &slotPosition = equipItem->getSlotPosition(); // Checks if a two-handed item is being equipped in the left slot when the right slot is already occupied and move to backpack if ( @@ -3429,10 +3433,35 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = && rightItem && !(it.weaponType == WEAPON_DISTANCE) && !rightItem->isQuiver() + && (!leftItem || leftItem->getWeaponType() != WEAPON_DISTANCE) ) { ret = internalCollectManagedItems(player, rightItem, getObjectCategory(rightItem), false); } + // Check if trying to equip a quiver while another quiver is already equipped in the right slot + if (slot == CONST_SLOT_RIGHT && rightItem && rightItem->isQuiver() && it.isQuiver()) { + // Replace the existing quiver with the new one + ret = internalMoveItem(rightItem->getParent(), player, INDEX_WHEREEVER, rightItem, rightItem->getItemCount(), nullptr); + if (ret == RETURNVALUE_NOERROR) { + g_logger().debug("Quiver {} was unequipped to equip new quiver", rightItem->getName()); + } else { + player->sendCancelMessage(ret); + return; + } + } else { + // Check if trying to equip a shield while a two-handed weapon is equipped in the left slot + if (slot == CONST_SLOT_RIGHT && leftItem && leftItem->getSlotPosition() & SLOTP_TWO_HAND) { + // Unequip the two-handed weapon from the left slot + ret = internalMoveItem(leftItem->getParent(), player, INDEX_WHEREEVER, leftItem, leftItem->getItemCount(), nullptr); + if (ret == RETURNVALUE_NOERROR) { + g_logger().debug("Two-handed weapon {} was unequipped to equip shield", leftItem->getName()); + } else { + player->sendCancelMessage(ret); + return; + } + } + } + if (slotItem) { ret = internalMoveItem(slotItem->getParent(), player, INDEX_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); g_logger().debug("Item {} was moved back to player", slotItem->getName()); @@ -3676,7 +3705,7 @@ void Game::playerStopAutoWalk(uint32_t playerId) { } void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t fromStackPos, uint16_t fromItemId, const Position &toPos, uint8_t toStackPos, uint16_t toItemId) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player) { return; @@ -3821,7 +3850,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f } void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPos, uint8_t index, uint16_t itemId) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player) { return; @@ -3931,7 +3960,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo } void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t itemId) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player) { return; @@ -5285,7 +5314,7 @@ void Game::internalCloseTrade(const std::shared_ptr &player) { } void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint16_t amount, bool ignoreCap /* = false*/, bool inBackpacks /* = false*/) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (amount == 0) { return; } @@ -5341,7 +5370,7 @@ void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint } void Game::playerSellItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint16_t amount, bool ignoreEquipped) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (amount == 0) { return; } @@ -6445,14 +6474,14 @@ void Game::addCreatureCheck(const std::shared_ptr &creature) { } void Game::removeCreatureCheck(const std::shared_ptr &creature) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); if (creature->inCheckCreaturesVector.load()) { creature->creatureCheck.store(false); } } void Game::checkCreatures() { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); static size_t index = 0; std::erase_if(checkCreatureLists[index], [this](const std::shared_ptr creature) { @@ -7428,10 +7457,6 @@ bool Game::combatChangeHealth(const std::shared_ptr &attacker, const s target->drainHealth(attacker, realDamage); if (realDamage > 0 && targetMonster) { - if (attackerPlayer && attackerPlayer->getPlayer()) { - attackerPlayer->updateImpactTracker(damage.secondary.type, damage.secondary.value); - } - if (targetMonster->israndomStepping()) { targetMonster->setIgnoreFieldDamage(true); } @@ -9481,7 +9506,7 @@ void Game::playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, ui } void Game::playerForgeFuseItems(uint32_t playerId, ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool usedCore, bool reduceTierLoss, bool convergence) { - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); const auto &player = getPlayerByID(playerId); if (!player) { return; diff --git a/src/game/movement/teleport.cpp b/src/game/movement/teleport.cpp index 368bcf6e476..d11552220a3 100644 --- a/src/game/movement/teleport.cpp +++ b/src/game/movement/teleport.cpp @@ -80,29 +80,27 @@ void Teleport::addThing(int32_t, const std::shared_ptr &thing) { // Prevent infinity loop if (checkInfinityLoop(destTile)) { const Position &pos = getPosition(); - g_logger().warn("[Teleport:addThing] - " - "Infinity loop teleport at position: {}", - pos.toString()); + g_logger().warn("[Teleport:addThing] - Infinity loop teleport at position: {}", pos.toString()); return; } const MagicEffectClasses effect = Item::items[id].magicEffect; - if (const std::shared_ptr &creature = thing->getCreature()) { + if (const auto &creature = thing->getCreature()) { Position origPos = creature->getPosition(); g_game().internalCreatureTurn(creature, origPos.x > destPos.x ? DIRECTION_WEST : DIRECTION_EAST); - g_dispatcher().addWalkEvent([=] { - g_game().map.moveCreature(creature, destTile); - if (effect != CONST_ME_NONE) { - g_game().addMagicEffect(origPos, effect); - g_game().addMagicEffect(destTile->getPosition(), effect); - } - }); + g_game().map.moveCreature(creature, destTile); + + if (effect != CONST_ME_NONE) { + g_game().addMagicEffect(origPos, effect); + g_game().addMagicEffect(destTile->getPosition(), effect); + } } else if (const auto &item = thing->getItem()) { if (effect != CONST_ME_NONE) { g_game().addMagicEffect(destTile->getPosition(), effect); g_game().addMagicEffect(item->getPosition(), effect); } + g_game().internalMoveItem(getTile(), destTile, INDEX_WHEREEVER, item, item->getItemCount(), nullptr); } } diff --git a/src/io/io_wheel.cpp b/src/io/io_wheel.cpp index 2e1b57c79bc..cfcf6593010 100644 --- a/src/io/io_wheel.cpp +++ b/src/io/io_wheel.cpp @@ -58,7 +58,7 @@ namespace InternalPlayerWheel { return; } - auto spell = g_spells().getInstantSpellByName(name); + const auto &spell = g_spells().getInstantSpellByName(name); if (spell) { g_logger().trace("[{}] registering instant spell with name {}", __FUNCTION__, spell->getName()); // Increase data @@ -127,7 +127,7 @@ bool IOWheel::initializeGlobalData(bool reload /* = false*/) { // Register spells for druid for (const auto &data : getWheelBonusData().spells.druid) { for (size_t i = 1; i < 3; ++i) { - const auto grade = data.grade[i]; + const auto &grade = data.grade[i]; InternalPlayerWheel::registerWheelSpellTable(grade, data.name, static_cast(i)); } } diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 4eb84d246c6..2ec09945f36 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -942,35 +942,7 @@ void WeaponWand::configureWeapon(const ItemType &it) { } int32_t WeaponWand::getWeaponDamage(const std::shared_ptr &player, const std::shared_ptr &, const std::shared_ptr &, bool maxDamage /* = false*/) const { - if (!g_configManager().getBoolean(TOGGLE_CHAIN_SYSTEM)) { - // Returns maximum damage or a random value between minChange and maxChange - return maxDamage ? -maxChange : -normal_random(minChange, maxChange); - } - - // If chain system is enabled, calculates magic-based damage - int32_t attackSkill = 0; - int32_t attackValue = 0; - float attackFactor = 0.0; - [[maybe_unused]] int16_t elementAttack = 0; - [[maybe_unused]] CombatDamage combatDamage; - calculateSkillFormula(player, attackSkill, attackValue, attackFactor, elementAttack, combatDamage); - - const auto magLevel = player->getMagicLevel(); - const auto level = player->getLevel(); - - // Check if level is greater than zero before performing division - const auto levelDivision = level > 0 ? level / 5.0 : 0.0; - - const auto totalAttackValue = magLevel + attackValue; - - // Check if magLevel is greater than zero before performing division - const auto magicLevelDivision = totalAttackValue > 0 ? totalAttackValue / 3.0 : 0.0; - - const double min = levelDivision + magicLevelDivision; - const double max = levelDivision + totalAttackValue; - - // Returns the calculated maximum damage or a random value between the calculated minimum and maximum - return maxDamage ? -max : -normal_random(min, max); + return maxDamage ? -maxChange : -normal_random(minChange, maxChange); } int16_t WeaponWand::getElementDamageValue() const { diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index 045ea7519f1..029895a43e5 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -83,7 +83,7 @@ int ConfigFunctions::luaConfigManagerGetFloat(lua_State* L) { float value = g_configManager().getFloat(key); double finalValue = shouldRound ? static_cast(std::round(value * 100.0) / 100.0) : value; - g_logger().debug("[{}] key: {}, finalValue: {}, shouldRound: {}", __METHOD_NAME__, magic_enum::enum_name(key), finalValue, shouldRound); + g_logger().debug("[{}] key: {}, finalValue: {}, shouldRound: {}", __FUNCTION__, magic_enum::enum_name(key), finalValue, shouldRound); lua_pushnumber(L, finalValue); return 1; } diff --git a/src/lua/functions/lua_functions_loader.hpp b/src/lua/functions/lua_functions_loader.hpp index fffc7e5b355..68cfeb45c51 100644 --- a/src/lua/functions/lua_functions_loader.hpp +++ b/src/lua/functions/lua_functions_loader.hpp @@ -63,7 +63,7 @@ class Lua { static void setCreatureMetatable(lua_State* L, int32_t index, const std::shared_ptr &creature); template - static T getNumber(lua_State* L, int32_t arg) { + static T getNumber(lua_State* L, int32_t arg, std::source_location location = std::source_location::current()) { auto number = lua_tonumber(L, arg); if constexpr (std::is_enum_v) { @@ -73,7 +73,7 @@ class Lua { if constexpr (std::is_integral_v) { if constexpr (std::is_unsigned_v) { if (number < 0) { - g_logger().debug("[{}] overflow, setting to default unsigned value (0)", __FUNCTION__); + g_logger().debug("[{}] overflow, setting to default unsigned value (0), called line: {}:{}, in {}", __FUNCTION__, location.line(), location.column(), location.function_name()); return T(0); } } diff --git a/src/lua/scripts/luascript.cpp b/src/lua/scripts/luascript.cpp index 7c0ce2c096c..9bb926b5676 100644 --- a/src/lua/scripts/luascript.cpp +++ b/src/lua/scripts/luascript.cpp @@ -237,7 +237,7 @@ bool LuaScriptInterface::closeState() { std::string LuaScriptInterface::getMetricsScope() const { #ifdef FEATURE_METRICS - metrics::method_latency measure(__METHOD_NAME__); + metrics::method_latency measure(__METRICS_METHOD_NAME__); int32_t scriptId; int32_t callbackId; bool timerEvent; diff --git a/src/pch.hpp b/src/pch.hpp index 79f14433fcc..d66b5fc10ce 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -170,20 +170,5 @@ format_as(E e) { #include "lua/global/shared_object.hpp" -constexpr std::string_view methodName(const char* s) { - const std::string_view prettyFunction(s); - const size_t bracket = prettyFunction.rfind('('); - const size_t space = prettyFunction.rfind(' ', bracket) + 1; - return prettyFunction.substr(space, bracket - space); -} - -#if defined(__GNUC__) || defined(__clang__) - #define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__) -#elif defined(_MSC_VER) - #define __METHOD_NAME__ methodName(__FUNCSIG__) -#else - #error "Compiler not supported" -#endif - #include "account/account_info.hpp" #include "config/config_enums.hpp" diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 727c1cc0097..2f7ff9e2479 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3480,8 +3480,8 @@ void ProtocolGame::sendCyclopediaCharacterGeneralStats() { msg.add(player->getLevel()); msg.addByte(player->getLevelPercent()); msg.add(player->getBaseXpGain()); // BaseXPGainRate - msg.add(player->getGrindingXpBoost()); // LowLevelBonus - msg.add(player->getXpBoostPercent()); // XPBoost + msg.add(player->getDisplayGrindingXpBoost()); // LowLevelBonus + msg.add(player->getDisplayXpBoostPercent()); // XPBoost msg.add(player->getStaminaXpBoost()); // StaminaMultiplier(100=x1.0) msg.add(player->getXpBoostTime()); // xpBoostRemainingTime msg.addByte(player->getXpBoostTime() > 0 ? 0x00 : 0x01); // canBuyXpBoost @@ -5745,14 +5745,23 @@ void ProtocolGame::sendForgeHistory(uint8_t page) { page = page + 1; auto historyVector = player->getForgeHistory(); auto historyVectorLen = historyVector.size(); - uint16_t lastPage = (1 < std::floor((historyVectorLen - 1) / 9) + 1) ? static_cast(std::floor((historyVectorLen - 1) / 9) + 1) : 1; - uint16_t currentPage = (lastPage < page) ? lastPage : page; + + uint16_t currentPage = 1; + uint16_t lastPage = 1; + uint16_t pageFirstEntry = 0; + uint16_t pageLastEntry = 0; std::vector historyPerPage; - uint16_t pageFirstEntry = (0 < historyVectorLen - (currentPage - 1) * 9) ? historyVectorLen - (currentPage - 1) * 9 : 0; - uint16_t pageLastEntry = (0 < historyVectorLen - currentPage * 9) ? historyVectorLen - currentPage * 9 : 0; - for (uint16_t entry = pageFirstEntry; entry > pageLastEntry; --entry) { - historyPerPage.emplace_back(historyVector[entry - 1]); + if (historyVectorLen > 0) { + lastPage = std::clamp(std::floor((historyVectorLen - 1) / 9) + 1, 0, std::numeric_limits::max()); + currentPage = (lastPage < page) ? lastPage : page; + + pageFirstEntry = std::clamp(historyVectorLen - (currentPage - 1) * 9, 0, std::numeric_limits::max()); + pageLastEntry = historyVectorLen > currentPage * 9 ? std::clamp(historyVectorLen - currentPage * 9, 0, std::numeric_limits::max()) : 0; + + for (uint16_t entry = pageFirstEntry; entry > pageLastEntry; --entry) { + historyPerPage.emplace_back(historyVector[entry - 1]); + } } auto historyPageToSend = historyPerPage.size(); @@ -7840,8 +7849,8 @@ void ProtocolGame::AddPlayerStats(NetworkMessage &msg) { msg.add(player->getVoucherXpBoost()); // xp voucher } - msg.add(player->getGrindingXpBoost()); // low level bonus - msg.add(player->getXpBoostPercent()); // xp boost + msg.add(player->getDisplayGrindingXpBoost()); // low level bonus + msg.add(player->getDisplayXpBoostPercent()); // xp boost msg.add(player->getStaminaXpBoost()); // stamina multiplier (100 = 1.0x) if (!oldProtocol) { diff --git a/src/utils/definitions.hpp b/src/utils/definitions.hpp index d557896f361..24a6b7d134b 100644 --- a/src/utils/definitions.hpp +++ b/src/utils/definitions.hpp @@ -13,6 +13,8 @@ #define __FUNCTION__ __func__ #endif +#define __METRICS_METHOD_NAME__ std::source_location::current().function_name() + #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif diff --git a/src/utils/pugicast.hpp b/src/utils/pugicast.hpp index b59e95d37ab..91043596605 100644 --- a/src/utils/pugicast.hpp +++ b/src/utils/pugicast.hpp @@ -38,14 +38,12 @@ namespace pugi { // If the string could not be parsed as the specified type if (errorCode == std::errc::invalid_argument) { // Throw an exception indicating that the argument is invalid - logError(fmt::format("Invalid argument {}", str)); - throw std::invalid_argument("Invalid argument: " + std::string(str)); + logError(fmt::format("[{}] Invalid argument {}", __FUNCTION__, str)); } // If the parsed value is out of range for the specified type else if (errorCode == std::errc::result_out_of_range) { // Throw an exception indicating that the result is out of range - logError(fmt::format("Result out of range: {}", str)); - throw std::out_of_range("Result out of range: " + std::string(str)); + logError(fmt::format("[{}] Result out of range: {}", __FUNCTION__, str)); } // Return a default value if no exception is thrown