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/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/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index cdd8af8a40f..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"
@@ -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;
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 6e79ff65274..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 {
@@ -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;
+ }
- msg.addByte(0); // Lesser gems
- msg.addByte(0); // Greater gems
+ 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;
+ }
+
+ 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 {
@@ -1268,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) {
@@ -1735,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);
}
@@ -1803,19 +2208,22 @@ void PlayerWheel::processActiveGems() {
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);
+ 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);
+ 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);
+ m_modifierContext->addStrategies(supremeModifier, grade);
}
}
@@ -1994,6 +2402,25 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons
}
}
+ const auto vocationBaseId = m_player.getVocation()->getBaseId();
+ const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId);
+
+ if (modsSupremeIt != modsSupremePositionByVocation.end()) {
+ for (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;
} else if (totalPoints >= static_cast(WheelStagePointsEnum_t::TWO)) {
@@ -2609,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")) {
@@ -2618,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);
}
}
@@ -3180,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 46968c2d7f7..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;
/*
@@ -141,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
*/
@@ -172,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;
diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp
index c23d2adf53f..8fc3783b4cb 100644
--- a/src/creatures/players/wheel/wheel_definitions.hpp
+++ b/src/creatures/players/wheel/wheel_definitions.hpp
@@ -253,6 +253,7 @@ struct PlayerWheelMethodsBonusData {
Stages stages;
Avatar avatar;
+ float momentum = 0;
float mitigation = 0;
std::vector spells;
};
diff --git a/src/creatures/players/wheel/wheel_gems.cpp b/src/creatures/players/wheel/wheel_gems.cpp
index 186a6939186..fb77643913a 100644
--- a/src/creatures/players/wheel/wheel_gems.cpp
+++ b/src/creatures/players/wheel/wheel_gems.cpp
@@ -28,184 +28,193 @@ void GemModifierSpellBonusStrategy::execute() {
m_wheel.addSpellBonus(m_spellName, m_bonus);
}
-void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier) {
+void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier, uint8_t grade) {
+ float gradeMultiplier = 1.0;
+ if (grade == 1) {
+ gradeMultiplier = 1.1;
+ } else if (grade == 2) {
+ gradeMultiplier = 1.2;
+ } else if (grade == 3) {
+ gradeMultiplier = 1.5;
+ }
+
switch (modifier) {
case WheelGemBasicModifier_t::General_PhysicalResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_PHYSICALDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_PHYSICALDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_HolyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_DeathResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_FireResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 200));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 200 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_EarthResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 200));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 200 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_IceResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 200));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 200 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 200));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 200 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_HolyResistance_DeathWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 150 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, -100));
break;
case WheelGemBasicModifier_t::General_DeathResistance_HolyWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 150 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, -100));
break;
case WheelGemBasicModifier_t::General_FireResistance_EarthResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_FireResistance_IceResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_FireResistance_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_EarthResistance_IceResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_EarthResistance_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_IceResistance_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_FireResistance_EarthWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_FireResistance_IceWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_FireResistance_EnergyWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EarthResistance_FireWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EarthResistance_IceWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EarthResistance_EnergyWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_IceResistance_EarthWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_IceResistance_FireWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_IceResistance_EnergyWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EnergyResistance_EarthWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EnergyResistance_IceWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_EnergyResistance_FireWeakness:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 300 * gradeMultiplier));
m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, -200));
break;
case WheelGemBasicModifier_t::General_ManaDrainResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 300 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_LifeDrainResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 300));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 300 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_ManaDrainResistance_LifeDrainResistance:
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 150));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_MANADRAIN, 150 * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_LIFEDRAIN, 150 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::General_MitigationMultiplier:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MITIGATION, 500));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MITIGATION, 500 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Health:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mana_FireResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mana:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Health_FireResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Health_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Health_EarthResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Health_IceResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Mixed:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, getHealthValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, getManaValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::HEALTH, WheelGemUtils::getHealthValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA, WheelGemUtils::getManaValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Capacity_FireResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Capacity_IceResistance:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
- m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 100 * gradeMultiplier));
break;
case WheelGemBasicModifier_t::Vocation_Capacity:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, getCapacityValue(m_vocation, modifier)));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CAPACITY, WheelGemUtils::getCapacityValue(m_vocation, modifier) * gradeMultiplier));
break;
default:
@@ -213,304 +222,337 @@ void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier) {
}
}
-void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier) {
+void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uint8_t grade) {
WheelSpells::Bonus bonus;
+ auto &wheelBonus = m_wheel.getBonusData();
+
+ float gradeMultiplier = 1.0;
+ if (grade == 1) {
+ gradeMultiplier = 1.1;
+ } else if (grade == 2) {
+ gradeMultiplier = 1.2;
+ } else if (grade == 3) {
+ gradeMultiplier = 1.5;
+ }
switch (modifier) {
case WheelGemSupremeModifier_t::General_Dodge:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 25));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 25 * gradeMultiplier));
break;
case WheelGemSupremeModifier_t::General_LifeLeech:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 120));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 120 * gradeMultiplier));
break;
case WheelGemSupremeModifier_t::General_ManaLeech:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 40));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 40 * gradeMultiplier));
break;
case WheelGemSupremeModifier_t::General_CriticalDamage:
- m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 150 * gradeMultiplier));
break;
case WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Green, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Green, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Green, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::SorcererDruid_UltimateHealing:
- bonus.increase.heal = 10;
+ bonus.increase.heal = 10 * gradeMultiplier;
m_strategies.push_back(std::make_unique(m_wheel, "Ultimate Healing", bonus));
break;
case WheelGemSupremeModifier_t::Knight_RevelationMastery_ExecutionersThrow:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Knight_RevelationMastery_AvatarOfSteel:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Knight_RevelationMastery_CombatMastery:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineGrenade:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Paladin_RevelationMastery_AvatarOfLight:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Paladin_RevelationMastery_DivineEmpowerment:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Druid_RevelationMastery_BlessingOfTheGrove:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Druid_RevelationMastery_AvatarOfNature:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Druid_RevelationMastery_TwinBursts:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_BeamMastery:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Red, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Red, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_AvatarOfStorm:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Purple, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Purple, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Sorcerer_RevelationMastery_DrainBody:
- m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150));
+ m_strategies.push_back(std::make_unique(m_wheel, WheelGemAffinity_t::Blue, 150 * gradeMultiplier));
+ m_wheel.addRevelationBonus(WheelGemAffinity_t::Blue, 150 * gradeMultiplier);
break;
case WheelGemSupremeModifier_t::Knight_AvatarOfSteel_Cooldown:
bonus.decrease.cooldown = 300 * 1000;
+ wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1;
m_strategies.push_back(std::make_unique(m_wheel, "Avatar of Steel", bonus));
break;
case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_Cooldown:
bonus.decrease.cooldown = 1 * 1000;
+ wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1;
m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus));
break;
case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_DamageIncrease:
- bonus.increase.damage = 25;
+ bonus.increase.damage = 25 * gradeMultiplier;
m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus));
break;
case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_CriticalExtraDamage:
- bonus.increase.criticalDamage = 8;
+ bonus.increase.criticalDamage = 8 * gradeMultiplier;
m_strategies.push_back(std::make_unique(m_wheel, "Executioner's Throw", bonus));
break;
case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_DamageIncrease:
- bonus.increase.damage = 25;
+ bonus.increase.damage = 25 * gradeMultiplier;
m_strategies.push_back(std::make_unique(m_wheel, "Fierce Berserk", bonus));
break;
case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_CriticalExtraDamage:
- bonus.increase.criticalDamage = 8;
+ bonus.increase.criticalDamage = 8 * gradeMultiplier;
m_strategies.push_back(std::make_unique(m_wheel, "Fierce Berserk", bonus));
break;
case WheelGemSupremeModifier_t::Knight_Berserk_DamageIncrease:
- bonus.increase.damage = 25;
+ bonus.increase.damage = 25 * gradeMultiplier;
m_strategies.push_back(std::make_unique