From bd72f6618e11221e2d8b8d58c3436e34e44fe0d9 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:20:33 -0300 Subject: [PATCH 01/95] fix: transcendence applies correct status without unlocked stages (#2566) Fix the logic to ensure correct status application when using tiered legs and undergoing transcendence without any avatar stages unlocked. Remove the early return that blocks code execution in this scenario. Ensure proper application of damage reduction, 100% critical chance, and 15% critical damage. --- src/creatures/players/player.cpp | 2 +- src/creatures/players/wheel/player_wheel.cpp | 24 +++++++++++-------- .../players/wheel/wheel_definitions.hpp | 5 ++-- .../creatures/player/player_functions.cpp | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 618ffea1081..bcb31a4abfa 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6688,7 +6688,7 @@ void Player::triggerTranscendance() { outfit.lookType = getVocation()->getAvatarLookType(); outfitCondition->setOutfit(outfit); addCondition(outfitCondition); - wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, OTSYS_TIME() + duration); + wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); sendSkills(); diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 77a15f7cbf7..20e8f683bf9 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -2410,21 +2410,25 @@ int32_t PlayerWheel::checkBattleHealingAmount() const { } int32_t PlayerWheel::checkAvatarSkill(WheelAvatarSkill_t skill) const { - if (skill == WheelAvatarSkill_t::NONE || getOnThinkTimer(WheelOnThink_t::AVATAR) <= OTSYS_TIME()) { + if (skill == WheelAvatarSkill_t::NONE || (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) <= OTSYS_TIME() && getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) <= OTSYS_TIME())) { return 0; } uint8_t stage = 0; - if (getInstant("Avatar of Light")) { - stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); - } else if (getInstant("Avatar of Steel")) { - stage = getStage(WheelStage_t::AVATAR_OF_STEEL); - } else if (getInstant("Avatar of Nature")) { - stage = getStage(WheelStage_t::AVATAR_OF_NATURE); - } else if (getInstant("Avatar of Storm")) { - stage = getStage(WheelStage_t::AVATAR_OF_STORM); + if (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) > OTSYS_TIME()) { + if (getInstant("Avatar of Light")) { + stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); + } else if (getInstant("Avatar of Steel")) { + stage = getStage(WheelStage_t::AVATAR_OF_STEEL); + } else if (getInstant("Avatar of Nature")) { + stage = getStage(WheelStage_t::AVATAR_OF_NATURE); + } else if (getInstant("Avatar of Storm")) { + stage = getStage(WheelStage_t::AVATAR_OF_STORM); + } else { + return 0; + } } else { - return 0; + stage = 3; } if (skill == WheelAvatarSkill_t::DAMAGE_REDUCTION) { diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index 8432e9dca21..c23d2adf53f 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -105,9 +105,10 @@ enum class WheelOnThink_t : uint8_t { FOCUS_MASTERY = 4, GIFT_OF_LIFE = 5, DIVINE_EMPOWERMENT = 6, - AVATAR = 7, + AVATAR_SPELL = 7, + AVATAR_FORGE = 8, - TOTAL_COUNT = 8 + TOTAL_COUNT = 9 }; enum class WheelStat_t : uint8_t { diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 3ba2c43841d..621023e5c52 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3997,9 +3997,9 @@ int PlayerFunctions::luaPlayerAvatarTimer(lua_State* L) { } if (lua_gettop(L) == 1) { - lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR)); + lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL)); } else { - player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, getNumber(L, 2)); + player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_SPELL, getNumber(L, 2)); pushBoolean(L, true); } return 1; From e60612e9ad6da36c2de1824e597e5c3b6efd7615 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:36 -0300 Subject: [PATCH 02/95] fix: alana sio only to "aleta som" users (#2564) --- data/scripts/spells/house/kick.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/scripts/spells/house/kick.lua b/data/scripts/spells/house/kick.lua index b4b583c1007..265ac48f796 100644 --- a/data/scripts/spells/house/kick.lua +++ b/data/scripts/spells/house/kick.lua @@ -4,6 +4,7 @@ function spell.onCastSpell(player, variant) local targetPlayer = Player(variant:getString()) or player local guest = targetPlayer:getTile():getHouse() local owner = player:getTile():getHouse() + -- Owner kick yourself from house if targetPlayer == player then player:getPosition():sendMagicEffect(CONST_ME_POFF) @@ -11,6 +12,13 @@ function spell.onCastSpell(player, variant) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true end + + if not owner:canEditAccessList(GUEST_LIST, player) then + player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) player:getPosition():sendMagicEffect(CONST_ME_POFF) From 72794c40f1d13ffe64613cf67bae6c7bb1208281 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:52 -0300 Subject: [PATCH 03/95] fix: destroy field is working inside pz (#2558) --- data/scripts/runes/destroy_field_rune.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/scripts/runes/destroy_field_rune.lua b/data/scripts/runes/destroy_field_rune.lua index 024dbc1bcd7..8752be7eb51 100644 --- a/data/scripts/runes/destroy_field_rune.lua +++ b/data/scripts/runes/destroy_field_rune.lua @@ -4,6 +4,13 @@ local fields = { 105, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2132 local rune = Spell("rune") function rune.onCastSpell(creature, variant, isHotkey) + local inPz = creature:getTile():hasFlag(TILESTATE_PROTECTIONZONE) + if inPz then + creature:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + creature:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + local position = Variant.getPosition(variant) local tile = Tile(position) local field = tile and tile:getItemByType(ITEM_TYPE_MAGICFIELD) From 69b25aa431c869e8758a2624fd9dd18de8508b87 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:13:12 -0300 Subject: [PATCH 04/95] fix: stamina not recovering when dead (#2557) --- src/creatures/players/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index bcb31a4abfa..39a8841fdf2 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2918,6 +2918,7 @@ bool Player::spawn() { getParent()->postAddNotification(static_self_cast(), nullptr, 0); g_game().addCreatureCheck(static_self_cast()); g_game().addPlayer(static_self_cast()); + static_self_cast()->onChangeZone(static_self_cast()->getZoneType()); return true; } @@ -4673,6 +4674,8 @@ void Player::onPlacedCreature() { removePlayer(true); } + this->onChangeZone(this->getZoneType()); + sendUnjustifiedPoints(); } From 38651ca103ee8674bdd6bcb7a994e2cea2220afd Mon Sep 17 00:00:00 2001 From: Aluisio Penna Date: Thu, 25 Apr 2024 10:15:52 -0300 Subject: [PATCH 05/95] fix: kill count of each type of minotaur in 'Turmoil of War' quest log (#2569) --- .../quests/killing_in_the_name_of/monster_kill.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua index b2ba681d251..f8f7ddc5c6b 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua @@ -57,7 +57,7 @@ function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) end end -- Minotaurs - killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, nil, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) + killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.MinotaurCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) -- Necromancers and Priestesses killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 0, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 3, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) From dd8cdde213f4258f8f577462d12622bb88872e3d Mon Sep 17 00:00:00 2001 From: svetrey <51045033+svetrey@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:17:02 +0100 Subject: [PATCH 06/95] fix: diamond arrow static attack points (#2560) --- data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua index 61984209287..36be8dc38b4 100644 --- a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua +++ b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua @@ -16,7 +16,7 @@ combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) function onGetFormulaValues(player, skill, attack, factor) local distanceSkill = player:getEffectiveSkillLevel(SKILL_DISTANCE) local min = (player:getLevel() / 5) - local max = (0.09 * factor) * distanceSkill * 37 + (player:getLevel() / 5) + local max = (0.09 * factor) * distanceSkill * attack + (player:getLevel() / 5) return -min, -max end From 0f91db0e379714e20652fda85714a4259e7a2bba Mon Sep 17 00:00:00 2001 From: Jeswill David Bolivar Mendoza <76903590+jeswilldbm@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:17:53 +0200 Subject: [PATCH 07/95] fix: missing attribute in bone fiddle (#2531) --- data/items/items.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index 0bc1c782452..421e981ad02 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -53105,6 +53105,9 @@ hands of its owner. Granted by TibiaRoyal.com"/> + + + From 13798e59702e8de713ae8f78fded17e2e5de35e2 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 25 Apr 2024 10:18:20 -0300 Subject: [PATCH 08/95] feat: include day count in getTimeInWords function (#2525) --- data/libs/functions/functions.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/data/libs/functions/functions.lua b/data/libs/functions/functions.lua index 9eb1b0b57b5..c2349ce7fd6 100644 --- a/data/libs/functions/functions.lua +++ b/data/libs/functions/functions.lua @@ -67,17 +67,28 @@ end function getTimeInWords(secsParam) local secs = tonumber(secsParam) + local days = math.floor(secs / (24 * 3600)) + secs = secs - (days * 24 * 3600) local hours, minutes, seconds = getHours(secs), getMinutes(secs), getSeconds(secs) local timeStr = "" + if days > 0 then + timeStr = days .. (days > 1 and " days" or " day") + end + if hours > 0 then - timeStr = hours .. (hours > 1 and " hours" or " hour") + if timeStr ~= "" then + timeStr = timeStr .. ", " + end + + timeStr = timeStr .. hours .. (hours > 1 and " hours" or " hour") end if minutes > 0 then if timeStr ~= "" then timeStr = timeStr .. ", " end + timeStr = timeStr .. minutes .. (minutes > 1 and " minutes" or " minute") end @@ -85,9 +96,9 @@ function getTimeInWords(secsParam) if timeStr ~= "" then timeStr = timeStr .. " and " end + timeStr = timeStr .. seconds .. (seconds > 1 and " seconds" or " second") end - return timeStr end From 09984296e7172f550d3889670205bc7f0bda5bca Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 25 Apr 2024 10:18:39 -0300 Subject: [PATCH 09/95] feat: playerOnWalk event callback and afk talkaction (#2518) --- data/scripts/eventcallbacks/README.md | 1 + data/scripts/talkactions/gm/afk.lua | 94 +++++++++++++++++++++ src/creatures/players/player.cpp | 2 + src/lua/callbacks/callbacks_definitions.hpp | 1 + src/lua/callbacks/event_callback.cpp | 23 +++++ src/lua/callbacks/event_callback.hpp | 1 + 6 files changed, 122 insertions(+) create mode 100644 data/scripts/talkactions/gm/afk.lua diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md index bdafcb41b33..601653574bd 100644 --- a/data/scripts/eventcallbacks/README.md +++ b/data/scripts/eventcallbacks/README.md @@ -47,6 +47,7 @@ Event callbacks are available for several categories of game entities, such as ` - `(void)` `playerOnCombat` - `(void)` `playerOnInventoryUpdate` - `(bool)` `playerOnRotateItem` +- `(void)` `playerOnWalk` - `(void)` `monsterOnDropLoot` - `(void)` `monsterPostDropLoot` - `(void)` `monsterOnSpawn` diff --git a/data/scripts/talkactions/gm/afk.lua b/data/scripts/talkactions/gm/afk.lua new file mode 100644 index 00000000000..6167a2b6068 --- /dev/null +++ b/data/scripts/talkactions/gm/afk.lua @@ -0,0 +1,94 @@ +local afk = TalkAction("/afk") + +playersAFKs = {} + +local function checkIsAFK(id) + for index, item in pairs(playersAFKs) do + if id == item.id then + return { afk = true, index = index } + end + end + return { afk = false } +end + +local function showAfkMessage(playerPosition) + local spectators = Game.getSpectators(playerPosition, false, true, 8, 8, 8, 8) + if #spectators > 0 then + for _, spectator in ipairs(spectators) do + spectator:say("AFK !", TALKTYPE_MONSTER_SAY, false, spectator, playerPosition) + end + end +end + +function afk.onSay(player, words, param) + if param == "" then + player:sendCancelMessage("You need to specify on/off param.") + return true + end + + local id, playerPosition = player:getId(), player:getPosition() + local isAfk = checkIsAFK(id) + if param == "on" then + if isAfk.afk then + player:sendCancelMessage("You are already AFK!") + return true + end + + table.insert(playersAFKs, { id = id, position = playerPosition }) + if player:isInGhostMode() then + player:setGhostMode(false) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are now AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + showAfkMessage(playerPosition) + elseif param == "off" then + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + end + end + + return true +end + +afk:separator(" ") +afk:groupType("gamemaster") +afk:register() + +------------------ AFK Effect Message ------------------ +local afkEffect = GlobalEvent("GodAfkEffect") +function afkEffect.onThink(interval) + for _, player in ipairs(playersAFKs) do + showAfkMessage(player.position) + end + return true +end + +afkEffect:interval(5000) +afkEffect:register() + +------------------ Stop AFK Message when moves ------------------ +local callback = EventCallback() +function callback.playerOnWalk(player, creature, creaturePos, toPos) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + end + return true +end + +callback:register() + +------------------ Player Logout ------------------ +local godAfkLogout = CreatureEvent("GodAfkLogout") +function godAfkLogout.onLogout(player) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + end + return true +end + +godAfkLogout:register() diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 39a8841fdf2..7e53f476683 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1926,6 +1926,8 @@ void Player::onWalk(Direction &dir) { Creature::onWalk(dir); setNextActionTask(nullptr); setNextAction(OTSYS_TIME() + getStepDuration(dir)); + + g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp index 521a2c4cda7..3b8016f5f5b 100644 --- a/src/lua/callbacks/callbacks_definitions.hpp +++ b/src/lua/callbacks/callbacks_definitions.hpp @@ -56,6 +56,7 @@ enum class EventCallback_t : uint16_t { playerOnCombat, playerOnInventoryUpdate, playerOnRotateItem, + playerOnWalk, // Monster monsterOnDropLoot, monsterPostDropLoot, diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 51c03131fa9..d2b88c30f3a 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -985,6 +985,29 @@ bool EventCallback::playerOnRotateItem(std::shared_ptr player, std::shar return getScriptInterface()->callFunction(3); } +void EventCallback::playerOnWalk(std::shared_ptr player, Direction &dir) const { + if (!getScriptInterface()->reserveScriptEnv()) { + g_logger().error("[EventCallback::eventOnWalk - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); + return; + } + + ScriptEnvironment* scriptEnvironment = getScriptInterface()->getScriptEnv(); + scriptEnvironment->setScriptId(getScriptId(), getScriptInterface()); + + lua_State* L = getScriptInterface()->getLuaState(); + getScriptInterface()->pushFunction(getScriptId()); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, dir); + + getScriptInterface()->callVoidFunction(2); +} + void EventCallback::playerOnStorageUpdate(std::shared_ptr player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::eventOnStorageUpdate - " diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp index d9dc4a9b110..9141235a028 100644 --- a/src/lua/callbacks/event_callback.hpp +++ b/src/lua/callbacks/event_callback.hpp @@ -116,6 +116,7 @@ class EventCallback : public Script { void playerOnCombat(std::shared_ptr player, std::shared_ptr target, std::shared_ptr item, CombatDamage &damage) const; void playerOnInventoryUpdate(std::shared_ptr player, std::shared_ptr item, Slots_t slot, bool equip) const; bool playerOnRotateItem(std::shared_ptr player, std::shared_ptr item, const Position &position) const; + void playerOnWalk(std::shared_ptr player, Direction &dir) const; // Monster void monsterOnDropLoot(std::shared_ptr monster, std::shared_ptr corpse) const; From 5718a4d4f6cfa74296ca3789eb14ad9613bc937f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Thu, 25 Apr 2024 17:14:00 -0300 Subject: [PATCH 10/95] fix: brain head can be killed more than once (#2536) --- .../actions/quests/feaster_of_souls/portal_brain_head.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua index 725359eb64a..d06ccbefb70 100644 --- a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua +++ b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua @@ -103,26 +103,26 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition) end local player = creature if player:getLevel() < config.requiredLevel then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to be level " .. config.requiredLevel .. " or higher.") return true end if locked then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. config.bossName .. ".") return false end if zone:countPlayers(IgnoredByMonsters) >= 5 then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The boss room is full.") return false end local timeLeft = player:getBossCooldown(config.bossName) - os.time() if timeLeft > 0 then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. getTimeInWords(timeLeft) .. " to face " .. config.bossName .. " again!") player:getPosition():sendMagicEffect(CONST_ME_POFF) From bf8aa79f2b7d234d12ee2123a7edae87f152b5d5 Mon Sep 17 00:00:00 2001 From: Beats Date: Thu, 25 Apr 2024 16:15:05 -0400 Subject: [PATCH 11/95] feat: shared_ptr vocation and vocation reload (#2555) --- data/scripts/talkactions/god/reload.lua | 1 + src/creatures/players/player.cpp | 6 +- src/creatures/players/player.hpp | 6 +- src/creatures/players/vocations/vocation.cpp | 85 ++++++++++--------- src/creatures/players/vocations/vocation.hpp | 7 +- src/game/functions/game_reload.cpp | 8 ++ src/game/functions/game_reload.hpp | 2 + src/game/game.cpp | 6 +- src/items/item.cpp | 2 +- .../creatures/player/player_functions.cpp | 4 +- .../creatures/player/vocation_functions.cpp | 48 +++++------ .../creatures/player/vocation_functions.hpp | 2 +- src/server/network/protocol/protocolgame.cpp | 10 +-- 13 files changed, 102 insertions(+), 85 deletions(-) diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua index 5bf72868320..20b9431dbba 100644 --- a/data/scripts/talkactions/god/reload.lua +++ b/data/scripts/talkactions/god/reload.lua @@ -30,6 +30,7 @@ local reloadTypes = { ["scripts"] = RELOAD_TYPE_SCRIPTS, ["stage"] = RELOAD_TYPE_CORE, ["stages"] = RELOAD_TYPE_CORE, + ["vocations"] = RELOAD_TYPE_VOCATIONS, } local reload = TalkAction("/reload") diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 7e53f476683..92285c2bca6 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -72,7 +72,7 @@ Player::~Player() { } bool Player::setVocation(uint16_t vocId) { - Vocation* voc = g_vocations().getVocation(vocId); + const auto &voc = g_vocations().getVocation(vocId); if (!voc) { return false; } @@ -2395,7 +2395,7 @@ void Player::addExperience(std::shared_ptr target, uint64_t exp, bool ++level; // Player stats gain for vocations level <= 8 if (vocation->getId() != VOCATION_NONE && level <= 8) { - const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE); + const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE); healthMax += noneVocation->getHPGain(); health += noneVocation->getHPGain(); manaMax += noneVocation->getManaGain(); @@ -2490,7 +2490,7 @@ void Player::removeExperience(uint64_t exp, bool sendText /* = false*/) { --level; // Player stats loss for vocations level <= 8 if (vocation->getId() != VOCATION_NONE && level <= 8) { - const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE); + const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE); healthMax = std::max(0, healthMax - noneVocation->getHPGain()); manaMax = std::max(0, manaMax - noneVocation->getManaGain()); capacity = std::max(0, capacity - noneVocation->getCapGain()); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 855b48a782b..70c8c05aecd 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -334,7 +334,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isBossOnBosstiaryTracker(const std::shared_ptr &monsterType) const; - Vocation* getVocation() const { + std::shared_ptr getVocation() const { return vocation; } @@ -2522,7 +2522,7 @@ class Player final : public Creature, public Cylinder, public Bankable { // Concoction system void updateConcoction(uint16_t itemId, uint16_t timeLeft) { - if (timeLeft < 0) { + if (timeLeft == 0) { activeConcoctions.erase(itemId); } else { activeConcoctions[itemId] = timeLeft; @@ -2773,7 +2773,7 @@ class Player final : public Creature, public Cylinder, public Bankable { ProtocolGame_ptr client; std::shared_ptr walkTask; std::shared_ptr town; - Vocation* vocation = nullptr; + std::shared_ptr vocation = nullptr; std::shared_ptr rewardChest = nullptr; uint32_t inventoryWeight = 0; diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index c9a276a641d..98dbaeb1bea 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -14,6 +14,11 @@ #include "utils/pugicast.hpp" #include "utils/tools.hpp" +bool Vocations::reload() { + vocationsMap.clear(); + return loadFromXml(); +} + bool Vocations::loadFromXml() { pugi::xml_document doc; auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/vocations.xml"; @@ -32,87 +37,87 @@ bool Vocations::loadFromXml() { uint16_t id = pugi::cast(attr.value()); - auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id)); - Vocation &voc = res.first->second; + auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(std::make_shared(id))); + auto voc = res.first->second; if ((attr = vocationNode.attribute("name"))) { - voc.name = attr.as_string(); + voc->name = attr.as_string(); } if ((attr = vocationNode.attribute("clientid"))) { - voc.clientId = pugi::cast(attr.value()); + voc->clientId = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("baseid"))) { - voc.baseId = pugi::cast(attr.value()); + voc->baseId = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("description"))) { - voc.description = attr.as_string(); + voc->description = attr.as_string(); } if ((attr = vocationNode.attribute("magicshield"))) { - voc.magicShield = attr.as_bool(); + voc->magicShield = attr.as_bool(); } if ((attr = vocationNode.attribute("gaincap"))) { - voc.gainCap = pugi::cast(attr.value()) * 100; + voc->gainCap = pugi::cast(attr.value()) * 100; } if ((attr = vocationNode.attribute("gainhp"))) { - voc.gainHP = pugi::cast(attr.value()); + voc->gainHP = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmana"))) { - voc.gainMana = pugi::cast(attr.value()); + voc->gainMana = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainhpticks"))) { - voc.gainHealthTicks = pugi::cast(attr.value()); + voc->gainHealthTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainhpamount"))) { - voc.gainHealthAmount = pugi::cast(attr.value()); + voc->gainHealthAmount = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmanaticks"))) { - voc.gainManaTicks = pugi::cast(attr.value()); + voc->gainManaTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmanaamount"))) { - voc.gainManaAmount = pugi::cast(attr.value()); + voc->gainManaAmount = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("manamultiplier"))) { - voc.manaMultiplier = pugi::cast(attr.value()); + voc->manaMultiplier = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("attackspeed"))) { - voc.attackSpeed = pugi::cast(attr.value()); + voc->attackSpeed = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("basespeed"))) { - voc.baseSpeed = pugi::cast(attr.value()); + voc->baseSpeed = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("soulmax"))) { - voc.soulMax = pugi::cast(attr.value()); + voc->soulMax = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainsoulticks"))) { - voc.gainSoulTicks = pugi::cast(attr.value()); + voc->gainSoulTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("fromvoc"))) { - voc.fromVocation = pugi::cast(attr.value()); + voc->fromVocation = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("canCombat"))) { - voc.combat = attr.as_bool(); + voc->combat = attr.as_bool(); } if ((attr = vocationNode.attribute("avatarlooktype"))) { - voc.avatarLookType = pugi::cast(attr.value()); + voc->avatarLookType = pugi::cast(attr.value()); } for (auto childNode : vocationNode.children()) { @@ -121,75 +126,75 @@ bool Vocations::loadFromXml() { if (skillIdAttribute) { uint16_t skill_id = pugi::cast(skillIdAttribute.value()); if (skill_id <= SKILL_LAST) { - voc.skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value()); + voc->skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value()); } else { g_logger().warn("[Vocations::loadFromXml] - " "No valid skill id: {} for vocation: {}", - skill_id, voc.id); + skill_id, voc->id); } } else { g_logger().warn("[Vocations::loadFromXml] - " "Missing skill id for vocation: {}", - voc.id); + voc->id); } } else if (strcasecmp(childNode.name(), "mitigation") == 0) { pugi::xml_attribute factorAttribute = childNode.attribute("multiplier"); if (factorAttribute) { - voc.mitigationFactor = pugi::cast(factorAttribute.value()); + voc->mitigationFactor = pugi::cast(factorAttribute.value()); } pugi::xml_attribute primaryShieldAttribute = childNode.attribute("primaryShield"); if (primaryShieldAttribute) { - voc.mitigationPrimaryShield = pugi::cast(primaryShieldAttribute.value()); + voc->mitigationPrimaryShield = pugi::cast(primaryShieldAttribute.value()); } pugi::xml_attribute secondaryShieldAttribute = childNode.attribute("secondaryShield"); if (secondaryShieldAttribute) { - voc.mitigationSecondaryShield = pugi::cast(secondaryShieldAttribute.value()); + voc->mitigationSecondaryShield = pugi::cast(secondaryShieldAttribute.value()); } } else if (strcasecmp(childNode.name(), "formula") == 0) { pugi::xml_attribute meleeDamageAttribute = childNode.attribute("meleeDamage"); if (meleeDamageAttribute) { - voc.meleeDamageMultiplier = pugi::cast(meleeDamageAttribute.value()); + voc->meleeDamageMultiplier = pugi::cast(meleeDamageAttribute.value()); } pugi::xml_attribute distDamageAttribute = childNode.attribute("distDamage"); if (distDamageAttribute) { - voc.distDamageMultiplier = pugi::cast(distDamageAttribute.value()); + voc->distDamageMultiplier = pugi::cast(distDamageAttribute.value()); } pugi::xml_attribute defenseAttribute = childNode.attribute("defense"); if (defenseAttribute) { - voc.defenseMultiplier = pugi::cast(defenseAttribute.value()); + voc->defenseMultiplier = pugi::cast(defenseAttribute.value()); } pugi::xml_attribute armorAttribute = childNode.attribute("armor"); if (armorAttribute) { - voc.armorMultiplier = pugi::cast(armorAttribute.value()); + voc->armorMultiplier = pugi::cast(armorAttribute.value()); } } else if (strcasecmp(childNode.name(), "pvp") == 0) { pugi::xml_attribute pvpDamageReceivedMultiplier = childNode.attribute("damageReceivedMultiplier"); if (pvpDamageReceivedMultiplier) { - voc.pvpDamageReceivedMultiplier = pugi::cast(pvpDamageReceivedMultiplier.value()); + voc->pvpDamageReceivedMultiplier = pugi::cast(pvpDamageReceivedMultiplier.value()); } pugi::xml_attribute pvpDamageDealtMultiplier = childNode.attribute("damageDealtMultiplier"); if (pvpDamageDealtMultiplier) { - voc.pvpDamageDealtMultiplier = pugi::cast(pvpDamageDealtMultiplier.value()); + voc->pvpDamageDealtMultiplier = pugi::cast(pvpDamageDealtMultiplier.value()); } } else if (strcasecmp(childNode.name(), "gem") == 0) { pugi::xml_attribute qualityAttr = childNode.attribute("quality"); pugi::xml_attribute nameAttr = childNode.attribute("name"); auto quality = pugi::cast(qualityAttr.value()); auto name = nameAttr.as_string(); - voc.wheelGems[static_cast(quality)] = name; + voc->wheelGems[static_cast(quality)] = name; } } } return true; } -Vocation* Vocations::getVocation(uint16_t id) { +std::shared_ptr Vocations::getVocation(uint16_t id) { auto it = vocationsMap.find(id); if (it == vocationsMap.end()) { g_logger().warn("[Vocations::getVocation] - " @@ -197,12 +202,12 @@ Vocation* Vocations::getVocation(uint16_t id) { id); return nullptr; } - return &it->second; + return it->second; } uint16_t Vocations::getVocationId(const std::string &name) const { for (const auto &it : vocationsMap) { - if (strcasecmp(it.second.name.c_str(), name.c_str()) == 0) { + if (strcasecmp(it.second->name.c_str(), name.c_str()) == 0) { return it.first; } } @@ -211,7 +216,7 @@ uint16_t Vocations::getVocationId(const std::string &name) const { uint16_t Vocations::getPromotedVocation(uint16_t vocationId) const { for (const auto &it : vocationsMap) { - if (it.second.fromVocation == vocationId && it.first != vocationId) { + if (it.second->fromVocation == vocationId && it.first != vocationId) { return it.first; } } @@ -292,7 +297,7 @@ std::vector Vocation::getSupremeGemModifiers() { auto allModifiers = magic_enum::enum_entries(); g_logger().debug("Loading supreme gem modifiers for vocation: {}", vocationName); for (const auto &[value, modifierName] : allModifiers) { - std::string targetVocation(modifierName.substr(0, modifierName.find("_"))); + std::string targetVocation(modifierName.substr(0, modifierName.find('_'))); toLowerCaseString(targetVocation); g_logger().debug("Checking supreme gem modifier: {}, targetVocation: {}", modifierName, targetVocation); if (targetVocation == "general" || targetVocation.find(vocationName) != std::string::npos) { diff --git a/src/creatures/players/vocations/vocation.hpp b/src/creatures/players/vocations/vocation.hpp index a658e21ece7..0ad95ac0fc0 100644 --- a/src/creatures/players/vocations/vocation.hpp +++ b/src/creatures/players/vocations/vocation.hpp @@ -179,16 +179,17 @@ class Vocations { } bool loadFromXml(); + bool reload(); - Vocation* getVocation(uint16_t id); - const std::map &getVocations() const { + std::shared_ptr getVocation(uint16_t id); + const std::map> &getVocations() const { return vocationsMap; } uint16_t getVocationId(const std::string &name) const; uint16_t getPromotedVocation(uint16_t vocationId) const; private: - std::map vocationsMap; + std::map> vocationsMap; }; constexpr auto g_vocations = Vocations::getInstance; diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index 887877bb1eb..9a7246a86ec 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -52,6 +52,8 @@ bool GameReload::init(Reload_t reloadTypes) const { return reloadScripts(); case Reload_t::RELOAD_TYPE_GROUPS: return reloadGroups(); + case Reload_t::RELOAD_TYPE_VOCATIONS: + return reloadVocations(); default: return false; } @@ -201,3 +203,9 @@ bool GameReload::reloadGroups() const { logReloadStatus("Groups", result); return result; } + +bool GameReload::reloadVocations() const { + const bool result = g_vocations().reload(); + logReloadStatus("Vocations", result); + return result; +} diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp index e2f3789cfde..45ff35c01a2 100644 --- a/src/game/functions/game_reload.hpp +++ b/src/game/functions/game_reload.hpp @@ -29,6 +29,7 @@ enum class Reload_t : uint8_t { RELOAD_TYPE_RAIDS, RELOAD_TYPE_SCRIPTS, RELOAD_TYPE_GROUPS, + RELOAD_TYPE_VOCATIONS, // Every is last RELOAD_TYPE_LAST @@ -65,6 +66,7 @@ class GameReload : public Game { bool reloadRaids() const; bool reloadScripts() const; bool reloadGroups() const; + bool reloadVocations() const; }; constexpr auto g_gameReload = GameReload::getInstance; diff --git a/src/game/game.cpp b/src/game/game.cpp index 0dfba6f6724..4e83325b63d 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8330,12 +8330,12 @@ std::string Game::generateVocationConditionHighscore(uint32_t vocation) { const auto vocationsMap = g_vocations().getVocations(); for (const auto &it : vocationsMap) { const auto &voc = it.second; - if (voc.getFromVocation() == vocation) { + if (voc->getFromVocation() == vocation) { if (firstVocation) { - queryPart << " WHERE `vocation` = " << voc.getId(); + queryPart << " WHERE `vocation` = " << voc->getId(); firstVocation = false; } else { - queryPart << " OR `vocation` = " << voc.getId(); + queryPart << " OR `vocation` = " << voc->getId(); } } } diff --git a/src/items/item.cpp b/src/items/item.cpp index b51c076d80b..eea07a4e5dd 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -2315,7 +2315,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std:: s << " (\"" << it.runeSpellName << "\"). " << (it.stackable && tmpSubType > 1 ? "They" : "It") << " can only be used by "; const VocSpellMap &vocMap = rune->getVocMap(); - std::vector showVocMap; + std::vector> showVocMap; // vocations are usually listed with the unpromoted and promoted version, the latter being // hidden from description, so `total / 2` is most likely the amount of vocations to be shown. diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 621023e5c52..94da8f4ffbc 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1384,13 +1384,13 @@ int PlayerFunctions::luaPlayerSetVocation(lua_State* L) { return 1; } - Vocation* vocation; + std::shared_ptr vocation; if (isNumber(L, 2)) { vocation = g_vocations().getVocation(getNumber(L, 2)); } else if (isString(L, 2)) { vocation = g_vocations().getVocation(g_vocations().getVocationId(getString(L, 2))); } else if (isUserdata(L, 2)) { - vocation = getUserdata(L, 2); + vocation = getUserdataShared(L, 2); } else { vocation = nullptr; } diff --git a/src/lua/functions/creatures/player/vocation_functions.cpp b/src/lua/functions/creatures/player/vocation_functions.cpp index 5e95a827ff9..15cf888bf8a 100644 --- a/src/lua/functions/creatures/player/vocation_functions.cpp +++ b/src/lua/functions/creatures/player/vocation_functions.cpp @@ -21,7 +21,7 @@ int VocationFunctions::luaVocationCreate(lua_State* L) { vocationId = g_vocations().getVocationId(getString(L, 2)); } - Vocation* vocation = g_vocations().getVocation(vocationId); + std::shared_ptr vocation = g_vocations().getVocation(vocationId); if (vocation) { pushUserdata(L, vocation); setMetatable(L, -1, "Vocation"); @@ -33,7 +33,7 @@ int VocationFunctions::luaVocationCreate(lua_State* L) { int VocationFunctions::luaVocationGetId(lua_State* L) { // vocation:getId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getId()); } else { @@ -44,7 +44,7 @@ int VocationFunctions::luaVocationGetId(lua_State* L) { int VocationFunctions::luaVocationGetClientId(lua_State* L) { // vocation:getClientId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getClientId()); } else { @@ -55,7 +55,7 @@ int VocationFunctions::luaVocationGetClientId(lua_State* L) { int VocationFunctions::luaVocationGetBaseId(lua_State* L) { // vocation:getBaseId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseId()); } else { @@ -66,7 +66,7 @@ int VocationFunctions::luaVocationGetBaseId(lua_State* L) { int VocationFunctions::luaVocationGetName(lua_State* L) { // vocation:getName() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { pushString(L, vocation->getVocName()); } else { @@ -77,7 +77,7 @@ int VocationFunctions::luaVocationGetName(lua_State* L) { int VocationFunctions::luaVocationGetDescription(lua_State* L) { // vocation:getDescription() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { pushString(L, vocation->getVocDescription()); } else { @@ -88,7 +88,7 @@ int VocationFunctions::luaVocationGetDescription(lua_State* L) { int VocationFunctions::luaVocationGetRequiredSkillTries(lua_State* L) { // vocation:getRequiredSkillTries(skillType, skillLevel) - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { skills_t skillType = getNumber(L, 2); uint16_t skillLevel = getNumber(L, 3); @@ -101,7 +101,7 @@ int VocationFunctions::luaVocationGetRequiredSkillTries(lua_State* L) { int VocationFunctions::luaVocationGetRequiredManaSpent(lua_State* L) { // vocation:getRequiredManaSpent(magicLevel) - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { uint32_t magicLevel = getNumber(L, 2); lua_pushnumber(L, vocation->getReqMana(magicLevel)); @@ -113,7 +113,7 @@ int VocationFunctions::luaVocationGetRequiredManaSpent(lua_State* L) { int VocationFunctions::luaVocationGetCapacityGain(lua_State* L) { // vocation:getCapacityGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getCapGain()); } else { @@ -124,7 +124,7 @@ int VocationFunctions::luaVocationGetCapacityGain(lua_State* L) { int VocationFunctions::luaVocationGetHealthGain(lua_State* L) { // vocation:getHealthGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHPGain()); } else { @@ -135,7 +135,7 @@ int VocationFunctions::luaVocationGetHealthGain(lua_State* L) { int VocationFunctions::luaVocationGetHealthGainTicks(lua_State* L) { // vocation:getHealthGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHealthGainTicks()); } else { @@ -146,7 +146,7 @@ int VocationFunctions::luaVocationGetHealthGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetHealthGainAmount(lua_State* L) { // vocation:getHealthGainAmount() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHealthGainAmount()); } else { @@ -157,7 +157,7 @@ int VocationFunctions::luaVocationGetHealthGainAmount(lua_State* L) { int VocationFunctions::luaVocationGetManaGain(lua_State* L) { // vocation:getManaGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGain()); } else { @@ -168,7 +168,7 @@ int VocationFunctions::luaVocationGetManaGain(lua_State* L) { int VocationFunctions::luaVocationGetManaGainTicks(lua_State* L) { // vocation:getManaGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGainTicks()); } else { @@ -179,7 +179,7 @@ int VocationFunctions::luaVocationGetManaGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetManaGainAmount(lua_State* L) { // vocation:getManaGainAmount() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGainAmount()); } else { @@ -190,7 +190,7 @@ int VocationFunctions::luaVocationGetManaGainAmount(lua_State* L) { int VocationFunctions::luaVocationGetMaxSoul(lua_State* L) { // vocation:getMaxSoul() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getSoulMax()); } else { @@ -201,7 +201,7 @@ int VocationFunctions::luaVocationGetMaxSoul(lua_State* L) { int VocationFunctions::luaVocationGetSoulGainTicks(lua_State* L) { // vocation:getSoulGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getSoulGainTicks()); } else { @@ -212,7 +212,7 @@ int VocationFunctions::luaVocationGetSoulGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetBaseAttackSpeed(lua_State* L) { // vocation:getBaseAttackSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseAttackSpeed()); } else { @@ -223,7 +223,7 @@ int VocationFunctions::luaVocationGetBaseAttackSpeed(lua_State* L) { int VocationFunctions::luaVocationGetAttackSpeed(lua_State* L) { // vocation:getAttackSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getAttackSpeed()); } else { @@ -234,7 +234,7 @@ int VocationFunctions::luaVocationGetAttackSpeed(lua_State* L) { int VocationFunctions::luaVocationGetBaseSpeed(lua_State* L) { // vocation:getBaseSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseSpeed()); } else { @@ -245,7 +245,7 @@ int VocationFunctions::luaVocationGetBaseSpeed(lua_State* L) { int VocationFunctions::luaVocationGetDemotion(lua_State* L) { // vocation:getDemotion() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (!vocation) { lua_pushnil(L); return 1; @@ -257,7 +257,7 @@ int VocationFunctions::luaVocationGetDemotion(lua_State* L) { return 1; } - Vocation* demotedVocation = g_vocations().getVocation(fromId); + std::shared_ptr demotedVocation = g_vocations().getVocation(fromId); if (demotedVocation && demotedVocation != vocation) { pushUserdata(L, demotedVocation); setMetatable(L, -1, "Vocation"); @@ -269,7 +269,7 @@ int VocationFunctions::luaVocationGetDemotion(lua_State* L) { int VocationFunctions::luaVocationGetPromotion(lua_State* L) { // vocation:getPromotion() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (!vocation) { lua_pushnil(L); return 1; @@ -281,7 +281,7 @@ int VocationFunctions::luaVocationGetPromotion(lua_State* L) { return 1; } - Vocation* promotedVocation = g_vocations().getVocation(promotedId); + std::shared_ptr promotedVocation = g_vocations().getVocation(promotedId); if (promotedVocation && promotedVocation != vocation) { pushUserdata(L, promotedVocation); setMetatable(L, -1, "Vocation"); diff --git a/src/lua/functions/creatures/player/vocation_functions.hpp b/src/lua/functions/creatures/player/vocation_functions.hpp index 7205580f90f..0895f6ac8d6 100644 --- a/src/lua/functions/creatures/player/vocation_functions.hpp +++ b/src/lua/functions/creatures/player/vocation_functions.hpp @@ -14,7 +14,7 @@ class VocationFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Vocation", "", VocationFunctions::luaVocationCreate); + registerSharedClass(L, "Vocation", "", VocationFunctions::luaVocationCreate); registerMetaMethod(L, "Vocation", "__eq", VocationFunctions::luaUserdataCompare); registerMethod(L, "Vocation", "getId", VocationFunctions::luaVocationGetId); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 0d0d8e79f17..22a5ae614ba 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2144,12 +2144,12 @@ void ProtocolGame::sendHighscores(const std::vector &charact uint32_t selectedVocation = 0xFFFFFFFF; const auto vocationsMap = g_vocations().getVocations(); for (const auto &it : vocationsMap) { - const Vocation &vocation = it.second; - if (vocation.getFromVocation() == static_cast(vocation.getId())) { - msg.add(vocation.getFromVocation()); // Vocation Id - msg.addString(vocation.getVocName(), "ProtocolGame::sendHighscores - vocation.getVocName()"); // Vocation Name + const auto &vocation = it.second; + if (vocation->getFromVocation() == static_cast(vocation->getId())) { + msg.add(vocation->getFromVocation()); // Vocation Id + msg.addString(vocation->getVocName(), "ProtocolGame::sendHighscores - vocation.getVocName()"); // Vocation Name ++vocations; - if (vocation.getFromVocation() == vocationId) { + if (vocation->getFromVocation() == vocationId) { selectedVocation = vocationId; } } From 77368ae224f6eaf6e3e7f58fe62a644e2efc929f Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Thu, 25 Apr 2024 19:03:34 -0300 Subject: [PATCH 12/95] refactor: "requestLockerItems" for improved safety and clarity (#2565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a refactoring of the requestLockerItems function to enhance its safety, prevent potential infinite loops, and improve code clarity. Key changes include: • Loop Structure: Replaced the original do-while loop with a safer for loop that ensures all accesses are within vector bounds. • Loop Removal: Eliminated the second do-while loop that manipulated countSize, which could lead to infinite loops and overflow issues. • Simplified Item Handling: Streamlined the handling of items from stashToSend, directly updating lockerItems without unnecessary intermediate steps. • General Cleanup: Improved overall readability and maintainability of the code, making it easier to understand and less prone to errors. These modifications ensure that the function behaves as intended while being more robust and easier to debug. Additionally, a small problem was fixed that prevented the server from shutting down. Resolves #2561 --- src/canary_server.cpp | 1 + src/creatures/players/player.cpp | 50 ++++++++++---------------------- src/lib/thread/thread_pool.cpp | 8 +++-- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 0961bc93fde..c40534deb89 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -389,4 +389,5 @@ void CanaryServer::shutdown() { g_dispatcher().shutdown(); g_metrics().shutdown(); inject().shutdown(); + std::exit(0); } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 92285c2bca6..516ed7b5cfa 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6910,8 +6910,9 @@ std::shared_ptr Player::getItemFromDepotSearch(uint16_t itemId, const Posi return nullptr; } -std::pair>, std::map>> Player::requestLockerItems(std::shared_ptr depotLocker, bool sendToClient /*= false*/, uint8_t tier /*= 0*/) const { - if (depotLocker == nullptr) { +std::pair>, std::map>> +Player::requestLockerItems(std::shared_ptr depotLocker, bool sendToClient /*= false*/, uint8_t tier /*= 0*/) const { + if (!depotLocker) { g_logger().error("{} - Depot locker is nullptr", __FUNCTION__); return {}; } @@ -6920,24 +6921,18 @@ std::pair>, std::map> itemVector; std::vector> containers { depotLocker }; - size_t size = 0; - do { - std::shared_ptr container = containers[size]; - size++; + for (size_t i = 0; i < containers.size(); ++i) { + std::shared_ptr container = containers[i]; - for (std::shared_ptr item : container->getItemList()) { + for (const auto &item : container->getItemList()) { std::shared_ptr lockerContainers = item->getContainer(); if (lockerContainers && !lockerContainers->empty()) { containers.push_back(lockerContainers); continue; } - if (item->isStoreItem()) { - continue; - } - const ItemType &itemType = Item::items[item->getID()]; - if (itemType.wareId == 0) { + if (item->isStoreItem() || itemType.wareId == 0) { continue; } @@ -6945,37 +6940,24 @@ std::pair>, std::maphasMarketAttributes()) { - continue; - } - - if (!sendToClient && item->getTier() != tier) { + if (!item->hasMarketAttributes() || (!sendToClient && item->getTier() != tier)) { continue; } - (lockerItems[itemType.wareId])[item->getTier()] += Item::countByType(item, -1); + lockerItems[itemType.wareId][item->getTier()] += Item::countByType(item, -1); itemVector.push_back(item); } - } while (size < containers.size()); - StashItemList stashToSend = getStashItems(); - uint32_t countSize = 0; - for (auto [itemId, itemCount] : stashToSend) { - countSize += itemCount; } - do { - for (auto [itemId, itemCount] : stashToSend) { - const ItemType &itemType = Item::items[itemId]; - if (itemType.wareId == 0) { - continue; - } - - countSize = countSize - itemCount; - (lockerItems[itemType.wareId])[0] += itemCount; + StashItemList stashToSend = getStashItems(); + for (const auto &[itemId, itemCount] : stashToSend) { + const ItemType &itemType = Item::items[itemId]; + if (itemType.wareId != 0) { + lockerItems[itemType.wareId][0] += itemCount; } - } while (countSize > 0); + } - return std::make_pair(itemVector, lockerItems); + return { itemVector, lockerItems }; } std::pair>, uint16_t> Player::getLockerItemsAndCountById(const std::shared_ptr &depotLocker, uint8_t tier, uint16_t itemId) { diff --git a/src/lib/thread/thread_pool.cpp b/src/lib/thread/thread_pool.cpp index e01028810bc..3971d3a6a02 100644 --- a/src/lib/thread/thread_pool.cpp +++ b/src/lib/thread/thread_pool.cpp @@ -8,7 +8,10 @@ */ #include "pch.hpp" + #include "lib/thread/thread_pool.hpp" + +#include "game/game.hpp" #include "utils/tools.hpp" #ifndef DEFAULT_NUMBER_OF_THREADS @@ -65,7 +68,6 @@ void ThreadPool::shutdown() { while (status == std::future_status::timeout && std::chrono::steady_clock::now() - start < timeout) { tries++; if (tries > 5) { - logger.error("Thread pool shutdown timed out."); break; } for (auto &future : futures) { @@ -84,7 +86,9 @@ asio::io_context &ThreadPool::getIoContext() { void ThreadPool::addLoad(const std::function &load) { asio::post(ioService, [this, load]() { if (ioService.stopped()) { - logger.error("Shutting down, cannot execute task."); + if (g_game().getGameState() != GAME_STATE_SHUTDOWN) { + logger.error("Shutting down, cannot execute task."); + } return; } From 142066af378319684f7c9e980c020b021a64a579 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 25 Apr 2024 22:49:56 -0300 Subject: [PATCH 13/95] feat: not show royal costume or golden outfit if player doesn't has (#2527) Check if the player already has the royal or golden outfit to show in customize character windows. --- src/server/network/protocol/protocolgame.cpp | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 22a5ae614ba..13b328dd2cf 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6707,17 +6707,21 @@ void ProtocolGame::sendOutfitWindow() { msg.addByte(0x00); ++outfitSize; } else if (outfit->lookType == 1210 || outfit->lookType == 1211) { - msg.add(outfit->lookType); - msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); - msg.addByte(3); - msg.addByte(0x02); - ++outfitSize; + if (player->canWear(1210, 0) || player->canWear(1211, 0)) { + msg.add(outfit->lookType); + msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); + msg.addByte(3); + msg.addByte(0x02); + ++outfitSize; + } } else if (outfit->lookType == 1456 || outfit->lookType == 1457) { - msg.add(outfit->lookType); - msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); - msg.addByte(3); - msg.addByte(0x03); - ++outfitSize; + if (player->canWear(1456, 0) || player->canWear(1457, 0)) { + msg.add(outfit->lookType); + msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); + msg.addByte(3); + msg.addByte(0x03); + ++outfitSize; + } } else if (outfit->from == "store") { msg.add(outfit->lookType); msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); From 56793a0a6514ad3fb09771f22cd3b5f452ea7c76 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Silva <104630060+CarlosE-Dev@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:54:10 -0300 Subject: [PATCH 14/95] feat: The Lost Brother Quest (#2454) Implementation of the quest "The Lost Brother". It's a small quest initiated with the NPC Tarun, with one of the rewards being able to negotiate with the NPC. So, this part was also modified so that the negotiation can only be done once this quest is complete. --- data-otservbr-global/lib/core/quests.lua | 14 +++- data-otservbr-global/lib/core/storages.lua | 3 +- data-otservbr-global/npc/tarun.lua | 68 +++++++++++++++++++ .../adventurers_guild/warrior_skeleton.lua | 5 ++ .../creaturescripts/customs/freequests.lua | 2 + .../movement-find-remains.lua | 19 ++++++ 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua diff --git a/data-otservbr-global/lib/core/quests.lua b/data-otservbr-global/lib/core/quests.lua index ef61ee2549b..a35dcb15dea 100644 --- a/data-otservbr-global/lib/core/quests.lua +++ b/data-otservbr-global/lib/core/quests.lua @@ -5592,7 +5592,7 @@ if not Quests then }, [41] = { name = "Adventurers Guild", - startStorageId = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, + startStorageId = Storage.AdventurersGuild.QuestLine, startStorageValue = 1, missions = { [1] = { @@ -5606,6 +5606,18 @@ if not Quests then But the dragon hoards might justify the risks. You killed %d/50 dragons and dragon lords."):format(math.max(player:getStorageValue(Storage.AdventurersGuild.GreatDragonHunt.DragonCounter), 0)) end, }, + [2] = { + name = "The Lost Brother", + storageId = Storage.AdventurersGuild.TheLostBrother, + missionId = 11000, + startValue = 1, + endValue = 3, + states = { + [1] = "At the Kha'zeel Mountains you met the merchant Tarun. His brother has gone missing and was last seen following a beautiful woman into a palace. Tarun fears this woman might have been a demon.", + [2] = "You found the remains of Tarun's brother containing a message. Go back to Tarun and report his brother's last words.", + [3] = "You told Tarun about his brother's sad fate. He was very downhearted but gave you his sincere thanks. The beautiful asuri have taken a young man's life and the happiness of another one.", + }, + }, }, }, [42] = { diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua index 6ce29967451..fce8059f7d1 100644 --- a/data-otservbr-global/lib/core/storages.lua +++ b/data-otservbr-global/lib/core/storages.lua @@ -1570,6 +1570,8 @@ Storage = { WarriorSkeleton = 52146, DragonCounter = 52147, }, + QuestLine = 52148, + TheLostBrother = 52149, }, DreamersChallenge = { -- Reserved storage from 52160 - 52199 @@ -2534,7 +2536,6 @@ Storage = { NightmareTeddy = {}, PoacherCavesMiniWorldChange = {}, TheGreatDragonHunt = {}, - TheLostBrother = {}, TheTaintedSouls = {}, }, U10_90 = { -- update 10.90 - Reserved Storages 45201 - 45350 diff --git a/data-otservbr-global/npc/tarun.lua b/data-otservbr-global/npc/tarun.lua index 31f56602f81..9f0b8be5883 100644 --- a/data-otservbr-global/npc/tarun.lua +++ b/data-otservbr-global/npc/tarun.lua @@ -51,6 +51,74 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end +local function creatureSayCallback(npc, creature, type, message) + local player = Player(creature) + if not player then + return false + end + local playerId = player:getId() + + if not npcHandler:checkInteraction(npc, creature) then + return false + end + + local theLostBrotherStorage = player:getStorageValue(Storage.AdventurersGuild.TheLostBrother) + if MsgContains(message, "mission") then + if theLostBrotherStorage < 1 then + npcHandler:say({ + "My brother is missing. I fear, he went to this evil palace north of here. A place of great beauty, certainly filled with riches and luxury. But in truth it is a threshold to hell and demonesses are after his blood. ...", + "He is my brother, and I am deeply ashamed to admit but I don't dare to go there. Perhaps your heart is more courageous than mine. Would you go to see this place and search for my brother?", + }, npc, creature) + npcHandler:setTopic(playerId, 1) + elseif theLostBrotherStorage == 1 then + npcHandler:say("I hope you will find my brother.", npc, creature) + npcHandler:setTopic(playerId, 0) + elseif theLostBrotherStorage == 2 then + npcHandler:say({ + "So, he is dead as I feared. I warned him not to go with this woman, but he gave in to temptation. My heart darkens and moans. But you have my sincere thanks. ...", + "Without your help I would have stayed in the dark about his fate. Please, take this as a little recompense.", + }, npc, creature) + player:addItem(3039, 1) + player:addExperience(3000, true) + player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 3) + npcHandler:setTopic(playerId, 0) + end + elseif npcHandler:getTopic(playerId) == 1 then + if MsgContains(message, "yes") then + npcHandler:say("I thank you! This is more than I could hope!", npc, creature) + if theLostBrotherStorage < 1 then + player:setStorageValue(Storage.AdventurersGuild.QuestLine, 1) + end + player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 1) + elseif MsgContains(message, "no") then + npcHandler:say("As you wish.", npc, creature) + end + npcHandler:setTopic(playerId, 0) + end + + return true +end + +local function onTradeRequest(npc, creature) + local player = Player(creature) + if not player then + return false + end + local playerId = player:getId() + + if player:getStorageValue(Storage.AdventurersGuild.TheLostBrother) ~= 3 then + return false + end + + return true +end + +npcHandler:setMessage(MESSAGE_GREET, "Greetings!") +npcHandler:setMessage(MESSAGE_FAREWELL, "Farewell.") +npcHandler:setMessage(MESSAGE_SENDTRADE, "Of course, just have a look.") +npcHandler:setCallback(CALLBACK_ON_TRADE_REQUEST, onTradeRequest) +npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) +npcHandler:setMessage(MESSAGE_WALKAWAY, "Farewell.") npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) npcConfig.shop = { diff --git a/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua b/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua index a1aec80aaee..73a5d3f8acb 100644 --- a/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua +++ b/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua @@ -3,6 +3,11 @@ function adventurersWarriorSkeleton.onUse(player, item, fromPosition, target, to if player:getStorageValue(Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton) < 1 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have discovered a deceased warrior's skeleton. It seems he tried to hunt the dragons around here - and failed.") player:addItem(5882, 1) -- red dragon scale + + if player:getStorageValue(Storage.AdventurersGuild.QuestLine) < 1 then + player:setStorageValue(Storage.AdventurersGuild.QuestLine, 1) + end + player:setStorageValue(Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, 1) player:setStorageValue(Storage.AdventurersGuild.GreatDragonHunt.DragonCounter, 0) else diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua index ac8f4506c70..ad0e8d49b73 100644 --- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua +++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua @@ -275,8 +275,10 @@ local questTable = { { storage = Storage.Quest.U11_40.ThreatenedDreams.QuestLine, storageValue = 1 }, { storage = Storage.Quest.U11_40.ThreatenedDreams.Mission01[1], storageValue = 16 }, { storage = Storage.Quest.U11_40.ThreatenedDreams.Mission02.KroazurAccess, storageValue = 1 }, + { storage = Storage.AdventurersGuild.QuestLine, storageValue = 1 }, { storage = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, storageValue = 1 }, { storage = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, storageValue = 2 }, + { storage = Storage.AdventurersGuild.TheLostBrother, storageValue = 3 }, { storage = Storage.Quest.U10_55.Dawnport.Questline, storageValue = 1 }, { storage = Storage.Quest.U10_55.Dawnport.GoMain, storageValue = 1 }, { storage = Storage.ForgottenKnowledge.AccessDeath, storageValue = 1 }, diff --git a/data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua b/data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua new file mode 100644 index 00000000000..c2ddb8efcb7 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua @@ -0,0 +1,19 @@ +local findRemains = MoveEvent() + +function findRemains.onStepIn(creature, item, position, fromPosition) + local player = creature:getPlayer() + if not player then + return true + end + + if player:getStorageValue(Storage.AdventurersGuild.TheLostBrother) == 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You stumble over some old bones. Something is carved into the stone wall here: 'Tarun, my brother, you were right. She's evil.'") + player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 2) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN) + end + + return true +end + +findRemains:position(Position(32959, 32674, 4)) +findRemains:register() From e133c33b9a43ed7a3c413ee844120133a7e201fa Mon Sep 17 00:00:00 2001 From: Eduardo Augusto <38956084+duuh30@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:45:25 -0300 Subject: [PATCH 15/95] fix: use flask on familiar (#2571) --- src/game/game.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index 4e83325b63d..6670575d991 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3744,6 +3744,19 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin return; } } + + const std::shared_ptr monster = creature->getMonster(); + if (monster && monster->isFamiliar() && creature->getMaster()->getPlayer() == player && (it.isRune() || it.type == ITEM_TYPE_POTION)) { + player->setNextPotionAction(OTSYS_TIME() + g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL, __FUNCTION__)); + + if (it.isMultiUse()) { + player->sendUseItemCooldown(g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL, __FUNCTION__)); + } + + player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); + return; + } + Position toPos = creature->getPosition(); Position walkToPos = fromPos; ReturnValue ret = g_actions().canUse(player, fromPos); From a820101006406d6d28d8a676e1f4718c85e5887f Mon Sep 17 00:00:00 2001 From: Mirkaan <98285577+Mirkaanks@users.noreply.github.com> Date: Fri, 26 Apr 2024 09:34:58 -0300 Subject: [PATCH 16/95] fix: typo in gamestore function and remove cooldown for item usage while walking (#2485) --- data/modules/scripts/gamestore/init.lua | 4 ++-- src/creatures/players/player.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index e53bab813f0..7d9081df1fb 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -479,7 +479,7 @@ function parseBuyStoreOffer(playerId, msg) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE then GameStore.processSexChangePurchase(player) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then - GameStore.processExpBoostPuchase(player) + GameStore.processExpBoostPurchase(player) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HUNTINGSLOT then GameStore.processTaskHuntingThirdSlot(player) elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT then @@ -1742,7 +1742,7 @@ function GameStore.processSexChangePurchase(player) player:toggleSex() end -function GameStore.processExpBoostPuchase(player) +function GameStore.processExpBoostPurchase(player) local currentExpBoostTime = player:getExpBoostStamina() local expBoostCount = player:getStorageValue(GameStore.Storages.expBoostCount) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 516ed7b5cfa..1f0c9b45dce 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1925,8 +1925,7 @@ void Player::onWalk(Direction &dir) { Creature::onWalk(dir); setNextActionTask(nullptr); - setNextAction(OTSYS_TIME() + getStepDuration(dir)); - + g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } From 6094875b168baf64ea7e45d4c1166e5774ba4585 Mon Sep 17 00:00:00 2001 From: Karin Date: Sat, 27 Apr 2024 14:07:32 -0300 Subject: [PATCH 17/95] fix: exercise error log (#2575) --- data/scripts/actions/items/exercise_training_weapons.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/actions/items/exercise_training_weapons.lua b/data/scripts/actions/items/exercise_training_weapons.lua index 4a72f19e5c8..3c62d7c1183 100644 --- a/data/scripts/actions/items/exercise_training_weapons.lua +++ b/data/scripts/actions/items/exercise_training_weapons.lua @@ -133,7 +133,7 @@ end local exerciseTraining = Action() function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if not target or not target:getId() then + if not target or type(target) == "table" or not target:getId() then return true end From 36b185b2d2d79510a8695a45cbe3ff7658ae1598 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 27 Apr 2024 14:08:40 -0300 Subject: [PATCH 18/95] fix: otclient fixes and adjustments (#2573) --- src/creatures/creature.cpp | 3 ++- src/items/containers/container.cpp | 8 ++++---- src/server/network/connection/connection.cpp | 4 ++-- src/server/network/protocol/protocolgame.cpp | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 6101ff0473d..9684e03b6d2 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -805,7 +805,8 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared auto monster = getMonster(); if (monster && !monster->isRewardBoss()) { std::ostringstream lootMessage; - lootMessage << "Loot of " << getNameDescription() << ": " << corpseContainer->getContentDescription(player->getProtocolVersion() < 1200) << "."; + auto collorMessage = player->getProtocolVersion() < 1200 || player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX; + lootMessage << "Loot of " << getNameDescription() << ": " << corpseContainer->getContentDescription(collorMessage) << "."; auto suffix = corpseContainer->getAttribute(ItemAttribute_t::LOOTMESSAGE_SUFFIX); if (!suffix.empty()) { lootMessage << suffix; diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 7bd7dcef2c9..5d2ffd98769 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -218,7 +218,7 @@ std::string Container::getContentDescription(bool oldProtocol) { return getContentDescription(os, oldProtocol).str(); } -std::ostringstream &Container::getContentDescription(std::ostringstream &os, bool oldProtocol) { +std::ostringstream &Container::getContentDescription(std::ostringstream &os, bool sendColoredMessage) { bool firstitem = true; for (ContainerIterator it = iterator(); it.hasNext(); it.advance()) { std::shared_ptr item = *it; @@ -234,10 +234,10 @@ std::ostringstream &Container::getContentDescription(std::ostringstream &os, boo os << ", "; } - if (oldProtocol) { - os << item->getNameDescription(); - } else { + if (sendColoredMessage) { os << "{" << item->getID() << "|" << item->getNameDescription() << "}"; + } else { + os << item->getNameDescription(); } } diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index cc27eb0e9eb..1c575ff00d0 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -399,9 +399,9 @@ void Connection::handleTimeout(ConnectionWeak_ptr connectionWeak, const std::err if (auto connection = connectionWeak.lock()) { if (!error) { - g_logger().warn("Connection Timeout, IP: {}", convertIPToString(connection->getIP())); + g_logger().debug("Connection Timeout, IP: {}", convertIPToString(connection->getIP())); } else { - g_logger().warn("Connection Timeout or error: {}, IP: {}", error.message(), convertIPToString(connection->getIP())); + g_logger().debug("Connection Timeout or error: {}, IP: {}", error.message(), convertIPToString(connection->getIP())); } connection->close(FORCE_CLOSE); } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 13b328dd2cf..22b032f5bc4 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -8772,7 +8772,7 @@ void ProtocolGame::parseSaveWheel(NetworkMessage &msg) { } void ProtocolGame::sendDisableLoginMusic() { - if (oldProtocol) { + if (oldProtocol || !player || player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { return; } From 8b898176eac5a8059b4e6ca65eca396bf450a2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Mon, 29 Apr 2024 12:38:28 -0300 Subject: [PATCH 19/95] hotfix: brain head can be killed more than once - missing reference to config table (#2581) --- .../actions/quests/feaster_of_souls/portal_brain_head.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua index d06ccbefb70..aebc0b3ca7a 100644 --- a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua +++ b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua @@ -103,26 +103,26 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition) end local player = creature if player:getLevel() < config.requiredLevel then - player:teleportTo(exitPosition, true) + player:teleportTo(config.exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to be level " .. config.requiredLevel .. " or higher.") return true end if locked then - player:teleportTo(exitPosition, true) + player:teleportTo(config.exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. config.bossName .. ".") return false end if zone:countPlayers(IgnoredByMonsters) >= 5 then - player:teleportTo(exitPosition, true) + player:teleportTo(config.exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The boss room is full.") return false end local timeLeft = player:getBossCooldown(config.bossName) - os.time() if timeLeft > 0 then - player:teleportTo(exitPosition, true) + player:teleportTo(config.exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. getTimeInWords(timeLeft) .. " to face " .. config.bossName .. " again!") player:getPosition():sendMagicEffect(CONST_ME_POFF) From 7c9b8d54fbc9d1d071347358262a00532f0b1705 Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Mon, 29 Apr 2024 12:39:56 -0300 Subject: [PATCH 20/95] fix: xp boost time granted by reward daily (#2586) --- data/events/scripts/player.lua | 36 +++++++++++-------- .../scripts/daily_reward/daily_reward.lua | 11 ++++-- data/modules/scripts/gamestore/init.lua | 8 ++--- src/creatures/players/player.cpp | 2 +- src/creatures/players/player.hpp | 26 +++++++------- src/io/functions/iologindata_load_player.cpp | 4 +-- src/io/functions/iologindata_save_player.cpp | 4 +-- .../creatures/player/player_functions.cpp | 28 +++++++-------- .../creatures/player/player_functions.hpp | 16 ++++----- src/server/network/protocol/protocolgame.cpp | 10 +++--- 10 files changed, 80 insertions(+), 65 deletions(-) diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index 35e0bb17e7f..ec1a92f3528 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -140,8 +140,8 @@ local function useStaminaXpBoost(player) return false end - local staminaMinutes = player:getExpBoostStamina() / 60 - if staminaMinutes == 0 then + local xpBoostMinutes = player:getXpBoostTime() / 60 + if xpBoostMinutes == 0 then return end @@ -156,18 +156,26 @@ local function useStaminaXpBoost(player) return end + local xpBoostLeftMinutesByDailyReward = player:kv():get("daily-reward-xp-boost") or 0 if timePassed > 60 then - if staminaMinutes > 2 then - staminaMinutes = staminaMinutes - 2 + if xpBoostMinutes > 2 then + xpBoostMinutes = xpBoostMinutes - 2 + if xpBoostLeftMinutesByDailyReward > 2 then + player:kv():set("daily-reward-xp-boost", xpBoostLeftMinutesByDailyReward - 2) + end else - staminaMinutes = 0 + xpBoostMinutes = 0 + player:kv():remove("daily-reward-xp-boost") end _G.NextUseXpStamina[playerId] = currentTime + 120 else - staminaMinutes = staminaMinutes - 1 + xpBoostMinutes = xpBoostMinutes - 1 + if xpBoostLeftMinutesByDailyReward > 0 then + player:kv():set("daily-reward-xp-boost", xpBoostLeftMinutesByDailyReward - 1) + end _G.NextUseXpStamina[playerId] = currentTime + 60 end - player:setExpBoostStamina(staminaMinutes * 60) + player:setXpBoostTime(xpBoostMinutes * 60) end local function useConcoctionTime(player) @@ -519,14 +527,14 @@ function Player:onGainExperience(target, exp, rawExp) self:addCondition(soulCondition) end - -- Store Bonus - useStaminaXpBoost(self) -- Use store boost stamina + -- XP Boost Bonus + useStaminaXpBoost(self) -- Use stamina XP boost (store or daily reward) - local Boost = self:getExpBoostStamina() - local stillHasBoost = Boost > 0 - local storeXpBoostAmount = stillHasBoost and self:getStoreXpBoost() or 0 + local xpBoostTimeLeft = self:getXpBoostTime() + local stillHasXpBoost = xpBoostTimeLeft > 0 + local xpBoostPercent = stillHasXpBoost and self:getXpBoostPercent() or 0 - self:setStoreXpBoost(storeXpBoostAmount) + self:setXpBoostPercent(xpBoostPercent) -- Stamina Bonus local staminaBonusXp = 1 @@ -564,7 +572,7 @@ function Player:onGainExperience(target, exp, rawExp) local lowLevelBonuxExp = self:getFinalLowLevelBonus() local baseRate = self:getFinalBaseRateExperience() - return (exp + (exp * (storeXpBoostAmount / 100) + (exp * (lowLevelBonuxExp / 100)))) * staminaBonusXp * baseRate + return (exp + (exp * (xpBoostPercent / 100) + (exp * (lowLevelBonuxExp / 100)))) * staminaBonusXp * baseRate end function Player:onLoseExperience(exp) diff --git a/data/modules/scripts/daily_reward/daily_reward.lua b/data/modules/scripts/daily_reward/daily_reward.lua index 1f0e805704c..09b927cedda 100644 --- a/data/modules/scripts/daily_reward/daily_reward.lua +++ b/data/modules/scripts/daily_reward/daily_reward.lua @@ -476,8 +476,15 @@ function Player.selectDailyReward(self, msg) end dailyRewardMessage = "Picked items: " .. description elseif dailyTable.type == DAILY_REWARD_TYPE_XP_BOOST then - self:setExpBoostStamina(self:getExpBoostStamina() + (rewardCount * 60)) - self:setStoreXpBoost(50) + local rewardCountReviewed = rewardCount + local xpBoostLeftMinutes = self:kv():get("daily-reward-xp-boost") or 0 + if xpBoostLeftMinutes > 0 then + rewardCountReviewed = rewardCountReviewed - xpBoostLeftMinutes + end + + self:setXpBoostTime(self:getXpBoostTime() + (rewardCountReviewed * 60)) + self:kv():set("daily-reward-xp-boost", rewardCount) + self:setXpBoostPercent(50) dailyRewardMessage = "Picked reward: XP Bonus for " .. rewardCount .. " minutes." elseif dailyTable.type == DAILY_REWARD_TYPE_PREY_REROLL then self:addPreyCards(rewardCount) diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 7d9081df1fb..ba7398d9d3e 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -732,7 +732,7 @@ function Player.canBuyOffer(self, offer) disabled = 1 disabledReason = "You can't buy XP Boost for today." end - if self:getExpBoostStamina() > 0 then + if self:getXpBoostTime() > 0 then disabled = 1 disabledReason = "You already have an active XP boost." end @@ -1743,11 +1743,11 @@ function GameStore.processSexChangePurchase(player) end function GameStore.processExpBoostPurchase(player) - local currentExpBoostTime = player:getExpBoostStamina() + local currentXpBoostTime = player:getXpBoostTime() local expBoostCount = player:getStorageValue(GameStore.Storages.expBoostCount) - player:setStoreXpBoost(50) - player:setExpBoostStamina(currentExpBoostTime + 3600) + player:setXpBoostPercent(50) + player:setXpBoostTime(currentXpBoostTime + 3600) if expBoostCount == -1 or expBoostCount == 6 then expBoostCount = 1 diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 1f0c9b45dce..a81981bd20c 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1925,7 +1925,7 @@ void Player::onWalk(Direction &dir) { Creature::onWalk(dir); setNextActionTask(nullptr); - + g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 70c8c05aecd..c2a9f29831a 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1807,11 +1807,11 @@ class Player final : public Creature, public Cylinder, public Bankable { void setGrindingXpBoost(uint16_t value) { grindingXpBoost = std::min(std::numeric_limits::max(), value); } - uint16_t getStoreXpBoost() const { - return storeXpBoost; + uint16_t getXpBoostPercent() const { + return xpBoostPercent; } - void setStoreXpBoost(uint16_t exp) { - storeXpBoost = exp; + void setXpBoostPercent(uint16_t percent) { + xpBoostPercent = percent; } uint16_t getStaminaXpBoost() const { return staminaXpBoost; @@ -1820,17 +1820,17 @@ class Player final : public Creature, public Cylinder, public Bankable { staminaXpBoost = std::min(std::numeric_limits::max(), value); } - void setExpBoostStamina(uint16_t stamina) { - // only allow stamina boosts of 12 hours or less - if (stamina > 12 * 3600) { - expBoostStamina = 12 * 3600; + void setXpBoostTime(uint16_t timeLeft) { + // only allow time boosts of 12 hours or less + if (timeLeft > 12 * 3600) { + xpBoostTime = 12 * 3600; return; } - expBoostStamina = stamina; + xpBoostTime = timeLeft; } - uint16_t getExpBoostStamina() { - return expBoostStamina; + uint16_t getXpBoostTime() { + return xpBoostTime; } int32_t getIdleTime() const { @@ -2811,7 +2811,7 @@ class Player final : public Creature, public Cylinder, public Bankable { int32_t m_deathTime = 0; uint32_t coinBalance = 0; uint32_t coinTransferableBalance = 0; - uint16_t expBoostStamina = 0; + uint16_t xpBoostTime = 0; uint8_t randomMount = 0; uint16_t lastStatsTrainingTime = 0; @@ -2821,7 +2821,7 @@ class Player final : public Creature, public Cylinder, public Bankable { uint16_t baseXpGain = 100; uint16_t voucherXpBoost = 0; uint16_t grindingXpBoost = 0; - uint16_t storeXpBoost = 0; + uint16_t xpBoostPercent = 0; uint16_t staminaXpBoost = 100; int16_t lastDepotId = -1; StashItemList stashItems; // [ItemID] = amount diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index d4a4f33774d..2c3cbf48fea 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -177,8 +177,8 @@ bool IOLoginDataLoad::loadPlayerFirst(std::shared_ptr player, DBResult_p } player->staminaMinutes = result->getNumber("stamina"); - player->setStoreXpBoost(result->getNumber("xpboost_value")); - player->setExpBoostStamina(result->getNumber("xpboost_stamina")); + player->setXpBoostPercent(result->getNumber("xpboost_value")); + player->setXpBoostTime(result->getNumber("xpboost_stamina")); player->setManaShield(result->getNumber("manashield")); player->setMaxManaShield(result->getNumber("max_manashield")); diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 7bc1b34c864..82ba286d622 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -292,8 +292,8 @@ bool IOLoginDataSave::savePlayerFirst(std::shared_ptr player) { query << "`skill_mana_leech_amount_tries` = " << player->skills[SKILL_MANA_LEECH_AMOUNT].tries << ","; query << "`manashield` = " << player->getManaShield() << ","; query << "`max_manashield` = " << player->getMaxManaShield() << ","; - query << "`xpboost_value` = " << player->getStoreXpBoost() << ","; - query << "`xpboost_stamina` = " << player->getExpBoostStamina() << ","; + query << "`xpboost_value` = " << player->getXpBoostPercent() << ","; + query << "`xpboost_stamina` = " << player->getXpBoostTime() << ","; query << "`quickloot_fallback` = " << (player->quickLootFallbackToMainContainer ? 1 : 0) << ","; if (!player->isOffline()) { diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 94da8f4ffbc..94100a1f406 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3219,23 +3219,23 @@ int PlayerFunctions::luaPlayerSetGrindingXpBoost(lua_State* L) { return 1; } -int PlayerFunctions::luaPlayerGetStoreXpBoost(lua_State* L) { - // player:getStoreXpBoost() +int PlayerFunctions::luaPlayerGetXpBoostPercent(lua_State* L) { + // player:getXpBoostPercent() std::shared_ptr player = getUserdataShared(L, 1); if (player) { - lua_pushnumber(L, player->getStoreXpBoost()); + lua_pushnumber(L, player->getXpBoostPercent()); } else { lua_pushnil(L); } return 1; } -int PlayerFunctions::luaPlayerSetStoreXpBoost(lua_State* L) { - // player:setStoreXpBoost(value) +int PlayerFunctions::luaPlayerSetXpBoostPercent(lua_State* L) { + // player:setXpBoostPercent(value) std::shared_ptr player = getUserdataShared(L, 1); if (player) { - uint16_t experience = getNumber(L, 2); - player->setStoreXpBoost(experience); + uint16_t percent = getNumber(L, 2); + player->setXpBoostPercent(percent); pushBoolean(L, true); } else { lua_pushnil(L); @@ -3267,12 +3267,12 @@ int PlayerFunctions::luaPlayerSetStaminaXpBoost(lua_State* L) { return 1; } -int PlayerFunctions::luaPlayerSetExpBoostStamina(lua_State* L) { - // player:setExpBoostStamina(percent) +int PlayerFunctions::luaPlayerSetXpBoostTime(lua_State* L) { + // player:setXpBoostTime(timeLeft) std::shared_ptr player = getUserdataShared(L, 1); if (player) { - uint16_t stamina = getNumber(L, 2); - player->setExpBoostStamina(stamina); + uint16_t timeLeft = getNumber(L, 2); + player->setXpBoostTime(timeLeft); player->sendStats(); pushBoolean(L, true); } else { @@ -3281,11 +3281,11 @@ int PlayerFunctions::luaPlayerSetExpBoostStamina(lua_State* L) { return 1; } -int PlayerFunctions::luaPlayerGetExpBoostStamina(lua_State* L) { - // player:getExpBoostStamina() +int PlayerFunctions::luaPlayerGetXpBoostTime(lua_State* L) { + // player:getXpBoostTime() std::shared_ptr player = getUserdataShared(L, 1); if (player) { - lua_pushnumber(L, player->getExpBoostStamina()); + lua_pushnumber(L, player->getXpBoostTime()); } else { lua_pushnil(L); } diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index fafc99ff955..2be6e5076cd 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -273,12 +273,12 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "setVoucherXpBoost", PlayerFunctions::luaPlayerSetVoucherXpBoost); registerMethod(L, "Player", "getGrindingXpBoost", PlayerFunctions::luaPlayerGetGrindingXpBoost); registerMethod(L, "Player", "setGrindingXpBoost", PlayerFunctions::luaPlayerSetGrindingXpBoost); - registerMethod(L, "Player", "getStoreXpBoost", PlayerFunctions::luaPlayerGetStoreXpBoost); - registerMethod(L, "Player", "setStoreXpBoost", PlayerFunctions::luaPlayerSetStoreXpBoost); + registerMethod(L, "Player", "getXpBoostPercent", PlayerFunctions::luaPlayerGetXpBoostPercent); + registerMethod(L, "Player", "setXpBoostPercent", PlayerFunctions::luaPlayerSetXpBoostPercent); registerMethod(L, "Player", "getStaminaXpBoost", PlayerFunctions::luaPlayerGetStaminaXpBoost); registerMethod(L, "Player", "setStaminaXpBoost", PlayerFunctions::luaPlayerSetStaminaXpBoost); - registerMethod(L, "Player", "getExpBoostStamina", PlayerFunctions::luaPlayerGetExpBoostStamina); - registerMethod(L, "Player", "setExpBoostStamina", PlayerFunctions::luaPlayerSetExpBoostStamina); + registerMethod(L, "Player", "getXpBoostTime", PlayerFunctions::luaPlayerGetXpBoostTime); + registerMethod(L, "Player", "setXpBoostTime", PlayerFunctions::luaPlayerSetXpBoostTime); registerMethod(L, "Player", "getIdleTime", PlayerFunctions::luaPlayerGetIdleTime); registerMethod(L, "Player", "getFreeBackpackSlots", PlayerFunctions::luaPlayerGetFreeBackpackSlots); @@ -627,12 +627,12 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerSetVoucherXpBoost(lua_State* L); static int luaPlayerGetGrindingXpBoost(lua_State* L); static int luaPlayerSetGrindingXpBoost(lua_State* L); - static int luaPlayerGetStoreXpBoost(lua_State* L); - static int luaPlayerSetStoreXpBoost(lua_State* L); + static int luaPlayerGetXpBoostPercent(lua_State* L); + static int luaPlayerSetXpBoostPercent(lua_State* L); static int luaPlayerGetStaminaXpBoost(lua_State* L); static int luaPlayerSetStaminaXpBoost(lua_State* L); - static int luaPlayerGetExpBoostStamina(lua_State* L); - static int luaPlayerSetExpBoostStamina(lua_State* L); + static int luaPlayerGetXpBoostTime(lua_State* L); + static int luaPlayerSetXpBoostTime(lua_State* L); static int luaPlayerGetIdleTime(lua_State* L); static int luaPlayerGetFreeBackpackSlots(lua_State* L); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 22b032f5bc4..1748699ab34 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3390,9 +3390,9 @@ void ProtocolGame::sendCyclopediaCharacterGeneralStats() { msg.addByte(player->getLevelPercent()); msg.add(player->getBaseXpGain()); // BaseXPGainRate msg.add(player->getGrindingXpBoost()); // LowLevelBonus - msg.add(player->getStoreXpBoost()); // XPBoost + msg.add(player->getXpBoostPercent()); // XPBoost msg.add(player->getStaminaXpBoost()); // StaminaMultiplier(100=x1.0) - msg.add(player->getExpBoostStamina()); // xpBoostRemainingTime + msg.add(player->getXpBoostTime()); // xpBoostRemainingTime msg.addByte(0x01); // canBuyXpBoost msg.add(std::min(player->getHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); @@ -3802,7 +3802,7 @@ void ProtocolGame::sendCyclopediaCharacterStoreSummary() { msg.addByte(CYCLOPEDIA_CHARACTERINFO_STORESUMMARY); msg.addByte(0x00); // Remaining Store Xp Boost Time - msg.add(player->getExpBoostStamina()); + msg.add(player->getXpBoostTime()); // RemainingDailyRewardXpBoostTime msg.add(0); msg.addByte(0x00); @@ -7303,7 +7303,7 @@ void ProtocolGame::AddPlayerStats(NetworkMessage &msg) { } msg.add(player->getGrindingXpBoost()); // low level bonus - msg.add(player->getStoreXpBoost()); // xp boost + msg.add(player->getXpBoostPercent()); // xp boost msg.add(player->getStaminaXpBoost()); // stamina multiplier (100 = 1.0x) if (!oldProtocol) { @@ -7329,7 +7329,7 @@ void ProtocolGame::AddPlayerStats(NetworkMessage &msg) { msg.add(player->getOfflineTrainingTime() / 60 / 1000); - msg.add(player->getExpBoostStamina()); // xp boost time (seconds) + msg.add(player->getXpBoostTime()); // xp boost time (seconds) msg.addByte(1); // enables exp boost in the store if (!oldProtocol) { From 98e927dac27e2b4ba5c42ad486d007fa7c70acce Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Mon, 29 Apr 2024 14:34:25 -0300 Subject: [PATCH 21/95] fix: gem atelier critical extra damage not being applied (#2585) --- src/creatures/players/player.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index a81981bd20c..149f41380ec 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5371,6 +5371,7 @@ uint16_t Player::getSkillLevel(skills_t skill) const { } else if (skill == SKILL_MANA_LEECH_AMOUNT) { skillLevel += m_wheelPlayer->getStat(WheelStat_t::MANA_LEECH); } else if (skill == SKILL_CRITICAL_HIT_DAMAGE) { + skillLevel += m_wheelPlayer->getStat(WheelStat_t::CRITICAL_DAMAGE); skillLevel += m_wheelPlayer->getMajorStatConditional("Combat Mastery", WheelMajor_t::CRITICAL_DMG_2); skillLevel += m_wheelPlayer->getMajorStatConditional("Ballistic Mastery", WheelMajor_t::CRITICAL_DMG); skillLevel += m_wheelPlayer->checkAvatarSkill(WheelAvatarSkill_t::CRITICAL_DAMAGE); From e1faa1f5bc1e7eb53982c6b20431a23e5adfe3f7 Mon Sep 17 00:00:00 2001 From: Aluisio Penna Date: Tue, 30 Apr 2024 18:15:14 -0300 Subject: [PATCH 22/95] fix: equipping two-handed distance weapons via action bar removes quiver if equipped (#2587) --- src/game/game.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 6670575d991..b1d76f9f760 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3199,7 +3199,13 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = } else { 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 (slotPosition & SLOTP_LEFT && rightItem && (slotPosition & SLOTP_TWO_HAND)) { + if ( + (slotPosition & SLOTP_LEFT) + && (slotPosition & SLOTP_TWO_HAND) + && rightItem + && !(it.weaponType == WEAPON_DISTANCE) + && !rightItem->isQuiver() + ) { ret = internalCollectManagedItems(player, rightItem, getObjectCategory(rightItem), false); } From c3076740f29eb7df44837ec3052ca4c2ef72594a Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Tue, 30 Apr 2024 18:16:59 -0300 Subject: [PATCH 23/95] fix: oldProtocol compatibility in sendTextMessage (#2510) --- src/server/network/protocol/protocolgame.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 1748699ab34..c822ff4af7c 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -4088,11 +4088,11 @@ void ProtocolGame::sendTextMessage(const TextMessage &message) { break; } case MESSAGE_MARKET: { - internalType = MESSAGE_GAME_HIGHLIGHT; + internalType = MESSAGE_EVENT_ADVANCE; break; } case MESSAGE_MANA: { - internalType = MESSAGE_THANK_YOU; + internalType = MESSAGE_HEALED; break; } case MESSAGE_BEYOND_LAST: { @@ -4100,7 +4100,7 @@ void ProtocolGame::sendTextMessage(const TextMessage &message) { break; } case MESSAGE_ATTENTION: { - internalType = MESSAGE_DAMAGE_DEALT; + internalType = MESSAGE_EVENT_ADVANCE; break; } case MESSAGE_BOOSTED_CREATURE: { @@ -4143,11 +4143,9 @@ void ProtocolGame::sendTextMessage(const TextMessage &message) { } case MESSAGE_HEALED: case MESSAGE_HEALED_OTHERS: { - if (!oldProtocol) { - msg.addPosition(message.position); - msg.add(message.primary.value); - msg.addByte(message.primary.color); - } + msg.addPosition(message.position); + msg.add(message.primary.value); + msg.addByte(message.primary.color); break; } case MESSAGE_EXPERIENCE: From 9f74b27eff36c4e2774e635dc52baf8655e47c6b Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Thu, 2 May 2024 22:38:51 -0300 Subject: [PATCH 24/95] fix: colored loot on cip client and disable chain system (#2600) --- config.lua.dist | 4 ++-- src/creatures/creature.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 0507b652e29..b731fadbcd7 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -397,7 +397,6 @@ resetSessionsOnStartup = false -- Misc. -- NOTE: experienceDisplayRates: set to false to ignore exp rate or true to include exp rate -- NOTE: disableLegacyRaids: set to true to disable legacy XML raids --- NOTE: combatChainDelay: set to minimum 50 miliseconds allowChangeOutfit = true toggleMountInProtectionZone = false freePremium = false @@ -430,7 +429,8 @@ maxElementalResistance = 200 maxDamageReflection = 200 -- Chain system -toggleChainSystem = true +-- NOTE: combatChainDelay: set to minimum 50 miliseconds +toggleChainSystem = false combatChainDelay = 50 combatChainTargets = 5 combatChainSkillFormulaAxe = 0.9 diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 9684e03b6d2..fd78e33ecf4 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -805,7 +805,7 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared auto monster = getMonster(); if (monster && !monster->isRewardBoss()) { std::ostringstream lootMessage; - auto collorMessage = player->getProtocolVersion() < 1200 || player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX; + auto collorMessage = player->getProtocolVersion() > 1200 && player->getOperatingSystem() < CLIENTOS_OTCLIENT_LINUX; lootMessage << "Loot of " << getNameDescription() << ": " << corpseContainer->getContentDescription(collorMessage) << "."; auto suffix = corpseContainer->getAttribute(ItemAttribute_t::LOOTMESSAGE_SUFFIX); if (!suffix.empty()) { From 9efbe4c10f19555208bbb755cb3dc09cc1ac8e99 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Sat, 4 May 2024 15:16:32 -0300 Subject: [PATCH 25/95] feature: badge system (#2533) --- data/scripts/talkactions/god/manage_badge.lua | 33 ++++ src/canary_server.cpp | 1 + src/creatures/CMakeLists.txt | 1 + .../players/cyclopedia/player_badge.cpp | 156 ++++++++++++++++++ .../players/cyclopedia/player_badge.hpp | 65 ++++++++ src/creatures/players/player.cpp | 59 +++++-- src/creatures/players/player.hpp | 22 +++ src/creatures/players/wheel/player_wheel.cpp | 33 +--- src/creatures/players/wheel/player_wheel.hpp | 1 - src/game/game.cpp | 93 +++++++++-- src/game/game.hpp | 11 ++ src/game/game_definitions.hpp | 26 +++ src/io/functions/iologindata_load_player.cpp | 1 + .../functions/core/game/game_functions.cpp | 1 + .../creatures/player/player_functions.cpp | 14 ++ .../creatures/player/player_functions.hpp | 5 + src/server/network/protocol/protocolgame.cpp | 146 +++++++++++----- src/server/network/protocol/protocolgame.hpp | 4 + vcproj/canary.vcxproj | 4 +- 19 files changed, 573 insertions(+), 103 deletions(-) create mode 100644 data/scripts/talkactions/god/manage_badge.lua create mode 100644 src/creatures/players/cyclopedia/player_badge.cpp create mode 100644 src/creatures/players/cyclopedia/player_badge.hpp diff --git a/data/scripts/talkactions/god/manage_badge.lua b/data/scripts/talkactions/god/manage_badge.lua new file mode 100644 index 00000000000..f2899a0d592 --- /dev/null +++ b/data/scripts/talkactions/god/manage_badge.lua @@ -0,0 +1,33 @@ +local addBadge = TalkAction("/addbadge") + +function addBadge.onSay(player, words, param) + -- create log + logCommand(player, words, param) + + if param == "" then + player:sendCancelMessage("Command param required.") + return true + end + + local split = param:split(",") + if not split[2] then + player:sendCancelMessage("Insufficient parameters. Usage: /addbadge playerName, badgeID") + return true + end + + local target = Player(split[1]) + if not target then + player:sendCancelMessage("A player with that name is not online.") + return true + end + + -- Trim left + split[2] = split[2]:gsub("^%s*(.-)$", "%1") + local id = tonumber(split[2]) + target:addBadge(id) + return true +end + +addBadge:separator(" ") +addBadge:groupType("god") +addBadge:register() diff --git a/src/canary_server.cpp b/src/canary_server.cpp index c40534deb89..bfabbf51ab8 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -376,6 +376,7 @@ void CanaryServer::loadModules() { g_game().loadBoostedCreature(); g_ioBosstiary().loadBoostedBoss(); g_ioprey().initializeTaskHuntOptions(); + g_game().logCyclopediaStats(); } void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) { diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index f3dedf3a8f3..630812c7996 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE players/storages/storages.cpp players/player.cpp players/achievement/player_achievement.cpp + players/cyclopedia/player_badge.cpp players/wheel/player_wheel.cpp players/wheel/wheel_gems.cpp players/vocations/vocation.cpp diff --git a/src/creatures/players/cyclopedia/player_badge.cpp b/src/creatures/players/cyclopedia/player_badge.cpp new file mode 100644 index 00000000000..9b892a6164c --- /dev/null +++ b/src/creatures/players/cyclopedia/player_badge.cpp @@ -0,0 +1,156 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "player_badge.hpp" + +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "kv/kv.hpp" + +PlayerBadge::PlayerBadge(Player &player) : + m_player(player) { } + +bool PlayerBadge::hasBadge(uint8_t id) const { + if (id == 0) { + return false; + } + + if (auto it = std::find_if(m_badgesUnlocked.begin(), m_badgesUnlocked.end(), [id](auto badge_it) { + return badge_it.first.m_id == id; + }); + it != m_badgesUnlocked.end()) { + return true; + } + + return false; +} + +bool PlayerBadge::add(uint8_t id, uint32_t timestamp /* = 0*/) { + if (hasBadge(id)) { + return false; + } + + const Badge &badge = g_game().getBadgeById(id); + if (badge.m_id == 0) { + return false; + } + + int toSaveTimeStamp = timestamp != 0 ? timestamp : (OTSYS_TIME() / 1000); + getUnlockedKV()->set(badge.m_name, toSaveTimeStamp); + m_badgesUnlocked.emplace_back(badge, toSaveTimeStamp); + m_badgesUnlocked.shrink_to_fit(); + return true; +} + +void PlayerBadge::checkAndUpdateNewBadges() { + for (const auto &badge : g_game().getBadges()) { + switch (badge.m_type) { + case CyclopediaBadge_t::ACCOUNT_AGE: + if (accountAge(badge.m_amount)) { + add(badge.m_id); + } + break; + case CyclopediaBadge_t::LOYALTY: + if (loyalty(badge.m_amount)) { + add(badge.m_id); + } + break; + case CyclopediaBadge_t::ACCOUNT_ALL_LEVEL: + if (accountAllLevel(badge.m_amount)) { + add(badge.m_id); + } + break; + case CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS: + if (accountAllVocations(badge.m_amount)) { + add(badge.m_id); + } + break; + case CyclopediaBadge_t::TOURNAMENT_PARTICIPATION: + case CyclopediaBadge_t::TOURNAMENT_POINTS: + break; + } + } + + loadUnlockedBadges(); +} + +void PlayerBadge::loadUnlockedBadges() { + const auto &unlockedBadges = getUnlockedKV()->keys(); + g_logger().debug("[{}] - Loading unlocked badges: {}", __FUNCTION__, unlockedBadges.size()); + for (const auto &badgeName : unlockedBadges) { + const Badge &badge = g_game().getBadgeByName(badgeName); + if (badge.m_id == 0) { + g_logger().error("[{}] - Badge {} not found.", __FUNCTION__, badgeName); + continue; + } + + g_logger().debug("[{}] - Badge {} found for player {}.", __FUNCTION__, badge.m_name, m_player.getName()); + + m_badgesUnlocked.emplace_back(badge, getUnlockedKV()->get(badgeName)->getNumber()); + } +} + +const std::shared_ptr &PlayerBadge::getUnlockedKV() { + if (m_badgeUnlockedKV == nullptr) { + m_badgeUnlockedKV = m_player.kv()->scoped("badges")->scoped("unlocked"); + } + + return m_badgeUnlockedKV; +} + +// Badge Calculate Functions +bool PlayerBadge::accountAge(uint8_t amount) { + return std::floor(m_player.getLoyaltyPoints() / 365) >= amount; +} + +bool PlayerBadge::loyalty(uint8_t amount) { + return m_player.getLoyaltyPoints() >= amount; +} + +bool PlayerBadge::accountAllLevel(uint8_t amount) { + const auto &players = g_game().getPlayersByAccount(m_player.getAccount(), true); + uint16_t total = std::accumulate(players.begin(), players.end(), 0, [](uint16_t sum, const std::shared_ptr &player) { + return sum + player->getLevel(); + }); + return total >= amount; +} + +bool PlayerBadge::accountAllVocations(uint8_t amount) { + auto knight = false; + auto paladin = false; + auto druid = false; + auto sorcerer = false; + for (const auto &player : g_game().getPlayersByAccount(m_player.getAccount(), true)) { + if (player->getLevel() >= amount) { + auto vocationEnum = player->getPlayerVocationEnum(); + if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { + knight = true; + } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { + sorcerer = true; + } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { + paladin = true; + } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { + druid = true; + } + } + } + return knight && paladin && druid && sorcerer; +} + +bool PlayerBadge::tournamentParticipation(uint8_t skill) { + // todo check if is used + return false; +} + +bool PlayerBadge::tournamentPoints(uint8_t race) { + // todo check if is used + return false; +} diff --git a/src/creatures/players/cyclopedia/player_badge.hpp b/src/creatures/players/cyclopedia/player_badge.hpp new file mode 100644 index 00000000000..01c9dc0e63f --- /dev/null +++ b/src/creatures/players/cyclopedia/player_badge.hpp @@ -0,0 +1,65 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "game/game_definitions.hpp" + +class Player; +class KV; + +struct Badge { + uint8_t m_id = 0; + CyclopediaBadge_t m_type; + std::string m_name; + uint16_t m_amount = 0; + + Badge() = default; + + Badge(uint8_t id, CyclopediaBadge_t type, std::string name, uint16_t amount) : + m_id(id), m_type(type), m_name(std::move(name)), m_amount(amount) { } + + bool operator==(const Badge &other) const { + return m_id == other.m_id; + } +}; + +namespace std { + template <> + struct hash { + std::size_t operator()(const Badge &b) const { + return hash()(b.m_id); + } + }; +} + +class PlayerBadge { +public: + explicit PlayerBadge(Player &player); + + [[nodiscard]] bool hasBadge(uint8_t id) const; + bool add(uint8_t id, uint32_t timestamp = 0); + void checkAndUpdateNewBadges(); + void loadUnlockedBadges(); + const std::shared_ptr &getUnlockedKV(); + + // Badge Calculate Functions + bool accountAge(uint8_t amount); + bool loyalty(uint8_t amount); + bool accountAllLevel(uint8_t amount); + bool accountAllVocations(uint8_t amount); + [[nodiscard]] bool tournamentParticipation(uint8_t skill); + [[nodiscard]] bool tournamentPoints(uint8_t race); + +private: + // {badge ID, time when it was unlocked} + std::shared_ptr m_badgeUnlockedKV; + std::vector> m_badgesUnlocked; + Player &m_player; +}; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 149f41380ec..8b889ac200b 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -16,6 +16,7 @@ #include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/storages/storages.hpp" #include "game/game.hpp" #include "game/modal_window/modal_window.hpp" @@ -49,6 +50,7 @@ Player::Player(ProtocolGame_ptr p) : client(std::move(p)) { m_wheelPlayer = std::make_unique(*this); m_playerAchievement = std::make_unique(*this); + m_playerBadge = std::make_unique(*this); } Player::~Player() { @@ -129,7 +131,7 @@ std::string Player::getDescription(int32_t lookDistance) { s << " You have no vocation."; } - if (loyaltyTitle.length() != 0) { + if (!loyaltyTitle.empty()) { s << " You are a " << loyaltyTitle << "."; } @@ -141,9 +143,8 @@ std::string Player::getDescription(int32_t lookDistance) { if (!group->access) { s << " (Level " << level << ')'; } - s << '.'; - s << " " << subjectPronoun; + s << ". " << subjectPronoun; if (group->access) { s << " " << getSubjectVerb() << " " << group->name << '.'; @@ -153,7 +154,7 @@ std::string Player::getDescription(int32_t lookDistance) { s << " has no vocation."; } - if (loyaltyTitle.length() != 0) { + if (!loyaltyTitle.empty()) { std::string article = "a"; if (loyaltyTitle[0] == 'A' || loyaltyTitle[0] == 'E' || loyaltyTitle[0] == 'I' || loyaltyTitle[0] == 'O' || loyaltyTitle[0] == 'U') { article = "an"; @@ -628,6 +629,20 @@ phmap::flat_hash_map> Player::getAllSlotItems() c return itemMap; } +phmap::flat_hash_map Player::getBlessingNames() const { + static phmap::flat_hash_map blessingNames = { + { TWIST_OF_FATE, "Twist of Fate" }, + { WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" }, + { SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" }, + { FIRE_OF_THE_SUNS, "The Fire of the Suns" }, + { SPIRITUAL_SHIELDING, "The Spiritual Shielding" }, + { EMBRACE_OF_TIBIA, "The Embrace of Tibia" }, + { BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" }, + { HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" }, + }; + return blessingNames; +} + void Player::setTraining(bool value) { for (const auto &[key, player] : g_game().getPlayers()) { if (!this->isInGhostMode() || player->isAccessPlayer()) { @@ -6579,17 +6594,6 @@ void Player::initializeTaskHunting() { } std::string Player::getBlessingsName() const { - static const phmap::flat_hash_map BlessingNames = { - { TWIST_OF_FATE, "Twist of Fate" }, - { WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" }, - { SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" }, - { FIRE_OF_THE_SUNS, "The Fire of the Suns" }, - { SPIRITUAL_SHIELDING, "The Spiritual Shielding" }, - { EMBRACE_OF_TIBIA, "The Embrace of Tibia" }, - { BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" }, - { HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" }, - }; - uint8_t count = 0; std::for_each(blessings.begin(), blessings.end(), [&count](uint8_t amount) { if (amount != 0) { @@ -6597,6 +6601,7 @@ std::string Player::getBlessingsName() const { } }); + auto BlessingNames = getBlessingNames(); std::ostringstream os; for (uint8_t i = 1; i <= 8; i++) { if (hasBlessing(i)) { @@ -7954,6 +7959,15 @@ const std::unique_ptr &Player::achiev() const { return m_playerAchievement; } +// Badge interface +std::unique_ptr &Player::badge() { + return m_playerBadge; +} + +const std::unique_ptr &Player::badge() const { + return m_playerBadge; +} + void Player::sendLootMessage(const std::string &message) const { auto party = getParty(); if (!party) { @@ -8073,3 +8087,18 @@ bool Player::canSpeakWithHireling(uint8_t speechbubble) { return true; } + +uint16_t Player::getPlayerVocationEnum() const { + int cipTibiaId = getVocation()->getClientId(); + if (cipTibiaId == 1 || cipTibiaId == 11) { + return Vocation_t::VOCATION_KNIGHT_CIP; // Knight + } else if (cipTibiaId == 2 || cipTibiaId == 12) { + return Vocation_t::VOCATION_PALADIN_CIP; // Paladin + } else if (cipTibiaId == 3 || cipTibiaId == 13) { + return Vocation_t::VOCATION_SORCERER_CIP; // Sorcerer + } else if (cipTibiaId == 4 || cipTibiaId == 14) { + return Vocation_t::VOCATION_DRUID_CIP; // Druid + } + + return Vocation_t::VOCATION_NONE; +} diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index c2a9f29831a..ca9c944bb2e 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -34,6 +34,7 @@ #include "creatures/npcs/npc.hpp" #include "game/bank/bank.hpp" #include "enums/object_category.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" class House; class NetworkMessage; @@ -49,11 +50,13 @@ class TaskHuntingSlot; class Spell; class PlayerWheel; class PlayerAchievement; +class PlayerBadge; class Spectators; class Account; struct ModalWindow; struct Achievement; +struct Badge; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -735,6 +738,13 @@ class Player final : public Creature, public Cylinder, public Bankable { uint32_t getCapacity() const; + uint32_t getBonusCapacity() const { + if (hasFlag(PlayerFlags_t::CannotPickupItem) || hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { + return std::numeric_limits::max(); + } + return bonusCapacity; + } + uint32_t getFreeCapacity() const { if (hasFlag(PlayerFlags_t::CannotPickupItem)) { return 0; @@ -2577,6 +2587,9 @@ class Player final : public Creature, public Cylinder, public Bankable { // This get all players slot items phmap::flat_hash_map> getAllSlotItems() const; + // This get all blessings + phmap::flat_hash_map getBlessingNames() const; + /** * @brief Get the equipped items of the player-> * @details This function returns a vector containing the items currently equipped by the player @@ -2592,6 +2605,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &achiev(); const std::unique_ptr &achiev() const; + // Player badge interface + std::unique_ptr &badge(); + const std::unique_ptr &badge() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr getLootPouch(); @@ -2602,6 +2619,8 @@ class Player final : public Creature, public Cylinder, public Bankable { bool canSpeakWithHireling(uint8_t speechbubble); + uint16_t getPlayerVocationEnum() const; + private: friend class PlayerLock; std::mutex mutex; @@ -2659,6 +2678,7 @@ class Player final : public Creature, public Cylinder, public Bankable { uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override; void stashContainer(StashContainerList itemDict); ItemsTierCountList getInventoryItemsId() const; + // todo ItemsTierCountList getStoreInboxItemsId() const; // This function is a override function of base class std::map &getAllItemTypeCount(std::map &countMap) const override; @@ -2984,9 +3004,11 @@ class Player final : public Creature, public Cylinder, public Bankable { friend class IOLoginDataLoad; friend class IOLoginDataSave; friend class PlayerAchievement; + friend class PlayerBadge; std::unique_ptr m_wheelPlayer; std::unique_ptr m_playerAchievement; + std::unique_ptr m_playerBadge; std::mutex quickLootMutex; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 20e8f683bf9..5c63e9aecc2 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -689,7 +689,7 @@ bool PlayerWheel::getSpellAdditionalArea(const std::string &spellName) const { return false; } - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { return checkSpellArea(g_game().getIOWheel()->getWheelBonusData().spells.knight, spellName, stage); } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { @@ -709,7 +709,7 @@ int PlayerWheel::getSpellAdditionalTarget(const std::string &spellName) const { return 0; } - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { return checkSpellAdditionalTarget(g_game().getIOWheel()->getWheelBonusData().spells.knight, spellName, stage); } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { @@ -729,7 +729,7 @@ int PlayerWheel::getSpellAdditionalDuration(const std::string &spellName) const return 0; } - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { return checkSpellAdditionalDuration(g_game().getIOWheel()->getWheelBonusData().spells.knight, spellName, stage); } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { @@ -1016,7 +1016,7 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con } msg.addByte(getOptions(ownerId)); // Options - msg.addByte(getPlayerVocationEnum()); // Vocation id + msg.addByte(m_player.getPlayerVocationEnum()); // Vocation id msg.add(getWheelPoints(false)); // Points (false param for not send extra points) msg.add(getExtraPoints()); // Extra points @@ -1262,7 +1262,7 @@ uint16_t PlayerWheel::getWheelPoints(bool includeExtraPoints /* = true*/) const bool PlayerWheel::canOpenWheel() const { // Vocation check - if (getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) { + if (m_player.getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) { return false; } @@ -1304,21 +1304,6 @@ uint8_t PlayerWheel::getOptions(uint32_t ownerId) const { return 2; } -uint8_t PlayerWheel::getPlayerVocationEnum() const { - int cipTibiaId = m_player.getVocation()->getClientId(); - if (cipTibiaId == 1 || cipTibiaId == 11) { - return Vocation_t::VOCATION_KNIGHT_CIP; // Knight - } else if (cipTibiaId == 2 || cipTibiaId == 12) { - return Vocation_t::VOCATION_PALADIN_CIP; // Paladin - } else if (cipTibiaId == 3 || cipTibiaId == 13) { - return Vocation_t::VOCATION_SORCERER_CIP; // Sorcerer - } else if (cipTibiaId == 4 || cipTibiaId == 14) { - return Vocation_t::VOCATION_DRUID_CIP; // Druid - } - - return Vocation_t::VOCATION_NONE; -} - bool PlayerWheel::canSelectSlotFullOrPartial(WheelSlots_t slot) const { if (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) { g_logger().debug("[{}] points on slot {}, max points {}", __FUNCTION__, getPointsBySlotType(slot), getMaxPointsPerSlot(slot)); @@ -1785,7 +1770,7 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus void PlayerWheel::loadDedicationAndConvictionPerks() { using VocationBonusFunction = std::function &, uint16_t, uint8_t, PlayerWheelMethodsBonusData &)>; auto wheelFunctions = g_game().getIOWheel()->getWheelMapFunctions(); - auto vocationCipId = getPlayerVocationEnum(); + auto vocationCipId = m_player.getPlayerVocationEnum(); if (vocationCipId < VOCATION_KNIGHT_CIP || vocationCipId > VOCATION_DRUID_CIP) { return; } @@ -1826,7 +1811,7 @@ void PlayerWheel::loadRevelationPerks() { m_playerBonusData.stats.healing += statsHealing; auto redStageValue = static_cast(redStageEnum); - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { m_playerBonusData.stages.blessingOfTheGrove = redStageValue; } else if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { @@ -1854,7 +1839,7 @@ void PlayerWheel::loadRevelationPerks() { m_playerBonusData.stats.healing += statsHealing; auto purpleStage = static_cast(purpleStageEnum); - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { m_playerBonusData.avatar.steel = purpleStage; for (uint8_t i = 0; i < purpleStage; ++i) { @@ -1885,7 +1870,7 @@ void PlayerWheel::loadRevelationPerks() { m_playerBonusData.stats.healing += statsHealing; auto blueStage = static_cast(blueStageEnum); - auto vocationEnum = getPlayerVocationEnum(); + auto vocationEnum = m_player.getPlayerVocationEnum(); if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { m_playerBonusData.stages.combatMastery = blueStage; } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index 303063beb9b..14f922e9597 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -205,7 +205,6 @@ class PlayerWheel { * indicating that the player can increase points but cannot decrease the ID. */ uint8_t getOptions(uint32_t ownerId) const; - uint8_t getPlayerVocationEnum() const; std::shared_ptr gemsKV() const; diff --git a/src/game/game.cpp b/src/game/game.cpp index b1d76f9f760..eeaaabb39f7 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -37,6 +37,7 @@ #include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/npcs/npc.hpp" #include "server/network/webhook/webhook.hpp" #include "server/network/protocol/protocollogin.hpp" @@ -53,23 +54,6 @@ #include -enum class HighscoreCategories_t : uint8_t { - EXPERIENCE = 0, - FIST_FIGHTING = 1, - CLUB_FIGHTING = 2, - SWORD_FIGHTING = 3, - AXE_FIGHTING = 4, - DISTANCE_FIGHTING = 5, - SHIELDING = 6, - FISHING = 7, - MAGIC_LEVEL = 8, - LOYALTY = 9, - ACHIEVEMENTS = 10, - CHARMS = 11, - DROME = 12, - GOSHNAR = 13, -}; - namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, std::shared_ptr source) { if (blockType == BLOCK_DEFENSE) { @@ -209,6 +193,35 @@ Game::Game() { wildcardTree = std::make_shared(false); + m_badges = { + Badge(1, CyclopediaBadge_t::ACCOUNT_AGE, "Fledegeling Hero", 1), + Badge(2, CyclopediaBadge_t::ACCOUNT_AGE, "Veteran Hero", 5), + Badge(3, CyclopediaBadge_t::ACCOUNT_AGE, "Senior Hero", 10), + Badge(4, CyclopediaBadge_t::ACCOUNT_AGE, "Ancient Hero", 15), + Badge(5, CyclopediaBadge_t::ACCOUNT_AGE, "Exalted Hero", 20), + + Badge(6, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 1)", 100), + Badge(7, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 2)", 1000), + Badge(8, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 3)", 5000), + + Badge(9, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 1)", 500), + Badge(10, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 2)", 1000), + Badge(11, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 3)", 2000), + + Badge(12, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 1)", 100), + Badge(13, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 2)", 250), + Badge(14, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 3)", 500), + + Badge(15, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Freshman of the Tournament", 1), + Badge(16, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Regular of the Tournament", 5), + Badge(17, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Hero of the Tournament", 10), + + Badge(18, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Competitor", 1000), + Badge(19, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Challenger", 2500), + Badge(20, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Master", 5000), + Badge(21, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Champion", 10000), + }; + m_highscoreCategoriesNames = { { static_cast(HighscoreCategories_t::ACHIEVEMENTS), "Achievement Points" }, { static_cast(HighscoreCategories_t::AXE_FIGHTING), "Axe Fighting" }, @@ -8147,6 +8160,17 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) { player->removePlayer(displayEffect); } +void Game::playerFriendSystemAction(std::shared_ptr player, uint8_t type, uint8_t titleId) { + uint32_t playerGUID = player->getGUID(); + if (type == 0x0E) { + // todo in titles system PR + // player->title()->setCurrentTitle(titleId); + // player->sendCyclopediaCharacterBaseInformation(); + // player->sendCyclopediaCharacterTitles(); + return; + } +} + void Game::playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_t characterID, CyclopediaCharacterInfoType_t characterInfoType, uint16_t entriesPerPage, uint16_t page) { uint32_t playerGUID = player->getGUID(); if (characterID != playerGUID) { @@ -10538,3 +10562,38 @@ std::vector Game::getPublicAchievements() { std::map Game::getAchievements() { return m_achievements; } + +void Game::logCyclopediaStats() { + g_logger().info("Loaded {} badges from Badge System", m_badges.size()); + // todo in title system: g_logger().info("Loaded {} titles from Title system", m_titles.size()); +} + +std::unordered_set Game::getBadges() { + return m_badges; +} + +Badge Game::getBadgeById(uint8_t id) { + if (id == 0) { + return {}; + } + auto it = std::find_if(m_badges.begin(), m_badges.end(), [id](const Badge &b) { + return b.m_id == id; + }); + if (it != m_badges.end()) { + return *it; + } + return {}; +} + +Badge Game::getBadgeByName(const std::string &name) { + if (name.empty()) { + return {}; + } + auto it = std::find_if(m_badges.begin(), m_badges.end(), [name](const Badge &b) { + return b.m_name == name; + }); + if (it != m_badges.end()) { + return *it; + } + return {}; +} diff --git a/src/game/game.hpp b/src/game/game.hpp index 708aa4b1782..9d743895c4c 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -50,6 +50,7 @@ class Spectators; struct Achievement; struct HighscoreCategory; +struct Badge; static constexpr uint16_t SERVER_BEAT = 0x32; static constexpr int32_t EVENT_MS = 10000; @@ -98,6 +99,8 @@ class Game { void forceRemoveCondition(uint32_t creatureId, ConditionType_t type, ConditionId_t conditionId); + void logCyclopediaStats(); + /** * Load the main map * \param filename Is the map custom name (Example: "map".otbm, not is necessary add extension .otbm) @@ -305,6 +308,8 @@ class Game { void playerReportRuleViolationReport(uint32_t playerId, const std::string &targetName, uint8_t reportType, uint8_t reportReason, const std::string &comment, const std::string &translation); + void playerFriendSystemAction(std::shared_ptr player, uint8_t type, uint8_t titleId); + void playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_t characterID, CyclopediaCharacterInfoType_t characterInfoType, uint16_t entriesPerPage, uint16_t page); void playerHighscores(std::shared_ptr player, HighscoreType_t type, uint8_t category, uint32_t vocation, const std::string &worldName, uint16_t page, uint8_t entriesPerPage); @@ -717,10 +722,16 @@ class Game { std::vector getPublicAchievements(); std::map getAchievements(); + std::unordered_set getBadges(); + Badge getBadgeById(uint8_t id); + Badge getBadgeByName(const std::string &name); + private: std::map m_achievements; std::map m_achievementsNameToId; + std::unordered_set m_badges; + std::vector m_highscoreCategories; std::unordered_map m_highscoreCategoriesNames; diff --git a/src/game/game_definitions.hpp b/src/game/game_definitions.hpp index 2dadcbbd87c..2c2b40fac78 100644 --- a/src/game/game_definitions.hpp +++ b/src/game/game_definitions.hpp @@ -61,6 +61,15 @@ enum LightState_t { LIGHT_STATE_SUNRISE, }; +enum CyclopediaBadge_t : uint8_t { + ACCOUNT_AGE = 1, + LOYALTY, + ACCOUNT_ALL_LEVEL, + ACCOUNT_ALL_VOCATIONS, + TOURNAMENT_PARTICIPATION, + TOURNAMENT_POINTS, +}; + enum CyclopediaCharacterInfoType_t : uint8_t { CYCLOPEDIA_CHARACTERINFO_BASEINFORMATION = 0, CYCLOPEDIA_CHARACTERINFO_GENERALSTATS = 1, @@ -84,6 +93,23 @@ enum CyclopediaCharacterInfo_RecentKillStatus_t : uint8_t { CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_ARENA = 4 }; +enum class HighscoreCategories_t : uint8_t { + EXPERIENCE = 0, + FIST_FIGHTING = 1, + CLUB_FIGHTING = 2, + SWORD_FIGHTING = 3, + AXE_FIGHTING = 4, + DISTANCE_FIGHTING = 5, + SHIELDING = 6, + FISHING = 7, + MAGIC_LEVEL = 8, + LOYALTY = 9, + ACHIEVEMENTS = 10, + CHARMS = 11, + DROME = 12, + GOSHNAR = 13, +}; + enum HighscoreType_t : uint8_t { HIGHSCORE_GETENTRIES = 0, HIGHSCORE_OURRANK = 1 diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 2c3cbf48fea..041cd323e28 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -889,6 +889,7 @@ void IOLoginDataLoad::loadPlayerInitializeSystem(std::shared_ptr player) player->wheel()->initializePlayerData(); player->achiev()->loadUnlockedAchievements(); + player->badge()->checkAndUpdateNewBadges(); player->initializePrey(); player->initializeTaskHunting(); diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index ea418dede66..5f705cf426d 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -27,6 +27,7 @@ #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" #include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" #include "map/spectators.hpp" // Game diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 94100a1f406..81b70263a0a 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -15,6 +15,7 @@ #include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" #include "io/ioprey.hpp" @@ -4279,3 +4280,16 @@ int PlayerFunctions::luaPlayerRemoveAchievementPoints(lua_State* L) { pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerAddBadge(lua_State* L) { + // player:addBadge(id) + const auto &player = getUserdataShared(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + player->badge()->add(getNumber(L, 2, 0)); + pushBoolean(L, true); + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 2be6e5076cd..a30f9f755ca 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -364,6 +364,9 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "addAchievementPoints", PlayerFunctions::luaPlayerAddAchievementPoints); registerMethod(L, "Player", "removeAchievementPoints", PlayerFunctions::luaPlayerRemoveAchievementPoints); + // Badge Functions + registerMethod(L, "Player", "addBadge", PlayerFunctions::luaPlayerAddBadge); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -718,5 +721,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerAddAchievementPoints(lua_State* L); static int luaPlayerRemoveAchievementPoints(lua_State* L); + static int luaPlayerAddBadge(lua_State* L); + friend class CreatureFunctions; }; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index c822ff4af7c..db850af5ecc 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -27,6 +27,7 @@ #include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/grouping/familiars.hpp" #include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/dispatcher.hpp" @@ -1090,6 +1091,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt case 0x80: g_game().playerCloseTrade(player->getID()); break; + case 0x81: + parseFriendSystemAction(msg); + break; case 0x82: parseUseItem(msg); break; @@ -2051,6 +2055,15 @@ void ProtocolGame::sendItemInspection(uint16_t itemId, uint8_t itemCount, std::s writeToOutputBuffer(msg); } +void ProtocolGame::parseFriendSystemAction(NetworkMessage &msg) { + uint8_t state = msg.getByte(); + if (state == 0x0E) { + // todo title system pr + // uint8_t titleId = msg.getByte(); + // g_game().playerFriendSystemAction(player, state, titleId); + } +} + void ProtocolGame::parseCyclopediaCharacterInfo(NetworkMessage &msg) { if (oldProtocol) { return; @@ -2125,7 +2138,7 @@ void ProtocolGame::sendHighscores(const std::vector &charact NetworkMessage msg; msg.addByte(0xB1); - msg.addByte(0x00); // No data available + msg.addByte(0x00); // All data available msg.addByte(1); // Worlds msg.addString(g_configManager().getString(SERVER_NAME, __FUNCTION__), "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // First World @@ -3367,8 +3380,9 @@ void ProtocolGame::sendCyclopediaCharacterBaseInformation() { msg.add(player->getLevel()); AddOutfit(msg, player->getDefaultOutfit(), false); - msg.addByte(0x00); // hide stamina + msg.addByte(0x00); // Store summary & Character titles msg.addString("", "ProtocolGame::sendCyclopediaCharacterBaseInformation - empty"); // character title + // msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterBaseInformation - player->title()->getCurrentTitleName()"); // character title writeToOutputBuffer(msg); } @@ -3384,7 +3398,8 @@ void ProtocolGame::sendCyclopediaCharacterGeneralStats() { // 1: No data available at the moment. // 2: You are not allowed to see this character's data. // 3: You are not allowed to inspect this character. - msg.addByte(0x00); + msg.addByte(0x00); // 0x00 Here means 'no error' + msg.add(player->getExperience()); msg.add(player->getLevel()); msg.addByte(player->getLevelPercent()); @@ -3393,7 +3408,7 @@ void ProtocolGame::sendCyclopediaCharacterGeneralStats() { msg.add(player->getXpBoostPercent()); // XPBoost msg.add(player->getStaminaXpBoost()); // StaminaMultiplier(100=x1.0) msg.add(player->getXpBoostTime()); // xpBoostRemainingTime - msg.addByte(0x01); // canBuyXpBoost + msg.addByte(player->getXpBoostTime() > 0 ? 0x00 : 0x01); // canBuyXpBoost msg.add(std::min(player->getHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMana(), std::numeric_limits::max())); @@ -3406,8 +3421,8 @@ void ProtocolGame::sendCyclopediaCharacterGeneralStats() { msg.add(player->getOfflineTrainingTime() / 60 / 1000); msg.add(player->getSpeed()); msg.add(player->getBaseSpeed()); - msg.add(player->getCapacity()); - msg.add(player->getCapacity()); + msg.add(player->getBonusCapacity()); + msg.add(player->getBaseCapacity()); msg.add(player->hasFlag(PlayerFlags_t::HasInfiniteCapacity) ? 1000000 : player->getFreeCapacity()); msg.addByte(8); msg.addByte(1); @@ -3611,10 +3626,17 @@ void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t p msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTDEATHS); msg.addByte(0x00); - msg.add(page); + + uint16_t totalPages = static_cast(std::ceil(static_cast(entries.size()) / pages)); + uint16_t currentPage = std::min(page, totalPages); + uint16_t firstObject = (currentPage - 1) * pages; + uint16_t finalObject = firstObject + pages; + + msg.add(currentPage); + msg.add(totalPages); msg.add(pages); - msg.add(entries.size()); - for (const RecentDeathEntry &entry : entries) { + for (uint16_t i = firstObject; i < finalObject; i++) { + RecentDeathEntry entry = entries[i]; msg.add(entry.timestamp); msg.addString(entry.cause, "ProtocolGame::sendCyclopediaCharacterRecentDeaths - entry.cause"); } @@ -3630,10 +3652,17 @@ void ProtocolGame::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTPVPKILLS); msg.addByte(0x00); - msg.add(page); + + uint16_t totalPages = static_cast(std::ceil(static_cast(entries.size()) / pages)); + uint16_t currentPage = std::min(page, totalPages); + uint16_t firstObject = (currentPage - 1) * pages; + uint16_t finalObject = firstObject + pages; + + msg.add(currentPage); + msg.add(totalPages); msg.add(pages); - msg.add(entries.size()); - for (const RecentPvPKillEntry &entry : entries) { + for (uint16_t i = firstObject; i < finalObject; i++) { + RecentPvPKillEntry entry = entries[i]; msg.add(entry.timestamp); msg.addString(entry.description, "ProtocolGame::sendCyclopediaCharacterRecentPvPKills - entry.description"); msg.addByte(entry.status); @@ -3678,13 +3707,13 @@ void ProtocolGame::sendCyclopediaCharacterItemSummary() { NetworkMessage msg; msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_ITEMSUMMARY); - msg.addByte(0x00); + msg.addByte(0x00); // 0x00 Here means 'no error' - msg.add(0); - msg.add(0); - msg.add(0); - msg.add(0); - msg.add(0); + msg.add(0); // inventoryItems.size() + msg.add(0); // storeInboxItems.size() + msg.add(0); // supplyStashItems.size() + msg.add(0); // depotBoxItems.size() + msg.add(0); // inboxItems.size() writeToOutputBuffer(msg); } @@ -3800,20 +3829,19 @@ void ProtocolGame::sendCyclopediaCharacterStoreSummary() { NetworkMessage msg; msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_STORESUMMARY); - msg.addByte(0x00); - // Remaining Store Xp Boost Time - msg.add(player->getXpBoostTime()); - // RemainingDailyRewardXpBoostTime - msg.add(0); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.addByte(0x00); - msg.add(0); + msg.addByte(0x00); // 0x00 Here means 'no error' + msg.add(player->getXpBoostTime()); // Remaining Store Xp Boost Time + msg.add(0); // RemainingDailyRewardXpBoostTime + + msg.addByte(0x00); // getBlessingsObtained + msg.addByte(0x00); // getTaskHuntingSlotById + msg.addByte(0x00); // getPreyCardsObtained + msg.addByte(0x00); // getRewardCollectionObtained + msg.addByte(0x00); // player->hasCharmExpansion() ? 0x01 : 0x00 + msg.addByte(0x00); // getHirelingsObtained + msg.addByte(0x00); // getHirelinsJobsObtained + msg.addByte(0x00); // getHirelinsOutfitsObtained + msg.add(0); // getHouseItemsObtained writeToOutputBuffer(msg); } @@ -3837,7 +3865,24 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { msg.addByte(slot); msg.addString(inventoryItem->getName(), "ProtocolGame::sendCyclopediaCharacterInspection - inventoryItem->getName()"); AddItem(msg, inventoryItem); - msg.addByte(0); + + uint8_t itemImbuements = 0; + auto startImbuements = msg.getBufferPosition(); + msg.skipBytes(1); + for (uint8_t slotid = 0; slotid < inventoryItem->getImbuementSlot(); slotid++) { + ImbuementInfo imbuementInfo; + if (!inventoryItem->getImbuementInfo(slotid, &imbuementInfo)) { + continue; + } + + msg.add(imbuementInfo.imbuement->getID()); + itemImbuements++; + } + + auto endImbuements = msg.getBufferPosition(); + msg.setBufferPosition(startImbuements); + msg.addByte(itemImbuements); + msg.setBufferPosition(endImbuements); auto descriptions = Item::getDescriptions(Item::items[inventoryItem->getID()], inventoryItem); msg.addByte(descriptions.size()); @@ -3866,7 +3911,7 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { msg.addString(player->getVocation()->getVocName(), "ProtocolGame::sendCyclopediaCharacterInspection - player->getVocation()->getVocName()"); // Loyalty title - if (player->getLoyaltyTitle().length() != 0) { + if (!player->getLoyaltyTitle().empty()) { playerDescriptionSize++; msg.addString("Loyalty Title", "ProtocolGame::sendCyclopediaCharacterInspection - Loyalty Title"); msg.addString(player->getLoyaltyTitle(), "ProtocolGame::sendCyclopediaCharacterInspection - player->getLoyaltyTitle()"); @@ -3899,19 +3944,29 @@ void ProtocolGame::sendCyclopediaCharacterBadges() { msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_BADGES); msg.addByte(0x00); - // ShowAccountInformation - msg.addByte(0x01); - // if ShowAccountInformation show IsOnline, IsPremium, character title, badges - // IsOnline + msg.addByte(0x01); // ShowAccountInformation, if 0x01 will show IsOnline, IsPremium, character title, badges + const auto loggedPlayer = g_game().getPlayerUniqueLogin(player->getName()); - msg.addByte(loggedPlayer ? 0x01 : 0x00); - // IsPremium (GOD has always 'Premium') - msg.addByte(player->isPremium() ? 0x01 : 0x00); + msg.addByte(loggedPlayer ? 0x01 : 0x00); // IsOnline + msg.addByte(player->isPremium() ? 0x01 : 0x00); // IsPremium (GOD has always 'Premium') // Character loyalty title msg.addString(player->getLoyaltyTitle(), "ProtocolGame::sendCyclopediaCharacterBadges - player->getLoyaltyTitle()"); - // Enable badges - msg.addByte(0x00); - // Todo badges loop + // msg.addByte(0x01); // Enable badges + + uint8_t badgesSize = 0; + auto badgesSizePosition = msg.getBufferPosition(); + msg.skipBytes(1); + for (const auto &badge : g_game().getBadges()) { + if (player->badge()->hasBadge(badge.m_id)) { + msg.add(badge.m_id); + msg.addString(badge.m_name, "ProtocolGame::sendCyclopediaCharacterBadges - name"); + badgesSize++; + } + } + + msg.setBufferPosition(badgesSizePosition); + msg.addByte(badgesSize); + writeToOutputBuffer(msg); } @@ -3923,9 +3978,10 @@ void ProtocolGame::sendCyclopediaCharacterTitles() { NetworkMessage msg; msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_TITLES); + msg.addByte(0x00); // 0x00 Here means 'no error' msg.addByte(0x00); msg.addByte(0x00); - msg.addByte(0x00); + writeToOutputBuffer(msg); } diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 306bb873f25..7aea8ff805d 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -13,6 +13,7 @@ #include "creatures/interactions/chat.hpp" #include "creatures/creature.hpp" #include "enums/forge_conversion.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" class NetworkMessage; class Player; @@ -29,6 +30,7 @@ class TaskHuntingOption; struct ModalWindow; struct Achievement; +struct Badge; using ProtocolGame_ptr = std::shared_ptr; @@ -126,6 +128,8 @@ class ProtocolGame final : public Protocol { void sendItemInspection(uint16_t itemId, uint8_t itemCount, std::shared_ptr item, bool cyclopedia); void parseInspectionObject(NetworkMessage &msg); + void parseFriendSystemAction(NetworkMessage &msg); + void parseCyclopediaCharacterInfo(NetworkMessage &msg); void parseHighscores(NetworkMessage &msg); diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index e31fdbad08a..5d40d930646 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -45,6 +45,7 @@ + @@ -257,6 +258,7 @@ + @@ -595,4 +597,4 @@ - \ No newline at end of file + From afb1b0d7f0533df37d98728abd61f3e48d3ba988 Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Tue, 7 May 2024 15:27:07 -0300 Subject: [PATCH 26/95] fix: quiver bugs (#2609) --- src/items/item.cpp | 33 ++++++++++++-------- src/server/network/protocol/protocolgame.cpp | 2 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index eea07a4e5dd..81d24a790e7 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -1147,6 +1147,10 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr item /*= nullptr descriptions.emplace_back("Description", it.description); } + if (item->getContainer()) { + descriptions.emplace_back("Capacity", std::to_string(item->getContainer()->capacity())); + } + if (it.showCharges) { auto charges = item->getAttribute(ItemAttribute_t::CHARGES); if (charges != 0) { @@ -1403,10 +1407,6 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr item /*= nullptr descriptions.emplace_back("Tier", std::to_string(item->getTier())); } - if (item->getContainer()) { - descriptions.emplace_back("Capacity", std::to_string(item->getContainer()->capacity())); - } - std::string slotName; if (item->getImbuementSlot() > 0) { for (uint8_t i = 0; i < item->getImbuementSlot(); ++i) { @@ -1562,6 +1562,10 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr item /*= nullptr descriptions.emplace_back("Description", it.description); } + if (it.isContainer()) { + descriptions.emplace_back("Capacity", std::to_string(it.maxItems)); + } + int32_t attack = it.attack; if (it.isRanged()) { bool separator = false; @@ -1781,10 +1785,6 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr item /*= nullptr } } - if (it.isContainer()) { - descriptions.emplace_back("Capacity", std::to_string(it.maxItems)); - } - if (it.imbuementSlot > 0) { descriptions.emplace_back("Imbuement Slots", std::to_string(it.imbuementSlot)); } @@ -2075,13 +2075,18 @@ std::string Item::parseShowDuration(std::shared_ptr item) { std::string Item::parseShowAttributesDescription(std::shared_ptr item, const uint16_t itemId) { std::ostringstream itemDescription; const ItemType &itemType = Item::items[itemId]; + if (itemType.armor != 0 || (item && item->getArmor() != 0) || itemType.showAttributes) { - bool begin = true; + bool begin = itemType.isQuiver() ? false : true; int32_t armor = (item ? item->getArmor() : itemType.armor); if (armor != 0) { - itemDescription << " (Arm:" << armor; - begin = false; + if (begin) { + itemDescription << " (Arm:" << armor; + begin = false; + } else { + itemDescription << ", Arm:" << armor; + } } if (itemType.abilities) { @@ -2167,7 +2172,7 @@ std::string Item::parseShowAttributesDescription(std::shared_ptr item, con itemDescription << ", "; } - itemDescription << "Perfect Shot " << std::showpos << itemType.abilities->perfectShotDamage << std::noshowpos << " at range " << unsigned(itemType.abilities->perfectShotRange); + itemDescription << "perfect shot " << std::showpos << itemType.abilities->perfectShotDamage << std::noshowpos << " at range " << unsigned(itemType.abilities->perfectShotRange); } if (itemType.abilities->reflectFlat[0] != 0) { @@ -2869,8 +2874,10 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std:: } } - if (volume != 0) { + if (volume != 0 && !it.isQuiver()) { s << " (Vol:" << volume << ')'; + } else if (volume != 0 && it.isQuiver()) { + s << " (Vol:" << volume; } } else { bool found = true; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index db850af5ecc..3b133d68893 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5643,7 +5643,7 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) { if (it.abilities->perfectShotDamage > 0) { string.clear(); - string << std::showpos << it.abilities->perfectShotDamage << std::noshowpos << " at " << it.abilities->perfectShotRange << "%"; + string << std::showpos << it.abilities->perfectShotDamage << std::noshowpos << " at range " << unsigned(it.abilities->perfectShotRange); msg.addString(string.str(), "ProtocolGame::sendMarketDetail - string.str()"); } else { msg.add(0x00); From 231b3c8aed670f3030c99c2d0b3d2fb884c1a43a Mon Sep 17 00:00:00 2001 From: Karin Date: Tue, 7 May 2024 16:08:01 -0300 Subject: [PATCH 27/95] fix: convert 16 to 32 int mana shield (#2603) --- data-otservbr-global/migrations/43.lua | 2 +- data-otservbr-global/migrations/44.lua | 10 +++++++++- data-otservbr-global/migrations/45.lua | 3 +++ schema.sql | 6 +++--- src/creatures/combat/condition.cpp | 4 ++-- src/creatures/combat/condition.hpp | 2 +- src/creatures/creature.hpp | 8 ++++---- src/game/game.cpp | 2 +- src/io/functions/iologindata_load_player.cpp | 4 ++-- 9 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 data-otservbr-global/migrations/45.lua diff --git a/data-otservbr-global/migrations/43.lua b/data-otservbr-global/migrations/43.lua index 1464703c96e..6d3492a815a 100644 --- a/data-otservbr-global/migrations/43.lua +++ b/data-otservbr-global/migrations/43.lua @@ -1,5 +1,5 @@ function onUpdateDatabase() - logger.info("Updating database to version 43 (feat frags_limit, payment and duration_days in guild wars)") + logger.info("Updating database to version 44 (feat frags_limit, payment and duration_days in guild wars)") db.query([[ ALTER TABLE `guild_wars` diff --git a/data-otservbr-global/migrations/44.lua b/data-otservbr-global/migrations/44.lua index 86a6d8ffec1..c551fc79aeb 100644 --- a/data-otservbr-global/migrations/44.lua +++ b/data-otservbr-global/migrations/44.lua @@ -1,3 +1,11 @@ function onUpdateDatabase() - return false -- true = There are others migrations file | false = this is the last migration file + logger.info("Updating database to version 45 (fix: mana shield column size for more than 65k)") + + db.query([[ + ALTER TABLE `players` + MODIFY COLUMN `manashield` INT UNSIGNED NOT NULL DEFAULT '0', + MODIFY COLUMN `max_manashield` INT UNSIGNED NOT NULL DEFAULT '0'; + ]]) + + return true end diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua new file mode 100644 index 00000000000..86a6d8ffec1 --- /dev/null +++ b/data-otservbr-global/migrations/45.lua @@ -0,0 +1,3 @@ +function onUpdateDatabase() + return false -- true = There are others migrations file | false = this is the last migration file +end diff --git a/schema.sql b/schema.sql index 58e6ea216ce..624f434509e 100644 --- a/schema.sql +++ b/schema.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` ( CONSTRAINT `server_config_pk` PRIMARY KEY (`config`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '44'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0'); +INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '45'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0'); -- Table structure `accounts` CREATE TABLE IF NOT EXISTS `accounts` ( @@ -127,8 +127,8 @@ CREATE TABLE IF NOT EXISTS `players` ( `skill_lifeleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `skill_manaleech_chance` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `skill_manaleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0', - `manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', - `max_manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', + `manashield` INT UNSIGNED NOT NULL DEFAULT '0', + `max_manashield` INT UNSIGNED NOT NULL DEFAULT '0', `xpboost_stamina` smallint(5) UNSIGNED DEFAULT NULL, `xpboost_value` tinyint(4) UNSIGNED DEFAULT NULL, `marriage_status` bigint(20) UNSIGNED NOT NULL DEFAULT '0', diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 9d0f6962884..56c67700d13 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1308,7 +1308,7 @@ void ConditionManaShield::addCondition(std::shared_ptr creature, const bool ConditionManaShield::unserializeProp(ConditionAttr_t attr, PropStream &propStream) { if (attr == CONDITIONATTR_MANASHIELD) { - return propStream.read(manaShield); + return propStream.read(manaShield); } return Condition::unserializeProp(attr, propStream); } @@ -1317,7 +1317,7 @@ void ConditionManaShield::serialize(PropWriteStream &propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_MANASHIELD); - propWriteStream.write(manaShield); + propWriteStream.write(manaShield); } bool ConditionManaShield::setParam(ConditionParam_t param, int32_t value) { diff --git a/src/creatures/combat/condition.hpp b/src/creatures/combat/condition.hpp index 1cffba99b50..539a8711723 100644 --- a/src/creatures/combat/condition.hpp +++ b/src/creatures/combat/condition.hpp @@ -219,7 +219,7 @@ class ConditionManaShield final : public Condition { bool unserializeProp(ConditionAttr_t attr, PropStream &propStream) override; private: - uint16_t manaShield = 0; + uint32_t manaShield = 0; }; class ConditionSoul final : public ConditionGeneric { diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 4acbacb570a..2c92032bd65 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -209,19 +209,19 @@ class Creature : virtual public Thing, public SharedObject { return mana; } - uint16_t getManaShield() const { + uint32_t getManaShield() const { return manaShield; } - void setManaShield(uint16_t value) { + void setManaShield(uint32_t value) { manaShield = value; } - uint16_t getMaxManaShield() const { + uint32_t getMaxManaShield() const { return maxManaShield; } - void setMaxManaShield(uint16_t value) { + void setMaxManaShield(uint32_t value) { maxManaShield = value; } diff --git a/src/game/game.cpp b/src/game/game.cpp index eeaaabb39f7..18c46668a4f 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7008,7 +7008,7 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt if (target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) { int32_t manaDamage = std::min(target->getMana(), healthChange); - uint16_t manaShield = target->getManaShield(); + uint32_t manaShield = target->getManaShield(); if (manaShield > 0) { if (manaShield > manaDamage) { target->setManaShield(manaShield - manaDamage); diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 041cd323e28..2ac2cc863ea 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -180,8 +180,8 @@ bool IOLoginDataLoad::loadPlayerFirst(std::shared_ptr player, DBResult_p player->setXpBoostPercent(result->getNumber("xpboost_value")); player->setXpBoostTime(result->getNumber("xpboost_stamina")); - player->setManaShield(result->getNumber("manashield")); - player->setMaxManaShield(result->getNumber("max_manashield")); + player->setManaShield(result->getNumber("manashield")); + player->setMaxManaShield(result->getNumber("max_manashield")); return true; } From 62991d4fd3ac86059e271ca3fe72320f778bbcaf Mon Sep 17 00:00:00 2001 From: Karin Date: Wed, 8 May 2024 09:20:57 -0300 Subject: [PATCH 28/95] fix: imbuement elemental damage only to physical damage (#2614) --- src/creatures/combat/combat.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index cf98fcb6a7b..56cca63fe56 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -649,6 +649,10 @@ CombatDamage Combat::applyImbuementElementalDamage(std::shared_ptr attac continue; } + if (damage.primary.type != COMBAT_PHYSICALDAMAGE) { + break; + } + float damagePercent = imbuementInfo.imbuement->elementDamage / 100.0; damage.secondary.type = imbuementInfo.imbuement->combatType; From 10bc46b8fc84b3d6e24c289a8c54b8923779b972 Mon Sep 17 00:00:00 2001 From: Karin Date: Wed, 8 May 2024 09:31:56 -0300 Subject: [PATCH 29/95] fix: sanguine range wand/rod (#2606) --- data/items/items.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index 421e981ad02..0858df99219 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -75422,6 +75422,7 @@ Granted by TibiaGoals.com"/> + @@ -75449,6 +75450,7 @@ Granted by TibiaGoals.com"/> + @@ -75492,6 +75494,7 @@ Granted by TibiaGoals.com"/> + @@ -75522,6 +75525,7 @@ Granted by TibiaGoals.com"/> + From 94f178c26148f1be3095aa313ac8b43b6cfaef2c Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Thu, 9 May 2024 09:19:32 -0300 Subject: [PATCH 30/95] fix: beds (#2611) --- src/items/bed.cpp | 24 ++++++++++++++++++------ src/items/bed.hpp | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/items/bed.cpp b/src/items/bed.cpp index d093dcc5fd9..4b38be3b474 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -94,12 +94,7 @@ bool BedItem::canUse(std::shared_ptr player) { return false; } - auto partName = itemType.name; - auto nextPartname = nextBedItem->getName(); - auto firstPart = keepFirstWordOnly(partName); - auto nextPartOf = keepFirstWordOnly(nextPartname); - g_logger().debug("First bed part name {}, second part name {}", firstPart, nextPartOf); - if (!isMovable() || !nextBedItem->isMovable() || firstPart != nextPartOf) { + if (!isMovable() || !nextBedItem->isMovable() || !isBedComplete(nextBedItem)) { return false; } @@ -122,6 +117,23 @@ bool BedItem::canUse(std::shared_ptr player) { return true; } +bool BedItem::isBedComplete(std::shared_ptr nextBedItem) { + const ItemType &it = Item::items[id]; + + if (nextBedItem == nullptr) { + return false; + } + + auto partName = it.name; + auto nextPartname = nextBedItem->getName(); + auto firstPart = keepFirstWordOnly(partName); + auto nextPartOf = keepFirstWordOnly(nextPartname); + + g_logger().debug("First bed part id {} name {}, second part id {} name {}", it.id, firstPart, nextBedItem->getID(), nextPartOf); + + return it.bedPartOf == nextBedItem->getID(); +} + bool BedItem::trySleep(std::shared_ptr player) { if (!house || player->isRemoved()) { return false; diff --git a/src/items/bed.hpp b/src/items/bed.hpp index 59c0c66f68c..2aeb6e38e5b 100644 --- a/src/items/bed.hpp +++ b/src/items/bed.hpp @@ -39,6 +39,8 @@ class BedItem final : public Item { bool canUse(std::shared_ptr player); + bool isBedComplete(std::shared_ptr nextBedItem); + bool trySleep(std::shared_ptr player); bool sleep(std::shared_ptr player); void wakeUp(std::shared_ptr player); From 09765f8b2cac1b14cb1313d7b4cb1e9295238b61 Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Thu, 9 May 2024 09:19:56 -0300 Subject: [PATCH 31/95] feat: cyclopedia item summary (#2601) --- src/creatures/players/player.cpp | 54 +++++++++- src/creatures/players/player.hpp | 14 ++- src/game/game.cpp | 11 +- src/server/network/protocol/protocolgame.cpp | 105 +++++++++++++++++-- src/server/network/protocol/protocolgame.hpp | 2 +- 5 files changed, 170 insertions(+), 16 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 8b889ac200b..0ddb407687b 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3930,7 +3930,7 @@ bool Player::removeItemCountById(uint16_t itemId, uint32_t itemAmount, bool remo return false; } -ItemsTierCountList Player::getInventoryItemsId() const { +ItemsTierCountList Player::getInventoryItemsId(bool ignoreStoreInbox /* false */) const { ItemsTierCountList itemMap; for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) { std::shared_ptr item = inventory[i]; @@ -3938,8 +3938,14 @@ ItemsTierCountList Player::getInventoryItemsId() const { continue; } - (itemMap[item->getID()])[item->getTier()] += Item::countByType(item, -1); - if (std::shared_ptr container = item->getContainer()) { + const bool isStoreInbox = item->getID() == ITEM_STORE_INBOX; + + if (!isStoreInbox) { + (itemMap[item->getID()])[item->getTier()] += Item::countByType(item, -1); + } + + const auto &container = item->getContainer(); + if (container && (!isStoreInbox || !ignoreStoreInbox)) { for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { auto containerItem = *it; (itemMap[containerItem->getID()])[containerItem->getTier()] += Item::countByType(containerItem, -1); @@ -4038,6 +4044,48 @@ double_t Player::calculateDamageReduction(double_t currentTotal, int16_t resista return (100 - currentTotal) / 100.0 * resistance + currentTotal; } +ItemsTierCountList Player::getStoreInboxItemsId() const { + ItemsTierCountList itemMap; + const auto &container = getStoreInbox(); + if (container) { + for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { + std::shared_ptr item = *it; + (itemMap[item->getID()])[item->getTier()] += Item::countByType(item, -1); + } + } + + return itemMap; +} + +ItemsTierCountList Player::getDepotChestItemsId() const { + ItemsTierCountList itemMap; + + for (const auto &[index, depot] : depotChests) { + const std::shared_ptr &container = depot->getContainer(); + for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { + std::shared_ptr item = *it; + (itemMap[item->getID()])[item->getTier()] += Item::countByType(item, -1); + } + } + + return itemMap; +} + +ItemsTierCountList Player::getDepotInboxItemsId() const { + ItemsTierCountList itemMap; + + const std::shared_ptr &inbox = getInbox(); + const std::shared_ptr &container = inbox->getContainer(); + if (container) { + for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { + const auto &item = *it; + (itemMap[item->getID()])[item->getTier()] += Item::countByType(item, -1); + } + } + + return itemMap; +} + std::vector> Player::getAllInventoryItems(bool ignoreEquiped /*= false*/, bool ignoreItemWithTier /* false*/) const { std::vector> itemVector; for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index ca9c944bb2e..7d0291c4a3f 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1641,9 +1641,9 @@ class Player final : public Creature, public Cylinder, public Bankable { } } void sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, std::vector> achievementsUnlocked); - void sendCyclopediaCharacterItemSummary() { + void sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems) { if (client) { - client->sendCyclopediaCharacterItemSummary(); + client->sendCyclopediaCharacterItemSummary(inventoryItems, storeInboxItems, supplyStashItems, depotBoxItems, inboxItems); } } void sendCyclopediaCharacterOutfitsMounts() { @@ -2581,6 +2581,13 @@ class Player final : public Creature, public Cylinder, public Bankable { // Get specific inventory item from itemid std::vector> getInventoryItemsFromId(uint16_t itemId, bool ignore = true) const; + // this get all player store inbox items and return as ItemsTierCountList + ItemsTierCountList getStoreInboxItemsId() const; + // this get all player depot chest items and return as ItemsTierCountList + ItemsTierCountList getDepotChestItemsId() const; + // this get all player depot inbox items and return as ItemsTierCountList + ItemsTierCountList getDepotInboxItemsId() const; + // This get all player inventory items std::vector> getAllInventoryItems(bool ignoreEquiped = false, bool ignoreItemWithTier = false) const; @@ -2677,8 +2684,7 @@ class Player final : public Creature, public Cylinder, public Bankable { size_t getLastIndex() const override; uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override; void stashContainer(StashContainerList itemDict); - ItemsTierCountList getInventoryItemsId() const; - // todo ItemsTierCountList getStoreInboxItemsId() const; + ItemsTierCountList getInventoryItemsId(bool ignoreStoreInbox = false) const; // This function is a override function of base class std::map &getAllItemTypeCount(std::map &countMap) const override; diff --git a/src/game/game.cpp b/src/game/game.cpp index 18c46668a4f..fe52310e6b4 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8307,9 +8307,16 @@ void Game::playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_ case CYCLOPEDIA_CHARACTERINFO_ACHIEVEMENTS: player->achiev()->sendUnlockedSecretAchievements(); break; - case CYCLOPEDIA_CHARACTERINFO_ITEMSUMMARY: - player->sendCyclopediaCharacterItemSummary(); + case CYCLOPEDIA_CHARACTERINFO_ITEMSUMMARY: { + const ItemsTierCountList &inventoryItems = player->getInventoryItemsId(true); + const ItemsTierCountList &storeInboxItems = player->getStoreInboxItemsId(); + const StashItemList &supplyStashItems = player->getStashItems(); + const ItemsTierCountList &depotBoxItems = player->getDepotChestItemsId(); + const ItemsTierCountList &inboxItems = player->getDepotInboxItemsId(); + + player->sendCyclopediaCharacterItemSummary(inventoryItems, storeInboxItems, supplyStashItems, depotBoxItems, inboxItems); break; + } case CYCLOPEDIA_CHARACTERINFO_OUTFITSMOUNTS: player->sendCyclopediaCharacterOutfitsMounts(); break; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 3b133d68893..73b639885e6 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3699,7 +3699,7 @@ void ProtocolGame::sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, writeToOutputBuffer(msg); } -void ProtocolGame::sendCyclopediaCharacterItemSummary() { +void ProtocolGame::sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems) { if (!player || oldProtocol) { return; } @@ -3709,11 +3709,104 @@ void ProtocolGame::sendCyclopediaCharacterItemSummary() { msg.addByte(CYCLOPEDIA_CHARACTERINFO_ITEMSUMMARY); msg.addByte(0x00); // 0x00 Here means 'no error' - msg.add(0); // inventoryItems.size() - msg.add(0); // storeInboxItems.size() - msg.add(0); // supplyStashItems.size() - msg.add(0); // depotBoxItems.size() - msg.add(0); // inboxItems.size() + uint16_t inventoryItemsCount = 0; + const auto startInventory = msg.getBufferPosition(); + msg.skipBytes(2); + + for (const auto &inventoryItems_it : inventoryItems) { + for (const auto &[itemTier, itemCount] : inventoryItems_it.second) { + const ItemType &it = Item::items[inventoryItems_it.first]; + msg.add(inventoryItems_it.first); // Item ID + if (it.upgradeClassification > 0) { + msg.addByte(itemTier); + } + msg.add(itemCount); + + ++inventoryItemsCount; + } + } + + const auto endInventory = msg.getBufferPosition(); + + msg.setBufferPosition(startInventory); + msg.add(inventoryItemsCount); + + msg.setBufferPosition(endInventory); + + uint16_t storeInboxItemsCount = 0; + const auto startStoreInbox = msg.getBufferPosition(); + msg.skipBytes(2); + + for (const auto &storeInboxItems_it : storeInboxItems) { + for (const auto &[itemTier, itemCount] : storeInboxItems_it.second) { + const ItemType &it = Item::items[storeInboxItems_it.first]; + msg.add(storeInboxItems_it.first); // Item ID + if (it.upgradeClassification > 0) { + msg.addByte(itemTier); + } + msg.add(itemCount); + + ++storeInboxItemsCount; + } + } + + const auto endStoreInbox = msg.getBufferPosition(); + + msg.setBufferPosition(startStoreInbox); + msg.add(storeInboxItemsCount); + + msg.setBufferPosition(endStoreInbox); + + msg.add(supplyStashItems.size()); + + for (const auto &[itemId, itemCount] : supplyStashItems) { + msg.add(itemId); + msg.add(itemCount); + } + + uint16_t depotBoxItemsCount = 0; + const auto startDepotBox = msg.getBufferPosition(); + msg.skipBytes(2); + + for (const auto &depotBoxItems_it : depotBoxItems) { + for (const auto &[itemTier, itemCount] : depotBoxItems_it.second) { + const ItemType &it = Item::items[depotBoxItems_it.first]; + msg.add(depotBoxItems_it.first); // Item ID + if (it.upgradeClassification > 0) { + msg.addByte(itemTier); + } + msg.add(itemCount); + + ++depotBoxItemsCount; + } + } + + const auto endDepotBox = msg.getBufferPosition(); + + msg.setBufferPosition(startDepotBox); + msg.add(depotBoxItemsCount); + + msg.setBufferPosition(endDepotBox); + + uint16_t inboxItemsCount = 0; + const auto startInbox = msg.getBufferPosition(); + msg.skipBytes(2); + + for (const auto &inboxItems_it : inboxItems) { + for (const auto &[itemTier, itemCount] : inboxItems_it.second) { + const ItemType &it = Item::items[inboxItems_it.first]; + msg.add(inboxItems_it.first); // Item ID + if (it.upgradeClassification > 0) { + msg.addByte(itemTier); + } + msg.add(itemCount); + + ++inboxItemsCount; + } + } + + msg.setBufferPosition(startInbox); + msg.add(inboxItemsCount); writeToOutputBuffer(msg); } diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 7aea8ff805d..19e62c428e9 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -317,7 +317,7 @@ class ProtocolGame final : public Protocol { void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries); void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries); void sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, std::vector> achievementsUnlocked); - void sendCyclopediaCharacterItemSummary(); + void sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems); void sendCyclopediaCharacterOutfitsMounts(); void sendCyclopediaCharacterStoreSummary(); void sendCyclopediaCharacterInspection(); From 529c7a43859b58256bb51bf55dbe0efe9a6dbf7e Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Sat, 11 May 2024 16:46:41 -0300 Subject: [PATCH 32/95] feat: title system (#2576) --- data/scripts/talkactions/god/add_skill.lua | 3 +- data/scripts/talkactions/god/charms.lua | 8 +- .../talkactions/god/create_monster.lua | 6 +- data/scripts/talkactions/god/manage_badge.lua | 8 +- .../talkactions/god/manage_storage.lua | 3 +- data/scripts/talkactions/god/manage_title.lua | 70 ++++ .../god/{vip_manager.lua => manage_vip.lua} | 0 src/creatures/CMakeLists.txt | 1 + .../players/cyclopedia/player_badge.hpp | 2 +- .../players/cyclopedia/player_title.cpp | 324 ++++++++++++++++++ .../players/cyclopedia/player_title.hpp | 104 ++++++ src/creatures/players/player.cpp | 16 +- src/creatures/players/player.hpp | 10 + src/enums/player_cyclopedia.hpp | 38 ++ src/game/game.cpp | 219 +++++++++--- src/game/game.hpp | 7 + src/game/game_definitions.hpp | 10 +- src/io/functions/iologindata_load_player.cpp | 1 + .../functions/core/game/game_functions.cpp | 1 + .../creatures/player/player_functions.cpp | 55 +++ .../creatures/player/player_functions.hpp | 9 + src/server/network/protocol/protocolgame.cpp | 46 ++- src/server/network/protocol/protocolgame.hpp | 2 + src/server/server_definitions.hpp | 6 +- vcproj/canary.vcxproj | 3 + 25 files changed, 865 insertions(+), 87 deletions(-) create mode 100644 data/scripts/talkactions/god/manage_title.lua rename data/scripts/talkactions/god/{vip_manager.lua => manage_vip.lua} (100%) create mode 100644 src/creatures/players/cyclopedia/player_title.cpp create mode 100644 src/creatures/players/cyclopedia/player_title.hpp create mode 100644 src/enums/player_cyclopedia.hpp diff --git a/data/scripts/talkactions/god/add_skill.lua b/data/scripts/talkactions/god/add_skill.lua index f1c6244fba6..20644582325 100644 --- a/data/scripts/talkactions/god/add_skill.lua +++ b/data/scripts/talkactions/god/add_skill.lua @@ -44,8 +44,7 @@ function addSkill.onSay(player, words, param) return true end - -- Trim left - split[2] = split[2]:gsub("^%s*(.-)$", "%1") + split[2] = split[2]:trimSpace() local count = 1 if split[3] then diff --git a/data/scripts/talkactions/god/charms.lua b/data/scripts/talkactions/god/charms.lua index 14dde0a58d7..f06c17ac7e7 100644 --- a/data/scripts/talkactions/god/charms.lua +++ b/data/scripts/talkactions/god/charms.lua @@ -19,8 +19,8 @@ function addCharm.onSay(player, words, param) player:sendCancelMessage("A player with that name is not online.") return true end - --trim left - split[2] = split[2]:gsub("^%s*(.-)$", "%1") + + split[2] = split[2]:trimSpace() player:sendCancelMessage("Added " .. split[2] .. " charm points to character '" .. target:getName() .. "'.") target:sendCancelMessage("Received " .. split[2] .. " charm points!") @@ -133,8 +133,8 @@ function setBestiary.onSay(player, words, param) return true end - split[2] = split[2]:gsub("^%s*(.-)$", "%1") --Trim left - split[3] = split[3]:gsub("^%s*(.-)$", "%1") --Trim left + split[2] = split[2]:trimSpace() + split[3] = split[3]:trimSpace() local monsterName = split[2] local mType = MonsterType(monsterName) diff --git a/data/scripts/talkactions/god/create_monster.lua b/data/scripts/talkactions/god/create_monster.lua index 924a0d0906d..ce5869a2eee 100644 --- a/data/scripts/talkactions/god/create_monster.lua +++ b/data/scripts/talkactions/god/create_monster.lua @@ -73,20 +73,20 @@ function createMonster.onSay(player, words, param) local monsterName = split[1] local monsterCount = 0 if split[2] then - split[2] = split[2]:gsub("^%s*(.-)$", "%1") --Trim left + split[2] = split[2]:trimSpace() monsterCount = tonumber(split[2]) end local monsterForge = nil if split[3] then - split[3] = split[3]:gsub("^%s*(.-)$", "%1") --Trim left + split[3] = split[3]:trimSpace() monsterForge = split[3] end if monsterCount > 1 then local spawnRadius = 5 if split[4] then - split[4] = split[4]:gsub("^%s*(.-)$", "%1") --Trim left + split[4] = split[4]:trimSpace() spawnRadius = split[4] print(spawnRadius) end diff --git a/data/scripts/talkactions/god/manage_badge.lua b/data/scripts/talkactions/god/manage_badge.lua index f2899a0d592..310cce247b0 100644 --- a/data/scripts/talkactions/god/manage_badge.lua +++ b/data/scripts/talkactions/god/manage_badge.lua @@ -21,10 +21,12 @@ function addBadge.onSay(player, words, param) return true end - -- Trim left - split[2] = split[2]:gsub("^%s*(.-)$", "%1") + split[2] = split[2]:trimSpace() local id = tonumber(split[2]) - target:addBadge(id) + if target:addBadge(id) then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You added a badge with ID "%i" to player "%s".', id, target:getName())) + target:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s added a badge to you.", player:getName())) + end return true end diff --git a/data/scripts/talkactions/god/manage_storage.lua b/data/scripts/talkactions/god/manage_storage.lua index 929ef7b4417..e1973ebde00 100644 --- a/data/scripts/talkactions/god/manage_storage.lua +++ b/data/scripts/talkactions/god/manage_storage.lua @@ -18,8 +18,7 @@ function Player.getStorageValueTalkaction(self, param) return true end - -- Trim left - split[2] = split[2]:gsub("^%s*(.-)$", "%1") + split[2] = split[2]:trimSpace() -- Try to convert the second parameter to a number. If it's not a number, treat it as a storage name local storageKey = tonumber(split[2]) diff --git a/data/scripts/talkactions/god/manage_title.lua b/data/scripts/talkactions/god/manage_title.lua new file mode 100644 index 00000000000..d99adbe5dc0 --- /dev/null +++ b/data/scripts/talkactions/god/manage_title.lua @@ -0,0 +1,70 @@ +local addTitle = TalkAction("/addtitle") + +function addTitle.onSay(player, words, param) + -- create log + logCommand(player, words, param) + + if param == "" then + player:sendCancelMessage("Command param required.") + return true + end + + local split = param:split(",") + if not split[2] then + player:sendCancelMessage("Insufficient parameters. Usage: /addtitle playerName, badgeID") + return true + end + + local target = Player(split[1]) + if not target then + player:sendCancelMessage("A player with that name is not online.") + return true + end + + split[2] = split[2]:trimSpace() + local id = tonumber(split[2]) + if target:addTitle(id) then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You added a title with ID "%i" to player "%s".', id, target:getName())) + target:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s added a title to you.", player:getName())) + end + + return true +end + +addTitle:separator(" ") +addTitle:groupType("god") +addTitle:register() + +----------------------------------------- +local setTitle = TalkAction("/settitle") + +function setTitle.onSay(player, words, param) + -- create log + logCommand(player, words, param) + + if param == "" then + player:sendCancelMessage("Command param required.") + return true + end + + local split = param:split(",") + if not split[2] then + player:sendCancelMessage("Insufficient parameters. Usage: /settitle playerName, badgeID") + return true + end + + local target = Player(split[1]) + if not target then + player:sendCancelMessage("A player with that name is not online.") + return true + end + + split[2] = split[2]:trimSpace() + local id = tonumber(split[2]) + target:setCurrentTitle(id) + return true +end + +setTitle:separator(" ") +setTitle:groupType("god") +setTitle:register() diff --git a/data/scripts/talkactions/god/vip_manager.lua b/data/scripts/talkactions/god/manage_vip.lua similarity index 100% rename from data/scripts/talkactions/god/vip_manager.lua rename to data/scripts/talkactions/god/manage_vip.lua diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index 630812c7996..6715281439f 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE players/player.cpp players/achievement/player_achievement.cpp players/cyclopedia/player_badge.cpp + players/cyclopedia/player_title.cpp players/wheel/player_wheel.cpp players/wheel/wheel_gems.cpp players/vocations/vocation.cpp diff --git a/src/creatures/players/cyclopedia/player_badge.hpp b/src/creatures/players/cyclopedia/player_badge.hpp index 01c9dc0e63f..7bf28c0c302 100644 --- a/src/creatures/players/cyclopedia/player_badge.hpp +++ b/src/creatures/players/cyclopedia/player_badge.hpp @@ -9,7 +9,7 @@ #pragma once -#include "game/game_definitions.hpp" +#include "enums/player_cyclopedia.hpp" class Player; class KV; diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp new file mode 100644 index 00000000000..9e1c763a4ad --- /dev/null +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -0,0 +1,324 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "player_title.hpp" + +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "kv/kv.hpp" + +PlayerTitle::PlayerTitle(Player &player) : + m_player(player) { } + +bool PlayerTitle::isTitleUnlocked(uint8_t id) const { + if (id == 0) { + return false; + } + + if (auto it = std::find_if(m_titlesUnlocked.begin(), m_titlesUnlocked.end(), [id](auto title_it) { + return title_it.first.m_id == id; + }); + it != m_titlesUnlocked.end()) { + return true; + } + + return false; +} + +bool PlayerTitle::manage(bool canAdd, uint8_t id, uint32_t timestamp /* = 0*/) { + const Title &title = g_game().getTitleById(id); + if (title.m_id == 0) { + return false; + } + + if (!canAdd) { + if (!title.m_permanent) { + remove(title); + } + return false; + } + + if (isTitleUnlocked(id)) { + return false; + } + + int toSaveTimeStamp = timestamp != 0 ? timestamp : (OTSYS_TIME() / 1000); + getUnlockedKV()->set(title.m_maleName, toSaveTimeStamp); + m_titlesUnlocked.emplace_back(title, toSaveTimeStamp); + m_titlesUnlocked.shrink_to_fit(); + g_logger().debug("[{}] - Added title: {}", __FUNCTION__, title.m_maleName); + + return true; +} + +void PlayerTitle::remove(const Title &title) { + auto id = title.m_id; + if (!isTitleUnlocked(id)) { + return; + } + + auto it = std::find_if(m_titlesUnlocked.begin(), m_titlesUnlocked.end(), [id](auto title_it) { + return title_it.first.m_id == id; + }); + + if (it == m_titlesUnlocked.end()) { + return; + } + + getUnlockedKV()->remove(title.m_maleName); + m_titlesUnlocked.erase(it); + m_titlesUnlocked.shrink_to_fit(); + g_logger().debug("[{}] - Removed title: {}", __FUNCTION__, title.m_maleName); +} + +const std::vector> &PlayerTitle::getUnlockedTitles() { + return m_titlesUnlocked; +} + +uint8_t PlayerTitle::getCurrentTitle() const { + return static_cast(m_player.kv()->scoped("titles")->get("current-title")->getNumber()); +} + +void PlayerTitle::setCurrentTitle(uint8_t id) { + m_player.kv()->scoped("titles")->set("current-title", id != 0 && isTitleUnlocked(id) ? id : 0); +} + +std::string PlayerTitle::getCurrentTitleName() { + auto currentTitle = getCurrentTitle(); + if (currentTitle == 0) { + return ""; + } + + auto title = g_game().getTitleById(currentTitle); + if (title.m_id == 0) { + return ""; + } + + return getNameBySex(m_player.getSex(), title.m_maleName, title.m_femaleName); +} + +const std::string &PlayerTitle::getNameBySex(PlayerSex_t sex, const std::string &male, const std::string &female) { + return sex == PLAYERSEX_FEMALE && !female.empty() ? female : male; +} + +void PlayerTitle::checkAndUpdateNewTitles() { + Benchmark bm_checkTitles; + for (const auto &title : g_game().getTitles()) { + switch (title.m_type) { + case CyclopediaTitle_t::NOTHING: + break; + case CyclopediaTitle_t::GOLD: + manage(checkGold(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::MOUNTS: + manage(checkMount(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::OUTFITS: + manage(checkOutfit(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::LEVEL: + manage(checkLevel(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::HIGHSCORES: + manage(checkHighscore(title.m_skill), title.m_id); + break; + case CyclopediaTitle_t::BESTIARY: + case CyclopediaTitle_t::BOSSTIARY: + manage(checkBestiary(title.m_maleName, title.m_race, title.m_type == CyclopediaTitle_t::BOSSTIARY, title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::DAILY_REWARD: + manage(checkLoginStreak(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::TASK: + manage(checkTask(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::MAP: + // manage(checkMap(title.m_amount), title.m_id); + break; + case CyclopediaTitle_t::OTHERS: + manage(checkOther(title.m_maleName), title.m_id); + break; + } + } + + g_logger().debug("Checking and updating titles of player {} took {} milliseconds.", m_player.getName(), bm_checkTitles.duration()); + + loadUnlockedTitles(); +} + +void PlayerTitle::loadUnlockedTitles() { + const auto &unlockedTitles = getUnlockedKV()->keys(); + g_logger().debug("[{}] - Loading unlocked titles: {}", __FUNCTION__, unlockedTitles.size()); + for (const auto &titleName : unlockedTitles) { + const Title &title = g_game().getTitleByName(titleName); + if (title.m_id == 0) { + g_logger().error("[{}] - Title {} not found.", __FUNCTION__, titleName); + continue; + } + + m_titlesUnlocked.emplace_back(title, getUnlockedKV()->get(titleName)->getNumber()); + } +} + +const std::shared_ptr &PlayerTitle::getUnlockedKV() { + if (m_titleUnlockedKV == nullptr) { + m_titleUnlockedKV = m_player.kv()->scoped("titles")->scoped("unlocked"); + } + + return m_titleUnlockedKV; +} + +// Title Calculate Functions +bool PlayerTitle::checkGold(uint32_t amount) { + return m_player.getBankBalance() >= amount; +} + +bool PlayerTitle::checkMount(uint32_t amount) { + uint8_t total = 0; + for (const auto &mount : g_game().mounts.getMounts()) { + if (m_player.hasMount(mount)) { + total = total++; + } + } + return total >= amount; +} + +bool PlayerTitle::checkOutfit(uint32_t amount) { + return m_player.outfits.size() >= amount; +} + +bool PlayerTitle::checkLevel(uint32_t amount) { + return m_player.getLevel() >= amount; +} + +bool PlayerTitle::checkHighscore(uint8_t skill) { + Database &db = Database::getInstance(); + std::string query; + std::string fieldCheck = "id"; + + switch (static_cast(skill)) { + case HighscoreCategories_t::CHARMS: + query = fmt::format( + "SELECT `pc`.`player_guid`, `pc`.`charm_points`, `p`.`group_id` FROM `player_charms` pc JOIN `players` p ON `pc`.`player_guid` = `p`.`id` WHERE `p`.`group_id` < {} ORDER BY `pc`.`charm_points` DESC LIMIT 1", + static_cast(GROUP_TYPE_GAMEMASTER) + ); + fieldCheck = "player_guid"; + break; + case HighscoreCategories_t::DROME: + // todo check if player is in the top 5 for the previous rota of the Tibiadrome. + return false; + case HighscoreCategories_t::GOSHNAR: + // todo check if player is the most killer of Goshnar and his aspects. + return false; + default: + std::string skillName = g_game().getSkillNameById(skill); + query = fmt::format( + "SELECT * FROM `players` WHERE `group_id` < {} AND `{}` > 10 ORDER BY `{}` DESC LIMIT 1", + static_cast(GROUP_TYPE_GAMEMASTER), skillName, skillName + ); + break; + } + + DBResult_ptr result = db.storeQuery(query); + if (!result) { + return false; + } + + auto resultValue = result->getNumber(fieldCheck); + g_logger().debug("top id: {}, player id: {}", resultValue, m_player.getGUID()); + + return resultValue == m_player.getGUID(); +} + +bool PlayerTitle::checkBestiary(const std::string &name, uint16_t race, bool isBoss /* = false*/, uint32_t amount) { + if (race == 0) { + if (name == "Executioner") { + // todo check if player has unlocked all bestiary + } else if (name == "Boss Executioner") { + // todo check if player has unlocked all bosses + } + return false; + } + if (isBoss && amount > 0) { + // todo check if this way, is calculating by boss race + return m_player.getBestiaryKillCount(race) >= amount; + } + return m_player.isCreatureUnlockedOnTaskHunting(g_monsters().getMonsterTypeByRaceId(race, isBoss)); +} + +bool PlayerTitle::checkLoginStreak(uint32_t amount) { + auto streakKV = m_player.kv()->scoped("daily-reward")->get("streak"); + return streakKV && streakKV.has_value() && static_cast(streakKV->getNumber()) >= amount; +} + +bool PlayerTitle::checkTask(uint32_t amount) { + return m_player.getTaskHuntingPoints() >= amount; +} + +bool PlayerTitle::checkMap(uint32_t amount) { + // todo cyclopledia + return false; +} + +bool PlayerTitle::checkOther(const std::string &name) { + if (name == "Guild Leader") { + auto rank = m_player.getGuildRank(); + return rank && rank->level == 3; + } else if (name == "Proconsul of Iksupan") { + // Win Ancient Aucar Outfits complete so fight with Atab and be teleported to the arena. + } else if (name == "Admirer of the Crown") { + // Complete the Royal Costume Outfits. + return m_player.canWear(1457, 3) && m_player.canWear(1456, 3); + } else if (name == "Big Spender") { + // Unlocked the full Golden Outfit. + return m_player.canWear(1211, 3) && m_player.canWear(1210, 3); + } else if (name == "Challenger of the Iks") { + // Defeat Ahau while equipping a Broken Iks Headpiece, a Broken Iks Cuirass, some Broken Iks Faulds and Broken Iks Sandals + return m_player.getBestiaryKillCount(2346) >= 1; + } else if (name == "Royal Bounacean Advisor") { + // Complete the Galthen and the Lost Queen quest line + // Win Royal Bounacean Outfit + return m_player.canWear(1437, 3) && m_player.canWear(1436, 3); + } else if (name == "Aeternal") { + // Unlocked by 10-year-old characters. + } else if (name == "Robinson Crusoe") { + // Visit Schrödinger's Island. + } else if (name == "Chompmeister") { + // Complete all Jean Pierre's dishes in Hot Cuisine Quest. + } else if (name == "Bringer of Rain") { + // Clear wave 100 in the Tibiadrome. + } else if (name == "Beastly") { + // Reached 2000 charm points + return m_player.getCharmPoints() >= 2000; + } else if (name == "Midnight Hunter") { + // Kill a certain amount of Midnight Panthers. + // (The exact number is yet to be confirmed but is at least 21 and at most 28 panthers.) + return m_player.getBestiaryKillCount(698) >= 25; + } else if (name == "Ratinator") { + // Kill 10,000 Cave Rats. + return m_player.getBestiaryKillCount(56) >= 10000; + } else if (name == "Doomsday Nemesis") { + // Kill Gaz'haragoth one time. + return m_player.getBestiaryKillCount(1003) >= 1; + } else if (name == "Hero of Bounac") { + // Complete The Order of the Lion Quest. + } else if (name == "King of Demon") { + // Defeat Morshabaal 5 times. + return m_player.getBestiaryKillCount(2118) >= 5; + } else if (name == "Planegazer") { + // Kill Planestrider in Opticording Sphere Quest. + } else if (name == "Time Traveller") { + // Complete 25 Years of Tibia Quest. + } else if (name == "Truly Boss") { + return m_player.getBossPoints() >= 15000; + } + return false; +} diff --git a/src/creatures/players/cyclopedia/player_title.hpp b/src/creatures/players/cyclopedia/player_title.hpp new file mode 100644 index 00000000000..abf29650961 --- /dev/null +++ b/src/creatures/players/cyclopedia/player_title.hpp @@ -0,0 +1,104 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include + +#include "creatures/creatures_definitions.hpp" +#include "enums/player_cyclopedia.hpp" +#include "enums/account_group_type.hpp" + +class Player; +class KV; + +struct Title { + uint8_t m_id = 0; + CyclopediaTitle_t m_type = CyclopediaTitle_t::NOTHING; + std::string m_maleName; + std::string m_femaleName; + std::string m_description; + uint32_t m_amount = 0; + bool m_permanent = false; + uint8_t m_skill = 0; + uint16_t m_race = 0; + + Title() = default; + + Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, uint32_t amount, bool permanent) : + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_amount(amount), + m_permanent(permanent) { } + + Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, uint32_t amount, bool permanent, std::string femaleName) : + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_amount(amount), + m_permanent(permanent), m_femaleName(std::move(femaleName)) { } + + Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string femaleName, std::string description, uint8_t skill) : + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), m_description(std::move(description)), + m_skill(skill) { } + + Title(uint8_t id, CyclopediaTitle_t type, uint16_t race, std::string maleName, std::string femaleName, std::string description) : + m_id(id), m_type(type), m_race(race), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), + m_description(std::move(description)) { } + + Title(uint8_t id, CyclopediaTitle_t type, uint16_t race, std::string maleName, std::string femaleName, std::string description, uint32_t amount, bool permanent) : + m_id(id), m_type(type), m_race(race), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), + m_description(std::move(description)), m_amount(amount), m_permanent(permanent) { } + + Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, bool permanent) : + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_permanent(permanent) { } + + bool operator==(const Title &other) const { + return m_id == other.m_id; + } +}; + +namespace std { + template <> + struct hash { + std::size_t operator()(const Title &t) const { + return hash<uint8_t>()(t.m_id); + } + }; +} + +class PlayerTitle { +public: + explicit PlayerTitle(Player &player); + + [[nodiscard]] bool isTitleUnlocked(uint8_t id) const; + bool manage(bool canAdd, uint8_t id, uint32_t timestamp = 0); + void remove(const Title &title); + const std::vector<std::pair<Title, uint32_t>> &getUnlockedTitles(); + [[nodiscard]] uint8_t getCurrentTitle() const; + void setCurrentTitle(uint8_t id); + std::string getCurrentTitleName(); + static const std::string &getNameBySex(PlayerSex_t sex, const std::string &male, const std::string &female); + void checkAndUpdateNewTitles(); + void loadUnlockedTitles(); + const std::shared_ptr<KV> &getUnlockedKV(); + + // Title Calculate Functions + bool checkGold(uint32_t amount); + bool checkMount(uint32_t amount); + bool checkOutfit(uint32_t amount); + bool checkLevel(uint32_t amount); + bool checkHighscore(uint8_t skill); + bool checkBestiary(const std::string &name, uint16_t race, bool isBoss = false, uint32_t amount = 0); + bool checkLoginStreak(uint32_t amount); + bool checkTask(uint32_t amount); + bool checkMap(uint32_t amount); + bool checkOther(const std::string &name); + +private: + // {title ID, time when it was unlocked} + std::shared_ptr<KV> m_titleUnlockedKV; + std::vector<std::pair<Title, uint32_t>> m_titlesUnlocked; + Player &m_player; +}; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 0ddb407687b..92249e1114f 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -17,6 +17,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/players/storages/storages.hpp" #include "game/game.hpp" #include "game/modal_window/modal_window.hpp" @@ -51,6 +52,7 @@ Player::Player(ProtocolGame_ptr p) : m_wheelPlayer = std::make_unique<PlayerWheel>(*this); m_playerAchievement = std::make_unique<PlayerAchievement>(*this); m_playerBadge = std::make_unique<PlayerBadge>(*this); + m_playerTitle = std::make_unique<PlayerTitle>(*this); } Player::~Player() { @@ -119,9 +121,10 @@ std::string Player::getDescription(int32_t lookDistance) { std::ostringstream s; std::string subjectPronoun = getSubjectPronoun(); capitalizeWords(subjectPronoun); + auto playerTitle = title()->getCurrentTitle() == 0 ? "" : (", " + title()->getCurrentTitleName()); if (lookDistance == -1) { - s << "yourself."; + s << "yourself" << playerTitle << "."; if (group->access) { s << " You are " << group->name << '.'; @@ -144,7 +147,7 @@ std::string Player::getDescription(int32_t lookDistance) { s << " (Level " << level << ')'; } - s << ". " << subjectPronoun; + s << playerTitle << ". " << subjectPronoun; if (group->access) { s << " " << getSubjectVerb() << " " << group->name << '.'; @@ -8016,6 +8019,15 @@ const std::unique_ptr<PlayerBadge> &Player::badge() const { return m_playerBadge; } +// Title interface +std::unique_ptr<PlayerTitle> &Player::title() { + return m_playerTitle; +} + +const std::unique_ptr<PlayerTitle> &Player::title() const { + return m_playerTitle; +} + void Player::sendLootMessage(const std::string &message) const { auto party = getParty(); if (!party) { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 7d0291c4a3f..da4c0cec9e8 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -34,7 +34,9 @@ #include "creatures/npcs/npc.hpp" #include "game/bank/bank.hpp" #include "enums/object_category.hpp" +#include "enums/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" class House; class NetworkMessage; @@ -51,12 +53,14 @@ class Spell; class PlayerWheel; class PlayerAchievement; class PlayerBadge; +class PlayerTitle; class Spectators; class Account; struct ModalWindow; struct Achievement; struct Badge; +struct Title; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -2616,6 +2620,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr<PlayerBadge> &badge(); const std::unique_ptr<PlayerBadge> &badge() const; + // Player title interface + std::unique_ptr<PlayerTitle> &title(); + const std::unique_ptr<PlayerTitle> &title() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr<Container> getLootPouch(); @@ -3011,10 +3019,12 @@ class Player final : public Creature, public Cylinder, public Bankable { friend class IOLoginDataSave; friend class PlayerAchievement; friend class PlayerBadge; + friend class PlayerTitle; std::unique_ptr<PlayerWheel> m_wheelPlayer; std::unique_ptr<PlayerAchievement> m_playerAchievement; std::unique_ptr<PlayerBadge> m_playerBadge; + std::unique_ptr<PlayerTitle> m_playerTitle; std::mutex quickLootMutex; diff --git a/src/enums/player_cyclopedia.hpp b/src/enums/player_cyclopedia.hpp new file mode 100644 index 00000000000..f0637011a19 --- /dev/null +++ b/src/enums/player_cyclopedia.hpp @@ -0,0 +1,38 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR <opentibiabr@outlook.com> + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include <cstdint> +#endif + +enum CyclopediaBadge_t : uint8_t { + ACCOUNT_AGE = 1, + LOYALTY, + ACCOUNT_ALL_LEVEL, + ACCOUNT_ALL_VOCATIONS, + TOURNAMENT_PARTICIPATION, + TOURNAMENT_POINTS, +}; + +enum CyclopediaTitle_t : uint8_t { + NOTHING = 0, + GOLD, + MOUNTS, + OUTFITS, + LEVEL, + HIGHSCORES, + BESTIARY, + BOSSTIARY, + DAILY_REWARD, + TASK, + MAP, + OTHERS, +}; diff --git a/src/game/game.cpp b/src/game/game.cpp index fe52310e6b4..d731bd766b6 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -38,6 +38,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/npcs/npc.hpp" #include "server/network/webhook/webhook.hpp" #include "server/network/protocol/protocollogin.hpp" @@ -222,14 +223,121 @@ Game::Game() { Badge(21, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Champion", 10000), }; + m_titles = { + Title(1, CyclopediaTitle_t::GOLD, "Gold Hoarder", "Earned at least 1,000,000 gold.", 1000000, false), + Title(2, CyclopediaTitle_t::GOLD, "Platinum Hoarder", "Earned at least 10,000,000 gold.", 10000000, false), + Title(3, CyclopediaTitle_t::GOLD, "Crystal Hoarder", "Earned at least 100,000,000 gold.", 100000000, false), + + Title(4, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 1)", "Unlocked 10 or more Mounts.", 10, true), + Title(5, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 2)", "Unlocked 20 or more Mounts.", 20, true), + Title(6, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 3)", "Unlocked 30 or more Mounts.", 30, true), + Title(7, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 4)", "Unlocked 40 or more Mounts.", 40, true), + Title(8, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 5)", "Unlocked 50 or more Mounts.", 50, true), + + Title(9, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 1)", "Unlocked 10 or more Outfits.", 10, true), + Title(10, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 2)", "Unlocked 20 or more Outfits.", 20, true), + Title(11, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 3)", "Unlocked 30 or more Outfits.", 30, true), + Title(12, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 4)", "Unlocked 40 or more Outfits.", 40, true), + Title(13, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 5)", "Unlocked 50 or more Outfits.", 50, true), + + Title(14, CyclopediaTitle_t::LEVEL, "Trolltrasher", "Reached level 50.", 50, false), + Title(15, CyclopediaTitle_t::LEVEL, "Cyclopscamper", "Reached level 100.", 100, false), + Title(16, CyclopediaTitle_t::LEVEL, "Dragondouser", "Reached level 200.", 200, false), + Title(17, CyclopediaTitle_t::LEVEL, "Demondoom", "Reached level 300.", 300, false), + Title(18, CyclopediaTitle_t::LEVEL, "Drakenbane", "Reached level 400.", 400, false), + Title(19, CyclopediaTitle_t::LEVEL, "Silencer", "Reached level 500.", 500, false), + Title(20, CyclopediaTitle_t::LEVEL, "Exalted", "Reached level 1000.", 1000, false), + + Title(21, CyclopediaTitle_t::HIGHSCORES, "Apex Predator", "", "Highest Level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::EXPERIENCE)), + Title(22, CyclopediaTitle_t::HIGHSCORES, "Big Boss", "", "Highest score of accumulated boss points on character's world.", static_cast<uint8_t>(HighscoreCategories_t::BOSS_POINTS)), + Title(23, CyclopediaTitle_t::HIGHSCORES, "Jack of all Taints", "", "Highest score for killing Goshnar and his aspects on character's world.", static_cast<uint8_t>(HighscoreCategories_t::GOSHNAR)), + Title(24, CyclopediaTitle_t::HIGHSCORES, "Legend of Fishing", "", "Highest fishing level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::FISHING)), + Title(25, CyclopediaTitle_t::HIGHSCORES, "Legend of Magic", "", "Highest magic level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::MAGIC_LEVEL)), + Title(26, CyclopediaTitle_t::HIGHSCORES, "Legend of Marksmanship", "", "Highest distance level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::DISTANCE_FIGHTING)), + Title(27, CyclopediaTitle_t::HIGHSCORES, "Legend of the Axe", "", "Highest axe level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::AXE_FIGHTING)), + Title(28, CyclopediaTitle_t::HIGHSCORES, "Legend of the Club", "", "Highest club level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::CLUB_FIGHTING)), + Title(29, CyclopediaTitle_t::HIGHSCORES, "Legend of the Fist", "", "Highest fist level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::FIST_FIGHTING)), + Title(30, CyclopediaTitle_t::HIGHSCORES, "Legend of the Shield", "", "Highest shielding level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::SHIELDING)), + Title(31, CyclopediaTitle_t::HIGHSCORES, "Legend of the Sword", "", "Highest sword level on character's world.", static_cast<uint8_t>(HighscoreCategories_t::SWORD_FIGHTING)), + Title(32, CyclopediaTitle_t::HIGHSCORES, "Prince Charming", "Princess Charming", "Highest score of accumulated charm points on character's world.", static_cast<uint8_t>(HighscoreCategories_t::CHARMS)), + Title(33, CyclopediaTitle_t::HIGHSCORES, "Reigning Drome Champion", "", "Finished most recent Tibiadrome rota ranked in the top 5.", static_cast<uint8_t>(HighscoreCategories_t::DROME)), + + Title(34, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_HUMANOID), "Bipedantic", "", "Unlocked All Humanoid Bestiary entries."), + Title(35, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_LYCANTHROPE), "Blood Moon Hunter", "Blood Moon Huntress", "Unlocked All Lycanthrope Bestiary entries."), + Title(36, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_AMPHIBIC), "Coldblooded", "", "Unlocked All Amphibic Bestiary entries."), + Title(37, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_BIRD), "Death from Below", "", "Unlocked all Bird Bestiary entries."), + Title(38, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_DEMON), "Demonator", "", "Unlocked all Demon Bestiary entries."), + Title(39, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_DRAGON), "Dragonslayer", "", "Unlocked all Dragon Bestiary entries."), + Title(40, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_ELEMENTAL), "Elementalist", "", "Unlocked all Elemental Bestiary entries."), + Title(41, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_VERMIN), "Exterminator", "", "Unlocked all Vermin Bestiary entries."), + Title(42, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_FEY), "Fey Swatter", "", "Unlocked all Fey Bestiary entries."), + Title(43, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_UNDEAD), "Ghosthunter", "Ghosthuntress", "Unlocked all Undead Bestiary entries."), + Title(44, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_CONSTRUCT), "Handyman", "Handywoman", "Unlocked all Construct Bestiary entries."), + Title(45, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_MAMMAL), "Huntsman", "Huntress", "Unlocked all Mammal Bestiary entries."), + Title(46, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_EXTRA_DIMENSIONAL), "Interdimensional Destroyer", "", "Unlocked all Extra Dimensional Bestiary entries."), + Title(47, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_HUMAN), "Manhunter", "Manhuntress", "Unlocked all Human Bestiary entries."), + Title(48, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_MAGICAL), "Master of Illusion", "Mistress of Illusion", "Unlocked all Magical Bestiary entries."), + Title(49, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_SLIME), "Ooze Blues", "", "Unlocked all Slime Bestiary entries."), + Title(50, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_AQUATIC), "Sea Bane", "", "Unlocked all Aquatic Bestiary entries."), + Title(51, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_REPTILE), "Snake Charmer", "", "Unlocked all Reptile Bestiary entries."), + Title(52, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_GIANT), "Tumbler", "", "Unlocked all Giant Bestiary entries."), + Title(53, CyclopediaTitle_t::BESTIARY, static_cast<uint16_t>(BestiaryType_t::BESTY_RACE_PLANT), "Weedkiller", "", "Unlocked all Plant Bestiary entries."), + Title(54, CyclopediaTitle_t::BESTIARY, 0, "Executioner", "", "Unlocked all Bestiary entries."), + + Title(55, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Annihilator", "", "Unlocked all Nemesis bosses.", 0, false), + Title(56, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Destroyer", "", "Unlocked 10 or more Archfoe bosses.", 10, true), + Title(57, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Devastator", "", "Unlocked 10 or more Nemesis bosses.", 10, true), + Title(58, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Eraser", "", "Unlocked all Archfoe bosses.", 0, false), + Title(59, CyclopediaTitle_t::BOSSTIARY, 0, "Boss Executioner", "", "Unlocked all bosses.", 0, false), + Title(60, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_BANE), "Boss Hunter", "", "Unlocked 10 or more Bane bosses.", 10, true), + Title(61, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Obliterator", "", "Unlocked 40 or more Nemesis bosses.", 40, true), + Title(62, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_BANE), "Boss Slayer", "", "Unlocked all Bane bosses.", 0, false), + Title(63, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Smiter", "", "Unlocked 40 or more Archfoe bosses.", 40, true), + Title(64, CyclopediaTitle_t::BOSSTIARY, static_cast<uint16_t>(BosstiaryRarity_t::RARITY_BANE), "Boss Veteran", "", "Unlocked 40 or more Bane bosses.", 40, true), + + Title(65, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 1)", "Reward Streak of at least 7 days of consecutive logins.", 7, true), + Title(66, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 2)", "Reward Streak of at least 30 days of consecutive logins.", 30, true), + Title(67, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 3)", "Reward Streak of at least 90 days of consecutive logins.", 90, true), + Title(68, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 4)", "Reward Streak of at least 180 days of consecutive logins.", 180, true), + Title(69, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 5)", "Reward Streak of at least 365 days of consecutive logins.", 365, true), + + Title(70, CyclopediaTitle_t::TASK, "Aspiring Huntsman", "Invested 160,000 tasks points.", 160000, true, "Aspiring Huntswoman"), + Title(71, CyclopediaTitle_t::TASK, "Competent Beastslayer", "Invested 320,000 tasks points.", 320000, true), + Title(72, CyclopediaTitle_t::TASK, "Feared Bountyhunter", "Invested 430,000 tasks points.", 430000, true), + + Title(73, CyclopediaTitle_t::MAP, "Dedicated Entrepreneur", "Explored 50% of all the map areas.", 50, false), + Title(74, CyclopediaTitle_t::MAP, "Globetrotter", "Explored all map areas.", 100, false), + + Title(75, CyclopediaTitle_t::OTHERS, "Guild Leader", "Leading a Guild.", false), + Title(76, CyclopediaTitle_t::OTHERS, "Proconsul of Iksupan", "Only a true devotee to the cause of the ancient Iks and their lost legacy may step up to the rank of proconsul.", true), + Title(77, CyclopediaTitle_t::OTHERS, "Admirer of the Crown", "Adjust your crown and handle it.", true), + Title(78, CyclopediaTitle_t::OTHERS, "Big Spender", "Unlocked the full Golden Outfit.", true), + Title(79, CyclopediaTitle_t::OTHERS, "Challenger of the Iks", "Challenged Ahau, guardian of Iksupan, in traditional Iks warrior attire.", true), + Title(80, CyclopediaTitle_t::OTHERS, "Royal Bounacean Advisor", "Called to the court of Bounac by Kesar the Younger himself.", true), + Title(81, CyclopediaTitle_t::OTHERS, "Aeternal", "Awarded exclusively to stalwart heroes keeping the faith under all circumstances.", true), + Title(82, CyclopediaTitle_t::OTHERS, "Robinson Crusoe", "Some discoveries are reserved to only the most experienced adventurers. Until the next frontier opens on the horizon.", true), + Title(83, CyclopediaTitle_t::OTHERS, "Chompmeister", "Awarded only to true connoisseurs undertaking even the most exotic culinary escapades.", true), + Title(84, CyclopediaTitle_t::OTHERS, "Bringer of Rain", "Forging through battle after battle like a true gladiator.", true), + Title(85, CyclopediaTitle_t::OTHERS, "Beastly", "Reached 2000 charm points. Quite beastly!", true), + Title(86, CyclopediaTitle_t::OTHERS, "Midnight Hunter", "When the hunter becomes the hunted, perseverance decides the game.", true), + Title(87, CyclopediaTitle_t::OTHERS, "Ratinator", "Killing some snarky cave rats is helpful, killing over ten thousand of them is a statement.", true), + Title(88, CyclopediaTitle_t::OTHERS, "Doomsday Nemesis", "Awarded for great help in the battle against Gaz'haragoth.", true), + Title(89, CyclopediaTitle_t::OTHERS, "Hero of Bounac", "You prevailed during the battle of Bounac and broke the siege that held Bounac's people in its firm grasp.", true), // Derrotar o boss Drume. + Title(90, CyclopediaTitle_t::OTHERS, "King of Demon", "Defeat Morshabaal 5 times.", 0, true, "Queen of Demon"), + Title(91, CyclopediaTitle_t::OTHERS, "Planegazer", "Followed the trail of the Planestrider to the end.", true), // Derrotar o boss Planestrider + Title(92, CyclopediaTitle_t::OTHERS, "Time Traveller", "Anywhere in time or space.", true), // Derrotar o boss Lord Retro + Title(93, CyclopediaTitle_t::OTHERS, "Truly Boss", "Reach 15,000 boss points.", true), + }; + m_highscoreCategoriesNames = { { static_cast<uint8_t>(HighscoreCategories_t::ACHIEVEMENTS), "Achievement Points" }, { static_cast<uint8_t>(HighscoreCategories_t::AXE_FIGHTING), "Axe Fighting" }, + { static_cast<uint8_t>(HighscoreCategories_t::BOSS_POINTS), "Boss Points" }, { static_cast<uint8_t>(HighscoreCategories_t::CHARMS), "Charm Points" }, { static_cast<uint8_t>(HighscoreCategories_t::CLUB_FIGHTING), "Club Fighting" }, - { static_cast<uint8_t>(HighscoreCategories_t::EXPERIENCE), "Experience Points" }, { static_cast<uint8_t>(HighscoreCategories_t::DISTANCE_FIGHTING), "Distance Fighting" }, { static_cast<uint8_t>(HighscoreCategories_t::DROME), "Drome Score" }, + { static_cast<uint8_t>(HighscoreCategories_t::EXPERIENCE), "Experience Points" }, { static_cast<uint8_t>(HighscoreCategories_t::FISHING), "Fishing" }, { static_cast<uint8_t>(HighscoreCategories_t::FIST_FIGHTING), "Fist Fighting" }, { static_cast<uint8_t>(HighscoreCategories_t::GOSHNAR), "Goshnar's Taint" }, @@ -8163,10 +8271,9 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) { void Game::playerFriendSystemAction(std::shared_ptr<Player> player, uint8_t type, uint8_t titleId) { uint32_t playerGUID = player->getGUID(); if (type == 0x0E) { - // todo in titles system PR - // player->title()->setCurrentTitle(titleId); - // player->sendCyclopediaCharacterBaseInformation(); - // player->sendCyclopediaCharacterTitles(); + player->title()->setCurrentTitle(titleId); + player->sendCyclopediaCharacterBaseInformation(); + player->sendCyclopediaCharacterTitles(); return; } } @@ -8429,14 +8536,10 @@ void Game::processHighscoreResults(DBResult_ptr result, uint32_t playerID, uint8 characters.reserve(result->countResults()); if (result) { do { - uint8_t characterVocation; const auto &voc = g_vocations().getVocation(result->getNumber<uint16_t>("vocation")); - if (voc) { - characterVocation = voc->getClientId(); - } else { - characterVocation = 0; - } - characters.emplace_back(std::move(result->getString("name")), result->getNumber<uint64_t>("points"), result->getNumber<uint32_t>("id"), result->getNumber<uint32_t>("rank"), result->getNumber<uint16_t>("level"), characterVocation); + uint8_t characterVocation = voc ? voc->getClientId() : 0; + std::string loyaltyTitle = ""; // todo get loyalty title from player + characters.emplace_back(std::move(result->getString("name")), result->getNumber<uint64_t>("points"), result->getNumber<uint32_t>("id"), result->getNumber<uint32_t>("rank"), result->getNumber<uint16_t>("level"), characterVocation, loyaltyTitle); } while (result->next()); } @@ -8491,39 +8594,7 @@ void Game::playerHighscores(std::shared_ptr<Player> player, HighscoreType_t type return; } - std::string categoryName; - const auto &categoryType = static_cast<HighscoreCategories_t>(category); - switch (categoryType) { - case HighscoreCategories_t::FIST_FIGHTING: - categoryName = "skill_fist"; - break; - case HighscoreCategories_t::CLUB_FIGHTING: - categoryName = "skill_club"; - break; - case HighscoreCategories_t::SWORD_FIGHTING: - categoryName = "skill_sword"; - break; - case HighscoreCategories_t::AXE_FIGHTING: - categoryName = "skill_axe"; - break; - case HighscoreCategories_t::DISTANCE_FIGHTING: - categoryName = "skill_dist"; - break; - case HighscoreCategories_t::SHIELDING: - categoryName = "skill_shielding"; - break; - case HighscoreCategories_t::FISHING: - categoryName = "skill_fishing"; - break; - case HighscoreCategories_t::MAGIC_LEVEL: - categoryName = "maglevel"; - break; - default: { - category = static_cast<uint8_t>(HighscoreCategories_t::EXPERIENCE); - categoryName = "experience"; - break; - } - } + std::string categoryName = getSkillNameById(category); std::string query; if (type == HIGHSCORE_GETENTRIES) { @@ -8541,6 +8612,32 @@ void Game::playerHighscores(std::shared_ptr<Player> player, HighscoreType_t type player->addAsyncOngoingTask(PlayerAsyncTask_Highscore); } +std::string Game::getSkillNameById(uint8_t &skill) { + switch (static_cast<HighscoreCategories_t>(skill)) { + case HighscoreCategories_t::FIST_FIGHTING: + return "skill_fist"; + case HighscoreCategories_t::CLUB_FIGHTING: + return "skill_club"; + case HighscoreCategories_t::SWORD_FIGHTING: + return "skill_sword"; + case HighscoreCategories_t::AXE_FIGHTING: + return "skill_axe"; + case HighscoreCategories_t::DISTANCE_FIGHTING: + return "skill_dist"; + case HighscoreCategories_t::SHIELDING: + return "skill_shielding"; + case HighscoreCategories_t::FISHING: + return "skill_fishing"; + case HighscoreCategories_t::MAGIC_LEVEL: + return "maglevel"; + case HighscoreCategories_t::BOSS_POINTS: + return "boss_points"; + default: + skill = static_cast<uint8_t>(HighscoreCategories_t::EXPERIENCE); + return "experience"; + } +} + void Game::playerReportRuleViolationReport(uint32_t playerId, const std::string &targetName, uint8_t reportType, uint8_t reportReason, const std::string &comment, const std::string &translation) { std::shared_ptr<Player> player = getPlayerByID(playerId); if (!player) { @@ -10572,7 +10669,7 @@ std::map<uint16_t, Achievement> Game::getAchievements() { void Game::logCyclopediaStats() { g_logger().info("Loaded {} badges from Badge System", m_badges.size()); - // todo in title system: g_logger().info("Loaded {} titles from Title system", m_titles.size()); + g_logger().info("Loaded {} titles from Title system", m_titles.size()); } std::unordered_set<Badge> Game::getBadges() { @@ -10604,3 +10701,33 @@ Badge Game::getBadgeByName(const std::string &name) { } return {}; } + +std::unordered_set<Title> Game::getTitles() { + return m_titles; +} + +Title Game::getTitleById(uint8_t id) { + if (id == 0) { + return {}; + } + auto it = std::find_if(m_titles.begin(), m_titles.end(), [id](const Title &t) { + return t.m_id == id; + }); + if (it != m_titles.end()) { + return *it; + } + return {}; +} + +Title Game::getTitleByName(const std::string &name) { + if (name.empty()) { + return {}; + } + auto it = std::find_if(m_titles.begin(), m_titles.end(), [name](const Title &t) { + return t.m_maleName == name; + }); + if (it != m_titles.end()) { + return *it; + } + return {}; +} diff --git a/src/game/game.hpp b/src/game/game.hpp index 9d743895c4c..554c418a2c8 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -51,6 +51,7 @@ class Spectators; struct Achievement; struct HighscoreCategory; struct Badge; +struct Title; static constexpr uint16_t SERVER_BEAT = 0x32; static constexpr int32_t EVENT_MS = 10000; @@ -313,6 +314,7 @@ class Game { void playerCyclopediaCharacterInfo(std::shared_ptr<Player> player, uint32_t characterID, CyclopediaCharacterInfoType_t characterInfoType, uint16_t entriesPerPage, uint16_t page); void playerHighscores(std::shared_ptr<Player> player, HighscoreType_t type, uint8_t category, uint32_t vocation, const std::string &worldName, uint16_t page, uint8_t entriesPerPage); + static std::string getSkillNameById(uint8_t &skill); void updatePlayerSaleItems(uint32_t playerId); @@ -726,11 +728,16 @@ class Game { Badge getBadgeById(uint8_t id); Badge getBadgeByName(const std::string &name); + std::unordered_set<Title> getTitles(); + Title getTitleById(uint8_t id); + Title getTitleByName(const std::string &name); + private: std::map<uint16_t, Achievement> m_achievements; std::map<std::string, uint16_t> m_achievementsNameToId; std::unordered_set<Badge> m_badges; + std::unordered_set<Title> m_titles; std::vector<HighscoreCategory> m_highscoreCategories; std::unordered_map<uint8_t, std::string> m_highscoreCategoriesNames; diff --git a/src/game/game_definitions.hpp b/src/game/game_definitions.hpp index 2c2b40fac78..a6ce6e7eaa8 100644 --- a/src/game/game_definitions.hpp +++ b/src/game/game_definitions.hpp @@ -61,15 +61,6 @@ enum LightState_t { LIGHT_STATE_SUNRISE, }; -enum CyclopediaBadge_t : uint8_t { - ACCOUNT_AGE = 1, - LOYALTY, - ACCOUNT_ALL_LEVEL, - ACCOUNT_ALL_VOCATIONS, - TOURNAMENT_PARTICIPATION, - TOURNAMENT_POINTS, -}; - enum CyclopediaCharacterInfoType_t : uint8_t { CYCLOPEDIA_CHARACTERINFO_BASEINFORMATION = 0, CYCLOPEDIA_CHARACTERINFO_GENERALSTATS = 1, @@ -108,6 +99,7 @@ enum class HighscoreCategories_t : uint8_t { CHARMS = 11, DROME = 12, GOSHNAR = 13, + BOSS_POINTS = 14, }; enum HighscoreType_t : uint8_t { diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 2ac2cc863ea..0498b40e29f 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -890,6 +890,7 @@ void IOLoginDataLoad::loadPlayerInitializeSystem(std::shared_ptr<Player> player) player->achiev()->loadUnlockedAchievements(); player->badge()->checkAndUpdateNewBadges(); + player->title()->checkAndUpdateNewTitles(); player->initializePrey(); player->initializeTaskHunting(); diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 5f705cf426d..a91889e43bc 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -28,6 +28,7 @@ #include "lua/callbacks/events_callbacks.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "map/spectators.hpp" // Game diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 81b70263a0a..51dd79638ab 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -16,6 +16,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" #include "io/ioprey.hpp" @@ -4293,3 +4294,57 @@ int PlayerFunctions::luaPlayerAddBadge(lua_State* L) { pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerAddTitle(lua_State* L) { + // player:addTitle(id) + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + player->title()->manage(true, getNumber<uint8_t>(L, 2, 0)); + pushBoolean(L, true); + return 1; +} + +int PlayerFunctions::luaPlayerGetTitles(lua_State* L) { + // player:getTitles() + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + auto playerTitles = player->title()->getUnlockedTitles(); + lua_createtable(L, static_cast<int>(playerTitles.size()), 0); + + int index = 0; + for (const auto &title : playerTitles) { + lua_createtable(L, 0, 3); + setField(L, "id", title.first.m_id); + setField(L, "name", player->title()->getNameBySex(player->getSex(), title.first.m_maleName, title.first.m_femaleName)); + setField(L, "description", title.first.m_description); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int PlayerFunctions::luaPlayerSetCurrentTitle(lua_State* L) { + // player:setCurrentTitle(id) + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const auto &title = g_game().getTitleById(getNumber<uint8_t>(L, 2, 0)); + if (title.m_id == 0) { + reportErrorFunc(getErrorDesc(LUA_ERROR_VARIANT_NOT_FOUND)); + return 1; + } + + player->title()->setCurrentTitle(title.m_id); + pushBoolean(L, true); + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index a30f9f755ca..4d89a86d27f 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -367,6 +367,11 @@ class PlayerFunctions final : LuaScriptInterface { // Badge Functions registerMethod(L, "Player", "addBadge", PlayerFunctions::luaPlayerAddBadge); + // Title Functions + registerMethod(L, "Player", "addTitle", PlayerFunctions::luaPlayerAddTitle); + registerMethod(L, "Player", "getTitles", PlayerFunctions::luaPlayerGetTitles); + registerMethod(L, "Player", "setCurrentTitle", PlayerFunctions::luaPlayerSetCurrentTitle); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -723,5 +728,9 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerAddBadge(lua_State* L); + static int luaPlayerAddTitle(lua_State* L); + static int luaPlayerGetTitles(lua_State* L); + static int luaPlayerSetCurrentTitle(lua_State* L); + friend class CreatureFunctions; }; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 73b639885e6..d924e0bfb0e 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -28,6 +28,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/players/grouping/familiars.hpp" #include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/dispatcher.hpp" @@ -2058,9 +2059,8 @@ void ProtocolGame::sendItemInspection(uint16_t itemId, uint8_t itemCount, std::s void ProtocolGame::parseFriendSystemAction(NetworkMessage &msg) { uint8_t state = msg.getByte(); if (state == 0x0E) { - // todo title system pr - // uint8_t titleId = msg.getByte(); - // g_game().playerFriendSystemAction(player, state, titleId); + uint8_t titleId = msg.getByte(); + g_game().playerFriendSystemAction(player, state, titleId); } } @@ -2141,8 +2141,9 @@ void ProtocolGame::sendHighscores(const std::vector<HighscoreCharacter> &charact msg.addByte(0x00); // All data available msg.addByte(1); // Worlds - msg.addString(g_configManager().getString(SERVER_NAME, __FUNCTION__), "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // First World - msg.addString(g_configManager().getString(SERVER_NAME, __FUNCTION__), "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // Selected World + auto serverName = g_configManager().getString(SERVER_NAME, __FUNCTION__); + msg.addString(serverName, "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // First World + msg.addString(serverName, "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // Selected World msg.addByte(0); // Game World Category: 0xFF(-1) - Selected World msg.addByte(0); // BattlEye World Type @@ -2190,9 +2191,9 @@ void ProtocolGame::sendHighscores(const std::vector<HighscoreCharacter> &charact for (const HighscoreCharacter &character : characters) { msg.add<uint32_t>(character.rank); // Rank msg.addString(character.name, "ProtocolGame::sendHighscores - character.name"); // Character Name - msg.addString("", "ProtocolGame::sendHighscores - empty"); // Probably Character Title(not visible in window) + msg.addString(character.loyaltyTitle, "ProtocolGame::sendHighscores - character.loyaltyTitle"); // Character Loyalty Title msg.addByte(character.vocation); // Vocation Id - msg.addString(g_configManager().getString(SERVER_NAME, __FUNCTION__), "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // World + msg.addString(serverName, "ProtocolGame::sendHighscores - g_configManager().getString(SERVER_NAME)"); // World msg.add<uint16_t>(character.level); // Level msg.addByte((player->getGUID() == character.id)); // Player Indicator Boolean msg.add<uint64_t>(character.points); // Points @@ -3380,9 +3381,8 @@ void ProtocolGame::sendCyclopediaCharacterBaseInformation() { msg.add<uint16_t>(player->getLevel()); AddOutfit(msg, player->getDefaultOutfit(), false); - msg.addByte(0x00); // Store summary & Character titles - msg.addString("", "ProtocolGame::sendCyclopediaCharacterBaseInformation - empty"); // character title - // msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterBaseInformation - player->title()->getCurrentTitleName()"); // character title + msg.addByte(0x01); // Store summary & Character titles + msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterBaseInformation - player->title()->getCurrentTitleName()"); // character title writeToOutputBuffer(msg); } @@ -4003,6 +4003,13 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { msg.addString("Vocation", "ProtocolGame::sendCyclopediaCharacterInspection - Vocation"); msg.addString(player->getVocation()->getVocName(), "ProtocolGame::sendCyclopediaCharacterInspection - player->getVocation()->getVocName()"); + // Player title + if (player->title()->getCurrentTitle() != 0) { + playerDescriptionSize++; + msg.addString("Title", "ProtocolGame::sendCyclopediaCharacterInspection - Title"); + msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterInspection - player->title()->getCurrentTitleName()"); + } + // Loyalty title if (!player->getLoyaltyTitle().empty()) { playerDescriptionSize++; @@ -4044,7 +4051,6 @@ void ProtocolGame::sendCyclopediaCharacterBadges() { msg.addByte(player->isPremium() ? 0x01 : 0x00); // IsPremium (GOD has always 'Premium') // Character loyalty title msg.addString(player->getLoyaltyTitle(), "ProtocolGame::sendCyclopediaCharacterBadges - player->getLoyaltyTitle()"); - // msg.addByte(0x01); // Enable badges uint8_t badgesSize = 0; auto badgesSizePosition = msg.getBufferPosition(); @@ -4068,12 +4074,26 @@ void ProtocolGame::sendCyclopediaCharacterTitles() { return; } + auto titles = g_game().getTitles(); + NetworkMessage msg; msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_TITLES); msg.addByte(0x00); // 0x00 Here means 'no error' - msg.addByte(0x00); - msg.addByte(0x00); + msg.addByte(player->title()->getCurrentTitle()); + msg.addByte(static_cast<uint8_t>(titles.size())); + + std::string messageTitleName = "ProtocolGame::sendCyclopediaCharacterTitles - title.name"; + std::string messageTitleDesc = "ProtocolGame::sendCyclopediaCharacterTitles - title.description"; + for (const auto &title : titles) { + msg.addByte(title.m_id); + auto titleName = player->title()->getNameBySex(player->getSex(), title.m_maleName, title.m_femaleName); + msg.addString(titleName, messageTitleName); + msg.addString(title.m_description, messageTitleDesc); + msg.addByte(title.m_permanent ? 0x01 : 0x00); + auto isUnlocked = player->title()->isTitleUnlocked(title.m_id); + msg.addByte(isUnlocked ? 0x01 : 0x00); + } writeToOutputBuffer(msg); } diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 19e62c428e9..16105ee0b8b 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -14,6 +14,7 @@ #include "creatures/creature.hpp" #include "enums/forge_conversion.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" class NetworkMessage; class Player; @@ -31,6 +32,7 @@ class TaskHuntingOption; struct ModalWindow; struct Achievement; struct Badge; +struct Title; using ProtocolGame_ptr = std::shared_ptr<ProtocolGame>; diff --git a/src/server/server_definitions.hpp b/src/server/server_definitions.hpp index ad816b76b02..1d5ed5b628e 100644 --- a/src/server/server_definitions.hpp +++ b/src/server/server_definitions.hpp @@ -113,13 +113,14 @@ enum Supply_Stash_Actions_t : uint8_t { }; struct HighscoreCharacter { - HighscoreCharacter(std::string name, uint64_t points, uint32_t id, uint32_t rank, uint16_t level, uint8_t vocation) : + HighscoreCharacter(std::string name, uint64_t points, uint32_t id, uint32_t rank, uint16_t level, uint8_t vocation, std::string loyaltyTitle) : name(std::move(name)), points(points), id(id), rank(rank), level(level), - vocation(vocation) { } + vocation(vocation), + loyaltyTitle(std::move(loyaltyTitle)) { } std::string name; uint64_t points; @@ -127,4 +128,5 @@ struct HighscoreCharacter { uint32_t rank; uint16_t level; uint8_t vocation; + std::string loyaltyTitle; }; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 5d40d930646..1c21dc87210 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -46,6 +46,7 @@ <ClInclude Include="..\src\creatures\players\wheel\wheel_gems.hpp" /> <ClInclude Include="..\src\creatures\players\achievement\player_achievement.hpp" /> <ClInclude Include="..\src\creatures\players\cyclopedia\player_badge.hpp" /> + <ClInclude Include="..\src\creatures\players\cyclopedia\player_title.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\player_wheel.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\wheel_definitions.hpp" /> <ClInclude Include="..\src\database\database.hpp" /> @@ -259,6 +260,7 @@ <ClCompile Include="..\src\creatures\players\wheel\wheel_gems.cpp" /> <ClCompile Include="..\src\creatures\players\achievement\player_achievement.cpp" /> <ClCompile Include="..\src\creatures\players\cyclopedia\player_badge.cpp" /> + <ClCompile Include="..\src\creatures\players\cyclopedia\player_title.cpp" /> <ClCompile Include="..\src\creatures\players\wheel\player_wheel.cpp" /> <ClCompile Include="..\src\database\database.cpp" /> <ClCompile Include="..\src\database\databasemanager.cpp" /> @@ -598,3 +600,4 @@ <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> + From fa2bf6fca4d14a210bca673ded262cb390a53338 Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sun, 12 May 2024 01:57:53 -0300 Subject: [PATCH 33/95] fix: stats after transcendence effect (#2599) Resolves #2595 Co-authored-by: Eduardo Dantas <eduardo.dantas@hotmail.com.br> --- src/creatures/players/player.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 92249e1114f..925086f2b53 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6735,6 +6735,10 @@ void Player::clearCooldowns() { } void Player::triggerTranscendance() { + if (wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) > OTSYS_TIME()) { + return; + } + auto item = getInventoryItem(CONST_SLOT_LEGS); if (item == nullptr) { return; @@ -6751,10 +6755,28 @@ void Player::triggerTranscendance() { addCondition(outfitCondition); wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); - sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); + sendSkills(); sendStats(); sendBasicData(); + + sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); + + // Send player data after transcendance timer expire + const auto &task = createPlayerTask( + std::max<uint32_t>(SCHEDULER_MINTICKS, duration), + [playerId = getID()] { + auto player = g_game().getPlayerByID(playerId); + if (player) { + player->sendSkills(); + player->sendStats(); + player->sendBasicData(); + } + }, + "Player::triggerTranscendance" + ); + g_dispatcher().scheduleEvent(task); + wheel()->sendGiftOfLifeCooldown(); g_game().reloadCreature(getPlayer()); } From 5aeb64c444fed8c6bbf2fdff15ba5c42d1df921e Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sun, 12 May 2024 02:12:13 -0300 Subject: [PATCH 34/95] fix: possible crash in creatureevents (#2622) Changes Proposed - Fixed broken iteration. Issues addressed: - Crash because removing element from map and iterating. Co-authored-by: Eduardo Dantas <eduardo.dantas@hotmail.com.br> --- src/lua/creature/creatureevent.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index e3ac954908f..fe2a782b914 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -105,11 +105,9 @@ CreatureEvent::CreatureEvent(LuaScriptInterface* interface) : Script(interface) { } void CreatureEvents::removeInvalidEvents() { - for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) { - if (it->second->getScriptId() == 0) { - creatureEvents.erase(it->second->getName()); - } - } + std::erase_if(creatureEvents, [](const auto &pair) { + return pair.second->getScriptId() == 0; + }); } std::string CreatureEvent::getScriptTypeName() const { From a9360fae6d4747d491ed79543315972e1d1fe124 Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Sun, 12 May 2024 08:29:13 -0300 Subject: [PATCH 35/95] fix: avatar cooldown reduction (#2607) # Description This PR fixes the avatar cooldown reduction spells to be done in the source and remove from scripts, this is being done to work with #2602. ## Behaviour ### **Actual** The avatar cooldown reduction is via script. ### **Expected** The avatar cooldown reduction is via source. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - [x] Cast Avatar spell of each vocation with stage 1 in wheel, the cooldown should be 120 minutes (two hours) - [x] Cast Avatar spell of each vocation with stage 2 in wheel, the cooldown should be 90 minutes (one and a half hour) - [x] Cast Avatar spell of each vocation with stage 3 in wheel, the cooldown should be 60 minutes (one hour) **Test Configuration**: - Server Version: Latest - Client: 13.32 - Operating System: Windows 11 ## Checklist - [X] My code follows the style guidelines of this project - [X] I have performed a self-review of my own code - [X] I checked the PR checks reports - [X] I have commented my code, particularly in hard-to-understand areas - [X] I have made corresponding changes to the documentation - [X] My changes generate no new warnings - [X] I have added tests that prove my fix is effective or that my feature works --------- Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com> --- data/scripts/spells/support/avatar_of_light.lua | 15 ++------------- data/scripts/spells/support/avatar_of_nature.lua | 15 ++------------- data/scripts/spells/support/avatar_of_steel.lua | 15 ++------------- data/scripts/spells/support/avatar_of_storm.lua | 15 ++------------- src/creatures/players/wheel/player_wheel.cpp | 16 ++++++++-------- 5 files changed, 16 insertions(+), 60 deletions(-) diff --git a/data/scripts/spells/support/avatar_of_light.lua b/data/scripts/spells/support/avatar_of_light.lua index edc25c4f66e..a84fd3b8c8e 100644 --- a/data/scripts/spells/support/avatar_of_light.lua +++ b/data/scripts/spells/support/avatar_of_light.lua @@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 60 - elseif grade >= 2 then - cooldown = 90 - elseif grade >= 1 then - cooldown = 120 - end local duration = 15000 condition:setTicks(duration) - local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 265) - conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) - creature:addCondition(conditionCooldown) + creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) creature:addCondition(condition) creature:avatarTimer((os.time() * 1000) + duration) creature:reloadData() @@ -43,7 +32,7 @@ spell:words("uteta res sac") spell:level(300) spell:mana(1500) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours spell:groupCooldown(2 * 1000) spell:vocation("paladin;true", "royal paladin;true") spell:hasParams(true) diff --git a/data/scripts/spells/support/avatar_of_nature.lua b/data/scripts/spells/support/avatar_of_nature.lua index 0bd1473c3ca..fa32dd6b821 100644 --- a/data/scripts/spells/support/avatar_of_nature.lua +++ b/data/scripts/spells/support/avatar_of_nature.lua @@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 60 - elseif grade >= 2 then - cooldown = 90 - elseif grade >= 1 then - cooldown = 120 - end local duration = 15000 condition:setTicks(duration) - local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 267) - conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) - creature:addCondition(conditionCooldown) + creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) creature:addCondition(condition) creature:avatarTimer((os.time() * 1000) + duration) creature:reloadData() @@ -43,7 +32,7 @@ spell:words("uteta res dru") spell:level(300) spell:mana(2200) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours spell:groupCooldown(2 * 1000) spell:vocation("druid;true", "elder druid;true") spell:hasParams(true) diff --git a/data/scripts/spells/support/avatar_of_steel.lua b/data/scripts/spells/support/avatar_of_steel.lua index 8380a524c6d..da71ff14f96 100644 --- a/data/scripts/spells/support/avatar_of_steel.lua +++ b/data/scripts/spells/support/avatar_of_steel.lua @@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 60 - elseif grade >= 2 then - cooldown = 90 - elseif grade >= 1 then - cooldown = 120 - end local duration = 15000 condition:setTicks(duration) - local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 264) - conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) - creature:addCondition(conditionCooldown) + creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) creature:addCondition(condition) creature:avatarTimer((os.time() * 1000) + duration) creature:reloadData() @@ -43,7 +32,7 @@ spell:words("uteta res eq") spell:level(300) spell:mana(800) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours spell:groupCooldown(2 * 1000) spell:vocation("knight;true", "elite knight;true") spell:hasParams(true) diff --git a/data/scripts/spells/support/avatar_of_storm.lua b/data/scripts/spells/support/avatar_of_storm.lua index 79bb4e3b37e..ad644de01ba 100644 --- a/data/scripts/spells/support/avatar_of_storm.lua +++ b/data/scripts/spells/support/avatar_of_storm.lua @@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 60 - elseif grade >= 2 then - cooldown = 90 - elseif grade >= 1 then - cooldown = 120 - end local duration = 15000 condition:setTicks(duration) - local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 266) - conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) - creature:addCondition(conditionCooldown) + creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR) creature:addCondition(condition) creature:avatarTimer((os.time() * 1000) + duration) creature:reloadData() @@ -43,7 +32,7 @@ spell:words("uteta res ven") spell:level(300) spell:mana(2200) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours spell:groupCooldown(2 * 1000) spell:vocation("sorcerer;true", "master sorcerer;true") spell:hasParams(true) diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 5c63e9aecc2..8101f51ae42 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -1537,12 +1537,12 @@ void PlayerWheel::registerPlayerBonusData() { } if (m_playerBonusData.avatar.light >= 2) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 3 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Light", bonus); } if (m_playerBonusData.avatar.light >= 3) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 2 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Light", bonus); } } else { @@ -1555,12 +1555,12 @@ void PlayerWheel::registerPlayerBonusData() { } if (m_playerBonusData.avatar.nature >= 2) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 3 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Nature", bonus); } if (m_playerBonusData.avatar.nature >= 3) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 2 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Nature", bonus); } } else { @@ -1573,12 +1573,12 @@ void PlayerWheel::registerPlayerBonusData() { } if (m_playerBonusData.avatar.steel >= 2) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 3 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Steel", bonus); } if (m_playerBonusData.avatar.steel >= 3) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 2 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Steel", bonus); } } else { @@ -1591,12 +1591,12 @@ void PlayerWheel::registerPlayerBonusData() { } if (m_playerBonusData.avatar.storm >= 2) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 3 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Storm", bonus); } if (m_playerBonusData.avatar.storm >= 3) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 2 * 60 * 1000; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Storm", bonus); } } else { From bf5f8e9dc8fd7704ba95beb4fa67767ea27535e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Sun, 12 May 2024 09:32:23 -0300 Subject: [PATCH 36/95] fix: players can't claim rewards after killing bosses (#2620) --- .../creaturescripts/quests/dark_trails/kill_the_ravager.lua | 2 +- .../creaturescripts/quests/liquid_black/deepling_boss_kill.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua b/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua index 97798dadfeb..f11bfa41efe 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua @@ -7,7 +7,7 @@ local function removeTeleport(position) end local theRavager = CreatureEvent("TheRavagerDeath") -function theRavager.onDeath(player, creature) +function theRavager.onDeath(creature) local position = creature:getPosition() position:sendMagicEffect(CONST_ME_TELEPORT) local item = Game.createItem(1949, 1, { x = 33496, y = 32070, z = 8 }) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua index 6916f3dfa5a..49fb3e17c11 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua @@ -5,12 +5,11 @@ local bosses = { } local deeplingBosses = CreatureEvent("DeeplingBossDeath") -function deeplingBosses.onDeath(player, creature) +function deeplingBosses.onDeath(creature) local bossConfig = bosses[creature:getName():lower()] if not bossConfig then return true end - onDeathForDamagingPlayers(creature, function(creature, player) if player:getStorageValue(Storage.DeeplingBosses.DeeplingStatus) < bossConfig.status then player:setStorageValue(Storage.DeeplingBosses.DeeplingStatus, bossConfig.status) From e0e7ab0d1d2c23eadcaabac907fce2eaecffa933 Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Mon, 13 May 2024 18:35:04 -0300 Subject: [PATCH 37/95] feat: augments (#2602) --- config.lua.dist | 14 ++- data/items/items.xml | 105 +++++++++++++++++++ src/config/config_enums.hpp | 5 +- src/config/configmanager.cpp | 3 + src/creatures/combat/combat.cpp | 4 + src/creatures/combat/spells.cpp | 41 +++++++- src/creatures/combat/spells.hpp | 3 + src/creatures/players/player.cpp | 29 +++++ src/creatures/players/player.hpp | 6 ++ src/enums/item_attribute.hpp | 1 + src/items/functions/item/item_parse.cpp | 53 ++++++++++ src/items/functions/item/item_parse.hpp | 8 ++ src/items/item.cpp | 12 +++ src/items/item.hpp | 29 +++++ src/items/items.cpp | 41 ++++++++ src/items/items.hpp | 22 ++++ src/items/items_definitions.hpp | 21 ++++ src/server/network/protocol/protocolgame.cpp | 7 +- src/utils/tools.cpp | 16 +++ src/utils/tools.hpp | 1 + 20 files changed, 417 insertions(+), 4 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index b731fadbcd7..ed2561d4532 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -52,7 +52,8 @@ cleanProtectionZones = false -- Connection Config -- NOTE: allowOldProtocol can allow login on 10x protocol. (11.00) -- NOTE: maxPlayers set to 0 means no limit --- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25 +-- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25, +-- It's recommended to use a range like min 50 in this function, otherwise you will be disconnected after equipping two-handed distance weapons. ip = "127.0.0.1" allowOldProtocol = false bindOnlyGlobalAddress = false @@ -80,6 +81,17 @@ freeDepotLimit = 2000 premiumDepotLimit = 10000 depotBoxes = 20 +-- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602) +-- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact". +-- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above. +-- NOTE: The values represent percentage. +-- NOTE: augmentIncreasedDamagePercent = value between 1 and 100 (damage percent to increase. ex: 5 = 5%, 50 = 50%) +-- NOTE: augmentPowerfulImpactPercent = value between 1 and 100 (damage percent to increase. ex: 10 = 10%, 100 = 100%) +-- NOTE: augmentStrongImpactPercent = value between 1 and 100 (damage percent to increase. ex: 7 = 7%, 70 = 70%) +augmentIncreasedDamagePercent = 5 +augmentPowerfulImpactPercent = 7 +augmentStrongImpactPercent = 10 + -- Prey system -- NOTE: preyRerollPricePerLevel: Price multiplier in gold coin for rerolling prey list. -- NOTE: preySelectListPrice: Price to manually select creature on list and to lock prey slot. diff --git a/data/items/items.xml b/data/items/items.xml index 0858df99219..45cde62bd9d 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -75009,6 +75009,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost sword" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75032,6 +75035,12 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost sword" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + <attribute key="intense wound cleansing" value="cooldown"> + <attribute key="value" value="120000" /> + </attribute> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75055,6 +75064,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost club" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75078,6 +75090,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost club" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75101,6 +75116,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost axe" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75124,6 +75142,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost axe" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75149,6 +75170,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost sword" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75173,6 +75197,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost sword" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75197,6 +75224,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost club" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75221,6 +75251,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost club" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75245,6 +75278,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost axe" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75269,6 +75305,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost axe" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="fierce berserk" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75285,6 +75324,14 @@ Granted by TibiaGoals.com"/> <attribute key="skillsword" value="4"/> <attribute key="armor" value="12"/> <attribute key="weight" value="3800"/> + <attribute key="augments" value="1"> + <attribute key="avatar of steel" value="cooldown"> + <attribute key="value" value="900000" /> + </attribute> + <attribute key="fierce beserk" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + </attribute> <attribute key="script" value="moveevent"> <attribute key="level" value="500"/> <attribute key="slot" value="legs"/> @@ -75311,6 +75358,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost distance" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="divine caldera" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75338,6 +75388,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost distance" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="divine caldera" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75365,6 +75418,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost distance" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="divine caldera" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75392,6 +75448,9 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost distance" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="divine caldera" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="unproperly" value="true"/> @@ -75407,6 +75466,14 @@ Granted by TibiaGoals.com"/> <attribute key="holymagiclevelpoints" value="1"/> <attribute key="armor" value="11"/> <attribute key="weight" value="3200"/> + <attribute key="augments" value="1"> + <attribute key="avatar of light" value="cooldown"> + <attribute key="value" value="900000" /> + </attribute> + <attribute key="divine caldera" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + </attribute> <attribute key="script" value="moveevent"> <attribute key="level" value="500"/> <attribute key="slot" value="legs"/> @@ -75430,6 +75497,10 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost magic level" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="hell's core" value="strong impact" /> + <attribute key="energy wave" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="mana" value="21"/> @@ -75459,6 +75530,10 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost magic level" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="hell's core" value="powerful impact" /> + <attribute key="energy wave" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="mana" value="21"/> @@ -75483,6 +75558,17 @@ Granted by TibiaGoals.com"/> <attribute key="imbuementslot" value="1"> <attribute key="increase speed" value="10"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="avatar of storm" value="cooldown"> + <attribute key="value" value="900000" /> + </attribute> + <attribute key="hell's core" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + <attribute key="energy wave" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + </attribute> <attribute key="script" value="moveevent"> <attribute key="level" value="500"/> <attribute key="slot" value="feet"/> @@ -75506,6 +75592,10 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost magic level" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="eternal winter" value="strong impact" /> + <attribute key="terra wave" value="strong impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="mana" value="20"/> @@ -75535,6 +75625,10 @@ Granted by TibiaGoals.com"/> <attribute key="critical hit" value="3"/> <attribute key="skillboost magic level" value="3"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="eternal winter" value="powerful impact" /> + <attribute key="terra wave" value="powerful impact" /> + </attribute> <attribute key="script" value="moveevent;weapon"> <attribute key="level" value="600"/> <attribute key="mana" value="20"/> @@ -75559,6 +75653,17 @@ Granted by TibiaGoals.com"/> <attribute key="imbuementslot" value="1"> <attribute key="increase speed" value="10"/> </attribute> + <attribute key="augments" value="1"> + <attribute key="avatar of nature" value="cooldown"> + <attribute key="value" value="900000" /> + </attribute> + <attribute key="eternal winter" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + <attribute key="terra wave" value="critical extra damage"> + <attribute key="value" value="8" /> + </attribute> + </attribute> <attribute key="script" value="moveevent"> <attribute key="level" value="500"/> <attribute key="slot" value="feet"/> diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 4abf29c04cb..30a4b172af1 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -17,6 +17,9 @@ enum ConfigKey_t : uint16_t { ALLOW_BLOCK_SPAWN, ALLOW_CHANGEOUTFIT, ALLOW_RELOAD, + AUGMENT_INCREASED_DAMAGE_PERCENT, + AUGMENT_POWERFUL_IMPACT_PERCENT, + AUGMENT_STRONG_IMPACT_PERCENT, AUTH_TYPE, AUTOBANK, AUTOLOOT, @@ -324,5 +327,5 @@ enum ConfigKey_t : uint16_t { WHEEL_POINTS_PER_LEVEL, WHITE_SKULL_TIME, WORLD_TYPE, - XP_DISPLAY_MODE, + XP_DISPLAY_MODE }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 8d4ba7a77da..0525c5a560a 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -345,6 +345,9 @@ bool ConfigManager::load() { loadIntConfig(L, WHEEL_ATELIER_ROTATE_REGULAR_COST, "wheelAtelierRotateRegularCost", 250000); loadIntConfig(L, WHEEL_POINTS_PER_LEVEL, "wheelPointsPerLevel", 1); loadIntConfig(L, WHITE_SKULL_TIME, "whiteSkullTime", 15 * 60 * 1000); + loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5); + loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10); + loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7); loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data"); loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global"); diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 56cca63fe56..06812095cbf 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -98,7 +98,11 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr<Creature> creature, std::sh } } } + if (attackerPlayer && wheelSpell && wheelSpell->isInstant()) { + wheelSpell->getCombatDataAugment(attackerPlayer, damage); + } } + return damage; } diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 60cc5e7d2a9..3aa445b34aa 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -630,6 +630,43 @@ void Spell::setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t gr } } +void Spell::getCombatDataAugment(std::shared_ptr<Player> player, CombatDamage &damage) { + if (!(damage.instantSpellName).empty()) { + const auto equippedAugmentItems = player->getEquippedAugmentItems(); + for (const auto &item : equippedAugmentItems) { + const auto augments = item->getAugmentsBySpellName(damage.instantSpellName); + for (auto &augment : augments) { + if (augment->value == 0) { + continue; + } + if (augment->type == Augment_t::IncreasedDamage || augment->type == Augment_t::PowerfulImpact || augment->type == Augment_t::StrongImpact) { + const float augmentPercent = augment->value / 100.0; + damage.primary.value += static_cast<int32_t>(damage.primary.value * augmentPercent); + damage.secondary.value += static_cast<int32_t>(damage.secondary.value * augmentPercent); + } else if (augment->type != Augment_t::Cooldown) { + const int32_t augmentValue = augment->value * 100; + damage.lifeLeech += augment->type == Augment_t::LifeLeech ? augmentValue : 0; + damage.manaLeech += augment->type == Augment_t::ManaLeech ? augmentValue : 0; + damage.criticalDamage += augment->type == Augment_t::CriticalExtraDamage ? augmentValue : 0; + } + } + } + } +}; + +int32_t Spell::calculateAugmentSpellCooldownReduction(std::shared_ptr<Player> player) const { + int32_t spellCooldown = 0; + const auto equippedAugmentItems = player->getEquippedAugmentItemsByType(Augment_t::Cooldown); + for (const auto &item : equippedAugmentItems) { + const auto augments = item->getAugmentsBySpellNameAndType(getName(), Augment_t::Cooldown); + for (auto &augment : augments) { + spellCooldown += augment->value; + } + } + + return spellCooldown; +} + void Spell::applyCooldownConditions(std::shared_ptr<Player> player) const { WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName()); bool isUpgraded = getWheelOfDestinyUpgraded() && static_cast<uint8_t>(spellGrade) > 0; @@ -644,8 +681,10 @@ void Spell::applyCooldownConditions(std::shared_ptr<Player> player) const { if (isUpgraded) { spellCooldown -= getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, spellGrade); } - g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN)); + int32_t augmentCooldownReduction = calculateAugmentSpellCooldownReduction(player); + g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}, augment {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN), augmentCooldownReduction); spellCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN); + spellCooldown -= augmentCooldownReduction; if (spellCooldown > 0) { std::shared_ptr<Condition> condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, m_spellId); player->addCondition(condition); diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index cd1a7aaa623..dce7b3caba4 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -345,6 +345,9 @@ class Spell : public BaseSpell { m_separator = newSeparator.data(); } + void getCombatDataAugment(std::shared_ptr<Player> player, CombatDamage &damage); + int32_t calculateAugmentSpellCooldownReduction(std::shared_ptr<Player> player) const; + protected: void applyCooldownConditions(std::shared_ptr<Player> player) const; bool playerSpellCheck(std::shared_ptr<Player> player) const; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 925086f2b53..78478953ade 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -4115,6 +4115,35 @@ std::vector<std::shared_ptr<Item>> Player::getAllInventoryItems(bool ignoreEquip return itemVector; } +std::vector<std::shared_ptr<Item>> Player::getEquippedAugmentItemsByType(Augment_t augmentType) const { + std::vector<std::shared_ptr<Item>> equippedAugmentItemsByType; + const auto equippedAugmentItems = getEquippedItems(); + + for (const auto &item : equippedAugmentItems) { + for (auto &augment : item->getAugments()) { + if (augment->type == augmentType) { + equippedAugmentItemsByType.push_back(item); + } + } + } + + return equippedAugmentItemsByType; +} + +std::vector<std::shared_ptr<Item>> Player::getEquippedAugmentItems() const { + std::vector<std::shared_ptr<Item>> equippedAugmentItems; + const auto equippedItems = getEquippedItems(); + + for (const auto &item : equippedItems) { + if (item->getAugments().size() < 1) { + continue; + } + equippedAugmentItems.push_back(item); + } + + return equippedAugmentItems; +} + std::vector<std::shared_ptr<Item>> Player::getEquippedItems() const { std::vector<Slots_t> valid_slots { CONST_SLOT_HEAD, diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index da4c0cec9e8..b3bdd875b20 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2601,6 +2601,12 @@ class Player final : public Creature, public Cylinder, public Bankable { // This get all blessings phmap::flat_hash_map<Blessings_t, std::string> getBlessingNames() const; + // Gets the equipped items with augment by type + std::vector<std::shared_ptr<Item>> getEquippedAugmentItemsByType(Augment_t augmentType) const; + + // Gets the equipped items with augment + std::vector<std::shared_ptr<Item>> getEquippedAugmentItems() const; + /** * @brief Get the equipped items of the player-> * @details This function returns a vector containing the items currently equipped by the player diff --git a/src/enums/item_attribute.hpp b/src/enums/item_attribute.hpp index adc49c11a19..be7b7445710 100644 --- a/src/enums/item_attribute.hpp +++ b/src/enums/item_attribute.hpp @@ -46,6 +46,7 @@ enum ItemAttribute_t : uint64_t { LOOTMESSAGE_SUFFIX = 33, STORE_INBOX_CATEGORY = 34, OBTAINCONTAINER = 35, + AUGMENTS = 36, }; enum ItemDecayState_t : uint8_t { diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 2d1ba9301c5..1f247b611bc 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -68,6 +68,7 @@ void ItemParse::initParse(const std::string &tmpStrValue, pugi::xml_node attribu ItemParse::parseWalk(tmpStrValue, valueAttribute, itemType); ItemParse::parseAllowDistanceRead(tmpStrValue, valueAttribute, itemType); ItemParse::parseImbuement(tmpStrValue, attributeNode, valueAttribute, itemType); + ItemParse::parseAugment(tmpStrValue, attributeNode, valueAttribute, itemType); ItemParse::parseStackSize(tmpStrValue, valueAttribute, itemType); ItemParse::parseSpecializedMagicLevelPoint(tmpStrValue, valueAttribute, itemType); ItemParse::parseMagicShieldCapacity(tmpStrValue, valueAttribute, itemType); @@ -870,6 +871,58 @@ void ItemParse::parseImbuement(const std::string &tmpStrValue, pugi::xml_node at } } +void ItemParse::parseAugment(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { + if (tmpStrValue != "augments") { + return; + } + + // Check if the augments value is 1 or 0 (1 = enable - 0 = disable) + if (valueAttribute.as_bool()) { + for (const auto subAttributeNode : attributeNode.children()) { + const pugi::xml_attribute subKeyAttribute = subAttributeNode.attribute("key"); + if (!subKeyAttribute) { + continue; + } + + const pugi::xml_attribute subValueAttribute = subAttributeNode.attribute("value"); + if (!subValueAttribute) { + continue; + } + + const auto &augmentEnum = magic_enum::enum_cast<Augment_t>(toPascalCase(subValueAttribute.as_string())); + if (augmentEnum.has_value()) { + const Augment_t augmentType = augmentEnum.value(); + g_logger().trace("[ParseAugment::initParseAugment] - Item '{}' has an augment '{}'", itemType.name, subValueAttribute.as_string()); + int32_t augmentValue = 0; + const bool hasValueDescrition = isAugmentWithoutValueDescription(augmentType); + + if (hasValueDescrition) { + const auto it = AugmentWithoutValueDescriptionDefaultKeys.find(augmentType); + if (it != AugmentWithoutValueDescriptionDefaultKeys.end()) { + augmentValue = g_configManager().getNumber(it->second, __FUNCTION__); + } + } + + const auto augmentName = asLowerCaseString(subKeyAttribute.as_string()); + const pugi::xml_object_range<pugi::xml_node_iterator> augmentValueAttributeNode = subAttributeNode.children(); + if (!augmentValueAttributeNode.empty()) { + const pugi::xml_node augmentValueNode = *augmentValueAttributeNode.begin(); + const pugi::xml_attribute augmentValueAttribute = augmentValueNode.attribute("value"); + augmentValue = augmentValueAttribute ? pugi::cast<int32_t>(augmentValueAttribute.value()) : augmentValue; + } else if (!hasValueDescrition) { + g_logger().warn("[{}] - Item '{}' has an augment '{}' without a value", __FUNCTION__, itemType.name, augmentName); + } + + if (augmentType != Augment_t::None) { + itemType.addAugment(augmentName, augmentType, augmentValue); + } + } else { + g_logger().warn("[{}] - Unknown type '{}'", __FUNCTION__, subValueAttribute.as_string()); + } + } + } +} + void ItemParse::parseStackSize(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { std::string stringValue = tmpStrValue; if (stringValue == "stacksize") { diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index 7859a77fe9c..f63b6bc0689 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -158,6 +158,7 @@ const phmap::flat_hash_map<std::string, ItemParseAttributes_t> ItemParseAttribut { "primarytype", ITEM_PARSE_PRIMARYTYPE }, { "usedbyhouseguests", ITEM_PARSE_USEDBYGUESTS }, { "script", ITEM_PARSE_SCRIPT }, + { "augments", ITEM_PARSE_AUGMENT } }; const phmap::flat_hash_map<std::string, ItemTypes_t> ItemTypesMap = { @@ -247,6 +248,12 @@ const phmap::flat_hash_map<std::string, ImbuementTypes_t> ImbuementsTypeMap = { { "increase capacity", IMBUEMENT_INCREASE_CAPACITY } }; +const phmap::flat_hash_map<Augment_t, ConfigKey_t> AugmentWithoutValueDescriptionDefaultKeys = { + { Augment_t::IncreasedDamage, AUGMENT_INCREASED_DAMAGE_PERCENT }, + { Augment_t::PowerfulImpact, AUGMENT_POWERFUL_IMPACT_PERCENT }, + { Augment_t::StrongImpact, AUGMENT_STRONG_IMPACT_PERCENT }, +}; + class ItemParse : public Items { public: static void initParse(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); @@ -304,6 +311,7 @@ class ItemParse : public Items { static void parseWalk(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseAllowDistanceRead(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseImbuement(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parseAugment(const std::string &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseStackSize(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseSpecializedMagicLevelPoint(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseMagicShieldCapacity(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); diff --git a/src/items/item.cpp b/src/items/item.cpp index 81d24a790e7..104efc7f925 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -1437,6 +1437,11 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr } } + std::string augmentsDescription = parseAugmentDescription(item, true); + if (!augmentsDescription.empty()) { + descriptions.emplace_back("Augments", augmentsDescription); + } + if (it.isKey()) { ss.str(""); ss << fmt::format("{:04}", item->getAttribute<uint16_t>(ItemAttribute_t::ACTIONID)); @@ -1789,6 +1794,11 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr descriptions.emplace_back("Imbuement Slots", std::to_string(it.imbuementSlot)); } + std::string augmentsDescription = it.parseAugmentDescription(true); + if (!augmentsDescription.empty()) { + descriptions.emplace_back("Augments", augmentsDescription); + } + if (it.isKey()) { ss.str(""); ss << fmt::format("{:04}", 0); @@ -3017,6 +3027,8 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std:: s << '.'; } + s << parseAugmentDescription(item); + s << parseImbuementDescription(item); s << parseClassificationDescription(item); diff --git a/src/items/item.hpp b/src/items/item.hpp index 9ff0d3d15aa..a9774c902cd 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -291,6 +291,12 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { return isStoreItem() || hasOwner(); } + static std::string parseAugmentDescription(std::shared_ptr<Item> item, bool inspect = false) { + if (!item) { + return ""; + } + return items[item->getID()].parseAugmentDescription(inspect); + } static std::string parseImbuementDescription(std::shared_ptr<Item> item); static std::string parseShowDurationSpeed(int32_t speed, bool &begin); static std::string parseShowDuration(std::shared_ptr<Item> item); @@ -419,6 +425,29 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { } return items[id].extraDefense; } + std::vector<std::shared_ptr<AugmentInfo>> getAugments() const { + return items[id].augments; + } + std::vector<std::shared_ptr<AugmentInfo>> getAugmentsBySpellNameAndType(std::string spellName, Augment_t augmentType) const { + std::vector<std::shared_ptr<AugmentInfo>> augments; + for (auto &augment : items[id].augments) { + if (strcasecmp(augment->spellName.c_str(), spellName.c_str()) == 0 && augment->type == augmentType) { + augments.push_back(augment); + } + } + + return augments; + } + std::vector<std::shared_ptr<AugmentInfo>> getAugmentsBySpellName(std::string spellName) const { + std::vector<std::shared_ptr<AugmentInfo>> augments; + for (auto &augment : items[id].augments) { + if (strcasecmp(augment->spellName.c_str(), spellName.c_str()) == 0) { + augments.push_back(augment); + } + } + + return augments; + } uint8_t getImbuementSlot() const { if (hasAttribute(ItemAttribute_t::IMBUEMENT_SLOT)) { return getAttribute<uint8_t>(ItemAttribute_t::IMBUEMENT_SLOT); diff --git a/src/items/items.cpp b/src/items/items.cpp index 9e31f5f105f..7d8ff0cf7c0 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -67,6 +67,47 @@ ItemTypes_t Items::getLootType(const std::string &strValue) { return ITEM_TYPE_NONE; } +const std::string Items::getAugmentNameByType(Augment_t augmentType) { + std::string augmentTypeName = magic_enum::enum_name(augmentType).data(); + augmentTypeName = toStartCaseWithSpace(augmentTypeName); + if (!isAugmentWithoutValueDescription(augmentType)) { + toLowerCaseString(augmentTypeName); + } + return augmentTypeName; +} + +std::string ItemType::parseAugmentDescription(bool inspect /*= false*/) const { + if (augments.empty()) { + return ""; + } + + std::vector<std::string> descriptions; + for (const auto &augment : augments) { + descriptions.push_back(getFormattedAugmentDescription(augment)); + } + + if (inspect) { + return fmt::format("{}.", fmt::join(descriptions.begin(), descriptions.end(), ", ")); + } else { + return fmt::format("\nAugments: ({}).", fmt::join(descriptions.begin(), descriptions.end(), ", ")); + } +} + +std::string ItemType::getFormattedAugmentDescription(const std::shared_ptr<AugmentInfo> &augmentInfo) const { + const std::string augmentName = Items::getAugmentNameByType(augmentInfo->type); + std::string augmentSpellNameCapitalized = augmentInfo->spellName; + capitalizeWordsIgnoringString(augmentSpellNameCapitalized, " of "); + + char signal = augmentInfo->value > 0 ? '-' : '+'; + + if (Items::isAugmentWithoutValueDescription(augmentInfo->type)) { + return fmt::format("{} -> {}", augmentSpellNameCapitalized, augmentName); + } else if (augmentInfo->type == Augment_t::Cooldown) { + return fmt::format("{} -> {}{}s {}", augmentSpellNameCapitalized, signal, augmentInfo->value / 1000, augmentName); + } + return fmt::format("{} -> {:+}% {}", augmentSpellNameCapitalized, augmentInfo->value, augmentName); +} + bool Items::reload() { clear(); loadFromProtobuf(); diff --git a/src/items/items.hpp b/src/items/items.hpp index f9e48ae1032..82af0659af4 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -252,6 +252,14 @@ class ItemType { return str; } + std::string parseAugmentDescription(bool inspect = false) const; + std::string getFormattedAugmentDescription(const std::shared_ptr<AugmentInfo> &augmentInfo) const; + + void addAugment(std::string spellName, Augment_t augmentType, int32_t value) { + auto augmentInfo = std::make_shared<AugmentInfo>(spellName, augmentType, value); + augments.emplace_back(augmentInfo); + } + void setImbuementType(ImbuementTypes_t imbuementType, uint16_t slotMaxTier) { imbuementTypes[imbuementType] = std::min<uint16_t>(IMBUEMENT_MAX_TIER, slotMaxTier); } @@ -331,6 +339,8 @@ class ItemType { int8_t hitChance = 0; + std::vector<std::shared_ptr<AugmentInfo>> augments; + // 12.90 bool wearOut = false; bool clockExpire = false; @@ -435,6 +445,18 @@ class Items { return dummys; } + static const std::string getAugmentNameByType(Augment_t augmentType); + + static const bool isAugmentWithoutValueDescription(Augment_t augmentType) { + static std::vector<Augment_t> vector = { + Augment_t::IncreasedDamage, + Augment_t::PowerfulImpact, + Augment_t::StrongImpact, + }; + + return std::find(vector.begin(), vector.end(), augmentType) != vector.end(); + } + private: std::vector<ItemType> items; std::vector<uint16_t> ladders; diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index f044d4b5248..badac532951 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -270,6 +270,17 @@ enum ImbuementTypes_t : int64_t { IMBUEMENT_INCREASE_CAPACITY = 17 }; +enum class Augment_t : uint8_t { + None, + PowerfulImpact, + StrongImpact, + IncreasedDamage, + Cooldown, + CriticalExtraDamage, + LifeLeech, + ManaLeech +}; + enum class ContainerCategory_t : uint8_t { All, Ammunition, @@ -606,9 +617,19 @@ enum ItemParseAttributes_t { ITEM_PARSE_PRIMARYTYPE, ITEM_PARSE_USEDBYGUESTS, ITEM_PARSE_SCRIPT, + ITEM_PARSE_AUGMENT, }; struct ImbuementInfo { Imbuement* imbuement; uint32_t duration = 0; }; + +struct AugmentInfo { + AugmentInfo(std::string spellName, Augment_t type, int32_t value) : + spellName(std::move(spellName)), type(type), value(value) { } + + std::string spellName; + Augment_t type; + int32_t value; +}; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d924e0bfb0e..ab111fd3c8f 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5717,7 +5717,12 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) { } if (!oldProtocol) { - msg.add<uint16_t>(0x00); // Augment + std::string augmentsDescription = it.parseAugmentDescription(true); + if (!augmentsDescription.empty()) { + msg.addString(augmentsDescription, "ProtocolGame::sendMarketDetail - augmentsDescription"); + } else { + msg.add<uint16_t>(0x00); // no augments + } } if (it.imbuementSlot > 0) { diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 0d8b39a485d..b1fe671f416 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1558,6 +1558,22 @@ void capitalizeWords(std::string &source) { } } +void capitalizeWordsIgnoringString(std::string &source, const std::string stringToIgnore) { + toLowerCaseString(source); + uint8_t size = (uint8_t)source.size(); + uint8_t indexFound = source.find(stringToIgnore); + for (uint8_t i = 0; i < size; i++) { + if (indexFound != std::string::npos && (i > indexFound - 1) && i < (indexFound + stringToIgnore.size())) { + continue; + } + if (i == 0) { + source[i] = (char)toupper(source[i]); + } else if (source[i - 1] == ' ' || source[i - 1] == '\'') { + source[i] = (char)toupper(source[i]); + } + } +} + /** * @details * Prevents the console from closing so there is time to read the error information diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 769fb5d68d3..25683bf22bb 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -138,6 +138,7 @@ const char* getReturnMessage(ReturnValue value); void sleep_for(uint64_t ms); void capitalizeWords(std::string &source); +void capitalizeWordsIgnoringString(std::string &source, const std::string stringToIgnore); void consoleHandlerExit(); NameEval_t validateName(const std::string &name); From 1d025c2c7b17fdb4a9ba7cdde4687f9654dec018 Mon Sep 17 00:00:00 2001 From: Beats <daniel15042014@hotmail.com> Date: Tue, 14 May 2024 12:22:07 -0300 Subject: [PATCH 38/95] fix: warning build ubuntu (#2490) --- CMakeLists.txt | 4 +- cmake/modules/BaseConfig.cmake | 4 +- src/canary_server.cpp | 2 +- src/canary_server.hpp | 2 +- src/config/configmanager.cpp | 1 - src/creatures/appearance/mounts/mounts.cpp | 9 +- src/creatures/appearance/mounts/mounts.hpp | 8 +- src/creatures/appearance/outfit/outfit.cpp | 31 +++--- src/creatures/appearance/outfit/outfit.hpp | 13 ++- src/creatures/combat/combat.cpp | 24 ++--- src/creatures/combat/combat.hpp | 8 +- src/creatures/combat/condition.cpp | 16 ++- src/creatures/combat/condition.hpp | 42 ++++---- src/creatures/combat/spells.cpp | 5 +- src/creatures/combat/spells.hpp | 6 +- src/creatures/creature.cpp | 36 +++---- src/creatures/creature.hpp | 12 +-- src/creatures/creatures_definitions.hpp | 54 ++-------- src/creatures/interactions/chat.hpp | 6 +- src/creatures/monsters/monster.cpp | 6 +- src/creatures/monsters/monster.hpp | 7 +- src/creatures/monsters/monsters.cpp | 8 +- src/creatures/monsters/monsters.hpp | 10 +- .../monsters/spawns/spawn_monster.cpp | 30 ++---- .../monsters/spawns/spawn_monster.hpp | 6 +- src/creatures/npcs/npc.cpp | 21 ++-- src/creatures/npcs/npc.hpp | 7 +- src/creatures/npcs/npcs.cpp | 10 +- src/creatures/npcs/npcs.hpp | 2 +- src/creatures/npcs/spawns/spawn_npc.cpp | 9 +- src/creatures/npcs/spawns/spawn_npc.hpp | 4 +- .../achievement/player_achievement.hpp | 2 +- src/creatures/players/grouping/familiars.cpp | 22 ++-- src/creatures/players/grouping/familiars.hpp | 30 +++++- src/creatures/players/grouping/groups.cpp | 13 +-- src/creatures/players/grouping/groups.hpp | 6 +- src/creatures/players/grouping/guild.cpp | 2 +- src/creatures/players/grouping/guild.hpp | 3 +- src/creatures/players/grouping/party.cpp | 53 +++++----- src/creatures/players/grouping/party.hpp | 2 +- .../players/grouping/team_finder.hpp | 4 +- src/creatures/players/highscore_category.hpp | 4 +- src/creatures/players/player.cpp | 100 +++++++++--------- src/creatures/players/player.hpp | 8 +- src/creatures/players/wheel/player_wheel.cpp | 14 +-- src/creatures/players/wheel/wheel_gems.hpp | 14 +-- src/game/functions/game_reload.cpp | 1 - src/game/game.cpp | 93 ++++++++-------- src/game/scheduling/events_scheduler.cpp | 1 - src/io/functions/iologindata_load_player.cpp | 4 +- src/io/functions/iologindata_save_player.cpp | 4 +- src/io/iobestiary.cpp | 11 +- src/io/iobestiary.hpp | 12 +-- src/io/iologindata.cpp | 2 +- src/io/iomap.cpp | 9 -- src/items/containers/container.cpp | 2 +- src/items/item.cpp | 2 +- src/items/tile.cpp | 4 +- src/items/weapons/weapons.hpp | 4 +- src/lib/metrics/metrics.cpp | 2 +- src/lib/metrics/metrics.hpp | 12 +-- src/lua/creature/movement.cpp | 2 - src/lua/creature/movement.hpp | 2 +- .../functions/core/game/config_functions.cpp | 1 - .../functions/core/game/game_functions.cpp | 8 +- .../creatures/creature_functions.cpp | 2 +- .../creatures/monster/charm_functions.cpp | 2 +- .../monster/monster_spell_functions.cpp | 2 +- .../functions/creatures/npc/npc_functions.cpp | 2 +- .../creatures/player/group_functions.cpp | 16 +-- .../creatures/player/group_functions.hpp | 2 +- .../creatures/player/player_functions.cpp | 2 +- src/map/house/house.cpp | 52 ++++----- src/map/map.cpp | 2 +- src/map/spectators.cpp | 6 +- src/server/network/protocol/protocolgame.cpp | 58 +++++----- 76 files changed, 482 insertions(+), 520 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4093f4de2d..52c35a471f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,10 +86,9 @@ endif() # === IPO === -option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON) if(OPTIONS_ENABLE_IPO) - log_option_enabled("IPO/LTO") if(MSVC) + log_option_enabled("IPO/LTO") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /GL") set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") @@ -97,6 +96,7 @@ if(OPTIONS_ENABLE_IPO) set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") else() if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "Release") + log_option_enabled("IPO/LTO") include(CheckIPOSupported) check_ipo_supported(RESULT result OUTPUT output) if(result) diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index 7e3f6404b3f..a1980d0f604 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -121,7 +121,9 @@ if (MSVC) endforeach(type) add_compile_options(/MP /FS /Zf /EHsc) -endif (MSVC) +else() + add_compile_options(-Wno-unused-parameter -Wno-sign-compare -Wno-switch -Wno-implicit-fallthrough -Wno-extra) +endif() ## Link compilation files to build/bin folder, else link to the main dir function(set_output_directory target_name) diff --git a/src/canary_server.cpp b/src/canary_server.cpp index bfabbf51ab8..17a36ff3d23 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -327,7 +327,7 @@ void CanaryServer::loadModules() { // If "USE_ANY_DATAPACK_FOLDER" is set to true then you can choose any datapack folder for your server const auto useAnyDatapack = g_configManager().getBoolean(USE_ANY_DATAPACK_FOLDER, __FUNCTION__); auto datapackName = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); - if (!useAnyDatapack && (datapackName != "data-canary" && datapackName != "data-otservbr-global" || datapackName != "data-otservbr-global" && datapackName != "data-canary")) { + if (!useAnyDatapack && datapackName != "data-canary" && datapackName != "data-otservbr-global") { throw FailedToInitializeCanary(fmt::format( "The datapack folder name '{}' is wrong, please select valid " "datapack name 'data-canary' or 'data-otservbr-global " diff --git a/src/canary_server.hpp b/src/canary_server.hpp index bb22232b8e4..57c931cd133 100644 --- a/src/canary_server.hpp +++ b/src/canary_server.hpp @@ -46,8 +46,8 @@ class CanaryServer { FAILED }; - RSA &rsa; Logger &logger; + RSA &rsa; ServiceManager &serviceManager; std::atomic<LoaderStatus> loaderStatus = LoaderStatus::LOADING; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 0525c5a560a..37634abf514 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -11,7 +11,6 @@ #include "config/configmanager.hpp" #include "lib/di/container.hpp" -#include "declarations.hpp" #include "game/game.hpp" #include "server/network/webhook/webhook.hpp" diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index dff0f02a451..7051d04ddb8 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -35,7 +35,7 @@ bool Mounts::loadFromXml() { continue; } - mounts.emplace_back(std::make_shared<Mount>( + mounts.emplace(std::make_shared<Mount>( static_cast<uint8_t>(pugi::cast<uint16_t>(mountNode.attribute("id").value())), lookType, mountNode.attribute("name").as_string(), @@ -44,12 +44,11 @@ bool Mounts::loadFromXml() { mountNode.attribute("type").as_string() )); } - mounts.shrink_to_fit(); return true; } std::shared_ptr<Mount> Mounts::getMountByID(uint8_t id) { - auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr<Mount> mount) { + auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr<Mount> &mount) { return mount->id == id; // Note the use of -> operator to access the members of the Mount object }); @@ -58,7 +57,7 @@ std::shared_ptr<Mount> Mounts::getMountByID(uint8_t id) { std::shared_ptr<Mount> Mounts::getMountByName(const std::string &name) { auto mountName = name.c_str(); - auto it = std::find_if(mounts.begin(), mounts.end(), [mountName](const std::shared_ptr<Mount> mount) { + auto it = std::find_if(mounts.begin(), mounts.end(), [mountName](const std::shared_ptr<Mount> &mount) { return strcasecmp(mountName, mount->name.c_str()) == 0; }); @@ -66,7 +65,7 @@ std::shared_ptr<Mount> Mounts::getMountByName(const std::string &name) { } std::shared_ptr<Mount> Mounts::getMountByClientID(uint16_t clientId) { - auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr<Mount> mount) { + auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr<Mount> &mount) { return mount->clientId == clientId; // Note the use of -> operator to access the members of the Mount object }); diff --git a/src/creatures/appearance/mounts/mounts.hpp b/src/creatures/appearance/mounts/mounts.hpp index 05bd330a66b..ca4f969842a 100644 --- a/src/creatures/appearance/mounts/mounts.hpp +++ b/src/creatures/appearance/mounts/mounts.hpp @@ -11,8 +11,8 @@ struct Mount { Mount(uint8_t initId, uint16_t initClientId, std::string initName, int32_t initSpeed, bool initPremium, std::string initType) : - name(initName), speed(initSpeed), clientId(initClientId), id(initId), premium(initPremium), - type(initType) { } + name(std::move(initName)), speed(initSpeed), clientId(initClientId), id(initId), premium(initPremium), + type(std::move(initType)) { } std::string name; int32_t speed; @@ -30,10 +30,10 @@ class Mounts { std::shared_ptr<Mount> getMountByName(const std::string &name); std::shared_ptr<Mount> getMountByClientID(uint16_t clientId); - [[nodiscard]] const std::vector<std::shared_ptr<Mount>> &getMounts() const { + [[nodiscard]] const phmap::parallel_flat_hash_set<std::shared_ptr<Mount>> &getMounts() const { return mounts; } private: - std::vector<std::shared_ptr<Mount>> mounts; + phmap::parallel_flat_hash_set<std::shared_ptr<Mount>> mounts; }; diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 6f0490339de..9e96c60e756 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -14,6 +14,13 @@ #include "utils/tools.hpp" #include "game/game.hpp" +bool Outfits::reload() { + for (auto &outfitsVector : outfits) { + outfitsVector.clear(); + } + return loadFromXml(); +} + bool Outfits::loadFromXml() { pugi::xml_document doc; auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/outfits.xml"; @@ -68,10 +75,12 @@ bool Outfits::loadFromXml() { } std::shared_ptr<Outfit> Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { - for (const auto &outfit : outfits[sex]) { - if (outfit->lookType == lookType) { - return outfit; - } + auto it = std::ranges::find_if(outfits[sex], [&lookType](const auto &outfit) { + return outfit->lookType == lookType; + }); + + if (it != outfits[sex].end()) { + return *it; } return nullptr; } @@ -83,17 +92,7 @@ std::shared_ptr<Outfit> Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t l * @return <b>const</b> pointer to the outfit or <b>nullptr</b> if it could not be found. */ -std::shared_ptr<Outfit> Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) { +std::shared_ptr<Outfit> Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { PlayerSex_t searchSex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE; - - for (uint16_t i = 0; i < outfits[sex].size(); i++) { - if (outfits[sex].at(i)->lookType == lookType) { - if (outfits[searchSex].size() > i) { - return outfits[searchSex].at(i); - } else { // looktype found but the oposite sex array doesn't have this index. - return nullptr; - } - } - } - return nullptr; + return getOutfitByLookType(searchSex, lookType); } diff --git a/src/creatures/appearance/outfit/outfit.hpp b/src/creatures/appearance/outfit/outfit.hpp index e1a5143c673..30b84d1aca2 100644 --- a/src/creatures/appearance/outfit/outfit.hpp +++ b/src/creatures/appearance/outfit/outfit.hpp @@ -12,9 +12,17 @@ #include "declarations.hpp" #include "lib/di/container.hpp" +struct OutfitEntry { + constexpr OutfitEntry(uint16_t initLookType, uint8_t initAddons) : + lookType(initLookType), addons(initAddons) { } + + uint16_t lookType; + uint8_t addons; +}; + struct Outfit { Outfit(std::string initName, uint16_t initLookType, bool initPremium, bool initUnlocked, std::string initFrom) : - name(initName), lookType(initLookType), premium(initPremium), unlocked(initUnlocked), from(initFrom) { } + name(std::move(initName)), lookType(initLookType), premium(initPremium), unlocked(initUnlocked), from(std::move(initFrom)) { } std::string name; uint16_t lookType; @@ -38,9 +46,10 @@ class Outfits { return inject<Outfits>(); } - std::shared_ptr<Outfit> getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType); + std::shared_ptr<Outfit> getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; bool loadFromXml(); + bool reload(); [[nodiscard]] std::shared_ptr<Outfit> getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; [[nodiscard]] const std::vector<std::shared_ptr<Outfit>> &getOutfits(PlayerSex_t sex) const { diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 06812095cbf..2aaadac4d61 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -287,7 +287,7 @@ bool Combat::isProtected(std::shared_ptr<Player> attacker, std::shared_ptr<Playe return true; } - if (!attacker->getVocation()->canCombat() || !target->getVocation()->canCombat() && (attacker->getVocationId() == VOCATION_NONE || target->getVocationId() == VOCATION_NONE)) { + if ((!attacker->getVocation()->canCombat() || !target->getVocation()->canCombat()) && (attacker->getVocationId() == VOCATION_NONE || target->getVocationId() == VOCATION_NONE)) { return true; } @@ -707,7 +707,7 @@ bool Combat::checkFearConditionAffected(std::shared_ptr<Player> player) { auto affectedCount = (party->getMemberCount() + 5) / 5; g_logger().debug("[{}] Player is member of a party, {} members can be feared", __FUNCTION__, affectedCount); - for (const auto member : party->getMembers()) { + for (const auto &member : party->getMembers()) { if (member->hasCondition(CONDITION_FEARED)) { affectedCount -= 1; } @@ -759,7 +759,7 @@ void Combat::CombatConditionFunc(std::shared_ptr<Creature> caster, std::shared_p } } - if (caster == target || target && !target->isImmune(condition->getType())) { + if (caster == target || (target && !target->isImmune(condition->getType()))) { auto conditionCopy = condition->clone(); if (caster) { conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); @@ -1030,10 +1030,10 @@ bool Combat::doCombatChain(std::shared_ptr<Creature> caster, std::shared_ptr<Cre uint8_t chainDistance; bool backtracking = false; params.chainCallback->getChainValues(caster, maxTargets, chainDistance, backtracking); - auto targets = pickChainTargets(caster, params, chainDistance, maxTargets, backtracking, aggressive, target); + auto targets = pickChainTargets(caster, params, chainDistance, maxTargets, aggressive, backtracking, std::move(target)); g_logger().debug("[{}] Chain targets: {}", __FUNCTION__, targets.size()); - if (targets.empty() || targets.size() == 1 && targets.begin()->second.empty()) { + if (targets.empty() || (targets.size() == 1 && targets.begin()->second.empty())) { return false; } @@ -1122,7 +1122,7 @@ void Combat::CombatFunc(std::shared_ptr<Creature> caster, const Position &origin uint32_t maxY = 0; // calculate the max viewable range - for (std::shared_ptr<Tile> tile : tileList) { + for (const std::shared_ptr<Tile> &tile : tileList) { const Position &tilePos = tile->getPosition(); uint32_t diff = Position::getDistanceX(tilePos, pos); @@ -1140,7 +1140,7 @@ void Combat::CombatFunc(std::shared_ptr<Creature> caster, const Position &origin const int32_t rangeY = maxY + MAP_MAX_VIEW_PORT_Y; int affected = 0; - for (std::shared_ptr<Tile> tile : tileList) { + for (const std::shared_ptr<Tile> &tile : tileList) { if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { continue; } @@ -1195,7 +1195,7 @@ void Combat::CombatFunc(std::shared_ptr<Creature> caster, const Position &origin uint8_t beamAffectedCurrent = 0; tmpDamage.affected = affected; - for (std::shared_ptr<Tile> tile : tileList) { + for (const std::shared_ptr<Tile> &tile : tileList) { if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { continue; } @@ -1241,7 +1241,7 @@ void Combat::CombatFunc(std::shared_ptr<Creature> caster, const Position &origin } void Combat::doCombatHealth(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, CombatDamage &damage, const CombatParams ¶ms) { - doCombatHealth(caster, target, caster ? caster->getPosition() : Position(), damage, params); + doCombatHealth(caster, std::move(target), caster ? caster->getPosition() : Position(), damage, params); } void Combat::doCombatHealth(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const Position &origin, CombatDamage &damage, const CombatParams ¶ms) { @@ -1382,7 +1382,7 @@ void Combat::doCombatDispel(std::shared_ptr<Creature> caster, std::shared_ptr<Cr } } -void Combat::doCombatDefault(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const CombatParams ¶ms) { +[[maybe_unused]] void Combat::doCombatDefault(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const CombatParams ¶ms) { doCombatDefault(caster, target, caster ? caster->getPosition() : Position(), params); } @@ -2076,7 +2076,7 @@ void AreaCombat::setupExtArea(const std::list<uint32_t> &list, uint32_t rows) { void MagicField::onStepInField(const std::shared_ptr<Creature> &creature) { // remove magic walls/wild growth - if (!isBlocking() && g_game().getWorldType() == WORLD_TYPE_NO_PVP && id == ITEM_MAGICWALL_SAFE || id == ITEM_WILDGROWTH_SAFE) { + if ((!isBlocking() && g_game().getWorldType() == WORLD_TYPE_NO_PVP && id == ITEM_MAGICWALL_SAFE) || id == ITEM_WILDGROWTH_SAFE) { if (!creature->isInGhostMode()) { g_game().internalRemoveItem(static_self_cast<Item>(), 1); } @@ -2091,7 +2091,7 @@ void MagicField::onStepInField(const std::shared_ptr<Creature> &creature) { if (ownerId) { bool harmfulField = true; auto itemTile = getTile(); - if (g_game().getWorldType() == WORLD_TYPE_NO_PVP || itemTile && itemTile->hasFlag(TILESTATE_NOPVPZONE)) { + if (g_game().getWorldType() == WORLD_TYPE_NO_PVP || (itemTile && itemTile->hasFlag(TILESTATE_NOPVPZONE))) { auto ownerPlayer = g_game().getPlayerByGUID(ownerId); if (ownerPlayer) { harmfulField = false; diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 6a79455c7c4..93b02502f25 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -72,8 +72,8 @@ class ChainCallback final : public CallBack { private: void onChainCombat(std::shared_ptr<Creature> creature, uint8_t &chainTargets, uint8_t &chainDistance, bool &backtracking); - uint8_t m_chainTargets = 0; uint8_t m_chainDistance = 0; + uint8_t m_chainTargets = 0; bool m_backtracking = false; bool m_fromLua = false; }; @@ -125,7 +125,7 @@ class MatrixArea { data_[row] = new bool[cols]; for (uint32_t col = 0; col < cols; ++col) { - data_[row][col] = 0; + data_[row][col] = false; } } } @@ -324,7 +324,7 @@ class Combat { } void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); void postCombatEffects(std::shared_ptr<Creature> caster, const Position &origin, const Position &pos) const { - postCombatEffects(caster, origin, pos, params); + postCombatEffects(std::move(caster), origin, pos, params); } void setOrigin(CombatOrigin origin) { @@ -393,7 +393,7 @@ class Combat { * @param damage The combat damage. * @return The calculated level formula. */ - int32_t getLevelFormula(std::shared_ptr<Player> player, const std::shared_ptr<Spell> wheelSpell, const CombatDamage &damage) const; + int32_t getLevelFormula(std::shared_ptr<Player> player, std::shared_ptr<Spell> wheelSpell, const CombatDamage &damage) const; CombatDamage getCombatDamage(std::shared_ptr<Creature> creature, std::shared_ptr<Creature> target) const; // configureable diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 56c67700d13..7f477f1d859 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1472,7 +1472,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream &propStre } else if (attr == CONDITIONATTR_OWNER) { return propStream.skip(4); } else if (attr == CONDITIONATTR_INTERVALDATA) { - IntervalInfo damageInfo; + IntervalInfo damageInfo {}; if (!propStream.read<IntervalInfo>(damageInfo)) { return false; } @@ -1530,7 +1530,7 @@ bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value) { // rounds, time, damage for (int32_t i = 0; i < rounds; ++i) { - IntervalInfo damageInfo; + IntervalInfo damageInfo {}; damageInfo.interval = time; damageInfo.timeLeft = time; damageInfo.value = value; @@ -1803,13 +1803,9 @@ void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::lis * ConditionFeared */ bool ConditionFeared::isStuck(std::shared_ptr<Creature> creature, Position pos) const { - for (Direction dir : m_directionsVector) { - if (canWalkTo(creature, pos, dir)) { - return false; - } - } - - return true; + return std::ranges::all_of(m_directionsVector, [&](Direction dir) { + return !canWalkTo(creature, pos, dir); + }); } bool ConditionFeared::getRandomDirection(std::shared_ptr<Creature> creature, Position pos) { @@ -1995,6 +1991,8 @@ bool ConditionFeared::getFleePath(std::shared_ptr<Creature> creature, const Posi futurePos.y -= wsize; g_logger().debug("[{}] Trying to flee to NORTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; + case DIRECTION_NONE: + break; } found = creature->getPathTo(futurePos, dirList, 0, 30); diff --git a/src/creatures/combat/condition.hpp b/src/creatures/combat/condition.hpp index 539a8711723..07b31b42d15 100644 --- a/src/creatures/combat/condition.hpp +++ b/src/creatures/combat/condition.hpp @@ -27,7 +27,7 @@ class Condition : public SharedObject { virtual bool startCondition(std::shared_ptr<Creature> creature); virtual bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval); virtual void endCondition(std::shared_ptr<Creature> creature) = 0; - virtual void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) = 0; + virtual void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) = 0; virtual uint32_t getIcons() const; ConditionId_t getId() const { return id; @@ -65,14 +65,14 @@ class Condition : public SharedObject { protected: uint8_t drainBodyStage = 0; - int64_t endTime; - uint32_t subId; - int32_t ticks; - ConditionType_t conditionType; - ConditionId_t id; - bool isBuff; + int64_t endTime {}; + uint32_t subId {}; + int32_t ticks {}; + ConditionType_t conditionType {}; + ConditionId_t id {}; + bool isBuff {}; - virtual bool updateCondition(const std::shared_ptr<Condition> addCondition); + virtual bool updateCondition(std::shared_ptr<Condition> addCondition); private: SoundEffect_t tickSound = SoundEffect_t::SILENCE; @@ -90,7 +90,7 @@ class ConditionGeneric : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; uint32_t getIcons() const override; std::shared_ptr<Condition> clone() const override { @@ -106,7 +106,7 @@ class ConditionAttributes final : public ConditionGeneric { bool startCondition(std::shared_ptr<Creature> creature) final; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) final; void endCondition(std::shared_ptr<Creature> creature) final; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) final; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) final; bool setParam(ConditionParam_t param, int32_t value) final; @@ -172,7 +172,7 @@ class ConditionRegeneration final : public ConditionGeneric { bool startCondition(std::shared_ptr<Creature> creature) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> addCondition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> addCondition) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; bool setParam(ConditionParam_t param, int32_t value) override; @@ -205,7 +205,7 @@ class ConditionManaShield final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> addCondition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> addCondition) override; uint32_t getIcons() const override; bool setParam(ConditionParam_t param, int32_t value) override; @@ -227,7 +227,7 @@ class ConditionSoul final : public ConditionGeneric { ConditionSoul(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0) : ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { } - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> addCondition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> addCondition) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; bool setParam(ConditionParam_t param, int32_t value) override; @@ -270,7 +270,7 @@ class ConditionDamage final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; uint32_t getIcons() const override; std::shared_ptr<Condition> clone() const override { @@ -309,7 +309,7 @@ class ConditionDamage final : public Condition { bool getNextDamage(int32_t &damage); bool doDamage(std::shared_ptr<Creature> creature, int32_t healthChange); - bool updateCondition(const std::shared_ptr<Condition> addCondition) override; + bool updateCondition(std::shared_ptr<Condition> addCondition) override; }; class ConditionFeared final : public Condition { @@ -321,7 +321,7 @@ class ConditionFeared final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; uint32_t getIcons() const override; std::shared_ptr<Condition> clone() const override { @@ -360,7 +360,7 @@ class ConditionSpeed final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; uint32_t getIcons() const override; std::shared_ptr<Condition> clone() const override { @@ -395,7 +395,7 @@ class ConditionOutfit final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; std::shared_ptr<Condition> clone() const override { return std::make_shared<ConditionOutfit>(*this); @@ -421,7 +421,7 @@ class ConditionLight final : public Condition { bool startCondition(std::shared_ptr<Creature> creature) override; bool executeCondition(std::shared_ptr<Creature> creature, int32_t interval) override; void endCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> addCondition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> addCondition) override; std::shared_ptr<Condition> clone() const override { return std::make_shared<ConditionLight>(*this); @@ -445,7 +445,7 @@ class ConditionSpellCooldown final : public ConditionGeneric { ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } bool startCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; std::shared_ptr<Condition> clone() const override { return std::make_shared<ConditionSpellCooldown>(*this); @@ -458,7 +458,7 @@ class ConditionSpellGroupCooldown final : public ConditionGeneric { ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } bool startCondition(std::shared_ptr<Creature> creature) override; - void addCondition(std::shared_ptr<Creature> creature, const std::shared_ptr<Condition> condition) override; + void addCondition(std::shared_ptr<Creature> creature, std::shared_ptr<Condition> condition) override; std::shared_ptr<Condition> clone() const override { return std::make_shared<ConditionSpellGroupCooldown>(*this); diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 3aa445b34aa..dd4305de14e 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -766,7 +766,6 @@ uint32_t Spell::getManaCost(std::shared_ptr<Player> player) const { if (manaPercent != 0) { uint32_t maxMana = player->getMaxMana(); uint32_t manaCost = (maxMana * manaPercent) / 100; - WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName()); if (manaRedution > manaCost) { return 0; } @@ -789,7 +788,7 @@ bool InstantSpell::playerCastInstant(std::shared_ptr<Player> player, std::string var.type = VARIANT_NUMBER; var.number = player->getID(); } else if (needTarget || casterTargetOrDirection) { - std::shared_ptr<Creature> target = nullptr; + std::shared_ptr<Creature> target; bool useDirection = false; if (hasParam) { @@ -1085,7 +1084,7 @@ bool RuneSpell::castSpell(std::shared_ptr<Creature> creature, std::shared_ptr<Cr bool RuneSpell::internalCastSpell(std::shared_ptr<Creature> creature, const LuaVariant &var, bool isHotkey) { bool result; if (isLoadedCallback()) { - result = executeCastSpell(creature, var, isHotkey); + result = executeCastSpell(std::move(creature), var, isHotkey); } else { result = false; } diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index dce7b3caba4..fc23bbf9030 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -63,8 +63,8 @@ class Spells final : public Scripts { } void clear(); - bool registerInstantLuaEvent(const std::shared_ptr<InstantSpell> instant); - bool registerRuneLuaEvent(const std::shared_ptr<RuneSpell> rune); + bool registerInstantLuaEvent(std::shared_ptr<InstantSpell> instant); + bool registerRuneLuaEvent(std::shared_ptr<RuneSpell> rune); private: std::map<uint16_t, std::shared_ptr<RuneSpell>> runes; @@ -92,7 +92,7 @@ class BaseSpell { class CombatSpell final : public Script, public BaseSpell, public std::enable_shared_from_this<CombatSpell> { public: // Constructor - CombatSpell(const std::shared_ptr<Combat> newCombat, bool newNeedTarget, bool newNeedDirection); + CombatSpell(std::shared_ptr<Combat> newCombat, bool newNeedTarget, bool newNeedDirection); // The copy constructor and the assignment operator have been deleted to prevent accidental copying. CombatSpell(const CombatSpell &) = delete; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index fd78e33ecf4..43e7ab89ccf 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -125,7 +125,7 @@ void Creature::onThink(uint32_t interval) { auto onThink = [self = getCreature(), interval] { // scripting event - onThink const auto &thinkEvents = self->getCreatureEvents(CREATURE_EVENT_THINK); - for (const auto creatureEventPtr : thinkEvents) { + for (const auto &creatureEventPtr : thinkEvents) { creatureEventPtr->executeOnThink(self->static_self_cast<Creature>(), interval); } }; @@ -299,7 +299,7 @@ void Creature::updateTileCache(std::shared_ptr<Tile> upTile, const Position &pos if (pos.z == myPos.z) { int32_t dx = Position::getOffsetX(pos, myPos); int32_t dy = Position::getOffsetY(pos, myPos); - updateTileCache(upTile, dx, dy); + updateTileCache(std::move(upTile), dx, dy); } } @@ -332,7 +332,7 @@ int32_t Creature::getWalkCache(const Position &pos) { void Creature::onAddTileItem(std::shared_ptr<Tile> tileItem, const Position &pos) { if (isMapLoaded && pos.z == getPosition().z) { - updateTileCache(tileItem, pos); + updateTileCache(std::move(tileItem), pos); } } @@ -343,7 +343,7 @@ void Creature::onUpdateTileItem(std::shared_ptr<Tile> updateTile, const Position if (oldType.blockSolid || oldType.blockPathFind || newType.blockPathFind || newType.blockSolid) { if (pos.z == getPosition().z) { - updateTileCache(updateTile, pos); + updateTileCache(std::move(updateTile), pos); } } } @@ -355,7 +355,7 @@ void Creature::onRemoveTileItem(std::shared_ptr<Tile> updateTile, const Position if (iType.blockSolid || iType.blockPathFind || iType.isGroundTile()) { if (pos.z == getPosition().z) { - updateTileCache(updateTile, pos); + updateTileCache(std::move(updateTile), pos); } } } @@ -439,7 +439,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) { // Check if any of our summons is out of range (+/- 2 floors or 30 tiles away) bool checkRemoveDist = Position::getDistanceZ(newPos, pos) > 2 || (std::max<int32_t>(Position::getDistanceX(newPos, pos), Position::getDistanceY(newPos, pos)) > 30); - if (monster && monster->isFamiliar() && checkSummonDist || teleportSummon && !protectionZoneCheck && checkSummonDist) { + if ((monster && monster->isFamiliar() && checkSummonDist) || (teleportSummon && !protectionZoneCheck && checkSummonDist)) { const auto &creatureMaster = summon->getMaster(); if (!creatureMaster) { continue; @@ -709,7 +709,7 @@ void Creature::onDeath() { } } - bool killedByPlayer = mostDamageCreature && mostDamageCreature->getPlayer() || mostDamageCreatureMaster && mostDamageCreatureMaster->getPlayer(); + bool killedByPlayer = (mostDamageCreature && mostDamageCreature->getPlayer()) || (mostDamageCreatureMaster && mostDamageCreatureMaster->getPlayer()); if (getPlayer()) { g_metrics().addCounter( "player_death", @@ -761,7 +761,7 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared if (getMaster()) { // Scripting event onDeath const CreatureEventList &deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH); - for (const auto deathEventPtr : deathEvents) { + for (const auto &deathEventPtr : deathEvents) { deathEventPtr->executeOnDeath(static_self_cast<Creature>(), nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); } } @@ -834,7 +834,7 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared } // Scripting event onDeath - for (const auto deathEventPtr : getCreatureEvents(CREATURE_EVENT_DEATH)) { + for (const auto &deathEventPtr : getCreatureEvents(CREATURE_EVENT_DEATH)) { if (deathEventPtr) { deathEventPtr->executeOnDeath(static_self_cast<Creature>(), corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); } @@ -1169,7 +1169,7 @@ double Creature::getDamageRatio(std::shared_ptr<Creature> attacker) const { } uint64_t Creature::getGainedExperience(std::shared_ptr<Creature> attacker) const { - return std::floor(getDamageRatio(attacker) * getLostExperience()); + return std::floor(getDamageRatio(std::move(attacker)) * getLostExperience()); } void Creature::addDamagePoints(std::shared_ptr<Creature> attacker, int32_t damagePoints) { @@ -1181,7 +1181,7 @@ void Creature::addDamagePoints(std::shared_ptr<Creature> attacker, int32_t damag auto it = damageMap.find(attackerId); if (it == damageMap.end()) { - CountBlock_t cb; + CountBlock_t cb {}; cb.ticks = OTSYS_TIME(); cb.total = damagePoints; damageMap[attackerId] = cb; @@ -1247,7 +1247,7 @@ void Creature::onTickCondition(ConditionType_t type, bool &bRemove) { } void Creature::onCombatRemoveCondition(std::shared_ptr<Condition> condition) { - removeCondition(condition); + removeCondition(std::move(condition)); } void Creature::onAttacked() { @@ -1275,7 +1275,7 @@ bool Creature::deprecatedOnKilledCreature(std::shared_ptr<Creature> target, bool // scripting event - onKill const CreatureEventList &killEvents = getCreatureEvents(CREATURE_EVENT_KILL); - for (const auto killEventPtr : killEvents) { + for (const auto &killEventPtr : killEvents) { killEventPtr->executeOnKill(static_self_cast<Creature>(), target, lastHit); } return false; @@ -1293,7 +1293,7 @@ void Creature::onGainExperience(uint64_t gainExp, std::shared_ptr<Creature> targ gainExp /= 2; } - master->onGainExperience(gainExp, target); + master->onGainExperience(gainExp, std::move(target)); if (!m->isFamiliar()) { auto spectators = Spectators().find<Player>(position); @@ -1574,7 +1574,7 @@ void Creature::setSpeed(int32_t varSpeedDelta) { } void Creature::setCreatureLight(LightInfo lightInfo) { - internalLight = std::move(lightInfo); + internalLight = lightInfo; } void Creature::setNormalCreatureLight() { @@ -1589,7 +1589,7 @@ bool Creature::registerCreatureEvent(const std::string &name) { CreatureEventType_t type = event->getEventType(); if (hasEventRegistered(type)) { - for (const auto creatureEventPtr : eventsList) { + for (const auto &creatureEventPtr : eventsList) { if (creatureEventPtr == event) { return false; } @@ -1642,7 +1642,7 @@ CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) { return tmpEventList; } - for (const auto creatureEventPtr : eventsList) { + for (const auto &creatureEventPtr : eventsList) { if (creatureEventPtr->getEventType() == type) { tmpEventList.push_back(creatureEventPtr); } @@ -1727,7 +1727,7 @@ bool FrozenPathingConditionCall::operator()(const Position &startPos, const Posi } bool Creature::isInvisible() const { - return std::find_if(conditions.begin(), conditions.end(), [](const std::shared_ptr<Condition> condition) { + return std::find_if(conditions.begin(), conditions.end(), [](const std::shared_ptr<Condition> &condition) { return condition->getType() == CONDITION_INVISIBLE; }) != conditions.end(); diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 2c92032bd65..670d5f3d688 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -400,13 +400,13 @@ class Creature : virtual public Thing, public SharedObject { void executeConditions(uint32_t interval); bool hasCondition(ConditionType_t type, uint32_t subId = 0) const; - virtual bool isImmune(CombatType_t type) const { + virtual bool isImmune([[maybe_unused]] CombatType_t type) const { return false; } - virtual bool isImmune(ConditionType_t type) const { + virtual bool isImmune([[maybe_unused]] ConditionType_t type) const { return false; } - virtual bool isSuppress(ConditionType_t type, bool attackerPlayer) const { + virtual bool isSuppress([[maybe_unused]] ConditionType_t type, [[maybe_unused]] bool attackerPlayer) const { return false; }; @@ -424,7 +424,7 @@ class Creature : virtual public Thing, public SharedObject { virtual void drainHealth(std::shared_ptr<Creature> attacker, int32_t damage); virtual void drainMana(std::shared_ptr<Creature> attacker, int32_t manaLoss); - virtual bool challengeCreature(std::shared_ptr<Creature>, int targetChangeCooldown) { + virtual bool challengeCreature(std::shared_ptr<Creature>, [[maybe_unused]] int targetChangeCooldown) { return false; } @@ -448,10 +448,10 @@ class Creature : virtual public Thing, public SharedObject { * @deprecated -- This is here to trigger the deprecated onKill events in lua */ bool deprecatedOnKilledCreature(std::shared_ptr<Creature> target, bool lastHit); - virtual bool onKilledPlayer(const std::shared_ptr<Player> &target, bool lastHit) { + virtual bool onKilledPlayer([[maybe_unused]] const std::shared_ptr<Player> &target, [[maybe_unused]] bool lastHit) { return false; }; - virtual bool onKilledMonster(const std::shared_ptr<Monster> &target) { + virtual bool onKilledMonster([[maybe_unused]] const std::shared_ptr<Monster> &target) { return false; }; virtual void onGainExperience(uint64_t gainExp, std::shared_ptr<Creature> target); diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 46fb5fe5979..136f587a64a 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -124,7 +124,7 @@ enum ConditionType_t : uint8_t { // constexpr definiting suppressible conditions constexpr bool IsConditionSuppressible(ConditionType_t condition) { - constexpr ConditionType_t suppressibleConditions[] = { + constexpr std::array suppressibleConditions = { CONDITION_POISON, CONDITION_FIRE, CONDITION_ENERGY, @@ -135,13 +135,9 @@ constexpr bool IsConditionSuppressible(ConditionType_t condition) { CONDITION_CURSED, }; - for (const auto &suppressibleCondition : suppressibleConditions) { - if (condition == suppressibleCondition) { - return true; - } - } - - return false; + return std::ranges::any_of(suppressibleConditions, [condition](const auto &suppressibleCondition) { + return condition == suppressibleCondition; + }); } enum ConditionParam_t { @@ -1415,20 +1411,6 @@ struct VIPEntry { bool notify; }; -struct OutfitEntry { - constexpr OutfitEntry(uint16_t initLookType, uint8_t initAddons) : - lookType(initLookType), addons(initAddons) { } - - uint16_t lookType; - uint8_t addons; -}; - -struct FamiliarEntry { - constexpr explicit FamiliarEntry(uint16_t initLookType) : - lookType(initLookType) { } - uint16_t lookType; -}; - struct Skill { uint64_t tries = 0; uint16_t level = 10; @@ -1539,19 +1521,6 @@ using ItemsTierCountList = std::map<uint16_t, std::map<uint8_t, uint32_t>>; | ... */ -struct Familiar { - Familiar(std::string initName, uint16_t initLookType, bool initPremium, bool initUnlocked, std::string initType) : - name(initName), lookType(initLookType), - premium(initPremium), unlocked(initUnlocked), - type(initType) { } - - std::string name; - uint16_t lookType; - bool premium; - bool unlocked; - std::string type; -}; - struct ProtocolFamiliars { ProtocolFamiliars(const std::string &initName, uint16_t initLookType) : name(initName), lookType(initLookType) { } @@ -1654,18 +1623,11 @@ struct ShopBlock { int32_t itemStorageValue; std::vector<ShopBlock> childShop; - ShopBlock() { - itemId = 0; - itemName = ""; - itemSubType = 0; - itemBuyPrice = 0; - itemSellPrice = 0; - itemStorageKey = 0; - itemStorageValue = 0; - } + ShopBlock() : + itemId(0), itemName(""), itemSubType(0), itemBuyPrice(0), itemSellPrice(0), itemStorageKey(0), itemStorageValue(0) { } - explicit ShopBlock(uint16_t newItemId, int32_t newSubType = 0, uint32_t newBuyPrice = 0, uint32_t newSellPrice = 0, int32_t newStorageKey = 0, int32_t newStorageValue = 0, std::string newName = "") : - itemId(newItemId), itemSubType(newSubType), itemBuyPrice(newBuyPrice), itemSellPrice(newSellPrice), itemStorageKey(newStorageKey), itemStorageValue(newStorageValue), itemName(std::move(newName)) { } + explicit ShopBlock(uint16_t newItemId, std::string newName = "", int32_t newSubType = 0, uint32_t newBuyPrice = 0, uint32_t newSellPrice = 0, int32_t newStorageKey = 0, int32_t newStorageValue = 0) : + itemId(newItemId), itemName(std::move(newName)), itemSubType(newSubType), itemBuyPrice(newBuyPrice), itemSellPrice(newSellPrice), itemStorageKey(newStorageKey), itemStorageValue(newStorageValue) { } bool operator==(const ShopBlock &other) const { return itemId == other.itemId && itemName == other.itemName && itemSubType == other.itemSubType && itemBuyPrice == other.itemBuyPrice && itemSellPrice == other.itemSellPrice && itemStorageKey == other.itemStorageKey && itemStorageValue == other.itemStorageValue && childShop == other.childShop; diff --git a/src/creatures/interactions/chat.hpp b/src/creatures/interactions/chat.hpp index 022b3f540a1..3643086184c 100644 --- a/src/creatures/interactions/chat.hpp +++ b/src/creatures/interactions/chat.hpp @@ -71,7 +71,7 @@ class ChatChannel { int32_t onLeaveEvent = -1; int32_t onSpeakEvent = -1; - uint16_t id; + uint16_t id {}; bool publicChannel = false; friend class Chat; @@ -79,8 +79,8 @@ class ChatChannel { class PrivateChatChannel final : public ChatChannel { public: - PrivateChatChannel(uint16_t channelId, std::string channelName) : - ChatChannel(channelId, channelName) { } + PrivateChatChannel(uint16_t channelId, [[maybe_unused]] std::string channelName) : + ChatChannel(channelId, std::move(channelName)) { } uint32_t getOwner() const override { return owner; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index f15616d6af5..fccc19b05e5 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -385,7 +385,7 @@ void Monster::onCreatureFound(std::shared_ptr<Creature> creature, bool pushFront } void Monster::onCreatureEnter(std::shared_ptr<Creature> creature) { - onCreatureFound(creature, true); + onCreatureFound(std::move(creature), true); } bool Monster::isFriend(const std::shared_ptr<Creature> &creature) const { @@ -701,7 +701,7 @@ void Monster::updateIdleStatus() { isWalkingBack = true; } } else if (const auto &master = getMaster()) { - if ((!isSummon() && totalPlayersOnScreen == 0 || isSummon() && master->getMonster() && master->getMonster()->totalPlayersOnScreen == 0) && getFaction() != FACTION_DEFAULT) { + if (((!isSummon() && totalPlayersOnScreen == 0) || (isSummon() && master->getMonster() && master->getMonster()->totalPlayersOnScreen == 0)) && getFaction() != FACTION_DEFAULT) { idle = true; } } @@ -838,7 +838,7 @@ void Monster::doAttacking(uint32_t interval) { for (const spellBlock_t &spellBlock : mType->info.attackSpells) { bool inRange = false; - if (spellBlock.spell == nullptr || spellBlock.isMelee && isFleeing()) { + if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { continue; } diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index edf3874161d..bf9a39e4133 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -15,7 +15,6 @@ class Creature; class Game; -class Spawn; class Monster final : public Creature { public: @@ -23,7 +22,7 @@ class Monster final : public Creature { static int32_t despawnRange; static int32_t despawnRadius; - explicit Monster(const std::shared_ptr<MonsterType> mType); + explicit Monster(std::shared_ptr<MonsterType> mType); // non-copyable Monster(const Monster &) = delete; @@ -160,7 +159,7 @@ class Monster final : public Creature { } std::vector<CreatureIcon> getIcons() const override { - const auto creatureIcons = Creature::getIcons(); + auto creatureIcons = Creature::getIcons(); if (!creatureIcons.empty()) { return creatureIcons; } @@ -324,7 +323,7 @@ class Monster final : public Creature { return timeToChangeFiendish; } - const std::shared_ptr<MonsterType> getMonsterType() const { + std::shared_ptr<MonsterType> getMonsterType() const { return mType; } diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 190c14c76e5..3d457aa4d47 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -20,7 +20,7 @@ void MonsterType::loadLoot(const std::shared_ptr<MonsterType> monsterType, LootB if (lootBlock.childLoot.empty()) { bool isContainer = Item::items[lootBlock.id].isContainer(); if (isContainer) { - for (LootBlock child : lootBlock.childLoot) { + for (const LootBlock &child : lootBlock.childLoot) { lootBlock.childLoot.push_back(child); } } @@ -74,7 +74,7 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell return true; } - std::shared_ptr<CombatSpell> combatSpell = nullptr; + std::shared_ptr<CombatSpell> combatSpell; auto combatPtr = std::make_shared<Combat>(); @@ -156,7 +156,7 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell std::shared_ptr<ConditionOutfit> condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)->static_self_cast<ConditionOutfit>(); - if (spell->outfitMonster != "") { + if (!spell->outfitMonster.empty()) { condition->setLazyMonsterOutfit(spell->outfitMonster); } else if (spell->outfitItem > 0) { Outfit_t outfit; @@ -307,7 +307,7 @@ std::shared_ptr<MonsterType> Monsters::getMonsterType(const std::string &name, b } std::shared_ptr<MonsterType> Monsters::getMonsterTypeByRaceId(uint16_t raceId, bool isBoss /* = false*/) const { - const auto bossType = g_ioBosstiary().getMonsterTypeByBossRaceId(raceId); + auto bossType = g_ioBosstiary().getMonsterTypeByBossRaceId(raceId); if (isBoss && bossType) { return bossType; } diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 0fa2a3d88db..518befd8a70 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -30,7 +30,7 @@ struct spellBlock_t { ~spellBlock_t() = default; spellBlock_t(const spellBlock_t &other) = delete; spellBlock_t &operator=(const spellBlock_t &other) = delete; - spellBlock_t(spellBlock_t &&other) : + spellBlock_t(spellBlock_t &&other) noexcept : spell(other.spell), chance(other.chance), speed(other.speed), @@ -57,7 +57,7 @@ struct spellBlock_t { class MonsterType { struct MonsterInfo { - LuaScriptInterface* scriptInterface; + LuaScriptInterface* scriptInterface {}; std::map<CombatType_t, int32_t> elementMap; std::map<CombatType_t, int32_t> reflectMap; @@ -201,7 +201,7 @@ class MonsterType { return !info.bosstiaryClass.empty(); } - void loadLoot(const std::shared_ptr<MonsterType> monsterType, LootBlock lootblock); + void loadLoot(std::shared_ptr<MonsterType> monsterType, LootBlock lootblock); bool canSpawn(const Position &pos); }; @@ -271,8 +271,8 @@ class Monsters { std::shared_ptr<MonsterType> getMonsterType(const std::string &name, bool silent = false) const; std::shared_ptr<MonsterType> getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; - bool tryAddMonsterType(const std::string &name, const std::shared_ptr<MonsterType> mType); - bool deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spellBlock_t &sb, const std::string &description = ""); + bool tryAddMonsterType(const std::string &name, std::shared_ptr<MonsterType> mType); + bool deserializeSpell(std::shared_ptr<MonsterSpell> spell, spellBlock_t &sb, const std::string &description = ""); std::unique_ptr<LuaScriptInterface> scriptInterface; std::map<std::string, std::shared_ptr<MonsterType>> monsters; diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 21eab1a7c0b..ef5912c8052 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -39,7 +39,6 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { this->filemonstername = filemonstername; loaded = true; - uint32_t eventschedule = g_eventsScheduler().getSpawnMonsterSchedule(); std::string boostedNameGet = g_game().getBoostedMonsterName(); for (auto spawnMonsterNode : doc.child("monsters").children()) { @@ -89,12 +88,6 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { centerPos.z ); - int32_t boostedrate = 1; - - if (nameAttribute.value() == boostedNameGet) { - boostedrate = 2; - } - pugi::xml_attribute weightAttribute = childMonsterNode.attribute("weight"); uint32_t weight = 1; if (weightAttribute) { @@ -157,12 +150,9 @@ SpawnMonster::~SpawnMonster() { bool SpawnMonster::findPlayer(const Position &pos) { auto spectators = Spectators().find<Player>(pos); - for (const auto &spectator : spectators) { - if (!spectator->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { - return true; - } - } - return false; + return std::ranges::any_of(spectators, [](const auto &spectator) { + return !spectator->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters); + }); } bool SpawnMonster::isInSpawnMonsterZone(const Position &pos) { @@ -299,7 +289,7 @@ void SpawnMonster::cleanup() { } bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval, uint32_t weight /*= 1*/) { - std::string variant = ""; + std::string variant; for (const auto &zone : Zone::getZones(pos)) { if (!zone->getMonsterVariant().empty()) { variant = zone->getMonsterVariant() + "|"; @@ -343,7 +333,7 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire g_logger().error("[SpawnMonster] Monster {} already exists in spawn block at {}", name, pos.toString()); return false; } - if (monsterType->isBoss() && sb->monsterTypes.size() > 0) { + if (monsterType->isBoss() && !sb->monsterTypes.empty()) { g_logger().error("[SpawnMonster] Boss monster {} has been added to spawn block with other monsters. This is not allowed.", name); return false; } @@ -436,10 +426,8 @@ std::shared_ptr<MonsterType> spawnBlock_t::getMonsterType() const { } bool spawnBlock_t::hasBoss() const { - for (const auto &[monsterType, weight] : monsterTypes) { - if (monsterType->isBoss()) { - return true; - } - } - return false; + return std::ranges::any_of(monsterTypes, [](const auto &pair) { + const auto &[monsterType, weight] = pair; + return monsterType->isBoss(); + }); } diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 800b36fda5a..35b856d23d4 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -29,7 +29,7 @@ struct spawnBlock_t { class SpawnMonster { public: SpawnMonster(Position initPos, int32_t initRadius) : - centerPos(std::move(initPos)), radius(initRadius) { } + centerPos(initPos), radius(initRadius) { } ~SpawnMonster(); // non-copyable @@ -71,9 +71,9 @@ class SpawnMonster { uint32_t checkSpawnMonsterEvent = 0; static bool findPlayer(const Position &pos); - bool spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr<MonsterType> monsterType, bool startup = false); + bool spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, std::shared_ptr<MonsterType> monsterType, bool startup = false); void checkSpawnMonster(); - void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, const std::shared_ptr<MonsterType> monsterType, uint16_t interval, bool startup = false); + void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, std::shared_ptr<MonsterType> monsterType, uint16_t interval, bool startup = false); }; class SpawnsMonster { diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index d347ec6b66c..e445a58fcca 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -52,9 +52,6 @@ Npc::Npc(const std::shared_ptr<NpcType> &npcType) : } } -Npc::~Npc() { -} - void Npc::addList() { g_game().addNpc(static_self_cast<Npc>()); } @@ -248,7 +245,7 @@ void Npc::onPlayerBuyItem(std::shared_ptr<Player> player, uint16_t itemId, uint8 uint32_t shoppingBagSlots = 20; const ItemType &itemType = Item::items[itemId]; if (std::shared_ptr<Tile> tile = ignore ? player->getTile() : nullptr; tile) { - double slotsNedeed = 0; + double slotsNedeed; if (itemType.stackable) { slotsNedeed = inBackpacks ? std::ceil(std::ceil(static_cast<double>(amount) / itemType.stackSize) / shoppingBagSlots) : std::ceil(static_cast<double>(amount) / itemType.stackSize); } else { @@ -263,7 +260,7 @@ void Npc::onPlayerBuyItem(std::shared_ptr<Player> player, uint16_t itemId, uint8 uint32_t buyPrice = 0; const std::vector<ShopBlock> &shopVector = getShopItemVector(player->getGUID()); - for (ShopBlock shopBlock : shopVector) { + for (const ShopBlock &shopBlock : shopVector) { if (itemType.id == shopBlock.itemId && shopBlock.itemBuyPrice != 0) { buyPrice = shopBlock.itemBuyPrice; } @@ -308,7 +305,7 @@ void Npc::onPlayerBuyItem(std::shared_ptr<Player> player, uint16_t itemId, uint8 void Npc::onPlayerSellItem(std::shared_ptr<Player> player, uint16_t itemId, uint8_t subType, uint16_t amount, bool ignore) { uint64_t totalPrice = 0; - onPlayerSellItem(player, itemId, subType, amount, ignore, totalPrice); + onPlayerSellItem(std::move(player), itemId, subType, amount, ignore, totalPrice); } void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, uint64_t totalPrice) { @@ -324,7 +321,6 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u bool hasMore = false; uint64_t toSellCount = 0; phmap::flat_hash_map<uint16_t, uint16_t> toSell; - int64_t start = OTSYS_TIME(); for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { if (toSellCount >= 500) { hasMore = true; @@ -341,8 +337,8 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u toSellCount += item->getItemAmount(); } } - for (auto &[itemId, amount] : toSell) { - onPlayerSellItem(player, itemId, 0, amount, ignore, totalPrice, container); + for (auto &[m_itemId, amount] : toSell) { + onPlayerSellItem(player, m_itemId, 0, amount, ignore, totalPrice, container); } auto ss = std::stringstream(); if (totalPrice == 0) { @@ -377,7 +373,7 @@ void Npc::onPlayerSellItem(std::shared_ptr<Player> player, uint16_t itemId, uint uint32_t sellPrice = 0; const ItemType &itemType = Item::items[itemId]; const std::vector<ShopBlock> &shopVector = getShopItemVector(player->getGUID()); - for (ShopBlock shopBlock : shopVector) { + for (const ShopBlock &shopBlock : shopVector) { if (itemType.id == shopBlock.itemId && shopBlock.itemSellPrice != 0) { sellPrice = shopBlock.itemSellPrice; } @@ -387,7 +383,7 @@ void Npc::onPlayerSellItem(std::shared_ptr<Player> player, uint16_t itemId, uint } auto toRemove = amount; - for (auto item : player->getInventoryItemsFromId(itemId, ignore)) { + for (const auto &item : player->getInventoryItemsFromId(itemId, ignore)) { if (!item || item->getTier() > 0 || item->hasImbuements()) { continue; } @@ -452,7 +448,6 @@ void Npc::onPlayerCheckItem(std::shared_ptr<Player> player, uint16_t itemId, uin return; } - const ItemType &itemType = Item::items[itemId]; // onPlayerCheckItem(self, player, itemId, subType) CreatureCallback callback = CreatureCallback(npcType->info.scriptInterface, getNpc()); if (callback.startScriptInterface(npcType->info.playerLookEvent)) { @@ -515,7 +510,7 @@ void Npc::onThinkWalk(uint32_t interval) { } // If talking, no walking - if (playerInteractions.size() > 0) { + if (!playerInteractions.empty()) { walkTicks = 0; eventWalk = 0; return; diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 2d4e87ac7b8..7e8be84c7f1 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -27,7 +27,6 @@ class Npc final : public Creature { explicit Npc(const std::shared_ptr<NpcType> &npcType); Npc() = default; - ~Npc(); // Singleton - ensures we don't accidentally copy it Npc(const Npc &) = delete; @@ -68,7 +67,7 @@ class Npc final : public Creature { } void setName(std::string newName) { - npcType->name = newName; + npcType->name = std::move(newName); } CreatureType_t getType() const override { @@ -190,13 +189,13 @@ class Npc final : public Creature { std::shared_ptr<NpcType> npcType; std::shared_ptr<SpawnNpc> spawnNpc; - uint8_t speechBubble; + uint8_t speechBubble {}; uint32_t yellTicks = 0; uint32_t walkTicks = 0; uint32_t soundTicks = 0; - bool ignoreHeight; + bool ignoreHeight {}; phmap::flat_hash_set<std::shared_ptr<Player>> playerSpectators; Position masterPos; diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp index ecfd2750a94..6fe7f0b8320 100644 --- a/src/creatures/npcs/npcs.cpp +++ b/src/creatures/npcs/npcs.cpp @@ -84,16 +84,16 @@ void NpcType::loadShop(const std::shared_ptr<NpcType> &npcType, ShopBlock shopBl } // Check if the item already exists in the shop vector and ignore it - for (auto shopIterator = npcType->info.shopItemVector.begin(); shopIterator != npcType->info.shopItemVector.end(); ++shopIterator) { - if (*shopIterator == shopBlock) { - return; - } + if (std::any_of(npcType->info.shopItemVector.begin(), npcType->info.shopItemVector.end(), [&shopBlock](const auto &shopIterator) { + return shopIterator == shopBlock; + })) { + return; } if (shopBlock.childShop.empty()) { bool isContainer = iType.isContainer(); if (isContainer) { - for (ShopBlock child : shopBlock.childShop) { + for (const ShopBlock &child : shopBlock.childShop) { shopBlock.childShop.push_back(child); } } diff --git a/src/creatures/npcs/npcs.hpp b/src/creatures/npcs/npcs.hpp index 1a984abf2e8..ab5e700caea 100644 --- a/src/creatures/npcs/npcs.hpp +++ b/src/creatures/npcs/npcs.hpp @@ -25,7 +25,7 @@ class Shop { class NpcType : public SharedObject { struct NpcInfo { - LuaScriptInterface* scriptInterface; + LuaScriptInterface* scriptInterface {}; Outfit_t outfit = {}; RespawnType respawnType = {}; diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 968deca5331..a8be411dc6c 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -146,12 +146,9 @@ SpawnNpc::~SpawnNpc() { bool SpawnNpc::findPlayer(const Position &pos) { auto spectators = Spectators().find<Player>(pos); - for (const auto &spectator : spectators) { - if (!spectator->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByNpcs)) { - return true; - } - } - return false; + return std::ranges::any_of(spectators, [](const auto &spectator) { + return !spectator->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByNpcs); + }); } bool SpawnNpc::isInSpawnNpcZone(const Position &pos) { diff --git a/src/creatures/npcs/spawns/spawn_npc.hpp b/src/creatures/npcs/spawns/spawn_npc.hpp index d02a83e80c5..49eb3bc6f2b 100644 --- a/src/creatures/npcs/spawns/spawn_npc.hpp +++ b/src/creatures/npcs/spawns/spawn_npc.hpp @@ -26,7 +26,7 @@ struct spawnBlockNpc_t { class SpawnNpc : public SharedObject { public: SpawnNpc(Position initPos, int32_t initRadius) : - centerPos(std::move(initPos)), radius(initRadius) { } + centerPos(initPos), radius(initRadius) { } ~SpawnNpc(); // non-copyable @@ -91,7 +91,7 @@ class SpawnsNpc { } std::string setFileName(std::string setName) { - return fileName = setName; + return fileName = std::move(setName); } std::forward_list<std::shared_ptr<SpawnNpc>> &getSpawnNpcList() { diff --git a/src/creatures/players/achievement/player_achievement.hpp b/src/creatures/players/achievement/player_achievement.hpp index 7d141c96a16..d1073a9bf1e 100644 --- a/src/creatures/players/achievement/player_achievement.hpp +++ b/src/creatures/players/achievement/player_achievement.hpp @@ -13,7 +13,7 @@ class Player; class KV; struct Achievement { - Achievement() { } + Achievement() = default; std::string name; std::string description; diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp index 538519685c3..a922013f19b 100644 --- a/src/creatures/players/grouping/familiars.cpp +++ b/src/creatures/players/grouping/familiars.cpp @@ -14,6 +14,13 @@ #include "utils/pugicast.hpp" #include "utils/tools.hpp" +bool Familiars::reload() { + for (auto &familiarsVector : familiars) { + familiarsVector.clear(); + } + return loadFromXml(); +} + bool Familiars::loadFromXml() { pugi::xml_document doc; auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/familiars.xml"; @@ -47,13 +54,13 @@ bool Familiars::loadFromXml() { continue; } - familiars[vocation].emplace_back( + familiars[vocation].emplace_back(std::make_shared<Familiar>( familiarsNode.attribute("name").as_string(), pugi::cast<uint16_t>(lookTypeAttribute.value()), familiarsNode.attribute("premium").as_bool(), familiarsNode.attribute("unlocked").as_bool(true), familiarsNode.attribute("type").as_string() - ); + )); } for (uint16_t vocation = VOCATION_NONE; vocation <= VOCATION_LAST; ++vocation) { familiars[vocation].shrink_to_fit(); @@ -61,11 +68,12 @@ bool Familiars::loadFromXml() { return true; } -const Familiar* Familiars::getFamiliarByLookType(uint16_t vocation, uint16_t lookType) const { - for (const Familiar &familiar : familiars[vocation]) { - if (familiar.lookType == lookType) { - return &familiar; - } +std::shared_ptr<Familiar> Familiars::getFamiliarByLookType(uint16_t vocation, uint16_t lookType) const { + if (auto it = std::find_if(familiars[vocation].begin(), familiars[vocation].end(), [lookType](auto familiar_it) { + return familiar_it->lookType == lookType; + }); + it != familiars[vocation].end()) { + return *it; } return nullptr; } diff --git a/src/creatures/players/grouping/familiars.hpp b/src/creatures/players/grouping/familiars.hpp index aae9b34e227..8f553af9fbe 100644 --- a/src/creatures/players/grouping/familiars.hpp +++ b/src/creatures/players/grouping/familiars.hpp @@ -9,20 +9,42 @@ #pragma once -#include "declarations.hpp" #include "lib/di/container.hpp" +struct FamiliarEntry { + constexpr explicit FamiliarEntry(uint16_t initLookType) : + lookType(initLookType) { } + uint16_t lookType; +}; + +struct Familiar { + Familiar(std::string initName, uint16_t initLookType, bool initPremium, bool initUnlocked, std::string initType) : + name(std::move(initName)), lookType(initLookType), + premium(initPremium), unlocked(initUnlocked), + type(std::move(initType)) { } + + std::string name; + uint16_t lookType; + bool premium; + bool unlocked; + std::string type; +}; + class Familiars { public: static Familiars &getInstance() { return inject<Familiars>(); } + bool loadFromXml(); - const std::vector<Familiar> &getFamiliars(uint16_t vocation) const { + bool reload(); + + std::vector<std::shared_ptr<Familiar>> &getFamiliars(uint16_t vocation) { return familiars[vocation]; } - const Familiar* getFamiliarByLookType(uint16_t vocation, uint16_t lookType) const; + + [[nodiscard]] std::shared_ptr<Familiar> getFamiliarByLookType(uint16_t vocation, uint16_t lookType) const; private: - std::vector<Familiar> familiars[VOCATION_LAST + 1]; + std::vector<std::shared_ptr<Familiar>> familiars[VOCATION_LAST + 1]; }; diff --git a/src/creatures/players/grouping/groups.cpp b/src/creatures/players/grouping/groups.cpp index 40dd13bc385..27ae68d9fd4 100644 --- a/src/creatures/players/grouping/groups.cpp +++ b/src/creatures/players/grouping/groups.cpp @@ -93,17 +93,18 @@ bool Groups::load() { // Parsing group flags parseGroupFlags(group, groupNode); - groups_vector.push_back(group); + groups_vector.emplace_back(std::make_shared<Group>(group)); } groups_vector.shrink_to_fit(); return true; } -Group* Groups::getGroup(uint16_t id) { - for (Group &group : groups_vector) { - if (group.id == id) { - return &group; - } +std::shared_ptr<Group> Groups::getGroup(uint16_t id) { + if (auto it = std::find_if(groups_vector.begin(), groups_vector.end(), [id](auto group_it) { + return group_it->id == id; + }); + it != groups_vector.end()) { + return *it; } return nullptr; } diff --git a/src/creatures/players/grouping/groups.hpp b/src/creatures/players/grouping/groups.hpp index 3d74564173d..d76914e2632 100644 --- a/src/creatures/players/grouping/groups.hpp +++ b/src/creatures/players/grouping/groups.hpp @@ -26,11 +26,11 @@ class Groups { static PlayerFlags_t getFlagFromNumber(uint8_t value); bool reload() const; bool load(); - Group* getGroup(uint16_t id); - std::vector<Group> &getGroups() { + std::shared_ptr<Group> getGroup(uint16_t id); + std::vector<std::shared_ptr<Group>> &getGroups() { return groups_vector; } private: - std::vector<Group> groups_vector; + std::vector<std::shared_ptr<Group>> groups_vector; }; diff --git a/src/creatures/players/grouping/guild.cpp b/src/creatures/players/grouping/guild.cpp index aca512f28e3..bbd662d1a1d 100644 --- a/src/creatures/players/grouping/guild.cpp +++ b/src/creatures/players/grouping/guild.cpp @@ -14,7 +14,7 @@ void Guild::addMember(const std::shared_ptr<Player> &player) { membersOnline.push_back(player); - for (auto member : getMembersOnline()) { + for (const auto &member : getMembersOnline()) { g_game().updatePlayerHelpers(member); } } diff --git a/src/creatures/players/grouping/guild.hpp b/src/creatures/players/grouping/guild.hpp index 834eb03252c..82b0a7367ea 100644 --- a/src/creatures/players/grouping/guild.hpp +++ b/src/creatures/players/grouping/guild.hpp @@ -32,9 +32,10 @@ class Guild : public Bankable { void addMember(const std::shared_ptr<Player> &player); void removeMember(const std::shared_ptr<Player> &player); - bool isGuild() { + bool isGuild() override { return true; } + void setOnline(bool value) override { online = value; } diff --git a/src/creatures/players/grouping/party.cpp b/src/creatures/players/grouping/party.cpp index c7d6fd48363..76f1d955f10 100644 --- a/src/creatures/players/grouping/party.cpp +++ b/src/creatures/players/grouping/party.cpp @@ -7,6 +7,8 @@ * Website: https://docs.opentibiabr.com/ */ +#include <utility> + #include "pch.hpp" #include "creatures/players/grouping/party.hpp" @@ -47,23 +49,23 @@ void Party::disband() { currentLeader->sendCreatureSkull(currentLeader); currentLeader->sendTextMessage(MESSAGE_PARTY_MANAGEMENT, "Your party has been disbanded."); - for (auto invitee : getInvitees()) { + for (const auto &invitee : getInvitees()) { invitee->removePartyInvitation(getParty()); currentLeader->sendCreatureShield(invitee); } inviteList.clear(); auto members = getMembers(); - for (auto member : members) { + for (const auto &member : members) { member->setParty(nullptr); member->sendClosePrivate(CHANNEL_PARTY); member->sendTextMessage(MESSAGE_PARTY_MANAGEMENT, "Your party has been disbanded."); } - for (auto member : members) { + for (const auto &member : members) { g_game().updatePlayerShield(member); - for (auto otherMember : members) { + for (const auto &otherMember : members) { otherMember->sendCreatureSkull(member); } @@ -132,7 +134,7 @@ bool Party::leaveParty(std::shared_ptr<Player> player) { g_game().updatePlayerShield(player); g_game().updatePlayerHelpers(player); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->sendCreatureSkull(player); player->sendPlayerPartyIcons(member); member->sendPartyCreatureUpdate(player); @@ -185,12 +187,12 @@ bool Party::passPartyLeadership(std::shared_ptr<Player> player) { updateSharedExperience(); updateTrackerAnalyzer(); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->sendPartyCreatureShield(oldLeader); member->sendPartyCreatureShield(player); } - for (auto invitee : getInvitees()) { + for (const auto &invitee : getInvitees()) { invitee->sendCreatureShield(oldLeader); invitee->sendCreatureShield(player); } @@ -231,7 +233,7 @@ bool Party::joinParty(const std::shared_ptr<Player> &player) { g_game().updatePlayerShield(player); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->sendCreatureSkull(player); member->sendPlayerPartyIcons(player); player->sendPlayerPartyIcons(member); @@ -282,7 +284,7 @@ bool Party::removeInvite(const std::shared_ptr<Player> &player, bool removeFromP if (empty()) { disband(); } else { - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { g_game().updatePlayerHelpers(member); } @@ -332,7 +334,7 @@ bool Party::invitePlayer(const std::shared_ptr<Player> &player) { inviteList.push_back(player); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { g_game().updatePlayerHelpers(member); } @@ -359,8 +361,8 @@ void Party::updateAllPartyIcons() { return; } auto members = getMembers(); - for (auto member : members) { - for (auto otherMember : members) { + for (const auto &member : members) { + for (const auto &otherMember : members) { member->sendPartyCreatureShield(otherMember); } @@ -376,14 +378,14 @@ void Party::broadcastPartyMessage(MessageClasses msgClass, const std::string &ms if (!leader) { return; } - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->sendTextMessage(msgClass, msg); } leader->sendTextMessage(msgClass, msg); if (sendToInvitations) { - for (auto invitee : getInvitees()) { + for (const auto &invitee : getInvitees()) { invitee->sendTextMessage(msgClass, msg); } } @@ -454,14 +456,14 @@ void Party::shareExperience(uint64_t experience, std::shared_ptr<Creature> targe g_events().eventPartyOnShareExperience(getParty(), shareExperience); g_callbacks().executeCallback(EventCallback_t::partyOnShareExperience, &EventCallback::partyOnShareExperience, getParty(), shareExperience); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->onGainSharedExperience(shareExperience, target); } leader->onGainSharedExperience(shareExperience, target); } bool Party::canUseSharedExperience(std::shared_ptr<Player> player) { - return getMemberSharedExperienceStatus(player) == SHAREDEXP_OK; + return getMemberSharedExperienceStatus(std::move(player)) == SHAREDEXP_OK; } SharedExpStatus_t Party::getMemberSharedExperienceStatus(std::shared_ptr<Player> player) { @@ -473,7 +475,6 @@ SharedExpStatus_t Party::getMemberSharedExperienceStatus(std::shared_ptr<Player> return SHAREDEXP_EMPTYPARTY; } - uint32_t highestLevel = getHighestLevel(); uint32_t minLevel = getMinLevel(); if (player->getLevel() < minLevel) { return SHAREDEXP_LEVELDIFFTOOLARGE; @@ -502,7 +503,7 @@ uint32_t Party::getHighestLevel() { } uint32_t highestLevel = leader->getLevel(); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { if (member->getLevel() > highestLevel) { highestLevel = member->getLevel(); } @@ -520,7 +521,7 @@ uint32_t Party::getLowestLevel() { return 0; } uint32_t lowestLevel = leader->getLevel(); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { if (member->getLevel() < lowestLevel) { lowestLevel = member->getLevel(); } @@ -551,7 +552,7 @@ SharedExpStatus_t Party::getSharedExperienceStatus() { return leaderStatus; } - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { SharedExpStatus_t memberStatus = getMemberSharedExperienceStatus(member); if (memberStatus != SHAREDEXP_OK) { return memberStatus; @@ -620,7 +621,7 @@ void Party::updatePlayerStatus(std::shared_ptr<Player> player) { } int32_t maxDistance = g_configManager().getNumber(PARTY_LIST_MAX_DISTANCE, __FUNCTION__); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { bool condition = (maxDistance == 0 || (Position::getDistanceX(player->getPosition(), member->getPosition()) <= maxDistance && Position::getDistanceY(player->getPosition(), member->getPosition()) <= maxDistance)); if (condition) { showPlayerStatus(player, member, true); @@ -644,7 +645,7 @@ void Party::updatePlayerStatus(std::shared_ptr<Player> player, const Position &o int32_t maxDistance = g_configManager().getNumber(PARTY_LIST_MAX_DISTANCE, __FUNCTION__); if (maxDistance != 0) { - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { bool condition1 = (Position::getDistanceX(oldPos, member->getPosition()) <= maxDistance && Position::getDistanceY(oldPos, member->getPosition()) <= maxDistance); bool condition2 = (Position::getDistanceX(newPos, member->getPosition()) <= maxDistance && Position::getDistanceY(newPos, member->getPosition()) <= maxDistance); if (condition1 && !condition2) { @@ -673,7 +674,7 @@ void Party::updatePlayerHealth(std::shared_ptr<Player> player, std::shared_ptr<C int32_t maxDistance = g_configManager().getNumber(PARTY_LIST_MAX_DISTANCE, __FUNCTION__); auto playerPosition = player->getPosition(); auto leaderPosition = leader->getPosition(); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { auto memberPosition = member->getPosition(); bool condition = (maxDistance == 0 || (Position::getDistanceX(playerPosition, memberPosition) <= maxDistance && Position::getDistanceY(playerPosition, memberPosition) <= maxDistance)); if (condition) { @@ -693,7 +694,7 @@ void Party::updatePlayerMana(std::shared_ptr<Player> player, uint8_t manaPercent } int32_t maxDistance = g_configManager().getNumber(PARTY_LIST_MAX_DISTANCE, __FUNCTION__); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { bool condition = (maxDistance == 0 || (Position::getDistanceX(player->getPosition(), member->getPosition()) <= maxDistance && Position::getDistanceY(player->getPosition(), member->getPosition()) <= maxDistance)); if (condition) { member->sendPartyPlayerMana(player, manaPercent); @@ -712,7 +713,7 @@ void Party::updatePlayerVocation(std::shared_ptr<Player> player) { } int32_t maxDistance = g_configManager().getNumber(PARTY_LIST_MAX_DISTANCE, __FUNCTION__); - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { bool condition = (maxDistance == 0 || (Position::getDistanceX(player->getPosition(), member->getPosition()) <= maxDistance && Position::getDistanceY(player->getPosition(), member->getPosition()) <= maxDistance)); if (condition) { member->sendPartyPlayerVocation(player); @@ -730,7 +731,7 @@ void Party::updateTrackerAnalyzer() { return; } - for (auto member : getMembers()) { + for (const auto &member : getMembers()) { member->updatePartyTrackerAnalyzer(); } diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index 5da0f4e0647..10663a278bf 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -105,7 +105,7 @@ class Party : public SharedObject { void reloadPrices(); std::shared_ptr<PartyAnalyzer> getPlayerPartyAnalyzerStruct(uint32_t playerId) const { - if (auto it = std::find_if(membersData.begin(), membersData.end(), [playerId](const std::shared_ptr<PartyAnalyzer> preyIt) { + if (auto it = std::find_if(membersData.begin(), membersData.end(), [playerId](const std::shared_ptr<PartyAnalyzer> &preyIt) { return preyIt->id == playerId; }); it != membersData.end()) { diff --git a/src/creatures/players/grouping/team_finder.hpp b/src/creatures/players/grouping/team_finder.hpp index 123ce7c07d8..9fafbd03227 100644 --- a/src/creatures/players/grouping/team_finder.hpp +++ b/src/creatures/players/grouping/team_finder.hpp @@ -1,3 +1,5 @@ +#include <utility> + /** * Canary - A free and open-source MMORPG server emulator * Copyright (©) 2019-2024 OpenTibiaBR <opentibiabr@outlook.com> @@ -31,7 +33,7 @@ class TeamFinder { hunt_area(initHunt_area), questID(initQuestID), leaderGuid(initLeaderGuid), - membersMap(initMembersMap) { } + membersMap(std::move(initMembersMap)) { } virtual ~TeamFinder() = default; uint16_t minLevel = 0; diff --git a/src/creatures/players/highscore_category.hpp b/src/creatures/players/highscore_category.hpp index b387199f315..71da6b76ec6 100644 --- a/src/creatures/players/highscore_category.hpp +++ b/src/creatures/players/highscore_category.hpp @@ -10,8 +10,8 @@ #pragma once struct HighscoreCategory { - HighscoreCategory(const std::string &name, uint8_t id) : - m_name(name), + HighscoreCategory(std::string name, uint8_t id) : + m_name(std::move(name)), m_id(id) { } std::string m_name; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 78478953ade..3182607ecba 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -56,7 +56,7 @@ Player::Player(ProtocolGame_ptr p) : } Player::~Player() { - for (std::shared_ptr<Item> item : inventory) { + for (const std::shared_ptr<Item> &item : inventory) { if (item) { item->resetParent(); item->stopDecaying(); @@ -1335,7 +1335,7 @@ void Player::getRewardList(std::vector<uint64_t> &rewards) const { std::vector<std::shared_ptr<Item>> Player::getRewardsFromContainer(std::shared_ptr<Container> container) const { std::vector<std::shared_ptr<Item>> rewardItemsVector; if (container) { - for (auto item : container->getItems(false)) { + for (const auto &item : container->getItems(false)) { if (item->getID() == ITEM_REWARD_CONTAINER) { auto items = getRewardsFromContainer(item->getContainer()); rewardItemsVector.insert(rewardItemsVector.end(), items.begin(), items.end()); @@ -1754,7 +1754,7 @@ void Player::onCreatureAppear(std::shared_ptr<Creature> creature, bool isLogin) offlineTime = 0; } - for (std::shared_ptr<Condition> condition : getMuteConditions()) { + for (const std::shared_ptr<Condition> &condition : getMuteConditions()) { condition->setTicks(condition->getTicks() - (offlineTime * 1000)); if (condition->getTicks() <= 0) { removeCondition(condition); @@ -2232,7 +2232,7 @@ uint32_t Player::isMuted() const { } int32_t muteTicks = 0; - for (std::shared_ptr<Condition> condition : conditions) { + for (const std::shared_ptr<Condition> &condition : conditions) { if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) { muteTicks = condition->getTicks(); } @@ -2401,7 +2401,7 @@ void Player::addExperience(std::shared_ptr<Creature> target, uint64_t exp, bool if (!spectators.empty()) { message.type = MESSAGE_EXPERIENCE_OTHERS; message.text = getName() + " gained " + expString; - for (std::shared_ptr<Creature> spectator : spectators) { + for (const std::shared_ptr<Creature> &spectator : spectators) { spectator->getPlayer()->sendTextMessage(message); } } @@ -2494,7 +2494,7 @@ void Player::removeExperience(uint64_t exp, bool sendText /* = false*/) { if (!spectators.empty()) { message.type = MESSAGE_EXPERIENCE_OTHERS; message.text = getName() + " lost " + expString; - for (std::shared_ptr<Creature> spectator : spectators) { + for (const std::shared_ptr<Creature> &spectator : spectators) { spectator->getPlayer()->sendTextMessage(message); } } @@ -3553,7 +3553,7 @@ std::shared_ptr<Cylinder> Player::queryDestination(int32_t &index, const std::sh n--; } - for (std::shared_ptr<Item> tmpContainerItem : tmpContainer->getItemList()) { + for (const std::shared_ptr<Item> &tmpContainerItem : tmpContainer->getItemList()) { if (std::shared_ptr<Container> subContainer = tmpContainerItem->getContainer()) { containers.push_back(subContainer); } @@ -3564,7 +3564,7 @@ std::shared_ptr<Cylinder> Player::queryDestination(int32_t &index, const std::sh uint32_t n = 0; - for (std::shared_ptr<Item> tmpItem : tmpContainer->getItemList()) { + for (const std::shared_ptr<Item> &tmpItem : tmpContainer->getItemList()) { if (tmpItem == tradeItem) { continue; } @@ -3772,7 +3772,7 @@ uint32_t Player::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) con void Player::stashContainer(StashContainerList itemDict) { StashItemList stashItemDict; // ItemID - Count - for (auto it_dict : itemDict) { + for (const auto &it_dict : itemDict) { stashItemDict[(it_dict.first)->getID()] = it_dict.second; } @@ -3792,7 +3792,7 @@ void Player::stashContainer(StashContainerList itemDict) { uint32_t totalStowed = 0; std::ostringstream retString; uint16_t refreshDepotSearchOnItem = 0; - for (auto stashIterator : itemDict) { + for (const auto &stashIterator : itemDict) { uint16_t iteratorCID = (stashIterator.first)->getID(); if (g_game().internalRemoveItem(stashIterator.first, stashIterator.second) == RETURNVALUE_NOERROR) { addItemOnStash(iteratorCID, stashIterator.second); @@ -3878,7 +3878,7 @@ bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool Player::hasItemCountById(uint16_t itemId, uint32_t itemAmount, bool checkStash) const { uint32_t newCount = 0; // Check items from inventory - for (const auto item : getAllInventoryItems()) { + for (const auto &item : getAllInventoryItems()) { if (!item || item->getID() != itemId) { continue; } @@ -3909,7 +3909,7 @@ bool Player::removeItemCountById(uint16_t itemId, uint32_t itemAmount, bool remo uint32_t amountToRemove = itemAmount; // Check items from inventory - for (auto item : getAllInventoryItems()) { + for (const auto &item : getAllInventoryItems()) { if (!item || item->getID() != itemId) { continue; } @@ -4171,14 +4171,14 @@ std::vector<std::shared_ptr<Item>> Player::getEquippedItems() const { } std::map<uint32_t, uint32_t> &Player::getAllItemTypeCount(std::map<uint32_t, uint32_t> &countMap) const { - for (const auto item : getAllInventoryItems()) { + for (const auto &item : getAllInventoryItems()) { countMap[static_cast<uint32_t>(item->getID())] += Item::countByType(item, -1); } return countMap; } std::map<uint16_t, uint16_t> &Player::getAllSaleItemIdAndCount(std::map<uint16_t, uint16_t> &countMap) const { - for (const auto item : getAllInventoryItems(false, true)) { + for (const auto &item : getAllInventoryItems(false, true)) { countMap[item->getID()] += item->getItemCount(); } @@ -4186,7 +4186,7 @@ std::map<uint16_t, uint16_t> &Player::getAllSaleItemIdAndCount(std::map<uint16_t } void Player::getAllItemTypeCountAndSubtype(std::map<uint32_t, uint32_t> &countMap) const { - for (const auto item : getAllInventoryItems()) { + for (const auto &item : getAllInventoryItems()) { uint16_t itemId = item->getID(); if (Item::items[itemId].isFluidContainer()) { countMap[static_cast<uint32_t>(itemId) | (item->getAttribute<uint32_t>(ItemAttribute_t::FLUIDTYPE)) << 16] += item->getItemCount(); @@ -4263,7 +4263,7 @@ void Player::postAddNotification(std::shared_ptr<Thing> thing, std::shared_ptr<C } } - for (std::shared_ptr<Container> container : containers) { + for (const std::shared_ptr<Container> &container : containers) { autoCloseContainers(container); } } @@ -4570,7 +4570,7 @@ void Player::updateItemsLight(bool internal /*=false*/) { LightInfo curLight = item->getLightInfo(); if (curLight.level > maxLight.level) { - maxLight = std::move(curLight); + maxLight = curLight; } } } @@ -5101,7 +5101,7 @@ bool Player::canFamiliar(uint16_t lookType) const { return true; } - const Familiar* familiar = Familiars::getInstance().getFamiliarByLookType(getVocationId(), lookType); + const auto &familiar = Familiars::getInstance().getFamiliarByLookType(getVocationId(), lookType); if (!familiar) { return false; } @@ -5142,24 +5142,24 @@ bool Player::removeFamiliar(uint16_t lookType) { return false; } -bool Player::getFamiliar(const Familiar &familiar) const { +bool Player::getFamiliar(const std::shared_ptr<Familiar> &familiar) const { if (group->access) { return true; } - if (familiar.premium && !isPremium()) { + if (familiar->premium && !isPremium()) { return false; } for (const FamiliarEntry &familiarEntry : familiars) { - if (familiarEntry.lookType != familiar.lookType) { + if (familiarEntry.lookType != familiar->lookType) { continue; } return true; } - if (!familiar.unlocked) { + if (!familiar->unlocked) { return false; } @@ -5514,7 +5514,7 @@ void Player::setTibiaCoins(int32_t v) { int32_t Player::getCleavePercent(bool useCharges) const { int32_t result = cleavePercent; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &it = Item::items[item->getID()]; if (!it.abilities) { continue; @@ -5540,7 +5540,7 @@ int32_t Player::getPerfectShotDamage(uint8_t range, bool useCharges) const { result = it->second; } - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5564,7 +5564,7 @@ int32_t Player::getPerfectShotDamage(uint8_t range, bool useCharges) const { int32_t Player::getSpecializedMagicLevel(CombatType_t combat, bool useCharges) const { int32_t result = specializedMagicLevel[combatTypeToIndex(combat)]; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5585,7 +5585,7 @@ int32_t Player::getSpecializedMagicLevel(CombatType_t combat, bool useCharges) c int32_t Player::getMagicShieldCapacityFlat(bool useCharges) const { int32_t result = magicShieldCapacityFlat; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5606,7 +5606,7 @@ int32_t Player::getMagicShieldCapacityFlat(bool useCharges) const { int32_t Player::getMagicShieldCapacityPercent(bool useCharges) const { int32_t result = magicShieldCapacityPercent; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5627,7 +5627,7 @@ int32_t Player::getMagicShieldCapacityPercent(bool useCharges) const { int32_t Player::getReflectPercent(CombatType_t combat, bool useCharges) const { int32_t result = reflectPercent[combatTypeToIndex(combat)]; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5648,7 +5648,7 @@ int32_t Player::getReflectPercent(CombatType_t combat, bool useCharges) const { int32_t Player::getReflectFlat(CombatType_t combat, bool useCharges) const { int32_t result = reflectFlat[combatTypeToIndex(combat)]; - for (const auto item : getEquippedItems()) { + for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; @@ -5855,7 +5855,7 @@ void Player::setCurrentMount(uint8_t mount) { bool Player::hasAnyMount() const { const auto mounts = g_game().mounts.getMounts(); - for (const auto mount : mounts) { + for (const auto &mount : mounts) { if (hasMount(mount)) { return true; } @@ -5866,7 +5866,7 @@ bool Player::hasAnyMount() const { uint8_t Player::getRandomMountId() const { std::vector<uint8_t> playerMounts; const auto mounts = g_game().mounts.getMounts(); - for (const auto mount : mounts) { + for (const auto &mount : mounts) { if (hasMount(mount)) { playerMounts.push_back(mount->id); } @@ -6253,7 +6253,7 @@ uint64_t Player::getMoney() const { size_t i = 0; while (i < containers.size()) { std::shared_ptr<Container> container = containers[i++]; - for (std::shared_ptr<Item> item : container->getItemList()) { + for (const std::shared_ptr<Item> &item : container->getItemList()) { std::shared_ptr<Container> tmpContainer = item->getContainer(); if (tmpContainer) { containers.push_back(tmpContainer); @@ -6270,7 +6270,7 @@ std::pair<uint64_t, uint64_t> Player::getForgeSliversAndCores() const { uint64_t coreCount = 0; // Check items from inventory - for (const auto item : getAllInventoryItems()) { + for (const auto &item : getAllInventoryItems()) { if (!item) { continue; } @@ -6313,7 +6313,7 @@ size_t Player::getMaxDepotItems() const { std::forward_list<std::shared_ptr<Condition>> Player::getMuteConditions() const { std::forward_list<std::shared_ptr<Condition>> muteConditions; - for (std::shared_ptr<Condition> condition : conditions) { + for (const std::shared_ptr<Condition> &condition : conditions) { if (condition->getTicks() <= 0) { continue; } @@ -6519,7 +6519,7 @@ void sendStowItems(const std::shared_ptr<Item> &item, const std::shared_ptr<Item } if (auto container = stowItem->getContainer()) { - for (auto stowable_it : container->getStowableItems()) { + for (const auto &stowable_it : container->getStowableItems()) { if ((stowable_it.first)->getID() == item->getID()) { itemDict.push_back(stowable_it); } @@ -6597,14 +6597,14 @@ void Player::openPlayerContainers() { if (itemContainer) { auto cid = item->getAttribute<int64_t>(ItemAttribute_t::OPENCONTAINER); if (cid > 0) { - openContainersList.emplace_back(std::make_pair(cid, itemContainer)); + openContainersList.emplace_back(cid, itemContainer); } for (ContainerIterator it = itemContainer->iterator(); it.hasNext(); it.advance()) { std::shared_ptr<Container> subContainer = (*it)->getContainer(); if (subContainer) { auto subcid = (*it)->getAttribute<uint8_t>(ItemAttribute_t::OPENCONTAINER); if (subcid > 0) { - openContainersList.emplace_back(std::make_pair(subcid, subContainer)); + openContainersList.emplace_back(subcid, subContainer); } } } @@ -6822,7 +6822,7 @@ void Player::requestDepotItems() { return; } - for (std::shared_ptr<Item> locker : depotLocker->getItemList()) { + for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty()) { continue; @@ -6888,7 +6888,7 @@ void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier) { return; } - for (std::shared_ptr<Item> locker : depotLocker->getItemList()) { + for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty()) { continue; @@ -6925,7 +6925,7 @@ void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool } std::vector<std::shared_ptr<Item>> itemsVector; - for (std::shared_ptr<Item> locker : depotLocker->getItemList()) { + for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty() || // Retrieve from inbox. @@ -6948,7 +6948,7 @@ void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool } ReturnValue ret = RETURNVALUE_NOERROR; - for (std::shared_ptr<Item> item : itemsVector) { + for (const std::shared_ptr<Item> &item : itemsVector) { // First lets try to retrieve the item to the stash retrieve container. if (g_game().tryRetrieveStashItems(static_self_cast<Player>(), item)) { continue; @@ -6994,7 +6994,7 @@ std::shared_ptr<Item> Player::getItemFromDepotSearch(uint16_t itemId, const Posi } uint8_t index = 0; - for (std::shared_ptr<Item> locker : depotLocker->getItemList()) { + for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty() || (c->isInbox() && pos.y != 0x21) || // From inbox. (!c->isInbox() && pos.y != 0x20)) { // From depot. @@ -7071,7 +7071,7 @@ std::pair<std::vector<std::shared_ptr<Item>>, uint16_t> Player::getLockerItemsAn std::vector<std::shared_ptr<Item>> lockerItems; auto [itemVector, itemMap] = requestLockerItems(depotLocker, false, tier); uint16_t totalCount = 0; - for (auto item : itemVector) { + for (const auto &item : itemVector) { if (!item || item->getID() != itemId) { continue; } @@ -7117,7 +7117,7 @@ bool Player::saySpell( int32_t valueEmote = 0; // Send to client - for (std::shared_ptr<Creature> spectator : spectators) { + for (const std::shared_ptr<Creature> &spectator : spectators) { if (std::shared_ptr<Player> tmpPlayer = spectator->getPlayer()) { if (g_configManager().getBoolean(EMOTE_SPELLS, __FUNCTION__)) { valueEmote = tmpPlayer->getStorageValue(STORAGEVALUE_EMOTE); @@ -7133,7 +7133,7 @@ bool Player::saySpell( } // Execute lua event method - for (std::shared_ptr<Creature> spectator : spectators) { + for (const std::shared_ptr<Creature> &spectator : spectators) { auto tmpPlayer = spectator->getPlayer(); if (!tmpPlayer) { continue; @@ -7777,7 +7777,7 @@ void Player::closeAllExternalContainers() { } } - for (std::shared_ptr<Container> container : containerToClose) { + for (const std::shared_ptr<Container> &container : containerToClose) { autoCloseContainers(container); } } @@ -7898,7 +7898,7 @@ bool Player::setAccount(uint32_t accountId) { } uint8_t Player::getAccountType() const { - return account ? account->getAccountType() : AccountType::ACCOUNT_TYPE_NORMAL; + return static_cast<uint8_t>(account ? account->getAccountType() : static_cast<uint8_t>(AccountType::ACCOUNT_TYPE_NORMAL)); } uint32_t Player::getAccountId() const { @@ -7941,7 +7941,7 @@ void Player::parseAttackRecvHazardSystem(CombatDamage &damage, std::shared_ptr<M auto points = getHazardSystemPoints(); if (m_party) { - for (const auto partyMember : m_party->getMembers()) { + for (const auto &partyMember : m_party->getMembers()) { if (partyMember && partyMember->getHazardSystemPoints() < points) { points = partyMember->getHazardSystemPoints(); } @@ -8000,7 +8000,7 @@ void Player::parseAttackDealtHazardSystem(CombatDamage &damage, std::shared_ptr< auto points = getHazardSystemPoints(); if (m_party) { - for (const auto partyMember : m_party->getMembers()) { + for (const auto &partyMember : m_party->getMembers()) { if (partyMember && partyMember->getHazardSystemPoints() < points) { points = partyMember->getHazardSystemPoints(); } @@ -8089,7 +8089,7 @@ void Player::sendLootMessage(const std::string &message) const { if (auto partyLeader = party->getLeader()) { partyLeader->sendTextMessage(MESSAGE_LOOT, message); } - for (const auto partyMember : party->getMembers()) { + for (const auto &partyMember : party->getMembers()) { if (partyMember) { partyMember->sendTextMessage(MESSAGE_LOOT, message); } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index b3bdd875b20..f61e33a37ef 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -517,10 +517,10 @@ class Player final : public Creature, public Cylinder, public Bankable { void genReservedStorageRange(); - void setGroup(Group* newGroup) { + void setGroup(std::shared_ptr<Group> newGroup) { group = newGroup; } - Group* getGroup() const { + std::shared_ptr<Group> getGroup() const { return group; } @@ -1041,7 +1041,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool canFamiliar(uint16_t lookType) const; void addFamiliar(uint16_t lookType); bool removeFamiliar(uint16_t lookType); - bool getFamiliar(const Familiar &familiar) const; + bool getFamiliar(const std::shared_ptr<Familiar> &familiar) const; void setFamiliarLooktype(uint16_t familiarLooktype) { this->defaultOutfit.lookFamiliarsType = familiarLooktype; } @@ -2800,7 +2800,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::shared_ptr<BedItem> bedItem = nullptr; std::shared_ptr<Guild> guild = nullptr; GuildRank_ptr guildRank; - Group* group = nullptr; + std::shared_ptr<Group> group = nullptr; std::shared_ptr<Inbox> inbox; std::shared_ptr<Item> imbuingItem = nullptr; std::shared_ptr<Item> tradeItem = nullptr; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 8101f51ae42..c4fdbf5e169 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -283,7 +283,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) return true; } } else if (slot == WheelSlots_t::SLOT_GREEN_50) { - return recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) || true; + return (recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot))) || true; } // Red quadrant @@ -410,7 +410,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) return true; } } else if (slot == WheelSlots_t::SLOT_RED_50) { - return recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) || true; + return (recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot))) || true; } // Purple quadrant @@ -537,7 +537,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) return true; } } else if (slot == WheelSlots_t::SLOT_PURPLE_50) { - return recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) || true; + return (recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot))) || true; } // Blue quadrant @@ -664,7 +664,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) return true; } } else if (slot == WheelSlots_t::SLOT_BLUE_50) { - return recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) || true; + return (recursive && (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot))) || true; } return false; @@ -1603,7 +1603,7 @@ void PlayerWheel::registerPlayerBonusData() { setSpellInstant("Avatar of Storm", false); } - for (const auto spell : m_playerBonusData.spells) { + for (const auto &spell : m_playerBonusData.spells) { upgradeSpell(spell); } @@ -1759,7 +1759,7 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus auto &spellsVector = bonusData.spells; if (!spellsVector.empty()) { g_logger().debug("Spells:"); - for (const auto spell : bonusData.spells) { + for (const auto &spell : bonusData.spells) { g_logger().debug(" {}", spell); } } @@ -2523,7 +2523,7 @@ void PlayerWheel::reduceAllSpellsCooldownTimer(int32_t value) { } void PlayerWheel::resetUpgradedSpells() { - for (const auto spell : m_learnedSpellsSelected) { + for (const auto &spell : m_learnedSpellsSelected) { if (m_player.hasLearnedInstantSpell(spell)) { m_player.forgetInstantSpell(spell); } diff --git a/src/creatures/players/wheel/wheel_gems.hpp b/src/creatures/players/wheel/wheel_gems.hpp index 762684cb383..668d8fe05db 100644 --- a/src/creatures/players/wheel/wheel_gems.hpp +++ b/src/creatures/players/wheel/wheel_gems.hpp @@ -174,7 +174,7 @@ class GemModifierStrategy { public: explicit GemModifierStrategy(PlayerWheel &wheel) : m_wheel(wheel) { } - virtual ~GemModifierStrategy() { } + virtual ~GemModifierStrategy() = default; virtual void execute() = 0; protected: @@ -211,7 +211,7 @@ class GemModifierStatStrategy : public GemModifierStrategy { class GemModifierRevelationStrategy : public GemModifierStrategy { public: - explicit GemModifierRevelationStrategy(PlayerWheel &wheel, WheelGemAffinity_t affinity, uint16_t value) : + explicit GemModifierRevelationStrategy(PlayerWheel &wheel, WheelGemAffinity_t affinity, [[maybe_unused]] uint16_t value) : GemModifierStrategy(wheel), m_affinity(affinity) { } @@ -224,9 +224,9 @@ class GemModifierRevelationStrategy : public GemModifierStrategy { class GemModifierSpellBonusStrategy : public GemModifierStrategy { public: - explicit GemModifierSpellBonusStrategy(PlayerWheel &wheel, const std::string &spellName, WheelSpells::Bonus bonus) : + explicit GemModifierSpellBonusStrategy(PlayerWheel &wheel, std::string spellName, WheelSpells::Bonus bonus) : GemModifierStrategy(wheel), - m_spellName(spellName), + m_spellName(std::move(spellName)), m_bonus(bonus) { } void execute() override; @@ -256,7 +256,7 @@ class WheelModifierContext { Vocation_t m_vocation; }; -static int32_t getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { +[[maybe_unused]] static int32_t getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { static const std::unordered_map<WheelGemBasicModifier_t, std::unordered_map<Vocation_t, int32_t>> stats = { { WheelGemBasicModifier_t::Vocation_Health, @@ -333,7 +333,7 @@ static int32_t getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modif return 0; } -static int32_t getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { +[[maybe_unused]] static int32_t getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { static const std::unordered_map<WheelGemBasicModifier_t, std::unordered_map<Vocation_t, int32_t>> stats = { { WheelGemBasicModifier_t::Vocation_Mana_FireResistance, @@ -409,7 +409,7 @@ static int32_t getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifie return 0; } -static int32_t getCapacityValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { +[[maybe_unused]] static int32_t getCapacityValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { static const std::unordered_map<WheelGemBasicModifier_t, std::unordered_map<Vocation_t, int32_t>> stats = { { WheelGemBasicModifier_t::Vocation_Capacity_FireResistance, diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index 9a7246a86ec..0fc74bcee71 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -116,7 +116,6 @@ bool GameReload::reloadCore() const { const bool coreLoaded = g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0; if (coreLoaded) { - const auto &datapackFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); const bool scriptsLoaded = g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false); if (scriptsLoaded) { return true; diff --git a/src/game/game.cpp b/src/game/game.cpp index d731bd766b6..1743edb2f32 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -94,7 +94,7 @@ namespace InternalGame { } if (blockType != BLOCK_NONE) { - g_game().sendSingleSoundEffect(targetPos, SoundEffect_t::NO_DAMAGE, source); + g_game().sendSingleSoundEffect(targetPos, SoundEffect_t::NO_DAMAGE, std::move(source)); } } @@ -405,15 +405,15 @@ void Game::loadBoostedCreature() { MonsterRace selectedMonster; if (!monsterlist.empty()) { - std::vector<MonsterRace> monsters; + std::vector<MonsterRace> m_monsters; for (const auto &[raceId, _name] : BestiaryList) { if (raceId != oldRace) { - monsters.emplace_back(raceId, _name); + m_monsters.emplace_back(raceId, _name); } } - if (!monsters.empty()) { - selectedMonster = monsters[normal_random(0, monsters.size() - 1)]; + if (!m_monsters.empty()) { + selectedMonster = m_monsters[normal_random(0, m_monsters.size() - 1)]; } } @@ -626,7 +626,7 @@ void Game::loadCustomMaps(const std::filesystem::path &customMapPath) { int customMapIndex = 0; for (const auto &entry : fs::directory_iterator(customMapPath)) { - const auto realPath = entry.path(); + const auto &realPath = entry.path(); if (realPath.extension() != ".otbm") { continue; @@ -1133,7 +1133,7 @@ bool Game::removeCreature(std::shared_ptr<Creature> creature, bool isLogout /* = } // event method - for (auto spectator : spectators) { + for (const auto &spectator : spectators) { spectator->onRemoveCreature(creature, isLogout); } } @@ -1150,7 +1150,7 @@ bool Game::removeCreature(std::shared_ptr<Creature> creature, bool isLogout /* = removeCreatureCheck(creature); - for (auto summon : creature->getSummons()) { + for (const auto &summon : creature->getSummons()) { summon->setSkillLoss(false); removeCreature(summon); } @@ -1772,12 +1772,10 @@ void Game::playerMoveItem(std::shared_ptr<Player> player, const Position &fromPo player->stowItem(item, count, false); return; } - if (!item->isPushable() || item->hasAttribute(ItemAttribute_t::UNIQUEID)) { player->sendCancelMessage(RETURNVALUE_NOTMOVABLE); return; } - ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); @@ -1868,7 +1866,7 @@ ReturnValue Game::checkMoveItemToCylinder(std::shared_ptr<Player> player, std::s } if (item->getContainer() && !item->isStoreItem()) { - for (std::shared_ptr<Item> containerItem : item->getContainer()->getItems(true)) { + for (const std::shared_ptr<Item> &containerItem : item->getContainer()->getItems(true)) { if (containerItem->isStoreItem() && ((containerID != ITEM_GOLD_POUCH && containerID != ITEM_DEPOT && containerID != ITEM_STORE_INBOX) || (topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && (!topParentContainer->getParent()->getContainer()->isDepotChest() || topParentContainer->getParent()->getContainer()->getID() != ITEM_STORE_INBOX)))) { return RETURNVALUE_NOTPOSSIBLE; } @@ -1884,7 +1882,7 @@ ReturnValue Game::checkMoveItemToCylinder(std::shared_ptr<Player> player, std::s } } if (item->getContainer() && !item->isStoreItem()) { - for (std::shared_ptr<Item> containerItem : item->getContainer()->getItems(true)) { + for (const std::shared_ptr<Item> &containerItem : item->getContainer()->getItems(true)) { if (containerItem->isStoreItem()) { return RETURNVALUE_NOTPOSSIBLE; } @@ -2155,7 +2153,7 @@ ReturnValue Game::internalMoveItem(std::shared_ptr<Cylinder> fromCylinder, std:: ReturnValue Game::internalAddItem(std::shared_ptr<Cylinder> toCylinder, std::shared_ptr<Item> item, int32_t index /*= INDEX_WHEREEVER*/, uint32_t flags /* = 0*/, bool test /* = false*/) { uint32_t remainderCount = 0; - return internalAddItem(toCylinder, item, index, flags, test, remainderCount); + return internalAddItem(std::move(toCylinder), std::move(item), index, flags, test, remainderCount); } ReturnValue Game::internalAddItem(std::shared_ptr<Cylinder> toCylinder, std::shared_ptr<Item> item, int32_t index, uint32_t flags, bool test, uint32_t &remainderCount) { @@ -2553,7 +2551,7 @@ bool Game::removeMoney(std::shared_ptr<Cylinder> cylinder, uint64_t money, uint3 size_t i = 0; while (i < containers.size()) { std::shared_ptr<Container> container = containers[i++]; - for (std::shared_ptr<Item> item : container->getItemList()) { + for (const std::shared_ptr<Item> &item : container->getItemList()) { std::shared_ptr<Container> tmpContainer = item->getContainer(); if (tmpContainer) { containers.push_back(tmpContainer); @@ -2848,7 +2846,7 @@ void Game::playerQuickLootCorpse(std::shared_ptr<Player> player, std::shared_ptr uint32_t totalLootedGold = 0; uint32_t totalLootedItems = 0; - for (std::shared_ptr<Item> item : itemList) { + for (const std::shared_ptr<Item> &item : itemList) { uint32_t worth = item->getWorth(); uint16_t baseCount = item->getItemCount(); ObjectCategory_t category = getObjectCategory(item); @@ -3099,7 +3097,7 @@ ReturnValue Game::collectRewardChestItems(std::shared_ptr<Player> player, uint32 auto rewardCount = rewardItemsVector.size(); uint32_t movedRewardItems = 0; std::string lootedItemsMessage; - for (auto item : rewardItemsVector) { + for (const auto &item : rewardItemsVector) { // Stop if player not have free capacity if (item && player->getCapacity() < item->getWeight()) { player->sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY); @@ -4471,7 +4469,7 @@ void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std:: return; } - for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) { + for (const auto &creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) { if (!creatureEvent->executeTextEdit(player, writeItem, text)) { player->setWriteItem(nullptr); return; @@ -4836,7 +4834,7 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st } if (tradeItemContainer) { - for (std::shared_ptr<Item> containerItem : tradeItemContainer->getItems(true)) { + for (const std::shared_ptr<Item> &containerItem : tradeItemContainer->getItems(true)) { if (containerItem->isStoreItem()) { player->sendTextMessage(MESSAGE_TRADE, "This item cannot be trade."); return; @@ -5067,7 +5065,7 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t size_t i = 0; while (i < containers.size()) { std::shared_ptr<Container> container = containers[i++]; - for (std::shared_ptr<Item> item : container->getItemList()) { + for (const std::shared_ptr<Item> &item : container->getItemList()) { std::shared_ptr<Container> tmpContainer = item->getContainer(); if (tmpContainer) { containers.push_back(tmpContainer); @@ -5519,7 +5517,7 @@ void Game::playerSetManagedContainer(uint32_t playerId, ObjectCategory_t categor std::shared_ptr<Container> container = thing->getContainer(); auto allowConfig = g_configManager().getBoolean(TOGGLE_GOLD_POUCH_ALLOW_ANYTHING, __FUNCTION__) || g_configManager().getBoolean(TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY, __FUNCTION__); - if (!container || (container->getID() == ITEM_GOLD_POUCH && category != OBJECTCATEGORY_GOLD) && !allowConfig) { + if (!container || ((container->getID() == ITEM_GOLD_POUCH && category != OBJECTCATEGORY_GOLD) && !allowConfig)) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -5849,7 +5847,6 @@ void Game::playerCloseImbuementWindow(uint32_t playerid) { } player->setImbuingItem(nullptr); - return; } void Game::playerTurn(uint32_t playerId, Direction dir) { @@ -6157,7 +6154,7 @@ void Game::playerSpeakToNpc(std::shared_ptr<Player> player, const std::string &t } std::shared_ptr<Task> Game::createPlayerTask(uint32_t delay, std::function<void(void)> f, std::string context) const { - return Player::createPlayerTask(delay, f, context); + return Player::createPlayerTask(delay, std::move(f), std::move(context)); } //-- @@ -6742,7 +6739,7 @@ void Game::handleHazardSystemAttack(CombatDamage &damage, std::shared_ptr<Player void Game::notifySpectators(const CreatureVector &spectators, const Position &targetPos, std::shared_ptr<Player> attackerPlayer, std::shared_ptr<Monster> targetMonster) { if (!spectators.empty()) { - for (auto spectator : spectators) { + for (const auto &spectator : spectators) { if (!spectator) { continue; } @@ -6912,7 +6909,7 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt if (damage.origin != ORIGIN_NONE) { const auto events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); if (!events.empty()) { - for (const auto creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeHealthChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7131,7 +7128,7 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt if (damage.origin != ORIGIN_NONE) { const auto events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (const auto creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } healthChange = damage.primary.value + damage.secondary.value; @@ -7235,7 +7232,7 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt if (damage.origin != ORIGIN_NONE) { const auto events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); if (!events.empty()) { - for (const auto creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeHealthChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7260,7 +7257,7 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt if (realDamage == 0) { return true; } else if (realDamage >= targetHealth) { - for (const auto creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { + for (const auto &creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { if (!creatureEvent->executeOnPrepareDeath(target, attacker)) { return false; } @@ -7336,7 +7333,7 @@ void Game::sendDamageMessageAndEffects( sendEffects(target, damage, targetPos, message, spectators); if (shouldSendMessage(message)) { - sendMessages(attacker, target, damage, targetPos, attackerPlayer, targetPlayer, message, spectators, realDamage); + sendMessages(std::move(attacker), target, damage, targetPos, std::move(attackerPlayer), std::move(targetPlayer), message, spectators, realDamage); } } @@ -7375,7 +7372,7 @@ void Game::sendMessages( std::string spectatorMessage; - for (std::shared_ptr<Creature> spectator : spectators) { + for (const std::shared_ptr<Creature> &spectator : spectators) { std::shared_ptr<Player> tmpPlayer = spectator->getPlayer(); if (!tmpPlayer || tmpPlayer->getPosition().z != targetPos.z) { continue; @@ -7496,7 +7493,7 @@ void Game::applyCharmRune( int8_t chance = charm->id == CHARM_CRIPPLE ? charm->chance : charm->chance + attackerPlayer->getCharmChanceModifier(); g_logger().debug("charm chance: {}, base: {}, bonus: {}", chance, charm->chance, attackerPlayer->getCharmChanceModifier()); if (charm->type == CHARM_OFFENSIVE && (chance >= normal_random(0, 100))) { - g_iobestiary().parseCharmCombat(charm, attackerPlayer, target, realDamage); + g_iobestiary().parseCharmCombat(charm, attackerPlayer, std::move(target), realDamage); } } } @@ -7589,7 +7586,7 @@ bool Game::combatChangeMana(std::shared_ptr<Creature> attacker, std::shared_ptr< if (damage.origin != ORIGIN_NONE) { const auto events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (const auto creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7683,7 +7680,7 @@ bool Game::combatChangeMana(std::shared_ptr<Creature> attacker, std::shared_ptr< if (damage.origin != ORIGIN_NONE) { const auto events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (const auto creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7991,7 +7988,7 @@ void Game::broadcastMessage(const std::string &text, MessageClasses type) const void Game::updateCreatureWalkthrough(std::shared_ptr<Creature> creature) { // Send to clients - for (const auto spectator : Spectators().find<Player>(creature->getPosition(), true)) { + for (const auto &spectator : Spectators().find<Player>(creature->getPosition(), true)) { const auto &tmpPlayer = spectator->getPlayer(); tmpPlayer->sendCreatureWalkthrough(creature, tmpPlayer->canWalkthroughEx(creature)); } @@ -8224,7 +8221,7 @@ void Game::playerLeaveParty(uint32_t playerId) { } std::shared_ptr<Party> party = player->getParty(); - if (!party || player->hasCondition(CONDITION_INFIGHT) && !player->getZoneType() == ZONE_PROTECTION) { + if (!party || (player->hasCondition(CONDITION_INFIGHT) && !player->getZoneType() == ZONE_PROTECTION)) { player->sendTextMessage(TextMessage(MESSAGE_FAILURE, "You cannot leave party, contact the administrator.")); return; } @@ -8302,7 +8299,7 @@ void Game::playerCyclopediaCharacterInfo(std::shared_ptr<Player> player, uint32_ query << "SELECT `time`, `level`, `killed_by`, `mostdamage_by`, (select count(*) FROM `player_deaths` WHERE `player_id` = " << playerGUID << ") as `entries` FROM `player_deaths` WHERE `player_id` = " << playerGUID << " ORDER BY `time` DESC LIMIT " << offset << ", " << entriesPerPage; uint32_t playerID = player->getID(); - std::function<void(DBResult_ptr, bool)> callback = [playerID, page, entriesPerPage](DBResult_ptr result, bool) { + std::function<void(DBResult_ptr, bool)> callback = [playerID, page, entriesPerPage](const DBResult_ptr &result, bool) { std::shared_ptr<Player> player = g_game().getPlayerByID(playerID); if (!player) { return; @@ -8367,7 +8364,7 @@ void Game::playerCyclopediaCharacterInfo(std::shared_ptr<Player> player, uint32_ query << "SELECT `d`.`time`, `d`.`killed_by`, `d`.`mostdamage_by`, `d`.`unjustified`, `d`.`mostdamage_unjustified`, `p`.`name`, (select count(*) FROM `player_deaths` WHERE ((`killed_by` = " << escapedName << " AND `is_player` = 1) OR (`mostdamage_by` = " << escapedName << " AND `mostdamage_is_player` = 1))) as `entries` FROM `player_deaths` AS `d` INNER JOIN `players` AS `p` ON `d`.`player_id` = `p`.`id` WHERE ((`d`.`killed_by` = " << escapedName << " AND `d`.`is_player` = 1) OR (`d`.`mostdamage_by` = " << escapedName << " AND `d`.`mostdamage_is_player` = 1)) ORDER BY `time` DESC LIMIT " << offset << ", " << entriesPerPage; uint32_t playerID = player->getID(); - std::function<void(DBResult_ptr, bool)> callback = [playerID, page, entriesPerPage](DBResult_ptr result, bool) { + std::function<void(DBResult_ptr, bool)> callback = [playerID, page, entriesPerPage](const DBResult_ptr &result, bool) { std::shared_ptr<Player> player = g_game().getPlayerByID(playerID); if (!player) { return; @@ -8812,13 +8809,13 @@ namespace { auto [itemVector, totalCount] = player->getLockerItemsAndCountById(depotLocker, tier, itemType.id); if (removeAmount > 0) { - if (totalCount == 0 || itemVector.size() == 0) { + if (totalCount == 0 || itemVector.empty()) { offerStatus << "Player " << player->getName() << " not have item for create offer"; return false; } uint32_t count = 0; - for (auto item : itemVector) { + for (const auto &item : itemVector) { if (!item) { continue; } @@ -8904,7 +8901,7 @@ bool checkCanInitCreateMarketOffer(std::shared_ptr<Player> player, uint8_t type, return false; } - if (amount == 0 || !it.stackable && amount > 2000 || it.stackable && amount > 64000) { + if (amount == 0 || (!it.stackable && amount > 2000) || (it.stackable && amount > 64000)) { offerStatus << "Failed to load amount " << amount << " for player " << player->getName(); return false; } @@ -9123,7 +9120,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 return; } - if (amount == 0 || !it.stackable && amount > 2000 || it.stackable && amount > 64000 || amount > offer.amount) { + if (amount == 0 || (!it.stackable && amount > 2000) || (it.stackable && amount > 64000) || amount > offer.amount) { offerStatus << "Invalid offer amount " << amount << " for player " << player->getName(); return; } @@ -9372,7 +9369,7 @@ void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const st return; } - for (const auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) { + for (const auto &creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) { creatureEvent->executeExtendedOpcode(player, opcode, buffer); } } @@ -9424,7 +9421,7 @@ void Game::playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, ui player->setBedItem(nullptr); } else { - for (const auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_MODALWINDOW)) { + for (const auto &creatureEvent : player->getCreatureEvents(CREATURE_EVENT_MODALWINDOW)) { creatureEvent->executeModalWindow(player, modalWindowId, button, choice); } } @@ -9694,7 +9691,7 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st bool isPodiumOfRenown = itemId == ITEM_PODIUM_OF_RENOWN1 || itemId == ITEM_PODIUM_OF_RENOWN2; if (!isPodiumOfRenown) { auto lookTypeExAttribute = item->getCustomAttribute("LookTypeEx"); - if (!isMonsterVisible || podiumRaceId == 0 || lookTypeExAttribute && lookTypeExAttribute->getInteger() == 39003) { + if (!isMonsterVisible || podiumRaceId == 0 || (lookTypeExAttribute && lookTypeExAttribute->getInteger() == 39003)) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -9910,7 +9907,7 @@ void Game::removeGuild(uint32_t guildId) { void Game::internalRemoveItems(const std::vector<std::shared_ptr<Item>> &itemVector, uint32_t amount, bool stackable) { if (stackable) { - for (std::shared_ptr<Item> item : itemVector) { + for (const std::shared_ptr<Item> &item : itemVector) { if (item->getItemCount() > amount) { internalRemoveItem(item, amount); break; @@ -9920,7 +9917,7 @@ void Game::internalRemoveItems(const std::vector<std::shared_ptr<Item>> &itemVec } } } else { - for (std::shared_ptr<Item> item : itemVector) { + for (const std::shared_ptr<Item> &item : itemVector) { internalRemoveItem(item); } } @@ -9935,7 +9932,7 @@ std::shared_ptr<BedItem> Game::getBedBySleeper(uint32_t guid) const { } void Game::setBedSleeper(std::shared_ptr<BedItem> bed, uint32_t guid) { - bedSleepersMap[guid] = bed; + bedSleepersMap[guid] = std::move(bed); } void Game::removeBedSleeper(uint32_t guid) { @@ -10489,7 +10486,7 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint bool Game::tryRetrieveStashItems(std::shared_ptr<Player> player, std::shared_ptr<Item> item) { ObjectCategory_t category = getObjectCategory(item); - return internalCollectManagedItems(player, item, category, false) == RETURNVALUE_NOERROR; + return internalCollectManagedItems(std::move(player), item, category, false) == RETURNVALUE_NOERROR; } std::unique_ptr<IOWheel> &Game::getIOWheel() { @@ -10621,7 +10618,7 @@ void Game::registerAchievement(uint16_t id, std::string name, std::string descri m_achievements[id] = Achievement(); m_achievements[id].id = id; m_achievements[id].name = name; - m_achievements[id].description = description; + m_achievements[id].description = std::move(description); m_achievements[id].secret = secret; m_achievements[id].grade = grade; m_achievements[id].points = points; diff --git a/src/game/scheduling/events_scheduler.cpp b/src/game/scheduling/events_scheduler.cpp index c57f84b667c..a86254fa35c 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -27,7 +27,6 @@ bool EventsScheduler::loadScheduleEventFromXml() { int daysMath = ((timePtr->tm_year + 1900) * 365) + ((timePtr->tm_mon + 1) * 30) + (timePtr->tm_mday); // Keep track of loaded scripts to check for duplicates - int count = 0; phmap::flat_hash_set<std::string_view> loadedScripts; std::map<std::string, EventRates> eventsOnSameDay; for (const auto &eventNode : doc.child("events").children()) { diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 0498b40e29f..bcbfc531468 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -66,7 +66,7 @@ bool IOLoginDataLoad::preLoadPlayer(std::shared_ptr<Player> player, const std::s } player->setGUID(result->getNumber<uint32_t>("id")); - Group* group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); + std::shared_ptr<Group> group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); if (!group) { g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber<uint16_t>("group_id")); return false; @@ -118,7 +118,7 @@ bool IOLoginDataLoad::loadPlayerFirst(std::shared_ptr<Player> player, DBResult_p player->setAccount(result->getNumber<uint32_t>("account_id")); } - Group* group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); + std::shared_ptr<Group> group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); if (!group) { g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber<uint16_t>("group_id")); return false; diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 82ba286d622..eccb88c8229 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -436,7 +436,7 @@ bool IOLoginDataSave::savePlayerBestiarySystem(std::shared_ptr<Player> player) { query << "`UnlockedRunesBit` = " << player->UnlockedRunesBit << ","; PropWriteStream propBestiaryStream; - for (const auto trackedType : player->getCyclopediaMonsterTrackerSet(false)) { + for (const auto &trackedType : player->getCyclopediaMonsterTrackerSet(false)) { propBestiaryStream.write<uint16_t>(trackedType->info.raceid); } size_t trackerSize; @@ -737,7 +737,7 @@ bool IOLoginDataSave::savePlayerBosstiary(std::shared_ptr<Player> player) { // Bosstiary tracker PropWriteStream stream; - for (const auto monsterType : player->getCyclopediaMonsterTrackerSet(true)) { + for (const auto &monsterType : player->getCyclopediaMonsterTrackerSet(true)) { if (!monsterType) { continue; } diff --git a/src/io/iobestiary.cpp b/src/io/iobestiary.cpp index f7305d8503c..c544021367e 100644 --- a/src/io/iobestiary.cpp +++ b/src/io/iobestiary.cpp @@ -97,7 +97,7 @@ bool IOBestiary::parseCharmCombat(const std::shared_ptr<Charm> charm, std::share std::shared_ptr<Charm> IOBestiary::getBestiaryCharm(charmRune_t activeCharm, bool force /*= false*/) const { const auto charmInternal = g_game().getCharmList(); - for (const auto tmpCharm : charmInternal) { + for (const auto &tmpCharm : charmInternal) { if (tmpCharm->id == activeCharm) { return tmpCharm; } @@ -169,7 +169,7 @@ void IOBestiary::setCharmRuneCreature(std::shared_ptr<Player> player, const std: std::list<charmRune_t> IOBestiary::getCharmUsedRuneBitAll(std::shared_ptr<Player> player) { int32_t input = player->getUsedRunesBit(); - ; + int8_t i = 0; std::list<charmRune_t> rtn; while (input != 0) { @@ -191,7 +191,7 @@ uint16_t IOBestiary::getBestiaryRaceUnlocked(std::shared_ptr<Player> player, Bes uint16_t count = 0; std::map<uint16_t, std::string> besty_l = g_game().getBestiaryList(); - for (auto it : besty_l) { + for (const auto &it : besty_l) { const auto mtype = g_monsters().getMonsterType(it.second); if (mtype && mtype->info.bestiaryRace == race && player->getBestiaryKillCount(mtype->info.raceid) > 0) { count++; @@ -271,7 +271,7 @@ int32_t IOBestiary::bitToggle(int32_t input, const std::shared_ptr<Charm> charm, return CHARM_NONE; } - int32_t returnToggle = 0; + int32_t returnToggle; int32_t binary = charm->binary; if (on) { returnToggle = input | binary; @@ -307,7 +307,7 @@ void IOBestiary::sendBuyCharmRune(std::shared_ptr<Player> player, charmRune_t ru player->setUnlockedRunesBit(value); } else if (action == 1) { std::list<charmRune_t> usedRunes = getCharmUsedRuneBitAll(player); - uint16_t limitRunes = 0; + uint16_t limitRunes; if (player->isPremium()) { if (player->hasCharmExpansion()) { @@ -343,7 +343,6 @@ void IOBestiary::sendBuyCharmRune(std::shared_ptr<Player> player, charmRune_t ru player->sendFYIBox("You don't have enough gold."); } player->BestiarysendCharms(); - return; } std::map<uint8_t, int16_t> IOBestiary::getMonsterElements(const std::shared_ptr<MonsterType> mtype) const { diff --git a/src/io/iobestiary.hpp b/src/io/iobestiary.hpp index 9cb2ef2fe55..a521ce68852 100644 --- a/src/io/iobestiary.hpp +++ b/src/io/iobestiary.hpp @@ -20,16 +20,18 @@ class Charm { public: Charm() = default; Charm(std::string initname, charmRune_t initcharmRune_t, std::string initdescription, charm_t inittype, uint16_t initpoints, int32_t initbinary) : - name(initname), id(initcharmRune_t), description(initdescription), type(inittype), points(initpoints), binary(initbinary) { } + name(std::move(initname)), id(initcharmRune_t), description(std::move(initdescription)), type(inittype), points(initpoints), binary(initbinary) { } virtual ~Charm() = default; std::string name; + charmRune_t id = CHARM_NONE; + std::string description; + charm_t type; + uint16_t points = 0; + int32_t binary = 0; std::string cancelMsg; std::string logMsg; - std::string description; - charm_t type; - charmRune_t id = CHARM_NONE; CombatType_t dmgtype = COMBAT_NONE; uint16_t effect = CONST_ME_NONE; @@ -38,8 +40,6 @@ class Charm { uint16_t percent = 0; int8_t chance = 0; - uint16_t points = 0; - int32_t binary = 0; }; class IOBestiary { diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 4c6b95dc32e..46426451ddc 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -72,7 +72,7 @@ uint8_t IOLoginData::getAccountType(uint32_t accountId) { void IOLoginData::updateOnlineStatus(uint32_t guid, bool login) { static phmap::flat_hash_map<uint32_t, bool> updateOnline; - if (login && updateOnline.find(guid) != updateOnline.end() || guid <= 0) { + if ((login && updateOnline.find(guid) != updateOnline.end()) || guid <= 0) { return; } diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp index 4edfefa66d1..d50d9b2804f 100644 --- a/src/io/iomap.cpp +++ b/src/io/iomap.cpp @@ -122,8 +122,6 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) { const uint16_t base_y = stream.getU16(); const uint8_t base_z = stream.getU8(); - bool tileIsStatic = false; - while (stream.startNode()) { const uint8_t tileType = stream.getU8(); if (tileType != OTBM_HOUSETILE && tileType != OTBM_TILE) { @@ -166,9 +164,6 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) { const auto &iType = Item::items[id]; if (!tile->isHouse() || (!iType.isBed() && !iType.isTrashHolder())) { - if (iType.blockSolid) { - tileIsStatic = true; - } const auto item = std::make_shared<BasicItem>(); item->id = id; @@ -194,10 +189,6 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) { const auto &iType = Item::items[id]; - if (iType.blockSolid) { - tileIsStatic = true; - } - const auto item = std::make_shared<BasicItem>(); item->id = id; diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 5d2ffd98769..63ce657741a 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -383,7 +383,7 @@ bool Container::isInsideContainerWithId(const uint16_t id) { } bool Container::isAnyKindOfRewardChest() { - return getID() == ITEM_REWARD_CHEST || getID() == ITEM_REWARD_CONTAINER && getParent() && getParent()->getContainer() && getParent()->getContainer()->getID() == ITEM_REWARD_CHEST || isBrowseFieldAndHoldsRewardChest(); + return getID() == ITEM_REWARD_CHEST || (getID() == ITEM_REWARD_CONTAINER && getParent() && getParent()->getContainer() && getParent()->getContainer()->getID() == ITEM_REWARD_CHEST) || isBrowseFieldAndHoldsRewardChest(); } bool Container::isAnyKindOfRewardContainer() { diff --git a/src/items/item.cpp b/src/items/item.cpp index 104efc7f925..823eb13b393 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -3237,7 +3237,7 @@ std::shared_ptr<Item> Item::transform(uint16_t itemId, uint16_t itemCount /*= -1 } std::shared_ptr<Item> newItem; - if (itemCount == -1) { + if (itemCount == 0) { newItem = Item::CreateItem(itemId, 1); } else { newItem = Item::CreateItem(itemId, itemCount); diff --git a/src/items/tile.cpp b/src/items/tile.cpp index ca354700f22..11e3fafd6fd 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -791,7 +791,7 @@ ReturnValue Tile::queryAdd(int32_t, const std::shared_ptr<Thing> &thing, uint32_ if (ground) { const ItemType &iiType = Item::items[ground->getID()]; if (iiType.blockSolid) { - if (!iiType.pickupable && iiType.type != ITEM_TYPE_TRASHHOLDER || item->isMagicField() || item->isBlocking()) { + if ((!iiType.pickupable && iiType.type != ITEM_TYPE_TRASHHOLDER) || item->isMagicField() || item->isBlocking()) { if (!item->isPickupable() && !item->isCarpet()) { return RETURNVALUE_NOTENOUGHROOM; } @@ -1575,7 +1575,7 @@ void Tile::internalAddThing(uint32_t, std::shared_ptr<Thing> thing) { if (!thing) { return; } - for (const auto zone : getZones()) { + for (const auto &zone : getZones()) { zone->thingAdded(thing); } diff --git a/src/items/weapons/weapons.hpp b/src/items/weapons/weapons.hpp index 5b2b260e2ee..59cc23636e2 100644 --- a/src/items/weapons/weapons.hpp +++ b/src/items/weapons/weapons.hpp @@ -200,11 +200,11 @@ class Weapon : public Script { return m_isDisabledChain; } - const WeaponType_t getWeaponType() const { + WeaponType_t getWeaponType() const { return weaponType; } - const std::shared_ptr<Combat> getCombat() const { + std::shared_ptr<Combat> getCombat() const { if (!m_combat) { g_logger().error("Weapon::getCombat() - m_combat is nullptr"); return nullptr; diff --git a/src/lib/metrics/metrics.cpp b/src/lib/metrics/metrics.cpp index cf11060125a..f005c79c966 100644 --- a/src/lib/metrics/metrics.cpp +++ b/src/lib/metrics/metrics.cpp @@ -87,7 +87,7 @@ void Metrics::shutdown() { metrics_api::Provider::SetMeterProvider(none); } -ScopedLatency::ScopedLatency(std::string_view name, const std::string &histogramName, const std::string &scopeKey) : +ScopedLatency::ScopedLatency(const std::string_view &name, const std::string &histogramName, const std::string &scopeKey) : ScopedLatency(name, g_metrics().latencyHistograms[histogramName], { { scopeKey, std::string(name) } }, g_metrics().defaultContext) { if (histogram == nullptr) { stopped = true; diff --git a/src/lib/metrics/metrics.hpp b/src/lib/metrics/metrics.hpp index 279ea5f91c5..1e3dbe31086 100644 --- a/src/lib/metrics/metrics.hpp +++ b/src/lib/metrics/metrics.hpp @@ -54,9 +54,9 @@ namespace metrics { class ScopedLatency { public: - explicit ScopedLatency(std::string_view name, const std::string &histogramName, const std::string &scopeKey); - explicit ScopedLatency(std::string_view name, Histogram<double> &histogram, std::map<std::string, std::string> attrs = {}, opentelemetry::context::Context context = opentelemetry::context::Context()) : - begin(std::chrono::steady_clock::now()), histogram(histogram), attrs(attrs), context(context) { + explicit ScopedLatency(const std::string_view &name, const std::string &histogramName, const std::string &scopeKey); + explicit ScopedLatency([[maybe_unused]] const std::string_view &name, Histogram<double> &histogram, std::map<std::string, std::string> attrs = {}, opentelemetry::context::Context context = opentelemetry::context::Context()) : + begin(std::chrono::steady_clock::now()), histogram(histogram), attrs(std::move(attrs)), context(std::move(context)) { } void stop(); @@ -64,10 +64,10 @@ namespace metrics { ~ScopedLatency(); private: - opentelemetry::context::Context context; - Histogram<double> &histogram; std::chrono::steady_clock::time_point begin; + Histogram<double> &histogram; std::map<std::string, std::string> attrs; + opentelemetry::context::Context context; bool stopped { false }; }; @@ -94,7 +94,7 @@ namespace metrics { class Metrics final { public: - Metrics() { } + Metrics() = default; ~Metrics() = default; void init(Options opts); diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index 0af09476ed8..b302877b3d3 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -24,8 +24,6 @@ void MoveEvents::clear(bool isFromXML /*= false*/) { for (int moveEventType = 0; moveEventType < MOVE_EVENT_LAST; ++moveEventType) { auto &eventList = moveEventList.moveEvent[moveEventType]; - int originalSize = eventList.size(); - eventList.remove_if([&](const std::shared_ptr<MoveEvent> &moveEvent) { bool removed = moveEvent && moveEvent->isFromXML(); if (removed) { diff --git a/src/lua/creature/movement.hpp b/src/lua/creature/movement.hpp index 485454a0564..4ce4297b0f5 100644 --- a/src/lua/creature/movement.hpp +++ b/src/lua/creature/movement.hpp @@ -180,7 +180,7 @@ class MoveEvent final : public Script, public SharedObject { } void addVocEquipMap(std::string vocName) { uint16_t vocationId = g_vocations().getVocationId(vocName); - if (vocationId != -1) { + if (vocationId != 65535) { vocEquipMap[vocationId] = true; } } diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index a84c500b985..d0e77a69352 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -22,7 +22,6 @@ void ConfigFunctions::init(lua_State* L) { #define registerMagicEnumIn(L, tableName, enumValue) \ do { \ - auto number = magic_enum::enum_integer(enumValue); \ auto name = magic_enum::enum_name(enumValue).data(); \ registerVariable(L, tableName, name, value); \ } while (0) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index a91889e43bc..0fdbc96f4d6 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -214,7 +214,7 @@ int GameFunctions::luaGameGetMonsterTypes(lua_State* L) { const auto type = g_monsters().monsters; lua_createtable(L, type.size(), 0); - for (const auto [typeName, mType] : type) { + for (const auto &[typeName, mType] : type) { pushUserdata<MonsterType>(L, mType); setMetatable(L, -1, "MonsterType"); lua_setfield(L, -2, typeName.c_str()); @@ -517,7 +517,7 @@ int GameFunctions::luaGameGetBestiaryCharm(lua_State* L) { lua_createtable(L, c_list.size(), 0); int index = 0; - for (const auto charmPtr : c_list) { + for (const auto &charmPtr : c_list) { pushUserdata<Charm>(L, charmPtr); setMetatable(L, -1, "Charm"); lua_rawseti(L, -2, ++index); @@ -699,7 +699,7 @@ int GameFunctions::luaGameGetInfluencedMonsters(lua_State* L) { int GameFunctions::luaGameGetLadderIds(lua_State* L) { // Game.getLadderIds() - const auto ladders = Item::items.getLadders(); + const auto &ladders = Item::items.getLadders(); lua_createtable(L, static_cast<int>(ladders.size()), 0); int index = 0; for (const auto ladderId : ladders) { @@ -721,7 +721,7 @@ int GameFunctions::luaGameGetDummies(lua_State* L) { local rate = dummies[1] -- Retrieve dummy rate */ - const auto dummies = Item::items.getDummys(); + const auto &dummies = Item::items.getDummys(); lua_createtable(L, dummies.size(), 0); for (const auto &[dummyId, rate] : dummies) { lua_pushnumber(L, static_cast<lua_Number>(rate)); diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index a633afe9749..7a40cc426f1 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -54,7 +54,7 @@ int CreatureFunctions::luaCreatureGetEvents(lua_State* L) { lua_createtable(L, static_cast<int>(eventList.size()), 0); int index = 0; - for (const auto eventPtr : eventList) { + for (const auto &eventPtr : eventList) { pushString(L, eventPtr->getName()); lua_rawseti(L, -2, ++index); } diff --git a/src/lua/functions/creatures/monster/charm_functions.cpp b/src/lua/functions/creatures/monster/charm_functions.cpp index 48f7ff5aec9..d456f1c96a8 100644 --- a/src/lua/functions/creatures/monster/charm_functions.cpp +++ b/src/lua/functions/creatures/monster/charm_functions.cpp @@ -18,7 +18,7 @@ int CharmFunctions::luaCharmCreate(lua_State* L) { if (isNumber(L, 2)) { charmRune_t charmid = getNumber<charmRune_t>(L, 2); const auto charmList = g_game().getCharmList(); - for (const auto charm : charmList) { + for (const auto &charm : charmList) { if (charm->id == charmid) { pushUserdata<Charm>(L, charm); setMetatable(L, -1, "Charm"); diff --git a/src/lua/functions/creatures/monster/monster_spell_functions.cpp b/src/lua/functions/creatures/monster/monster_spell_functions.cpp index ee00fd43af7..a20d57008d2 100644 --- a/src/lua/functions/creatures/monster/monster_spell_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_spell_functions.cpp @@ -170,7 +170,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionType(lua_State* L) { const auto spell = getUserdataShared<MonsterSpell>(L, 1); if (spell) { auto conditionType = getNumber<uint8_t>(L, 2); - if (conditionType == -1) { + if (conditionType == 254) { g_logger().error("[{}] trying to register condition type none for monster: {}", __FUNCTION__, spell->name); reportErrorFunc(fmt::format("trying to register condition type none for monster: {}", spell->name)); pushBoolean(L, false); diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index 43c2950a802..8c19a4ede0a 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -400,7 +400,7 @@ int NpcFunctions::luaNpcOpenShopWindowTable(lua_State* L) { if (itemName.empty()) { itemName = Item::items[itemId].name; } - items.emplace_back(itemId, subType, buyPrice, sellPrice, storageKey, storageValue, itemName); + items.emplace_back(itemId, itemName, subType, buyPrice, sellPrice, storageKey, storageValue); lua_pop(L, 8); } lua_pop(L, 3); diff --git a/src/lua/functions/creatures/player/group_functions.cpp b/src/lua/functions/creatures/player/group_functions.cpp index 0ea195fe745..5acc16d72a9 100644 --- a/src/lua/functions/creatures/player/group_functions.cpp +++ b/src/lua/functions/creatures/player/group_functions.cpp @@ -17,7 +17,7 @@ int GroupFunctions::luaGroupCreate(lua_State* L) { // Group(id) uint32_t id = getNumber<uint32_t>(L, 2); - Group* group = g_game().groups.getGroup(id); + std::shared_ptr<Group> group = g_game().groups.getGroup(id); if (group) { pushUserdata<Group>(L, group); setMetatable(L, -1, "Group"); @@ -29,7 +29,7 @@ int GroupFunctions::luaGroupCreate(lua_State* L) { int GroupFunctions::luaGroupGetId(lua_State* L) { // group:getId() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->id); } else { @@ -40,7 +40,7 @@ int GroupFunctions::luaGroupGetId(lua_State* L) { int GroupFunctions::luaGroupGetName(lua_State* L) { // group:getName() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { pushString(L, group->name); } else { @@ -51,7 +51,7 @@ int GroupFunctions::luaGroupGetName(lua_State* L) { int GroupFunctions::luaGroupGetFlags(lua_State* L) { // group:getFlags() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { std::bitset<magic_enum::enum_integer(PlayerFlags_t::FlagLast)> flags; for (uint8_t i = 0; i < magic_enum::enum_integer(PlayerFlags_t::FlagLast); ++i) { @@ -68,7 +68,7 @@ int GroupFunctions::luaGroupGetFlags(lua_State* L) { int GroupFunctions::luaGroupGetAccess(lua_State* L) { // group:getAccess() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { pushBoolean(L, group->access); } else { @@ -79,7 +79,7 @@ int GroupFunctions::luaGroupGetAccess(lua_State* L) { int GroupFunctions::luaGroupGetMaxDepotItems(lua_State* L) { // group:getMaxDepotItems() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->maxDepotItems); } else { @@ -90,7 +90,7 @@ int GroupFunctions::luaGroupGetMaxDepotItems(lua_State* L) { int GroupFunctions::luaGroupGetMaxVipEntries(lua_State* L) { // group:getMaxVipEntries() - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->maxVipEntries); } else { @@ -101,7 +101,7 @@ int GroupFunctions::luaGroupGetMaxVipEntries(lua_State* L) { int GroupFunctions::luaGroupHasFlag(lua_State* L) { // group:hasFlag(flag) - Group* group = getUserdata<Group>(L, 1); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); if (group) { auto flag = static_cast<PlayerFlags_t>(getNumber<int>(L, 2)); pushBoolean(L, group->flags[Groups::getFlagNumber(flag)]); diff --git a/src/lua/functions/creatures/player/group_functions.hpp b/src/lua/functions/creatures/player/group_functions.hpp index ecc3ac07280..4a33d1b988e 100644 --- a/src/lua/functions/creatures/player/group_functions.hpp +++ b/src/lua/functions/creatures/player/group_functions.hpp @@ -14,7 +14,7 @@ class GroupFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Group", "", GroupFunctions::luaGroupCreate); + registerSharedClass(L, "Group", "", GroupFunctions::luaGroupCreate); registerMetaMethod(L, "Group", "__eq", GroupFunctions::luaUserdataCompare); registerMethod(L, "Group", "getId", GroupFunctions::luaGroupGetId); diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 51dd79638ab..7f4d96f3773 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1604,7 +1604,7 @@ int PlayerFunctions::luaPlayerGetGroup(lua_State* L) { int PlayerFunctions::luaPlayerSetGroup(lua_State* L) { // player:setGroup(group) - Group* group = getUserdata<Group>(L, 2); + std::shared_ptr<Group> group = getUserdataShared<Group>(L, 2); if (!group) { pushBoolean(L, false); return 1; diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index 7967e21c3cc..842622b2af5 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -45,7 +45,7 @@ void House::setNewOwnerGuid(int32_t newOwnerGuid, bool serverStartup) { void House::clearHouseInfo(bool preventOwnerDeletion) { // Remove players from beds - for (auto bed : bedsList) { + for (const auto &bed : bedsList) { if (bed->getSleeper() != 0) { bed->wakeUp(nullptr); } @@ -60,7 +60,7 @@ void House::clearHouseInfo(bool preventOwnerDeletion) { setAccessList(SUBOWNER_LIST, ""); setAccessList(GUEST_LIST, ""); - for (auto door : doorList) { + for (const auto &door : doorList) { door->setAccessList(""); } } @@ -73,7 +73,7 @@ bool House::tryTransferOwnership(std::shared_ptr<Player> player, bool serverStar transferSuccess = transferToDepot(); } - for (auto tile : houseTiles) { + for (const auto &tile : houseTiles) { if (const CreatureVector* creatures = tile->getCreatures()) { for (int32_t i = creatures->size(); --i >= 0;) { const auto creature = (*creatures)[i]; @@ -103,7 +103,7 @@ void House::setOwner(uint32_t guid, bool updateDatabase /* = true*/, std::shared isLoaded = true; if (owner != 0) { - tryTransferOwnership(player, false); + tryTransferOwnership(std::move(player), false); } else { std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD, __FUNCTION__)); time_t currentTime = time(nullptr); @@ -237,7 +237,7 @@ void House::setAccessList(uint32_t listId, const std::string &textlist) { } // kick uninvited players - for (std::shared_ptr<HouseTile> tile : houseTiles) { + for (const std::shared_ptr<HouseTile> &tile : houseTiles) { if (CreatureVector* creatures = tile->getCreatures()) { for (int32_t i = creatures->size(); --i >= 0;) { std::shared_ptr<Player> player = (*creatures)[i]->getPlayer(); @@ -272,7 +272,7 @@ bool House::transferToDepot(std::shared_ptr<Player> player) const { if (townId == 0 || !player) { return false; } - for (std::shared_ptr<HouseTile> tile : houseTiles) { + for (const std::shared_ptr<HouseTile> &tile : houseTiles) { if (!transferToDepot(player, tile)) { return false; } @@ -304,7 +304,7 @@ bool House::transferToDepot(std::shared_ptr<Player> player, std::shared_ptr<Hous std::unordered_set<std::shared_ptr<Player>> playersToSave = { player }; - for (std::shared_ptr<Item> item : moveItemList) { + for (const std::shared_ptr<Item> &item : moveItemList) { g_logger().debug("[{}] moving item '{}' to depot", __FUNCTION__, item->getName()); auto targetPlayer = player; if (item->hasOwner() && !item->isOwner(targetPlayer)) { @@ -317,7 +317,7 @@ bool House::transferToDepot(std::shared_ptr<Player> player, std::shared_ptr<Hous } g_game().internalMoveItem(item->getParent(), targetPlayer->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); } - for (auto playerToSave : playersToSave) { + for (const auto &playerToSave : playersToSave) { g_saveManager().savePlayer(playerToSave); } return true; @@ -379,7 +379,7 @@ void House::handleWrapableItem(ItemList &moveItemList, std::shared_ptr<Item> ite void House::handleContainer(ItemList &moveItemList, std::shared_ptr<Item> item) const { if (const auto container = item->getContainer()) { - for (std::shared_ptr<Item> containerItem : container->getItemList()) { + for (const std::shared_ptr<Item> &containerItem : container->getItemList()) { moveItemList.push_back(containerItem); } } @@ -554,32 +554,32 @@ void AccessList::parseList(const std::string &list) { } auto lines = explodeString(validList, "\n", 100); - for (auto &line : lines) { - trimString(line); - trim_left(line, '\t'); - trim_right(line, '\t'); - trimString(line); + for (auto &m_line : lines) { + trimString(m_line); + trim_left(m_line, '\t'); + trim_right(m_line, '\t'); + trimString(m_line); - if (line.empty() || line.front() == '#' || line.length() > 100) { + if (m_line.empty() || m_line.front() == '#' || m_line.length() > 100) { continue; } - toLowerCaseString(line); + toLowerCaseString(m_line); - std::string::size_type at_pos = line.find("@"); + std::string::size_type at_pos = m_line.find('@'); if (at_pos != std::string::npos) { if (at_pos == 0) { - addGuild(line.substr(1)); + addGuild(m_line.substr(1)); } else { - addGuildRank(line.substr(0, at_pos - 1), line.substr(at_pos + 1)); + addGuildRank(m_line.substr(0, at_pos - 1), m_line.substr(at_pos + 1)); } - } else if (line == "*") { + } else if (m_line == "*") { allowEveryone = true; - } else if (line.find_first_of("!*?") != std::string::npos) { + } else if (m_line.find_first_of("!*?") != std::string::npos) { // Remove regular expressions since they don't make much sense in houses continue; - } else if (line.length() <= NETWORKMESSAGE_PLAYERNAME_MAXLENGTH) { - addPlayer(line); + } else if (m_line.length() <= NETWORKMESSAGE_PLAYERNAME_MAXLENGTH) { + addPlayer(m_line); } } } @@ -603,7 +603,7 @@ namespace { return nullptr; } - const auto guild = g_game().getGuild(guildId); + auto guild = g_game().getGuild(guildId); if (guild) { return guild; } @@ -615,7 +615,7 @@ namespace { void AccessList::addGuild(const std::string &name) { const auto guild = getGuildByName(name); if (guild) { - for (const auto rank : guild->getRanks()) { + for (const auto &rank : guild->getRanks()) { guildRankList.insert(rank->id); } } @@ -669,7 +669,7 @@ void Door::setHouse(std::shared_ptr<House> newHouse) { return; } - this->house = newHouse; + this->house = std::move(newHouse); if (!accessList) { accessList = std::make_unique<AccessList>(); diff --git a/src/map/map.cpp b/src/map/map.cpp index 395e6d7d2be..fcf14262c30 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -611,7 +611,7 @@ bool Map::getPathMatching(const std::shared_ptr<Creature> &creature, const Posit const bool withoutCreature = creature == nullptr; const auto &tile = neighborNode || withoutCreature ? getTile(pos.x, pos.y, pos.z) : canWalkTo(creature, pos); - if (!tile || !neighborNode && withoutCreature && tile->hasFlag(TILESTATE_BLOCKSOLID)) { + if (!tile || (!neighborNode && withoutCreature && tile->hasFlag(TILESTATE_BLOCKSOLID))) { continue; } diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 18ce13871fe..7c0e80a0412 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -79,7 +79,7 @@ bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onl if (checkDistance) { SpectatorList spectators; spectators.reserve(creatures.size()); - for (const auto creature : *list) { + for (const auto &creature : *list) { const auto &specPos = creature->getPosition(); if (centerPos.x - specPos.x >= minRangeX && centerPos.y - specPos.y >= minRangeY @@ -184,7 +184,7 @@ Spectators Spectators::find(const Position ¢erPos, bool multifloor, bool onl for (uint_fast16_t nx = startx1; nx <= endx2; nx += FLOOR_SIZE) { if (leafE) { const auto &node_list = (onlyPlayers ? leafE->player_list : leafE->creature_list); - for (const auto creature : node_list) { + for (const auto &creature : node_list) { const auto &cpos = creature->getPosition(); if (minRangeZ > cpos.z || maxRangeZ < cpos.z) { continue; @@ -211,7 +211,7 @@ Spectators Spectators::find(const Position ¢erPos, bool multifloor, bool onl } // It is necessary to create the cache even if no spectators is found, so that there is no future query. - auto &cache = cacheFound ? it->second : spectatorsCache.emplace(centerPos, SpectatorsCache { .minRangeX = minRangeX, .maxRangeX = maxRangeX, .minRangeY = minRangeY, .maxRangeY = maxRangeY }).first->second; + auto &cache = cacheFound ? it->second : spectatorsCache.emplace(centerPos, SpectatorsCache { .minRangeX = minRangeX, .maxRangeX = maxRangeX, .minRangeY = minRangeY, .maxRangeY = maxRangeY, .creatures = {}, .players = {} }).first->second; auto &creaturesCache = onlyPlayers ? cache.players : cache.creatures; auto &creatureList = (multifloor ? creaturesCache.multiFloor : creaturesCache.floor); if (creatureList) { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index ab111fd3c8f..ada1340a7c6 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2247,7 +2247,7 @@ void ProtocolGame::parseBestiarysendRaces() { for (uint8_t i = BESTY_RACE_FIRST; i <= BESTY_RACE_LAST; i++) { std::string BestClass = ""; uint16_t count = 0; - for (auto rit : mtype_list) { + for (const auto &rit : mtype_list) { const auto mtype = g_monsters().getMonsterType(rit.second); if (!mtype) { return; @@ -2323,7 +2323,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { std::vector<LootBlock> lootList = mtype->info.lootItems; newmsg.addByte(lootList.size()); - for (LootBlock loot : lootList) { + for (const LootBlock &loot : lootList) { int8_t difficult = g_iobestiary().calculateDifficult(loot.chance); bool shouldAddItem = false; @@ -2635,7 +2635,7 @@ void ProtocolGame::createLeaderTeamFinder(NetworkMessage &msg) { auto party = player->getParty(); if (teamAssemble->partyBool && party) { - for (std::shared_ptr<Player> member : party->getMembers()) { + for (const std::shared_ptr<Player> &member : party->getMembers()) { if (member && member->getGUID() != player->getGUID()) { teamAssemble->membersMap.insert({ member->getGUID(), 3 }); } @@ -2907,7 +2907,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { std::string raceName = msg.getString(); race = g_iobestiary().findRaceByName(raceName); - if (race.size() == 0) { + if (race.empty()) { g_logger().warn("[ProtocolGame::parseBestiarysendCreature] - " "Race was not found: {}, search: {}", raceName, search); @@ -2921,7 +2921,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { newmsg.add<uint16_t>(race.size()); std::map<uint16_t, uint32_t> creaturesKilled = g_iobestiary().getBestiaryKillCountByMonsterIDs(player, race); - for (auto it_ : race) { + for (const auto &it_ : race) { uint16_t raceid_ = it_.first; newmsg.add<uint16_t>(raceid_); @@ -3889,14 +3889,14 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { auto startFamiliars = msg.getBufferPosition(); msg.skipBytes(2); const auto familiars = Familiars::getInstance().getFamiliars(player->getVocationId()); - for (const Familiar &familiar : familiars) { - const std::string type = familiar.type; + for (const auto &familiar : familiars) { + const std::string type = familiar->type; if (!player->getFamiliar(familiar)) { continue; } ++familiarsSize; - msg.add<uint16_t>(familiar.lookType); - msg.addString(familiar.name, "ProtocolGame::sendCyclopediaCharacterOutfitsMounts - familiar.name"); + msg.add<uint16_t>(familiar->lookType); + msg.addString(familiar->name, "ProtocolGame::sendCyclopediaCharacterOutfitsMounts - familiar.name"); if (type == "quest") { msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_QUEST); } else { @@ -4151,7 +4151,7 @@ void ProtocolGame::sendBasicData() { // Send total size of spells msg.add<uint16_t>(validSpells.size()); // Send each spell valid ids - for (auto spell : validSpells) { + for (const auto &spell : validSpells) { if (!spell) { continue; } @@ -4488,7 +4488,7 @@ void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr<Container> contain msg.addByte(0x00); } else if (container->getID() == ITEM_STORE_INBOX && !itemsStoreInboxToSend.empty()) { msg.addByte(std::min<uint32_t>(maxItemsToSend, containerSize)); - for (const auto item : itemsStoreInboxToSend) { + for (const auto &item : itemsStoreInboxToSend) { AddItem(msg, item); } } else { @@ -4616,7 +4616,7 @@ void ProtocolGame::sendShop(std::shared_ptr<Npc> npc) { msg.add<uint16_t>(itemsToSend); uint16_t i = 0; - for (ShopBlock shopBlock : shoplist) { + for (const ShopBlock &shopBlock : shoplist) { if (++i > itemsToSend) { break; } @@ -4699,7 +4699,7 @@ void ProtocolGame::sendSaleItemList(const std::vector<ShopBlock> &shopVector, co msg.addByte(0xEE); msg.addByte(0x00); msg.add<uint64_t>(player->getBankBalance()); - uint16_t currency = player->getShopOwner() ? player->getShopOwner()->getCurrency() : ITEM_GOLD_COIN; + uint16_t currency = player->getShopOwner() ? player->getShopOwner()->getCurrency() : static_cast<uint16_t>(ITEM_GOLD_COIN); msg.addByte(0xEE); if (currency == ITEM_GOLD_COIN) { msg.addByte(0x01); @@ -4724,7 +4724,7 @@ void ProtocolGame::sendSaleItemList(const std::vector<ShopBlock> &shopVector, co auto msgPosition = msg.getBufferPosition(); msg.skipBytes(1); - for (ShopBlock shopBlock : shopVector) { + for (const ShopBlock &shopBlock : shopVector) { if (shopBlock.itemSellPrice == 0) { continue; } @@ -5859,7 +5859,7 @@ void ProtocolGame::sendTradeItemRequest(const std::string &traderName, std::shar std::shared_ptr<Container> container = listContainer.front(); listContainer.pop_front(); - for (std::shared_ptr<Item> containerItem : container->getItemList()) { + for (const std::shared_ptr<Item> &containerItem : container->getItemList()) { std::shared_ptr<Container> tmpContainer = containerItem->getContainer(); if (tmpContainer) { listContainer.push_back(tmpContainer); @@ -5869,7 +5869,7 @@ void ProtocolGame::sendTradeItemRequest(const std::string &traderName, std::shar } msg.addByte(itemList.size()); - for (std::shared_ptr<Item> listItem : itemList) { + for (const std::shared_ptr<Item> &listItem : itemList) { AddItem(msg, listItem); } } else { @@ -6810,14 +6810,14 @@ void ProtocolGame::sendOutfitWindow() { } std::vector<std::shared_ptr<Mount>> mounts; - for (const auto mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().mounts.getMounts()) { if (player->hasMount(mount)) { mounts.push_back(mount); } } msg.addByte(mounts.size()); - for (const auto mount : mounts) { + for (const auto &mount : mounts) { msg.add<uint16_t>(mount->clientId); msg.addString(mount->name, "ProtocolGame::sendOutfitWindow - mount->name"); } @@ -6919,7 +6919,7 @@ void ProtocolGame::sendOutfitWindow() { msg.skipBytes(2); const auto mounts = g_game().mounts.getMounts(); - for (const auto mount : mounts) { + for (const auto &mount : mounts) { if (player->hasMount(mount)) { msg.add<uint16_t>(mount->clientId); msg.addString(mount->name, "ProtocolGame::sendOutfitWindow - mount->name"); @@ -6950,13 +6950,13 @@ void ProtocolGame::sendOutfitWindow() { const auto familiars = Familiars::getInstance().getFamiliars(player->getVocationId()); - for (const Familiar &familiar : familiars) { + for (const auto &familiar : familiars) { if (!player->getFamiliar(familiar)) { continue; } - msg.add<uint16_t>(familiar.lookType); - msg.addString(familiar.name, "ProtocolGame::sendOutfitWindow - familiar.name"); + msg.add<uint16_t>(familiar->lookType); + msg.addString(familiar->name, "ProtocolGame::sendOutfitWindow - familiar.name"); msg.addByte(0x00); if (++familiarSize == limitFamiliars) { break; @@ -7039,7 +7039,7 @@ void ProtocolGame::sendPodiumWindow(std::shared_ptr<Item> podium, const Position msg.skipBytes(2); const auto mounts = g_game().mounts.getMounts(); - for (const auto mount : mounts) { + for (const auto &mount : mounts) { if (player->hasMount(mount)) { msg.add<uint16_t>(mount->clientId); msg.addString(mount->name, "ProtocolGame::sendPodiumWindow - mount->name"); @@ -7432,7 +7432,7 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr<Creature> cr } auto bubble = creature->getSpeechBubble(); - msg.addByte(oldProtocol && bubble == SPEECHBUBBLE_HIRELING ? SPEECHBUBBLE_NONE : bubble); + msg.addByte(oldProtocol && bubble == SPEECHBUBBLE_HIRELING ? static_cast<uint8_t>(SPEECHBUBBLE_NONE) : bubble); msg.addByte(0xFF); // MARK_UNMARKED if (!oldProtocol) { msg.addByte(0x00); // inspection type @@ -7715,7 +7715,7 @@ void ProtocolGame::updatePartyTrackerAnalyzer(const std::shared_ptr<Party> party msg.addByte(static_cast<uint8_t>(party->priceType)); msg.addByte(static_cast<uint8_t>(party->membersData.size())); - for (const std::shared_ptr<PartyAnalyzer> analyzer : party->membersData) { + for (const std::shared_ptr<PartyAnalyzer> &analyzer : party->membersData) { msg.add<uint32_t>(analyzer->id); if (std::shared_ptr<Player> member = g_game().getPlayerByID(analyzer->id); !member || !member->getParty() || member->getParty() != party) { @@ -7734,7 +7734,7 @@ void ProtocolGame::updatePartyTrackerAnalyzer(const std::shared_ptr<Party> party msg.addByte(showNames ? 0x01 : 0x00); if (showNames) { msg.addByte(static_cast<uint8_t>(party->membersData.size())); - for (const std::shared_ptr<PartyAnalyzer> analyzer : party->membersData) { + for (const std::shared_ptr<PartyAnalyzer> &analyzer : party->membersData) { msg.add<uint32_t>(analyzer->id); msg.addString(analyzer->name, "ProtocolGame::updatePartyTrackerAnalyzer - analyzer->name"); } @@ -8573,7 +8573,7 @@ void ProtocolGame::parseSendBosstiarySlots() { std::string boostedBossName = g_ioBosstiary().getBoostedBossName(); const auto mTypeBoosted = g_monsters().getMonsterType(boostedBossName); auto boostedBossRace = mTypeBoosted ? mTypeBoosted->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; - auto isValidBoostedBoss = boostedBossId == 0 || boostedBossRace >= BosstiaryRarity_t::RARITY_BANE && boostedBossRace <= BosstiaryRarity_t::RARITY_NEMESIS; + auto isValidBoostedBoss = boostedBossId == 0 || (boostedBossRace >= BosstiaryRarity_t::RARITY_BANE && boostedBossRace <= BosstiaryRarity_t::RARITY_NEMESIS); if (!isValidBoostedBoss) { g_logger().error("[{}] The boosted boss '{}' has an invalid race", __FUNCTION__, boostedBossName); return; @@ -8581,7 +8581,7 @@ void ProtocolGame::parseSendBosstiarySlots() { const auto mTypeSlotOne = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotOne); auto bossRaceSlotOne = mTypeSlotOne ? mTypeSlotOne->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; - auto isValidBossSlotOne = bossIdSlotOne == 0 || bossRaceSlotOne >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotOne <= BosstiaryRarity_t::RARITY_NEMESIS; + auto isValidBossSlotOne = bossIdSlotOne == 0 || (bossRaceSlotOne >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotOne <= BosstiaryRarity_t::RARITY_NEMESIS); if (!isValidBossSlotOne) { g_logger().error("[{}] boss slot1 with race id '{}' has an invalid race", __FUNCTION__, bossIdSlotOne); return; @@ -8589,7 +8589,7 @@ void ProtocolGame::parseSendBosstiarySlots() { const auto mTypeSlotTwo = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotTwo); auto bossRaceSlotTwo = mTypeSlotTwo ? mTypeSlotTwo->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; - auto isValidBossSlotTwo = bossIdSlotTwo == 0 || bossRaceSlotTwo >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotTwo <= BosstiaryRarity_t::RARITY_NEMESIS; + auto isValidBossSlotTwo = bossIdSlotTwo == 0 || (bossRaceSlotTwo >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotTwo <= BosstiaryRarity_t::RARITY_NEMESIS); if (!isValidBossSlotTwo) { g_logger().error("[{}] boss slot1 with race id '{}' has an invalid race", __FUNCTION__, bossIdSlotTwo); return; From c9d1035379e55419e6fa8c44bfb750f8090ebc08 Mon Sep 17 00:00:00 2001 From: Elson Costa <elsongabriel@hotmail.com> Date: Tue, 14 May 2024 17:57:10 -0300 Subject: [PATCH 39/95] fix: missing achievements (#2590) --- data/scripts/lib/register_achievements.lua | 67 ++++++++++++++-------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/data/scripts/lib/register_achievements.lua b/data/scripts/lib/register_achievements.lua index 18a2d6e41ef..23d4bbd93ca 100644 --- a/data/scripts/lib/register_achievements.lua +++ b/data/scripts/lib/register_achievements.lua @@ -58,7 +58,7 @@ ACHIEVEMENTS = { [57] = { name = "Annihilator", grade = 2, points = 5, description = "You've daringly jumped into the infamous Annihilator and survived - taking home fame, glory and your reward." }, [58] = { name = "Master of the Nexus", grade = 2, points = 6, description = "You were able to fight your way through the countless hordes in the Demon Forge. Once more you proved that nothing is impossible." }, [59] = { name = "Talented Dancer", grade = 1, points = 1, description = "You're a lord or lady of the dance - and not afraid to use your skills to impress tribal gods. One step to the left, one jump to the right, twist and shout!" }, - [60] = { name = "Ministrel", grade = 1, points = 2, secret = true, description = "You can handle any music instrument you're given - and actually manage to produce a pleasant sound with it. You're a welcome guest and entertainer in most taverns." }, + [60] = { name = "Allow Cookies?", grade = 1, points = 2, description = "With a perfectly harmless smile, you tricked all the funny guys into eating your exploding cookies. Next time you pull this prank, consider wearing a Boy Scout outfit to make it even better." }, [61] = { name = "Ruthless", grade = 2, points = 5, description = "You've touched all thrones of the Ruthless Seven and absorbed some of their evil spirit. It may have changed you forever." }, [62] = { name = "Champion of Chazorai", grade = 2, points = 4, description = "You won the merciless 2 vs. 2 team tournament on the Isle of Strife and wiped out wave after wave of fearsome opponents. Death or victory - you certainly chose the latter." }, [63] = { name = "Wayfarer", grade = 1, points = 3, secret = true, description = "Dragon dreams are golden." }, @@ -193,7 +193,7 @@ ACHIEVEMENTS = { [192] = { name = "I Like it Fancy", grade = 1, points = 1, secret = true, description = "You definitely know how to bring out the best in your furniture and decoration pieces. Beautiful." }, [193] = { name = "Skin-Deep", grade = 2, points = 4, secret = true, description = "You always carry your obsidian knife with you and won't hesitate to use it. You've skinned countless little - and bigger - critters and yeah: they usually don't get any more beautiful on the inside. It's rather blood and gore and all that..." }, [194] = { name = "Ashes to Dust", grade = 2, points = 4, secret = true, description = "Staking vampires and demons has almost turned into your profession. You make sure to gather even the tiniest amount of evil dust particles. Beware of silicosis." }, - [195] = { name = "Silent Pet", grade = 1, points = 1, secret = true, description = "Awww. Your very own little goldfish friend - he's cute, he's shiny and he can't complain should you forget to feed him. He'll definitely brighten up your day!" }, + -- [195] = Unknown/non-existent [196] = { name = "Safely Stored Away", grade = 1, points = 2, secret = true, description = "Don't worry, no one will be able to take it from you. Probably." }, [197] = { name = "Something's in There", grade = 1, points = 1, secret = true, description = "By the gods! What was that?" }, [198] = { name = "Silent Pet", grade = 1, points = 1, secret = true, description = "Awww. Your very own little goldfish friend - he's cute, he's shiny and he can't complain should you forget to feed him. He'll definitely brighten up your day!" }, @@ -218,7 +218,7 @@ ACHIEVEMENTS = { [217] = { name = "Doctor! Doctor!", grade = 1, points = 2, secret = true, description = "Did someone call a doctor? You delivered 100 medicine bags to Ottokar of the Venore poor house in times of dire need, well done!" }, [218] = { name = "Beak Doctor", grade = 2, points = 4, description = "You significantly helped the afflicted citizens of Venore in times of dire need. Somehow you still feel close to the victims of the fever outbreak. Your clothes make you one of them, one poor soul amongst the countless afflicted." }, [219] = { name = "Mystic Fabric Magic", grade = 2, points = 4, description = "You vanquished the mad mage, you subdued the raging mage - no spellweaving self-exposer can stand in your way. Yet you are quite absorbed in magical studies yourself. This very fabric reflects this personal approval of the magic arts." }, - [220] = { name = "Breaking the Ice", grade = 1, points = 1, description = "You almost made friends with Shardhead... before he died. Poor guy only seems to attract violence with his frosty attitude." }, + -- [220] = Unknown/non-existent [221] = { name = "Arachnoise", grade = 1, points = 1, description = "You've shattered each of Bloodweb's eight frozen legs. As they say: break a leg, and then some more." }, [222] = { name = "Rootless Behaviour", grade = 1, points = 1, description = "You've descended into the swampy depths of Deathbine's lair and made quick work of it." }, [223] = { name = "Twisted Mutation", grade = 1, points = 1, description = "You've slain Esmeralda, the most hideous and aggressive of the mutated rats. No one will know that you almost lost a finger in the process." }, @@ -248,7 +248,7 @@ ACHIEVEMENTS = { [247] = { name = "Torn Treasures", grade = 1, points = 1, secret = true, description = "Wyda seems to be really, really bored. You also found out that she doesn't really need all those blood herbs that adventurers brought her. Still, she was nice enough to take one from you and gave you something quite cool in exchange." }, [248] = { name = "Loyal Subject", grade = 1, points = 1, secret = true, description = "You joined the Kingsday festivities and payed King Tibianus your respects. Now, off to party!" }, [249] = { name = "Desert Fisher", grade = 1, points = 1, description = "You managed to catch a fish in a surrounding that usually doesn't even carry water. Everything is subject to change, probably..." }, - [250] = { name = "Gem Cutter", grade = 1, points = 1, secret = true, description = 'You cut your first gem - and it bears your own name! Now that would be a nice gift! This does not make it a "true" Heart of the Sea, however...' }, + -- [250] = Unknown/non-existent [251] = { name = "Dog Sitter", grade = 1, points = 1, description = "You showed Noodles the way home. How long will it take this time until he's on the loose again? That dog must be really bored in the throne room by now." }, [252] = { name = "Ice Harvester", grade = 1, points = 1, description = "You witnessed the thawing of Svargrond and harvested rare seeds from some strange icy plants. They must be good for something." }, [253] = { name = "Preservationist", grade = 1, points = 1, secret = true, description = "You are a pretty smart thinker and managed to create everlasting flowers. They might become a big hit with all the people who aren't blessed with a green thumb or just forgetful." }, @@ -273,7 +273,7 @@ ACHIEVEMENTS = { [272] = { name = "Headache", grade = 1, points = 2, description = "Even in the deepest structures of the hive, you began to strike against the mighty foe. Your actions probably already gave the hive a headache." }, [273] = { name = "Confusion", grade = 1, points = 3, description = "The destruction you have caused by now can be felt throughout the whole hive. The mayhem that follows your step caused significant confusion in the consciousness of the hive." }, [274] = { name = "Manic", grade = 2, points = 4, description = "You have destroyed a significant amount of the hive's vital nerve centers and caused massive destruction to the hive's awareness. You are probably causing the hive horrible nightmares." }, - [275] = { name = "Suppressor", grade = 2, points = 4, description = "A war is won by those who have the best supply of troops. The hive's troops have been dealt a significant blow by your actions. You interrupted the hive's replenishment of troops lastingly and severely." }, + -- [275] = Unknown/non-existent [276] = { name = "Navigational Error", grade = 2, points = 5, secret = true, description = "You confronted the Navigator." }, [277] = { name = "Si, Ariki!", grade = 1, points = 1, description = "You've found the oriental traveller Yasir and were able to trade with him - even if you didn't really understand his language." }, [278] = { name = "Guardian Downfall", grade = 2, points = 4, description = "You ended the life of over three hundred Deepling Guards. Not quite the guardian of the Deeplings, are you?" }, @@ -282,7 +282,7 @@ ACHIEVEMENTS = { [281] = { name = "Gem Cutter", grade = 1, points = 1, secret = true, description = 'You cut your first gem - and it bears your own name! Now that would be a nice gift! This does not make it a "true" Heart of the Sea, however...' }, [282] = { name = "Spolium Profundis", grade = 2, points = 4, description = "You travelled the depths of this very world. You entered the blackness of the deep sea to conquer the realm of the Deeplings. May this suit remind you of the strange beauty below." }, [283] = { name = "Bane of the Hive", grade = 1, points = 2, description = "Countless fights and never tiring effort in the war against the hive grant you the experience to finish your outfit with the last remaining part. Your chitin outfit is a testament of your skills and dedication for the cause." }, - [284] = { name = "King of the Ring", grade = 1, points = 2, description = "Bretzecutioner's body just got slammed away. You are a true king of the ring!" }, + -- [284] = Unknown/non-existent [285] = { name = "Hive War Veteran", grade = 1, points = 1, description = "Your invaluable experience in fighting the hive allows you to add another piece of armor to your chitin outfit to prove your dedication for the cause." }, [286] = { name = "Hive Fighter", grade = 1, points = 1, description = "You have participated that much in the hive war, that you are able to create some makeshift armor from the remains of dead hive born that can be found in the major hive, to show of your skill." }, [287] = { name = "Howly Silence", grade = 1, points = 1, description = "You muted the everlasting howling of Hemming." }, @@ -294,7 +294,7 @@ ACHIEVEMENTS = { [293] = { name = "King of the Ring", grade = 1, points = 2, description = "Bretzecutioner's body just got slammed away. You are a true king of the ring!" }, [294] = { name = "Back from the Dead", grade = 1, points = 2, description = "You overcame the undead Zanakeph and sent him back into the darkness that spawned him." }, [295] = { name = "Pwned All Fur", grade = 3, points = 8, secret = true, description = "You've faced and defeated each of the mighty bosses the Paw and Fur society sent you out to kill. All by yourself. What a hunt!" }, - [296] = { name = "Diplomatic Immunity", grade = 2, points = 4, secret = true, description = "You killed the ambassador of the abyss that often that they might consider sending another one. Perhaps that will one day stop further intrusions." }, + -- [296] = Unknown/non-existent [297] = { name = "Bibby's Bloodbath", grade = 1, points = 1, secret = true, description = "You lend a helping hand in defeating invading Orcs by destroying their warcamp along with their leader. Bibby's personal bloodbath..." }, [298] = { name = "Nestling", grade = 1, points = 1, description = "You cleansed the land from an eight legged nuisance by defeating Mamma Longlegs three times. She won't be back soon... or will she?" }, [299] = { name = "Becoming a Bigfoot", grade = 1, points = 1, description = "You did it! You convinced the reclusive gnomes to accept you as one of their Bigfoots. Now you are ready to help them. With big feet big missions seen to come." }, @@ -321,14 +321,14 @@ ACHIEVEMENTS = { [320] = { name = "Funghitastic", grade = 1, points = 3, description = "Finally your dream to become a walking mushroom has come true ... No, wait a minute!" }, [321] = { name = "Crystal Clear", grade = 1, points = 3, description = "If the gnomes had told you that crystal armor is see-through you had probably changed your underwear in time." }, [322] = { name = "Gnomish Art Of War", grade = 1, points = 3, description = "You have unleashed your inner gnome and slain some of the most fearsome threats that gnomekind has ever faced. Now you can come and go to the warzones as it pleases you. The enemies of gnomekind will never be safe again." }, - [323] = { name = "Never Surrender", grade = 1, points = 3, description = "You did not show any signs of surrender to any sight of... you get the picture. Even a hundred of them did not pose a threat to you." }, + -- [323] = Unknown/non-existent [324] = { name = "True Dedication", grade = 2, points = 5, secret = true, description = "You conquered the demon challenge and prevailed... now show off your success in style!" }, [325] = { name = "Task Manager", grade = 1, points = 2, secret = true, description = "Helping a poor, stupid goblin to feed his starving children and wifes feels good ... if you'd only get rid of the strange feeling that you're missing something." }, [326] = { name = "Gravedigger", grade = 1, points = 3, description = "Assisting Omrabas' sick plan to resurrect made you dig your way through the blood-soaked halls of Drefia. Maybe better he failed!" }, [327] = { name = "Repenter", grade = 1, points = 1, secret = true, description = "You cleansed your soul in serving the Repenter enclave and purified thine self in completing all tasks in a single day of labour." }, [328] = { name = "Umbral Swordsman", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your blade into a master state and have proven yourself worthy in a nightmarish world." }, - [329] = { name = "Umbral Berserker", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your hammer into a master state and have proven yourself worthy in a nightmarish world." }, - [330] = { name = "Umbral Bladelord", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your slayer into a master state and have proven yourself worthy in a nightmarish world." }, + -- [329] = Unknown/non-existent + -- [330] = Unknown/non-existent [331] = { name = "Cave Completionist", grade = 1, points = 2, description = "You have helped the gnomes of the spike in securing the caves and explored enough of the lightles depths to earn you a complete cave explorers outfit. Well done!" }, [332] = { name = "Umbral Bladelord", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your slayer into a master state and have proven yourself worthy in a nightmarish world." }, [333] = { name = "Umbral Headsman", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your axe into a master state and have proven yourself worthy in a nightmarish world." }, @@ -367,7 +367,7 @@ ACHIEVEMENTS = { [366] = { name = "Publicity", grade = 1, points = 1, description = "You are a man of the public. Or of good publicity at least. Through your efforts in advertising the airtight cloth, Zeronex might yet be redeemed - and Rathleton might yet see its first working Gloud Ship." }, [367] = { name = "Snake Charmer", grade = 1, points = 1, description = "By restoring the Everhungry Altar, you charmed the Fire-Feathered Sea Serpent back into its fitful sleep, twenty miles beneath the sea." }, [368] = { name = "Hoard of the Dragon", grade = 1, points = 1, secret = true, description = "Your adventurous way through countless dragon lairs earned you a pretty treasure - and surely the enmity of many a dragon." }, - [369] = { name = "Icy Glare", grade = 1, points = 1, description = "Here's looking at you, kid. This ancient creature seems to size you up with its brilliant eyes and barely tolerates you riding it. Maybe it thinks you're the defrosted snack, after all?" }, + -- [369] = Unknown/non-existent [370] = { name = "Little Ball of Wool", grade = 1, points = 1, description = "You found a lost sheep and thus a steady source of black wool. But careful: don't get entangled." }, [371] = { name = "Luminous Kitty", grade = 1, points = 3, description = "You made some efforts to bring a little more light into the world. And what a nice present you got in return!" }, [372] = { name = "The Right Tone", grade = 1, points = 1, description = "By setting the right tone you convinced a crystal wolf to accompany you. Remember it is made of crystal, though, so be careful in a banshee's presence." }, @@ -404,10 +404,10 @@ ACHIEVEMENTS = { [403] = { name = "Icy Glare", grade = 1, points = 1, description = "Here's looking at you, kid. This ancient creature seems to size you up with its brilliant eyes and barely tolerates you riding it. Maybe it thinks you're the defrosted snack, after all?" }, [404] = { name = "Cartography 101", grade = 1, points = 2, description = "You succeeded in finding and charting several previously unexplored landmarks and locations for the Adventurer's Guild, you probably never need to ask anyone for the way - do you?" }, [405] = { name = "Lost Palace Raider", grade = 1, points = 2, secret = true, description = "Lifting the secrets of a fabulous palace and defeating a beautiful demon princess was a thrilling experience indeed. This site's marvels nearly matched its terrors. Nearly." }, - [406] = { name = "The More the Merrier", grade = 0, points = 0, secret = true, description = "It's dangerous to go alone... Take ten friends." }, - [407] = { name = "Contender", grade = 1, points = 3, description = "You have fully unlocked 10 medium monsters in the cyclopedia." }, + [406] = { name = "The More the Merrier", grade = 1, points = 0, secret = true, description = "It's dangerous to go alone... Take ten friends." }, + -- [407] = Unknown/non-existent [408] = { name = "Rift Warrior", grade = 1, points = 3, description = "You went through hell. Seven times. You defeated the demons. Countless times. You put an end to Ferumbras claims to ascendancy. Once and for all." }, - [409] = { name = "Duked It Out", grade = 1, points = 1, description = "You defeated the Duke of the Depths and destroyed his lava pump!" }, + -- [409] = Unknown/non-existent [410] = { name = "Hat Hunter", grade = 2, points = 5, description = "You sucessfully fought against all odds to protect your world from an ascending god! – You weren't there for the hat only after all?" }, [411] = { name = "Ogre Chef", grade = 1, points = 1, description = "You didn't manage to become an ogre chief. But at least you are, beyond doubt, a worthy ogre chef." }, [412] = { name = "The Call of the Wild", grade = 1, points = 2, description = "You opposed man-eating ogres and clumsy clomps. You grappled with hungry chieftains, desperate goblins and angry spirits. So you truly overcame the wild vastness of Krailos." }, @@ -421,13 +421,13 @@ ACHIEVEMENTS = { [420] = { name = "Toothfairy Assistant", grade = 1, points = 1, description = "You assisted a very prominent fae and you fought tooth and nail to earn this title." }, [421] = { name = "Fairy Teasing", grade = 1, points = 1, secret = true, description = "Teasing fairies is fun. They leave behind such pretty clouds of glittering dust when chased. Just hope they don't get you back for it." }, [422] = { name = "Corruption Contained", grade = 2, points = 5, description = "You have managed to stall the worst incursion of corruption. Still this is just one battle won in an all out war for your world." }, - [423] = { name = "Daraman's Footsteps", grade = 1, points = 1, description = "You journeyed through Darashia and the sea of sand around it, while fighting the perils of the desert." }, - [424] = { name = "Dwarven Mines", grade = 1, points = 1, description = "Vast mines, an orc fortress and the magnificence of Kazordoon - you really know every corner of North-Eastern Mainland now." }, - [425] = { name = "Elven Woods", grade = 1, points = 1, description = "Tall trees, deep forests and and the beauty of Ab'Dendriel - you really know every corner of the elven lands now." }, - [426] = { name = "Glooth Punk", grade = 1, points = 1, description = "Glooth is the substance that powers a whole continent and all its weird inhabitants, workshops and factories. You travelled this strange smorgasbord of curiosities in its entirety - just in time for tea." }, - [427] = { name = "High and Dry", grade = 1, points = 2, description = "You asked Captain Charles to take a shortcut quite a few times. Now you are all too familiar with desert islands all over Tibia." }, - [428] = { name = "Jewel in the Swamp", grade = 1, points = 1, description = "Damp swamps, a dry desert and the opulence of Venore - you really know every corner of Eastern Mainland now." }, - [429] = { name = "King of the Jungle", grade = 1, points = 1, description = "You have searched Port Hope and the jungle that thoroughly, that you are up to adoption by a friendly ape family." }, + -- [423] = Unknown/non-existent + -- [424] = Unknown/non-existent + -- [425] = Unknown/non-existent + -- [426] = Unknown/non-existent + -- [427] = Unknown/non-existent + -- [428] = Unknown/non-existent + -- [429] = Unknown/non-existent [430] = { name = "Little Adventure", grade = 1, points = 1, description = "You have fully unlocked 10 easy monsters in the cyclopedia." }, [431] = { name = "Little Big Adventure", grade = 1, points = 2, secret = true, description = "You have fully unlocked 100 easy monsters in the cyclopedia." }, [432] = { name = "Contender", grade = 1, points = 3, description = "You have fully unlocked 10 medium monsters in the cyclopedia." }, @@ -470,7 +470,7 @@ ACHIEVEMENTS = { [469] = { name = "Battle Mage", grade = 2, points = 6, description = "Wielding dangerous knowledge as well as the sword is your expertise. You have proven yourself versatile in all manner of situations." }, [470] = { name = "Widely Travelled", grade = 3, points = 7, description = "As a true globetrotter you can now show your colours proudly with this extraordinary outfit." }, [471] = { name = "Running the Rift", grade = 1, points = 3, description = "You don't just have a permission to ride a rift runner, you literally went through hell and earned it!" }, - [472] = { name = "Nothing but Hot Air", grade = 1, points = 2, description = "You have tamed the ghostly mists to do your bidding. For now ..." }, + -- [472] = Unknown/non-existent [473] = { name = "Exalted Battle Mage", grade = 1, points = 2, description = "Not only did you master the battlefield as a mage, you were also induced to the most inner secrets of the art of magical warfare and prevailed." }, [474] = { name = "Areas of Effect", grade = 1, points = 3, secret = true, description = "Wisely contributing your resources to areas, you pushed creatures to maximum effect, allowing improved respawn for everyone! Well done!" }, [475] = { name = "Tied the Knot", grade = 1, points = 1, secret = true, description = "You figured out the right order of spells in the buried cathedral, how enchanting!" }, @@ -483,7 +483,7 @@ ACHIEVEMENTS = { [482] = { name = "Dream Catcher", grade = 1, points = 3, description = "You are the slayer of the ancient nightmare beast and prevented the nightmare to spread its madness." }, [483] = { name = "Champion of Summer", grade = 1, points = 2, secret = true, description = "You have vanquished numerous arena champions in the name of the Summer Court." }, [484] = { name = "Champion of Winter", grade = 1, points = 2, secret = true, description = "You have vanquished numerous arena champions in the name of the Winter Court." }, - [485] = { name = "Allow Cookies?", grade = 2, points = 6, description = "With a perfectly harmless smile, you tricked all the funny guys into eating your exploding cookies. Next time you pull this prank, consider wearing a Boy Scout outfit to make it even better." }, + -- [485] = Unknown/non-existent [486] = { name = "Bewitcher", grade = 2, points = 5, secret = true, description = "You literally put everything in that cauldron except lilac and gooseberries." }, [487] = { name = "Gryphon Rider", grade = 1, points = 3, description = "Unmasking spies, killing demons, discovering omens, solving puzzles and fighting ogres, manticores and feral sphinxes. - Nobody said it was easy to become a gryphon rider." }, [488] = { name = "Sculptor Apprentice", grade = 1, points = 2, secret = true, description = "Granted, you didn't carve those lifelike animal figurines yourself. But helping a medusa to find proper objects and even watching her using her petrifying gaze is almost as rewarding." }, @@ -496,6 +496,7 @@ ACHIEVEMENTS = { [495] = { name = "Inquisition's Arm", grade = 1, points = 2, description = "Your special garb, solely awarded to a dedicated Hand of the Inquisition, is now complete." }, [496] = { name = "Traditionalist", grade = 2, points = 6, description = "You proudly wear the traditional Orcsoberfest garb, same as it ever was and as it always will be." }, [497] = { name = "Do a Barrel Roll!", grade = 1, points = 3, description = "Riding a traditional beer barrel from the Orcsoberfest is a once-in-a-lifetime experience. Beer sold separately." }, + -- [498] = Unknown/non-existent [499] = { name = "Orcsoberfest Welcome", grade = 1, points = 3, secret = true, description = 'The Orcsoberfest is not only known for its traditional food, beer and customs but also fun events and excitement! You took part in all of that and can now truly say: "I survived!"' }, [500] = { name = "Prospectre", grade = 1, points = 1, secret = true, description = "You made acquaintance with the Thaian. A strange contemporary with a dark history. No man but a derivate of greed and obsession." }, [501] = { name = "Nothing but Hot Air", grade = 1, points = 3, description = "You have tamed the ghostly mists to do your bidding. For now ..." }, @@ -506,19 +507,24 @@ ACHIEVEMENTS = { [506] = { name = "Falconer", grade = 1, points = 2, description = "A true beastmaster learns the language of his animal companions. Now you as well can bolster your unique bond with nature and help preserve the balance of life as a proud falconer." }, [507] = { name = "Steppe Elegance", grade = 1, points = 3, description = "Champion of the wildlands, a swift strider among the creatures of the wild. The elegant nature of the gallop, this envoy of speed has mastered, indicates the precise understanding of its terrain and environment." }, [508] = { name = "Beyonder", grade = 1, points = 3, description = "Adventurous beyond death, you travelled the Netherworld. Although you had just the ghost of a chance you survived and even came back from the realm of the dead." }, + -- [509] = Unknown/non-existent [510] = { name = "Drama in Darama", grade = 1, points = 3, description = "If a pride of lions and a pack of hyaenas feud, it is not called a catfight but a ... whatsoever. For sure, it caused a lot of drama in the Darama Desert." }, [511] = { name = "Malefitz", grade = 1, points = 1, secret = true, description = "Made acquaintance with three brothers Fitz." }, [512] = { name = "Lionheart", grade = 1, points = 3, description = "You bested the maleficent duo Drume and Fugue and restored order to the besieged town of Bounac. You conquered the exotic stronghold of the Order of the Cobra and bested the undead knights of the Order of the Falcon. A true knight in heart and mind." }, + [513] = { name = "Soul Mender", grade = 4, points = 10, description = "Brought back to the realm of the living this magnificent creature will carry you through death and everything that lays beyond." }, [514] = { name = "You Got Horse Power", grade = 3, points = 8, description = "Brought back to the realm of the living this magnificent creature will carry you through death and everything that lays beyond." }, [515] = { name = "Unleash the Beast", grade = 3, points = 8, description = "You defeated the manifestation of Goshnar's evil traits by fighting your way through beasts you didn't even want to imagine. It transformed you and now you can also look the part." }, [516] = { name = "Well Roared, Lion!", grade = 1, points = 1, description = "You helped Domizian and thus proved yourself worthy to enter the werelion sanctum underneath Lion's Rock. You faced the mighty werelions there and one of the rare white lions even chose to accompany you." }, + -- [517] = Unknown/non-existent [518] = { name = "Honorary Rascoohan", grade = 1, points = 2, description = "When in Rascacoon, do as the Rascoohans do!" }, [519] = { name = "Release the Kraken", grade = 1, points = 3, description = "Riding around on this squishy companion gives you the feeling of flying through the air... uhm... swimming through the seven seas!" }, + -- [520] = Unknown/non-existent [521] = { name = "Pied Piper", grade = 1, points = 3, secret = true, description = "You are not exactly the Pied Piper of Hamelin but at least you managed to fend off a decent amount of pirats and helped to keep them out of the cities." }, [522] = { name = "Woodcarver", grade = 1, points = 3, secret = true, description = "You defeated Megasylvan Yselda in the wake of the sleeping carnisylvan menace deep under Bounac." }, [523] = { name = "Bounacean Chivalry", grade = 1, points = 2, secret = true, description = "Yselda forever stands watch against the carnisylvan menace. Ever awake, waiting in the dark, her heart longs to be united with her king once again. Deep empathy let a hero to bring her Kesar's tulip as a token of his love. That hero was you." }, [524] = { name = "Knowledge Raider", grade = 1, points = 3, description = "Your thirst for knowledge is insatiable. In the task of helping your gnomish friends, flawless execution is just the icing on the cake." }, [525] = { name = "Citizen of Issavi", grade = 1, points = 2, description = "It was not the first time that you helped the Sapphire Blade or the Midnight Flame with a difficult task. You may now wear the Kilmareshian robes as well as the tagralt blade and the eye-embroidered veil of the seers as a sign of Issavi's gratitude." }, + [526] = { name = "King's Council", grade = 1, points = 0, description = "Your continued efforts in keeping Bounac and the people of Kesar the Younger safe, earned you a permanent place at the royal court as an advisor to the king." }, [527] = { name = "Hot on the Trail", grade = 1, points = 3, description = "Since it is fireproof, this flaming creature feels right at home in raging infernos. But remember: just because it doesn't burn, you still do!" }, [528] = { name = "Shell We Take a Ride", grade = 1, points = 3, description = "Equipped with the shell of a tortoise and claws of a lobster this insect like companion will help you through every hardship." }, [529] = { name = "Phantastic!", grade = 1, points = 3, description = "This mighty pachyderm will march into battle as if just taking its Sunday stroll. The cost of friendship was only a few drome points!" }, @@ -526,7 +532,22 @@ ACHIEVEMENTS = { [531] = { name = "First Achievement", grade = 1, points = 1, secret = true, description = "Congratulations to your very first achievement! ... Well, not really. But imagine, it is. Because at this point during your journey into Tibia's past, achievements have been introduced." }, [532] = { name = "Sharp Dressed", grade = 1, points = 2, description = "Just everyone will be crazy about you if you are wearing this formal dress. They will come running, promise!" }, [533] = { name = "Engine Driver", grade = 1, points = 3, description = "This glooth-driven locomotive will bring you to any party in the blink of an eye." }, + [534] = { name = "Friendly Fire", grade = 1, points = 2, description = "You mastered the fire and tamed a supervulcano!" }, + [535] = { name = "Wedding Planner", grade = 1, points = 3, description = "Alas! What could be more beautiful and satisfying than bringing two loving hearts together? So romantic!" }, + [536] = { name = "Beaver Away", grade = 1, points = 1, description = "You really were as busy as a beaver in order to help the nagas. Enjoy some eager company!" }, + [537] = { name = "Snake Pit", grade = 1, points = 1, description = "Mysterious nagas, a vibrant jungle and a sinking island - you really know every corner of Marapur now." }, + [538] = { name = "Royalty of Hazard", grade = 1, points = 1, description = "For some it can't be hazardous enough." }, + [539] = { name = "Measuring the World", grade = 1, points = 2, description = "Step by step you discovered many of the secrets hidden in the world, thus gaining the right to wear the Discoverer outfit and hat. Made-to-measure for a brave traveller of the Tibian wilds." }, [540] = { name = "Ripp-Ripp Hooray!", grade = 1, points = 3, description = "Don't get carried away by your success. Get carried away by your Ripptor." }, + [541] = { name = "Warrior of the Iks", grade = 1, points = 2, description = "Combining unabating courage in combat and respect for the traditions and culture of the ancient Iks earned you the honours of true Aucar." }, + [542] = { name = "Mutagenius", grade = 1, points = 2, description = "You accomplished the impossible and created 16 mutagens of corresponding colours." }, + [543] = { name = "Strangest Thing", grade = 1, points = 3, description = "Only its rider can love this abomination of a mount." }, + [544] = { name = "Fully Decayed", grade = 1, points = 2, description = "You defeated the embodiments of decay and live to tell the tale, wear the rotting attire of the unfaltering defender proudly." }, + [545] = { name = "Like Fox and Mouse", grade = 1, points = 3, description = "Sly as a fox, quiet as a mouse - the perfect mount for a stealthy foray." }, + [546] = { name = "The Spirit of Purity", grade = 1, points = 3, description = "Withstanding both filth and desolation of the rotten darkness that corrupted the very core of this world, you embodied the weapon of purity and light to defy all that was tainted. This spirit will continue guide you on all future paths." }, + [547] = { name = "Museum Goer", grade = 1, points = 2, description = "You unveiled the secret plot of the Mitmah who stole away an entire civilisation for their own entertainment. Let the death of their outpost vanguard be an eternal lesson to them." }, + [548] = { name = "Mystic Predator", grade = 1, points = 3, description = "Proving your true worth to a mystic creature like the jaguar, king of the hunt, granted you not only respect but also its heart." }, + [549] = { name = "The Rule of Raccool", grade = 1, points = 2, description = "You almost feel as cool as a raccoon. Now, where's the trash?" }, } --[[ From 84c9658dc1c6882a38ec8f847d62f9ce29b40859 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Wed, 15 May 2024 20:27:51 -0300 Subject: [PATCH 40/95] fix: sonar bugs and gha checks (#2633) Fix to use std::cmp_greater and iteration using total ++ Co-authored-by: Elson Costa <elsongabriel@hotmail.com> --- .github/workflows/build-ubuntu-dummy.yml | 4 +--- .github/workflows/build-ubuntu.yml | 4 +--- .../players/cyclopedia/player_title.cpp | 2 +- .../players/cyclopedia/player_title.hpp | 21 ++++++++++--------- src/game/game.cpp | 1 - src/items/items.hpp | 2 +- src/utils/tools.cpp | 13 ++++++------ 7 files changed, 21 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-ubuntu-dummy.yml b/.github/workflows/build-ubuntu-dummy.yml index 00c4efdef87..f4ebb00ff27 100644 --- a/.github/workflows/build-ubuntu-dummy.yml +++ b/.github/workflows/build-ubuntu-dummy.yml @@ -17,11 +17,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-22.04] + os: [ubuntu-22.04] buildtype: [linux-release, linux-debug] include: - - os: ubuntu-20.04 - triplet: x64-linux - os: ubuntu-22.04 triplet: x64-linux diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index cdc54dc33f2..2b369e34875 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -36,11 +36,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-22.04] + os: [ubuntu-22.04] buildtype: [linux-release, linux-debug] include: - - os: ubuntu-20.04 - triplet: x64-linux - os: ubuntu-22.04 triplet: x64-linux diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp index 9e1c763a4ad..624b0313457 100644 --- a/src/creatures/players/cyclopedia/player_title.cpp +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -185,7 +185,7 @@ bool PlayerTitle::checkMount(uint32_t amount) { uint8_t total = 0; for (const auto &mount : g_game().mounts.getMounts()) { if (m_player.hasMount(mount)) { - total = total++; + total++; } } return total >= amount; diff --git a/src/creatures/players/cyclopedia/player_title.hpp b/src/creatures/players/cyclopedia/player_title.hpp index abf29650961..0530fcc083d 100644 --- a/src/creatures/players/cyclopedia/player_title.hpp +++ b/src/creatures/players/cyclopedia/player_title.hpp @@ -32,27 +32,28 @@ struct Title { Title() = default; Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, uint32_t amount, bool permanent) : - m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_amount(amount), - m_permanent(permanent) { } + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(), m_description(std::move(description)), + m_amount(amount), m_permanent(permanent), m_skill(0), m_race(0) { } Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, uint32_t amount, bool permanent, std::string femaleName) : - m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_amount(amount), - m_permanent(permanent), m_femaleName(std::move(femaleName)) { } + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), m_description(std::move(description)), + m_amount(amount), m_permanent(permanent), m_skill(0), m_race(0) { } Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string femaleName, std::string description, uint8_t skill) : m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), m_description(std::move(description)), - m_skill(skill) { } + m_amount(0), m_permanent(false), m_skill(skill), m_race(0) { } Title(uint8_t id, CyclopediaTitle_t type, uint16_t race, std::string maleName, std::string femaleName, std::string description) : - m_id(id), m_type(type), m_race(race), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), - m_description(std::move(description)) { } + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), m_description(std::move(description)), + m_amount(0), m_permanent(false), m_skill(0), m_race(race) { } Title(uint8_t id, CyclopediaTitle_t type, uint16_t race, std::string maleName, std::string femaleName, std::string description, uint32_t amount, bool permanent) : - m_id(id), m_type(type), m_race(race), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), - m_description(std::move(description)), m_amount(amount), m_permanent(permanent) { } + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(std::move(femaleName)), m_description(std::move(description)), + m_amount(amount), m_permanent(permanent), m_skill(0), m_race(race) { } Title(uint8_t id, CyclopediaTitle_t type, std::string maleName, std::string description, bool permanent) : - m_id(id), m_type(type), m_maleName(std::move(maleName)), m_description(std::move(description)), m_permanent(permanent) { } + m_id(id), m_type(type), m_maleName(std::move(maleName)), m_femaleName(), m_description(std::move(description)), + m_amount(0), m_permanent(permanent), m_skill(0), m_race(0) { } bool operator==(const Title &other) const { return m_id == other.m_id; diff --git a/src/game/game.cpp b/src/game/game.cpp index 1743edb2f32..67a02ea8523 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8266,7 +8266,6 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) { } void Game::playerFriendSystemAction(std::shared_ptr<Player> player, uint8_t type, uint8_t titleId) { - uint32_t playerGUID = player->getGUID(); if (type == 0x0E) { player->title()->setCurrentTitle(titleId); player->sendCyclopediaCharacterBaseInformation(); diff --git a/src/items/items.hpp b/src/items/items.hpp index 82af0659af4..e56678af6f3 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -447,7 +447,7 @@ class Items { static const std::string getAugmentNameByType(Augment_t augmentType); - static const bool isAugmentWithoutValueDescription(Augment_t augmentType) { + static bool isAugmentWithoutValueDescription(Augment_t augmentType) { static std::vector<Augment_t> vector = { Augment_t::IncreasedDamage, Augment_t::PowerfulImpact, diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index b1fe671f416..584d4a6b5b6 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1560,16 +1560,15 @@ void capitalizeWords(std::string &source) { void capitalizeWordsIgnoringString(std::string &source, const std::string stringToIgnore) { toLowerCaseString(source); - uint8_t size = (uint8_t)source.size(); - uint8_t indexFound = source.find(stringToIgnore); + auto size = static_cast<uint8_t>(source.size()); + auto indexFound = source.find(stringToIgnore); + for (uint8_t i = 0; i < size; i++) { - if (indexFound != std::string::npos && (i > indexFound - 1) && i < (indexFound + stringToIgnore.size())) { + if (indexFound != std::string::npos && indexFound > 0 && std::cmp_greater(i, static_cast<uint8_t>(indexFound - 1)) && i < (indexFound + stringToIgnore.size())) { continue; } - if (i == 0) { - source[i] = (char)toupper(source[i]); - } else if (source[i - 1] == ' ' || source[i - 1] == '\'') { - source[i] = (char)toupper(source[i]); + if (i == 0 || source[i - 1] == ' ' || source[i - 1] == '\'') { + source[i] = static_cast<char>(std::toupper(source[i])); } } } From b83c9b44d12f36705195220a62a7e8a656d39731 Mon Sep 17 00:00:00 2001 From: Aluisio Penna <aluisioordones1@gmail.com> Date: Wed, 15 May 2024 21:11:45 -0300 Subject: [PATCH 41/95] fix: log error 'primal pack beast not exist' and health display (#2598) Resolves the log error stating "Monster with name primal pack beast not exist" that appears when a player engages with 'the primal menace'. It also corrects the health bar display for primal beasts to ensure it appears filled upon their creation, aligning with the global standards for creature health displays. These adjustments improve both the accuracy of the game's logging system and the visual feedback provided to players during encounters with primal beasts. --- data-otservbr-global/lib/quests/the_primal_ordeal.lua | 6 ++---- .../quests/primal_ordeal_quest/the_primal_menace.lua | 3 --- src/game/game.cpp | 5 +++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/data-otservbr-global/lib/quests/the_primal_ordeal.lua b/data-otservbr-global/lib/quests/the_primal_ordeal.lua index de708ee4de2..24324e1147b 100644 --- a/data-otservbr-global/lib/quests/the_primal_ordeal.lua +++ b/data-otservbr-global/lib/quests/the_primal_ordeal.lua @@ -6,12 +6,10 @@ function RegisterPrimalPackBeast(template) primalMonster.loot = {} primalMonster.name = "Primal Pack Beast" primalMonster.description = "a primal pack beast" - - primalMonster.health = primalMonster.health - primalMonster.maxHealth = primalMonster.maxHealth + primalMonster.maxHealth = primalMonster.maxHealth * 0.7 + primalMonster.health = primalMonster.maxHealth primalMonster.raceId = nil primalMonster.Bestiary = nil primalMonster.corpse = 0 - primal:register(primalMonster) end diff --git a/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua b/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua index b0bc59364d6..7ad43b016c9 100644 --- a/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua +++ b/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua @@ -24,7 +24,6 @@ local thePrimalMenaceConfig = { CountGrowthPerHazard = 1.05, CountMax = 6, - HpRateOnSpawn = 0.7, MonsterPool = { "Emerald Tortoise (Primal)", "Gore Horn (Primal)", @@ -291,8 +290,6 @@ local function spawnMonster(monsterId, spawnPosition) MonsterId = primalMonster:getId(), Created = os.time(), } - local monsterMaxHealth = primalMonster:getMaxHealth() - primalMonster:setHealth(monsterMaxHealth * thePrimalMenaceConfig.MonsterConfig.HpRateOnSpawn) local primalBeasts = monster:getStorageValue(thePrimalMenaceConfig.Storage.PrimalBeasts) table.insert(primalBeasts, primalBeastEntry) diff --git a/src/game/game.cpp b/src/game/game.cpp index 67a02ea8523..00ec056ccb3 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6493,9 +6493,10 @@ bool Game::combatBlockHit(CombatDamage &damage, std::shared_ptr<Creature> attack InternalGame::sendBlockEffect(primaryBlockType, damage.primary.type, target->getPosition(), attacker); // Damage reflection primary if (!damage.extension && attacker) { - if (targetPlayer && attacker->getMonster() && damage.primary.type != COMBAT_HEALING) { + std::shared_ptr<Monster> attackerMonster = attacker->getMonster(); + if (attackerMonster && targetPlayer && damage.primary.type != COMBAT_HEALING) { // Charm rune (target as player) - const auto mType = g_monsters().getMonsterType(attacker->getName()); + const auto &mType = attackerMonster->getMonsterType(); if (mType) { charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, mType); if (activeCharm == CHARM_PARRY) { From 216653966cb2be4416de80b1aef698c71605f021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Fri, 17 May 2024 09:42:33 -0300 Subject: [PATCH 42/95] fix: doctor marrow's spell null reference (#2624) --- .../monster/doctor_marrow_explosion.lua | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua b/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua index f616e8b8cee..491d9f2516d 100644 --- a/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua +++ b/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua @@ -21,7 +21,7 @@ end local spell = Spell("instant") function onTargetCreature(creature, target) - if not targetPos then + if not target then return true end local master = target:getMaster() @@ -29,7 +29,7 @@ function onTargetCreature(creature, target) return true end - local distance = math.floor(targetPos:getDistance(target:getPosition())) + local distance = math.floor(creature:getPosition():getDistance(target:getPosition())) local actualDamage = damage / (2 ^ distance) doTargetCombatHealth(0, target, COMBAT_EARTHDAMAGE, actualDamage, actualDamage, CONST_ME_NONE) if crit then @@ -63,21 +63,26 @@ function spell.onCastSpell(creature, var) end, i * 100, targetPos) end - addEvent(function(cid) + addEvent(function(cid, pos) local creature = Creature(cid) - creature:getPosition():sendMagicEffect(CONST_ME_ORANGE_ENERGY_SPARK) - targetPos:sendMagicEffect(CONST_ME_ORANGETELEPORT) - end, 2000, creature:getId()) + if creature then + creature:getPosition():sendMagicEffect(CONST_ME_ORANGE_ENERGY_SPARK) + pos:sendMagicEffect(CONST_ME_ORANGETELEPORT) + end + end, 2000, creature:getId(), targetPos) addEvent(function(cid, pos) - damage = -math.random(3500, 7000) - if math.random(1, 100) <= 10 then - crit = true - damage = damage * 1.5 - else - crit = false + local creature = Creature(cid) + if creature then + damage = -math.random(3500, 7000) + if math.random(1, 100) <= 10 then + crit = true + damage = damage * 1.5 + else + crit = false + end + spellCombat:execute(creature, Variant(pos)) end - spellCombat:execute(creature, Variant(pos)) end, totalDelay, creature:getId(), targetPos) return true end From c67424169752c1e417dc86bd0843993745d17598 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Fri, 17 May 2024 21:17:20 -0300 Subject: [PATCH 43/95] fix: check nil storage key from setStorageValue (#2636) When a key from Lua is nil, it is automatically transformed into 0 without being checked. Consequently, even with a nil key, no error is thrown, and the code continues as if everything had occurred normally, resulting in the storage not being set. Removed two duplicated checks in creature.cpp; they are already verified in the if statement above. --- src/creatures/creature.cpp | 2 +- src/lua/functions/creatures/player/player_functions.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 43e7ab89ccf..7796863f066 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -824,7 +824,7 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared auto isReachable = g_game().map.getPathMatching(player->getPosition(), dirList, FrozenPathingConditionCall(corpse->getPosition()), fpp); - if (player->checkAutoLoot(monster->isRewardBoss()) && corpseContainer && mostDamageCreature->getPlayer() && isReachable) { + if (player->checkAutoLoot(monster->isRewardBoss()) && isReachable) { g_dispatcher().addEvent([player, corpseContainer, corpsePosition = corpse->getPosition()] { g_game().playerQuickLootCorpse(player, corpseContainer, corpsePosition); }, diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 7f4d96f3773..50774e35e2a 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1744,6 +1744,11 @@ int PlayerFunctions::luaPlayerSetStorageValue(lua_State* L) { return 1; } + if (key == 0) { + reportErrorFunc("Storage key is nil"); + return 1; + } + if (player) { player->addStorageValue(key, value); pushBoolean(L, true); From 7dda937c32e7caaf66151e5152c9ab7b86c81b39 Mon Sep 17 00:00:00 2001 From: Guilherme <guilherme.vrsantana@gmail.com> Date: Mon, 20 May 2024 12:41:57 -0300 Subject: [PATCH 44/95] fix: items on npcs shop, update monsters spawn and items (#2616) --- data-otservbr-global/npc/alaistar.lua | 5 ++- data-otservbr-global/npc/alexander.lua | 22 +++++++++ data-otservbr-global/npc/asima.lua | 33 ++++++++++++++ data-otservbr-global/npc/briasol.lua | 2 +- data-otservbr-global/npc/chantalle.lua | 2 +- data-otservbr-global/npc/chuckles.lua | 32 +++++++++++++ data-otservbr-global/npc/edmund.lua | 2 +- data-otservbr-global/npc/fenech.lua | 14 ++++++ data-otservbr-global/npc/frans.lua | 14 ++++++ data-otservbr-global/npc/frederik.lua | 14 ++++++ data-otservbr-global/npc/gail.lua | 2 +- data-otservbr-global/npc/gnomegica.lua | 14 ++++++ data-otservbr-global/npc/hanna.lua | 2 +- data-otservbr-global/npc/ishina.lua | 2 +- data-otservbr-global/npc/iwan.lua | 2 +- data-otservbr-global/npc/jessica.lua | 2 +- data-otservbr-global/npc/khanna.lua | 42 ++++++++++++++++- data-otservbr-global/npc/mordecai.lua | 11 +++++ data-otservbr-global/npc/nelly.lua | 14 ++++++ data-otservbr-global/npc/nipuna.lua | 11 +++++ data-otservbr-global/npc/odemara.lua | 2 +- data-otservbr-global/npc/oiriz.lua | 2 +- data-otservbr-global/npc/rabaz.lua | 14 ++++++ data-otservbr-global/npc/rachel.lua | 17 +++++++ data-otservbr-global/npc/romir.lua | 14 ++++++ data-otservbr-global/npc/shiriel.lua | 14 ++++++ data-otservbr-global/npc/sigurd.lua | 14 ++++++ data-otservbr-global/npc/sundara.lua | 11 +++++ data-otservbr-global/npc/tandros.lua | 14 ++++++ data-otservbr-global/npc/tesha.lua | 2 +- data-otservbr-global/npc/tezila.lua | 2 +- data-otservbr-global/npc/topsy.lua | 14 ++++++ data-otservbr-global/npc/valindara.lua | 2 +- data-otservbr-global/npc/xodet.lua | 14 ++++++ data-otservbr-global/npc/yasir.lua | 10 +++++ data-otservbr-global/npc/yonan.lua | 2 +- .../world/otservbr-monster.xml | 45 ++++--------------- data/items/items.xml | 16 ++++++- 38 files changed, 396 insertions(+), 55 deletions(-) diff --git a/data-otservbr-global/npc/alaistar.lua b/data-otservbr-global/npc/alaistar.lua index e893975b24b..68aaa75679a 100644 --- a/data-otservbr-global/npc/alaistar.lua +++ b/data-otservbr-global/npc/alaistar.lua @@ -36,13 +36,14 @@ local itemsTable = { { itemName = "strong health potion", clientId = 236, buy = 115 }, { itemName = "strong mana potion", clientId = 237, buy = 93 }, { itemName = "supreme health potion", clientId = 23375, buy = 625 }, - { itemName = "ultimate health potion", clientId = 7643, buy = 438 }, - { itemName = "ultimate mana potion", clientId = 23373, buy = 379 }, + { itemName = "ultimate health potion", clientId = 7643, buy = 379 }, + { itemName = "ultimate mana potion", clientId = 23373, buy = 438 }, { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 }, { itemName = "vial", clientId = 2874, sell = 5 }, }, ["creature products"] = { { itemName = "cowbell", clientId = 21204, sell = 210 }, + { itemName = "execowtioner mask", clientId = 21201, sell = 240 }, { itemName = "giant pacifier", clientId = 21199, sell = 170 }, { itemName = "glob of glooth", clientId = 21182, sell = 125 }, { itemName = "glooth injection tube", clientId = 21103, sell = 350 }, diff --git a/data-otservbr-global/npc/alexander.lua b/data-otservbr-global/npc/alexander.lua index 78f51e486c8..fda2799bf4d 100644 --- a/data-otservbr-global/npc/alexander.lua +++ b/data-otservbr-global/npc/alexander.lua @@ -30,6 +30,28 @@ npcConfig.voices = { } local itemsTable = { + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["creature products"] = { + { itemName = "crystal ball", clientId = 3076, buy = 530, sell = 190 }, + { itemName = "life crystal", clientId = 3061, sell = 83 }, + { itemName = "mind stone", clientId = 3062, sell = 170 }, + }, + ["shields"] = { + { itemName = "spellbook of enlightenment", clientId = 8072, sell = 4000 }, + { itemName = "spellbook of warding", clientId = 8073, sell = 8000 }, + { itemName = "spellbook of mind control", clientId = 8074, sell = 13000 }, + { itemName = "spellbook of lost souls", clientId = 8075, sell = 19000 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, ["runes"] = { { itemName = "animate dead rune", clientId = 3203, buy = 375 }, { itemName = "blank rune", clientId = 3147, buy = 10 }, diff --git a/data-otservbr-global/npc/asima.lua b/data-otservbr-global/npc/asima.lua index 645689c42d4..c3aca3fcd5e 100644 --- a/data-otservbr-global/npc/asima.lua +++ b/data-otservbr-global/npc/asima.lua @@ -24,6 +24,14 @@ npcConfig.flags = { } local itemsTable = { + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, ["potions"] = { { itemName = "empty potion flask", clientId = 283, sell = 5 }, { itemName = "empty potion flask", clientId = 284, sell = 5 }, @@ -41,6 +49,12 @@ local itemsTable = { { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 }, { itemName = "vial", clientId = 2874, sell = 5 }, }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, ["runes"] = { { itemName = "avalanche rune", clientId = 3161, buy = 57 }, { itemName = "blank rune", clientId = 3147, buy = 10 }, @@ -61,8 +75,27 @@ local itemsTable = { { itemName = "poison field rune", clientId = 3172, buy = 21 }, { itemName = "poison wall rune", clientId = 3176, buy = 52 }, { itemName = "sudden death rune", clientId = 3155, buy = 135 }, + { itemName = "stalagmite rune", clientId = 3179, buy = 12 }, { itemName = "ultimate healing rune", clientId = 3160, buy = 175 }, }, + ["wands"] = { + { itemName = "hailstorm rod", clientId = 3067, buy = 15000 }, + { itemName = "moonlight rod", clientId = 3070, buy = 1000 }, + { itemName = "necrotic rod", clientId = 3069, buy = 5000 }, + { itemName = "northwind rod", clientId = 8083, buy = 7500 }, + { itemName = "snakebite rod", clientId = 3066, buy = 500 }, + { itemName = "springsprout rod", clientId = 8084, buy = 18000 }, + { itemName = "terra rod", clientId = 3065, buy = 10000 }, + { itemName = "underworld rod", clientId = 8082, buy = 22000 }, + { itemName = "wand of cosmic energy", clientId = 3073, buy = 10000 }, + { itemName = "wand of decay", clientId = 3072, buy = 5000 }, + { itemName = "wand of draconia", clientId = 8093, buy = 7500 }, + { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, + { itemName = "wand of inferno", clientId = 3071, buy = 15000 }, + { itemName = "wand of starstorm", clientId = 8092, buy = 18000 }, + { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, + { itemName = "wand of vortex", clientId = 3074, buy = 500 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/briasol.lua b/data-otservbr-global/npc/briasol.lua index 6905011fee9..38dcd074159 100644 --- a/data-otservbr-global/npc/briasol.lua +++ b/data-otservbr-global/npc/briasol.lua @@ -113,7 +113,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/chantalle.lua b/data-otservbr-global/npc/chantalle.lua index 2615f6557da..3ed42984f5c 100644 --- a/data-otservbr-global/npc/chantalle.lua +++ b/data-otservbr-global/npc/chantalle.lua @@ -99,7 +99,7 @@ npcConfig.shop = { { itemName = "diamond", clientId = 32770, sell = 15000 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/chuckles.lua b/data-otservbr-global/npc/chuckles.lua index 51fb3f2add6..4eb06b6bb3d 100644 --- a/data-otservbr-global/npc/chuckles.lua +++ b/data-otservbr-global/npc/chuckles.lua @@ -59,6 +59,38 @@ local itemsTable = { { itemName = "sudden death rune", clientId = 3155, buy = 135 }, { itemName = "ultimate healing rune", clientId = 3160, buy = 175 }, }, + ["wands"] = { + { itemName = "hailstorm rod", clientId = 3067, buy = 15000 }, + { itemName = "moonlight rod", clientId = 3070, buy = 1000 }, + { itemName = "necrotic rod", clientId = 3069, buy = 5000 }, + { itemName = "northwind rod", clientId = 8083, buy = 7500 }, + { itemName = "snakebite rod", clientId = 3066, buy = 500 }, + { itemName = "springsprout rod", clientId = 8084, buy = 18000 }, + { itemName = "terra rod", clientId = 3065, buy = 10000 }, + { itemName = "underworld rod", clientId = 8082, buy = 22000 }, + { itemName = "wand of cosmic energy", clientId = 3073, buy = 10000 }, + { itemName = "wand of decay", clientId = 3072, buy = 5000 }, + { itemName = "wand of draconia", clientId = 8093, buy = 7500 }, + { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, + { itemName = "wand of inferno", clientId = 3071, buy = 15000 }, + { itemName = "wand of starstorm", clientId = 8092, buy = 18000 }, + { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, + { itemName = "wand of vortex", clientId = 3074, buy = 500 }, + }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/edmund.lua b/data-otservbr-global/npc/edmund.lua index d8635297949..c61a95bcd72 100644 --- a/data-otservbr-global/npc/edmund.lua +++ b/data-otservbr-global/npc/edmund.lua @@ -68,7 +68,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/fenech.lua b/data-otservbr-global/npc/fenech.lua index 7152c0ef9c5..f69dcafaa89 100644 --- a/data-otservbr-global/npc/fenech.lua +++ b/data-otservbr-global/npc/fenech.lua @@ -71,6 +71,20 @@ local itemsTable = { { itemName = "sudden death rune", clientId = 3155, buy = 135 }, { itemName = "ultimate healing rune", clientId = 3160, buy = 175 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/frans.lua b/data-otservbr-global/npc/frans.lua index 8761a7d89d6..a1b695adf14 100644 --- a/data-otservbr-global/npc/frans.lua +++ b/data-otservbr-global/npc/frans.lua @@ -58,6 +58,20 @@ local itemsTable = { { itemName = "sudden death rune", clientId = 3155, buy = 135 }, { itemName = "ultimate healing rune", clientId = 3160, buy = 175 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/frederik.lua b/data-otservbr-global/npc/frederik.lua index 9b33ccf9684..81ff1ec58b6 100644 --- a/data-otservbr-global/npc/frederik.lua +++ b/data-otservbr-global/npc/frederik.lua @@ -82,6 +82,20 @@ local itemsTable = { { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 }, { itemName = "vial", clientId = 2874, sell = 5 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/gail.lua b/data-otservbr-global/npc/gail.lua index 80a9b54b3e5..de2a52dc7f4 100644 --- a/data-otservbr-global/npc/gail.lua +++ b/data-otservbr-global/npc/gail.lua @@ -109,7 +109,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/gnomegica.lua b/data-otservbr-global/npc/gnomegica.lua index 0805ca33ace..b860caf3962 100644 --- a/data-otservbr-global/npc/gnomegica.lua +++ b/data-otservbr-global/npc/gnomegica.lua @@ -78,6 +78,20 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/hanna.lua b/data-otservbr-global/npc/hanna.lua index 7fca4c908aa..dfea1547135 100644 --- a/data-otservbr-global/npc/hanna.lua +++ b/data-otservbr-global/npc/hanna.lua @@ -145,7 +145,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/ishina.lua b/data-otservbr-global/npc/ishina.lua index 1fd61200c15..358ee2619a3 100644 --- a/data-otservbr-global/npc/ishina.lua +++ b/data-otservbr-global/npc/ishina.lua @@ -139,7 +139,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/iwan.lua b/data-otservbr-global/npc/iwan.lua index 2cc24843318..77689b12003 100644 --- a/data-otservbr-global/npc/iwan.lua +++ b/data-otservbr-global/npc/iwan.lua @@ -78,7 +78,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/jessica.lua b/data-otservbr-global/npc/jessica.lua index 37ac18ed54a..43b1839b3ee 100644 --- a/data-otservbr-global/npc/jessica.lua +++ b/data-otservbr-global/npc/jessica.lua @@ -98,7 +98,7 @@ npcConfig.shop = { { itemName = "diamond", clientId = 32770, sell = 15000 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/khanna.lua b/data-otservbr-global/npc/khanna.lua index 02fcee7b6d5..76b5c1e70da 100644 --- a/data-otservbr-global/npc/khanna.lua +++ b/data-otservbr-global/npc/khanna.lua @@ -34,7 +34,7 @@ local itemsTable = { ["runes"] = { { itemName = "animate dead rune", clientId = 3203, buy = 375 }, { itemName = "avalanche rune", clientId = 3161, buy = 57 }, - { itemName = "blank rune", clientId = 3147, buy = 10 }, + { itemName = "blank rune", clientId = 3147, buy = 20 }, { itemName = "chameleon rune", clientId = 3178, buy = 210 }, { itemName = "convince creature rune", clientId = 3177, buy = 80 }, { itemName = "cure poison rune", clientId = 3153, buy = 65 }, @@ -84,6 +84,46 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["creature products"] = { + { itemName = "bashmu fang", clientId = 36820, sell = 600 }, + { itemName = "bashmu feather", clientId = 36820, sell = 350 }, + { itemName = "bashmu tongue", clientId = 36820, sell = 400 }, + { itemName = "blue goanna scale", clientId = 31559, sell = 230 }, + { itemName = "crystal ball", clientId = 3076, buy = 650 }, + { itemName = "fafnar symbol", clientId = 31443, sell = 950 }, + { itemName = "goanna claw", clientId = 31561, sell = 950 }, + { itemName = "goanna meat", clientId = 31560, sell = 190 }, + { itemName = "lamassu hoof", clientId = 31441, sell = 330 }, + { itemName = "lamassu horn", clientId = 31442, sell = 240 }, + { itemName = "life crystal", clientId = 3061, sell = 85 }, + { itemName = "lizard heart", clientId = 31340, sell = 530 }, + { itemName = "manticore ear", clientId = 31440, sell = 310 }, + { itemName = "manticore tail", clientId = 31439, sell = 220 }, + { itemName = "mind stone", clientId = 3062, sell = 170 }, + { itemName = "old girtablilu carapace", clientId = 36972, sell = 570 }, + { itemName = "red goanna scale", clientId = 31558, sell = 270 }, + { itemName = "scorpion charm", clientId = 36822, sell = 620 }, + { itemName = "sphinx feather", clientId = 31437, sell = 470 }, + { itemName = "sphinx tiara", clientId = 31438, sell = 360 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + { itemName = "spellbook of enlightenment", clientId = 8072, sell = 4000 }, + { itemName = "spellbook of lost souls", clientId = 8075, sell = 19000 }, + { itemName = "spellbook of mind control", clientId = 8074, sell = 13000 }, + { itemName = "spellbook of warding", clientId = 8073, sell = 8000 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/mordecai.lua b/data-otservbr-global/npc/mordecai.lua index 60063a423ba..dc0e3c07a77 100644 --- a/data-otservbr-global/npc/mordecai.lua +++ b/data-otservbr-global/npc/mordecai.lua @@ -88,6 +88,17 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/nelly.lua b/data-otservbr-global/npc/nelly.lua index 8c3b123c47a..911464524c6 100644 --- a/data-otservbr-global/npc/nelly.lua +++ b/data-otservbr-global/npc/nelly.lua @@ -94,6 +94,20 @@ local itemsTable = { { itemName = "letter", clientId = 3505, buy = 8 }, { itemName = "parcel", clientId = 3503, buy = 15 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/nipuna.lua b/data-otservbr-global/npc/nipuna.lua index aa9d74cec52..ef3211bce42 100644 --- a/data-otservbr-global/npc/nipuna.lua +++ b/data-otservbr-global/npc/nipuna.lua @@ -101,6 +101,17 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/odemara.lua b/data-otservbr-global/npc/odemara.lua index bcfe94bf8eb..3e1986714c7 100644 --- a/data-otservbr-global/npc/odemara.lua +++ b/data-otservbr-global/npc/odemara.lua @@ -70,7 +70,7 @@ npcConfig.shop = { { itemName = "diamond", clientId = 32770, sell = 15000 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/oiriz.lua b/data-otservbr-global/npc/oiriz.lua index 3a5d3a6b411..4b731c015a2 100644 --- a/data-otservbr-global/npc/oiriz.lua +++ b/data-otservbr-global/npc/oiriz.lua @@ -68,7 +68,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/rabaz.lua b/data-otservbr-global/npc/rabaz.lua index 3e47da6a5ca..e532e7def66 100644 --- a/data-otservbr-global/npc/rabaz.lua +++ b/data-otservbr-global/npc/rabaz.lua @@ -82,6 +82,20 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/rachel.lua b/data-otservbr-global/npc/rachel.lua index 3206c99864a..057787692c9 100644 --- a/data-otservbr-global/npc/rachel.lua +++ b/data-otservbr-global/npc/rachel.lua @@ -71,6 +71,23 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["valuables"] = { + { itemName = "talon", clientId = 3034, sell = 320 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/romir.lua b/data-otservbr-global/npc/romir.lua index 11ea038d266..09ab62ab1a4 100644 --- a/data-otservbr-global/npc/romir.lua +++ b/data-otservbr-global/npc/romir.lua @@ -82,6 +82,20 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/shiriel.lua b/data-otservbr-global/npc/shiriel.lua index 546bb26447b..fd982f345b9 100644 --- a/data-otservbr-global/npc/shiriel.lua +++ b/data-otservbr-global/npc/shiriel.lua @@ -70,6 +70,20 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/sigurd.lua b/data-otservbr-global/npc/sigurd.lua index cca63a33e3e..e4d1b585307 100644 --- a/data-otservbr-global/npc/sigurd.lua +++ b/data-otservbr-global/npc/sigurd.lua @@ -72,6 +72,20 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/sundara.lua b/data-otservbr-global/npc/sundara.lua index c1364240fa9..95ff206f2ad 100644 --- a/data-otservbr-global/npc/sundara.lua +++ b/data-otservbr-global/npc/sundara.lua @@ -101,6 +101,17 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/tandros.lua b/data-otservbr-global/npc/tandros.lua index e25ba65f2a3..ae647b26127 100644 --- a/data-otservbr-global/npc/tandros.lua +++ b/data-otservbr-global/npc/tandros.lua @@ -88,6 +88,20 @@ local itemsTable = { { itemName = "wand of voodoo", clientId = 8094, buy = 22000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/tesha.lua b/data-otservbr-global/npc/tesha.lua index 95e31f97cb7..6a3c4b9fadb 100644 --- a/data-otservbr-global/npc/tesha.lua +++ b/data-otservbr-global/npc/tesha.lua @@ -98,7 +98,7 @@ npcConfig.shop = { { itemName = "diamond", clientId = 32770, sell = 15000 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/tezila.lua b/data-otservbr-global/npc/tezila.lua index dab92c807f5..fe667133ad7 100644 --- a/data-otservbr-global/npc/tezila.lua +++ b/data-otservbr-global/npc/tezila.lua @@ -67,7 +67,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/topsy.lua b/data-otservbr-global/npc/topsy.lua index 395fd23cbeb..66ea6f27e6a 100644 --- a/data-otservbr-global/npc/topsy.lua +++ b/data-otservbr-global/npc/topsy.lua @@ -78,6 +78,20 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/valindara.lua b/data-otservbr-global/npc/valindara.lua index 69655358dfe..cf9506fcdd5 100644 --- a/data-otservbr-global/npc/valindara.lua +++ b/data-otservbr-global/npc/valindara.lua @@ -111,7 +111,7 @@ npcConfig.shop = { { itemName = "fire wall rune", clientId = 3190, buy = 61 }, { itemName = "fireball rune", clientId = 3189, buy = 30 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/npc/xodet.lua b/data-otservbr-global/npc/xodet.lua index 2d8832bd964..f687b491e89 100644 --- a/data-otservbr-global/npc/xodet.lua +++ b/data-otservbr-global/npc/xodet.lua @@ -72,6 +72,20 @@ local itemsTable = { { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 }, { itemName = "wand of vortex", clientId = 3074, buy = 500 }, }, + ["exercise weapons"] = { + { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 }, + { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 }, + { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 }, + { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 }, + { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 }, + { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 }, + }, + ["others"] = { + { itemName = "spellwand", clientId = 651, sell = 299 }, + }, + ["shields"] = { + { itemName = "spellbook", clientId = 3059, buy = 150 }, + }, } npcConfig.shop = {} diff --git a/data-otservbr-global/npc/yasir.lua b/data-otservbr-global/npc/yasir.lua index d576b611e53..9c5cf3dbf69 100644 --- a/data-otservbr-global/npc/yasir.lua +++ b/data-otservbr-global/npc/yasir.lua @@ -60,6 +60,7 @@ npcConfig.shop = { { itemName = "ape fur", clientId = 5883, sell = 120 }, { itemName = "apron", clientId = 33933, sell = 1300 }, { itemName = "badger fur", clientId = 903, sell = 15 }, + { itemName = "bakragore's amalgamation", clientId = 43968, sell = 2000000 }, { itemName = "bamboo stick", clientId = 11445, sell = 30 }, { itemName = "banana sash", clientId = 11511, sell = 55 }, { itemName = "basalt fetish", clientId = 17856, sell = 210 }, @@ -75,6 +76,7 @@ npcConfig.shop = { { itemName = "black hood", clientId = 9645, sell = 190 }, { itemName = "black wool", clientId = 11448, sell = 300 }, { itemName = "blazing bone", clientId = 16131, sell = 610 }, + { itemName = "bloated maggot", clientId = 43856, sell = 5200 }, { itemName = "blood preservation", clientId = 11449, sell = 320 }, { itemName = "blood tincture in a vial", clientId = 18928, sell = 360 }, { itemName = "bloody dwarven beard", clientId = 17827, sell = 110 }, @@ -173,7 +175,11 @@ npcConfig.shop = { { itemName = "dandelion seeds", clientId = 25695, sell = 200 }, { itemName = "dangerous proto matter", clientId = 23515, sell = 300 }, { itemName = "dark bell", clientId = 32596, sell = 310000 }, + { itemName = "dark obsidian splinter", clientId = 43850, sell = 4400 }, { itemName = "dark rosary", clientId = 10303, sell = 48 }, + { itemName = "darklight core", clientId = 43853, sell = 4100 }, + { itemName = "darklight figurine", clientId = 43961, sell = 3400000 }, + { itemName = "darklight matter", clientId = 43851, sell = 5500 }, { itemName = "dead weight", clientId = 20202, sell = 450 }, { itemName = "deepling breaktime snack", clientId = 14011, sell = 90 }, { itemName = "deepling claw", clientId = 14044, sell = 430 }, @@ -229,6 +235,7 @@ npcConfig.shop = { { itemName = "falcon crest", clientId = 28823, sell = 650 }, { itemName = "fern", clientId = 3737, sell = 20 }, { itemName = "fiery heart", clientId = 9636, sell = 375 }, + { itemName = "fiery tear", clientId = 39040, sell = 1070000 }, { itemName = "fig leaf", clientId = 25742, sell = 200 }, { itemName = "figurine of cruelty", clientId = 34019, sell = 3100000 }, { itemName = "figurine of greed", clientId = 34021, sell = 2900000 }, @@ -480,6 +487,8 @@ npcConfig.shop = { { itemName = "rorc feather", clientId = 18993, sell = 70 }, { itemName = "rotten heart", clientId = 31589, sell = 74000 }, { itemName = "rotten piece of cloth", clientId = 10291, sell = 30 }, + { itemName = "rotten roots", clientId = 43849, sell = 3800 }, + { itemName = "rotten vermin ichor", clientId = 43847, sell = 4500 }, { itemName = "sabretooth", clientId = 10311, sell = 400 }, { itemName = "sabretooth fur", clientId = 39378, sell = 2500 }, { itemName = "safety pin", clientId = 11493, sell = 120 }, @@ -636,6 +645,7 @@ npcConfig.shop = { { itemName = "wolf paw", clientId = 5897, sell = 70 }, { itemName = "wood", clientId = 5901, sell = 5 }, { itemName = "wool", clientId = 10319, sell = 15 }, + { itemName = "worm sponge", clientId = 43848, sell = 4200 }, { itemName = "writhing brain", clientId = 32600, sell = 370000 }, { itemName = "writhing heart", clientId = 32599, sell = 185000 }, { itemName = "wyrm scale", clientId = 9665, sell = 400 }, diff --git a/data-otservbr-global/npc/yonan.lua b/data-otservbr-global/npc/yonan.lua index 44dbb8dd83c..69bddee5cb3 100644 --- a/data-otservbr-global/npc/yonan.lua +++ b/data-otservbr-global/npc/yonan.lua @@ -39,7 +39,7 @@ npcConfig.shop = { { itemName = "cyan crystal fragment", clientId = 16125, sell = 800 }, { itemName = "dragon figurine", clientId = 30053, sell = 45000 }, { itemName = "gemmed figurine", clientId = 24392, sell = 3500 }, - { itemName = "giant amethyst", clientId = 30061, sell = 60000 }, + { itemName = "giant amethyst", clientId = 32622, sell = 60000 }, { itemName = "giant emerald", clientId = 30060, sell = 90000 }, { itemName = "giant ruby", clientId = 30059, sell = 70000 }, { itemName = "giant sapphire", clientId = 30061, sell = 50000 }, diff --git a/data-otservbr-global/world/otservbr-monster.xml b/data-otservbr-global/world/otservbr-monster.xml index d3bbc69e858..974c2ea6809 100644 --- a/data-otservbr-global/world/otservbr-monster.xml +++ b/data-otservbr-global/world/otservbr-monster.xml @@ -4076,7 +4076,6 @@ <monster name="Midnight Asura" x="-2" y="2" z="3" spawntime="90" /> </monster> <monster centerx="32959" centery="32672" centerz="3" radius="3"> - <monster name="Destroyer" x="-1" y="3" z="3" spawntime="90" /> <monster name="Midnight Asura" x="1" y="3" z="3" spawntime="90" /> </monster> <monster centerx="33064" centery="32672" centerz="3" radius="3"> @@ -4122,9 +4121,6 @@ <monster centerx="33060" centery="32684" centerz="3" radius="1"> <monster name="Massive Energy Elemental" x="1" y="-1" z="3" spawntime="90" /> </monster> - <monster centerx="32938" centery="32686" centerz="3" radius="3"> - <monster name="Destroyer" x="-1" y="-2" z="3" spawntime="90" /> - </monster> <monster centerx="33379" centery="32686" centerz="3" radius="3"> <monster name="Cobra Scout" x="3" y="0" z="3" spawntime="60" /> </monster> @@ -6928,19 +6924,13 @@ <monster name="Hydra" x="0" y="2" z="4" spawntime="90" /> </monster> <monster centerx="32945" centery="32672" centerz="4" radius="3"> - <monster name="Massive Fire Elemental" x="0" y="-1" z="4" spawntime="90" /> <monster name="Midnight Asura" x="1" y="-1" z="4" spawntime="90" /> <monster name="Destroyer" x="0" y="1" z="4" spawntime="90" /> </monster> <monster centerx="32959" centery="32672" centerz="4" radius="3"> - <monster name="Massive Fire Elemental" x="2" y="-2" z="4" spawntime="90" /> <monster name="Midnight Asura" x="3" y="-1" z="4" spawntime="90" /> <monster name="Destroyer" x="1" y="2" z="4" spawntime="60" /> </monster> - <monster centerx="32966" centery="32672" centerz="4" radius="3"> - <monster name="Destroyer" x="-3" y="-2" z="4" spawntime="90" /> - <monster name="Destroyer" x="-1" y="1" z="4" spawntime="90" /> - </monster> <monster centerx="33085" centery="32672" centerz="4" radius="1"> <monster name="Energy Elemental" x="-1" y="1" z="4" spawntime="90" /> </monster> @@ -6953,9 +6943,6 @@ <monster name="Dawnfire Asura" x="1" y="-1" z="4" spawntime="90" /> <monster name="Midnight Asura" x="0" y="0" z="4" spawntime="90" /> </monster> - <monster centerx="32934" centery="32677" centerz="4" radius="1"> - <monster name="Massive Fire Elemental" x="0" y="0" z="4" spawntime="90" /> - </monster> <monster centerx="32944" centery="32677" centerz="4" radius="1"> <monster name="Dawnfire Asura" x="0" y="0" z="4" spawntime="90" /> </monster> @@ -6963,7 +6950,6 @@ <monster name="Destroyer" x="0" y="0" z="4" spawntime="60" /> </monster> <monster centerx="32952" centery="32679" centerz="4" radius="3"> - <monster name="Destroyer" x="0" y="-3" z="4" spawntime="90" /> <monster name="Midnight Asura" x="-1" y="-2" z="4" spawntime="90" /> </monster> <monster centerx="33379" centery="32679" centerz="4" radius="3"> @@ -6985,15 +6971,11 @@ <monster name="Dawnfire Asura" x="0" y="0" z="4" spawntime="90" /> </monster> <monster centerx="32957" centery="32684" centerz="4" radius="1"> - <monster name="Massive Fire Elemental" x="-1" y="0" z="4" spawntime="90" /> <monster name="Dawnfire Asura" x="0" y="0" z="4" spawntime="90" /> </monster> <monster centerx="33073" centery="32684" centerz="4" radius="2"> <monster name="Energy Elemental" x="0" y="0" z="4" spawntime="90" /> </monster> - <monster centerx="32931" centery="32686" centerz="4" radius="3"> - <monster name="Destroyer" x="2" y="-1" z="4" spawntime="90" /> - </monster> <monster centerx="32121" centery="32687" centerz="4" radius="7"> <monster name="Demon" x="-4" y="-4" z="4" spawntime="90" /> <monster name="Demon" x="4" y="-4" z="4" spawntime="90" /> @@ -7009,9 +6991,6 @@ <monster centerx="32953" centery="32687" centerz="4" radius="1"> <monster name="Dawnfire Asura" x="0" y="0" z="4" spawntime="90" /> </monster> - <monster centerx="32964" centery="32687" centerz="4" radius="1"> - <monster name="Massive Fire Elemental" x="0" y="0" z="4" spawntime="90" /> - </monster> <monster centerx="33049" centery="32687" centerz="4" radius="1"> <monster name="Dragon Lord" x="1" y="0" z="4" spawntime="90" /> </monster> @@ -63284,9 +63263,6 @@ <monster centerx="33319" centery="32310" centerz="8" radius="1"> <monster name="Rotworm" x="0" y="1" z="8" spawntime="90" /> </monster> - <monster centerx="31968" centery="32311" centerz="8" radius="1"> - <monster name="Demon" x="0" y="0" z="8" spawntime="60" /> - </monster> <monster centerx="31991" centery="32311" centerz="8" radius="1"> <monster name="Mean Lost Soul" x="0" y="0" z="8" spawntime="60" /> </monster> @@ -63491,9 +63467,6 @@ <monster centerx="33309" centery="32327" centerz="8" radius="1"> <monster name="Rotworm" x="1" y="1" z="8" spawntime="90" /> </monster> - <monster centerx="31951" centery="32328" centerz="8" radius="1"> - <monster name="Demon" x="0" y="0" z="8" spawntime="60" /> - </monster> <monster centerx="33106" centery="32328" centerz="8" radius="1"> <monster name="Burster Spectre" x="0" y="0" z="8" spawntime="60" /> </monster> @@ -96125,15 +96098,15 @@ <monster centerx="33234" centery="32794" centerz="9" radius="2"> <monster name="Larva" x="0" y="-2" z="9" spawntime="90" /> </monster> + <monster centerx="32006" centery="32795" centerz="9" radius="2"> + <monster name="Ghoul" x="-1" y="-1" z="9" spawntime="90" /> + </monster> <monster centerx="32296" centery="32795" centerz="9" radius="1"> <monster name="Corym Skirmisher" x="0" y="0" z="9" spawntime="60" /> </monster> <monster centerx="32300" centery="32795" centerz="9" radius="1"> <monster name="Corym Charlatan" x="0" y="0" z="9" spawntime="60" /> </monster> - <monster centerx="32006" centery="32795" centerz="9" radius="2"> - <monster name="Ghoul" x="-1" y="-1" z="9" spawntime="90" /> - </monster> <monster centerx="33071" centery="32795" centerz="9" radius="2"> <monster name="Scarab" x="1" y="-1" z="9" spawntime="90" /> </monster> @@ -96718,6 +96691,9 @@ <monster centerx="32245" centery="32835" centerz="9" radius="1"> <monster name="Rotworm" x="1" y="-1" z="9" spawntime="90" /> </monster> + <monster centerx="32274" centery="32835" centerz="9" radius="1"> + <monster name="Corym Skirmisher" x="0" y="0" z="9" spawntime="60" /> + </monster> <monster centerx="32015" centery="32836" centerz="9" radius="3"> <monster name="Ghoul" x="1" y="-3" z="9" spawntime="90" /> <monster name="Ghoul" x="-2" y="0" z="9" spawntime="90" /> @@ -96725,9 +96701,6 @@ <monster centerx="32024" centery="32836" centerz="9" radius="3"> <monster name="Pirate Skeleton" x="2" y="-1" z="9" spawntime="90" /> </monster> - <monster centerx="32274" centery="32835" centerz="9" radius="1"> - <monster name="Corym Skirmisher" x="0" y="0" z="9" spawntime="60" /> - </monster> <monster centerx="32566" centery="32836" centerz="9" radius="2"> <monster name="Water Elemental" x="0" y="0" z="9" spawntime="90" /> </monster> @@ -118889,12 +118862,12 @@ <monster name="Rotworm" x="-3" y="3" z="10" spawntime="90" /> <monster name="Carrion Worm" x="1" y="3" z="10" spawntime="90" /> </monster> - <monster centerx="32026" centery="32830" centerz="10" radius="2"> - <monster name="Pirate Skeleton" x="0" y="-1" z="10" spawntime="90" /> - </monster> <monster centerx="32276" centery="32829" centerz="10" radius="1"> <monster name="Corym Skirmisher" x="0" y="0" z="10" spawntime="60" /> </monster> + <monster centerx="32026" centery="32830" centerz="10" radius="2"> + <monster name="Pirate Skeleton" x="0" y="-1" z="10" spawntime="90" /> + </monster> <monster centerx="32224" centery="32831" centerz="10" radius="4"> <monster name="Rotworm" x="-3" y="-1" z="10" spawntime="90" /> <monster name="Carrion Worm" x="2" y="1" z="10" spawntime="90" /> diff --git a/data/items/items.xml b/data/items/items.xml index 45cde62bd9d..e7e73d9753b 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -64466,8 +64466,8 @@ hands of its owner. Granted by TibiaRoyal.com"/> <attribute key="range" value="5"/> <attribute key="weight" value="2600"/> <attribute key="imbuementslot" value="2"> + <attribute key="life leech" value="3"/> <attribute key="mana leech" value="3"/> - <attribute key="critical hit" value="3"/> <attribute key="skillboost magic level" value="3"/> </attribute> <attribute key="script" value="moveevent;weapon"> @@ -74873,11 +74873,14 @@ Granted by TibiaGoals.com"/> <attribute key="weight" value="280"/> </item> <item id="43740" article="a" name="scarab ocarina"> - <attribute key="primarytype" value="light sources"/> + <attribute key="primarytype" value="quest items"/> <attribute key="showAttributes" value="1"/> <attribute key="description" value="It has a turquoise shimmer and was made from a real scarab shell."/> <attribute key="absorbpercentearth" value="2"/> <attribute key="weight" value="120"/> + <attribute key="script" value="moveevent"> + <attribute key="slot" value="ammo"/> + </attribute> </item> <item id="43665" article="a" name="dead elder bloodjaw"> <attribute key="decayTo" value="0"/> @@ -74994,6 +74997,15 @@ Granted by TibiaGoals.com"/> <attribute key="duration" value="10"/> <attribute key="fluidSource" value="blood"/> </item> + <item id="43863" article="a" name="conch shell horn"> + <attribute key="primarytype" value="quest items"/> + <attribute key="showAttributes" value="1"/> + <attribute key="absorbpercentice" value="5"/> + <attribute key="weight" value="150"/> + <attribute key="script" value="moveevent"> + <attribute key="slot" value="ammo"/> + </attribute> + </item> <item id="43864" article="a" name="sanguine blade"> <attribute key="primarytype" value="sword weapons"/> <attribute key="weaponType" value="sword"/> From 50b4c26a3fcab7fb01130186eb18a196c24e3db4 Mon Sep 17 00:00:00 2001 From: Guilherme <guilherme.vrsantana@gmail.com> Date: Mon, 20 May 2024 16:03:10 -0300 Subject: [PATCH 45/95] feat: Galthen's Satchel and Artefact Box (#2149) Fixes from map on pr #2314 --- data-otservbr-global/npc/an_idol.lua | 68 +++++++++++++++++++ .../adventures_of_galthen/galthens_tree.lua | 19 ++++++ data-otservbr-global/world/otservbr-npc.xml | 5 +- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/npc/an_idol.lua create mode 100644 data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua diff --git a/data-otservbr-global/npc/an_idol.lua b/data-otservbr-global/npc/an_idol.lua new file mode 100644 index 00000000000..69977dc81ec --- /dev/null +++ b/data-otservbr-global/npc/an_idol.lua @@ -0,0 +1,68 @@ +local internalNpcName = "An Idol" +local npcType = Game.createNpcType(internalNpcName) +local npcConfig = {} + +npcConfig.name = internalNpcName +npcConfig.description = internalNpcName + +npcConfig.health = 100 +npcConfig.maxHealth = npcConfig.health +npcConfig.walkInterval = 0 +npcConfig.walkRadius = 2 + +npcConfig.outfit = { + lookTypeEx = 15894, +} + +npcConfig.flags = { + floorchange = false, +} + +local keywordHandler = KeywordHandler:new() +local npcHandler = NpcHandler:new(keywordHandler) + +npcType.onThink = function(npc, interval) + npcHandler:onThink(npc, interval) +end + +npcType.onAppear = function(npc, creature) + npcHandler:onAppear(npc, creature) +end + +npcType.onDisappear = function(npc, creature) + npcHandler:onDisappear(npc, creature) +end + +npcType.onMove = function(npc, creature, fromPosition, toPosition) + npcHandler:onMove(npc, creature, fromPosition, toPosition) +end + +npcType.onSay = function(npc, creature, type, message) + npcHandler:onSay(npc, creature, type, message) +end + +npcType.onCloseChannel = function(npc, creature) + npcHandler:onCloseChannel(npc, creature) +end + +local function creatureSayCallback(npc, creature, type, message) + local player = Player(creature) + + if not npcHandler:checkInteraction(npc, creature) then + return false + end + + if MsgContains(message, "VBOX") then + npcHandler:say("J-T B^C J^BXT°", npc, creature) + player:teleportTo(Position(32366, 32531, 8), false) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + + return true +end + +npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) +npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, false) + +-- npcType registering the npcConfig table +npcType:register(npcConfig) diff --git a/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua b/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua new file mode 100644 index 00000000000..441b91fa345 --- /dev/null +++ b/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua @@ -0,0 +1,19 @@ +local galthensTree = Action() +function galthensTree.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local hasExhaustion, message = player:kv():get("galthens-satchel") or 0, "Empty." + if hasExhaustion < os.time() then + local container = player:addItem(36813) + container:addItem(36810, 1) + player:kv():set("galthens-satchel", os.time() + 30 * 24 * 60 * 60) + message = "You have found a galthens satchel." + end + + player:teleportTo(Position(32396, 32520, 7)) + player:getPosition():sendMagicEffect(CONST_ME_WATERSPLASH) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + + return true +end + +galthensTree:position(Position(32366, 32542, 8)) +galthensTree:register() diff --git a/data-otservbr-global/world/otservbr-npc.xml b/data-otservbr-global/world/otservbr-npc.xml index 10772902341..f97bc118fdc 100644 --- a/data-otservbr-global/world/otservbr-npc.xml +++ b/data-otservbr-global/world/otservbr-npc.xml @@ -2994,5 +2994,8 @@ </npc> <npc centerx="33068" centery="32917" centerz="7" radius="1"> <npc name="Tonar Oskayaat" x="0" y="0" z="7" spawntime="60" /> -</npc> + </npc> + <npc centerx="32398" centery="32509" centerz="7" radius="1"> + <npc name="An Idol" x="0" y="0" z="15" spawntime="60" /> + </npc> </npcs> From 360e00683afe6dd92f0237f42b09fdd5bd902c7a Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Tue, 21 May 2024 19:14:27 -0300 Subject: [PATCH 46/95] fix: update market average price and configurable refresh interval (#2642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Optimized Resource Usage: Reduced unnecessary copying of the statistics and sale maps in protocolgame.cpp, enhancing safety with the use of find for more secure access. • Bug Fix: Addressed a loading issue with items in market_offers and market_history. • Improved Readability: Replaced ostringstream with fmt::format to enhance code clarity and readability. • Feature: change price update interval to 30 by default and added a setting to enable/disable it (only set to 0). --- config.lua.dist | 3 + src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/game/game.cpp | 30 +++++----- src/game/game.hpp | 3 +- src/io/iomarket.cpp | 24 ++++++-- src/io/iomarket.hpp | 7 +-- src/server/network/protocol/protocolgame.cpp | 60 ++++++++++++-------- 8 files changed, 78 insertions(+), 51 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index ed2561d4532..447a90647c2 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -377,7 +377,10 @@ partyListMaxDistance = 30 toggleMapCustom = true -- Market +-- NOTE: marketRefreshPricesInterval (in minutes, minimum is 1 minute) +-- NOTE: set it to 0 for disable, is the time in which the task will run updating the prices of the items that will be sent to the client marketOfferDuration = 30 * 24 * 60 * 60 +marketRefreshPricesInterval = 30 premiumToCreateMarketOffer = true checkExpiredMarketOffersEachMinutes = 60 maxMarketOffersAtATimePerPlayer = 100 diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 30a4b172af1..22043f42525 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -139,6 +139,7 @@ enum ConfigKey_t : uint16_t { MAP_DOWNLOAD_URL, MAP_NAME, MARKET_OFFER_DURATION, + MARKET_REFRESH_PRICES, MARKET_PREMIUM, MAX_ALLOWED_ON_A_DUMMY, MAX_CONTAINER_ITEM, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 37634abf514..e1345bbac4b 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -61,6 +61,7 @@ bool ConfigManager::load() { loadIntConfig(L, GAME_PORT, "gameProtocolPort", 7172); loadIntConfig(L, LOGIN_PORT, "loginProtocolPort", 7171); loadIntConfig(L, MARKET_OFFER_DURATION, "marketOfferDuration", 30 * 24 * 60 * 60); + loadIntConfig(L, MARKET_REFRESH_PRICES, "marketRefreshPricesInterval", 30); loadIntConfig(L, PREMIUM_DEPOT_LIMIT, "premiumDepotLimit", 8000); loadIntConfig(L, SQL_PORT, "mysqlPort", 3306); loadIntConfig(L, STASH_ITEMS, "stashItemCount", 5000); diff --git a/src/game/game.cpp b/src/game/game.cpp index 00ec056ccb3..6188973781a 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -486,9 +486,16 @@ void Game::start(ServiceManager* manager) { g_dispatcher().cycleEvent( EVENT_LUA_GARBAGE_COLLECTION, [this] { g_luaEnvironment().collectGarbage(); }, "Calling GC" ); - g_dispatcher().cycleEvent( - EVENT_REFRESH_MARKET_PRICES, [this] { loadItemsPrice(); }, "Game::loadItemsPrice" - ); + auto marketItemsPriceIntervalMinutes = g_configManager().getNumber(MARKET_REFRESH_PRICES, __FUNCTION__); + if (marketItemsPriceIntervalMinutes > 0) { + auto marketItemsPriceIntervalMS = marketItemsPriceIntervalMinutes * 60000; + if (marketItemsPriceIntervalMS < 60000) { + marketItemsPriceIntervalMS = 60000; + } + g_dispatcher().cycleEvent( + marketItemsPriceIntervalMS, [this] { loadItemsPrice(); }, "Game::loadItemsPrice" + ); + } } GameState_t Game::getGameState() const { @@ -580,18 +587,11 @@ void Game::setGameState(GameState_t newState) { } } -bool Game::loadItemsPrice() { +void Game::loadItemsPrice() { IOMarket::getInstance().updateStatistics(); - std::ostringstream query, marketQuery; - query << "SELECT DISTINCT `itemtype` FROM `market_offers`;"; - - Database &db = Database::getInstance(); - DBResult_ptr result = db.storeQuery(query.str()); - if (!result) { - return false; - } - auto stats = IOMarket::getInstance().getPurchaseStatistics(); + // Update purchased offers (market_history) + const auto &stats = IOMarket::getInstance().getPurchaseStatistics(); for (const auto &[itemId, itemStats] : stats) { std::map<uint8_t, uint64_t> tierToPrice; for (const auto &[tier, tierStats] : itemStats) { @@ -600,12 +600,12 @@ bool Game::loadItemsPrice() { } itemsPriceMap[itemId] = tierToPrice; } + + // Update active buy offers (market_offers) auto offers = IOMarket::getInstance().getActiveOffers(MARKETACTION_BUY); for (const auto &offer : offers) { itemsPriceMap[offer.itemId][offer.tier] = std::max(itemsPriceMap[offer.itemId][offer.tier], offer.price); } - - return true; } void Game::loadMainMap(const std::string &filename) { diff --git a/src/game/game.hpp b/src/game/game.hpp index 554c418a2c8..0e7b5fc147f 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -60,7 +60,6 @@ static constexpr int32_t EVENT_DECAYINTERVAL = 250; static constexpr int32_t EVENT_DECAY_BUCKETS = 4; static constexpr int32_t EVENT_FORGEABLEMONSTERCHECKINTERVAL = 300000; static constexpr int32_t EVENT_LUA_GARBAGE_COLLECTION = 60000 * 10; // 10min -static constexpr int32_t EVENT_REFRESH_MARKET_PRICES = 60000; // 1min static constexpr std::chrono::minutes CACHE_EXPIRATION_TIME { 10 }; // 10min static constexpr std::chrono::minutes HIGHSCORE_CACHE_EXPIRATION_TIME { 10 }; // 10min @@ -517,7 +516,7 @@ class Game { return lightHour; } - bool loadItemsPrice(); + void loadItemsPrice(); void loadMotdNum(); void saveMotdNum() const; diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index ca1bdb209d2..a0253e2ea01 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -29,10 +29,14 @@ uint8_t IOMarket::getTierFromDatabaseTable(const std::string &string) { MarketOfferList IOMarket::getActiveOffers(MarketAction_t action) { MarketOfferList offerList; - std::ostringstream query; - query << "SELECT `id`, `amount`, `price`, `tier`, `created`, `anonymous`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` FROM `market_offers` WHERE `sale` = " << action; + std::string query = fmt::format( + "SELECT `id`, `itemtype`, `amount`, `price`, `tier`, `created`, `anonymous`, " + "(SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` " + "FROM `market_offers` WHERE `sale` = {}", + action + ); - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = g_database().storeQuery(query); if (!result) { return offerList; } @@ -41,6 +45,7 @@ MarketOfferList IOMarket::getActiveOffers(MarketAction_t action) { do { MarketOffer offer; + offer.itemId = result->getNumber<uint16_t>("itemtype"); offer.amount = result->getNumber<uint16_t>("amount"); offer.price = result->getNumber<uint64_t>("price"); offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration; @@ -71,6 +76,7 @@ MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId do { MarketOffer offer; + offer.itemId = itemId; offer.amount = result->getNumber<uint16_t>("amount"); offer.price = result->getNumber<uint64_t>("price"); offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration; @@ -333,9 +339,15 @@ bool IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state) { } void IOMarket::updateStatistics() { - std::ostringstream query; - query << "SELECT `sale` AS `sale`, `itemtype` AS `itemtype`, COUNT(`price`) AS `num`, MIN(`price`) AS `min`, MAX(`price`) AS `max`, SUM(`price`) AS `sum`, `tier` AS `tier` FROM `market_history` WHERE `state` = " << OFFERSTATE_ACCEPTED << " GROUP BY `itemtype`, `sale`, `tier`"; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + auto query = fmt::format( + "SELECT sale, itemtype, COUNT(price) AS num, MIN(price) AS min, MAX(price) AS max, SUM(price) AS sum, tier " + "FROM market_history " + "WHERE state = '{}' " + "GROUP BY itemtype, sale, tier", + OFFERSTATE_ACCEPTED + ); + + DBResult_ptr result = g_database().storeQuery(query); if (!result) { return; } diff --git a/src/io/iomarket.hpp b/src/io/iomarket.hpp index 33180053584..4292651fb47 100644 --- a/src/io/iomarket.hpp +++ b/src/io/iomarket.hpp @@ -14,8 +14,6 @@ #include "lib/di/container.hpp" class IOMarket { - using StatisticsMap = std::map<uint16_t, std::map<uint8_t, MarketStatistics>>; - public: IOMarket() = default; @@ -43,10 +41,11 @@ class IOMarket { void updateStatistics(); - StatisticsMap getPurchaseStatistics() const { + using StatisticsMap = std::map<uint16_t, std::map<uint8_t, MarketStatistics>>; + const StatisticsMap &getPurchaseStatistics() const { return purchaseStatistics; } - StatisticsMap getSaleStatistics() const { + const StatisticsMap &getSaleStatistics() const { return saleStatistics; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index ada1340a7c6..e16f015eb49 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5804,35 +5804,47 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) { } } - auto purchase = IOMarket::getInstance().getPurchaseStatistics()[itemId][tier]; - if (const MarketStatistics* purchaseStatistics = &purchase; purchaseStatistics) { - msg.addByte(0x01); - msg.add<uint32_t>(purchaseStatistics->numTransactions); - if (oldProtocol) { - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics->totalPrice)); - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics->highestPrice)); - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics->lowestPrice)); - } else { - msg.add<uint64_t>(purchaseStatistics->totalPrice); - msg.add<uint64_t>(purchaseStatistics->highestPrice); - msg.add<uint64_t>(purchaseStatistics->lowestPrice); + const auto &purchaseStatsMap = IOMarket::getInstance().getPurchaseStatistics(); + auto purchaseIterator = purchaseStatsMap.find(itemId); + if (purchaseIterator != purchaseStatsMap.end()) { + const auto &tierStatsMap = purchaseIterator->second; + auto tierStatsIter = tierStatsMap.find(tier); + if (tierStatsIter != tierStatsMap.end()) { + const auto &purchaseStatistics = tierStatsIter->second; + msg.addByte(0x01); + msg.add<uint32_t>(purchaseStatistics.numTransactions); + if (oldProtocol) { + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics.totalPrice)); + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics.highestPrice)); + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), purchaseStatistics.lowestPrice)); + } else { + msg.add<uint64_t>(purchaseStatistics.totalPrice); + msg.add<uint64_t>(purchaseStatistics.highestPrice); + msg.add<uint64_t>(purchaseStatistics.lowestPrice); + } } } else { msg.addByte(0x00); // send to old protocol ? } - auto sale = IOMarket::getInstance().getSaleStatistics()[itemId][tier]; - if (const MarketStatistics* saleStatistics = &sale; saleStatistics) { - msg.addByte(0x01); - msg.add<uint32_t>(saleStatistics->numTransactions); - if (oldProtocol) { - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics->totalPrice)); - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics->highestPrice)); - msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics->lowestPrice)); - } else { - msg.add<uint64_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics->totalPrice)); - msg.add<uint64_t>(saleStatistics->highestPrice); - msg.add<uint64_t>(saleStatistics->lowestPrice); + const auto &saleStatsMap = IOMarket::getInstance().getSaleStatistics(); + auto saleIterator = saleStatsMap.find(itemId); + if (saleIterator != saleStatsMap.end()) { + const auto &tierStatsMap = saleIterator->second; + auto tierStatsIter = tierStatsMap.find(tier); + if (tierStatsIter != tierStatsMap.end()) { + const auto &saleStatistics = tierStatsIter->second; + msg.addByte(0x01); + msg.add<uint32_t>(saleStatistics.numTransactions); + if (oldProtocol) { + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics.totalPrice)); + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics.highestPrice)); + msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics.lowestPrice)); + } else { + msg.add<uint64_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), saleStatistics.totalPrice)); + msg.add<uint64_t>(saleStatistics.highestPrice); + msg.add<uint64_t>(saleStatistics.lowestPrice); + } } } else { msg.addByte(0x00); // send to old protocol ? From c57f5a28d6affef23dcf5a801b321d7e72465c52 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Wed, 22 May 2024 20:14:22 -0300 Subject: [PATCH 47/95] enhance: MapSector system for improved performance and flexibility Introduces a new map sector system using a hashmap instead of a node-based structure, which significantly improves search performance. It stores tiles and creatures for each area, and is critical for server functionality. Credits @saiyansking --- src/items/tile.cpp | 2 +- .../functions/core/game/global_functions.cpp | 2 +- src/map/CMakeLists.txt | 2 +- src/map/map.cpp | 67 +++++++----- src/map/map.hpp | 8 +- src/map/map_const.hpp | 7 +- src/map/mapcache.cpp | 46 +++++++- src/map/mapcache.hpp | 61 ++++------- src/map/spectators.cpp | 70 ++++++------ src/map/utils/mapsector.cpp | 46 ++++++++ src/map/utils/mapsector.hpp | 92 ++++++++++++++++ src/map/utils/qtreenode.cpp | 103 ------------------ src/map/utils/qtreenode.hpp | 92 ---------------- vcproj/canary.vcxproj | 4 +- 14 files changed, 290 insertions(+), 312 deletions(-) create mode 100644 src/map/utils/mapsector.cpp create mode 100644 src/map/utils/mapsector.hpp delete mode 100644 src/map/utils/qtreenode.cpp delete mode 100644 src/map/utils/qtreenode.hpp diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 11e3fafd6fd..f2006f627e1 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -1272,7 +1272,7 @@ void Tile::removeThing(std::shared_ptr<Thing> thing, uint32_t count) { } void Tile::removeCreature(std::shared_ptr<Creature> creature) { - g_game().map.getQTNode(tilePos.x, tilePos.y)->removeCreature(creature); + g_game().map.getMapSector(tilePos.x, tilePos.y)->removeCreature(creature); removeThing(creature, 0); } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index 0403e88dd95..af89c1854f7 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -712,7 +712,7 @@ int GlobalFunctions::luaSaveServer(lua_State* L) { } int GlobalFunctions::luaCleanMap(lua_State* L) { - lua_pushnumber(L, Map::clean()); + lua_pushnumber(L, g_game().map.clean()); return 1; } diff --git a/src/map/CMakeLists.txt b/src/map/CMakeLists.txt index 7d744950c1f..cab0eb03b15 100644 --- a/src/map/CMakeLists.txt +++ b/src/map/CMakeLists.txt @@ -2,7 +2,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE house/house.cpp house/housetile.cpp utils/astarnodes.cpp - utils/qtreenode.cpp + utils/mapsector.cpp map.cpp mapcache.cpp spectators.cpp diff --git a/src/map/map.cpp b/src/map/map.cpp index fcf14262c30..d4d1c4147b1 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -163,7 +163,7 @@ std::shared_ptr<Tile> Map::getLoadedTile(uint16_t x, uint16_t y, uint8_t z) { return nullptr; } - const auto leaf = getQTNode(x, y); + const auto leaf = getMapSector(x, y); if (!leaf) { return nullptr; } @@ -182,12 +182,12 @@ std::shared_ptr<Tile> Map::getTile(uint16_t x, uint16_t y, uint8_t z) { return nullptr; } - const auto leaf = getQTNode(x, y); - if (!leaf) { + const auto sector = getMapSector(x, y); + if (!sector) { return nullptr; } - const auto &floor = leaf->getFloor(z); + const auto &floor = sector->getFloor(z); if (!floor) { return nullptr; } @@ -215,10 +215,10 @@ void Map::setTile(uint16_t x, uint16_t y, uint8_t z, std::shared_ptr<Tile> newTi return; } - if (const auto leaf = getQTNode(x, y)) { - leaf->createFloor(z)->setTile(x, y, newTile); + if (const auto sector = getMapSector(x, y)) { + sector->createFloor(z)->setTile(x, y, newTile); } else { - root.getBestLeaf(x, y, 15)->createFloor(z)->setTile(x, y, newTile); + getBestMapSector(x, y)->createFloor(z)->setTile(x, y, newTile); } } @@ -315,7 +315,7 @@ bool Map::placeCreature(const Position ¢erPos, std::shared_ptr<Creature> cre toCylinder->internalAddThing(creature); const Position &dest = toCylinder->getPosition(); - getQTNode(dest.x, dest.y)->addCreature(creature); + getMapSector(dest.x, dest.y)->addCreature(creature); return true; } @@ -351,13 +351,13 @@ void Map::moveCreature(const std::shared_ptr<Creature> &creature, const std::sha // remove the creature oldTile->removeThing(creature, 0); - auto leaf = getQTNode(oldPos.x, oldPos.y); - auto new_leaf = getQTNode(newPos.x, newPos.y); + MapSector* old_sector = getMapSector(oldPos.x, oldPos.y); + MapSector* new_sector = getMapSector(newPos.x, newPos.y); // Switch the node ownership - if (leaf != new_leaf) { - leaf->removeCreature(creature); - new_leaf->addCreature(creature); + if (old_sector != new_sector) { + old_sector->removeCreature(creature); + new_sector->addCreature(creature); } // add the creature @@ -687,32 +687,47 @@ bool Map::getPathMatching(const std::shared_ptr<Creature> &creature, const Posit uint32_t Map::clean() { uint64_t start = OTSYS_TIME(); - size_t tiles = 0; + size_t qntTiles = 0; if (g_game().getGameState() == GAME_STATE_NORMAL) { g_game().setGameState(GAME_STATE_MAINTAIN); } - std::vector<std::shared_ptr<Item>> toRemove; - for (const auto &tile : g_game().getTilesToClean()) { - if (!tile) { - continue; - } - if (const auto items = tile->getItemList()) { - ++tiles; - for (const auto &item : *items) { - if (item->isCleanable()) { - toRemove.emplace_back(item); + ItemVector toRemove; + toRemove.reserve(128); + for (const auto &mit : mapSectors) { + for (uint8_t z = 0; z < MAP_MAX_LAYERS; ++z) { + if (const auto &floor = mit.second.getFloor(z)) { + for (auto &tiles : floor->getTiles()) { + for (const auto &[tile, cachedTile] : tiles) { + if (!tile || tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + continue; + } + + TileItemVector* itemList = tile->getItemList(); + if (!itemList) { + continue; + } + + ++qntTiles; + + for (auto it = ItemVector::const_reverse_iterator(itemList->getEndDownItem()), end = ItemVector::const_reverse_iterator(itemList->getBeginDownItem()); it != end; ++it) { + const auto &item = *it; + if (item->isCleanable()) { + toRemove.push_back(item); + } + } + } } } } } + const size_t count = toRemove.size(); for (const auto &item : toRemove) { g_game().internalRemoveItem(item, -1); } - size_t count = toRemove.size(); g_game().clearTilesToClean(); if (g_game().getGameState() == GAME_STATE_MAINTAIN) { @@ -720,6 +735,6 @@ uint32_t Map::clean() { } uint64_t end = OTSYS_TIME(); - g_logger().info("CLEAN: Removed {} item{} from {} tile{} in {} seconds", count, (count != 1 ? "s" : ""), tiles, (tiles != 1 ? "s" : ""), (end - start) / (1000.f)); + g_logger().info("CLEAN: Removed {} item{} from {} tile{} in {} seconds", count, (count != 1 ? "s" : ""), qntTiles, (qntTiles != 1 ? "s" : ""), (end - start) / (1000.f)); return count; } diff --git a/src/map/map.hpp b/src/map/map.hpp index 0894853e30e..e57328e12b3 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -29,9 +29,9 @@ class FrozenPathingConditionCall; * Map class. * Holds all the actual map-data */ -class Map : protected MapCache { +class Map : public MapCache { public: - static uint32_t clean(); + uint32_t clean(); std::filesystem::path getPath() const { return path; @@ -131,10 +131,6 @@ class Map : protected MapCache { std::map<std::string, Position> waypoints; - QTreeLeafNode* getQTNode(uint16_t x, uint16_t y) { - return QTreeNode::getLeafStatic<QTreeLeafNode*, QTreeNode*>(&root, x, y); - } - // Storage made by "loadFromXML" of houses, monsters and npcs for main map SpawnsMonster spawnsMonster; SpawnsNpc spawnsNpc; diff --git a/src/map/map_const.hpp b/src/map/map_const.hpp index 10f814d7f8b..109641d6bfe 100644 --- a/src/map/map_const.hpp +++ b/src/map/map_const.hpp @@ -18,6 +18,7 @@ static constexpr int8_t MAP_MAX_LAYERS = 16; static constexpr int8_t MAP_INIT_SURFACE_LAYER = 7; // (MAP_MAX_LAYERS / 2) -1 static constexpr int8_t MAP_LAYER_VIEW_LIMIT = 2; -static constexpr int32_t FLOOR_BITS = 3; -static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS); -static constexpr int32_t FLOOR_MASK = (FLOOR_SIZE - 1); +// SECTOR_SIZE must be power of 2 value +// The bigger the SECTOR_SIZE is the less hash map collision there should be but it'll consume more memory +static constexpr int32_t SECTOR_SIZE = 16; +static constexpr int32_t SECTOR_MASK = SECTOR_SIZE - 1; diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index ede4d3fd862..0448615ee99 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -154,10 +154,10 @@ void MapCache::setBasicTile(uint16_t x, uint16_t y, uint8_t z, const std::shared } const auto tile = static_tryGetTileFromCache(newTile); - if (const auto leaf = QTreeNode::getLeafStatic<QTreeLeafNode*, QTreeNode*>(&root, x, y)) { - leaf->createFloor(z)->setTileCache(x, y, tile); + if (const auto sector = getMapSector(x, y)) { + sector->createFloor(z)->setTileCache(x, y, tile); } else { - root.getBestLeaf(x, y, 15)->createFloor(z)->setTileCache(x, y, tile); + getBestMapSector(x, y)->createFloor(z)->setTileCache(x, y, tile); } } @@ -165,6 +165,46 @@ std::shared_ptr<BasicItem> MapCache::tryReplaceItemFromCache(const std::shared_p return static_tryGetItemFromCache(ref); } +MapSector* MapCache::createMapSector(const uint32_t x, const uint32_t y) { + const uint32_t index = x / SECTOR_SIZE | y / SECTOR_SIZE << 16; + const auto it = mapSectors.find(index); + if (it != mapSectors.end()) { + return &it->second; + } + + MapSector::newSector = true; + return &mapSectors[index]; +} + +MapSector* MapCache::getBestMapSector(uint32_t x, uint32_t y) { + MapSector::newSector = false; + const auto sector = createMapSector(x, y); + + if (MapSector::newSector) { + // update north sector + if (const auto northSector = getMapSector(x, y - SECTOR_SIZE)) { + northSector->sectorS = sector; + } + + // update west sector + if (const auto westSector = getMapSector(x - SECTOR_SIZE, y)) { + westSector->sectorE = sector; + } + + // update south sector + if (const auto southSector = getMapSector(x, y + SECTOR_SIZE)) { + sector->sectorS = southSector; + } + + // update east sector + if (const auto eastSector = getMapSector(x + SECTOR_SIZE, y)) { + sector->sectorE = eastSector; + } + } + + return sector; +} + void BasicTile::hash(size_t &h) const { std::array<uint32_t, 4> arr = { flags, houseId, type, isStatic }; for (const auto v : arr) { diff --git a/src/map/mapcache.hpp b/src/map/mapcache.hpp index bc3e59a700a..429d786972b 100644 --- a/src/map/mapcache.hpp +++ b/src/map/mapcache.hpp @@ -10,7 +10,7 @@ #pragma once #include "items/items_definitions.hpp" -#include "utils/qtreenode.hpp" +#include "utils/mapsector.hpp" class Map; class Tile; @@ -79,42 +79,6 @@ struct BasicTile { #pragma pack() -struct Floor { - explicit Floor(uint8_t z) : - z(z) { } - - std::shared_ptr<Tile> getTile(uint16_t x, uint16_t y) const { - std::shared_lock sl(mutex); - return tiles[x & FLOOR_MASK][y & FLOOR_MASK].first; - } - - void setTile(uint16_t x, uint16_t y, std::shared_ptr<Tile> tile) { - tiles[x & FLOOR_MASK][y & FLOOR_MASK].first = tile; - } - - std::shared_ptr<BasicTile> getTileCache(uint16_t x, uint16_t y) const { - std::shared_lock sl(mutex); - return tiles[x & FLOOR_MASK][y & FLOOR_MASK].second; - } - - void setTileCache(uint16_t x, uint16_t y, const std::shared_ptr<BasicTile> &newTile) { - tiles[x & FLOOR_MASK][y & FLOOR_MASK].second = newTile; - } - - uint8_t getZ() const { - return z; - } - - auto &getMutex() const { - return mutex; - } - -private: - std::pair<std::shared_ptr<Tile>, std::shared_ptr<BasicTile>> tiles[FLOOR_SIZE][FLOOR_SIZE] = {}; - mutable std::shared_mutex mutex; - uint8_t z { 0 }; -}; - class MapCache { public: virtual ~MapCache() = default; @@ -125,10 +89,31 @@ class MapCache { void flush(); + /** + * Creates a map sector. + * \returns A pointer to that map sector. + */ + MapSector* createMapSector(uint32_t x, uint32_t y); + MapSector* getBestMapSector(uint32_t x, uint32_t y); + + /** + * Gets a map sector. + * \returns A pointer to that map sector. + */ + MapSector* getMapSector(const uint32_t x, const uint32_t y) { + const auto it = mapSectors.find(x / SECTOR_SIZE | y / SECTOR_SIZE << 16); + return it != mapSectors.end() ? &it->second : nullptr; + } + + const MapSector* getMapSector(const uint32_t x, const uint32_t y) const { + const auto it = mapSectors.find(x / SECTOR_SIZE | y / SECTOR_SIZE << 16); + return it != mapSectors.end() ? &it->second : nullptr; + } + protected: std::shared_ptr<Tile> getOrCreateTileFromCache(const std::unique_ptr<Floor> &floor, uint16_t x, uint16_t y); - QTreeNode root; + std::unordered_map<uint32_t, MapSector> mapSectors; private: void parseItemAttr(const std::shared_ptr<BasicItem> &BasicItem, std::shared_ptr<Item> item); diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 7c0e80a0412..36cce6d535b 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -154,59 +154,57 @@ Spectators Spectators::find(const Position ¢erPos, bool multifloor, bool onl } } - const int_fast32_t min_y = centerPos.y + minRangeY; - const int_fast32_t min_x = centerPos.x + minRangeX; - const int_fast32_t max_y = centerPos.y + maxRangeY; - const int_fast32_t max_x = centerPos.x + maxRangeX; + const int32_t min_y = centerPos.y + minRangeY; + const int32_t min_x = centerPos.x + minRangeX; + const int32_t max_y = centerPos.y + maxRangeY; + const int32_t max_x = centerPos.x + maxRangeX; - const int_fast16_t minoffset = centerPos.getZ() - maxRangeZ; - const int_fast32_t x1 = std::min<int_fast32_t>(0xFFFF, std::max<int_fast32_t>(0, (min_x + minoffset))); - const int_fast32_t y1 = std::min<int_fast32_t>(0xFFFF, std::max<int_fast32_t>(0, (min_y + minoffset))); + const auto width = static_cast<uint32_t>(max_x - min_x); + const auto height = static_cast<uint32_t>(max_y - min_y); + const auto depth = static_cast<uint32_t>(maxRangeZ - minRangeZ); - const int_fast16_t maxoffset = centerPos.getZ() - minRangeZ; - const int_fast32_t x2 = std::min<int_fast32_t>(0xFFFF, std::max<int_fast32_t>(0, (max_x + maxoffset))); - const int_fast32_t y2 = std::min<int_fast32_t>(0xFFFF, std::max<int_fast32_t>(0, (max_y + maxoffset))); + const int32_t minoffset = centerPos.getZ() - maxRangeZ; + const int32_t x1 = std::min<int32_t>(0xFFFF, std::max<int32_t>(0, min_x + minoffset)); + const int32_t y1 = std::min<int32_t>(0xFFFF, std::max<int32_t>(0, min_y + minoffset)); - const uint_fast16_t startx1 = x1 - (x1 % FLOOR_SIZE); - const uint_fast16_t starty1 = y1 - (y1 % FLOOR_SIZE); - const uint_fast16_t endx2 = x2 - (x2 % FLOOR_SIZE); - const uint_fast16_t endy2 = y2 - (y2 % FLOOR_SIZE); + const int32_t maxoffset = centerPos.getZ() - minRangeZ; + const int32_t x2 = std::min<int32_t>(0xFFFF, std::max<int32_t>(0, max_x + maxoffset)); + const int32_t y2 = std::min<int32_t>(0xFFFF, std::max<int32_t>(0, max_y + maxoffset)); - const auto startLeaf = g_game().map.getQTNode(static_cast<uint16_t>(startx1), static_cast<uint16_t>(starty1)); - const QTreeLeafNode* leafS = startLeaf; - const QTreeLeafNode* leafE; + const int32_t startx1 = x1 - (x1 & SECTOR_MASK); + const int32_t starty1 = y1 - (y1 & SECTOR_MASK); + const int32_t endx2 = x2 - (x2 & SECTOR_MASK); + const int32_t endy2 = y2 - (y2 & SECTOR_MASK); SpectatorList spectators; spectators.reserve(std::max<uint8_t>(MAP_MAX_VIEW_PORT_X, MAP_MAX_VIEW_PORT_Y) * 2); - for (uint_fast16_t ny = starty1; ny <= endy2; ny += FLOOR_SIZE) { - leafE = leafS; - for (uint_fast16_t nx = startx1; nx <= endx2; nx += FLOOR_SIZE) { - if (leafE) { - const auto &node_list = (onlyPlayers ? leafE->player_list : leafE->creature_list); + const MapSector* startSector = g_game().map.getMapSector(startx1, starty1); + const MapSector* sectorS = startSector; + for (int32_t ny = starty1; ny <= endy2; ny += SECTOR_SIZE) { + const MapSector* sectorE = sectorS; + for (int32_t nx = startx1; nx <= endx2; nx += SECTOR_SIZE) { + if (sectorE) { + const auto &node_list = onlyPlayers ? sectorE->player_list : sectorE->creature_list; for (const auto &creature : node_list) { const auto &cpos = creature->getPosition(); - if (minRangeZ > cpos.z || maxRangeZ < cpos.z) { - continue; + if (static_cast<uint32_t>(static_cast<int32_t>(cpos.z) - minRangeZ) <= depth) { + const int_fast16_t offsetZ = Position::getOffsetZ(centerPos, cpos); + if (static_cast<uint32_t>(cpos.x - offsetZ - min_x) <= width && static_cast<uint32_t>(cpos.y - offsetZ - min_y) <= height) { + spectators.emplace_back(creature); + } } - - const int_fast16_t offsetZ = Position::getOffsetZ(centerPos, cpos); - if ((min_y + offsetZ) > cpos.y || (max_y + offsetZ) < cpos.y || (min_x + offsetZ) > cpos.x || (max_x + offsetZ) < cpos.x) { - continue; - } - - spectators.emplace_back(creature); } - leafE = leafE->leafE; + sectorE = sectorE->sectorE; } else { - leafE = g_game().map.getQTNode(static_cast<uint16_t>(nx + FLOOR_SIZE), static_cast<uint16_t>(ny)); + sectorE = g_game().map.getMapSector(nx + SECTOR_SIZE, ny); } } - if (leafS) { - leafS = leafS->leafS; + if (sectorS) { + sectorS = sectorS->sectorS; } else { - leafS = g_game().map.getQTNode(static_cast<uint16_t>(startx1), static_cast<uint16_t>(ny + FLOOR_SIZE)); + sectorS = g_game().map.getMapSector(startx1, ny + SECTOR_SIZE); } } diff --git a/src/map/utils/mapsector.cpp b/src/map/utils/mapsector.cpp new file mode 100644 index 00000000000..de036728b76 --- /dev/null +++ b/src/map/utils/mapsector.cpp @@ -0,0 +1,46 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR <opentibiabr@outlook.com> + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "creatures/creature.hpp" +#include "mapsector.hpp" + +bool MapSector::newSector = false; + +void MapSector::addCreature(const std::shared_ptr<Creature> &c) { + creature_list.emplace_back(c); + if (c->getPlayer()) { + player_list.emplace_back(c); + } +} + +void MapSector::removeCreature(const std::shared_ptr<Creature> &c) { + auto iter = std::find(creature_list.begin(), creature_list.end(), c); + if (iter == creature_list.end()) { + g_logger().error("[{}]: Creature not found in creature_list!", __FUNCTION__); + return; + } + + assert(iter != creature_list.end()); + *iter = creature_list.back(); + creature_list.pop_back(); + + if (c->getPlayer()) { + iter = std::find(player_list.begin(), player_list.end(), c); + if (iter == player_list.end()) { + g_logger().error("[{}]: Player not found in player_list!", __FUNCTION__); + return; + } + + assert(iter != player_list.end()); + *iter = player_list.back(); + player_list.pop_back(); + } +} diff --git a/src/map/utils/mapsector.hpp b/src/map/utils/mapsector.hpp new file mode 100644 index 00000000000..7b95db7f78b --- /dev/null +++ b/src/map/utils/mapsector.hpp @@ -0,0 +1,92 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR <opentibiabr@outlook.com> + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "map/map_const.hpp" + +class Creature; +class Tile; +struct BasicTile; + +struct Floor { + explicit Floor(uint8_t z) : + z(z) { } + + std::shared_ptr<Tile> getTile(uint16_t x, uint16_t y) const { + std::shared_lock sl(mutex); + return tiles[x & SECTOR_MASK][y & SECTOR_MASK].first; + } + + void setTile(uint16_t x, uint16_t y, std::shared_ptr<Tile> tile) { + tiles[x & SECTOR_MASK][y & SECTOR_MASK].first = tile; + } + + std::shared_ptr<BasicTile> getTileCache(uint16_t x, uint16_t y) const { + std::shared_lock sl(mutex); + return tiles[x & SECTOR_MASK][y & SECTOR_MASK].second; + } + + void setTileCache(uint16_t x, uint16_t y, const std::shared_ptr<BasicTile> &newTile) { + tiles[x & SECTOR_MASK][y & SECTOR_MASK].second = newTile; + } + + const auto &getTiles() const { + return tiles; + } + + uint8_t getZ() const { + return z; + } + + auto &getMutex() const { + return mutex; + } + +private: + std::pair<std::shared_ptr<Tile>, std::shared_ptr<BasicTile>> tiles[SECTOR_SIZE][SECTOR_SIZE] = {}; + mutable std::shared_mutex mutex; + uint8_t z { 0 }; +}; + +class MapSector { +public: + MapSector() = default; + + // non-copyable + MapSector(const MapSector &) = delete; + MapSector &operator=(const MapSector &) = delete; + + // non-moveable + MapSector(const MapSector &&) = delete; + MapSector &operator=(const MapSector &&) = delete; + + const std::unique_ptr<Floor> &createFloor(uint32_t z) { + return floors[z] ? floors[z] : (floors[z] = std::make_unique<Floor>(z)); + } + + const std::unique_ptr<Floor> &getFloor(uint8_t z) const { + return floors[z]; + } + + void addCreature(const std::shared_ptr<Creature> &c); + void removeCreature(const std::shared_ptr<Creature> &c); + +private: + static bool newSector; + MapSector* sectorS = nullptr; + MapSector* sectorE = nullptr; + std::vector<std::shared_ptr<Creature>> creature_list; + std::vector<std::shared_ptr<Creature>> player_list; + std::unique_ptr<Floor> floors[MAP_MAX_LAYERS] = {}; + uint32_t floorBits = 0; + + friend class Spectators; + friend class MapCache; +}; diff --git a/src/map/utils/qtreenode.cpp b/src/map/utils/qtreenode.cpp deleted file mode 100644 index 279bcabe3fa..00000000000 --- a/src/map/utils/qtreenode.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2023 OpenTibiaBR <opentibiabr@outlook.com> - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "creatures/creature.hpp" -#include "qtreenode.hpp" - -bool QTreeLeafNode::newLeaf = false; - -QTreeLeafNode* QTreeNode::getLeaf(uint32_t x, uint32_t y) { - if (leaf) { - return static_cast<QTreeLeafNode*>(this); - } - - const auto node = child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; - return node ? node->getLeaf(x << 1, y << 1) : nullptr; -} - -QTreeLeafNode* QTreeNode::createLeaf(uint32_t x, uint32_t y, uint32_t level) { - if (isLeaf()) { - return static_cast<QTreeLeafNode*>(this); - } - - const uint32_t index = ((x & 0x8000) >> 15) | ((y & 0x8000) >> 14); - if (!child[index]) { - if (level != FLOOR_BITS) { - child[index] = new QTreeNode(); - } else { - child[index] = new QTreeLeafNode(); - QTreeLeafNode::newLeaf = true; - } - } - - return child[index]->createLeaf(x * 2, y * 2, level - 1); -} - -QTreeLeafNode* QTreeNode::getBestLeaf(uint32_t x, uint32_t y, uint32_t level) { - QTreeLeafNode::newLeaf = false; - auto tempLeaf = createLeaf(x, y, level); - - if (QTreeLeafNode::newLeaf) { - // update north - if (const auto northLeaf = getLeaf(x, y - FLOOR_SIZE)) { - northLeaf->leafS = tempLeaf; - } - - // update west leaf - if (const auto westLeaf = getLeaf(x - FLOOR_SIZE, y)) { - westLeaf->leafE = tempLeaf; - } - - // update south - if (const auto southLeaf = getLeaf(x, y + FLOOR_SIZE)) { - tempLeaf->leafS = southLeaf; - } - - // update east - if (const auto eastLeaf = getLeaf(x + FLOOR_SIZE, y)) { - tempLeaf->leafE = eastLeaf; - } - } - - return tempLeaf; -} - -void QTreeLeafNode::addCreature(const std::shared_ptr<Creature> &c) { - creature_list.push_back(c); - - if (c->getPlayer()) { - player_list.push_back(c); - } -} - -void QTreeLeafNode::removeCreature(std::shared_ptr<Creature> c) { - auto iter = std::find(creature_list.begin(), creature_list.end(), c); - if (iter == creature_list.end()) { - g_logger().error("[{}]: Creature not found in creature_list!", __FUNCTION__); - return; - } - - assert(iter != creature_list.end()); - *iter = creature_list.back(); - creature_list.pop_back(); - - if (c->getPlayer()) { - iter = std::find(player_list.begin(), player_list.end(), c); - if (iter == player_list.end()) { - g_logger().error("[{}]: Player not found in player_list!", __FUNCTION__); - return; - } - - assert(iter != player_list.end()); - *iter = player_list.back(); - player_list.pop_back(); - } -} diff --git a/src/map/utils/qtreenode.hpp b/src/map/utils/qtreenode.hpp deleted file mode 100644 index 83f052a6abf..00000000000 --- a/src/map/utils/qtreenode.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2023 OpenTibiaBR <opentibiabr@outlook.com> - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#pragma once - -#include "map/map_const.hpp" - -struct Floor; -class QTreeLeafNode; -class Creature; - -class QTreeNode { -public: - constexpr QTreeNode() = default; - - virtual ~QTreeNode() { } - - // non-copyable - QTreeNode(const QTreeNode &) = delete; - QTreeNode &operator=(const QTreeNode &) = delete; - - bool isLeaf() const { - return leaf; - } - - template <typename Leaf, typename Node> - static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) { - do { - node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; - if (!node) { - return nullptr; - } - - x <<= 1; - y <<= 1; - } while (!node->leaf); - return static_cast<Leaf>(node); - } - - QTreeLeafNode* getLeaf(uint32_t x, uint32_t y); - QTreeLeafNode* getBestLeaf(uint32_t x, uint32_t y, uint32_t level); - - QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level); - -protected: - QTreeNode* child[4] = {}; - bool leaf = false; -}; - -class QTreeLeafNode final : public QTreeNode { -public: - QTreeLeafNode() { - QTreeNode::leaf = true; - newLeaf = true; - } - - // non-copyable - QTreeLeafNode(const QTreeLeafNode &) = delete; - QTreeLeafNode &operator=(const QTreeLeafNode &) = delete; - - const std::unique_ptr<Floor> &createFloor(uint32_t z) { - return array[z] ? array[z] : (array[z] = std::make_unique<Floor>(z)); - } - - const std::unique_ptr<Floor> &getFloor(uint8_t z) const { - return array[z]; - } - - void addCreature(const std::shared_ptr<Creature> &c); - void removeCreature(std::shared_ptr<Creature> c); - -private: - static bool newLeaf; - QTreeLeafNode* leafS = nullptr; - QTreeLeafNode* leafE = nullptr; - - std::unique_ptr<Floor> array[MAP_MAX_LAYERS] = {}; - - std::vector<std::shared_ptr<Creature>> creature_list; - std::vector<std::shared_ptr<Creature>> player_list; - - friend class Map; - friend class MapCache; - friend class QTreeNode; - friend class Spectators; -}; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 1c21dc87210..b3e4eaffb42 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -203,7 +203,7 @@ <ClInclude Include="..\src\map\spectators.hpp" /> <ClInclude Include="..\src\map\town.hpp" /> <ClInclude Include="..\src\map\utils\astarnodes.hpp" /> - <ClInclude Include="..\src\map\utils\qtreenode.hpp" /> + <ClInclude Include="..\src\map\utils\mapsector.hpp" /> <ClInclude Include="..\src\security\rsa.hpp" /> <ClInclude Include="..\src\server\network\connection\connection.hpp" /> <ClInclude Include="..\src\server\network\message\networkmessage.hpp" /> @@ -388,7 +388,7 @@ <ClCompile Include="..\src\map\house\housetile.cpp" /> <ClCompile Include="..\src\map\spectators.cpp" /> <ClCompile Include="..\src\map\utils\astarnodes.cpp" /> - <ClCompile Include="..\src\map\utils\qtreenode.cpp" /> + <ClCompile Include="..\src\map\utils\mapsector.cpp" /> <ClCompile Include="..\src\map\map.cpp" /> <ClCompile Include="..\src\map\mapcache.cpp" /> <ClCompile Include="..\src\main.cpp" /> From caa35461ea75fea7fdbdf34a308b2183e499b5b9 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Fri, 24 May 2024 00:46:48 -0300 Subject: [PATCH 48/95] improve: move wheel scrolls to kv (#2637) --- ...e_wheel_scrolls_from_storagename_to_kv.lua | 24 +++++++++++++ data/scripts/actions/items/wheel_scrolls.lua | 17 +++++----- data/scripts/lib/register_migrations.lua | 2 +- src/creatures/players/wheel/player_wheel.cpp | 34 ++++++++++++------- .../creatures/player/player_functions.cpp | 2 ++ 5 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua diff --git a/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua b/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua new file mode 100644 index 00000000000..a5cc9a123f4 --- /dev/null +++ b/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua @@ -0,0 +1,24 @@ +local promotionScrolls = { + { oldScroll = "wheel.scroll.abridged", newScroll = "abridged" }, + { oldScroll = "wheel.scroll.basic", newScroll = "basic" }, + { oldScroll = "wheel.scroll.revised", newScroll = "revised" }, + { oldScroll = "wheel.scroll.extended", newScroll = "extended" }, + { oldScroll = "wheel.scroll.advanced", newScroll = "advanced" }, +} + +local function migrate(player) + for _, scrollTable in ipairs(promotionScrolls) do + local oldStorage = player:getStorageValueByName(scrollTable.oldScroll) + if oldStorage > 0 then + player:kv():scoped("wheel-of-destiny"):scoped("scrolls"):set(scrollTable.newScroll, true) + end + end +end + +local migration = Migration("20241715984279_move_wheel_scrolls_from_storagename_to_kv") + +function migration:onExecute() + self:forEachPlayer(migrate) +end + +migration:register() diff --git a/data/scripts/actions/items/wheel_scrolls.lua b/data/scripts/actions/items/wheel_scrolls.lua index b42339a706e..61aaa6fe40a 100644 --- a/data/scripts/actions/items/wheel_scrolls.lua +++ b/data/scripts/actions/items/wheel_scrolls.lua @@ -1,9 +1,9 @@ local promotionScrolls = { - [43946] = { storageName = "wheel.scroll.abridged", points = 3, name = "abridged promotion scroll" }, - [43947] = { storageName = "wheel.scroll.basic", points = 5, name = "basic promotion scroll" }, - [43948] = { storageName = "wheel.scroll.revised", points = 9, name = "revised promotion scroll" }, - [43949] = { storageName = "wheel.scroll.extended", points = 13, name = "extended promotion scroll" }, - [43950] = { storageName = "wheel.scroll.advanced", points = 20, name = "advanced promotion scroll" }, + [43946] = { name = "abridged", points = 3, itemName = "abridged promotion scroll" }, + [43947] = { name = "basic", points = 5, itemName = "basic promotion scroll" }, + [43948] = { name = "revised", points = 9, itemName = "revised promotion scroll" }, + [43949] = { name = "extended", points = 13, itemName = "extended promotion scroll" }, + [43950] = { name = "advanced", points = 20, itemName = "advanced promotion scroll" }, } local scroll = Action() @@ -15,13 +15,14 @@ function scroll.onUse(player, item, fromPosition, target, toPosition, isHotkey) end local scrollData = promotionScrolls[item:getId()] - if player:getStorageValueByName(scrollData.storageName) == 1 then + local scrollKV = player:kv():scoped("wheel-of-destiny"):scoped("scrolls") + if scrollKV:get(scrollData.name) then player:sendTextMessage(MESSAGE_LOOK, "You have already deciphered this scroll.") return true end - player:setStorageValueByName(scrollData.storageName, 1) - player:sendTextMessage(MESSAGE_LOOK, "You have gained " .. scrollData.points .. " promotion points for the Wheel of Destiny by deciphering the " .. scrollData.name .. ".") + 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 end diff --git a/data/scripts/lib/register_migrations.lua b/data/scripts/lib/register_migrations.lua index 5a2734dfc51..26b9a7b1a94 100644 --- a/data/scripts/lib/register_migrations.lua +++ b/data/scripts/lib/register_migrations.lua @@ -45,7 +45,7 @@ function Migration:register() return end if not self:_validateName() then - error("Invalid migration name: " .. self.name .. ". Migration names must be in the format: <timestamp>_<description>. Example: 20231128213149_add_new_monsters") + logger.error("Invalid migration name: " .. self.name .. ". Migration names must be in the format: <timestamp>_<description>. Example: 20231128213149_add_new_monsters") end table.insert(Migration.registry, self) diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index c4fdbf5e169..4c7a3dc52e0 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -130,16 +130,16 @@ namespace { struct PromotionScroll { uint16_t itemId; - std::string storageKey; + std::string name; uint8_t extraPoints; }; std::vector<PromotionScroll> WheelOfDestinyPromotionScrolls = { - { 43946, "wheel.scroll.abridged", 3 }, - { 43947, "wheel.scroll.basic", 5 }, - { 43948, "wheel.scroll.revised", 9 }, - { 43949, "wheel.scroll.extended", 13 }, - { 43950, "wheel.scroll.advanced", 20 }, + { 43946, "abridged", 3 }, + { 43947, "basic", 5 }, + { 43948, "revised", 9 }, + { 43949, "extended", 13 }, + { 43950, "advanced", 20 }, }; } // namespace @@ -744,18 +744,21 @@ int PlayerWheel::getSpellAdditionalDuration(const std::string &spellName) const } void PlayerWheel::addPromotionScrolls(NetworkMessage &msg) const { - uint16_t count = 0; std::vector<uint16_t> unlockedScrolls; for (const auto &scroll : WheelOfDestinyPromotionScrolls) { - auto storageValue = m_player.getStorageValueByName(scroll.storageKey); - if (storageValue > 0) { - count++; + const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); + if (!scrollKv) { + continue; + } + + auto scrollOpt = scrollKv->get(scroll.name); + if (scrollOpt && scrollOpt->get<bool>()) { unlockedScrolls.push_back(scroll.itemId); } } - msg.add<uint16_t>(count); + msg.add<uint16_t>(unlockedScrolls.size()); for (const auto &itemId : unlockedScrolls) { msg.add<uint16_t>(itemId); } @@ -1239,8 +1242,13 @@ uint16_t PlayerWheel::getExtraPoints() const { uint16_t totalBonus = 0; for (const auto &scroll : WheelOfDestinyPromotionScrolls) { - auto storageValue = m_player.getStorageValueByName(scroll.storageKey); - if (storageValue > 0) { + const auto &scrollKv = m_player.kv()->scoped("wheel-of-destiny")->scoped("scrolls"); + if (!scrollKv) { + continue; + } + + auto scrollKV = scrollKv->get(scroll.name); + if (scrollKV && scrollKV->get<bool>()) { totalBonus += scroll.extraPoints; } } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 50774e35e2a..413f1bd3f47 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1767,6 +1767,7 @@ int PlayerFunctions::luaPlayerGetStorageValueByName(lua_State* L) { return 0; } + g_logger().warn("The function 'player:getStorageValueByName' is deprecated and will be removed in future versions, please use KV system"); auto name = getString(L, 2); lua_pushnumber(L, player->getStorageValueByName(name)); return 1; @@ -1781,6 +1782,7 @@ int PlayerFunctions::luaPlayerSetStorageValueByName(lua_State* L) { return 0; } + g_logger().warn("The function 'player:setStorageValueByName' is deprecated and will be removed in future versions, please use KV system"); auto storageName = getString(L, 2); int32_t value = getNumber<int32_t>(L, 3); From c545325193705989e2e912a08b3d763ad0672301 Mon Sep 17 00:00:00 2001 From: Karin <pessoacarlos98@gmail.com> Date: Fri, 24 May 2024 08:56:43 -0300 Subject: [PATCH 49/95] fix: prevent lag stacking items on npc with shopping bags (#2640) --- src/game/game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index 6188973781a..afc58e75198 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5165,6 +5165,23 @@ void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint return; } + if (inBackpacks) { + uint32_t maxContainer = static_cast<uint32_t>(g_configManager().getNumber(MAX_CONTAINER, __FUNCTION__)); + auto backpack = player->getInventoryItem(CONST_SLOT_BACKPACK); + auto mainBackpack = backpack ? backpack->getContainer() : nullptr; + + if (mainBackpack && mainBackpack->getContainerHoldingCount() >= maxContainer) { + player->sendCancelMessage(RETURNVALUE_CONTAINERISFULL); + return; + } + + std::shared_ptr<Tile> tile = player->getTile(); + if (tile && tile->getItemCount() >= 20) { + player->sendCancelMessage(RETURNVALUE_CONTAINERISFULL); + return; + } + } + merchant->onPlayerBuyItem(player, it.id, count, amount, ignoreCap, inBackpacks); player->updateUIExhausted(); } From 1df6ce57d6ab19912dcb3b326e997be38b09926b Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Fri, 24 May 2024 08:57:04 -0300 Subject: [PATCH 50/95] feat: vip groups (#2635) --- data-otservbr-global/migrations/45.lua | 55 +++- data-otservbr-global/migrations/46.lua | 3 + schema.sql | 57 +++- src/creatures/CMakeLists.txt | 1 + src/creatures/creatures_definitions.hpp | 33 ++- src/creatures/players/player.cpp | 105 ++------ src/creatures/players/player.hpp | 19 +- src/creatures/players/vip/player_vip.cpp | 247 ++++++++++++++++++ src/creatures/players/vip/player_vip.hpp | 74 ++++++ src/game/game.cpp | 16 +- src/game/game.hpp | 2 +- src/io/functions/iologindata_load_player.cpp | 30 ++- src/io/iologindata.cpp | 77 ++++-- src/io/iologindata.hpp | 7 + .../creatures/player/player_functions.cpp | 4 +- src/server/network/protocol/protocolgame.cpp | 84 +++++- src/server/network/protocol/protocolgame.hpp | 4 + vcproj/canary.vcxproj | 2 + 18 files changed, 658 insertions(+), 162 deletions(-) create mode 100644 data-otservbr-global/migrations/46.lua create mode 100644 src/creatures/players/vip/player_vip.cpp create mode 100644 src/creatures/players/vip/player_vip.hpp diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua index 86a6d8ffec1..c606f18522e 100644 --- a/data-otservbr-global/migrations/45.lua +++ b/data-otservbr-global/migrations/45.lua @@ -1,3 +1,56 @@ function onUpdateDatabase() - return false -- true = There are others migrations file | false = this is the last migration file + logger.info("Updating database to version 46 (feat: vip groups)") + + db.query([[ + CREATE TABLE IF NOT EXISTS `account_vipgroups` ( + `id` tinyint(3) UNSIGNED NOT NULL, + `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is', + `name` varchar(128) NOT NULL, + `customizable` BOOLEAN NOT NULL DEFAULT '1', + CONSTRAINT `account_vipgroups_pk` PRIMARY KEY (`id`, `account_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + ]]) + + db.query([[ + CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0); + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0); + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0); + END; + ]]) + + db.query([[ + CREATE TABLE IF NOT EXISTS `account_vipgrouplist` ( + `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is', + `player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry', + `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', + INDEX `account_id` (`account_id`), + INDEX `player_id` (`player_id`), + INDEX `vipgroup_id` (`vipgroup_id`), + CONSTRAINT `account_vipgrouplist_unique` UNIQUE (`account_id`, `player_id`, `vipgroup_id`), + CONSTRAINT `account_vipgrouplist_player_fk` + FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) + ON DELETE CASCADE, + CONSTRAINT `account_vipgrouplist_vipgroup_fk` + FOREIGN KEY (`vipgroup_id`, `account_id`) REFERENCES `account_vipgroups` (`id`, `account_id`) + ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + ]]) + + db.query([[ + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) + SELECT 1, id, 'Friends', 0 FROM `accounts`; + ]]) + + db.query([[ + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) + SELECT 2, id, 'Enemies', 0 FROM `accounts`; + ]]) + + db.query([[ + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) + SELECT 3, id, 'Trading Partners', 0 FROM `accounts`; + ]]) + + return true end diff --git a/data-otservbr-global/migrations/46.lua b/data-otservbr-global/migrations/46.lua new file mode 100644 index 00000000000..86a6d8ffec1 --- /dev/null +++ b/data-otservbr-global/migrations/46.lua @@ -0,0 +1,3 @@ +function onUpdateDatabase() + return false -- true = There are others migrations file | false = this is the last migration file +end diff --git a/schema.sql b/schema.sql index 624f434509e..7bbf6c86ac5 100644 --- a/schema.sql +++ b/schema.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` ( CONSTRAINT `server_config_pk` PRIMARY KEY (`config`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '45'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0'); +INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '46'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0'); -- Table structure `accounts` CREATE TABLE IF NOT EXISTS `accounts` ( @@ -215,6 +215,44 @@ CREATE TABLE IF NOT EXISTS `account_viplist` ( ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- Table structure `account_vipgroup` +CREATE TABLE IF NOT EXISTS `account_vipgroups` ( + `id` tinyint(3) UNSIGNED NOT NULL, + `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is', + `name` varchar(128) NOT NULL, + `customizable` BOOLEAN NOT NULL DEFAULT '1', + CONSTRAINT `account_vipgroups_pk` PRIMARY KEY (`id`, `account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Trigger +-- +DELIMITER // +CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0); + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0); + INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0); +END +// +DELIMITER ; + +-- Table structure `account_vipgrouplist` +CREATE TABLE IF NOT EXISTS `account_vipgrouplist` ( + `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is', + `player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry', + `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', + INDEX `account_id` (`account_id`), + INDEX `player_id` (`player_id`), + INDEX `vipgroup_id` (`vipgroup_id`), + CONSTRAINT `account_vipgrouplist_unique` UNIQUE (`account_id`, `player_id`, `vipgroup_id`), + CONSTRAINT `account_vipgrouplist_player_fk` + FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) + ON DELETE CASCADE, + CONSTRAINT `account_vipgrouplist_vipgroup_fk` + FOREIGN KEY (`vipgroup_id`, `account_id`) REFERENCES `account_vipgroups` (`id`, `account_id`) + ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- Table structure `boosted_boss` CREATE TABLE IF NOT EXISTS `boosted_boss` ( `boostname` TEXT, @@ -372,9 +410,9 @@ CREATE TABLE IF NOT EXISTS `guild_ranks` ( -- DELIMITER // CREATE TRIGGER `oncreate_guilds` AFTER INSERT ON `guilds` FOR EACH ROW BEGIN - INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('The Leader', 3, NEW.`id`); - INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Vice-Leader', 2, NEW.`id`); - INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Member', 1, NEW.`id`); + INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('The Leader', 3, NEW.`id`); + INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Vice-Leader', 2, NEW.`id`); + INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Member', 1, NEW.`id`); END // DELIMITER ; @@ -428,9 +466,8 @@ CREATE TABLE IF NOT EXISTS `houses` ( -- trigger -- DELIMITER // -CREATE TRIGGER `ondelete_players` BEFORE DELETE ON `players` - FOR EACH ROW BEGIN - UPDATE `houses` SET `owner` = 0 WHERE `owner` = OLD.`id`; +CREATE TRIGGER `ondelete_players` BEFORE DELETE ON `players` FOR EACH ROW BEGIN + UPDATE `houses` SET `owner` = 0 WHERE `owner` = OLD.`id`; END // DELIMITER ; @@ -815,3 +852,9 @@ INSERT INTO `players` (4, 'Paladin Sample', 1, 1, 8, 3, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0), (5, 'Knight Sample', 1, 1, 8, 4, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0), (6, 'GOD', 6, 1, 2, 0, 155, 155, 100, 113, 115, 95, 39, 75, 0, 60, 60, 0, 8, '', 410, 1, 10, 0, 10, 0, 10, 0, 10, 0); + +-- Create vip groups for GOD account +INSERT INTO `account_vipgroups` (`id`, `name`, `account_id`. `customizable`) VALUES +(1, 'Friends', 1, 0), +(2, 'Enemies', 1, 0), +(3, 'Trading Partners', 1, 0); diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index 6715281439f..af6ba129eac 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -27,4 +27,5 @@ target_sources(${PROJECT_NAME}_lib PRIVATE players/wheel/player_wheel.cpp players/wheel/wheel_gems.cpp players/vocations/vocation.cpp + players/vip/player_vip.cpp ) diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 136f587a64a..33f62dbd2aa 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -714,11 +714,11 @@ enum ChannelEvent_t : uint8_t { CHANNELEVENT_EXCLUDE = 3, }; -enum VipStatus_t : uint8_t { - VIPSTATUS_OFFLINE = 0, - VIPSTATUS_ONLINE = 1, - VIPSTATUS_PENDING = 2, - VIPSTATUS_TRAINING = 3 +enum class VipStatus_t : uint8_t { + Offline = 0, + Online = 1, + Pending = 2, + Training = 3 }; enum Vocation_t : uint16_t { @@ -1397,18 +1397,29 @@ struct CreatureIcon { struct Position; struct VIPEntry { - VIPEntry(uint32_t initGuid, std::string initName, std::string initDescription, uint32_t initIcon, bool initNotify) : + VIPEntry(uint32_t initGuid, const std::string &initName, const std::string &initDescription, uint32_t initIcon, bool initNotify) : guid(initGuid), name(std::move(initName)), description(std::move(initDescription)), icon(initIcon), notify(initNotify) { } - uint32_t guid; - std::string name; - std::string description; - uint32_t icon; - bool notify; + uint32_t guid = 0; + std::string name = ""; + std::string description = ""; + uint32_t icon = 0; + bool notify = false; +}; + +struct VIPGroupEntry { + VIPGroupEntry(uint8_t initId, const std::string &initName, bool initCustomizable) : + id(initId), + name(std::move(initName)), + customizable(initCustomizable) { } + + uint8_t id = 0; + std::string name = ""; + bool customizable = false; }; struct Skill { diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 3182607ecba..553ca2e3b04 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -49,6 +49,7 @@ Player::Player(ProtocolGame_ptr p) : lastPong(lastPing), inbox(std::make_shared<Inbox>(ITEM_INBOX)), client(std::move(p)) { + m_playerVIP = std::make_unique<PlayerVIP>(*this); m_wheelPlayer = std::make_unique<PlayerWheel>(*this); m_playerAchievement = std::make_unique<PlayerAchievement>(*this); m_playerBadge = std::make_unique<PlayerBadge>(*this); @@ -649,10 +650,10 @@ phmap::flat_hash_map<Blessings_t, std::string> Player::getBlessingNames() const void Player::setTraining(bool value) { for (const auto &[key, player] : g_game().getPlayers()) { if (!this->isInGhostMode() || player->isAccessPlayer()) { - player->notifyStatusChange(static_self_cast<Player>(), value ? VIPSTATUS_TRAINING : VIPSTATUS_ONLINE, false); + player->vip()->notifyStatusChange(static_self_cast<Player>(), value ? VipStatus_t::Training : VipStatus_t::Online, false); } } - this->statusVipList = VIPSTATUS_TRAINING; + vip()->setStatus(VipStatus_t::Training); setExerciseTraining(value); } @@ -2985,7 +2986,7 @@ void Player::despawn() { // show player as pending for (const auto &[key, player] : g_game().getPlayers()) { - player->notifyStatusChange(static_self_cast<Player>(), VIPSTATUS_PENDING, false); + player->vip()->notifyStatusChange(static_self_cast<Player>(), VipStatus_t::Pending, false); } setDead(true); @@ -3039,13 +3040,13 @@ void Player::removeList() { g_game().removePlayer(static_self_cast<Player>()); for (const auto &[key, player] : g_game().getPlayers()) { - player->notifyStatusChange(static_self_cast<Player>(), VIPSTATUS_OFFLINE); + player->vip()->notifyStatusChange(static_self_cast<Player>(), VipStatus_t::Offline); } } void Player::addList() { for (const auto &[key, player] : g_game().getPlayers()) { - player->notifyStatusChange(static_self_cast<Player>(), this->statusVipList); + player->vip()->notifyStatusChange(static_self_cast<Player>(), vip()->getStatus()); } g_game().addPlayer(static_self_cast<Player>()); @@ -3060,82 +3061,6 @@ void Player::removePlayer(bool displayEffect, bool forced /*= true*/) { } } -void Player::notifyStatusChange(std::shared_ptr<Player> loginPlayer, VipStatus_t status, bool message) const { - if (!client) { - return; - } - - if (!VIPList.contains(loginPlayer->guid)) { - return; - } - - client->sendUpdatedVIPStatus(loginPlayer->guid, status); - - if (message) { - if (status == VIPSTATUS_ONLINE) { - client->sendTextMessage(TextMessage(MESSAGE_FAILURE, loginPlayer->getName() + " has logged in.")); - } else if (status == VIPSTATUS_OFFLINE) { - client->sendTextMessage(TextMessage(MESSAGE_FAILURE, loginPlayer->getName() + " has logged out.")); - } - } -} - -bool Player::removeVIP(uint32_t vipGuid) { - if (!VIPList.erase(vipGuid)) { - return false; - } - - VIPList.erase(vipGuid); - if (account) { - IOLoginData::removeVIPEntry(account->getID(), vipGuid); - } - - return true; -} - -bool Player::addVIP(uint32_t vipGuid, const std::string &vipName, VipStatus_t status) { - if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53 - sendTextMessage(MESSAGE_FAILURE, "You cannot add more buddies."); - return false; - } - - if (!VIPList.insert(vipGuid).second) { - sendTextMessage(MESSAGE_FAILURE, "This player is already in your list."); - return false; - } - - if (account) { - IOLoginData::addVIPEntry(account->getID(), vipGuid, "", 0, false); - } - - if (client) { - client->sendVIP(vipGuid, vipName, "", 0, false, status); - } - - return true; -} - -bool Player::addVIPInternal(uint32_t vipGuid) { - if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53 - return false; - } - - return VIPList.insert(vipGuid).second; -} - -bool Player::editVIP(uint32_t vipGuid, const std::string &description, uint32_t icon, bool notify) const { - auto it = VIPList.find(vipGuid); - if (it == VIPList.end()) { - return false; // player is not in VIP - } - - if (account) { - IOLoginData::editVIPEntry(account->getID(), vipGuid, description, icon, notify); - } - - return true; -} - // close container and its child containers void Player::autoCloseContainers(std::shared_ptr<Container> container) { std::vector<uint32_t> closeList; @@ -6293,15 +6218,6 @@ std::pair<uint64_t, uint64_t> Player::getForgeSliversAndCores() const { return std::make_pair(sliverCount, coreCount); } -size_t Player::getMaxVIPEntries() const { - if (group->maxVipEntries != 0) { - return group->maxVipEntries; - } else if (isPremium()) { - return 100; - } - return 20; -} - size_t Player::getMaxDepotItems() const { if (group->maxDepotItems != 0) { return group->maxDepotItems; @@ -8079,6 +7995,15 @@ const std::unique_ptr<PlayerTitle> &Player::title() const { return m_playerTitle; } +// VIP interface +std::unique_ptr<PlayerVIP> &Player::vip() { + return m_playerVIP; +} + +const std::unique_ptr<PlayerVIP> &Player::vip() const { + return m_playerVIP; +} + void Player::sendLootMessage(const std::string &message) const { auto party = getParty(); if (!party) { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index f61e33a37ef..58bebfb9f2f 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -37,6 +37,7 @@ #include "enums/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/players/vip/player_vip.hpp" class House; class NetworkMessage; @@ -54,6 +55,7 @@ class PlayerWheel; class PlayerAchievement; class PlayerBadge; class PlayerTitle; +class PlayerVIP; class Spectators; class Account; @@ -61,6 +63,7 @@ struct ModalWindow; struct Achievement; struct Badge; struct Title; +struct VIPGroup; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -830,13 +833,6 @@ class Player final : public Creature, public Cylinder, public Bankable { return shopOwner; } - // V.I.P. functions - void notifyStatusChange(std::shared_ptr<Player> player, VipStatus_t status, bool message = true) const; - bool removeVIP(uint32_t vipGuid); - bool addVIP(uint32_t vipGuid, const std::string &vipName, VipStatus_t status); - bool addVIPInternal(uint32_t vipGuid); - bool editVIP(uint32_t vipGuid, const std::string &description, uint32_t icon, bool notify) const; - // follow functions bool setFollowCreature(std::shared_ptr<Creature> creature) override; void goToFollowCreature() override; @@ -1050,7 +1046,6 @@ class Player final : public Creature, public Cylinder, public Bankable { bool hasKilled(std::shared_ptr<Player> player) const; - size_t getMaxVIPEntries() const; size_t getMaxDepotItems() const; // tile @@ -2630,6 +2625,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr<PlayerTitle> &title(); const std::unique_ptr<PlayerTitle> &title() const; + // Player vip interface + std::unique_ptr<PlayerVIP> &vip(); + const std::unique_ptr<PlayerVIP> &vip() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr<Container> getLootPouch(); @@ -2716,7 +2715,6 @@ class Player final : public Creature, public Cylinder, public Bankable { void addBosstiaryKill(const std::shared_ptr<MonsterType> &mType); phmap::flat_hash_set<uint32_t> attackedSet; - phmap::flat_hash_set<uint32_t> VIPList; std::map<uint8_t, OpenContainer> openContainers; std::map<uint32_t, std::shared_ptr<DepotLocker>> depotLockerMap; @@ -2911,7 +2909,6 @@ class Player final : public Creature, public Cylinder, public Bankable { FightMode_t fightMode = FIGHTMODE_ATTACK; Faction_t faction = FACTION_PLAYER; QuickLootFilter_t quickLootFilter; - VipStatus_t statusVipList = VIPSTATUS_ONLINE; PlayerPronoun_t pronoun = PLAYERPRONOUN_THEY; bool chaseMode = false; @@ -3026,11 +3023,13 @@ class Player final : public Creature, public Cylinder, public Bankable { friend class PlayerAchievement; friend class PlayerBadge; friend class PlayerTitle; + friend class PlayerVIP; std::unique_ptr<PlayerWheel> m_wheelPlayer; std::unique_ptr<PlayerAchievement> m_playerAchievement; std::unique_ptr<PlayerBadge> m_playerBadge; std::unique_ptr<PlayerTitle> m_playerTitle; + std::unique_ptr<PlayerVIP> m_playerVIP; std::mutex quickLootMutex; diff --git a/src/creatures/players/vip/player_vip.cpp b/src/creatures/players/vip/player_vip.cpp new file mode 100644 index 00000000000..b4b1642ec69 --- /dev/null +++ b/src/creatures/players/vip/player_vip.cpp @@ -0,0 +1,247 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR <opentibiabr@outlook.com> + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "creatures/players/vip/player_vip.hpp" + +#include "io/iologindata.hpp" + +#include "game/game.hpp" +#include "creatures/players/player.hpp" + +const uint8_t PlayerVIP::firstID = 1; +const uint8_t PlayerVIP::lastID = 8; + +PlayerVIP::PlayerVIP(Player &player) : + m_player(player) { } + +size_t PlayerVIP::getMaxEntries() const { + if (m_player.group && m_player.group->maxVipEntries != 0) { + return m_player.group->maxVipEntries; + } else if (m_player.isPremium()) { + return 100; + } + return 20; +} + +uint8_t PlayerVIP::getMaxGroupEntries() const { + if (m_player.isPremium()) { + return 8; // max number of groups is 8 (5 custom and 3 default) + } + return 0; +} + +void PlayerVIP::notifyStatusChange(std::shared_ptr<Player> loginPlayer, VipStatus_t status, bool message) const { + if (!m_player.client) { + return; + } + + if (!vipGuids.contains(loginPlayer->getGUID())) { + return; + } + + m_player.client->sendUpdatedVIPStatus(loginPlayer->getGUID(), status); + + if (message) { + if (status == VipStatus_t::Online) { + m_player.sendTextMessage(TextMessage(MESSAGE_FAILURE, fmt::format("{} has logged in.", loginPlayer->getName()))); + } else if (status == VipStatus_t::Offline) { + m_player.sendTextMessage(TextMessage(MESSAGE_FAILURE, fmt::format("{} has logged out.", loginPlayer->getName()))); + } + } +} + +bool PlayerVIP::remove(uint32_t vipGuid) { + if (!vipGuids.erase(vipGuid)) { + return false; + } + + vipGuids.erase(vipGuid); + if (m_player.account) { + IOLoginData::removeVIPEntry(m_player.account->getID(), vipGuid); + } + + return true; +} + +bool PlayerVIP::add(uint32_t vipGuid, const std::string &vipName, VipStatus_t status) { + if (vipGuids.size() >= getMaxEntries() || vipGuids.size() == 200) { // max number of buddies is 200 in 9.53 + m_player.sendTextMessage(MESSAGE_FAILURE, "You cannot add more buddies."); + return false; + } + + if (!vipGuids.insert(vipGuid).second) { + m_player.sendTextMessage(MESSAGE_FAILURE, "This player is already in your list."); + return false; + } + + if (m_player.account) { + IOLoginData::addVIPEntry(m_player.account->getID(), vipGuid, "", 0, false); + } + + if (m_player.client) { + m_player.client->sendVIP(vipGuid, vipName, "", 0, false, status); + } + + return true; +} + +bool PlayerVIP::addInternal(uint32_t vipGuid) { + if (vipGuids.size() >= getMaxEntries() || vipGuids.size() == 200) { // max number of buddies is 200 in 9.53 + return false; + } + + return vipGuids.insert(vipGuid).second; +} + +bool PlayerVIP::edit(uint32_t vipGuid, const std::string &description, uint32_t icon, bool notify, std::vector<uint8_t> groupsId) const { + const auto it = vipGuids.find(vipGuid); + if (it == vipGuids.end()) { + return false; // player is not in VIP + } + + if (m_player.account) { + IOLoginData::editVIPEntry(m_player.account->getID(), vipGuid, description, icon, notify); + } + + IOLoginData::removeGuidVIPGroupEntry(m_player.account->getID(), vipGuid); + + for (const auto groupId : groupsId) { + const auto &group = getGroupByID(groupId); + if (group) { + group->vipGroupGuids.insert(vipGuid); + IOLoginData::addGuidVIPGroupEntry(group->id, m_player.account->getID(), vipGuid); + } + } + + return true; +} + +std::shared_ptr<VIPGroup> PlayerVIP::getGroupByID(uint8_t groupId) const { + auto it = std::find_if(vipGroups.begin(), vipGroups.end(), [groupId](const std::shared_ptr<VIPGroup> vipGroup) { + return vipGroup->id == groupId; + }); + + return it != vipGroups.end() ? *it : nullptr; +} + +std::shared_ptr<VIPGroup> PlayerVIP::getGroupByName(const std::string &name) const { + const auto groupName = name.c_str(); + auto it = std::find_if(vipGroups.begin(), vipGroups.end(), [groupName](const std::shared_ptr<VIPGroup> vipGroup) { + return strcmp(groupName, vipGroup->name.c_str()) == 0; + }); + + return it != vipGroups.end() ? *it : nullptr; +} + +void PlayerVIP::addGroupInternal(uint8_t groupId, const std::string &name, bool customizable) { + if (getGroupByName(name) != nullptr) { + g_logger().warn("{} - Group name already exists.", __FUNCTION__); + return; + } + + const auto freeId = getFreeId(); + if (freeId == 0) { + g_logger().warn("{} - No id available.", __FUNCTION__); + return; + } + + vipGroups.emplace_back(std::make_shared<VIPGroup>(freeId, name, customizable)); +} + +void PlayerVIP::removeGroup(uint8_t groupId) { + auto it = std::find_if(vipGroups.begin(), vipGroups.end(), [groupId](const std::shared_ptr<VIPGroup> vipGroup) { + return vipGroup->id == groupId; + }); + + if (it == vipGroups.end()) { + return; + } + + vipGroups.erase(it); + + if (m_player.account) { + IOLoginData::removeVIPGroupEntry(groupId, m_player.account->getID()); + } + + if (m_player.client) { + m_player.client->sendVIPGroups(); + } +} + +void PlayerVIP::addGroup(const std::string &name, bool customizable /*= true */) { + if (getGroupByName(name) != nullptr) { + m_player.sendCancelMessage("A group with this name already exists. Please choose another name."); + return; + } + + const auto freeId = getFreeId(); + if (freeId == 0) { + g_logger().warn("{} - No id available.", __FUNCTION__); + return; + } + + std::shared_ptr<VIPGroup> vipGroup = std::make_shared<VIPGroup>(freeId, name, customizable); + vipGroups.emplace_back(vipGroup); + + if (m_player.account) { + IOLoginData::addVIPGroupEntry(vipGroup->id, m_player.account->getID(), vipGroup->name, vipGroup->customizable); + } + + if (m_player.client) { + m_player.client->sendVIPGroups(); + } +} + +void PlayerVIP::editGroup(uint8_t groupId, const std::string &newName, bool customizable /*= true*/) { + if (getGroupByName(newName) != nullptr) { + m_player.sendCancelMessage("A group with this name already exists. Please choose another name."); + return; + } + + const auto &vipGroup = getGroupByID(groupId); + vipGroup->name = newName; + vipGroup->customizable = customizable; + + if (m_player.account) { + IOLoginData::editVIPGroupEntry(vipGroup->id, m_player.account->getID(), vipGroup->name, vipGroup->customizable); + } + + if (m_player.client) { + m_player.client->sendVIPGroups(); + } +} + +uint8_t PlayerVIP::getFreeId() const { + for (uint8_t i = firstID; i <= lastID; ++i) { + if (getGroupByID(i) == nullptr) { + return i; + } + } + + return 0; +} + +const std::vector<uint8_t> PlayerVIP::getGroupsIdGuidBelongs(uint32_t guid) { + std::vector<uint8_t> guidBelongs; + for (const auto &vipGroup : vipGroups) { + if (vipGroup->vipGroupGuids.contains(guid)) { + guidBelongs.emplace_back(vipGroup->id); + } + } + return guidBelongs; +} + +void PlayerVIP::addGuidToGroupInternal(uint8_t groupId, uint32_t guid) { + const auto &group = getGroupByID(groupId); + if (group) { + group->vipGroupGuids.insert(guid); + } +} diff --git a/src/creatures/players/vip/player_vip.hpp b/src/creatures/players/vip/player_vip.hpp new file mode 100644 index 00000000000..e9aeaf85326 --- /dev/null +++ b/src/creatures/players/vip/player_vip.hpp @@ -0,0 +1,74 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR <opentibiabr@outlook.com> + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "creatures/creatures_definitions.hpp" + +class Player; + +struct VIPGroup { + uint8_t id = 0; + std::string name = ""; + bool customizable = false; + phmap::flat_hash_set<uint32_t> vipGroupGuids; + + VIPGroup() = default; + VIPGroup(uint8_t id, const std::string &name, bool customizable) : + id(id), name(std::move(name)), customizable(customizable) { } +}; +class PlayerVIP { + +public: + explicit PlayerVIP(Player &player); + + static const uint8_t firstID; + static const uint8_t lastID; + + size_t getMaxEntries() const; + uint8_t getMaxGroupEntries() const; + + VipStatus_t getStatus() const { + return status; + } + void setStatus(VipStatus_t newStatus) { + status = newStatus; + } + + void notifyStatusChange(std::shared_ptr<Player> loginPlayer, VipStatus_t status, bool message = true) const; + bool remove(uint32_t vipGuid); + bool add(uint32_t vipGuid, const std::string &vipName, VipStatus_t status); + bool addInternal(uint32_t vipGuid); + bool edit(uint32_t vipGuid, const std::string &description, uint32_t icon, bool notify, std::vector<uint8_t> groupsId) const; + + // VIP Group + std::shared_ptr<VIPGroup> getGroupByID(uint8_t groupId) const; + std::shared_ptr<VIPGroup> getGroupByName(const std::string &name) const; + + void addGroupInternal(uint8_t groupId, const std::string &name, bool customizable); + void removeGroup(uint8_t groupId); + void addGroup(const std::string &name, bool customizable = true); + void editGroup(uint8_t groupId, const std::string &newName, bool customizable = true); + + void addGuidToGroupInternal(uint8_t groupId, uint32_t guid); + + uint8_t getFreeId() const; + const std::vector<uint8_t> getGroupsIdGuidBelongs(uint32_t guid); + + [[nodiscard]] const std::vector<std::shared_ptr<VIPGroup>> &getGroups() const { + return vipGroups; + } + +private: + Player &m_player; + + VipStatus_t status = VipStatus_t::Online; + std::vector<std::shared_ptr<VIPGroup>> vipGroups; + phmap::flat_hash_set<uint32_t> vipGuids; +}; diff --git a/src/game/game.cpp b/src/game/game.cpp index afc58e75198..621e3593816 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5773,21 +5773,21 @@ void Game::playerRequestAddVip(uint32_t playerId, const std::string &name) { } if (specialVip && !player->hasFlag(PlayerFlags_t::SpecialVIP)) { - player->sendTextMessage(MESSAGE_FAILURE, "You can not add this player->"); + player->sendTextMessage(MESSAGE_FAILURE, "You can not add this player"); return; } - player->addVIP(guid, formattedName, VIPSTATUS_OFFLINE); + player->vip()->add(guid, formattedName, VipStatus_t::Offline); } else { if (vipPlayer->hasFlag(PlayerFlags_t::SpecialVIP) && !player->hasFlag(PlayerFlags_t::SpecialVIP)) { - player->sendTextMessage(MESSAGE_FAILURE, "You can not add this player->"); + player->sendTextMessage(MESSAGE_FAILURE, "You can not add this player"); return; } if (!vipPlayer->isInGhostMode() || player->isAccessPlayer()) { - player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), vipPlayer->statusVipList); + player->vip()->add(vipPlayer->getGUID(), vipPlayer->getName(), vipPlayer->vip()->getStatus()); } else { - player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_OFFLINE); + player->vip()->add(vipPlayer->getGUID(), vipPlayer->getName(), VipStatus_t::Offline); } } } @@ -5798,16 +5798,16 @@ void Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid) { return; } - player->removeVIP(guid); + player->vip()->remove(guid); } -void Game::playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string &description, uint32_t icon, bool notify) { +void Game::playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string &description, uint32_t icon, bool notify, std::vector<uint8_t> vipGroupsId) { std::shared_ptr<Player> player = getPlayerByID(playerId); if (!player) { return; } - player->editVIP(guid, description, icon, notify); + player->vip()->edit(guid, description, icon, notify, vipGroupsId); } void Game::playerApplyImbuement(uint32_t playerId, uint16_t imbuementid, uint8_t slot, bool protectionCharm) { diff --git a/src/game/game.hpp b/src/game/game.hpp index 0e7b5fc147f..3a199254259 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -393,7 +393,7 @@ class Game { void playerRequestAddVip(uint32_t playerId, const std::string &name); void playerRequestRemoveVip(uint32_t playerId, uint32_t guid); - void playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); + void playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string &description, uint32_t icon, bool notify, std::vector<uint8_t> vipGroupsId); void playerApplyImbuement(uint32_t playerId, uint16_t imbuementid, uint8_t slot, bool protectionCharm); void playerClearImbuement(uint32_t playerid, uint8_t slot); void playerCloseImbuementWindow(uint32_t playerid); diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index bcbfc531468..6273f8fefbb 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -678,12 +678,34 @@ void IOLoginDataLoad::loadPlayerVip(std::shared_ptr<Player> player, DBResult_ptr return; } + uint32_t accountId = player->getAccountId(); + Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccountId(); - if ((result = db.storeQuery(query.str()))) { + std::string query = fmt::format("SELECT `player_id` FROM `account_viplist` WHERE `account_id` = {}", accountId); + if ((result = db.storeQuery(query))) { + do { + player->vip()->addInternal(result->getNumber<uint32_t>("player_id")); + } while (result->next()); + } + + query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId); + if ((result = db.storeQuery(query))) { + do { + player->vip()->addGroupInternal( + result->getNumber<uint8_t>("id"), + result->getString("name"), + result->getNumber<uint8_t>("customizable") == 0 ? false : true + ); + } while (result->next()); + } + + query = fmt::format("SELECT `player_id`, `vipgroup_id` FROM `account_vipgrouplist` WHERE `account_id` = {}", accountId); + if ((result = db.storeQuery(query))) { do { - player->addVIPInternal(result->getNumber<uint32_t>("player_id")); + player->vip()->addGuidToGroupInternal( + result->getNumber<uint8_t>("vipgroup_id"), + result->getNumber<uint32_t>("player_id") + ); } while (result->next()); } } diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 46426451ddc..122ade42c72 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -352,10 +352,9 @@ bool IOLoginData::hasBiddedOnHouse(uint32_t guid) { std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId) { std::forward_list<VIPEntry> entries; - std::ostringstream query; - query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = " << accountId; + std::string query = fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = {}", accountId); - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = Database::getInstance().storeQuery(query); if (result) { do { entries.emplace_front( @@ -371,27 +370,69 @@ std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId) { } void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify) { - Database &db = Database::getInstance(); - - std::ostringstream query; - query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')'; - if (!db.executeQuery(query.str())) { - g_logger().error("Failed to add VIP entry for account %u. QUERY: %s", accountId, query.str().c_str()); + std::string query = fmt::format("INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES ({}, {}, {}, {}, {})", accountId, guid, g_database().escapeString(description), icon, notify); + if (!g_database().executeQuery(query)) { + g_logger().error("Failed to add VIP entry for account {}. QUERY: {}", accountId, query.c_str()); } } void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify) { - Database &db = Database::getInstance(); - - std::ostringstream query; - query << "UPDATE `account_viplist` SET `description` = " << db.escapeString(description) << ", `icon` = " << icon << ", `notify` = " << notify << " WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; - if (!db.executeQuery(query.str())) { - g_logger().error("Failed to edit VIP entry for account %u. QUERY: %s", accountId, query.str().c_str()); + std::string query = fmt::format("UPDATE `account_viplist` SET `description` = {}, `icon` = {}, `notify` = {} WHERE `account_id` = {} AND `player_id` = {}", g_database().escapeString(description), icon, notify, accountId, guid); + if (!g_database().executeQuery(query)) { + g_logger().error("Failed to edit VIP entry for account {}. QUERY: {}", accountId, query.c_str()); } } void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) { - std::ostringstream query; - query << "DELETE FROM `account_viplist` WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; - Database::getInstance().executeQuery(query.str()); + std::string query = fmt::format("DELETE FROM `account_viplist` WHERE `account_id` = {} AND `player_id` = {}", accountId, guid); + g_database().executeQuery(query); +} + +std::forward_list<VIPGroupEntry> IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) { + std::forward_list<VIPGroupEntry> entries; + + std::string query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId); + + DBResult_ptr result = g_database().storeQuery(query); + if (result) { + do { + entries.emplace_front( + result->getNumber<uint8_t>("id"), + result->getString("name"), + result->getNumber<uint8_t>("customizable") == 0 ? false : true + ); + } while (result->next()); + } + return entries; +} + +void IOLoginData::addVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable) { + std::string query = fmt::format("INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES ({}, {}, {}, {})", groupId, accountId, g_database().escapeString(groupName), customizable); + if (!g_database().executeQuery(query)) { + g_logger().error("Failed to add VIP Group entry for account {} and group {}. QUERY: {}", accountId, groupId, query.c_str()); + } +} + +void IOLoginData::editVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable) { + std::string query = fmt::format("UPDATE `account_vipgroups` SET `name` = {}, `customizable` = {} WHERE `id` = {} AND `account_id` = {}", g_database().escapeString(groupName), customizable, groupId, accountId); + if (!g_database().executeQuery(query)) { + g_logger().error("Failed to update VIP Group entry for account {} and group {}. QUERY: {}", accountId, groupId, query.c_str()); + } +} + +void IOLoginData::removeVIPGroupEntry(uint8_t groupId, uint32_t accountId) { + std::string query = fmt::format("DELETE FROM `account_vipgroups` WHERE `id` = {} AND `account_id` = {}", groupId, accountId); + g_database().executeQuery(query); +} + +void IOLoginData::addGuidVIPGroupEntry(uint8_t groupId, uint32_t accountId, uint32_t guid) { + std::string query = fmt::format("INSERT INTO `account_vipgrouplist` (`account_id`, `player_id`, `vipgroup_id`) VALUES ({}, {}, {})", accountId, guid, groupId); + if (!g_database().executeQuery(query)) { + g_logger().error("Failed to add guid VIP Group entry for account {}, player {} and group {}. QUERY: {}", accountId, guid, groupId, query.c_str()); + } +} + +void IOLoginData::removeGuidVIPGroupEntry(uint32_t accountId, uint32_t guid) { + std::string query = fmt::format("DELETE FROM `account_vipgrouplist` WHERE `account_id` = {} AND `player_id` = {}", accountId, guid); + g_database().executeQuery(query); } diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp index b9fcc124ea0..be414739837 100644 --- a/src/io/iologindata.hpp +++ b/src/io/iologindata.hpp @@ -36,6 +36,13 @@ class IOLoginData { static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); static void removeVIPEntry(uint32_t accountId, uint32_t guid); + static std::forward_list<VIPGroupEntry> getVIPGroupEntries(uint32_t accountId, uint32_t guid); + static void addVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); + static void editVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); + static void removeVIPGroupEntry(uint8_t groupId, uint32_t accountId); + static void addGuidVIPGroupEntry(uint8_t groupId, uint32_t accountId, uint32_t guid); + static void removeGuidVIPGroupEntry(uint32_t accountId, uint32_t guid); + private: static bool savePlayerGuard(std::shared_ptr<Player> player); }; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 413f1bd3f47..5f14db6ea99 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3024,14 +3024,14 @@ int PlayerFunctions::luaPlayerSetGhostMode(lua_State* L) { if (player->isInGhostMode()) { for (const auto &it : g_game().getPlayers()) { if (!it.second->isAccessPlayer()) { - it.second->notifyStatusChange(player, VIPSTATUS_OFFLINE); + it.second->vip()->notifyStatusChange(player, VipStatus_t::Offline); } } IOLoginData::updateOnlineStatus(player->getGUID(), false); } else { for (const auto &it : g_game().getPlayers()) { if (!it.second->isAccessPlayer()) { - it.second->notifyStatusChange(player, player->statusVipList); + it.second->vip()->notifyStatusChange(player, player->vip()->getStatus()); } } IOLoginData::updateOnlineStatus(player->getGUID(), true); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index e16f015eb49..751da531273 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1276,6 +1276,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt case 0xDE: parseEditVip(msg); break; + case 0xDF: + parseVipGroupActions(msg); + break; case 0xE1: parseBestiarysendRaces(); break; @@ -1972,11 +1975,43 @@ void ProtocolGame::parseRemoveVip(NetworkMessage &msg) { } void ProtocolGame::parseEditVip(NetworkMessage &msg) { + std::vector<uint8_t> vipGroupsId; uint32_t guid = msg.get<uint32_t>(); const std::string description = msg.getString(); uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63 bool notify = msg.getByte() != 0; - g_game().playerRequestEditVip(player->getID(), guid, description, icon, notify); + uint8_t groupsAmount = msg.getByte(); + for (uint8_t i = 0; i < groupsAmount; ++i) { + uint8_t groupId = msg.getByte(); + vipGroupsId.emplace_back(groupId); + } + g_game().playerRequestEditVip(player->getID(), guid, description, icon, notify, vipGroupsId); +} + +void ProtocolGame::parseVipGroupActions(NetworkMessage &msg) { + uint8_t action = msg.getByte(); + + switch (action) { + case 0x01: { + const std::string groupName = msg.getString(); + player->vip()->addGroup(groupName); + break; + } + case 0x02: { + const uint8_t groupId = msg.getByte(); + const std::string newGroupName = msg.getString(); + player->vip()->editGroup(groupId, newGroupName); + break; + } + case 0x03: { + const uint8_t groupId = msg.getByte(); + player->vip()->removeGroup(groupId); + break; + } + default: { + break; + } + } } void ProtocolGame::parseRotateItem(NetworkMessage &msg) { @@ -6535,6 +6570,8 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos // player light level sendCreatureLight(creature); + sendVIPGroups(); + const std::forward_list<VIPEntry> &vipEntries = IOLoginData::getVIPEntries(player->getAccountId()); if (player->isAccessPlayer()) { @@ -6543,9 +6580,9 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos std::shared_ptr<Player> vipPlayer = g_game().getPlayerByGUID(entry.guid); if (!vipPlayer) { - vipStatus = VIPSTATUS_OFFLINE; + vipStatus = VipStatus_t::Offline; } else { - vipStatus = vipPlayer->statusVipList; + vipStatus = vipPlayer->vip()->getStatus(); } sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus); @@ -6556,9 +6593,9 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos std::shared_ptr<Player> vipPlayer = g_game().getPlayerByGUID(entry.guid); if (!vipPlayer || vipPlayer->isInGhostMode()) { - vipStatus = VIPSTATUS_OFFLINE; + vipStatus = VipStatus_t::Offline; } else { - vipStatus = vipPlayer->statusVipList; + vipStatus = vipPlayer->vip()->getStatus(); } sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus); @@ -7085,19 +7122,19 @@ void ProtocolGame::sendPodiumWindow(std::shared_ptr<Item> podium, const Position } void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { - if (oldProtocol && newStatus == VIPSTATUS_TRAINING) { + if (oldProtocol && newStatus == VipStatus_t::Training) { return; } NetworkMessage msg; msg.addByte(0xD3); msg.add<uint32_t>(guid); - msg.addByte(newStatus); + msg.addByte(enumToValue(newStatus)); writeToOutputBuffer(msg); } void ProtocolGame::sendVIP(uint32_t guid, const std::string &name, const std::string &description, uint32_t icon, bool notify, VipStatus_t status) { - if (oldProtocol && status == VIPSTATUS_TRAINING) { + if (oldProtocol && status == VipStatus_t::Training) { return; } @@ -7108,10 +7145,37 @@ void ProtocolGame::sendVIP(uint32_t guid, const std::string &name, const std::st msg.addString(description, "ProtocolGame::sendVIP - description"); msg.add<uint32_t>(std::min<uint32_t>(10, icon)); msg.addByte(notify ? 0x01 : 0x00); - msg.addByte(status); + msg.addByte(enumToValue(status)); + + const auto &vipGuidGroups = player->vip()->getGroupsIdGuidBelongs(guid); + if (!oldProtocol) { - msg.addByte(0x00); // vipGroups + msg.addByte(vipGuidGroups.size()); // vipGroups + for (const auto &vipGroupID : vipGuidGroups) { + msg.addByte(vipGroupID); + } + } + + writeToOutputBuffer(msg); +} + +void ProtocolGame::sendVIPGroups() { + if (oldProtocol) { + return; + } + + const auto &vipGroups = player->vip()->getGroups(); + + NetworkMessage msg; + msg.addByte(0xD4); + msg.addByte(vipGroups.size()); // vipGroups.size() + for (const auto &vipGroup : vipGroups) { + msg.addByte(vipGroup->id); + msg.addString(vipGroup->name, "ProtocolGame::sendVIP - vipGroup.name"); + msg.addByte(vipGroup->customizable ? 0x01 : 0x00); // 0x00 = not Customizable, 0x01 = Customizable } + msg.addByte(player->vip()->getMaxGroupEntries() - vipGroups.size()); // max vip groups + writeToOutputBuffer(msg); } diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 16105ee0b8b..0386093cfc4 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -18,6 +18,7 @@ class NetworkMessage; class Player; +class VIPGroup; class Game; class House; class Container; @@ -213,6 +214,7 @@ class ProtocolGame final : public Protocol { void parseAddVip(NetworkMessage &msg); void parseRemoveVip(NetworkMessage &msg); void parseEditVip(NetworkMessage &msg); + void parseVipGroupActions(NetworkMessage &msg); void parseRotateItem(NetworkMessage &msg); void parseConfigureShowOffSocket(NetworkMessage &msg); @@ -360,6 +362,7 @@ class ProtocolGame final : public Protocol { void sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus); void sendVIP(uint32_t guid, const std::string &name, const std::string &description, uint32_t icon, bool notify, VipStatus_t status); + void sendVIPGroups(); void sendPendingStateEntered(); void sendEnterWorld(); @@ -477,6 +480,7 @@ class ProtocolGame final : public Protocol { friend class Player; friend class PlayerWheel; + friend class PlayerVIP; std::unordered_set<uint32_t> knownCreatureSet; std::shared_ptr<Player> player = nullptr; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index b3e4eaffb42..4cb91d1cb94 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -47,6 +47,7 @@ <ClInclude Include="..\src\creatures\players\achievement\player_achievement.hpp" /> <ClInclude Include="..\src\creatures\players\cyclopedia\player_badge.hpp" /> <ClInclude Include="..\src\creatures\players\cyclopedia\player_title.hpp" /> + <ClInclude Include="..\src\creatures\players\vip\player_vip.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\player_wheel.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\wheel_definitions.hpp" /> <ClInclude Include="..\src\database\database.hpp" /> @@ -261,6 +262,7 @@ <ClCompile Include="..\src\creatures\players\achievement\player_achievement.cpp" /> <ClCompile Include="..\src\creatures\players\cyclopedia\player_badge.cpp" /> <ClCompile Include="..\src\creatures\players\cyclopedia\player_title.cpp" /> + <ClCompile Include="..\src\creatures\players\vip\player_vip.cpp" /> <ClCompile Include="..\src\creatures\players\wheel\player_wheel.cpp" /> <ClCompile Include="..\src\database\database.cpp" /> <ClCompile Include="..\src\database\databasemanager.cpp" /> From 5f88c6d635018debc3fbc6168782d504aeec19cb Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Fri, 24 May 2024 08:58:51 -0300 Subject: [PATCH 51/95] fix: onDeEquip properly handled at logout/death (#2625) --- src/creatures/players/player.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 553ca2e3b04..4109eb77472 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1862,6 +1862,13 @@ void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) Creature::onRemoveCreature(creature, isLogout); if (auto player = getPlayer(); player == creature) { + for (uint8_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + const auto item = inventory[slot]; + if (item) { + g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast<Slots_t>(slot)); + } + } + if (isLogout) { if (m_party) { m_party->leaveParty(player); From dabbeada591b89445c73d4e8a524fff640f8a7c9 Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Fri, 24 May 2024 22:14:53 -0300 Subject: [PATCH 52/95] fix: vip groups schema (#2651) Fixes the schema of vip groups. --- schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema.sql b/schema.sql index 7bbf6c86ac5..bf37d1c2f37 100644 --- a/schema.sql +++ b/schema.sql @@ -854,7 +854,7 @@ INSERT INTO `players` (6, 'GOD', 6, 1, 2, 0, 155, 155, 100, 113, 115, 95, 39, 75, 0, 60, 60, 0, 8, '', 410, 1, 10, 0, 10, 0, 10, 0, 10, 0); -- Create vip groups for GOD account -INSERT INTO `account_vipgroups` (`id`, `name`, `account_id`. `customizable`) VALUES +INSERT INTO `account_vipgroups` (`id`, `name`, `account_id`, `customizable`) VALUES (1, 'Friends', 1, 0), (2, 'Enemies', 1, 0), (3, 'Trading Partners', 1, 0); From b9a2336247cc246ef3ddc8baea719e108041ad83 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Tue, 28 May 2024 20:27:19 -0300 Subject: [PATCH 53/95] perf: change ThreadPool by ASIO to barak shoshany (#2660) Replaces the existing ThreadPool with ASIO to enhance server performance, implementing a strategy inspired by Barak Shoshany's methodology. The goal is to optimize asynchronous task execution and thread management for improved scalability and efficiency https://github.com/bshoshany/thread-pool --------- Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com> Co-authored-by: Eduardo Dantas <eduardo.dantas@hotmail.com.br> --- CMakeLists.txt | 4 +- src/database/databasetasks.cpp | 4 +- src/game/scheduling/dispatcher.cpp | 6 +- src/game/scheduling/dispatcher.hpp | 2 +- src/game/scheduling/save_manager.cpp | 4 +- src/lib/thread/README.md | 2 +- src/lib/thread/thread_pool.cpp | 77 ++++---------------------- src/lib/thread/thread_pool.hpp | 19 +++---- src/server/network/webhook/webhook.cpp | 2 +- vcpkg.json | 1 + 10 files changed, 30 insertions(+), 91 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c35a471f7..07701451aad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.22 FATAL_ERROR) # VCPKG # cmake -DCMAKE_TOOLCHAIN_FILE=/opt/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake .. # Needed libs is in file vcpkg.json -# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil +# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil bshoshany-thread-pool if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") @@ -124,4 +124,4 @@ add_subdirectory(src) if(BUILD_TESTS) add_subdirectory(tests) -endif() \ No newline at end of file +endif() diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp index 6d43992ac81..06cfda93fc0 100644 --- a/src/database/databasetasks.cpp +++ b/src/database/databasetasks.cpp @@ -23,7 +23,7 @@ DatabaseTasks &DatabaseTasks::getInstance() { } void DatabaseTasks::execute(const std::string &query, std::function<void(DBResult_ptr, bool)> callback /* nullptr */) { - threadPool.addLoad([this, query, callback]() { + threadPool.detach_task([this, query, callback]() { bool success = db.executeQuery(query); if (callback != nullptr) { g_dispatcher().addEvent([callback, success]() { callback(nullptr, success); }, "DatabaseTasks::execute"); @@ -32,7 +32,7 @@ void DatabaseTasks::execute(const std::string &query, std::function<void(DBResul } void DatabaseTasks::store(const std::string &query, std::function<void(DBResult_ptr, bool)> callback /* nullptr */) { - threadPool.addLoad([this, query, callback]() { + threadPool.detach_task([this, query, callback]() { DBResult_ptr result = db.storeQuery(query); if (callback != nullptr) { g_dispatcher().addEvent([callback, result]() { callback(result, true); }, "DatabaseTasks::store"); diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index ec999848a92..7cff69d66bf 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -23,10 +23,10 @@ Dispatcher &Dispatcher::getInstance() { void Dispatcher::init() { UPDATE_OTSYS_TIME(); - threadPool.addLoad([this] { + threadPool.detach_task([this] { std::unique_lock asyncLock(dummyMutex); - while (!threadPool.getIoContext().stopped()) { + while (!threadPool.isStopped()) { UPDATE_OTSYS_TIME(); executeEvents(); @@ -60,7 +60,7 @@ void Dispatcher::executeParallelEvents(std::vector<Task> &tasks, const uint8_t g std::atomic_bool isTasksCompleted = false; for (const auto &task : tasks) { - threadPool.addLoad([groupId, &task, &isTasksCompleted, &totalTaskSize] { + threadPool.detach_task([groupId, &task, &isTasksCompleted, &totalTaskSize] { dispacherContext.type = DispatcherType::AsyncEvent; dispacherContext.group = static_cast<TaskGroup>(groupId); dispacherContext.taskName = task.getContext(); diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index d9e26a0b5d5..a6cbc8dd6dd 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -84,7 +84,7 @@ class Dispatcher { public: explicit Dispatcher(ThreadPool &threadPool) : threadPool(threadPool) { - threads.reserve(threadPool.getNumberOfThreads() + 1); + threads.reserve(threadPool.get_thread_count() + 1); for (uint_fast16_t i = 0; i < threads.capacity(); ++i) { threads.emplace_back(std::make_unique<ThreadTask>()); } diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index 2c1eff6657c..fbad528598b 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -41,7 +41,7 @@ void SaveManager::scheduleAll() { return; } - threadPool.addLoad([this, scheduledAt]() { + threadPool.detach_task([this, scheduledAt]() { if (m_scheduledAt.load() != scheduledAt) { logger.warn("Skipping save for server because another save has been scheduled."); return; @@ -69,7 +69,7 @@ void SaveManager::schedulePlayer(std::weak_ptr<Player> playerPtr) { logger.debug("Scheduling player {} for saving.", playerToSave->getName()); auto scheduledAt = std::chrono::steady_clock::now(); m_playerMap[playerToSave->getGUID()] = scheduledAt; - threadPool.addLoad([this, playerPtr, scheduledAt]() { + threadPool.detach_task([this, playerPtr, scheduledAt]() { auto player = playerPtr.lock(); if (!player) { logger.debug("Skipping save for player because player is no longer online."); diff --git a/src/lib/thread/README.md b/src/lib/thread/README.md index ddb5cd6239c..52792d07b3d 100644 --- a/src/lib/thread/README.md +++ b/src/lib/thread/README.md @@ -20,7 +20,7 @@ int main() { ThreadPool &pool = inject<ThreadPool>(); // preferrably uses constructor injection or setter injection. // Post a task to the thread pool - pool.addLoad([]() { + pool.detach_task([]() { std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl; }); } diff --git a/src/lib/thread/thread_pool.cpp b/src/lib/thread/thread_pool.cpp index 3971d3a6a02..702f0c05504 100644 --- a/src/lib/thread/thread_pool.cpp +++ b/src/lib/thread/thread_pool.cpp @@ -14,84 +14,27 @@ #include "game/game.hpp" #include "utils/tools.hpp" +/** + * Regardless of how many cores your computer have, we want at least + * 4 threads because, even though they won't improve processing they + * will make processing non-blocking in some way and that would allow + * single core computers to process things concurrently, but not in parallel. + */ + #ifndef DEFAULT_NUMBER_OF_THREADS #define DEFAULT_NUMBER_OF_THREADS 4 #endif ThreadPool::ThreadPool(Logger &logger) : - logger(logger) { + logger(logger), BS::thread_pool(std::max<int>(getNumberOfCores(), DEFAULT_NUMBER_OF_THREADS)) { start(); } void ThreadPool::start() { - logger.info("Setting up thread pool"); - - /** - * Regardless of how many cores your computer have, we want at least - * 4 threads because, even though they won't improve processing they - * will make processing non-blocking in some way and that would allow - * single core computers to process things concurrently, but not in parallel. - */ - nThreads = std::max<uint16_t>(static_cast<int>(getNumberOfCores()), DEFAULT_NUMBER_OF_THREADS); - - for (std::size_t i = 0; i < nThreads; ++i) { - threads.emplace_back([this] { ioService.run(); }); - } - - logger.info("Running with {} threads.", threads.size()); + logger.info("Running with {} threads.", get_thread_count()); } void ThreadPool::shutdown() { - if (ioService.stopped()) { - return; - } - + stopped = true; logger.info("Shutting down thread pool..."); - - ioService.stop(); - - std::vector<std::future<void>> futures; - for (std::size_t i = 0; i < threads.size(); i++) { - logger.debug("Joining thread {}/{}.", i + 1, threads.size()); - - if (threads[i].joinable()) { - futures.emplace_back(std::async(std::launch::async, [&]() { - threads[i].join(); - })); - } - } - - std::future_status status = std::future_status::timeout; - auto timeout = std::chrono::seconds(5); - auto start = std::chrono::steady_clock::now(); - int tries = 0; - while (status == std::future_status::timeout && std::chrono::steady_clock::now() - start < timeout) { - tries++; - if (tries > 5) { - break; - } - for (auto &future : futures) { - status = future.wait_for(std::chrono::seconds(0)); - if (status != std::future_status::timeout) { - break; - } - } - } -} - -asio::io_context &ThreadPool::getIoContext() { - return ioService; -} - -void ThreadPool::addLoad(const std::function<void(void)> &load) { - asio::post(ioService, [this, load]() { - if (ioService.stopped()) { - if (g_game().getGameState() != GAME_STATE_SHUTDOWN) { - logger.error("Shutting down, cannot execute task."); - } - return; - } - - load(); - }); } diff --git a/src/lib/thread/thread_pool.hpp b/src/lib/thread/thread_pool.hpp index e36d8beca57..ea24d3486cb 100644 --- a/src/lib/thread/thread_pool.hpp +++ b/src/lib/thread/thread_pool.hpp @@ -9,8 +9,9 @@ #pragma once #include "lib/logging/logger.hpp" +#include "BS_thread_pool.hpp" -class ThreadPool { +class ThreadPool : public BS::thread_pool { public: explicit ThreadPool(Logger &logger); @@ -20,12 +21,6 @@ class ThreadPool { void start(); void shutdown(); - asio::io_context &getIoContext(); - void addLoad(const std::function<void(void)> &load); - - uint16_t getNumberOfThreads() const { - return nThreads; - } static int16_t getThreadId() { static std::atomic_int16_t lastId = -1; @@ -39,11 +34,11 @@ class ThreadPool { return id; }; + bool isStopped() const { + return stopped; + } + private: Logger &logger; - asio::io_context ioService; - std::vector<std::jthread> threads; - asio::io_context::work work { ioService }; - - uint16_t nThreads = 0; + bool stopped = false; }; diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index 57d4f607aac..f80ff4e59b3 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -38,7 +38,7 @@ Webhook &Webhook::getInstance() { } void Webhook::run() { - threadPool.addLoad([this] { sendWebhook(); }); + threadPool.detach_task([this] { sendWebhook(); }); g_dispatcher().scheduleEvent( g_configManager().getNumber(DISCORD_WEBHOOK_DELAY_MS, __FUNCTION__), [this] { run(); }, "Webhook::run" ); diff --git a/vcpkg.json b/vcpkg.json index dda054f3774..1f821379bad 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -17,6 +17,7 @@ "pugixml", "spdlog", "zlib", + "bshoshany-thread-pool", { "name": "libmariadb", "features": [ From 5636d549bb5bb7fe2ab960524f5588dc08d9c8bf Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Tue, 28 May 2024 21:11:34 -0300 Subject: [PATCH 54/95] fix: clean map in pz (#2661) --- src/map/map.cpp | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index d4d1c4147b1..173b4fbcc3d 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -695,29 +695,17 @@ uint32_t Map::clean() { ItemVector toRemove; toRemove.reserve(128); - for (const auto &mit : mapSectors) { - for (uint8_t z = 0; z < MAP_MAX_LAYERS; ++z) { - if (const auto &floor = mit.second.getFloor(z)) { - for (auto &tiles : floor->getTiles()) { - for (const auto &[tile, cachedTile] : tiles) { - if (!tile || tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - continue; - } - - TileItemVector* itemList = tile->getItemList(); - if (!itemList) { - continue; - } - - ++qntTiles; - - for (auto it = ItemVector::const_reverse_iterator(itemList->getEndDownItem()), end = ItemVector::const_reverse_iterator(itemList->getBeginDownItem()); it != end; ++it) { - const auto &item = *it; - if (item->isCleanable()) { - toRemove.push_back(item); - } - } - } + + for (const auto &tile : g_game().getTilesToClean()) { + if (!tile) { + continue; + } + + if (const auto items = tile->getItemList()) { + ++qntTiles; + for (const auto &item : *items) { + if (item->isCleanable()) { + toRemove.emplace_back(item); } } } From e0193bbd9b1794dff6744d04a933e9515b2879b9 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Tue, 28 May 2024 21:22:27 -0300 Subject: [PATCH 55/95] fix: crash on move creature (#2662) Fix #2657 --- src/map/map.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index 173b4fbcc3d..8b8cebeb539 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -320,13 +320,22 @@ bool Map::placeCreature(const Position ¢erPos, std::shared_ptr<Creature> cre } void Map::moveCreature(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, bool forceTeleport /* = false*/) { - auto oldTile = creature->getTile(); + if (!creature || !newTile) { + return; + } - Position oldPos = oldTile->getPosition(); - Position newPos = newTile->getPosition(); + const auto &oldTile = creature->getTile(); + + if (!oldTile) { + return; + } + + const auto &oldPos = oldTile->getPosition(); + const auto &newPos = newTile->getPosition(); const auto &fromZones = oldTile->getZones(); const auto &toZones = newTile->getZones(); + if (auto ret = g_game().beforeCreatureZoneChange(creature, fromZones, toZones); ret != RETURNVALUE_NOERROR) { return; } From 368f9d30dc629f9a78b55b78c00952015cb78a9f Mon Sep 17 00:00:00 2001 From: Slawomir Boczek <slawkens@gmail.com> Date: Wed, 29 May 2024 02:26:19 +0200 Subject: [PATCH 56/95] Add GitHub Workflow: MySQL Schema Checker (#2659) --- .github/workflows/mysql-schema-check.yml | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/mysql-schema-check.yml diff --git a/.github/workflows/mysql-schema-check.yml b/.github/workflows/mysql-schema-check.yml new file mode 100644 index 00000000000..b0291956edc --- /dev/null +++ b/.github/workflows/mysql-schema-check.yml @@ -0,0 +1,43 @@ +--- +name: MySQL Schema Check +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "schema.sql" + merge_group: + push: + paths: + - "schema.sql" + branches: + - main + +jobs: + mysql-schema-check: + runs-on: ubuntu-latest + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: canary + MYSQL_USER: canary + MYSQL_PASSWORD: canary + ports: + - 3306/tcp + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + strategy: + fail-fast: false + name: Check + steps: + - name: Checkout repository + uses: actions/checkout@main + - name: 📌 MySQL Start & init & show db + run: | + sudo /etc/init.d/mysql start + mysql -e 'CREATE DATABASE canary;' -uroot -proot + mysql -e "SHOW DATABASES" -uroot -proot + - name: Import Canary Schema + run: | + mysql -uroot -proot canary < schema.sql From 38c2ec359ab5eda18f873922cf49f11e6f8e73ba Mon Sep 17 00:00:00 2001 From: Karin <pessoacarlos98@gmail.com> Date: Tue, 28 May 2024 21:26:46 -0300 Subject: [PATCH 57/95] fix: wrong exit position fear feaster (#2654) --- .../scripts/actions/bosses_levers/the_fear_feaster.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua b/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua index af870accd41..41dfa1ffe33 100644 --- a/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua +++ b/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua @@ -15,7 +15,7 @@ local config = { from = Position(33705, 31463, 14), to = Position(33719, 31477, 14), }, - exit = Position(33609, 31499, 10), + exit = Position(33609, 31495, 10), } local lever = BossLever(config) From e3fefe824ce825cd96bb20d6a418ddfcdb81ff66 Mon Sep 17 00:00:00 2001 From: Karin <pessoacarlos98@gmail.com> Date: Wed, 29 May 2024 09:21:37 -0300 Subject: [PATCH 58/95] fix: adding the correct range (#2663) --- .../globalevents/quests/secret_library_preceptor_lazare.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua b/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua index 4da496fb659..ea05353ad09 100644 --- a/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua +++ b/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua @@ -1,7 +1,7 @@ local config = { monsterName = "Preceptor Lazare", bossPosition = Position(33374, 31338, 3), - range = 5, + range = 50, } local preceptorLazare = GlobalEvent("PreceptorLazareRespawn") From 6fed1764a943b6034cdbf0428f2bb87ae63efde8 Mon Sep 17 00:00:00 2001 From: Elson Costa <elsongabriel@hotmail.com> Date: Wed, 29 May 2024 17:08:03 -0300 Subject: [PATCH 59/95] fix: schema query (#2664) --- data-otservbr-global/migrations/45.lua | 10 ++--- schema.sql | 62 +++++++++++++------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua index c606f18522e..4ceb5f7e3fd 100644 --- a/data-otservbr-global/migrations/45.lua +++ b/data-otservbr-global/migrations/45.lua @@ -3,7 +3,7 @@ function onUpdateDatabase() db.query([[ CREATE TABLE IF NOT EXISTS `account_vipgroups` ( - `id` tinyint(3) UNSIGNED NOT NULL, + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is', `name` varchar(128) NOT NULL, `customizable` BOOLEAN NOT NULL DEFAULT '1', @@ -13,9 +13,9 @@ function onUpdateDatabase() db.query([[ CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0); - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0); - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Enemies', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Friends', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Trading Partner', 0); END; ]]) @@ -23,7 +23,7 @@ function onUpdateDatabase() CREATE TABLE IF NOT EXISTS `account_vipgrouplist` ( `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is', `player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry', - `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', + `vipgroup_id` int(11) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', INDEX `account_id` (`account_id`), INDEX `player_id` (`player_id`), INDEX `vipgroup_id` (`vipgroup_id`), diff --git a/schema.sql b/schema.sql index bf37d1c2f37..2fbc7dd649b 100644 --- a/schema.sql +++ b/schema.sql @@ -217,7 +217,7 @@ CREATE TABLE IF NOT EXISTS `account_viplist` ( -- Table structure `account_vipgroup` CREATE TABLE IF NOT EXISTS `account_vipgroups` ( - `id` tinyint(3) UNSIGNED NOT NULL, + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is', `name` varchar(128) NOT NULL, `customizable` BOOLEAN NOT NULL DEFAULT '1', @@ -229,9 +229,9 @@ CREATE TABLE IF NOT EXISTS `account_vipgroups` ( -- DELIMITER // CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0); - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0); - INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Enemies', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Friends', 0); + INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Trading Partner', 0); END // DELIMITER ; @@ -240,7 +240,7 @@ DELIMITER ; CREATE TABLE IF NOT EXISTS `account_vipgrouplist` ( `account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is', `player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry', - `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', + `vipgroup_id` int(11) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs', INDEX `account_id` (`account_id`), INDEX `player_id` (`player_id`), INDEX `vipgroup_id` (`vipgroup_id`), @@ -258,35 +258,36 @@ CREATE TABLE IF NOT EXISTS `boosted_boss` ( `boostname` TEXT, `date` varchar(250) NOT NULL DEFAULT '', `raceid` varchar(250) NOT NULL DEFAULT '', - `looktypeEx` int(11) NOT NULL DEFAULT "0", - `looktype` int(11) NOT NULL DEFAULT "136", - `lookfeet` int(11) NOT NULL DEFAULT "0", - `looklegs` int(11) NOT NULL DEFAULT "0", - `lookhead` int(11) NOT NULL DEFAULT "0", - `lookbody` int(11) NOT NULL DEFAULT "0", - `lookaddons` int(11) NOT NULL DEFAULT "0", - `lookmount` int(11) DEFAULT "0", + `looktypeEx` int(11) NOT NULL DEFAULT 0, + `looktype` int(11) NOT NULL DEFAULT 136, + `lookfeet` int(11) NOT NULL DEFAULT 0, + `looklegs` int(11) NOT NULL DEFAULT 0, + `lookhead` int(11) NOT NULL DEFAULT 0, + `lookbody` int(11) NOT NULL DEFAULT 0, + `lookaddons` int(11) NOT NULL DEFAULT 0, + `lookmount` int(11) DEFAULT 0, PRIMARY KEY (`date`) -) AS SELECT 0 AS date, "default" AS boostname, 0 AS raceid; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `boosted_boss` (`boostname`, `date`, `raceid`) VALUES ('default', 0, 0); -- Table structure `boosted_creature` CREATE TABLE IF NOT EXISTS `boosted_creature` ( `boostname` TEXT, `date` varchar(250) NOT NULL DEFAULT '', `raceid` varchar(250) NOT NULL DEFAULT '', - `looktype` int(11) NOT NULL DEFAULT "136", - `lookfeet` int(11) NOT NULL DEFAULT "0", - `looklegs` int(11) NOT NULL DEFAULT "0", - `lookhead` int(11) NOT NULL DEFAULT "0", - `lookbody` int(11) NOT NULL DEFAULT "0", - `lookaddons` int(11) NOT NULL DEFAULT "0", - `lookmount` int(11) DEFAULT "0", + `looktype` int(11) NOT NULL DEFAULT 136, + `lookfeet` int(11) NOT NULL DEFAULT 0, + `looklegs` int(11) NOT NULL DEFAULT 0, + `lookhead` int(11) NOT NULL DEFAULT 0, + `lookbody` int(11) NOT NULL DEFAULT 0, + `lookaddons` int(11) NOT NULL DEFAULT 0, + `lookmount` int(11) DEFAULT 0, PRIMARY KEY (`date`) -) AS SELECT 0 AS date, "default" AS boostname, 0 AS raceid; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- -------------------------------------------------------- +INSERT INTO `boosted_creature` (`boostname`, `date`, `raceid`) VALUES ('default', 0, 0); --- -- Tabble Structure `daily_reward_history` CREATE TABLE IF NOT EXISTS `daily_reward_history` ( `id` int(11) NOT NULL AUTO_INCREMENT, @@ -473,7 +474,6 @@ END DELIMITER ; -- Table structure `house_lists` - CREATE TABLE IF NOT EXISTS `house_lists` ( `house_id` int NOT NULL, `listid` int NOT NULL, @@ -485,7 +485,6 @@ CREATE TABLE IF NOT EXISTS `house_lists` ( CONSTRAINT `houses_list_house_fk` FOREIGN KEY (`house_id`) REFERENCES `houses` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; - -- Table structure `ip_bans` CREATE TABLE IF NOT EXISTS `ip_bans` ( `ip` int(11) NOT NULL, @@ -540,7 +539,6 @@ CREATE TABLE IF NOT EXISTS `market_offers` ( ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -- Table structure `players_online` CREATE TABLE IF NOT EXISTS `players_online` ( `player_id` int(11) NOT NULL, @@ -671,7 +669,6 @@ CREATE TABLE IF NOT EXISTS `player_wheeldata` ( PRIMARY KEY (`player_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -- Table structure `player_kills` CREATE TABLE IF NOT EXISTS `player_kills` ( `player_id` int(11) NOT NULL, @@ -830,6 +827,7 @@ CREATE TABLE IF NOT EXISTS `account_sessions` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- Table structure `kv_store` CREATE TABLE IF NOT EXISTS `kv_store` ( `key_name` varchar(191) NOT NULL, `timestamp` bigint NOT NULL, @@ -854,7 +852,7 @@ INSERT INTO `players` (6, 'GOD', 6, 1, 2, 0, 155, 155, 100, 113, 115, 95, 39, 75, 0, 60, 60, 0, 8, '', 410, 1, 10, 0, 10, 0, 10, 0, 10, 0); -- Create vip groups for GOD account -INSERT INTO `account_vipgroups` (`id`, `name`, `account_id`, `customizable`) VALUES -(1, 'Friends', 1, 0), -(2, 'Enemies', 1, 0), -(3, 'Trading Partners', 1, 0); +INSERT INTO `account_vipgroups` (`name`, `account_id`, `customizable`) VALUES +('Friends', 1, 0), +('Enemies', 1, 0), +('Trading Partners', 1, 0); From 83ebce55a86dcd31ebd89c301822f8af0daa2d98 Mon Sep 17 00:00:00 2001 From: Karin <pessoacarlos98@gmail.com> Date: Sat, 1 Jun 2024 01:44:39 -0300 Subject: [PATCH 60/95] fix: checking if the item is container in playerBuyItem (#2672) --- src/game/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 621e3593816..899a89549d9 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5165,7 +5165,7 @@ void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint return; } - if (inBackpacks) { + if (inBackpacks || it.isContainer()) { uint32_t maxContainer = static_cast<uint32_t>(g_configManager().getNumber(MAX_CONTAINER, __FUNCTION__)); auto backpack = player->getInventoryItem(CONST_SLOT_BACKPACK); auto mainBackpack = backpack ? backpack->getContainer() : nullptr; From 7e7a6109f521e87bcd82296ee570fff62d7a66c1 Mon Sep 17 00:00:00 2001 From: Marco <marcomoa@hotmail.com> Date: Sat, 1 Jun 2024 14:41:18 -0300 Subject: [PATCH 61/95] refactor: experience calculation efficiency and increase level cap (#2631) --- data/scripts/talkactions/god/add_skill.lua | 7 +------ src/creatures/players/player.hpp | 5 ++--- src/lua/functions/core/game/game_functions.cpp | 11 +++++++++++ src/lua/functions/core/game/game_functions.hpp | 2 ++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/data/scripts/talkactions/god/add_skill.lua b/data/scripts/talkactions/god/add_skill.lua index 20644582325..09d240ece4d 100644 --- a/data/scripts/talkactions/god/add_skill.lua +++ b/data/scripts/talkactions/god/add_skill.lua @@ -16,11 +16,6 @@ local function getSkillId(skillName) end end -local function getExpForLevel(level) - level = level - 1 - return ((50 * level * level * level) - (150 * level * level) + (400 * level)) / 3 -end - local addSkill = TalkAction("/addskill") function addSkill.onSay(player, words, param) @@ -54,7 +49,7 @@ function addSkill.onSay(player, words, param) local ch = split[2]:sub(1, 1) if ch == "l" or ch == "e" then targetLevel = target:getLevel() + count - targetExp = getExpForLevel(targetLevel) + targetExp = Game.getExperienceForLevel(targetLevel) addExp = targetExp - target:getExperience() target:addExperience(addExp, false) elseif ch == "m" then diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 58bebfb9f2f..c0761704db4 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -230,9 +230,8 @@ class Player final : public Creature, public Cylinder, public Bankable { void addList() override; void removePlayer(bool displayEffect, bool forced = true); - static uint64_t getExpForLevel(int32_t lv) { - lv--; - return ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL; + static uint64_t getExpForLevel(const uint32_t level) { + return (((level - 6ULL) * level + 17ULL) * level - 12ULL) / 6ULL * 100ULL; } uint16_t getStaminaMinutes() const { diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 0fdbc96f4d6..4858b3ab264 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -191,6 +191,17 @@ int GameFunctions::luaGameloadMapChunk(lua_State* L) { return 0; } +int GameFunctions::luaGameGetExperienceForLevel(lua_State* L) { + // Game.getExperienceForLevel(level) + const uint32_t level = getNumber<uint32_t>(L, 1); + if (level == 0) { + reportErrorFunc("Level must be greater than 0."); + } else { + lua_pushnumber(L, Player::getExpForLevel(level)); + } + return 1; +} + int GameFunctions::luaGameGetMonsterCount(lua_State* L) { // Game.getMonsterCount() lua_pushnumber(L, g_game().getMonstersOnline()); diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 7f3b642e97d..70e81061c9c 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -28,6 +28,7 @@ class GameFunctions final : LuaScriptInterface { registerMethod(L, "Game", "loadMap", GameFunctions::luaGameLoadMap); registerMethod(L, "Game", "loadMapChunk", GameFunctions::luaGameloadMapChunk); + registerMethod(L, "Game", "getExperienceForLevel", GameFunctions::luaGameGetExperienceForLevel); registerMethod(L, "Game", "getMonsterCount", GameFunctions::luaGameGetMonsterCount); registerMethod(L, "Game", "getPlayerCount", GameFunctions::luaGameGetPlayerCount); registerMethod(L, "Game", "getNpcCount", GameFunctions::luaGameGetNpcCount); @@ -103,6 +104,7 @@ class GameFunctions final : LuaScriptInterface { static int luaGameLoadMap(lua_State* L); static int luaGameloadMapChunk(lua_State* L); + static int luaGameGetExperienceForLevel(lua_State* L); static int luaGameGetMonsterCount(lua_State* L); static int luaGameGetPlayerCount(lua_State* L); static int luaGameGetNpcCount(lua_State* L); From 3b90a6970a7d91c8c58e1faf49a8af40f35a6835 Mon Sep 17 00:00:00 2001 From: Leandro <leandro.rufma@gmail.com> Date: Sat, 1 Jun 2024 15:32:11 -0300 Subject: [PATCH 62/95] feat: place monster spawn with default time if not set correctly (#2671) --- config.lua.dist | 1 + src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/creatures/monsters/spawns/spawn_monster.cpp | 10 +++++++++- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config.lua.dist b/config.lua.dist index 447a90647c2..9d1ed2fa681 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -511,6 +511,7 @@ bossDefaultTimeToFightAgain = 20 * 60 * 60 -- 20 hours bossDefaultTimeToDefeat = 20 * 60 -- 20 minutes -- Monsters +defaultRespawnTime = 60 deSpawnRange = 2 deSpawnRadius = 50 diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 22043f42525..0e53c97546e 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -50,6 +50,7 @@ enum ConfigKey_t : uint16_t { DATA_DIRECTORY, DAY_KILLS_TO_RED, DEATH_LOSE_PERCENT, + DEFAULT_RESPAWN_TIME, DEFAULT_DESPAWNRADIUS, DEFAULT_DESPAWNRANGE, DEFAULT_PRIORITY, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index e1345bbac4b..35d2cf3b1be 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -225,6 +225,7 @@ bool ConfigManager::load() { loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10); loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3); loadIntConfig(L, DEATH_LOSE_PERCENT, "deathLosePercent", -1); + loadIntConfig(L, DEFAULT_RESPAWN_TIME, "defaultRespawnTime", 60); loadIntConfig(L, DEFAULT_DESPAWNRADIUS, "deSpawnRadius", 50); loadIntConfig(L, DEFAULT_DESPAWNRANGE, "deSpawnRange", 2); loadIntConfig(L, DEPOTCHEST, "depotChest", 4); diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index ef5912c8052..968b90d8cae 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -94,7 +94,15 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { weight = pugi::cast<uint32_t>(weightAttribute.value()); } - spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, pugi::cast<uint32_t>(childMonsterNode.attribute("spawntime").value()) * 1000, weight); + uint32_t scheduleInterval = g_configManager().getNumber(DEFAULT_RESPAWN_TIME, __FUNCTION__); + + try { + scheduleInterval = pugi::cast<uint32_t>(childMonsterNode.attribute("spawntime").value()); + } catch (...) { + g_logger().warn("Failed to add schedule interval to monster: {}, interval: {}. Setting to default respawn time: {}", nameAttribute.value(), childMonsterNode.attribute("spawntime").value(), scheduleInterval); + } + + spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, scheduleInterval * 1000, weight); } } } From 8d74ea6db7c663fdc199fc39be3d137fa685544c Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Sun, 2 Jun 2024 00:02:44 -0300 Subject: [PATCH 63/95] fix: possible memory leak in connection (#2668) May never be called async_wait and therefore may leave the connection stuck in limbo. --- src/server/network/connection/connection.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 1c575ff00d0..3effd52f829 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -105,7 +105,7 @@ void Connection::accept(Protocol_ptr protocolPtr) { void Connection::acceptInternal(bool toggleParseHeader) { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + readTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); try { asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this(), toggleParseHeader](const std::error_code &error, std::size_t N) { @@ -147,7 +147,7 @@ void Connection::parseProxyIdentification(const std::error_code &error) { connectionState = CONNECTION_STATE_READINGS; try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + readTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); // Read the remainder of proxy identification asio::async_read(socket, asio::buffer(msg.getBuffer(), remainder), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseProxyIdentification(error); }); @@ -208,7 +208,7 @@ void Connection::parseHeader(const std::error_code &error) { try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + readTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); // Read packet content msg.setLength(size + HEADER_LENGTH); @@ -275,7 +275,7 @@ void Connection::parsePacket(const std::error_code &error) { try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + readTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); if (!skipReadingNextPacket) { // Wait to the next packet @@ -289,7 +289,7 @@ void Connection::parsePacket(const std::error_code &error) { void Connection::resumeWork() { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + readTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); try { asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseHeader(error); }); @@ -358,7 +358,7 @@ uint32_t Connection::getIP() { void Connection::internalSend(const OutputMessage_ptr &outputMessage) { writeTimer.expires_from_now(std::chrono::seconds(CONNECTION_WRITE_TIMEOUT)); - readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr<Connection>(self), error); }); + writeTimer.async_wait([self = std::weak_ptr<Connection>(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); }); try { asio::async_write(socket, asio::buffer(outputMessage->getOutputBuffer(), outputMessage->getLength()), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->onWriteOperation(error); }); From 02a7bc0c85c3a9cc0817b59950679a2b355f6c71 Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Mon, 3 Jun 2024 15:44:36 -0300 Subject: [PATCH 64/95] feat: equipments winter update 2023 (#2608) --- data/items/items.xml | 290 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index e7e73d9753b..9fdc89bbd8f 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -75820,5 +75820,295 @@ Granted by TibiaGoals.com"/> <attribute key="primarytype" value="valuables" /> <attribute key="weight" value="120" /> </item> + <item id="44619" name="stoic iks cuirass"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="15"/> + <attribute key="magiclevelpoints" value="4"/> + <attribute key="absorbpercentdeath" value="5"/> + <attribute key="weight" value="4400"/> + <attribute key="imbuementslot" value="2"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="wrath of nature" value="strong impact" /> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Druid;true, Elder Druid"/> + </attribute> + </item> + <item id="44620" name="stoic iks chestplate"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="16"/> + <attribute key="skillshield" value="3"/> + <attribute key="absorbpercentphysical" value="8"/> + <attribute key="weight" value="14200"/> + <attribute key="imbuementslot" value="2"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="intense wound cleansing" value="cooldown"> + <attribute key="value" value="120000" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Knight;true, Elite Knight"/> + </attribute> + </item> + <item id="44621" article="a" name="dauntless dragon scale armor"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="18"/> + <attribute key="skillaxe" value="3"/> + <attribute key="skillsword" value="3"/> + <attribute key="skillclub" value="3"/> + <attribute key="absorbpercentphysical" value="8"/> + <attribute key="weight" value="15200"/> + <attribute key="imbuementslot" value="2"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="front sweep" value="life leech"> + <attribute key="value" value="4" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="300"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Knight;true, Elite Knight"/> + </attribute> + </item> + <item id="44622" article="an" name="unerring dragon scale armor"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="17"/> + <attribute key="skilldist" value="3"/> + <attribute key="holymagiclevelpoints" value="1"/> + <attribute key="absorbpercentenergy" value="10"/> + <attribute key="absorbpercentearth" value="-5"/> + <attribute key="weight" value="13500"/> + <attribute key="imbuementslot" value="2"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="strong ethereal spear" value="life leech"> + <attribute key="value" value="4" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="300"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Paladin;true, Royal Paladin"/> + </attribute> + </item> + <item id="44623" article="an" name="arcane dragon robe"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="15"/> + <attribute key="magiclevelpoints" value="3"/> + <attribute key="energymagiclevelpoints" value="1"/> + <attribute key="absorbpercentfire" value="-6"/> + <attribute key="absorbpercentice" value="12"/> + <attribute key="weight" value="4300"/> + <attribute key="imbuementslot" value="1"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="energy wave" value="life leech"> + <attribute key="value" value="5" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="300"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Sorcerer;true, Master Sorcerer"/> + </attribute> + </item> + <item id="44624" article="an" name="mystical dragon robe"> + <attribute key="primarytype" value="armors"/> + <attribute key="armor" value="15"/> + <attribute key="magiclevelpoints" value="3"/> + <attribute key="earthmagiclevelpoints" value="1"/> + <attribute key="absorbpercentice" value="-6"/> + <attribute key="absorbpercentfire" value="12"/> + <attribute key="weight" value="3800"/> + <attribute key="imbuementslot" value="1"> + <attribute key="life leech" value="3"/> + <attribute key="elemental protection death" value="3"/> + <attribute key="elemental protection earth" value="3"/> + <attribute key="elemental protection fire" value="3"/> + <attribute key="elemental protection ice" value="3"/> + <attribute key="elemental protection energy" value="3"/> + <attribute key="elemental protection holy" value="3"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="terra wave" value="life leech"> + <attribute key="value" value="3" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="300"/> + <attribute key="slot" value="armor"/> + <attribute key="vocation" value="Druid;true, Elder Druid"/> + </attribute> + </item> + <item id="44636" name="stoic iks casque"> + <attribute key="primarytype" value="helmets"/> + <attribute key="armor" value="8"/> + <attribute key="skilldist" value="2"/> + <attribute key="absorbpercentphysical" value="3"/> + <attribute key="absorbpercentenergy" value="6"/> + <attribute key="weight" value="3500"/> + <attribute key="imbuementslot" value="2"> + <attribute key="mana leech" value="3"/> + <attribute key="skillboost axe" value="3"/> + <attribute key="skillboost sword" value="3"/> + <attribute key="skillboost club" value="3"/> + <attribute key="skillboost shielding" value="3"/> + <attribute key="skillboost distance" value="3"/> + <attribute key="skillboost magic level" value="2"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="divine missile" value="strong impact" /> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="feet"/> + <attribute key="vocation" value="Paladin;true, Royal Paladin"/> + </attribute> + </item> + <item id="44637" name="stoic iks headpiece"> + <attribute key="primarytype" value="helmets"/> + <attribute key="armor" value="8"/> + <attribute key="magiclevelpoints" value="2"/> + <attribute key="absorbpercentdeath" value="3"/> + <attribute key="weight" value="2100"/> + <attribute key="imbuementslot" value="2"> + <attribute key="mana leech" value="3"/> + <attribute key="skillboost axe" value="3"/> + <attribute key="skillboost sword" value="3"/> + <attribute key="skillboost club" value="3"/> + <attribute key="skillboost shielding" value="3"/> + <attribute key="skillboost distance" value="3"/> + <attribute key="skillboost magic level" value="2"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="wrath of nature" value="critical extra damage"> + <attribute key="value" value="5" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="head"/> + <attribute key="vocation" value="Druid;true, Elder Druid"/> + </attribute> + </item> + <item id="44642" name="stoic iks culet"> + <attribute key="primarytype" value="legs"/> + <attribute key="armor" value="9"/> + <attribute key="skillaxe" value="2"/> + <attribute key="skillsword" value="2"/> + <attribute key="skillclub" value="2"/> + <attribute key="absorbpercentphysical" value="4"/> + <attribute key="absorbpercentenergy" value="2"/> + <attribute key="weight" value="5100"/> + <attribute key="augments" value="1"> + <attribute key="intense wound cleansing" value="strong impact" /> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="legs"/> + <attribute key="vocation" value="Knight;true, Elite Knight"/> + </attribute> + </item> + <item id="44643" name="stoic iks faulds"> + <attribute key="primarytype" value="legs"/> + <attribute key="armor" value="8"/> + <attribute key="magiclevelpoints" value="2"/> + <attribute key="absorbpercentfire" value="6"/> + <attribute key="weight" value="3400"/> + <attribute key="augments" value="1"> + <attribute key="great energy beam" value="increased damage" /> + <attribute key="great death beam" value="increased damage" /> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="legs"/> + <attribute key="vocation" value="Sorcerer;true, Master Sorcerer"/> + </attribute> + </item> + <item id="44648" name="stoic iks boots"> + <attribute key="primarytype" value="boots"/> + <attribute key="speed" value="15"/> + <attribute key="armor" value="2"/> + <attribute key="skilldist" value="1"/> + <attribute key="weight" value="1500"/> + <attribute key="imbuementslot" value="1"> + <attribute key="increase speed" value="10"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="divine missile" value="critical extra damage"> + <attribute key="value" value="5" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="feet"/> + <attribute key="vocation" value="Paladin;true, Royal Paladin"/> + </attribute> + </item> + <item id="44649" name="stoic iks sandals"> + <attribute key="primarytype" value="boots"/> + <attribute key="armor" value="2"/> + <attribute key="magiclevelpoints" value="1"/> + <attribute key="absorbpercentice" value="6"/> + <attribute key="weight" value="900"/> + <attribute key="imbuementslot" value="1"> + <attribute key="increase speed" value="10"/> + </attribute> + <attribute key="augments" value="1"> + <attribute key="great energy beam" value="critical extra damage"> + <attribute key="value" value="5" /> + </attribute> + <attribute key="great death beam" value="critical extra damage"> + <attribute key="value" value="5" /> + </attribute> + </attribute> + <attribute key="script" value="moveevent"> + <attribute key="level" value="250"/> + <attribute key="slot" value="feet"/> + <attribute key="vocation" value="Sorcerer;true, Master Sorcerer"/> + </attribute> + </item> </items> From 2db5fee1f44a6d5cadca8db6a9ef9defdbad67f0 Mon Sep 17 00:00:00 2001 From: miah-sebastian <jeremiah_robertson@live.com> Date: Tue, 4 Jun 2024 18:42:36 -0400 Subject: [PATCH 65/95] fix: opentelemetry linker error (#2678) --- vcpkg.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vcpkg.json b/vcpkg.json index 1f821379bad..eed7e776ee0 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -18,6 +18,11 @@ "spdlog", "zlib", "bshoshany-thread-pool", + { + "name": "opentelemetry-cpp", + "default-features": true, + "features": ["otlp-http", "prometheus"] + }, { "name": "libmariadb", "features": [ From 4af13eefdcc71f900587f3f4e9afa8083a1fe1ae Mon Sep 17 00:00:00 2001 From: Marco <marcomoa@hotmail.com> Date: Tue, 4 Jun 2024 22:17:28 -0300 Subject: [PATCH 66/95] fix: small adjustments and corrections to the core (#2524) - Previously, players were not being removed from the room upon killing the boss, as the relevant script was confined to rlmap only. This update ensures its functionality across any datapack. - Hirelings are now accessible beyond rlmap environments. - Addressed an issue with the loading of VIP days library. - Relocated the large sea shell asset to the core. - Implemented VIP status check and associated actions. - Transferred weapons scripts to the core. --- .../scripts/actions/other/large_sea_shell.lua | 28 ----- .../scripts/weapons/scripted_weapons.lua | 101 ------------------ data-otservbr-global/lib/others/load.lua | 1 - .../scripts/creaturescripts/customs/vip.lua | 20 ---- .../creaturescripts/others/forge_kill.lua | 12 --- .../{bosslever.lua => boss_lever.lua} | 0 data/libs/functions/load.lua | 10 +- data/libs/systems/load.lua | 1 + .../libs/systems/vip.lua | 20 ++-- .../scripts/actions/items/hireling_lamp.lua | 7 +- .../actions/objects/large_seashell.lua | 30 ++++++ .../monster/boss_lever_death.lua | 7 ++ .../creaturescripts/monster}/forge_kill.lua | 0 .../player}/adventure_blessing_login.lua | 1 + data/scripts/creaturescripts/player/login.lua | 24 ++++- .../scripts/weapons/dawnport_weapons.lua | 0 .../scripts/weapons/scripts/burst_arrow.lua | 0 .../scripts/weapons/scripts/diamond_arrow.lua | 0 .../scripts/weapons/scripts/poison_arrow.lua | 0 .../scripts/weapons/scripts/viper_star.lua | 0 20 files changed, 75 insertions(+), 187 deletions(-) delete mode 100644 data-canary/scripts/actions/other/large_sea_shell.lua delete mode 100644 data-canary/scripts/weapons/scripted_weapons.lua delete mode 100644 data-otservbr-global/scripts/creaturescripts/customs/vip.lua delete mode 100644 data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua rename data/libs/functions/{bosslever.lua => boss_lever.lua} (100%) rename data-otservbr-global/lib/others/vip_system.lua => data/libs/systems/vip.lua (61%) rename data-otservbr-global/scripts/actions/other/hirelinglamp.lua => data/scripts/actions/items/hireling_lamp.lua (93%) create mode 100644 data/scripts/actions/objects/large_seashell.lua rename data-otservbr-global/scripts/globalevents/others/bosslever_death.lua => data/scripts/creaturescripts/monster/boss_lever_death.lua (99%) rename {data-canary/scripts/creaturescripts => data/scripts/creaturescripts/monster}/forge_kill.lua (100%) rename {data-otservbr-global/scripts/creaturescripts/others => data/scripts/creaturescripts/player}/adventure_blessing_login.lua (99%) rename data-otservbr-global/scripts/weapons/dawnport_weapon.lua => data/scripts/weapons/dawnport_weapons.lua (100%) rename {data-otservbr-global => data}/scripts/weapons/scripts/burst_arrow.lua (100%) rename {data-otservbr-global => data}/scripts/weapons/scripts/diamond_arrow.lua (100%) rename {data-otservbr-global => data}/scripts/weapons/scripts/poison_arrow.lua (100%) rename {data-otservbr-global => data}/scripts/weapons/scripts/viper_star.lua (100%) diff --git a/data-canary/scripts/actions/other/large_sea_shell.lua b/data-canary/scripts/actions/other/large_sea_shell.lua deleted file mode 100644 index b396bd70611..00000000000 --- a/data-canary/scripts/actions/other/large_sea_shell.lua +++ /dev/null @@ -1,28 +0,0 @@ -local largeSeaShell = Action() - -function largeSeaShell.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.DelayLargeSeaShell) <= os.time() then - local chance = math.random(100) - local msg = "" - if chance <= 16 then - doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE) - msg = "Ouch! You squeezed your fingers." - elseif chance > 16 and chance <= 64 then - Game.createItem(math.random(281, 282), 1, player:getPosition()) - msg = "You found a beautiful pearl." - else - msg = "Nothing is inside." - end - player:say(msg, TALKTYPE_MONSTER_SAY, false, player, item:getPosition()) - item:transform(198) - item:decay() - player:setStorageValue(Storage.DelayLargeSeaShell, os.time() + 20 * 60 * 60) - item:getPosition():sendMagicEffect(CONST_ME_BUBBLES) - else - player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition()) - end - return true -end - -largeSeaShell:id(197) -largeSeaShell:register() diff --git a/data-canary/scripts/weapons/scripted_weapons.lua b/data-canary/scripts/weapons/scripted_weapons.lua deleted file mode 100644 index 93e677d43cd..00000000000 --- a/data-canary/scripts/weapons/scripted_weapons.lua +++ /dev/null @@ -1,101 +0,0 @@ -local burstArea = createCombatArea({ - { 1, 1, 1 }, - { 1, 3, 1 }, - { 1, 1, 1 }, -}) - -local burstCombat = Combat() -burstCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) -burstCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONAREA) -burstCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_BURSTARROW) -burstCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) -burstCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0) -burstCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_BURST_ARROW_EFFECT) -burstCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW) -burstCombat:setArea(burstArea) - -local burstarrow = Weapon(WEAPON_AMMO) -burstarrow.onUseWeapon = function(player, variant) - if player:getSkull() == SKULL_BLACK then - return false - end - - return burstCombat:execute(player, variant) -end - -burstarrow:id(3449) -burstarrow:action("removecount") -burstarrow:register() - -local poisonCombat = Combat() -poisonCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) -poisonCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_POISONARROW) -poisonCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) -poisonCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0) - -local poisonarrow = Weapon(WEAPON_AMMO) -poisonarrow.onUseWeapon = function(player, variant) - if not poisonCombat:execute(player, variant) then - return false - end - - player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 3) - return true -end - -poisonarrow:id(3448) -poisonarrow:action("removecount") -poisonarrow:register() - -local viperCombat = Combat() -viperCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) -viperCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_GREENSTAR) -viperCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) -viperCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0) - -local viperstar = Weapon(WEAPON_DISTANCE) -viperstar.onUseWeapon = function(player, variant) - if not viperCombat:execute(player, variant) then - return false - end - - if math.random(1, 100) <= 90 then - return false - end - - player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 2) - return true -end - -viperstar:id(7366) -viperstar:breakChance(9) -viperstar:register() - -local diamondArea = createCombatArea({ - { 0, 1, 1, 1, 0 }, - { 1, 1, 1, 1, 1 }, - { 1, 1, 3, 1, 1 }, - { 1, 1, 1, 1, 1 }, - { 0, 1, 1, 1, 0 }, -}) - -local diamondCombat = Combat() -diamondCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) -diamondCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT) -diamondCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_DIAMONDARROW) -diamondCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_DIAMOND_ARROW_EFFECT) -diamondCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW) -diamondCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) -diamondCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0) -diamondCombat:setArea(diamondArea) - -local diamondarrow = Weapon(WEAPON_AMMO) -diamondarrow.onUseWeapon = function(player, variant) - return diamondCombat:execute(player, variant) -end - -diamondarrow:id(ITEM_OLD_DIAMOND_ARROW) -diamondarrow:action("removecount") -diamondarrow:level(150) -diamondarrow:wieldUnproperly(true) -diamondarrow:register() diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua index 1052efb7bd6..031c8fb2026 100644 --- a/data-otservbr-global/lib/others/load.lua +++ b/data-otservbr-global/lib/others/load.lua @@ -1,2 +1 @@ dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua") -dofile(DATA_DIRECTORY .. "/lib/others/vip_system.lua") diff --git a/data-otservbr-global/scripts/creaturescripts/customs/vip.lua b/data-otservbr-global/scripts/creaturescripts/customs/vip.lua deleted file mode 100644 index 0ae99c00f2d..00000000000 --- a/data-otservbr-global/scripts/creaturescripts/customs/vip.lua +++ /dev/null @@ -1,20 +0,0 @@ -local playerLogin = CreatureEvent("VipLogin") - -function playerLogin.onLogin(player) - if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then - local wasVip = player:kv():scoped("account"):get("vip-system") or false - if wasVip and not player:isVip() then - player:onRemoveVip() - end - if not wasVip and player:isVip() then - player:onAddVip(player:getVipDays()) - end - - if player:isVip() then - CheckPremiumAndPrint(player, MESSAGE_LOGIN) - end - end - return true -end - -playerLogin:register() diff --git a/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua b/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua deleted file mode 100644 index 3e454d84d14..00000000000 --- a/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua +++ /dev/null @@ -1,12 +0,0 @@ -local forgeKill = CreatureEvent("ForgeSystemMonster") - -function forgeKill.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) - local targetMonster = creature:getMonster() - if not targetMonster then - return true - end - - return ForgeMonster:onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) -end - -forgeKill:register() diff --git a/data/libs/functions/bosslever.lua b/data/libs/functions/boss_lever.lua similarity index 100% rename from data/libs/functions/bosslever.lua rename to data/libs/functions/boss_lever.lua diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua index c63276d9444..0fab2c3cb5e 100644 --- a/data/libs/functions/load.lua +++ b/data/libs/functions/load.lua @@ -1,14 +1,15 @@ -- Load core functions dofile(CORE_DIRECTORY .. "/libs/functions/bit.lua") dofile(CORE_DIRECTORY .. "/libs/functions/bitwise_flags.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/boss_lever.lua") dofile(CORE_DIRECTORY .. "/libs/functions/combat.lua") dofile(CORE_DIRECTORY .. "/libs/functions/constants.lua") dofile(CORE_DIRECTORY .. "/libs/functions/container.lua") dofile(CORE_DIRECTORY .. "/libs/functions/creature.lua") -dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua") -dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua") dofile(CORE_DIRECTORY .. "/libs/functions/fs.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua") dofile(CORE_DIRECTORY .. "/libs/functions/game.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua") dofile(CORE_DIRECTORY .. "/libs/functions/item.lua") dofile(CORE_DIRECTORY .. "/libs/functions/itemtype.lua") dofile(CORE_DIRECTORY .. "/libs/functions/lever.lua") @@ -20,14 +21,13 @@ dofile(CORE_DIRECTORY .. "/libs/functions/player.lua") dofile(CORE_DIRECTORY .. "/libs/functions/position.lua") dofile(CORE_DIRECTORY .. "/libs/functions/pronouns.lua") dofile(CORE_DIRECTORY .. "/libs/functions/quests.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua") dofile(CORE_DIRECTORY .. "/libs/functions/revscriptsys.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/set.lua") dofile(CORE_DIRECTORY .. "/libs/functions/spawn.lua") dofile(CORE_DIRECTORY .. "/libs/functions/spectators.lua") -dofile(CORE_DIRECTORY .. "/libs/functions/bosslever.lua") dofile(CORE_DIRECTORY .. "/libs/functions/string.lua") dofile(CORE_DIRECTORY .. "/libs/functions/tables.lua") dofile(CORE_DIRECTORY .. "/libs/functions/teleport.lua") dofile(CORE_DIRECTORY .. "/libs/functions/tile.lua") dofile(CORE_DIRECTORY .. "/libs/functions/vocation.lua") -dofile(CORE_DIRECTORY .. "/libs/functions/set.lua") -dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua") diff --git a/data/libs/systems/load.lua b/data/libs/systems/load.lua index 5c7073ad64e..281b8b1d466 100644 --- a/data/libs/systems/load.lua +++ b/data/libs/systems/load.lua @@ -9,4 +9,5 @@ dofile(CORE_DIRECTORY .. "/libs/systems/hazard.lua") dofile(CORE_DIRECTORY .. "/libs/systems/hireling.lua") dofile(CORE_DIRECTORY .. "/libs/systems/raids.lua") dofile(CORE_DIRECTORY .. "/libs/systems/reward_boss.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/vip.lua") dofile(CORE_DIRECTORY .. "/libs/systems/zones.lua") diff --git a/data-otservbr-global/lib/others/vip_system.lua b/data/libs/systems/vip.lua similarity index 61% rename from data-otservbr-global/lib/others/vip_system.lua rename to data/libs/systems/vip.lua index 5a393157c8c..9e76fd8ad80 100644 --- a/data-otservbr-global/lib/others/vip_system.lua +++ b/data/libs/systems/vip.lua @@ -1,16 +1,10 @@ local config = { - activationMessage = "You have received %s VIP days.", - activationMessageType = MESSAGE_EVENT_ADVANCE, - - expirationMessage = "Your VIP days ran out.", - expirationMessageType = MESSAGE_ADMINISTRATOR, - outfits = {}, mounts = {}, } function Player.onRemoveVip(self) - self:sendTextMessage(config.expirationMessageType, config.expirationMessage) + self:sendTextMessage(MESSAGE_ADMINISTRATOR, "Your VIP status has expired. All VIP benefits have been removed.") for _, outfit in ipairs(config.outfits) do self:removeOutfit(outfit) @@ -21,13 +15,14 @@ function Player.onRemoveVip(self) end local playerOutfit = self:getOutfit() - if table.contains(config.outfits, self:getOutfit().lookType) then + if table.contains(config.outfits, playerOutfit.lookType) then if self:getSex() == PLAYERSEX_FEMALE then playerOutfit.lookType = 136 else playerOutfit.lookType = 128 end playerOutfit.lookAddons = 0 + self:setOutfit(playerOutfit) end @@ -36,7 +31,7 @@ end function Player.onAddVip(self, days, silent) if not silent then - self:sendTextMessage(config.activationMessageType, string.format(config.activationMessage, days)) + self:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have been granted %s days of VIP status.", days)) end for _, outfit in ipairs(config.outfits) do @@ -52,16 +47,15 @@ end function CheckPremiumAndPrint(player, msgType) if player:getVipDays() == 0xFFFF then - player:sendTextMessage(msgType, "You have infinite amount of VIP days left.") + player:sendTextMessage(msgType, "You have an unlimited VIP status.") return true end local playerVipTime = player:getVipTime() if playerVipTime < os.time() then - local msg = "You do not have VIP on your account." - player:sendTextMessage(msgType, msg) + player:sendTextMessage(msgType, "Your VIP status is currently inactive.") return true end - player:sendTextMessage(msgType, string.format("You have %s of VIP time left.", getFormattedTimeRemaining(playerVipTime))) + player:sendTextMessage(msgType, string.format("You have %s of VIP time remaining.", getFormattedTimeRemaining(playerVipTime))) end diff --git a/data-otservbr-global/scripts/actions/other/hirelinglamp.lua b/data/scripts/actions/items/hireling_lamp.lua similarity index 93% rename from data-otservbr-global/scripts/actions/other/hirelinglamp.lua rename to data/scripts/actions/items/hireling_lamp.lua index 8117b5366bc..0ffed1fa744 100644 --- a/data-otservbr-global/scripts/actions/other/hirelinglamp.lua +++ b/data/scripts/actions/items/hireling_lamp.lua @@ -2,8 +2,9 @@ local hirelingLamp = Action() function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHotkey) local spawnPosition = player:getPosition() - local hireling_id = item:getCustomAttribute("Hireling") + local hirelingId = item:getCustomAttribute("Hireling") local house = spawnPosition and spawnPosition:getTile() and spawnPosition:getTile():getHouse() or nil + if not house then player:getPosition():sendMagicEffect(CONST_ME_POFF) player:sendTextMessage(MESSAGE_FAILURE, "You may use this only inside a house.") @@ -22,10 +23,10 @@ function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHo return false end - local hireling = getHirelingById(hireling_id) + local hireling = getHirelingById(hirelingId) if not hireling then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error creating the hireling and it has been deleted, please, contact server admin.") - logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hireling_id) + logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hirelingId) logger.error("Deleted the lamp") item:remove(1) return true diff --git a/data/scripts/actions/objects/large_seashell.lua b/data/scripts/actions/objects/large_seashell.lua new file mode 100644 index 00000000000..c6d767146ec --- /dev/null +++ b/data/scripts/actions/objects/large_seashell.lua @@ -0,0 +1,30 @@ +local largeSeashell = Action() + +function largeSeashell.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:hasExhaustion("delay-large-seashell") then + player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition()) + return true + end + + local chance = math.random(100) + local message = "Nothing is inside." + + if chance <= 16 then + doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE) + message = "Ouch! You squeezed your fingers." + elseif chance > 16 and chance <= 64 then + Game.createItem(math.random(281, 282), 1, player:getPosition()) + message = "You found a beautiful pearl." + player:addAchievementProgress("Shell Seeker", 100) + end + + player:setExhaustion("delay-large-seashell", 20 * 60 * 60) + player:say(message, TALKTYPE_MONSTER_SAY, false, player, item:getPosition()) + item:transform(198) + item:decay() + item:getPosition():sendMagicEffect(CONST_ME_BUBBLES) + return true +end + +largeSeashell:id(197) +largeSeashell:register() diff --git a/data-otservbr-global/scripts/globalevents/others/bosslever_death.lua b/data/scripts/creaturescripts/monster/boss_lever_death.lua similarity index 99% rename from data-otservbr-global/scripts/globalevents/others/bosslever_death.lua rename to data/scripts/creaturescripts/monster/boss_lever_death.lua index 9538fe15eb7..a2c58d1e43b 100644 --- a/data-otservbr-global/scripts/globalevents/others/bosslever_death.lua +++ b/data/scripts/creaturescripts/monster/boss_lever_death.lua @@ -1,22 +1,28 @@ local onBossDeath = CreatureEvent("BossLeverOnDeath") + function onBossDeath.onDeath(creature) if not creature then return true end + local name = creature:getName() local key = "boss." .. toKey(name) local zone = Zone(key) + if not zone then return true end + local bossLever = BossLever[name] if not bossLever then return true end + if bossLever.timeoutEvent then stopEvent(bossLever.timeoutEvent) bossLever.timeoutEvent = nil end + if bossLever.timeAfterKill > 0 then zone:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The " .. name .. " has been defeated. You have " .. bossLever.timeAfterKill .. " seconds to leave the room.") bossLever.timeoutEvent = addEvent(function(zn) @@ -26,4 +32,5 @@ function onBossDeath.onDeath(creature) end return true end + onBossDeath:register() diff --git a/data-canary/scripts/creaturescripts/forge_kill.lua b/data/scripts/creaturescripts/monster/forge_kill.lua similarity index 100% rename from data-canary/scripts/creaturescripts/forge_kill.lua rename to data/scripts/creaturescripts/monster/forge_kill.lua diff --git a/data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua similarity index 99% rename from data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua rename to data/scripts/creaturescripts/player/adventure_blessing_login.lua index df04052716f..8ebf88a72e9 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua +++ b/data/scripts/creaturescripts/player/adventure_blessing_login.lua @@ -1,6 +1,7 @@ dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") local adventurerBlessingLogin = CreatureEvent("AdventurerBlessingLogin") + function adventurerBlessingLogin.onLogin(player) return Blessings.doAdventurerBlessing(player) end diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua index 34d139a047d..fbe4f9e63b6 100644 --- a/data/scripts/creaturescripts/player/login.lua +++ b/data/scripts/creaturescripts/player/login.lua @@ -49,10 +49,8 @@ function playerLoginGlobal.onLogin(player) end -- Boosted - player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted creature: " .. Game.getBoostedCreature() .. " \ - Boosted creatures yield more experience points, carry more loot than usual and respawn at a faster rate.") - player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted boss: " .. Game.getBoostedBoss() .. " \ - Boosted bosses contain more loot and count more kills for your Bosstiary.") + player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted creature: %s.\nBoosted creatures yield more experience points, carry more loot than usual, and respawn at a faster rate.", Game.getBoostedCreature())) + player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted boss: %s.\nBoosted bosses contain more loot and count more kills for your Bosstiary.", Game.getBoostedBoss())) -- Rewards local rewards = #player:getRewardList() @@ -118,6 +116,24 @@ function playerLoginGlobal.onLogin(player) player:setStaminaXpBoost(player:getFinalBonusStamina() * 100) player:getFinalLowLevelBonus() + -- Updates the player's VIP status and executes corresponding actions if applicable. + if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then + local isVipNow = player:isVip() + local wasVip = player:kv():scoped("account"):get("vip-system") or false + + if wasVip ~= isVipNow then + if wasVip then + player:onRemoveVip() + else + player:onAddVip(player:getVipDays()) + end + end + + if isVipNow then + CheckPremiumAndPrint(player, MESSAGE_LOGIN) + end + end + -- Set Ghost Mode if player:getGroup():getId() >= GROUP_TYPE_GAMEMASTER then player:setGhostMode(true) diff --git a/data-otservbr-global/scripts/weapons/dawnport_weapon.lua b/data/scripts/weapons/dawnport_weapons.lua similarity index 100% rename from data-otservbr-global/scripts/weapons/dawnport_weapon.lua rename to data/scripts/weapons/dawnport_weapons.lua diff --git a/data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua b/data/scripts/weapons/scripts/burst_arrow.lua similarity index 100% rename from data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua rename to data/scripts/weapons/scripts/burst_arrow.lua diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data/scripts/weapons/scripts/diamond_arrow.lua similarity index 100% rename from data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua rename to data/scripts/weapons/scripts/diamond_arrow.lua diff --git a/data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua b/data/scripts/weapons/scripts/poison_arrow.lua similarity index 100% rename from data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua rename to data/scripts/weapons/scripts/poison_arrow.lua diff --git a/data-otservbr-global/scripts/weapons/scripts/viper_star.lua b/data/scripts/weapons/scripts/viper_star.lua similarity index 100% rename from data-otservbr-global/scripts/weapons/scripts/viper_star.lua rename to data/scripts/weapons/scripts/viper_star.lua From 8af607bdf87ab8004ed01e9557175f2d33116eec Mon Sep 17 00:00:00 2001 From: Paulo Henrique Lisboa <phlinho10@gmail.com> Date: Tue, 11 Jun 2024 10:05:04 -0300 Subject: [PATCH 67/95] fix: docker ubuntu package and start.sh permission (#2681) --- docker/Dockerfile.dev | 6 +++--- docker/data/start.sh | 4 ++-- docker/docker-compose.yml | 2 -- start.sh | 0 4 files changed, 5 insertions(+), 7 deletions(-) mode change 100644 => 100755 start.sh diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 161a80a2e21..4cd63334d5e 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,10 +1,10 @@ # Stage 1: Download all dependencies -FROM ubuntu:23.04 AS dependencies +FROM ubuntu:24.04 AS dependencies RUN --mount=type=cache,target=/var/cache/apt \ apt-get update && apt-get install -y --no-install-recommends cmake git \ unzip build-essential ca-certificates curl zip unzip tar \ - pkg-config ninja-build autoconf automake libtool \ + pkg-config ninja-build autoconf automake libtool glibc-tools \ python3 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -32,7 +32,7 @@ COPY recompile.sh CMakeLists.txt CMakePresets.json vcpkg.json ./ RUN ./recompile.sh "/opt" # Stage 3: execute -FROM ubuntu:23.04 AS prod +FROM ubuntu:24.04 AS prod COPY --from=build /srv/build/build/linux-release/bin/canary /bin/canary WORKDIR /srv/canary ENTRYPOINT ["/srv/canary/start.sh", "canary"] diff --git a/docker/data/start.sh b/docker/data/start.sh index db688b488d0..c7d10fa0fdb 100755 --- a/docker/data/start.sh +++ b/docker/data/start.sh @@ -10,8 +10,8 @@ OT_SERVER_LOGIN_PORT="${OT_SERVER_LOGIN_PORT:-7171}" OT_SERVER_GAME_PORT="${OT_SERVER_GAME_PORT:-7172}" OT_SERVER_STATUS_PORT="${OT_SERVER_STATUS_PORT:-7171}" OT_SERVER_TEST_ACCOUNTS="${OT_SERVER_TEST_ACCOUNTS:-false}" -OT_SERVER_DATA="${OT_SERVER_DATA:-data-canary}" -OT_SERVER_MAP="${OT_SERVER_MAP:-https://github.com/opentibiabr/otservbr-global/releases/download/v1.5.0/otservbr.otbm}" +OT_SERVER_DATA="${OT_SERVER_DATA:-data-otservbr-global}" +OT_SERVER_MAP="${OT_SERVER_MAP:-https://github.com/opentibiabr/canary/releases/download/v3.1.0/otservbr.otbm}" echo "" echo "===== Print Variables =====" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 46a2fb619b2..88fd938e500 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,3 @@ ---- -version: '3.8' name: otbr services: database: diff --git a/start.sh b/start.sh old mode 100644 new mode 100755 From ede4b035bcb3c664bd42c757f1d4466b8d7b5c43 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:05:20 -0300 Subject: [PATCH 68/95] feat: rebalancing monsters (#2683) --- data-canary/monster/demons/fury.lua | 2 +- data-canary/monster/demons/juggernaut.lua | 4 ++-- .../monster/aquatics/quara_constrictor.lua | 2 +- .../monster/aquatics/quara_hydromancer.lua | 2 +- .../monster/aquatics/quara_mantassin.lua | 2 +- .../monster/aquatics/quara_pincher.lua | 2 +- .../monster/aquatics/quara_predator.lua | 2 +- data-otservbr-global/monster/bosses/splasher.lua | 2 +- .../monster/constructs/clay_guardian.lua | 2 +- .../monster/constructs/infected_weeper.lua | 2 +- .../monster/constructs/lava_golem.lua | 3 +-- .../monster/constructs/magma_crawler.lua | 8 ++++---- .../monster/constructs/orewalker.lua | 3 +-- .../monster/constructs/stone_devourer.lua | 2 +- data-otservbr-global/monster/constructs/weeper.lua | 6 +++--- .../monster/demons/hellfire_fighter.lua | 2 +- .../monster/elementals/cliff_strider.lua | 6 +++--- .../monster/elementals/earth_elemental.lua | 4 ++-- .../monster/elementals/high_voltage_elemental.lua | 2 +- .../monster/elementals/ironblight.lua | 2 +- .../monster/elementals/massive_earth_elemental.lua | 4 ++-- .../monster/elementals/sulphur_spouter.lua | 2 +- .../monster/humanoids/lost_basher.lua | 6 +++--- .../monster/humanoids/lost_berserker.lua | 12 +++++------- .../monster/humanoids/lost_husher.lua | 9 ++++----- .../monster/humanoids/lost_thrower.lua | 6 +++--- data-otservbr-global/monster/magicals/armadile.lua | 13 +++++-------- .../monster/magicals/choking_fear.lua | 2 +- data-otservbr-global/monster/magicals/phantasm.lua | 2 +- .../monster/magicals/retching_horror.lua | 2 +- .../monster/mammals/mutated_bat.lua | 2 +- .../monster/plants/hideous_fungus.lua | 13 ++++++------- .../monster/plants/humongous_fungus.lua | 7 +++---- .../quests/bigfoots_burden/bosses/abyssador.lua | 2 +- .../quests/bigfoots_burden/bosses/gnomevil.lua | 2 +- .../quests/bigfoots_burden/humorless_fungus.lua | 9 +++------ .../bosses/the_count_of_the_core.lua | 2 +- .../bosses/the_duke_of_the_depths.lua | 2 +- .../bosses/ferumbras_mortal_shell.lua | 2 +- .../quests/ferumbras_ascendant/bosses/mazoran.lua | 2 +- .../quests/ferumbras_ascendant/bosses/plagirath.lua | 2 +- .../quests/ferumbras_ascendant/bosses/ragiaz.lua | 2 +- .../quests/ferumbras_ascendant/bosses/razzagorn.lua | 2 +- .../quests/ferumbras_ascendant/bosses/shulgrax.lua | 2 +- .../quests/ferumbras_ascendant/bosses/tarbaz.lua | 2 +- .../quests/ferumbras_ascendant/bosses/zamulosh.lua | 2 +- .../monster/quests/in_service_of_yalahar/inky.lua | 2 +- .../quests/in_service_of_yalahar/sharptooth.lua | 2 +- .../monster/reptiles/seacrest_serpent.lua | 2 +- .../monster/reptiles/young_sea_serpent.lua | 2 +- .../monster/undeads/betrayed_wraith.lua | 2 +- .../monster/undeads/blightwalker.lua | 7 +++---- .../monster/undeads/crypt_warrior.lua | 2 +- .../monster/undeads/falcon_knight.lua | 2 +- .../monster/undeads/falcon_paladin.lua | 2 +- .../monster/undeads/hand_of_cursed_fate.lua | 4 ++-- .../monster/undeads/skeleton_elite_warrior.lua | 2 +- .../monster/undeads/undead_dragon.lua | 2 +- .../monster/undeads/undead_elite_gladiator.lua | 2 +- .../monster/vermins/cave_devourer.lua | 4 ++-- .../monster/vermins/chasm_spawn.lua | 4 ++-- data-otservbr-global/monster/vermins/deepworm.lua | 2 +- data-otservbr-global/monster/vermins/diremaw.lua | 2 +- data-otservbr-global/monster/vermins/drillworm.lua | 2 +- .../monster/vermins/tunnel_tyrant.lua | 4 ++-- ...d_,master_oberon.lua => grand_master_oberon.lua} | 0 66 files changed, 105 insertions(+), 119 deletions(-) rename data-otservbr-global/scripts/actions/bosses_levers/{grand_,master_oberon.lua => grand_master_oberon.lua} (100%) diff --git a/data-canary/monster/demons/fury.lua b/data-canary/monster/demons/fury.lua index 3835050ce67..a01553afeed 100644 --- a/data-canary/monster/demons/fury.lua +++ b/data-canary/monster/demons/fury.lua @@ -125,7 +125,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 30 }, + { type = COMBAT_ICEDAMAGE, percent = 5 }, { type = COMBAT_HOLYDAMAGE, percent = 30 }, { type = COMBAT_DEATHDAMAGE, percent = -10 }, } diff --git a/data-canary/monster/demons/juggernaut.lua b/data-canary/monster/demons/juggernaut.lua index 7b3e3d1795b..704ce847f13 100644 --- a/data-canary/monster/demons/juggernaut.lua +++ b/data-canary/monster/demons/juggernaut.lua @@ -27,8 +27,8 @@ monster.Bestiary = { The Blood Halls, The Vats, The Hive, The Shadow Nexus, a room deep in Formorgar Mines, Roshamuul Prison, Oramond Dungeon, Grounds of Destruction.", } -monster.health = 20000 -monster.maxHealth = 20000 +monster.health = 18000 +monster.maxHealth = 18000 monster.race = "blood" monster.corpse = 6335 monster.speed = 170 diff --git a/data-otservbr-global/monster/aquatics/quara_constrictor.lua b/data-otservbr-global/monster/aquatics/quara_constrictor.lua index 02b4877620e..c04460f1f50 100644 --- a/data-otservbr-global/monster/aquatics/quara_constrictor.lua +++ b/data-otservbr-global/monster/aquatics/quara_constrictor.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Constrictor") local monster = {} monster.description = "a quara constrictor" -monster.experience = 250 +monster.experience = 380 monster.outfit = { lookType = 46, lookHead = 0, diff --git a/data-otservbr-global/monster/aquatics/quara_hydromancer.lua b/data-otservbr-global/monster/aquatics/quara_hydromancer.lua index 4129dcfe81c..07c3ffbfda3 100644 --- a/data-otservbr-global/monster/aquatics/quara_hydromancer.lua +++ b/data-otservbr-global/monster/aquatics/quara_hydromancer.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Hydromancer") local monster = {} monster.description = "a quara hydromancer" -monster.experience = 800 +monster.experience = 950 monster.outfit = { lookType = 47, lookHead = 0, diff --git a/data-otservbr-global/monster/aquatics/quara_mantassin.lua b/data-otservbr-global/monster/aquatics/quara_mantassin.lua index 53594881d50..3e857cab7f2 100644 --- a/data-otservbr-global/monster/aquatics/quara_mantassin.lua +++ b/data-otservbr-global/monster/aquatics/quara_mantassin.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Mantassin") local monster = {} monster.description = "a quara mantassin" -monster.experience = 400 +monster.experience = 600 monster.outfit = { lookType = 72, lookHead = 0, diff --git a/data-otservbr-global/monster/aquatics/quara_pincher.lua b/data-otservbr-global/monster/aquatics/quara_pincher.lua index 200aed87628..09fea436314 100644 --- a/data-otservbr-global/monster/aquatics/quara_pincher.lua +++ b/data-otservbr-global/monster/aquatics/quara_pincher.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Pincher") local monster = {} monster.description = "a quara pincher" -monster.experience = 1200 +monster.experience = 1500 monster.outfit = { lookType = 77, lookHead = 0, diff --git a/data-otservbr-global/monster/aquatics/quara_predator.lua b/data-otservbr-global/monster/aquatics/quara_predator.lua index e513aa5712c..b12719e26f8 100644 --- a/data-otservbr-global/monster/aquatics/quara_predator.lua +++ b/data-otservbr-global/monster/aquatics/quara_predator.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Predator") local monster = {} monster.description = "a quara predator" -monster.experience = 1600 +monster.experience = 1850 monster.outfit = { lookType = 20, lookHead = 0, diff --git a/data-otservbr-global/monster/bosses/splasher.lua b/data-otservbr-global/monster/bosses/splasher.lua index 60b89710c4f..426d3be3c26 100644 --- a/data-otservbr-global/monster/bosses/splasher.lua +++ b/data-otservbr-global/monster/bosses/splasher.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Splasher") local monster = {} monster.description = "Splasher" -monster.experience = 500 +monster.experience = 1500 monster.outfit = { lookType = 47, lookHead = 0, diff --git a/data-otservbr-global/monster/constructs/clay_guardian.lua b/data-otservbr-global/monster/constructs/clay_guardian.lua index 804d671bdf9..fb28abe50c3 100644 --- a/data-otservbr-global/monster/constructs/clay_guardian.lua +++ b/data-otservbr-global/monster/constructs/clay_guardian.lua @@ -107,7 +107,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 35 }, + { type = COMBAT_ICEDAMAGE, percent = 20 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, { type = COMBAT_DEATHDAMAGE, percent = 40 }, } diff --git a/data-otservbr-global/monster/constructs/infected_weeper.lua b/data-otservbr-global/monster/constructs/infected_weeper.lua index e302618f3eb..2b7d97f0479 100644 --- a/data-otservbr-global/monster/constructs/infected_weeper.lua +++ b/data-otservbr-global/monster/constructs/infected_weeper.lua @@ -105,7 +105,7 @@ monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 50 }, { type = COMBAT_ENERGYDAMAGE, percent = 25 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, - { type = COMBAT_FIREDAMAGE, percent = 100 }, + { type = COMBAT_FIREDAMAGE, percent = -100 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/constructs/lava_golem.lua b/data-otservbr-global/monster/constructs/lava_golem.lua index aed9ad864c0..5c3f695cf32 100644 --- a/data-otservbr-global/monster/constructs/lava_golem.lua +++ b/data-otservbr-global/monster/constructs/lava_golem.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lava Golem") local monster = {} monster.description = "a lava golem" -monster.experience = 6200 +monster.experience = 7900 monster.outfit = { lookType = 491, lookHead = 0, @@ -109,7 +109,6 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -400 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -350, maxDamage = -700, length = 8, spread = 0, effect = CONST_ME_FIREATTACK, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -600, maxDamage = -1300, length = 8, spread = 3, effect = CONST_ME_MORTAREA, target = false }, { name = "lava golem soulfire", interval = 2000, chance = 15, target = false }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -220, maxDamage = -350, radius = 4, effect = CONST_ME_FIREAREA, target = true }, { name = "speed", interval = 2000, chance = 10, speedChange = -800, length = 5, spread = 3, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 }, diff --git a/data-otservbr-global/monster/constructs/magma_crawler.lua b/data-otservbr-global/monster/constructs/magma_crawler.lua index 39c58ab8838..bf5f7ac4feb 100644 --- a/data-otservbr-global/monster/constructs/magma_crawler.lua +++ b/data-otservbr-global/monster/constructs/magma_crawler.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Magma Crawler") local monster = {} monster.description = "a magma crawler" -monster.experience = 2700 +monster.experience = 3900 monster.outfit = { lookType = 492, lookHead = 0, @@ -35,7 +35,7 @@ monster.manaCost = 0 monster.changeTarget = { interval = 4000, - chance = 10, + chance = 5, } monster.strategiesTarget = { @@ -118,7 +118,7 @@ monster.defenses = { defense = 45, armor = 84, mitigation = 2.51, - { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE }, + { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE }, } monster.elements = { @@ -129,7 +129,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 10 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, { type = COMBAT_DEATHDAMAGE, percent = 25 }, } diff --git a/data-otservbr-global/monster/constructs/orewalker.lua b/data-otservbr-global/monster/constructs/orewalker.lua index 767b8a049ae..3e1dc4deedd 100644 --- a/data-otservbr-global/monster/constructs/orewalker.lua +++ b/data-otservbr-global/monster/constructs/orewalker.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Orewalker") local monster = {} monster.description = "an orewalker" -monster.experience = 4800 +monster.experience = 5900 monster.outfit = { lookType = 490, lookHead = 0, @@ -112,7 +112,6 @@ monster.attacks = { { name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -1500, length = 6, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false }, -- poison { name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -800, maxDamage = -1080, radius = 3, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = true }, - { name = "drunk", interval = 2000, chance = 15, radius = 4, effect = CONST_ME_SOUND_PURPLE, target = false, duration = 6000 }, { name = "speed", interval = 2000, chance = 15, speedChange = -800, radius = 2, effect = CONST_ME_MAGIC_RED, target = false, duration = 20000 }, } diff --git a/data-otservbr-global/monster/constructs/stone_devourer.lua b/data-otservbr-global/monster/constructs/stone_devourer.lua index 5a9174c3129..39c85402870 100644 --- a/data-otservbr-global/monster/constructs/stone_devourer.lua +++ b/data-otservbr-global/monster/constructs/stone_devourer.lua @@ -118,7 +118,7 @@ monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 10 }, { type = COMBAT_ENERGYDAMAGE, percent = 30 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, - { type = COMBAT_FIREDAMAGE, percent = 100 }, + { type = COMBAT_FIREDAMAGE, percent = -5 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/constructs/weeper.lua b/data-otservbr-global/monster/constructs/weeper.lua index 75a8d7ebb42..cfc7c6b0d68 100644 --- a/data-otservbr-global/monster/constructs/weeper.lua +++ b/data-otservbr-global/monster/constructs/weeper.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Weeper") local monster = {} monster.description = "a weeper" -monster.experience = 4800 +monster.experience = 5800 monster.outfit = { lookType = 489, lookHead = 0, @@ -55,7 +55,7 @@ monster.flags = { canPushCreatures = true, staticAttackChance = 70, targetDistance = 1, - runHealth = 570, + runHealth = 0, healthHidden = false, isBlockable = false, canWalkOnEnergy = false, @@ -101,7 +101,7 @@ monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -450 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -400, maxDamage = -1000, length = 8, spread = 0, effect = CONST_ME_FIREATTACK, target = false }, { name = "combat", interval = 3000, chance = 100, type = COMBAT_FIREDAMAGE, minDamage = -80, maxDamage = -250, radius = 3, effect = CONST_ME_HITBYFIRE, target = false }, - { name = "speed", interval = 2000, chance = 10, speedChange = -800, length = 5, spread = 0, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 }, + { name = "speed", interval = 2000, chance = 10, speedChange = -600, length = 5, spread = 0, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 }, } monster.defenses = { diff --git a/data-otservbr-global/monster/demons/hellfire_fighter.lua b/data-otservbr-global/monster/demons/hellfire_fighter.lua index 6a90b5af06d..2ae5b00303a 100644 --- a/data-otservbr-global/monster/demons/hellfire_fighter.lua +++ b/data-otservbr-global/monster/demons/hellfire_fighter.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hellfire Fighter") local monster = {} monster.description = "a hellfire fighter" -monster.experience = 3400 +monster.experience = 3800 monster.outfit = { lookType = 243, lookHead = 0, diff --git a/data-otservbr-global/monster/elementals/cliff_strider.lua b/data-otservbr-global/monster/elementals/cliff_strider.lua index 2690f356f23..2e143d42796 100644 --- a/data-otservbr-global/monster/elementals/cliff_strider.lua +++ b/data-otservbr-global/monster/elementals/cliff_strider.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Cliff Strider") local monster = {} monster.description = "a cliff strider" -monster.experience = 5700 +monster.experience = 7100 monster.outfit = { lookType = 497, lookHead = 0, @@ -119,7 +119,7 @@ monster.attacks = { { name = "cliff strider skill reducer", interval = 2000, chance = 10, target = false }, { name = "cliff strider electrify", interval = 2000, chance = 15, range = 1, target = false }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -1000, length = 6, spread = 0, effect = CONST_ME_GROUNDSHAKER, target = false }, - { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = -100, maxDamage = -300, radius = 4, effect = CONST_ME_YELLOWENERGY, target = false }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -100, maxDamage = -300, radius = 4, effect = CONST_ME_YELLOWENERGY, target = false }, } monster.defenses = { @@ -130,7 +130,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 10 }, - { type = COMBAT_ENERGYDAMAGE, percent = 100 }, + { type = COMBAT_ENERGYDAMAGE, percent = 5 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 20 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, diff --git a/data-otservbr-global/monster/elementals/earth_elemental.lua b/data-otservbr-global/monster/elementals/earth_elemental.lua index f71d6a7964d..4c3b3e11aa3 100644 --- a/data-otservbr-global/monster/elementals/earth_elemental.lua +++ b/data-otservbr-global/monster/elementals/earth_elemental.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Earth Elemental") local monster = {} monster.description = "an earth elemental" -monster.experience = 450 +monster.experience = 550 monster.outfit = { lookType = 301, lookHead = 0, @@ -117,7 +117,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 85 }, + { type = COMBAT_ICEDAMAGE, percent = 5 }, { type = COMBAT_HOLYDAMAGE, percent = 50 }, { type = COMBAT_DEATHDAMAGE, percent = 40 }, } diff --git a/data-otservbr-global/monster/elementals/high_voltage_elemental.lua b/data-otservbr-global/monster/elementals/high_voltage_elemental.lua index ca99e57527e..3d6806a707b 100644 --- a/data-otservbr-global/monster/elementals/high_voltage_elemental.lua +++ b/data-otservbr-global/monster/elementals/high_voltage_elemental.lua @@ -102,7 +102,7 @@ monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 35 }, { type = COMBAT_ENERGYDAMAGE, percent = 100 }, { type = COMBAT_EARTHDAMAGE, percent = -15 }, - { type = COMBAT_FIREDAMAGE, percent = 100 }, + { type = COMBAT_FIREDAMAGE, percent = -100 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/elementals/ironblight.lua b/data-otservbr-global/monster/elementals/ironblight.lua index 593069318ed..7cb7c7c58fc 100644 --- a/data-otservbr-global/monster/elementals/ironblight.lua +++ b/data-otservbr-global/monster/elementals/ironblight.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ironblight") local monster = {} monster.description = "an ironblight" -monster.experience = 4400 +monster.experience = 5400 monster.outfit = { lookType = 498, lookHead = 0, diff --git a/data-otservbr-global/monster/elementals/massive_earth_elemental.lua b/data-otservbr-global/monster/elementals/massive_earth_elemental.lua index e894319b6ec..1b131e7c7ae 100644 --- a/data-otservbr-global/monster/elementals/massive_earth_elemental.lua +++ b/data-otservbr-global/monster/elementals/massive_earth_elemental.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Massive Earth Elemental") local monster = {} monster.description = "a massive earth elemental" -monster.experience = 950 +monster.experience = 1100 monster.outfit = { lookType = 285, lookHead = 0, @@ -119,7 +119,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 100 }, + { type = COMBAT_ICEDAMAGE, percent = 15 }, { type = COMBAT_HOLYDAMAGE, percent = 50 }, { type = COMBAT_DEATHDAMAGE, percent = 45 }, } diff --git a/data-otservbr-global/monster/elementals/sulphur_spouter.lua b/data-otservbr-global/monster/elementals/sulphur_spouter.lua index 6f574cc0a07..79ebcf8db4b 100644 --- a/data-otservbr-global/monster/elementals/sulphur_spouter.lua +++ b/data-otservbr-global/monster/elementals/sulphur_spouter.lua @@ -105,7 +105,7 @@ monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, { type = COMBAT_ENERGYDAMAGE, percent = 0 }, { type = COMBAT_EARTHDAMAGE, percent = 0 }, - { type = COMBAT_FIREDAMAGE, percent = 100 }, + { type = COMBAT_FIREDAMAGE, percent = 25 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/humanoids/lost_basher.lua b/data-otservbr-global/monster/humanoids/lost_basher.lua index e19aef97f78..f8866bdd177 100644 --- a/data-otservbr-global/monster/humanoids/lost_basher.lua +++ b/data-otservbr-global/monster/humanoids/lost_basher.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Basher") local monster = {} monster.description = "a lost basher" -monster.experience = 1800 +monster.experience = 2300 monster.outfit = { lookType = 538, lookHead = 0, @@ -61,7 +61,7 @@ monster.flags = { healthHidden = false, isBlockable = false, canWalkOnEnergy = false, - canWalkOnFire = false, + canWalkOnFire = true, canWalkOnPoison = true, } @@ -126,7 +126,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 20 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, { type = COMBAT_DEATHDAMAGE, percent = 15 }, } diff --git a/data-otservbr-global/monster/humanoids/lost_berserker.lua b/data-otservbr-global/monster/humanoids/lost_berserker.lua index cd48787df95..cbc7012ff95 100644 --- a/data-otservbr-global/monster/humanoids/lost_berserker.lua +++ b/data-otservbr-global/monster/humanoids/lost_berserker.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Berserker") local monster = {} monster.description = "a lost berserker" -monster.experience = 4400 +monster.experience = 4800 monster.outfit = { lookType = 496, lookHead = 0, @@ -111,27 +111,25 @@ monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -501 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -300, range = 7, shootEffect = CONST_ANI_WHIRLWINDAXE, target = false }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -250, range = 7, radius = 3, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_EXPLOSIONAREA, target = true }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -150, maxDamage = -250, radius = 5, effect = CONST_ME_MAGIC_RED, target = false }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -50, maxDamage = -100, radius = 5, effect = CONST_ME_MAGIC_RED, target = false }, { name = "speed", interval = 2000, chance = 10, speedChange = -800, radius = 2, effect = CONST_ME_MAGIC_RED, target = false, duration = 20000 }, - { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_STUN, target = true, duration = 6000 }, } monster.defenses = { defense = 40, armor = 80, mitigation = 2.40, - { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_TELEPORT }, } monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 20 }, - { type = COMBAT_ENERGYDAMAGE, percent = 17 }, + { type = COMBAT_ENERGYDAMAGE, percent = 100 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 10 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 40 }, + { type = COMBAT_ICEDAMAGE, percent = 10 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, { type = COMBAT_DEATHDAMAGE, percent = 15 }, } @@ -139,7 +137,7 @@ monster.elements = { monster.immunities = { { type = "paralyze", condition = true }, { type = "outfit", condition = false }, - { type = "invisible", condition = true }, + { type = "invisible", condition = false }, { type = "bleed", condition = false }, } diff --git a/data-otservbr-global/monster/humanoids/lost_husher.lua b/data-otservbr-global/monster/humanoids/lost_husher.lua index d8167f9710d..419bae1ab96 100644 --- a/data-otservbr-global/monster/humanoids/lost_husher.lua +++ b/data-otservbr-global/monster/humanoids/lost_husher.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Husher") local monster = {} monster.description = "a lost husher" -monster.experience = 1800 +monster.experience = 1100 monster.outfit = { lookType = 537, lookHead = 0, @@ -61,7 +61,7 @@ monster.flags = { healthHidden = false, isBlockable = false, canWalkOnEnergy = false, - canWalkOnFire = false, + canWalkOnFire = true, canWalkOnPoison = true, } @@ -103,7 +103,6 @@ monster.loot = { monster.attacks = { { name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -150, maxDamage = -300, length = 6, spread = 0, effect = CONST_ME_BLACKSMOKE, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -150, maxDamage = -250, radius = 5, effect = CONST_ME_BLACKSMOKE, target = false }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -150, maxDamage = -200, range = 7, shootEffect = CONST_ANI_SUDDENDEATH, effect = CONST_ME_MORTAREA, target = false }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_EARTHDAMAGE, minDamage = -150, maxDamage = -250, range = 7, radius = 2, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_MAGIC_GREEN, target = true }, { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_SOUND_RED, target = false, duration = 6000 }, @@ -125,7 +124,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 15 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, { type = COMBAT_HOLYDAMAGE, percent = -10 }, { type = COMBAT_DEATHDAMAGE, percent = 20 }, } @@ -133,7 +132,7 @@ monster.elements = { monster.immunities = { { type = "paralyze", condition = true }, { type = "outfit", condition = false }, - { type = "invisible", condition = true }, + { type = "invisible", condition = false }, { type = "bleed", condition = false }, } diff --git a/data-otservbr-global/monster/humanoids/lost_thrower.lua b/data-otservbr-global/monster/humanoids/lost_thrower.lua index c2a87f0d551..21a14bce085 100644 --- a/data-otservbr-global/monster/humanoids/lost_thrower.lua +++ b/data-otservbr-global/monster/humanoids/lost_thrower.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Thrower") local monster = {} monster.description = "a lost thrower" -monster.experience = 1200 +monster.experience = 1500 monster.outfit = { lookType = 539, lookHead = 0, @@ -61,7 +61,7 @@ monster.flags = { healthHidden = false, isBlockable = false, canWalkOnEnergy = false, - canWalkOnFire = false, + canWalkOnFire = true, canWalkOnPoison = true, } @@ -117,7 +117,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 15 }, + { type = COMBAT_ICEDAMAGE, percent = -5 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, { type = COMBAT_DEATHDAMAGE, percent = 10 }, } diff --git a/data-otservbr-global/monster/magicals/armadile.lua b/data-otservbr-global/monster/magicals/armadile.lua index beca89510f4..98665845d28 100644 --- a/data-otservbr-global/monster/magicals/armadile.lua +++ b/data-otservbr-global/monster/magicals/armadile.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Armadile") local monster = {} monster.description = "an armadile" -monster.experience = 2900 +monster.experience = 3200 monster.outfit = { lookType = 487, lookHead = 0, @@ -56,8 +56,8 @@ monster.flags = { canPushItems = false, canPushCreatures = true, staticAttackChance = 90, - targetDistance = 4, - runHealth = 300, + targetDistance = 1, + runHealth = 0, healthHidden = false, isBlockable = false, canWalkOnEnergy = false, @@ -102,9 +102,7 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -150 }, - { name = "drunk", interval = 2000, chance = 15, radius = 4, effect = CONST_ME_FIREAREA, target = true, duration = 5000 }, - { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = -430, maxDamage = -550, range = 7, effect = CONST_ME_MAGIC_BLUE, target = false }, - -- poison + { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_FIREAREA, target = true, duration = 5000 }, { name = "condition", type = CONDITION_POISON, interval = 2000, chance = 15, minDamage = -200, maxDamage = -400, radius = 4, effect = CONST_ME_POISONAREA, target = false }, } @@ -112,14 +110,13 @@ monster.defenses = { defense = 25, armor = 66, mitigation = 1.96, - { name = "invisible", interval = 2000, chance = 15, effect = CONST_ME_MAGIC_RED }, } monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 5 }, { type = COMBAT_ENERGYDAMAGE, percent = 15 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, - { type = COMBAT_FIREDAMAGE, percent = 20 }, + { type = COMBAT_FIREDAMAGE, percent = 0 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/magicals/choking_fear.lua b/data-otservbr-global/monster/magicals/choking_fear.lua index 16361bab0e9..604ca5cdaa6 100644 --- a/data-otservbr-global/monster/magicals/choking_fear.lua +++ b/data-otservbr-global/monster/magicals/choking_fear.lua @@ -124,7 +124,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 10 }, - { type = COMBAT_ENERGYDAMAGE, percent = 15 }, + { type = COMBAT_ENERGYDAMAGE, percent = 2 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 100 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, diff --git a/data-otservbr-global/monster/magicals/phantasm.lua b/data-otservbr-global/monster/magicals/phantasm.lua index 9dc266f8bb3..36c0baed405 100644 --- a/data-otservbr-global/monster/magicals/phantasm.lua +++ b/data-otservbr-global/monster/magicals/phantasm.lua @@ -70,7 +70,7 @@ monster.light = { monster.summon = { maxSummons = 4, summons = { - { name = "Phantasm Summon", chance = 20, interval = 2000, count = 4 }, + { name = "Phantasm Summon", chance = 35, interval = 2000, count = 4 }, }, } diff --git a/data-otservbr-global/monster/magicals/retching_horror.lua b/data-otservbr-global/monster/magicals/retching_horror.lua index a6814343eaf..0479a7cb368 100644 --- a/data-otservbr-global/monster/magicals/retching_horror.lua +++ b/data-otservbr-global/monster/magicals/retching_horror.lua @@ -113,7 +113,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 5 }, - { type = COMBAT_ENERGYDAMAGE, percent = 10 }, + { type = COMBAT_ENERGYDAMAGE, percent = -3 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 85 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, diff --git a/data-otservbr-global/monster/mammals/mutated_bat.lua b/data-otservbr-global/monster/mammals/mutated_bat.lua index 211ca62d2ad..dfea480341b 100644 --- a/data-otservbr-global/monster/mammals/mutated_bat.lua +++ b/data-otservbr-global/monster/mammals/mutated_bat.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Mutated Bat") local monster = {} monster.description = "a mutated bat" -monster.experience = 615 +monster.experience = 750 monster.outfit = { lookType = 307, lookHead = 0, diff --git a/data-otservbr-global/monster/plants/hideous_fungus.lua b/data-otservbr-global/monster/plants/hideous_fungus.lua index a80ba080fd7..275adb03b2f 100644 --- a/data-otservbr-global/monster/plants/hideous_fungus.lua +++ b/data-otservbr-global/monster/plants/hideous_fungus.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hideous Fungus") local monster = {} monster.description = "a hideous fungus" -monster.experience = 2900 +monster.experience = 3700 monster.outfit = { lookType = 499, lookHead = 0, @@ -57,7 +57,7 @@ monster.flags = { canPushCreatures = true, staticAttackChance = 90, targetDistance = 4, - runHealth = 275, + runHealth = 0, healthHidden = false, isBlockable = false, canWalkOnEnergy = true, @@ -71,9 +71,9 @@ monster.light = { } monster.summon = { - maxSummons = 2, + maxSummons = 1, summons = { - { name = "humorless fungus", chance = 10, interval = 2000, count = 2 }, + { name = "humorless fungus", chance = 10, interval = 2000, count = 1 }, }, } @@ -110,7 +110,6 @@ monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -450 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -250, maxDamage = -430, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = false }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_ICEDAMAGE, minDamage = -250, maxDamage = -550, length = 8, spread = 0, shootEffect = CONST_ANI_SNOWBALL, effect = CONST_ME_ICEAREA, target = false }, - { name = "speed", interval = 2000, chance = 10, speedChange = -600, radius = 1, effect = CONST_ME_MAGIC_RED, target = true, duration = 60000 }, { name = "drunk", interval = 2000, chance = 10, range = 7, radius = 5, shootEffect = CONST_ANI_SMALLSTONE, effect = CONST_ME_STUN, target = true, duration = 4000 }, -- poison { name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -400, maxDamage = -640, range = 7, radius = 3, effect = CONST_ME_HITBYPOISON, target = false }, @@ -121,14 +120,14 @@ monster.defenses = { armor = 60, mitigation = 1.74, { name = "combat", interval = 2000, chance = 15, type = COMBAT_HEALING, minDamage = 275, maxDamage = 350, effect = CONST_ME_MAGIC_BLUE, target = false }, - { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE }, + { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE, duration = 2000 }, } monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, { type = COMBAT_ENERGYDAMAGE, percent = 15 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, - { type = COMBAT_FIREDAMAGE, percent = 5 }, + { type = COMBAT_FIREDAMAGE, percent = -5 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/plants/humongous_fungus.lua b/data-otservbr-global/monster/plants/humongous_fungus.lua index 942684e732c..09f301a6a59 100644 --- a/data-otservbr-global/monster/plants/humongous_fungus.lua +++ b/data-otservbr-global/monster/plants/humongous_fungus.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Humongous Fungus") local monster = {} monster.description = "a humongous fungus" -monster.experience = 2600 +monster.experience = 2900 monster.outfit = { lookType = 488, lookHead = 0, @@ -104,7 +104,7 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -330 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -180, maxDamage = -350, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = false }, - { name = "poisonfield", interval = 2000, chance = 20, radius = 4, target = false }, + { name = "poisonfield", interval = 2000, chance = 10, radius = 4, target = false }, -- poison { name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -500, maxDamage = -1000, length = 8, spread = 0, effect = CONST_ME_GREEN_RINGS, target = false }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = -130, maxDamage = -260, length = 5, spread = 0, effect = CONST_ME_MAGIC_RED, target = false }, @@ -117,14 +117,13 @@ monster.defenses = { armor = 70, mitigation = 2.02, { name = "combat", interval = 2000, chance = 10, type = COMBAT_HEALING, minDamage = 225, maxDamage = 380, effect = CONST_ME_MAGIC_BLUE, target = false }, - { name = "invisible", interval = 2000, chance = 15, effect = CONST_ME_MAGIC_BLUE }, } monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, { type = COMBAT_ENERGYDAMAGE, percent = 15 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, - { type = COMBAT_FIREDAMAGE, percent = 5 }, + { type = COMBAT_FIREDAMAGE, percent = -10 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua index 24c993fda5b..1ca0ff1ca75 100644 --- a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua +++ b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Abyssador") local monster = {} monster.description = "Abyssador" -monster.experience = 50000 +monster.experience = 400000 monster.outfit = { lookType = 495, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua index cd287d83709..8d410c85d26 100644 --- a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua +++ b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Gnomevil") local monster = {} monster.description = "Gnomevil" -monster.experience = 45000 +monster.experience = 400000 monster.outfit = { lookType = 504, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua b/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua index 19ab3230231..ead6b096fbf 100644 --- a/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua +++ b/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua @@ -13,8 +13,8 @@ monster.outfit = { lookMount = 0, } -monster.health = 2500 -monster.maxHealth = 2500 +monster.health = 1600 +monster.maxHealth = 1600 monster.race = "venom" monster.corpse = 16083 monster.speed = 115 @@ -70,17 +70,14 @@ monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -475 }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_EARTHDAMAGE, minDamage = -40, maxDamage = -197, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = true }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_ICEDAMAGE, minDamage = 0, maxDamage = -525, range = 7, shootEffect = CONST_ANI_SNOWBALL, effect = CONST_ME_ICEAREA, target = true }, - -- poison { name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -400, maxDamage = -640, range = 7, radius = 3, effect = CONST_ME_HITBYPOISON, target = false }, - { name = "drunk", interval = 2000, chance = 10, range = 7, radius = 4, effect = CONST_ME_STUN, target = true, duration = 4000 }, } monster.defenses = { defense = 0, armor = 0, - -- mitigation = ???, { name = "combat", interval = 2000, chance = 5, type = COMBAT_HEALING, minDamage = 0, maxDamage = 230, effect = CONST_ME_MAGIC_BLUE, target = false }, - { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE }, + { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE }, } monster.elements = { diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua index d611e78d84b..17cb6520d29 100644 --- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua +++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("The Count of the Core") local monster = {} monster.description = "The Count Of The Core" -monster.experience = 40000 +monster.experience = 300000 monster.outfit = { lookType = 1046, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua index d654288c405..99053b93882 100644 --- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua +++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("The Duke of the Depths") local monster = {} monster.description = "The Duke Of The Depths" -monster.experience = 40000 +monster.experience = 300000 monster.outfit = { lookType = 1047, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua index 90b3124369a..7ad855bc88a 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ferumbras Mortal Shell") local monster = {} monster.description = "Ferumbras Mortal Shell" -monster.experience = 500000 +monster.experience = 2000000 monster.outfit = { lookType = 229, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua index 0e0822e3d72..3f32bac95d4 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Mazoran") local monster = {} monster.description = "Mazoran" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 842, lookHead = 77, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua index 625f10cf7fc..364b4f864a8 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Plagirath") local monster = {} monster.description = "Plagirath" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 862, lookHead = 84, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua index 9701dc7d0f9..df016edfd84 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ragiaz") local monster = {} monster.description = "Ragiaz" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 862, lookHead = 76, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua index 67d218c5ee0..066e22237c0 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Razzagorn") local monster = {} monster.description = "Razzagorn" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 842, lookHead = 78, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua index c0de2c0c362..72c908104af 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Shulgrax") local monster = {} monster.description = "Shulgrax" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 842, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua index 583167460e1..30b99353cdf 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Tarbaz") local monster = {} monster.description = "Tarbaz" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 842, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua index 5a441ff10f1..03fa92d3394 100644 --- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua +++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Zamulosh") local monster = {} monster.description = "Zamulosh" -monster.experience = 250000 +monster.experience = 500000 monster.outfit = { lookType = 862, lookHead = 16, diff --git a/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua b/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua index 49025f4c6c4..bbc58881ee4 100644 --- a/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua +++ b/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Inky") local monster = {} monster.description = "Inky" -monster.experience = 250 +monster.experience = 700 monster.outfit = { lookType = 46, lookHead = 0, diff --git a/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua b/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua index 583df08b8b5..0325642344c 100644 --- a/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua +++ b/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Sharptooth") local monster = {} monster.description = "Sharptooth" -monster.experience = 1600 +monster.experience = 3000 monster.outfit = { lookType = 20, lookHead = 0, diff --git a/data-otservbr-global/monster/reptiles/seacrest_serpent.lua b/data-otservbr-global/monster/reptiles/seacrest_serpent.lua index 9be39aaa571..04b7dba7416 100644 --- a/data-otservbr-global/monster/reptiles/seacrest_serpent.lua +++ b/data-otservbr-global/monster/reptiles/seacrest_serpent.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Seacrest Serpent") local monster = {} monster.description = "a seacrest serpent" -monster.experience = 2600 +monster.experience = 2900 monster.outfit = { lookType = 675, lookHead = 0, diff --git a/data-otservbr-global/monster/reptiles/young_sea_serpent.lua b/data-otservbr-global/monster/reptiles/young_sea_serpent.lua index 961ff8204c6..98e8744b7b1 100644 --- a/data-otservbr-global/monster/reptiles/young_sea_serpent.lua +++ b/data-otservbr-global/monster/reptiles/young_sea_serpent.lua @@ -108,7 +108,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = -20 }, { type = COMBAT_ENERGYDAMAGE, percent = -10 }, - { type = COMBAT_EARTHDAMAGE, percent = 100 }, + { type = COMBAT_EARTHDAMAGE, percent = -5 }, { type = COMBAT_FIREDAMAGE, percent = 30 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, diff --git a/data-otservbr-global/monster/undeads/betrayed_wraith.lua b/data-otservbr-global/monster/undeads/betrayed_wraith.lua index 0c0648acda5..201887a7712 100644 --- a/data-otservbr-global/monster/undeads/betrayed_wraith.lua +++ b/data-otservbr-global/monster/undeads/betrayed_wraith.lua @@ -112,7 +112,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, - { type = COMBAT_ENERGYDAMAGE, percent = 100 }, + { type = COMBAT_ENERGYDAMAGE, percent = 10 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 100 }, { type = COMBAT_LIFEDRAIN, percent = 100 }, diff --git a/data-otservbr-global/monster/undeads/blightwalker.lua b/data-otservbr-global/monster/undeads/blightwalker.lua index 28706adeb50..d900484b19f 100644 --- a/data-otservbr-global/monster/undeads/blightwalker.lua +++ b/data-otservbr-global/monster/undeads/blightwalker.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Blightwalker") local monster = {} monster.description = "a blightwalker" -monster.experience = 5850 +monster.experience = 6400 monster.outfit = { lookType = 246, lookHead = 0, @@ -108,9 +108,8 @@ monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -490 }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -220, maxDamage = -405, range = 7, radius = 1, shootEffect = CONST_ANI_POISON, target = true }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_LIFEDRAIN, minDamage = -65, maxDamage = -135, radius = 4, effect = CONST_ME_MAGIC_GREEN, target = false }, - { name = "drunk", interval = 2000, chance = 10, radius = 3, effect = CONST_ME_HITBYPOISON, target = false, duration = 5000 }, { name = "blightwalker curse", interval = 2000, chance = 15, target = false }, - { name = "speed", interval = 2000, chance = 15, speedChange = -300, range = 7, shootEffect = CONST_ANI_POISON, target = true, duration = 30000 }, + { name = "speed", interval = 2000, chance = 10, speedChange = -300, range = 7, shootEffect = CONST_ANI_POISON, target = true, duration = 15000 }, } monster.defenses = { @@ -127,7 +126,7 @@ monster.elements = { { type = COMBAT_LIFEDRAIN, percent = 0 }, { type = COMBAT_MANADRAIN, percent = 0 }, { type = COMBAT_DROWNDAMAGE, percent = 0 }, - { type = COMBAT_ICEDAMAGE, percent = 50 }, + { type = COMBAT_ICEDAMAGE, percent = 15 }, { type = COMBAT_HOLYDAMAGE, percent = -30 }, { type = COMBAT_DEATHDAMAGE, percent = 100 }, } diff --git a/data-otservbr-global/monster/undeads/crypt_warrior.lua b/data-otservbr-global/monster/undeads/crypt_warrior.lua index 5de0ab637ba..dad0cbe7529 100644 --- a/data-otservbr-global/monster/undeads/crypt_warrior.lua +++ b/data-otservbr-global/monster/undeads/crypt_warrior.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Crypt Warrior") local monster = {} monster.description = "a crypt warrior" -monster.experience = 4200 +monster.experience = 6050 monster.outfit = { lookType = 298, lookHead = 0, diff --git a/data-otservbr-global/monster/undeads/falcon_knight.lua b/data-otservbr-global/monster/undeads/falcon_knight.lua index 54a23130758..2e2ac28a1fe 100644 --- a/data-otservbr-global/monster/undeads/falcon_knight.lua +++ b/data-otservbr-global/monster/undeads/falcon_knight.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Falcon Knight") local monster = {} monster.description = "a falcon knight" -monster.experience = 5985 +monster.experience = 6300 monster.outfit = { lookType = 1071, lookHead = 57, diff --git a/data-otservbr-global/monster/undeads/falcon_paladin.lua b/data-otservbr-global/monster/undeads/falcon_paladin.lua index 799143fe943..cc6a41571e4 100644 --- a/data-otservbr-global/monster/undeads/falcon_paladin.lua +++ b/data-otservbr-global/monster/undeads/falcon_paladin.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Falcon Paladin") local monster = {} monster.description = "a falcon paladin" -monster.experience = 6544 +monster.experience = 6900 monster.outfit = { lookType = 1071, lookHead = 57, diff --git a/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua b/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua index b1f76414dd4..527d0708730 100644 --- a/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua +++ b/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua @@ -108,7 +108,7 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -520, condition = { type = CONDITION_POISON, totalDamage = 380, interval = 4000 } }, - { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -920, range = 1, target = false }, + { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -620, range = 1, target = false }, { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_SMALLCLOUDS, target = false, duration = 3000 }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_LIFEDRAIN, minDamage = -220, maxDamage = -880, range = 1, effect = CONST_ME_SMALLCLOUDS, target = false }, } @@ -124,7 +124,7 @@ monster.defenses = { monster.elements = { { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, - { type = COMBAT_ENERGYDAMAGE, percent = 100 }, + { type = COMBAT_ENERGYDAMAGE, percent = 5 }, { type = COMBAT_EARTHDAMAGE, percent = 100 }, { type = COMBAT_FIREDAMAGE, percent = 100 }, { type = COMBAT_LIFEDRAIN, percent = 0 }, diff --git a/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua b/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua index a0cc7545b99..25ebd9c1f6d 100644 --- a/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua +++ b/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Skeleton Elite Warrior") local monster = {} monster.description = "a skeleton elite warrior" -monster.experience = 4500 +monster.experience = 4800 monster.outfit = { lookType = 298, lookHead = 0, diff --git a/data-otservbr-global/monster/undeads/undead_dragon.lua b/data-otservbr-global/monster/undeads/undead_dragon.lua index 2ab63af8b77..b9039c5d914 100644 --- a/data-otservbr-global/monster/undeads/undead_dragon.lua +++ b/data-otservbr-global/monster/undeads/undead_dragon.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Undead Dragon") local monster = {} monster.description = "an undead dragon" -monster.experience = 7200 +monster.experience = 7500 monster.outfit = { lookType = 231, lookHead = 0, diff --git a/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua b/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua index 0f66c87cb21..10d1d5b08bc 100644 --- a/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua +++ b/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Undead Elite Gladiator") local monster = {} monster.description = "an undead elite gladiator" -monster.experience = 4740 +monster.experience = 5090 monster.outfit = { lookType = 306, lookHead = 0, diff --git a/data-otservbr-global/monster/vermins/cave_devourer.lua b/data-otservbr-global/monster/vermins/cave_devourer.lua index 1283ac20b81..17d89dfb176 100644 --- a/data-otservbr-global/monster/vermins/cave_devourer.lua +++ b/data-otservbr-global/monster/vermins/cave_devourer.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Cave Devourer") local monster = {} monster.description = "a cave devourer" -monster.experience = 2380 +monster.experience = 3380 monster.outfit = { lookType = 1036, lookHead = 0, @@ -88,7 +88,7 @@ monster.loot = { { name = "slime heart", chance = 13770, maxCount = 4 }, { name = "cave devourer legs", chance = 17160 }, { id = 3049, chance = 2540 }, -- stealth ring - { name = "suspicious device", chance = 420 }, + { name = "suspicious device", chance = 850 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/vermins/chasm_spawn.lua b/data-otservbr-global/monster/vermins/chasm_spawn.lua index 0764367a1d0..9f27f57b006 100644 --- a/data-otservbr-global/monster/vermins/chasm_spawn.lua +++ b/data-otservbr-global/monster/vermins/chasm_spawn.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Chasm Spawn") local monster = {} monster.description = "a chasm spawn" -monster.experience = 2700 +monster.experience = 3600 monster.outfit = { lookType = 1037, lookHead = 0, @@ -89,7 +89,7 @@ monster.loot = { { name = "green crystal shard", chance = 7850 }, { name = "violet crystal shard", chance = 4690 }, { name = "mushroom backpack", chance = 610 }, - { name = "suspicious device", chance = 520 }, + { name = "suspicious device", chance = 850 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/vermins/deepworm.lua b/data-otservbr-global/monster/vermins/deepworm.lua index 91b2ded8752..21775b2c673 100644 --- a/data-otservbr-global/monster/vermins/deepworm.lua +++ b/data-otservbr-global/monster/vermins/deepworm.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Deepworm") local monster = {} monster.description = "a deepworm" -monster.experience = 2300 +monster.experience = 2520 monster.outfit = { lookType = 1033, lookHead = 0, diff --git a/data-otservbr-global/monster/vermins/diremaw.lua b/data-otservbr-global/monster/vermins/diremaw.lua index b89c47c99e7..fd74c4e2be6 100644 --- a/data-otservbr-global/monster/vermins/diremaw.lua +++ b/data-otservbr-global/monster/vermins/diremaw.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Diremaw") local monster = {} monster.description = "a diremaw" -monster.experience = 2500 +monster.experience = 2770 monster.outfit = { lookType = 1034, lookHead = 0, diff --git a/data-otservbr-global/monster/vermins/drillworm.lua b/data-otservbr-global/monster/vermins/drillworm.lua index 3cf92fce18a..a435dc16ca3 100644 --- a/data-otservbr-global/monster/vermins/drillworm.lua +++ b/data-otservbr-global/monster/vermins/drillworm.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Drillworm") local monster = {} monster.description = "a drillworm" -monster.experience = 858 +monster.experience = 1200 monster.outfit = { lookType = 527, lookHead = 0, diff --git a/data-otservbr-global/monster/vermins/tunnel_tyrant.lua b/data-otservbr-global/monster/vermins/tunnel_tyrant.lua index f8e3b7bfb03..c2b8eb2df90 100644 --- a/data-otservbr-global/monster/vermins/tunnel_tyrant.lua +++ b/data-otservbr-global/monster/vermins/tunnel_tyrant.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Tunnel Tyrant") local monster = {} monster.description = "a tunnel tyrant" -monster.experience = 3400 +monster.experience = 4420 monster.outfit = { lookType = 1035, lookHead = 0, @@ -87,7 +87,7 @@ monster.loot = { { name = "crystal mace", chance = 1580 }, { id = 23508, chance = 3010 }, -- energy vein { name = "crystalline armor", chance = 860 }, - { name = "suspicious device", chance = 1290 }, + { name = "suspicious device", chance = 1850 }, } monster.attacks = { diff --git a/data-otservbr-global/scripts/actions/bosses_levers/grand_,master_oberon.lua b/data-otservbr-global/scripts/actions/bosses_levers/grand_master_oberon.lua similarity index 100% rename from data-otservbr-global/scripts/actions/bosses_levers/grand_,master_oberon.lua rename to data-otservbr-global/scripts/actions/bosses_levers/grand_master_oberon.lua From f56af162772371b5a109e2514ff12341b7693148 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:37:27 -0300 Subject: [PATCH 69/95] fix: sam sellable missing items (#2685) --- data-otservbr-global/npc/sam.lua | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/data-otservbr-global/npc/sam.lua b/data-otservbr-global/npc/sam.lua index cd3a517c147..fdca8c02f01 100644 --- a/data-otservbr-global/npc/sam.lua +++ b/data-otservbr-global/npc/sam.lua @@ -210,6 +210,7 @@ npcConfig.shop = { { itemName = "legion helmet", clientId = 3374, sell = 22 }, { itemName = "longsword", clientId = 3285, buy = 160, sell = 51 }, { itemName = "mace", clientId = 3286, buy = 90, sell = 30 }, + { itemName = "magic plate armor", clientId = 3366, sell = 6400 }, { itemName = "morning star", clientId = 3282, buy = 430, sell = 100 }, { itemName = "orcish axe", clientId = 3316, sell = 350 }, { itemName = "plate armor", clientId = 3357, buy = 1200, sell = 400 }, @@ -227,16 +228,16 @@ npcConfig.shop = { { itemName = "steel shield", clientId = 3409, buy = 240, sell = 80 }, { itemName = "studded armor", clientId = 3378, buy = 90, sell = 25 }, { itemName = "studded club", clientId = 3336, sell = 10 }, - { itemName = "studded helmet", clientId = 3376, buy = 63 }, - { itemName = "studded legs", clientId = 3362, buy = 50 }, - { itemName = "studded shield", clientId = 3426, buy = 50 }, - { itemName = "sword", clientId = 3264, buy = 85 }, - { itemName = "throwing knife", clientId = 3298, buy = 25 }, - { itemName = "two handed sword", clientId = 3265, buy = 950 }, - { itemName = "viking helmet", clientId = 3367, buy = 265 }, - { itemName = "viking shield", clientId = 3431, buy = 260 }, - { itemName = "war hammer", clientId = 3279, buy = 10000 }, - { itemName = "wooden shield", clientId = 3412, buy = 15 }, + { itemName = "studded helmet", clientId = 3376, buy = 63, sell = 20 }, + { itemName = "studded legs", clientId = 3362, buy = 50, sell = 15 }, + { itemName = "studded shield", clientId = 3426, buy = 50, sell = 16 }, + { itemName = "sword", clientId = 3264, buy = 85, sell = 25 }, + { itemName = "throwing knife", clientId = 3298, buy = 25, sell = 2 }, + { itemName = "two handed sword", clientId = 3265, buy = 950, sell = 450 }, + { itemName = "viking helmet", clientId = 3367, buy = 265, sell = 66 }, + { itemName = "viking shield", clientId = 3431, buy = 260, sell = 85 }, + { itemName = "war hammer", clientId = 3279, buy = 10000, sell = 470 }, + { itemName = "wooden shield", clientId = 3412, buy = 15, sell = 5 }, } -- On buy npc shop message npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost) From 552eaefeca2530fee2194da37498fc21083ce2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Tue, 11 Jun 2024 10:39:55 -0300 Subject: [PATCH 70/95] fix: mispelled storages in freequests.lua and add some quests (#2658) --- .../creaturescripts/customs/freequests.lua | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua index ad0e8d49b73..6f7e00e77f6 100644 --- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua +++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua @@ -60,15 +60,15 @@ local questTable = { { storage = Storage.InServiceofYalahar.DoorToMatrix, storageValue = 1 }, { storage = Storage.InServiceofYalahar.DoorToQuara, storageValue = 1 }, { storage = Storage.CultsOfTibia.Questline, storageValue = 7 }, - { storage = Storage.CultsOfTibia.Minotaurs.jamesfrancisTask, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 }, { storage = Storage.CultsOfTibia.Minotaurs.Mission, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Minotaurs.bossTimer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 }, { storage = Storage.CultsOfTibia.MotA.Mission, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Pedra1, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Pedra2, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Pedra3, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Respostas, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Perguntaid, storageValue = 1 }, + { storage = Storage.CultsOfTibia.MotA.Stone1, storageValue = 1 }, + { storage = Storage.CultsOfTibia.MotA.Stone2, storageValue = 1 }, + { storage = Storage.CultsOfTibia.MotA.Stone3, storageValue = 1 }, + { storage = Storage.CultsOfTibia.MotA.Answer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.MotA.QuestionId, storageValue = 1 }, { storage = Storage.CultsOfTibia.Barkless.Mission, storageValue = 1 }, { storage = Storage.CultsOfTibia.Barkless.sulphur, storageValue = 4 }, { storage = Storage.CultsOfTibia.Barkless.Tar, storageValue = 3 }, @@ -76,19 +76,19 @@ local questTable = { { storage = Storage.CultsOfTibia.Barkless.Objects, storageValue = 1 }, { storage = Storage.CultsOfTibia.Barkless.Temp, storageValue = 1 }, { storage = Storage.CultsOfTibia.Orcs.Mission, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Orcs.lookType, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Orcs.bossTimer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Orcs.LookType, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Orcs.BossTimer, storageValue = 1 }, { storage = Storage.CultsOfTibia.Life.Mission, storageValue = 7 }, - { storage = Storage.CultsOfTibia.Life.bossTimer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Life.BossTimer, storageValue = 1 }, { storage = Storage.CultsOfTibia.Humans.Mission, storageValue = 1 }, { storage = Storage.CultsOfTibia.Humans.Vaporized, storageValue = 1 }, { storage = Storage.CultsOfTibia.Humans.Decaying, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Humans.bossTimer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Humans.BossTimer, storageValue = 1 }, { storage = Storage.CultsOfTibia.Misguided.Mission, storageValue = 1 }, { storage = Storage.CultsOfTibia.Misguided.Monsters, storageValue = 1 }, { storage = Storage.CultsOfTibia.Misguided.Exorcisms, storageValue = 1 }, { storage = Storage.CultsOfTibia.Misguided.Time, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Misguided.bossTimer, storageValue = 1 }, + { storage = Storage.CultsOfTibia.Misguided.BossTimer, storageValue = 1 }, { storage = Storage.CultsOfTibia.Minotaurs.EntranceAccessDoor, storageValue = 1 }, { storage = Storage.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 }, { storage = Storage.ExplorerSociety.QuestLine, storageValue = 1 }, @@ -119,6 +119,7 @@ local questTable = { { storage = Storage.ForgottenKnowledge.LloydKilled, storageValue = 1 }, { storage = Storage.ForgottenKnowledge.LadyTenebrisKilled, storageValue = 1 }, { storage = Storage.ForgottenKnowledge.AccessMachine, storageValue = 1 }, + { storage = Storage.ForgottenKnowledge.AccessLavaTeleport, storageValue = 1 }, { storage = Storage.BarbarianTest.Questline, storageValue = 8 }, { storage = Storage.BarbarianTest.Mission01, storageValue = 3 }, { storage = Storage.BarbarianTest.Mission02, storageValue = 3 }, @@ -146,8 +147,8 @@ local questTable = { { storage = Storage.DjinnWar.MaridFaction.Mission02, storageValue = 2 }, { storage = Storage.DjinnWar.MaridFaction.RataMari, storageValue = 2 }, { storage = Storage.DjinnWar.MaridFaction.Mission03, storageValue = 3 }, - { storage = Storage.TheWayToYalahar.Questline, storageValue = 1 }, - { storage = Storage.SearoutesAroundYalahar.TownsCounter, storageValue = 1 }, + { storage = Storage.TheWayToYalahar.QuestLine, storageValue = 1 }, + { storage = Storage.SearoutesAroundYalahar.TownsCounter, storageValue = 5 }, { storage = Storage.SearoutesAroundYalahar.AbDendriel, storageValue = 1 }, { storage = Storage.SearoutesAroundYalahar.Darashia, storageValue = 1 }, { storage = Storage.SearoutesAroundYalahar.Venore, storageValue = 1 }, @@ -205,7 +206,7 @@ local questTable = { { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.KingTibianus, storageValue = 1 }, { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Leeland, storageValue = 1 }, { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Angus, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wydrin, storageValue = 1 }, + { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wyrdin, storageValue = 1 }, { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Telas, storageValue = 1 }, { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Humgolf, storageValue = 1 }, { storage = Storage.TheShatteredIsles.DefaultStart, storageValue = 3 }, @@ -314,6 +315,7 @@ local questTable = { { storage = Storage.FerumbrasAscension.TarbazDoor, storageValue = 1 }, { storage = Storage.FerumbrasAscension.HabitatsAccess, storageValue = 1 }, { storage = Storage.FerumbrasAscension.TheLordOfTheLiceAccess, storageValue = 1 }, + { storage = Storage.FerumbrasAscension.Statue, storageValue = 1 }, { storage = Storage.Quest.U12_00.TheDreamCourts.AndrewDoor, storageValue = 1 }, @@ -372,11 +374,19 @@ local questTable = { { storage = Storage.OutfitQuest.DefaultStart, storageValue = 1 }, { storage = Storage.HeroRathleton.AccessDoor, storageValue = 1 }, - { storage = Storage.HeroRathleton.FastWay, storageValue = 1 }, + { storage = Storage.HeroRathleton.AccessTeleport1, storageValue = 1 }, + { storage = Storage.HeroRathleton.AccessTeleport2, storageValue = 1 }, + { storage = Storage.HeroRathleton.AccessTeleport3, storageValue = 1 }, -- Sea Serpent Quest { storage = Storage.Quest.U8_2.FishForASerpent.QuestLine, storageValue = 5 }, { storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.QuestLine, storageValue = 2 }, + + --The White Raven Monastery + { storage = Storage.WhiteRavenMonastery.QuestLog, storageValue = 1 }, + { storage = Storage.WhiteRavenMonastery.Passage, storageValue = 1 }, + { storage = Storage.WhiteRavenMonastery.Diary, storageValue = 2 }, + { storage = Storage.WhiteRavenMonastery.Door, storageValue = 1 }, } -- from Position: (33201, 31762, 1) From 5d0d1c9e0947dee376461a51caa828c430521ca7 Mon Sep 17 00:00:00 2001 From: Karin <pessoacarlos98@gmail.com> Date: Wed, 12 Jun 2024 10:17:48 -0300 Subject: [PATCH 71/95] fix: prevent guests to rotate dummy (#2684) --- src/game/game.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index 899a89549d9..fed3a3910a1 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -126,6 +126,10 @@ namespace InternalGame { if (isGuest && !isItemInGuestInventory && !item->isLadder() && !item->canBeUsedByGuests()) { return false; } + + if (isGuest && item->isDummy()) { + return false; + } } return true; From 201aa8cb940bab3c2db6779a0dee3be3ae218198 Mon Sep 17 00:00:00 2001 From: Beats <daniel15042014@hotmail.com> Date: Wed, 12 Jun 2024 10:43:03 -0300 Subject: [PATCH 72/95] feat: shared_ptr: familiar, groups and reload: familiars, outfits (#2556) --- CMakePresets.json | 1 + data/scripts/talkactions/god/reload.lua | 3 + src/creatures/appearance/mounts/mounts.cpp | 2 +- src/creatures/appearance/outfit/outfit.cpp | 37 +++-- src/creatures/appearance/outfit/outfit.hpp | 14 +- src/creatures/players/grouping/familiars.cpp | 7 +- src/creatures/players/grouping/familiars.hpp | 5 +- src/creatures/players/grouping/groups.cpp | 4 +- src/creatures/players/grouping/groups.hpp | 4 +- src/creatures/players/player.cpp | 6 +- src/creatures/players/player.hpp | 11 +- src/game/functions/game_reload.cpp | 154 ++++++++++-------- src/game/functions/game_reload.hpp | 50 +++--- src/game/game.cpp | 4 +- src/game/game.hpp | 2 + src/io/functions/iologindata_load_player.cpp | 4 +- .../creatures/player/group_functions.cpp | 16 +- src/server/network/protocol/protocolgame.cpp | 2 +- 18 files changed, 184 insertions(+), 142 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 92ef2d33c6a..49f631e1921 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -54,6 +54,7 @@ "DEBUG_LOG": "ON", "ASAN_ENABLED": "OFF", "BUILD_STATIC_LIBRARY": "OFF", + "SPEED_UP_BUILD_UNITY": "OFF", "VCPKG_TARGET_TRIPLET": "x64-windows" } }, diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua index 20b9431dbba..d0c0cdd45a8 100644 --- a/data/scripts/talkactions/god/reload.lua +++ b/data/scripts/talkactions/god/reload.lua @@ -7,6 +7,7 @@ local reloadTypes = { ["configuration"] = RELOAD_TYPE_CONFIG, ["core"] = RELOAD_TYPE_CORE, ["events"] = RELOAD_TYPE_EVENTS, + ["familiar"] = RELOAD_TYPE_FAMILIARS, ["global"] = RELOAD_TYPE_CORE, ["group"] = RELOAD_TYPE_GROUPS, ["groups"] = RELOAD_TYPE_GROUPS, @@ -20,6 +21,8 @@ local reloadTypes = { ["monsters"] = RELOAD_TYPE_MONSTERS, ["mount"] = RELOAD_TYPE_MOUNTS, ["mounts"] = RELOAD_TYPE_MOUNTS, + ["outfit"] = RELOAD_TYPE_OUTFITS, + ["outfits"] = RELOAD_TYPE_OUTFITS, ["npc"] = RELOAD_TYPE_NPCS, ["npcs"] = RELOAD_TYPE_NPCS, ["raid"] = RELOAD_TYPE_RAIDS, diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index 7051d04ddb8..7014d0b026b 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -29,7 +29,7 @@ bool Mounts::loadFromXml() { } for (auto mountNode : doc.child("mounts").children()) { - uint16_t lookType = pugi::cast<uint16_t>(mountNode.attribute("clientid").value()); + auto lookType = pugi::cast<uint16_t>(mountNode.attribute("clientid").value()); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("{} - An unregistered creature mount with id '{}' was blocked to prevent client crash.", __FUNCTION__, lookType); continue; diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 9e96c60e756..97120659333 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -14,6 +14,10 @@ #include "utils/tools.hpp" #include "game/game.hpp" +Outfits &Outfits::getInstance() { + return inject<Outfits>(); +} + bool Outfits::reload() { for (auto &outfitsVector : outfits) { outfitsVector.clear(); @@ -41,7 +45,7 @@ bool Outfits::loadFromXml() { continue; } - uint16_t type = pugi::cast<uint16_t>(attr.value()); + auto type = pugi::cast<uint16_t>(attr.value()); if (type > PLAYERSEX_LAST) { g_logger().warn("[Outfits::loadFromXml] - Invalid outfit type {}", type); continue; @@ -53,7 +57,7 @@ bool Outfits::loadFromXml() { continue; } - if (uint16_t lookType = pugi::cast<uint16_t>(lookTypeAttribute.value()); + if (auto lookType = pugi::cast<uint16_t>(lookTypeAttribute.value()); g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was ignored to prevent client crash.", lookType); @@ -74,7 +78,22 @@ bool Outfits::loadFromXml() { return true; } -std::shared_ptr<Outfit> Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { +std::shared_ptr<Outfit> Outfits::getOutfitByLookType(const std::shared_ptr<const Player> &player, uint16_t lookType, bool isOppositeOutfit) const { + if (!player) { + g_logger().error("[{}] - Player not found", __FUNCTION__); + return nullptr; + } + + auto sex = player->getSex(); + if (sex != PLAYERSEX_FEMALE && sex != PLAYERSEX_MALE) { + g_logger().error("[{}] - Sex invalid or player: {}", __FUNCTION__, player->getName()); + return nullptr; + } + + if (isOppositeOutfit) { + sex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE; + } + auto it = std::ranges::find_if(outfits[sex], [&lookType](const auto &outfit) { return outfit->lookType == lookType; }); @@ -84,15 +103,3 @@ std::shared_ptr<Outfit> Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t l } return nullptr; } - -/** - * Get the oposite sex equivalent outfit - * @param sex current sex - * @param lookType current looktype - * @return <b>const</b> pointer to the outfit or <b>nullptr</b> if it could not be found. - */ - -std::shared_ptr<Outfit> Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { - PlayerSex_t searchSex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE; - return getOutfitByLookType(searchSex, lookType); -} diff --git a/src/creatures/appearance/outfit/outfit.hpp b/src/creatures/appearance/outfit/outfit.hpp index 30b84d1aca2..0d89a2c932d 100644 --- a/src/creatures/appearance/outfit/outfit.hpp +++ b/src/creatures/appearance/outfit/outfit.hpp @@ -12,8 +12,10 @@ #include "declarations.hpp" #include "lib/di/container.hpp" +class Player; + struct OutfitEntry { - constexpr OutfitEntry(uint16_t initLookType, uint8_t initAddons) : + constexpr explicit OutfitEntry(uint16_t initLookType, uint8_t initAddons) : lookType(initLookType), addons(initAddons) { } uint16_t lookType; @@ -42,16 +44,12 @@ struct ProtocolOutfit { class Outfits { public: - static Outfits &getInstance() { - return inject<Outfits>(); - } - - std::shared_ptr<Outfit> getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; + static Outfits &getInstance(); - bool loadFromXml(); bool reload(); + bool loadFromXml(); - [[nodiscard]] std::shared_ptr<Outfit> getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; + [[nodiscard]] std::shared_ptr<Outfit> getOutfitByLookType(const std::shared_ptr<const Player> &player, uint16_t lookType, bool isOppositeOutfit = false) const; [[nodiscard]] const std::vector<std::shared_ptr<Outfit>> &getOutfits(PlayerSex_t sex) const { return outfits[sex]; } diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp index a922013f19b..6e923b823dc 100644 --- a/src/creatures/players/grouping/familiars.cpp +++ b/src/creatures/players/grouping/familiars.cpp @@ -10,10 +10,15 @@ #include "pch.hpp" #include "creatures/players/grouping/familiars.hpp" +#include "lib/di/container.hpp" #include "config/configmanager.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" +Familiars &Familiars::getInstance() { + return inject<Familiars>(); +} + bool Familiars::reload() { for (auto &familiarsVector : familiars) { familiarsVector.clear(); @@ -42,7 +47,7 @@ bool Familiars::loadFromXml() { continue; } - uint16_t vocation = pugi::cast<uint16_t>(attr.value()); + auto vocation = pugi::cast<uint16_t>(attr.value()); if (vocation > VOCATION_LAST) { g_logger().warn("[Familiars::loadFromXml] - Invalid familiar vocation {}", vocation); continue; diff --git a/src/creatures/players/grouping/familiars.hpp b/src/creatures/players/grouping/familiars.hpp index 8f553af9fbe..9eda7d95ba2 100644 --- a/src/creatures/players/grouping/familiars.hpp +++ b/src/creatures/players/grouping/familiars.hpp @@ -9,6 +9,7 @@ #pragma once +#include "declarations.hpp" #include "lib/di/container.hpp" struct FamiliarEntry { @@ -32,9 +33,7 @@ struct Familiar { class Familiars { public: - static Familiars &getInstance() { - return inject<Familiars>(); - } + static Familiars &getInstance(); bool loadFromXml(); bool reload(); diff --git a/src/creatures/players/grouping/groups.cpp b/src/creatures/players/grouping/groups.cpp index 27ae68d9fd4..937fa848f3f 100644 --- a/src/creatures/players/grouping/groups.cpp +++ b/src/creatures/players/grouping/groups.cpp @@ -43,7 +43,7 @@ PlayerFlags_t Groups::getFlagFromNumber(uint8_t value) { return magic_enum::enum_value<PlayerFlags_t>(value); } -bool Groups::reload() const { +bool Groups::reload() { // Clear groups g_game().groups.getGroups().clear(); return g_game().groups.load(); @@ -99,7 +99,7 @@ bool Groups::load() { return true; } -std::shared_ptr<Group> Groups::getGroup(uint16_t id) { +std::shared_ptr<Group> Groups::getGroup(uint16_t id) const { if (auto it = std::find_if(groups_vector.begin(), groups_vector.end(), [id](auto group_it) { return group_it->id == id; }); diff --git a/src/creatures/players/grouping/groups.hpp b/src/creatures/players/grouping/groups.hpp index d76914e2632..af319e95772 100644 --- a/src/creatures/players/grouping/groups.hpp +++ b/src/creatures/players/grouping/groups.hpp @@ -24,9 +24,9 @@ class Groups { public: static uint8_t getFlagNumber(PlayerFlags_t playerFlags); static PlayerFlags_t getFlagFromNumber(uint8_t value); - bool reload() const; + static bool reload(); bool load(); - std::shared_ptr<Group> getGroup(uint16_t id); + [[nodiscard]] std::shared_ptr<Group> getGroup(uint16_t id) const; std::vector<std::shared_ptr<Group>> &getGroups() { return groups_vector; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 4109eb77472..2b10c0f77ee 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -4914,7 +4914,7 @@ bool Player::canWear(uint16_t lookType, uint8_t addons) const { return true; } - const auto &outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType); + const auto &outfit = Outfits::getInstance().getOutfitByLookType(getPlayer(), lookType); if (!outfit) { return false; } @@ -5001,7 +5001,7 @@ bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons) { return false; } -bool Player::getOutfitAddons(const std::shared_ptr<Outfit> outfit, uint8_t &addons) const { +bool Player::getOutfitAddons(const std::shared_ptr<Outfit> &outfit, uint8_t &addons) const { if (group->access) { addons = 3; return true; @@ -5826,7 +5826,7 @@ bool Player::toggleMount(bool mount) { return false; } - const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType); + const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(getPlayer(), defaultOutfit.lookType); if (!playerOutfit) { return false; } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index c0761704db4..19e3af5ca9c 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -476,7 +476,12 @@ class Player final : public Creature, public Cylinder, public Bankable { return blessings[index - 1] != 0; } uint8_t getBlessingCount(uint8_t index) const { - return blessings[index - 1]; + if (index > 0 && index <= blessings.size()) { + return blessings[index - 1]; + } else { + g_logger().error("[{}] - index outside range 0-10.", __FUNCTION__); + return 0; + } } std::string getBlessingsName() const; @@ -1031,7 +1036,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void addOutfit(uint16_t lookType, uint8_t addons); bool removeOutfit(uint16_t lookType); bool removeOutfitAddon(uint16_t lookType, uint8_t addons); - bool getOutfitAddons(const std::shared_ptr<Outfit> outfit, uint8_t &addons) const; + bool getOutfitAddons(const std::shared_ptr<Outfit> &outfit, uint8_t &addons) const; bool canFamiliar(uint16_t lookType) const; void addFamiliar(uint16_t lookType); @@ -2853,7 +2858,7 @@ class Player final : public Creature, public Cylinder, public Bankable { uint16_t lastStatsTrainingTime = 0; uint16_t staminaMinutes = 2520; - std::vector<uint8_t> blessings = { 0, 0, 0, 0, 0, 0, 0, 0 }; + std::vector<uint8_t> blessings = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint16_t maxWriteLen = 0; uint16_t baseXpGain = 100; uint16_t voucherXpBoost = 0; diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index 0fc74bcee71..d9d9eb1f449 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -22,7 +22,7 @@ GameReload::GameReload() = default; GameReload::~GameReload() = default; -bool GameReload::init(Reload_t reloadTypes) const { +bool GameReload::init(Reload_t reloadTypes) { switch (reloadTypes) { case Reload_t::RELOAD_TYPE_ALL: return reloadAll(); @@ -32,34 +32,38 @@ bool GameReload::init(Reload_t reloadTypes) const { return reloadConfig(); case Reload_t::RELOAD_TYPE_EVENTS: return reloadEvents(); - case Reload_t::RELOAD_TYPE_CORE: - return reloadCore(); + case Reload_t::RELOAD_TYPE_MODULES: + return reloadModules(); + case Reload_t::RELOAD_TYPE_OUTFITS: + return reloadOutfits(); + case Reload_t::RELOAD_TYPE_MOUNTS: + return reloadMounts(); + case Reload_t::RELOAD_TYPE_FAMILIARS: + return reloadFamiliars(); case Reload_t::RELOAD_TYPE_IMBUEMENTS: return reloadImbuements(); + case Reload_t::RELOAD_TYPE_VOCATIONS: + return reloadVocations(); + case Reload_t::RELOAD_TYPE_CORE: + return reloadCore(); + case Reload_t::RELOAD_TYPE_GROUPS: + return reloadGroups(); + case Reload_t::RELOAD_TYPE_SCRIPTS: + return reloadScripts(); case Reload_t::RELOAD_TYPE_ITEMS: return reloadItems(); - case Reload_t::RELOAD_TYPE_MODULES: - return reloadModules(); case Reload_t::RELOAD_TYPE_MONSTERS: return reloadMonsters(); - case Reload_t::RELOAD_TYPE_MOUNTS: - return reloadMounts(); case Reload_t::RELOAD_TYPE_NPCS: return reloadNpcs(); case Reload_t::RELOAD_TYPE_RAIDS: return reloadRaids(); - case Reload_t::RELOAD_TYPE_SCRIPTS: - return reloadScripts(); - case Reload_t::RELOAD_TYPE_GROUPS: - return reloadGroups(); - case Reload_t::RELOAD_TYPE_VOCATIONS: - return reloadVocations(); default: return false; } } -uint8_t GameReload::getReloadNumber(Reload_t reloadTypes) const { +uint8_t GameReload::getReloadNumber(Reload_t reloadTypes) { return magic_enum::enum_integer(reloadTypes); } @@ -78,7 +82,7 @@ void logReloadStatus(const std::string &name, bool result) { * If it is necessary to call elsewhere, seriously think about creating a function that calls this * Changing this to public may cause some unexpected behavior or bug */ -bool GameReload::reloadAll() const { +bool GameReload::reloadAll() { std::vector<bool> reloadResults; reloadResults.reserve(magic_enum::enum_count<Reload_t>()); @@ -93,25 +97,62 @@ bool GameReload::reloadAll() const { return std::ranges::any_of(reloadResults, [](bool result) { return result; }); } -bool GameReload::reloadChat() const { +bool GameReload::reloadChat() { const bool result = g_chat().load(); logReloadStatus("Chat", result); return result; } -bool GameReload::reloadConfig() const { +bool GameReload::reloadConfig() { const bool result = g_configManager().reload(); logReloadStatus("Config", result); return result; } -bool GameReload::reloadEvents() const { +bool GameReload::reloadEvents() { const bool result = g_events().loadFromXml(); logReloadStatus("Events", result); return result; } -bool GameReload::reloadCore() const { +bool GameReload::reloadModules() { + const bool result = g_modules().reload(); + logReloadStatus("Modules", result); + return result; +} + +bool GameReload::reloadOutfits() { + const bool result = g_game().outfits.reload(); + logReloadStatus("Outfits", result); + return result; +} + +bool GameReload::reloadMounts() { + const bool result = g_game().mounts.reload(); + logReloadStatus("Mounts", result); + return result; +} + +bool GameReload::reloadFamiliars() { + const bool result = g_game().familiars.reload(); + logReloadStatus("Familiars", result); + return result; +} + +bool GameReload::reloadImbuements() { + const bool result = g_imbuements().reload(); + logReloadStatus("Imbuements", result); + return result; +} + +bool GameReload::reloadVocations() { + const bool result = g_vocations().reload(); + reloadScripts(); + logReloadStatus("Vocations", result); + return result; +} + +bool GameReload::reloadCore() { const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); const bool coreLoaded = g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0; @@ -126,25 +167,38 @@ bool GameReload::reloadCore() const { return false; } -bool GameReload::reloadImbuements() const { - const bool result = g_imbuements().reload(); - logReloadStatus("Imbuements", result); +bool GameReload::reloadGroups() { + const bool result = g_game().groups.reload(); + logReloadStatus("Groups", result); return result; } -bool GameReload::reloadItems() const { - const bool result = Item::items.reload(); - logReloadStatus("Items", result); - return result; +bool GameReload::reloadScripts() { + g_scripts().clearAllScripts(); + Zone::clearZones(); + + const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); + const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); + + g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false); + g_scripts().loadScripts(datapackFolder + "/scripts", false, true); + g_scripts().loadScripts(coreFolder + "/scripts", false, true); + + // It should come last, after everything else has been cleaned up. + reloadMonsters(); + reloadNpcs(); + reloadItems(); + logReloadStatus("Scripts", true); + return true; } -bool GameReload::reloadModules() const { - const bool result = g_modules().reload(); - logReloadStatus("Modules", result); +bool GameReload::reloadItems() { + const bool result = Item::items.reload(); + logReloadStatus("Items", result); return result; } -bool GameReload::reloadMonsters() const { +bool GameReload::reloadMonsters() { g_monsters().clear(); const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); @@ -161,50 +215,14 @@ bool GameReload::reloadMonsters() const { } } -bool GameReload::reloadMounts() const { - const bool result = g_game().mounts.reload(); - logReloadStatus("Mounts", result); - return result; -} - -bool GameReload::reloadNpcs() const { +bool GameReload::reloadNpcs() { const bool result = g_npcs().reload(); logReloadStatus("NPCs", result); return result; } -bool GameReload::reloadRaids() const { +bool GameReload::reloadRaids() { const bool result = g_game().raids.reload() && g_game().raids.startup(); logReloadStatus("Raids", result); return result; } - -bool GameReload::reloadScripts() const { - g_scripts().clearAllScripts(); - Zone::clearZones(); - - const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); - const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); - - g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false); - g_scripts().loadScripts(datapackFolder + "/scripts", false, true); - g_scripts().loadScripts(coreFolder + "/scripts", false, true); - - // It should come last, after everything else has been cleaned up. - reloadMonsters(); - reloadNpcs(); - logReloadStatus("Scripts", true); - return true; -} - -bool GameReload::reloadGroups() const { - const bool result = g_game().groups.reload(); - logReloadStatus("Groups", result); - return result; -} - -bool GameReload::reloadVocations() const { - const bool result = g_vocations().reload(); - logReloadStatus("Vocations", result); - return result; -} diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp index 45ff35c01a2..0f59046a9d4 100644 --- a/src/game/functions/game_reload.hpp +++ b/src/game/functions/game_reload.hpp @@ -19,17 +19,19 @@ enum class Reload_t : uint8_t { RELOAD_TYPE_CHAT, RELOAD_TYPE_CONFIG, RELOAD_TYPE_EVENTS, - RELOAD_TYPE_CORE, + RELOAD_TYPE_MODULES, + RELOAD_TYPE_OUTFITS, + RELOAD_TYPE_MOUNTS, + RELOAD_TYPE_FAMILIARS, RELOAD_TYPE_IMBUEMENTS, + RELOAD_TYPE_VOCATIONS, + RELOAD_TYPE_CORE, + RELOAD_TYPE_GROUPS, + RELOAD_TYPE_SCRIPTS, RELOAD_TYPE_ITEMS, - RELOAD_TYPE_MODULES, RELOAD_TYPE_MONSTERS, - RELOAD_TYPE_MOUNTS, RELOAD_TYPE_NPCS, RELOAD_TYPE_RAIDS, - RELOAD_TYPE_SCRIPTS, - RELOAD_TYPE_GROUPS, - RELOAD_TYPE_VOCATIONS, // Every is last RELOAD_TYPE_LAST @@ -48,25 +50,27 @@ class GameReload : public Game { return inject<GameReload>(); } - bool init(Reload_t reloadType) const; - uint8_t getReloadNumber(Reload_t reloadTypes) const; + static bool init(Reload_t reloadType); + static uint8_t getReloadNumber(Reload_t reloadTypes); private: - bool reloadAll() const; - bool reloadChat() const; - bool reloadConfig() const; - bool reloadEvents() const; - bool reloadCore() const; - bool reloadImbuements() const; - bool reloadItems() const; - bool reloadModules() const; - bool reloadMonsters() const; - bool reloadMounts() const; - bool reloadNpcs() const; - bool reloadRaids() const; - bool reloadScripts() const; - bool reloadGroups() const; - bool reloadVocations() const; + static bool reloadAll(); + static bool reloadChat(); + static bool reloadConfig(); + static bool reloadEvents(); + static bool reloadModules(); + static bool reloadOutfits(); + static bool reloadMounts(); + static bool reloadFamiliars(); + static bool reloadImbuements(); + static bool reloadVocations(); + static bool reloadCore(); + static bool reloadGroups(); + static bool reloadScripts(); + static bool reloadItems(); + static bool reloadMonsters(); + static bool reloadNpcs(); + static bool reloadRaids(); }; constexpr auto g_gameReload = GameReload::getInstance; diff --git a/src/game/game.cpp b/src/game/game.cpp index fed3a3910a1..57daddded2e 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4261,7 +4261,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos name << item->getName() << " displaying the "; bool outfited = false; if (outfit.lookType != 0) { - const auto &outfitInfo = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); + const auto &outfitInfo = Outfits::getInstance().getOutfitByLookType(player, outfit.lookType); if (!outfitInfo) { return; } @@ -5927,7 +5927,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun outfit.lookMount = randomMount->clientId; } - const auto playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); + const auto playerOutfit = Outfits::getInstance().getOutfitByLookType(player, outfit.lookType); if (!playerOutfit) { outfit.lookMount = 0; } diff --git a/src/game/game.hpp b/src/game/game.hpp index 3a199254259..b4be8facd09 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -582,8 +582,10 @@ class Game { bool hasDistanceEffect(uint16_t effectId); Groups groups; + Familiars familiars; Map map; Mounts mounts; + Outfits outfits; Raids raids; std::unique_ptr<Canary::protobuf::appearances::Appearances> m_appearancesPtr; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 6273f8fefbb..8f7cf461fdf 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -66,7 +66,7 @@ bool IOLoginDataLoad::preLoadPlayer(std::shared_ptr<Player> player, const std::s } player->setGUID(result->getNumber<uint32_t>("id")); - std::shared_ptr<Group> group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); + const auto &group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); if (!group) { g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber<uint16_t>("group_id")); return false; @@ -118,7 +118,7 @@ bool IOLoginDataLoad::loadPlayerFirst(std::shared_ptr<Player> player, DBResult_p player->setAccount(result->getNumber<uint32_t>("account_id")); } - std::shared_ptr<Group> group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); + const auto &group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id")); if (!group) { g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber<uint16_t>("group_id")); return false; diff --git a/src/lua/functions/creatures/player/group_functions.cpp b/src/lua/functions/creatures/player/group_functions.cpp index 5acc16d72a9..f547eec1590 100644 --- a/src/lua/functions/creatures/player/group_functions.cpp +++ b/src/lua/functions/creatures/player/group_functions.cpp @@ -17,7 +17,7 @@ int GroupFunctions::luaGroupCreate(lua_State* L) { // Group(id) uint32_t id = getNumber<uint32_t>(L, 2); - std::shared_ptr<Group> group = g_game().groups.getGroup(id); + const auto &group = g_game().groups.getGroup(id); if (group) { pushUserdata<Group>(L, group); setMetatable(L, -1, "Group"); @@ -29,7 +29,7 @@ int GroupFunctions::luaGroupCreate(lua_State* L) { int GroupFunctions::luaGroupGetId(lua_State* L) { // group:getId() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->id); } else { @@ -40,7 +40,7 @@ int GroupFunctions::luaGroupGetId(lua_State* L) { int GroupFunctions::luaGroupGetName(lua_State* L) { // group:getName() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { pushString(L, group->name); } else { @@ -51,7 +51,7 @@ int GroupFunctions::luaGroupGetName(lua_State* L) { int GroupFunctions::luaGroupGetFlags(lua_State* L) { // group:getFlags() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { std::bitset<magic_enum::enum_integer(PlayerFlags_t::FlagLast)> flags; for (uint8_t i = 0; i < magic_enum::enum_integer(PlayerFlags_t::FlagLast); ++i) { @@ -68,7 +68,7 @@ int GroupFunctions::luaGroupGetFlags(lua_State* L) { int GroupFunctions::luaGroupGetAccess(lua_State* L) { // group:getAccess() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { pushBoolean(L, group->access); } else { @@ -79,7 +79,7 @@ int GroupFunctions::luaGroupGetAccess(lua_State* L) { int GroupFunctions::luaGroupGetMaxDepotItems(lua_State* L) { // group:getMaxDepotItems() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->maxDepotItems); } else { @@ -90,7 +90,7 @@ int GroupFunctions::luaGroupGetMaxDepotItems(lua_State* L) { int GroupFunctions::luaGroupGetMaxVipEntries(lua_State* L) { // group:getMaxVipEntries() - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { lua_pushnumber(L, group->maxVipEntries); } else { @@ -101,7 +101,7 @@ int GroupFunctions::luaGroupGetMaxVipEntries(lua_State* L) { int GroupFunctions::luaGroupHasFlag(lua_State* L) { // group:hasFlag(flag) - std::shared_ptr<Group> group = getUserdataShared<Group>(L, 1); + const auto &group = getUserdataShared<Group>(L, 1); if (group) { auto flag = static_cast<PlayerFlags_t>(getNumber<int>(L, 2)); pushBoolean(L, group->flags[Groups::getFlagNumber(flag)]); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 751da531273..98bacfc5e12 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -4055,7 +4055,7 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { // Outfit description playerDescriptionSize++; msg.addString("Outfit", "ProtocolGame::sendCyclopediaCharacterInspection - Outfit"); - if (const auto outfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), player->getDefaultOutfit().lookType)) { + if (const auto outfit = Outfits::getInstance().getOutfitByLookType(player, player->getDefaultOutfit().lookType)) { msg.addString(outfit->name, "ProtocolGame::sendCyclopediaCharacterInspection - outfit->name"); } else { msg.addString("unknown", "ProtocolGame::sendCyclopediaCharacterInspection - unknown"); From e20e7eb68b24eafdbc13b1c3c72928ee8caf3ee1 Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Wed, 12 Jun 2024 10:43:24 -0300 Subject: [PATCH 73/95] feat: monster rename functionality (#2621) --- ...{create_monster.lua => manage_monster.lua} | 32 +++++++++++++++++- src/creatures/monsters/monster.cpp | 33 ++++++++++++++++++- src/creatures/monsters/monster.hpp | 20 ++++++----- src/creatures/players/player.hpp | 9 +++-- .../creatures/monster/monster_functions.cpp | 20 ++++++++++- .../creatures/monster/monster_functions.hpp | 2 ++ src/server/network/protocol/protocolgame.cpp | 33 ++++++++++++++----- src/server/network/protocol/protocolgame.hpp | 1 + 8 files changed, 128 insertions(+), 22 deletions(-) rename data/scripts/talkactions/god/{create_monster.lua => manage_monster.lua} (83%) diff --git a/data/scripts/talkactions/god/create_monster.lua b/data/scripts/talkactions/god/manage_monster.lua similarity index 83% rename from data/scripts/talkactions/god/create_monster.lua rename to data/scripts/talkactions/god/manage_monster.lua index ce5869a2eee..da110d59265 100644 --- a/data/scripts/talkactions/god/create_monster.lua +++ b/data/scripts/talkactions/god/manage_monster.lua @@ -56,7 +56,7 @@ local createMonster = TalkAction("/m") -- @param param: String containing the command parameters. -- Format: "/m monstername, monstercount, [fiendish/influenced level], spawnRadius, [forceCreate]" -- Example: "/m rat, 10, fiendish, 5, true" --- @param: the last param is by default "false", if add "," or any value i'ts set to true +-- @param: the last param is by default "false", if add "," or any value it's set to true -- @return true if the command is executed successfully, false otherwise. function createMonster.onSay(player, words, param) -- create log @@ -127,3 +127,33 @@ end createMonster:separator(" ") createMonster:groupType("god") createMonster:register() + +----------------- Rename monster name ----------------- +local setMonsterName = TalkAction("/setmonstername") + +-- @function setMonsterName.onSay +-- @desc TalkAction to rename nearby monsters within a radius of 4 sqm. +-- Format: "/setmonstername newName" +function setMonsterName.onSay(player, words, param) + if param == "" then + player:sendCancelMessage("Command param required.") + return true + end + + local split = param:split(",") + local monsterNewName = split[1] + + local spectators, spectator = Game.getSpectators(player:getPosition(), false, false, 4, 4, 4, 4) + for i = 1, #spectators do + spectator = spectators[i] + if spectator:isMonster() then + spectator:setName(monsterNewName) + end + end + + return true +end + +setMonsterName:separator(" ") +setMonsterName:groupType("god") +setMonsterName:register() diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index fccc19b05e5..5e0a7d16bb4 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -33,7 +33,7 @@ std::shared_ptr<Monster> Monster::createMonster(const std::string &name) { Monster::Monster(const std::shared_ptr<MonsterType> mType) : Creature(), - strDescription(asLowerCaseString(mType->nameDescription)), + nameDescription(asLowerCaseString(mType->nameDescription)), mType(mType) { defaultOutfit = mType->info.outfit; currentOutfit = mType->info.outfit; @@ -64,6 +64,37 @@ void Monster::removeList() { g_game().removeMonster(static_self_cast<Monster>()); } +const std::string &Monster::getName() const { + if (name.empty()) { + return mType->name; + } + return name; +} + +void Monster::setName(const std::string &name) { + if (getName() == name) { + return; + } + + this->name = name; + + // NOTE: Due to how client caches known creatures, + // it is not feasible to send creature update to everyone that has ever met it + auto spectators = Spectators().find<Player>(position, true); + for (const auto &spectator : spectators) { + if (const auto &tmpPlayer = spectator->getPlayer()) { + tmpPlayer->sendUpdateTileCreature(static_self_cast<Monster>()); + } + } +} + +const std::string &Monster::getNameDescription() const { + if (nameDescription.empty()) { + return mType->nameDescription; + } + return nameDescription; +} + bool Monster::canWalkOnFieldType(CombatType_t combatType) const { switch (combatType) { case COMBAT_ENERGYDAMAGE: diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index bf9a39e4133..6c45fa3eaca 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -41,21 +41,22 @@ class Monster final : public Creature { } } - void removeList() override; void addList() override; + void removeList() override; + + const std::string &getName() const override; + void setName(const std::string &name); - const std::string &getName() const override { - return mType->name; - } // Real monster name, set on monster creation "createMonsterType(typeName)" const std::string &getTypeName() const override { return mType->typeName; } - const std::string &getNameDescription() const override { - return mType->nameDescription; - } + const std::string &getNameDescription() const override; + void setNameDescription(const std::string &nameDescription) { + this->nameDescription = nameDescription; + }; std::string getDescription(int32_t) override { - return strDescription + '.'; + return nameDescription + '.'; } CreatureType_t getType() const override { @@ -363,7 +364,8 @@ class Monster final : public Creature { uint16_t forgeStack = 0; ForgeClassifications_t monsterForgeClassification = ForgeClassifications_t::FORGE_NORMAL_MONSTER; - std::string strDescription; + std::string name; + std::string nameDescription; std::shared_ptr<MonsterType> mType; SpawnMonster* spawnMonster = nullptr; diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 19e3af5ca9c..947acedca54 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -153,8 +153,8 @@ class Player final : public Creature, public Cylinder, public Bankable { const std::string &getName() const override { return name; } - void setName(std::string newName) { - this->name = std::move(newName); + void setName(const std::string &name) { + this->name = name; } const std::string &getTypeName() const override { return name; @@ -1075,6 +1075,11 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendRemoveTileThing(pos, stackpos); } } + void sendUpdateTileCreature(const std::shared_ptr<Creature> creature) { + if (client) { + client->sendUpdateTileCreature(creature->getPosition(), creature->getTile()->getClientIndexOfCreature(static_self_cast<Player>(), creature), creature); + } + } void sendUpdateTile(std::shared_ptr<Tile> updateTile, const Position &pos) { if (client) { client->sendUpdateTile(updateTile, pos); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 1591053c391..273f6af5c99 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -77,7 +77,7 @@ int MonsterFunctions::luaMonsterSetType(lua_State* L) { } // Assign new MonsterType monster->mType = mType; - monster->strDescription = asLowerCaseString(mType->nameDescription); + monster->nameDescription = asLowerCaseString(mType->nameDescription); monster->defaultOutfit = mType->info.outfit; monster->currentOutfit = mType->info.outfit; monster->skull = mType->info.skull; @@ -529,6 +529,24 @@ int MonsterFunctions::luaMonsterGetName(lua_State* L) { return 1; } +int MonsterFunctions::luaMonsterSetName(lua_State* L) { + // monster:setName(name[, nameDescription]) + auto monster = getUserdataShared<Monster>(L, 1); + if (!monster) { + reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_NOT_FOUND)); + pushBoolean(L, false); + return 0; + } + + monster->setName(getString(L, 2)); + if (lua_gettop(L) >= 3) { + monster->setNameDescription(getString(L, 3)); + } + + pushBoolean(L, true); + return 1; +} + int MonsterFunctions::luaMonsterHazard(lua_State* L) { // get: monster:hazard() ; set: monster:hazard(hazard) std::shared_ptr<Monster> monster = getUserdataShared<Monster>(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index bf2785ae430..dd1c3827344 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -56,6 +56,7 @@ class MonsterFunctions final : LuaScriptInterface { registerMethod(L, "Monster", "isForgeable", MonsterFunctions::luaMonsterIsForgeable); registerMethod(L, "Monster", "getName", MonsterFunctions::luaMonsterGetName); + registerMethod(L, "Monster", "setName", MonsterFunctions::luaMonsterSetName); registerMethod(L, "Monster", "hazard", MonsterFunctions::luaMonsterHazard); registerMethod(L, "Monster", "hazardCrit", MonsterFunctions::luaMonsterHazardCrit); @@ -116,6 +117,7 @@ class MonsterFunctions final : LuaScriptInterface { static int luaMonsterIsForgeable(lua_State* L); static int luaMonsterGetName(lua_State* L); + static int luaMonsterSetName(lua_State* L); static int luaMonsterHazard(lua_State* L); static int luaMonsterHazardCrit(lua_State* L); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 98bacfc5e12..8cf0660a262 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5947,7 +5947,7 @@ void ProtocolGame::sendCreatureTurn(std::shared_ptr<Creature> creature, uint32_t NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(creature->getPosition()); - msg.addByte(stackPos); + msg.addByte(static_cast<uint8_t>(stackPos)); msg.add<uint16_t>(0x63); msg.add<uint32_t>(creature->getID()); msg.addByte(creature->getDirection()); @@ -6385,7 +6385,7 @@ void ProtocolGame::sendAddTileItem(const Position &pos, uint32_t stackpos, std:: NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); - msg.addByte(stackpos); + msg.addByte(static_cast<uint8_t>(stackpos)); AddItem(msg, item); writeToOutputBuffer(msg); } @@ -6398,7 +6398,7 @@ void ProtocolGame::sendUpdateTileItem(const Position &pos, uint32_t stackpos, st NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(pos); - msg.addByte(stackpos); + msg.addByte(static_cast<uint8_t>(stackpos)); AddItem(msg, item); writeToOutputBuffer(msg); } @@ -6413,6 +6413,23 @@ void ProtocolGame::sendRemoveTileThing(const Position &pos, uint32_t stackpos) { writeToOutputBuffer(msg); } +void ProtocolGame::sendUpdateTileCreature(const Position &pos, uint32_t stackpos, const std::shared_ptr<Creature> creature) { + if (!canSee(pos)) { + return; + } + + NetworkMessage msg; + msg.addByte(0x6B); + msg.addPosition(pos); + msg.addByte(static_cast<uint8_t>(stackpos)); + + bool known; + uint32_t removedKnown; + checkCreatureAsKnown(creature->getID(), known, removedKnown); + AddCreature(msg, creature, false, removedKnown); + writeToOutputBuffer(msg); +} + void ProtocolGame::sendUpdateTile(std::shared_ptr<Tile> tile, const Position &pos) { if (!canSee(pos)) { return; @@ -6484,7 +6501,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); - msg.addByte(stackpos); + msg.addByte(static_cast<uint8_t>(stackpos)); bool known; uint32_t removedKnown; @@ -6641,7 +6658,7 @@ void ProtocolGame::sendMoveCreature(std::shared_ptr<Creature> creature, const Po } else { msg.addByte(0x6D); msg.addPosition(oldPos); - msg.addByte(oldStackPos); + msg.addByte(static_cast<uint8_t>(oldStackPos)); msg.addPosition(newPos); } @@ -6676,7 +6693,7 @@ void ProtocolGame::sendMoveCreature(std::shared_ptr<Creature> creature, const Po NetworkMessage msg; msg.addByte(0x6D); msg.addPosition(oldPos); - msg.addByte(oldStackPos); + msg.addByte(static_cast<uint8_t>(oldStackPos)); msg.addPosition(newPos); writeToOutputBuffer(msg); } @@ -7836,7 +7853,7 @@ void ProtocolGame::RemoveTileThing(NetworkMessage &msg, const Position &pos, uin msg.addByte(0x6C); msg.addPosition(pos); - msg.addByte(stackpos); + msg.addByte(static_cast<uint8_t>(stackpos)); } void ProtocolGame::sendKillTrackerUpdate(std::shared_ptr<Container> corpse, const std::string &name, const Outfit_t creatureOutfit) { @@ -8279,7 +8296,7 @@ void ProtocolGame::reloadCreature(std::shared_ptr<Creature> creature) { if (knownCreatureSet.contains(creature->getID())) { msg.addByte(0x6B); msg.addPosition(creature->getPosition()); - msg.addByte(stackpos); + msg.addByte(static_cast<uint8_t>(stackpos)); AddCreature(msg, creature, false, 0); } else { sendAddCreature(creature, creature->getPosition(), stackpos, false); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 0386093cfc4..100ca199b77 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -393,6 +393,7 @@ class ProtocolGame final : public Protocol { void sendAddTileItem(const Position &pos, uint32_t stackpos, std::shared_ptr<Item> item); void sendUpdateTileItem(const Position &pos, uint32_t stackpos, std::shared_ptr<Item> item); void sendRemoveTileThing(const Position &pos, uint32_t stackpos); + void sendUpdateTileCreature(const Position &pos, uint32_t stackpos, const std::shared_ptr<Creature> creature); void sendUpdateTile(std::shared_ptr<Tile> tile, const Position &pos); void sendAddCreature(std::shared_ptr<Creature> creature, const Position &pos, int32_t stackpos, bool isLogin); From 7c3393a1015ff4d8c1f43c5988b33f489302e20f Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Wed, 12 Jun 2024 10:48:41 -0300 Subject: [PATCH 74/95] fix: crash on shutdown (#2676) --- src/lib/thread/thread_pool.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/thread/thread_pool.cpp b/src/lib/thread/thread_pool.cpp index 702f0c05504..b2f51ef8f9d 100644 --- a/src/lib/thread/thread_pool.cpp +++ b/src/lib/thread/thread_pool.cpp @@ -26,7 +26,7 @@ #endif ThreadPool::ThreadPool(Logger &logger) : - logger(logger), BS::thread_pool(std::max<int>(getNumberOfCores(), DEFAULT_NUMBER_OF_THREADS)) { + BS::thread_pool(std::max<int>(getNumberOfCores(), DEFAULT_NUMBER_OF_THREADS)), logger(logger) { start(); } @@ -35,6 +35,7 @@ void ThreadPool::start() { } void ThreadPool::shutdown() { - stopped = true; logger.info("Shutting down thread pool..."); + stopped = true; + wait(); } From f741bebc0bbd4fb04bdd5948b551f7f5ec91064a Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Thu, 13 Jun 2024 14:14:10 -0300 Subject: [PATCH 75/95] improver: mini-refactor inventory update (#2652) --- src/creatures/players/player.cpp | 36 +++++++++++++++++++------------- src/creatures/players/player.hpp | 3 +++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 2b10c0f77ee..2c92f694197 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1715,13 +1715,7 @@ void Player::onCreatureAppear(std::shared_ptr<Creature> creature, bool isLogin) Creature::onCreatureAppear(creature, isLogin); if (isLogin && creature == getPlayer()) { - for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { - std::shared_ptr<Item> item = inventory[slot]; - if (item) { - item->startDecaying(); - g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast<Slots_t>(slot), false); - } - } + onEquipInventory(); // Refresh bosstiary tracker onLogin refreshCyclopediaMonsterTracker(true); @@ -1862,14 +1856,9 @@ void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) Creature::onRemoveCreature(creature, isLogout); if (auto player = getPlayer(); player == creature) { - for (uint8_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { - const auto item = inventory[slot]; - if (item) { - g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast<Slots_t>(slot)); - } - } - if (isLogout) { + onDeEquipInventory(); + if (m_party) { m_party->leaveParty(player); } @@ -2011,6 +2000,25 @@ void Player::onCreatureMove(const std::shared_ptr<Creature> &creature, const std } } +void Player::onEquipInventory() { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + std::shared_ptr<Item> item = inventory[slot]; + if (item) { + item->startDecaying(); + g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast<Slots_t>(slot), false); + } + } +} + +void Player::onDeEquipInventory() { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + std::shared_ptr<Item> item = inventory[slot]; + if (item) { + g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast<Slots_t>(slot)); + } + } +} + // container void Player::onAddContainerItem(std::shared_ptr<Item> item) { checkTradeState(item); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 947acedca54..d374bd59911 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1315,6 +1315,9 @@ class Player final : public Creature, public Cylinder, public Bankable { void onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) override; void onCreatureMove(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, const Position &newPos, const std::shared_ptr<Tile> &oldTile, const Position &oldPos, bool teleport) override; + void onEquipInventory(); + void onDeEquipInventory(); + void onAttackedCreatureDisappear(bool isLogout) override; void onFollowCreatureDisappear(bool isLogout) override; From e1fd98e010c35471094a9a6f12c0e3af7e08f8a5 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Fri, 14 Jun 2024 21:25:21 -0300 Subject: [PATCH 76/95] improve: async batch dispatcher (#2666) Now the asynchronous requests will be made in batches and not one at a time, that is, we will take the total number of tasks divided by the number of available threads, this avoids locking the threads to retrieve the next task to be processed. ex: if we have 1000 tasks and 8 threads, 125 tasks will be triggered per thread. (1000/8) = 125 In this PR I also brought a new method called asyncWait, with this method you can make asynchronous requests but without needing to define a group, the asynchronous tasks will be executed immediately and will wait for them to finish. --- src/game/scheduling/dispatcher.cpp | 55 +++++++++++++++++++++--------- src/game/scheduling/dispatcher.hpp | 19 +++++++++++ 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index 7cff69d66bf..dbbfc020be5 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -56,30 +56,51 @@ void Dispatcher::executeSerialEvents(std::vector<Task> &tasks) { } void Dispatcher::executeParallelEvents(std::vector<Task> &tasks, const uint8_t groupId) { - std::atomic_uint_fast64_t totalTaskSize = tasks.size(); - std::atomic_bool isTasksCompleted = false; + asyncWait(tasks.size(), [groupId, &tasks](size_t i) { + dispacherContext.type = DispatcherType::AsyncEvent; + dispacherContext.group = static_cast<TaskGroup>(groupId); + tasks[i].execute(); - for (const auto &task : tasks) { - threadPool.detach_task([groupId, &task, &isTasksCompleted, &totalTaskSize] { - dispacherContext.type = DispatcherType::AsyncEvent; - dispacherContext.group = static_cast<TaskGroup>(groupId); - dispacherContext.taskName = task.getContext(); + dispacherContext.reset(); + }); - task.execute(); + tasks.clear(); +} - dispacherContext.reset(); +void Dispatcher::asyncWait(size_t requestSize, std::function<void(size_t i)> &&f) { + if (requestSize == 0) { + return; + } - totalTaskSize.fetch_sub(1); - if (totalTaskSize.load() == 0) { - isTasksCompleted.store(true); - isTasksCompleted.notify_one(); - } - }); + // This prevents an async call from running inside another async call. + if (asyncWaitDisabled) { + for (uint_fast64_t i = 0; i < requestSize; ++i) { + f(i); + } + return; } - isTasksCompleted.wait(false); + const auto &partitions = generatePartition(requestSize); + const auto pSize = partitions.size(); - tasks.clear(); + BS::multi_future<void> retFuture; + + if (pSize > 1) { + asyncWaitDisabled = true; + const auto min = partitions[1].first; + const auto max = partitions[partitions.size() - 1].second; + retFuture = threadPool.submit_loop(min, max, [&f](const unsigned int i) { f(i); }); + } + + const auto &[min, max] = partitions[0]; + for (uint_fast64_t i = min; i < max; ++i) { + f(i); + } + + if (pSize > 1) { + retFuture.wait(); + asyncWaitDisabled = false; + } } void Dispatcher::executeEvents(const TaskGroup startGroup) { diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index a6cbc8dd6dd..94b284c9316 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -108,6 +108,7 @@ class Dispatcher { } void asyncEvent(std::function<void(void)> &&f, TaskGroup group = TaskGroup::GenericParallel); + void asyncWait(size_t size, std::function<void(size_t i)> &&f); uint64_t asyncCycleEvent(uint32_t delay, std::function<void(void)> &&f, TaskGroup group = TaskGroup::GenericParallel) { return scheduleEvent( @@ -173,6 +174,22 @@ class Dispatcher { } } + std::vector<std::pair<uint64_t, uint64_t>> generatePartition(size_t size) const { + if (size == 0) { + return {}; + } + + std::vector<std::pair<uint64_t, uint64_t>> list; + list.reserve(threadPool.get_thread_count()); + + const auto size_per_block = std::ceil(size / static_cast<float>(threadPool.get_thread_count())); + for (uint_fast64_t i = 0; i < size; i += size_per_block) { + list.emplace_back(i, std::min<uint64_t>(size, i + size_per_block)); + } + + return list; + } + uint_fast64_t dispatcherCycle = 0; ThreadPool &threadPool; @@ -200,6 +217,8 @@ class Dispatcher { phmap::btree_multiset<std::shared_ptr<Task>, Task::Compare> scheduledTasks; phmap::parallel_flat_hash_map_m<uint64_t, std::shared_ptr<Task>> scheduledTasksRef; + bool asyncWaitDisabled = false; + friend class CanaryServer; }; From 883c3ff7ce55b8ad69413fbb551580e310d95909 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Fri, 14 Jun 2024 21:26:44 -0300 Subject: [PATCH 77/95] perf: optimized moveCreature and spectators (#2667) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • In moveCreature, a check was added so that the spectator's search is not called twice, now only one position is passed with the interval adjusted to assume both positions as if it were. • In spectators we no longer use vector_set but just vector and if there is a need to remove record duplication, it will use unordered_set passing the vector in the constructor which is much faster. • Why was useCacheMap disabled? As the map cache is done synchronously for each movement that a monster makes, it is better to disable it, as the pathfinder, which is one of the resources that uses this cache the most, is multithreding and thus the processing cost is divided between the threads. --- src/creatures/monsters/monster.hpp | 6 +++- src/map/map.cpp | 28 ++++++++++++++-- src/map/spectators.cpp | 51 ++++++++---------------------- src/map/spectators.hpp | 49 ++++++++++++++++++++-------- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 6c45fa3eaca..061ad2b9879 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -455,7 +455,11 @@ class Monster final : public Creature { void dropLoot(std::shared_ptr<Container> corpse, std::shared_ptr<Creature> lastHitCreature) override; void getPathSearchParams(const std::shared_ptr<Creature> &creature, FindPathParams &fpp) override; bool useCacheMap() const override { - return !randomStepping; + // return !randomStepping; + // As the map cache is done synchronously for each movement that a monster makes, it is better to disable it, + // as the pathfinder, which is one of the resources that uses this cache the most, + // is multithreding and thus the processing cost is divided between the threads. + return false; } friend class MonsterFunctions; diff --git a/src/map/map.cpp b/src/map/map.cpp index 8b8cebeb539..82629aeae05 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -342,13 +342,35 @@ void Map::moveCreature(const std::shared_ptr<Creature> &creature, const std::sha bool teleport = forceTeleport || !newTile->getGround() || !Position::areInRange<1, 1, 0>(oldPos, newPos); - auto spectators = Spectators() - .find<Creature>(oldPos, true) - .find<Creature>(newPos, true); + Spectators spectators; + if (!teleport && oldPos.z == newPos.z) { + int32_t minRangeX = MAP_MAX_VIEW_PORT_X; + int32_t maxRangeX = MAP_MAX_VIEW_PORT_X; + int32_t minRangeY = MAP_MAX_VIEW_PORT_Y; + int32_t maxRangeY = MAP_MAX_VIEW_PORT_Y; + + if (oldPos.y > newPos.y) { + ++minRangeY; + } else if (oldPos.y < newPos.y) { + ++maxRangeY; + } + + if (oldPos.x < newPos.x) { + ++maxRangeX; + } else if (oldPos.x > newPos.x) { + ++minRangeX; + } + + spectators.find<Creature>(oldPos, true, minRangeX, maxRangeX, minRangeY, maxRangeY); + } else { + spectators.find<Creature>(oldPos, true); + spectators.find<Creature>(newPos, true); + } auto playersSpectators = spectators.filter<Player>(); std::vector<int32_t> oldStackPosVector; + oldStackPosVector.reserve(playersSpectators.size()); for (const auto &spec : playersSpectators) { if (spec->canSeeCreature(creature)) { oldStackPosVector.push_back(oldTile->getClientIndexOfCreature(spec->getPlayer(), creature)); diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 36cce6d535b..10aaf0faf87 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -18,50 +18,27 @@ void Spectators::clearCache() { spectatorsCache.clear(); } -bool Spectators::contains(const std::shared_ptr<Creature> &creature) { - return creatures.contains(creature); -} - -bool Spectators::erase(const std::shared_ptr<Creature> &creature) { - return creatures.erase(creature); -} - Spectators Spectators::insert(const std::shared_ptr<Creature> &creature) { if (creature) { - creatures.emplace(creature); + creatures.emplace_back(creature); } return *this; } -Spectators Spectators::insertAll(const SpectatorList &list) { +Spectators Spectators::insertAll(const CreatureVector &list) { if (!list.empty()) { - creatures.insertAll(list); - } - return *this; -} + const bool hasValue = !creatures.empty(); -Spectators Spectators::join(Spectators &anotherSpectators) { - return insertAll(anotherSpectators.creatures.data()); -} - -bool Spectators::empty() const noexcept { - return creatures.empty(); -} + creatures.insert(creatures.end(), list.begin(), list.end()); -size_t Spectators::size() noexcept { - return creatures.size(); -} - -CreatureVector::iterator Spectators::begin() noexcept { - return creatures.begin(); -} - -CreatureVector::iterator Spectators::end() noexcept { - return creatures.end(); -} - -const CreatureVector &Spectators::data() noexcept { - return creatures.data(); + // Remove duplicate + if (hasValue) { + std::unordered_set uset(creatures.begin(), creatures.end()); + creatures.clear(); + creatures.insert(creatures.end(), uset.begin(), uset.end()); + } + } + return *this; } bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onlyPlayers, const Position ¢erPos, bool checkDistance, bool multifloor, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY) { @@ -77,7 +54,7 @@ bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onl } if (checkDistance) { - SpectatorList spectators; + CreatureVector spectators; spectators.reserve(creatures.size()); for (const auto &creature : *list) { const auto &specPos = creature->getPosition(); @@ -176,7 +153,7 @@ Spectators Spectators::find(const Position ¢erPos, bool multifloor, bool onl const int32_t endx2 = x2 - (x2 & SECTOR_MASK); const int32_t endy2 = y2 - (y2 & SECTOR_MASK); - SpectatorList spectators; + CreatureVector spectators; spectators.reserve(std::max<uint8_t>(MAP_MAX_VIEW_PORT_X, MAP_MAX_VIEW_PORT_Y) * 2); const MapSector* startSector = g_game().map.getMapSector(startx1, starty1); diff --git a/src/map/spectators.hpp b/src/map/spectators.hpp index 93526e05c93..9e998da2bfa 100644 --- a/src/map/spectators.hpp +++ b/src/map/spectators.hpp @@ -16,12 +16,10 @@ class Monster; class Npc; struct Position; -using SpectatorList = std::vector<std::shared_ptr<Creature>>; - struct SpectatorsCache { struct FloorData { - std::optional<SpectatorList> floor; - std::optional<SpectatorList> multiFloor; + std::optional<CreatureVector> floor; + std::optional<CreatureVector> multiFloor; }; int32_t minRangeX { 0 }; @@ -48,16 +46,39 @@ class Spectators { requires std::is_base_of_v<Creature, T> Spectators filter(); - bool contains(const std::shared_ptr<Creature> &creature); - bool erase(const std::shared_ptr<Creature> &creature); Spectators insert(const std::shared_ptr<Creature> &creature); - Spectators insertAll(const SpectatorList &list); - Spectators join(Spectators &anotherSpectators); - bool empty() const noexcept; - size_t size() noexcept; - CreatureVector::iterator begin() noexcept; - CreatureVector::iterator end() noexcept; - const CreatureVector &data() noexcept; + Spectators insertAll(const CreatureVector &list); + Spectators join(const Spectators &anotherSpectators) { + return insertAll(anotherSpectators.creatures); + } + + bool contains(const std::shared_ptr<Creature> &creature) const { + return std::ranges::find(creatures, creature) != creatures.end(); + } + + bool erase(const std::shared_ptr<Creature> &creature) { + return std::erase(creatures, creature) > 0; + } + + bool empty() const noexcept { + return creatures.empty(); + } + + size_t size() const noexcept { + return creatures.size(); + } + + auto begin() const noexcept { + return creatures.begin(); + } + + auto end() const noexcept { + return creatures.end(); + } + + const auto &data() const noexcept { + return creatures; + } private: static phmap::flat_hash_map<Position, SpectatorsCache> spectatorsCache; @@ -65,7 +86,7 @@ class Spectators { Spectators find(const Position ¢erPos, bool multifloor = false, bool onlyPlayers = false, int32_t minRangeX = 0, int32_t maxRangeX = 0, int32_t minRangeY = 0, int32_t maxRangeY = 0); bool checkCache(const SpectatorsCache::FloorData &specData, bool onlyPlayers, const Position ¢erPos, bool checkDistance, bool multifloor, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY); - stdext::vector_set<std::shared_ptr<Creature>> creatures; + CreatureVector creatures; }; template <typename T> From 8a2e5f2e2cbd79cb3606e1553ea233b388782ac8 Mon Sep 17 00:00:00 2001 From: Renato Machado <mehahx@gmail.com> Date: Fri, 14 Jun 2024 21:55:29 -0300 Subject: [PATCH 78/95] perf: remove browseField on container destruction (#2677) Instead of checking all the time if there is a browsefield to be destroyed, why not remove it at the time of its destruction?! Besides, the linear search of phmap::flat_hash_map is very slow. --- src/game/game.cpp | 14 -------------- src/game/game.hpp | 1 - src/items/containers/container.cpp | 4 ++++ 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 57daddded2e..984e675407c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6255,7 +6255,6 @@ void Game::checkCreatureWalk(uint32_t creatureId) { const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { creature->onCreatureWalk(); - cleanup(); } } @@ -6318,7 +6317,6 @@ void Game::checkCreatures() { --end; } } - cleanup(); index = (index + 1) % EVENT_CREATURECOUNT; } @@ -7967,8 +7965,6 @@ void Game::shutdown() { map.spawnsNpc.clear(); raids.clear(); - cleanup(); - if (serviceManager) { serviceManager->stop(); } @@ -7980,16 +7976,6 @@ void Game::shutdown() { g_logger().info("Done!"); } -void Game::cleanup() { - for (auto it = browseFields.begin(); it != browseFields.end();) { - if (it->second.expired()) { - it = browseFields.erase(it); - } else { - ++it; - } - } -} - void Game::addBestiaryList(uint16_t raceid, std::string name) { auto it = BestiaryList.find(raceid); if (it != BestiaryList.end()) { diff --git a/src/game/game.hpp b/src/game/game.hpp index b4be8facd09..2de00410e5e 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -426,7 +426,6 @@ class Game { void updatePlayerHelpers(std::shared_ptr<Player> player); - void cleanup(); void shutdown(); void dieSafely(const std::string &errorMsg); void addBestiaryList(uint16_t raceid, std::string name); diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 63ce657741a..6c526a8500a 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -63,6 +63,10 @@ std::shared_ptr<Container> Container::create(std::shared_ptr<Tile> tile) { Container::~Container() { if (getID() == ITEM_BROWSEFIELD) { + if (getParent() && getParent()->getTile()) { + g_game().browseFields.erase(getParent()->getTile()); + } + for (std::shared_ptr<Item> item : itemlist) { item->setParent(getParent()); } From 81e250a8f9448c476fc12a1c63b53daa8a762aaa Mon Sep 17 00:00:00 2001 From: Elson Costa <elsongabriel@hotmail.com> Date: Fri, 14 Jun 2024 21:56:31 -0300 Subject: [PATCH 79/95] fix: autoloot default value (#2690) --- src/creatures/players/player.hpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index d374bd59911..802b4e6fb6e 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2562,8 +2562,7 @@ class Player final : public Creature, public Cylinder, public Bankable { } bool checkAutoLoot(bool isBoss) const { - const bool autoLoot = g_configManager().getBoolean(AUTOLOOT, __FUNCTION__); - if (!autoLoot) { + if (!g_configManager().getBoolean(AUTOLOOT, __FUNCTION__)) { return false; } if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY, __FUNCTION__) && !isVip()) { @@ -2571,18 +2570,13 @@ class Player final : public Creature, public Cylinder, public Bankable { } auto featureKV = kv()->scoped("features")->get("autoloot"); - if (featureKV.has_value()) { - auto value = featureKV->getNumber(); - if (value == 2) { - return true; - } else if (value == 1) { - return !isBoss; - } else if (value == 0) { - return false; - } + auto value = featureKV.has_value() ? featureKV->getNumber() : 0; + if (value == 2) { + return true; + } else if (value == 1) { + return !isBoss; } - - return true; + return false; } QuickLootFilter_t getQuickLootFilter() const { From b682da775f4c573d15f1dc15a899eb540a7ec969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Sat, 15 Jun 2024 00:13:42 -0300 Subject: [PATCH 80/95] fix: prevent client debug when purchase/sale statistics are missing (#2693) This PR introduces a change to ensure proper handling of cases where sale statistics for a specific item with tier are not found in the tierStatsMap. Fixes #2656 --- src/server/network/protocol/protocolgame.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 8cf0660a262..1c40b67d6c6 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5857,6 +5857,8 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) { msg.add<uint64_t>(purchaseStatistics.highestPrice); msg.add<uint64_t>(purchaseStatistics.lowestPrice); } + } else { + msg.addByte(0x00); } } else { msg.addByte(0x00); // send to old protocol ? @@ -5880,6 +5882,8 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId, uint8_t tier) { msg.add<uint64_t>(saleStatistics.highestPrice); msg.add<uint64_t>(saleStatistics.lowestPrice); } + } else { + msg.addByte(0x00); } } else { msg.addByte(0x00); // send to old protocol ? From 4af5b2dc1898f0e69da2730bf1a41768b43c29ed Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Sat, 15 Jun 2024 00:14:03 -0300 Subject: [PATCH 81/95] fix: store UI exhaustion and improve bosslever messages (#2694) Fixed the store exhaustion to work correctly. Fixed the boss lever messages to better inform the participants. --- data/libs/functions/boss_lever.lua | 32 +++++++++++++++++-------- data/modules/scripts/gamestore/init.lua | 17 ++++++++----- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua index 3792cdf629d..48d00582e89 100644 --- a/data/libs/functions/boss_lever.lua +++ b/data/libs/functions/boss_lever.lua @@ -174,24 +174,36 @@ function BossLever:onUse(player) end if creature:getLevel() < self.requiredLevel then - creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. self.requiredLevel .. " or higher.") + local message = "All players need to be level " .. self.requiredLevel .. " or higher." + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) return false end - if self:lastEncounterTime(creature) > os.time() then - local info = lever:getInfoPositions() - for _, v in pairs(info) do - local newPlayer = v.creature - if newPlayer then - local timeLeft = self:lastEncounterTime(newPlayer) - os.time() - newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. getTimeInWords(timeLeft) .. " to face " .. self.name .. " again!") - if self:lastEncounterTime(newPlayer) > os.time() then - newPlayer:getPosition():sendMagicEffect(CONST_ME_POFF) + if creature:getGroup():getId() < GROUP_TYPE_GOD and self:lastEncounterTime(creature) > os.time() then + local infoPositions = lever:getInfoPositions() + for _, posInfo in pairs(infoPositions) do + local currentPlayer = posInfo.creature + if currentPlayer then + local lastEncounter = self:lastEncounterTime(currentPlayer) + local currentTime = os.time() + if lastEncounter and currentTime < lastEncounter then + local timeLeft = lastEncounter - currentTime + local timeMessage = getTimeInWords(timeLeft) .. " to face " .. monsterName .. " again!" + local message = "You have to wait " .. timeMessage + + if currentPlayer ~= player then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "A member in your team has to wait " .. timeMessage) + end + + currentPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + currentPlayer:getPosition():sendMagicEffect(CONST_ME_POFF) end end end return false end + self.onUseExtra(creature) return true end) diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index ba7398d9d3e..214aec77d43 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -247,6 +247,11 @@ function onRecvbyte(player, msg, byte) return player:sendCancelMessage("Store don't have offers for rookgaard citizen.") end + if player:isUIExhausted(250) then + player:sendCancelMessage("You are exhausted.") + return + end + if byte == GameStore.RecivedPackets.C_StoreEvent then elseif byte == GameStore.RecivedPackets.C_TransferCoins then parseTransferableCoins(player:getId(), msg) @@ -262,12 +267,6 @@ function onRecvbyte(player, msg, byte) parseRequestTransactionHistory(player:getId(), msg) end - if player:isUIExhausted(250) then - player:sendCancelMessage("You are exhausted.") - return false - end - - player:updateUIExhausted() return true end @@ -306,6 +305,7 @@ function parseTransferableCoins(playerId, msg) GameStore.insertHistory(accountId, GameStore.HistoryTypes.HISTORY_TYPE_NONE, player:getName() .. " transferred you this amount.", amount, GameStore.CoinType.Transferable) GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, "You transferred this amount to " .. reciver, -1 * amount, GameStore.CoinType.Transferable) openStore(playerId) + player:updateUIExhausted() end function parseOpenStore(playerId, msg) @@ -396,6 +396,7 @@ function parseRequestStoreOffers(playerId, msg) addPlayerEvent(sendShowStoreOffers, 250, playerId, searchResultsCategory) end + player:updateUIExhausted() end function parseBuyStoreOffer(playerId, msg) @@ -532,6 +533,8 @@ function parseBuyStoreOffer(playerId, msg) sendUpdatedStoreBalances(playerId) return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message) end + + player:updateUIExhausted() return true end @@ -540,11 +543,13 @@ function parseOpenTransactionHistory(playerId, msg) local page = 1 GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE = msg:getByte() sendStoreTransactionHistory(playerId, page, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE) + player:updateUIExhausted() end function parseRequestTransactionHistory(playerId, msg) local page = msg:getU32() sendStoreTransactionHistory(playerId, page + 1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE) + player:updateUIExhausted() end local function getCategoriesRook() From 74a5df00160ad11ecfc4557b1dd4cf6857fb8ee7 Mon Sep 17 00:00:00 2001 From: attack787 <47985212+attack787@users.noreply.github.com> Date: Wed, 19 Jun 2024 22:49:43 -0300 Subject: [PATCH 82/95] Fix: send timeMessage when using boss lever (#2701) --- data/libs/functions/boss_lever.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua index 48d00582e89..b95bf7211b0 100644 --- a/data/libs/functions/boss_lever.lua +++ b/data/libs/functions/boss_lever.lua @@ -189,7 +189,7 @@ function BossLever:onUse(player) local currentTime = os.time() if lastEncounter and currentTime < lastEncounter then local timeLeft = lastEncounter - currentTime - local timeMessage = getTimeInWords(timeLeft) .. " to face " .. monsterName .. " again!" + local timeMessage = getTimeInWords(timeLeft) .. " to face " .. self.name .. " again!" local message = "You have to wait " .. timeMessage if currentPlayer ~= player then From e6294912ae9e260b8ecc488a186387f300440ded Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Thu, 20 Jun 2024 01:48:50 -0300 Subject: [PATCH 83/95] fix: prevent requesting a new trade window with each "trade" (#2700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Identified Problems: • Item Loop Performance: The loop in the ProtocolGame::sendShop function performed key-value store (KV) access operations for each NPC item during every iteration. This was highly inefficient for NPCs with many items, such as "The Lootmonger," which has 1380 items. • Lack of Database Caching: Currently, our database does not implement caching for query results. This leads to redundant database accesses, particularly noticeable when commands like "trade" are used repeatedly, significantly increasing CPU usage and processing time. Implemented Solutions: • Optimization of the Item Loop: Necessary initialization has been moved outside the loop in the ProtocolGame::sendShop method. This reduces overhead by avoiding unnecessary repetitive database accesses. • Removal of Unnecessary Map: Removed an extraneous map in npc.hpp and simplified the use of the shop player cache. These changes aim to streamline interactions and reduce the computational load, especially during frequent trade requests with item-rich NPCs like "The Lootmonger." Additional optimizations have been made to enhance overall system performance. --- data/npclib/npc_system/npc_handler.lua | 1 - src/creatures/npcs/npc.cpp | 36 +++++++++---------- src/creatures/npcs/npc.hpp | 17 ++++----- src/creatures/players/player.cpp | 21 +++++++---- src/creatures/players/player.hpp | 2 +- .../functions/creatures/npc/npc_functions.cpp | 8 ++--- src/server/network/message/networkmessage.cpp | 4 +-- src/server/network/message/networkmessage.hpp | 15 +++++++- src/server/network/protocol/protocolgame.cpp | 32 +++++++++-------- 9 files changed, 73 insertions(+), 63 deletions(-) diff --git a/data/npclib/npc_system/npc_handler.lua b/data/npclib/npc_system/npc_handler.lua index 8f4d5f2ca48..31aaa88faaf 100644 --- a/data/npclib/npc_system/npc_handler.lua +++ b/data/npclib/npc_system/npc_handler.lua @@ -480,7 +480,6 @@ if NpcHandler == nil then -- If is npc shop, send shop window and parse default message (if not have callback on the npc) if npc:isMerchant() then - npc:closeShopWindow(player) npc:openShopWindow(player) self:say(msg, npc, player) end diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index e445a58fcca..53fcfd07a3c 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -108,7 +108,7 @@ void Npc::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) { spawnNpc->startSpawnNpcCheck(); } - shopPlayerMap.clear(); + shopPlayers.clear(); } void Npc::onCreatureMove(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, const Position &newPos, const std::shared_ptr<Tile> &oldTile, const Position &oldPos, bool teleport) { @@ -259,7 +259,7 @@ void Npc::onPlayerBuyItem(std::shared_ptr<Player> player, uint16_t itemId, uint8 } uint32_t buyPrice = 0; - const std::vector<ShopBlock> &shopVector = getShopItemVector(player->getGUID()); + const auto &shopVector = getShopItemVector(player->getGUID()); for (const ShopBlock &shopBlock : shopVector) { if (itemType.id == shopBlock.itemId && shopBlock.itemBuyPrice != 0) { buyPrice = shopBlock.itemBuyPrice; @@ -372,7 +372,7 @@ void Npc::onPlayerSellItem(std::shared_ptr<Player> player, uint16_t itemId, uint uint32_t sellPrice = 0; const ItemType &itemType = Item::items[itemId]; - const std::vector<ShopBlock> &shopVector = getShopItemVector(player->getGUID()); + const auto &shopVector = getShopItemVector(player->getGUID()); for (const ShopBlock &shopBlock : shopVector) { if (itemType.id == shopBlock.itemId && shopBlock.itemSellPrice != 0) { sellPrice = shopBlock.itemSellPrice; @@ -586,7 +586,7 @@ void Npc::setPlayerInteraction(uint32_t playerId, uint16_t topicId /*= 0*/) { void Npc::removePlayerInteraction(std::shared_ptr<Player> player) { if (playerInteractions.contains(player->getID())) { playerInteractions.erase(player->getID()); - player->closeShopWindow(true); + player->closeShopWindow(); } } @@ -643,30 +643,26 @@ bool Npc::getRandomStep(Direction &moveDirection) { return false; } -void Npc::addShopPlayer(const std::shared_ptr<Player> &player, const std::vector<ShopBlock> &shopItems /* = {}*/) { - if (!player) { - return; - } - - shopPlayerMap.try_emplace(player->getGUID(), shopItems); +bool Npc::isShopPlayer(uint32_t playerGUID) const { + return shopPlayers.find(playerGUID) != shopPlayers.end(); } -void Npc::removeShopPlayer(const std::shared_ptr<Player> &player) { - if (!player) { - return; - } +void Npc::addShopPlayer(uint32_t playerGUID) { + shopPlayers.insert(playerGUID); +} - shopPlayerMap.erase(player->getGUID()); +void Npc::removeShopPlayer(uint32_t playerGUID) { + shopPlayers.erase(playerGUID); } void Npc::closeAllShopWindows() { - for (const auto &[playerGUID, playerPtr] : shopPlayerMap) { - auto shopPlayer = g_game().getPlayerByGUID(playerGUID); - if (shopPlayer) { - shopPlayer->closeShopWindow(); + for (const auto playerGUID : shopPlayers) { + const auto &player = g_game().getPlayerByGUID(playerGUID); + if (player) { + player->closeShopWindow(); } } - shopPlayerMap.clear(); + shopPlayers.clear(); } void Npc::handlePlayerMove(std::shared_ptr<Player> player, const Position &newPos) { diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 7e8be84c7f1..605aaae570e 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -95,14 +95,7 @@ class Npc final : public Creature { npcType->info.currencyId = currency; } - std::vector<ShopBlock> getShopItemVector(uint32_t playerGUID) { - if (playerGUID != 0) { - auto it = shopPlayerMap.find(playerGUID); - if (it != shopPlayerMap.end() && !it->second.empty()) { - return it->second; - } - } - + const std::vector<ShopBlock> &getShopItemVector(uint32_t playerGUID) const { return npcType->info.shopItemVector; } @@ -165,8 +158,10 @@ class Npc final : public Creature { internalLight = npcType->info.light; } - void addShopPlayer(const std::shared_ptr<Player> &player, const std::vector<ShopBlock> &shopItems = {}); - void removeShopPlayer(const std::shared_ptr<Player> &player); + bool isShopPlayer(uint32_t playerGUID) const; + + void addShopPlayer(uint32_t playerGUID); + void removeShopPlayer(uint32_t playerGUID); void closeAllShopWindows(); static uint32_t npcAutoID; @@ -184,7 +179,7 @@ class Npc final : public Creature { std::map<uint32_t, uint16_t> playerInteractions; - phmap::flat_hash_map<uint32_t, std::vector<ShopBlock>> shopPlayerMap; + std::unordered_set<uint32_t> shopPlayers; std::shared_ptr<NpcType> npcType; std::shared_ptr<SpawnNpc> spawnNpc; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 2c92f694197..52ceb8b3377 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1895,31 +1895,38 @@ void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) } bool Player::openShopWindow(std::shared_ptr<Npc> npc) { + Benchmark brenchmark; if (!npc) { g_logger().error("[Player::openShopWindow] - Npc is wrong or nullptr"); return false; } + if (npc->isShopPlayer(getGUID())) { + g_logger().debug("[Player::openShopWindow] - Player {} is already in shop window", getName()); + return false; + } + + npc->addShopPlayer(getGUID()); + setShopOwner(npc); sendShop(npc); std::map<uint16_t, uint16_t> inventoryMap; sendSaleItemList(getAllSaleItemIdAndCount(inventoryMap)); + + g_logger().debug("[Player::openShopWindow] - Player {} has opened shop window in {} ms", getName(), brenchmark.duration()); return true; } -bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/) { +bool Player::closeShopWindow() { if (!shopOwner) { return false; } - shopOwner->removeShopPlayer(static_self_cast<Player>()); + shopOwner->removeShopPlayer(getGUID()); setShopOwner(nullptr); - if (sendCloseShopWindow) { - sendCloseShop(); - } - + sendCloseShop(); return true; } @@ -4293,7 +4300,7 @@ bool Player::hasShopItemForSale(uint16_t itemId, uint8_t subType) const { } const ItemType &itemType = Item::items[itemId]; - std::vector<ShopBlock> shoplist = shopOwner->getShopItemVector(getGUID()); + const auto &shoplist = shopOwner->getShopItemVector(getGUID()); return std::any_of(shoplist.begin(), shoplist.end(), [&](const ShopBlock &shopBlock) { return shopBlock.itemId == itemId && shopBlock.itemBuyPrice != 0 && (!itemType.isFluidContainer() || shopBlock.itemSubType == subType); }); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 802b4e6fb6e..332887acc51 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -851,7 +851,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void stopWalk(); bool openShopWindow(std::shared_ptr<Npc> npc); - bool closeShopWindow(bool sendCloseShopWindow = true); + bool closeShopWindow(); bool updateSaleShopList(std::shared_ptr<Item> item); bool hasShopItemForSale(uint16_t itemId, uint8_t subType) const; diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index 8c19a4ede0a..2b14ef46b72 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -354,7 +354,6 @@ int NpcFunctions::luaNpcOpenShopWindow(lua_State* L) { return 1; } - npc->addShopPlayer(player); pushBoolean(L, player->openShopWindow(npc)); return 1; } @@ -405,9 +404,6 @@ int NpcFunctions::luaNpcOpenShopWindowTable(lua_State* L) { } lua_pop(L, 3); - // Close any eventual other shop window currently open. - player->closeShopWindow(true); - npc->addShopPlayer(player, items); pushBoolean(L, player->openShopWindow(npc)); return 1; } @@ -429,7 +425,7 @@ int NpcFunctions::luaNpcCloseShopWindow(lua_State* L) { } if (player->getShopOwner() == npc) { - player->closeShopWindow(true); + player->closeShopWindow(); } pushBoolean(L, true); @@ -577,7 +573,7 @@ int NpcFunctions::luaNpcSellItem(lua_State* L) { } uint64_t pricePerUnit = 0; - const std::vector<ShopBlock> &shopVector = npc->getShopItemVector(player->getGUID()); + const auto &shopVector = npc->getShopItemVector(player->getGUID()); for (ShopBlock shopBlock : shopVector) { if (itemId == shopBlock.itemId && shopBlock.itemBuyPrice != 0) { pricePerUnit = shopBlock.itemBuyPrice; diff --git a/src/server/network/message/networkmessage.cpp b/src/server/network/message/networkmessage.cpp index 15963135560..38acc0b484c 100644 --- a/src/server/network/message/networkmessage.cpp +++ b/src/server/network/message/networkmessage.cpp @@ -40,9 +40,9 @@ Position NetworkMessage::getPosition() { return pos; } -void NetworkMessage::addString(const std::string &value, const std::string &function) { +void NetworkMessage::addString(const std::string &value, const std::string &function /* = ""*/) { size_t stringLen = value.length(); - if (value.empty()) { + if (value.empty() && !function.empty()) { g_logger().debug("[NetworkMessage::addString] - Value string is empty, function '{}'", function); } if (!canAdd(stringLen + 2)) { diff --git a/src/server/network/message/networkmessage.hpp b/src/server/network/message/networkmessage.hpp index 72f0e69c3dc..02e19253146 100644 --- a/src/server/network/message/networkmessage.hpp +++ b/src/server/network/message/networkmessage.hpp @@ -90,7 +90,20 @@ class NetworkMessage { void addBytes(const char* bytes, size_t size); void addPaddingBytes(size_t n); - void addString(const std::string &value, const std::string &function); + /** + * Adds a string to the network message buffer. + * + * @param value The string value to be added to the message buffer. + * @param function * @param function An optional parameter that specifies the function name from which `addString` is invoked. + * Including this enhances logging by adding the function name to the debug and error log messages. + * This helps in debugging by indicating the context when issues occur, such as attempting to add an + * empty string or when there are message size errors. + * + * When the function parameter is used, it aids in identifying the context in log messages, + * making it easier to diagnose issues related to network message construction, + * especially in complex systems where the same method might be called from multiple places. + */ + void addString(const std::string &value, const std::string &function = ""); void addDouble(double value, uint8_t precision = 2); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 1c40b67d6c6..c006e315ab1 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -4637,6 +4637,7 @@ void ProtocolGame::sendLootStats(std::shared_ptr<Item> item, uint8_t count) { } void ProtocolGame::sendShop(std::shared_ptr<Npc> npc) { + Benchmark brenchmark; NetworkMessage msg; msg.addByte(0x7A); msg.addString(npc->getName(), "ProtocolGame::sendShop - npc->getName()"); @@ -4646,20 +4647,35 @@ void ProtocolGame::sendShop(std::shared_ptr<Npc> npc) { msg.addString(std::string(), "ProtocolGame::sendShop - std::string()"); // Currency name } - std::vector<ShopBlock> shoplist = npc->getShopItemVector(player->getGUID()); + const auto &shoplist = npc->getShopItemVector(player->getGUID()); uint16_t itemsToSend = std::min<size_t>(shoplist.size(), std::numeric_limits<uint16_t>::max()); msg.add<uint16_t>(itemsToSend); + // Initialize before the loop to avoid database overload on each iteration + auto talkactionHidden = player->kv()->get("npc-shop-hidden-sell-item"); + // Initialize the inventoryMap outside the loop to avoid creation on each iteration + std::map<uint16_t, uint16_t> inventoryMap; + player->getAllSaleItemIdAndCount(inventoryMap); uint16_t i = 0; for (const ShopBlock &shopBlock : shoplist) { if (++i > itemsToSend) { break; } + // Hidden sell items from the shop if they are not in the player's inventory + if (talkactionHidden && talkactionHidden->get<bool>()) { + const auto &foundItem = inventoryMap.find(shopBlock.itemId); + if (foundItem == inventoryMap.end() && shopBlock.itemSellPrice > 0 && shopBlock.itemBuyPrice == 0) { + AddHiddenShopItem(msg); + continue; + } + } + AddShopItem(msg, shopBlock); } writeToOutputBuffer(msg); + g_logger().debug("ProtocolGame::sendShop - Time: {} ms, shop items: {}", brenchmark.duration(), shoplist.size()); } void ProtocolGame::sendCloseShop() { @@ -8101,7 +8117,7 @@ void ProtocolGame::AddHiddenShopItem(NetworkMessage &msg) { // Empty bytes from AddShopItem msg.add<uint16_t>(0); msg.addByte(0); - msg.addString(std::string(), "ProtocolGame::AddHiddenShopItem - std::string()"); + msg.addString(std::string()); msg.add<uint32_t>(0); msg.add<uint32_t>(0); msg.add<uint32_t>(0); @@ -8114,18 +8130,6 @@ void ProtocolGame::AddShopItem(NetworkMessage &msg, const ShopBlock &shopBlock) return; } - // Hidden sell items from the shop if they are not in the player's inventory - auto talkactionHidden = player->kv()->get("npc-shop-hidden-sell-item"); - if (talkactionHidden && talkactionHidden->get<BooleanType>() == true) { - std::map<uint16_t, uint16_t> inventoryMap; - player->getAllSaleItemIdAndCount(inventoryMap); - auto inventoryItems = inventoryMap.find(shopBlock.itemId); - if (inventoryItems == inventoryMap.end() && shopBlock.itemSellPrice > 0 && shopBlock.itemBuyPrice == 0) { - AddHiddenShopItem(msg); - return; - } - } - const ItemType &it = Item::items[shopBlock.itemId]; msg.add<uint16_t>(shopBlock.itemId); if (it.isSplash() || it.isFluidContainer()) { From fb20f6f40c88b0181aaf3d2def66a7f9aa48a648 Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sat, 22 Jun 2024 14:36:40 -0300 Subject: [PATCH 84/95] improve: small adjustment to 'hasTraceableContext()' (#2374) - The 'checkCreatureWalk' function is of the 'Game' class and not 'Creature' as advertised. - '__FUNCTION__' was only returning the name of the function and not the class, so it had been necessary to add it to 'hasTraceableContext()'. --- src/creatures/creature.cpp | 2 +- src/game/scheduling/task.hpp | 3 +-- src/server/network/protocol/protocol.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 7796863f066..d033d59c829 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -259,7 +259,7 @@ void Creature::addEventWalk(bool firstStep) { self->eventWalk = g_dispatcher().scheduleEvent( static_cast<uint32_t>(ticks), - [creatureId = self->getID()] { g_game().checkCreatureWalk(creatureId); }, "Creature::checkCreatureWalk" + [creatureId = self->getID()] { g_game().checkCreatureWalk(creatureId); }, "Game::checkCreatureWalk" ); }); } diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index c6591887c8a..7bdc7db8084 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -70,10 +70,10 @@ class Task { bool hasTraceableContext() const { const static auto tasksContext = std::unordered_set<std::string_view>({ - "Creature::checkCreatureWalk", "Decay::checkDecay", "Dispatcher::asyncEvent", "Game::checkCreatureAttack", + "Game::checkCreatureWalk", "Game::checkCreatures", "Game::checkImbuements", "Game::checkLight", @@ -94,7 +94,6 @@ class Task { "SpawnNpc::checkSpawnNpc", "Webhook::run", "Protocol::sendRecvMessageCallback", - "sendRecvMessageCallback", }); return tasksContext.contains(context); diff --git a/src/server/network/protocol/protocol.cpp b/src/server/network/protocol/protocol.cpp index 6f6a1c8228e..78cff72fab4 100644 --- a/src/server/network/protocol/protocol.cpp +++ b/src/server/network/protocol/protocol.cpp @@ -50,7 +50,7 @@ bool Protocol::sendRecvMessageCallback(NetworkMessage &msg) { protocol->parsePacket(msg); protocolConnection->resumeWork(); } - } }, __FUNCTION__); + } }, "Protocol::sendRecvMessageCallback"); return true; } From 0e134942812e0b0e84534b6aa3d912706c27225d Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sat, 22 Jun 2024 14:49:07 -0300 Subject: [PATCH 85/95] feat: auto switch hotkey presets (#2115) Auto switch hotkey presets in login. --- src/server/network/protocol/protocolgame.cpp | 15 +++++++++++++++ src/server/network/protocol/protocolgame.hpp | 1 + 2 files changed, 16 insertions(+) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index c006e315ab1..ddfc867f918 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6584,6 +6584,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos if (isLogin) { sendMagicEffect(pos, CONST_ME_TELEPORT); + sendHotkeyPreset(); sendDisableLoginMusic(); } @@ -9056,3 +9057,17 @@ void ProtocolGame::sendDisableLoginMusic() { msg.addByte(0x00); writeToOutputBuffer(msg); } + +void ProtocolGame::sendHotkeyPreset() { + if (!player || oldProtocol) { + return; + } + + auto vocation = g_vocations().getVocation(player->getVocation()->getBaseId()); + if (vocation) { + NetworkMessage msg; + msg.addByte(0x9D); + msg.add<uint32_t>(vocation->getClientId()); + writeToOutputBuffer(msg); + } +} diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 100ca199b77..24341f15e1e 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -514,6 +514,7 @@ class ProtocolGame final : public Protocol { void sendSingleSoundEffect(const Position &pos, SoundEffect_t id, SourceEffect_t source); void sendDoubleSoundEffect(const Position &pos, SoundEffect_t mainSoundId, SourceEffect_t mainSource, SoundEffect_t secondarySoundId, SourceEffect_t secondarySource); + void sendHotkeyPreset(); void sendDisableLoginMusic(); uint8_t m_playerDeathTime = 0; From 3cf22437f0bc7b91f4fc39f6f5818121f7704da6 Mon Sep 17 00:00:00 2001 From: odisk777 <65802862+odisk777@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:06:20 -0500 Subject: [PATCH 86/95] fix: lord azaram health (#2687) --- .../monster/quests/grave_danger/bosses/lord_azaram.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua b/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua index 701a0b6f92b..8507c3a0c1c 100644 --- a/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua +++ b/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua @@ -17,8 +17,8 @@ monster.events = { "GraveDangerBossDeath", } -monster.health = 75000 -monster.maxHealth = 75000 +monster.health = 300000 +monster.maxHealth = 300000 monster.race = "venom" monster.corpse = 31599 monster.speed = 125 From b4647d191dd1402d15f24be533dbb52a3b0ad72d Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sat, 22 Jun 2024 21:07:43 -0300 Subject: [PATCH 87/95] feat: faceless bane mechanics (#2619) --- data-otservbr-global/lib/core/storages.lua | 9 ++ .../the_dream_courts/bosses/faceless_bane.lua | 19 +-- .../monster/faceless_bane_immunity.lua | 47 ++++++++ .../faceless_bane_step_positions.lua | 114 ++++++++++++++++++ 4 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua create mode 100644 data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua index fce8059f7d1..2b8d9a7783d 100644 --- a/data-otservbr-global/lib/core/storages.lua +++ b/data-otservbr-global/lib/core/storages.lua @@ -3079,6 +3079,15 @@ GlobalStorage = { DarashiaWest = 60193, }, }, + TheDreamCourts = { + -- Reserved storage from 60194 - 60196 + FacelessBane = { + -- Global + StepsOn = 60194, + Deaths = 60195, + ResetSteps = 60196, + }, + }, FuryGates = 65000, Yakchal = 65001, PitsOfInfernoLevers = 65002, diff --git a/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua b/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua index e4d8553c46a..868fe08e756 100644 --- a/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua +++ b/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Faceless Bane") local monster = {} monster.description = "Faceless Bane" -monster.experience = 30000 +monster.experience = 20000 monster.outfit = { lookType = 1119, lookHead = 0, @@ -22,7 +22,11 @@ monster.manaCost = 0 monster.changeTarget = { interval = 4000, - chance = 10, + chance = 20, +} + +monster.reflects = { + { type = COMBAT_DEATHDAMAGE, percent = 90 }, } monster.bosstiary = { @@ -131,11 +135,7 @@ monster.elements = { { type = COMBAT_DROWNDAMAGE, percent = 0 }, { type = COMBAT_ICEDAMAGE, percent = 0 }, { type = COMBAT_HOLYDAMAGE, percent = 0 }, - { type = COMBAT_DEATHDAMAGE, percent = 99 }, -} - -monster.heals = { - { type = COMBAT_DEATHDAMAGE, percent = 100 }, + { type = COMBAT_DEATHDAMAGE, percent = 50 }, } monster.immunities = { @@ -149,6 +149,11 @@ mType.onThink = function(monster, interval) end mType.onAppear = function(monster, creature) if monster:getType():isRewardBoss() then + -- reset global storage state to default / ensure sqm's reset for the next team + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths, -1) + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, -1) + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 1) + monster:registerEvent("facelessBaneImmunity") monster:setReward(true) end end diff --git a/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua b/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua new file mode 100644 index 00000000000..36e1ecd11c3 --- /dev/null +++ b/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua @@ -0,0 +1,47 @@ +local bossName = "Faceless Bane" + +local function healBoss(creature) + if creature then + creature:addHealth(creature:getMaxHealth()) + creature:getPosition():sendMagicEffect(CONST_ME_BLOCKHIT) + end +end + +local function createSummons(creature) + if creature then + local pos = creature:getPosition() + Game.createMonster("Gazer Spectre", pos, true, false, creature) + Game.createMonster("Ripper Spectre", pos, true, false, creature) + Game.createMonster("Burster Spectre", pos, true, false, creature) + end +end + +local function resetBoss(creature, deaths) + if creature then + healBoss(creature) + createSummons(creature) + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths, deaths + 1) + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 0) + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 1) + end +end + +local facelessBaneImmunity = CreatureEvent("facelessBaneImmunity") + +function facelessBaneImmunity.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if creature and creature:isMonster() and creature:getName() == bossName then + local creatureHealthPercent = (creature:getHealth() * 100) / creature:getMaxHealth() + local facelessBaneDeathsStorage = Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths) + + if creatureHealthPercent <= 20 and facelessBaneDeathsStorage < 1 then + resetBoss(creature, facelessBaneDeathsStorage) + return true + elseif Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn) < 1 then + healBoss(creature) + return true + end + end + return primaryDamage, primaryType, secondaryDamage, secondaryType +end + +facelessBaneImmunity:register() diff --git a/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua b/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua new file mode 100644 index 00000000000..8ebdc47ae6f --- /dev/null +++ b/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua @@ -0,0 +1,114 @@ +local walkedPositions = {} +local lastResetTime = os.time() +local checkTime = false + +local function resetWalkedPositions(checkLastResetTime) + if lastResetTime > os.time() and checkLastResetTime then + return true + end + + walkedPositions = {} + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 0) + lastResetTime = os.time() + (1 * 60) +end + +local pipePositions = { + Position(33612, 32568, 13), + Position(33612, 32567, 13), + Position(33612, 32566, 13), + Position(33612, 32565, 13), + Position(33612, 32564, 13), + Position(33612, 32563, 13), + Position(33612, 32562, 13), + Position(33612, 32561, 13), + Position(33612, 32560, 13), + Position(33612, 32559, 13), + Position(33612, 32558, 13), + Position(33612, 32557, 13), + Position(33612, 32556, 13), + Position(33622, 32556, 13), + Position(33622, 32557, 13), + Position(33622, 32558, 13), + Position(33622, 32559, 13), + Position(33622, 32560, 13), + Position(33622, 32561, 13), + Position(33622, 32562, 13), + Position(33622, 32563, 13), + Position(33622, 32564, 13), + Position(33622, 32565, 13), + Position(33622, 32566, 13), + Position(33622, 32567, 13), + Position(33622, 32568, 13), +} + +local function sendEnergyEffect() + for _, position in ipairs(pipePositions) do + position:sendMagicEffect(CONST_ME_PURPLEENERGY) + position:sendSingleSoundEffect(SOUND_EFFECT_TYPE_SPELL_GREAT_ENERGY_BEAM) + end + + return true +end + +local facelessBaneStepPositions = MoveEvent() + +function facelessBaneStepPositions.onStepIn(creature, item, position, fromPosition) + local player = creature:getPlayer() + if not player then + return true + end + + if Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps) == 1 then + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 0) + lastResetTime = os.time() + resetWalkedPositions(true) + end + + if not checkTime then + checkTime = addEvent(resetWalkedPositions, 15 * 1000, false) + end + + if Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn) < 1 then + if #walkedPositions > 0 then + for _, walkedPos in ipairs(walkedPositions) do + if walkedPos == position then + return true + end + end + end + + position:sendSingleSoundEffect(SOUND_EFFECT_TYPE_SPELL_BUZZ) + position:sendMagicEffect(CONST_ME_YELLOWENERGY) + table.insert(walkedPositions, position) + + if #walkedPositions == 13 then + Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 1) + addEvent(resetWalkedPositions, 60 * 1000, true) + sendEnergyEffect() + checkTime = nil + end + end + return true +end + +local facelessBaneSteps = { + Position(33615, 32567, 13), + Position(33613, 32567, 13), + Position(33611, 32563, 13), + Position(33610, 32561, 13), + Position(33611, 32558, 13), + Position(33614, 32557, 13), + Position(33617, 32558, 13), + Position(33620, 32557, 13), + Position(33623, 32558, 13), + Position(33624, 32561, 13), + Position(33623, 32563, 13), + Position(33621, 32567, 13), + Position(33619, 32567, 13), +} + +for _, pos in ipairs(facelessBaneSteps) do + facelessBaneStepPositions:position(pos) +end + +facelessBaneStepPositions:register() From 7b53154f96a07fa41b99de4a4335fad10b96c10f Mon Sep 17 00:00:00 2001 From: Pedro Cruz <phac@cin.ufpe.br> Date: Sat, 22 Jun 2024 21:22:32 -0300 Subject: [PATCH 88/95] fix: vip groups (#2703) Fix vip groups schema and update warnings of free id and group name already exists to debug to avoid spam in the console in production. --- schema.sql | 6 ------ src/creatures/players/vip/player_vip.cpp | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/schema.sql b/schema.sql index 2fbc7dd649b..af245067057 100644 --- a/schema.sql +++ b/schema.sql @@ -850,9 +850,3 @@ INSERT INTO `players` (4, 'Paladin Sample', 1, 1, 8, 3, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0), (5, 'Knight Sample', 1, 1, 8, 4, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0), (6, 'GOD', 6, 1, 2, 0, 155, 155, 100, 113, 115, 95, 39, 75, 0, 60, 60, 0, 8, '', 410, 1, 10, 0, 10, 0, 10, 0, 10, 0); - --- Create vip groups for GOD account -INSERT INTO `account_vipgroups` (`name`, `account_id`, `customizable`) VALUES -('Friends', 1, 0), -('Enemies', 1, 0), -('Trading Partners', 1, 0); diff --git a/src/creatures/players/vip/player_vip.cpp b/src/creatures/players/vip/player_vip.cpp index b4b1642ec69..95ebe91ad5f 100644 --- a/src/creatures/players/vip/player_vip.cpp +++ b/src/creatures/players/vip/player_vip.cpp @@ -143,13 +143,13 @@ std::shared_ptr<VIPGroup> PlayerVIP::getGroupByName(const std::string &name) con void PlayerVIP::addGroupInternal(uint8_t groupId, const std::string &name, bool customizable) { if (getGroupByName(name) != nullptr) { - g_logger().warn("{} - Group name already exists.", __FUNCTION__); + g_logger().debug("{} - Group name already exists.", __FUNCTION__); return; } const auto freeId = getFreeId(); if (freeId == 0) { - g_logger().warn("{} - No id available.", __FUNCTION__); + g_logger().debug("{} - No id available.", __FUNCTION__); return; } From 3f6ac9adb9c4e9f086748916bb894c52e1629aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Sat, 22 Jun 2024 21:26:42 -0300 Subject: [PATCH 89/95] fix: eventcallbacks which returns a ReturnValue (#2532) --- data/scripts/eventcallbacks/README.md | 33 ++++++++++++++++--- .../creature/on_area_combat.lua | 2 +- src/creatures/combat/combat.cpp | 15 +++++++-- src/lua/callbacks/events_callbacks.hpp | 26 +++++++++++++++ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md index 601653574bd..ae5de046bd2 100644 --- a/data/scripts/eventcallbacks/README.md +++ b/data/scripts/eventcallbacks/README.md @@ -14,8 +14,8 @@ Event callbacks are available for several categories of game entities, such as ` ### These are the functions available to use - `(bool)` `creatureOnChangeOutfit` -- `(bool)` `creatureOnAreaCombat` -- `(bool)` `creatureOnTargetCombat` +- `(ReturnValue)` `creatureOnAreaCombat` +- `(ReturnValue)` `creatureOnTargetCombat` - `(void)` `creatureOnHear` - `(void)` `creatureOnDrainHealth` - `(bool)` `partyOnJoin` @@ -66,7 +66,7 @@ local callback = EventCallback() function callback.creatureOnAreaCombat(creature, tile, isAggressive) -- custom behavior when a creature enters combat area - return true + return RETURNVALUE_NOERROR end callback:register() @@ -131,14 +131,36 @@ Here is an example of a boolean event callback: ```lua local callback = EventCallback() +function callback.playerOnMoveItem(player, item, count, fromPos, toPos, fromCylinder, toCylinder) + if item:getId() == ITEM_PARCEL then + --Custom behavior when the player moves a parcel. + return false + end + return true +end + +callback:register() +``` + +### In this example, when a player moves an item, the function checks if the item is a parcel and apply a custom behaviour, returning false making it impossible to move, stopping the associated function on the C++ side. + +## ReturnValue Event Callbacks + +Some event callbacks are expected to return a enum value, in this case, the enum ReturnValue. If the return is different of RETURNVALUE_NOERROR, it will stop the execution of the next callbacks. + +Here is an example of a ReturnValue event callback: + +```lua +local callback = EventCallback() + function callback.creatureOnAreaCombat(creature, tile, isAggressive) -- if the creature is not aggressive, stop the execution of the C++ function if not isAggressive then - return false + return RETURNVALUE_NOTPOSSIBLE end -- custom behavior when an aggressive creature enters a combat area - return true + return RETURNVALUE_NOERROR end callback:register() @@ -146,6 +168,7 @@ callback:register() ### In this example, when a non-aggressive creature enters a combat area, the creatureOnAreaCombat function returns false, stopping the associated function on the C++ side. + ## Multiple Callbacks for the Same Event You can define multiple callbacks for the same event type. This allows you to encapsulate different behaviors in separate callbacks, making your code more modular and easier to manage. diff --git a/data/scripts/eventcallbacks/creature/on_area_combat.lua b/data/scripts/eventcallbacks/creature/on_area_combat.lua index a6295074df3..f68cc95ccad 100644 --- a/data/scripts/eventcallbacks/creature/on_area_combat.lua +++ b/data/scripts/eventcallbacks/creature/on_area_combat.lua @@ -1,7 +1,7 @@ local callback = EventCallback() function callback.creatureOnAreaCombat(creature, tile, isAggressive) - return true + return RETURNVALUE_NOERROR end callback:register() diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 2aaadac4d61..175fad49e20 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -21,6 +21,8 @@ #include "items/weapons/weapons.hpp" #include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" +#include "lua/callbacks/event_callback.hpp" +#include "lua/callbacks/events_callbacks.hpp" int32_t Combat::getLevelFormula(std::shared_ptr<Player> player, const std::shared_ptr<Spell> wheelSpell, const CombatDamage &damage) const { if (!player) { @@ -273,8 +275,11 @@ ReturnValue Combat::canDoCombat(std::shared_ptr<Creature> caster, std::shared_pt } } } - - return g_events().eventCreatureOnAreaCombat(caster, tile, aggressive); + ReturnValue ret = g_events().eventCreatureOnAreaCombat(caster, tile, aggressive); + if (ret == RETURNVALUE_NOERROR) { + ret = g_callbacks().checkCallbackWithReturnValue(EventCallback_t::creatureOnTargetCombat, &EventCallback::creatureOnAreaCombat, caster, tile, aggressive); + } + return ret; } bool Combat::isInPvpZone(std::shared_ptr<Creature> attacker, std::shared_ptr<Creature> target) { @@ -409,7 +414,11 @@ ReturnValue Combat::canDoCombat(std::shared_ptr<Creature> attacker, std::shared_ } } } - return g_events().eventCreatureOnTargetCombat(attacker, target); + ReturnValue ret = g_events().eventCreatureOnTargetCombat(attacker, target); + if (ret == RETURNVALUE_NOERROR) { + ret = g_callbacks().checkCallbackWithReturnValue(EventCallback_t::creatureOnTargetCombat, &EventCallback::creatureOnTargetCombat, attacker, target); + } + return ret; } void Combat::setPlayerCombatValues(formulaType_t newFormulaType, double newMina, double newMinb, double newMaxa, double newMaxb) { diff --git a/src/lua/callbacks/events_callbacks.hpp b/src/lua/callbacks/events_callbacks.hpp index 53b119f6445..f71103047e6 100644 --- a/src/lua/callbacks/events_callbacks.hpp +++ b/src/lua/callbacks/events_callbacks.hpp @@ -89,6 +89,32 @@ class EventsCallbacks { } } } + /** + * @brief Checks if all registered callbacks of the specified event type succeed. + * @param eventType The type of event to check. + * @param callbackFunc Function pointer to the callback method. + * @param args Variadic arguments to pass to the callback function. + * @return ReturnValue enum. + */ + template <typename CallbackFunc, typename... Args> + ReturnValue checkCallbackWithReturnValue(EventCallback_t eventType, CallbackFunc callbackFunc, Args &&... args) { + ReturnValue res = RETURNVALUE_NOERROR; + for (const auto &callback : getCallbacksByType(eventType)) { + auto argsCopy = std::make_tuple(args...); + if (callback && callback->isLoadedCallback()) { + ReturnValue callbackResult = std::apply( + [&callback, &callbackFunc](auto &&... args) { + return ((*callback).*callbackFunc)(std::forward<decltype(args)>(args)...); + }, + argsCopy + ); + if (callbackResult != RETURNVALUE_NOERROR) { + return callbackResult; + } + } + } + return res; + } /** * @brief Checks if all registered callbacks of the specified event type succeed. From 95dd0fb3c5c04a49aa121323e5c8f8a417512c6e Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Sat, 22 Jun 2024 22:13:37 -0300 Subject: [PATCH 90/95] fix: hireling shop (#2707) --- src/creatures/npcs/npc.cpp | 9 ++++----- src/creatures/npcs/npc.hpp | 11 +++++++++-- src/creatures/players/player.cpp | 4 ++-- src/creatures/players/player.hpp | 2 +- src/enums/player_cyclopedia.hpp | 14 ++++++++++++++ src/lua/functions/creatures/npc/npc_functions.cpp | 2 +- src/server/network/protocol/protocolgame.cpp | 6 +++--- 7 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 53fcfd07a3c..8f6fa370087 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -101,14 +101,13 @@ void Npc::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) { } if (auto player = creature->getPlayer()) { + removeShopPlayer(player->getGUID()); onPlayerDisappear(player); } if (spawnNpc) { spawnNpc->startSpawnNpcCheck(); } - - shopPlayers.clear(); } void Npc::onCreatureMove(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, const Position &newPos, const std::shared_ptr<Tile> &oldTile, const Position &oldPos, bool teleport) { @@ -647,8 +646,8 @@ bool Npc::isShopPlayer(uint32_t playerGUID) const { return shopPlayers.find(playerGUID) != shopPlayers.end(); } -void Npc::addShopPlayer(uint32_t playerGUID) { - shopPlayers.insert(playerGUID); +void Npc::addShopPlayer(uint32_t playerGUID, const std::vector<ShopBlock> &shopItems) { + shopPlayers.try_emplace(playerGUID, shopItems); } void Npc::removeShopPlayer(uint32_t playerGUID) { @@ -656,7 +655,7 @@ void Npc::removeShopPlayer(uint32_t playerGUID) { } void Npc::closeAllShopWindows() { - for (const auto playerGUID : shopPlayers) { + for (const auto &[playerGUID, shopBlock] : shopPlayers) { const auto &player = g_game().getPlayerByGUID(playerGUID); if (player) { player->closeShopWindow(); diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 605aaae570e..c246aa4b68d 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -96,6 +96,13 @@ class Npc final : public Creature { } const std::vector<ShopBlock> &getShopItemVector(uint32_t playerGUID) const { + if (playerGUID != 0) { + auto it = shopPlayers.find(playerGUID); + if (it != shopPlayers.end() && !it->second.empty()) { + return it->second; + } + } + return npcType->info.shopItemVector; } @@ -160,7 +167,7 @@ class Npc final : public Creature { bool isShopPlayer(uint32_t playerGUID) const; - void addShopPlayer(uint32_t playerGUID); + void addShopPlayer(uint32_t playerGUID, const std::vector<ShopBlock> &shopItems); void removeShopPlayer(uint32_t playerGUID); void closeAllShopWindows(); @@ -179,7 +186,7 @@ class Npc final : public Creature { std::map<uint32_t, uint16_t> playerInteractions; - std::unordered_set<uint32_t> shopPlayers; + std::unordered_map<uint32_t, std::vector<ShopBlock>> shopPlayers; std::shared_ptr<NpcType> npcType; std::shared_ptr<SpawnNpc> spawnNpc; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 52ceb8b3377..49c553f1326 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1894,7 +1894,7 @@ void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) } } -bool Player::openShopWindow(std::shared_ptr<Npc> npc) { +bool Player::openShopWindow(std::shared_ptr<Npc> npc, const std::vector<ShopBlock> &shopItems) { Benchmark brenchmark; if (!npc) { g_logger().error("[Player::openShopWindow] - Npc is wrong or nullptr"); @@ -1906,7 +1906,7 @@ bool Player::openShopWindow(std::shared_ptr<Npc> npc) { return false; } - npc->addShopPlayer(getGUID()); + npc->addShopPlayer(getGUID(), shopItems); setShopOwner(npc); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 332887acc51..141caafacd9 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -850,7 +850,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void onWalkComplete() override; void stopWalk(); - bool openShopWindow(std::shared_ptr<Npc> npc); + bool openShopWindow(std::shared_ptr<Npc> npc, const std::vector<ShopBlock> &shopItems = {}); bool closeShopWindow(); bool updateSaleShopList(std::shared_ptr<Item> item); bool hasShopItemForSale(uint16_t itemId, uint8_t subType) const; diff --git a/src/enums/player_cyclopedia.hpp b/src/enums/player_cyclopedia.hpp index f0637011a19..c6e1b7032c0 100644 --- a/src/enums/player_cyclopedia.hpp +++ b/src/enums/player_cyclopedia.hpp @@ -36,3 +36,17 @@ enum CyclopediaTitle_t : uint8_t { MAP, OTHERS, }; + +enum class CyclopediaMapData_t : uint8_t { + MinimapMarker = 0, + DiscoveryData = 1, + ActiveRaid = 2, + ImminentRaidMainArea = 3, + ImminentRaidSubArea = 4, + SetDiscoveryArea = 5, + Passage = 6, + SubAreaMonsters = 7, + MonsterBestiary = 8, + Donations = 9, + SetCurrentArea = 10, +}; diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index 2b14ef46b72..13e9b499abc 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -404,7 +404,7 @@ int NpcFunctions::luaNpcOpenShopWindowTable(lua_State* L) { } lua_pop(L, 3); - pushBoolean(L, player->openShopWindow(npc)); + pushBoolean(L, player->openShopWindow(npc, items)); return 1; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index ddfc867f918..58f06eee5ae 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3381,7 +3381,7 @@ void ProtocolGame::sendAddMarker(const Position &pos, uint8_t markType, const st msg.addByte(0xDD); if (!oldProtocol) { - msg.addByte(0x00); // unknow + msg.addByte(enumToValue(CyclopediaMapData_t::MinimapMarker)); } msg.addPosition(pos); @@ -4644,7 +4644,7 @@ void ProtocolGame::sendShop(std::shared_ptr<Npc> npc) { if (!oldProtocol) { msg.add<uint16_t>(npc->getCurrency()); - msg.addString(std::string(), "ProtocolGame::sendShop - std::string()"); // Currency name + msg.addString(std::string()); // Currency name } const auto &shoplist = npc->getShopItemVector(player->getGUID()); @@ -7472,7 +7472,7 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr<Creature> cr } if (!oldProtocol && creature->isHealthHidden()) { - msg.addString("", "ProtocolGame::AddCreature - empty"); + msg.addString(std::string()); } else { msg.addString(creature->getName(), "ProtocolGame::AddCreature - creature->getName()"); } From bfb3149e165f6f652b6f3e5361e36a0c92633697 Mon Sep 17 00:00:00 2001 From: Luan Luciano <luanluciano@outlook.com> Date: Sat, 22 Jun 2024 22:22:26 -0300 Subject: [PATCH 91/95] fix: correction in checking inbox:getMaxCapacity() (#2679) --- data-otservbr-global/npc/emael.lua | 2 +- data-otservbr-global/npc/emperor_kruzak.lua | 2 +- data-otservbr-global/npc/hireling.lua | 2 +- data-otservbr-global/npc/king_tibianus.lua | 2 +- data-otservbr-global/npc/queen_eloise.lua | 2 +- data-otservbr-global/npc/walter_jaeger.lua | 2 +- data/libs/systems/hireling.lua | 4 ++-- data/modules/scripts/daily_reward/daily_reward.lua | 2 +- data/scripts/talkactions/player/reward.lua | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data-otservbr-global/npc/emael.lua b/data-otservbr-global/npc/emael.lua index 79111f8f324..4fff95b1b95 100644 --- a/data-otservbr-global/npc/emael.lua +++ b/data-otservbr-global/npc/emael.lua @@ -69,7 +69,7 @@ local function creatureSayCallback(npc, creature, type, message) npcHandler:say("Ah, I see you killed a lot of dangerous creatures. Here's your podium of vigour!", npc, creature) local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() then + if inbox and #inboxItems < inbox:getMaxCapacity() then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a <" .. ItemType(38707):getName() .. ">.") diff --git a/data-otservbr-global/npc/emperor_kruzak.lua b/data-otservbr-global/npc/emperor_kruzak.lua index 4f838396df0..daf0742f615 100644 --- a/data-otservbr-global/npc/emperor_kruzak.lua +++ b/data-otservbr-global/npc/emperor_kruzak.lua @@ -82,7 +82,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() then + if inbox and #inboxItems < inbox:getMaxCapacity() then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua index aad7785079d..6897bafdcf7 100644 --- a/data-otservbr-global/npc/hireling.lua +++ b/data-otservbr-global/npc/hireling.lua @@ -521,7 +521,7 @@ function createHirelingType(HirelingName) local inboxItems = inbox:getItems() if player:getFreeCapacity() < itType:getWeight(1) then npcHandler:say("Sorry, but you don't have enough capacity.", npc, creature) - elseif not inbox or #inboxItems > inbox:getMaxCapacity() then + 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 diff --git a/data-otservbr-global/npc/king_tibianus.lua b/data-otservbr-global/npc/king_tibianus.lua index de47e45acca..d9968357fa1 100644 --- a/data-otservbr-global/npc/king_tibianus.lua +++ b/data-otservbr-global/npc/king_tibianus.lua @@ -87,7 +87,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() then + if inbox and #inboxItems < inbox:getMaxCapacity() then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/queen_eloise.lua b/data-otservbr-global/npc/queen_eloise.lua index a9397061a56..0467bb6e74a 100644 --- a/data-otservbr-global/npc/queen_eloise.lua +++ b/data-otservbr-global/npc/queen_eloise.lua @@ -77,7 +77,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() then + if inbox and #inboxItems < inbox:getMaxCapacity() then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/walter_jaeger.lua b/data-otservbr-global/npc/walter_jaeger.lua index 6911d7ed323..6b0e26075d6 100644 --- a/data-otservbr-global/npc/walter_jaeger.lua +++ b/data-otservbr-global/npc/walter_jaeger.lua @@ -283,7 +283,7 @@ local function processItemInboxPurchase(player, name, id) local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() then + if inbox and #inboxItems < inbox:getMaxCapacity() then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item with the Walter Jaeger.\nUnwrap it in your own house to create a <" .. name .. ">.") diff --git a/data/libs/systems/hireling.lua b/data/libs/systems/hireling.lua index 8b5784759fa..30134cd7cd0 100644 --- a/data/libs/systems/hireling.lua +++ b/data/libs/systems/hireling.lua @@ -361,7 +361,7 @@ function Hireling:returnToLamp(player_id) local inbox = owner:getStoreInbox() local inboxItems = inbox:getItems() - if not inbox or #inboxItems > inbox:getMaxCapacity() then + if not inbox or #inboxItems >= inbox:getMaxCapacity() then owner:getPosition():sendMagicEffect(CONST_ME_POFF) return owner:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.") end @@ -556,7 +556,7 @@ function Player:addNewHireling(name, sex) local inbox = self:getStoreInbox() local inboxItems = inbox:getItems() - if not inbox or #inboxItems > inbox:getMaxCapacity() then + if not inbox or #inboxItems >= inbox:getMaxCapacity() then self:getPosition():sendMagicEffect(CONST_ME_POFF) self:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.") return false diff --git a/data/modules/scripts/daily_reward/daily_reward.lua b/data/modules/scripts/daily_reward/daily_reward.lua index 09b927cedda..b6a7c16993a 100644 --- a/data/modules/scripts/daily_reward/daily_reward.lua +++ b/data/modules/scripts/daily_reward/daily_reward.lua @@ -454,7 +454,7 @@ function Player.selectDailyReward(self, msg) -- Adding items to store inbox local inbox = self:getStoreInbox() local inboxItems = inbox:getItems() - if not inbox or #inboxItems > inbox:getMaxCapacity() then + if not inbox or #inboxItems >= inbox:getMaxCapacity() then self:sendError("You do not have enough space in your store inbox.") return false end diff --git a/data/scripts/talkactions/player/reward.lua b/data/scripts/talkactions/player/reward.lua index 3f4cc3787de..a05dab3a933 100644 --- a/data/scripts/talkactions/player/reward.lua +++ b/data/scripts/talkactions/player/reward.lua @@ -26,7 +26,7 @@ local function sendExerciseRewardModal(player) local inbox = player:getStoreInbox() local inboxItems = inbox:getItems() - if inbox and #inboxItems <= inbox:getMaxCapacity() and player:getFreeCapacity() >= iType:getWeight() then + if inbox and #inboxItems < inbox:getMaxCapacity() and player:getFreeCapacity() >= iType:getWeight() then local item = inbox:addItem(it.id, it.charges) if item then item:setActionId(IMMOVABLE_ACTION_ID) From 0fdd8b042bc868ddd85ee0247cd07bb9e7febac2 Mon Sep 17 00:00:00 2001 From: Beats <daniel15042014@hotmail.com> Date: Mon, 24 Jun 2024 13:23:51 -0400 Subject: [PATCH 92/95] fix: clang format (#2710) --- .clang-format | 4 +- .github/workflows/clang-lint.yml | 6 +- src/canary_server.cpp | 10 +- src/creatures/appearance/outfit/outfit.cpp | 4 +- src/creatures/combat/combat.cpp | 40 +-- src/creatures/combat/condition.cpp | 2 +- src/creatures/combat/spells.cpp | 20 +- src/creatures/creature.cpp | 2 +- src/creatures/creatures_definitions.hpp | 12 +- src/creatures/interactions/chat.cpp | 16 +- src/creatures/monsters/monster.cpp | 28 +-- src/creatures/monsters/monsters.cpp | 20 +- src/creatures/npcs/npc.cpp | 4 +- .../achievement/player_achievement.cpp | 4 +- .../players/cyclopedia/player_badge.cpp | 2 +- .../players/cyclopedia/player_title.cpp | 2 +- src/creatures/players/grouping/familiars.cpp | 2 +- src/creatures/players/grouping/groups.cpp | 2 +- src/creatures/players/grouping/party.hpp | 2 +- .../players/imbuements/imbuements.cpp | 6 +- src/creatures/players/player.cpp | 32 +-- src/creatures/players/player.hpp | 10 +- src/creatures/players/vocations/vocation.cpp | 12 +- src/database/databasemanager.cpp | 4 +- src/game/game.cpp | 94 +++---- src/game/movement/teleport.cpp | 4 +- src/io/io_bosstiary.cpp | 6 +- src/io/iobestiary.cpp | 6 +- src/io/iomap.cpp | 54 ++-- src/io/ioprey.cpp | 6 +- src/items/decay/decay.cpp | 8 +- src/items/functions/item/item_parse.cpp | 4 +- src/items/item.cpp | 32 +-- src/items/items.cpp | 8 +- src/items/tile.cpp | 2 +- src/items/weapons/weapons.cpp | 4 +- src/lua/callbacks/event_callback.cpp | 230 +++++++++--------- src/lua/creature/actions.cpp | 10 +- src/lua/creature/actions.hpp | 8 +- src/lua/creature/creatureevent.cpp | 60 ++--- src/lua/creature/events.cpp | 194 +++++++-------- src/lua/creature/movement.cpp | 32 +-- src/lua/creature/movement.hpp | 8 +- src/lua/creature/raids.cpp | 104 ++++---- src/lua/creature/talkaction.cpp | 4 +- .../functions/core/game/game_functions.cpp | 4 +- .../creatures/combat/spell_functions.cpp | 22 +- .../creatures/creature_functions.cpp | 2 +- .../creatures/monster/loot_functions.cpp | 12 +- .../monster/monster_type_functions.cpp | 12 +- .../creatures/npc/shop_functions.cpp | 12 +- .../creatures/player/player_functions.cpp | 16 +- src/lua/functions/events/action_functions.cpp | 2 +- .../events/creature_event_functions.cpp | 4 +- .../events/global_event_functions.cpp | 14 +- .../functions/events/move_event_functions.cpp | 8 +- src/lua/functions/items/item_functions.cpp | 2 +- src/lua/functions/items/weapon_functions.cpp | 16 +- src/lua/functions/lua_functions_loader.cpp | 4 +- src/lua/global/baseevents.cpp | 6 +- src/lua/global/globalevent.cpp | 16 +- src/lua/scripts/lua_environment.cpp | 4 +- src/lua/scripts/scripts.cpp | 2 +- src/map/house/house.cpp | 4 +- src/map/house/housetile.cpp | 6 +- src/map/mapcache.cpp | 2 +- src/map/spectators.cpp | 10 +- src/server/network/connection/connection.cpp | 2 +- src/server/network/protocol/protocol.cpp | 2 +- src/server/network/protocol/protocol.hpp | 2 +- src/server/network/protocol/protocolgame.cpp | 46 ++-- src/server/network/protocol/protocollogin.cpp | 2 +- .../network/protocol/protocolstatus.cpp | 4 +- src/server/server.hpp | 8 +- src/server/signals.cpp | 4 +- src/utils/simd.hpp | 2 +- 76 files changed, 688 insertions(+), 688 deletions(-) diff --git a/.clang-format b/.clang-format index de45e1b2e7a..289f5508316 100644 --- a/.clang-format +++ b/.clang-format @@ -111,7 +111,7 @@ QualifierAlignment: Left ReferenceAlignment: Right ReflowComments: true RemoveBracesLLVM: false -SortIncludes: false +SortIncludes: Never SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false @@ -138,4 +138,4 @@ StatementMacros: - QT_REQUIRE_VERSION TabWidth: 4 UseCRLF: false -UseTab: true +UseTab: AlignWithSpaces diff --git a/.github/workflows/clang-lint.yml b/.github/workflows/clang-lint.yml index 67e6427d853..b57e407cbb9 100644 --- a/.github/workflows/clang-lint.yml +++ b/.github/workflows/clang-lint.yml @@ -37,17 +37,17 @@ jobs: - name: Run clang format lint if: ${{ github.ref != 'refs/heads/main' }} - uses: DoozyX/clang-format-lint-action@v0.16.2 + uses: DoozyX/clang-format-lint-action@v0.17 with: source: "src" exclude: "src/protobuf" extensions: "cpp,hpp,h" - clangFormatVersion: 16 + clangFormatVersion: 17 inplace: true - name: Run add and commit if: ${{ github.ref != 'refs/heads/main' }} - uses: EndBug/add-and-commit@v9 + uses: EndBug/add-and-commit@v9.1.4 with: author_name: GitHub Actions author_email: github-actions[bot]@users.noreply.github.com diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 17a36ff3d23..e49a86d7d9f 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -93,8 +93,8 @@ int CanaryServer::run() { #ifndef _WIN32 if (getuid() == 0 || geteuid() == 0) { logger.warn("{} has been executed as root user, " - "please consider running it as a normal user", - ProtocolStatus::SERVER_NAME); + "please consider running it as a normal user", + ProtocolStatus::SERVER_NAME); } #endif @@ -213,7 +213,7 @@ void CanaryServer::logInfos() { logger.info("A server developed by: {}", ProtocolStatus::SERVER_DEVELOPERS); logger.info("Visit our website for updates, support, and resources: " - "https://docs.opentibiabr.com/"); + "https://docs.opentibiabr.com/"); } /** @@ -234,7 +234,7 @@ void CanaryServer::toggleForceCloseButton() { void CanaryServer::badAllocationHandler() { // Use functions that only use stack allocation g_logger().error("Allocation failed, server out of memory, " - "decrease the size of your map or compile in 64 bits mode"); + "decrease the size of your map or compile in 64 bits mode"); if (isatty(STDIN_FILENO)) { getchar(); @@ -318,7 +318,7 @@ void CanaryServer::initializeDatabase() { DatabaseManager::updateDatabase(); if (g_configManager().getBoolean(OPTIMIZE_DATABASE, __FUNCTION__) - && !DatabaseManager::optimizeTables()) { + && !DatabaseManager::optimizeTables()) { logger.debug("No tables were optimized"); } } diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 97120659333..251bf7bde35 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -58,8 +58,8 @@ bool Outfits::loadFromXml() { } if (auto lookType = pugi::cast<uint16_t>(lookTypeAttribute.value()); - g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 - && !g_game().isLookTypeRegistered(lookType)) { + g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 + && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was ignored to prevent client crash.", lookType); continue; } diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 175fad49e20..87f7500d0a9 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -657,8 +657,8 @@ CombatDamage Combat::applyImbuementElementalDamage(std::shared_ptr<Player> attac } if (imbuementInfo.imbuement->combatType == COMBAT_NONE - || damage.primary.type == COMBAT_HEALING - || damage.secondary.type == COMBAT_HEALING) { + || damage.primary.type == COMBAT_HEALING + || damage.secondary.type == COMBAT_HEALING) { continue; } @@ -1256,8 +1256,8 @@ void Combat::doCombatHealth(std::shared_ptr<Creature> caster, std::shared_ptr<Cr void Combat::doCombatHealth(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const Position &origin, CombatDamage &damage, const CombatParams ¶ms) { bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR); if ((caster && target) - && (caster == target || canCombat) - && (params.impactEffect != CONST_ME_NONE)) { + && (caster == target || canCombat) + && (params.impactEffect != CONST_ME_NONE)) { g_game().addMagicEffect(target->getPosition(), params.impactEffect); } @@ -1300,8 +1300,8 @@ void Combat::doCombatMana(std::shared_ptr<Creature> caster, std::shared_ptr<Crea void Combat::doCombatMana(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const Position &origin, CombatDamage &damage, const CombatParams ¶ms) { bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR); if ((caster && target) - && (caster == target || canCombat) - && (params.impactEffect != CONST_ME_NONE)) { + && (caster == target || canCombat) + && (params.impactEffect != CONST_ME_NONE)) { g_game().addMagicEffect(target->getPosition(), params.impactEffect); } @@ -1368,8 +1368,8 @@ void Combat::doCombatDispel(std::shared_ptr<Creature> caster, const Position &po void Combat::doCombatDispel(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const CombatParams ¶ms) { bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR); if ((caster && target) - && (caster == target || canCombat) - && (params.impactEffect != CONST_ME_NONE)) { + && (caster == target || canCombat) + && (params.impactEffect != CONST_ME_NONE)) { g_game().addMagicEffect(target->getPosition(), params.impactEffect); } @@ -1408,7 +1408,7 @@ void Combat::doCombatDefault(std::shared_ptr<Creature> caster, std::shared_ptr<C /* if (params.impactEffect != CONST_ME_NONE) { - g_game().addMagicEffect(target->getPosition(), params.impactEffect); + g_game().addMagicEffect(target->getPosition(), params.impactEffect); } */ @@ -1540,8 +1540,8 @@ void ValueCallback::getMinMaxValues(std::shared_ptr<Player> player, CombatDamage // onGetPlayerMinMaxValues(...) if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[ValueCallback::getMinMaxValues - Player {} formula {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(type)); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(type)); return; } @@ -1633,8 +1633,8 @@ void TileCallback::onTileCombat(std::shared_ptr<Creature> creature, std::shared_ // onTileCombat(creature, pos) if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[TileCallback::onTileCombat - Creature {} type {} on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ()); return; } @@ -1664,8 +1664,8 @@ void TargetCallback::onTargetCombat(std::shared_ptr<Creature> creature, std::sha // onTargetCombat(creature, target) if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[TargetCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return; } @@ -1724,8 +1724,8 @@ void ChainCallback::onChainCombat(std::shared_ptr<Creature> creature, uint8_t &m // onChainCombat(creature) if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[ChainCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return; } @@ -1766,8 +1766,8 @@ bool ChainPickerCallback::onChainCombat(std::shared_ptr<Creature> creature, std: // onChainCombat(creature, target) if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[ChainPickerCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return true; } @@ -2178,7 +2178,7 @@ void Combat::applyExtensions(std::shared_ptr<Creature> caster, std::shared_ptr<C if (player) { // Fatal hit (onslaught) if (auto playerWeapon = player->getInventoryItem(CONST_SLOT_LEFT); - playerWeapon != nullptr && playerWeapon->getTier() > 0) { + playerWeapon != nullptr && playerWeapon->getTier() > 0) { double_t fatalChance = playerWeapon->getFatalChance(); double_t randomChance = uniform_random(0, 10000) / 100; if (fatalChance > 0 && randomChance < fatalChance) { diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 7f477f1d859..b9603d010a2 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -2042,7 +2042,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr<Creature> creature, int32 g_dispatcher().addEvent([id = creature->getID(), listDir = listDir.data()] { g_game().forcePlayerAutoWalk(id, listDir); }, - "ConditionFeared::executeCondition"); + "ConditionFeared::executeCondition"); g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index dd4305de14e..f8852c5e534 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -108,7 +108,7 @@ void Spells::clear() { bool Spells::hasInstantSpell(const std::string &word) const { if (auto iterate = instants.find(word); - iterate != instants.end()) { + iterate != instants.end()) { return true; } return false; @@ -127,8 +127,8 @@ bool Spells::registerInstantLuaEvent(const std::shared_ptr<InstantSpell> instant // Checks if there is any spell registered with the same name if (hasInstantSpell(words)) { g_logger().warn("[Spells::registerInstantLuaEvent] - " - "Duplicate registered instant spell with words: {}, on spell with name: {}", - words, instantName); + "Duplicate registered instant spell with words: {}, on spell with name: {}", + words, instantName); return false; } // Register spell word in the map @@ -166,7 +166,7 @@ std::list<uint16_t> Spells::getSpellsByVocation(uint16_t vocationId) { vocSpellsIt = vocSpells.find(vocationId); if (vocSpellsIt != vocSpells.end() - && vocSpellsIt->second) { + && vocSpellsIt->second) { spellsList.push_back(it.second->getSpellId()); } } @@ -361,8 +361,8 @@ bool CombatSpell::executeCastSpell(std::shared_ptr<Creature> creature, const Lua // onCastSpell(creature, var) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CombatSpell::executeCastSpell - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -950,8 +950,8 @@ bool InstantSpell::executeCastSpell(std::shared_ptr<Creature> creature, const Lu // onCastSpell(creature, var) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[InstantSpell::executeCastSpell - Creature {} words {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getWords()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getWords()); return false; } @@ -1095,8 +1095,8 @@ bool RuneSpell::executeCastSpell(std::shared_ptr<Creature> creature, const LuaVa // onCastSpell(creature, var, isHotkey) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[RuneSpell::executeCastSpell - Creature {} runeId {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getRuneItemId()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getRuneItemId()); return false; } diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index d033d59c829..c68ab4f351b 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -828,7 +828,7 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared g_dispatcher().addEvent([player, corpseContainer, corpsePosition = corpse->getPosition()] { g_game().playerQuickLootCorpse(player, corpseContainer, corpsePosition); }, - "Game::playerQuickLootCorpse"); + "Game::playerQuickLootCorpse"); } } } diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 33f62dbd2aa..74ec6c5f2c8 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -1524,12 +1524,12 @@ using StashItemList = std::map<uint16_t, uint32_t>; using ItemsTierCountList = std::map<uint16_t, std::map<uint8_t, uint32_t>>; /* - > ItemsTierCountList structure: - |- [itemID] - |- [itemTier] - |- Count - | ... - | ... + > ItemsTierCountList structure: + |- [itemID] + |- [itemTier] + |- Count + | ... + | ... */ struct ProtocolFamiliars { diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index e16af670fcc..900247e457a 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -145,8 +145,8 @@ bool ChatChannel::executeCanJoinEvent(const std::shared_ptr<Player> &player) { LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[CanJoinChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -171,8 +171,8 @@ bool ChatChannel::executeOnJoinEvent(const std::shared_ptr<Player> &player) { LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[OnJoinChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested", + player->getName(), getName()); return false; } @@ -197,8 +197,8 @@ bool ChatChannel::executeOnLeaveEvent(const std::shared_ptr<Player> &player) { LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[OnLeaveChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -223,8 +223,8 @@ bool ChatChannel::executeOnSpeakEvent(const std::shared_ptr<Player> &player, Spe LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[OnSpeakChannelEvent::execute - Player {}, type {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(type)); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(type)); return false; } diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 5e0a7d16bb4..4b993cbab0e 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -50,8 +50,8 @@ Monster::Monster(const std::shared_ptr<MonsterType> mType) : for (const std::string &scriptName : mType->info.scripts) { if (!registerCreatureEvent(scriptName)) { g_logger().warn("[Monster::Monster] - " - "Unknown event name: {}", - scriptName); + "Unknown event name: {}", + scriptName); } } } @@ -138,8 +138,8 @@ void Monster::onCreatureAppear(std::shared_ptr<Creature> creature, bool isLogin) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[Monster::onCreatureAppear - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -176,8 +176,8 @@ void Monster::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[Monster::onCreatureDisappear - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -217,8 +217,8 @@ void Monster::onCreatureMove(const std::shared_ptr<Creature> &creature, const st LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("[Monster::onCreatureMove - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -291,8 +291,8 @@ void Monster::onCreatureSay(std::shared_ptr<Creature> creature, SpeakClasses typ LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("Monster {} creature {}] Call stack overflow. Too many lua " - "script calls being nested.", - getName(), creature->getName()); + "script calls being nested.", + getName(), creature->getName()); return; } @@ -771,8 +771,8 @@ void Monster::onThink(uint32_t interval) { LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("Monster {} Call stack overflow. Too many lua script calls " - "being nested.", - getName()); + "being nested.", + getName()); return; } @@ -2041,8 +2041,8 @@ void Monster::dropLoot(std::shared_ptr<Container> corpse, std::shared_ptr<Creatu if (corpse && lootDrop) { // Only fiendish drops sliver if (ForgeClassifications_t classification = getMonsterForgeClassification(); - // Condition - classification == ForgeClassifications_t::FORGE_FIENDISH_MONSTER) { + // Condition + classification == ForgeClassifications_t::FORGE_FIENDISH_MONSTER) { auto minSlivers = g_configManager().getNumber(FORGE_MIN_SLIVERS, __FUNCTION__); auto maxSlivers = g_configManager().getNumber(FORGE_MAX_SLIVERS, __FUNCTION__); diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 3d457aa4d47..78358f69d87 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -97,7 +97,7 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell } if (std::string spellName = asLowerCaseString(spell->name); - spellName == "melee") { + spellName == "melee") { sb.isMelee = true; if (spell->attack > 0 && spell->skill > 0) { @@ -164,8 +164,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell condition->setOutfit(outfit); } else { g_logger().error("[Monsters::deserializeSpell] - " - "Missing outfit monster or item in outfit spell for: {}", - description); + "Missing outfit monster or item in outfit spell for: {}", + description); return false; } @@ -208,8 +208,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell } else if (spellName == "condition") { if (spell->conditionType == CONDITION_NONE) { g_logger().error("[Monsters::deserializeSpell] - " - "{} condition is not set for: {}", - description, spell->name); + "{} condition is not set for: {}", + description, spell->name); } } else if (spellName == "strength") { // @@ -217,8 +217,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> spell, spell // } else { g_logger().error("[Monsters::deserializeSpell] - " - "{} unknown or missing parameter on spell with name: {}", - description, spell->name); + "{} unknown or missing parameter on spell with name: {}", + description, spell->name); } if (spell->shoot != CONST_ANI_NONE) { @@ -295,9 +295,9 @@ bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) { std::shared_ptr<MonsterType> Monsters::getMonsterType(const std::string &name, bool silent /* = false*/) const { std::string lowerCaseName = asLowerCaseString(name); if (auto it = monsters.find(lowerCaseName); - it != monsters.end() - // We will only return the MonsterType if it match the exact name of the monster - && it->first.find(lowerCaseName) != it->first.npos) { + it != monsters.end() + // We will only return the MonsterType if it match the exact name of the monster + && it->first.find(lowerCaseName) != it->first.npos) { return it->second; } if (!silent) { diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 8f6fa370087..fdbf58853b4 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -521,7 +521,7 @@ void Npc::onThinkWalk(uint32_t interval) { } if (Direction newDirection; - getRandomStep(newDirection)) { + getRandomStep(newDirection)) { listWalkDir.push_front(newDirection); addEventWalk(); } @@ -633,7 +633,7 @@ bool Npc::getRandomStep(Direction &moveDirection) { std::ranges::shuffle(directionvector, getRandomGenerator()); for (const Position &creaturePos = getPosition(); - Direction direction : directionvector) { + Direction direction : directionvector) { if (canWalkTo(creaturePos, direction)) { moveDirection = direction; return true; diff --git a/src/creatures/players/achievement/player_achievement.cpp b/src/creatures/players/achievement/player_achievement.cpp index 2db53dbe776..69d1d7ab1fa 100644 --- a/src/creatures/players/achievement/player_achievement.cpp +++ b/src/creatures/players/achievement/player_achievement.cpp @@ -53,7 +53,7 @@ bool PlayerAchievement::remove(uint16_t id) { if (auto it = std::find_if(m_achievementsUnlocked.begin(), m_achievementsUnlocked.end(), [id](auto achievement_it) { return achievement_it.first == id; }); - it != m_achievementsUnlocked.end()) { + it != m_achievementsUnlocked.end()) { getUnlockedKV()->remove(achievement.name); m_achievementsUnlocked.erase(it); removePoints(achievement.points); @@ -72,7 +72,7 @@ bool PlayerAchievement::isUnlocked(uint16_t id) const { if (auto it = std::find_if(m_achievementsUnlocked.begin(), m_achievementsUnlocked.end(), [id](auto achievement_it) { return achievement_it.first == id; }); - it != m_achievementsUnlocked.end()) { + it != m_achievementsUnlocked.end()) { return true; } diff --git a/src/creatures/players/cyclopedia/player_badge.cpp b/src/creatures/players/cyclopedia/player_badge.cpp index 9b892a6164c..639640b2ffe 100644 --- a/src/creatures/players/cyclopedia/player_badge.cpp +++ b/src/creatures/players/cyclopedia/player_badge.cpp @@ -26,7 +26,7 @@ bool PlayerBadge::hasBadge(uint8_t id) const { if (auto it = std::find_if(m_badgesUnlocked.begin(), m_badgesUnlocked.end(), [id](auto badge_it) { return badge_it.first.m_id == id; }); - it != m_badgesUnlocked.end()) { + it != m_badgesUnlocked.end()) { return true; } diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp index 624b0313457..a6b44f3d3c4 100644 --- a/src/creatures/players/cyclopedia/player_title.cpp +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -26,7 +26,7 @@ bool PlayerTitle::isTitleUnlocked(uint8_t id) const { if (auto it = std::find_if(m_titlesUnlocked.begin(), m_titlesUnlocked.end(), [id](auto title_it) { return title_it.first.m_id == id; }); - it != m_titlesUnlocked.end()) { + it != m_titlesUnlocked.end()) { return true; } diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp index 6e923b823dc..6312aaa285d 100644 --- a/src/creatures/players/grouping/familiars.cpp +++ b/src/creatures/players/grouping/familiars.cpp @@ -77,7 +77,7 @@ std::shared_ptr<Familiar> Familiars::getFamiliarByLookType(uint16_t vocation, ui if (auto it = std::find_if(familiars[vocation].begin(), familiars[vocation].end(), [lookType](auto familiar_it) { return familiar_it->lookType == lookType; }); - it != familiars[vocation].end()) { + it != familiars[vocation].end()) { return *it; } return nullptr; diff --git a/src/creatures/players/grouping/groups.cpp b/src/creatures/players/grouping/groups.cpp index 937fa848f3f..c4dab4a9039 100644 --- a/src/creatures/players/grouping/groups.cpp +++ b/src/creatures/players/grouping/groups.cpp @@ -103,7 +103,7 @@ std::shared_ptr<Group> Groups::getGroup(uint16_t id) const { if (auto it = std::find_if(groups_vector.begin(), groups_vector.end(), [id](auto group_it) { return group_it->id == id; }); - it != groups_vector.end()) { + it != groups_vector.end()) { return *it; } return nullptr; diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index 10663a278bf..6aaecc56190 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -108,7 +108,7 @@ class Party : public SharedObject { if (auto it = std::find_if(membersData.begin(), membersData.end(), [playerId](const std::shared_ptr<PartyAnalyzer> &preyIt) { return preyIt->id == playerId; }); - it != membersData.end()) { + it != membersData.end()) { return *it; } diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp index ed312dbf8e1..4bfb0de836b 100644 --- a/src/creatures/players/imbuements/imbuements.cpp +++ b/src/creatures/players/imbuements/imbuements.cpp @@ -347,9 +347,9 @@ std::vector<Imbuement*> Imbuements::getImbuements(std::shared_ptr<Player> player // Parse the storages for each imbuement in imbuements.xml and config.lua (enable/disable storage) if (g_configManager().getBoolean(TOGGLE_IMBUEMENT_SHRINE_STORAGE, __FUNCTION__) - && imbuement->getStorage() != 0 - && player->getStorageValue(imbuement->getStorage() == -1) - && imbuement->getBaseID() >= 1 && imbuement->getBaseID() <= 3) { + && imbuement->getStorage() != 0 + && player->getStorageValue(imbuement->getStorage() == -1) + && imbuement->getBaseID() >= 1 && imbuement->getBaseID() <= 3) { continue; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 49c553f1326..a9320ffba5f 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -284,7 +284,7 @@ std::shared_ptr<Item> Player::getQuiverAmmoOfType(const ItemType &it) const { std::shared_ptr<Item> quiver = inventory[CONST_SLOT_RIGHT]; for (std::shared_ptr<Container> container = quiver->getContainer(); - auto ammoItem : container->getItemList()) { + auto ammoItem : container->getItemList()) { if (ammoItem->getAmmoType() == it.ammoType) { if (level >= Item::items[ammoItem->getID()].minReqLevel) { return ammoItem; @@ -3835,7 +3835,7 @@ bool Player::hasItemCountById(uint16_t itemId, uint32_t itemAmount, bool checkSt // Check items from stash for (StashItemList stashToSend = getStashItems(); - auto [stashItemId, itemCount] : stashToSend) { + auto [stashItemId, itemCount] : stashToSend) { if (!checkStash) { break; } @@ -5392,7 +5392,7 @@ uint16_t Player::getSkillLevel(skills_t skill) const { skillLevel = std::max<int32_t>(0, skillLevel + varSkills[skill]); if (auto it = maxValuePerSkill.find(skill); - it != maxValuePerSkill.end()) { + it != maxValuePerSkill.end()) { skillLevel = std::min<int32_t>(it->second, skillLevel); } @@ -6228,7 +6228,7 @@ std::pair<uint64_t, uint64_t> Player::getForgeSliversAndCores() const { // Check items from stash for (StashItemList stashToSend = getStashItems(); - auto [itemId, itemCount] : stashToSend) { + auto [itemId, itemCount] : stashToSend) { if (itemId == ITEM_FORGE_SLIVER) { sliverCount += itemCount; } @@ -6624,7 +6624,7 @@ std::string Player::getBlessingsName() const { for (uint8_t i = 1; i <= 8; i++) { if (hasBlessing(i)) { if (auto blessName = BlessingNames.find(static_cast<Blessings_t>(i)); - blessName != BlessingNames.end()) { + blessName != BlessingNames.end()) { os << (*blessName).second; } else { continue; @@ -6817,7 +6817,7 @@ void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier) { uint32_t stashCount = 0; if (const ItemType &iType = Item::items[itemId]; - iType.stackable && iType.wareId > 0) { + iType.stackable && iType.wareId > 0) { stashCount = getStashItemCount(itemId); } @@ -6866,10 +6866,10 @@ void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty() || - // Retrieve from inbox. - (c->isInbox() && isDepot) || - // Retrieve from depot. - (!c->isInbox() && !isDepot)) { + // Retrieve from inbox. + (c->isInbox() && isDepot) || + // Retrieve from depot. + (!c->isInbox() && !isDepot)) { continue; } @@ -6935,7 +6935,7 @@ std::shared_ptr<Item> Player::getItemFromDepotSearch(uint16_t itemId, const Posi for (const std::shared_ptr<Item> &locker : depotLocker->getItemList()) { std::shared_ptr<Container> c = locker->getContainer(); if (!c || c->empty() || (c->isInbox() && pos.y != 0x21) || // From inbox. - (!c->isInbox() && pos.y != 0x20)) { // From depot. + (!c->isInbox() && pos.y != 0x20)) { // From depot. continue; } @@ -7119,7 +7119,7 @@ void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint return; } if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1); - returnValue != RETURNVALUE_NOERROR) { + returnValue != RETURNVALUE_NOERROR) { g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); @@ -7362,7 +7362,7 @@ void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemI return; } if (returnValue = g_game().internalRemoveItem(receiveItem, 1); - returnValue != RETURNVALUE_NOERROR) { + returnValue != RETURNVALUE_NOERROR) { g_logger().error("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); @@ -7502,7 +7502,7 @@ void Player::forgeResourceConversion(ForgeAction_t actionType) { } if (std::shared_ptr<Item> item = Item::CreateItem(ITEM_FORGE_CORE, 1); - item) { + item) { returnValue = g_game().internalPlayerAddItem(static_self_cast<Player>(), item); } if (returnValue != RETURNVALUE_NOERROR) { @@ -7524,7 +7524,7 @@ void Player::forgeResourceConversion(ForgeAction_t actionType) { auto upgradeCost = dustLevel - 75; if (auto dusts = getForgeDusts(); - upgradeCost > dusts) { + upgradeCost > dusts) { g_logger().error("[{}] Not enough dust", __FUNCTION__); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -8100,7 +8100,7 @@ bool Player::hasPermittedConditionInPZ() const { uint16_t Player::getDodgeChance() const { uint16_t chance = 0; if (auto playerArmor = getInventoryItem(CONST_SLOT_ARMOR); - playerArmor != nullptr && playerArmor->getTier()) { + playerArmor != nullptr && playerArmor->getTier()) { chance += static_cast<uint16_t>(playerArmor->getDodgeChance() * 100); } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 141caafacd9..04b6a139a02 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1956,7 +1956,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isImmuneCleanse(ConditionType_t conditiontype) { uint64_t timenow = OTSYS_TIME(); if ((cleanseCondition.first == conditiontype) - && (timenow <= cleanseCondition.second)) { + && (timenow <= cleanseCondition.second)) { return true; } return false; @@ -2154,7 +2154,7 @@ class Player final : public Creature, public Cylinder, public Bankable { if (auto it = std::find_if(preys.begin(), preys.end(), [slotid](const std::unique_ptr<PreySlot> &preyIt) { return preyIt->id == slotid; }); - it != preys.end()) { + it != preys.end()) { return *it; } @@ -2221,7 +2221,7 @@ class Player final : public Creature, public Cylinder, public Bankable { if (auto it = std::find_if(preys.begin(), preys.end(), [raceId](const std::unique_ptr<PreySlot> &it) { return it->selectedRaceId == raceId; }); - it != preys.end()) { + it != preys.end()) { return *it; } @@ -2252,7 +2252,7 @@ class Player final : public Creature, public Cylinder, public Bankable { if (auto it = std::find_if(taskHunting.begin(), taskHunting.end(), [slotid](const std::unique_ptr<TaskHuntingSlot> &itTask) { return itTask->id == slotid; }); - it != taskHunting.end()) { + it != taskHunting.end()) { return *it; } @@ -2321,7 +2321,7 @@ class Player final : public Creature, public Cylinder, public Bankable { if (auto it = std::find_if(taskHunting.begin(), taskHunting.end(), [raceId](const std::unique_ptr<TaskHuntingSlot> &itTask) { return itTask->selectedRaceId == raceId; }); - it != taskHunting.end()) { + it != taskHunting.end()) { return *it; } diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index 98dbaeb1bea..6fec2725164 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -129,13 +129,13 @@ bool Vocations::loadFromXml() { voc->skillMultipliers[skill_id] = pugi::cast<float>(childNode.attribute("multiplier").value()); } else { g_logger().warn("[Vocations::loadFromXml] - " - "No valid skill id: {} for vocation: {}", - skill_id, voc->id); + "No valid skill id: {} for vocation: {}", + skill_id, voc->id); } } else { g_logger().warn("[Vocations::loadFromXml] - " - "Missing skill id for vocation: {}", - voc->id); + "Missing skill id for vocation: {}", + voc->id); } } else if (strcasecmp(childNode.name(), "mitigation") == 0) { pugi::xml_attribute factorAttribute = childNode.attribute("multiplier"); @@ -198,8 +198,8 @@ std::shared_ptr<Vocation> Vocations::getVocation(uint16_t id) { auto it = vocationsMap.find(id); if (it == vocationsMap.end()) { g_logger().warn("[Vocations::getVocation] - " - "Vocation {} not found", - id); + "Vocation {} not found", + id); return nullptr; } return it->second; diff --git a/src/database/databasemanager.cpp b/src/database/databasemanager.cpp index cbcc116dd9a..dfbb7d9a64a 100644 --- a/src/database/databasemanager.cpp +++ b/src/database/databasemanager.cpp @@ -89,8 +89,8 @@ void DatabaseManager::updateDatabase() { ss << g_configManager().getString(DATA_DIRECTORY, __FUNCTION__) + "/migrations/" << version << ".lua"; if (luaL_dofile(L, ss.str().c_str()) != 0) { g_logger().error("DatabaseManager::updateDatabase - Version: {}" - "] {}", - version, lua_tostring(L, -1)); + "] {}", + version, lua_tostring(L, -1)); break; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 984e675407c..bae7975a295 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -386,7 +386,7 @@ void Game::loadBoostedCreature() { const auto result = db.storeQuery("SELECT * FROM `boosted_creature`"); if (!result) { g_logger().warn("[Game::loadBoostedCreature] - " - "Failed to detect boosted creature database. (CODE 01)"); + "Failed to detect boosted creature database. (CODE 01)"); return; } @@ -423,15 +423,15 @@ void Game::loadBoostedCreature() { if (selectedMonster.raceId == 0) { g_logger().warn("[Game::loadBoostedCreature] - " - "It was not possible to generate a new boosted creature->"); + "It was not possible to generate a new boosted creature->"); return; } const auto monsterType = g_monsters().getMonsterType(selectedMonster.name); if (!monsterType) { g_logger().warn("[Game::loadBoostedCreature] - " - "It was not possible to generate a new boosted creature-> Monster '{}' not found.", - selectedMonster.name); + "It was not possible to generate a new boosted creature-> Monster '{}' not found.", + selectedMonster.name); return; } @@ -451,7 +451,7 @@ void Game::loadBoostedCreature() { if (!db.executeQuery(query)) { g_logger().warn("[Game::loadBoostedCreature] - " - "Failed to detect boosted creature database. (CODE 02)"); + "Failed to detect boosted creature database. (CODE 02)"); } } @@ -1703,7 +1703,7 @@ void Game::playerMoveItem(std::shared_ptr<Player> player, const Position &fromPo uint8_t itemStackPos = fromStackPos; if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos) - && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) { + && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) { // need to pickup the item first std::shared_ptr<Item> moveItem = nullptr; @@ -2093,20 +2093,20 @@ ReturnValue Game::internalMoveItem(std::shared_ptr<Cylinder> fromCylinder, std:: std::shared_ptr<Item> quiver = toCylinder->getItem(); if (quiver && quiver->isQuiver() - && quiver->getHoldingPlayer() - && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { + && quiver->getHoldingPlayer() + && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver); } else { quiver = fromCylinder->getItem(); if (quiver && quiver->isQuiver() - && quiver->getHoldingPlayer() - && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { + && quiver->getHoldingPlayer() + && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver); } } if (SoundEffect_t soundEffect = item->getMovementSound(toCylinder); - toCylinder && soundEffect != SoundEffect_t::SILENCE) { + toCylinder && soundEffect != SoundEffect_t::SILENCE) { if (toCylinder->getContainer() && actor && actor->getPlayer() && (toCylinder->getContainer()->isInsideDepot(true) || toCylinder->getContainer()->getHoldingPlayer())) { actor->getPlayer()->sendSingleSoundEffect(toCylinder->getPosition(), soundEffect, SourceEffect_t::OWN); } else { @@ -2239,8 +2239,8 @@ ReturnValue Game::internalAddItem(std::shared_ptr<Cylinder> toCylinder, std::sha } if (addedItem && addedItem->isQuiver() - && addedItem->getHoldingPlayer() - && addedItem->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == addedItem) { + && addedItem->getHoldingPlayer() + && addedItem->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == addedItem) { addedItem->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, addedItem); } @@ -2300,8 +2300,8 @@ ReturnValue Game::internalRemoveItem(std::shared_ptr<Item> item, int32_t count / std::shared_ptr<Item> quiver = cylinder->getItem(); if (quiver && quiver->isQuiver() - && quiver->getHoldingPlayer() - && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { + && quiver->getHoldingPlayer() + && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver); } @@ -2760,8 +2760,8 @@ std::shared_ptr<Item> Game::transformItem(std::shared_ptr<Item> item, uint16_t n std::shared_ptr<Item> quiver = cylinder->getItem(); if (quiver && quiver->isQuiver() - && quiver->getHoldingPlayer() - && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { + && quiver->getHoldingPlayer() + && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver); } item->startDecaying(); @@ -2772,8 +2772,8 @@ std::shared_ptr<Item> Game::transformItem(std::shared_ptr<Item> item, uint16_t n std::shared_ptr<Item> quiver = cylinder->getItem(); if (quiver && quiver->isQuiver() - && quiver->getHoldingPlayer() - && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { + && quiver->getHoldingPlayer() + && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) { quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver); } @@ -3699,7 +3699,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f mustReloadDepotSearch = true; } else { if (auto targetThing = internalGetThing(player, toPos, toStackPos, toItemId, STACKPOS_FIND_THING); - targetThing && targetThing->getItem() && targetThing->getItem()->isInsideDepot(true)) { + targetThing && targetThing->getItem() && targetThing->getItem()->isInsideDepot(true)) { mustReloadDepotSearch = true; } } @@ -4235,7 +4235,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos item->setCustomAttribute("LookFeet", static_cast<int64_t>(outfit.lookFeet)); item->setCustomAttribute("LookAddons", static_cast<int64_t>(outfit.lookAddons)); } else if (auto pastLookType = item->getCustomAttribute("PastLookType"); - pastLookType && pastLookType->getInteger() > 0) { + pastLookType && pastLookType->getInteger() > 0) { item->removeCustomAttribute("LookType"); item->removeCustomAttribute("PastLookType"); } @@ -4247,7 +4247,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos item->setCustomAttribute("LookMountLegs", static_cast<int64_t>(outfit.lookMountLegs)); item->setCustomAttribute("LookMountFeet", static_cast<int64_t>(outfit.lookMountFeet)); } else if (auto pastLookMount = item->getCustomAttribute("PastLookMount"); - pastLookMount && pastLookMount->getInteger() > 0) { + pastLookMount && pastLookMount->getInteger() > 0) { item->removeCustomAttribute("LookMount"); item->removeCustomAttribute("PastLookMount"); } @@ -5496,8 +5496,8 @@ void Game::playerLootAllCorpses(std::shared_ptr<Player> player, const Position & } if (!tileCorpse->isRewardCorpse() - && tileCorpse->getCorpseOwner() != 0 - && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { + && tileCorpse->getCorpseOwner() != 0 + && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); g_logger().debug("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition().toString()); continue; @@ -7120,9 +7120,9 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt if (!damage.extension && attackerMonster && targetPlayer) { // Charm rune (target as player) if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, g_monsters().getMonsterTypeByRaceId(attackerMonster->getRaceId())); - activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) { + activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) { if (const auto charm = g_iobestiary().getBestiaryCharm(activeCharm); - charm->type == CHARM_DEFENSIVE && charm->chance > normal_random(0, 100) && g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value))) { + charm->type == CHARM_DEFENSIVE && charm->chance > normal_random(0, 100) && g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value))) { return false; // Dodge charm } } @@ -7508,7 +7508,7 @@ void Game::applyCharmRune( return; } if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(attackerPlayer, g_monsters().getMonsterTypeByRaceId(targetMonster->getRaceId())); - activeCharm != CHARM_NONE) { + activeCharm != CHARM_NONE) { const auto charm = g_iobestiary().getBestiaryCharm(activeCharm); int8_t chance = charm->id == CHARM_CRIPPLE ? charm->chance : charm->chance + attackerPlayer->getCharmChanceModifier(); g_logger().debug("charm chance: {}, base: {}, bonus: {}", chance, charm->chance, attackerPlayer->getCharmChanceModifier()); @@ -7534,7 +7534,7 @@ void Game::applyManaLeech( // Void charm rune if (targetMonster) { if (uint16_t playerCharmRaceidVoid = attackerPlayer->parseRacebyCharm(CHARM_VOID, false, 0); - playerCharmRaceidVoid != 0 && playerCharmRaceidVoid == targetMonster->getRace()) { + playerCharmRaceidVoid != 0 && playerCharmRaceidVoid == targetMonster->getRace()) { if (const auto charm = g_iobestiary().getBestiaryCharm(CHARM_VOID)) { manaSkill += charm->percent; } @@ -7565,7 +7565,7 @@ void Game::applyLifeLeech( } if (targetMonster) { if (uint16_t playerCharmRaceidVamp = attackerPlayer->parseRacebyCharm(CHARM_VAMP, false, 0); - playerCharmRaceidVamp != 0 && playerCharmRaceidVamp == targetMonster->getRaceId()) { + playerCharmRaceidVamp != 0 && playerCharmRaceidVamp == targetMonster->getRaceId()) { if (const auto lifec = g_iobestiary().getBestiaryCharm(CHARM_VAMP)) { lifeSkill += lifec->percent; } @@ -9523,7 +9523,7 @@ void Game::playerBosstiarySlot(uint32_t playerId, uint8_t slotId, uint32_t selec uint32_t bossIdSlot = player->getSlotBossId(slotId); if (uint32_t boostedBossId = g_ioBosstiary().getBoostedBossId(); - selectedBossId == 0 && bossIdSlot != boostedBossId) { + selectedBossId == 0 && bossIdSlot != boostedBossId) { uint8_t removeTimes = player->getRemoveTimes(); uint32_t removePrice = g_ioBosstiary().calculteRemoveBoss(removeTimes); g_game().removeMoney(player, removePrice, 0, true); @@ -9559,7 +9559,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (stdext::arraylist<Direction> listDir(128); - player->getPathTo(pos, listDir, 0, 1, true, false)) { + player->getPathTo(pos, listDir, 0, 1, true, false)) { g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); std::shared_ptr<Task> task = createPlayerTask( 400, [this, playerId, pos] { playerBrowseField(playerId, pos); }, "Game::playerBrowseField" @@ -9597,7 +9597,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con const auto [podiumVisible, monsterVisible] = podiumAndMonsterVisible; bool changeTentuglyName = false; if (auto monsterOutfit = mType->info.outfit; - (monsterOutfit.lookType != 0 || monsterOutfit.lookTypeEx != 0) && monsterVisible) { + (monsterOutfit.lookType != 0 || monsterOutfit.lookTypeEx != 0) && monsterVisible) { // "Tantugly's Head" boss have to send other looktype to the podium if (monsterOutfit.lookTypeEx == 35105) { monsterOutfit.lookTypeEx = 39003; @@ -9659,7 +9659,7 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (stdext::arraylist<Direction> listDir(128); - player->getPathTo(pos, listDir, 0, 1, true, true)) { + player->getPathTo(pos, listDir, 0, 1, true, true)) { g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); std::shared_ptr<Task> task = createPlayerTask( 400, [this, playerId, pos, stackPos, itemId] { @@ -10031,8 +10031,8 @@ void Game::sendUpdateCreature(std::shared_ptr<Creature> creature) { uint32_t Game::makeInfluencedMonster() { if (auto influencedLimit = g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT, __FUNCTION__); - // Condition - forgeableMonsters.empty() || influencedMonsters.size() >= influencedLimit) { + // Condition + forgeableMonsters.empty() || influencedMonsters.size() >= influencedLimit) { return 0; } @@ -10112,8 +10112,8 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr } if (auto fiendishLimit = g_configManager().getNumber(FORGE_FIENDISH_CREATURES_LIMIT, __FUNCTION__); - // Condition - forgeableMonsters.empty() || fiendishMonsters.size() >= fiendishLimit) { + // Condition + forgeableMonsters.empty() || fiendishMonsters.size() >= fiendishLimit) { return 0; } @@ -10217,8 +10217,8 @@ bool Game::removeForgeMonster(uint32_t id, ForgeClassifications_t monsterForgeCl bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { if (auto find = influencedMonsters.find(id); - // Condition - find != influencedMonsters.end()) { + // Condition + find != influencedMonsters.end()) { influencedMonsters.erase(find); if (create) { @@ -10234,8 +10234,8 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { if (auto find = fiendishMonsters.find(id); - // Condition - find != fiendishMonsters.end()) { + // Condition + find != fiendishMonsters.end()) { fiendishMonsters.erase(find); checkForgeEventId(id); @@ -10286,8 +10286,8 @@ void Game::createFiendishMonsters() { } if (auto ret = makeFiendishMonster(); - // Condition - ret == 0) { + // Condition + ret == 0) { return; } @@ -10305,8 +10305,8 @@ void Game::createInfluencedMonsters() { } if (auto ret = makeInfluencedMonster(); - // If condition - ret == 0) { + // If condition + ret == 0) { return; } @@ -10325,8 +10325,8 @@ void Game::checkForgeEventId(uint32_t monsterId) { bool Game::addInfluencedMonster(std::shared_ptr<Monster> monster) { if (monster && monster->canBeForgeMonster()) { if (auto maxInfluencedMonsters = static_cast<uint32_t>(g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT, __FUNCTION__)); - // If condition - (influencedMonsters.size() + 1) > maxInfluencedMonsters) { + // If condition + (influencedMonsters.size() + 1) > maxInfluencedMonsters) { return false; } diff --git a/src/game/movement/teleport.cpp b/src/game/movement/teleport.cpp index d5a3a73daf1..44050100eb6 100644 --- a/src/game/movement/teleport.cpp +++ b/src/game/movement/teleport.cpp @@ -80,8 +80,8 @@ void Teleport::addThing(int32_t, std::shared_ptr<Thing> thing) { if (checkInfinityLoop(destTile)) { const Position &pos = getPosition(); g_logger().warn("[Teleport:addThing] - " - "Infinity loop teleport at position: {}", - pos.toString()); + "Infinity loop teleport at position: {}", + pos.toString()); return; } diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 0090c0b3ebb..2f625cdb7cc 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -89,7 +89,7 @@ void IOBosstiary::loadBoostedBoss() { query << "`date` = '" << today << "',"; query << "`boostname` = " << database.escapeString(bossName) << ","; if (const auto bossType = getMonsterTypeByBossRaceId(bossId); - bossType) { + bossType) { query << "`looktypeEx` = " << static_cast<int>(bossType->info.outfit.lookTypeEx) << ","; query << "`looktype` = " << static_cast<int>(bossType->info.outfit.lookType) << ","; query << "`lookfeet` = " << static_cast<int>(bossType->info.outfit.lookFeet) << ","; @@ -124,7 +124,7 @@ void IOBosstiary::loadBoostedBoss() { void IOBosstiary::addBosstiaryMonster(uint16_t raceId, const std::string &name) { if (auto it = bosstiaryMap.find(raceId); - it != bosstiaryMap.end()) { + it != bosstiaryMap.end()) { return; } @@ -282,7 +282,7 @@ uint8_t IOBosstiary::getBossCurrentLevel(std::shared_ptr<Player> player, uint16_ auto bossRace = mType->info.bosstiaryRace; uint8_t level = 0; if (auto it = levelInfos.find(bossRace); - it != levelInfos.end()) { + it != levelInfos.end()) { const std::vector<LevelInfo> &infoForCurrentRace = it->second; for (const auto &raceInfo : infoForCurrentRace) { if (currentKills >= raceInfo.kills) { diff --git a/src/io/iobestiary.cpp b/src/io/iobestiary.cpp index c544021367e..9cd964e7c7c 100644 --- a/src/io/iobestiary.cpp +++ b/src/io/iobestiary.cpp @@ -225,9 +225,9 @@ void IOBestiary::addBestiaryKill(std::shared_ptr<Player> player, const std::shar player->addBestiaryKillCount(raceid, amount); if ((curCount == 0) || // Initial kill stage - (curCount < mtype->info.bestiaryFirstUnlock && (curCount + amount) >= mtype->info.bestiaryFirstUnlock) || // First kill stage reached - (curCount < mtype->info.bestiarySecondUnlock && (curCount + amount) >= mtype->info.bestiarySecondUnlock) || // Second kill stage reached - (curCount < mtype->info.bestiaryToUnlock && (curCount + amount) >= mtype->info.bestiaryToUnlock)) { // Final kill stage reached + (curCount < mtype->info.bestiaryFirstUnlock && (curCount + amount) >= mtype->info.bestiaryFirstUnlock) || // First kill stage reached + (curCount < mtype->info.bestiarySecondUnlock && (curCount + amount) >= mtype->info.bestiarySecondUnlock) || // Second kill stage reached + (curCount < mtype->info.bestiaryToUnlock && (curCount + amount) >= mtype->info.bestiaryToUnlock)) { // Final kill stage reached ss << "You unlocked details for the creature '" << mtype->name << "'"; player->sendTextMessage(MESSAGE_STATUS, ss.str()); player->sendBestiaryEntryChanged(raceid); diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp index d50d9b2804f..c17978e8d1d 100644 --- a/src/io/iomap.cpp +++ b/src/io/iomap.cpp @@ -14,27 +14,27 @@ #include "io/filestream.hpp" /* - OTBM_ROOTV1 - | - |--- OTBM_MAP_DATA - | | - | |--- OTBM_TILE_AREA - | | |--- OTBM_TILE - | | |--- OTBM_TILE_SQUARE (not implemented) - | | |--- OTBM_TILE_REF (not implemented) - | | |--- OTBM_HOUSETILE - | | - | |--- OTBM_SPAWNS (not implemented) - | | |--- OTBM_SPAWN_AREA (not implemented) - | | |--- OTBM_MONSTER (not implemented) - | | - | |--- OTBM_TOWNS - | | |--- OTBM_TOWN - | | - | |--- OTBM_WAYPOINTS - | |--- OTBM_WAYPOINT - | - |--- OTBM_ITEM_DEF (not implemented) + OTBM_ROOTV1 + | + |--- OTBM_MAP_DATA + | | + | |--- OTBM_TILE_AREA + | | |--- OTBM_TILE + | | |--- OTBM_TILE_SQUARE (not implemented) + | | |--- OTBM_TILE_REF (not implemented) + | | |--- OTBM_HOUSETILE + | | + | |--- OTBM_SPAWNS (not implemented) + | | |--- OTBM_SPAWN_AREA (not implemented) + | | |--- OTBM_MONSTER (not implemented) + | | + | |--- OTBM_TOWNS + | | |--- OTBM_TOWN + | | + | |--- OTBM_WAYPOINTS + | |--- OTBM_WAYPOINT + | + |--- OTBM_ITEM_DEF (not implemented) */ void IOMap::loadMap(Map* map, const Position &pos) { @@ -170,9 +170,9 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) { if (tile->isHouse() && iType.movable) { g_logger().warn("[IOMap::loadMap] - " - "Movable item with ID: {}, in house: {}, " - "at position: x {}, y {}, z {}", - id, tile->houseId, x, y, z); + "Movable item with ID: {}, in house: {}, " + "at position: x {}, y {}, z {}", + id, tile->houseId, x, y, z); } else if (iType.isGroundTile()) { tile->ground = map.tryReplaceItemFromCache(item); } else { @@ -200,9 +200,9 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) { // nothing } else if (tile->isHouse() && iType.movable) { g_logger().warn("[IOMap::loadMap] - " - "Movable item with ID: {}, in house: {}, " - "at position: x {}, y {}, z {}", - id, tile->houseId, x, y, z); + "Movable item with ID: {}, in house: {}, " + "at position: x {}, y {}, z {}", + id, tile->houseId, x, y, z); } else if (iType.isGroundTile()) { tile->ground = map.tryReplaceItemFromCache(item); } else { diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp index 642423c2abd..6b2816355ff 100644 --- a/src/io/ioprey.cpp +++ b/src/io/ioprey.cpp @@ -73,7 +73,7 @@ void PreySlot::reloadMonsterGrid(std::vector<uint16_t> blackList, uint32_t level uint8_t stageThree; uint8_t stageFour; if (auto levelStage = static_cast<uint32_t>(std::floor(level / 100)); - levelStage == 0) { // From level 0 to 99 + levelStage == 0) { // From level 0 to 99 stageOne = 3; stageTwo = 3; stageThree = 2; @@ -154,7 +154,7 @@ void TaskHuntingSlot::reloadMonsterGrid(std::vector<uint16_t> blackList, uint32_ uint8_t stageThree; uint8_t stageFour; if (auto levelStage = static_cast<uint32_t>(std::floor(level / 100)); - levelStage == 0) { // From level 0 to 99 + levelStage == 0) { // From level 0 to 99 stageOne = 3; stageTwo = 3; stageThree = 2; @@ -253,7 +253,7 @@ void IOPrey::checkPlayerPreys(std::shared_ptr<Player> player, uint8_t amount) co for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { if (const auto &slot = player->getPreySlotById(static_cast<PreySlot_t>(slotId)); - slot && slot->isOccupied()) { + slot && slot->isOccupied()) { if (slot->bonusTimeLeft <= amount) { if (slot->option == PreyOption_AutomaticReroll) { if (player->usePreyCards(static_cast<uint16_t>(g_configManager().getNumber(PREY_BONUS_REROLL_PRICE, __FUNCTION__)))) { diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index 848acffbf68..5eaff61a7a9 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -156,8 +156,8 @@ void Decay::internalDecayItem(std::shared_ptr<Item> item) { auto player = item->getHoldingPlayer(); if (player) { g_logger().error("[{}] - internalDecayItem failed to player {}, item id is same from transform equip/deequip, " - " item id: {}, equip to id: '{}', deequip to id '{}'", - __FUNCTION__, player->getName(), it.id, it.transformEquipTo, it.transformDeEquipTo); + " item id: {}, equip to id: '{}', deequip to id '{}'", + __FUNCTION__, player->getName(), it.id, it.transformEquipTo, it.transformDeEquipTo); } return; } @@ -207,8 +207,8 @@ void Decay::internalDecayItem(std::shared_ptr<Item> item) { ReturnValue ret = g_game().internalRemoveItem(item); if (ret != RETURNVALUE_NOERROR) { g_logger().error("[Decay::internalDecayItem] - internalDecayItem failed, " - "error code: {}, item id: {}", - static_cast<uint32_t>(ret), item->getID()); + "error code: {}, item id: {}", + static_cast<uint32_t>(ret), item->getID()); } } } diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 1f247b611bc..9b63b9aee0e 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -400,7 +400,7 @@ void ItemParse::parseTransform(const std::string &tmpStrValue, pugi::xml_attribu itemType.decayTo = 0; } if (ItemType &transform = Item::items.getItemType(itemType.transformEquipTo); - transform.type == ITEM_TYPE_NONE) { + transform.type == ITEM_TYPE_NONE) { transform.type = itemType.type; } } else if (stringValue == "transformdeequipto") { @@ -1140,7 +1140,7 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri token.erase(std::find_if(token.rbegin(), token.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), - token.end()); + token.end()); std::string v1; bool showInDescription = false; diff --git a/src/items/item.cpp b/src/items/item.cpp index 823eb13b393..dd2d5c965a6 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -132,7 +132,7 @@ bool Item::hasImbuementCategoryId(uint16_t categoryId) const { ImbuementInfo imbuementInfo; if (getImbuementInfo(slotid, &imbuementInfo)) { if (const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuementInfo.imbuement->getCategory()); - categoryImbuement->id == categoryId) { + categoryImbuement->id == categoryId) { return true; } } @@ -142,14 +142,14 @@ bool Item::hasImbuementCategoryId(uint16_t categoryId) const { std::shared_ptr<Container> Item::CreateItemAsContainer(const uint16_t type, uint16_t size) { if (const ItemType &it = Item::items[type]; - it.id == 0 - || it.stackable - || it.multiUse - || it.movable - || it.pickupable - || it.isDepot() - || it.isSplash() - || it.isDoor()) { + it.id == 0 + || it.stackable + || it.multiUse + || it.movable + || it.pickupable + || it.isDepot() + || it.isSplash() + || it.isDoor()) { return nullptr; } @@ -883,7 +883,7 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const { } if (const std::string &text = getString(ItemAttribute_t::TEXT); - !text.empty()) { + !text.empty()) { propWriteStream.write<uint8_t>(ATTR_TEXT); propWriteStream.writeString(text); } @@ -911,7 +911,7 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const { } if (auto decayState = getDecaying(); - decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) { + decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) { propWriteStream.write<uint8_t>(ATTR_DECAYING_STATE); propWriteStream.write<uint8_t>(decayState); } @@ -1166,7 +1166,7 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr separator = true; } if (int32_t hitChance = item->getHitChance(); - hitChance != 0) { + hitChance != 0) { if (separator) { ss << ", "; } @@ -1174,7 +1174,7 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr separator = true; } if (int32_t shootRange = item->getShootRange(); - shootRange != 0) { + shootRange != 0) { if (separator) { ss << ", "; } @@ -1579,7 +1579,7 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr separator = true; } if (int32_t hitChance = it.hitChance; - hitChance != 0) { + hitChance != 0) { if (separator) { ss << ", "; } @@ -1587,7 +1587,7 @@ Item::getDescriptions(const ItemType &it, std::shared_ptr<Item> item /*= nullptr separator = true; } if (int32_t shootRange = it.shootRange; - shootRange != 0) { + shootRange != 0) { if (separator) { ss << ", "; } @@ -1950,7 +1950,7 @@ SoundEffect_t Item::getMovementSound(std::shared_ptr<Cylinder> toCylinder) const } if (std::shared_ptr<Container> toContainer = toCylinder->getContainer(); - toContainer && toContainer->getHoldingPlayer()) { + toContainer && toContainer->getHoldingPlayer()) { return SoundEffect_t::ITEM_MOVE_BACKPACK; } diff --git a/src/items/items.cpp b/src/items/items.cpp index 7d8ff0cf7c0..2d4020c5416 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -260,8 +260,8 @@ bool Items::loadFromXml() { auto toIdAttribute = itemNode.attribute("toid"); if (!toIdAttribute) { g_logger().warn("[Items::loadFromXml] - " - "tag fromid: {} without toid", - fromIdAttribute.value()); + "tag fromid: {} without toid", + fromIdAttribute.value()); continue; } @@ -302,12 +302,12 @@ void Items::parseItemNode(const pugi::xml_node &itemNode, uint16_t id) { } if (std::string xmlName = itemNode.attribute("name").as_string(); - !xmlName.empty() && itemType.name != xmlName) { + !xmlName.empty() && itemType.name != xmlName) { if (!itemType.name.empty()) { if (auto it = std::find_if(nameToItems.begin(), nameToItems.end(), [id](const auto nameMapIt) { return nameMapIt.second == id; }); - it != nameToItems.end()) { + it != nameToItems.end()) { nameToItems.erase(it); } } diff --git a/src/items/tile.cpp b/src/items/tile.cpp index f2006f627e1..fc16dd5799c 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -393,7 +393,7 @@ void Tile::onAddTileItem(std::shared_ptr<Item> item) { } if ((!hasFlag(TILESTATE_PROTECTIONZONE) || g_configManager().getBoolean(CLEAN_PROTECTION_ZONES, __FUNCTION__)) - && item->isCleanable()) { + && item->isCleanable()) { if (!this->getHouse()) { g_game().addTileToClean(static_self_cast<Tile>()); } diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 6c0d56d882e..69d20d43e06 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -360,8 +360,8 @@ bool Weapon::executeUseWeapon(std::shared_ptr<Player> player, const LuaVariant & if (!getScriptInterface()->reserveScriptEnv()) { std::string playerName = player ? player->getName() : "Player nullptr"; g_logger().error("[Weapon::executeUseWeapon - Player {} weaponId {}]" - "Call stack overflow. Too many lua script calls being nested.", - playerName, getID()); + "Call stack overflow. Too many lua script calls being nested.", + playerName, getID()); return false; } diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index d2b88c30f3a..38e7654d8d5 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -50,8 +50,8 @@ void EventCallback::setType(EventCallback_t type) { bool EventCallback::creatureOnChangeOutfit(std::shared_ptr<Creature> creature, const Outfit_t &outfit) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::creatureOnChangeOutfit - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -72,9 +72,9 @@ bool EventCallback::creatureOnChangeOutfit(std::shared_ptr<Creature> creature, c ReturnValue EventCallback::creatureOnAreaCombat(std::shared_ptr<Creature> creature, std::shared_ptr<Tile> tile, bool aggressive) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::creatureOnAreaCombat - " - "Creature {} on tile position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), tile->getPosition().toString()); + "Creature {} on tile position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), tile->getPosition().toString()); return RETURNVALUE_NOTPOSSIBLE; } @@ -112,9 +112,9 @@ ReturnValue EventCallback::creatureOnAreaCombat(std::shared_ptr<Creature> creatu ReturnValue EventCallback::creatureOnTargetCombat(std::shared_ptr<Creature> creature, std::shared_ptr<Creature> target) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::creatureOnTargetCombat - " - "Creature {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName()); + "Creature {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName()); return RETURNVALUE_NOTPOSSIBLE; } @@ -150,9 +150,9 @@ ReturnValue EventCallback::creatureOnTargetCombat(std::shared_ptr<Creature> crea void EventCallback::creatureOnHear(std::shared_ptr<Creature> creature, std::shared_ptr<Creature> speaker, const std::string &words, SpeakClasses type) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::creatureOnHear - " - "Creature {} speaker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), speaker->getName()); + "Creature {} speaker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), speaker->getName()); return; } @@ -177,9 +177,9 @@ void EventCallback::creatureOnHear(std::shared_ptr<Creature> creature, std::shar void EventCallback::creatureOnDrainHealth(std::shared_ptr<Creature> creature, std::shared_ptr<Creature> attacker, CombatType_t &typePrimary, int32_t &damagePrimary, CombatType_t &typeSecondary, int32_t &damageSecondary, TextColor_t &colorPrimary, TextColor_t &colorSecondary) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::creatureOnDrainHealth - " - "Creature {} attacker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName()); + "Creature {} attacker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName()); return; } @@ -229,9 +229,9 @@ void EventCallback::creatureOnDrainHealth(std::shared_ptr<Creature> creature, st bool EventCallback::partyOnJoin(std::shared_ptr<Party> party, std::shared_ptr<Player> player) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::partyOnJoin - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -253,9 +253,9 @@ bool EventCallback::partyOnJoin(std::shared_ptr<Party> party, std::shared_ptr<Pl bool EventCallback::partyOnLeave(std::shared_ptr<Party> party, std::shared_ptr<Player> player) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::partyOnLeave - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -277,8 +277,8 @@ bool EventCallback::partyOnLeave(std::shared_ptr<Party> party, std::shared_ptr<P bool EventCallback::partyOnDisband(std::shared_ptr<Party> party) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::partyOnDisband - Party leader {}] Call stack " - "overflow. Too many lua script calls being nested.", - party->getLeader() ? party->getLeader()->getName() : "unknown"); + "overflow. Too many lua script calls being nested.", + party->getLeader() ? party->getLeader()->getName() : "unknown"); return false; } @@ -325,9 +325,9 @@ void EventCallback::partyOnShareExperience(std::shared_ptr<Party> party, uint64_ bool EventCallback::playerOnBrowseField(std::shared_ptr<Player> player, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnBrowseField - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -348,9 +348,9 @@ bool EventCallback::playerOnBrowseField(std::shared_ptr<Player> player, const Po void EventCallback::playerOnLook(std::shared_ptr<Player> player, const Position &position, std::shared_ptr<Thing> thing, uint8_t stackpos, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnLook - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -382,9 +382,9 @@ void EventCallback::playerOnLook(std::shared_ptr<Player> player, const Position void EventCallback::playerOnLookInBattleList(std::shared_ptr<Player> player, std::shared_ptr<Creature> creature, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnLookInBattleList - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -408,9 +408,9 @@ void EventCallback::playerOnLookInBattleList(std::shared_ptr<Player> player, std void EventCallback::playerOnLookInTrade(std::shared_ptr<Player> player, std::shared_ptr<Player> partner, std::shared_ptr<Item> item, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnLookInTrade - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -437,9 +437,9 @@ void EventCallback::playerOnLookInTrade(std::shared_ptr<Player> player, std::sha bool EventCallback::playerOnLookInShop(std::shared_ptr<Player> player, const ItemType* itemType, uint8_t count) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnLookInShop - " - "Player {} itemType {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), itemType->getPluralName()); + "Player {} itemType {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), itemType->getPluralName()); return false; } @@ -463,9 +463,9 @@ bool EventCallback::playerOnLookInShop(std::shared_ptr<Player> player, const Ite void EventCallback::playerOnRemoveCount(std::shared_ptr<Player> player, std::shared_ptr<Item> item) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnMove - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -492,8 +492,8 @@ bool EventCallback::playerOnMoveItem(std::shared_ptr<Player> player, std::shared if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[Action::executeUse - Player {}, on item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -522,9 +522,9 @@ bool EventCallback::playerOnMoveItem(std::shared_ptr<Player> player, std::shared void EventCallback::playerOnItemMoved(std::shared_ptr<Player> player, std::shared_ptr<Item> item, uint16_t count, const Position &fromPosition, const Position &toPosition, std::shared_ptr<Cylinder> fromCylinder, std::shared_ptr<Cylinder> toCylinder) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnItemMoved - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -553,9 +553,9 @@ void EventCallback::playerOnItemMoved(std::shared_ptr<Player> player, std::share void EventCallback::playerOnChangeZone(std::shared_ptr<Player> player, ZoneType_t zone) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnChangeZone - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -575,9 +575,9 @@ void EventCallback::playerOnChangeZone(std::shared_ptr<Player> player, ZoneType_ bool EventCallback::playerOnMoveCreature(std::shared_ptr<Player> player, std::shared_ptr<Creature> creature, const Position &fromPosition, const Position &toPosition) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnMoveCreature - " - "Player {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), creature->getName()); + "Player {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), creature->getName()); return false; } @@ -602,9 +602,9 @@ bool EventCallback::playerOnMoveCreature(std::shared_ptr<Player> player, std::sh void EventCallback::playerOnReportRuleViolation(std::shared_ptr<Player> player, const std::string &targetName, uint8_t reportType, uint8_t reportReason, const std::string &comment, const std::string &translation) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnReportRuleViolation - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -631,9 +631,9 @@ void EventCallback::playerOnReportRuleViolation(std::shared_ptr<Player> player, void EventCallback::playerOnReportBug(std::shared_ptr<Player> player, const std::string &message, const Position &position, uint8_t category) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnReportBug - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -656,9 +656,9 @@ void EventCallback::playerOnReportBug(std::shared_ptr<Player> player, const std: bool EventCallback::playerOnTurn(std::shared_ptr<Player> player, Direction direction) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnTurn - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -679,9 +679,9 @@ bool EventCallback::playerOnTurn(std::shared_ptr<Player> player, Direction direc bool EventCallback::playerOnTradeRequest(std::shared_ptr<Player> player, std::shared_ptr<Player> target, std::shared_ptr<Item> item) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnTradeRequest - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -706,9 +706,9 @@ bool EventCallback::playerOnTradeRequest(std::shared_ptr<Player> player, std::sh bool EventCallback::playerOnTradeAccept(std::shared_ptr<Player> player, std::shared_ptr<Player> target, std::shared_ptr<Item> item, std::shared_ptr<Item> targetItem) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnTradeAccept - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -736,9 +736,9 @@ bool EventCallback::playerOnTradeAccept(std::shared_ptr<Player> player, std::sha void EventCallback::playerOnGainExperience(std::shared_ptr<Player> player, std::shared_ptr<Creature> target, uint64_t &exp, uint64_t rawExp) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnGainExperience - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -774,9 +774,9 @@ void EventCallback::playerOnGainExperience(std::shared_ptr<Player> player, std:: void EventCallback::playerOnLoseExperience(std::shared_ptr<Player> player, uint64_t &exp) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnLoseExperience - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -804,9 +804,9 @@ void EventCallback::playerOnLoseExperience(std::shared_ptr<Player> player, uint6 void EventCallback::playerOnGainSkillTries(std::shared_ptr<Player> player, skills_t skill, uint64_t &tries) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnGainSkillTries - " - "Player {} skill {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(skill)); + "Player {} skill {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(skill)); return; } @@ -835,9 +835,9 @@ void EventCallback::playerOnGainSkillTries(std::shared_ptr<Player> player, skill void EventCallback::playerOnCombat(std::shared_ptr<Player> player, std::shared_ptr<Creature> target, std::shared_ptr<Item> item, CombatDamage &damage) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnCombat - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -880,7 +880,7 @@ void EventCallback::playerOnCombat(std::shared_ptr<Player> player, std::shared_p damage.secondary.value = -damage.secondary.value; } /* - Only EK with dealing physical damage will get elemental damage on skill + Only EK with dealing physical damage will get elemental damage on skill */ if (damage.origin == ORIGIN_SPELL) { if (player->getVocationId() != 4 && player->getVocationId() != 8) { @@ -897,9 +897,9 @@ void EventCallback::playerOnCombat(std::shared_ptr<Player> player, std::shared_p void EventCallback::playerOnRequestQuestLog(std::shared_ptr<Player> player) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnRequestQuestLog - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -918,9 +918,9 @@ void EventCallback::playerOnRequestQuestLog(std::shared_ptr<Player> player) cons void EventCallback::playerOnRequestQuestLine(std::shared_ptr<Player> player, uint16_t questId) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::playerOnRequestQuestLine - " - "Player {} questId {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), questId); + "Player {} questId {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), questId); return; } @@ -988,9 +988,9 @@ bool EventCallback::playerOnRotateItem(std::shared_ptr<Player> player, std::shar void EventCallback::playerOnWalk(std::shared_ptr<Player> player, Direction &dir) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::eventOnWalk - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -1011,9 +1011,9 @@ void EventCallback::playerOnWalk(std::shared_ptr<Player> player, Direction &dir) void EventCallback::playerOnStorageUpdate(std::shared_ptr<Player> player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::eventOnStorageUpdate - " - "Player {} key {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), key); + "Player {} key {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), key); return; } @@ -1038,9 +1038,9 @@ void EventCallback::playerOnStorageUpdate(std::shared_ptr<Player> player, const void EventCallback::monsterOnDropLoot(std::shared_ptr<Monster> monster, std::shared_ptr<Container> corpse) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::monsterOnDropLoot - " - "Monster corpse {}] " - "Call stack overflow. Too many lua script calls being nested.", - corpse->getName()); + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); return; } @@ -1062,9 +1062,9 @@ void EventCallback::monsterOnDropLoot(std::shared_ptr<Monster> monster, std::sha void EventCallback::monsterPostDropLoot(std::shared_ptr<Monster> monster, std::shared_ptr<Container> corpse) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::monsterPostDropLoot - " - "Monster corpse {}] " - "Call stack overflow. Too many lua script calls being nested.", - corpse->getName()); + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); return; } @@ -1086,9 +1086,9 @@ void EventCallback::monsterPostDropLoot(std::shared_ptr<Monster> monster, std::s void EventCallback::monsterOnSpawn(std::shared_ptr<Monster> monster, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -1115,9 +1115,9 @@ void EventCallback::monsterOnSpawn(std::shared_ptr<Monster> monster, const Posit void EventCallback::npcOnSpawn(std::shared_ptr<Npc> npc, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -1143,9 +1143,9 @@ void EventCallback::npcOnSpawn(std::shared_ptr<Npc> npc, const Position &positio bool EventCallback::zoneBeforeCreatureEnter(std::shared_ptr<Zone> zone, std::shared_ptr<Creature> creature) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::zoneBeforeCreatureEnter - " - "Zone {} Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - zone->getName(), creature->getName()); + "Zone {} Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + zone->getName(), creature->getName()); return false; } @@ -1167,9 +1167,9 @@ bool EventCallback::zoneBeforeCreatureEnter(std::shared_ptr<Zone> zone, std::sha bool EventCallback::zoneBeforeCreatureLeave(std::shared_ptr<Zone> zone, std::shared_ptr<Creature> creature) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::zoneBeforeCreatureLeave - " - "Zone {} Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - zone->getName(), creature->getName()); + "Zone {} Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + zone->getName(), creature->getName()); return false; } @@ -1191,9 +1191,9 @@ bool EventCallback::zoneBeforeCreatureLeave(std::shared_ptr<Zone> zone, std::sha void EventCallback::zoneAfterCreatureEnter(std::shared_ptr<Zone> zone, std::shared_ptr<Creature> creature) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::zoneAfterCreatureEnter - " - "Zone {} Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - zone->getName(), creature->getName()); + "Zone {} Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + zone->getName(), creature->getName()); return; } @@ -1215,9 +1215,9 @@ void EventCallback::zoneAfterCreatureEnter(std::shared_ptr<Zone> zone, std::shar void EventCallback::zoneAfterCreatureLeave(std::shared_ptr<Zone> zone, std::shared_ptr<Creature> creature) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::zoneAfterCreatureLeave - " - "Zone {} Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - zone->getName(), creature->getName()); + "Zone {} Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + zone->getName(), creature->getName()); return; } diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index 42f3e12c865..485509cae06 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -231,11 +231,11 @@ std::shared_ptr<Action> Actions::getAction(std::shared_ptr<Item> item) { } if (auto iteratePositions = actionPositionMap.find(item->getPosition()); - iteratePositions != actionPositionMap.end()) { + iteratePositions != actionPositionMap.end()) { if (std::shared_ptr<Tile> tile = item->getTile(); - tile) { + tile) { if (std::shared_ptr<Player> player = item->getHoldingPlayer(); - player && item->getTopParent() == player) { + player && item->getTopParent() == player) { g_logger().debug("[Actions::getAction] - The position only is valid for use item in the map, player name {}", player->getName()); return nullptr; } @@ -515,8 +515,8 @@ bool Action::executeUse(std::shared_ptr<Player> player, std::shared_ptr<Item> it // onUse(player, item, fromPosition, target, toPosition, isHotkey) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[Action::executeUse - Player {}, on item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } diff --git a/src/lua/creature/actions.hpp b/src/lua/creature/actions.hpp index 80fd2ea54e9..81a7754a5b1 100644 --- a/src/lua/creature/actions.hpp +++ b/src/lua/creature/actions.hpp @@ -161,7 +161,7 @@ class Actions final : public Scripts { private: bool hasPosition(Position position) const { if (auto it = actionPositionMap.find(position); - it != actionPositionMap.end()) { + it != actionPositionMap.end()) { return true; } return false; @@ -177,7 +177,7 @@ class Actions final : public Scripts { bool hasItemId(uint16_t itemId) const { if (auto it = useItemMap.find(itemId); - it != useItemMap.end()) { + it != useItemMap.end()) { return true; } return false; @@ -189,7 +189,7 @@ class Actions final : public Scripts { bool hasUniqueId(uint16_t uniqueId) const { if (auto it = uniqueItemMap.find(uniqueId); - it != uniqueItemMap.end()) { + it != uniqueItemMap.end()) { return true; } return false; @@ -201,7 +201,7 @@ class Actions final : public Scripts { bool hasActionId(uint16_t actionId) const { if (auto it = actionItemMap.find(actionId); - it != actionItemMap.end()) { + it != actionItemMap.end()) { return true; } return false; diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index fe2a782b914..09dbdf086ce 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -173,8 +173,8 @@ bool CreatureEvent::executeOnLogin(std::shared_ptr<Player> player) const { // onLogin(player) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnLogin - Player {} event {}]" - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -193,8 +193,8 @@ bool CreatureEvent::executeOnLogout(std::shared_ptr<Player> player) const { // onLogout(player) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnLogout - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -213,8 +213,8 @@ bool CreatureEvent::executeOnThink(std::shared_ptr<Creature> creature, uint32_t // onThink(creature, interval) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnThink - Creature {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getName()); return false; } @@ -235,8 +235,8 @@ bool CreatureEvent::executeOnPrepareDeath(std::shared_ptr<Creature> creature, st // onPrepareDeath(creature, killer) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnPrepareDeath - Creature {} killer {}" - " event {}] Call stack overflow. Too many lua script calls being nested.", - creature->getName(), killer->getName(), getName()); + " event {}] Call stack overflow. Too many lua script calls being nested.", + creature->getName(), killer->getName(), getName()); return false; } @@ -264,8 +264,8 @@ bool CreatureEvent::executeOnDeath(std::shared_ptr<Creature> creature, std::shar // onDeath(creature, corpse, lasthitkiller, mostdamagekiller, lasthitunjustified, mostdamageunjustified) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnDeath - Creature {} killer {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), killer->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), killer->getName(), getName()); return false; } @@ -304,8 +304,8 @@ bool CreatureEvent::executeAdvance(std::shared_ptr<Player> player, skills_t skil // onAdvance(player, skill, oldLevel, newLevel) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeAdvance - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -330,12 +330,12 @@ bool CreatureEvent::executeAdvance(std::shared_ptr<Player> player, skills_t skil void CreatureEvent::executeOnKill(std::shared_ptr<Creature> creature, std::shared_ptr<Creature> target, bool lastHit) const { // onKill(creature, target, lastHit) g_logger().warn("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " - "Deprecated use of onKill event. Use registered onDeath events instead for better performance.", - creature->getName(), target->getName(), getName()); + "Deprecated use of onKill event. Use registered onDeath events instead for better performance.", + creature->getName(), target->getName(), getName()); if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName(), getName()); return; } @@ -357,9 +357,9 @@ void CreatureEvent::executeModalWindow(std::shared_ptr<Player> player, uint32_t // onModalWindow(player, modalWindowId, buttonId, choiceId) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeModalWindow - " - "Player {} modaw window id {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), modalWindowId, getName()); + "Player {} modaw window id {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), modalWindowId, getName()); return; } @@ -383,8 +383,8 @@ bool CreatureEvent::executeTextEdit(std::shared_ptr<Player> player, std::shared_ // onTextEdit(player, item, text) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeTextEdit - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -407,9 +407,9 @@ void CreatureEvent::executeHealthChange(std::shared_ptr<Creature> creature, std: // onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeHealthChange - " - "Creature {} attacker {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName(), getName()); + "Creature {} attacker {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName(), getName()); return; } @@ -452,9 +452,9 @@ void CreatureEvent::executeManaChange(std::shared_ptr<Creature> creature, std::s // onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeManaChange - " - "Creature {} attacker {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName(), getName()); + "Creature {} attacker {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName(), getName()); return; } @@ -492,9 +492,9 @@ void CreatureEvent::executeExtendedOpcode(std::shared_ptr<Player> player, uint8_ // onExtendedOpcode(player, opcode, buffer) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeExtendedOpcode - " - "Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + "Player {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return; } diff --git a/src/lua/creature/events.cpp b/src/lua/creature/events.cpp index 2917d128b8a..8bdd53465b3 100644 --- a/src/lua/creature/events.cpp +++ b/src/lua/creature/events.cpp @@ -158,9 +158,9 @@ void Events::eventMonsterOnSpawn(std::shared_ptr<Monster> monster, const Positio if (!scriptInterface.reserveScriptEnv()) { g_logger().error("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -192,9 +192,9 @@ void Events::eventNpcOnSpawn(std::shared_ptr<Npc> npc, const Position &position) if (!scriptInterface.reserveScriptEnv()) { g_logger().error("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -226,8 +226,8 @@ bool Events::eventCreatureOnChangeOutfit(std::shared_ptr<Creature> creature, con if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventCreatureOnChangeOutfit - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -253,9 +253,9 @@ ReturnValue Events::eventCreatureOnAreaCombat(std::shared_ptr<Creature> creature if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventCreatureOnAreaCombat - " - "Creature {} on tile position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), tile->getPosition().toString()); + "Creature {} on tile position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), tile->getPosition().toString()); return RETURNVALUE_NOTPOSSIBLE; } @@ -298,9 +298,9 @@ ReturnValue Events::eventCreatureOnTargetCombat(std::shared_ptr<Creature> creatu if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventCreatureOnTargetCombat - " - "Creature {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName()); + "Creature {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName()); return RETURNVALUE_NOTPOSSIBLE; } @@ -341,9 +341,9 @@ void Events::eventCreatureOnHear(std::shared_ptr<Creature> creature, std::shared if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventCreatureOnHear - " - "Creature {} speaker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), speaker->getName()); + "Creature {} speaker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), speaker->getName()); return; } @@ -372,9 +372,9 @@ void Events::eventCreatureOnDrainHealth(std::shared_ptr<Creature> creature, std: if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventCreatureOnDrainHealth - " - "Creature {} attacker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName()); + "Creature {} attacker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName()); return; } @@ -429,9 +429,9 @@ bool Events::eventPartyOnJoin(std::shared_ptr<Party> party, std::shared_ptr<Play if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPartyOnJoin - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -458,9 +458,9 @@ bool Events::eventPartyOnLeave(std::shared_ptr<Party> party, std::shared_ptr<Pla if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPartyOnLeave - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -487,8 +487,8 @@ bool Events::eventPartyOnDisband(std::shared_ptr<Party> party) { if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPartyOnDisband - Party leader {}] Call stack " - "overflow. Too many lua script calls being nested.", - party->getLeader() ? party->getLeader()->getName() : "unknown"); + "overflow. Too many lua script calls being nested.", + party->getLeader() ? party->getLeader()->getName() : "unknown"); return false; } @@ -545,9 +545,9 @@ bool Events::eventPlayerOnBrowseField(std::shared_ptr<Player> player, const Posi if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnBrowseField - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -573,9 +573,9 @@ void Events::eventPlayerOnLook(std::shared_ptr<Player> player, const Position &p if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnLook - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -612,9 +612,9 @@ void Events::eventPlayerOnLookInBattleList(std::shared_ptr<Player> player, std:: if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnLookInBattleList - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -643,9 +643,9 @@ void Events::eventPlayerOnLookInTrade(std::shared_ptr<Player> player, std::share if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnLookInTrade - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -677,9 +677,9 @@ bool Events::eventPlayerOnLookInShop(std::shared_ptr<Player> player, const ItemT if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnLookInShop - " - "Player {} itemType {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), itemType->getPluralName()); + "Player {} itemType {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), itemType->getPluralName()); return false; } @@ -708,9 +708,9 @@ bool Events::eventPlayerOnRemoveCount(std::shared_ptr<Player> player, std::share if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnMove - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -737,9 +737,9 @@ bool Events::eventPlayerOnMoveItem(std::shared_ptr<Player> player, std::shared_p if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnMoveItem - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -773,9 +773,9 @@ void Events::eventPlayerOnItemMoved(std::shared_ptr<Player> player, std::shared_ if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnItemMoved - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -809,9 +809,9 @@ void Events::eventPlayerOnChangeZone(std::shared_ptr<Player> player, ZoneType_t if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnChangeZone - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -836,9 +836,9 @@ bool Events::eventPlayerOnMoveCreature(std::shared_ptr<Player> player, std::shar if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnMoveCreature - " - "Player {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), creature->getName()); + "Player {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), creature->getName()); return false; } @@ -868,9 +868,9 @@ void Events::eventPlayerOnReportRuleViolation(std::shared_ptr<Player> player, co if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnReportRuleViolation - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -902,9 +902,9 @@ bool Events::eventPlayerOnReportBug(std::shared_ptr<Player> player, const std::s if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnReportBug - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -932,9 +932,9 @@ bool Events::eventPlayerOnTurn(std::shared_ptr<Player> player, Direction directi if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnTurn - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -960,9 +960,9 @@ bool Events::eventPlayerOnTradeRequest(std::shared_ptr<Player> player, std::shar if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnTradeRequest - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -992,9 +992,9 @@ bool Events::eventPlayerOnTradeAccept(std::shared_ptr<Player> player, std::share if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnTradeAccept - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -1028,9 +1028,9 @@ void Events::eventPlayerOnGainExperience(std::shared_ptr<Player> player, std::sh if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnGainExperience - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -1071,9 +1071,9 @@ void Events::eventPlayerOnLoseExperience(std::shared_ptr<Player> player, uint64_ if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnLoseExperience - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -1106,9 +1106,9 @@ void Events::eventPlayerOnGainSkillTries(std::shared_ptr<Player> player, skills_ if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnGainSkillTries - " - "Player {} skill {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(skill)); + "Player {} skill {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(skill)); return; } @@ -1142,9 +1142,9 @@ void Events::eventPlayerOnCombat(std::shared_ptr<Player> player, std::shared_ptr if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnCombat - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -1199,9 +1199,9 @@ void Events::eventPlayerOnRequestQuestLog(std::shared_ptr<Player> player) { if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnRequestQuestLog - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -1225,9 +1225,9 @@ void Events::eventPlayerOnRequestQuestLine(std::shared_ptr<Player> player, uint1 if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventPlayerOnRequestQuestLine - " - "Player {} questId {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), questId); + "Player {} questId {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), questId); return; } @@ -1282,9 +1282,9 @@ void Events::eventOnStorageUpdate(std::shared_ptr<Player> player, const uint32_t if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventOnStorageUpdate - " - "Player {} key {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), key); + "Player {} key {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), key); return; } @@ -1314,9 +1314,9 @@ void Events::eventMonsterOnDropLoot(std::shared_ptr<Monster> monster, std::share if (!scriptInterface.reserveScriptEnv()) { g_logger().error("[Events::eventMonsterOnDropLoot - " - "Monster corpse {}] " - "Call stack overflow. Too many lua script calls being nested.", - corpse->getName()); + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); return; } diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index b302877b3d3..b75ff31c661 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -133,9 +133,9 @@ bool MoveEvents::registerLuaPositionEvent(const std::shared_ptr<MoveEvent> moveE bool MoveEvents::registerLuaEvent(const std::shared_ptr<MoveEvent> moveEvent) { // Check if event is correct if (registerLuaItemEvent(moveEvent) - || registerLuaUniqueEvent(moveEvent) - || registerLuaActionEvent(moveEvent) - || registerLuaPositionEvent(moveEvent)) { + || registerLuaUniqueEvent(moveEvent) + || registerLuaActionEvent(moveEvent) + || registerLuaPositionEvent(moveEvent)) { return true; } else { g_logger().warn( @@ -292,7 +292,7 @@ bool MoveEvents::registerEvent(const std::shared_ptr<MoveEvent> moveEvent, const std::shared_ptr<MoveEvent> MoveEvents::getEvent(const std::shared_ptr<Tile> &tile, MoveEvent_t eventType) { if (auto it = positionsMap.find(tile->getPosition()); - it != positionsMap.end()) { + it != positionsMap.end()) { std::list<std::shared_ptr<MoveEvent>> &moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { return *moveEventList.begin(); @@ -727,12 +727,12 @@ bool MoveEvent::executeStep(const std::shared_ptr<Creature> &creature, std::shar if (!getScriptInterface()->reserveScriptEnv()) { if (item != nullptr) { g_logger().error("[MoveEvent::executeStep - Creature {} item {}, position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), item->getName(), pos.toString()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), item->getName(), pos.toString()); } else { g_logger().error("[MoveEvent::executeStep - Creature {}, position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), pos.toString()); + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), pos.toString()); } return false; } @@ -770,8 +770,8 @@ bool MoveEvent::executeEquip(const std::shared_ptr<Player> &player, const std::s // onDeEquip(player, item, slot, isCheck) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[MoveEvent::executeEquip - Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -803,9 +803,9 @@ bool MoveEvent::executeAddRemItem(const std::shared_ptr<Item> &item, const std:: // onRemoveItem(moveitem, tileitem, pos) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[MoveEvent::executeAddRemItem - " - "Item {} item on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - item->getName(), pos.getX(), pos.getY(), pos.getZ()); + "Item {} item on tile x: {} y: {} z: {}] " + "Call stack overflow. Too many lua script calls being nested.", + item->getName(), pos.getX(), pos.getY(), pos.getZ()); return false; } @@ -835,9 +835,9 @@ bool MoveEvent::executeAddRemItem(const std::shared_ptr<Item> &item, const Posit // onRemoveItem(moveitem, pos) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[MoveEvent::executeAddRemItem - " - "Item {} item on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - item->getName(), pos.getX(), pos.getY(), pos.getZ()); + "Item {} item on tile x: {} y: {} z: {}] " + "Call stack overflow. Too many lua script calls being nested.", + item->getName(), pos.getX(), pos.getY(), pos.getZ()); return false; } diff --git a/src/lua/creature/movement.hpp b/src/lua/creature/movement.hpp index 4ce4297b0f5..b8168f58521 100644 --- a/src/lua/creature/movement.hpp +++ b/src/lua/creature/movement.hpp @@ -47,7 +47,7 @@ class MoveEvents final : public Scripts { bool hasPosition(Position position) const { if (auto it = positionsMap.find(position); - it != positionsMap.end()) { + it != positionsMap.end()) { return true; } return false; @@ -63,7 +63,7 @@ class MoveEvents final : public Scripts { bool hasItemId(int32_t itemId) const { if (auto it = itemIdMap.find(itemId); - it != itemIdMap.end()) { + it != itemIdMap.end()) { return true; } return false; @@ -79,7 +79,7 @@ class MoveEvents final : public Scripts { bool hasUniqueId(int32_t uniqueId) const { if (auto it = uniqueIdMap.find(uniqueId); - it != uniqueIdMap.end()) { + it != uniqueIdMap.end()) { return true; } return false; @@ -95,7 +95,7 @@ class MoveEvents final : public Scripts { bool hasActionId(int32_t actionId) const { if (auto it = actionIdMap.find(actionId); - it != actionIdMap.end()) { + it != actionIdMap.end()) { return true; } return false; diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index a4500d126ba..8064bf52b81 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -52,16 +52,16 @@ bool Raids::loadFromXml() { ss << "raids/" << name << ".xml"; file = ss.str(); g_logger().warn("{} - " - "'file' tag missing for raid: {} using default: {}", - __FUNCTION__, name, file); + "'file' tag missing for raid: {} using default: {}", + __FUNCTION__, name, file); } interval = pugi::cast<uint32_t>(raidNode.attribute("interval2").value()) * 60; if (interval == 0) { g_logger().error("{} - " - "'interval2' tag missing or zero " - "(would divide by 0) for raid: {}", - __FUNCTION__, name); + "'interval2' tag missing or zero " + "(would divide by 0) for raid: {}", + __FUNCTION__, name); continue; } @@ -69,8 +69,8 @@ bool Raids::loadFromXml() { margin = pugi::cast<uint32_t>(attr.value()) * 60 * 1000; } else { g_logger().warn("{} - " - "'margin' tag missing for raid: {}", - __FUNCTION__, name); + "'margin' tag missing for raid: {}", + __FUNCTION__, name); margin = 0; } @@ -202,8 +202,8 @@ bool Raid::loadFromXml(const std::string &filename) { raidEvents.push_back(event); } else { g_logger().error("{} - " - "In file: {}, eventNode: {}", - __FUNCTION__, filename, eventNode.name()); + "In file: {}, eventNode: {}", + __FUNCTION__, filename, eventNode.name()); } } @@ -288,8 +288,8 @@ bool AnnounceEvent::configureRaidEvent(const pugi::xml_node &eventNode) { pugi::xml_attribute messageAttribute = eventNode.attribute("message"); if (!messageAttribute) { g_logger().error("{} - " - "'message' tag missing for announce event", - __FUNCTION__); + "'message' tag missing for announce event", + __FUNCTION__); return false; } message = messageAttribute.as_string(); @@ -311,16 +311,16 @@ bool AnnounceEvent::configureRaidEvent(const pugi::xml_node &eventNode) { messageType = MESSAGE_GAMEMASTER_CONSOLE; } else { g_logger().warn("{} - " - "Unknown type tag missing for announce event, " - "using default: {}", - __FUNCTION__, static_cast<uint32_t>(messageType)); + "Unknown type tag missing for announce event, " + "using default: {}", + __FUNCTION__, static_cast<uint32_t>(messageType)); } } else { messageType = MESSAGE_EVENT_ADVANCE; g_logger().warn("{} - " - "Type tag missing for announce event, " - "using default: {}", - __FUNCTION__, static_cast<uint32_t>(messageType)); + "Type tag missing for announce event, " + "using default: {}", + __FUNCTION__, static_cast<uint32_t>(messageType)); } return true; } @@ -341,8 +341,8 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { monsterName = attr.as_string(); } else { g_logger().error("{} - " - "'Name' tag missing for singlespawn event", - __FUNCTION__); + "'Name' tag missing for singlespawn event", + __FUNCTION__); return false; } @@ -350,8 +350,8 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { position.x = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'X' tag missing for singlespawn event", - __FUNCTION__); + "'X' tag missing for singlespawn event", + __FUNCTION__); return false; } @@ -359,8 +359,8 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { position.y = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'Y' tag missing for singlespawn event", - __FUNCTION__); + "'Y' tag missing for singlespawn event", + __FUNCTION__); return false; } @@ -368,8 +368,8 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { position.z = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'Z' tag missing for singlespawn event", - __FUNCTION__); + "'Z' tag missing for singlespawn event", + __FUNCTION__); return false; } return true; @@ -405,9 +405,9 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { centerPos.x = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "" - "'centerx' tag missing for areaspawn event", - __FUNCTION__); + "" + "'centerx' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -415,8 +415,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { centerPos.y = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'centery' tag missing for areaspawn event", - __FUNCTION__); + "'centery' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -424,8 +424,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { centerPos.z = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "centerz' tag missing for areaspawn event", - __FUNCTION__); + "centerz' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -441,8 +441,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { fromPos.x = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'fromx' tag missing for areaspawn event", - __FUNCTION__); + "'fromx' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -450,8 +450,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { fromPos.y = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'fromy' tag missing for areaspawn event", - __FUNCTION__); + "'fromy' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -459,8 +459,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { fromPos.z = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'fromz' tag missing for areaspawn event", - __FUNCTION__); + "'fromz' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -468,8 +468,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { toPos.x = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'tox' tag missing for areaspawn event", - __FUNCTION__); + "'tox' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -477,8 +477,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { toPos.y = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'toy' tag missing for areaspawn event", - __FUNCTION__); + "'toy' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -486,8 +486,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { toPos.z = pugi::cast<uint16_t>(attr.value()); } else { g_logger().error("{} - " - "'toz' tag missing for areaspawn event", - __FUNCTION__); + "'toz' tag missing for areaspawn event", + __FUNCTION__); return false; } } @@ -499,8 +499,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { name = attr.value(); } else { g_logger().error("{} - " - "'name' tag missing for monster node", - __FUNCTION__); + "'name' tag missing for monster node", + __FUNCTION__); return false; } @@ -524,8 +524,8 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { maxAmount = minAmount; } else { g_logger().error("{} - " - "'amount' tag missing for monster node", - __FUNCTION__); + "'amount' tag missing for monster node", + __FUNCTION__); return false; } } @@ -570,8 +570,8 @@ bool ScriptEvent::configureRaidEvent(const pugi::xml_node &eventNode) { pugi::xml_attribute scriptAttribute = eventNode.attribute("script"); if (!scriptAttribute) { g_logger().error("{} - " - "No script file found for raid", - __FUNCTION__); + "No script file found for raid", + __FUNCTION__); return false; } @@ -595,8 +595,8 @@ bool ScriptEvent::executeEvent() { // onRaid() if (!scriptInterface->reserveScriptEnv()) { g_logger().error("{} - Script with name {} " - "Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, getScriptName()); + "Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, getScriptName()); return false; } diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index 3586bdbceea..c1b8c028ddc 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -83,8 +83,8 @@ bool TalkAction::executeSay(std::shared_ptr<Player> player, const std::string &w // onSay(player, words, param, type) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[TalkAction::executeSay - Player {} words {}] " - "Call stack overflow. Too many lua script calls being nested. Script name {}", - player->getName(), getWords(), getScriptInterface()->getLoadingScriptName()); + "Call stack overflow. Too many lua script calls being nested. Script name {}", + player->getName(), getWords(), getScriptInterface()->getLoadingScriptName()); return false; } diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 4858b3ab264..ec5c5102794 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -728,8 +728,8 @@ int GameFunctions::luaGameGetDummies(lua_State* L) { * @details This function provides a table containing two sub-tables: one for free dummies and one for house (or premium) dummies. * @note usage on lua: - local dummies = Game.getDummies() - local rate = dummies[1] -- Retrieve dummy rate + local dummies = Game.getDummies() + local rate = dummies[1] -- Retrieve dummy rate */ const auto &dummies = Item::items.getDummys(); diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index 0bcf7c9c11a..a24407c1682 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -18,7 +18,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { // Spell(type) ex: Spell(SPELL_INSTANT) or Spell(SPELL_RUNE) to create a new spell if (lua_gettop(L) == 1) { g_logger().error("[SpellFunctions::luaSpellCreate] - " - "There is no parameter set!"); + "There is no parameter set!"); lua_pushnil(L); return 1; } @@ -209,16 +209,16 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { spell->setGroup(group); } else { g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); + "Unknown group: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } pushBoolean(L, true); } else { g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); + "Unknown group: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } @@ -235,8 +235,8 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { spell->setGroup(primaryGroup); } else { g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {}", - getString(L, 2)); + "Unknown primaryGroup: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } @@ -245,16 +245,16 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { spell->setSecondaryGroup(secondaryGroup); } else { g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown secondaryGroup: {}", - getString(L, 3)); + "Unknown secondaryGroup: {}", + getString(L, 3)); pushBoolean(L, false); return 1; } pushBoolean(L, true); } else { g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {} or secondaryGroup: {}", - getString(L, 2), getString(L, 3)); + "Unknown primaryGroup: {} or secondaryGroup: {}", + getString(L, 2), getString(L, 3)); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 7a40cc426f1..4ae5da0e132 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -793,7 +793,7 @@ int CreatureFunctions::luaCreatureTeleportTo(lua_State* L) { const Position oldPosition = creature->getPosition(); if (auto ret = g_game().internalTeleport(creature, position, pushMovement); - ret != RETURNVALUE_NOERROR) { + ret != RETURNVALUE_NOERROR) { g_logger().debug("[{}] Failed to teleport creature {}, on position {}, error code: {}", __FUNCTION__, creature->getName(), oldPosition.toString(), getReturnMessage(ret)); pushBoolean(L, false); return 1; diff --git a/src/lua/functions/creatures/monster/loot_functions.cpp b/src/lua/functions/creatures/monster/loot_functions.cpp index 3fe3a188368..47e0d8778e3 100644 --- a/src/lua/functions/creatures/monster/loot_functions.cpp +++ b/src/lua/functions/creatures/monster/loot_functions.cpp @@ -29,7 +29,7 @@ int LootFunctions::luaLootSetId(lua_State* L) { pushBoolean(L, true); } else { g_logger().warn("[LootFunctions::luaLootSetId] - " - "Unknown loot item loot, int value expected"); + "Unknown loot item loot, int value expected"); lua_pushnil(L); } } else { @@ -47,16 +47,16 @@ int LootFunctions::luaLootSetIdFromName(lua_State* L) { if (ids.first == Item::items.nameToItems.cend()) { g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " - "Unknown loot item {}", - name); + "Unknown loot item {}", + name); lua_pushnil(L); return 1; } if (std::next(ids.first) != ids.second) { g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " - "Non-unique loot item {}", - name); + "Non-unique loot item {}", + name); lua_pushnil(L); return 1; } @@ -65,7 +65,7 @@ int LootFunctions::luaLootSetIdFromName(lua_State* L) { pushBoolean(L, true); } else { g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " - "Unknown loot item loot, string value expected"); + "Unknown loot item loot, string value expected"); lua_pushnil(L); } return 1; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index b937083fddb..ded7c2054db 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -681,8 +681,8 @@ int MonsterTypeFunctions::luaMonsterTypeCombatImmunities(lua_State* L) { combatType = COMBAT_NEUTRALDAMAGE; } else { g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeCombatImmunities] - " - "Unknown immunity name {} for monster: {}", - immunity, monsterType->name); + "Unknown immunity name {} for monster: {}", + immunity, monsterType->name); lua_pushnil(L); } @@ -739,8 +739,8 @@ int MonsterTypeFunctions::luaMonsterTypeConditionImmunities(lua_State* L) { conditionType = CONDITION_BLEEDING; } else { g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeConditionImmunities] - " - "Unknown immunity name: {} for monster: {}", - immunity, monsterType->name); + "Unknown immunity name: {} for monster: {}", + immunity, monsterType->name); lua_pushnil(L); } @@ -1195,8 +1195,8 @@ int MonsterTypeFunctions::luaMonsterTypeRace(lua_State* L) { monsterType->info.race = RACE_INK; } else { g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeRace] - " - "Unknown race type {}", - race); + "Unknown race type {}", + race); lua_pushnil(L); return 1; } diff --git a/src/lua/functions/creatures/npc/shop_functions.cpp b/src/lua/functions/creatures/npc/shop_functions.cpp index ee848e0bf47..122e249a8db 100644 --- a/src/lua/functions/creatures/npc/shop_functions.cpp +++ b/src/lua/functions/creatures/npc/shop_functions.cpp @@ -28,7 +28,7 @@ int ShopFunctions::luaShopSetId(lua_State* L) { pushBoolean(L, true); } else { g_logger().warn("[ShopFunctions::luaShopSetId] - " - "Unknown shop item shop, int value expected"); + "Unknown shop item shop, int value expected"); lua_pushnil(L); } } else { @@ -46,16 +46,16 @@ int ShopFunctions::luaShopSetIdFromName(lua_State* L) { if (ids.first == Item::items.nameToItems.cend()) { g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " - "Unknown shop item {}", - name); + "Unknown shop item {}", + name); lua_pushnil(L); return 1; } if (std::next(ids.first) != ids.second) { g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " - "Non-unique shop item {}", - name); + "Non-unique shop item {}", + name); lua_pushnil(L); return 1; } @@ -64,7 +64,7 @@ int ShopFunctions::luaShopSetIdFromName(lua_State* L) { pushBoolean(L, true); } else { g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " - "Unknown shop item shop, string value expected"); + "Unknown shop item shop, string value expected"); lua_pushnil(L); } return 1; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 5f14db6ea99..9cc5434abd5 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -407,7 +407,7 @@ int PlayerFunctions::luaPlayerGetPreyExperiencePercentage(lua_State* L) { // player:getPreyExperiencePercentage(raceId) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1)) { if (const std::unique_ptr<PreySlot> &slot = player->getPreyWithMonster(getNumber<uint16_t>(L, 2, 0)); - slot && slot->isOccupied() && slot->bonus == PreyBonus_Experience && slot->bonusTimeLeft > 0) { + slot && slot->isOccupied() && slot->bonus == PreyBonus_Experience && slot->bonusTimeLeft > 0) { lua_pushnumber(L, static_cast<lua_Number>(100 + slot->bonusPercentage)); } else { lua_pushnumber(L, 100); @@ -457,7 +457,7 @@ int PlayerFunctions::luaPlayerGetPreyLootPercentage(lua_State* L) { // player:getPreyLootPercentage(raceid) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1)) { if (const std::unique_ptr<PreySlot> &slot = player->getPreyWithMonster(getNumber<uint16_t>(L, 2, 0)); - slot && slot->isOccupied() && slot->bonus == PreyBonus_Loot) { + slot && slot->isOccupied() && slot->bonus == PreyBonus_Loot) { lua_pushnumber(L, slot->bonusPercentage); } else { lua_pushnumber(L, 0); @@ -472,7 +472,7 @@ int PlayerFunctions::luaPlayerisMonsterPrey(lua_State* L) { // player:isMonsterPrey(raceid) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1)) { if (const std::unique_ptr<PreySlot> &slot = player->getPreyWithMonster(getNumber<uint16_t>(L, 2, 0)); - slot && slot->isOccupied()) { + slot && slot->isOccupied()) { pushBoolean(L, true); } else { pushBoolean(L, false); @@ -486,7 +486,7 @@ int PlayerFunctions::luaPlayerisMonsterPrey(lua_State* L) { int PlayerFunctions::luaPlayerPreyThirdSlot(lua_State* L) { // get: player:preyThirdSlot() set: player:preyThirdSlot(bool) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1); - const auto &slot = player->getPreySlotById(PreySlot_Three)) { + const auto &slot = player->getPreySlotById(PreySlot_Three)) { if (!slot) { lua_pushnil(L); } else if (lua_gettop(L) == 1) { @@ -513,7 +513,7 @@ int PlayerFunctions::luaPlayerPreyThirdSlot(lua_State* L) { int PlayerFunctions::luaPlayerTaskThirdSlot(lua_State* L) { // get: player:taskHuntingThirdSlot() set: player:taskHuntingThirdSlot(bool) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1); - const auto &slot = player->getTaskHuntingSlotById(PreySlot_Three)) { + const auto &slot = player->getTaskHuntingSlotById(PreySlot_Three)) { if (lua_gettop(L) == 1) { pushBoolean(L, slot->state != PreyTaskDataState_Locked); } else { @@ -3574,7 +3574,7 @@ int PlayerFunctions::luaPlayerBosstiaryCooldownTimer(lua_State* L) { int PlayerFunctions::luaPlayerGetBosstiaryLevel(lua_State* L) { // player:getBosstiaryLevel(name) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1); - player) { + player) { const auto mtype = g_monsters().getMonsterType(getString(L, 2)); if (mtype) { uint32_t bossId = mtype->info.raceid; @@ -3596,7 +3596,7 @@ int PlayerFunctions::luaPlayerGetBosstiaryLevel(lua_State* L) { int PlayerFunctions::luaPlayerGetBosstiaryKills(lua_State* L) { // player:getBosstiaryKills(name) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1); - player) { + player) { const auto mtype = g_monsters().getMonsterType(getString(L, 2)); if (mtype) { uint32_t bossId = mtype->info.raceid; @@ -3618,7 +3618,7 @@ int PlayerFunctions::luaPlayerGetBosstiaryKills(lua_State* L) { int PlayerFunctions::luaPlayerAddBosstiaryKill(lua_State* L) { // player:addBosstiaryKill(name[, amount = 1]) if (std::shared_ptr<Player> player = getUserdataShared<Player>(L, 1); - player) { + player) { const auto mtype = g_monsters().getMonsterType(getString(L, 2)); if (mtype) { g_ioBosstiary().addBosstiaryKill(player, mtype, getNumber<uint32_t>(L, 3, 1)); diff --git a/src/lua/functions/events/action_functions.cpp b/src/lua/functions/events/action_functions.cpp index c1fd581f8bd..31b202465c7 100644 --- a/src/lua/functions/events/action_functions.cpp +++ b/src/lua/functions/events/action_functions.cpp @@ -133,7 +133,7 @@ int ActionFunctions::luaActionPosition(lua_State* L) { // The parameter "- 1" because self is a parameter aswell, which we want to skip L 1 (UserData) // isNumber(L, 2) is for skip the itemId if (int parameters = lua_gettop(L) - 1; - parameters > 1 && isNumber(L, 2)) { + parameters > 1 && isNumber(L, 2)) { for (int i = 0; i < parameters; ++i) { action->setPositionsVector(getPosition(L, 2 + i)); } diff --git a/src/lua/functions/events/creature_event_functions.cpp b/src/lua/functions/events/creature_event_functions.cpp index 7b5e25a12f3..7edbabad1d3 100644 --- a/src/lua/functions/events/creature_event_functions.cpp +++ b/src/lua/functions/events/creature_event_functions.cpp @@ -54,8 +54,8 @@ int CreatureEventFunctions::luaCreatureEventType(lua_State* L) { creatureEvent->setEventType(CREATURE_EVENT_EXTENDED_OPCODE); } else { g_logger().error("[CreatureEventFunctions::luaCreatureEventType] - " - "Invalid type for creature event: {}", - typeName); + "Invalid type for creature event: {}", + typeName); pushBoolean(L, false); } creatureEvent->setLoaded(true); diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index 8f54e5b03fd..ea6a5f7df03 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -42,7 +42,7 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { global->setEventType(GLOBALEVENT_ON_THINK); } else { g_logger().error("[GlobalEventFunctions::luaGlobalEventType] - " - "Invalid type for global event: {}"); + "Invalid type for global event: {}"); pushBoolean(L, false); } pushBoolean(L, true); @@ -97,8 +97,8 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { int32_t hour = params.front(); if (hour < 0 || hour > 23) { g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid hour {} for globalevent with name: {}", - timer, globalevent->getName()); + "Invalid hour {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } @@ -111,8 +111,8 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { min = params[1]; if (min < 0 || min > 59) { g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid minute: {} for globalevent with name: {}", - timer, globalevent->getName()); + "Invalid minute: {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } @@ -121,8 +121,8 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { sec = params[2]; if (sec < 0 || sec > 59) { g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid minute: {} for globalevent with name: {}", - timer, globalevent->getName()); + "Invalid minute: {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/events/move_event_functions.cpp b/src/lua/functions/events/move_event_functions.cpp index 5de49c26f59..8340dded92f 100644 --- a/src/lua/functions/events/move_event_functions.cpp +++ b/src/lua/functions/events/move_event_functions.cpp @@ -47,8 +47,8 @@ int MoveEventFunctions::luaMoveEventType(lua_State* L) { moveevent->moveFunction = moveevent->RemoveItemField; } else { g_logger().error("[MoveEventFunctions::luaMoveEventType] - " - "No valid event name: {}", - typeName); + "No valid event name: {}", + typeName); pushBoolean(L, false); } pushBoolean(L, true); @@ -126,8 +126,8 @@ int MoveEventFunctions::luaMoveEventSlot(lua_State* L) { moveevent->setSlot(SLOTP_AMMO); } else { g_logger().warn("[MoveEventFunctions::luaMoveEventSlot] - " - "Unknown slot type: {}", - slotName); + "Unknown slot type: {}", + slotName); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/items/item_functions.cpp b/src/lua/functions/items/item_functions.cpp index 71ab040f47d..361a469da32 100644 --- a/src/lua/functions/items/item_functions.cpp +++ b/src/lua/functions/items/item_functions.cpp @@ -398,7 +398,7 @@ int ItemFunctions::luaItemSetAttribute(lua_State* L) { switch (attribute) { case ItemAttribute_t::DECAYSTATE: { if (ItemDecayState_t decayState = getNumber<ItemDecayState_t>(L, 3); - decayState == DECAYING_FALSE || decayState == DECAYING_STOPPING) { + decayState == DECAYING_FALSE || decayState == DECAYING_STOPPING) { g_decay().stopDecay(item); } else { g_decay().startDecay(item); diff --git a/src/lua/functions/items/weapon_functions.cpp b/src/lua/functions/items/weapon_functions.cpp index 962c1181fe4..61eff2dfb66 100644 --- a/src/lua/functions/items/weapon_functions.cpp +++ b/src/lua/functions/items/weapon_functions.cpp @@ -77,8 +77,8 @@ int WeaponFunctions::luaWeaponAction(lua_State* L) { weapon->action = WEAPONACTION_MOVE; } else { g_logger().error("[WeaponFunctions::luaWeaponAction] - " - "No valid action {}", - typeName); + "No valid action {}", + typeName); pushBoolean(L, false); } pushBoolean(L, true); @@ -285,8 +285,8 @@ int WeaponFunctions::luaWeaponElement(lua_State* L) { weapon->params.combatType = COMBAT_HOLYDAMAGE; } else { g_logger().warn("[WeaponFunctions:luaWeaponElement] - " - "Type {} does not exist", - element); + "Type {} does not exist", + element); } } else { weapon->params.combatType = getNumber<CombatType_t>(L, 2); @@ -539,8 +539,8 @@ int WeaponFunctions::luaWeaponAmmoType(lua_State* L) { it.ammoType = AMMO_BOLT; } else { g_logger().warn("[WeaponFunctions:luaWeaponAmmoType] - " - "Type {} does not exist", - type); + "Type {} does not exist", + type); lua_pushnil(L); return 1; } @@ -604,8 +604,8 @@ int WeaponFunctions::luaWeaponExtraElement(lua_State* L) { it.abilities->elementType = COMBAT_HOLYDAMAGE; } else { g_logger().warn("[WeaponFunctions:luaWeaponExtraElement] - " - "Type {} does not exist", - element); + "Type {} does not exist", + element); } } else { it.abilities->elementType = getNumber<CombatType_t>(L, 3); diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index 69ee85dd865..c2dedfb57b6 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -289,13 +289,13 @@ void LuaFunctionsLoader::setWeakMetatable(lua_State* L, int32_t index, const std int metatable = lua_gettop(L); for (static const std::vector<std::string> methodKeys = { "__index", "__metatable", "__eq" }; - const std::string &metaKey : methodKeys) { + const std::string &metaKey : methodKeys) { lua_getfield(L, childMetatable, metaKey.c_str()); lua_setfield(L, metatable, metaKey.c_str()); } for (static const std::vector<int> methodIndexes = { 'h', 'p', 't' }; - int metaIndex : methodIndexes) { + int metaIndex : methodIndexes) { lua_rawgeti(L, childMetatable, metaIndex); lua_rawseti(L, metatable, metaIndex); } diff --git a/src/lua/global/baseevents.cpp b/src/lua/global/baseevents.cpp index 9728342f7ed..3b312100d3b 100644 --- a/src/lua/global/baseevents.cpp +++ b/src/lua/global/baseevents.cpp @@ -25,7 +25,7 @@ bool BaseEvents::loadFromXml() { basePath + "lib/" + scriptsName + ".lua", scriptsName + ".lua" ) - == -1) { + == -1) { g_logger().warn(__FUNCTION__, scriptsName, scriptsName); } @@ -107,8 +107,8 @@ bool Event::checkScript(const std::string &basePath, const std::string &scriptsN int32_t id = testInterface->getEvent(getScriptEventName()); if (id == -1) { g_logger().warn("[Event::checkScript] - Event " - "{} not found {}", - getScriptEventName(), scriptFile); + "{} not found {}", + getScriptEventName(), scriptFile); return false; } return true; diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 9626173dc9f..04420431def 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -126,8 +126,8 @@ void GlobalEvents::think() { if (!globalEvent->executeEvent()) { g_logger().error("[GlobalEvents::think] - " - "Failed to execute event: {}", - globalEvent->getName()); + "Failed to execute event: {}", + globalEvent->getName()); } nextExecutionTime = globalEvent->getInterval(); @@ -206,8 +206,8 @@ bool GlobalEvent::executePeriodChange(LightState_t lightState, LightInfo lightIn // onPeriodChange(lightState, lightTime) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[GlobalEvent::executePeriodChange - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } @@ -226,8 +226,8 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) { // onRecord(current, old) if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[GlobalEvent::executeRecord - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } @@ -245,8 +245,8 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) { bool GlobalEvent::executeEvent() const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[GlobalEvent::executeEvent - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } diff --git a/src/lua/scripts/lua_environment.cpp b/src/lua/scripts/lua_environment.cpp index f3d2273df34..cd5023a8d4b 100644 --- a/src/lua/scripts/lua_environment.cpp +++ b/src/lua/scripts/lua_environment.cpp @@ -174,8 +174,8 @@ void LuaEnvironment::executeTimerEvent(uint32_t eventIndex) { callFunction(timerEventDesc.parameters.size()); } else { g_logger().error("[LuaEnvironment::executeTimerEvent - Lua file {}] " - "Call stack overflow. Too many lua script calls being nested", - getLoadingFile()); + "Call stack overflow. Too many lua script calls being nested", + getLoadingFile()); } // free resources diff --git a/src/lua/scripts/scripts.cpp b/src/lua/scripts/scripts.cpp index 1d395cdd096..cb92c1cc29a 100644 --- a/src/lua/scripts/scripts.cpp +++ b/src/lua/scripts/scripts.cpp @@ -88,7 +88,7 @@ bool Scripts::loadScripts(std::string loadPath, bool isLib, bool reload) { // Check if file start with "#" if (std::string disable("#"); - file.front() == disable.front()) { + file.front() == disable.front()) { // Send log of disabled script if (g_configManager().getBoolean(SCRIPTS_CONSOLE_LOGS, __FUNCTION__)) { g_logger().info("[script]: {} [disabled]", realPath.filename().string()); diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index 842622b2af5..261147fff40 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -753,8 +753,8 @@ bool Houses::loadHousesXML(const std::string &filename) { ); if (entryPos.x == 0 && entryPos.y == 0 && entryPos.z == 0) { g_logger().warn("[Houses::loadHousesXML] - Entry not set for house " - "name: {} with id: {}", - house->getName(), houseId); + "name: {} with id: {}", + house->getName(), houseId); } house->setEntryPos(entryPos); diff --git a/src/map/house/housetile.cpp b/src/map/house/housetile.cpp index 6b301f0e817..5e728a14ea3 100644 --- a/src/map/house/housetile.cpp +++ b/src/map/house/housetile.cpp @@ -95,9 +95,9 @@ std::shared_ptr<Cylinder> HouseTile::queryDestination(int32_t &index, const std: std::shared_ptr<Tile> destTile = g_game().map.getTile(entryPos); if (!destTile) { g_logger().error("[HouseTile::queryDestination] - " - "Entry not correct for house name: {} " - "with id: {} not found tile: {}", - house->getName(), house->getId(), entryPos.toString()); + "Entry not correct for house name: {} " + "with id: {} not found tile: {}", + house->getName(), house->getId(), entryPos.toString()); destTile = g_game().map.getTile(player->getTemplePosition()); if (!destTile) { destTile = Tile::nullptr_tile; diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index 0448615ee99..cb3e95eb30e 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -70,7 +70,7 @@ void MapCache::parseItemAttr(const std::shared_ptr<BasicItem> &BasicItem, std::s } /* if (BasicItem.description != 0) - item->setAttribute(ItemAttribute_t::DESCRIPTION, STRING_CACHE[BasicItem.description]);*/ + item->setAttribute(ItemAttribute_t::DESCRIPTION, STRING_CACHE[BasicItem.description]);*/ } std::shared_ptr<Item> MapCache::createItem(const std::shared_ptr<BasicItem> &BasicItem, Position position) { diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 10aaf0faf87..405119b83e9 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -59,11 +59,11 @@ bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onl for (const auto &creature : *list) { const auto &specPos = creature->getPosition(); if (centerPos.x - specPos.x >= minRangeX - && centerPos.y - specPos.y >= minRangeY - && centerPos.x - specPos.x <= maxRangeX - && centerPos.y - specPos.y <= maxRangeY - && (multifloor || specPos.z == centerPos.z) - && (!onlyPlayers || creature->getPlayer())) { + && centerPos.y - specPos.y >= minRangeY + && centerPos.x - specPos.x <= maxRangeX + && centerPos.y - specPos.y <= maxRangeY + && (multifloor || specPos.z == centerPos.z) + && (!onlyPlayers || creature->getPlayer())) { spectators.emplace_back(creature); } } diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 3effd52f829..3072384420d 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -241,7 +241,7 @@ void Connection::parsePacket(const std::error_code &error) { // Check packet checksum uint32_t checksum; if (int32_t len = msg.getLength() - msg.getBufferPosition() - CHECKSUM_LENGTH; - len > 0) { + len > 0) { checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + CHECKSUM_LENGTH, len); } else { checksum = 0; diff --git a/src/server/network/protocol/protocol.cpp b/src/server/network/protocol/protocol.cpp index 78cff72fab4..255899f2f99 100644 --- a/src/server/network/protocol/protocol.cpp +++ b/src/server/network/protocol/protocol.cpp @@ -78,7 +78,7 @@ bool Protocol::onRecvMessage(NetworkMessage &msg) { } else { uint32_t checksum; if (int32_t len = msg.getLength() - msg.getBufferPosition(); - len > 0) { + len > 0) { checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition(), len); } else { checksum = 0; diff --git a/src/server/network/protocol/protocol.hpp b/src/server/network/protocol/protocol.hpp index c264f49bf01..54e3dcfd4c3 100644 --- a/src/server/network/protocol/protocol.hpp +++ b/src/server/network/protocol/protocol.hpp @@ -50,7 +50,7 @@ class Protocol : public std::enable_shared_from_this<Protocol> { void send(OutputMessage_ptr msg) const { if (auto connection = getConnection(); - connection != nullptr) { + connection != nullptr) { connection->send(msg); } } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 58f06eee5ae..1476f255135 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1485,7 +1485,7 @@ void ProtocolGame::GetFloorDescription(NetworkMessage &msg, int32_t x, int32_t y void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool &known, uint32_t &removedKnown) { if (auto [creatureKnown, creatureInserted] = knownCreatureSet.insert(id); - !creatureInserted) { + !creatureInserted) { known = true; return; } @@ -1499,7 +1499,7 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool &known, uint32_t &remo // We need to protect party players from removing std::shared_ptr<Creature> creature = g_game().getCreatureByID(*it); if (std::shared_ptr<Player> checkPlayer; - creature && (checkPlayer = creature->getPlayer()) != nullptr) { + creature && (checkPlayer = creature->getPlayer()) != nullptr) { if (player->getParty() != checkPlayer->getParty() && !canSee(creature)) { removedKnown = *it; knownCreatureSet.erase(it); @@ -2334,7 +2334,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { if (!mtype) { g_logger().warn("[ProtocolGame::parseBestiarysendMonsterData] - " - "MonsterType was not found"); + "MonsterType was not found"); return; } @@ -2944,8 +2944,8 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { if (race.empty()) { g_logger().warn("[ProtocolGame::parseBestiarysendCreature] - " - "Race was not found: {}, search: {}", - raceName, search); + "Race was not found: {}, search: {}", + raceName, search); return; } text = raceName; @@ -4887,7 +4887,7 @@ void ProtocolGame::updateCoinBalance() { threadPlayer->sendCoinBalance(); } }, - "ProtocolGame::updateCoinBalance"); + "ProtocolGame::updateCoinBalance"); } void ProtocolGame::sendMarketLeave() { @@ -5282,9 +5282,9 @@ void ProtocolGame::sendOpenForge() { for each convergence fusion (1 per item slot, only class 4): 1 byte: count fusable items for each fusable item: - 2 bytes: item id - 1 byte: tier - 2 bytes: count + 2 bytes: item id + 1 byte: tier + 2 bytes: count */ for (const auto &[slot, itemMap] : convergenceItemsMap) { uint8_t totalItemsCount = 0; @@ -5364,15 +5364,15 @@ void ProtocolGame::sendOpenForge() { /* for each convergence transfer: - 2 bytes: count donors - for each donor: - 2 bytes: item id - 1 byte: tier - 2 bytes: count - 2 bytes: count receivers - for each receiver: - 2 bytes: item id - 2 bytes: count + 2 bytes: count donors + for each donor: + 2 bytes: item id + 1 byte: tier + 2 bytes: count + 2 bytes: count receivers + for each receiver: + 2 bytes: item id + 2 bytes: count */ for (const auto &[slot, itemMap] : convergenceItemsMap) { uint16_t donorCount = 0; @@ -7694,7 +7694,7 @@ void ProtocolGame::addImbuementInfo(NetworkMessage &msg, uint16_t imbuementId) c msg.add<uint32_t>(imbuementId); msg.addString(baseImbuement->name + " " + imbuement->getName(), "ProtocolGame::addImbuementInfo - baseImbuement->name + " - " + imbuement->getName()"); + " + imbuement->getName()"); msg.addString(imbuement->getDescription(), "ProtocolGame::addImbuementInfo - imbuement->getDescription()"); msg.addString(categoryImbuement->name + imbuement->getSubGroup(), "ProtocolGame::addImbuementInfo - categoryImbuement->name + imbuement->getSubGroup()"); @@ -7832,7 +7832,7 @@ void ProtocolGame::updatePartyTrackerAnalyzer(const std::shared_ptr<Party> party for (const std::shared_ptr<PartyAnalyzer> &analyzer : party->membersData) { msg.add<uint32_t>(analyzer->id); if (std::shared_ptr<Player> member = g_game().getPlayerByID(analyzer->id); - !member || !member->getParty() || member->getParty() != party) { + !member || !member->getParty() || member->getParty() != party) { msg.addByte(0); } else { msg.addByte(1); @@ -8226,7 +8226,7 @@ void ProtocolGame::sendInventoryImbuements(const std::map<Slots_t, std::shared_p const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); msg.addByte(0x01); msg.addString(baseImbuement->name + " " + imbuement->getName(), "ProtocolGame::sendInventoryImbuements - baseImbuement->name + " - " + imbuement->getName()"); + " + imbuement->getName()"); msg.add<uint16_t>(imbuement->getIconID()); msg.add<uint32_t>(imbuementInfo.duration); @@ -8714,7 +8714,7 @@ void ProtocolGame::parseSendBosstiarySlots() { auto bossesUnlockedList = g_ioBosstiary().getBosstiaryFinished(player); if (auto it = std::ranges::find(bossesUnlockedList.begin(), bossesUnlockedList.end(), boostedBossId); - it != bossesUnlockedList.end()) { + it != bossesUnlockedList.end()) { bossesUnlockedList.erase(it); } auto bossesUnlockedSize = static_cast<uint16_t>(bossesUnlockedList.size()); @@ -8930,7 +8930,7 @@ void ProtocolGame::sendBosstiaryCooldownTimer() { msg.skipBytes(2); // Boss count uint16_t bossesCount = 0; for (std::map<uint16_t, std::string> bossesMap = g_ioBosstiary().getBosstiaryMap(); - const auto &[bossRaceId, _] : bossesMap) { + const auto &[bossRaceId, _] : bossesMap) { const auto mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossRaceId); if (!mType) { continue; diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index b59a3704429..d6e9e3cb9f1 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -177,5 +177,5 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { g_dispatcher().addEvent([self = std::static_pointer_cast<ProtocolLogin>(shared_from_this()), accountDescriptor, password] { self->getCharacterList(accountDescriptor, password); }, - "ProtocolLogin::getCharacterList"); + "ProtocolLogin::getCharacterList"); } diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index a4dfe1651d6..1674b80a2b5 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -47,7 +47,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { g_dispatcher().addEvent([self = std::static_pointer_cast<ProtocolStatus>(shared_from_this())] { self->sendStatusString(); }, - "ProtocolStatus::sendStatusString"); + "ProtocolStatus::sendStatusString"); return; } break; @@ -63,7 +63,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { g_dispatcher().addEvent([self = std::static_pointer_cast<ProtocolStatus>(shared_from_this()), requestedInfo, characterName] { self->sendInfo(requestedInfo, characterName); }, - "ProtocolStatus::sendInfo"); + "ProtocolStatus::sendInfo"); return; } diff --git a/src/server/server.hpp b/src/server/server.hpp index 054ec2607c8..42134f765b0 100644 --- a/src/server/server.hpp +++ b/src/server/server.hpp @@ -114,8 +114,8 @@ template <typename ProtocolType> bool ServiceManager::add(uint16_t port) { if (port == 0) { g_logger().error("[ServiceManager::add] - " - "No port provided for service {}, service disabled", - ProtocolType::protocol_name()); + "No port provided for service {}, service disabled", + ProtocolType::protocol_name()); return false; } @@ -132,8 +132,8 @@ bool ServiceManager::add(uint16_t port) { if (service_port->is_single_socket() || ProtocolType::SERVER_SENDS_FIRST) { g_logger().error("[ServiceManager::add] - " - "{} and {} cannot use the same port {}", - ProtocolType::protocol_name(), service_port->get_protocol_names(), port); + "{} and {} cannot use the same port {}", + ProtocolType::protocol_name(), service_port->get_protocol_names(), port); return false; } } diff --git a/src/server/signals.cpp b/src/server/signals.cpp index 978b589eb38..c85b21312eb 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -38,8 +38,8 @@ void Signals::asyncWait() { set.async_wait([this](std::error_code err, int signal) { if (err) { g_logger().error("[Signals::asyncWait] - " - "Signal handling error: {}", - err.message()); + "Signal handling error: {}", + err.message()); return; } dispatchSignalHandler(signal); diff --git a/src/utils/simd.hpp b/src/utils/simd.hpp index 3961d51e708..7e57e75bfad 100644 --- a/src/utils/simd.hpp +++ b/src/utils/simd.hpp @@ -13,7 +13,7 @@ #if defined(__DISABLE_VECTORIZATION__) // You might want to disable vectorization on some compilers - // it can just get buggy and the engine will crashes + // it can just get buggy and the engine will crashes #undef __NEON__ #undef __ARM_NEON__ #undef __ARM_FEATURE_SIMD32 From 609a1871780b01e34f3bf116bb69e28ad5009716 Mon Sep 17 00:00:00 2001 From: sebbesiren <35768829+sebbesiren@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:53:24 +0200 Subject: [PATCH 93/95] fix: offline training (#2341) --- .../creaturescripts/player/offline_training.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/data/scripts/creaturescripts/player/offline_training.lua b/data/scripts/creaturescripts/player/offline_training.lua index 4466c6ee0e3..7d08f07efe8 100644 --- a/data/scripts/creaturescripts/player/offline_training.lua +++ b/data/scripts/creaturescripts/player/offline_training.lua @@ -55,18 +55,23 @@ function offlineTraining.onLogin(player) local vocation = player:getVocation() local promotion = vocation:getPromotion() local topVocation = not promotion and vocation or promotion - local updateSkills = false + local tries = nil if table.contains({ SKILL_CLUB, SKILL_SWORD, SKILL_AXE, SKILL_DISTANCE }, offlineTrainingSkill) then - local modifier = topVocation:getBaseAttackSpeed() / 1000 / configManager.getFloat(configKeys.RATE_OFFLINE_TRAINING_SPEED) - updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2)) + local modifier = topVocation:getBaseAttackSpeed() / 1000 + tries = (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2) elseif offlineTrainingSkill == SKILL_MAGLEVEL then - local gainTicks = topVocation:getManaGainTicks() * 2 + local gainTicks = topVocation:getManaGainTicks() / 1000 if gainTicks == 0 then gainTicks = 1 end - updateSkills = player:addOfflineTrainingTries(SKILL_MAGLEVEL, trainingTime * (vocation:getManaGainAmount() / gainTicks)) + tries = trainingTime * (vocation:getManaGainAmount() / gainTicks) + end + + local updateSkills = false + if tries then + updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, tries * configManager.getFloat(configKeys.RATE_OFFLINE_TRAINING_SPEED)) end if updateSkills then From d3ddea0b038c6da6f8019f7afe3f98827f386dec Mon Sep 17 00:00:00 2001 From: Eduardo Dantas <eduardo.dantas@hotmail.com.br> Date: Thu, 27 Jun 2024 13:58:25 -0300 Subject: [PATCH 94/95] fix: gamestore nil player (#2715) Related to #2694 --- data/modules/scripts/gamestore/init.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 214aec77d43..322a7749c93 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -540,6 +540,11 @@ end -- Both functions use same formula! function parseOpenTransactionHistory(playerId, msg) + local player = Player(playerId) + if not player then + return + end + local page = 1 GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE = msg:getByte() sendStoreTransactionHistory(playerId, page, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE) @@ -547,6 +552,11 @@ function parseOpenTransactionHistory(playerId, msg) end function parseRequestTransactionHistory(playerId, msg) + local player = Player(playerId) + if not player then + return + end + local page = msg:getU32() sendStoreTransactionHistory(playerId, page + 1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE) player:updateUIExhausted() From 926a5f3e1605f73b0560c994a0116cec5685be7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= <brunolamonato@gmail.com> Date: Tue, 2 Jul 2024 16:13:00 -0300 Subject: [PATCH 95/95] feat: possibility to persist NPC on map with /n talkaction (#2682) --- data/scripts/talkactions/god/create_npc.lua | 38 ++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/data/scripts/talkactions/god/create_npc.lua b/data/scripts/talkactions/god/create_npc.lua index 4aeec3dde80..b6d0412d391 100644 --- a/data/scripts/talkactions/god/create_npc.lua +++ b/data/scripts/talkactions/god/create_npc.lua @@ -1,3 +1,6 @@ +-- To summon a temporary npc use /n npcname +-- To summon a permanent npc use /n npcname,true + local createNpc = TalkAction("/n") function createNpc.onSay(player, words, param) @@ -9,11 +12,44 @@ function createNpc.onSay(player, words, param) return true end + local split = param:split(",") + local name = split[1] + local permanentStr = split[2] + local position = player:getPosition() - local npc = Game.createNpc(param, position) + local npc = Game.createNpc(name, position) if npc then npc:setMasterPos(position) position:sendMagicEffect(CONST_ME_MAGIC_RED) + + if permanentStr and permanentStr == "true" then + local mapName = configManager.getString(configKeys.MAP_NAME) + local mapNpcsPath = mapName .. "-npc.xml" + local filePath = string.format("%s/world/%s", DATA_DIRECTORY, mapNpcsPath) + local npcsFile = io.open(filePath, "r") + if not npcsFile then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to add permanent NPC. NPC File not found.") + return true + end + local fileContent = npcsFile:read("*all") + npcsFile:close() + local endTag = "</npcs>" + if not fileContent:find(endTag, 1, true) then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to add permanent NPC. The NPC file format is incorrect. Missing end tag " .. endTag .. ".") + return true + end + local textToAdd = string.format('\t<npc centerx="%i" centery="%i" centerz="%i" radius="1">\n\t\t<npc name="%s" x="0" y="0" z="%i" spawntime="60" />\n\t</npc>', position.x, position.y, position.z, name, position.z) + local newFileContent = fileContent:gsub(endTag, textToAdd .. "\n" .. endTag) + npcsFile = io.open(filePath, "w") + if not npcsFile then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to write to the NPC file.") + return true + end + npcsFile:write(newFileContent) + npcsFile:close() + + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Permanent NPC added successfully.") + end else player:sendCancelMessage("There is not enough room.") position:sendMagicEffect(CONST_ME_POFF)