diff --git a/config.lua.dist b/config.lua.dist
index 14365b8dbd2..266a70e44f6 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -118,8 +118,10 @@ forgeCostOneSliver = 20
forgeSliverAmount = 3
forgeCoreCost = 50
forgeMaxDust = 225
-forgeFusionCost = 100
-forgeTransferCost = 100
+forgeFusionDustCost = 100
+forgeConvergenceFusionDustCost = 130
+forgeTransferDustCost = 100
+forgeConvergenceTransferCost = 160
forgeBaseSuccessRate = 50
forgeBonusSuccessRate = 15
forgeTierLossReduction = 50
@@ -131,6 +133,24 @@ forgeFiendishLimit = 3
forgeFiendishIntervalType = "hour"
forgeFiendishIntervalTime = "1"
+ruseChanceFormulaA = 0.0307576
+ruseChanceFormulaB = 0.440697
+ruseChanceFormulaC = 0.026
+
+onslaughtChanceFormulaA = 0.05
+onslaughtChanceFormulaB = 0.4
+onslaughtChanceFormulaC = 0.05
+
+momentumChanceFormulaA = 0.05
+momentumChanceFormulaB = 1.9
+momentumChanceFormulaC = 0.05
+
+transcendanceChanceFormulaA = 0.0127
+transcendanceChanceFormulaB = 0.1070
+transcendanceChanceFormulaC = 0.0073
+
+transcendanceAvatarDuration = 7000
+
-- Bestiary & Bosstiary system
-- NOTE: bestiaryKillMultiplier, multiplier value of monster killed, default 1
-- NOTE: bosstiaryKillMultiplier, multiplier value of boss killed, default 1
@@ -177,6 +197,15 @@ pvpMaxLevelDifference = 0
wheelSystemEnabled = true
wheelPointsPerLevel = 1
+-- Gem Atelier
+wheelAtelierRotateLesserCost = 125000
+wheelAtelierRotateRegularCost = 250000
+wheelAtelierRotateGreaterCost = 500000
+
+wheelAtelierRevealLesserCost = 125000
+wheelAtelierRevealRegularCost = 1000000
+wheelAtelierRevealGreaterCost = 6000000
+
-- Familiar system
-- NOTE: the time will be divided by 2 to get half the value, the familiar lasts 15 minutes by default and the cooldown of the spell is 30 minutes
-- Only change it here if you know what you are doing or to make testing easier with familiars
diff --git a/data-canary/scripts/item_classification/item_tiers.lua b/data-canary/scripts/item_classification/item_tiers.lua
index 8551624c9a2..2eb0d08bb23 100644
--- a/data-canary/scripts/item_classification/item_tiers.lua
+++ b/data-canary/scripts/item_classification/item_tiers.lua
@@ -2,33 +2,91 @@ local itemTierClassifications = {
-- Upgrade classification 1
[1] = {
-- Update tier 0
- [1] = { price = 25000, core = 1 },
+ [1] = {
+ regular = 25000,
+ core = 1,
+ },
},
-- Upgrade classification 2
[2] = {
-- Update tier 0
- [1] = { price = 750000, core = 1 },
+ [1] = {
+ regular = 750000,
+ core = 1,
+ },
-- Update tier 1
- [2] = { price = 5000000, core = 1 },
+ [2] = {
+ regular = 5000000,
+ core = 1,
+ },
},
-- Upgrade classification 3
[3] = {
- [1] = { price = 4000000, core = 1 },
- [2] = { price = 10000000, core = 1 },
- [3] = { price = 20000000, core = 2 },
+ [1] = {
+ regular = 4000000,
+ core = 1,
+ },
+ [2] = {
+ regular = 10000000,
+ core = 1,
+ },
+ [3] = {
+ regular = 20000000,
+ core = 2,
+ },
},
-- Upgrade classification 4
[4] = {
- [1] = { price = 8000000, core = 1 },
- [2] = { price = 20000000, core = 1 },
- [3] = { price = 40000000, core = 2 },
- [4] = { price = 65000000, core = 5 },
- [5] = { price = 100000000, core = 10 },
- [6] = { price = 250000000, core = 15 },
- [7] = { price = 750000000, core = 25 },
- [8] = { price = 2500000000, core = 35 },
- [9] = { price = 8000000000, core = 50 },
- [10] = { price = 15000000000, core = 65 },
+ [1] = {
+ regular = 8000000,
+ core = 1,
+ convergence = { fusion = { price = 55000000 }, transfer = { price = 65000000 } },
+ },
+ [2] = {
+ regular = 20000000,
+ core = 2,
+ convergence = { fusion = { price = 110000000 }, transfer = { price = 165000000 } },
+ },
+ [3] = {
+ regular = 40000000,
+ core = 5,
+ convergence = { fusion = { price = 170000000 }, transfer = { price = 375000000 } },
+ },
+ [4] = {
+ regular = 65000000,
+ core = 10,
+ convergence = { fusion = { price = 300000000 }, transfer = { price = 800000000 } },
+ },
+ [5] = {
+ regular = 100000000,
+ core = 15,
+ convergence = { fusion = { price = 875000000 }, transfer = { price = 2000000000 } },
+ },
+ [6] = {
+ regular = 250000000,
+ core = 25,
+ convergence = { fusion = { price = 2350000000 }, transfer = { price = 5250000000 } },
+ },
+ [7] = {
+ regular = 750000000,
+ core = 35,
+ convergence = { fusion = { price = 6950000000 }, transfer = { price = 14500000000 } },
+ },
+ [8] = {
+ regular = 2500000000,
+ core = 50,
+ convergence = { fusion = { price = 21250000000 }, transfer = { price = 42500000000 } },
+ },
+ [9] = {
+ regular = 8000000000,
+ core = 60,
+ convergence = { fusion = { price = 50000000000 }, transfer = { price = 100000000000 } },
+ },
+ [10] = {
+ regular = 15000000000,
+ core = 85,
+ convergence = { fusion = { price = 125000000000 }, transfer = { price = 300000000000 } },
+ },
},
}
@@ -40,9 +98,13 @@ for classificationId, classificationTable in ipairs(itemTierClassifications) do
-- Registers table for register_item_tier.lua interface
classification.Upgrades = {}
for tierId, tierTable in ipairs(classificationTable) do
- if tierId and tierTable.price and tierTable.core ~= nil then
- table.insert(classification.Upgrades, { TierId = tierId - 1, Price = tierTable.price, Core = tierTable.core })
- end
+ table.insert(classification.Upgrades, {
+ TierId = tierId,
+ Core = tierTable.core,
+ RegularPrice = tierTable.regular,
+ ConvergenceFustionPrice = tierTable.convergence and tierTable.convergence.fusion.price or 0,
+ ConvergenceTransferPrice = tierTable.convergence and tierTable.convergence.transfer.price or 0,
+ })
end
-- Create item classification and register classification table
itemClassification:register(classification)
diff --git a/data-otservbr-global/monster/constructs/animated_snowman.lua b/data-otservbr-global/monster/constructs/animated_snowman.lua
index 72f7c5c9dc6..6fcf9883043 100644
--- a/data-otservbr-global/monster/constructs/animated_snowman.lua
+++ b/data-otservbr-global/monster/constructs/animated_snowman.lua
@@ -78,7 +78,7 @@ monster.loot = {
{ name = "shiver arrow", chance = 7310 },
{ name = "ice rapier", chance = 4750 },
{ name = "glacier mask", chance = 4570 },
- { name = "snowball", chance = 4000, maxCount = 5 },
+ { id = 2992, chance = 4000, maxCount = 5 }, -- snowball
{ name = "hailstorm rod", chance = 3470 },
{ name = "glacier mask", chance = 250 },
{ name = "glacier amulet", chance = 3290 },
diff --git a/data-otservbr-global/monster/demons/brachiodemon.lua b/data-otservbr-global/monster/demons/brachiodemon.lua
index 88c09c550a6..930ecfe469f 100644
--- a/data-otservbr-global/monster/demons/brachiodemon.lua
+++ b/data-otservbr-global/monster/demons/brachiodemon.lua
@@ -108,6 +108,7 @@ monster.attacks = {
{ name = "combat", interval = 2000, chance = 22, type = COMBAT_DEATHDAMAGE, minDamage = -900, maxDamage = -1280, radius = 4, effect = CONST_ME_EXPLOSIONHIT, target = false },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -1150, maxDamage = -1460, range = 7, effect = CONST_ANI_SUDDENDEATH, target = true },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_DEATHDAMAGE, minDamage = -950, maxDamage = -1100, range = 7, radius = 4, shootEffect = CONST_ANI_SUDDENDEATH, effect = CONST_ME_MORTAREA, target = true },
+ { name = "destroy magic walls", interval = 1000, chance = 30 },
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/demons/many_faces.lua b/data-otservbr-global/monster/demons/many_faces.lua
index e557770d9c8..8b9724a6252 100644
--- a/data-otservbr-global/monster/demons/many_faces.lua
+++ b/data-otservbr-global/monster/demons/many_faces.lua
@@ -104,6 +104,7 @@ monster.attacks = {
{ name = "combat", interval = 5000, chance = 44, type = COMBAT_ICEDAMAGE, minDamage = -1000, maxDamage = -1450, range = 7, radius = 5, shootEffect = CONST_ANI_ICE, effect = CONST_ME_ICEAREA, target = true },
{ name = "combat", interval = 9500, chance = 59, type = COMBAT_HOLYDAMAGE, minDamage = -1050, maxDamage = -1300, radius = 4, effect = CONST_ME_HOLYAREA, target = false },
{ name = "extended holy chain", interval = 10000, chance = 59, minDamage = -1150, maxDamage = -1300, range = 7 },
+ { name = "destroy magic walls", interval = 1000, chance = 30 },
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/event_creatures/grynch_clan_goblin.lua b/data-otservbr-global/monster/event_creatures/grynch_clan_goblin.lua
index 3c852794ac8..d13e2a3d487 100644
--- a/data-otservbr-global/monster/event_creatures/grynch_clan_goblin.lua
+++ b/data-otservbr-global/monster/event_creatures/grynch_clan_goblin.lua
@@ -106,7 +106,7 @@ monster.loot = {
{ id = 2639, chance = 4000 }, -- picture
{ id = 2950, chance = 5000 }, -- lute
{ id = 2983, chance = 500 }, -- flower bowl
- { name = "snowball", chance = 7000, maxCount = 5 },
+ { id = 2992, chance = 7000, maxCount = 5 }, -- snowball
{ name = "piggy bank", chance = 1000 },
{ name = "gold coin", chance = 22500, maxCount = 22 },
{ name = "scarab coin", chance = 500, maxCount = 2 },
diff --git a/data-otservbr-global/monster/mammals/yeti.lua b/data-otservbr-global/monster/mammals/yeti.lua
index 7ef601194dc..890751bbc7d 100644
--- a/data-otservbr-global/monster/mammals/yeti.lua
+++ b/data-otservbr-global/monster/mammals/yeti.lua
@@ -76,7 +76,7 @@ monster.voices = {
}
monster.loot = {
- { name = "snowball", chance = 10000, maxCount = 22 },
+ { id = 2992, chance = 10000, maxCount = 22 }, -- snowball
{ name = "gold coin", chance = 100000, maxCount = 60 },
{ name = "gold coin", chance = 100000, maxCount = 40 },
{ name = "bunnyslippers", chance = 1333 },
diff --git a/data-otservbr-global/monster/plants/cloak_of_terror.lua b/data-otservbr-global/monster/plants/cloak_of_terror.lua
index fd9dbf2a971..184ba93fab2 100644
--- a/data-otservbr-global/monster/plants/cloak_of_terror.lua
+++ b/data-otservbr-global/monster/plants/cloak_of_terror.lua
@@ -102,6 +102,7 @@ monster.attacks = {
{ name = "combat", interval = 3000, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -1150, maxDamage = -1300, range = 7, radius = 4, effect = CONST_ME_ENERGYHIT, target = true },
{ name = "combat", interval = 2000, chance = 14, type = COMBAT_HOLYDAMAGE, minDamage = -1000, maxDamage = -1300, range = 7, shootEffect = CONST_ANI_SPECTRALBOLT, effect = CONST_ME_HOLYDAMAGE, target = true },
{ name = "combat", interval = 2000, chance = 24, type = COMBAT_HOLYDAMAGE, minDamage = -800, maxDamage = -1200, range = 7, radius = 3, shootEffect = CONST_ANI_SMALLHOLY, effect = CONST_ME_YELLOW_ENERGY_SPARK, target = true },
+ { name = "destroy magic walls", interval = 1000, chance = 30 },
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_baron_from_below.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_baron_from_below.lua
index e780ac65c8b..a381c3b07d6 100644
--- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_baron_from_below.lua
+++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_baron_from_below.lua
@@ -112,6 +112,8 @@ monster.loot = {
{ name = "slimy leg", chance = 4170 },
{ name = "badger boots", chance = 4170 },
{ name = "spellbook of warding", chance = 2080 },
+ { name = "gnome sword", chance = 4170 },
+ { name = "gnome armor", chance = 3390 },
}
monster.attacks = {
diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
index 31c354f7667..d654288c405 100644
--- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
+++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
@@ -111,6 +111,7 @@ monster.loot = {
{ name = "gnome sword", chance = 4170 },
{ name = "terra mantle", chance = 2080 },
{ name = "violet gem", chance = 2080 },
+ { name = "gnome legs", chance = 3390 },
}
monster.attacks = {
diff --git a/data-otservbr-global/monster/quests/the_inquisition/ushuriel.lua b/data-otservbr-global/monster/quests/the_inquisition/ushuriel.lua
index 35c7d81b030..6d1bb8f9945 100644
--- a/data-otservbr-global/monster/quests/the_inquisition/ushuriel.lua
+++ b/data-otservbr-global/monster/quests/the_inquisition/ushuriel.lua
@@ -93,7 +93,7 @@ monster.loot = {
{ name = "mysterious voodoo skull", chance = 12500 },
{ name = "skull helmet", chance = 20000 },
{ name = "iron ore", chance = 33333 },
- { name = "spirit container", chance = 4761 },
+ { id = 5884, chance = 4761 }, -- spirit container
{ name = "flask of warrior's sweat", chance = 5555 },
{ name = "enchanted chicken wing", chance = 7692 },
{ name = "huge chunk of crude iron", chance = 14285 },
diff --git a/data-otservbr-global/monster/reptiles/corrupt_naga.lua b/data-otservbr-global/monster/reptiles/corrupt_naga.lua
index bcdd68ee8f6..7292452428e 100644
--- a/data-otservbr-global/monster/reptiles/corrupt_naga.lua
+++ b/data-otservbr-global/monster/reptiles/corrupt_naga.lua
@@ -5,10 +5,10 @@ monster.description = "a corrupt naga"
monster.experience = 4380
monster.outfit = {
lookType = 1538,
- lookHead = 55,
- lookBody = 6,
- lookLegs = 0,
- lookFeet = 78,
+ lookHead = 86,
+ lookBody = 57,
+ lookLegs = 75,
+ lookFeet = 94,
lookAddons = 3,
lookMount = 0,
}
@@ -16,7 +16,7 @@ monster.outfit = {
monster.health = 5990
monster.maxHealth = 5990
monster.race = "blood"
-monster.corpse = 0
+monster.corpse = 39217
monster.speed = 182
monster.manaCost = 0
@@ -60,15 +60,14 @@ monster.voices = {
}
monster.loot = {
- { name = "Platinum Coin", chance = 75420, minCount = 1, maxCount = 8 },
- { name = "Violet Crystal Shard", chance = 24580, minCount = 1, maxCount = 2 },
- { name = "Corrupt Naga Scales", chance = 17720 },
+ { name = "corrupt naga scales", chance = 17720 },
}
monster.attacks = {
- { name = "combat", interval = 2000, chance = 100, minDamage = -300, maxDamage = -600, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_PURPLEENERGY, target = true },
- { name = "nagadeath", interval = 6000, chance = 39, target = false, minDamage = -1000, maxDamage = -2200 },
- { name = "nagadeathattack", interval = 3000, chance = 68, target = true, minDamage = -400, maxDamage = -600 },
+ { name = "combat", interval = 2000, chance = 100, type = COMBAT_PHYSICALDAMAGE, minDamage = -120, maxDamage = -340, target = true }, -- basic_attack
+ { name = "combat", interval = 2500, chance = 30, type = COMBAT_PHYSICALDAMAGE, minDamage = -320, maxDamage = -430, effect = CONST_ME_YELLOWSMOKE, range = 3, target = true }, -- eruption_strike
+ { name = "nagadeathattack", interval = 3000, chance = 35, minDamage = -360, maxDamage = -415, target = true }, -- death_strike
+ { name = "combat", interval = 3500, chance = 35, type = COMBAT_LIFEDRAIN, minDamage = -360, maxDamage = -386, radius = 4, effect = CONST_ME_DRAWBLOOD, target = false }, -- great_blood_ball
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/reptiles/rogue_naga.lua b/data-otservbr-global/monster/reptiles/rogue_naga.lua
index 25ec8c56ccf..e3e7e662fc3 100644
--- a/data-otservbr-global/monster/reptiles/rogue_naga.lua
+++ b/data-otservbr-global/monster/reptiles/rogue_naga.lua
@@ -5,10 +5,10 @@ monster.description = "a rogue naga"
monster.experience = 4510
monster.outfit = {
lookType = 1543,
- lookHead = 55,
- lookBody = 6,
- lookLegs = 0,
- lookFeet = 78,
+ lookHead = 75,
+ lookBody = 13,
+ lookLegs = 95,
+ lookFeet = 109,
lookAddons = 3,
lookMount = 0,
}
@@ -16,7 +16,7 @@ monster.outfit = {
monster.health = 6200
monster.maxHealth = 6200
monster.race = "blood"
-monster.corpse = 0
+monster.corpse = 39221
monster.speed = 182
monster.manaCost = 0
@@ -64,15 +64,15 @@ monster.voices = {
}
monster.loot = {
- { name = "Platinum Coin", chance = 85600, minCount = 1, maxCount = 12 },
- { name = "Rogue Naga Scales", chance = 15450 },
- { name = "Green Crystal Shard", chance = 14400, minCount = 1, maxCount = 2 },
+ { name = "rogue naga scales", chance = 15450 },
}
monster.attacks = {
- { name = "combat", interval = 2000, chance = 100, minDamage = -300, maxDamage = -600, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_PURPLEENERGY, target = true },
- { name = "combat", interval = 2000, chance = 47, type = COMBAT_PHYSICALDAMAGE, minDamage = -350, maxDamage = -400, effect = CONST_ME_BIG_SCRATCH, target = true },
- { name = "combat", interval = 1000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = -380, maxDamage = -470, length = 5, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false },
+ { name = "combat", interval = 2000, chance = 50, type = COMBAT_PHYSICALDAMAGE, minDamage = -95, maxDamage = -390, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_PURPLEENERGY, range = 6, target = true }, -- basic_attack
+ { name = "nagadeathattack", interval = 2500, chance = 20, minDamage = -430, maxDamage = -505, range = 6, target = true }, -- death_strike
+ { name = "nagadeath", interval = 3000, chance = 20, minDamage = -380, maxDamage = -470, target = false }, -- short_death_wave
+ { name = "death chain", interval = 3500, chance = 20, minDamage = -460, maxDamage = -520, range = 6, target = true }, -- death_chain
+ { name = "combat", interval = 4000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -85, maxDamage = -190, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_PURPLEENERGY, range = 6, target = true }, -- explosion_strike
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/undeads/bony_sea_devil.lua b/data-otservbr-global/monster/undeads/bony_sea_devil.lua
index 5e57027cc6b..1199240df4b 100644
--- a/data-otservbr-global/monster/undeads/bony_sea_devil.lua
+++ b/data-otservbr-global/monster/undeads/bony_sea_devil.lua
@@ -107,6 +107,7 @@ monster.attacks = {
{ name = "combat", interval = 2000, chance = 25, type = COMBAT_ICEDAMAGE, minDamage = -950, maxDamage = -1100, range = 7, radius = 5, shootEffect = CONST_ANI_ICE, effect = CONST_ME_ICEAREA, target = true },
{ name = "ice chain", interval = 2000, chance = 15, minDamage = -1100, maxDamage = -1300, range = 7 },
{ name = "soulwars fear", interval = 2000, chance = 1, target = true },
+ { name = "destroy magic walls", interval = 1000, chance = 30 },
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/undeads/souleater.lua b/data-otservbr-global/monster/undeads/souleater.lua
index 987e34523bf..dd0912cfd7c 100644
--- a/data-otservbr-global/monster/undeads/souleater.lua
+++ b/data-otservbr-global/monster/undeads/souleater.lua
@@ -83,7 +83,7 @@ monster.loot = {
{ name = "platinum coin", chance = 49610, maxCount = 6 },
{ name = "necrotic rod", chance = 980 },
{ name = "wand of cosmic energy", chance = 910 },
- { name = "spirit container", chance = 140 },
+ { id = 5884, chance = 140 }, -- spirit container
{ id = 6299, chance = 300 }, -- death ring
{ name = "great mana potion", chance = 8000 },
{ name = "ultimate health potion", chance = 9400 },
diff --git a/data-otservbr-global/npc/briasol.lua b/data-otservbr-global/npc/briasol.lua
index 1dd3a0060bf..8b0d23061b9 100644
--- a/data-otservbr-global/npc/briasol.lua
+++ b/data-otservbr-global/npc/briasol.lua
@@ -96,10 +96,11 @@ keywordHandler:addGreetKeyword({ "ashari" }, { npcHandler = npcHandler, text = "
keywordHandler:addFarewellKeyword({ "asgha thrazi" }, { npcHandler = npcHandler, text = "Good bye, |PLAYERNAME|." })
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
+
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -121,17 +122,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -148,9 +161,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/chantalle.lua b/data-otservbr-global/npc/chantalle.lua
index 5805682e961..1b80f81b8aa 100644
--- a/data-otservbr-global/npc/chantalle.lua
+++ b/data-otservbr-global/npc/chantalle.lua
@@ -82,9 +82,9 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "bar of gold", clientId = 14112, sell = 10000 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
@@ -109,19 +109,31 @@ npcConfig.shop = {
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden figurine", clientId = 5799, sell = 3000 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "scarab coin", clientId = 3042, sell = 100 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
@@ -140,9 +152,10 @@ npcConfig.shop = {
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "watermelon tourmaline", clientId = 33780, sell = 230000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/edmund.lua b/data-otservbr-global/npc/edmund.lua
index 9478a876aeb..687721458ef 100644
--- a/data-otservbr-global/npc/edmund.lua
+++ b/data-otservbr-global/npc/edmund.lua
@@ -53,9 +53,9 @@ end
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -77,17 +77,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -104,9 +116,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/gail.lua b/data-otservbr-global/npc/gail.lua
index 5478419dc04..aa65e5f5592 100644
--- a/data-otservbr-global/npc/gail.lua
+++ b/data-otservbr-global/npc/gail.lua
@@ -94,9 +94,9 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -118,17 +118,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -145,9 +157,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/giri.lua b/data-otservbr-global/npc/giri.lua
index 92947372d08..8a243b22f85 100644
--- a/data-otservbr-global/npc/giri.lua
+++ b/data-otservbr-global/npc/giri.lua
@@ -75,11 +75,22 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
@@ -88,6 +99,7 @@ npcConfig.shop = {
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "royal almandine", clientId = 39038, sell = 460000 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -106,9 +118,10 @@ npcConfig.shop = {
{ itemName = "watermelon tourmaline piece", clientId = 33779, sell = 30000 },
{ itemName = "watermelon tourmaline", clientId = 33780, sell = 230000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990, sell = 100 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320, sell = 160 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/hanna.lua b/data-otservbr-global/npc/hanna.lua
index 5a062678829..02f89d87631 100644
--- a/data-otservbr-global/npc/hanna.lua
+++ b/data-otservbr-global/npc/hanna.lua
@@ -131,9 +131,9 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -156,16 +156,28 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -181,10 +193,11 @@ npcConfig.shop = {
{ itemName = "tiger eye", clientId = 24961, sell = 350 },
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990, sell = 100 },
{ itemName = "white pearl", clientId = 3026, buy = 320, sell = 160 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/ishina.lua b/data-otservbr-global/npc/ishina.lua
index c82252356fb..455aed2dda2 100644
--- a/data-otservbr-global/npc/ishina.lua
+++ b/data-otservbr-global/npc/ishina.lua
@@ -124,9 +124,9 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -148,17 +148,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -174,10 +186,11 @@ npcConfig.shop = {
{ itemName = "tiger eye", clientId = 24961, sell = 350 },
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/iwan.lua b/data-otservbr-global/npc/iwan.lua
index f0f84ea1d65..31f593c7cb0 100644
--- a/data-otservbr-global/npc/iwan.lua
+++ b/data-otservbr-global/npc/iwan.lua
@@ -63,9 +63,9 @@ npcHandler:setMessage(MESSAGE_SENDTRADE, "Here, take a look.")
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -87,17 +87,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -113,10 +125,11 @@ npcConfig.shop = {
{ itemName = "tiger eye", clientId = 24961, sell = 350 },
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/jessica.lua b/data-otservbr-global/npc/jessica.lua
index ff0b8ba0d91..32cd5ac34fd 100644
--- a/data-otservbr-global/npc/jessica.lua
+++ b/data-otservbr-global/npc/jessica.lua
@@ -81,9 +81,9 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "bar of gold", clientId = 14112, sell = 10000 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
@@ -108,19 +108,31 @@ npcConfig.shop = {
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden figurine", clientId = 5799, sell = 3000 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "scarab coin", clientId = 3042, sell = 100 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
@@ -138,10 +150,11 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "watermelon tourmaline", clientId = 33780, sell = 230000 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/odemara.lua b/data-otservbr-global/npc/odemara.lua
index fdebc6f7449..8ab4e84152f 100644
--- a/data-otservbr-global/npc/odemara.lua
+++ b/data-otservbr-global/npc/odemara.lua
@@ -53,9 +53,9 @@ end
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "bar of gold", clientId = 14112, sell = 10000 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
@@ -80,19 +80,31 @@ npcConfig.shop = {
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden figurine", clientId = 5799, sell = 3000 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "scarab coin", clientId = 3042, sell = 100 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
@@ -110,10 +122,11 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "watermelon tourmaline", clientId = 33780, sell = 230000 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/oiriz.lua b/data-otservbr-global/npc/oiriz.lua
index 62817bd0349..38a49116949 100644
--- a/data-otservbr-global/npc/oiriz.lua
+++ b/data-otservbr-global/npc/oiriz.lua
@@ -53,9 +53,9 @@ end
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -77,17 +77,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -104,9 +116,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/talila.lua b/data-otservbr-global/npc/talila.lua
index ac14feedbe6..eff8f9b5c54 100644
--- a/data-otservbr-global/npc/talila.lua
+++ b/data-otservbr-global/npc/talila.lua
@@ -64,9 +64,9 @@ npcHandler:setMessage(MESSAGE_FAREWELL, "May enlightenment be your path, |PLAYER
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
{ itemName = "avalanche rune", clientId = 3161, buy = 57 },
@@ -121,10 +121,15 @@ npcConfig.shop = {
{ itemName = "great health potion", clientId = 239, buy = 225 },
{ itemName = "great mana potion", clientId = 238, buy = 144 },
{ itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
@@ -133,12 +138,18 @@ npcConfig.shop = {
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "leaf star", clientId = 25735, sell = 50 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
{ itemName = "magic wall rune", clientId = 3180, buy = 116 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "mandrake", clientId = 5014, sell = 5000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
@@ -153,6 +164,7 @@ npcConfig.shop = {
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "red rose", clientId = 3658, sell = 10 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "shimmering beetles", clientId = 25693, sell = 150 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
@@ -197,6 +209,7 @@ npcConfig.shop = {
{ itemName = "wood cape", clientId = 3575, sell = 5000 },
{ itemName = "wooden spellbook", clientId = 25699, sell = 12000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/tezila.lua b/data-otservbr-global/npc/tezila.lua
index f6580c6b602..ada79994ccc 100644
--- a/data-otservbr-global/npc/tezila.lua
+++ b/data-otservbr-global/npc/tezila.lua
@@ -52,9 +52,9 @@ end
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -76,17 +76,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -103,9 +115,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/valindara.lua b/data-otservbr-global/npc/valindara.lua
index 52d7762eded..dbb4ab04b7d 100644
--- a/data-otservbr-global/npc/valindara.lua
+++ b/data-otservbr-global/npc/valindara.lua
@@ -69,9 +69,9 @@ npcHandler:setMessage(MESSAGE_FAREWELL, "May enlightenment be your path, |PLAYER
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
{ itemName = "avalanche rune", clientId = 3161, buy = 57 },
@@ -126,10 +126,15 @@ npcConfig.shop = {
{ itemName = "great health potion", clientId = 239, buy = 225 },
{ itemName = "great mana potion", clientId = 238, buy = 144 },
{ itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
@@ -138,11 +143,17 @@ npcConfig.shop = {
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "leaf star", clientId = 25735, sell = 50 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "magic wall rune", clientId = 3180, buy = 116 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "mandrake", clientId = 5014, sell = 5000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate lion figurine", clientId = 33781, sell = 10000 },
@@ -158,6 +169,7 @@ npcConfig.shop = {
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "red rose", clientId = 3658, sell = 10 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "shimmering beetles", clientId = 25693, sell = 150 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
@@ -202,6 +214,7 @@ npcConfig.shop = {
{ itemName = "wood cape", clientId = 3575, sell = 5000 },
{ itemName = "wooden spellbook", clientId = 25699, sell = 12000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/npc/yonan.lua b/data-otservbr-global/npc/yonan.lua
index 1c6101ab842..b499e110254 100644
--- a/data-otservbr-global/npc/yonan.lua
+++ b/data-otservbr-global/npc/yonan.lua
@@ -24,9 +24,9 @@ npcConfig.flags = {
}
npcConfig.shop = {
- { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "amber with a bug", clientId = 32624, sell = 41000 },
{ itemName = "amber with a dragonfly", clientId = 32625, sell = 56000 },
+ { itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -48,17 +48,29 @@ npcConfig.shop = {
{ itemName = "gold nugget", clientId = 3040, sell = 850 },
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "lion figurine", clientId = 33781, sell = 10000 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 400 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "ornate locket", clientId = 30056, sell = 18000 },
{ itemName = "prismatic quartz", clientId = 24962, sell = 450 },
{ itemName = "red crystal fragment", clientId = 16126, sell = 800 },
{ itemName = "ruby necklace", clientId = 3016, buy = 3560 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "silver goblet", clientId = 5806, buy = 3000 },
{ itemName = "skull coin", clientId = 32583, sell = 12000 },
{ itemName = "small amethyst", clientId = 3033, buy = 400, sell = 200 },
@@ -75,9 +87,10 @@ npcConfig.shop = {
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
{ itemName = "wedding ring", clientId = 3004, buy = 990 },
- { itemName = "white silk flower", clientId = 34008, sell = 9000 },
{ itemName = "white pearl", clientId = 3026, buy = 320 },
+ { itemName = "white silk flower", clientId = 34008, sell = 9000 },
}
+
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks)
diff --git a/data-otservbr-global/scripts/actions/other/bag_you_covet.lua b/data-otservbr-global/scripts/actions/other/bag_you_covet.lua
index e332d9fd3f2..bb62da59372 100644
--- a/data-otservbr-global/scripts/actions/other/bag_you_covet.lua
+++ b/data-otservbr-global/scripts/actions/other/bag_you_covet.lua
@@ -27,6 +27,13 @@ function bagyouCovet.onUse(player, item, fromPosition, target, toPosition, isHot
item:remove(1)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You received a " .. rewardItem.name .. ".")
+
+ local text = player:getName() .. " received a " .. rewardItem.name .. " from a " .. item:getName() .. "."
+ local vocation = player:vocationAbbrev()
+ Webhook.sendMessage(":game_die: " .. player:getMarkdownLink() .. " received a **" .. rewardItem.name .. "** from a _" .. item:getName() .. "_.")
+ Broadcast(text, function(targetPlayer)
+ return targetPlayer ~= player
+ end)
return true
end
diff --git a/data-otservbr-global/scripts/actions/other/bag_you_desire.lua b/data-otservbr-global/scripts/actions/other/bag_you_desire.lua
index be25529c09b..6716d58eef3 100644
--- a/data-otservbr-global/scripts/actions/other/bag_you_desire.lua
+++ b/data-otservbr-global/scripts/actions/other/bag_you_desire.lua
@@ -33,6 +33,13 @@ function bagyouDesire.onUse(player, item, fromPosition, target, toPosition, isHo
item:remove(1)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You received a " .. rewardItem.name .. ".")
+
+ local text = player:getName() .. " received a " .. rewardItem.name .. " from a " .. item:getName() .. "."
+ local vocation = player:vocationAbbrev()
+ Webhook.sendMessage(":game_die: " .. player:getMarkdownLink() .. " received a **" .. rewardItem.name .. "** from a _" .. item:getName() .. "_.")
+ Broadcast(text, function(targetPlayer)
+ return targetPlayer ~= player
+ end)
return true
end
diff --git a/data-otservbr-global/scripts/actions/other/primal_bag.lua b/data-otservbr-global/scripts/actions/other/primal_bag.lua
index 5837caa08ae..ab9f44cdb78 100644
--- a/data-otservbr-global/scripts/actions/other/primal_bag.lua
+++ b/data-otservbr-global/scripts/actions/other/primal_bag.lua
@@ -26,7 +26,13 @@ function primalBag.onUse(player, item, fromPosition, target, toPosition, isHotke
player:addItem(rewardItem.id, 1)
item:remove(1)
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You received one " .. rewardItem.name .. ".")
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You received a " .. rewardItem.name .. ".")
+ local text = player:getName() .. " received a " .. rewardItem.name .. " from a " .. item:getName() .. "."
+ local vocation = player:vocationAbbrev()
+ Webhook.sendMessage(":game_die: " .. player:getMarkdownLink() .. " received a **" .. rewardItem.name .. "** from a _" .. item:getName() .. "_.")
+ Broadcast(text, function(targetPlayer)
+ return targetPlayer ~= player
+ end)
return true
end
diff --git a/data-otservbr-global/scripts/actions/quests/lions_rock/lions_rock.lua b/data-otservbr-global/scripts/actions/quests/lions_rock/lions_rock.lua
index 652ba60f1ef..dc2870c1d72 100644
--- a/data-otservbr-global/scripts/actions/quests/lions_rock/lions_rock.lua
+++ b/data-otservbr-global/scripts/actions/quests/lions_rock/lions_rock.lua
@@ -113,6 +113,9 @@ lionsGetHolyWater:register()
local lionsRockFountain = Action()
function lionsRockFountain.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if item:getId() ~= 6389 then
+ return false
+ end
if player:getStorageValue(Storage.LionsRock.Time) < os.time() then
local reward = ""
if player:hasMount(40) then
@@ -122,7 +125,8 @@ function lionsRockFountain.onUse(player, item, fromPosition, target, toPosition,
else
reward = math.random(1, #rewards)
end
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Something sparkles in the fountain's water. You draw out a " .. rewards[reward] .. ".")
+ local iType = ItemType(rewards[reward])
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Something sparkles in the fountain's water. You draw out " .. iType:getArticle() .. " " .. iType:getName() .. ".")
player:getPosition():sendMagicEffect(CONST_ME_HOLYAREA)
player:addAchievement("Lion's Den Explorer")
item:transform(lionsRockSanctuaryRockId)
@@ -135,5 +139,5 @@ function lionsRockFountain.onUse(player, item, fromPosition, target, toPosition,
return true
end
-lionsRockFountain:id(6389)
+lionsRockFountain:position({ x = 33073, y = 32300, z = 9 })
lionsRockFountain:register()
diff --git a/data-otservbr-global/scripts/actions/tibiadrome/concoctions.lua b/data-otservbr-global/scripts/actions/tibiadrome/concoctions.lua
index 8e7a23d85ea..fe17dfef06b 100644
--- a/data-otservbr-global/scripts/actions/tibiadrome/concoctions.lua
+++ b/data-otservbr-global/scripts/actions/tibiadrome/concoctions.lua
@@ -17,7 +17,7 @@ local configs = {
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your spells are no longer on cooldown.")
end,
},
- [Concoction.Ids.StrikeEnhancement] = { condition = { CONDITION_PARAM_SKILL_CRITICAL_HIT_CHANCE, 5 } },
+ [Concoction.Ids.StrikeEnhancement] = { condition = { CONDITION_PARAM_SKILL_CRITICAL_HIT_CHANCE, 500 } },
[Concoction.Ids.CharmUpgrade] = { condition = { CONDITION_PARAM_CHARM_CHANCE_MODIFIER, 5 } },
[Concoction.Ids.WealthDuplex] = { rate = 100 },
[Concoction.Ids.BestiaryBetterment] = { multiplier = 2.0 },
diff --git a/data-otservbr-global/scripts/item_classification/item_tiers.lua b/data-otservbr-global/scripts/item_classification/item_tiers.lua
index 8551624c9a2..c199f81a3fe 100644
--- a/data-otservbr-global/scripts/item_classification/item_tiers.lua
+++ b/data-otservbr-global/scripts/item_classification/item_tiers.lua
@@ -2,33 +2,115 @@ local itemTierClassifications = {
-- Upgrade classification 1
[1] = {
-- Update tier 0
- [1] = { price = 25000, core = 1 },
+ [1] = {
+ regular = 25000,
+ core = 1,
+ },
+ [2] = {
+ regular = 50000,
+ core = 1,
+ },
+ [3] = {
+ regular = 100000,
+ core = 1,
+ },
},
-- Upgrade classification 2
[2] = {
-- Update tier 0
- [1] = { price = 750000, core = 1 },
+ [1] = {
+ regular = 50000,
+ core = 1,
+ },
-- Update tier 1
- [2] = { price = 5000000, core = 1 },
+ [2] = {
+ regular = 100000,
+ core = 1,
+ },
+ [3] = {
+ regular = 200000,
+ core = 2,
+ },
+ [4] = {
+ regular = 400000,
+ core = 2,
+ },
},
-- Upgrade classification 3
[3] = {
- [1] = { price = 4000000, core = 1 },
- [2] = { price = 10000000, core = 1 },
- [3] = { price = 20000000, core = 2 },
+ [1] = {
+ regular = 200000,
+ core = 1,
+ },
+ [2] = {
+ regular = 400000,
+ core = 2,
+ },
+ [3] = {
+ regular = 800000,
+ core = 3,
+ },
+ [4] = {
+ regular = 1600000,
+ core = 4,
+ },
+ [5] = {
+ regular = 3200000,
+ core = 5,
+ },
},
-- Upgrade classification 4
[4] = {
- [1] = { price = 8000000, core = 1 },
- [2] = { price = 20000000, core = 1 },
- [3] = { price = 40000000, core = 2 },
- [4] = { price = 65000000, core = 5 },
- [5] = { price = 100000000, core = 10 },
- [6] = { price = 250000000, core = 15 },
- [7] = { price = 750000000, core = 25 },
- [8] = { price = 2500000000, core = 35 },
- [9] = { price = 8000000000, core = 50 },
- [10] = { price = 15000000000, core = 65 },
+ [1] = {
+ regular = 1500000,
+ core = 1,
+ convergence = { fusion = { price = 6000000 }, transfer = { price = 12000000 } },
+ },
+ [2] = {
+ regular = 3000000,
+ core = 2,
+ convergence = { fusion = { price = 12000000 }, transfer = { price = 24000000 } },
+ },
+ [3] = {
+ regular = 6000000,
+ core = 5,
+ convergence = { fusion = { price = 24000000 }, transfer = { price = 48000000 } },
+ },
+ [4] = {
+ regular = 15000000,
+ core = 10,
+ convergence = { fusion = { price = 48000000 }, transfer = { price = 100000000 } },
+ },
+ [5] = {
+ regular = 30000000,
+ core = 15,
+ convergence = { fusion = { price = 100000000 }, transfer = { price = 200000000 } },
+ },
+ [6] = {
+ regular = 80000000,
+ core = 25,
+ convergence = { fusion = { price = 200000000 }, transfer = { price = 400000000 } },
+ },
+ [7] = {
+ regular = 200000000,
+ core = 35,
+ convergence = { fusion = { price = 400000000 }, transfer = { price = 800000000 } },
+ },
+ [8] = {
+ regular = 400000000,
+ core = 50,
+ convergence = { fusion = { price = 800000000 }, transfer = { price = 1600000000 } },
+ },
+ [9] = {
+ regular = 800000000,
+ core = 60,
+ convergence = { fusion = { price = 1600000000 }, transfer = { price = 3200000000 } },
+ },
+ [10] = {
+ regular = 1600000000,
+ core = 85,
+ convergence = { fusion = { price = 3200000000 }, transfer = { price = 6400000000 } },
+ },
},
}
@@ -40,9 +122,13 @@ for classificationId, classificationTable in ipairs(itemTierClassifications) do
-- Registers table for register_item_tier.lua interface
classification.Upgrades = {}
for tierId, tierTable in ipairs(classificationTable) do
- if tierId and tierTable.price and tierTable.core ~= nil then
- table.insert(classification.Upgrades, { TierId = tierId - 1, Price = tierTable.price, Core = tierTable.core })
- end
+ table.insert(classification.Upgrades, {
+ TierId = tierId,
+ Core = tierTable.core,
+ RegularPrice = tierTable.regular,
+ ConvergenceFustionPrice = tierTable.convergence and tierTable.convergence.fusion.price or 0,
+ ConvergenceTransferPrice = tierTable.convergence and tierTable.convergence.transfer.price or 0,
+ })
end
-- Create item classification and register classification table
itemClassification:register(classification)
diff --git a/data-otservbr-global/scripts/lib/register_item_tier.lua b/data-otservbr-global/scripts/lib/register_item_tier.lua
index 51ad62ccc6e..b869dd229a1 100644
--- a/data-otservbr-global/scripts/lib/register_item_tier.lua
+++ b/data-otservbr-global/scripts/lib/register_item_tier.lua
@@ -14,8 +14,9 @@ end
registerItemClassification.Upgrades = function(itemClassification, mask)
if mask.Upgrades then
for _, value in ipairs(mask.Upgrades) do
- if value.TierId and value.Price then
- itemClassification:addTier(value.TierId, value.Price, value.Core)
+ if value.TierId then
+ logger.debug("Registering tier {}, core {}, regular price {}, fusion price {}, transfer price {}", value.TierId, value.Core, value.RegularPrice, value.ConvergenceFustionPrice, value.ConvergenceTransferPrice)
+ itemClassification:addTier(value.TierId, value.Core, value.RegularPrice, value.ConvergenceFustionPrice, value.ConvergenceTransferPrice)
else
logger.warn("[registerItemClassification.Upgrades] - Item classification failed on adquire TierID or Price attribute.")
end
diff --git a/data-otservbr-global/scripts/lib/shops.lua b/data-otservbr-global/scripts/lib/shops.lua
index 84902899489..af19fd80386 100644
--- a/data-otservbr-global/scripts/lib/shops.lua
+++ b/data-otservbr-global/scripts/lib/shops.lua
@@ -1334,17 +1334,28 @@ LootShopConfigTable = {
{ itemName = "golden skull", clientId = 35580, sell = 9000 },
{ itemName = "golden sun coin", clientId = 43734, sell = 11000 },
{ itemName = "golden tiger coin", clientId = 43735, sell = 11000 },
+ { itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
+ { itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
+ { itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
+ { itemName = "greater sage gem", clientId = 44610, sell = 10000 },
{ itemName = "green crystal fragment", clientId = 16127, sell = 800 },
{ itemName = "green crystal shard", clientId = 16121, sell = 1500 },
{ itemName = "green crystal splinter", clientId = 16122, sell = 400 },
{ itemName = "green gem", clientId = 3038, sell = 5000 },
{ itemName = "green giant shimmering pearl", clientId = 281, sell = 3000 },
+ { itemName = "guardian gem", clientId = 44603, sell = 5000 },
{ itemName = "hexagonal ruby", clientId = 30180, sell = 30000 },
+ { itemName = "lesser guardian gem", clientId = 44602, sell = 1000 },
+ { itemName = "lesser marksman gem", clientId = 44605, sell = 1000 },
+ { itemName = "lesser mystic gem", clientId = 44611, sell = 1000 },
+ { itemName = "lesser sage gem", clientId = 44608, sell = 1000 },
{ itemName = "life crystal", clientId = 3061, sell = 85 },
{ itemName = "magic light wand", clientId = 3046, sell = 35 },
+ { itemName = "marksman gem", clientId = 44606, sell = 5000 },
{ itemName = "medal of valiance", clientId = 31591, sell = 410000 },
{ itemName = "mind stone", clientId = 3062, sell = 100 },
{ itemName = "moonstone", clientId = 32771, sell = 13000 },
+ { itemName = "mystic gem", clientId = 44612, sell = 5000 },
{ itemName = "onyx chip", clientId = 22193, sell = 500 },
{ itemName = "opal", clientId = 22194, sell = 500 },
{ itemName = "orb", clientId = 3060, sell = 750 },
@@ -1358,6 +1369,7 @@ LootShopConfigTable = {
{ itemName = "red gem", clientId = 3039, sell = 1000 },
{ itemName = "red tome", clientId = 2852, sell = 2000 },
{ itemName = "royal almandine", clientId = 39038, sell = 460000 },
+ { itemName = "sage gem", clientId = 44609, sell = 5000 },
{ itemName = "scarab coin", clientId = 3042, sell = 100 },
{ itemName = "sea horse figurine", clientId = 31323, sell = 42000 },
{ itemName = "seacrest pearl", clientId = 21747, sell = 400 },
diff --git a/data-otservbr-global/scripts/weapons/unscripted_weapons.lua b/data-otservbr-global/scripts/weapons/unscripted_weapons.lua
index 667e242afaf..6feecd6376f 100644
--- a/data-otservbr-global/scripts/weapons/unscripted_weapons.lua
+++ b/data-otservbr-global/scripts/weapons/unscripted_weapons.lua
@@ -233,7 +233,7 @@ local weapons = {
},
{
-- broken macuahuitl
- itemid = 40530,
+ itemId = 40530,
type = WEAPON_SWORD,
},
{
@@ -705,7 +705,7 @@ local weapons = {
},
{
-- phantasmal axe
- itemid = 32616,
+ itemId = 32616,
type = WEAPON_AXE,
level = 180,
unproperly = true,
@@ -716,12 +716,12 @@ local weapons = {
},
{
-- meat hammer
- itemid = 32093,
+ itemId = 32093,
type = WEAPON_CLUB,
},
{
-- tagralt blade
- itemid = 31614,
+ itemId = 31614,
type = WEAPON_SWORD,
level = 250,
unproperly = true,
@@ -732,7 +732,7 @@ local weapons = {
},
{
-- bow of cataclysm
- itemid = 31581,
+ itemId = 31581,
type = WEAPON_DISTANCE,
level = 250,
unproperly = true,
@@ -743,7 +743,7 @@ local weapons = {
},
{
-- mortal mace
- itemid = 31580,
+ itemId = 31580,
type = WEAPON_CLUB,
level = 220,
unproperly = true,
@@ -754,7 +754,7 @@ local weapons = {
},
{
-- cobra rod
- itemid = 30400,
+ itemId = 30400,
type = WEAPON_WAND,
wandType = "earth",
level = 220,
@@ -767,7 +767,7 @@ local weapons = {
},
{
-- cobra wand
- itemid = 30399,
+ itemId = 30399,
type = WEAPON_WAND,
wandType = "energy",
level = 270,
@@ -780,7 +780,7 @@ local weapons = {
},
{
-- cobra sword
- itemid = 30398,
+ itemId = 30398,
type = WEAPON_SWORD,
level = 220,
unproperly = true,
@@ -791,7 +791,7 @@ local weapons = {
},
{
-- cobra axe
- itemid = 30396,
+ itemId = 30396,
type = WEAPON_AXE,
level = 220,
unproperly = true,
@@ -802,7 +802,7 @@ local weapons = {
},
{
-- cobra club
- itemid = 30395,
+ itemId = 30395,
type = WEAPON_CLUB,
level = 220,
unproperly = true,
@@ -813,7 +813,7 @@ local weapons = {
},
{
-- cobra crossbow
- itemid = 30393,
+ itemId = 30393,
type = WEAPON_DISTANCE,
level = 220,
unproperly = true,
@@ -824,12 +824,12 @@ local weapons = {
},
{
-- ice hatchet
- itemid = 30283,
+ itemId = 30283,
type = WEAPON_AXE,
},
{
-- energized limb
- itemid = 29425,
+ itemId = 29425,
type = WEAPON_WAND,
wandType = "fire",
level = 180,
@@ -844,7 +844,7 @@ local weapons = {
},
{
-- winterblade
- itemid = 29422,
+ itemId = 29422,
type = WEAPON_SWORD,
level = 200,
unproperly = true,
@@ -855,7 +855,7 @@ local weapons = {
},
{
-- summerblade
- itemid = 29421,
+ itemId = 29421,
type = WEAPON_SWORD,
level = 200,
unproperly = true,
@@ -866,7 +866,7 @@ local weapons = {
},
{
-- resizer
- itemid = 29419,
+ itemId = 29419,
type = WEAPON_CLUB,
level = 230,
unproperly = true,
@@ -877,7 +877,7 @@ local weapons = {
},
{
-- living vine bow
- itemid = 29417,
+ itemId = 29417,
type = WEAPON_DISTANCE,
level = 220,
unproperly = true,
@@ -888,65 +888,65 @@ local weapons = {
},
{
-- golden axe
- itemid = 29286,
+ itemId = 29286,
type = WEAPON_AXE,
},
{
-- wand of destruction test
- itemid = 28479,
+ itemId = 28479,
type = WEAPON_WAND,
},
{
-- umbral master bow test
- itemid = 28478,
+ itemId = 28478,
type = WEAPON_DISTANCE,
},
{
-- sorcerer test weapon
- itemid = 28466,
+ itemId = 28466,
type = WEAPON_WAND,
},
{
-- bow of destruction test
- itemid = 28465,
+ itemId = 28465,
type = WEAPON_DISTANCE,
},
{
-- test weapon for knights
- itemid = 28464,
+ itemId = 28464,
type = WEAPON_SWORD,
},
{
-- sulphurous demonbone
- itemid = 28832,
+ itemId = 28832,
type = WEAPON_CLUB,
level = 80,
unproperly = true,
},
{
-- unliving demonbone
- itemid = 28831,
+ itemId = 28831,
type = WEAPON_CLUB,
level = 80,
unproperly = true,
},
{
-- energized demonbone
- itemid = 28830,
+ itemId = 28830,
type = WEAPON_CLUB,
level = 80,
unproperly = true,
},
{
-- rotten demonbone
- itemid = 28829,
+ itemId = 28829,
type = WEAPON_CLUB,
level = 80,
unproperly = true,
},
{
-- deepling fork
- itemid = 28826,
+ itemId = 28826,
type = WEAPON_WAND,
wandType = "ice",
level = 230,
@@ -961,7 +961,7 @@ local weapons = {
},
{
-- deepling ceremonial dagger
- itemid = 28825,
+ itemId = 28825,
type = WEAPON_WAND,
wandType = "ice",
level = 180,
@@ -976,7 +976,7 @@ local weapons = {
},
{
-- falcon mace
- itemid = 28725,
+ itemId = 28725,
type = WEAPON_CLUB,
level = 300,
unproperly = true,
@@ -987,7 +987,7 @@ local weapons = {
},
{
-- falcon battleaxe
- itemid = 28724,
+ itemId = 28724,
type = WEAPON_AXE,
level = 300,
unproperly = true,
@@ -998,7 +998,7 @@ local weapons = {
},
{
-- falcon longsword
- itemid = 28723,
+ itemId = 28723,
type = WEAPON_SWORD,
level = 300,
unproperly = true,
@@ -1009,7 +1009,7 @@ local weapons = {
},
{
-- falcon bow
- itemid = 28718,
+ itemId = 28718,
type = WEAPON_DISTANCE,
level = 300,
unproperly = true,
@@ -1020,7 +1020,7 @@ local weapons = {
},
{
-- falcon wand
- itemid = 28717,
+ itemId = 28717,
type = WEAPON_WAND,
wandType = "energy",
level = 300,
@@ -1033,7 +1033,7 @@ local weapons = {
},
{
-- falcon rod
- itemid = 28716,
+ itemId = 28716,
type = WEAPON_WAND,
wandType = "earth",
level = 300,
@@ -1046,7 +1046,7 @@ local weapons = {
},
{
-- gnome sword
- itemid = 27651,
+ itemId = 27651,
type = WEAPON_SWORD,
level = 250,
unproperly = true,
@@ -1057,17 +1057,17 @@ local weapons = {
},
{
-- mallet handle
- itemid = 27525,
+ itemId = 27525,
type = WEAPON_CLUB,
},
{
-- strange mallet
- itemid = 27523,
+ itemId = 27523,
type = WEAPON_CLUB,
},
{
-- rod of destruction
- itemid = 27458,
+ itemId = 27458,
type = WEAPON_WAND,
wandType = "ice",
level = 200,
@@ -1080,7 +1080,7 @@ local weapons = {
},
{
-- wand of destruction
- itemid = 27457,
+ itemId = 27457,
type = WEAPON_WAND,
wandType = "energy",
level = 200,
@@ -1093,7 +1093,7 @@ local weapons = {
},
{
-- crossbow of destruction
- itemid = 27456,
+ itemId = 27456,
type = WEAPON_DISTANCE,
level = 200,
unproperly = true,
@@ -1104,7 +1104,7 @@ local weapons = {
},
{
-- bow of destruction
- itemid = 27455,
+ itemId = 27455,
type = WEAPON_DISTANCE,
level = 200,
unproperly = true,
@@ -1115,7 +1115,7 @@ local weapons = {
},
{
-- hammer of destruction
- itemid = 27454,
+ itemId = 27454,
type = WEAPON_CLUB,
level = 200,
unproperly = true,
@@ -1126,7 +1126,7 @@ local weapons = {
},
{
-- mace of destruction
- itemid = 27453,
+ itemId = 27453,
type = WEAPON_CLUB,
level = 200,
unproperly = true,
@@ -1137,7 +1137,7 @@ local weapons = {
},
{
-- chopper of destruction
- itemid = 27452,
+ itemId = 27452,
type = WEAPON_AXE,
level = 200,
unproperly = true,
@@ -1148,7 +1148,7 @@ local weapons = {
},
{
-- axe of destruction
- itemid = 27451,
+ itemId = 27451,
type = WEAPON_AXE,
level = 200,
unproperly = true,
@@ -1159,7 +1159,7 @@ local weapons = {
},
{
-- slayer of destruction
- itemid = 27450,
+ itemId = 27450,
type = WEAPON_SWORD,
level = 200,
unproperly = true,
@@ -1170,7 +1170,7 @@ local weapons = {
},
{
-- blade of destruction
- itemid = 27449,
+ itemId = 27449,
type = WEAPON_SWORD,
level = 200,
unproperly = true,
@@ -1181,577 +1181,577 @@ local weapons = {
},
{
-- ornate carving hammer
- itemid = 26061,
+ itemId = 26061,
type = WEAPON_CLUB,
},
{
-- valuable carving hammer
- itemid = 26060,
+ itemId = 26060,
type = WEAPON_CLUB,
},
{
-- plain carving hammer
- itemid = 26059,
+ itemId = 26059,
type = WEAPON_CLUB,
},
{
-- ornate carving mace
- itemid = 26058,
+ itemId = 26058,
type = WEAPON_CLUB,
},
{
-- valuable carving mace
- itemid = 26057,
+ itemId = 26057,
type = WEAPON_CLUB,
},
{
-- plain carving mace
- itemid = 26056,
+ itemId = 26056,
type = WEAPON_CLUB,
},
{
-- ornate carving chopper
- itemid = 26055,
+ itemId = 26055,
type = WEAPON_AXE,
},
{
-- valuable carving chopper
- itemid = 26054,
+ itemId = 26054,
type = WEAPON_AXE,
},
{
-- plain carving chopper
- itemid = 26053,
+ itemId = 26053,
type = WEAPON_AXE,
},
{
-- ornate carving axe
- itemid = 26052,
+ itemId = 26052,
type = WEAPON_AXE,
},
{
-- valuable carving axe
- itemid = 26051,
+ itemId = 26051,
type = WEAPON_AXE,
},
{
-- plain carving axe
- itemid = 26050,
+ itemId = 26050,
type = WEAPON_AXE,
},
{
-- ornate carving slayer
- itemid = 26049,
+ itemId = 26049,
type = WEAPON_SWORD,
},
{
-- valuable carving slayer
- itemid = 26048,
+ itemId = 26048,
type = WEAPON_SWORD,
},
{
-- plain carving slayer
- itemid = 26047,
+ itemId = 26047,
type = WEAPON_SWORD,
},
{
-- ornate carving blade
- itemid = 26046,
+ itemId = 26046,
type = WEAPON_SWORD,
},
{
-- valuable carving blade
- itemid = 26045,
+ itemId = 26045,
type = WEAPON_SWORD,
},
{
-- plain carving blade
- itemid = 26044,
+ itemId = 26044,
type = WEAPON_SWORD,
},
{
-- ornate remedy hammer
- itemid = 26031,
+ itemId = 26031,
type = WEAPON_CLUB,
},
{
-- valuable remedy hammer
- itemid = 26030,
+ itemId = 26030,
type = WEAPON_CLUB,
},
{
-- plain remedy hammer
- itemid = 26029,
+ itemId = 26029,
type = WEAPON_CLUB,
},
{
-- ornate remedy mace
- itemid = 26028,
+ itemId = 26028,
type = WEAPON_CLUB,
},
{
-- valuable remedy mace
- itemid = 26027,
+ itemId = 26027,
type = WEAPON_CLUB,
},
{
-- plain remedy mace
- itemid = 26026,
+ itemId = 26026,
type = WEAPON_CLUB,
},
{
-- ornate remedy chopper
- itemid = 26025,
+ itemId = 26025,
type = WEAPON_AXE,
},
{
-- valuable remedy chopper
- itemid = 26024,
+ itemId = 26024,
type = WEAPON_AXE,
},
{
-- plain remedy chopper
- itemid = 26023,
+ itemId = 26023,
type = WEAPON_AXE,
},
{
-- ornate remedy axe
- itemid = 26022,
+ itemId = 26022,
type = WEAPON_AXE,
},
{
-- valuable remedy axe
- itemid = 26021,
+ itemId = 26021,
type = WEAPON_AXE,
},
{
-- plain remedy axe
- itemid = 26020,
+ itemId = 26020,
type = WEAPON_AXE,
},
{
-- ornate remedy slayer
- itemid = 26019,
+ itemId = 26019,
type = WEAPON_SWORD,
},
{
-- valuable remedy slayer
- itemid = 26018,
+ itemId = 26018,
type = WEAPON_SWORD,
},
{
-- plain remedy slayer
- itemid = 26017,
+ itemId = 26017,
type = WEAPON_SWORD,
},
{
-- ornate remedy blade
- itemid = 26016,
+ itemId = 26016,
type = WEAPON_SWORD,
},
{
-- valuable remedy blade
- itemid = 26015,
+ itemId = 26015,
type = WEAPON_SWORD,
},
{
-- plain remedy blade
- itemid = 26014,
+ itemId = 26014,
type = WEAPON_SWORD,
},
{
-- ornate mayhem hammer
- itemid = 26000,
+ itemId = 26000,
type = WEAPON_CLUB,
},
{
-- valuable mayhem hammer
- itemid = 25999,
+ itemId = 25999,
type = WEAPON_CLUB,
},
{
-- plain mayhem hammer
- itemid = 25998,
+ itemId = 25998,
type = WEAPON_CLUB,
},
{
-- ornate mayhem mace
- itemid = 25997,
+ itemId = 25997,
type = WEAPON_CLUB,
},
{
-- valuable mayhem mace
- itemid = 25996,
+ itemId = 25996,
type = WEAPON_CLUB,
},
{
-- plain mayhem mace
- itemid = 25995,
+ itemId = 25995,
type = WEAPON_CLUB,
},
{
-- ornate mayhem chopper
- itemid = 25994,
+ itemId = 25994,
type = WEAPON_AXE,
},
{
-- valuable mayhem chopper
- itemid = 25993,
+ itemId = 25993,
type = WEAPON_AXE,
},
{
-- plain mayhem chopper
- itemid = 25992,
+ itemId = 25992,
type = WEAPON_AXE,
},
{
-- ornate mayhem axe
- itemid = 25991,
+ itemId = 25991,
type = WEAPON_AXE,
},
{
-- valuable mayhem axe
- itemid = 25990,
+ itemId = 25990,
type = WEAPON_AXE,
},
{
-- plain mayhem axe
- itemid = 25989,
+ itemId = 25989,
type = WEAPON_AXE,
},
{
-- ornate mayhem slayer
- itemid = 25988,
+ itemId = 25988,
type = WEAPON_SWORD,
},
{
-- valuable mayhem slayer
- itemid = 25987,
+ itemId = 25987,
type = WEAPON_SWORD,
},
{
-- plain mayhem slayer
- itemid = 25986,
+ itemId = 25986,
type = WEAPON_SWORD,
},
{
-- ornate mayhem blade
- itemid = 25985,
+ itemId = 25985,
type = WEAPON_SWORD,
},
{
-- valuable mayhem blade
- itemid = 25984,
+ itemId = 25984,
type = WEAPON_SWORD,
},
{
-- plain mayhem blade
- itemid = 25983,
+ itemId = 25983,
type = WEAPON_SWORD,
},
{
-- energy war hammer replica
- itemid = 25974,
+ itemId = 25974,
type = WEAPON_CLUB,
},
{
-- energy orcish maul replica
- itemid = 25973,
+ itemId = 25973,
type = WEAPON_CLUB,
},
{
-- energy basher replica
- itemid = 25972,
+ itemId = 25972,
type = WEAPON_CLUB,
},
{
-- energy crystal mace replica
- itemid = 25971,
+ itemId = 25971,
type = WEAPON_CLUB,
},
{
-- energy clerical mace replica
- itemid = 25970,
+ itemId = 25970,
type = WEAPON_CLUB,
},
{
-- energy war axe replica
- itemid = 25969,
+ itemId = 25969,
type = WEAPON_AXE,
},
{
-- energy headchopper replica
- itemid = 25968,
+ itemId = 25968,
type = WEAPON_AXE,
},
{
-- energy heroic axe replica
- itemid = 25967,
+ itemId = 25967,
type = WEAPON_AXE,
},
{
-- energy knight axe replica
- itemid = 25966,
+ itemId = 25966,
type = WEAPON_AXE,
},
{
-- energy barbarian axe replica
- itemid = 25965,
+ itemId = 25965,
type = WEAPON_AXE,
},
{
-- energy dragon slayer replica
- itemid = 25964,
+ itemId = 25964,
type = WEAPON_SWORD,
},
{
-- energy blacksteel replica
- itemid = 25963,
+ itemId = 25963,
type = WEAPON_SWORD,
},
{
-- energy mystic blade replica
- itemid = 25962,
+ itemId = 25962,
type = WEAPON_SWORD,
},
{
-- energy relic sword replica
- itemid = 25961,
+ itemId = 25961,
type = WEAPON_SWORD,
},
{
-- energy spike sword replica
- itemid = 25960,
+ itemId = 25960,
type = WEAPON_SWORD,
},
{
-- earth war hammer replica
- itemid = 25959,
+ itemId = 25959,
type = WEAPON_CLUB,
},
{
-- earth orcish maul replica
- itemid = 25958,
+ itemId = 25958,
type = WEAPON_CLUB,
},
{
-- earth basher replica
- itemid = 25957,
+ itemId = 25957,
type = WEAPON_CLUB,
},
{
-- earth crystal mace replica
- itemid = 25956,
+ itemId = 25956,
type = WEAPON_CLUB,
},
{
-- earth clerical mace replica
- itemid = 25955,
+ itemId = 25955,
type = WEAPON_CLUB,
},
{
-- earth war axe replica
- itemid = 25954,
+ itemId = 25954,
type = WEAPON_AXE,
},
{
-- earth headchopper replica
- itemid = 25953,
+ itemId = 25953,
type = WEAPON_AXE,
},
{
-- earth heroic axe replica
- itemid = 25952,
+ itemId = 25952,
type = WEAPON_AXE,
},
{
-- earth knight axe replica
- itemid = 25951,
+ itemId = 25951,
type = WEAPON_AXE,
},
{
-- earth barbarian axe replica
- itemid = 25950,
+ itemId = 25950,
type = WEAPON_AXE,
},
{
-- earth dragon slayer replica
- itemid = 25949,
+ itemId = 25949,
type = WEAPON_SWORD,
},
{
-- earth blacksteel replica
- itemid = 25948,
+ itemId = 25948,
type = WEAPON_SWORD,
},
{
-- earth mystic blade replica
- itemid = 25947,
+ itemId = 25947,
type = WEAPON_SWORD,
},
{
-- earth relic sword replica
- itemid = 25946,
+ itemId = 25946,
type = WEAPON_SWORD,
},
{
-- earth spike sword replica
- itemid = 25945,
+ itemId = 25945,
type = WEAPON_SWORD,
},
{
-- icy war hammer replica
- itemid = 25944,
+ itemId = 25944,
type = WEAPON_CLUB,
},
{
-- icy orcish maul replica
- itemid = 25943,
+ itemId = 25943,
type = WEAPON_CLUB,
},
{
-- icy basher replica
- itemid = 25942,
+ itemId = 25942,
type = WEAPON_CLUB,
},
{
-- icy crystal mace replica
- itemid = 25941,
+ itemId = 25941,
type = WEAPON_CLUB,
},
{
-- icy clerical mace replica
- itemid = 25940,
+ itemId = 25940,
type = WEAPON_CLUB,
},
{
-- icy war axe replica
- itemid = 25939,
+ itemId = 25939,
type = WEAPON_AXE,
},
{
-- icy headchopper replica
- itemid = 25938,
+ itemId = 25938,
type = WEAPON_AXE,
},
{
-- icy heroic axe replica
- itemid = 25937,
+ itemId = 25937,
type = WEAPON_AXE,
},
{
-- icy knight axe replica
- itemid = 25936,
+ itemId = 25936,
type = WEAPON_AXE,
},
{
-- icy barbarian axe replica
- itemid = 25935,
+ itemId = 25935,
type = WEAPON_AXE,
},
{
-- icy dragon slayer replica
- itemid = 25934,
+ itemId = 25934,
type = WEAPON_SWORD,
},
{
-- icy blacksteel replica
- itemid = 25933,
+ itemId = 25933,
type = WEAPON_SWORD,
},
{
-- icy mystic blade replica
- itemid = 25932,
+ itemId = 25932,
type = WEAPON_SWORD,
},
{
-- icy relic sword replica
- itemid = 25931,
+ itemId = 25931,
type = WEAPON_SWORD,
},
{
-- icy spike sword replica
- itemid = 25930,
+ itemId = 25930,
type = WEAPON_SWORD,
},
{
-- fiery war hammer replica
- itemid = 25929,
+ itemId = 25929,
type = WEAPON_CLUB,
},
{
-- fiery orcish maul replica
- itemid = 25928,
+ itemId = 25928,
type = WEAPON_CLUB,
},
{
-- fiery basher replica
- itemid = 25927,
+ itemId = 25927,
type = WEAPON_CLUB,
},
{
-- fiery crystal mace replica
- itemid = 25926,
+ itemId = 25926,
type = WEAPON_CLUB,
},
{
-- fiery clerical mace replica
- itemid = 25925,
+ itemId = 25925,
type = WEAPON_CLUB,
},
{
-- fiery war axe replica
- itemid = 25924,
+ itemId = 25924,
type = WEAPON_AXE,
},
{
-- fiery headchopper replica
- itemid = 25923,
+ itemId = 25923,
type = WEAPON_AXE,
},
{
-- fiery heroic axe replica
- itemid = 25922,
+ itemId = 25922,
type = WEAPON_AXE,
},
{
-- fiery knight axe replica
- itemid = 25921,
+ itemId = 25921,
type = WEAPON_AXE,
},
{
-- fiery barbarian axe replica
- itemid = 25920,
+ itemId = 25920,
type = WEAPON_AXE,
},
{
-- fiery dragon slayer replica
- itemid = 25919,
+ itemId = 25919,
type = WEAPON_SWORD,
},
{
-- fiery blacksteel replica
- itemid = 25918,
+ itemId = 25918,
type = WEAPON_SWORD,
},
{
-- fiery mystic blade replica
- itemid = 25917,
+ itemId = 25917,
type = WEAPON_SWORD,
},
{
-- fiery relic sword replica
- itemid = 25916,
+ itemId = 25916,
type = WEAPON_SWORD,
},
{
-- fiery spike sword replica
- itemid = 25915,
+ itemId = 25915,
type = WEAPON_SWORD,
},
{
-- wand of darkness
- itemid = 25760,
+ itemId = 25760,
type = WEAPON_WAND,
wandType = "death",
level = 41,
@@ -1772,7 +1772,7 @@ local weapons = {
},
{
-- spectral bolt
- itemid = 25758,
+ itemId = 25758,
type = WEAPON_AMMO,
level = 150,
unproperly = true,
@@ -1788,7 +1788,7 @@ local weapons = {
},
{
-- dream blossom staff
- itemid = 25700,
+ itemId = 25700,
type = WEAPON_WAND,
wandType = "energy",
level = 80,
@@ -1803,7 +1803,7 @@ local weapons = {
},
{
-- rod of carving
- itemid = 23339,
+ itemId = 23339,
type = WEAPON_WAND,
wandType = "ice",
level = 100,
@@ -1816,7 +1816,7 @@ local weapons = {
},
{
-- wand of carving
- itemid = 23335,
+ itemId = 23335,
type = WEAPON_WAND,
wandType = "energy",
level = 100,
@@ -1829,7 +1829,7 @@ local weapons = {
},
{
-- crossbow of carving
- itemid = 23331,
+ itemId = 23331,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -1840,7 +1840,7 @@ local weapons = {
},
{
-- bow of carving
- itemid = 23327,
+ itemId = 23327,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -1851,7 +1851,7 @@ local weapons = {
},
{
-- hammer of carving
- itemid = 23323,
+ itemId = 23323,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -1862,7 +1862,7 @@ local weapons = {
},
{
-- mace of carving
- itemid = 23319,
+ itemId = 23319,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -1873,7 +1873,7 @@ local weapons = {
},
{
-- chopper of carving
- itemid = 23315,
+ itemId = 23315,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -1884,7 +1884,7 @@ local weapons = {
},
{
-- axe of carving
- itemid = 23311,
+ itemId = 23311,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -1895,7 +1895,7 @@ local weapons = {
},
{
-- slayer of carving
- itemid = 23307,
+ itemId = 23307,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -1906,7 +1906,7 @@ local weapons = {
},
{
-- blade of carving
- itemid = 23303,
+ itemId = 23303,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -1917,7 +1917,7 @@ local weapons = {
},
{
-- rod of remedy
- itemid = 23299,
+ itemId = 23299,
type = WEAPON_WAND,
wandType = "ice",
level = 100,
@@ -1930,7 +1930,7 @@ local weapons = {
},
{
-- wand of remedy
- itemid = 23295,
+ itemId = 23295,
type = WEAPON_WAND,
wandType = "energy",
level = 100,
@@ -1943,7 +1943,7 @@ local weapons = {
},
{
-- crossbow of remedy
- itemid = 23291,
+ itemId = 23291,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -1954,7 +1954,7 @@ local weapons = {
},
{
-- bow of remedy
- itemid = 23287,
+ itemId = 23287,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -1965,7 +1965,7 @@ local weapons = {
},
{
-- hammer of remedy
- itemid = 23283,
+ itemId = 23283,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -1976,7 +1976,7 @@ local weapons = {
},
{
-- mace of remedy
- itemid = 23279,
+ itemId = 23279,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -1987,7 +1987,7 @@ local weapons = {
},
{
-- chopper of remedy
- itemid = 23275,
+ itemId = 23275,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -1998,7 +1998,7 @@ local weapons = {
},
{
-- axe of remedy
- itemid = 23271,
+ itemId = 23271,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -2009,7 +2009,7 @@ local weapons = {
},
{
-- slayer of remedy
- itemid = 23267,
+ itemId = 23267,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -2020,7 +2020,7 @@ local weapons = {
},
{
-- blade of remedy
- itemid = 23263,
+ itemId = 23263,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -2031,7 +2031,7 @@ local weapons = {
},
{
-- rod of mayhem
- itemid = 23232,
+ itemId = 23232,
type = WEAPON_WAND,
wandType = "ice",
level = 100,
@@ -2044,7 +2044,7 @@ local weapons = {
},
{
-- wand of mayhem
- itemid = 23231,
+ itemId = 23231,
type = WEAPON_WAND,
wandType = "energy",
level = 100,
@@ -2057,7 +2057,7 @@ local weapons = {
},
{
-- crossbow of mayhem
- itemid = 23230,
+ itemId = 23230,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -2068,7 +2068,7 @@ local weapons = {
},
{
-- bow of mayhem
- itemid = 23229,
+ itemId = 23229,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -2079,7 +2079,7 @@ local weapons = {
},
{
-- hammer of mayhem
- itemid = 23228,
+ itemId = 23228,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -2090,7 +2090,7 @@ local weapons = {
},
{
-- mace of mayhem
- itemid = 23227,
+ itemId = 23227,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -2101,7 +2101,7 @@ local weapons = {
},
{
-- chopper of mayhem
- itemid = 23226,
+ itemId = 23226,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -2112,7 +2112,7 @@ local weapons = {
},
{
-- axe of mayhem
- itemid = 23225,
+ itemId = 23225,
type = WEAPON_AXE,
level = 100,
unproperly = true,
@@ -2123,7 +2123,7 @@ local weapons = {
},
{
-- slayer of mayhem
- itemid = 23224,
+ itemId = 23224,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -2134,7 +2134,7 @@ local weapons = {
},
{
-- blade of mayhem
- itemid = 23223,
+ itemId = 23223,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -2145,7 +2145,7 @@ local weapons = {
},
{
-- rift crossbow
- itemid = 22867,
+ itemId = 22867,
type = WEAPON_DISTANCE,
level = 120,
unproperly = true,
@@ -2156,7 +2156,7 @@ local weapons = {
},
{
-- rift bow
- itemid = 22866,
+ itemId = 22866,
type = WEAPON_DISTANCE,
level = 120,
unproperly = true,
@@ -2167,7 +2167,7 @@ local weapons = {
},
{
-- ferumbras' staff (enchanted)
- itemid = 22766,
+ itemId = 22766,
type = WEAPON_WAND,
wandType = "energy",
level = 100,
@@ -2180,7 +2180,7 @@ local weapons = {
},
{
-- ferumbras' staff (failed)
- itemid = 22765,
+ itemId = 22765,
type = WEAPON_WAND,
wandType = "energy",
level = 65,
@@ -2193,42 +2193,42 @@ local weapons = {
},
{
-- Ferumbras' staff
- itemid = 22764,
+ itemId = 22764,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
},
{
-- maimer
- itemid = 22762,
+ itemId = 22762,
type = WEAPON_CLUB,
level = 150,
unproperly = true,
},
{
-- Impaler of the igniter
- itemid = 22760,
+ itemId = 22760,
type = WEAPON_SWORD,
level = 150,
unproperly = true,
},
{
-- plague bite
- itemid = 22759,
+ itemId = 22759,
type = WEAPON_AXE,
level = 150,
unproperly = true,
},
{
-- rift lance
- itemid = 22727,
+ itemId = 22727,
type = WEAPON_AXE,
level = 70,
unproperly = true,
},
{
-- ogre sceptra
- itemid = 22183,
+ itemId = 22183,
type = WEAPON_WAND,
wandType = "earth",
level = 37,
@@ -2241,27 +2241,27 @@ local weapons = {
},
{
-- ogre choppa
- itemid = 22172,
+ itemId = 22172,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- ogre klubba
- itemid = 22171,
+ itemId = 22171,
type = WEAPON_AXE,
level = 50,
unproperly = true,
},
{
-- simple arrow
- itemid = 21470,
+ itemId = 21470,
type = WEAPON_AMMO,
action = "removecount",
},
{
-- the chiller
- itemid = 21350,
+ itemId = 21350,
type = WEAPON_WAND,
wandType = "ice",
level = 1,
@@ -2274,7 +2274,7 @@ local weapons = {
},
{
-- the scorcher
- itemid = 21348,
+ itemId = 21348,
type = WEAPON_WAND,
wandType = "fire",
level = 1,
@@ -2287,14 +2287,14 @@ local weapons = {
},
{
-- one hit wonder
- itemid = 21219,
+ itemId = 21219,
type = WEAPON_CLUB,
level = 70,
unproperly = true,
},
{
-- glooth axe
- itemid = 21180,
+ itemId = 21180,
type = WEAPON_AXE,
level = 75,
unproperly = true,
@@ -2306,7 +2306,7 @@ local weapons = {
},
{
-- glooth blade
- itemid = 21179,
+ itemId = 21179,
type = WEAPON_SWORD,
level = 75,
unproperly = true,
@@ -2318,7 +2318,7 @@ local weapons = {
},
{
-- glooth club
- itemid = 21178,
+ itemId = 21178,
type = WEAPON_CLUB,
level = 75,
unproperly = true,
@@ -2330,42 +2330,42 @@ local weapons = {
},
{
-- cowtana
- itemid = 21177,
+ itemId = 21177,
type = WEAPON_SWORD,
level = 25,
unproperly = true,
},
{
-- execowtioner axe
- itemid = 21176,
+ itemId = 21176,
type = WEAPON_AXE,
level = 55,
unproperly = true,
},
{
-- mino lance
- itemid = 21174,
+ itemId = 21174,
type = WEAPON_AXE,
level = 45,
unproperly = true,
},
{
-- moohtant cudgel
- itemid = 21173,
+ itemId = 21173,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
},
{
-- glooth whip
- itemid = 21172,
+ itemId = 21172,
type = WEAPON_CLUB,
level = 25,
unproperly = true,
},
{
-- metal bat
- itemid = 21171,
+ itemId = 21171,
type = WEAPON_CLUB,
level = 55,
unproperly = true,
@@ -2380,7 +2380,7 @@ local weapons = {
},
{
-- umbral master crossbow
- itemid = 20087,
+ itemId = 20087,
type = WEAPON_DISTANCE,
level = 250,
unproperly = true,
@@ -2391,7 +2391,7 @@ local weapons = {
},
{
-- umbral crossbow
- itemid = 20086,
+ itemId = 20086,
type = WEAPON_DISTANCE,
level = 120,
unproperly = true,
@@ -2402,7 +2402,7 @@ local weapons = {
},
{
-- crude umbral crossbow
- itemid = 20085,
+ itemId = 20085,
type = WEAPON_DISTANCE,
level = 75,
unproperly = true,
@@ -2413,7 +2413,7 @@ local weapons = {
},
{
-- umbral master bow
- itemid = 20084,
+ itemId = 20084,
type = WEAPON_DISTANCE,
level = 250,
unproperly = true,
@@ -2424,7 +2424,7 @@ local weapons = {
},
{
-- umbral bow
- itemid = 20083,
+ itemId = 20083,
type = WEAPON_DISTANCE,
level = 120,
unproperly = true,
@@ -2435,7 +2435,7 @@ local weapons = {
},
{
-- crude umbral bow
- itemid = 20082,
+ itemId = 20082,
type = WEAPON_DISTANCE,
level = 75,
unproperly = true,
@@ -2446,7 +2446,7 @@ local weapons = {
},
{
-- umbral master hammer
- itemid = 20081,
+ itemId = 20081,
type = WEAPON_CLUB,
level = 250,
unproperly = true,
@@ -2457,7 +2457,7 @@ local weapons = {
},
{
-- umbral hammer
- itemid = 20080,
+ itemId = 20080,
type = WEAPON_CLUB,
level = 120,
unproperly = true,
@@ -2468,7 +2468,7 @@ local weapons = {
},
{
-- crude umbral hammer
- itemid = 20079,
+ itemId = 20079,
type = WEAPON_CLUB,
level = 75,
unproperly = true,
@@ -2479,7 +2479,7 @@ local weapons = {
},
{
-- umbral master mace
- itemid = 20078,
+ itemId = 20078,
type = WEAPON_CLUB,
level = 250,
unproperly = true,
@@ -2490,7 +2490,7 @@ local weapons = {
},
{
-- umbral mace
- itemid = 20077,
+ itemId = 20077,
type = WEAPON_CLUB,
level = 120,
unproperly = true,
@@ -2501,7 +2501,7 @@ local weapons = {
},
{
-- crude umbral mace
- itemid = 20076,
+ itemId = 20076,
type = WEAPON_CLUB,
level = 75,
unproperly = true,
@@ -2512,7 +2512,7 @@ local weapons = {
},
{
-- umbral master chopper
- itemid = 20075,
+ itemId = 20075,
type = WEAPON_AXE,
level = 250,
unproperly = true,
@@ -2523,7 +2523,7 @@ local weapons = {
},
{
-- umbral chopper
- itemid = 20074,
+ itemId = 20074,
type = WEAPON_AXE,
level = 120,
unproperly = true,
@@ -2534,7 +2534,7 @@ local weapons = {
},
{
-- crude umbral chopper
- itemid = 20073,
+ itemId = 20073,
type = WEAPON_AXE,
level = 75,
unproperly = true,
@@ -2545,7 +2545,7 @@ local weapons = {
},
{
-- umbral master axe
- itemid = 20072,
+ itemId = 20072,
type = WEAPON_AXE,
level = 250,
unproperly = true,
@@ -2556,7 +2556,7 @@ local weapons = {
},
{
-- umbral axe
- itemid = 20071,
+ itemId = 20071,
type = WEAPON_AXE,
level = 120,
unproperly = true,
@@ -2567,7 +2567,7 @@ local weapons = {
},
{
-- crude umbral axe
- itemid = 20070,
+ itemId = 20070,
type = WEAPON_AXE,
level = 75,
unproperly = true,
@@ -2578,7 +2578,7 @@ local weapons = {
},
{
-- umbral master slayer
- itemid = 20069,
+ itemId = 20069,
type = WEAPON_SWORD,
level = 250,
unproperly = true,
@@ -2589,7 +2589,7 @@ local weapons = {
},
{
-- umbral slayer
- itemid = 20068,
+ itemId = 20068,
type = WEAPON_SWORD,
level = 120,
unproperly = true,
@@ -2600,7 +2600,7 @@ local weapons = {
},
{
-- crude umbral slayer
- itemid = 20067,
+ itemId = 20067,
type = WEAPON_SWORD,
level = 75,
unproperly = true,
@@ -2611,7 +2611,7 @@ local weapons = {
},
{
-- umbral masterblade
- itemid = 20066,
+ itemId = 20066,
type = WEAPON_SWORD,
level = 250,
unproperly = true,
@@ -2622,7 +2622,7 @@ local weapons = {
},
{
-- umbral blade
- itemid = 20065,
+ itemId = 20065,
type = WEAPON_SWORD,
level = 120,
unproperly = true,
@@ -2633,7 +2633,7 @@ local weapons = {
},
{
-- crude umbral blade
- itemid = 20064,
+ itemId = 20064,
type = WEAPON_SWORD,
level = 75,
unproperly = true,
@@ -2644,13 +2644,13 @@ local weapons = {
},
{
-- icicle bow
- itemid = 19362,
+ itemId = 19362,
type = WEAPON_DISTANCE,
unproperly = true,
},
{
-- triple bolt crossbow
- itemid = 19356,
+ itemId = 19356,
type = WEAPON_DISTANCE,
level = 70,
unproperly = true,
@@ -2661,14 +2661,14 @@ local weapons = {
},
{
-- spiky club
- itemid = 17859,
+ itemId = 17859,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
},
{
-- pair of iron fists
- itemid = 17828,
+ itemId = 17828,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -2679,26 +2679,26 @@ local weapons = {
},
{
-- swampling club
- itemid = 17824,
+ itemId = 17824,
type = WEAPON_CLUB,
},
{
-- life preserver
- itemid = 17813,
+ itemId = 17813,
type = WEAPON_CLUB,
level = 15,
unproperly = true,
},
{
-- ratana
- itemid = 17812,
+ itemId = 17812,
type = WEAPON_SWORD,
level = 15,
unproperly = true,
},
{
-- sorc and druid staff
- itemid = 17111,
+ itemId = 17111,
type = WEAPON_WAND,
wandType = "energy",
level = 1,
@@ -2719,7 +2719,7 @@ local weapons = {
},
{
-- mean knight sword
- itemid = 17109,
+ itemId = 17109,
type = WEAPON_SWORD,
unproperly = true,
vocation = {
@@ -2728,14 +2728,14 @@ local weapons = {
},
{
-- shiny blade
- itemid = 16175,
+ itemId = 16175,
type = WEAPON_SWORD,
level = 120,
unproperly = true,
},
{
-- mycological bow
- itemid = 16164,
+ itemId = 16164,
type = WEAPON_DISTANCE,
level = 105,
unproperly = true,
@@ -2746,7 +2746,7 @@ local weapons = {
},
{
-- crystal crossbow
- itemid = 16163,
+ itemId = 16163,
type = WEAPON_DISTANCE,
level = 90,
unproperly = true,
@@ -2757,21 +2757,21 @@ local weapons = {
},
{
-- mycological mace
- itemid = 16162,
+ itemId = 16162,
type = WEAPON_CLUB,
level = 120,
unproperly = true,
},
{
-- crystalline axe
- itemid = 16161,
+ itemId = 16161,
type = WEAPON_AXE,
level = 120,
unproperly = true,
},
{
-- crystalline sword
- itemid = 16160,
+ itemId = 16160,
type = WEAPON_SWORD,
level = 62,
unproperly = true,
@@ -2786,7 +2786,7 @@ local weapons = {
},
{
-- drill bolt
- itemid = 16142,
+ itemId = 16142,
type = WEAPON_AMMO,
level = 70,
unproperly = true,
@@ -2794,7 +2794,7 @@ local weapons = {
},
{
-- prismatic bolt
- itemid = 16141,
+ itemId = 16141,
type = WEAPON_AMMO,
level = 90,
unproperly = true,
@@ -2802,7 +2802,7 @@ local weapons = {
},
{
-- glacial rod
- itemid = 16118,
+ itemId = 16118,
type = WEAPON_WAND,
wandType = "ice",
level = 65,
@@ -2815,7 +2815,7 @@ local weapons = {
},
{
-- muck rod
- itemid = 16117,
+ itemId = 16117,
type = WEAPON_WAND,
wandType = "earth",
level = 65,
@@ -2828,7 +2828,7 @@ local weapons = {
},
{
-- wand of everblazing
- itemid = 16115,
+ itemId = 16115,
type = WEAPON_WAND,
wandType = "fire",
level = 65,
@@ -2841,7 +2841,7 @@ local weapons = {
},
{
-- wand of defiance
- itemid = 16096,
+ itemId = 16096,
type = WEAPON_WAND,
wandType = "energy",
level = 65,
@@ -2862,13 +2862,13 @@ local weapons = {
},
{
-- crystal bolt
- itemid = 15792,
+ itemId = 15792,
type = WEAPON_AMMO,
action = "removecount",
},
{
-- thorn spitter
- itemid = 14768,
+ itemId = 14768,
type = WEAPON_DISTANCE,
level = 150,
unproperly = true,
@@ -2879,7 +2879,7 @@ local weapons = {
},
{
-- vortex bolt
- itemid = 14252,
+ itemId = 14252,
type = WEAPON_AMMO,
level = 40,
unproperly = true,
@@ -2895,14 +2895,14 @@ local weapons = {
},
{
-- deepling squelcher
- itemid = 14250,
+ itemId = 14250,
type = WEAPON_CLUB,
level = 48,
unproperly = true,
},
{
-- ornate crossbow
- itemid = 14247,
+ itemId = 14247,
type = WEAPON_DISTANCE,
level = 50,
unproperly = true,
@@ -2913,7 +2913,7 @@ local weapons = {
},
{
-- hive bow
- itemid = 14246,
+ itemId = 14246,
type = WEAPON_DISTANCE,
level = 85,
unproperly = true,
@@ -2924,49 +2924,49 @@ local weapons = {
},
{
-- hive scythe
- itemid = 14089,
+ itemId = 14089,
type = WEAPON_AXE,
level = 70,
unproperly = true,
},
{
-- guardian axe
- itemid = 14043,
+ itemId = 14043,
type = WEAPON_AXE,
level = 50,
unproperly = true,
},
{
-- warrior's axe
- itemid = 14040,
+ itemId = 14040,
type = WEAPON_AXE,
level = 40,
unproperly = true,
},
{
-- ornate mace
- itemid = 14001,
+ itemId = 14001,
type = WEAPON_CLUB,
level = 90,
unproperly = true,
},
{
-- deepling axe
- itemid = 13991,
+ itemId = 13991,
type = WEAPON_AXE,
level = 80,
unproperly = true,
},
{
-- deepling staff
- itemid = 13987,
+ itemId = 13987,
type = WEAPON_CLUB,
level = 38,
unproperly = true,
},
{
-- shimmer wand
- itemid = 12741,
+ itemId = 12741,
type = WEAPON_WAND,
wandType = "energy",
level = 40,
@@ -2979,7 +2979,7 @@ local weapons = {
},
{
-- shimmer bow
- itemid = 12733,
+ itemId = 12733,
type = WEAPON_DISTANCE,
level = 40,
unproperly = true,
@@ -2990,7 +2990,7 @@ local weapons = {
},
{
-- shimmer rod
- itemid = 12732,
+ itemId = 12732,
type = WEAPON_WAND,
wandType = "ice",
level = 40,
@@ -3003,26 +3003,26 @@ local weapons = {
},
{
-- shimmer sword
- itemid = 12731,
+ itemId = 12731,
type = WEAPON_SWORD,
level = 40,
unproperly = true,
},
{
-- heavy trident
- itemid = 12683,
+ itemId = 12683,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- wooden sword
- itemid = 12673,
+ itemId = 12673,
type = WEAPON_SWORD,
},
{
-- wand of dimensions
- itemid = 12603,
+ itemId = 12603,
type = WEAPON_WAND,
wandType = "death",
level = 37,
@@ -3035,21 +3035,21 @@ local weapons = {
},
{
-- blade of corruption
- itemid = 11693,
+ itemId = 11693,
type = WEAPON_SWORD,
level = 82,
unproperly = true,
},
{
-- snake god's sceptre
- itemid = 11692,
+ itemId = 11692,
type = WEAPON_CLUB,
level = 82,
unproperly = true,
},
{
-- twiceslicer
- itemid = 11657,
+ itemId = 11657,
type = WEAPON_SWORD,
level = 58,
unproperly = true,
@@ -3060,14 +3060,14 @@ local weapons = {
},
{
-- Zaoan halberd
- itemid = 10406,
+ itemId = 10406,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- twin hooks
- itemid = 10392,
+ itemId = 10392,
type = WEAPON_SWORD,
level = 20,
unproperly = true,
@@ -3078,7 +3078,7 @@ local weapons = {
},
{
-- drachaku
- itemid = 10391,
+ itemId = 10391,
type = WEAPON_CLUB,
level = 55,
unproperly = true,
@@ -3089,14 +3089,14 @@ local weapons = {
},
{
-- Zaoan sword
- itemid = 10390,
+ itemId = 10390,
type = WEAPON_SWORD,
level = 55,
unproperly = true,
},
{
-- sai
- itemid = 10389,
+ itemId = 10389,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -3107,59 +3107,59 @@ local weapons = {
},
{
-- drakinata
- itemid = 10388,
+ itemId = 10388,
type = WEAPON_AXE,
level = 60,
unproperly = true,
},
{
-- incredible mumpiz slayer
- itemid = 9396,
+ itemId = 9396,
type = WEAPON_SWORD,
},
{
-- poet's fencing quill
- itemid = 9387,
+ itemId = 9387,
type = WEAPON_SWORD,
},
{
-- farmer's avenger
- itemid = 9386,
+ itemId = 9386,
type = WEAPON_AXE,
},
{
-- club of the fury
- itemid = 9385,
+ itemId = 9385,
type = WEAPON_CLUB,
},
{
-- scythe of the reaper
- itemid = 9384,
+ itemId = 9384,
type = WEAPON_AXE,
},
{
-- musician's bow
- itemid = 9378,
+ itemId = 9378,
type = WEAPON_DISTANCE,
},
{
-- stale bread of ancientness
- itemid = 9376,
+ itemId = 9376,
type = WEAPON_CLUB,
},
{
-- pointed rabbitslayer
- itemid = 9375,
+ itemId = 9375,
type = WEAPON_SWORD,
},
{
-- glutton's mace
- itemid = 9373,
+ itemId = 9373,
type = WEAPON_CLUB,
},
{
-- the calamity
- itemid = 8104,
+ itemId = 8104,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
@@ -3170,21 +3170,21 @@ local weapons = {
},
{
-- the epiphany
- itemid = 8103,
+ itemId = 8103,
type = WEAPON_SWORD,
level = 120,
unproperly = true,
},
{
-- emerald sword
- itemid = 8102,
+ itemId = 8102,
type = WEAPON_SWORD,
level = 100,
unproperly = true,
},
{
-- the stomper
- itemid = 8101,
+ itemId = 8101,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
@@ -3195,21 +3195,21 @@ local weapons = {
},
{
-- obsidian truncheon
- itemid = 8100,
+ itemId = 8100,
type = WEAPON_CLUB,
level = 100,
unproperly = true,
},
{
-- dark trinity mace
- itemid = 8099,
+ itemId = 8099,
type = WEAPON_CLUB,
level = 120,
unproperly = true,
},
{
-- demonwing axe
- itemid = 8098,
+ itemId = 8098,
type = WEAPON_AXE,
level = 120,
unproperly = true,
@@ -3220,21 +3220,21 @@ local weapons = {
},
{
-- solar axe
- itemid = 8097,
+ itemId = 8097,
type = WEAPON_AXE,
level = 130,
unproperly = true,
},
{
-- hellforged axe
- itemid = 8096,
+ itemId = 8096,
type = WEAPON_AXE,
level = 110,
unproperly = true,
},
{
-- wand of voodoo
- itemid = 8094,
+ itemId = 8094,
type = WEAPON_WAND,
wandType = "death",
level = 42,
@@ -3247,7 +3247,7 @@ local weapons = {
},
{
-- wand of draconia
- itemid = 8093,
+ itemId = 8093,
type = WEAPON_WAND,
wandType = "fire",
level = 22,
@@ -3260,7 +3260,7 @@ local weapons = {
},
{
-- wand of starmstorm
- itemid = 8092,
+ itemId = 8092,
type = WEAPON_WAND,
wandType = "energy",
level = 37,
@@ -3273,7 +3273,7 @@ local weapons = {
},
{
-- springsprout rod
- itemid = 8084,
+ itemId = 8084,
type = WEAPON_WAND,
wandType = "earth",
level = 37,
@@ -3286,7 +3286,7 @@ local weapons = {
},
{
-- northwind rod
- itemid = 8083,
+ itemId = 8083,
type = WEAPON_WAND,
wandType = "ice",
level = 22,
@@ -3299,7 +3299,7 @@ local weapons = {
},
{
-- underworld rod
- itemid = 8082,
+ itemId = 8082,
type = WEAPON_WAND,
wandType = "death",
level = 42,
@@ -3312,7 +3312,7 @@ local weapons = {
},
{
-- elethriel's elemental bow
- itemid = 8030,
+ itemId = 8030,
type = WEAPON_DISTANCE,
level = 70,
unproperly = true,
@@ -3323,7 +3323,7 @@ local weapons = {
},
{
-- silkweaver bow
- itemid = 8029,
+ itemId = 8029,
type = WEAPON_DISTANCE,
level = 40,
unproperly = true,
@@ -3334,7 +3334,7 @@ local weapons = {
},
{
-- yol's bow
- itemid = 8028,
+ itemId = 8028,
type = WEAPON_DISTANCE,
level = 60,
unproperly = true,
@@ -3345,7 +3345,7 @@ local weapons = {
},
{
-- composite hornbow
- itemid = 8027,
+ itemId = 8027,
type = WEAPON_DISTANCE,
level = 50,
unproperly = true,
@@ -3356,7 +3356,7 @@ local weapons = {
},
{
-- warsinger bow
- itemid = 8026,
+ itemId = 8026,
type = WEAPON_DISTANCE,
level = 80,
unproperly = true,
@@ -3367,7 +3367,7 @@ local weapons = {
},
{
-- ironworker
- itemid = 8025,
+ itemId = 8025,
type = WEAPON_DISTANCE,
level = 80,
unproperly = true,
@@ -3378,7 +3378,7 @@ local weapons = {
},
{
-- devileye
- itemid = 8024,
+ itemId = 8024,
type = WEAPON_DISTANCE,
level = 100,
unproperly = true,
@@ -3389,7 +3389,7 @@ local weapons = {
},
{
-- royal crossbow
- itemid = 8023,
+ itemId = 8023,
type = WEAPON_DISTANCE,
level = 130,
unproperly = true,
@@ -3400,7 +3400,7 @@ local weapons = {
},
{
-- chain bolter
- itemid = 8022,
+ itemId = 8022,
type = WEAPON_DISTANCE,
level = 60,
unproperly = true,
@@ -3411,7 +3411,7 @@ local weapons = {
},
{
-- modified crossbow
- itemid = 8021,
+ itemId = 8021,
type = WEAPON_DISTANCE,
level = 45,
unproperly = true,
@@ -3422,22 +3422,22 @@ local weapons = {
},
{
-- jagged sword
- itemid = 7774,
+ itemId = 7774,
type = WEAPON_SWORD,
},
{
-- steel axe
- itemid = 7773,
+ itemId = 7773,
type = WEAPON_AXE,
},
{
-- crimson sword
- itemid = 860,
+ itemId = 860,
type = WEAPON_SWORD,
},
{
-- energy war hammer
- itemid = 810,
+ itemId = 810,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -3449,7 +3449,7 @@ local weapons = {
},
{
-- energy orcish maul
- itemid = 809,
+ itemId = 809,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3457,7 +3457,7 @@ local weapons = {
},
{
-- energy cranial basher
- itemid = 808,
+ itemId = 808,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
@@ -3465,7 +3465,7 @@ local weapons = {
},
{
-- energy crystal mace
- itemid = 807,
+ itemId = 807,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3473,7 +3473,7 @@ local weapons = {
},
{
-- energy clerical mace
- itemid = 806,
+ itemId = 806,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
@@ -3481,7 +3481,7 @@ local weapons = {
},
{
-- energy war axe
- itemid = 805,
+ itemId = 805,
type = WEAPON_AXE,
level = 65,
unproperly = true,
@@ -3493,7 +3493,7 @@ local weapons = {
},
{
-- energy headchopper
- itemid = 804,
+ itemId = 804,
type = WEAPON_AXE,
level = 35,
unproperly = true,
@@ -3505,7 +3505,7 @@ local weapons = {
},
{
-- energy heroic axe
- itemid = 803,
+ itemId = 803,
type = WEAPON_AXE,
level = 60,
unproperly = true,
@@ -3513,7 +3513,7 @@ local weapons = {
},
{
-- energy knight axe
- itemid = 802,
+ itemId = 802,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -3521,7 +3521,7 @@ local weapons = {
},
{
-- energy barbarian axe
- itemid = 801,
+ itemId = 801,
type = WEAPON_AXE,
level = 20,
unproperly = true,
@@ -3529,7 +3529,7 @@ local weapons = {
},
{
-- energy dragon slayer
- itemid = 798,
+ itemId = 798,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
@@ -3541,7 +3541,7 @@ local weapons = {
},
{
-- energy blacksteel sword
- itemid = 797,
+ itemId = 797,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
@@ -3553,7 +3553,7 @@ local weapons = {
},
{
-- energy mystic blade
- itemid = 796,
+ itemId = 796,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
@@ -3561,7 +3561,7 @@ local weapons = {
},
{
-- energy relic sword
- itemid = 795,
+ itemId = 795,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -3569,13 +3569,13 @@ local weapons = {
},
{
-- energy spike sword
- itemid = 794,
+ itemId = 794,
type = WEAPON_SWORD,
action = "removecharge",
},
{
-- earth war hammer
- itemid = 793,
+ itemId = 793,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -3587,7 +3587,7 @@ local weapons = {
},
{
-- earth orcish maul
- itemid = 792,
+ itemId = 792,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3595,7 +3595,7 @@ local weapons = {
},
{
-- earth cranial basher
- itemid = 791,
+ itemId = 791,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
@@ -3603,7 +3603,7 @@ local weapons = {
},
{
-- earth crystal mace
- itemid = 790,
+ itemId = 790,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3611,7 +3611,7 @@ local weapons = {
},
{
-- earth clerical mace
- itemid = 789,
+ itemId = 789,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
@@ -3619,7 +3619,7 @@ local weapons = {
},
{
-- earth war axe
- itemid = 788,
+ itemId = 788,
type = WEAPON_AXE,
level = 65,
unproperly = true,
@@ -3631,7 +3631,7 @@ local weapons = {
},
{
-- earth headchopper
- itemid = 787,
+ itemId = 787,
type = WEAPON_AXE,
level = 35,
unproperly = true,
@@ -3643,7 +3643,7 @@ local weapons = {
},
{
-- earth heroic axe
- itemid = 786,
+ itemId = 786,
type = WEAPON_AXE,
level = 60,
unproperly = true,
@@ -3651,7 +3651,7 @@ local weapons = {
},
{
-- earth knight axe
- itemid = 785,
+ itemId = 785,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -3659,7 +3659,7 @@ local weapons = {
},
{
-- earth barbarian axe
- itemid = 784,
+ itemId = 784,
type = WEAPON_AXE,
level = 20,
unproperly = true,
@@ -3667,7 +3667,7 @@ local weapons = {
},
{
-- earth dragon slayer
- itemid = 783,
+ itemId = 783,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
@@ -3679,7 +3679,7 @@ local weapons = {
},
{
-- earth blacksteel sword
- itemid = 782,
+ itemId = 782,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
@@ -3691,7 +3691,7 @@ local weapons = {
},
{
-- earth mystic blade
- itemid = 781,
+ itemId = 781,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
@@ -3699,7 +3699,7 @@ local weapons = {
},
{
-- earth relic sword
- itemid = 780,
+ itemId = 780,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -3707,7 +3707,7 @@ local weapons = {
},
{
-- earth spike sword
- itemid = 779,
+ itemId = 779,
type = WEAPON_SWORD,
action = "removecharge",
},
@@ -3745,7 +3745,7 @@ local weapons = {
},
{
-- icy war hammer
- itemid = 693,
+ itemId = 693,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -3757,7 +3757,7 @@ local weapons = {
},
{
-- icy orcish maul
- itemid = 692,
+ itemId = 692,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3765,7 +3765,7 @@ local weapons = {
},
{
-- icy cranial basher
- itemid = 691,
+ itemId = 691,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
@@ -3773,7 +3773,7 @@ local weapons = {
},
{
-- icy crystal mace
- itemid = 690,
+ itemId = 690,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3781,7 +3781,7 @@ local weapons = {
},
{
-- icy clerical mace
- itemid = 689,
+ itemId = 689,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
@@ -3789,7 +3789,7 @@ local weapons = {
},
{
-- icy war axe
- itemid = 688,
+ itemId = 688,
type = WEAPON_AXE,
level = 65,
unproperly = true,
@@ -3801,7 +3801,7 @@ local weapons = {
},
{
-- icy headchopper
- itemid = 687,
+ itemId = 687,
type = WEAPON_AXE,
level = 35,
unproperly = true,
@@ -3813,7 +3813,7 @@ local weapons = {
},
{
-- icy heroic axe
- itemid = 686,
+ itemId = 686,
type = WEAPON_AXE,
level = 60,
unproperly = true,
@@ -3821,7 +3821,7 @@ local weapons = {
},
{
-- icy knight axe
- itemid = 685,
+ itemId = 685,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -3829,7 +3829,7 @@ local weapons = {
},
{
-- icy barbarian axe
- itemid = 684,
+ itemId = 684,
type = WEAPON_AXE,
level = 20,
unproperly = true,
@@ -3837,7 +3837,7 @@ local weapons = {
},
{
-- icy dragon slayer
- itemid = 683,
+ itemId = 683,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
@@ -3849,7 +3849,7 @@ local weapons = {
},
{
-- icy blacksteel sword
- itemid = 682,
+ itemId = 682,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
@@ -3861,7 +3861,7 @@ local weapons = {
},
{
-- icy mystic blade
- itemid = 681,
+ itemId = 681,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
@@ -3869,7 +3869,7 @@ local weapons = {
},
{
-- icy relic sword
- itemid = 680,
+ itemId = 680,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -3877,13 +3877,13 @@ local weapons = {
},
{
-- icy spike sword
- itemid = 679,
+ itemId = 679,
type = WEAPON_SWORD,
action = "removecharge",
},
{
-- fiery war hammer
- itemid = 674,
+ itemId = 674,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -3895,7 +3895,7 @@ local weapons = {
},
{
-- fiery orcish maul
- itemid = 673,
+ itemId = 673,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3903,7 +3903,7 @@ local weapons = {
},
{
-- fiery cranial basher
- itemid = 672,
+ itemId = 672,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
@@ -3911,7 +3911,7 @@ local weapons = {
},
{
-- fiery crystal mace
- itemid = 671,
+ itemId = 671,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
@@ -3919,7 +3919,7 @@ local weapons = {
},
{
-- fiery clerical mace
- itemid = 670,
+ itemId = 670,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
@@ -3927,7 +3927,7 @@ local weapons = {
},
{
-- fiery war axe
- itemid = 669,
+ itemId = 669,
type = WEAPON_AXE,
level = 65,
unproperly = true,
@@ -3939,7 +3939,7 @@ local weapons = {
},
{
-- fiery headchopper
- itemid = 668,
+ itemId = 668,
type = WEAPON_AXE,
level = 35,
unproperly = true,
@@ -3951,7 +3951,7 @@ local weapons = {
},
{
-- fiery heroic axe
- itemid = 667,
+ itemId = 667,
type = WEAPON_AXE,
level = 60,
unproperly = true,
@@ -3959,7 +3959,7 @@ local weapons = {
},
{
-- fiery knight axe
- itemid = 666,
+ itemId = 666,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -3967,7 +3967,7 @@ local weapons = {
},
{
-- fiery barbarian axe
- itemid = 665,
+ itemId = 665,
type = WEAPON_AXE,
level = 20,
unproperly = true,
@@ -3975,7 +3975,7 @@ local weapons = {
},
{
-- fiery dragon slayer
- itemid = 664,
+ itemId = 664,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
@@ -3987,7 +3987,7 @@ local weapons = {
},
{
-- fiery blacksteel sword
- itemid = 663,
+ itemId = 663,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
@@ -3999,7 +3999,7 @@ local weapons = {
},
{
-- fiery mystic blade
- itemid = 662,
+ itemId = 662,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
@@ -4007,7 +4007,7 @@ local weapons = {
},
{
-- fiery relic sword
- itemid = 661,
+ itemId = 661,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -4015,27 +4015,27 @@ local weapons = {
},
{
-- fiery spike sword
- itemid = 660,
+ itemId = 660,
type = WEAPON_SWORD,
action = "removecharge",
},
{
-- noble axe
- itemid = 7456,
+ itemId = 7456,
type = WEAPON_AXE,
level = 35,
unproperly = true,
},
{
-- mythril axe
- itemid = 7455,
+ itemId = 7455,
type = WEAPON_AXE,
level = 80,
unproperly = true,
},
{
-- glorious axe
- itemid = 7454,
+ itemId = 7454,
type = WEAPON_AXE,
level = 30,
unproperly = true,
@@ -4046,7 +4046,7 @@ local weapons = {
},
{
-- executioner
- itemid = 7453,
+ itemId = 7453,
type = WEAPON_AXE,
level = 85,
unproperly = true,
@@ -4057,7 +4057,7 @@ local weapons = {
},
{
-- spiked squelcher
- itemid = 7452,
+ itemId = 7452,
type = WEAPON_CLUB,
level = 30,
unproperly = true,
@@ -4068,14 +4068,14 @@ local weapons = {
},
{
-- shadow sceptre
- itemid = 7451,
+ itemId = 7451,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
},
{
-- hammer of prophecy
- itemid = 7450,
+ itemId = 7450,
type = WEAPON_CLUB,
level = 120,
unproperly = true,
@@ -4086,7 +4086,7 @@ local weapons = {
},
{
-- crystal sword
- itemid = 7449,
+ itemId = 7449,
type = WEAPON_SWORD,
level = 25,
unproperly = true,
@@ -4097,19 +4097,19 @@ local weapons = {
},
{
-- elvish bow
- itemid = 7438,
+ itemId = 7438,
type = WEAPON_DISTANCE,
},
{
-- sapphire hammer
- itemid = 7437,
+ itemId = 7437,
type = WEAPON_CLUB,
level = 30,
unproperly = true,
},
{
-- angelic axe
- itemid = 7436,
+ itemId = 7436,
type = WEAPON_AXE,
level = 45,
unproperly = true,
@@ -4120,56 +4120,56 @@ local weapons = {
},
{
-- impaler
- itemid = 7435,
+ itemId = 7435,
type = WEAPON_AXE,
level = 85,
unproperly = true,
},
{
-- royal axe
- itemid = 7434,
+ itemId = 7434,
type = WEAPON_AXE,
level = 75,
unproperly = true,
},
{
-- ravenwing
- itemid = 7433,
+ itemId = 7433,
type = WEAPON_AXE,
level = 65,
unproperly = true,
},
{
-- furry club
- itemid = 7432,
+ itemId = 7432,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
},
{
-- demonbone
- itemid = 7431,
+ itemId = 7431,
type = WEAPON_CLUB,
level = 80,
unproperly = true,
},
{
-- dragonbone staff
- itemid = 7430,
+ itemId = 7430,
type = WEAPON_CLUB,
level = 30,
unproperly = true,
},
{
-- blessed sceptre
- itemid = 7429,
+ itemId = 7429,
type = WEAPON_CLUB,
level = 75,
unproperly = true,
},
{
-- bonebreaker
- itemid = 7428,
+ itemId = 7428,
type = WEAPON_CLUB,
level = 55,
unproperly = true,
@@ -4180,35 +4180,35 @@ local weapons = {
},
{
-- chaos mace
- itemid = 7427,
+ itemId = 7427,
type = WEAPON_CLUB,
level = 45,
unproperly = true,
},
{
-- amber staff
- itemid = 7426,
+ itemId = 7426,
type = WEAPON_CLUB,
level = 40,
unproperly = true,
},
{
-- taurus mace
- itemid = 7425,
+ itemId = 7425,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
},
{
-- lunar staff
- itemid = 7424,
+ itemId = 7424,
type = WEAPON_CLUB,
level = 30,
unproperly = true,
},
{
-- skullcrusher
- itemid = 7423,
+ itemId = 7423,
type = WEAPON_CLUB,
level = 85,
unproperly = true,
@@ -4219,63 +4219,63 @@ local weapons = {
},
{
-- jade hammer
- itemid = 7422,
+ itemId = 7422,
type = WEAPON_CLUB,
level = 70,
unproperly = true,
},
{
-- onyx flail
- itemid = 7421,
+ itemId = 7421,
type = WEAPON_CLUB,
level = 65,
unproperly = true,
},
{
-- reaper's axe
- itemid = 7420,
+ itemId = 7420,
type = WEAPON_AXE,
level = 70,
unproperly = true,
},
{
-- dreaded cleaver
- itemid = 7419,
+ itemId = 7419,
type = WEAPON_AXE,
level = 40,
unproperly = true,
},
{
-- nightmare blade
- itemid = 7418,
+ itemId = 7418,
type = WEAPON_SWORD,
level = 70,
unproperly = true,
},
{
-- runed sword
- itemid = 7417,
+ itemId = 7417,
type = WEAPON_SWORD,
level = 65,
unproperly = true,
},
{
-- bloody edge
- itemid = 7416,
+ itemId = 7416,
type = WEAPON_SWORD,
level = 55,
unproperly = true,
},
{
-- cranial basher
- itemid = 7415,
+ itemId = 7415,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
},
{
-- abyss hammer
- itemid = 7414,
+ itemId = 7414,
type = WEAPON_CLUB,
level = 60,
unproperly = true,
@@ -4286,7 +4286,7 @@ local weapons = {
},
{
-- titan axe
- itemid = 7413,
+ itemId = 7413,
type = WEAPON_AXE,
level = 40,
unproperly = true,
@@ -4297,42 +4297,42 @@ local weapons = {
},
{
-- butcher's axe
- itemid = 7412,
+ itemId = 7412,
type = WEAPON_AXE,
level = 45,
unproperly = true,
},
{
-- ornamented axe
- itemid = 7411,
+ itemId = 7411,
type = WEAPON_AXE,
level = 50,
unproperly = true,
},
{
-- queen's sceptre
- itemid = 7410,
+ itemId = 7410,
type = WEAPON_CLUB,
level = 55,
unproperly = true,
},
{
-- northern star
- itemid = 7409,
+ itemId = 7409,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
},
{
-- wyvern fang
- itemid = 7408,
+ itemId = 7408,
type = WEAPON_SWORD,
level = 25,
unproperly = true,
},
{
-- haunted blade
- itemid = 7407,
+ itemId = 7407,
type = WEAPON_SWORD,
level = 30,
unproperly = true,
@@ -4343,7 +4343,7 @@ local weapons = {
},
{
-- blacksteel sword
- itemid = 7406,
+ itemId = 7406,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
@@ -4354,7 +4354,7 @@ local weapons = {
},
{
-- havoc blade
- itemid = 7405,
+ itemId = 7405,
type = WEAPON_SWORD,
level = 70,
unproperly = true,
@@ -4365,14 +4365,14 @@ local weapons = {
},
{
-- assassin dagger
- itemid = 7404,
+ itemId = 7404,
type = WEAPON_SWORD,
level = 40,
unproperly = true,
},
{
-- berserker
- itemid = 7403,
+ itemId = 7403,
type = WEAPON_SWORD,
level = 65,
unproperly = true,
@@ -4383,7 +4383,7 @@ local weapons = {
},
{
-- dragon slayer
- itemid = 7402,
+ itemId = 7402,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
@@ -4394,14 +4394,14 @@ local weapons = {
},
{
-- orcish maul
- itemid = 7392,
+ itemId = 7392,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
},
{
-- thaian sword
- itemid = 7391,
+ itemId = 7391,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
@@ -4412,35 +4412,35 @@ local weapons = {
},
{
-- the justice seeker
- itemid = 7390,
+ itemId = 7390,
type = WEAPON_SWORD,
level = 75,
unproperly = true,
},
{
-- heroic axe
- itemid = 7389,
+ itemId = 7389,
type = WEAPON_AXE,
level = 60,
unproperly = true,
},
{
-- vile axe
- itemid = 7388,
+ itemId = 7388,
type = WEAPON_AXE,
level = 55,
unproperly = true,
},
{
-- diamond sceptre
- itemid = 7387,
+ itemId = 7387,
type = WEAPON_CLUB,
level = 25,
unproperly = true,
},
{
-- mercenary sword
- itemid = 7386,
+ itemId = 7386,
type = WEAPON_SWORD,
level = 40,
unproperly = true,
@@ -4451,28 +4451,28 @@ local weapons = {
},
{
-- crimson sword
- itemid = 7385,
+ itemId = 7385,
type = WEAPON_SWORD,
level = 20,
unproperly = true,
},
{
-- mystic blade
- itemid = 7384,
+ itemId = 7384,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
},
{
-- relic sword
- itemid = 7383,
+ itemId = 7383,
type = WEAPON_SWORD,
level = 50,
unproperly = true,
},
{
-- demonrage sword
- itemid = 7382,
+ itemId = 7382,
type = WEAPON_SWORD,
level = 60,
unproperly = true,
@@ -4483,14 +4483,14 @@ local weapons = {
},
{
-- mammoth whopper
- itemid = 7381,
+ itemId = 7381,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
},
{
-- headchopper
- itemid = 7380,
+ itemId = 7380,
type = WEAPON_AXE,
level = 35,
unproperly = true,
@@ -4501,7 +4501,7 @@ local weapons = {
},
{
-- brutetamer's staff
- itemid = 7379,
+ itemId = 7379,
type = WEAPON_CLUB,
level = 25,
unproperly = true,
@@ -4548,7 +4548,7 @@ local weapons = {
},
{
-- piercing bolt
- itemid = 7363,
+ itemId = 7363,
type = WEAPON_AMMO,
level = 30,
unproperly = true,
@@ -4556,7 +4556,7 @@ local weapons = {
},
{
-- ruthless axe
- itemid = 6553,
+ itemId = 6553,
type = WEAPON_AXE,
level = 75,
unproperly = true,
@@ -4567,7 +4567,7 @@ local weapons = {
},
{
-- infernal bolt
- itemid = 6528,
+ itemId = 6528,
type = WEAPON_AMMO,
level = 110,
unproperly = true,
@@ -4575,7 +4575,7 @@ local weapons = {
},
{
-- the avenger
- itemid = 6527,
+ itemId = 6527,
type = WEAPON_SWORD,
level = 75,
unproperly = true,
@@ -4586,12 +4586,12 @@ local weapons = {
},
{
-- Ron the Ripper's sabre
- itemid = 6101,
+ itemId = 6101,
type = WEAPON_SWORD,
},
{
-- arbalest
- itemid = 5803,
+ itemId = 5803,
type = WEAPON_DISTANCE,
level = 75,
unproperly = true,
@@ -4602,79 +4602,79 @@ local weapons = {
},
{
-- banana staff
- itemid = 3348,
+ itemId = 3348,
type = WEAPON_CLUB,
},
- {
- -- hunting spear
- itemid = 3347,
- type = WEAPON_MISSILE,
- level = 20,
- unproperly = true,
- breakchance = 6,
- },
+ -- {
+ -- -- hunting spear
+ -- itemId = 3347,
+ -- type = WEAPON_MISSILE,
+ -- level = 20,
+ -- unproperly = true,
+ -- breakchance = 6
+ -- },
{
-- ripper lance
- itemid = 3346,
+ itemId = 3346,
type = WEAPON_AXE,
},
{
-- templar scytheblade
- itemid = 3345,
+ itemId = 3345,
type = WEAPON_SWORD,
},
{
-- beastslayer axe
- itemid = 3344,
+ itemId = 3344,
type = WEAPON_AXE,
level = 30,
unproperly = true,
},
{
-- lich staff
- itemid = 3343,
+ itemId = 3343,
type = WEAPON_CLUB,
level = 40,
unproperly = true,
},
{
-- scythe
- itemid = 3453,
+ itemId = 3453,
type = WEAPON_CLUB,
},
{
-- power bolt
- itemid = 3450,
+ itemId = 3450,
type = WEAPON_AMMO,
level = 55,
unproperly = true,
action = "removecount",
},
- {
- -- arrow
- itemid = 3447,
- type = WEAPON_AMMO,
- action = "removecount",
- },
+ -- {
+ -- -- -- arrow
+ -- -- itemId = 3447,
+ -- -- type = WEAPON_AMMO,
+ -- -- -- action = "removecount"
+ -- },
{
-- bolt
- itemid = 3446,
+ itemId = 3446,
type = WEAPON_AMMO,
- action = "removecount",
+ -- action = "removecount"
},
{
-- bow
- itemid = 3350,
+ itemId = 3350,
type = WEAPON_DISTANCE,
},
{
-- crossbow
- itemid = 3349,
+ itemId = 3349,
type = WEAPON_DISTANCE,
},
{
-- war axe
- itemid = 3342,
+ itemId = 3342,
type = WEAPON_AXE,
level = 65,
unproperly = true,
@@ -4685,43 +4685,43 @@ local weapons = {
},
{
-- arcane staff
- itemid = 3341,
+ itemId = 3341,
type = WEAPON_CLUB,
level = 75,
unproperly = true,
},
{
-- heavy mace
- itemid = 3340,
+ itemId = 3340,
type = WEAPON_CLUB,
level = 70,
unproperly = true,
},
{
-- djinn blade
- itemid = 3339,
+ itemId = 3339,
type = WEAPON_SWORD,
level = 35,
unproperly = true,
},
{
-- bone sword
- itemid = 3338,
+ itemId = 3338,
type = WEAPON_SWORD,
},
{
-- bone club
- itemid = 3337,
+ itemId = 3337,
type = WEAPON_CLUB,
},
{
-- studded club
- itemid = 3336,
+ itemId = 3336,
type = WEAPON_CLUB,
},
{
-- twin axe
- itemid = 3335,
+ itemId = 3335,
type = WEAPON_AXE,
level = 50,
unproperly = true,
@@ -4732,21 +4732,21 @@ local weapons = {
},
{
-- pharaoh sword
- itemid = 3334,
+ itemId = 3334,
type = WEAPON_SWORD,
level = 45,
unproperly = true,
},
{
-- crystal mace
- itemid = 3333,
+ itemId = 3333,
type = WEAPON_CLUB,
level = 35,
unproperly = true,
},
{
-- hammer of wrath
- itemid = 3332,
+ itemId = 3332,
type = WEAPON_CLUB,
level = 65,
unproperly = true,
@@ -4757,24 +4757,24 @@ local weapons = {
},
{
-- ravager's axe
- itemid = 3331,
+ itemId = 3331,
type = WEAPON_AXE,
level = 70,
unproperly = true,
},
{
-- heavy machete
- itemid = 3330,
+ itemId = 3330,
type = WEAPON_SWORD,
},
{
-- daramian axe
- itemid = 3329,
+ itemId = 3329,
type = WEAPON_AXE,
},
{
-- daramian waraxe
- itemid = 3328,
+ itemId = 3328,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -4785,155 +4785,155 @@ local weapons = {
},
{
-- daramian mace
- itemid = 3327,
+ itemId = 3327,
type = WEAPON_CLUB,
},
{
-- epee
- itemid = 3326,
+ itemId = 3326,
type = WEAPON_SWORD,
level = 30,
unproperly = true,
},
{
-- light mace
- itemid = 3325,
+ itemId = 3325,
type = WEAPON_CLUB,
},
{
-- skull staff
- itemid = 3324,
+ itemId = 3324,
type = WEAPON_CLUB,
level = 30,
unproperly = true,
},
{
-- dwarven axe
- itemid = 3323,
+ itemId = 3323,
type = WEAPON_AXE,
level = 20,
unproperly = true,
},
{
-- dragon hammer
- itemid = 3322,
+ itemId = 3322,
type = WEAPON_CLUB,
level = 25,
unproperly = true,
},
{
-- enchanted staff
- itemid = 3321,
+ itemId = 3321,
type = WEAPON_CLUB,
},
{
-- fire axe
- itemid = 3320,
+ itemId = 3320,
type = WEAPON_AXE,
level = 35,
unproperly = true,
},
{
-- stonecutter axe
- itemid = 3319,
+ itemId = 3319,
type = WEAPON_AXE,
level = 90,
unproperly = true,
},
{
-- knight axe
- itemid = 3318,
+ itemId = 3318,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- barbarian axe
- itemid = 3317,
+ itemId = 3317,
type = WEAPON_AXE,
level = 20,
unproperly = true,
},
{
-- orcish axe
- itemid = 3316,
+ itemId = 3316,
type = WEAPON_AXE,
},
{
-- guardian halberd
- itemid = 3315,
+ itemId = 3315,
type = WEAPON_AXE,
level = 55,
unproperly = true,
},
{
-- naginata
- itemid = 3314,
+ itemId = 3314,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- obsidian lance
- itemid = 3313,
+ itemId = 3313,
type = WEAPON_AXE,
level = 20,
unproperly = true,
},
{
-- silver mace
- itemid = 3312,
+ itemId = 3312,
type = WEAPON_CLUB,
level = 45,
unproperly = true,
},
{
-- clerical mace
- itemid = 3311,
+ itemId = 3311,
type = WEAPON_CLUB,
level = 20,
unproperly = true,
},
{
-- iron hammer
- itemid = 3310,
+ itemId = 3310,
type = WEAPON_CLUB,
},
{
-- thunder hammer
- itemid = 3309,
+ itemId = 3309,
type = WEAPON_CLUB,
level = 85,
unproperly = true,
},
{
-- machete
- itemid = 3308,
+ itemId = 3308,
type = WEAPON_SWORD,
},
{
-- scimitar
- itemid = 3307,
+ itemId = 3307,
type = WEAPON_SWORD,
},
{
-- golden sickle
- itemid = 3306,
+ itemId = 3306,
type = WEAPON_AXE,
},
{
-- battle hammer
- itemid = 3305,
+ itemId = 3305,
type = WEAPON_CLUB,
},
{
-- crowbar
- itemid = 3304,
+ itemId = 3304,
type = WEAPON_CLUB,
},
{
-- great axe
- itemid = 3303,
+ itemId = 3303,
type = WEAPON_AXE,
level = 95,
unproperly = true,
@@ -4944,14 +4944,14 @@ local weapons = {
},
{
-- dragon lance
- itemid = 3302,
+ itemId = 3302,
type = WEAPON_AXE,
level = 60,
unproperly = true,
},
{
-- broadsword
- itemid = 3301,
+ itemId = 3301,
type = WEAPON_SWORD,
vocation = {
{ "Knight", true },
@@ -4960,7 +4960,7 @@ local weapons = {
},
{
-- katana
- itemid = 3300,
+ itemId = 3300,
type = WEAPON_SWORD,
},
{
@@ -4976,12 +4976,12 @@ local weapons = {
},
{
-- serpent sword
- itemid = 3297,
+ itemId = 3297,
type = WEAPON_SWORD,
},
{
-- warlord sword
- itemid = 3296,
+ itemId = 3296,
type = WEAPON_SWORD,
level = 120,
unproperly = true,
@@ -4992,83 +4992,81 @@ local weapons = {
},
{
-- bright sword
- itemid = 3295,
+ itemId = 3295,
type = WEAPON_SWORD,
- level = 30,
- unproperly = true,
},
{
-- short sword
- itemid = 3294,
+ itemId = 3294,
type = WEAPON_SWORD,
},
{
-- sickle
- itemid = 3293,
+ itemId = 3293,
type = WEAPON_AXE,
},
{
-- combat knife
- itemid = 3292,
+ itemId = 3292,
type = WEAPON_SWORD,
},
{
-- knife
- itemid = 3291,
+ itemId = 3291,
type = WEAPON_SWORD,
},
{
-- silver dagger
- itemid = 3290,
+ itemId = 3290,
type = WEAPON_SWORD,
},
{
-- staff
- itemid = 3289,
+ itemId = 3289,
type = WEAPON_CLUB,
},
{
-- magic sword
- itemid = 3288,
+ itemId = 3288,
type = WEAPON_SWORD,
level = 80,
unproperly = true,
},
- {
- -- throwing star
- itemid = 3287,
- type = WEAPON_MISSILE,
- breakchance = 10,
- },
+ -- {
+ -- -- throwing star
+ -- itemId = 3287,
+ -- type = WEAPON_MISSILE,
+ -- breakchance = 10
+ -- },
{
-- mace
- itemid = 3286,
+ itemId = 3286,
type = WEAPON_CLUB,
},
{
-- longsword
- itemid = 3285,
+ itemId = 3285,
type = WEAPON_SWORD,
},
{
-- ice rapier
- itemid = 3284,
+ itemId = 3284,
type = WEAPON_SWORD,
action = "removecharge",
},
{
-- carlin sword
- itemid = 3283,
+ itemId = 3283,
type = WEAPON_SWORD,
},
{
-- morning star
- itemid = 3282,
+ itemId = 3282,
type = WEAPON_CLUB,
},
{
-- giant sword
- itemid = 3281,
+ itemId = 3281,
type = WEAPON_SWORD,
level = 55,
unproperly = true,
@@ -5079,14 +5077,14 @@ local weapons = {
},
{
-- fire sword
- itemid = 3280,
+ itemId = 3280,
type = WEAPON_SWORD,
level = 30,
unproperly = true,
},
{
-- war hammer
- itemid = 3279,
+ itemId = 3279,
type = WEAPON_CLUB,
level = 50,
unproperly = true,
@@ -5097,7 +5095,7 @@ local weapons = {
},
{
-- magic longsword
- itemid = 3278,
+ itemId = 3278,
type = WEAPON_SWORD,
level = 140,
unproperly = true,
@@ -5106,20 +5104,20 @@ local weapons = {
{ "Elite Knight" },
},
},
- {
- -- spear
- itemid = 3277,
- type = WEAPON_MISSILE,
- breakchance = 3,
- },
+ -- {
+ -- -- spear
+ -- itemId = 3277,
+ -- type = WEAPON_MISSILE,
+ -- -- breakchance = 3
+ -- },
{
-- hatchet
- itemid = 3276,
+ itemId = 3276,
type = WEAPON_AXE,
},
{
-- double axe
- itemid = 3275,
+ itemId = 3275,
type = WEAPON_AXE,
level = 25,
unproperly = true,
@@ -5130,49 +5128,49 @@ local weapons = {
},
{
-- axe
- itemid = 3274,
+ itemId = 3274,
type = WEAPON_AXE,
},
{
-- sabre
- itemid = 3273,
+ itemId = 3273,
type = WEAPON_SWORD,
},
{
-- rapier
- itemid = 3272,
+ itemId = 3272,
type = WEAPON_SWORD,
},
{
-- spike sword
- itemid = 3271,
+ itemId = 3271,
type = WEAPON_SWORD,
},
{
-- club
- itemid = 3270,
+ itemId = 3270,
type = WEAPON_CLUB,
},
{
-- halberd
- itemid = 3269,
+ itemId = 3269,
type = WEAPON_AXE,
level = 25,
unproperly = true,
},
{
-- hand axe
- itemid = 3268,
+ itemId = 3268,
type = WEAPON_AXE,
},
{
-- dagger
- itemid = 3267,
+ itemId = 3267,
type = WEAPON_SWORD,
},
{
-- battle axe
- itemid = 3266,
+ itemId = 3266,
type = WEAPON_AXE,
unproperly = true,
vocation = {
@@ -5182,7 +5180,7 @@ local weapons = {
},
{
-- two handed sword
- itemid = 3265,
+ itemId = 3265,
type = WEAPON_SWORD,
level = 20,
unproperly = true,
@@ -5193,17 +5191,17 @@ local weapons = {
},
{
-- sword
- itemid = 3264,
+ itemId = 3264,
type = WEAPON_SWORD,
},
{
-- giant smithhammer
- itemid = 3208,
+ itemId = 3208,
type = WEAPON_CLUB,
},
{
-- wand of dragonbreath
- itemid = 3075,
+ itemId = 3075,
type = WEAPON_WAND,
wandType = "fire",
level = 13,
@@ -5216,7 +5214,7 @@ local weapons = {
},
{
-- wand of vortex
- itemid = 3074,
+ itemId = 3074,
type = WEAPON_WAND,
wandType = "energy",
level = 6,
@@ -5229,7 +5227,7 @@ local weapons = {
},
{
-- wand of cosmic energy
- itemid = 3073,
+ itemId = 3073,
type = WEAPON_WAND,
wandType = "energy",
level = 26,
@@ -5242,7 +5240,7 @@ local weapons = {
},
{
-- wand of decay
- itemid = 3072,
+ itemId = 3072,
type = WEAPON_WAND,
wandType = "death",
level = 19,
@@ -5255,7 +5253,7 @@ local weapons = {
},
{
-- wand of inferno
- itemid = 3071,
+ itemId = 3071,
type = WEAPON_WAND,
wandType = "fire",
level = 33,
@@ -5268,7 +5266,7 @@ local weapons = {
},
{
-- moonlight rod
- itemid = 3070,
+ itemId = 3070,
type = WEAPON_WAND,
wandType = "ice",
level = 13,
@@ -5281,7 +5279,7 @@ local weapons = {
},
{
-- necrotic rod
- itemid = 3069,
+ itemId = 3069,
type = WEAPON_WAND,
wandType = "death",
level = 19,
@@ -5294,7 +5292,7 @@ local weapons = {
},
{
-- hailstorm rod
- itemid = 3067,
+ itemId = 3067,
type = WEAPON_WAND,
wandType = "ice",
level = 33,
@@ -5307,7 +5305,7 @@ local weapons = {
},
{
-- snakebit rod
- itemid = 3066,
+ itemId = 3066,
type = WEAPON_WAND,
wandType = "earth",
level = 6,
@@ -5320,7 +5318,7 @@ local weapons = {
},
{
-- terra rod
- itemid = 3065,
+ itemId = 3065,
type = WEAPON_WAND,
wandType = "earth",
level = 26,
diff --git a/data/XML/imbuements.xml b/data/XML/imbuements.xml
index 867bab66f52..74a06dbd9f8 100644
--- a/data/XML/imbuements.xml
+++ b/data/XML/imbuements.xml
@@ -155,18 +155,18 @@
-
+
-
+
-
+
diff --git a/data/XML/vocations.xml b/data/XML/vocations.xml
index fd5ae77a31e..8cbf8e4ecd8 100644
--- a/data/XML/vocations.xml
+++ b/data/XML/vocations.xml
@@ -12,7 +12,7 @@
-
+
@@ -24,7 +24,7 @@
-
+
@@ -36,7 +36,7 @@
-
+
@@ -48,7 +48,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
@@ -71,8 +71,11 @@
+
+
+
-
+
@@ -83,8 +86,11 @@
+
+
+
-
+
@@ -95,8 +101,11 @@
+
+
+
-
+
@@ -107,5 +116,8 @@
+
+
+
diff --git a/data/global.lua b/data/global.lua
index 3508d7fbd44..2d5ffc31cf0 100644
--- a/data/global.lua
+++ b/data/global.lua
@@ -202,7 +202,7 @@ function addStamina(playerId, ...)
local regen = configManager.getNumber(configKeys.STAMINA_PZ_GAIN)
player:setStamina(player:getStamina() + regen)
- player:sendTextMessage(MESSAGE_STATUS, string.format("%i minute%s of stamina has been refilled.", regen, regen == 1 and "" or "s"))
+ player:sendTextMessage(MESSAGE_FAILURE, string.format("%i minute%s of stamina has been refilled.", regen, regen == 1 and "" or "s"))
staminaBonus.eventsPz[localPlayerId] = addEvent(addStamina, delay, nil, localPlayerId, delay)
return true
end
diff --git a/data/items/appearances.dat b/data/items/appearances.dat
index 9f0de0ece0e..0c609c42b9c 100644
Binary files a/data/items/appearances.dat and b/data/items/appearances.dat differ
diff --git a/data/items/items.xml b/data/items/items.xml
index abbe27c060c..c57bafaf745 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -12136,21 +12136,18 @@
-
-
-
-
-
-
@@ -12158,7 +12155,6 @@
-
-
@@ -12166,7 +12162,6 @@
-
-
@@ -12215,7 +12210,6 @@
-
-
@@ -15581,8 +15575,8 @@
-
-
-
+
+
-
@@ -21338,22 +21332,22 @@
-
-
+
-
-
+
-
-
+
-
-
+
-
@@ -24998,7 +24992,10 @@
-
-
+ -
+
+
+
-
@@ -36642,8 +36639,8 @@
-
- -
+
+ -
-
@@ -37181,10 +37178,10 @@
-
- -
+
-
- -
+
-
-
@@ -37360,18 +37357,18 @@
- -
+
-
- -
+
-
-
- -
+
+ -
-
- -
+
+ -
@@ -37425,18 +37422,18 @@
- -
+
-
- -
+
-
-
- -
+
+ -
-
- -
+
+ -
@@ -38282,7 +38279,6 @@
-
-
@@ -38735,19 +38731,19 @@
-
- -
+
-
- -
+
-
- -
+
-
- -
+
-
@@ -38802,7 +38798,7 @@
-
-
+
-
@@ -40182,7 +40178,7 @@
- -
+
-
-
@@ -40633,7 +40629,7 @@
-
- -
+
-
@@ -41020,7 +41016,7 @@
-
- -
+
-
-
@@ -44514,8 +44510,8 @@
-
-
+
+
@@ -49514,8 +49510,8 @@
-
-
+
+
@@ -51966,8 +51962,8 @@
-
-
+
+
@@ -55309,8 +55305,8 @@
-
-
+
+
@@ -55351,7 +55347,7 @@
-
+
@@ -58332,8 +58328,8 @@
-
-
+
+
@@ -58370,8 +58366,8 @@
-
-
+
+
@@ -58408,8 +58404,8 @@
-
-
+
+
@@ -58427,8 +58423,8 @@
-
-
+
+
@@ -58449,8 +58445,8 @@
-
-
+
+
@@ -58469,8 +58465,8 @@
-
-
+
+
@@ -58818,8 +58814,8 @@
-
-
+
+
@@ -60214,7 +60210,7 @@
-
-
+
-
@@ -61664,7 +61660,7 @@
-
-
+
@@ -61683,7 +61679,7 @@
-
-
+
@@ -65139,6 +65135,9 @@
+ -
+
+
-
@@ -65333,7 +65332,7 @@
-
-
+
@@ -65366,7 +65365,7 @@
-
-
+
@@ -67594,6 +67593,11 @@
+ -
+
+
+
+
-
@@ -68532,6 +68536,73 @@
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
-
@@ -68544,6 +68615,173 @@
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
-
@@ -68645,8 +68883,8 @@
-
-
+
+
@@ -68663,8 +68901,8 @@
-
-
+
+
@@ -68681,8 +68919,8 @@
-
-
+
+
@@ -68699,8 +68937,8 @@
-
-
+
+
@@ -68717,8 +68955,8 @@
-
-
+
+
@@ -68735,8 +68973,8 @@
-
-
+
+
@@ -68762,8 +69000,8 @@
-
-
+
+
@@ -68783,8 +69021,8 @@
-
-
+
+
@@ -68804,8 +69042,8 @@
-
-
+
+
@@ -68825,8 +69063,8 @@
-
-
+
+
@@ -68859,8 +69097,8 @@
-
-
+
+
@@ -68876,8 +69114,8 @@
-
-
+
+
@@ -68906,8 +69144,8 @@
-
-
+
+
@@ -68923,8 +69161,8 @@
-
-
+
+
@@ -68949,6 +69187,44 @@
+ -
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
-
@@ -68973,4 +69249,76 @@
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
diff --git a/data/libs/features_lib.lua b/data/libs/features_lib.lua
new file mode 100644
index 00000000000..3b8f283e89a
--- /dev/null
+++ b/data/libs/features_lib.lua
@@ -0,0 +1,36 @@
+Features = {
+ AutoLoot = "autoloot",
+}
+
+local function validateFeature(feature)
+ local found = false
+ for _, v in pairs(Features) do
+ if v == feature then
+ found = true
+ end
+ end
+ if not found then
+ error("Invalid feature: " .. feature)
+ end
+end
+
+function Player:hasFeature(feature)
+ validateFeature(feature)
+ local kv = self:kv():scoped("features")
+ if kv:get(feature) then
+ return true
+ end
+ return false
+end
+
+function Player:getFeature(feature)
+ validateFeature(feature)
+ local kv = self:kv():scoped("features")
+ return kv:get(feature)
+end
+
+function Player:setFeature(feature, value)
+ validateFeature(feature)
+ local kv = self:kv():scoped("features")
+ kv:set(feature, value)
+end
diff --git a/data/libs/forge_lib.lua b/data/libs/forge_lib.lua
index e232c60fe2d..986d9bf4a92 100644
--- a/data/libs/forge_lib.lua
+++ b/data/libs/forge_lib.lua
@@ -45,7 +45,7 @@ function ForgeMonster:onDeath(creature, corpse, killer, mostDamageKiller, unjust
return true
end
- local forgeAmountMultiplier = (configManager.getNumber(configKeys.FORGE_AMOUNT_MULTIPLIER) or 3)
+ local forgeAmountMultiplier = (configManager.getFloat(configKeys.FORGE_AMOUNT_MULTIPLIER) or 3)
local stack = creature:getForgeStack()
if stack > 0 then
diff --git a/data/libs/functions/gematelier.lua b/data/libs/functions/gematelier.lua
new file mode 100644
index 00000000000..23b3f8b8f73
--- /dev/null
+++ b/data/libs/functions/gematelier.lua
@@ -0,0 +1,91 @@
+local config = {
+ lesser = {
+ names = {
+ "lesser guardian gem",
+ "lesser marksman gem",
+ "lesser sage gem",
+ "lesser mystic gem",
+ },
+ chance = {
+ influenced = 9000,
+ fiendish = 3000,
+ archfoe = 0,
+ },
+ maxCount = 2,
+ },
+ regular = {
+ names = {
+ "guardian gem",
+ "marksman gem",
+ "sage gem",
+ "mystic gem",
+ },
+ chance = {
+ influenced = 0,
+ fiendish = 3000,
+ archfoe = 9000,
+ },
+ maxCount = 2,
+ },
+ greater = {
+ names = {
+ "greater guardian gem",
+ "greater marksman gem",
+ "greater sage gem",
+ "greater mystic gem",
+ },
+ chance = {
+ influenced = 0,
+ fiendish = 9000,
+ archfoe = 3000,
+ },
+ maxCount = 1,
+ },
+}
+
+function Monster:generateGemAtelierLoot()
+ local mType = self:getType()
+ if not mType then
+ return {}
+ end
+ local category = "none"
+ local forgeClassification = self:getMonsterForgeClassification()
+ if forgeClassification == FORGE_INFLUENCED_MONSTER then
+ category = "influenced"
+ elseif forgeClassification == FORGE_FIENDISH_MONSTER then
+ category = "fiendish"
+ elseif (mType:bossRace() or ""):lower() == "archfoe" then
+ category = "archfoe"
+ end
+ if category == "none" then
+ return {}
+ end
+
+ local loot = {}
+ for _, gemConfig in pairs(config) do
+ local chance = gemConfig.chance[category] or 0
+ local names = gemConfig.names
+ local maxCount = gemConfig.maxCount
+ if chance > 0 then
+ for i = 1, maxCount do
+ local roll = math.random(1, 100000)
+ if roll > chance then
+ goto continue
+ end
+
+ local name = names[math.random(1, #names)]
+ local itemType = ItemType(name)
+ if not itemType then
+ goto continue
+ end
+ if loot[itemType:getId()] then
+ loot[itemType:getId()].count = loot[itemType:getId()].count + 1
+ else
+ loot[itemType:getId()] = { count = 1 }
+ end
+ end
+ end
+ ::continue::
+ end
+ return loot
+end
diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua
index bedbbe84fe1..ef939a35fa7 100644
--- a/data/libs/functions/load.lua
+++ b/data/libs/functions/load.lua
@@ -5,6 +5,7 @@ dofile(CORE_DIRECTORY .. "/libs/functions/constants.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/container.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/creature.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/fs.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/game.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/item.lua")
diff --git a/data/libs/functions/player.lua b/data/libs/functions/player.lua
index 94e5f848603..f9940570c1d 100644
--- a/data/libs/functions/player.lua
+++ b/data/libs/functions/player.lua
@@ -1,10 +1,6 @@
-- Functions from The Forgotten Server
local foodCondition = Condition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
-local function firstToUpper(str)
- return (str:gsub("^%l", string.upper))
-end
-
function Player.feed(self, food)
local condition = self:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
if condition then
@@ -322,7 +318,7 @@ function Player.getMarriageDescription(thing)
if self == thing then
descr = descr .. " You are "
else
- descr = descr .. " " .. firstToUpper(thing:getSubjectPronoun()) .. " " .. thing:getSubjectVerb() .. " "
+ descr = descr .. " " .. thing:getSubjectPronoun():titleCase() .. " " .. thing:getSubjectVerb() .. " "
end
descr = descr .. "married to " .. getPlayerNameById(playerSpouse) .. "."
end
@@ -538,19 +534,17 @@ function Player.updateHazard(self)
return true
end
+ self:setHazardSystemPoints(0)
for _, zone in pairs(zones) do
local hazard = Hazard.getByName(zone:getName())
- if not hazard then
- self:setHazardSystemPoints(0)
+ if hazard then
+ if self:getParty() then
+ self:getParty():refreshHazard()
+ else
+ self:setHazardSystemPoints(hazard:getPlayerCurrentLevel(self))
+ end
return true
end
-
- if self:getParty() then
- self:getParty():refreshHazard()
- else
- self:setHazardSystemPoints(hazard:getPlayerCurrentLevel(self))
- end
- return true
end
return true
end
diff --git a/data/libs/functions/tables.lua b/data/libs/functions/tables.lua
index 1c798ecb2e0..13fdb3e271f 100644
--- a/data/libs/functions/tables.lua
+++ b/data/libs/functions/tables.lua
@@ -102,6 +102,14 @@ function table.unserialize(str)
return loadstring("return " .. str)()
end
+function table.shallowCopy(oldTable)
+ local newTable = {}
+ for k, v in pairs(oldTable) do
+ newTable[k] = v
+ end
+ return newTable
+end
+
function pairsByKeys(t, f)
local a = {}
for n in pairs(t) do
diff --git a/data/libs/hazard_lib.lua b/data/libs/hazard_lib.lua
index f9338299e79..a1501b2d1fd 100644
--- a/data/libs/hazard_lib.lua
+++ b/data/libs/hazard_lib.lua
@@ -100,16 +100,7 @@ function Hazard:setPlayerCurrentLevel(player, level)
if not zones then
return true
end
- for _, zone in ipairs(zones) do
- local hazard = Hazard.getByName(zone:getName())
- if hazard then
- if hazard == self then
- player:setHazardSystemPoints(level)
- else
- player:setHazardSystemPoints(0)
- end
- end
- end
+ player:updateHazard()
return true
end
diff --git a/data/libs/libs.lua b/data/libs/libs.lua
index bd6164111de..a3f9a3933e3 100644
--- a/data/libs/libs.lua
+++ b/data/libs/libs.lua
@@ -33,3 +33,4 @@ dofile(CORE_DIRECTORY .. "/libs/encounters_lib.lua")
dofile(CORE_DIRECTORY .. "/libs/raids_lib.lua")
dofile(CORE_DIRECTORY .. "/libs/concoctions_lib.lua")
dofile(CORE_DIRECTORY .. "/libs/kill_lib.lua")
+dofile(CORE_DIRECTORY .. "/libs/features_lib.lua")
diff --git a/data/scripts/discord_webhook/discord_webhook.lua b/data/scripts/discord_webhook/discord_webhook.lua
index 0aed824b278..f763ed90d10 100644
--- a/data/scripts/discord_webhook/discord_webhook.lua
+++ b/data/scripts/discord_webhook/discord_webhook.lua
@@ -2,11 +2,15 @@
-- The URL layout is https://discord.com/api/webhooks/:id/:token
-- Leave empty if you wish to disable.
-announcementChannels = {
- ["serverAnnouncements"] = "", -- Used for an announcement channel on your discord
- ["raids"] = "", -- Used to isolate raids on your discord
- ["player-kills"] = "", -- Self-explaining
-}
+if not announcementChannels then
+ announcementChannels = {
+ ["serverAnnouncements"] = "", -- Used for an announcement channel on your discord
+ ["raids"] = "", -- Used to isolate raids on your discord
+ ["player-kills"] = "", -- Self-explaining
+ ["player-levels"] = "", -- Self-explaining
+ ["reports"] = "",
+ }
+end
--[[
Example of notification (After you do the config):
diff --git a/data/scripts/talkactions/gm/broadcast.lua b/data/scripts/talkactions/gm/broadcast.lua
index ca8171e3635..021f218ebe3 100644
--- a/data/scripts/talkactions/gm/broadcast.lua
+++ b/data/scripts/talkactions/gm/broadcast.lua
@@ -1,5 +1,15 @@
local broadcast = TalkAction("/b")
+function Broadcast(text, filter)
+ for _, targetPlayer in ipairs(Game.getPlayers()) do
+ if filter and not filter(targetPlayer) then
+ goto continue
+ end
+ targetPlayer:sendTextMessage(MESSAGE_ADMINISTRADOR, text)
+ ::continue::
+ end
+end
+
function broadcast.onSay(player, words, param)
-- create log
logCommand(player, words, param)
@@ -11,10 +21,7 @@ function broadcast.onSay(player, words, param)
local text = player:getName() .. " broadcasted: " .. param
logger.info(text)
- Webhook.sendMessage("Broadcast", text, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"])
- for _, targetPlayer in ipairs(Game.getPlayers()) do
- targetPlayer:sendPrivateMessage(player, param, TALKTYPE_BROADCAST)
- end
+ Broadcast(param)
return true
end
diff --git a/data/scripts/talkactions/god/forge_functions.lua b/data/scripts/talkactions/god/forge_functions.lua
index b4194380214..5ed33b41dd3 100644
--- a/data/scripts/talkactions/god/forge_functions.lua
+++ b/data/scripts/talkactions/god/forge_functions.lua
@@ -267,3 +267,54 @@ end
forge:groupType("god")
forge:register()
+
+---------------- // ----------------
+-- Add dust level
+local addDustLevel = TalkAction("/adddustlevel")
+
+function addDustLevel.onSay(player, words, param)
+ -- create log
+ logCommand(player, words, param)
+
+ -- Check the first param (player name) exists
+ if param == "" then
+ player:sendCancelMessage("Player name param required.")
+ -- Distro log
+ logger.error("[addDustLevel.onSay] - Player name param not found.")
+ return true
+ end
+
+ local split = param:split(",")
+ local name = split[1]
+
+ -- Check if player is online
+ local targetPlayer = Player(name)
+ if not targetPlayer then
+ player:sendCancelMessage("Player " .. string.titleCase(name) .. " is not online.")
+ -- Distro log
+ logger.error("[addDustLevel.onSay] - Player {} is not online.", string.titleCase(name))
+ return true
+ end
+
+ local dustLevel = nil
+ if split[2] then
+ dustLevel = tonumber(split[2])
+ end
+
+ -- Check if the dustAmount is valid
+ if dustLevel <= 0 or dustLevel == nil then
+ player:sendCancelMessage("Invalid dust level.")
+ return true
+ end
+
+ targetPlayer:addForgeDustLevel(dustLevel)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("Successful added %d dust level for the %s player.", dustLevel, targetPlayer:getName()))
+ targetPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s adds %d dust level to your character.", player:getName(), dustLevel))
+ -- Distro log
+ logger.info("{} added {} dust level to {} player.", player:getName(), dustLevel, targetPlayer:getName())
+ return true
+end
+
+addDustLevel:separator(" ")
+addDustLevel:groupType("god")
+addDustLevel:register()
diff --git a/data/scripts/talkactions/god/test_send_message.lua b/data/scripts/talkactions/god/test_send_message.lua
new file mode 100644
index 00000000000..2db0ebcf3e6
--- /dev/null
+++ b/data/scripts/talkactions/god/test_send_message.lua
@@ -0,0 +1,25 @@
+local sendMessage = TalkAction("/testmessage")
+
+function sendMessage.onSay(player, words, param)
+ -- create log
+ logCommand(player, words, param)
+
+ if param == "" or param == nil then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Message type is missing, please enter a message type.")
+ return
+ end
+
+ local split = param:split(",")
+ local messageType = tonumber(split[1])
+ local textCollor = TEXTCOLOR_WHITE_EXP
+ if split[2] then
+ textCollor = tonumber(split[2])
+ end
+
+ player:sendTextMessage(messageType, "Testing message type.", player:getPosition(), 500, textCollor)
+ return true
+end
+
+sendMessage:separator(" ")
+sendMessage:groupType("god")
+sendMessage:register()
diff --git a/data/scripts/talkactions/player/auto_loot.lua b/data/scripts/talkactions/player/auto_loot.lua
index a5541d51438..927a5b100a1 100644
--- a/data/scripts/talkactions/player/auto_loot.lua
+++ b/data/scripts/talkactions/player/auto_loot.lua
@@ -1,6 +1,12 @@
-local autoLoot = TalkAction("!autoloot")
+local feature = TalkAction("!autoloot")
-function autoLoot.onSay(player, words, param)
+local validValues = {
+ -- "all",
+ "on",
+ "off",
+}
+
+function feature.onSay(player, words, param)
if not configManager.getBoolean(configKeys.AUTOLOOT) then
return true
end
@@ -8,20 +14,25 @@ function autoLoot.onSay(player, words, param)
player:sendCancelMessage("You need to be VIP to use this command!")
return true
end
- if param == "" then
- player:sendCancelMessage("You need to specify on/off param.")
+ if not table.contains(validValues, param) then
+ local validValuesStr = table.concat(validValues, "/")
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Invalid param specified. Usage: !feature [" .. validValuesStr .. "]")
return true
end
- if param == "on" then
- player:setStorageValue(STORAGEVALUE_AUTO_LOOT, 1)
- player:sendTextMessage(MESSAGE_LOOK, "You have successfully enabled your automatic looting!")
+
+ if param == "all" then
+ player:setFeature(Features.AutoLoot, 2)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "AutoLoot is now enabled for all kills (including bosses).")
+ elseif param == "on" then
+ player:setFeature(Features.AutoLoot, 1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "AutoLoot is now enabled for all regular kills (no bosses).")
elseif param == "off" then
- player:setStorageValue(STORAGEVALUE_AUTO_LOOT, -1)
- player:sendTextMessage(MESSAGE_LOOK, "You have successfully disabled your automatic looting!")
+ player:setFeature(Features.AutoLoot, 0)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "AutoLoot is now disabled.")
end
return true
end
-autoLoot:separator(" ")
-autoLoot:groupType("normal")
-autoLoot:register()
+feature:separator(" ")
+feature:groupType("normal")
+feature:register()
diff --git a/metrics/prometheus/prometheus.yml b/metrics/prometheus/prometheus.yml
index 97a21bc19f2..068e64503d3 100644
--- a/metrics/prometheus/prometheus.yml
+++ b/metrics/prometheus/prometheus.yml
@@ -1,8 +1,8 @@
---
global:
- scrape_interval: 5s
- scrape_timeout: 2s
- evaluation_interval: 5s
+ scrape_interval: 30s
+ scrape_timeout: 25s
+ evaluation_interval: 30s
scrape_configs:
- job_name: canary
static_configs:
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3a771b55dd6..abd35c09c36 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,6 +13,24 @@ if(MSVC)
target_sources(${PROJECT_NAME} PRIVATE ../cmake/canary.rc)
endif()
+if (UNIX)
+
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_options(${PROJECT_NAME}_lib
+ PRIVATE
+ -Wall -Wextra -Wpedantic
+ )
+ endif()
+
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_options(${PROJECT_NAME}
+ PRIVATE
+ -Wall -Wextra -Wpedantic
+ )
+ endif()
+
+endif (UNIX)
+
setup_target(${PROJECT_NAME})
set_output_directory(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib)
diff --git a/src/canary_server.cpp b/src/canary_server.cpp
index a3a27bfdef4..9e13e8b05fd 100644
--- a/src/canary_server.cpp
+++ b/src/canary_server.cpp
@@ -383,7 +383,7 @@ void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) {
}
void CanaryServer::shutdown() {
- inject().shutdown();
g_dispatcher().shutdown();
g_metrics().shutdown();
+ inject().shutdown();
}
diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp
index 31aa4659e06..b3f1e51a739 100644
--- a/src/config/config_definitions.hpp
+++ b/src/config/config_definitions.hpp
@@ -63,12 +63,15 @@ enum ConfigKey_t : uint16_t {
FORGE_AMOUNT_MULTIPLIER,
FORGE_BASE_SUCCESS_RATE,
FORGE_BONUS_SUCCESS_RATE,
+ FORGE_FUSION_DUST_COST,
+ FORGE_CONVERGENCE_FUSION_DUST_COST,
+ FORGE_TRANSFER_DUST_COST,
+ FORGE_CONVERGENCE_TRANSFER_DUST_COST,
FORGE_CORE_COST,
FORGE_COST_ONE_SLIVER,
FORGE_FIENDISH_CREATURES_LIMIT,
FORGE_FIENDISH_INTERVAL_TIME,
FORGE_FIENDISH_INTERVAL_TYPE,
- FORGE_FUSION_DUST_COST,
FORGE_INFLUENCED_CREATURES_LIMIT,
FORGE_MAX_DUST,
FORGE_MAX_ITEM_TIER,
@@ -76,7 +79,6 @@ enum ConfigKey_t : uint16_t {
FORGE_MIN_SLIVERS,
FORGE_SLIVER_AMOUNT,
FORGE_TIER_LOSS_REDUCTION,
- FORGE_TRANSFER_DUST_COST,
FRAG_TIME,
FREE_DEPOT_LIMIT,
FREE_PREMIUM,
@@ -146,6 +148,9 @@ enum ConfigKey_t : uint16_t {
METRICS_OSTREAM_INTERVAL,
METRICS_PROMETHEUS_ADDRESS,
MIN_ELEMENTAL_RESISTANCE,
+ MOMENTUM_CHANCE_FORMULA_A,
+ MOMENTUM_CHANCE_FORMULA_B,
+ MOMENTUM_CHANCE_FORMULA_C,
MONTH_KILLS_TO_RED,
MULTIPLIER_ATTACKONFIST,
MYSQL_DB,
@@ -158,6 +163,9 @@ enum ConfigKey_t : uint16_t {
ONE_PLAYER_ON_ACCOUNT,
ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS,
ONLY_PREMIUM_ACCOUNT,
+ ONSLAUGHT_CHANCE_FORMULA_A,
+ ONSLAUGHT_CHANCE_FORMULA_B,
+ ONSLAUGHT_CHANCE_FORMULA_C,
OPTIMIZE_DATABASE,
ORANGE_SKULL_DURATION,
OWNER_EMAIL,
@@ -221,6 +229,9 @@ enum ConfigKey_t : uint16_t {
RESET_SESSIONS_ON_STARTUP,
REWARD_CHEST_COLLECT_ENABLED,
REWARD_CHEST_MAX_COLLECT_ITEMS,
+ RUSE_CHANCE_FORMULA_A,
+ RUSE_CHANCE_FORMULA_B,
+ RUSE_CHANCE_FORMULA_C,
SAVE_INTERVAL_TIME,
SAVE_INTERVAL_TYPE,
SCRIPTS_CONSOLE_LOGS,
@@ -274,6 +285,10 @@ enum ConfigKey_t : uint16_t {
TOGGLE_SERVER_IS_RETRO,
TOGGLE_TRAVELS_FREE,
TOGGLE_WHEELSYSTEM,
+ TRANSCENDANCE_AVATAR_DURATION,
+ TRANSCENDANCE_CHANCE_FORMULA_A,
+ TRANSCENDANCE_CHANCE_FORMULA_B,
+ TRANSCENDANCE_CHANCE_FORMULA_C,
T_CONST,
URL,
USE_ANY_DATAPACK_FOLDER,
@@ -289,6 +304,12 @@ enum ConfigKey_t : uint16_t {
WEATHER_RAIN,
WEATHER_THUNDER,
WEEK_KILLS_TO_RED,
+ WHEEL_ATELIER_REVEAL_GREATER_COST,
+ WHEEL_ATELIER_REVEAL_LESSER_COST,
+ WHEEL_ATELIER_REVEAL_REGULAR_COST,
+ WHEEL_ATELIER_ROTATE_GREATER_COST,
+ WHEEL_ATELIER_ROTATE_LESSER_COST,
+ WHEEL_ATELIER_ROTATE_REGULAR_COST,
WHEEL_POINTS_PER_LEVEL,
WHITE_SKULL_TIME,
WORLD_TYPE,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 1afbc0295ae..2287a3ed0a4 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -227,16 +227,37 @@ bool ConfigManager::load() {
loadIntConfig(L, FORGE_SLIVER_AMOUNT, "forgeSliverAmount", 3);
loadIntConfig(L, FORGE_CORE_COST, "forgeCoreCost", 50);
loadIntConfig(L, FORGE_MAX_DUST, "forgeMaxDust", 225);
- loadIntConfig(L, FORGE_FUSION_DUST_COST, "forgeFusionCost", 100);
- loadIntConfig(L, FORGE_TRANSFER_DUST_COST, "forgeTransferCost", 100);
+ loadIntConfig(L, FORGE_FUSION_DUST_COST, "forgeFusionDustCost", 100);
+ loadIntConfig(L, FORGE_CONVERGENCE_FUSION_DUST_COST, "forgeConvergenceFusionCost", 130);
+ loadIntConfig(L, FORGE_TRANSFER_DUST_COST, "forgeTransferDustCost", 100);
+ loadIntConfig(L, FORGE_CONVERGENCE_TRANSFER_DUST_COST, "forgeConvergenceTransferCost", 160);
loadIntConfig(L, FORGE_BASE_SUCCESS_RATE, "forgeBaseSuccessRate", 50);
loadIntConfig(L, FORGE_BONUS_SUCCESS_RATE, "forgeBonusSuccessRate", 15);
loadIntConfig(L, FORGE_TIER_LOSS_REDUCTION, "forgeTierLossReduction", 50);
- loadIntConfig(L, FORGE_AMOUNT_MULTIPLIER, "forgeAmountMultiplier", 3);
+ loadFloatConfig(L, FORGE_AMOUNT_MULTIPLIER, "forgeAmountMultiplier", 3.0);
loadIntConfig(L, FORGE_MIN_SLIVERS, "forgeMinSlivers", 3);
loadIntConfig(L, FORGE_MAX_SLIVERS, "forgeMaxSlivers", 7);
loadIntConfig(L, FORGE_INFLUENCED_CREATURES_LIMIT, "forgeInfluencedLimit", 300);
loadIntConfig(L, FORGE_FIENDISH_CREATURES_LIMIT, "forgeFiendishLimit", 3);
+
+ loadFloatConfig(L, RUSE_CHANCE_FORMULA_A, "ruseChanceFormulaA", 0.0307576);
+ loadFloatConfig(L, RUSE_CHANCE_FORMULA_B, "ruseChanceFormulaB", 0.440697);
+ loadFloatConfig(L, RUSE_CHANCE_FORMULA_C, "ruseChanceFormulaC", 0.026);
+
+ loadFloatConfig(L, ONSLAUGHT_CHANCE_FORMULA_A, "onslaughtChanceFormulaA", 0.05);
+ loadFloatConfig(L, ONSLAUGHT_CHANCE_FORMULA_B, "onslaughtChanceFormulaB", 0.4);
+ loadFloatConfig(L, ONSLAUGHT_CHANCE_FORMULA_C, "onslaughtChanceFormulaC", 0.05);
+
+ loadFloatConfig(L, MOMENTUM_CHANCE_FORMULA_A, "momentumChanceFormulaA", 0.05);
+ loadFloatConfig(L, MOMENTUM_CHANCE_FORMULA_B, "momentumChanceFormulaB", 1.9);
+ loadFloatConfig(L, MOMENTUM_CHANCE_FORMULA_C, "momentumChanceFormulaC", 0.05);
+
+ loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_A, "transcendanceChanceFormulaA", 0.0127);
+ loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_B, "transcendanceChanceFormulaB", 0.1070);
+ loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_C, "transcendanceChanceFormulaC", 0.0073);
+
+ loadIntConfig(L, TRANSCENDANCE_AVATAR_DURATION, "transcendanceAvatarDuration", 7000);
+
loadIntConfig(L, DISCORD_WEBHOOK_DELAY_MS, "discordWebhookDelayMs", Webhook::DEFAULT_DELAY_MS);
loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0);
@@ -311,7 +332,7 @@ bool ConfigManager::load() {
loadIntConfig(L, HAZARD_PODS_DROP_MULTIPLIER, "hazardPodsDropMultiplier", 87);
loadIntConfig(L, HAZARD_PODS_TIME_TO_DAMAGE, "hazardPodsTimeToDamage", 2000);
loadIntConfig(L, HAZARD_PODS_TIME_TO_SPAWN, "hazardPodsTimeToSpawn", 4000);
- loadIntConfig(L, HAZARD_EXP_BONUS_MULTIPLIER, "hazardExpBonusMultiplier", 2);
+ loadFloatConfig(L, HAZARD_EXP_BONUS_MULTIPLIER, "hazardExpBonusMultiplier", 2.0);
loadIntConfig(L, HAZARD_LOOT_BONUS_MULTIPLIER, "hazardLootBonusMultiplier", 2);
loadIntConfig(L, HAZARD_PODS_DAMAGE, "hazardPodsDamage", 5);
loadIntConfig(L, HAZARD_SPAWN_PLUNDER_MULTIPLIER, "hazardSpawnPlunderMultiplier", 25);
@@ -326,6 +347,14 @@ bool ConfigManager::load() {
loadBoolConfig(L, TOGGLE_WHEELSYSTEM, "wheelSystemEnabled", true);
loadIntConfig(L, WHEEL_POINTS_PER_LEVEL, "wheelPointsPerLevel", 1);
+ loadIntConfig(L, WHEEL_ATELIER_ROTATE_LESSER_COST, "wheelAtelierRotateLesserCost", 125000);
+ loadIntConfig(L, WHEEL_ATELIER_ROTATE_REGULAR_COST, "wheelAtelierRotateRegularCost", 250000);
+ loadIntConfig(L, WHEEL_ATELIER_ROTATE_GREATER_COST, "wheelAtelierRotateGreaterCost", 500000);
+
+ loadIntConfig(L, WHEEL_ATELIER_REVEAL_LESSER_COST, "wheelAtelierRevealLesserCost", 125000);
+ loadIntConfig(L, WHEEL_ATELIER_REVEAL_REGULAR_COST, "wheelAtelierRevealRegularCost", 1000000);
+ loadIntConfig(L, WHEEL_ATELIER_REVEAL_GREATER_COST, "wheelAtelierRevealGreaterCost", 6000000);
+
loadBoolConfig(L, PARTY_AUTO_SHARE_EXPERIENCE, "partyAutoShareExperience", true);
loadBoolConfig(L, PARTY_SHARE_LOOT_BOOSTS, "partyShareLootBoosts", true);
loadFloatConfig(L, PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR, "partyShareLootBoostsDimishingFactor", 0.7f);
diff --git a/src/core.hpp b/src/core.hpp
index 67262257a32..8ca52d107da 100644
--- a/src/core.hpp
+++ b/src/core.hpp
@@ -15,7 +15,7 @@ static constexpr auto AUTHENTICATOR_PERIOD = 30U;
// SERVER_MAJOR_VERSION is the actual full version of the server, including minor and patch numbers.
// This is intended for internal use to identify the exact state of the server (release) software.
static constexpr auto SERVER_RELEASE_VERSION = "3.1.2";
-static constexpr auto CLIENT_VERSION = 1321;
+static constexpr auto CLIENT_VERSION = 1332;
#define CLIENT_VERSION_UPPER (CLIENT_VERSION / 100)
#define CLIENT_VERSION_LOWER (CLIENT_VERSION % 100)
diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt
index 3c055d508cb..d5884fdc871 100644
--- a/src/creatures/CMakeLists.txt
+++ b/src/creatures/CMakeLists.txt
@@ -22,5 +22,6 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
players/storages/storages.cpp
players/player.cpp
players/wheel/player_wheel.cpp
+ players/wheel/wheel_gems.cpp
players/vocations/vocation.cpp
)
diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp
index 779ba64c95c..5425f7fed31 100644
--- a/src/creatures/appearance/outfit/outfit.cpp
+++ b/src/creatures/appearance/outfit/outfit.cpp
@@ -49,8 +49,8 @@ bool Outfits::loadFromXml() {
if (uint16_t lookType = pugi::cast(lookTypeAttribute.value());
g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0
&& !g_game().isLookTypeRegistered(lookType)) {
- g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType);
- return false;
+ g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was ignored to prevent client crash.", lookType);
+ continue;
}
outfits[type].emplace_back(std::make_shared(
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index e3211a1f52e..3e0af9ea444 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -1324,6 +1324,7 @@ void Combat::setRuneSpellName(const std::string &value) {
}
std::vector>> Combat::pickChainTargets(std::shared_ptr caster, const CombatParams ¶ms, uint8_t chainDistance, uint8_t maxTargets, bool backtracking, bool aggressive, std::shared_ptr initialTarget /* = nullptr */) {
+ Benchmark bm_pickChain;
metrics::method_latency measure(__METHOD_NAME__);
if (!caster) {
return {};
@@ -1342,8 +1343,8 @@ std::vector>> Combat::pickChainTargets
maxTargets++;
}
- const int maxBacktrackingAttempts = 10; // Can be adjusted as needed
- while (!targets.empty() && targets.size() <= maxTargets) {
+ int backtrackingAttempts = 10;
+ while (!targets.empty() && targets.size() <= maxTargets && backtrackingAttempts > 0) {
auto currentTarget = targets.back();
auto spectators = Spectators().find(currentTarget->getPosition(), false, chainDistance, chainDistance, chainDistance, chainDistance);
g_logger().debug("Combat::pickChainTargets: currentTarget: {}, spectators: {}", currentTarget->getName(), spectators.size());
@@ -1367,7 +1368,7 @@ std::vector>> Combat::pickChainTargets
}
if (closestSpectator) {
- g_logger().debug("Combat::pickChainTargets: closestSpectator: {}", closestSpectator->getName());
+ g_logger().trace("[{}] closestSpectator: {}", __METHOD_NAME__, closestSpectator->getName());
bool found = false;
for (auto &[pos, vec] : resultMap) {
@@ -1385,14 +1386,15 @@ std::vector>> Combat::pickChainTargets
visited.insert(closestSpectator->getID());
continue;
} else if (backtracking) {
+ g_logger().debug("[{}] backtracking", __METHOD_NAME__);
targets.pop_back();
- if (targets.size() <= maxBacktrackingAttempts) {
- continue;
- }
+ backtrackingAttempts--;
+ continue;
}
break;
}
+ g_logger().debug("[{}] resultMap: {} in {} ms", __METHOD_NAME__, resultMap.size(), bm_pickChain.duration());
return resultMap;
}
@@ -2069,7 +2071,7 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptr(bonus) / 100;
+ double multiplier = 1.0 + static_cast(bonus) / 10000;
chance += (uint16_t)damage.criticalChance;
if (chance != 0 && uniform_random(1, 10000) <= chance) {
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index 4181ca39410..f1e2ec6e6d1 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -2051,7 +2051,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32
void ConditionFeared::endCondition(std::shared_ptr creature) {
creature->stopEventWalk();
/*
- * After a player is feared there's a 10 seconds before he can feared again.
+ * After a player is feared there's a 10 seconds before they can can feared again.
*/
std::shared_ptr player = creature->getPlayer();
if (player) {
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index 152bc37c496..013d1b4a1ff 100644
--- a/src/creatures/combat/spells.cpp
+++ b/src/creatures/combat/spells.cpp
@@ -639,6 +639,8 @@ void Spell::applyCooldownConditions(std::shared_ptr player) const {
if (isUpgraded) {
spellCooldown -= getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, spellGrade);
}
+ g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN));
+ spellCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN);
if (spellCooldown > 0) {
std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, m_spellId);
player->addCondition(condition);
@@ -676,6 +678,7 @@ void Spell::postCastSpell(std::shared_ptr player, bool finishedCast /*=
if (aggressive) {
player->addInFightTicks();
+ player->updateLastAggressiveAction();
}
if (player && soundCastEffect != SoundEffect_t::SILENCE) {
@@ -702,28 +705,26 @@ void Spell::postCastSpell(std::shared_ptr player, uint32_t manaCost, uin
}
uint32_t Spell::getManaCost(std::shared_ptr player) const {
+ WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
+ uint32_t manaRedution = 0;
+ if (getWheelOfDestinyUpgraded() && static_cast(spellGrade) > 0) {
+ manaRedution += getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, spellGrade);
+ }
+ manaRedution += player->wheel()->getSpellBonus(name, WheelSpellBoost_t::MANA);
+
if (mana != 0) {
- WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
- if (getWheelOfDestinyUpgraded() && static_cast(spellGrade) > 0) {
- if (getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, spellGrade) >= mana) {
- return 0;
- } else {
- return (mana - getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, spellGrade));
- }
+ if (manaRedution > mana) {
+ return 0;
}
- return mana;
+ return mana - manaRedution;
}
if (manaPercent != 0) {
uint32_t maxMana = player->getMaxMana();
uint32_t manaCost = (maxMana * manaPercent) / 100;
WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
- if (getWheelOfDestinyUpgraded() && static_cast(spellGrade) > 0) {
- if (getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, spellGrade) >= manaCost) {
- return 0;
- } else {
- return (manaCost - getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, spellGrade));
- }
+ if (manaRedution > manaCost) {
+ return 0;
}
return manaCost;
}
@@ -842,6 +843,7 @@ bool InstantSpell::playerCastInstant(std::shared_ptr player, std::string
auto worldType = g_game().getWorldType();
if (pzLocked && (worldType == WORLD_TYPE_PVP || worldType == WORLD_TYPE_PVP_ENFORCED)) {
player->addInFightTicks(true);
+ player->updateLastAggressiveAction();
}
bool result = executeCastSpell(player, var);
@@ -1014,6 +1016,7 @@ bool RuneSpell::executeUse(std::shared_ptr player, std::shared_ptr-
auto worldType = g_game().getWorldType();
if (pzLocked && (worldType == WORLD_TYPE_PVP || worldType == WORLD_TYPE_PVP_ENFORCED)) {
player->addInFightTicks(true);
+ player->updateLastAggressiveAction();
}
return true;
diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp
index 8e2e748c004..efbf3ea5cf7 100644
--- a/src/creatures/creature.cpp
+++ b/src/creatures/creature.cpp
@@ -798,10 +798,10 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared
g_game().internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT);
dropLoot(corpse->getContainer(), lastHitCreature);
corpse->startDecaying();
- bool corpses = corpse->isRewardCorpse() || (corpse->getID() == ITEM_MALE_CORPSE || corpse->getID() == ITEM_FEMALE_CORPSE);
+ bool disallowedCorpses = corpse->isRewardCorpse() || (corpse->getID() == ITEM_MALE_CORPSE || corpse->getID() == ITEM_FEMALE_CORPSE);
const auto player = mostDamageCreature ? mostDamageCreature->getPlayer() : nullptr;
auto corpseContainer = corpse->getContainer();
- if (corpseContainer && player && !corpses) {
+ if (corpseContainer && player && !disallowedCorpses) {
auto monster = getMonster();
if (monster && !monster->isRewardBoss()) {
std::ostringstream lootMessage;
@@ -813,9 +813,9 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared
player->sendLootMessage(lootMessage.str());
}
- if (player->checkAutoLoot() && corpseContainer && mostDamageCreature->getPlayer()) {
+ if (player->checkAutoLoot(monster->isRewardBoss()) && corpseContainer && mostDamageCreature->getPlayer()) {
g_dispatcher().addEvent(
- std::bind(&Game::playerQuickLootCorpse, &g_game(), player, corpseContainer),
+ std::bind(&Game::playerQuickLootCorpse, &g_game(), player, corpseContainer, corpse->getPosition()),
"Game::playerQuickLootCorpse"
);
}
diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp
index 39ada8745a5..ee1f390b9a2 100644
--- a/src/creatures/creatures_definitions.hpp
+++ b/src/creatures/creatures_definitions.hpp
@@ -321,7 +321,6 @@ enum ObjectCategory_t {
OBJECTCATEGORY_PREMIUMSCROLLS = 22, // not used in quickloot
OBJECTCATEGORY_TIBIACOINS = 23, // not used in quickloot
OBJECTCATEGORY_CREATUREPRODUCTS = 24,
- OBJECTCATEGORY_STASHRETRIEVE = 27,
OBJECTCATEGORY_GOLD = 30,
OBJECTCATEGORY_DEFAULT = 31, // unassigned loot
@@ -329,6 +328,39 @@ enum ObjectCategory_t {
OBJECTCATEGORY_LAST = OBJECTCATEGORY_DEFAULT,
};
+static bool isValidObjectCategory(uint8_t category) {
+ static std::unordered_set valid = {
+ OBJECTCATEGORY_NONE,
+ OBJECTCATEGORY_ARMORS,
+ OBJECTCATEGORY_NECKLACES,
+ OBJECTCATEGORY_BOOTS,
+ OBJECTCATEGORY_CONTAINERS,
+ OBJECTCATEGORY_DECORATION,
+ OBJECTCATEGORY_FOOD,
+ OBJECTCATEGORY_HELMETS,
+ OBJECTCATEGORY_LEGS,
+ OBJECTCATEGORY_OTHERS,
+ OBJECTCATEGORY_POTIONS,
+ OBJECTCATEGORY_RINGS,
+ OBJECTCATEGORY_RUNES,
+ OBJECTCATEGORY_SHIELDS,
+ OBJECTCATEGORY_TOOLS,
+ OBJECTCATEGORY_VALUABLES,
+ OBJECTCATEGORY_AMMO,
+ OBJECTCATEGORY_AXES,
+ OBJECTCATEGORY_CLUBS,
+ OBJECTCATEGORY_DISTANCEWEAPONS,
+ OBJECTCATEGORY_SWORDS,
+ OBJECTCATEGORY_WANDS,
+ OBJECTCATEGORY_PREMIUMSCROLLS,
+ OBJECTCATEGORY_TIBIACOINS,
+ OBJECTCATEGORY_CREATUREPRODUCTS,
+ OBJECTCATEGORY_GOLD,
+ OBJECTCATEGORY_DEFAULT,
+ };
+ return valid.contains(category);
+}
+
enum RespawnPeriod_t {
RESPAWNPERIOD_ALL,
RESPAWNPERIOD_DAY,
diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp
index b4b3337bbed..2d6c27558e7 100644
--- a/src/creatures/monsters/monster.hpp
+++ b/src/creatures/monsters/monster.hpp
@@ -336,12 +336,15 @@ class Monster final : public Creature {
float getAttackMultiplier() const {
float multiplier = mType->getAttackMultiplier();
- return multiplier * std::pow(1.03f, getForgeStack());
+ if (auto stacks = getForgeStack(); stacks > 0) {
+ multiplier *= (1.35 + (stacks - 1) * 0.1);
+ }
+ return multiplier;
}
float getDefenseMultiplier() const {
float multiplier = mType->getDefenseMultiplier();
- return multiplier * std::pow(1.01f, getForgeStack());
+ return multiplier * std::pow(1.02f, getForgeStack());
}
private:
diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp
index 7e7f7cf3f49..bf83e5dec0a 100644
--- a/src/creatures/monsters/spawns/spawn_monster.hpp
+++ b/src/creatures/monsters/spawns/spawn_monster.hpp
@@ -59,10 +59,10 @@ class SpawnMonster {
private:
// map of the spawned creatures
- phmap::parallel_flat_hash_map_m> spawnedMonsterMap;
+ std::map> spawnedMonsterMap;
// map of creatures in the spawn
- phmap::parallel_flat_hash_map_m spawnMonsterMap;
+ std::map spawnMonsterMap;
Position centerPos;
int32_t radius;
diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp
index 995ea3197a4..093eae3c956 100644
--- a/src/creatures/players/imbuements/imbuements.cpp
+++ b/src/creatures/players/imbuements/imbuements.cpp
@@ -240,7 +240,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) {
imbuement.skills[skillId] = bonus;
int32_t chance = 100;
if ((attr = childNode.attribute("chance"))) {
- chance = std::min(100, pugi::cast(attr.value()));
+ chance = std::min(10000, pugi::cast(attr.value()));
}
imbuement.skills[skillId - 1] = chance;
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 0a310061fc5..46528c79241 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -339,7 +339,7 @@ int32_t Player::getWeaponSkill(std::shared_ptr
- item) const {
int32_t Player::getArmor() const {
int32_t armor = 0;
- static const Slots_t armorSlots[] = { CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING };
+ static const Slots_t armorSlots[] = { CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING, CONST_SLOT_AMMO };
for (Slots_t slot : armorSlots) {
std::shared_ptr
- inventoryItem = inventory[slot];
if (inventoryItem) {
@@ -1024,79 +1024,114 @@ void Player::onReceiveMail() {
}
}
-std::shared_ptr Player::setLootContainer(ObjectCategory_t category, std::shared_ptr container, bool loading /* = false*/) {
+std::shared_ptr Player::refreshManagedContainer(ObjectCategory_t category, std::shared_ptr container, bool isLootContainer, bool loading /* = false*/) {
std::shared_ptr previousContainer = nullptr;
- if (auto it = quickLootContainers.find(category);
- it != quickLootContainers.end() && !loading) {
- previousContainer = (*it).second;
- auto flags = previousContainer->getAttribute(ItemAttribute_t::QUICKLOOTCONTAINER);
- flags &= ~(1 << category);
- if (flags == 0) {
- previousContainer->removeAttribute(ItemAttribute_t::QUICKLOOTCONTAINER);
+ auto toSetAttribute = isLootContainer ? ItemAttribute_t::QUICKLOOTCONTAINER : ItemAttribute_t::OBTAINCONTAINER;
+ if (auto it = m_managedContainers.find(category); it != m_managedContainers.end() && !loading) {
+ previousContainer = isLootContainer ? it->second.first : it->second.second;
+ if (previousContainer) {
+ auto flags = previousContainer->getAttribute(toSetAttribute);
+ flags &= ~(1 << category);
+ if (flags == 0) {
+ previousContainer->removeAttribute(toSetAttribute);
+ } else {
+ previousContainer->setAttribute(toSetAttribute, flags);
+ }
+ }
+
+ if (isLootContainer) {
+ it->second.first = nullptr;
} else {
- previousContainer->setAttribute(ItemAttribute_t::QUICKLOOTCONTAINER, flags);
+ it->second.second = nullptr;
}
- quickLootContainers.erase(it);
+ if (!it->second.first && !it->second.second) {
+ m_managedContainers.erase(it);
+ }
}
+
if (container) {
previousContainer = container;
- quickLootContainers[category] = container;
+ if (m_managedContainers.find(category) != m_managedContainers.end()) {
+ if (isLootContainer) {
+ m_managedContainers[category].first = container;
+ } else {
+ m_managedContainers[category].second = container;
+ }
+ } else {
+ std::pair, std::shared_ptr> newPair;
+ if (isLootContainer) {
+ newPair.first = container;
+ newPair.second = nullptr;
+ } else {
+ newPair.first = nullptr;
+ newPair.second = container;
+ }
+ m_managedContainers[category] = newPair;
+ }
if (!loading) {
- auto flags = container->getAttribute(ItemAttribute_t::QUICKLOOTCONTAINER);
- auto sendAttribute = flags | 1 << category;
- container->setAttribute(ItemAttribute_t::QUICKLOOTCONTAINER, sendAttribute);
+ auto flags = container->getAttribute(toSetAttribute);
+ auto sendAttribute = flags | (1 << category);
+ container->setAttribute(toSetAttribute, sendAttribute);
}
- return previousContainer;
}
- return nullptr;
+ return previousContainer;
}
-std::shared_ptr Player::getLootContainer(ObjectCategory_t category) const {
+std::shared_ptr Player::getManagedContainer(ObjectCategory_t category, bool isLootContainer) const {
if (category != OBJECTCATEGORY_DEFAULT && !isPremium()) {
category = OBJECTCATEGORY_DEFAULT;
}
- auto it = quickLootContainers.find(category);
- if (it != quickLootContainers.end()) {
- return (*it).second;
+ auto it = m_managedContainers.find(category);
+ std::shared_ptr container = nullptr;
+ if (it != m_managedContainers.end()) {
+ container = isLootContainer ? it->second.first : it->second.second;
}
- if (category != OBJECTCATEGORY_DEFAULT) {
+ if (!container && category != OBJECTCATEGORY_DEFAULT) {
// firstly, fallback to default
- return getLootContainer(OBJECTCATEGORY_DEFAULT);
+ container = getManagedContainer(OBJECTCATEGORY_DEFAULT, isLootContainer);
}
- return nullptr;
+ return container;
}
-void Player::checkLootContainers(std::shared_ptr
- item) {
- if (!item) {
- return;
- }
-
- std::shared_ptr container = item->getContainer();
+void Player::checkLootContainers(std::shared_ptr container) {
if (!container) {
return;
}
bool shouldSend = false;
+ for (auto it = m_managedContainers.begin(); it != m_managedContainers.end();) {
+ std::shared_ptr &lootContainer = it->second.first;
+ std::shared_ptr &obtainContainer = it->second.second;
+ bool removeLoot = false;
+ bool removeObtain = false;
+ if (lootContainer && container->getHoldingPlayer() != getPlayer() && (container == lootContainer || container->isHoldingItem(lootContainer))) {
+ removeLoot = true;
+ shouldSend = true;
+ lootContainer->removeAttribute(ItemAttribute_t::QUICKLOOTCONTAINER);
+ }
- auto it = quickLootContainers.begin();
- while (it != quickLootContainers.end()) {
- std::shared_ptr lootContainer = (*it).second;
+ if (obtainContainer && container->getHoldingPlayer() != getPlayer() && (container == obtainContainer || container->isHoldingItem(obtainContainer))) {
+ removeObtain = true;
+ shouldSend = true;
+ obtainContainer->removeAttribute(ItemAttribute_t::OBTAINCONTAINER);
+ }
- bool remove = false;
- if (item->getHoldingPlayer() != getPlayer() && (item == lootContainer || container->isHoldingItem(lootContainer))) {
- remove = true;
+ if (removeLoot) {
+ lootContainer.reset();
}
- if (remove) {
- shouldSend = true;
- it = quickLootContainers.erase(it);
- lootContainer->removeAttribute(ItemAttribute_t::QUICKLOOTCONTAINER);
+ if (removeObtain) {
+ obtainContainer.reset();
+ }
+
+ if (!lootContainer && !obtainContainer) {
+ it = m_managedContainers.erase(it);
} else {
++it;
}
@@ -1107,6 +1142,27 @@ void Player::checkLootContainers(std::shared_ptr
- item) {
}
}
+void Player::setMainBackpackUnassigned(std::shared_ptr container) {
+ if (!container) {
+ return;
+ }
+
+ // Update containers
+ bool toSendInventoryUpdate = false;
+ for (bool isLootContainer : { true, false }) {
+ std::shared_ptr managedContainer = getManagedContainer(OBJECTCATEGORY_DEFAULT, isLootContainer);
+ if (!managedContainer) {
+ refreshManagedContainer(OBJECTCATEGORY_DEFAULT, container, isLootContainer);
+ toSendInventoryUpdate = true;
+ }
+ }
+
+ if (toSendInventoryUpdate) {
+ sendInventoryItem(CONST_SLOT_BACKPACK, container);
+ sendLootContainers();
+ }
+}
+
void Player::sendLootStats(std::shared_ptr
- item, uint8_t count) {
uint64_t value = 0;
if (item->getID() == ITEM_GOLD_COIN || item->getID() == ITEM_PLATINUM_COIN || item->getID() == ITEM_CRYSTAL_COIN) {
@@ -1621,7 +1677,7 @@ void Player::onRemoveTileItem(std::shared_ptr fromTile, const Position &po
}
}
- checkLootContainers(item);
+ checkLootContainers(item->getContainer());
}
void Player::onCreatureAppear(std::shared_ptr creature, bool isLogin) {
@@ -1942,7 +1998,7 @@ void Player::onRemoveContainerItem(std::shared_ptr container, std::sh
}
}
- checkLootContainers(item);
+ checkLootContainers(item->getContainer());
}
void Player::onCloseContainer(std::shared_ptr container) {
@@ -1994,7 +2050,7 @@ void Player::onRemoveInventoryItem(std::shared_ptr
- item) {
}
}
- checkLootContainers(item);
+ checkLootContainers(item->getContainer());
}
void Player::checkTradeState(std::shared_ptr
- item) {
@@ -2108,6 +2164,8 @@ void Player::onThink(uint32_t interval) {
addMessageBuffer();
}
+ // Transcendance (avatar trigger)
+ triggerTranscendance();
// Momentum (cooldown resets)
triggerMomentum();
auto playerTile = getTile();
@@ -2287,7 +2345,7 @@ void Player::addExperience(std::shared_ptr target, uint64_t exp, bool
std::shared_ptr monster = target && target->getMonster() ? target->getMonster() : nullptr;
bool handleHazardExperience = monster && monster->getHazard() && getHazardSystemPoints() > 0;
if (handleHazardExperience) {
- exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getNumber(HAZARD_EXP_BONUS_MULTIPLIER, __FUNCTION__))) / 100.;
+ exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getFloat(HAZARD_EXP_BONUS_MULTIPLIER, __FUNCTION__))) / 100.;
}
experience += exp;
@@ -4312,7 +4370,8 @@ void Player::doAttacking(uint32_t) {
}
if (result) {
- lastAttack = OTSYS_TIME();
+ updateLastAggressiveAction();
+ updateLastAttack();
}
}
}
@@ -6519,7 +6578,7 @@ void Player::triggerMomentum() {
}
double_t chance = item->getMomentumChance();
- double_t randomChance = uniform_random(0, 10000) / 100;
+ 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;
auto it = conditions.begin();
@@ -6561,6 +6620,32 @@ void Player::clearCooldowns() {
}
}
+void Player::triggerTranscendance() {
+ auto item = getInventoryItem(CONST_SLOT_LEGS);
+ if (item == nullptr) {
+ return;
+ }
+
+ double_t chance = item->getTranscendenceChance();
+ double_t randomChance = uniform_random(0, 10000) / 100.;
+ if (getZoneType() != ZONE_PROTECTION && checkLastAggressiveActionWithin(2000) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) {
+ int64_t duration = g_configManager().getNumber(TRANSCENDANCE_AVATAR_DURATION, __FUNCTION__);
+ auto outfitCondition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)->static_self_cast();
+ Outfit_t outfit;
+ outfit.lookType = getVocation()->getAvatarLookType();
+ outfitCondition->setOutfit(outfit);
+ addCondition(outfitCondition);
+ wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, OTSYS_TIME() + duration);
+ g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR);
+ sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered.");
+ sendSkills();
+ sendStats();
+ sendBasicData();
+ wheel()->sendGiftOfLifeCooldown();
+ g_game().reloadCreature(getPlayer());
+ }
+}
+
/*******************************************************************************
* Depot search system
******************************************************************************/
@@ -6917,41 +7002,35 @@ bool Player::saySpell(
}
// Forge system
-void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool reduceTierLoss, uint8_t bonus, uint8_t coreCount) {
- if (this->getFreeBackpackSlots() < 1) {
- sendCancelMessage("You have no slots in your backpack.");
- sendForgeError(RETURNVALUE_NOTENOUGHROOM);
- return;
- }
-
+void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount) {
ForgeHistory history;
- history.actionType = ForgeConversion_t::FORGE_ACTION_FUSION;
+ history.actionType = actionType;
history.tier = tier;
history.success = success;
history.tierLoss = reduceTierLoss;
- auto firstForgingItem = getForgeItemFromId(itemId, tier);
+ auto firstForgingItem = getForgeItemFromId(firstItemId, tier);
if (!firstForgingItem) {
- g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), itemId);
+ g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
auto returnValue = g_game().internalRemoveItem(firstForgingItem, 1);
if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", itemId, getName());
+ g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", firstItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
- auto secondForgingItem = getForgeItemFromId(itemId, tier);
+ auto secondForgingItem = getForgeItemFromId(secondItemId, tier);
if (!secondForgingItem) {
- g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), itemId);
+ g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), secondItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1);
returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", itemId, getName());
+ g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
@@ -6970,151 +7049,179 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re
return;
}
- std::shared_ptr
- firstForgedItem = Item::CreateItem(itemId, 1);
+ std::shared_ptr
- firstForgedItem = Item::CreateItem(firstItemId, 1);
if (!firstForgedItem) {
- g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), itemId);
+ g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
- firstForgedItem->setTier(tier);
returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", itemId, getName());
+ g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
- std::shared_ptr
- secondForgedItem = Item::CreateItem(itemId, 1);
- if (!secondForgedItem) {
- g_logger().error("[Log 4] Player with name {} failed to fuse item with id {}", getName(), itemId);
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- }
+ auto configKey = convergence ? FORGE_CONVERGENCE_FUSION_DUST_COST : FORGE_FUSION_DUST_COST;
+ auto dustCost = static_cast(g_configManager().getNumber(configKey, __FUNCTION__));
+ if (convergence) {
+ firstForgedItem->setTier(tier + 1);
+ history.dustCost = dustCost;
+ setForgeDusts(getForgeDusts() - dustCost);
- secondForgedItem->setTier(tier);
- returnValue = g_game().internalAddItem(exaltationContainer, secondForgedItem, INDEX_WHEREEVER);
- if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 2] Failed to add forge item {} from player with name {}", itemId, getName());
- sendCancelMessage(getReturnMessage(returnValue));
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- }
+ uint64_t cost = 0;
+ for (const auto* itemClassification : g_game().getItemsClassifications()) {
+ if (itemClassification->id != firstForgingItem->getClassification()) {
+ continue;
+ }
- auto dustCost = static_cast(g_configManager().getNumber(FORGE_FUSION_DUST_COST, __FUNCTION__));
- if (success) {
- firstForgedItem->setTier(tier + 1);
+ for (const auto &[mapTier, mapPrice] : itemClassification->tiers) {
+ if (mapTier == firstForgingItem->getTier()) {
+ cost = mapPrice.convergenceFusionPrice;
+ break;
+ }
+ }
+ break;
+ }
+ if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) {
+ g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ }
+ g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_convergence_fuse" } });
+ history.cost = cost;
+ } else {
+ firstForgedItem->setTier(tier);
+ std::shared_ptr
- secondForgedItem = Item::CreateItem(secondItemId, 1);
+ if (!secondForgedItem) {
+ g_logger().error("[Log 4] Player with name {} failed to fuse item with id {}", getName(), secondItemId);
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ }
- if (bonus != 1) {
- history.dustCost = dustCost;
- setForgeDusts(getForgeDusts() - dustCost);
+ secondForgedItem->setTier(tier);
+ returnValue = g_game().internalAddItem(exaltationContainer, secondForgedItem, INDEX_WHEREEVER);
+ if (returnValue != RETURNVALUE_NOERROR) {
+ g_logger().error("[Log 2] Failed to add forge item {} from player with name {}", secondItemId, getName());
+ sendCancelMessage(getReturnMessage(returnValue));
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
}
- if (bonus != 2) {
- if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) {
- g_logger().error("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
+
+ if (success) {
+ firstForgedItem->setTier(tier + 1);
+
+ if (bonus != 1) {
+ history.dustCost = dustCost;
+ setForgeDusts(getForgeDusts() - dustCost);
}
- history.coresCost = coreCount;
- }
- if (bonus != 3) {
- uint64_t cost = 0;
- for (const auto* itemClassification : g_game().getItemsClassifications()) {
- if (itemClassification->id != firstForgingItem->getClassification()) {
- continue;
+ if (bonus != 2) {
+ if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) {
+ g_logger().error("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
}
-
- for (const auto &[mapTier, mapPrice] : itemClassification->tiers) {
- if (mapTier == firstForgingItem->getTier()) {
- cost = mapPrice.priceToUpgrade;
+ history.coresCost = coreCount;
+ }
+ if (bonus != 3) {
+ uint64_t cost = 0;
+ for (const auto* itemClassification : g_game().getItemsClassifications()) {
+ if (itemClassification->id != firstForgedItem->getClassification()) {
+ continue;
+ }
+ if (!itemClassification->tiers.contains(firstForgedItem->getTier())) {
+ g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgedItem->getTier(), firstForgedItem->getClassification(), itemClassification->id);
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
break;
}
+ cost = itemClassification->tiers.at(firstForgedItem->getTier()).regularPrice;
+ break;
}
- break;
- }
- if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) {
- g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
+ if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) {
+ g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ }
+ g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } });
+ history.cost = cost;
}
- g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } });
- history.cost = cost;
- }
- if (bonus == 4) {
- if (tier > 0) {
- secondForgedItem->setTier(tier - 1);
+ if (bonus == 4) {
+ if (tier > 0) {
+ secondForgedItem->setTier(tier - 1);
+ }
+ } else if (bonus == 6) {
+ secondForgedItem->setTier(tier + 1);
+ } else if (bonus == 7 && tier + 2 <= firstForgedItem->getClassification()) {
+ firstForgedItem->setTier(tier + 2);
}
- } else if (bonus == 6) {
- secondForgedItem->setTier(tier + 1);
- } else if (bonus == 7 && tier + 2 <= firstForgedItem->getClassification()) {
- firstForgedItem->setTier(tier + 2);
- }
- if (bonus != 4 && bonus != 5 && bonus != 6 && bonus != 8) {
- returnValue = g_game().internalRemoveItem(secondForgedItem, 1);
- if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 6] Failed to remove forge item {} from player with name {}", itemId, getName());
- sendCancelMessage(getReturnMessage(returnValue));
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- }
- }
- } else {
- auto isTierLost = uniform_random(1, 100) <= (reduceTierLoss ? g_configManager().getNumber(FORGE_TIER_LOSS_REDUCTION, __FUNCTION__) : 100);
- if (isTierLost) {
- if (secondForgedItem->getTier() >= 1) {
- secondForgedItem->setTier(tier - 1);
- } else {
+ if (bonus != 4 && bonus != 5 && bonus != 6 && bonus != 8) {
returnValue = g_game().internalRemoveItem(secondForgedItem, 1);
if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 7] Failed to remove forge item {} from player with name {}", itemId, getName());
+ g_logger().error("[Log 6] Failed to remove forge item {} from player with name {}", secondItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
}
- }
- bonus = (isTierLost ? 0 : 8);
- history.coresCost = coreCount;
-
- if (getForgeDusts() < dustCost) {
- g_logger().error("[Log 7] Failed to remove fuse dusts from player with name {}", getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
} else {
- setForgeDusts(getForgeDusts() - dustCost);
- }
+ auto isTierLost = uniform_random(1, 100) <= (reduceTierLoss ? g_configManager().getNumber(FORGE_TIER_LOSS_REDUCTION, __FUNCTION__) : 100);
+ if (isTierLost) {
+ if (secondForgedItem->getTier() >= 1) {
+ secondForgedItem->setTier(tier - 1);
+ } else {
+ returnValue = g_game().internalRemoveItem(secondForgedItem, 1);
+ if (returnValue != RETURNVALUE_NOERROR) {
+ g_logger().error("[Log 7] Failed to remove forge item {} from player with name {}", secondItemId, getName());
+ sendCancelMessage(getReturnMessage(returnValue));
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ }
+ }
+ }
+ bonus = (isTierLost ? 0 : 8);
+ history.coresCost = coreCount;
- if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) {
- g_logger().error("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- }
+ if (getForgeDusts() < dustCost) {
+ g_logger().error("[Log 7] Failed to remove fuse dusts from player with name {}", getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ } else {
+ setForgeDusts(getForgeDusts() - dustCost);
+ }
- uint64_t cost = 0;
- for (const auto* itemClassification : g_game().getItemsClassifications()) {
- if (itemClassification->id != firstForgingItem->getClassification()) {
- continue;
+ if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) {
+ g_logger().error("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
}
- for (const auto &[mapTier, mapPrice] : itemClassification->tiers) {
- if (mapTier == firstForgingItem->getTier()) {
- cost = mapPrice.priceToUpgrade;
+ uint64_t cost = 0;
+ for (const auto* itemClassification : g_game().getItemsClassifications()) {
+ if (itemClassification->id != firstForgingItem->getClassification()) {
+ continue;
+ }
+ if (!itemClassification->tiers.contains(firstForgingItem->getTier() + 1)) {
+ g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgingItem->getTier() + 1, firstForgingItem->getClassification(), itemClassification->id);
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
break;
}
+ cost = itemClassification->tiers.at(firstForgingItem->getTier() + 1).regularPrice;
+ break;
}
- break;
- }
- if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) {
- g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- }
- g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } });
+ if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) {
+ g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName());
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ return;
+ }
+ g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } });
- history.cost = cost;
+ history.cost = cost;
+ }
}
+
returnValue = g_game().internalAddItem(static_self_cast(), exaltationContainer, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName());
@@ -7124,22 +7231,18 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re
}
history.firstItemName = firstForgingItem->getName();
+ history.secondItemName = secondForgingItem->getName();
history.bonus = bonus;
history.createdAt = getTimeNow();
+ history.convergence = convergence;
registerForgeHistoryDescription(history);
- sendForgeFusionItem(itemId, tier, success, bonus, coreCount);
+ sendForgeResult(actionType, firstItemId, tier, secondItemId, tier + 1, success, bonus, coreCount, convergence);
}
-void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId) {
- if (this->getFreeBackpackSlots() < 1) {
- sendCancelMessage("You have no slots in your backpack.");
- sendForgeError(RETURNVALUE_NOTENOUGHROOM);
- return;
- }
-
+void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId, bool convergence) {
ForgeHistory history;
- history.actionType = ForgeConversion_t::FORGE_ACTION_TRANSFER;
+ history.actionType = actionType;
history.tier = tier;
history.success = true;
@@ -7184,27 +7287,27 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t
return;
}
- std::shared_ptr
- newDonorItem = Item::CreateItem(donorItemId, 1);
- if (!newDonorItem) {
- g_logger().error("[Log 4] Player with name {} failed to transfer item with id {}", getName(), donorItemId);
+ std::shared_ptr
- newReceiveItem = Item::CreateItem(receiveItemId, 1);
+ if (!newReceiveItem) {
+ g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
- returnValue = g_game().internalAddItem(exaltationContainer, newDonorItem, INDEX_WHEREEVER);
- if (returnValue != RETURNVALUE_NOERROR) {
- g_logger().error("[Log 5] Failed to add forge item {} from player with name {}", donorItemId, getName());
- sendCancelMessage(getReturnMessage(returnValue));
+
+ auto configKey = convergence ? FORGE_CONVERGENCE_TRANSFER_DUST_COST : FORGE_TRANSFER_DUST_COST;
+ if (getForgeDusts() < g_configManager().getNumber(configKey, __FUNCTION__)) {
+ g_logger().error("[Log 8] Failed to remove transfer dusts from player with name {}", getName());
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
+ } else {
+ setForgeDusts(getForgeDusts() - g_configManager().getNumber(configKey, __FUNCTION__));
}
- std::shared_ptr
- newReceiveItem = Item::CreateItem(receiveItemId, 1);
- if (!newReceiveItem) {
- g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId);
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
+ if (convergence) {
+ newReceiveItem->setTier(tier);
+ } else {
+ newReceiveItem->setTier(tier - 1);
}
- newReceiveItem->setTier(tier - 1);
returnValue = g_game().internalAddItem(exaltationContainer, newReceiveItem, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 7] Failed to add forge item {} from player with name {}", receiveItemId, getName());
@@ -7213,28 +7316,21 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t
return;
}
- if (getForgeDusts() < g_configManager().getNumber(FORGE_TRANSFER_DUST_COST, __FUNCTION__)) {
- g_logger().error("[Log 8] Failed to remove transfer dusts from player with name {}", getName());
- sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
- return;
- } else {
- setForgeDusts(getForgeDusts() - g_configManager().getNumber(FORGE_TRANSFER_DUST_COST, __FUNCTION__));
- }
-
uint8_t coresAmount = 0;
uint64_t cost = 0;
for (const auto &itemClassification : g_game().getItemsClassifications()) {
if (itemClassification->id != donorItem->getClassification()) {
continue;
}
-
- for (const auto &[mapTier, mapPrice] : itemClassification->tiers) {
- if (mapTier == donorItem->getTier() - 1) {
- cost = mapPrice.priceToUpgrade;
- coresAmount = mapPrice.corePriceToFuse;
- break;
- }
+ if (!itemClassification->tiers.contains(donorItem->getTier())) {
+ g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, donorItem->getTier(), donorItem->getClassification(), itemClassification->id);
+ sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
+ break;
}
+ auto tierPriecs = itemClassification->tiers.at(donorItem->getTier());
+ cost = convergence ? tierPriecs.convergenceTransferPrice : tierPriecs.regularPrice;
+ coresAmount = tierPriecs.corePrice;
+ break;
}
if (!removeItemCountById(ITEM_FORGE_CORE, coresAmount)) {
@@ -7259,22 +7355,22 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t
return;
}
- history.firstItemName = newDonorItem->getName();
+ history.firstItemName = Item::items[donorItemId].name;
history.secondItemName = newReceiveItem->getName();
history.createdAt = getTimeNow();
+ history.convergence = convergence;
registerForgeHistoryDescription(history);
- sendTransferItemTier(donorItemId, tier, receiveItemId);
+ sendForgeResult(actionType, donorItemId, tier, receiveItemId, convergence ? tier : tier - 1, true, 0, 0, convergence);
}
-void Player::forgeResourceConversion(uint8_t action) {
- auto actionEnum = magic_enum::enum_value(action);
+void Player::forgeResourceConversion(ForgeAction_t actionType) {
ForgeHistory history;
- history.actionType = actionEnum;
+ history.actionType = actionType;
history.success = true;
ReturnValue returnValue = RETURNVALUE_NOERROR;
- if (actionEnum == ForgeConversion_t::FORGE_ACTION_DUSTTOSLIVERS) {
+ if (actionType == ForgeAction_t::DUSTTOSLIVERS) {
auto dusts = getForgeDusts();
auto cost = static_cast(g_configManager().getNumber(FORGE_COST_ONE_SLIVER, __FUNCTION__) * g_configManager().getNumber(FORGE_SLIVER_AMOUNT, __FUNCTION__));
if (cost > dusts) {
@@ -7295,7 +7391,7 @@ void Player::forgeResourceConversion(uint8_t action) {
history.cost = cost;
history.gained = 3;
setForgeDusts(dusts - cost);
- } else if (actionEnum == ForgeConversion_t::FORGE_ACTION_SLIVERSTOCORES) {
+ } else if (actionType == ForgeAction_t::SLIVERSTOCORES) {
auto [sliverCount, coreCount] = getForgeSliversAndCores();
auto cost = static_cast(g_configManager().getNumber(FORGE_CORE_COST, __FUNCTION__));
if (cost > sliverCount) {
@@ -7361,10 +7457,10 @@ void Player::registerForgeHistoryDescription(ForgeHistory history) {
std::stringstream detailsResponse;
auto itemId = Item::items.getItemIdByName(history.firstItemName);
const ItemType &itemType = Item::items[itemId];
- if (history.actionType == ForgeConversion_t::FORGE_ACTION_FUSION) {
+ if (history.actionType == ForgeAction_t::FUSION) {
if (history.success) {
detailsResponse << fmt::format(
- "{:s}
"
+ "{:s}{:s}
"
"Fusion partners:"
" "
"- "
@@ -7398,6 +7494,7 @@ void Player::registerForgeHistoryDescription(ForgeHistory history) {
"
"
"
",
successfulString,
+ history.convergence ? " (convergence)" : "",
itemType.article, itemType.name, std::to_string(history.tier),
itemType.article, itemType.name, std::to_string(history.tier),
history.bonus == 8 ? "unchanged" : "consumed",
@@ -7405,7 +7502,7 @@ void Player::registerForgeHistoryDescription(ForgeHistory history) {
);
} else {
detailsResponse << fmt::format(
- "{:s}
"
+ "{:s}{:s}
"
"Fusion partners:"
" "
"- "
@@ -7439,15 +7536,16 @@ void Player::registerForgeHistoryDescription(ForgeHistory history) {
"
"
"
",
successfulString,
+ history.convergence ? " (convergence)" : "",
itemType.article, itemType.name, std::to_string(history.tier),
itemType.article, itemType.name, std::to_string(history.tier),
history.bonus == 8 ? "unchanged" : historyTierString,
history.coresCost, price
);
}
- } else if (history.actionType == ForgeConversion_t::FORGE_ACTION_TRANSFER) {
+ } else if (history.actionType == ForgeAction_t::TRANSFER) {
detailsResponse << fmt::format(
- "{:s}
"
+ "{:s}{:s}
"
"Transfer partners:"
" "
"- "
@@ -7481,19 +7579,20 @@ void Player::registerForgeHistoryDescription(ForgeHistory history) {
"
"
"
",
successfulString,
+ history.convergence ? " (convergence)" : "",
itemType.article, itemType.name, std::to_string(history.tier),
itemType.article, itemType.name, std::to_string(history.tier),
itemType.article, itemType.name, std::to_string(history.tier),
itemType.article, itemType.name, std::to_string(history.tier),
price
);
- } else if (history.actionType == ForgeConversion_t::FORGE_ACTION_DUSTTOSLIVERS) {
+ } else if (history.actionType == ForgeAction_t::DUSTTOSLIVERS) {
detailsResponse << fmt::format("Converted {:d} dust to {:d} slivers.", history.cost, history.gained);
- } else if (history.actionType == ForgeConversion_t::FORGE_ACTION_SLIVERSTOCORES) {
- history.actionType = ForgeConversion_t::FORGE_ACTION_DUSTTOSLIVERS;
+ } else if (history.actionType == ForgeAction_t::SLIVERSTOCORES) {
+ history.actionType = ForgeAction_t::DUSTTOSLIVERS;
detailsResponse << fmt::format("Converted {:d} slivers to {:d} exalted core.", history.cost, history.gained);
- } else if (history.actionType == ForgeConversion_t::FORGE_ACTION_INCREASELIMIT) {
- history.actionType = ForgeConversion_t::FORGE_ACTION_DUSTTOSLIVERS;
+ } else if (history.actionType == ForgeAction_t::INCREASELIMIT) {
+ history.actionType = ForgeAction_t::DUSTTOSLIVERS;
detailsResponse << fmt::format("Spent {:d} dust to increase the dust limit to {:d}.", history.cost, history.gained + 1);
} else {
detailsResponse << "(unknown)";
@@ -7844,6 +7943,18 @@ bool Player::hasPermittedConditionInPZ() const {
return hasPermittedCondition;
}
+uint16_t Player::getDodgeChance() const {
+ uint16_t chance = 0;
+ if (auto playerArmor = getInventoryItem(CONST_SLOT_ARMOR);
+ playerArmor != nullptr && playerArmor->getTier()) {
+ chance += static_cast(playerArmor->getDodgeChance() * 100);
+ }
+
+ chance += m_wheelPlayer->getStat(WheelStat_t::DODGE);
+
+ return chance;
+}
+
void Player::checkAndShowBlessingMessage() {
auto adventurerBlessingLevel = g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL, __FUNCTION__);
auto willNotLoseBless = getLevel() < adventurerBlessingLevel && getVocationId() > VOCATION_NONE;
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 521399425e9..ecf489b89ec 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -17,6 +17,7 @@
#include "items/containers/depot/depotchest.hpp"
#include "items/containers/depot/depotlocker.hpp"
#include "grouping/familiars.hpp"
+#include "enums/forge_conversion.hpp"
#include "grouping/groups.hpp"
#include "grouping/guild.hpp"
#include "imbuements/imbuements.hpp"
@@ -49,16 +50,8 @@ class Spell;
class PlayerWheel;
class Spectators;
-enum class ForgeConversion_t : uint8_t {
- FORGE_ACTION_FUSION = 0,
- FORGE_ACTION_TRANSFER = 1,
- FORGE_ACTION_DUSTTOSLIVERS = 2,
- FORGE_ACTION_SLIVERSTOCORES = 3,
- FORGE_ACTION_INCREASELIMIT = 4
-};
-
struct ForgeHistory {
- ForgeConversion_t actionType = ForgeConversion_t::FORGE_ACTION_FUSION;
+ ForgeAction_t actionType = ForgeAction_t::FUSION;
uint8_t tier = 0;
uint8_t bonus = 0;
@@ -75,6 +68,7 @@ struct ForgeHistory {
bool tierLoss = false;
bool successCore = false;
bool tierCore = false;
+ bool convergence = false;
std::string description;
std::string firstItemName;
@@ -172,6 +166,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
bool hasAnyMount() const;
uint8_t getRandomMountId() const;
void dismount();
+ uint16_t getDodgeChance() const;
uint8_t isRandomMounted() const {
return randomMount;
@@ -782,8 +777,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
void onReceiveMail();
bool isNearDepotBox();
- std::shared_ptr setLootContainer(ObjectCategory_t category, std::shared_ptr container, bool loading = false);
- std::shared_ptr getLootContainer(ObjectCategory_t category) const;
+ std::shared_ptr refreshManagedContainer(ObjectCategory_t category, std::shared_ptr container, bool isLootContainer, bool loading = false);
+ std::shared_ptr getManagedContainer(ObjectCategory_t category, bool isLootContainer) const;
+ void setMainBackpackUnassigned(std::shared_ptr container);
bool canSee(const Position &pos) override;
bool canSeeCreature(std::shared_ptr creature) const override;
@@ -880,7 +876,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
BlockType_t blockHit(std::shared_ptr attacker, CombatType_t combatType, int32_t &damage, bool checkDefense = false, bool checkArmor = false, bool field = false) override;
void doAttacking(uint32_t interval) override;
bool hasExtraSwing() override {
- return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) >= getAttackSpeed());
+ return lastAttack > 0 && !checkLastAttackWithin(getAttackSpeed());
}
uint16_t getSkillLevel(skills_t skill) const;
@@ -895,10 +891,48 @@ class Player final : public Creature, public Cylinder, public Bankable {
bool getAddAttackSkill() const {
return addAttackSkillPoint;
}
+
BlockType_t getLastAttackBlockType() const {
return lastAttackBlockType;
}
+ uint64_t getLastAttack() const {
+ return lastAttack;
+ }
+
+ bool checkLastAttackWithin(uint32_t interval) const {
+ return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) < interval);
+ }
+
+ void updateLastAttack() {
+ if (lastAttack == 0) {
+ lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
+ return;
+ }
+ lastAttack = OTSYS_TIME();
+ }
+
+ uint64_t getLastAggressiveAction() const {
+ return lastAggressiveAction;
+ }
+
+ bool checkLastAggressiveActionWithin(uint32_t interval) const {
+ return lastAggressiveAction > 0 && ((OTSYS_TIME() - lastAggressiveAction) < interval);
+ }
+
+ void updateLastAggressiveAction() {
+ lastAggressiveAction = OTSYS_TIME();
+ }
+
+ uint64_t getLastFocusLost() const {
+ return lastFocusLost;
+ }
+ void setLastFocusLost(uint64_t time) {
+ lastFocusLost = time;
+ }
+
+ std::unordered_set getNPCSkips();
+
std::shared_ptr- getWeapon(Slots_t slot, bool ignoreAmmo) const;
std::shared_ptr
- getWeapon(bool ignoreAmmo = false) const;
WeaponType_t getWeaponType() const;
@@ -2330,9 +2364,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
);
// Forge system
- void forgeFuseItems(uint16_t itemid, uint8_t tier, bool success, bool reduceTierLoss, uint8_t bonus, uint8_t coreCount);
- void forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId);
- void forgeResourceConversion(uint8_t action);
+ void forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemid, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount);
+ void forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId, bool convergence);
+ void forgeResourceConversion(ForgeAction_t actionType);
void forgeHistory(uint8_t page) const;
void sendOpenForge() const {
@@ -2345,14 +2379,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
client->sendForgeError(returnValue);
}
}
- void sendForgeFusionItem(uint16_t itemId, uint8_t tier, bool success, uint8_t bonus, uint8_t coreCount) const {
+ void sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId, uint8_t leftTier, uint16_t rightItemId, uint8_t rightTier, bool success, uint8_t bonus, uint8_t coreCount, bool convergence) const {
if (client) {
- client->sendForgeFusionItem(itemId, tier, success, bonus, coreCount);
- }
- }
- void sendTransferItemTier(uint16_t firstItem, uint8_t tier, uint16_t secondItem) const {
- if (client) {
- client->sendTransferItemTier(firstItem, tier, secondItem);
+ client->sendForgeResult(actionType, leftItemId, leftTier, rightItemId, rightTier, success, bonus, coreCount, convergence);
}
}
void sendForgeHistory(uint8_t page) const {
@@ -2518,12 +2547,32 @@ class Player final : public Creature, public Cylinder, public Bankable {
return timeLeft > 0;
}
- bool checkAutoLoot() const {
- const bool autoLoot = g_configManager().getBoolean(AUTOLOOT, __FUNCTION__) && getStorageValue(STORAGEVALUE_AUTO_LOOT) != 0;
- if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY, __FUNCTION__)) {
- return autoLoot && isVip();
+ bool checkAutoLoot(bool isBoss) const {
+ const bool autoLoot = g_configManager().getBoolean(AUTOLOOT, __FUNCTION__);
+ if (!autoLoot) {
+ return false;
+ }
+ if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY, __FUNCTION__) && !isVip()) {
+ return false;
}
- return autoLoot;
+
+ auto featureKV = kv()->scoped("features")->get("autoloot");
+ if (featureKV.has_value()) {
+ auto value = featureKV->getNumber();
+ if (value == 2) {
+ return true;
+ } else if (value == 1) {
+ return !isBoss;
+ } else if (value == 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ QuickLootFilter_t getQuickLootFilter() const {
+ return quickLootFilter;
}
// Get specific inventory item from itemid
@@ -2566,7 +2615,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
void checkTradeState(std::shared_ptr
- item);
bool hasCapacity(std::shared_ptr
- item, uint32_t count) const;
- void checkLootContainers(std::shared_ptr
- item);
+ void checkLootContainers(std::shared_ptr item);
void gainExperience(uint64_t exp, std::shared_ptr target);
void addExperience(std::shared_ptr target, uint64_t exp, bool sendText = false);
@@ -2640,12 +2689,12 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::map maxValuePerSkill = {
{ SKILL_LIFE_LEECH_CHANCE, 100 },
{ SKILL_MANA_LEECH_CHANCE, 100 },
- { SKILL_CRITICAL_HIT_CHANCE, g_configManager().getNumber(CRITICALCHANCE, "std::map::maxValuePerSkill") }
+ { SKILL_CRITICAL_HIT_CHANCE, 100 * g_configManager().getNumber(CRITICALCHANCE, "std::map::maxValuePerSkill") }
};
std::map> rewardMap;
- std::map> quickLootContainers;
+ std::map, std::shared_ptr>> m_managedContainers;
std::vector forgeHistoryVector;
std::vector quickLootListItemIds;
@@ -2682,6 +2731,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
uint64_t experience = 0;
uint64_t manaSpent = 0;
uint64_t lastAttack = 0;
+ uint64_t lastAggressiveAction = 0;
+ uint64_t lastFocusLost = 0;
uint64_t bankBalance = 0;
uint64_t lastQuestlogUpdate = 0;
uint64_t preyCards = 0;
@@ -2917,6 +2968,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
void triggerMomentum();
void clearCooldowns();
+ void triggerTranscendance();
friend class Game;
friend class SaveManager;
diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp
index a27413b61f6..fc07cea986f 100644
--- a/src/creatures/players/vocations/vocation.cpp
+++ b/src/creatures/players/vocations/vocation.cpp
@@ -111,6 +111,10 @@ bool Vocations::loadFromXml() {
voc.combat = attr.as_bool();
}
+ if ((attr = vocationNode.attribute("avatarlooktype"))) {
+ voc.avatarLookType = pugi::cast(attr.value());
+ }
+
for (auto childNode : vocationNode.children()) {
if (strcasecmp(childNode.name(), "skill") == 0) {
pugi::xml_attribute skillIdAttribute = childNode.attribute("id");
@@ -173,6 +177,12 @@ bool Vocations::loadFromXml() {
if (pvpDamageDealtMultiplier) {
voc.pvpDamageDealtMultiplier = pugi::cast(pvpDamageDealtMultiplier.value());
}
+ } else if (strcasecmp(childNode.name(), "gem") == 0) {
+ pugi::xml_attribute qualityAttr = childNode.attribute("quality");
+ pugi::xml_attribute nameAttr = childNode.attribute("name");
+ auto quality = pugi::cast(qualityAttr.value());
+ auto name = nameAttr.as_string();
+ voc.wheelGems[static_cast(quality)] = name;
}
}
}
@@ -272,3 +282,22 @@ uint64_t Vocation::getReqMana(uint32_t magLevel) {
cacheMana[magLevel] = reqMana;
return reqMana;
}
+
+std::vector Vocation::getSupremeGemModifiers() {
+ if (!m_supremeGemModifiers.empty()) {
+ return m_supremeGemModifiers;
+ }
+ auto baseVocation = g_vocations().getVocation(getBaseId());
+ auto vocationName = asLowerCaseString(baseVocation->getVocName());
+ auto allModifiers = magic_enum::enum_entries();
+ g_logger().debug("Loading supreme gem modifiers for vocation: {}", vocationName);
+ for (const auto &[value, modifierName] : allModifiers) {
+ std::string targetVocation(modifierName.substr(0, modifierName.find("_")));
+ toLowerCaseString(targetVocation);
+ g_logger().debug("Checking supreme gem modifier: {}, targetVocation: {}", modifierName, targetVocation);
+ if (targetVocation == "general" || targetVocation.find(vocationName) != std::string::npos) {
+ m_supremeGemModifiers.push_back(value);
+ }
+ }
+ return m_supremeGemModifiers;
+}
diff --git a/src/creatures/players/vocations/vocation.hpp b/src/creatures/players/vocations/vocation.hpp
index 45f5d29d361..8f81e3805e3 100644
--- a/src/creatures/players/vocations/vocation.hpp
+++ b/src/creatures/players/vocations/vocation.hpp
@@ -12,6 +12,7 @@
#include "declarations.hpp"
#include "items/item.hpp"
#include "lib/di/container.hpp"
+#include "creatures/players/wheel/wheel_gems.hpp"
class Vocation {
public:
@@ -41,6 +42,10 @@ class Vocation {
return baseId;
}
+ uint16_t getAvatarLookType() const {
+ return avatarLookType;
+ }
+
uint32_t getHPGain() const {
return gainHP;
}
@@ -110,6 +115,16 @@ class Vocation {
float pvpDamageReceivedMultiplier = 1.0f;
float pvpDamageDealtMultiplier = 1.0f;
+ std::vector getSupremeGemModifiers();
+
+ uint16_t getWheelGemId(WheelGemQuality_t quality) {
+ if (!wheelGems.contains(quality)) {
+ return 0;
+ }
+ const auto &name = wheelGems[quality];
+ return Item::items.getItemIdByName(name);
+ }
+
private:
friend class Vocations;
@@ -117,6 +132,7 @@ class Vocation {
std::map cacheManaTotal;
std::map cacheSkill[SKILL_LAST + 1];
std::map cacheSkillTotal[SKILL_LAST + 1];
+ std::map wheelGems;
std::string name = "none";
std::string description;
@@ -144,6 +160,9 @@ class Vocation {
uint8_t soulMax = 100;
uint8_t clientId = 0;
uint8_t baseId = 0;
+ uint16_t avatarLookType = 0;
+
+ std::vector m_supremeGemModifiers;
static uint32_t skillBase[SKILL_LAST + 1];
};
diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp
index a60d8124a76..dfd685bd83f 100644
--- a/src/creatures/players/wheel/player_wheel.cpp
+++ b/src/creatures/players/wheel/player_wheel.cpp
@@ -17,6 +17,21 @@
#include "creatures/players/player.hpp"
#include "creatures/combat/spells.hpp"
+const static std::vector wheelGemBasicSlot1Allowed = {
+ WheelGemBasicModifier_t::General_FireResistance,
+ WheelGemBasicModifier_t::General_IceResistance,
+ WheelGemBasicModifier_t::General_EnergyResistance,
+ WheelGemBasicModifier_t::General_EarthResistance,
+ WheelGemBasicModifier_t::General_MitigationMultiplier,
+ WheelGemBasicModifier_t::Vocation_Health,
+ WheelGemBasicModifier_t::Vocation_Mana,
+ WheelGemBasicModifier_t::Vocation_Capacity,
+ WheelGemBasicModifier_t::Vocation_Health_FireResistance,
+ WheelGemBasicModifier_t::Vocation_Health_IceResistance,
+ WheelGemBasicModifier_t::Vocation_Health_EnergyResistance,
+ WheelGemBasicModifier_t::Vocation_Health_EarthResistance,
+};
+
// To avoid conflict in other files that might use a function with the same name
// Here are built-in helper functions
namespace {
@@ -703,6 +718,211 @@ void PlayerWheel::addPromotionScrolls(NetworkMessage &msg) const {
}
}
+std::shared_ptr PlayerWheel::gemsKV() const {
+ return m_player.kv()->scoped("wheel-of-destiny")->scoped("gems");
+}
+
+std::vector PlayerWheel::getRevealedGems() const {
+ std::vector unlockedGems;
+ auto unlockedGemUUIDs = gemsKV()->scoped("revealed")->keys();
+ if (unlockedGemUUIDs.empty()) {
+ return unlockedGems;
+ }
+ std::vector sortedUnlockedGemGUIDs;
+ for (const auto &uuid : unlockedGemUUIDs) {
+ sortedUnlockedGemGUIDs.push_back(uuid);
+ }
+ std::sort(sortedUnlockedGemGUIDs.begin(), sortedUnlockedGemGUIDs.end(), [](const std::string &a, const std::string &b) {
+ return std::stoull(a) < std::stoull(b);
+ });
+
+ for (const auto &uuid : sortedUnlockedGemGUIDs) {
+ auto gem = PlayerWheelGem::load(gemsKV(), uuid);
+ if (gem.uuid.empty()) {
+ continue;
+ }
+ unlockedGems.push_back(gem);
+ }
+ return unlockedGems;
+}
+
+std::vector PlayerWheel::getActiveGems() const {
+ std::vector activeGems;
+ for (auto affinity : magic_enum::enum_values()) {
+ std::string key(magic_enum::enum_name(affinity));
+ auto uuidKV = gemsKV()->scoped("active")->get(key);
+ if (!uuidKV.has_value()) {
+ continue;
+ }
+
+ auto uuid = uuidKV->get();
+ if (uuid.empty()) {
+ continue;
+ }
+ auto gem = PlayerWheelGem::load(gemsKV(), uuid);
+ if (gem.uuid.empty()) {
+ continue;
+ }
+ activeGems.push_back(gem);
+ }
+ return activeGems;
+}
+
+void PlayerWheel::revealGem(WheelGemQuality_t quality) {
+ uint16_t gemId = m_player.getVocation()->getWheelGemId(quality);
+ if (gemId == 0) {
+ g_logger().error("[{}] Failed to get gem id for quality {} and vocation {}", __FUNCTION__, fmt::underlying(quality), m_player.getVocation()->getVocName());
+ return;
+ }
+ if (!m_player.hasItemCountById(gemId, 1, false)) {
+ g_logger().error("[{}] Player {} does not have gem with id {}", __FUNCTION__, m_player.getName(), gemId);
+ return;
+ }
+ auto goldCost = getGemRevealCost(quality);
+ if (!g_game().removeMoney(m_player.getPlayer(), goldCost, 0, true)) {
+ g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, goldCost, m_player.getName());
+ return;
+ }
+ if (!m_player.removeItemCountById(gemId, 1, false)) {
+ g_logger().error("[{}] Failed to remove gem with id {} from player with name {}", __FUNCTION__, gemId, m_player.getName());
+ return;
+ }
+ auto supremeModifiers = m_player.getVocation()->getSupremeGemModifiers();
+ PlayerWheelGem gem;
+ gem.uuid = KV::generateUUID();
+ gem.locked = false;
+ gem.affinity = static_cast(uniform_random(0, 3));
+ gem.quality = quality;
+ gem.basicModifier1 = wheelGemBasicSlot1Allowed[uniform_random(0, wheelGemBasicSlot1Allowed.size() - 1)];
+ gem.basicModifier2 = {};
+ gem.supremeModifier = {};
+ if (quality >= WheelGemQuality_t::Regular) {
+ gem.basicModifier2 = static_cast(uniform_random(0, magic_enum::enum_count() - 1));
+ }
+ if (quality >= WheelGemQuality_t::Greater && !supremeModifiers.empty()) {
+ gem.supremeModifier = supremeModifiers[uniform_random(0, supremeModifiers.size() - 1)];
+ }
+ g_logger().debug("[{}] {}", __FUNCTION__, gem.toString());
+ gem.save(gemsKV());
+ sendOpenWheelWindow(m_player.getID());
+}
+
+PlayerWheelGem PlayerWheel::getGem(uint8_t index) const {
+ auto gems = getRevealedGems();
+ if (gems.size() <= index) {
+ g_logger().error("[{}] Player {} trying to get gem with index {} but has only {} gems", __FUNCTION__, m_player.getName(), index, gems.size());
+ return {};
+ }
+ return gems[index];
+}
+
+PlayerWheelGem PlayerWheel::getGem(const std::string &uuid) const {
+ auto gem = PlayerWheelGem::load(gemsKV(), uuid);
+ if (gem.uuid.empty()) {
+ g_logger().error("[{}] Failed to load gem with uuid {}", __FUNCTION__, uuid);
+ return {};
+ }
+ return gem;
+}
+
+uint8_t PlayerWheel::getGemIndex(const std::string &uuid) const {
+ auto gems = getRevealedGems();
+ for (uint8_t i = 0; i < gems.size(); ++i) {
+ if (gems[i].uuid == uuid) {
+ return i;
+ }
+ }
+ g_logger().error("[{}] Failed to find gem with uuid {}", __FUNCTION__, uuid);
+ return 0xFF;
+}
+
+void PlayerWheel::destroyGem(uint8_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);
+ return;
+ }
+ gem.remove(gemsKV());
+ sendOpenWheelWindow(m_player.getID());
+}
+
+void PlayerWheel::switchGemDomain(uint8_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);
+ return;
+ }
+ auto goldCost = getGemRotateCost(gem.quality);
+ if (!g_game().removeMoney(m_player.getPlayer(), goldCost, 0, true)) {
+ g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, goldCost, m_player.getName());
+ return;
+ }
+
+ auto gemAffinity = convertWheelGemAffinityToDomain(static_cast(gem.affinity));
+ gem.affinity = static_cast(gemAffinity);
+ gem.save(gemsKV());
+ sendOpenWheelWindow(m_player.getID());
+}
+
+void PlayerWheel::toggleGemLock(uint8_t index) {
+ auto gem = getGem(index);
+ gem.locked = !gem.locked;
+ gem.save(gemsKV());
+ sendOpenWheelWindow(m_player.getID());
+}
+
+void PlayerWheel::setActiveGem(WheelGemAffinity_t affinity, uint8_t index) {
+ auto gem = getGem(index);
+ if (gem.uuid.empty()) {
+ g_logger().error("[{}] Failed to load gem with index {}", __FUNCTION__, index);
+ return;
+ }
+ if (gem.affinity != affinity) {
+ g_logger().error("[{}] Gem with index {} has affinity {} but trying to set it to {}", __FUNCTION__, index, fmt::underlying(gem.affinity), fmt::underlying(affinity));
+ return;
+ }
+ std::string key(magic_enum::enum_name(affinity));
+ gemsKV()->scoped("active")->set(key, gem.uuid);
+}
+
+void PlayerWheel::removeActiveGem(WheelGemAffinity_t affinity) {
+ std::string key(magic_enum::enum_name(affinity));
+ gemsKV()->scoped("active")->remove(key);
+}
+
+void PlayerWheel::addGems(NetworkMessage &msg) const {
+ auto activeGems = getActiveGems();
+ msg.addByte(activeGems.size());
+ g_logger().debug("[{}] Player {} has {} active gems", __FUNCTION__, m_player.getName(), activeGems.size());
+ for (const auto &gem : activeGems) {
+ auto index = getGemIndex(gem.uuid);
+ g_logger().debug("[{}] Adding active gem: {} with index {}", __FUNCTION__, gem.toString(), index);
+ msg.addByte(getGemIndex(gem.uuid));
+ }
+
+ auto revealedGems = getRevealedGems();
+ if (revealedGems.size() > 225) {
+ g_logger().error("[{}] Player {} has more than 225 gems unlocked", __FUNCTION__, m_player.getName());
+ revealedGems.resize(225);
+ }
+ msg.addByte(revealedGems.size());
+ int index = 0;
+ for (const auto &gem : revealedGems) {
+ g_logger().debug("[{}] Adding revealed gem: {}", __FUNCTION__, gem.toString());
+ msg.addByte(index++);
+ msg.addByte(gem.locked);
+ msg.addByte(static_cast(gem.affinity));
+ msg.addByte(static_cast(gem.quality));
+ msg.addByte(static_cast(gem.basicModifier1));
+ if (gem.quality >= WheelGemQuality_t::Regular) {
+ msg.addByte(static_cast(gem.basicModifier2));
+ }
+ if (gem.quality >= WheelGemQuality_t::Greater) {
+ msg.addByte(static_cast(gem.supremeModifier));
+ }
+ }
+}
+
void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) const {
if (m_player.client && m_player.client->oldProtocol) {
return;
@@ -725,6 +945,14 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con
msg.add(getPointsBySlotType(i));
}
addPromotionScrolls(msg);
+ addGems(msg);
+ // TODO: read items from inventory
+ auto voc = m_player.getVocation();
+ m_player.client->sendResourceBalance(RESOURCE_BANK, m_player.getBankBalance());
+ m_player.client->sendResourceBalance(RESOURCE_INVENTORY, m_player.getMoney());
+ 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)));
}
void PlayerWheel::sendGiftOfLifeCooldown() const {
@@ -844,6 +1072,17 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) {
g_logger().error("[parseSaveWheel] Player '{}' tried to select a slot without the valid requirements", m_player.getName());
}
+ // Gem Vessels
+ for (auto affinity : magic_enum::enum_values()) {
+ bool hasGem = msg.getByte();
+ if (!hasGem) {
+ removeActiveGem(affinity);
+ continue;
+ }
+ uint8_t gemIndex = msg.getByte();
+ setActiveGem(affinity, gemIndex);
+ }
+
// Player's bonus data is loaded, initialized, and registered, and the function logs
loadPlayerBonusData();
initializePlayerData();
@@ -1051,15 +1290,15 @@ void PlayerWheel::initializePlayerData() {
void PlayerWheel::setPlayerCombatStats(CombatType_t type, int32_t leechAmount) {
if (type == COMBAT_LIFEDRAIN) {
if (leechAmount > 0) {
- setStat(WheelStat_t::LIFE_LEECH, leechAmount);
+ addStat(WheelStat_t::LIFE_LEECH, leechAmount);
} else {
- setStat(WheelStat_t::LIFE_LEECH, 0);
+ addStat(WheelStat_t::LIFE_LEECH, 0);
}
} else if (type == COMBAT_MANADRAIN) {
if (leechAmount > 0) {
- setStat(WheelStat_t::MANA_LEECH, leechAmount);
+ addStat(WheelStat_t::MANA_LEECH, leechAmount);
} else {
- setStat(WheelStat_t::MANA_LEECH, 0);
+ addStat(WheelStat_t::MANA_LEECH, 0);
}
}
}
@@ -1078,27 +1317,49 @@ void PlayerWheel::reloadPlayerData() {
}
void PlayerWheel::registerPlayerBonusData() {
- // Reset stages and spell data
resetUpgradedSpells();
- // Reset resistance
resetResistance();
- // Stats
- setStat(WheelStat_t::HEALTH, m_playerBonusData.stats.health);
- setStat(WheelStat_t::MANA, m_playerBonusData.stats.mana);
- setStat(WheelStat_t::CAPACITY, m_playerBonusData.stats.capacity * 100);
- setStat(WheelStat_t::MITIGATION, m_playerBonusData.mitigation * 100);
- setStat(WheelStat_t::DAMAGE, m_playerBonusData.stats.damage);
- setStat(WheelStat_t::HEALING, m_playerBonusData.stats.healing);
-
- // Resistance
- for (uint16_t i = 0; i < COMBAT_COUNT; ++i) {
- setResistance(indexToCombatType(i), m_playerBonusData.resistance[i]);
- }
+ 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);
+ addStat(WheelStat_t::MITIGATION, m_playerBonusData.mitigation * 100);
+ 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
- setStat(WheelStat_t::MELEE, m_playerBonusData.skills.melee);
- setStat(WheelStat_t::DISTANCE, m_playerBonusData.skills.distance);
- setStat(WheelStat_t::MAGIC, m_playerBonusData.skills.magic);
+ addStat(WheelStat_t::MELEE, m_playerBonusData.skills.melee);
+ addStat(WheelStat_t::DISTANCE, m_playerBonusData.skills.distance);
+ addStat(WheelStat_t::MAGIC, m_playerBonusData.skills.magic);
// Leech
setPlayerCombatStats(COMBAT_LIFEDRAIN, m_playerBonusData.leech.lifeLeech * 100);
@@ -1142,6 +1403,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.stages.divineEmpowerment; ++i) {
setSpellInstant("Divine Empowerment", true);
}
+ if (m_playerBonusData.stages.divineEmpowerment >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 4000;
+ addSpellBonus("Divine Empowerment", bonus);
+ }
+ if (m_playerBonusData.stages.divineEmpowerment >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 4000;
+ addSpellBonus("Divine Empowerment", bonus);
+ }
} else {
setSpellInstant("Divine Empowerment", false);
}
@@ -1150,6 +1421,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.stages.divineGrenade; ++i) {
setSpellInstant("Divine Grenade", true);
}
+ if (m_playerBonusData.stages.divineGrenade >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 4000;
+ addSpellBonus("Divine Grenade", bonus);
+ }
+ if (m_playerBonusData.stages.divineGrenade >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 6000;
+ addSpellBonus("Divine Grenade", bonus);
+ }
} else {
setSpellInstant("Divine Grenade", false);
}
@@ -1190,6 +1471,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.avatar.light; ++i) {
setSpellInstant("Avatar of Light", true);
}
+ if (m_playerBonusData.avatar.light >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 3 * 60 * 1000;
+ addSpellBonus("Avatar of Light", bonus);
+ }
+ if (m_playerBonusData.avatar.light >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 2 * 60 * 1000;
+ addSpellBonus("Avatar of Light", bonus);
+ }
} else {
setSpellInstant("Avatar of Light", false);
}
@@ -1198,6 +1489,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.avatar.nature; ++i) {
setSpellInstant("Avatar of Nature", true);
}
+ if (m_playerBonusData.avatar.nature >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 3 * 60 * 1000;
+ addSpellBonus("Avatar of Nature", bonus);
+ }
+ if (m_playerBonusData.avatar.nature >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 2 * 60 * 1000;
+ addSpellBonus("Avatar of Nature", bonus);
+ }
} else {
setSpellInstant("Avatar of Nature", false);
}
@@ -1206,6 +1507,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.avatar.steel; ++i) {
setSpellInstant("Avatar of Steel", true);
}
+ if (m_playerBonusData.avatar.steel >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 3 * 60 * 1000;
+ addSpellBonus("Avatar of Steel", bonus);
+ }
+ if (m_playerBonusData.avatar.steel >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 2 * 60 * 1000;
+ addSpellBonus("Avatar of Steel", bonus);
+ }
} else {
setSpellInstant("Avatar of Steel", false);
}
@@ -1214,6 +1525,16 @@ void PlayerWheel::registerPlayerBonusData() {
for (int i = 0; i < m_playerBonusData.avatar.storm; ++i) {
setSpellInstant("Avatar of Storm", true);
}
+ if (m_playerBonusData.avatar.storm >= 2) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 3 * 60 * 1000;
+ addSpellBonus("Avatar of Storm", bonus);
+ }
+ if (m_playerBonusData.avatar.storm >= 3) {
+ WheelSpells::Bonus bonus;
+ bonus.decrease.cooldown = 2 * 60 * 1000;
+ addSpellBonus("Avatar of Storm", bonus);
+ }
} else {
setSpellInstant("Avatar of Storm", false);
}
@@ -1270,18 +1591,16 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus
g_logger().debug(" healing: {}", bonusData.stats.healing);
}
- g_logger().debug("Resistance:");
- for (size_t i = 0; i < bonusData.resistance.size(); ++i) {
- auto combatValue = bonusData.resistance[i];
- if (combatValue == 0) {
+ g_logger().debug("Vessel Resonance:");
+ for (size_t i = 0; i < bonusData.unlockedVesselResonances.size(); ++i) {
+ auto count = bonusData.unlockedVesselResonances[i];
+ if (count == 0) {
continue;
}
- CombatType_t combatType = indexToCombatType(i);
- std::string combatTypeStr = getCombatName(combatType);
- // Convert to percentage
- float percentage = bonusData.resistance[i] / 100.0f;
- g_logger().debug(" combatName: {} value: {} ({}%)", combatTypeStr, bonusData.resistance[i], percentage);
+ WheelGemAffinity_t affinity = static_cast(i);
+ std::string affinityName(magic_enum::enum_name(affinity));
+ g_logger().debug(" Affinity: {} count: {}", affinityName, bonusData.unlockedVesselResonances[i]);
}
g_logger().debug("Skills:");
@@ -1513,7 +1832,9 @@ void PlayerWheel::loadRevelationPerks() {
WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) const {
std::vector slots;
+ WheelGemAffinity_t affinity = WheelGemAffinity_t::Green;
if (color == "green") {
+ affinity = WheelGemAffinity_t::Green;
slots = {
WheelSlots_t::SLOT_GREEN_50,
WheelSlots_t::SLOT_GREEN_TOP_75,
@@ -1526,6 +1847,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons
WheelSlots_t::SLOT_GREEN_200
};
} else if (color == "red") {
+ affinity = WheelGemAffinity_t::Red;
slots = {
WheelSlots_t::SLOT_RED_50,
WheelSlots_t::SLOT_RED_TOP_75,
@@ -1538,6 +1860,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons
WheelSlots_t::SLOT_RED_200
};
} else if (color == "purple") {
+ affinity = WheelGemAffinity_t::Purple;
slots = {
WheelSlots_t::SLOT_PURPLE_50,
WheelSlots_t::SLOT_PURPLE_TOP_75,
@@ -1550,6 +1873,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons
WheelSlots_t::SLOT_PURPLE_200
};
} else if (color == "blue") {
+ affinity = WheelGemAffinity_t::Blue;
slots = {
WheelSlots_t::SLOT_BLUE_50,
WheelSlots_t::SLOT_BLUE_TOP_75,
@@ -1569,6 +1893,8 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons
for (const auto &slot : slots) {
totalPoints += getPointsBySlotType(slot);
}
+ totalPoints += m_bonusRevelationPoints[static_cast(affinity)];
+
if (totalPoints >= static_cast(WheelStagePointsEnum_t::THREE)) {
return WheelStageEnum_t::THREE;
} else if (totalPoints >= static_cast(WheelStagePointsEnum_t::TWO)) {
@@ -1711,7 +2037,7 @@ bool PlayerWheel::checkPositionalTatics() {
bool PlayerWheel::checkBallisticMastery() {
setOnThinkTimer(WheelOnThink_t::BALLISTIC_MASTERY, OTSYS_TIME() + 2000);
bool updateClient = false;
- int32_t newCritical = 10;
+ int32_t newCritical = 1000;
uint16_t newHolyBonus = 2; // 2%
uint16_t newPhysicalBonus = 2; // 2%
@@ -1760,11 +2086,11 @@ bool PlayerWheel::checkCombatMastery() {
if (item && item->getSlotPosition() & SLOTP_TWO_HAND) {
int32_t criticalSkill = 0;
if (stage >= 3) {
- criticalSkill = 12;
+ criticalSkill = 1200;
} else if (stage >= 2) {
- criticalSkill = 8;
+ criticalSkill = 800;
} else if (stage >= 1) {
- criticalSkill = 4;
+ criticalSkill = 400;
}
if (getMajorStat(WheelMajor_t::CRITICAL_DMG_2) != criticalSkill) {
@@ -1823,13 +2149,14 @@ bool PlayerWheel::checkDivineEmpowerment() {
if (isOwner) {
uint8_t stage = getStage(WheelStage_t::DIVINE_EMPOWERMENT);
if (stage >= 3) {
- damageBonus = 12;
+ damageBonus = 7;
} else if (stage >= 2) {
- damageBonus = 10;
+ damageBonus = 5;
} else if (stage >= 1) {
- damageBonus = 8;
+ damageBonus = 3;
}
}
+
if (damageBonus != getMajorStat(WheelMajor_t::DAMAGE)) {
setMajorStat(WheelMajor_t::DAMAGE, damageBonus);
updateClient = true;
@@ -2030,14 +2357,14 @@ int32_t PlayerWheel::checkAvatarSkill(WheelAvatarSkill_t skill) const {
return 5;
}
} else if (skill == WheelAvatarSkill_t::CRITICAL_CHANCE) {
- return 100;
+ return 10000;
} else if (skill == WheelAvatarSkill_t::CRITICAL_DAMAGE) {
if (stage >= 3) {
- return 15;
+ return 1500;
} else if (stage >= 2) {
- return 10;
+ return 1000;
} else if (stage >= 1) {
- return 5;
+ return 500;
}
}
@@ -2064,6 +2391,10 @@ int32_t PlayerWheel::checkElementSensitiveReduction(CombatType_t type) const {
void PlayerWheel::onThink(bool force /* = false*/) {
bool updateClient = false;
m_creaturesNearby = 0;
+ // Gift of life (Cooldown)
+ if (getGiftOfCooldown() > 0 /*getInstant("Gift of Life")*/ && getOnThinkTimer(WheelOnThink_t::GIFT_OF_LIFE) <= OTSYS_TIME()) {
+ decreaseGiftOfCooldown(1);
+ }
if (!m_player.hasCondition(CONDITION_INFIGHT) || m_player.getZoneType() == ZONE_PROTECTION || (!getInstant("Battle Instinct") && !getInstant("Positional Tatics") && !getInstant("Ballistic Mastery") && !getInstant("Gift of Life") && !getInstant("Combat Mastery") && !getInstant("Divine Empowerment") && getGiftOfCooldown() == 0)) {
bool mustReset = false;
for (int i = 0; i < static_cast(WheelMajor_t::TOTAL_COUNT); i++) {
@@ -2097,10 +2428,6 @@ void PlayerWheel::onThink(bool force /* = false*/) {
if (getInstant("Ballistic Mastery") && (force || getOnThinkTimer(WheelOnThink_t::BALLISTIC_MASTERY) < OTSYS_TIME()) && checkBallisticMastery()) {
updateClient = true;
}
- // Gift of life (Cooldown)
- if (getGiftOfCooldown() > 0 /*getInstant("Gift of Life")*/ && getOnThinkTimer(WheelOnThink_t::GIFT_OF_LIFE) <= OTSYS_TIME()) {
- decreaseGiftOfCooldown(1);
- }
// Combat Mastery
if (getInstant("Combat Mastery") && (force || getOnThinkTimer(WheelOnThink_t::COMBAT_MASTERY) < OTSYS_TIME()) && checkCombatMastery()) {
updateClient = true;
@@ -2188,15 +2515,15 @@ std::shared_ptr PlayerWheel::getCombatDataSpell(CombatDamage &damage) {
}
if (spell->getWheelOfDestinyUpgraded()) {
- damage.criticalDamage += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, spellGrade);
- 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::LIFE_LEECH_CHANCE, spellGrade);
- damage.lifeLeech += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, spellGrade);
- damage.lifeLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, spellGrade);
+ 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);
}
}
@@ -2240,21 +2567,22 @@ void PlayerWheel::setInstant(WheelInstant_t type, bool toggle) {
}
}
-void PlayerWheel::setStat(WheelStat_t type, int32_t value) {
+void PlayerWheel::addStat(WheelStat_t type, int32_t value) {
auto enumValue = static_cast(type);
- try {
- m_stats.at(enumValue) = value;
- } catch (const std::out_of_range &e) {
- g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what());
+ if (enumValue >= static_cast(WheelStat_t::TOTAL_COUNT)) {
+ g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, "Enum value is out of range");
+ return;
}
+ m_stats[enumValue] += value;
}
-void PlayerWheel::setResistance(CombatType_t type, int32_t value) {
- try {
- m_resistance.at(combatTypeToIndex(type)) = value;
- } catch (const std::out_of_range &e) {
- g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, combatTypeToIndex(type), value, e.what());
+void PlayerWheel::addResistance(CombatType_t type, int32_t value) {
+ auto index = combatTypeToIndex(type);
+ if (index >= static_cast(WheelStat_t::TOTAL_COUNT)) {
+ g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, index, value, "Enum value is out of range");
+ return;
}
+ m_resistance[index] += value;
}
void PlayerWheel::setSpellInstant(const std::string &name, bool value) {
@@ -2375,6 +2703,12 @@ void PlayerWheel::resetResistance() {
}
}
+void PlayerWheel::resetStats() {
+ for (int32_t i = 0; i < static_cast(WheelStat_t::TOTAL_COUNT); i++) {
+ m_stats[i] = 0;
+ }
+}
+
// Wheel of destiny - Header get:
bool PlayerWheel::getInstant(WheelInstant_t type) const {
auto enumValue = static_cast(type);
diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp
index 70e55ff28d5..42c4f0d35c5 100644
--- a/src/creatures/players/wheel/player_wheel.hpp
+++ b/src/creatures/players/wheel/player_wheel.hpp
@@ -10,6 +10,11 @@
#pragma once
#include "io/io_wheel.hpp"
+#include "utils/utils_definitions.hpp"
+#include "config/config_definitions.hpp"
+#include "config/configmanager.hpp"
+#include "kv/kv.hpp"
+#include "wheel_gems.hpp"
class Spell;
class Player;
@@ -17,6 +22,65 @@ class Creature;
class NetworkMessage;
class IOWheel;
+struct PlayerWheelGem {
+ std::string uuid;
+ bool locked;
+ WheelGemAffinity_t affinity;
+ WheelGemQuality_t quality;
+ WheelGemBasicModifier_t basicModifier1;
+ 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));
+ }
+
+ void save(const std::shared_ptr &kv) const {
+ kv->scoped("revealed")->set(uuid, serialize());
+ }
+
+ void remove(const std::shared_ptr &kv) const {
+ kv->scoped("revealed")->remove(uuid);
+ }
+
+ static PlayerWheelGem load(const std::shared_ptr &kv, const std::string &uuid) {
+ auto val = kv->scoped("revealed")->get(uuid);
+ if (!val || !val.has_value()) {
+ return {};
+ }
+ return deserialize(uuid, val.value());
+ }
+
+private:
+ ValueWrapper serialize() const {
+ return {
+ { "uuid", uuid },
+ { "locked", locked },
+ { "affinity", static_cast(affinity) },
+ { "quality", static_cast(quality) },
+ { "basicModifier1", static_cast(basicModifier1) },
+ { "basicModifier2", static_cast(basicModifier2) },
+ { "supremeModifier", static_cast(supremeModifier) }
+ };
+ }
+
+ static PlayerWheelGem deserialize(const std::string &uuid, const ValueWrapper &val) {
+ auto map = val.get();
+ if (map.empty()) {
+ return {};
+ }
+ return {
+ uuid,
+ map["locked"]->get(),
+ static_cast(map["affinity"]->get()),
+ static_cast(map["quality"]->get()),
+ static_cast(map["basicModifier1"]->get()),
+ static_cast(map["basicModifier2"]->get()),
+ static_cast(map["supremeModifier"]->get())
+ };
+ }
+};
+
class PlayerWheel {
public:
explicit PlayerWheel(Player &initPlayer);
@@ -51,6 +115,7 @@ 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 sendGiftOfLifeCooldown() const;
@@ -144,6 +209,47 @@ class PlayerWheel {
uint8_t getOptions(uint32_t ownerId) const;
uint8_t getPlayerVocationEnum() const;
+ std::shared_ptr gemsKV() const;
+
+ std::vector getRevealedGems() const;
+ std::vector getActiveGems() const;
+
+ static uint64_t getGemRotateCost(WheelGemQuality_t quality) {
+ ConfigKey_t key;
+ switch (quality) {
+ case WheelGemQuality_t::Lesser:
+ key = WHEEL_ATELIER_ROTATE_LESSER_COST;
+ break;
+ case WheelGemQuality_t::Regular:
+ key = WHEEL_ATELIER_ROTATE_REGULAR_COST;
+ break;
+ case WheelGemQuality_t::Greater:
+ key = WHEEL_ATELIER_ROTATE_GREATER_COST;
+ break;
+ default:
+ return 0;
+ }
+ return static_cast(g_configManager().getNumber(key, __FUNCTION__));
+ }
+
+ static uint64_t getGemRevealCost(WheelGemQuality_t quality) {
+ ConfigKey_t key;
+ switch (quality) {
+ case WheelGemQuality_t::Lesser:
+ key = WHEEL_ATELIER_REVEAL_LESSER_COST;
+ break;
+ case WheelGemQuality_t::Regular:
+ key = WHEEL_ATELIER_REVEAL_REGULAR_COST;
+ break;
+ case WheelGemQuality_t::Greater:
+ key = WHEEL_ATELIER_REVEAL_GREATER_COST;
+ break;
+ default:
+ return 0;
+ }
+ return static_cast(g_configManager().getNumber(key, __FUNCTION__));
+ }
+
// Members variables
const uint16_t m_minLevelToStartCountPoints = 50;
uint16_t m_pointsPerLevel = 1;
@@ -216,24 +322,24 @@ class PlayerWheel {
void setInstant(WheelInstant_t type, bool toggle);
/**
- * @brief Sets the value of a specific stat in the Wheel of Destiny.
+ * @brief Adds the value of a specific stat in the Wheel of Destiny.
*
* This function sets the value of the specified stat in the Wheel of Destiny to the provided value.
*
* @param type The type of the stat to set.
* @param value The value to set for the stat.
*/
- void setStat(WheelStat_t type, int32_t value);
+ void addStat(WheelStat_t type, int32_t value);
/**
- * @brief Sets the value of a specific resistance in the Wheel of Destiny.
+ * @brief Adds the value of a specific resistance in the Wheel of Destiny.
*
* This function sets the value of the specified resistance in the Wheel of Destiny to the provided value.
*
* @param type The type of the resistance to set.
* @param value The value to set for the resistance.
*/
- void setResistance(CombatType_t type, int32_t value);
+ void addResistance(CombatType_t type, int32_t value);
/**
* @brief Sets the value of a specific instant in the Wheel of Destiny based on its spell name.
@@ -246,6 +352,7 @@ class PlayerWheel {
*/
void setSpellInstant(const std::string &name, bool value);
void resetResistance();
+ void resetStats();
// Wheel of destiny - Header get:
bool getInstant(WheelInstant_t type) const;
@@ -314,6 +421,72 @@ class PlayerWheel {
* @return The calculated mitigation value.
*/
float calculateMitigation() const;
+ PlayerWheelGem getGem(uint8_t index) const;
+ PlayerWheelGem getGem(const std::string &uuid) const;
+ uint8_t getGemIndex(const std::string &uuid) const;
+ void revealGem(WheelGemQuality_t quality);
+ void destroyGem(uint8_t index);
+ void switchGemDomain(uint8_t index);
+ void toggleGemLock(uint8_t index);
+ void setActiveGem(WheelGemAffinity_t affinity, uint8_t index);
+ void removeActiveGem(WheelGemAffinity_t affinity);
+ void addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points) {
+ m_bonusRevelationPoints[static_cast(affinity)] += points;
+ }
+ void resetRevelationBonus() {
+ m_bonusRevelationPoints = { 0, 0, 0, 0 };
+ }
+
+ void addSpellBonus(const std::string &spellName, WheelSpells::Bonus bonus) {
+ if (m_spellsBonuses.contains(spellName)) {
+ m_spellsBonuses[spellName].decrease.cooldown += bonus.decrease.cooldown;
+ m_spellsBonuses[spellName].decrease.manaCost += bonus.decrease.manaCost;
+ m_spellsBonuses[spellName].decrease.secondaryGroupCooldown += bonus.decrease.secondaryGroupCooldown;
+ m_spellsBonuses[spellName].increase.aditionalTarget += bonus.increase.aditionalTarget;
+ m_spellsBonuses[spellName].increase.area = bonus.increase.area;
+ m_spellsBonuses[spellName].increase.criticalChance += bonus.increase.criticalChance;
+ m_spellsBonuses[spellName].increase.criticalDamage += bonus.increase.criticalDamage;
+ m_spellsBonuses[spellName].increase.damage += bonus.increase.damage;
+ m_spellsBonuses[spellName].increase.damageReduction += bonus.increase.damageReduction;
+ m_spellsBonuses[spellName].increase.duration += bonus.increase.duration;
+ m_spellsBonuses[spellName].increase.heal += bonus.increase.heal;
+ m_spellsBonuses[spellName].leech.life += bonus.leech.life;
+ m_spellsBonuses[spellName].leech.mana += bonus.leech.mana;
+ return;
+ }
+ m_spellsBonuses[spellName] = bonus;
+ }
+
+ int32_t getSpellBonus(const std::string &spellName, WheelSpellBoost_t boost) const {
+ if (!m_spellsBonuses.contains(spellName)) {
+ return 0;
+ }
+ auto bonus = m_spellsBonuses.at(spellName);
+ switch (boost) {
+ case WheelSpellBoost_t::COOLDOWN:
+ return bonus.decrease.cooldown;
+ case WheelSpellBoost_t::MANA:
+ return bonus.decrease.manaCost;
+ case WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN:
+ return bonus.decrease.secondaryGroupCooldown;
+ case WheelSpellBoost_t::CRITICAL_CHANCE:
+ return bonus.increase.criticalChance;
+ case WheelSpellBoost_t::CRITICAL_DAMAGE:
+ return bonus.increase.criticalDamage;
+ case WheelSpellBoost_t::DAMAGE:
+ return bonus.increase.damage;
+ case WheelSpellBoost_t::DAMAGE_REDUCTION:
+ return bonus.increase.damageReduction;
+ case WheelSpellBoost_t::HEAL:
+ return bonus.increase.heal;
+ case WheelSpellBoost_t::LIFE_LEECH:
+ return bonus.leech.life;
+ case WheelSpellBoost_t::MANA_LEECH:
+ return bonus.leech.mana;
+ default:
+ return 0;
+ }
+ }
private:
friend class Player;
@@ -322,8 +495,10 @@ class PlayerWheel {
// Starting count in 1 (1-37), slot enums are from 1 to 36, but the index always starts at 0 in c++
std::array m_wheelSlots = {};
+ std::array m_bonusRevelationPoints = { 0, 0, 0, 0 };
PlayerWheelMethodsBonusData m_playerBonusData;
+ std::unique_ptr m_modifierContext;
std::array(WheelStage_t::TOTAL_COUNT)> m_stages = { 0 };
std::array(WheelOnThink_t::TOTAL_COUNT)> m_onThink = { 0 };
@@ -335,4 +510,5 @@ class PlayerWheel {
int32_t m_creaturesNearby = 0;
std::map m_spellsSelected;
std::vector m_learnedSpellsSelected;
+ std::unordered_map m_spellsBonuses;
};
diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp
index 91666a8f1be..0f40d32bf96 100644
--- a/src/creatures/players/wheel/wheel_definitions.hpp
+++ b/src/creatures/players/wheel/wheel_definitions.hpp
@@ -124,8 +124,10 @@ enum class WheelStat_t : uint8_t {
DAMAGE = 10,
LIFE_LEECH_CHANCE = 11,
MANA_LEECH_CHANCE = 12,
+ DODGE = 13,
+ CRITICAL_DAMAGE = 14,
- TOTAL_COUNT = 13
+ TOTAL_COUNT = 15
};
enum class WheelMajor_t : uint8_t {
@@ -160,7 +162,7 @@ enum class WheelAvatarSkill_t : uint8_t {
NONE = 0,
DAMAGE_REDUCTION = 1,
CRITICAL_CHANCE = 2,
- CRITICAL_DAMAGE = 3
+ CRITICAL_DAMAGE = 3,
};
enum class WheelSpellGrade_t : uint8_t {
@@ -198,7 +200,7 @@ struct PlayerWheelMethodsBonusData {
int healing = 0;
};
// value * 100. Example: 1% == 100
- std::array resistance = {};
+ std::array unlockedVesselResonances = {};
// Raw value. Example: 1 == 1
struct Skills {
@@ -264,3 +266,33 @@ struct SlotInfo {
uint8_t slot; ///< The slot index.
uint16_t points; ///< The points for the slot.
};
+
+namespace WheelSpells {
+ struct Increase {
+ bool area = false;
+ int damage = 0;
+ int heal = 0;
+ int aditionalTarget = 0;
+ int damageReduction = 0;
+ int duration = 0;
+ int criticalDamage = 0;
+ int criticalChance = 0;
+ };
+
+ struct Decrease {
+ int cooldown = 0;
+ int manaCost = 0;
+ uint8_t secondaryGroupCooldown = 0;
+ };
+
+ struct Leech {
+ int mana = 0;
+ int life = 0;
+ };
+
+ struct Bonus {
+ Leech leech;
+ Increase increase;
+ Decrease decrease;
+ };
+}
diff --git a/src/creatures/players/wheel/wheel_gems.cpp b/src/creatures/players/wheel/wheel_gems.cpp
new file mode 100644
index 00000000000..d7957a9bb05
--- /dev/null
+++ b/src/creatures/players/wheel/wheel_gems.cpp
@@ -0,0 +1,524 @@
+/**
+ * Canary - A free and open-source MMORPG server emulator
+ * Copyright (©) 2019-2022 OpenTibiaBR
+ * Repository: https://github.com/opentibiabr/canary
+ * License: https://github.com/opentibiabr/canary/blob/main/LICENSE
+ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors
+ * Website: https://docs.opentibiabr.org/
+ */
+
+#include "pch.hpp"
+
+#include "creatures/players/wheel/player_wheel.hpp"
+
+void GemModifierResistanceStrategy::execute() {
+ m_wheel.addResistance(m_combatType, m_resistance);
+}
+
+void GemModifierStatStrategy::execute() {
+ m_wheel.addStat(m_stat, m_value);
+}
+
+void GemModifierRevelationStrategy::execute() {
+ m_wheel.addRevelationBonus(m_affinity, m_value);
+}
+
+void GemModifierSpellBonusStrategy::execute() {
+ m_wheel.addSpellBonus(m_spellName, m_bonus);
+}
+
+void WheelModifierContext::addStrategies(WheelGemBasicModifier_t modifier) {
+ switch (modifier) {
+ case WheelGemBasicModifier_t::General_PhysicalResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_PHYSICALDAMAGE, 100));
+ break;
+ case WheelGemBasicModifier_t::General_HolyResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_HOLYDAMAGE, 100));
+ break;
+ case WheelGemBasicModifier_t::General_DeathResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_DEATHDAMAGE, 100));
+ break;
+ case WheelGemBasicModifier_t::General_FireResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_FIREDAMAGE, 200));
+ break;
+ case WheelGemBasicModifier_t::General_EarthResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_EARTHDAMAGE, 200));
+ break;
+ case WheelGemBasicModifier_t::General_IceResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ICEDAMAGE, 200));
+ break;
+ case WheelGemBasicModifier_t::General_EnergyResistance:
+ m_strategies.push_back(std::make_unique(m_wheel, CombatType_t::COMBAT_ENERGYDAMAGE, 200));
+ 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_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_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));
+ 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));
+ 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));
+ 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