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