diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index e772f78b3a1..14dc6e22153 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -87,6 +87,7 @@ endif() # cmake -DDEBUG_LOG=ON .. if(DEBUG_LOG) add_definitions(-DDEBUG_LOG=ON) + add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) log_option_enabled("DEBUG LOG") else() log_option_disabled("DEBUG LOG") diff --git a/config.lua.dist b/config.lua.dist index 07792a0289b..7d0360d9360 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -9,7 +9,8 @@ coreDirectory = "data" -- Set log level -- It can be trace, debug, info, warning, error, critical, off (default: info). --- NOTE: Will only display logs with level higher or equal the one set. +-- NOTE: It will only be valid after the server starts up and only display logs with level higher or equal the one set. +-- NOTE: Debug and trace logs are only available if compiled in debug mode. logLevel = "info" --- Toggles the server's maintenance mode. @@ -52,7 +53,7 @@ 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 @@ -145,7 +146,7 @@ forgeAmountMultiplier = 3 forgeMinSlivers = 3 forgeMaxSlivers = 7 forgeInfluencedLimit = 300 -forgeFiendishLimit = 3 +forgeFiendishLimit = 4 forgeFiendishIntervalType = "hour" forgeFiendishIntervalTime = "1" diff --git a/data-otservbr-global/monster/quests/soul_war/normal_monsters/druid's_apparition.lua b/data-otservbr-global/monster/quests/soul_war/normal_monsters/druid's_apparition.lua index 957307c40eb..27f302c514d 100644 --- a/data-otservbr-global/monster/quests/soul_war/normal_monsters/druid's_apparition.lua +++ b/data-otservbr-global/monster/quests/soul_war/normal_monsters/druid's_apparition.lua @@ -17,6 +17,7 @@ monster.raceId = 1946 monster.Bestiary = { class = "Undead", race = BESTY_RACE_UNDEAD, + toKill = 5000, FirstUnlock = 200, SecondUnlock = 2000, CharmsPoints = 100, diff --git a/data-otservbr-global/npc/ahmet.lua b/data-otservbr-global/npc/ahmet.lua index 8a3db7e2d97..1e1795f6af9 100644 --- a/data-otservbr-global/npc/ahmet.lua +++ b/data-otservbr-global/npc/ahmet.lua @@ -30,6 +30,7 @@ npcConfig.shop = { -- Sellable items { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "deed of ownership", clientId = 7866, buy = 1000 }, { itemName = "document", clientId = 2818, buy = 12 }, diff --git a/data-otservbr-global/npc/asphota.lua b/data-otservbr-global/npc/asphota.lua index 13e9b83f6c4..c470cfc554d 100644 --- a/data-otservbr-global/npc/asphota.lua +++ b/data-otservbr-global/npc/asphota.lua @@ -60,6 +60,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "hand auger", clientId = 31334, buy = 25 }, { itemName = "inkwell", clientId = 3509, sell = 8 }, diff --git a/data-otservbr-global/npc/bashira.lua b/data-otservbr-global/npc/bashira.lua index 6a1f987cc2a..d818e5769bf 100644 --- a/data-otservbr-global/npc/bashira.lua +++ b/data-otservbr-global/npc/bashira.lua @@ -74,6 +74,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/npc/beatrice.lua b/data-otservbr-global/npc/beatrice.lua index 89fe93286dc..17d07a56ce5 100644 --- a/data-otservbr-global/npc/beatrice.lua +++ b/data-otservbr-global/npc/beatrice.lua @@ -99,6 +99,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "hand auger", clientId = 31334, buy = 25 }, { itemName = "inkwell", clientId = 3509, sell = 8 }, diff --git a/data-otservbr-global/npc/bertha.lua b/data-otservbr-global/npc/bertha.lua index 339b91aae7e..add1d0aa3eb 100644 --- a/data-otservbr-global/npc/bertha.lua +++ b/data-otservbr-global/npc/bertha.lua @@ -103,6 +103,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2884, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/npc/bezil.lua b/data-otservbr-global/npc/bezil.lua index 39f366ef309..61fd7854eaf 100644 --- a/data-otservbr-global/npc/bezil.lua +++ b/data-otservbr-global/npc/bezil.lua @@ -59,6 +59,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 40, sell = 40 }, { itemName = "grey backpack", clientId = 2870, buy = 10 }, diff --git a/data-otservbr-global/npc/gladys.lua b/data-otservbr-global/npc/gladys.lua index d9911e3f55b..d455de501e0 100644 --- a/data-otservbr-global/npc/gladys.lua +++ b/data-otservbr-global/npc/gladys.lua @@ -57,6 +57,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "fox paw", clientId = 27462, sell = 100 }, { itemName = "fur armor", clientId = 22085, sell = 5000 }, diff --git a/data-otservbr-global/npc/gorn.lua b/data-otservbr-global/npc/gorn.lua index 0d875652da9..64967e6251f 100644 --- a/data-otservbr-global/npc/gorn.lua +++ b/data-otservbr-global/npc/gorn.lua @@ -124,6 +124,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2884, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/npc/gree_dee.lua b/data-otservbr-global/npc/gree_dee.lua index 287edb79a1b..2997ffb7f55 100644 --- a/data-otservbr-global/npc/gree_dee.lua +++ b/data-otservbr-global/npc/gree_dee.lua @@ -62,6 +62,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "deed of ownership", clientId = 7866, buy = 1000 }, { itemName = "document", clientId = 2818, buy = 12 }, diff --git a/data-otservbr-global/npc/halif.lua b/data-otservbr-global/npc/halif.lua index 27317ec6692..99947d4bb3c 100644 --- a/data-otservbr-global/npc/halif.lua +++ b/data-otservbr-global/npc/halif.lua @@ -60,6 +60,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 40, sell = 40 }, diff --git a/data-otservbr-global/npc/lubo.lua b/data-otservbr-global/npc/lubo.lua index 2c1d7ab661b..20d1b2eba64 100644 --- a/data-otservbr-global/npc/lubo.lua +++ b/data-otservbr-global/npc/lubo.lua @@ -169,6 +169,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "hand auger", clientId = 31334, buy = 25 }, { itemName = "machete", clientId = 3308, buy = 35, sell = 6 }, diff --git a/data-otservbr-global/npc/maro.lua b/data-otservbr-global/npc/maro.lua index 7ba549262f7..adb5eb417b5 100644 --- a/data-otservbr-global/npc/maro.lua +++ b/data-otservbr-global/npc/maro.lua @@ -58,6 +58,7 @@ npcConfig.shop = { { itemName = "cleaver", clientId = 3471, buy = 15 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "fork", clientId = 3467, buy = 10 }, diff --git a/data-otservbr-global/npc/maun.lua b/data-otservbr-global/npc/maun.lua index 9e57ca6b862..1bce8918562 100644 --- a/data-otservbr-global/npc/maun.lua +++ b/data-otservbr-global/npc/maun.lua @@ -66,6 +66,7 @@ npcConfig.shop = { { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "crystalline arrow", clientId = 15793, buy = 20 }, { itemName = "desintegrate rune", clientId = 3197, buy = 26 }, { itemName = "diamond arrow", clientId = 35901, buy = 100 }, diff --git a/data-otservbr-global/npc/nezil.lua b/data-otservbr-global/npc/nezil.lua index 260a01bce4e..d9b509045bc 100644 --- a/data-otservbr-global/npc/nezil.lua +++ b/data-otservbr-global/npc/nezil.lua @@ -59,6 +59,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 40, sell = 40 }, { itemName = "grey backpack", clientId = 2870, buy = 10 }, diff --git a/data-otservbr-global/npc/perod.lua b/data-otservbr-global/npc/perod.lua index 7d3f32d6ad3..098117839de 100644 --- a/data-otservbr-global/npc/perod.lua +++ b/data-otservbr-global/npc/perod.lua @@ -105,6 +105,7 @@ npcConfig.shop = { { itemName = "closed trap", clientId = 3481, buy = 280 }, { itemName = "crossbow", clientId = 3349, buy = 500, sell = 500 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "crystalline arrow", clientId = 15793, buy = 20 }, { itemName = "cup", clientId = 2884, buy = 2 }, { itemName = "diamond arrow", clientId = 35901, buy = 100 }, diff --git a/data-otservbr-global/npc/red_lilly.lua b/data-otservbr-global/npc/red_lilly.lua index b744fae905d..637dee9c180 100644 --- a/data-otservbr-global/npc/red_lilly.lua +++ b/data-otservbr-global/npc/red_lilly.lua @@ -68,6 +68,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/npc/rock_in_a_hard_place.lua b/data-otservbr-global/npc/rock_in_a_hard_place.lua index 7abec0e60d1..4d4c6b1ee10 100644 --- a/data-otservbr-global/npc/rock_in_a_hard_place.lua +++ b/data-otservbr-global/npc/rock_in_a_hard_place.lua @@ -109,6 +109,7 @@ local itemsTable = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "deepling axe", clientId = 13991, sell = 40000 }, { itemName = "deepling squelcher", clientId = 14250, sell = 7000 }, { itemName = "deepling staff", clientId = 13987, sell = 4000 }, diff --git a/data-otservbr-global/npc/sarina.lua b/data-otservbr-global/npc/sarina.lua index 3429b5f6d35..bcc13ada91e 100644 --- a/data-otservbr-global/npc/sarina.lua +++ b/data-otservbr-global/npc/sarina.lua @@ -98,6 +98,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2881, buy = 3 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/npc/shiantis.lua b/data-otservbr-global/npc/shiantis.lua index 08493c2cf2e..a268b8dd8e8 100644 --- a/data-otservbr-global/npc/shiantis.lua +++ b/data-otservbr-global/npc/shiantis.lua @@ -104,6 +104,7 @@ npcConfig.shop = { { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "coal basin kit", clientId = 3513, buy = 25 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cuckoo clock", clientId = 2664, buy = 40 }, { itemName = "document", clientId = 2834, buy = 12 }, { itemName = "empty goldfish bowl", clientId = 5928, buy = 50 }, diff --git a/data-otservbr-global/npc/timur.lua b/data-otservbr-global/npc/timur.lua index f2a23509601..f3788c60494 100644 --- a/data-otservbr-global/npc/timur.lua +++ b/data-otservbr-global/npc/timur.lua @@ -74,6 +74,7 @@ npcConfig.shop = { { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crossbow", clientId = 3349, sell = 160 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, { itemName = "hand auger", clientId = 31334, buy = 25 }, diff --git a/data-otservbr-global/npc/zora.lua b/data-otservbr-global/npc/zora.lua index f9b3a458d52..e6f271a2699 100644 --- a/data-otservbr-global/npc/zora.lua +++ b/data-otservbr-global/npc/zora.lua @@ -98,6 +98,7 @@ npcConfig.shop = { { itemName = "candlestick", clientId = 2917, buy = 2 }, { itemName = "closed trap", clientId = 3481, buy = 280, sell = 75 }, { itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 }, + { itemName = "crusher", clientId = 46627, buy = 500 }, { itemName = "cup", clientId = 2884, buy = 2 }, { itemName = "document", clientId = 2818, buy = 12 }, { itemName = "fishing rod", clientId = 3483, buy = 150, sell = 40 }, diff --git a/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_iksupan_entrance.lua b/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_iksupan_entrance.lua index 79a74c4f8cd..8ec086eeb8b 100644 --- a/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_iksupan_entrance.lua +++ b/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_iksupan_entrance.lua @@ -1,5 +1,5 @@ local config = { - { position = { x = 32728, y = 32875, z = 7 }, destination = { x = 34015, y = 31890, z = 8 } }, + { position = { x = 32728, y = 32878, z = 7 }, destination = { x = 34015, y = 31890, z = 8 } }, } local entrance = Action() diff --git a/data/items/items.xml b/data/items/items.xml index df8ba8c122d..79f122afb9a 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -76351,5 +76351,15 @@ Granted by TibiaGoals.com"/> + + + + + + + + + + diff --git a/data/libs/functions/monster.lua b/data/libs/functions/monster.lua index 0327daab3b3..f39e0ad5060 100644 --- a/data/libs/functions/monster.lua +++ b/data/libs/functions/monster.lua @@ -140,7 +140,7 @@ function Monster.setFiendish(self, position, player) if fiendishMonster then Game.removeFiendishMonster(fiendishMonster:getId()) end - if Game.makeFiendishMonster(self:getId(), true) ~= 0 then + if Game.makeFiendishMonster(self:getId(), false) ~= 0 then success = "set sucessfully a new fiendish monster" else success = "have error to set fiendish monster" diff --git a/data/npclib/npc_system/npc_handler.lua b/data/npclib/npc_system/npc_handler.lua index 7de4172faaa..5ac57f68c33 100644 --- a/data/npclib/npc_system/npc_handler.lua +++ b/data/npclib/npc_system/npc_handler.lua @@ -511,13 +511,7 @@ if NpcHandler == nil then local callback = self:getCallback(CALLBACK_ON_MOVE) if callback == nil or callback(npc, player, fromPosition, toPosition) then if self:processModuleCallback(CALLBACK_ON_MOVE, npc, player, fromPosition, toPosition) then - if self:checkInteraction(npc, player) then - if not self:isInRange(npc, player) then - self:onWalkAway(npc, player) - else - self:updateInteraction(npc, player) - end - end + return true end end end diff --git a/data/scripts/systems/item_tiers.lua b/data/scripts/systems/item_tiers.lua index c199f81a3fe..06a8321a034 100644 --- a/data/scripts/systems/item_tiers.lua +++ b/data/scripts/systems/item_tiers.lua @@ -6,110 +6,86 @@ local itemTierClassifications = { regular = 25000, core = 1, }, - [2] = { - regular = 50000, - core = 1, - }, - [3] = { - regular = 100000, - core = 1, - }, }, -- Upgrade classification 2 [2] = { -- Update tier 0 [1] = { - regular = 50000, + regular = 750000, core = 1, }, -- Update tier 1 [2] = { - regular = 100000, + regular = 5000000, core = 1, }, - [3] = { - regular = 200000, - core = 2, - }, - [4] = { - regular = 400000, - core = 2, - }, }, -- Upgrade classification 3 [3] = { [1] = { - regular = 200000, + regular = 4000000, core = 1, }, [2] = { - regular = 400000, + regular = 10000000, core = 2, }, [3] = { - regular = 800000, + regular = 20000000, core = 3, }, - [4] = { - regular = 1600000, - core = 4, - }, - [5] = { - regular = 3200000, - core = 5, - }, }, -- Upgrade classification 4 [4] = { [1] = { - regular = 1500000, + regular = 8000000, core = 1, - convergence = { fusion = { price = 6000000 }, transfer = { price = 12000000 } }, + convergence = { fusion = { price = 55000000 }, transfer = { price = 65000000 } }, }, [2] = { - regular = 3000000, + regular = 20000000, core = 2, - convergence = { fusion = { price = 12000000 }, transfer = { price = 24000000 } }, + convergence = { fusion = { price = 110000000 }, transfer = { price = 165000000 } }, }, [3] = { - regular = 6000000, + regular = 40000000, core = 5, - convergence = { fusion = { price = 24000000 }, transfer = { price = 48000000 } }, + convergence = { fusion = { price = 170000000 }, transfer = { price = 375000000 } }, }, [4] = { - regular = 15000000, + regular = 65000000, core = 10, - convergence = { fusion = { price = 48000000 }, transfer = { price = 100000000 } }, + convergence = { fusion = { price = 300000000 }, transfer = { price = 800000000 } }, }, [5] = { - regular = 30000000, + regular = 100000000, core = 15, - convergence = { fusion = { price = 100000000 }, transfer = { price = 200000000 } }, + convergence = { fusion = { price = 875000000 }, transfer = { price = 2000000000 } }, }, [6] = { - regular = 80000000, + regular = 250000000, core = 25, - convergence = { fusion = { price = 200000000 }, transfer = { price = 400000000 } }, + convergence = { fusion = { price = 2350000000 }, transfer = { price = 5250000000 } }, }, [7] = { - regular = 200000000, + regular = 750000000, core = 35, - convergence = { fusion = { price = 400000000 }, transfer = { price = 800000000 } }, + convergence = { fusion = { price = 6950000000 }, transfer = { price = 14500000000 } }, }, [8] = { - regular = 400000000, + regular = 2500000000, core = 50, - convergence = { fusion = { price = 800000000 }, transfer = { price = 1600000000 } }, + convergence = { fusion = { price = 21250000000 }, transfer = { price = 42500000000 } }, }, [9] = { - regular = 800000000, + regular = 8000000000, core = 60, - convergence = { fusion = { price = 1600000000 }, transfer = { price = 3200000000 } }, + convergence = { fusion = { price = 50000000000 }, transfer = { price = 100000000000 } }, }, [10] = { - regular = 1600000000, + regular = 15000000000, core = 85, - convergence = { fusion = { price = 3200000000 }, transfer = { price = 6400000000 } }, + convergence = { fusion = { price = 125000000000 }, transfer = { price = 300000000000 } }, }, }, } diff --git a/src/account/account.cpp b/src/account/account.cpp index 8b67c09ebca..2cd411ef3e7 100644 --- a/src/account/account.cpp +++ b/src/account/account.cpp @@ -14,7 +14,6 @@ #include "utils/definitions.hpp" #include "security/argon.hpp" #include "utils/tools.hpp" -#include "lib/logging/log_with_spd_log.hpp" #include "enums/account_type.hpp" #include "enums/account_coins.hpp" diff --git a/src/account/account_repository_db.cpp b/src/account/account_repository_db.cpp index a6d169ef4af..10f3d6f3a41 100644 --- a/src/account/account_repository_db.cpp +++ b/src/account/account_repository_db.cpp @@ -10,7 +10,6 @@ #include "account/account_repository_db.hpp" #include "database/database.hpp" -#include "lib/logging/logger.hpp" #include "utils/definitions.hpp" #include "utils/tools.hpp" #include "enums/account_type.hpp" diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 7606a026362..aaf1de80530 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -97,7 +97,6 @@ int CanaryServer::run() { #endif g_game().start(&serviceManager); - g_game().setGameState(GAME_STATE_NORMAL); if (g_configManager().getBoolean(TOGGLE_MAINTAIN_MODE)) { g_game().setGameState(GAME_STATE_CLOSED); g_logger().warn("Initialized in maintain mode!"); @@ -133,6 +132,7 @@ int CanaryServer::run() { } logger.info("{} {}", g_configManager().getString(SERVER_NAME), "server online!"); + g_logger().setLevel(g_configManager().getString(LOGLEVEL)); serviceManager.run(); @@ -203,7 +203,7 @@ void CanaryServer::logInfos() { logger.info("{} - Version {}", ProtocolStatus::SERVER_NAME, SERVER_RELEASE_VERSION); #endif - logger.debug("Compiled with {}, on {} {}, for platform {}\n", getCompiler(), __DATE__, __TIME__, getPlatform()); + logger.debug("Compiled with {}, on {} {}, for platform {}", getCompiler(), __DATE__, __TIME__, getPlatform()); #if defined(LUAJIT_VERSION) logger.debug("Linked with {} for Lua support", LUAJIT_VERSION); diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 2cae928382f..aa028b52284 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -35,10 +35,6 @@ bool ConfigManager::load() { return false; } -#ifndef DEBUG_LOG - g_logger().setLevel(loadStringConfig(L, LOGLEVEL, "logLevel", "info")); -#endif - // Parse config // Info that must be loaded one time (unless we reset the modules involved) if (!loaded) { @@ -363,6 +359,7 @@ bool ConfigManager::load() { loadStringConfig(L, TIBIADROME_CONCOCTION_TICK_TYPE, "tibiadromeConcoctionTickType", "online"); loadStringConfig(L, URL, "url", ""); loadStringConfig(L, WORLD_TYPE, "worldType", "pvp"); + loadStringConfig(L, LOGLEVEL, "logLevel", "info"); loaded = true; lua_close(L); diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 5cf1905c2e8..bc262a41c76 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1873,12 +1873,15 @@ bool ConditionFeared::getRandomDirection(std::shared_ptr creature, Pos DIRECTION_NORTHWEST }; - std::ranges::shuffle(directions.begin(), directions.end(), getRandomGenerator()); - for (Direction dir : directions) { - if (canWalkTo(creature, pos, dir)) { - this->fleeIndx = static_cast(dir); - return true; - } + std::ranges::shuffle(directions, getRandomGenerator()); + + auto it = std::ranges::find_if(directions, [&](Direction dir) { + return canWalkTo(creature, pos, dir); + }); + + if (it != directions.end()) { + this->fleeIndx = static_cast(*it); + return true; } return false; diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index fc23bbf9030..5bcc1e02c19 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -333,7 +333,7 @@ class Spell : public BaseSpell { return m_words; } - void setWords(const std::string_view &newWord) { + void setWords(std::string_view newWord) { m_words = newWord.data(); } @@ -341,7 +341,7 @@ class Spell : public BaseSpell { return m_separator; } - void setSeparator(const std::string_view &newSeparator) { + void setSeparator(std::string_view newSeparator) { m_separator = newSeparator.data(); } diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 3d0eb24ff8a..eea144448ba 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -199,6 +199,11 @@ class Creature : virtual public Thing, public SharedObject { int32_t getHealth() const { return health; } + + bool isAlive() const { + return !isDead(); + } + virtual int32_t getMaxHealth() const { return healthMax; } diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index 1d006ca95aa..9b73e6caad8 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -604,11 +604,14 @@ std::shared_ptr Chat::getChannelById(uint16_t channelId) { return it->second; } -std::shared_ptr Chat::getPrivateChannel(const std::shared_ptr &player) { - for (auto &it : privateChannels) { - if (it.second->getOwner() == player->getGUID()) { - return it.second; - } +std::shared_ptr Chat::getPrivateChannel(const std::shared_ptr &player) const { + auto it = std::ranges::find_if(privateChannels, [&player](const auto &channelPair) { + const auto &[channelId, channel] = channelPair; + return channel->getOwner() == player->getGUID(); + }); + + if (it != privateChannels.end()) { + return it->second; } return nullptr; } diff --git a/src/creatures/interactions/chat.hpp b/src/creatures/interactions/chat.hpp index 3643086184c..fcc74563c34 100644 --- a/src/creatures/interactions/chat.hpp +++ b/src/creatures/interactions/chat.hpp @@ -137,7 +137,7 @@ class Chat { std::shared_ptr getChannel(const std::shared_ptr &player, uint16_t channelId); std::shared_ptr getChannelById(uint16_t channelId); std::shared_ptr getGuildChannelById(uint32_t guildId); - std::shared_ptr getPrivateChannel(const std::shared_ptr &player); + std::shared_ptr getPrivateChannel(const std::shared_ptr &player) const; LuaScriptInterface* getScriptInterface() { return &scriptInterface; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 581aac815ae..4d225dee696 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -2308,7 +2308,7 @@ void Monster::configureForgeSystem() { } // Change health based in stacks - float percentToIncrement = static_cast((forgeStack * 6) + 100) / 100.f; + const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f; auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement)); healthMax = newHealth; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 9b7fda9f6aa..36562af1e0f 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -354,7 +354,10 @@ class Monster final : public Creature { float getDefenseMultiplier() const { float multiplier = mType->getDefenseMultiplier(); - return multiplier * std::pow(1.02f, getForgeStack()); + if (auto stacks = getForgeStack(); stacks > 0) { + multiplier *= (1 + (0.1 * stacks)); + } + return multiplier; } bool isDead() const override { diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index fbf08461c00..2d615c022e4 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -65,6 +65,29 @@ bool Npc::canInteract(const Position &pos, uint32_t range /* = 4 */) { return Creature::canSee(getPosition(), pos, range, range); } +bool Npc::isInteractingWithPlayer(uint32_t playerId) { + if (playerInteractions.empty()) { + return false; + } + + if (!playerInteractions.contains(playerId)) { + return false; + } + return true; +} + +bool Npc::isPlayerInteractingOnTopic(uint32_t playerId, uint16_t topicId) { + if (playerInteractions.empty()) { + return false; + } + + auto it = playerInteractions.find(playerId); + if (it == playerInteractions.end()) { + return false; + } + return it->second == topicId; +} + void Npc::onCreatureAppear(std::shared_ptr creature, bool isLogin) { Creature::onCreatureAppear(creature, isLogin); @@ -590,16 +613,27 @@ void Npc::setPlayerInteraction(uint32_t playerId, uint16_t topicId /*= 0*/) { return; } - turnToCreature(creature); + if (playerInteractionsOrder.empty() || std::ranges::find(playerInteractionsOrder, playerId) == playerInteractionsOrder.end()) { + playerInteractionsOrder.emplace_back(playerId); + turnToCreature(creature); + } playerInteractions[playerId] = topicId; } void Npc::removePlayerInteraction(std::shared_ptr player) { + auto view = std::ranges::remove(playerInteractionsOrder, player->getID()); + playerInteractionsOrder.erase(view.begin(), view.end()); if (playerInteractions.contains(player->getID())) { playerInteractions.erase(player->getID()); player->closeShopWindow(); } + + if (!playerInteractionsOrder.empty()) { + if (const auto &creature = g_game().getCreatureByID(playerInteractionsOrder.back())) { + turnToCreature(creature); + } + } } void Npc::resetPlayerInteractions() { @@ -679,8 +713,11 @@ void Npc::closeAllShopWindows() { void Npc::handlePlayerMove(std::shared_ptr player, const Position &newPos) { if (!canInteract(newPos)) { - removePlayerInteraction(player); + onPlayerCloseChannel(player); + } else if (canInteract(newPos) && !playerInteractionsOrder.empty() && playerInteractionsOrder.back() == player->getID()) { + turnToCreature(player); } + if (canSee(newPos)) { onPlayerAppear(player); } else { diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index c246aa4b68d..382ab250156 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -129,20 +129,8 @@ class Npc final : public Creature { void removePlayerInteraction(std::shared_ptr player); void resetPlayerInteractions(); - bool isInteractingWithPlayer(uint32_t playerId) { - if (playerInteractions.find(playerId) == playerInteractions.end()) { - return false; - } - return true; - } - - bool isPlayerInteractingOnTopic(uint32_t playerId, uint16_t topicId) { - auto it = playerInteractions.find(playerId); - if (it == playerInteractions.end()) { - return false; - } - return it->second == topicId; - } + bool isInteractingWithPlayer(uint32_t playerId); + bool isPlayerInteractingOnTopic(uint32_t playerId, uint16_t topicId); void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; @@ -184,6 +172,8 @@ class Npc final : public Creature { std::string strDescription; + std::vector playerInteractionsOrder; + std::map playerInteractions; std::unordered_map> shopPlayers; diff --git a/src/creatures/players/grouping/party.cpp b/src/creatures/players/grouping/party.cpp index a409a8b3121..3a5d4d78183 100644 --- a/src/creatures/players/grouping/party.cpp +++ b/src/creatures/players/grouping/party.cpp @@ -75,7 +75,7 @@ void Party::disband() { membersData.clear(); } -bool Party::leaveParty(std::shared_ptr player) { +bool Party::leaveParty(std::shared_ptr player, bool forceRemove /* = false */) { if (!player) { return false; } @@ -89,7 +89,8 @@ bool Party::leaveParty(std::shared_ptr player) { return false; } - if (!g_events().eventPartyOnLeave(getParty(), player)) { + bool canRemove = g_events().eventPartyOnLeave(getParty(), player); + if (!forceRemove && !canRemove) { return false; } diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index 6aaecc56190..c8bc9b6a653 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -61,7 +61,7 @@ class Party : public SharedObject { bool joinParty(const std::shared_ptr &player); void revokeInvitation(const std::shared_ptr &player); bool passPartyLeadership(std::shared_ptr player); - bool leaveParty(std::shared_ptr player); + bool leaveParty(std::shared_ptr player, bool forceRemove = false); bool removeInvite(const std::shared_ptr &player, bool removeFromPlayer = true); diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 6cec2e21b16..5a6c4cbf6cc 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -13,6 +13,7 @@ #include "creatures/monsters/monsters.hpp" #include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" +#include "creatures/players/wheel/wheel_gems.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -1847,7 +1848,7 @@ void Player::onRemoveCreature(std::shared_ptr creature, bool isLogout) onDeEquipInventory(); if (m_party) { - m_party->leaveParty(player); + m_party->leaveParty(player, true); } if (guild) { guild->removeMember(player); @@ -5385,7 +5386,7 @@ uint32_t Player::getCapacity() const { } else if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { return std::numeric_limits::max(); } - return capacity + bonusCapacity + varStats[STAT_CAPACITY] + m_wheelPlayer->getStat(WheelStat_t::CAPACITY); + return capacity + bonusCapacity + varStats[STAT_CAPACITY] + (m_wheelPlayer->getStat(WheelStat_t::CAPACITY) * 100); } int32_t Player::getMaxHealth() const { @@ -6716,11 +6717,13 @@ bool Player::isCreatureUnlockedOnTaskHunting(const std::shared_ptr void Player::triggerMomentum() { auto item = getInventoryItem(CONST_SLOT_HEAD); - if (item == nullptr) { - return; + + double_t chance = 0; + if (item) { + chance += item->getMomentumChance(); } - double_t chance = item->getMomentumChance(); + chance += m_wheelPlayer->getBonusData().momentum; double_t randomChance = uniform_random(0, 10000) / 100.; if (getZoneType() != ZONE_PROTECTION && hasCondition(CONDITION_INFIGHT) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) { bool triggered = false; @@ -7484,7 +7487,9 @@ void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemI sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); break; } - auto tierPriecs = itemClassification->tiers.at(donorItem->getTier()); + + const uint8_t toTier = convergence ? donorItem->getTier() : donorItem->getTier() - 1; + auto tierPriecs = itemClassification->tiers.at(toTier); cost = convergence ? tierPriecs.convergenceTransferPrice : tierPriecs.regularPrice; coresAmount = tierPriecs.corePrice; break; diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index 5e497f18d44..0217cabe7db 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -11,6 +11,7 @@ #include "utils/pugicast.hpp" #include "utils/tools.hpp" +#include "enums/player_wheel.hpp" bool Vocations::reload() { vocationsMap.clear(); diff --git a/src/creatures/players/vocations/vocation.hpp b/src/creatures/players/vocations/vocation.hpp index f4f35082cfc..6bd8d9a2ebc 100644 --- a/src/creatures/players/vocations/vocation.hpp +++ b/src/creatures/players/vocations/vocation.hpp @@ -12,7 +12,9 @@ #include "declarations.hpp" #include "items/item.hpp" #include "lib/di/container.hpp" -#include "creatures/players/wheel/wheel_gems.hpp" + +enum class WheelGemQuality_t : uint8_t; +enum class WheelGemSupremeModifier_t : uint8_t; class Vocation { public: diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 7a317106074..b577e998651 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -9,6 +9,7 @@ #include "creatures/players/wheel/player_wheel.hpp" +#include "enums/player_wheel.hpp" #include "config/configmanager.hpp" #include "io/io_wheel.hpp" #include "game/game.hpp" @@ -74,6 +75,175 @@ const static std::vector wheelGemBasicSlot2Allowed = { WheelGemBasicModifier_t::General_MitigationMultiplier, }; +const static std::vector modsBasicPosition = { + WheelGemBasicModifier_t::General_PhysicalResistance, + WheelGemBasicModifier_t::General_HolyResistance, + WheelGemBasicModifier_t::General_DeathResistance, + WheelGemBasicModifier_t::General_FireResistance, + WheelGemBasicModifier_t::General_EarthResistance, + WheelGemBasicModifier_t::General_IceResistance, + WheelGemBasicModifier_t::General_EnergyResistance, + + WheelGemBasicModifier_t::General_HolyResistance_DeathWeakness, + WheelGemBasicModifier_t::General_DeathResistance_HolyWeakness, + WheelGemBasicModifier_t::General_FireResistance_EarthResistance, + WheelGemBasicModifier_t::General_FireResistance_IceResistance, + WheelGemBasicModifier_t::General_FireResistance_EnergyResistance, + WheelGemBasicModifier_t::General_EarthResistance_IceResistance, + WheelGemBasicModifier_t::General_EarthResistance_EnergyResistance, + WheelGemBasicModifier_t::General_IceResistance_EnergyResistance, + + WheelGemBasicModifier_t::General_FireResistance_EarthWeakness, + WheelGemBasicModifier_t::General_FireResistance_IceWeakness, + WheelGemBasicModifier_t::General_FireResistance_EnergyWeakness, + WheelGemBasicModifier_t::General_EarthResistance_FireWeakness, + WheelGemBasicModifier_t::General_EarthResistance_IceWeakness, + WheelGemBasicModifier_t::General_EarthResistance_EnergyWeakness, + WheelGemBasicModifier_t::General_IceResistance_EarthWeakness, + WheelGemBasicModifier_t::General_IceResistance_FireWeakness, + WheelGemBasicModifier_t::General_IceResistance_EnergyWeakness, + WheelGemBasicModifier_t::General_EnergyResistance_EarthWeakness, + WheelGemBasicModifier_t::General_EnergyResistance_IceWeakness, + WheelGemBasicModifier_t::General_EnergyResistance_FireWeakness, + WheelGemBasicModifier_t::General_ManaDrainResistance, + WheelGemBasicModifier_t::General_LifeDrainResistance, + WheelGemBasicModifier_t::General_ManaDrainResistance_LifeDrainResistance, + WheelGemBasicModifier_t::General_MitigationMultiplier, + + WheelGemBasicModifier_t::Vocation_Health, + WheelGemBasicModifier_t::Vocation_Mana_FireResistance, + WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance, + WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance, + WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance, + WheelGemBasicModifier_t::Vocation_Mana, + WheelGemBasicModifier_t::Vocation_Health_FireResistance, + WheelGemBasicModifier_t::Vocation_Health_EnergyResistance, + WheelGemBasicModifier_t::Vocation_Health_EarthResistance, + WheelGemBasicModifier_t::Vocation_Health_IceResistance, + + WheelGemBasicModifier_t::Vocation_Capacity_FireResistance, + WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance, + WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance, + WheelGemBasicModifier_t::Vocation_Capacity_IceResistance, + WheelGemBasicModifier_t::Vocation_Capacity, +}; + +const static std::vector modsSupremeKnightPosition = { + WheelGemSupremeModifier_t::General_Dodge, + WheelGemSupremeModifier_t::General_CriticalDamage, + WheelGemSupremeModifier_t::General_LifeLeech, + WheelGemSupremeModifier_t::General_ManaLeech, + WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife, + + WheelGemSupremeModifier_t::Knight_AvatarOfSteel_Cooldown, + WheelGemSupremeModifier_t::Knight_ExecutionersThrow_Cooldown, + WheelGemSupremeModifier_t::Knight_ExecutionersThrow_DamageIncrease, + WheelGemSupremeModifier_t::Knight_ExecutionersThrow_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Fierce_Berserk_DamageIncrease, + WheelGemSupremeModifier_t::Knight_Fierce_Berserk_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Berserk_DamageIncrease, + WheelGemSupremeModifier_t::Knight_Berserk_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Front_Sweep_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Front_Sweep_DamageIncrease, + WheelGemSupremeModifier_t::Knight_Groundshaker_DamageIncrease, + WheelGemSupremeModifier_t::Knight_Groundshaker_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Annihilation_CriticalExtraDamage, + WheelGemSupremeModifier_t::Knight_Annihilation_DamageIncrease, + WheelGemSupremeModifier_t::Knight_FairWoundCleansing_HealingIncrease, + WheelGemSupremeModifier_t::Knight_RevelationMastery_AvatarOfSteel, + WheelGemSupremeModifier_t::Knight_RevelationMastery_ExecutionersThrow, + WheelGemSupremeModifier_t::Knight_RevelationMastery_CombatMastery, +}; + +const static std::vector modsSupremePaladinPosition = { + WheelGemSupremeModifier_t::General_Dodge, + WheelGemSupremeModifier_t::General_CriticalDamage, + WheelGemSupremeModifier_t::General_LifeLeech, + WheelGemSupremeModifier_t::General_ManaLeech, + WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife, + + WheelGemSupremeModifier_t::Paladin_AvatarOfLight_Cooldown, + WheelGemSupremeModifier_t::Paladin_DivineDazzle_Cooldown, + WheelGemSupremeModifier_t::Paladin_DivineGrenade_DamageIncrease, + WheelGemSupremeModifier_t::Paladin_DivineGrenade_CriticalExtraDamage, + WheelGemSupremeModifier_t::Paladin_DivineCaldera_DamageIncrease, + WheelGemSupremeModifier_t::Paladin_DivineCaldera_CriticalExtraDamage, + WheelGemSupremeModifier_t::Paladin_DivineMissile_DamageIncrease, + WheelGemSupremeModifier_t::Paladin_DivineMissile_CriticalExtraDamage, + WheelGemSupremeModifier_t::Paladin_EtherealSpear_DamageIncrease, + WheelGemSupremeModifier_t::Paladin_EtherealSpear_CriticalExtraDamage, + WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_DamageIncrease, + WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_CriticalExtraDamage, + WheelGemSupremeModifier_t::Paladin_DivineEmpowerment_Cooldown, + WheelGemSupremeModifier_t::Paladin_DivineGrenade_Cooldown, + WheelGemSupremeModifier_t::Paladin_Salvation_HealingIncrease, + WheelGemSupremeModifier_t::Paladin_RevelationMastery_AvatarOfLight, + WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineGrenade, + WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineEmpowerment, +}; + +const static std::vector modsSupremeSorcererPosition = { + WheelGemSupremeModifier_t::General_Dodge, + WheelGemSupremeModifier_t::General_CriticalDamage, + WheelGemSupremeModifier_t::General_LifeLeech, + WheelGemSupremeModifier_t::General_ManaLeech, + WheelGemSupremeModifier_t::SorcererDruid_UltimateHealing, + WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife, + + WheelGemSupremeModifier_t::Sorcerer_AvatarOfStorm_Cooldown, + WheelGemSupremeModifier_t::Sorcerer_EnergyWave_Cooldown, + WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_HellsCore_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_HellsCore_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_EnergyWave_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_EnergyWave_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_DamageIncrease, + WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_CriticalExtraDamage, + WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_AvatarOfStorm, + WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_BeamMastery, + WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_DrainBody, +}; + +const static std::vector modsSupremeDruidPosition = { + WheelGemSupremeModifier_t::General_Dodge, + WheelGemSupremeModifier_t::General_CriticalDamage, + WheelGemSupremeModifier_t::General_LifeLeech, + WheelGemSupremeModifier_t::General_ManaLeech, + WheelGemSupremeModifier_t::SorcererDruid_UltimateHealing, + WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife, + + WheelGemSupremeModifier_t::Druid_AvatarOfNature_Cooldown, + WheelGemSupremeModifier_t::Druid_NaturesEmbrace_Cooldown, + WheelGemSupremeModifier_t::Druid_TerraBurst_DamageIncrease, + WheelGemSupremeModifier_t::Druid_TerraBurst_CriticalExtraDamage, + WheelGemSupremeModifier_t::Druid_IceBurst_DamageIncrease, + WheelGemSupremeModifier_t::Druid_IceBurst_CriticalExtraDamage, + WheelGemSupremeModifier_t::Druid_EternalWinter_CriticalExtraDamage, + WheelGemSupremeModifier_t::Druid_EternalWinter_DamageIncrease, + WheelGemSupremeModifier_t::Druid_TerraWave_DamageIncrease, + WheelGemSupremeModifier_t::Druid_TerraWave_CriticalExtraDamage, + WheelGemSupremeModifier_t::Druid_StrongIceWave_DamageIncrease, + WheelGemSupremeModifier_t::Druid_StrongIceWave_CriticalExtraDamage, + WheelGemSupremeModifier_t::Druid_HealFriend_HealingIncrease, + WheelGemSupremeModifier_t::Druid_MassHealing_HealingIncrease, + WheelGemSupremeModifier_t::Druid_RevelationMastery_AvatarOfNature, + WheelGemSupremeModifier_t::Druid_RevelationMastery_BlessingOfTheGrove, + WheelGemSupremeModifier_t::Druid_RevelationMastery_TwinBursts, +}; + +// Using reference wrapper to avoid copying the vector to the map +const static std::unordered_map>> modsSupremePositionByVocation = { + { 1, std::cref(modsSupremeSorcererPosition) }, + { 2, std::cref(modsSupremeDruidPosition) }, + { 3, std::cref(modsSupremePaladinPosition) }, + { 4, std::cref(modsSupremeKnightPosition) } +}; + // To avoid conflict in other files that might use a function with the same name // Here are built-in helper functions namespace { @@ -96,7 +266,7 @@ namespace { } template - int checkSpellAdditionalTarget(const std::array &spellsTable, const std::string_view &spellName, uint8_t stage) { + int checkSpellAdditionalTarget(const std::array &spellsTable, std::string_view spellName, uint8_t stage) { for (const auto &spellTable : spellsTable) { auto size = std::ssize(spellTable.grade); g_logger().debug("spell target stage {}, grade {}", stage, size); @@ -112,7 +282,7 @@ namespace { } template - int checkSpellAdditionalDuration(const std::array &spellsTable, const std::string_view &spellName, uint8_t stage) { + int checkSpellAdditionalDuration(const std::array &spellsTable, std::string_view spellName, uint8_t stage) { for (const auto &spellTable : spellsTable) { auto size = std::ssize(spellTable.grade); g_logger().debug("spell duration stage {}, grade {}", stage, size); @@ -669,10 +839,42 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) uint16_t PlayerWheel::getUnusedPoints() const { auto totalPoints = getWheelPoints(); + if (totalPoints == 0) { return 0; } + const auto vocationBaseId = m_player.getVocation()->getBaseId(); + const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); + + for (const auto &modPosition : modsBasicPosition) { + const auto pos = static_cast(modPosition); + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Lesser, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = static_cast(gradeKV->get()); + } + + totalPoints += grade == 3 ? 1 : 0; + } + + if (modsSupremeIt != modsSupremePositionByVocation.end()) { + for (const auto &modPosition : modsSupremeIt->second.get()) { + const auto pos = static_cast(modPosition); + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = gradeKV->get(); + } + + totalPoints += grade == 3 ? 1 : 0; + } + } else { + g_logger().error("[{}] supreme modifications not found for vocation base id: {}", std::source_location::current().function_name(), vocationBaseId); + } + for (uint8_t i = WheelSlots_t::SLOT_FIRST; i <= WheelSlots_t::SLOT_LAST; ++i) { totalPoints -= getPointsBySlotType(static_cast(i)); } @@ -765,6 +967,20 @@ std::shared_ptr PlayerWheel::gemsKV() const { return m_player.kv()->scoped("wheel-of-destiny")->scoped("gems"); } +std::shared_ptr PlayerWheel::gemsGradeKV(WheelFragmentType_t type, uint8_t pos) const { + return gemsKV()->scoped(std::string(magic_enum::enum_name(type)))->scoped(std::to_string(pos)); +} + +uint8_t PlayerWheel::getGemGrade(WheelFragmentType_t type, uint8_t pos) const { + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(type, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = static_cast(gradeKV->get()); + } + return grade; +} + std::vector PlayerWheel::getRevealedGems() const { std::vector unlockedGems; auto unlockedGemUUIDs = gemsKV()->scoped("revealed")->keys(); @@ -924,10 +1140,55 @@ uint16_t PlayerWheel::getGemIndex(const std::string &uuid) const { void PlayerWheel::destroyGem(uint16_t index) { auto gem = getGem(index); if (gem.locked) { - g_logger().error("[{}] Player {} trying to destroy locked gem with index {}", __FUNCTION__, m_player.getName(), index); + g_logger().error("[{}] Player {} destroyed locked gem with index {}", std::source_location::current().function_name(), m_player.getName(), index); return; } + + const auto &backpack = m_player.getInventoryItem(CONST_SLOT_BACKPACK); + const auto &mainBackpack = backpack ? backpack->getContainer() : nullptr; + + uint8_t lesserFragments = 0; + uint8_t greaterFragments = 0; + + switch (gem.quality) { + case WheelGemQuality_t::Lesser: + lesserFragments = normal_random(1, 5); + break; + case WheelGemQuality_t::Regular: + lesserFragments = normal_random(2, 10); + break; + case WheelGemQuality_t::Greater: + greaterFragments = normal_random(1, 5); + break; + } + + if (lesserFragments > 0) { + const auto &fragmentsItem = Item::CreateItem(ITEM_LESSER_FRAGMENT, lesserFragments); + auto returnValue = g_game().internalPlayerAddItem(m_player.getPlayer(), fragmentsItem, false, CONST_SLOT_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("Failed to add {} lesser fragments to player with name {}", lesserFragments, m_player.getName()); + m_player.sendCancelMessage(getReturnMessage(RETURNVALUE_CONTACTADMINISTRATOR)); + return; + } + g_logger().debug("[{}] Player {} destroyed a gem and received {} lesser fragments", std::source_location::current().function_name(), m_player.getName(), lesserFragments); + } + + if (greaterFragments > 0) { + const auto &fragmentsItem = Item::CreateItem(ITEM_GREATER_FRAGMENT, greaterFragments); + auto returnValue = g_game().internalPlayerAddItem(m_player.getPlayer(), fragmentsItem, false, CONST_SLOT_BACKPACK); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("Failed to add {} greater fragments to player with name {}", greaterFragments, m_player.getName()); + m_player.sendCancelMessage(getReturnMessage(RETURNVALUE_CONTACTADMINISTRATOR)); + return; + } + g_logger().debug("[{}] Player {} destroyed a gem and received {} greater fragments", std::source_location::current().function_name(), m_player.getName(), greaterFragments); + } + gem.remove(gemsKV()); + + m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT)); + m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT)); + sendOpenWheelWindow(m_player.getID()); } @@ -1002,24 +1263,132 @@ void PlayerWheel::addGems(NetworkMessage &msg) const { msg.addByte(static_cast(gem.supremeModifier)); } } +} + +void PlayerWheel::addGradeModifiers(NetworkMessage &msg) const { + msg.addByte(0x2E); // Modifiers for all Vocations + for (const auto &modPosition : modsBasicPosition) { + const auto pos = static_cast(modPosition); + msg.addByte(pos); + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Lesser, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = static_cast(gradeKV->get()); + } + msg.addByte(grade); + } + + msg.addByte(0x17); // Modifiers for specific per Vocations + + const auto vocationBaseId = m_player.getVocation()->getBaseId(); + const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); + + if (modsSupremeIt != modsSupremePositionByVocation.end()) { + for (const auto &modPosition : modsSupremeIt->second.get()) { + const auto pos = static_cast(modPosition); + msg.addByte(pos); + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = gradeKV->get(); + } + msg.addByte(grade); + } + } else { + g_logger().error("[{}] vocation base id: {}", std::source_location::current().function_name(), m_player.getVocation()->getBaseId()); + } +} + +void PlayerWheel::improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos) { + uint16_t fragmentId = 0; + uint32_t value = 0; + uint8_t quantity = 0; + uint8_t grade = 0; + + auto gradeKV = gemsGradeKV(fragmentType, pos)->get("grade"); + if (gradeKV.has_value()) { + grade = gradeKV->get(); + } + + ++grade; + + switch (fragmentType) { + case WheelFragmentType_t::Lesser: + fragmentId = ITEM_LESSER_FRAGMENT; + std::tie(value, quantity) = getLesserGradeCost(grade); + break; + case WheelFragmentType_t::Greater: + fragmentId = ITEM_GREATER_FRAGMENT; + std::tie(value, quantity) = getGreaterGradeCost(grade); + break; + default: + g_logger().error("[{}] Invalid Fragment Type: {}", std::source_location::current().function_name(), static_cast(fragmentType)); + return; + } + + if (!m_player.hasItemCountById(fragmentId, quantity, false)) { + g_logger().error("[{}] Player {} does not have the required {} fragments with id {}", __FUNCTION__, m_player.getName(), quantity, fragmentId); + return; + } + + if (!g_game().removeMoney(m_player.getPlayer(), value, 0, true)) { + g_logger().error("[{}] Failed to remove {} gold from player {}", std::source_location::current().function_name(), value, m_player.getName()); + return; + } - msg.addByte(0); // Lesser gems - msg.addByte(0); // Greater gems + if (!m_player.removeItemCountById(fragmentId, quantity, false)) { + g_logger().error("[{}] Failed to remove {} fragments with id {} from player {}", std::source_location::current().function_name(), quantity, fragmentId, m_player.getName()); + return; + } + + gemsGradeKV(fragmentType, pos)->set("grade", grade); + loadPlayerBonusData(); + sendOpenWheelWindow(m_player.getID()); } -void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) const { +std::tuple PlayerWheel::getLesserGradeCost(uint8_t grade) const { + switch (grade) { + case 1: + return std::make_tuple(2000000, 5); + case 2: + return std::make_tuple(5000000, 15); + case 3: + return std::make_tuple(30000000, 30); + default: + throw std::invalid_argument("Invalid level for Lesser Fragment."); + } +} + +std::tuple PlayerWheel::getGreaterGradeCost(uint8_t grade) const { + switch (grade) { + case 1: + return std::make_tuple(5000000, 5); + case 2: + return std::make_tuple(12000000, 15); + case 3: + return std::make_tuple(75000000, 30); + default: + throw std::invalid_argument("Invalid level for Greater Fragment."); + } +} + +void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) { if (m_player.client && m_player.client->oldProtocol) { return; } msg.addByte(0x5F); bool canUse = canOpenWheel(); + msg.add(ownerId); // Player ID msg.addByte(canUse ? 1 : 0); // Can Use if (!canUse) { return; } + addInitialGems(); msg.addByte(getOptions(ownerId)); // Options msg.addByte(m_player.getPlayerVocationEnum()); // Vocation id @@ -1030,6 +1399,7 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con } addPromotionScrolls(msg); addGems(msg); + addGradeModifiers(msg); // TODO: read items from inventory auto voc = m_player.getVocation(); m_player.client->sendResourceBalance(RESOURCE_BANK, m_player.getBankBalance()); @@ -1037,6 +1407,8 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con m_player.client->sendResourceBalance(RESOURCE_LESSER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Lesser))); m_player.client->sendResourceBalance(RESOURCE_REGULAR_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Regular))); m_player.client->sendResourceBalance(RESOURCE_GREATER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Greater))); + m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT)); + m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT)); } void PlayerWheel::sendGiftOfLifeCooldown() const { @@ -1167,10 +1539,8 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { setActiveGem(affinity, gemIndex); } - // Player's bonus data is loaded, initialized, and registered, and the function logs + // Player's bonus data is loaded, initialized, registered, and the function logs loadPlayerBonusData(); - initializePlayerData(); - registerPlayerBonusData(); g_logger().debug("Player: {} is saved the all slots info in: {} milliseconds", m_player.getName(), bm_saveSlot.duration()); } @@ -1180,7 +1550,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { */ void PlayerWheel::loadDBPlayerSlotPointsOnLogin() { auto resultString = fmt::format("SELECT `slot` FROM `player_wheeldata` WHERE `player_id` = {}", m_player.getGUID()); - DBResult_ptr result = Database::getInstance().storeQuery(resultString); + const DBResult_ptr &result = Database::getInstance().storeQuery(resultString); // Ignore if player not have nothing inserted in the table if (!result) { return; @@ -1270,6 +1640,35 @@ uint16_t PlayerWheel::getWheelPoints(bool includeExtraPoints /* = true*/) const return totalPoints; } +void PlayerWheel::addInitialGems() { + auto initialsGems = gemsKV()->get("initialGems"); + + if (!initialsGems.has_value()) { + for (auto gemAffinity : magic_enum::enum_values()) { + for (auto gemQuality : magic_enum::enum_values()) { + if (gemQuality == WheelGemQuality_t::Greater) { + continue; + } + + PlayerWheelGem gem; + gem.uuid = KV::generateUUID(); + gem.locked = false; + gem.affinity = gemAffinity; + gem.quality = gemQuality; + + gem.basicModifier1 = wheelGemBasicSlot1Allowed[uniform_random(0, wheelGemBasicSlot1Allowed.size() - 1)]; + gem.basicModifier2 = {}; + gem.supremeModifier = {}; + if (gemQuality >= WheelGemQuality_t::Regular) { + gem.basicModifier2 = selectBasicModifier2(gem.basicModifier1); + } + gem.save(gemsKV()); + } + } + gemsKV()->set("initialGems", true); + } +} + bool PlayerWheel::canOpenWheel() const { // Vocation check if (m_player.getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) { @@ -1348,8 +1747,13 @@ uint8_t PlayerWheel::getMaxPointsPerSlot(WheelSlots_t slot) const { return 0u; } -void PlayerWheel::resetPlayerBonusData() { +void PlayerWheel::resetPlayerData() { m_playerBonusData = PlayerWheelMethodsBonusData(); + + resetUpgradedSpells(); + resetResistance(); + resetStats(); + resetRevelationState(); } void PlayerWheel::initializePlayerData() { @@ -1357,7 +1761,6 @@ void PlayerWheel::initializePlayerData() { return; } - resetPlayerBonusData(); loadPlayerBonusData(); } @@ -1391,16 +1794,6 @@ void PlayerWheel::reloadPlayerData() { } void PlayerWheel::registerPlayerBonusData() { - resetUpgradedSpells(); - resetResistance(); - resetStats(); - resetRevelationBonus(); - if (!m_modifierContext) { - m_modifierContext = std::make_unique(*this, static_cast(m_player.getVocation()->getBaseId())); - } - m_modifierContext->resetStrategies(); - m_spellsBonuses.clear(); - addStat(WheelStat_t::HEALTH, m_playerBonusData.stats.health); addStat(WheelStat_t::MANA, m_playerBonusData.stats.mana); addStat(WheelStat_t::CAPACITY, m_playerBonusData.stats.capacity * 100); @@ -1408,28 +1801,6 @@ void PlayerWheel::registerPlayerBonusData() { addStat(WheelStat_t::DAMAGE, m_playerBonusData.stats.damage); addStat(WheelStat_t::HEALING, m_playerBonusData.stats.healing); - auto activeGems = getActiveGems(); - std::string playerName = m_player.getName(); - for (const auto &gem : activeGems) { - auto count = m_playerBonusData.unlockedVesselResonances[static_cast(gem.affinity)]; - if (count >= 1) { - std::string modifierName(magic_enum::enum_name(gem.basicModifier1)); - g_logger().debug("[{}] Adding basic modifier 1 {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(gem.quality), magic_enum::enum_name(gem.affinity)); - m_modifierContext->addStrategies(gem.basicModifier1); - } - if (count >= 2 && gem.quality >= WheelGemQuality_t::Regular) { - std::string modifierName(magic_enum::enum_name(gem.basicModifier2)); - g_logger().debug("[{}] Adding basic modifier 2 {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(gem.quality), magic_enum::enum_name(gem.affinity)); - m_modifierContext->addStrategies(gem.basicModifier2); - } - if (count >= 3 && gem.quality >= WheelGemQuality_t::Greater) { - std::string modifierName(magic_enum::enum_name(gem.supremeModifier)); - g_logger().debug("[{}] Adding supreme modifier {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(gem.quality), magic_enum::enum_name(gem.affinity)); - m_modifierContext->addStrategies(gem.supremeModifier); - } - } - m_modifierContext->executeStrategies(); - // Skills addStat(WheelStat_t::MELEE, m_playerBonusData.skills.melee); addStat(WheelStat_t::DISTANCE, m_playerBonusData.skills.distance); @@ -1637,9 +2008,12 @@ void PlayerWheel::loadPlayerBonusData() { return; } + // Reset data to prevent stats from accumulating + resetPlayerData(); + // Initialize the relevant IOWheel data in the PlayerWheel loadDedicationAndConvictionPerks(); - loadRevelationPerks(); + registerPlayerBonusData(); printPlayerWheelMethodsBonusData(m_playerBonusData); @@ -1762,6 +2136,10 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus g_logger().debug(" storm: {}", bonusData.avatar.storm); } + if (bonusData.momentum > 0) { + g_logger().debug("bonus: {}", bonusData.momentum); + } + if (bonusData.mitigation > 0) { g_logger().debug("mitigation: {}", bonusData.mitigation); } @@ -1805,101 +2183,148 @@ void PlayerWheel::addSpellToVector(const std::string &spellName) { } void PlayerWheel::loadRevelationPerks() { - // Stats (Damage and Healing) - WheelStageEnum_t greenStage = getPlayerSliceStage("green"); - if (greenStage != WheelStageEnum_t::NONE) { - auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(greenStage); - m_playerBonusData.stats.damage += statsDamage; - m_playerBonusData.stats.healing += statsHealing; - m_playerBonusData.stages.giftOfLife = static_cast(greenStage); - } - - WheelStageEnum_t redStageEnum = getPlayerSliceStage("red"); - if (redStageEnum != WheelStageEnum_t::NONE) { - auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(redStageEnum); - m_playerBonusData.stats.damage += statsDamage; - m_playerBonusData.stats.healing += statsHealing; - - auto redStageValue = static_cast(redStageEnum); - 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) { - m_playerBonusData.stages.executionersThrow = redStageValue; - for (uint8_t i = 0; i < redStageValue; ++i) { - addSpellToVector("Executioner's Throw"); - } - } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { - m_playerBonusData.stages.beamMastery = redStageValue; - for (uint8_t i = 0; i < redStageValue; ++i) { - addSpellToVector("Great Death Beam"); - } - } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { - m_playerBonusData.stages.divineGrenade = redStageValue; - for (uint8_t i = 0; i < redStageValue; ++i) { - addSpellToVector("Divine Grenade"); - } + processActiveGems(); + applyStageBonuses(); +} + +void PlayerWheel::resetRevelationState() { + // First we reset the information + resetRevelationBonus(); + if (!m_modifierContext) { + m_modifierContext = std::make_unique(*this, static_cast(m_player.getVocation()->getBaseId())); + } + m_modifierContext->resetStrategies(); + m_spellsBonuses.clear(); +} + +void PlayerWheel::processActiveGems() { + auto activeGems = getActiveGems(); + std::string playerName = m_player.getName(); + for (const auto &[uuid, locked, affinity, quality, basicModifier1, basicModifier2, supremeModifier] : activeGems) { + if (uuid.empty()) { + g_logger().error("[{}] Player {} has an empty gem uuid", __FUNCTION__, playerName); + continue; + } + + auto count = m_playerBonusData.unlockedVesselResonances[static_cast(affinity)]; + if (count >= 1) { + uint8_t grade = getGemGrade(WheelFragmentType_t::Lesser, static_cast(basicModifier1)); + std::string modifierName(magic_enum::enum_name(basicModifier1)); + g_logger().debug("[{}] Adding basic modifier 1 {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(quality), magic_enum::enum_name(affinity)); + m_modifierContext->addStrategies(basicModifier1, grade); + } + if (count >= 2 && quality >= WheelGemQuality_t::Regular) { + uint8_t grade = getGemGrade(WheelFragmentType_t::Lesser, static_cast(basicModifier2)); + std::string modifierName(magic_enum::enum_name(basicModifier2)); + g_logger().debug("[{}] Adding basic modifier 2 {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(quality), magic_enum::enum_name(affinity)); + m_modifierContext->addStrategies(basicModifier2, grade); + } + if (count >= 3 && quality >= WheelGemQuality_t::Greater) { + uint8_t grade = getGemGrade(WheelFragmentType_t::Greater, static_cast(supremeModifier)); + std::string modifierName(magic_enum::enum_name(supremeModifier)); + g_logger().info("[{}] Adding supreme modifier {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(quality), magic_enum::enum_name(affinity)); + m_modifierContext->addStrategies(supremeModifier, grade); } } - WheelStageEnum_t purpleStageEnum = getPlayerSliceStage("purple"); - if (purpleStageEnum != WheelStageEnum_t::NONE) { - auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(purpleStageEnum); - m_playerBonusData.stats.damage += statsDamage; - m_playerBonusData.stats.healing += statsHealing; + g_logger().debug("[{}] active gems: {} ", __FUNCTION__, activeGems.size()); + m_modifierContext->executeStrategies(); +} - auto purpleStage = static_cast(purpleStageEnum); - 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) { - addSpellToVector("Avatar of Steel"); - } - } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { - m_playerBonusData.avatar.light = purpleStage; - for (uint8_t i = 0; i < purpleStage; ++i) { - addSpellToVector("Avatar of Light"); - } - } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { - m_playerBonusData.avatar.nature = purpleStage; - for (uint8_t i = 0; i < purpleStage; ++i) { - addSpellToVector("Avatar of Nature"); - } - } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { - m_playerBonusData.avatar.storm = purpleStage; - for (uint8_t i = 0; i < purpleStage; ++i) { - addSpellToVector("Avatar of Storm"); - } +void PlayerWheel::applyStageBonuses() { + applyStageBonusForColor("green"); + applyStageBonusForColor("red"); + applyStageBonusForColor("purple"); + applyStageBonusForColor("blue"); +} + +void PlayerWheel::applyStageBonusForColor(const std::string &color) { + WheelStageEnum_t stageEnum = getPlayerSliceStage(color); + if (stageEnum == WheelStageEnum_t::NONE) { + return; + } + + auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(stageEnum); + m_playerBonusData.stats.damage += statsDamage; + m_playerBonusData.stats.healing += statsHealing; + + auto stageValue = static_cast(stageEnum); + auto vocationEnum = static_cast(m_player.getPlayerVocationEnum()); + if (color == "green") { + m_playerBonusData.stages.giftOfLife = stageValue; + } else if (color == "red") { + applyRedStageBonus(stageValue, vocationEnum); + } else if (color == "purple") { + applyPurpleStageBonus(stageValue, vocationEnum); + } else if (color == "blue") { + applyBlueStageBonus(stageValue, vocationEnum); + } +} + +void PlayerWheel::applyRedStageBonus(uint8_t stageValue, Vocation_t vocationEnum) { + if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { + m_playerBonusData.stages.blessingOfTheGrove = stageValue; + } else if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { + m_playerBonusData.stages.executionersThrow = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Executioner's Throw"); + } + } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { + m_playerBonusData.stages.beamMastery = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Great Death Beam"); + } + } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { + m_playerBonusData.stages.divineGrenade = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Divine Grenade"); } } +} - WheelStageEnum_t blueStageEnum = getPlayerSliceStage("blue"); - if (blueStageEnum != WheelStageEnum_t::NONE) { - auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(blueStageEnum); - m_playerBonusData.stats.damage += statsDamage; - m_playerBonusData.stats.healing += statsHealing; +void PlayerWheel::applyPurpleStageBonus(uint8_t stageValue, Vocation_t vocationEnum) { + if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { + m_playerBonusData.avatar.steel = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Avatar of Steel"); + } + } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { + m_playerBonusData.avatar.light = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Avatar of Light"); + } + } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { + m_playerBonusData.avatar.nature = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Avatar of Nature"); + } + } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { + m_playerBonusData.avatar.storm = stageValue; + for (uint8_t i = 0; i < stageValue; ++i) { + addSpellToVector("Avatar of Storm"); + } + } +} - auto blueStage = static_cast(blueStageEnum); - 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) { - m_playerBonusData.stages.drainBody = blueStage; - for (uint8_t i = 0; i <= blueStage; ++i) { - addSpellToVector("Drain_Body_Spells"); - } - } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { - m_playerBonusData.stages.divineEmpowerment = blueStage; - for (uint8_t i = 0; i <= blueStage; ++i) { - addSpellToVector("Divine Empowerment"); - } - } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { - m_playerBonusData.stages.twinBurst = blueStage; - for (uint8_t i = 1; i <= blueStage; ++i) { - addSpellToVector("Twin Burst"); - addSpellToVector("Terra Burst"); - addSpellToVector("Ice Burst"); - } +void PlayerWheel::applyBlueStageBonus(uint8_t stageValue, Vocation_t vocationEnum) { + if (vocationEnum == Vocation_t::VOCATION_KNIGHT_CIP) { + m_playerBonusData.stages.combatMastery = stageValue; + } else if (vocationEnum == Vocation_t::VOCATION_SORCERER_CIP) { + m_playerBonusData.stages.drainBody = stageValue; + for (uint8_t i = 0; i <= stageValue; ++i) { + addSpellToVector("Drain_Body_Spells"); + } + } else if (vocationEnum == Vocation_t::VOCATION_PALADIN_CIP) { + m_playerBonusData.stages.divineEmpowerment = stageValue; + for (uint8_t i = 0; i <= stageValue; ++i) { + addSpellToVector("Divine Empowerment"); + } + } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { + m_playerBonusData.stages.twinBurst = stageValue; + for (uint8_t i = 1; i <= stageValue; ++i) { + addSpellToVector("Twin Burst"); + addSpellToVector("Terra Burst"); + addSpellToVector("Ice Burst"); } } } @@ -1967,7 +2392,34 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons for (const auto &slot : slots) { totalPoints += getPointsBySlotType(slot); } - totalPoints += m_bonusRevelationPoints[static_cast(affinity)]; + + auto affinityNumber = static_cast(affinity); + if (affinityNumber < m_bonusRevelationPoints.size()) { + auto bonusRevelationPoints = m_bonusRevelationPoints[affinityNumber]; + if (bonusRevelationPoints > 0) { + totalPoints += bonusRevelationPoints; + g_logger().debug("[{}] Player: {}, has affinity: {}, revelation points: {} total points: {}, relations: {}", __FUNCTION__, m_player.getName(), magic_enum::enum_name(affinity), bonusRevelationPoints, totalPoints, m_bonusRevelationPoints.size()); + } + } + + const auto vocationBaseId = m_player.getVocation()->getBaseId(); + const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); + + if (modsSupremeIt != modsSupremePositionByVocation.end()) { + for (auto modPosition : modsSupremeIt->second.get()) { + const auto pos = static_cast(modPosition); + uint8_t grade = 0; + auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); + + if (gradeKV.has_value()) { + grade = gradeKV->get(); + } + + totalPoints += grade == 3 ? 1 : 0; + } + } else { + g_logger().error("[{}] supreme modifications not found for vocation base id: {}", std::source_location::current().function_name(), vocationBaseId); + } if (totalPoints >= static_cast(WheelStagePointsEnum_t::THREE)) { return WheelStageEnum_t::THREE; @@ -2584,8 +3036,10 @@ std::shared_ptr PlayerWheel::getCombatDataSpell(CombatDamage &damage) { spell = g_spells().getRuneSpellByName(damage.runeSpellName); } if (spell) { + const auto &spellName = spell->getName(); + damage.damageMultiplier += checkFocusMasteryDamage(); - if (getHealingLinkUpgrade(spell->getName())) { + if (getHealingLinkUpgrade(spellName)) { damage.healingLink += 10; } if (spell->getSecondaryGroup() == SPELLGROUP_FOCUS && getInstant("Focus Mastery")) { @@ -2593,15 +3047,27 @@ std::shared_ptr PlayerWheel::getCombatDataSpell(CombatDamage &damage) { } if (spell->getWheelOfDestinyUpgraded()) { - damage.criticalDamage += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::CRITICAL_DAMAGE); - damage.criticalChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::CRITICAL_CHANCE); - damage.damageMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::DAMAGE); - damage.damageReductionMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::DAMAGE_REDUCTION); - damage.healingMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::HEAL); - damage.manaLeech += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::MANA_LEECH); - damage.manaLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::LIFE_LEECH_CHANCE); - damage.lifeLeech += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::LIFE_LEECH); - damage.lifeLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, spellGrade) + getSpellBonus(spell->getName(), WheelSpellBoost_t::LIFE_LEECH_CHANCE); + damage.criticalDamage += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, spellGrade) * 100; + damage.criticalChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, spellGrade); + damage.damageMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, spellGrade); + damage.damageReductionMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, spellGrade); + damage.healingMultiplier += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, spellGrade); + damage.manaLeech += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, spellGrade); + damage.manaLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, spellGrade); + damage.lifeLeech += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, spellGrade); + damage.lifeLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, spellGrade); + } + + if (m_spellsBonuses.contains(spellName)) { + damage.criticalDamage += (getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_DAMAGE) * 100); + damage.criticalChance += getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_CHANCE); + damage.damageMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE); + damage.damageReductionMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE_REDUCTION); + damage.healingMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::HEAL); + damage.manaLeech += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH); + damage.manaLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH_CHANCE); + damage.lifeLeech += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH); + damage.lifeLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH_CHANCE); } } @@ -3155,6 +3621,10 @@ WheelGemBasicModifier_t PlayerWheel::selectBasicModifier2(WheelGemBasicModifier_ return modifier; } +std::string PlayerWheelGem::toString() const { + return fmt::format("[PlayerWheelGem] uuid: {}, locked: {}, affinity: {}, quality: {}, basicModifier1: {}, basicModifier2: {}, supremeModifier: {}", uuid, locked, static_cast(affinity), static_cast(quality), static_cast(basicModifier1), static_cast(basicModifier2), static_cast(supremeModifier)); +} + void PlayerWheelGem::save(const std::shared_ptr &kv) const { kv->scoped("revealed")->set(uuid, serialize()); } diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index b01b1c5a1b3..a99b3dac49a 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -9,17 +9,22 @@ #pragma once -#include "utils/utils_definitions.hpp" -#include "enums/player_wheel.hpp" #include "creatures/players/wheel/wheel_definitions.hpp" -#include "kv/kv_definitions.hpp" -class Spell; -class Player; class Creature; -class NetworkMessage; +class IOWheel; class KV; +class NetworkMessage; +class Player; +class Spell; class WheelModifierContext; +class ValueWrapper; + +enum class WheelFragmentType_t : uint8_t; +enum class WheelGemAffinity_t : uint8_t; +enum class WheelGemBasicModifier_t : uint8_t; +enum class WheelGemQuality_t : uint8_t; +enum class WheelGemSupremeModifier_t : uint8_t; struct PlayerWheelGem { std::string uuid; @@ -30,9 +35,7 @@ struct PlayerWheelGem { WheelGemBasicModifier_t basicModifier2; WheelGemSupremeModifier_t supremeModifier; - std::string toString() const { - return fmt::format("[PlayerWheelGem] uuid: {}, locked: {}, affinity: {}, quality: {}, basicModifier1: {}, basicModifier2: {}, supremeModifier: {}", uuid, locked, static_cast(affinity), static_cast(quality), static_cast(basicModifier1), static_cast(basicModifier2), static_cast(supremeModifier)); - } + std::string toString() const; void save(const std::shared_ptr &kv) const; @@ -81,7 +84,9 @@ class PlayerWheel { void saveSlotPointsOnPressSaveButton(NetworkMessage &msg); void addPromotionScrolls(NetworkMessage &msg) const; void addGems(NetworkMessage &msg) const; - void sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) const; + void addGradeModifiers(NetworkMessage &msg) const; + void improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos); + void sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId); void sendGiftOfLifeCooldown() const; /* @@ -121,8 +126,6 @@ class PlayerWheel { uint8_t getMaxPointsPerSlot(WheelSlots_t slot) const; uint16_t getUnusedPoints() const; - void resetPlayerBonusData(); - void setPlayerCombatStats(CombatType_t type, int32_t leechAmount); void reloadPlayerData(); @@ -143,9 +146,13 @@ class PlayerWheel { WheelStageEnum_t getPlayerSliceStage(const std::string &color) const; + std::tuple getLesserGradeCost(uint8_t grade) const; + std::tuple getGreaterGradeCost(uint8_t grade) const; + void printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonusData &bonusData) const; private: + void addInitialGems(); /* * Open wheel functions helpers */ @@ -174,6 +181,8 @@ class PlayerWheel { uint8_t getOptions(uint32_t ownerId) const; std::shared_ptr gemsKV() const; + std::shared_ptr gemsGradeKV(WheelFragmentType_t quality, uint8_t pos) const; + uint8_t getGemGrade(WheelFragmentType_t quality, uint8_t pos) const; std::vector getRevealedGems() const; std::vector getActiveGems() const; @@ -182,6 +191,8 @@ class PlayerWheel { static uint64_t getGemRevealCost(WheelGemQuality_t quality); + void resetPlayerData(); + // Members variables const uint16_t m_minLevelToStartCountPoints = 50; uint16_t m_pointsPerLevel = 1; @@ -423,6 +434,14 @@ class PlayerWheel { WheelGemBasicModifier_t selectBasicModifier2(WheelGemBasicModifier_t modifier1) const; private: + void resetRevelationState(); + void processActiveGems(); + void applyStageBonuses(); + void applyStageBonusForColor(const std::string &color); + void applyRedStageBonus(uint8_t stageValue, Vocation_t vocationEnum); + void applyPurpleStageBonus(uint8_t stageValue, Vocation_t vocationEnum); + void applyBlueStageBonus(uint8_t stageValue, Vocation_t vocationEnum); + friend class Player; // Reference to the player Player &m_player; diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index c23d2adf53f..8fc3783b4cb 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -253,6 +253,7 @@ struct PlayerWheelMethodsBonusData { Stages stages; Avatar avatar; + float momentum = 0; float mitigation = 0; std::vector spells; }; diff --git a/src/creatures/players/wheel/wheel_gems.cpp b/src/creatures/players/wheel/wheel_gems.cpp index 186a6939186..fb77643913a 100644 --- a/src/creatures/players/wheel/wheel_gems.cpp +++ b/src/creatures/players/wheel/wheel_gems.cpp @@ -28,184 +28,193 @@ void GemModifierSpellBonusStrategy::execute() { m_wheel.addSpellBonus(m_spellName, m_bonus); } -void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier) { +void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier, uint8_t grade) { + float gradeMultiplier = 1.0; + if (grade == 1) { + gradeMultiplier = 1.1; + } else if (grade == 2) { + gradeMultiplier = 1.2; + } else if (grade == 3) { + gradeMultiplier = 1.5; + } + switch (modifier) { case WheelGemBasicModifier_t::General_PhysicalResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_PHYSICALDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_PHYSICALDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_HolyResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_DeathResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_FireResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 200)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 200 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_EarthResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 200)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 200 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_IceResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 200)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 200 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 200)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 200 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_HolyResistance_DeathWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 150)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 150 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, -100)); break; case WheelGemBasicModifier_t::General_DeathResistance_HolyWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 150)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 150 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, -100)); break; case WheelGemBasicModifier_t::General_FireResistance_EarthResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_FireResistance_IceResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_FireResistance_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_EarthResistance_IceResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_EarthResistance_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_IceResistance_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_FireResistance_EarthWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_FireResistance_IceWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_FireResistance_EnergyWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EarthResistance_FireWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EarthResistance_IceWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EarthResistance_EnergyWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_IceResistance_EarthWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_IceResistance_FireWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_IceResistance_EnergyWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EnergyResistance_EarthWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EnergyResistance_IceWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_EnergyResistance_FireWeakness: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier)); m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200)); break; case WheelGemBasicModifier_t::General_ManaDrainResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 300 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_LifeDrainResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 300)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 300 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_ManaDrainResistance_LifeDrainResistance: - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 150)); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 150)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 150 * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 150 * gradeMultiplier)); break; case WheelGemBasicModifier_t::General_MitigationMultiplier: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MITIGATION, 500)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MITIGATION, 500 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Health: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mana_FireResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mana: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Health_FireResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Health_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Health_EarthResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Health_IceResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Mixed: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Capacity_FireResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Capacity_IceResistance: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); - m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); + m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier)); break; case WheelGemBasicModifier_t::Vocation_Capacity: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier))); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier)); break; default: @@ -213,304 +222,337 @@ void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier) { } } -void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier) { +void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uint8_t grade) { WheelSpells::Bonus bonus; + auto &wheelBonus = m_wheel.getBonusData(); + + float gradeMultiplier = 1.0; + if (grade == 1) { + gradeMultiplier = 1.1; + } else if (grade == 2) { + gradeMultiplier = 1.2; + } else if (grade == 3) { + gradeMultiplier = 1.5; + } switch (modifier) { case WheelGemSupremeModifier_t::General_Dodge: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 25)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 25 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_LifeLeech: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 120)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 120 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_ManaLeech: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 40)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 40 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_CriticalDamage: - m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 150 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Green, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Green, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Green, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::SorcererDruid_UltimateHealing: - bonus.increase.heal = 10; + bonus.increase.heal = 10 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Ultimate Healing", bonus)); break; case WheelGemSupremeModifier_t::Knight_RevelationMastery_ExecutionersThrow: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Knight_RevelationMastery_AvatarOfSteel: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Knight_RevelationMastery_CombatMastery: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineGrenade: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Paladin_RevelationMastery_AvatarOfLight: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineEmpowerment: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Druid_RevelationMastery_BlessingOfTheGrove: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Druid_RevelationMastery_AvatarOfNature: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Druid_RevelationMastery_TwinBursts: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_BeamMastery: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_AvatarOfStorm: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_DrainBody: - m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150)); + m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier)); + m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier); break; case WheelGemSupremeModifier_t::Knight_AvatarOfSteel_Cooldown: bonus.decrease.cooldown = 300 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Avatar of Steel", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_Cooldown: bonus.decrease.cooldown = 1 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Fierce Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Fierce Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Berserk_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Berserk_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Front_Sweep_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Front Sweep", bonus)); break; case WheelGemSupremeModifier_t::Knight_Front_Sweep_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Front Sweep", bonus)); break; case WheelGemSupremeModifier_t::Knight_Groundshaker_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Groundshaker", bonus)); break; case WheelGemSupremeModifier_t::Knight_Groundshaker_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Groundshaker", bonus)); break; case WheelGemSupremeModifier_t::Knight_Annihilation_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Annihilation", bonus)); break; case WheelGemSupremeModifier_t::Knight_Annihilation_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Annihilation", bonus)); break; case WheelGemSupremeModifier_t::Knight_FairWoundCleansing_HealingIncrease: - bonus.increase.heal = 10; + bonus.increase.heal = 10 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Fair Wound Cleansing", bonus)); break; case WheelGemSupremeModifier_t::Paladin_AvatarOfLight_Cooldown: bonus.decrease.cooldown = 300 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Avatar of Light", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineDazzle_Cooldown: bonus.decrease.cooldown = 2 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Divine Dazzle", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineCaldera_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Caldera", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineCaldera_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Caldera", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineMissile_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Missile", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineMissile_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Divine Missile", bonus)); break; case WheelGemSupremeModifier_t::Paladin_EtherealSpear_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_EtherealSpear_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Strong Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Strong Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineEmpowerment_Cooldown: bonus.decrease.cooldown = 3 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Divine Empowerment", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_Cooldown: bonus.decrease.cooldown = 1 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_Salvation_HealingIncrease: - bonus.increase.heal = 10; + bonus.increase.heal = 10 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Salvation", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_AvatarOfStorm_Cooldown: bonus.decrease.cooldown = 300 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Avatar of Storm", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_EnergyWave_Cooldown: bonus.decrease.cooldown = 1 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Death Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Death Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_HellsCore_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Hell's Core", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_HellsCore_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Hell's Core", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_EnergyWave_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_EnergyWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Fire Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Fire Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Rage of the Skies", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Rage of the Skies", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Energy Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Great Energy Beam", bonus)); break; case WheelGemSupremeModifier_t::Druid_AvatarOfNature_Cooldown: bonus.decrease.cooldown = 300 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Avatar of Nature", bonus)); break; case WheelGemSupremeModifier_t::Druid_NaturesEmbrace_Cooldown: bonus.decrease.cooldown = 5 * 1000; + wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.push_back(std::make_unique(m_wheel, "Nature's Embrace", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraBurst_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Terra Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraBurst_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Terra Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_IceBurst_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Ice Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_IceBurst_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Ice Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_EternalWinter_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Eternal Winter", bonus)); break; case WheelGemSupremeModifier_t::Druid_EternalWinter_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Eternal Winter", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraWave_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Terra Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Terra Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_StrongIceWave_DamageIncrease: - bonus.increase.damage = 25; + bonus.increase.damage = 25 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Strong Ice Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_StrongIceWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8; + bonus.increase.criticalDamage = 8 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Strong Ice Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_HealFriend_HealingIncrease: - bonus.increase.heal = 10; + bonus.increase.heal = 10 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Heal Friend", bonus)); break; case WheelGemSupremeModifier_t::Druid_MassHealing_HealingIncrease: - bonus.increase.heal = 10; + bonus.increase.heal = 10 * gradeMultiplier; m_strategies.push_back(std::make_unique(m_wheel, "Mass Healing", bonus)); break; default: @@ -523,3 +565,230 @@ void WheelModifierContext::executeStrategies() { strategy->execute(); } } + +int32_t WheelGemUtils::getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { + static const std::unordered_map> stats = { + { + WheelGemBasicModifier_t::Vocation_Health, + { + { Vocation_t::VOCATION_KNIGHT, 300 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Health_FireResistance, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Health_EnergyResistance, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Health_EarthResistance, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Health_IceResistance, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mixed, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mixed2, + { + { Vocation_t::VOCATION_KNIGHT, 150 }, + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 50 }, + { Vocation_t::VOCATION_DRUID, 50 }, + }, + }, + }; + + auto modifierIt = stats.find(modifier); + if (modifierIt != stats.end()) { + auto vocationIt = modifierIt->second.find(vocation); + if (vocationIt != modifierIt->second.end()) { + return vocationIt->second; + } + } + return 0; +} + +int32_t WheelGemUtils::getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { + static const std::unordered_map> stats = { + { + WheelGemBasicModifier_t::Vocation_Mana_FireResistance, + { + { Vocation_t::VOCATION_KNIGHT, 50 }, + { Vocation_t::VOCATION_PALADIN, 150 }, + { Vocation_t::VOCATION_SORCERER, 300 }, + { Vocation_t::VOCATION_DRUID, 300 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance, + { + { Vocation_t::VOCATION_KNIGHT, 50 }, + { Vocation_t::VOCATION_PALADIN, 150 }, + { Vocation_t::VOCATION_SORCERER, 300 }, + { Vocation_t::VOCATION_DRUID, 300 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance, + { + { Vocation_t::VOCATION_KNIGHT, 50 }, + { Vocation_t::VOCATION_PALADIN, 150 }, + { Vocation_t::VOCATION_SORCERER, 300 }, + { Vocation_t::VOCATION_DRUID, 300 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance, + { + { Vocation_t::VOCATION_KNIGHT, 50 }, + { Vocation_t::VOCATION_PALADIN, 150 }, + { Vocation_t::VOCATION_SORCERER, 300 }, + { Vocation_t::VOCATION_DRUID, 300 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mana, + { + { Vocation_t::VOCATION_KNIGHT, 100 }, + { Vocation_t::VOCATION_PALADIN, 300 }, + { Vocation_t::VOCATION_SORCERER, 600 }, + { Vocation_t::VOCATION_DRUID, 600 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mixed, + { + { Vocation_t::VOCATION_PALADIN, 100 }, + { Vocation_t::VOCATION_SORCERER, 150 }, + { Vocation_t::VOCATION_DRUID, 150 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Capacity, + { + { Vocation_t::VOCATION_KNIGHT, 50 }, + { Vocation_t::VOCATION_PALADIN, 150 }, + { Vocation_t::VOCATION_SORCERER, 300 }, + { Vocation_t::VOCATION_DRUID, 300 }, + }, + } + }; + + auto modifierIt = stats.find(modifier); + if (modifierIt != stats.end()) { + auto vocationIt = modifierIt->second.find(vocation); + if (vocationIt != modifierIt->second.end()) { + return vocationIt->second; + } + } + return 0; +} + +int32_t WheelGemUtils::getCapacityValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { + static const std::unordered_map> stats = { + { + WheelGemBasicModifier_t::Vocation_Capacity_FireResistance, + { + { Vocation_t::VOCATION_KNIGHT, 250 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance, + { + { Vocation_t::VOCATION_KNIGHT, 250 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance, + { + { Vocation_t::VOCATION_KNIGHT, 250 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Capacity_IceResistance, + { + { Vocation_t::VOCATION_KNIGHT, 250 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Capacity, + { + { Vocation_t::VOCATION_KNIGHT, 500 }, + { Vocation_t::VOCATION_PALADIN, 400 }, + { Vocation_t::VOCATION_SORCERER, 200 }, + { Vocation_t::VOCATION_DRUID, 200 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mixed, + { + { Vocation_t::VOCATION_KNIGHT, 125 }, + }, + }, + { + WheelGemBasicModifier_t::Vocation_Mixed2, + { + { Vocation_t::VOCATION_KNIGHT, 250 }, + { Vocation_t::VOCATION_PALADIN, 200 }, + { Vocation_t::VOCATION_SORCERER, 100 }, + { Vocation_t::VOCATION_DRUID, 100 }, + }, + } + }; + + auto modifierIt = stats.find(modifier); + if (modifierIt != stats.end()) { + auto vocationIt = modifierIt->second.find(vocation); + if (vocationIt != modifierIt->second.end()) { + return vocationIt->second; + } + } + return 0; +} diff --git a/src/creatures/players/wheel/wheel_gems.hpp b/src/creatures/players/wheel/wheel_gems.hpp index dc691fa2b26..dee64225183 100644 --- a/src/creatures/players/wheel/wheel_gems.hpp +++ b/src/creatures/players/wheel/wheel_gems.hpp @@ -9,12 +9,18 @@ #pragma once -#include "creatures/creatures_definitions.hpp" -#include "wheel_definitions.hpp" -#include "enums/player_wheel.hpp" +#include "creatures/players/wheel/wheel_definitions.hpp" class PlayerWheel; +enum CombatType_t : uint8_t; +enum Vocation_t : uint16_t; + +enum class WheelGemAffinity_t : uint8_t; +enum class WheelGemBasicModifier_t : uint8_t; +enum class WheelGemSupremeModifier_t : uint8_t; +enum class WheelStat_t : uint8_t; + class GemModifierStrategy { public: explicit GemModifierStrategy(PlayerWheel &wheel) : @@ -51,20 +57,21 @@ class GemModifierStatStrategy : public GemModifierStrategy { private: WheelStat_t m_stat; - int32_t m_value; + int32_t m_value {}; }; class GemModifierRevelationStrategy : public GemModifierStrategy { public: explicit GemModifierRevelationStrategy(PlayerWheel &wheel, WheelGemAffinity_t affinity, [[maybe_unused]] uint16_t value) : GemModifierStrategy(wheel), - m_affinity(affinity) { } + m_affinity(affinity), + m_value(value) { } void execute() override; private: WheelGemAffinity_t m_affinity; - uint16_t m_value; + uint16_t m_value {}; }; class GemModifierSpellBonusStrategy : public GemModifierStrategy { @@ -86,8 +93,8 @@ class WheelModifierContext { explicit WheelModifierContext(PlayerWheel &wheel, Vocation_t vocation) : m_wheel(wheel), m_vocation(vocation) { } - void addStrategies(WheelGemBasicModifier_t modifier); - void addStrategies(WheelGemSupremeModifier_t modifier); + void addStrategies(WheelGemBasicModifier_t modifier, uint8_t grade); + void addStrategies(WheelGemSupremeModifier_t modifier, uint8_t grade); void resetStrategies() { m_strategies.clear(); @@ -101,229 +108,9 @@ class WheelModifierContext { Vocation_t m_vocation; }; -[[maybe_unused]] static int32_t getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { - static const std::unordered_map> stats = { - { - WheelGemBasicModifier_t::Vocation_Health, - { - { Vocation_t::VOCATION_KNIGHT, 300 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Health_FireResistance, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Health_EnergyResistance, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Health_EarthResistance, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Health_IceResistance, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mixed, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mixed2, - { - { Vocation_t::VOCATION_KNIGHT, 150 }, - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 50 }, - { Vocation_t::VOCATION_DRUID, 50 }, - }, - }, - }; - - auto modifierIt = stats.find(modifier); - if (modifierIt != stats.end()) { - auto vocationIt = modifierIt->second.find(vocation); - if (vocationIt != modifierIt->second.end()) { - return vocationIt->second; - } - } - return 0; -} - -[[maybe_unused]] static int32_t getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { - static const std::unordered_map> stats = { - { - WheelGemBasicModifier_t::Vocation_Mana_FireResistance, - { - { Vocation_t::VOCATION_KNIGHT, 50 }, - { Vocation_t::VOCATION_PALADIN, 150 }, - { Vocation_t::VOCATION_SORCERER, 300 }, - { Vocation_t::VOCATION_DRUID, 300 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance, - { - { Vocation_t::VOCATION_KNIGHT, 50 }, - { Vocation_t::VOCATION_PALADIN, 150 }, - { Vocation_t::VOCATION_SORCERER, 300 }, - { Vocation_t::VOCATION_DRUID, 300 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance, - { - { Vocation_t::VOCATION_KNIGHT, 50 }, - { Vocation_t::VOCATION_PALADIN, 150 }, - { Vocation_t::VOCATION_SORCERER, 300 }, - { Vocation_t::VOCATION_DRUID, 300 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance, - { - { Vocation_t::VOCATION_KNIGHT, 50 }, - { Vocation_t::VOCATION_PALADIN, 150 }, - { Vocation_t::VOCATION_SORCERER, 300 }, - { Vocation_t::VOCATION_DRUID, 300 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mana, - { - { Vocation_t::VOCATION_KNIGHT, 100 }, - { Vocation_t::VOCATION_PALADIN, 300 }, - { Vocation_t::VOCATION_SORCERER, 600 }, - { Vocation_t::VOCATION_DRUID, 600 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mixed, - { - { Vocation_t::VOCATION_PALADIN, 100 }, - { Vocation_t::VOCATION_SORCERER, 150 }, - { Vocation_t::VOCATION_DRUID, 150 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Capacity, - { - { Vocation_t::VOCATION_KNIGHT, 50 }, - { Vocation_t::VOCATION_PALADIN, 150 }, - { Vocation_t::VOCATION_SORCERER, 300 }, - { Vocation_t::VOCATION_DRUID, 300 }, - }, - } - }; - - auto modifierIt = stats.find(modifier); - if (modifierIt != stats.end()) { - auto vocationIt = modifierIt->second.find(vocation); - if (vocationIt != modifierIt->second.end()) { - return vocationIt->second; - } - } - return 0; -} - -[[maybe_unused]] static int32_t getCapacityValue(Vocation_t vocation, WheelGemBasicModifier_t modifier) { - static const std::unordered_map> stats = { - { - WheelGemBasicModifier_t::Vocation_Capacity_FireResistance, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Capacity_IceResistance, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Capacity, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mixed, - { - { Vocation_t::VOCATION_KNIGHT, 125 }, - }, - }, - { - WheelGemBasicModifier_t::Vocation_Mixed2, - { - { Vocation_t::VOCATION_KNIGHT, 250 }, - { Vocation_t::VOCATION_PALADIN, 200 }, - { Vocation_t::VOCATION_SORCERER, 100 }, - { Vocation_t::VOCATION_DRUID, 100 }, - }, - } - }; - - auto modifierIt = stats.find(modifier); - if (modifierIt != stats.end()) { - auto vocationIt = modifierIt->second.find(vocation); - if (vocationIt != modifierIt->second.end()) { - return vocationIt->second; - } - } - return 0; -} +class WheelGemUtils { +public: + [[maybe_unused]] static int32_t getHealthValue(Vocation_t vocation, WheelGemBasicModifier_t modifier); + [[maybe_unused]] static int32_t getManaValue(Vocation_t vocation, WheelGemBasicModifier_t modifier); + [[maybe_unused]] static int32_t getCapacityValue(Vocation_t vocation, WheelGemBasicModifier_t modifier); +}; diff --git a/src/database/database.cpp b/src/database/database.cpp index 4f038605e05..37fe2b1a3c6 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -105,7 +105,7 @@ bool Database::isRecoverableError(unsigned int error) const { return error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR || error == CR_CONN_HOST_ERROR || error == 1053 /*ER_SERVER_SHUTDOWN*/ || error == CR_CONNECTION_ERROR; } -bool Database::retryQuery(const std::string_view &query, int retries) { +bool Database::retryQuery(std::string_view query, int retries) { while (retries > 0 && mysql_query(handle, query.data()) != 0) { g_logger().error("Query: {}", query.substr(0, 256)); g_logger().error("MySQL error [{}]: {}", mysql_errno(handle), mysql_error(handle)); @@ -123,7 +123,7 @@ bool Database::retryQuery(const std::string_view &query, int retries) { return true; } -bool Database::executeQuery(const std::string_view &query) { +bool Database::executeQuery(std::string_view query) { if (!handle) { g_logger().error("Database not initialized!"); return false; @@ -142,7 +142,7 @@ bool Database::executeQuery(const std::string_view &query) { return success; } -DBResult_ptr Database::storeQuery(const std::string_view &query) { +DBResult_ptr Database::storeQuery(std::string_view query) { if (!handle) { g_logger().error("Database not initialized!"); return nullptr; diff --git a/src/database/database.hpp b/src/database/database.hpp index 2a27c30159b..4984873ed62 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -10,7 +10,6 @@ #pragma once #include "declarations.hpp" -#include "lib/logging/log_with_spd_log.hpp" #ifndef USE_PRECOMPILED_HEADERS #include @@ -37,10 +36,10 @@ class Database { bool connect(const std::string* host, const std::string* user, const std::string* password, const std::string* database, uint32_t port, const std::string* sock); - bool retryQuery(const std::string_view &query, int retries); - bool executeQuery(const std::string_view &query); + bool retryQuery(std::string_view query, int retries); + bool executeQuery(std::string_view query); - DBResult_ptr storeQuery(const std::string_view &query); + DBResult_ptr storeQuery(std::string_view query); std::string escapeString(const std::string &s) const; diff --git a/src/enums/object_category.hpp b/src/enums/object_category.hpp index 74191bf9b50..87f6b7e00b7 100644 --- a/src/enums/object_category.hpp +++ b/src/enums/object_category.hpp @@ -9,7 +9,7 @@ #pragma once -enum ObjectCategory_t { +enum ObjectCategory_t : uint8_t { OBJECTCATEGORY_NONE = 0, OBJECTCATEGORY_ARMORS = 1, OBJECTCATEGORY_NECKLACES = 2, diff --git a/src/enums/player_wheel.hpp b/src/enums/player_wheel.hpp index 81cde57f651..6133f191581 100644 --- a/src/enums/player_wheel.hpp +++ b/src/enums/player_wheel.hpp @@ -18,6 +18,19 @@ enum class WheelGemAction_t : uint8_t { Reveal, SwitchDomain, ToggleLock, + ImproveGrade +}; + +enum class WheelImproveGemGrade_t : uint8_t { + Grade1, + Grade2, + Grade3, + Grade4, +}; + +enum class WheelFragmentType_t : uint8_t { + Greater, + Lesser, }; enum class WheelGemAffinity_t : uint8_t { @@ -69,8 +82,8 @@ enum class WheelGemBasicModifier_t : uint8_t { General_MitigationMultiplier, Vocation_Health, - Vocation_Capacity, - Vocation_Mana_FireResistance, + // Vocation_Mana_Capacity = 32, INVALID MODIFIER, WILL BE DISPLAYED AS (UNKNOWN) + Vocation_Mana_FireResistance = 33, Vocation_Mana_EnergyResistance, Vocation_Mana_Earth_Resistance, Vocation_Mana_Ice_Resistance, @@ -85,6 +98,7 @@ enum class WheelGemBasicModifier_t : uint8_t { Vocation_Capacity_EnergyResistance, Vocation_Capacity_EarthResistance, Vocation_Capacity_IceResistance, + Vocation_Capacity, }; enum class WheelGemSupremeModifier_t : uint8_t { @@ -103,12 +117,12 @@ enum class WheelGemSupremeModifier_t : uint8_t { Knight_Fierce_Berserk_CriticalExtraDamage, Knight_Berserk_DamageIncrease, Knight_Berserk_CriticalExtraDamage, - Knight_Front_Sweep_CriticalExtraDamage, Knight_Front_Sweep_DamageIncrease, + Knight_Front_Sweep_CriticalExtraDamage, Knight_Groundshaker_DamageIncrease, Knight_Groundshaker_CriticalExtraDamage, - Knight_Annihilation_CriticalExtraDamage, Knight_Annihilation_DamageIncrease, + Knight_Annihilation_CriticalExtraDamage, Knight_FairWoundCleansing_HealingIncrease, Knight_RevelationMastery_AvatarOfSteel, Knight_RevelationMastery_ExecutionersThrow, @@ -157,8 +171,8 @@ enum class WheelGemSupremeModifier_t : uint8_t { Druid_TerraBurst_CriticalExtraDamage, Druid_IceBurst_DamageIncrease, Druid_IceBurst_CriticalExtraDamage, - Druid_EternalWinter_CriticalExtraDamage, Druid_EternalWinter_DamageIncrease, + Druid_EternalWinter_CriticalExtraDamage, Druid_TerraWave_DamageIncrease, Druid_TerraWave_CriticalExtraDamage, Druid_StrongIceWave_DamageIncrease, diff --git a/src/game/game.cpp b/src/game/game.cpp index 279f4cf714b..c6ba09311d5 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -34,6 +34,7 @@ #include "items/weapons/weapons.hpp" #include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/wheel/player_wheel.hpp" +#include "enums/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -6395,28 +6396,17 @@ void Game::checkCreatures() { metrics::method_latency measure(__METHOD_NAME__); static size_t index = 0; - auto &checkCreatureList = checkCreatureLists[index]; - size_t it = 0, end = checkCreatureList.size(); - while (it < end) { - auto creature = checkCreatureList[it]; - if (creature && creature->creatureCheck) { - if (creature->getHealth() > 0) { - creature->onThink(EVENT_CREATURE_THINK_INTERVAL); - creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL); - creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL); - } else { - afterCreatureZoneChange(creature, creature->getZones(), {}); - creature->onDeath(); - } - ++it; - } else { - creature->inCheckCreaturesVector = false; - - checkCreatureList[it] = checkCreatureList.back(); - checkCreatureList.pop_back(); - --end; + std::erase_if(checkCreatureLists[index], [this](const std::shared_ptr &creature) { + if (creature->creatureCheck && creature->isAlive()) { + creature->onThink(EVENT_CREATURE_THINK_INTERVAL); + creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL); + creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL); + return false; } - } + + creature->inCheckCreaturesVector = false; + return true; + }); index = (index + 1) % EVENT_CREATURECOUNT; } @@ -9796,8 +9786,9 @@ void Game::playerWheelGemAction(uint32_t playerId, NetworkMessage &msg) { return; } - auto action = msg.get(); - auto param = msg.get(); + const auto action = msg.getByte(); + const auto param = msg.getByte(); + uint8_t pos = 0; switch (static_cast(action)) { case WheelGemAction_t::Destroy: @@ -9812,6 +9803,10 @@ void Game::playerWheelGemAction(uint32_t playerId, NetworkMessage &msg) { case WheelGemAction_t::ToggleLock: player->wheel()->toggleGemLock(param); break; + case WheelGemAction_t::ImproveGrade: + pos = msg.getByte(); + player->wheel()->improveGemGrade(static_cast(param), pos); + break; default: g_logger().error("[{}] player {} is trying to do invalid action {} on wheel", __FUNCTION__, player->getName(), action); break; @@ -10073,7 +10068,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr forgeableMonsters.clear(); // If the forgeable monsters haven't been created // Then we'll create them so they don't return in the next if (forgeableMonsters.empty()) - for (auto [monsterId, monster] : monsters) { + for (const auto &[monsterId, monster] : monsters) { auto monsterTile = monster->getTile(); if (!monster || !monsterTile) { continue; @@ -10092,7 +10087,9 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr } // If you're trying to create a new fiendish and it's already max size, let's remove one of them - if (getFiendishMonsters().size() >= 3) { + if (auto fiendishLimit = g_configManager().getNumber(FORGE_FIENDISH_CREATURES_LIMIT); + // Condition + getFiendishMonsters().size() >= fiendishLimit) { monster->clearFiendishStatus(); removeFiendishMonster(monsterId); break; @@ -10212,7 +10209,7 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { if (create) { g_dispatcher().scheduleEvent( - 200 * 1000, [this] { makeInfluencedMonster(); }, "Game::makeInfluencedMonster" + 10 * 1000, [this] { makeInfluencedMonster(); }, "Game::makeInfluencedMonster" ); } } else { @@ -10230,7 +10227,7 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { if (create) { g_dispatcher().scheduleEvent( - 300 * 1000, [this] { makeFiendishMonster(0, false); }, "Game::makeFiendishMonster" + 270 * 1000, [this] { makeFiendishMonster(0, false); }, "Game::makeFiendishMonster" ); } } else { @@ -10241,15 +10238,18 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { } void Game::updateForgeableMonsters() { - forgeableMonsters.clear(); - for (auto [monsterId, monster] : monsters) { - auto monsterTile = monster->getTile(); - if (!monsterTile) { - continue; - } + if (auto influencedLimit = g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT); + forgeableMonsters.size() < influencedLimit) { + forgeableMonsters.clear(); + for (const auto &[monsterId, monster] : monsters) { + const auto &monsterTile = monster->getTile(); + if (!monsterTile) { + continue; + } - if (monster->canBeForgeMonster() && !monsterTile->hasFlag(TILESTATE_NOLOGOUT)) { - forgeableMonsters.push_back(monster->getID()); + if (monster->canBeForgeMonster() && !monsterTile->hasFlag(TILESTATE_NOLOGOUT)) { + forgeableMonsters.push_back(monster->getID()); + } } } diff --git a/src/game/scheduling/task.cpp b/src/game/scheduling/task.cpp index 0e1f6489516..8e1b984a905 100644 --- a/src/game/scheduling/task.cpp +++ b/src/game/scheduling/task.cpp @@ -9,7 +9,6 @@ #include "task.hpp" -#include "lib/logging/log_with_spd_log.hpp" #include "lib/metrics/metrics.hpp" std::atomic_uint_fast64_t Task::LAST_EVENT_ID = 0; diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 0bb0ff15699..d10aed53615 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -135,7 +135,7 @@ const std::map &IOBosstiary::getBosstiaryMap() const { return bosstiaryMap; } -void IOBosstiary::setBossBoostedName(const std::string_view &name) { +void IOBosstiary::setBossBoostedName(std::string_view name) { boostedBoss = name; } diff --git a/src/io/io_bosstiary.hpp b/src/io/io_bosstiary.hpp index 84d9f0955e0..11eabf30a8b 100644 --- a/src/io/io_bosstiary.hpp +++ b/src/io/io_bosstiary.hpp @@ -54,7 +54,7 @@ class IOBosstiary { { BosstiaryRarity_t::RARITY_NEMESIS, { { 1, 10 }, { 3, 30 }, { 5, 60 } } } }; - void setBossBoostedName(const std::string_view &name); + void setBossBoostedName(std::string_view name); std::string getBoostedBossName() const; void setBossBoostedId(uint16_t raceId); uint16_t getBoostedBossId() const; diff --git a/src/io/io_wheel.cpp b/src/io/io_wheel.cpp index 1d2a7884e82..72d5e207d4c 100644 --- a/src/io/io_wheel.cpp +++ b/src/io/io_wheel.cpp @@ -9,6 +9,7 @@ #include "io/io_wheel.hpp" +#include "enums/player_wheel.hpp" #include "kv/kv.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/player.hpp" diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 220b50a85e6..4c20d8a6aa1 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -239,21 +239,19 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, std::shared_ptr til return; } - std::vector> items; - items.reserve(32); - + std::list> items; uint16_t count = 0; for (auto &item : *tileItems) { if (item->getID() == ITEM_BATHTUB_FILLED_NOTMOVABLE) { std::shared_ptr tub = Item::CreateItem(ITEM_BATHTUB_FILLED); - items.emplace_back(tub); + items.push_front(tub); ++count; continue; } else if (!item->isSavedToHouses()) { continue; } - items.emplace_back(item); + items.push_front(item); ++count; } @@ -264,7 +262,7 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, std::shared_ptr til stream.write(tilePosition.z); stream.write(count); - for (std::shared_ptr item : items) { + for (const std::shared_ptr &item : items) { saveItem(stream, item); } } diff --git a/src/items/bed.cpp b/src/items/bed.cpp index c6215a80a2b..e40a0f85e0f 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -169,7 +169,7 @@ bool BedItem::sleep(std::shared_ptr player) { g_game().setBedSleeper(static_self_cast(), player->getGUID()); // make the player walk onto the bed - g_dispatcher().addWalkEvent([=] { + g_dispatcher().addWalkEvent([player, this] { g_game().map.moveCreature(player, getTile()); }); diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index f9808e8f912..822d2099df2 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -1004,19 +1004,19 @@ void ItemParse::parseReflectDamage(const std::string &tmpStrValue, pugi::xml_att } } -void ItemParse::parseTransformOnUse(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { +void ItemParse::parseTransformOnUse(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { if (tmpStrValue == "transformonuse") { itemType.m_transformOnUse = pugi::cast(valueAttribute.value()); } } -void ItemParse::parsePrimaryType(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { +void ItemParse::parsePrimaryType(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { if (tmpStrValue == "primarytype") { itemType.m_primaryType = asLowerCaseString(valueAttribute.as_string()); } } -void ItemParse::parseHouseRelated(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { +void ItemParse::parseHouseRelated(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { if (tmpStrValue == "usedbyhouseguests") { g_logger().debug("[{}] item {}, used by guests {}", __FUNCTION__, itemType.id, valueAttribute.as_bool()); itemType.m_canBeUsedByGuests = valueAttribute.as_bool(); @@ -1260,7 +1260,7 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri } } -void ItemParse::parseUnscriptedItems(const std::string_view &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { +void ItemParse::parseUnscriptedItems(std::string_view tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { if (tmpStrValue == "script") { std::string scriptName = valueAttribute.as_string(); auto tokens = split(scriptName.data(), ';'); diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index e9f6133eb3a..fb891e58ed6 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -320,10 +320,10 @@ class ItemParse : public Items { static void parsePerfecShot(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseCleavePercent(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); static void parseReflectDamage(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); - static void parseTransformOnUse(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); - static void parsePrimaryType(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); - static void parseHouseRelated(const std::string_view &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); - static void parseUnscriptedItems(const std::string_view &tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parseTransformOnUse(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parsePrimaryType(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parseHouseRelated(std::string_view tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType); + static void parseUnscriptedItems(std::string_view tmpStrValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType); private: // Parent of the function: static void parseField diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index badac532951..7968e5cd705 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -33,7 +33,7 @@ enum Attr_ReadValue { ATTR_READ_END, }; -enum ReturnValue { +enum ReturnValue : uint16_t { RETURNVALUE_NOERROR, RETURNVALUE_NOTBOUGHTINSTORE, RETURNVALUE_ITEMCANNOTBEMOVEDTHERE, diff --git a/src/kv/kv.hpp b/src/kv/kv.hpp index fa32461b24b..c00a0fd4204 100644 --- a/src/kv/kv.hpp +++ b/src/kv/kv.hpp @@ -20,7 +20,6 @@ #include #endif -#include "lib/logging/logger.hpp" #include "kv/value_wrapper.hpp" class KV : public std::enable_shared_from_this { diff --git a/src/kv/kv_sql.cpp b/src/kv/kv_sql.cpp index 0391425884c..18f3c7a3205 100644 --- a/src/kv/kv_sql.cpp +++ b/src/kv/kv_sql.cpp @@ -10,7 +10,6 @@ #include "kv/kv_sql.hpp" #include "database/database.hpp" -#include "lib/logging/logger.hpp" #include "kv/value_wrapper_proto.hpp" #include "utils/tools.hpp" diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 55709c59ac3..d24f72347fc 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${PROJECT_NAME}_lib PRIVATE di/soft_singleton.cpp + logging/logger.cpp logging/log_with_spd_log.cpp thread/thread_pool.cpp ) diff --git a/src/lib/di/soft_singleton.hpp b/src/lib/di/soft_singleton.hpp index 12d90aa3bea..b64df1c77b6 100644 --- a/src/lib/di/soft_singleton.hpp +++ b/src/lib/di/soft_singleton.hpp @@ -9,7 +9,6 @@ #pragma once #include -#include "lib/logging/log_with_spd_log.hpp" class SoftSingleton { public: diff --git a/src/lib/logging/log_with_spd_log.cpp b/src/lib/logging/log_with_spd_log.cpp index a1893e3d501..6e6f8a298ca 100644 --- a/src/lib/logging/log_with_spd_log.cpp +++ b/src/lib/logging/log_with_spd_log.cpp @@ -6,8 +6,8 @@ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors * Website: https://docs.opentibiabr.com/ */ -#include +#include #include "lib/di/container.hpp" LogWithSpdLog::LogWithSpdLog() { @@ -23,17 +23,39 @@ Logger &LogWithSpdLog::getInstance() { return inject(); } -void LogWithSpdLog::setLevel(const std::string &name) { +void LogWithSpdLog::setLevel(const std::string &name) const { debug("Setting log level to: {}.", name); - auto level = spdlog::level::from_str(name); + const auto level = spdlog::level::from_str(name); spdlog::set_level(level); } std::string LogWithSpdLog::getLevel() const { - auto level = spdlog::level::to_string_view(spdlog::get_level()); + const auto level = spdlog::level::to_string_view(spdlog::get_level()); return std::string { level.begin(), level.end() }; } -void LogWithSpdLog::log(const std::string &lvl, const fmt::basic_string_view msg) const { - spdlog::log(spdlog::level::from_str(lvl), msg); +void LogWithSpdLog::info(const std::string &msg) const { + SPDLOG_INFO(msg); +} + +void LogWithSpdLog::warn(const std::string &msg) const { + SPDLOG_WARN(msg); } + +void LogWithSpdLog::error(const std::string &msg) const { + SPDLOG_ERROR(msg); +} + +void LogWithSpdLog::critical(const std::string &msg) const { + SPDLOG_CRITICAL(msg); +} + +#if defined(DEBUG_LOG) +void LogWithSpdLog::debug(const std::string &msg) const { + SPDLOG_DEBUG(msg); +} + +void LogWithSpdLog::trace(const std::string &msg) const { + SPDLOG_TRACE(msg); +} +#endif diff --git a/src/lib/logging/log_with_spd_log.hpp b/src/lib/logging/log_with_spd_log.hpp index 983ee716c0d..84dc09a1ea3 100644 --- a/src/lib/logging/log_with_spd_log.hpp +++ b/src/lib/logging/log_with_spd_log.hpp @@ -17,10 +17,37 @@ class LogWithSpdLog final : public Logger { static Logger &getInstance(); - void setLevel(const std::string &name) override; + void setLevel(const std::string &name) const override; std::string getLevel() const override; - void log(const std::string &lvl, fmt::basic_string_view msg) const override; + void info(const std::string &msg) const override; + void warn(const std::string &msg) const override; + void error(const std::string &msg) const override; + void critical(const std::string &msg) const override; + +#if defined(DEBUG_LOG) + void debug(const std::string &msg) const override; + void trace(const std::string &msg) const override; + + template + void debug(const fmt::format_string &fmt, Args &&... args) const { + debug(fmt::format(fmt, std::forward(args)...)); + } + + template + void trace(const fmt::format_string &fmt, Args &&... args) const { + trace(fmt::format(fmt, std::forward(args)...)); + } +#else + void debug(const std::string &) const override { } + void trace(const std::string &) const override { } + + template + void debug(const fmt::format_string &, Args &&...) const { } + + template + void trace(const fmt::format_string &, Args &&...) const { } +#endif }; constexpr auto g_logger = LogWithSpdLog::getInstance; diff --git a/src/lib/logging/logger.cpp b/src/lib/logging/logger.cpp new file mode 100644 index 00000000000..b9a3c889d18 --- /dev/null +++ b/src/lib/logging/logger.cpp @@ -0,0 +1,74 @@ +/** + * 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 +#include +#include +#include "lib/di/container.hpp" + +void Logger::setLevel(const std::string &name) const { + debug("Setting log level to: {}.", name); + const auto level = spdlog::level::from_str(name); + spdlog::set_level(level); +} + +std::string Logger::getLevel() const { + const auto level = spdlog::level::to_string_view(spdlog::get_level()); + return std::string { level.begin(), level.end() }; +} + +void Logger::logProfile(const std::string &name, double duration_ms) const { + std::string mutable_name = name; + + std::ranges::replace(mutable_name, ':', '_'); + std::ranges::replace(mutable_name, '\\', '_'); + std::ranges::replace(mutable_name, '/', '_'); + + std::string filename = "log/profile_log-" + mutable_name + ".txt"; + + const auto it = profile_loggers_.find(filename); + if (it == profile_loggers_.end()) { + try { + auto file_sink = std::make_shared(filename, true); + const auto profile_logger = std::make_shared(mutable_name, file_sink); + profile_loggers_[filename] = profile_logger; + profile_logger->info("Function {} executed in {} ms", name, duration_ms); + } catch (const spdlog::spdlog_ex &ex) { + error("Profile log initialization failed: {}", ex.what()); + } + } else { + it->second->info("Function {} executed in {} ms", mutable_name, duration_ms); + } +} + +void Logger::info(const std::string &msg) const { + SPDLOG_INFO(msg); +} + +void Logger::warn(const std::string &msg) const { + SPDLOG_WARN(msg); +} + +void Logger::error(const std::string &msg) const { + SPDLOG_ERROR(msg); +} + +void Logger::critical(const std::string &msg) const { + SPDLOG_CRITICAL(msg); +} + +#if defined(DEBUG_LOG) +void Logger::debug(const std::string &msg) const { + SPDLOG_DEBUG(msg); +} + +void Logger::trace(const std::string &msg) const { + SPDLOG_TRACE(msg); +} +#endif diff --git a/src/lib/logging/logger.hpp b/src/lib/logging/logger.hpp index bc6c455fbde..05c4ba751e5 100644 --- a/src/lib/logging/logger.hpp +++ b/src/lib/logging/logger.hpp @@ -8,34 +8,11 @@ */ #pragma once -#ifndef USE_PRECOMPILED_HEADERS - #include -#endif +#include "utils/transparent_string_hash.hpp" -#define LOG_LEVEL_TRACE \ - std::string { \ - "trace" \ - } -#define LOG_LEVEL_DEBUG \ - std::string { \ - "debug" \ - } -#define LOG_LEVEL_INFO \ - std::string { \ - "info" \ - } -#define LOG_LEVEL_WARNING \ - std::string { \ - "warning" \ - } -#define LOG_LEVEL_ERROR \ - std::string { \ - "error" \ - } -#define LOG_LEVEL_CRITICAL \ - std::string { \ - "critical" \ - } +namespace spdlog { + class logger; +} class Logger { public: @@ -46,67 +23,121 @@ class Logger { Logger(const Logger &) = delete; virtual Logger &operator=(const Logger &) = delete; - virtual void setLevel(const std::string &name) = 0; - [[nodiscard]] virtual std::string getLevel() const = 0; - virtual void log(const std::string &lvl, fmt::basic_string_view msg) const = 0; + virtual void setLevel(const std::string &name) const = 0; + virtual std::string getLevel() const = 0; + + /** + * @brief Logs the execution time of a given operation to a profile log file. + * + * This function records the duration of a named operation in a log file specific + * to that operation. If the log file doesn't exist, it creates a new one. + * The log file name is derived from the provided operation name. + * + * @param name Name of the operation to profile. + * @param duration_ms Execution duration in milliseconds. + * + * Example usage: + * @code + * class ExampleClass { + * public: + * void run() { + * g_logger().profile("quickTask", [this]() { + * quickTask(); + * }); + * } + * + * private: + * void quickTask() { + * std::this_thread::sleep_for(std::chrono::milliseconds(100)); + * } + * }; + * @endcode + */ + void logProfile(const std::string &name, double duration_ms) const; + + virtual void info(const std::string &msg) const; + virtual void warn(const std::string &msg) const; + virtual void error(const std::string &msg) const; + virtual void critical(const std::string &msg) const; + + template + auto profile(const std::string &name, Func func) -> decltype(func()) { + const auto start = std::chrono::high_resolution_clock::now(); + auto result = func(); + const auto end = std::chrono::high_resolution_clock::now(); + + const std::chrono::duration duration = end - start; + logProfile(name, duration.count()); + info("Function {} executed in {} ms", name, duration.count()); + + return result; + } + +#if defined(DEBUG_LOG) + virtual void debug(const std::string &msg) const; template - void trace(const fmt::format_string &fmt, Args &&... args) { - trace(fmt::format(fmt, std::forward(args)...)); + void debug(const fmt::format_string &fmt, Args &&... args) const { + debug(fmt::format(fmt, std::forward(args)...)); } + virtual void trace(const std::string &msg) const; + template - void debug(const fmt::format_string &fmt, Args &&... args) { - debug(fmt::format(fmt, std::forward(args)...)); + void trace(const fmt::format_string &fmt, Args &&... args) const { + trace(fmt::format(fmt, std::forward(args)...)); } +#else + virtual void debug(const std::string &) const { } + + template + void debug(const fmt::format_string &, Args &&...) const { } + + virtual void trace(const std::string &) const { } template - void info(fmt::format_string fmt, Args &&... args) { + void trace(const fmt::format_string &, Args &&...) const { } +#endif + + template + void info(const fmt::format_string &fmt, Args &&... args) const { info(fmt::format(fmt, std::forward(args)...)); } template - void warn(const fmt::format_string &fmt, Args &&... args) { + void warn(const fmt::format_string &fmt, Args &&... args) const { warn(fmt::format(fmt, std::forward(args)...)); } template - void error(const fmt::format_string fmt, Args &&... args) { + void error(const fmt::format_string &fmt, Args &&... args) const { error(fmt::format(fmt, std::forward(args)...)); } template - void critical(const fmt::format_string fmt, Args &&... args) { + void critical(const fmt::format_string &fmt, Args &&... args) const { critical(fmt::format(fmt, std::forward(args)...)); } - template - void trace(const T &msg) { - log(LOG_LEVEL_TRACE, msg); - } +private: + mutable std::unordered_map< + std::string, + std::shared_ptr, + TransparentStringHasher, + std::equal_to<>> + profile_loggers_; - template - void debug(const T &msg) { - log(LOG_LEVEL_DEBUG, msg); - } + std::tm get_local_time() const { + const auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::tm local_tm {}; - template - void info(const T &msg) { - log(LOG_LEVEL_INFO, msg); - } - - template - void warn(const T &msg) { - log(LOG_LEVEL_WARNING, msg); - } - - template - void error(const T &msg) { - log(LOG_LEVEL_ERROR, msg); - } +#if defined(_WIN32) || defined(_WIN64) + localtime_s(&local_tm, &now_time); +#else + localtime_r(&now_time, &local_tm); +#endif - template - void critical(const T &msg) { - log(LOG_LEVEL_CRITICAL, msg); + return local_tm; } }; diff --git a/src/lib/metrics/metrics.cpp b/src/lib/metrics/metrics.cpp index f005c79c966..cf11060125a 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(const std::string_view &name, const std::string &histogramName, const std::string &scopeKey) : +ScopedLatency::ScopedLatency(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 1e3dbe31086..2a6a1e5838f 100644 --- a/src/lib/metrics/metrics.hpp +++ b/src/lib/metrics/metrics.hpp @@ -54,8 +54,8 @@ namespace metrics { class ScopedLatency { public: - 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 &histogram, std::map attrs = {}, opentelemetry::context::Context context = opentelemetry::context::Context()) : + explicit ScopedLatency(std::string_view name, const std::string &histogramName, const std::string &scopeKey); + explicit ScopedLatency([[maybe_unused]] std::string_view name, Histogram &histogram, std::map attrs = {}, opentelemetry::context::Context context = opentelemetry::context::Context()) : begin(std::chrono::steady_clock::now()), histogram(histogram), attrs(std::move(attrs)), context(std::move(context)) { } diff --git a/src/lib/thread/thread_pool.cpp b/src/lib/thread/thread_pool.cpp index 9a44c0d6dbe..d67fd63b7d9 100644 --- a/src/lib/thread/thread_pool.cpp +++ b/src/lib/thread/thread_pool.cpp @@ -33,7 +33,17 @@ void ThreadPool::start() { } void ThreadPool::shutdown() { + if (stopped) { + return; + } + logger.info("Shutting down thread pool..."); - stopped = true; + { + std::unique_lock lock(mutex); + stopped = true; + condition.notify_all(); + } + wait(); + logger.info("Thread pool shutdown complete."); } diff --git a/src/lib/thread/thread_pool.hpp b/src/lib/thread/thread_pool.hpp index ea24d3486cb..69084090308 100644 --- a/src/lib/thread/thread_pool.hpp +++ b/src/lib/thread/thread_pool.hpp @@ -6,9 +6,9 @@ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors * Website: https://docs.opentibiabr.com/ */ + #pragma once -#include "lib/logging/logger.hpp" #include "BS_thread_pool.hpp" class ThreadPool : public BS::thread_pool { @@ -17,7 +17,7 @@ class ThreadPool : public BS::thread_pool { // Ensures that we don't accidentally copy it ThreadPool(const ThreadPool &) = delete; - ThreadPool operator=(const ThreadPool &) = delete; + ThreadPool &operator=(const ThreadPool &) = delete; void start(); void shutdown(); @@ -32,13 +32,16 @@ class ThreadPool : public BS::thread_pool { } return id; - }; + } bool isStopped() const { return stopped; } private: + std::mutex mutex; + std::condition_variable condition; + Logger &logger; bool stopped = false; }; diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index ae07bfe6765..4dbf829d3c5 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -23,7 +23,7 @@ bool TalkActions::registerLuaEvent(const TalkAction_ptr &talkAction) { return inserted; } -bool TalkActions::checkWord(std::shared_ptr player, SpeakClasses type, const std::string &words, const std::string_view &word, const TalkAction_ptr &talkActionPtr) const { +bool TalkActions::checkWord(std::shared_ptr player, SpeakClasses type, const std::string &words, std::string_view word, const TalkAction_ptr &talkActionPtr) const { auto spacePos = std::ranges::find_if(words.begin(), words.end(), ::isspace); std::string firstWord = words.substr(0, spacePos - words.begin()); diff --git a/src/lua/creature/talkaction.hpp b/src/lua/creature/talkaction.hpp index d4fd6165459..50ade58172d 100644 --- a/src/lua/creature/talkaction.hpp +++ b/src/lua/creature/talkaction.hpp @@ -82,7 +82,7 @@ class TalkActions final : public Scripts { return inject(); } - bool checkWord(std::shared_ptr player, SpeakClasses type, const std::string &words, const std::string_view &word, const TalkAction_ptr &talkActionPtr) const; + bool checkWord(std::shared_ptr player, SpeakClasses type, const std::string &words, std::string_view word, const TalkAction_ptr &talkActionPtr) const; TalkActionResult_t checkPlayerCanSayTalkAction(std::shared_ptr player, SpeakClasses type, const std::string &words) const; bool registerLuaEvent(const TalkAction_ptr &talkAction); diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 73516ce53de..064bdd166e1 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -699,7 +699,7 @@ int GameFunctions::luaGameGetInfluencedMonsters(lua_State* L) { const auto monsters = g_game().getInfluencedMonsters(); lua_createtable(L, static_cast(monsters.size()), 0); int index = 0; - for (const auto &monsterId : monsters) { + for (const auto monsterId : monsters) { ++index; lua_pushnumber(L, monsterId); lua_rawseti(L, -2, index); @@ -763,7 +763,7 @@ int GameFunctions::luaGameGetFiendishMonsters(lua_State* L) { lua_createtable(L, static_cast(monsters.size()), 0); int index = 0; - for (const auto &monsterId : monsters) { + for (const auto monsterId : monsters) { ++index; lua_pushnumber(L, monsterId); lua_rawseti(L, -2, index); diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index 79ecb29b957..18fdce42fdb 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -901,236 +901,3 @@ int SpellFunctions::luaSpellCheckFloor(lua_State* L) { } return 1; } - -// Wheel of destiny -int SpellFunctions::luaSpellManaWOD(lua_State* L) { - // spell:manaWOD(grade, mana) - const auto spell = getUserdataShared(L, 1); - WheelSpellGrade_t grade = getNumber(L, 2); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellCooldownWOD(lua_State* L) { - // spell:cooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellGroupCooldownWOD(lua_State* L) { - // spell:groupCooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellSecondaryGroupCooldownWOD(lua_State* L) { - // spell:secondaryGroupCooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseManaLeechWOD(lua_State* L) { - // spell:increaseManaLeechWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade)); - } else { - int32_t value = getNumber(L, 3); - if (value > 0) { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 100); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 0); - } - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade, value); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaselifeLeechWOD(lua_State* L) { - // spell:increaselifeLeechWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade)); - } else { - int32_t value = getNumber(L, 3); - if (value > 0) { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 100); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 0); - } - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade, value); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseDamageWOD(lua_State* L) { - // spell:increaseDamageWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseDamageReductionWOD(lua_State* L) { - // spell:increaseDamageReductionWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseHealWOD(lua_State* L) { - // spell:increaseHealWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseCriticalDamageWOD(lua_State* L) { - // spell:increaseCriticalDamageWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseCriticalChanceWOD(lua_State* L) { - // spell:increaseCriticalChanceWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} diff --git a/src/lua/functions/creatures/combat/spell_functions.hpp b/src/lua/functions/creatures/combat/spell_functions.hpp index 7f2487ba16f..ac28d9e5232 100644 --- a/src/lua/functions/creatures/combat/spell_functions.hpp +++ b/src/lua/functions/creatures/combat/spell_functions.hpp @@ -59,19 +59,6 @@ class SpellFunctions final : LuaScriptInterface { registerMethod(L, "Spell", "allowFarUse", SpellFunctions::luaSpellAllowFarUse); registerMethod(L, "Spell", "blockWalls", SpellFunctions::luaSpellBlockWalls); registerMethod(L, "Spell", "checkFloor", SpellFunctions::luaSpellCheckFloor); - - // Wheel of destiny - registerMethod(L, "Spell", "manaWOD", SpellFunctions::luaSpellManaWOD); - registerMethod(L, "Spell", "cooldownWOD", SpellFunctions::luaSpellCooldownWOD); - registerMethod(L, "Spell", "groupCooldownWOD", SpellFunctions::luaSpellGroupCooldownWOD); - registerMethod(L, "Spell", "secondaryGroupCooldownWOD", SpellFunctions::luaSpellSecondaryGroupCooldownWOD); - registerMethod(L, "Spell", "increaseManaLeechWOD", SpellFunctions::luaSpellIncreaseManaLeechWOD); - registerMethod(L, "Spell", "increaselifeLeechWOD", SpellFunctions::luaSpellIncreaselifeLeechWOD); - registerMethod(L, "Spell", "increaseDamageWOD", SpellFunctions::luaSpellIncreaseDamageWOD); - registerMethod(L, "Spell", "increaseDamageReductionWOD", SpellFunctions::luaSpellIncreaseDamageReductionWOD); - registerMethod(L, "Spell", "increaseHealWOD", SpellFunctions::luaSpellIncreaseHealWOD); - registerMethod(L, "Spell", "increaseCriticalDamageWOD", SpellFunctions::luaSpellIncreaseCriticalDamageWOD); - registerMethod(L, "Spell", "increaseCriticalChanceWOD", SpellFunctions::luaSpellIncreaseCriticalChanceWOD); } private: @@ -119,16 +106,4 @@ class SpellFunctions final : LuaScriptInterface { static int luaSpellAllowFarUse(lua_State* L); static int luaSpellBlockWalls(lua_State* L); static int luaSpellCheckFloor(lua_State* L); - - static int luaSpellManaWOD(lua_State* L); - static int luaSpellCooldownWOD(lua_State* L); - static int luaSpellGroupCooldownWOD(lua_State* L); - static int luaSpellSecondaryGroupCooldownWOD(lua_State* L); - static int luaSpellIncreaseManaLeechWOD(lua_State* L); - static int luaSpellIncreaselifeLeechWOD(lua_State* L); - static int luaSpellIncreaseDamageWOD(lua_State* L); - static int luaSpellIncreaseDamageReductionWOD(lua_State* L); - static int luaSpellIncreaseHealWOD(lua_State* L); - static int luaSpellIncreaseCriticalDamageWOD(lua_State* L); - static int luaSpellIncreaseCriticalChanceWOD(lua_State* L); }; diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 81add18e516..a01f6d9db7e 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -74,7 +74,7 @@ enum CreatureEventType_t { CREATURE_EVENT_EXTENDED_OPCODE, }; -enum MoveEvent_t { +enum MoveEvent_t : uint8_t { MOVE_EVENT_STEP_IN, MOVE_EVENT_STEP_OUT, MOVE_EVENT_EQUIP, diff --git a/src/lua/scripts/luascript.hpp b/src/lua/scripts/luascript.hpp index 06654c35a21..a376babedde 100644 --- a/src/lua/scripts/luascript.hpp +++ b/src/lua/scripts/luascript.hpp @@ -9,7 +9,6 @@ #pragma once -#include "lib/logging/log_with_spd_log.hpp" #include "lua/functions/lua_functions_loader.hpp" #include "lua/scripts/script_environment.hpp" diff --git a/src/main.cpp b/src/main.cpp index a2a77bd636c..8534bbb6911 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,26 +9,7 @@ #include "canary_server.hpp" #include "lib/di/container.hpp" -// Define um conceito para garantir que a função só aceite contêineres de números -template -concept NumberContainer = requires(T a) { - typename T::value_type; - requires std::integral || std::floating_point; -}; - -// Função que calcula a média de um contêiner de números -auto calculateAverage(const NumberContainer auto &container) { - // Utiliza ranges e std::views para processar o contêiner - return std::accumulate(container.begin(), container.end(), 0.0) / std::ranges::distance(container); -} int main() { - std::vector numbers = { 1, 2, 3, 4, 5 }; - - // Calcula a média usando a função com suporte a C++23 - double average = calculateAverage(numbers); - - std::cout << "Average: " << average << std::endl; - return inject().run(); } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index e924b489c25..ae67bad447f 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5595,14 +5595,23 @@ void ProtocolGame::parseForgeEnter(NetworkMessage &msg) { } // 0xBF -> 0 = fusion, 1 = transfer, 2 = dust to sliver, 3 = sliver to core, 4 = increase dust limit - auto actionType = static_cast(msg.getByte()); - bool convergence = msg.getByte(); - uint16_t firstItem = msg.get(); - uint8_t tier = msg.getByte(); - uint16_t secondItem = msg.get(); - bool usedCore = msg.getByte(); - bool reduceTierLoss = msg.getByte(); + const auto actionType = static_cast(msg.getByte()); + + bool convergence = false; + uint16_t firstItem = 0; + uint8_t tier = 0; + uint16_t secondItem = 0; + + if (actionType == ForgeAction_t::FUSION || actionType == ForgeAction_t::TRANSFER) { + convergence = msg.getByte(); + firstItem = msg.get(); + tier = msg.getByte(); + secondItem = msg.get(); + } + if (actionType == ForgeAction_t::FUSION) { + const bool usedCore = convergence ? false : msg.getByte(); + const bool reduceTierLoss = convergence ? false : msg.getByte(); g_game().playerForgeFuseItems(player->getID(), actionType, firstItem, tier, secondItem, usedCore, reduceTierLoss, convergence); } else if (actionType == ForgeAction_t::TRANSFER) { g_game().playerForgeTransferItemTier(player->getID(), actionType, firstItem, tier, secondItem, convergence); diff --git a/src/server/server.hpp b/src/server/server.hpp index 42134f765b0..dfb6aef5cf8 100644 --- a/src/server/server.hpp +++ b/src/server/server.hpp @@ -9,7 +9,6 @@ #pragma once -#include "lib/logging/logger.hpp" #include "lib/metrics/metrics.hpp" #include "server/network/connection/connection.hpp" #include "server/signals.hpp" diff --git a/src/server/server_definitions.hpp b/src/server/server_definitions.hpp index 79dd89df511..2a8d55e2c89 100644 --- a/src/server/server_definitions.hpp +++ b/src/server/server_definitions.hpp @@ -67,6 +67,8 @@ enum Resource_t : uint8_t { RESOURCE_LESSER_GEMS = 0x51, RESOURCE_REGULAR_GEMS = 0x52, RESOURCE_GREATER_GEMS = 0x53, + RESOURCE_LESSER_FRAGMENT = 0x54, + RESOURCE_GREATER_FRAGMENT = 0x55, RESOURCE_WHEEL_OF_DESTINY = 0x56 }; diff --git a/src/utils/pugicast.cpp b/src/utils/pugicast.cpp index 2269e62b1fb..5791a8383a8 100644 --- a/src/utils/pugicast.cpp +++ b/src/utils/pugicast.cpp @@ -7,8 +7,6 @@ * Website: https://docs.opentibiabr.com/ */ -#include "lib/logging/log_with_spd_log.hpp" - namespace pugi { void logError(const std::string &str) { g_logger().error(str); diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index df340a67f39..0037651f21c 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "utils/tools.hpp" + #include "core.hpp" #include "items/item.hpp" -#include "utils/tools.hpp" +#include "enums/object_category.hpp" void printXMLError(const std::string &where, const std::string &fileName, const pugi::xml_parse_result &result) { g_logger().error("[{}] Failed to load {}: {}", where, fileName, result.description()); @@ -185,7 +187,7 @@ std::string transformToSHA1(const std::string &input) { return std::string(hexstring, 40); } -uint16_t getStashSize(StashItemList itemList) { +uint16_t getStashSize(const std::map &itemList) { uint16_t size = 0; for (auto item : itemList) { size += ceil(item.second / (float_t)Item::items[item.first].stackSize); @@ -1906,6 +1908,39 @@ unsigned int getNumberOfCores() { return cores; } +Cipbia_Elementals_t getCipbiaElement(CombatType_t combatType) { + switch (combatType) { + case COMBAT_PHYSICALDAMAGE: + return CIPBIA_ELEMENTAL_PHYSICAL; + case COMBAT_ENERGYDAMAGE: + return CIPBIA_ELEMENTAL_ENERGY; + case COMBAT_EARTHDAMAGE: + return CIPBIA_ELEMENTAL_EARTH; + case COMBAT_FIREDAMAGE: + return CIPBIA_ELEMENTAL_FIRE; + case COMBAT_LIFEDRAIN: + return CIPBIA_ELEMENTAL_LIFEDRAIN; + case COMBAT_HEALING: + return CIPBIA_ELEMENTAL_HEALING; + case COMBAT_DROWNDAMAGE: + return CIPBIA_ELEMENTAL_DROWN; + case COMBAT_ICEDAMAGE: + return CIPBIA_ELEMENTAL_ICE; + case COMBAT_HOLYDAMAGE: + return CIPBIA_ELEMENTAL_HOLY; + case COMBAT_DEATHDAMAGE: + return CIPBIA_ELEMENTAL_DEATH; + case COMBAT_MANADRAIN: + return CIPBIA_ELEMENTAL_MANADRAIN; + case COMBAT_AGONYDAMAGE: + return CIPBIA_ELEMENTAL_AGONY; + case COMBAT_NEUTRALDAMAGE: + return CIPBIA_ELEMENTAL_AGONY; + default: + return CIPBIA_ELEMENTAL_UNDEFINED; + } +} + /** * @brief Formats a number to a string with commas * @param number The number to format diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 7e04132b36e..e6acddd43f2 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -9,16 +9,33 @@ #pragma once -#include "utils/utils_definitions.hpp" -#include "declarations.hpp" -#include "enums/item_attribute.hpp" -#include "game/movement/position.hpp" -#include "enums/object_category.hpp" - namespace pugi { class xml_parse_result; } +struct Position; + +enum CombatType_t : uint8_t; +enum Direction : uint8_t; +enum MagicEffectClasses : uint16_t; +enum ShootType_t : uint8_t; +enum Ammo_t : uint8_t; +enum WeaponAction_t : uint8_t; +enum Skulls_t : uint8_t; +enum ImbuementTypes_t : int64_t; +enum SpawnType_t : uint8_t; +enum WeaponType_t : uint8_t; +enum MoveEvent_t : uint8_t; +enum NameEval_t : uint8_t; +enum BedItemPart_t : uint8_t; +enum ObjectCategory_t : uint8_t; +enum ItemAttribute_t : uint64_t; +enum ReturnValue : uint16_t; +enum SpellGroup_t : uint8_t; +enum Cipbia_Elementals_t : uint8_t; +enum PlayerPronoun_t : uint8_t; +enum PlayerSex_t : uint8_t; + #ifndef USE_PRECOMPILED_HEADERS #include #endif @@ -27,7 +44,7 @@ void printXMLError(const std::string &where, const std::string &fileName, const std::string transformToSHA1(const std::string &input); -uint16_t getStashSize(StashItemList itemList); +uint16_t getStashSize(const std::map &itemList); std::string generateToken(const std::string &secret, uint32_t ticks); @@ -160,38 +177,7 @@ std::string getFormattedTimeRemaining(uint32_t time); unsigned int getNumberOfCores(); -static inline Cipbia_Elementals_t getCipbiaElement(CombatType_t combatType) { - switch (combatType) { - case COMBAT_PHYSICALDAMAGE: - return CIPBIA_ELEMENTAL_PHYSICAL; - case COMBAT_ENERGYDAMAGE: - return CIPBIA_ELEMENTAL_ENERGY; - case COMBAT_EARTHDAMAGE: - return CIPBIA_ELEMENTAL_EARTH; - case COMBAT_FIREDAMAGE: - return CIPBIA_ELEMENTAL_FIRE; - case COMBAT_LIFEDRAIN: - return CIPBIA_ELEMENTAL_LIFEDRAIN; - case COMBAT_HEALING: - return CIPBIA_ELEMENTAL_HEALING; - case COMBAT_DROWNDAMAGE: - return CIPBIA_ELEMENTAL_DROWN; - case COMBAT_ICEDAMAGE: - return CIPBIA_ELEMENTAL_ICE; - case COMBAT_HOLYDAMAGE: - return CIPBIA_ELEMENTAL_HOLY; - case COMBAT_DEATHDAMAGE: - return CIPBIA_ELEMENTAL_DEATH; - case COMBAT_MANADRAIN: - return CIPBIA_ELEMENTAL_MANADRAIN; - case COMBAT_AGONYDAMAGE: - return CIPBIA_ELEMENTAL_AGONY; - case COMBAT_NEUTRALDAMAGE: - return CIPBIA_ELEMENTAL_AGONY; - default: - return CIPBIA_ELEMENTAL_UNDEFINED; - } -} +Cipbia_Elementals_t getCipbiaElement(CombatType_t combatType); std::string formatNumber(uint64_t number); diff --git a/src/utils/transparent_string_hash.hpp b/src/utils/transparent_string_hash.hpp new file mode 100644 index 00000000000..2092aa2cb98 --- /dev/null +++ b/src/utils/transparent_string_hash.hpp @@ -0,0 +1,24 @@ +/** + * 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 + +class TransparentStringHasher { +public: + using is_transparent = void; + size_t operator()(const std::string &key) const noexcept { + return std::hash {}(key); + } + size_t operator()(std::string_view key) const noexcept { + return std::hash {}(key); + } + size_t operator()(const char* key) const noexcept { + return std::hash {}(key); + } +}; diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index 60e9903a8c2..6eae663daca 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -17,7 +17,7 @@ enum WieldInfo_t { WIELDINFO_PREMIUM = 1 << 3, }; -enum SpawnType_t { +enum SpawnType_t : uint8_t { RESPAWN_IN_ALL = 0, RESPAWN_IN_DAY = 1, RESPAWN_IN_NIGHT = 2, @@ -631,6 +631,9 @@ enum ItemID_t : uint16_t { ITEM_BATHTUB_FILLED = 26077, ITEM_BATHTUB_FILLED_NOTMOVABLE = 26100, + ITEM_LESSER_FRAGMENT = 46625, + ITEM_GREATER_FRAGMENT = 46626, + ITEM_NONE = 0 }; diff --git a/tests/fixture/lib/logging/in_memory_logger.hpp b/tests/fixture/lib/logging/in_memory_logger.hpp index 76f0e7307d6..b45af91715e 100644 --- a/tests/fixture/lib/logging/in_memory_logger.hpp +++ b/tests/fixture/lib/logging/in_memory_logger.hpp @@ -51,7 +51,7 @@ class InMemoryLogger : public Logger { return false; } - void setLevel(const std::string &name) override { + void setLevel(const std::string &name) const override { // For the stub, setting a level might not have any behavior. // But you can implement level filtering if you like. } @@ -61,10 +61,34 @@ class InMemoryLogger : public Logger { return "DEBUG"; } - virtual void log(const std::string &lvl, const fmt::basic_string_view msg) const override { - logs.push_back({ lvl, { msg.data(), msg.size() } }); + virtual void info(const std::string &msg) const override { + logs.push_back({ "info", msg }); } + virtual void warn(const std::string &msg) const override { + logs.push_back({ "warning", msg }); + } + + virtual void error(const std::string &msg) const override { + logs.push_back({ "error", msg }); + } + + virtual void critical(const std::string &msg) const override { + logs.push_back({ "critical", msg }); + } + +#if defined(DEBUG_LOG) + virtual void debug(const std::string &msg) const override { + logs.push_back({ "debug", msg }); + } + + virtual void trace(const std::string &msg) const override { + logs.push_back({ "trace", msg }); + } +#else + virtual void debug(const std::string &) const override { } + virtual void trace(const std::string &) const override { } +#endif // Helper methods for testing size_t logCount() const { return logs.size(); diff --git a/tests/unit/utils/position_functions_test.cpp b/tests/unit/utils/position_functions_test.cpp index 82e2696322d..de7c04b0de0 100644 --- a/tests/unit/utils/position_functions_test.cpp +++ b/tests/unit/utils/position_functions_test.cpp @@ -2,6 +2,7 @@ #include +#include "game/movement/position.hpp" #include "utils/tools.hpp" using namespace boost::ut; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 135389e491d..7cbdd118ccc 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -316,6 +316,7 @@ +