diff --git a/README.md b/README.md
index d82e7190d..02a97e78f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
## Builds
-[![Build on Ubuntu](https://github.com/mattyx14/otxserver/workflows/Build%20on%20Ubuntu/badge.svg?branch=otxserv6)](https://github.com/mattyx14/otxserver/actions?query=workflow%3A%22Build+on+Ubuntu%22)
+[![Build - Ubuntu](https://github.com/mattyx14/otxserver/actions/workflows/build-ubuntu.yml/badge.svg)](https://github.com/mattyx14/otxserver/actions/workflows/build-ubuntu.yml)
+[![Build - Windows](https://github.com/mattyx14/otxserver/actions/workflows/build-windows-cmake.yml/badge.svg)](https://github.com/mattyx14/otxserver/actions/workflows/build-windows-cmake.yml)
## What is OTX Server 6 Series
We are trying to create the perfect custom OpenTibia server.
@@ -7,25 +8,30 @@ We are trying to create the perfect custom OpenTibia server.
## How Compile:
We currently provide build instructions for the following systems:
-* [Windows Tutorial for OTX 6](https://forums.otserv.com.br/index.php?/forums/topic/169235-windowsvc2019-compilando-sources-otservbr-global/)
+* [Windows Tutorial for OTX 6](https://github.com/mattyx14/otxserver/wiki/Compiling-on-Windows-(OTX-6-))
+
+## Old Server Series:
+Server created with TFS 0.3.7-(AKA 0.4) with endless improvements that make it ideal for old school servers based on 8.6, clearly it should be used completely with its data defined there.
+* [OTX Server 2](https://github.com/mattyx14/otxserver/tree/otxserv2)
## OpenTibia Comunity:
With downloads, support, tutorials, Lua scripts, C++ codes, PHP codes and more ...
+* [OTServ Brasil](https://docs.opentibiabr.com/) - [Português/English]
+* [Tibiaking](https://tibiaking.com/) - [Português]
* [OTLand](https://otland.net/) - [English]
-* [OTServ Brasil](https://forums.otserv.com.br/) - [Português/English]
-* [Tibia Face](http://tibiaface.com/) - [Español]
-* [Black Tibia](http://blacktibia.foroactivo.com/) - [Español]
-* [Tibiaking](http://www.tibiaking.com/forum/) - [Português]
+* [Tibia Face](https://tibiaface.foroactivo.com/) - [Español]
+* [Black Tibia](https://tibiaface.foroactivo.com/) - [Español]
## Special Thanks OTServBR-Global
- our partners OTServBR-Global.
- our crew (majesty, gpedro, eduardo dantas)
- our testers (raphaellb, petardako, szulcek and olimpotibia)
-- [our contributors](https://github.com/opentibiabr/OTServBR-Global/graphs/contributors)
+- [our contributors](https://github.com/opentibiabr/canary/graphs/contributors)
- [fear lucien](https://github.com/FearLucien)
- [cjaker](https://github.com/Eternal-Scripts)
- [slavidodo](https://github.com/slavidodo)
- [mignari and our awesome tools](https://github.com/ottools)
+- [saiyansking/optimized_forgottenserver and contributors](https://github.com/SaiyansKing/optimized_forgottenserver)
- [mattyx14/otxserver](https://github.com/mattyx14/otxserver) and contributors.
- [otland/forgottenserver](https://github.com/otland/forgottenserver) and contributors.
- if we forget someone, we apologize by forgot you. but you know, **forgot**tenserver.
diff --git a/config.lua.dist b/config.lua.dist
index ffa2233b9..0642cc6c2 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -238,6 +238,7 @@ onlyPremiumAccount = false
-- NOTE: randomMonsterSpawn = true, will enable monsters from the same spawn to be randomized between them, thus making a variable hunt
-- NOTE: enablePlayerPutItemInAmmoSlot = true, will enable players to put any items on ammo slot, more used in custom shopping system
-- NOTE: startStreakLevel will make a reward streak level for new players who never logged in
+-- NOTE: if showLootsInBestiary is true, will cause all loots to be shown in the bestiary even if the player has not reached the required number of kills
stashMoving = false
depotChest = 4
autoLoot = false
@@ -255,6 +256,7 @@ lootPouchMaxLimit = 2000
storeInboxMaxLimit = 2000
enablePlayerPutItemInAmmoSlot = false
startStreakLevel = 0
+showLootsInBestiary = false
-- Teleport summon
-- Set to true will never remove the summon
@@ -427,6 +429,9 @@ maxDamageReflection = 200
toggleChainSystem = true
combatChainDelay = 50
combatChainTargets = 5
+combatChainSkillFormulaAxe = 0.9
+combatChainSkillFormulaClub = 0.7
+combatChainSkillFormulaSword = 1.1
-- Global server Save
-- NOTE: globalServerSaveNotifyDuration in minutes
diff --git a/data-otxserver/lib/core/storages.lua b/data-otxserver/lib/core/storages.lua
index 14257e3fe..69f1d7f79 100644
--- a/data-otxserver/lib/core/storages.lua
+++ b/data-otxserver/lib/core/storages.lua
@@ -103,7 +103,7 @@ Storage = {
Factions = 30024,
-- unused TrainerRoom = 30027,
-- unused NpcSpawn = 30028,
- ExerciseDummyExhaust = 30029,
+ -- unused ExerciseDummyExhaust = 30029,
SamsOldBackpack = 30030,
SamsOldBackpackDoor = 30031,
StrawberryCupcake = 30032,
diff --git a/data-otxserver/monster/demons/hellfire_fighter.lua b/data-otxserver/monster/demons/hellfire_fighter.lua
index 446234df5..6a90b5af0 100644
--- a/data-otxserver/monster/demons/hellfire_fighter.lua
+++ b/data-otxserver/monster/demons/hellfire_fighter.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hellfire Fighter")
local monster = {}
monster.description = "a hellfire fighter"
-monster.experience = 3120
+monster.experience = 3400
monster.outfit = {
lookType = 243,
lookHead = 0,
diff --git a/data-otxserver/monster/demons/hellflayer.lua b/data-otxserver/monster/demons/hellflayer.lua
index 93e9db81e..1e54dca6f 100644
--- a/data-otxserver/monster/demons/hellflayer.lua
+++ b/data-otxserver/monster/demons/hellflayer.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hellflayer")
local monster = {}
monster.description = "a hellflayer"
-monster.experience = 11000
+monster.experience = 11720
monster.outfit = {
lookType = 856,
lookHead = 0,
diff --git a/data-otxserver/monster/demons/juggernaut.lua b/data-otxserver/monster/demons/juggernaut.lua
index bab7fcf5d..8f783924e 100644
--- a/data-otxserver/monster/demons/juggernaut.lua
+++ b/data-otxserver/monster/demons/juggernaut.lua
@@ -28,8 +28,8 @@ monster.Bestiary = {
Oramond Dungeon, Grounds of Destruction and Halls of Ascension.",
}
-monster.health = 20000
-monster.maxHealth = 20000
+monster.health = 18000
+monster.maxHealth = 18000
monster.race = "blood"
monster.corpse = 6335
monster.speed = 170
diff --git a/data-otxserver/monster/demons/plaguesmith.lua b/data-otxserver/monster/demons/plaguesmith.lua
index 6fbc96b53..e919f78cf 100644
--- a/data-otxserver/monster/demons/plaguesmith.lua
+++ b/data-otxserver/monster/demons/plaguesmith.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Plaguesmith")
local monster = {}
monster.description = "a plaguesmith"
-monster.experience = 3555
+monster.experience = 3800
monster.outfit = {
lookType = 247,
lookHead = 0,
diff --git a/data-otxserver/monster/magicals/nightmare.lua b/data-otxserver/monster/magicals/nightmare.lua
index b5c93d2b8..00929effa 100644
--- a/data-otxserver/monster/magicals/nightmare.lua
+++ b/data-otxserver/monster/magicals/nightmare.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Nightmare")
local monster = {}
monster.description = "a nightmare"
-monster.experience = 1666
+monster.experience = 1800
monster.outfit = {
lookType = 245,
lookHead = 0,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
index ca11ccb54..90b312436 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ferumbras Mortal Shell")
local monster = {}
monster.description = "Ferumbras Mortal Shell"
-monster.experience = 100000
+monster.experience = 500000
monster.outfit = {
lookType = 229,
lookHead = 0,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/mazoran.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
index 9bc024812..0e0822e3d 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Mazoran")
local monster = {}
monster.description = "Mazoran"
-monster.experience = 50000
+monster.experience = 250000
monster.outfit = {
lookType = 842,
lookHead = 77,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/plagirath.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
index b86ac1a47..625f10cf7 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Plagirath")
local monster = {}
monster.description = "Plagirath"
-monster.experience = 50000
+monster.experience = 250000
monster.outfit = {
lookType = 862,
lookHead = 84,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
index e9f97824c..9701dc7d0 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ragiaz")
local monster = {}
monster.description = "Ragiaz"
-monster.experience = 50000
+monster.experience = 250000
monster.outfit = {
lookType = 862,
lookHead = 76,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
index 07d2a55b0..67d218c5e 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Razzagorn")
local monster = {}
monster.description = "Razzagorn"
-monster.experience = 50000
+monster.experience = 250000
monster.outfit = {
lookType = 842,
lookHead = 78,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
index 69eae8f36..c0de2c0c3 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Shulgrax")
local monster = {}
monster.description = "Shulgrax"
-monster.experience = 21000
+monster.experience = 250000
monster.outfit = {
lookType = 842,
lookHead = 0,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
index 16fd12f63..583167460 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Tarbaz")
local monster = {}
monster.description = "Tarbaz"
-monster.experience = 55000
+monster.experience = 250000
monster.outfit = {
lookType = 842,
lookHead = 0,
diff --git a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
index f3bbd04ef..5a441ff10 100644
--- a/data-otxserver/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
+++ b/data-otxserver/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Zamulosh")
local monster = {}
monster.description = "Zamulosh"
-monster.experience = 50000
+monster.experience = 250000
monster.outfit = {
lookType = 862,
lookHead = 16,
diff --git a/data-otxserver/monster/undeads/blightwalker.lua b/data-otxserver/monster/undeads/blightwalker.lua
index 2edb1712a..28706adeb 100644
--- a/data-otxserver/monster/undeads/blightwalker.lua
+++ b/data-otxserver/monster/undeads/blightwalker.lua
@@ -26,8 +26,8 @@ monster.Bestiary = {
Locations = "Pits of Inferno, Edron (In the Vats during The Inquisition Quest), Roshamuul Prison, Grounds of Undeath.",
}
-monster.health = 8900
-monster.maxHealth = 8900
+monster.health = 8100
+monster.maxHealth = 8100
monster.race = "undead"
monster.corpse = 6353
monster.speed = 175
diff --git a/data-otxserver/monster/undeads/hand_of_cursed_fate.lua b/data-otxserver/monster/undeads/hand_of_cursed_fate.lua
index be6f79fc6..b1f76414d 100644
--- a/data-otxserver/monster/undeads/hand_of_cursed_fate.lua
+++ b/data-otxserver/monster/undeads/hand_of_cursed_fate.lua
@@ -26,8 +26,8 @@ monster.Bestiary = {
Locations = "Pits of Inferno, The Battlefield, The Arcanum, The Blood Halls and The Crystal Caves.",
}
-monster.health = 7500
-monster.maxHealth = 7500
+monster.health = 6600
+monster.maxHealth = 6600
monster.race = "blood"
monster.corpse = 6311
monster.speed = 130
diff --git a/data-otxserver/monster/undeads/undead_dragon.lua b/data-otxserver/monster/undeads/undead_dragon.lua
index 0dc1652d8..2ab63af8b 100644
--- a/data-otxserver/monster/undeads/undead_dragon.lua
+++ b/data-otxserver/monster/undeads/undead_dragon.lua
@@ -133,7 +133,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 100 },
- { type = COMBAT_ICEDAMAGE, percent = 50 },
+ { type = COMBAT_ICEDAMAGE, percent = 90 },
{ type = COMBAT_HOLYDAMAGE, percent = -25 },
{ type = COMBAT_DEATHDAMAGE, percent = 100 },
}
diff --git a/data-otxserver/scripts/actions/other/dice.lua b/data-otxserver/scripts/actions/other/dice.lua
deleted file mode 100644
index bd0c6c8f3..000000000
--- a/data-otxserver/scripts/actions/other/dice.lua
+++ /dev/null
@@ -1,18 +0,0 @@
-local dice = Action()
-
-function dice.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- local dicePosition = item:getPosition()
- local value = math.random(6)
- local isInGhostMode = player:isInGhostMode()
-
- dicePosition:sendMagicEffect(CONST_ME_CRAPS, isInGhostMode and player)
- local spectators = Game.getSpectators(dicePosition, false, true, 3, 3)
- for i = 1, #spectators do
- player:say(player:getName() .. " rolled a " .. value .. ".", TALKTYPE_MONSTER_SAY, isInGhostMode, spectators[i], dicePosition)
- end
- item:transform(5791 + value)
- return true
-end
-
-dice:id(5792, 5793, 5794, 5795, 5796, 5797)
-dice:register()
diff --git a/data-otxserver/scripts/creaturescripts/others/advance_save.lua b/data-otxserver/scripts/creaturescripts/others/advance_save.lua
deleted file mode 100644
index 8bd6fd2fd..000000000
--- a/data-otxserver/scripts/creaturescripts/others/advance_save.lua
+++ /dev/null
@@ -1,32 +0,0 @@
-local config = {
- heal = true,
- save = true,
- effect = false,
-}
-
-local advanceSave = CreatureEvent("AdvanceSave")
-
-function advanceSave.onAdvance(player, skill, oldLevel, newLevel)
- if skill ~= SKILL_LEVEL or newLevel <= oldLevel then
- return true
- end
-
- if config.effect then
- player:getPosition():sendMagicEffect(math.random(CONST_ME_FIREWORK_YELLOW, CONST_ME_FIREWORK_BLUE))
- player:say("LEVEL UP!", TALKTYPE_MONSTER_SAY)
- end
-
- if config.heal then
- player:addHealth(player:getMaxHealth())
- end
-
- if config.save then
- player:save()
- end
-
- player:getFinalLowLevelBonus()
-
- return true
-end
-
-advanceSave:register()
diff --git a/data/items/items.xml b/data/items/items.xml
index faa2edfce..e8355264c 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -1252,7 +1252,6 @@
-
-
@@ -1327,7 +1326,6 @@
-
-
@@ -1386,7 +1384,6 @@
-
-
@@ -1401,7 +1398,6 @@
-
-
@@ -1490,7 +1486,6 @@
-
-
@@ -1565,7 +1560,6 @@
-
-
@@ -1624,7 +1618,6 @@
-
-
@@ -1639,7 +1632,6 @@
-
-
@@ -2267,7 +2259,6 @@
-
-
@@ -2342,7 +2333,6 @@
-
-
@@ -2401,7 +2391,6 @@
-
-
@@ -2416,7 +2405,6 @@
-
-
@@ -2488,7 +2476,6 @@
-
-
@@ -2567,7 +2554,6 @@
-
-
@@ -2626,7 +2612,6 @@
-
-
@@ -2641,7 +2626,6 @@
-
-
@@ -3113,7 +3097,6 @@
-
-
-
@@ -8267,7 +8250,6 @@
-
-
@@ -8281,7 +8263,6 @@
-
-
@@ -8317,7 +8298,6 @@
-
-
@@ -8398,7 +8378,6 @@
-
-
@@ -8439,7 +8418,6 @@
-
-
@@ -8461,7 +8439,6 @@
-
-
@@ -8500,7 +8477,6 @@
-
-
@@ -8608,7 +8584,6 @@
-
-
@@ -8701,7 +8676,6 @@
-
-
@@ -8772,7 +8746,6 @@
-
-
@@ -8794,7 +8767,6 @@
-
-
@@ -8817,7 +8789,6 @@
-
-
@@ -8964,7 +8935,6 @@
-
-
@@ -8978,7 +8948,6 @@
-
-
@@ -8999,7 +8968,6 @@
-
-
@@ -9074,7 +9042,6 @@
-
-
@@ -9218,7 +9185,6 @@
-
-
@@ -9260,7 +9226,6 @@
-
-
@@ -9282,7 +9247,6 @@
-
-
@@ -9339,7 +9303,6 @@
-
-
@@ -9417,7 +9380,6 @@
-
-
@@ -9461,7 +9423,6 @@
-
-
@@ -9521,7 +9482,6 @@
-
-
@@ -9568,7 +9528,6 @@
-
-
@@ -9580,7 +9539,6 @@
-
-
@@ -10827,7 +10785,6 @@
-
-
@@ -15647,7 +15604,6 @@
-
-
@@ -17989,7 +17945,6 @@
-
-
@@ -18117,7 +18072,6 @@
-
-
@@ -19369,7 +19323,6 @@
-
-
@@ -19392,7 +19345,6 @@
-
-
@@ -19428,7 +19380,6 @@
-
-
@@ -19508,7 +19459,6 @@
-
-
@@ -19594,7 +19544,6 @@
-
-
@@ -19616,7 +19565,6 @@
-
-
@@ -19674,7 +19622,6 @@
-
-
@@ -19696,7 +19643,6 @@
-
-
@@ -19739,7 +19685,6 @@
-
-
@@ -19762,7 +19707,6 @@
-
-
@@ -19784,7 +19728,6 @@
-
-
@@ -19887,7 +19830,6 @@
-
-
@@ -19910,7 +19852,6 @@
-
-
@@ -20085,7 +20026,6 @@
-
-
@@ -20106,7 +20046,6 @@
-
-
@@ -20120,7 +20059,6 @@
-
-
@@ -20141,7 +20079,6 @@
-
-
@@ -20156,7 +20093,6 @@
-
-
@@ -20172,7 +20108,6 @@
-
-
@@ -20322,7 +20257,6 @@
-
-
@@ -20364,7 +20298,6 @@
-
-
@@ -20421,7 +20354,6 @@
-
-
@@ -20435,7 +20367,6 @@
-
-
@@ -20477,7 +20408,6 @@
-
-
@@ -20499,7 +20429,6 @@
-
-
@@ -20514,7 +20443,6 @@
-
-
@@ -21456,7 +21384,6 @@
-
-
@@ -21479,7 +21406,6 @@
-
-
@@ -21503,7 +21429,6 @@
-
-
@@ -21526,7 +21451,6 @@
-
-
@@ -21548,7 +21472,6 @@
-
-
@@ -21572,7 +21495,6 @@
-
-
@@ -21596,7 +21518,6 @@
-
-
@@ -21611,7 +21532,6 @@
-
-
@@ -21634,7 +21554,6 @@
-
-
@@ -21649,7 +21568,6 @@
-
-
@@ -22373,7 +22291,6 @@
-
-
@@ -22429,7 +22346,6 @@
-
-
@@ -22484,7 +22400,6 @@
-
-
@@ -25285,7 +25200,6 @@
-
-
@@ -25373,7 +25287,6 @@
-
-
@@ -25396,7 +25309,6 @@
-
-
@@ -27223,7 +27135,6 @@
-
-
@@ -27238,7 +27149,6 @@
-
-
@@ -27274,7 +27184,6 @@
-
-
@@ -27296,7 +27205,6 @@
-
-
@@ -27376,7 +27284,6 @@
-
-
@@ -28881,7 +28788,6 @@
-
-
@@ -30741,7 +30647,6 @@
-
-
@@ -30884,7 +30789,6 @@
-
-
@@ -31910,7 +31814,6 @@
-
-
@@ -32300,7 +32203,6 @@
-
-
@@ -32657,7 +32559,6 @@
-
-
@@ -32674,7 +32575,6 @@
-
-
@@ -32936,7 +32836,6 @@
-
-
@@ -34608,7 +34507,6 @@
-
-
@@ -34632,7 +34530,6 @@
-
-
@@ -36066,7 +35963,6 @@
-
-
@@ -39422,7 +39318,6 @@
-
-
@@ -39487,7 +39382,6 @@
-
-
@@ -39625,6 +39519,11 @@
+
+
+
+
+
-
@@ -39953,7 +39852,6 @@
-
-
@@ -39975,7 +39873,6 @@
-
-
@@ -39998,7 +39895,6 @@
-
-
@@ -40073,7 +39969,6 @@
-
-
@@ -40095,7 +39990,6 @@
-
-
@@ -40118,7 +40012,6 @@
-
-
@@ -40193,7 +40086,6 @@
-
-
@@ -40215,7 +40107,6 @@
-
-
@@ -40238,7 +40129,6 @@
-
-
@@ -40255,7 +40145,6 @@
-
-
@@ -40279,7 +40168,6 @@
-
-
@@ -40304,7 +40192,6 @@
-
-
@@ -40321,7 +40208,6 @@
-
-
@@ -40345,7 +40231,6 @@
-
-
@@ -40370,7 +40255,6 @@
-
-
@@ -41953,7 +41837,6 @@
-
-
@@ -41989,7 +41872,6 @@
-
-
@@ -42021,7 +41903,6 @@
-
-
@@ -42039,7 +41920,6 @@
-
-
@@ -42057,7 +41937,6 @@
-
@@ -42211,7 +42090,6 @@
-
@@ -44335,7 +44213,6 @@ Awarded by TibiaMisterios.com.br"/>
-
-
@@ -44349,7 +44226,6 @@ Awarded by TibiaMisterios.com.br"/>
-
@@ -45194,7 +45070,6 @@ Awarded by TibiaMisterios.com.br"/>
-
-
@@ -45688,7 +45563,6 @@ Awarded by TibiaMisterios.com.br"/>
-
-
@@ -45712,7 +45586,6 @@ Awarded by TibiaMisterios.com.br"/>
-
-
@@ -48684,8 +48557,7 @@ hands of its owner. Granted by TibiaRoyal.com"/>
-
-
+
-
@@ -51745,7 +51617,6 @@ TibiaTome.com"/>
-
-
@@ -51786,7 +51657,6 @@ TibiaTome.com"/>
-
-
@@ -51827,7 +51697,6 @@ TibiaTome.com"/>
-
-
@@ -51850,7 +51719,6 @@ TibiaTome.com"/>
-
-
@@ -51873,7 +51741,6 @@ TibiaTome.com"/>
-
-
@@ -54202,7 +54069,6 @@ TibiaTome.com"/>
-
-
@@ -54307,7 +54173,6 @@ TibiaTome.com"/>
-
-
@@ -54331,7 +54196,6 @@ TibiaTome.com"/>
-
-
@@ -55251,7 +55115,6 @@ TibiaTome.com"/>
-
-
@@ -55469,7 +55332,6 @@ TibiaTome.com"/>
-
-
@@ -55513,7 +55375,6 @@ TibiaTome.com"/>
-
-
@@ -57316,7 +57177,6 @@ TibiaTome.com"/>
-
-
@@ -59168,7 +59028,6 @@ TibiaTome.com"/>
-
-
@@ -59309,7 +59168,6 @@ TibiaTome.com"/>
-
-
@@ -61756,7 +61614,6 @@ TibiaTome.com"/>
-
-
@@ -64022,7 +63879,6 @@ TibiaTome.com"/>
-
-
@@ -64074,7 +63930,6 @@ TibiaTome.com"/>
-
-
@@ -64126,7 +63981,6 @@ TibiaTome.com"/>
-
-
@@ -64154,7 +64008,6 @@ TibiaTome.com"/>
-
-
@@ -64182,7 +64035,6 @@ TibiaTome.com"/>
-
-
@@ -64599,7 +64451,6 @@ TibiaTome.com"/>
-
-
@@ -65924,7 +65775,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -65996,7 +65846,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67394,7 +67243,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67419,7 +67267,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67443,7 +67290,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67492,7 +67338,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67517,7 +67362,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67563,7 +67407,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -67590,7 +67433,6 @@ former students at the Noodles Academy. Awarded by TibiaLabs.com"/>
-
-
@@ -71641,7 +71483,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75310,7 +75151,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75335,7 +75175,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75360,7 +75199,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75385,7 +75223,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75410,7 +75247,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75435,7 +75271,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75478,7 +75313,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75506,7 +75340,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75534,7 +75367,6 @@ Granted by TibiaGoals.com"/>
-
-
@@ -75562,7 +75394,6 @@ Granted by TibiaGoals.com"/>
-
-
diff --git a/data/libs/functions/modal_window_helper.lua b/data/libs/functions/modal_window_helper.lua
index 9de5e394c..ed169805b 100644
--- a/data/libs/functions/modal_window_helper.lua
+++ b/data/libs/functions/modal_window_helper.lua
@@ -209,7 +209,7 @@ function ModalWindow:sendToPlayer(player)
local playerId = player:getId()
ModalWindows[playerId] = ModalWindows[playerId] or {}
ModalWindows[playerId][self.modalWindowId] = self
- player:registerEvent("modalWindowHelper")
+ player:registerEvent("ModalWindowHelper")
self.using = self.using + 1
return modalWindow:sendToPlayer(player)
end
diff --git a/data/libs/functions/player.lua b/data/libs/functions/player.lua
index 4de74ce60..3a5e4eeab 100644
--- a/data/libs/functions/player.lua
+++ b/data/libs/functions/player.lua
@@ -898,3 +898,37 @@ do
return true
end
end
+
+function Player:questKV(questName)
+ return self:kv():scoped("quests"):scoped(questName)
+end
+
+function Player:canGetReward(rewardId, questName)
+ if self:questKV(questName):get("completed") then
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The box is empty.")
+ return false
+ end
+
+ local rewardItem = ItemType(rewardId)
+ if not rewardItem then
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Reward item is wrong, please contact an administrator.")
+ return false
+ end
+
+ local itemWeight = rewardItem:getWeight() / 100
+ local baseMessage = "You have found a " .. rewardItem:getName()
+ local backpack = self:getSlotItem(CONST_SLOT_BACKPACK)
+ if not backpack or backpack:getEmptySlots(true) < 1 then
+ baseMessage = baseMessage .. ", but you have no room to take it."
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, baseMessage)
+ return false
+ end
+
+ if (self:getFreeCapacity() / 100) < itemWeight then
+ baseMessage = baseMessage .. ". Weighing " .. itemWeight .. " oz, it is too heavy for you to carry."
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, baseMessage)
+ return false
+ end
+
+ return true
+end
diff --git a/data/libs/functions/revscriptsys.lua b/data/libs/functions/revscriptsys.lua
index 5a31ec086..84b8275e3 100644
--- a/data/libs/functions/revscriptsys.lua
+++ b/data/libs/functions/revscriptsys.lua
@@ -108,7 +108,7 @@ do
local function EventCallbackNewIndex(self, key, value)
local func = eventCallbacks[key]
if func and type(func) == "function" then
- logger.debug("[Registering EventCallback: {}", key)
+ logger.trace("[Registering EventCallback: {}", key)
func(self, value)
self:type(key)
else
diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua
index 4fa9daea2..a927715cd 100644
--- a/data/modules/scripts/gamestore/init.lua
+++ b/data/modules/scripts/gamestore/init.lua
@@ -1739,8 +1739,8 @@ function GameStore.processExpBoostPuchase(player)
player:setStoreXpBoost(50)
player:setExpBoostStamina(currentExpBoostTime + 3600)
- if player:getStorageValue(GameStore.Storages.expBoostCount) == -1 or expBoostCount == 6 then
- player:setStorageValue(GameStore.Storages.expBoostCount, 1)
+ if expBoostCount == -1 or expBoostCount == 6 then
+ expBoostCount = 1
end
player:setStorageValue(GameStore.Storages.expBoostCount, expBoostCount + 1)
diff --git a/data/npclib/npc_system/bank_system.lua b/data/npclib/npc_system/bank_system.lua
index 02e47da9d..0b4a8e97f 100644
--- a/data/npclib/npc_system/bank_system.lua
+++ b/data/npclib/npc_system/bank_system.lua
@@ -180,19 +180,41 @@ function Npc:parseBank(message, npc, creature, npcHandler)
return true
elseif npcHandler:getTopic(playerId) == 7 then
if MsgContains(message, "yes") then
+ local totalValue = count[playerId]
+ local crystalCoins = math.floor(totalValue / 10000)
+ totalValue = totalValue % 10000
+ local platinumCoins = math.floor(totalValue / 100)
+ totalValue = totalValue % 100
+ local goldCoins = math.floor(totalValue / 1)
+ local crystalPiles = math.floor((crystalCoins + 99) / 100)
+ local platinumPiles = math.floor((platinumCoins + 99) / 100)
+ local goldPiles = math.floor((goldCoins + 99) / 100)
+ local totalPiles = crystalPiles + platinumPiles + goldPiles
if player:getFreeCapacity() >= getMoneyWeight(count[playerId]) then
- if not player:withdrawMoney(count[playerId]) then
- npcHandler:say("There is not enough gold on your account.", npc, creature)
+ if player:getFreeBackpackSlots() >= totalPiles then
+ if not player:withdrawMoney(count[playerId]) then
+ npcHandler:say("There is not enough gold on your account.", npc, creature)
+ else
+ npcHandler:say(string.format("Here you are, %i gold. Please let me know if there is something else I can do for you.", count[playerId]), npc, creature)
+ end
else
- npcHandler:say(string.format("Here you are, %d gold. Please let me know if there is something else I can do for you.", count[playerId]), npc, creature)
+ npcHandler:say(
+ string.format(
+ "Hold on, you don't have enough room in your backpack to carry all these coins. \nI don't want you to drop them on the floor, perhaps come back when you have more space in your backpack!\nYou will receive %i crystal stacks (%i coins), %i platinum stacks (%i coins), and %i gold stacks (%i coins). Please ensure you have at least %i free slots in your backpack.\n",
+ crystalPiles,
+ crystalCoins,
+ platinumPiles,
+ platinumCoins,
+ goldPiles,
+ goldCoins,
+ totalPiles
+ ),
+ npc,
+ creature
+ )
end
else
- npcHandler:say(
- "Whoah, hold on, you have no room in your inventory to carry all those coins. \z
- I don't want you to drop it on the floor, maybe come back with a cart!",
- npc,
- creature
- )
+ npcHandler:say("Whoah, hold on, you have no free capacity to carry all those coins!", npc, creature)
end
npcHandler:setTopic(playerId, 0)
elseif MsgContains(message, "no") then
diff --git a/data/scripts/actions/items/die.lua b/data/scripts/actions/items/die.lua
new file mode 100644
index 000000000..661926e79
--- /dev/null
+++ b/data/scripts/actions/items/die.lua
@@ -0,0 +1,36 @@
+local die = Action()
+
+function die.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ local position = item:getPosition()
+ local value = math.random(1, 6)
+ local isInGhostMode = player:isInGhostMode()
+
+ if not isInGhostMode then
+ position:sendMagicEffect(CONST_ME_CRAPS)
+
+ local spectators = Game.getSpectators(position, false, true, 3, 3)
+ for _, spectator in ipairs(spectators) do
+ player:say(player:getName() .. " rolled a " .. value .. ".", TALKTYPE_MONSTER_SAY, isInGhostMode, spectator, position)
+ end
+ end
+
+ if value == 6 then
+ local rolledSixCount = player:kv():get("die-rolled-six") or 0
+ player:kv():set("die-rolled-six", rolledSixCount + 1)
+
+ if rolledSixCount == 2 then
+ player:addAchievement("Number of the Beast")
+ end
+ else
+ player:kv():remove("die-rolled-six")
+ end
+
+ item:transform(5791 + value)
+ return true
+end
+
+for items = 5792, 5797 do
+ die:id(items)
+end
+
+die:register()
diff --git a/data/scripts/actions/items/explosive_present.lua b/data/scripts/actions/items/explosive_present.lua
new file mode 100644
index 000000000..2ca4730de
--- /dev/null
+++ b/data/scripts/actions/items/explosive_present.lua
@@ -0,0 +1,12 @@
+local explosivePresent = Action()
+
+function explosivePresent.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ player:say("KABOOOOOOOOOOM!", TALKTYPE_MONSTER_SAY)
+ player:getPosition():sendMagicEffect(CONST_ME_FIREAREA)
+ player:addAchievement("Joke's on You")
+ item:remove()
+ return true
+end
+
+explosivePresent:id(906)
+explosivePresent:register()
diff --git a/data/scripts/actions/items/foods.lua b/data/scripts/actions/items/foods.lua
index adce2e462..4a8072d16 100644
--- a/data/scripts/actions/items/foods.lua
+++ b/data/scripts/actions/items/foods.lua
@@ -125,7 +125,7 @@ local foods = {
local food = Action()
function food.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- local itemFood = setting[item.itemid]
+ local itemFood = foods[item.itemid]
if not itemFood then
return false
end
diff --git a/data/scripts/actions/items/garlic_bread.lua b/data/scripts/actions/items/garlic_bread.lua
index 1a62b1cd0..315dbbadd 100644
--- a/data/scripts/actions/items/garlic_bread.lua
+++ b/data/scripts/actions/items/garlic_bread.lua
@@ -5,5 +5,5 @@ function garlicBread.onUse(player, item, fromPosition, target, toPosition, isHot
return true
end
-garlicBread:id(9087)
+garlicBread:id(8194)
garlicBread:register()
diff --git a/data-otxserver/scripts/actions/other/voodoo_doll.lua b/data/scripts/actions/items/voodoo_doll.lua
similarity index 90%
rename from data-otxserver/scripts/actions/other/voodoo_doll.lua
rename to data/scripts/actions/items/voodoo_doll.lua
index 1b77081ee..6e906e9b9 100644
--- a/data-otxserver/scripts/actions/other/voodoo_doll.lua
+++ b/data/scripts/actions/items/voodoo_doll.lua
@@ -1,7 +1,7 @@
local voodooDoll = Action()
function voodooDoll.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if target.itemid ~= 1 or target.type ~= THING_TYPE_PLAYER then
+ if target.itemid ~= 1 or not target:isPlayer() then
return false
end
@@ -13,6 +13,7 @@ function voodooDoll.onUse(player, item, fromPosition, target, toPosition, isHotk
else
text = "You concentrate on your victim, hit the needle in the doll.......but nothing happens."
end
+
player:say(text, TALKTYPE_MONSTER_SAY, false, player)
return true
end
diff --git a/data-otxserver/scripts/actions/other/carpets.lua b/data/scripts/actions/objects/carpets.lua
similarity index 93%
rename from data-otxserver/scripts/actions/other/carpets.lua
rename to data/scripts/actions/objects/carpets.lua
index ccd69a7e4..6232eb2f1 100644
--- a/data-otxserver/scripts/actions/other/carpets.lua
+++ b/data/scripts/actions/objects/carpets.lua
@@ -170,23 +170,35 @@ function carpets.onUse(player, item, fp, target, toPosition, isHotkey)
end
local fromPosition = item:getPosition()
- local tile = Tile(fromPosition)
- if not fromPosition:getTile():getHouse() then
+ local tile = fromPosition:getTile()
+ if not tile then
player:sendTextMessage(MESSAGE_FAILURE, "You may use this only inside a house.")
- elseif tile:getItemCountById(item.itemid) == 1 then
+ return true
+ end
+
+ local house = tile:getHouse()
+ if not house then
+ player:sendTextMessage(MESSAGE_FAILURE, "You may use this only inside a house.")
+ return true
+ end
+
+ local itemCount = tile:getItemCountById(item.itemid)
+ if itemCount == 1 then
local topItem = tile:getTopTopItem()
if topItem and topItem:canReceiveAutoCarpet() then
item:remove()
return true
end
+
for k, v in pairs(carpetItems) do
- if tile:getItemCountById(k) > 0 and k ~= item.itemid then
+ if k ~= item.itemid and tile:getItemCountById(k) > 0 then
player:sendCancelMessage(Game.getReturnMessage(RETURNVALUE_NOTPOSSIBLE))
return true
end
end
- item:transform(carpet)
end
+
+ item:transform(carpet)
return true
end
diff --git a/data-otxserver/scripts/actions/tools/crowbar.lua b/data/scripts/actions/tools/crowbar.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/crowbar.lua
rename to data/scripts/actions/tools/crowbar.lua
diff --git a/data-otxserver/scripts/actions/tools/machete.lua b/data/scripts/actions/tools/machete.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/machete.lua
rename to data/scripts/actions/tools/machete.lua
diff --git a/data-otxserver/scripts/actions/tools/pick.lua b/data/scripts/actions/tools/pick.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/pick.lua
rename to data/scripts/actions/tools/pick.lua
diff --git a/data-otxserver/scripts/actions/tools/rope.lua b/data/scripts/actions/tools/rope.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/rope.lua
rename to data/scripts/actions/tools/rope.lua
diff --git a/data-otxserver/scripts/actions/tools/scythe.lua b/data/scripts/actions/tools/scythe.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/scythe.lua
rename to data/scripts/actions/tools/scythe.lua
diff --git a/data-otxserver/scripts/actions/tools/shovel.lua b/data/scripts/actions/tools/shovel.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/shovel.lua
rename to data/scripts/actions/tools/shovel.lua
diff --git a/data-otxserver/scripts/actions/tools/spoon.lua b/data/scripts/actions/tools/spoon.lua
similarity index 100%
rename from data-otxserver/scripts/actions/tools/spoon.lua
rename to data/scripts/actions/tools/spoon.lua
diff --git a/data-otxserver/scripts/creaturescripts/others/logout.lua b/data/scripts/creaturescripts/player/logout.lua
similarity index 73%
rename from data-otxserver/scripts/creaturescripts/others/logout.lua
rename to data/scripts/creaturescripts/player/logout.lua
index 07e0d5dfc..cab57c5ad 100644
--- a/data-otxserver/scripts/creaturescripts/others/logout.lua
+++ b/data/scripts/creaturescripts/player/logout.lua
@@ -1,22 +1,21 @@
local playerLogout = CreatureEvent("PlayerLogout")
+
function playerLogout.onLogout(player)
local playerId = player:getId()
- if _G.NextUseStaminaTime[playerId] ~= nil then
+ if _G.NextUseStaminaTime[playerId] then
_G.NextUseStaminaTime[playerId] = nil
end
- player:setStorageValue(Storage.ExerciseDummyExhaust, 0)
-
local stats = player:inBossFight()
if stats then
local boss = Monster(stats.bossId)
- -- Player logged out (or died) in the middle of a boss fight, store his damageOut and stamina
if boss then
local dmgOut = boss:getDamageMap()[playerId]
if dmgOut then
stats.damageOut = (stats.damageOut or 0) + dmgOut.total
end
+
stats.stamina = player:getStamina()
end
end
@@ -26,8 +25,6 @@ function playerLogout.onLogout(player)
_G.OnExerciseTraining[playerId] = nil
player:setTraining(false)
end
-
- player:setStorageValue(17101, 0)
return true
end
diff --git a/data/scripts/creaturescripts/player/update_player_on_advanced_level.lua b/data/scripts/creaturescripts/player/update_player_on_advanced_level.lua
new file mode 100644
index 000000000..aa2297674
--- /dev/null
+++ b/data/scripts/creaturescripts/player/update_player_on_advanced_level.lua
@@ -0,0 +1,15 @@
+local updatePlayerOnAdvancedLevel = CreatureEvent("UpdatePlayerOnAdvancedLevel")
+
+function updatePlayerOnAdvancedLevel.onAdvance(player, skill, oldLevel, newLevel)
+ if skill ~= SKILL_LEVEL or newLevel <= oldLevel then
+ return true
+ end
+
+ player:addHealth(player:getMaxHealth())
+ player:addMana(player:getMaxMana())
+ player:getFinalLowLevelBonus()
+ player:save()
+ return true
+end
+
+updatePlayerOnAdvancedLevel:register()
diff --git a/data/scripts/globalevents/global_server_save.lua b/data/scripts/globalevents/global_server_save.lua
index 00eaebbc3..c68c3dedc 100644
--- a/data/scripts/globalevents/global_server_save.lua
+++ b/data/scripts/globalevents/global_server_save.lua
@@ -5,7 +5,8 @@ local function ServerSave()
if configManager.getBoolean(configKeys.GLOBAL_SERVER_SAVE_CLOSE) then
Game.setGameState(GAME_STATE_CLOSED)
- elseif configManager.getBoolean(configKeys.GLOBAL_SERVER_SAVE_SHUTDOWN) then
+ end
+ if configManager.getBoolean(configKeys.GLOBAL_SERVER_SAVE_SHUTDOWN) then
Game.setGameState(GAME_STATE_SHUTDOWN)
end
diff --git a/data/scripts/lib/register_achievements.lua b/data/scripts/lib/register_achievements.lua
index b906afd28..18a2d6e41 100644
--- a/data/scripts/lib/register_achievements.lua
+++ b/data/scripts/lib/register_achievements.lua
@@ -526,6 +526,7 @@ ACHIEVEMENTS = {
[531] = { name = "First Achievement", grade = 1, points = 1, secret = true, description = "Congratulations to your very first achievement! ... Well, not really. But imagine, it is. Because at this point during your journey into Tibia's past, achievements have been introduced." },
[532] = { name = "Sharp Dressed", grade = 1, points = 2, description = "Just everyone will be crazy about you if you are wearing this formal dress. They will come running, promise!" },
[533] = { name = "Engine Driver", grade = 1, points = 3, description = "This glooth-driven locomotive will bring you to any party in the blink of an eye." },
+ [540] = { name = "Ripp-Ripp Hooray!", grade = 1, points = 3, description = "Don't get carried away by your success. Get carried away by your Ripptor." },
}
--[[
@@ -551,7 +552,7 @@ Functions:
ACHIEVEMENT_FIRST = 1
ACHIEVEMENT_LAST = #ACHIEVEMENTS
-for id, achievTable in ipairs(ACHIEVEMENTS) do
+for id, achievTable in pairs(ACHIEVEMENTS) do
if achievTable.name == nil then
logger.error(string.format("[Achievements registration] - Invalid achievement with no name, id: '%s'", id))
goto continue -- Skips to the next iteration using the 'continue' label
@@ -567,7 +568,7 @@ for id, achievTable in ipairs(ACHIEVEMENTS) do
local grade = achievTable.grade or 0
local points = achievTable.points or 0
- logger.debug("[Achievements registration] - Registering achievement '{}' with id '{}'", achievTable.name, id)
+ logger.trace("[Achievements registration] - Registering achievement '{}' with id '{}'", achievTable.name, id)
Game.registerAchievement(id, achievTable.name, achievTable.description, secret, grade, points)
::continue:: -- Label used by 'goto' to continue the loop
diff --git a/data/scripts/movements/snow.lua b/data/scripts/movements/snow.lua
index d4801dcb2..00f86bae1 100644
--- a/data/scripts/movements/snow.lua
+++ b/data/scripts/movements/snow.lua
@@ -2,7 +2,11 @@ local snow = MoveEvent()
function snow.onStepOut(creature, item, position, fromPosition)
local player = creature:getPlayer()
- if player and player:isInGhostMode() then
+ if not player then
+ return true
+ end
+
+ if player:isInGhostMode() then
return true
end
diff --git a/data-otxserver/scripts/movements/others/tiles.lua b/data/scripts/movements/special_tiles.lua
similarity index 68%
rename from data-otxserver/scripts/movements/others/tiles.lua
rename to data/scripts/movements/special_tiles.lua
index b797f0380..a1ec92dbb 100644
--- a/data-otxserver/scripts/movements/others/tiles.lua
+++ b/data/scripts/movements/special_tiles.lua
@@ -1,16 +1,15 @@
local increasing = { [419] = 420, [431] = 430, [452] = 453, [563] = 564, [549] = 562, [10145] = 10146 }
local decreasing = { [420] = 419, [430] = 431, [453] = 452, [564] = 563, [562] = 549, [10146] = 10145 }
--- onStepIn
-local tiles = MoveEvent()
+local tile = MoveEvent()
-function tiles.onStepIn(creature, item, position, fromPosition)
- if not increasing[item.itemid] then
+function tile.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player or player:isInGhostMode() then
return true
end
- local player = creature:getPlayer()
- if not player or player:isInGhostMode() then
+ if not increasing[item.itemid] then
return true
end
@@ -20,25 +19,28 @@ function tiles.onStepIn(creature, item, position, fromPosition)
if player:getLevel() < item.actionid - 1000 then
player:teleportTo(fromPosition, false)
position:sendMagicEffect(CONST_ME_MAGIC_BLUE)
- player:sendTextMessage(MESSAGE_FAILURE, "The tile seems to be protected against unwanted intruders.")
+ player:sendTextMessage(MESSAGE_LOOK, "The tile seems to be protected against unwanted intruders.")
end
return true
end
- if position:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
+ if Tile(position):hasFlag(TILESTATE_PROTECTIONZONE) then
for _, direction in ipairs(DIRECTIONS_TABLE) do
local playerPosition = player:getPosition()
playerPosition:getNextPosition(direction)
- local depotItem = playerPosition:getTile():getItemByType(ITEM_TYPE_DEPOT)
- if depotItem ~= nil then
+ local depotItem = playerPosition:getTile():getItemByType(ITEM_TYPE_DEPOT)
+ if depotItem then
local depotItems = 0
+
for id = 1, configManager.getNumber(configKeys.DEPOT_BOXES) do
depotItems = depotItems + player:getDepotChest(id, true):getItemHoldingCount()
end
- player:sendTextMessage(MESSAGE_FAILURE, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") .. "\
- Your supply stash contains " .. player:getStashCount() .. " item" .. (player:getStashCount() > 1 and "s." or "."))
+ local depotMessage = "Your depot contains " .. depotItems .. " item" .. (depotItems ~= 1 and "s." or ".")
+ local stashMessage = "Your supply stash contains " .. player:getStashCount() .. " item" .. (player:getStashCount() ~= 1 and "s." or ".")
+
+ player:sendTextMessage(MESSAGE_FAILURE, depotMessage .. "\n" .. stashMessage)
player:setSpecialContainersAvailable(true, true, true)
return true
end
@@ -51,37 +53,36 @@ function tiles.onStepIn(creature, item, position, fromPosition)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The tile seems to be protected against unwanted intruders.")
return true
end
+ return true
end
-tiles:type("stepin")
-
for index, value in pairs(increasing) do
- tiles:id(index)
+ tile:id(index)
end
-tiles:register()
-
-tiles = MoveEvent()
+tile:type("stepin")
+tile:register()
-function tiles.onStepOut(creature, item, position, fromPosition)
- if not decreasing[item.itemid] then
- return false
- end
+tile = MoveEvent()
+function tile.onStepOut(creature, item, position, fromPosition)
local player = creature:getPlayer()
if not player or player:isInGhostMode() then
return true
end
+ if not decreasing[item.itemid] then
+ return true
+ end
+
item:transform(decreasing[item.itemid])
player:setSpecialContainersAvailable(false, false, false)
return true
end
-tiles:type("stepout")
-
for index, value in pairs(decreasing) do
- tiles:id(index)
+ tile:id(index)
end
-tiles:register()
+tile:type("stepout")
+tile:register()
diff --git a/data/scripts/runes/avalanche.lua b/data/scripts/runes/avalanche.lua
index 6aefb1e10..883716269 100644
--- a/data/scripts/runes/avalanche.lua
+++ b/data/scripts/runes/avalanche.lua
@@ -30,4 +30,5 @@ rune:level(30)
rune:magicLevel(4)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/energy_bomb.lua b/data/scripts/runes/energy_bomb.lua
index 4396c02c0..b31ae0e2d 100644
--- a/data/scripts/runes/energy_bomb.lua
+++ b/data/scripts/runes/energy_bomb.lua
@@ -24,5 +24,5 @@ rune:level(37)
rune:magicLevel(10)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/energy_wall.lua b/data/scripts/runes/energy_wall.lua
index aae0bf029..d2e89e942 100644
--- a/data/scripts/runes/energy_wall.lua
+++ b/data/scripts/runes/energy_wall.lua
@@ -24,5 +24,5 @@ rune:level(41)
rune:magicLevel(9)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/fire_bomb.lua b/data/scripts/runes/fire_bomb.lua
index 257e07c75..5a3efac6d 100644
--- a/data/scripts/runes/fire_bomb.lua
+++ b/data/scripts/runes/fire_bomb.lua
@@ -24,5 +24,5 @@ rune:level(27)
rune:magicLevel(5)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/fire_wall.lua b/data/scripts/runes/fire_wall.lua
index a9b7696c3..66a8da38b 100644
--- a/data/scripts/runes/fire_wall.lua
+++ b/data/scripts/runes/fire_wall.lua
@@ -24,5 +24,5 @@ rune:level(33)
rune:magicLevel(6)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/great_fireball.lua b/data/scripts/runes/great_fireball.lua
index ca0285986..39fbca158 100644
--- a/data/scripts/runes/great_fireball.lua
+++ b/data/scripts/runes/great_fireball.lua
@@ -30,5 +30,5 @@ rune:level(30)
rune:magicLevel(4)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/poison_bomb.lua b/data/scripts/runes/poison_bomb.lua
index 117ac2c67..e220e4dc9 100644
--- a/data/scripts/runes/poison_bomb.lua
+++ b/data/scripts/runes/poison_bomb.lua
@@ -24,5 +24,5 @@ rune:level(25)
rune:magicLevel(4)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/poison_wall.lua b/data/scripts/runes/poison_wall.lua
index 1937b3058..64911f933 100644
--- a/data/scripts/runes/poison_wall.lua
+++ b/data/scripts/runes/poison_wall.lua
@@ -24,5 +24,5 @@ rune:level(29)
rune:magicLevel(5)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
-rune:isBlocking(true) -- True = Solid / False = Creature
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/stone_shower.lua b/data/scripts/runes/stone_shower.lua
index b3cfbd542..8be7e96b0 100644
--- a/data/scripts/runes/stone_shower.lua
+++ b/data/scripts/runes/stone_shower.lua
@@ -30,4 +30,5 @@ rune:level(28)
rune:magicLevel(4)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/runes/thunderstorm.lua b/data/scripts/runes/thunderstorm.lua
index 54ee6ed9c..d26978d75 100644
--- a/data/scripts/runes/thunderstorm.lua
+++ b/data/scripts/runes/thunderstorm.lua
@@ -30,4 +30,5 @@ rune:level(28)
rune:magicLevel(4)
rune:cooldown(2 * 1000)
rune:groupCooldown(2 * 1000)
+rune:isBlocking(false) -- True = Solid / False = Creature
rune:register()
diff --git a/data/scripts/talkactions/god/manage_storage.lua b/data/scripts/talkactions/god/manage_storage.lua
index 27a153e6d..929ef7b44 100644
--- a/data/scripts/talkactions/god/manage_storage.lua
+++ b/data/scripts/talkactions/god/manage_storage.lua
@@ -26,13 +26,13 @@ function Player.getStorageValueTalkaction(self, param)
if storageKey == nil then
-- Get the key for this storage name
local storageName = tostring(split[2])
- local storageValue = self:getStorageValueByName(storageName)
+ local storageValue = target:getStorageValueByName(storageName)
self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. storageName .. " from player " .. split[1] .. " is: " .. storageValue .. ".")
- return true
+ else
+ local storageValue = target:getStorageValue(storageKey)
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. storageKey .. " from player " .. split[1] .. " is: " .. storageValue .. ".")
end
- local storageValue = self:getStorageValue(storageKey)
- self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. storageKey .. " from player " .. split[1] .. " is: " .. storageValue .. ".")
return true
end
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index ee8b531dd..b9b857f43 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -37,6 +37,9 @@ enum ConfigKey_t : uint16_t {
CLEAN_PROTECTION_ZONES,
COMBAT_CHAIN_DELAY,
COMBAT_CHAIN_TARGETS,
+ COMBAT_CHAIN_SKILL_FORMULA_AXE,
+ COMBAT_CHAIN_SKILL_FORMULA_CLUB,
+ COMBAT_CHAIN_SKILL_FORMULA_SWORD,
COMPRESSION_LEVEL,
CONVERT_UNSAFE_SCRIPTS,
CORE_DIRECTORY,
@@ -240,6 +243,7 @@ enum ConfigKey_t : uint16_t {
SCRIPTS_CONSOLE_LOGS,
SERVER_MOTD,
SERVER_NAME,
+ SHOW_LOOTS_IN_BESTIARY,
SKULLED_DEATH_LOSE_STORE_ITEM,
SORT_LOOT_BY_CHANCE,
SQL_PORT,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 7069ca10f..c1702672e 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -128,6 +128,7 @@ bool ConfigManager::load() {
loadBoolConfig(L, REPLACE_KICK_ON_LOGIN, "replaceKickOnLogin", true);
loadBoolConfig(L, REWARD_CHEST_COLLECT_ENABLED, "rewardChestCollectEnabled", true);
loadBoolConfig(L, SCRIPTS_CONSOLE_LOGS, "showScriptsLogInConsole", true);
+ loadBoolConfig(L, SHOW_LOOTS_IN_BESTIARY, "showLootsInBestiary", false);
loadBoolConfig(L, SKULLED_DEATH_LOSE_STORE_ITEM, "skulledDeathLoseStoreItem", false);
loadBoolConfig(L, SORT_LOOT_BY_CHANCE, "sortLootByChance", false);
loadBoolConfig(L, STAMINA_PZ, "staminaPz", false);
@@ -217,6 +218,9 @@ bool ConfigManager::load() {
loadIntConfig(L, CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, "checkExpiredMarketOffersEachMinutes", 60);
loadIntConfig(L, COMBAT_CHAIN_DELAY, "combatChainDelay", 50);
loadIntConfig(L, COMBAT_CHAIN_TARGETS, "combatChainTargets", 5);
+ loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9);
+ loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_CLUB, "combatChainSkillFormulaClub", 0.7);
+ loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_SWORD, "combatChainSkillFormulaSword", 1.1);
loadIntConfig(L, COMPRESSION_LEVEL, "packetCompressionLevel", 6);
loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10);
loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3);
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 7456f5729..532f4d48b 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -524,7 +524,7 @@ bool Combat::setCallback(CallBackParam_t key) {
void Combat::setChainCallback(uint8_t chainTargets, uint8_t chainDistance, bool backtracking) {
params.chainCallback = std::make_unique(chainTargets, chainDistance, backtracking);
- g_logger().debug("ChainCallback created: {}, with targets: {}, distance: {}, backtracking: {}", params.chainCallback != nullptr, chainTargets, chainDistance, backtracking);
+ g_logger().trace("ChainCallback created: {}, with targets: {}, distance: {}, backtracking: {}", params.chainCallback != nullptr, chainTargets, chainDistance, backtracking);
}
CallBack* Combat::getCallback(CallBackParam_t key) {
@@ -941,6 +941,15 @@ void Combat::setupChain(const std::shared_ptr &weapon) {
return;
}
+ if (weapon->isChainDisabled()) {
+ return;
+ }
+
+ const auto &weaponType = weapon->getWeaponType();
+ if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO || weaponType == WEAPON_DISTANCE) {
+ return;
+ }
+
// clang-format off
static std::list areaList = {
0, 0, 0, 1, 0, 0, 0,
@@ -957,7 +966,6 @@ void Combat::setupChain(const std::shared_ptr &weapon) {
setArea(area);
g_logger().trace("Weapon: {}, element type: {}", Item::items[weapon->getID()].name, weapon->params.combatType);
setParam(COMBAT_PARAM_TYPE, weapon->params.combatType);
- const auto &weaponType = weapon->getWeaponType();
if (weaponType != WEAPON_WAND) {
setParam(COMBAT_PARAM_BLOCKARMOR, true);
}
@@ -976,15 +984,13 @@ void Combat::setupChain(const std::shared_ptr &weapon) {
switch (weaponType) {
case WEAPON_SWORD:
- setCommonValues(1.1, MELEE_ATK_SWORD, CONST_ME_SLASH);
+ setCommonValues(g_configManager().getFloat(COMBAT_CHAIN_SKILL_FORMULA_SWORD, __FUNCTION__), MELEE_ATK_SWORD, CONST_ME_SLASH);
break;
-
case WEAPON_CLUB:
- setCommonValues(0.7, MELEE_ATK_CLUB, CONST_ME_BLACK_BLOOD);
+ setCommonValues(g_configManager().getFloat(COMBAT_CHAIN_SKILL_FORMULA_CLUB, __FUNCTION__), MELEE_ATK_CLUB, CONST_ME_BLACK_BLOOD);
break;
-
case WEAPON_AXE:
- setCommonValues(0.9, MELEE_ATK_AXE, CONST_ANI_WHIRLWINDAXE);
+ setCommonValues(g_configManager().getFloat(COMBAT_CHAIN_SKILL_FORMULA_AXE, __FUNCTION__), MELEE_ATK_AXE, CONST_ANI_WHIRLWINDAXE);
break;
}
diff --git a/src/creatures/players/achievement/player_achievement.cpp b/src/creatures/players/achievement/player_achievement.cpp
index 18e2db3d3..2db53dbe7 100644
--- a/src/creatures/players/achievement/player_achievement.cpp
+++ b/src/creatures/players/achievement/player_achievement.cpp
@@ -16,18 +16,7 @@
#include "kv/kv.hpp"
PlayerAchievement::PlayerAchievement(Player &player) :
- m_player(player) {
- auto unlockedAchievements = getUnlockedKV()->keys();
- for (const auto &achievementName : unlockedAchievements) {
- const Achievement &achievement = g_game().getAchievementByName(achievementName);
- if (achievement.id == 0) {
- g_logger().error("[{}] - Achievement {} not found.", __FUNCTION__, achievementName);
- continue;
- }
-
- m_achievementsUnlocked.push_back({ achievement.id, getUnlockedKV()->get(achievementName)->getNumber() });
- }
-}
+ m_player(player) { }
bool PlayerAchievement::add(uint16_t id, bool message /* = true*/, uint32_t timestamp /* = 0*/) {
if (isUnlocked(id)) {
@@ -108,6 +97,22 @@ std::vector> PlayerAchievement::getUnlockedAchieve
return m_achievementsUnlocked;
}
+void PlayerAchievement::loadUnlockedAchievements() {
+ const auto &unlockedAchievements = getUnlockedKV()->keys();
+ g_logger().debug("[{}] - Loading unlocked achievements: {}", __FUNCTION__, unlockedAchievements.size());
+ for (const auto &achievementName : unlockedAchievements) {
+ const Achievement &achievement = g_game().getAchievementByName(achievementName);
+ if (achievement.id == 0) {
+ g_logger().error("[{}] - Achievement {} not found.", __FUNCTION__, achievementName);
+ continue;
+ }
+
+ g_logger().debug("[{}] - Achievement {} found for player {}.", __FUNCTION__, achievementName, m_player.getName());
+
+ m_achievementsUnlocked.push_back({ achievement.id, getUnlockedKV()->get(achievementName)->getNumber() });
+ }
+}
+
void PlayerAchievement::sendUnlockedSecretAchievements() {
std::vector> m_achievementsUnlocked;
uint16_t unlockedSecret = 0;
diff --git a/src/creatures/players/achievement/player_achievement.hpp b/src/creatures/players/achievement/player_achievement.hpp
index 8959ce429..7d141c96a 100644
--- a/src/creatures/players/achievement/player_achievement.hpp
+++ b/src/creatures/players/achievement/player_achievement.hpp
@@ -36,6 +36,7 @@ class PlayerAchievement {
void addPoints(uint16_t toAddPoints);
void removePoints(uint16_t toRemovePoints);
std::vector> getUnlockedAchievements() const;
+ void loadUnlockedAchievements();
void sendUnlockedSecretAchievements();
const std::shared_ptr &getUnlockedKV();
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 289862920..874089e56 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -3246,11 +3246,7 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing,
}
}
} else if (slotPosition & SLOTP_TWO_HAND) {
- if (inventory[CONST_SLOT_LEFT] && inventory[CONST_SLOT_LEFT] != item) {
- ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
- } else {
- ret = RETURNVALUE_NOERROR;
- }
+ ret = RETURNVALUE_CANNOTBEDRESSED;
} else if (inventory[CONST_SLOT_LEFT]) {
std::shared_ptr
- leftItem = inventory[CONST_SLOT_LEFT];
WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();
@@ -3273,11 +3269,19 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing,
}
case CONST_SLOT_LEFT: {
- if (slotPosition & SLOTP_LEFT) {
+ if (item->isQuiver()) {
+ ret = RETURNVALUE_CANNOTBEDRESSED;
+ } else if (slotPosition & SLOTP_LEFT) {
WeaponType_t type = item->getWeaponType();
if (type == WEAPON_NONE || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
ret = RETURNVALUE_CANNOTBEDRESSED;
- } else if (inventory[CONST_SLOT_RIGHT] && (slotPosition & SLOTP_TWO_HAND)) {
+ } else {
+ ret = RETURNVALUE_NOERROR;
+ }
+ } else if (slotPosition & SLOTP_TWO_HAND) {
+ if (inventory[CONST_SLOT_RIGHT]) {
+ WeaponType_t type = item->getWeaponType();
+ // Allow equip bow when quiver is in SLOT_RIGHT
if (type == WEAPON_DISTANCE && inventory[CONST_SLOT_RIGHT]->isQuiver()) {
ret = RETURNVALUE_NOERROR;
} else {
@@ -3286,12 +3290,6 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing,
} else {
ret = RETURNVALUE_NOERROR;
}
- } else if (slotPosition & SLOTP_TWO_HAND) {
- if (inventory[CONST_SLOT_RIGHT] && inventory[CONST_SLOT_RIGHT] != item) {
- ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
- } else {
- ret = RETURNVALUE_NOERROR;
- }
} else if (inventory[CONST_SLOT_RIGHT]) {
std::shared_ptr
- rightItem = inventory[CONST_SLOT_RIGHT];
WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();
@@ -8074,3 +8072,13 @@ void Player::checkAndShowBlessingMessage() {
sendTextMessage(MESSAGE_EVENT_ADVANCE, blessOutput.str());
}
}
+
+bool Player::canSpeakWithHireling(uint8_t speechbubble) {
+ const auto &playerTile = getTile();
+ const auto &house = playerTile ? playerTile->getHouse() : nullptr;
+ if (speechbubble == SPEECHBUBBLE_HIRELING && (!house || house->getHouseAccessLevel(static_self_cast()) == HOUSE_NOT_INVITED)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index d37214911..592db1790 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -2595,6 +2595,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::shared_ptr getStoreInbox() const;
+ bool canSpeakWithHireling(uint8_t speechbubble);
+
private:
friend class PlayerLock;
std::mutex mutex;
diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp
index b2fa39960..77a15f7cb 100644
--- a/src/creatures/players/wheel/player_wheel.cpp
+++ b/src/creatures/players/wheel/player_wheel.cpp
@@ -32,6 +32,47 @@ const static std::vector wheelGemBasicSlot1Allowed = {
WheelGemBasicModifier_t::Vocation_Health_IceResistance,
WheelGemBasicModifier_t::Vocation_Health_EnergyResistance,
WheelGemBasicModifier_t::Vocation_Health_EarthResistance,
+ WheelGemBasicModifier_t::Vocation_Mana_FireResistance,
+ WheelGemBasicModifier_t::Vocation_Mana_EnergyResistance,
+ WheelGemBasicModifier_t::Vocation_Mana_Earth_Resistance,
+ WheelGemBasicModifier_t::Vocation_Mana_Ice_Resistance,
+ WheelGemBasicModifier_t::Vocation_Capacity_FireResistance,
+ WheelGemBasicModifier_t::Vocation_Capacity_EnergyResistance,
+ WheelGemBasicModifier_t::Vocation_Capacity_EarthResistance,
+ WheelGemBasicModifier_t::Vocation_Capacity_IceResistance,
+};
+
+const static std::vector wheelGemBasicSlot2Allowed = {
+ WheelGemBasicModifier_t::General_FireResistance,
+ WheelGemBasicModifier_t::General_IceResistance,
+ WheelGemBasicModifier_t::General_EnergyResistance,
+ WheelGemBasicModifier_t::General_EarthResistance,
+ WheelGemBasicModifier_t::General_PhysicalResistance,
+ WheelGemBasicModifier_t::General_HolyResistance,
+ WheelGemBasicModifier_t::General_HolyResistance_DeathWeakness,
+ WheelGemBasicModifier_t::General_DeathResistance_HolyWeakness,
+ WheelGemBasicModifier_t::General_FireResistance_EarthResistance,
+ WheelGemBasicModifier_t::General_FireResistance_IceResistance,
+ WheelGemBasicModifier_t::General_FireResistance_EnergyResistance,
+ WheelGemBasicModifier_t::General_EarthResistance_IceResistance,
+ WheelGemBasicModifier_t::General_EarthResistance_EnergyResistance,
+ WheelGemBasicModifier_t::General_IceResistance_EnergyResistance,
+ WheelGemBasicModifier_t::General_FireResistance_EarthWeakness,
+ WheelGemBasicModifier_t::General_FireResistance_IceWeakness,
+ WheelGemBasicModifier_t::General_FireResistance_EnergyWeakness,
+ WheelGemBasicModifier_t::General_EarthResistance_FireWeakness,
+ WheelGemBasicModifier_t::General_EarthResistance_IceWeakness,
+ WheelGemBasicModifier_t::General_EarthResistance_EnergyWeakness,
+ WheelGemBasicModifier_t::General_IceResistance_EarthWeakness,
+ WheelGemBasicModifier_t::General_IceResistance_FireWeakness,
+ WheelGemBasicModifier_t::General_IceResistance_EnergyWeakness,
+ WheelGemBasicModifier_t::General_EnergyResistance_EarthWeakness,
+ WheelGemBasicModifier_t::General_EnergyResistance_IceWeakness,
+ WheelGemBasicModifier_t::General_EnergyResistance_FireWeakness,
+ WheelGemBasicModifier_t::General_ManaDrainResistance,
+ WheelGemBasicModifier_t::General_LifeDrainResistance,
+ WheelGemBasicModifier_t::General_ManaDrainResistance_LifeDrainResistance,
+ WheelGemBasicModifier_t::General_MitigationMultiplier,
};
// To avoid conflict in other files that might use a function with the same name
@@ -835,7 +876,7 @@ void PlayerWheel::revealGem(WheelGemQuality_t quality) {
gem.basicModifier2 = {};
gem.supremeModifier = {};
if (quality >= WheelGemQuality_t::Regular) {
- gem.basicModifier2 = static_cast(uniform_random(0, magic_enum::enum_count() - 1));
+ gem.basicModifier2 = selectBasicModifier2(gem.basicModifier1);
}
if (quality >= WheelGemQuality_t::Greater && !supremeModifiers.empty()) {
gem.supremeModifier = supremeModifiers[uniform_random(0, supremeModifiers.size() - 1)];
@@ -3106,3 +3147,11 @@ float PlayerWheel::calculateMitigation() const {
mitigation += (mitigation * (float)getMitigationMultiplier()) / 100.f;
return mitigation;
}
+
+WheelGemBasicModifier_t PlayerWheel::selectBasicModifier2(WheelGemBasicModifier_t modifier1) const {
+ WheelGemBasicModifier_t modifier = modifier1;
+ while (modifier == modifier1) {
+ modifier = wheelGemBasicSlot2Allowed[uniform_random(0, wheelGemBasicSlot2Allowed.size() - 1)];
+ }
+ return modifier;
+}
diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp
index 3ede17c33..303063beb 100644
--- a/src/creatures/players/wheel/player_wheel.hpp
+++ b/src/creatures/players/wheel/player_wheel.hpp
@@ -454,6 +454,8 @@ class PlayerWheel {
}
}
+ WheelGemBasicModifier_t selectBasicModifier2(WheelGemBasicModifier_t modifier1) const;
+
private:
friend class Player;
// Reference to the player
diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp
index 5f8db90fd..30345e049 100644
--- a/src/game/bank/bank.cpp
+++ b/src/game/bank/bank.cpp
@@ -97,16 +97,17 @@ bool Bank::transferTo(const std::shared_ptr destination, uint64_t amount)
g_logger().error("Bank::transferTo: destinationBankable is nullptr");
return false;
}
- if (destinationBankable->getPlayer() != nullptr) {
- auto player = destinationBankable->getPlayer();
- auto name = asLowerCaseString(player->getName());
+
+ auto destinationPlayer = destinationBankable->getPlayer();
+ if (destinationPlayer != nullptr) {
+ auto name = asLowerCaseString(destinationPlayer->getName());
replaceString(name, " ", "");
if (deniedNames.contains(name)) {
g_logger().warn("Bank::transferTo: denied name: {}", name);
return false;
}
- if (player->getTown()->getID() < minTownId) {
- g_logger().warn("Bank::transferTo: denied town: {}", player->getTown()->getID());
+ if (destinationPlayer->getTown()->getID() < minTownId) {
+ g_logger().warn("Bank::transferTo: denied town: {}", destinationPlayer->getTown()->getID());
return false;
}
}
@@ -114,8 +115,15 @@ bool Bank::transferTo(const std::shared_ptr destination, uint64_t amount)
if (!(debit(amount) && destination->credit(amount))) {
return false;
}
- g_metrics().addCounter("balance_increase", amount, { { "player", destination->getBankable()->getPlayer()->getName() }, { "context", "bank_transfer" } });
- g_metrics().addCounter("balance_decrease", amount, { { "player", getBankable()->getPlayer()->getName() }, { "context", "bank_transfer" } });
+
+ if (destinationPlayer) {
+ g_metrics().addCounter("balance_increase", amount, { { "player", destinationPlayer->getName() }, { "context", "bank_transfer" } });
+ }
+
+ if (bankable->getPlayer()) {
+ g_metrics().addCounter("balance_decrease", amount, { { "player", bankable->getPlayer()->getName() }, { "context", "bank_transfer" } });
+ }
+
return true;
}
@@ -151,6 +159,8 @@ bool Bank::deposit(const std::shared_ptr destination, uint64_t amount) {
if (!g_game().removeMoney(bankable->getPlayer(), amount)) {
return false;
}
- g_metrics().addCounter("balance_increase", amount, { { "player", bankable->getPlayer()->getName() }, { "context", "bank_deposit" } });
+ if (bankable->getPlayer() != nullptr) {
+ g_metrics().addCounter("balance_decrease", amount, { { "player", bankable->getPlayer()->getName() }, { "context", "bank_deposit" } });
+ }
return destination->credit(amount);
}
diff --git a/src/game/game.cpp b/src/game/game.cpp
index c17f16b1a..53e887c90 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -2581,7 +2581,15 @@ std::shared_ptr
- Game::transformItem(std::shared_ptr
- item, uint16_t n
uint16_t itemId = item->getID();
int32_t count = item->getSubType();
- if (curType.id != newType.id) {
+ auto decaying = item->getDecaying();
+ // If the item is decaying, we need to transform it to the new item
+ if (decaying > DECAYING_FALSE && item->getDuration() <= 1) {
+ g_logger().debug("Decay duration old type {}, transformEquipTo {}, transformDeEquipTo {}", curType.decayTo, curType.transformEquipTo, curType.transformDeEquipTo);
+ g_logger().debug("Decay duration new type, decayTo {}, transformEquipTo {}, transformDeEquipTo {}", newType.decayTo, newType.transformEquipTo, newType.transformDeEquipTo);
+ if (newType.decayTo) {
+ itemId = newType.decayTo;
+ }
+ } else if (curType.id != newType.id) {
if (newType.group != curType.group) {
item->setDefaultSubtype();
}
@@ -5285,7 +5293,7 @@ void Game::playerSetManagedContainer(uint32_t playerId, ObjectCategory_t categor
}
std::shared_ptr container = thing->getContainer();
- auto allowConfig = !g_configManager().getBoolean(TOGGLE_GOLD_POUCH_ALLOW_ANYTHING, __FUNCTION__) || !g_configManager().getBoolean(TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY, __FUNCTION__);
+ auto allowConfig = g_configManager().getBoolean(TOGGLE_GOLD_POUCH_ALLOW_ANYTHING, __FUNCTION__) || g_configManager().getBoolean(TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY, __FUNCTION__);
if (!container || (container->getID() == ITEM_GOLD_POUCH && category != OBJECTCATEGORY_GOLD) && !allowConfig) {
player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
@@ -5691,7 +5699,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun
return;
}
- if (playerTile->hasFlag(TILESTATE_PROTECTIONZONE)) {
+ if (!g_configManager().getBoolean(TOGGLE_MOUNT_IN_PZ, __FUNCTION__) && playerTile->hasFlag(TILESTATE_PROTECTIONZONE)) {
outfit.lookMount = 0;
}
@@ -5908,6 +5916,10 @@ void Game::playerSpeakToNpc(std::shared_ptr player, const std::string &t
}
for (const auto &spectator : Spectators().find(player->getPosition()).filter()) {
+ if (!player->canSpeakWithHireling(spectator->getNpc()->getSpeechBubble())) {
+ continue;
+ }
+
spectator->getNpc()->onCreatureSay(player, TALKTYPE_PRIVATE_PN, text);
}
@@ -8452,6 +8464,16 @@ void Game::playerNpcGreet(uint32_t playerId, uint32_t npcId) {
return;
}
+ // Check npc say exhausted
+ if (player->isUIExhausted()) {
+ player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
+ return;
+ }
+
+ if (!player->canSpeakWithHireling(npc->getSpeechBubble())) {
+ return;
+ }
+
auto spectators = Spectators().find(player->getPosition(), true);
spectators.insert(npc);
internalCreatureSay(player, TALKTYPE_SAY, "hi", false, &spectators);
@@ -8463,6 +8485,8 @@ void Game::playerNpcGreet(uint32_t playerId, uint32_t npcId) {
} else {
internalCreatureSay(player, TALKTYPE_PRIVATE_PN, "sail", false, &npcsSpectators);
}
+
+ player->updateUIExhausted();
}
void Game::playerLeaveMarket(uint32_t playerId) {
diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp
index bd61b2456..d4a4f3377 100644
--- a/src/io/functions/iologindata_load_player.cpp
+++ b/src/io/functions/iologindata_load_player.cpp
@@ -10,6 +10,7 @@
#include "pch.hpp"
#include "creatures/players/wheel/player_wheel.hpp"
+#include "creatures/players/achievement/player_achievement.hpp"
#include "io/functions/iologindata_load_player.hpp"
#include "game/game.hpp"
#include "enums/object_category.hpp"
@@ -887,6 +888,8 @@ void IOLoginDataLoad::loadPlayerInitializeSystem(std::shared_ptr player)
player->wheel()->loadDBPlayerSlotPointsOnLogin();
player->wheel()->initializePlayerData();
+ player->achiev()->loadUnlockedAchievements();
+
player->initializePrey();
player->initializeTaskHunting();
}
diff --git a/src/io/functions/iologindata_save_player.hpp b/src/io/functions/iologindata_save_player.hpp
index 5bc2ea9ce..bbd44105d 100644
--- a/src/io/functions/iologindata_save_player.hpp
+++ b/src/io/functions/iologindata_save_player.hpp
@@ -26,7 +26,7 @@ class IOLoginDataSave : public IOLoginData {
static bool savePlayerTaskHuntingClass(std::shared_ptr player);
static bool savePlayerForgeHistory(std::shared_ptr player);
static bool savePlayerBosstiary(std::shared_ptr player);
- static bool savePlayerStorage(std::shared_ptr palyer);
+ static bool savePlayerStorage(std::shared_ptr player);
protected:
using ItemBlockList = std::list>>;
diff --git a/src/io/io_wheel.cpp b/src/io/io_wheel.cpp
index 6e7c2b02b..e8ad30a5d 100644
--- a/src/io/io_wheel.cpp
+++ b/src/io/io_wheel.cpp
@@ -416,7 +416,7 @@ void IOWheel::slotGreen200(const std::shared_ptr &player, uint16_t point
// SLOT_GREEN_TOP_150 = 2
void IOWheel::slotGreenTop150(const std::shared_ptr &player, uint16_t points, uint8_t, PlayerWheelMethodsBonusData &bonusData) const {
bonusData.mitigation += MITIGATION_INCREASE * points;
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_GREEN_BOTTOM_150)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_GREEN_TOP_150)) {
bonusData.leech.manaLeech += MANA_LEECH_INCREASE;
}
}
@@ -522,7 +522,7 @@ void IOWheel::slotGreenTop75(const std::shared_ptr &player, uint16_t poi
} else {
bonusData.stats.mana += 6 * points;
}
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_GREEN_TOP_100)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_GREEN_TOP_75)) {
bonusData.leech.lifeLeech += HEALTH_LEECH_INCREASE;
}
}
@@ -566,7 +566,7 @@ void IOWheel::slotRedBottom150(const std::shared_ptr &player, uint16_t p
} else {
bonusData.stats.health += 1 * points;
}
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_RED_TOP_150)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_RED_BOTTOM_150)) {
bonusData.leech.manaLeech += MANA_LEECH_INCREASE;
}
}
@@ -760,7 +760,7 @@ void IOWheel::slotBlueTop150(const std::shared_ptr &player, uint16_t poi
} else {
bonusData.stats.capacity += 2 * points;
}
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_BLUE_BOTTOM_150)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_BLUE_TOP_150)) {
bonusData.leech.lifeLeech += HEALTH_LEECH_INCREASE;
}
}
@@ -796,7 +796,7 @@ void IOWheel::slotBlueBottom75(const std::shared_ptr &player, uint16_t p
// SLOT_PURPLE_BOTTOM_75 = 28
void IOWheel::slotPurpleBottom75(const std::shared_ptr &player, uint16_t points, uint8_t, PlayerWheelMethodsBonusData &bonusData) const {
bonusData.mitigation += MITIGATION_INCREASE * points;
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_PURPLE_BOTTOM_100)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_PURPLE_BOTTOM_75)) {
bonusData.leech.manaLeech += MANA_LEECH_INCREASE;
}
}
@@ -898,7 +898,7 @@ void IOWheel::slotPurpleBottom150(const std::shared_ptr &player, uint16_
} else {
bonusData.stats.mana += 6 * points;
}
- if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_PURPLE_TOP_150)) {
+ if (isMaxPointAddedToSlot(player, points, WheelSlots_t::SLOT_PURPLE_BOTTOM_150)) {
bonusData.leech.lifeLeech += HEALTH_LEECH_INCREASE;
}
}
diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp
index ac79ef4c5..ca1bdb209 100644
--- a/src/io/iomarket.cpp
+++ b/src/io/iomarket.cpp
@@ -55,6 +55,7 @@ MarketOfferList IOMarket::getActiveOffers(MarketAction_t action) {
} while (result->next());
return offerList;
}
+
MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId, uint8_t tier) {
MarketOfferList offerList;
diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp
index 365cda202..2d1ba9301 100644
--- a/src/items/functions/item/item_parse.cpp
+++ b/src/items/functions/item/item_parse.cpp
@@ -1025,8 +1025,8 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri
auto stringKey = asLowerCaseString(subKeyAttribute.as_string());
if (stringKey == "slot") {
+ auto slotName = asLowerCaseString(subValueAttribute.as_string());
if (moveevent && (moveevent->getEventType() == MOVE_EVENT_EQUIP || moveevent->getEventType() == MOVE_EVENT_DEEQUIP)) {
- auto slotName = asLowerCaseString(subValueAttribute.as_string());
if (slotName == "head") {
moveevent->setSlot(SLOTP_HEAD);
} else if (slotName == "necklace") {
@@ -1049,13 +1049,14 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri
moveevent->setSlot(SLOTP_RING);
} else if (slotName == "ammo") {
moveevent->setSlot(SLOTP_AMMO);
+ } else if (slotName == "two-handed") {
+ moveevent->setSlot(SLOTP_TWO_HAND);
} else {
g_logger().warn("[{}] unknown slot type '{}'", __FUNCTION__, slotName);
}
} else if (weapon) {
uint16_t id = weapon->getID();
ItemType &it = Item::items.getItemType(id);
- auto slotName = asLowerCaseString(subValueAttribute.as_string());
if (slotName == "two-handed") {
it.slotPosition = SLOTP_TWO_HAND;
} else {
@@ -1164,10 +1165,17 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri
} else {
g_logger().warn("[{}] - wandtype '{}' does not exist", __FUNCTION__, elementName);
}
+
} else if (stringKey == "chain" && weapon) {
- auto value = subValueAttribute.as_double();
- weapon->setChainSkillValue(value);
- g_logger().trace("Found chain skill value '{}' for weapon: {}", value, itemType.name);
+ auto doubleValue = subValueAttribute.as_double();
+ if (doubleValue > 0) {
+ weapon->setChainSkillValue(doubleValue);
+ g_logger().trace("Found chain skill value '{}' for weapon: {}", doubleValue, itemType.name);
+ }
+ if (doubleValue < 0.1 && subValueAttribute.as_bool() == false) {
+ weapon->setDisabledChain();
+ g_logger().trace("Chain disabled for weapon: {}", itemType.name);
+ }
}
}
@@ -1176,6 +1184,7 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri
g_logger().trace("Added weapon damage from '{}', to '{}'", fromDamage, toDamage);
weaponWand->setMinChange(fromDamage);
weaponWand->setMaxChange(toDamage);
+ weaponWand->configureWeapon(itemType);
}
auto combat = weapon->getCombat();
diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp
index 25287ca9f..6c0d56d88 100644
--- a/src/items/weapons/weapons.cpp
+++ b/src/items/weapons/weapons.cpp
@@ -221,9 +221,9 @@ void Weapon::internalUseWeapon(std::shared_ptr player, std::shared_ptr player, std::shared_ptrdoCombatChain(player, target, params.aggressive);
+ g_logger().debug("Weapon::internalUseWeapon - Chain callback executed.");
} else {
Combat::doCombatHealth(player, target, damage, params);
}
+
g_logger().debug("Weapon::internalUseWeapon - cpp callback executed.");
}
@@ -886,17 +888,26 @@ int32_t WeaponWand::getWeaponDamage(std::shared_ptr player, std::shared_
}
// If chain system is enabled, calculates magic-based damage
- int32_t attackSkill;
- int32_t attackValue;
- float attackFactor;
- [[maybe_unused]] int16_t elementAttack;
+ int32_t attackSkill = 0;
+ int32_t attackValue = 0;
+ float attackFactor = 0.0;
+ [[maybe_unused]] int16_t elementAttack = 0;
[[maybe_unused]] CombatDamage combatDamage;
calculateSkillFormula(player, attackSkill, attackValue, attackFactor, elementAttack, combatDamage);
auto magLevel = player->getMagicLevel();
auto level = player->getLevel();
- double min = (level / 5.0) + (magLevel + attackValue) / 3.0;
- double max = (level / 5.0) + (magLevel + attackValue);
+
+ // Check if level is greater than zero before performing division
+ auto levelDivision = level > 0 ? level / 5.0 : 0.0;
+
+ auto totalAttackValue = magLevel + attackValue;
+
+ // Check if magLevel is greater than zero before performing division
+ auto magicLevelDivision = totalAttackValue > 0 ? totalAttackValue / 3.0 : 0.0;
+
+ double min = levelDivision + magicLevelDivision;
+ double max = levelDivision + totalAttackValue;
// Returns the calculated maximum damage or a random value between the calculated minimum and maximum
return maxDamage ? -max : -normal_random(min, max);
diff --git a/src/items/weapons/weapons.hpp b/src/items/weapons/weapons.hpp
index 2cf97568a..5b2b260e2 100644
--- a/src/items/weapons/weapons.hpp
+++ b/src/items/weapons/weapons.hpp
@@ -192,6 +192,14 @@ class Weapon : public Script {
return m_chainSkillValue;
}
+ void setDisabledChain() {
+ m_isDisabledChain = true;
+ }
+
+ bool isChainDisabled() const {
+ return m_isDisabledChain;
+ }
+
const WeaponType_t getWeaponType() const {
return weaponType;
}
@@ -243,6 +251,7 @@ class Weapon : public Script {
bool enabled = true;
bool premium = false;
bool wieldUnproperly = false;
+ bool m_isDisabledChain = false;
std::string vocationString = "";
void onUsedWeapon(std::shared_ptr player, std::shared_ptr
- item, std::shared_ptr destTile) const;
diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp
index 076d2b902..0af09476e 100644
--- a/src/lua/creature/movement.cpp
+++ b/src/lua/creature/movement.cpp
@@ -530,6 +530,7 @@ uint32_t MoveEvent::EquipItem(const std::shared_ptr moveEvent, std::s
}
if (player->isItemAbilityEnabled(slot)) {
+ g_logger().debug("[{}] item ability is already enabled", __FUNCTION__);
return 1;
}
diff --git a/src/lua/functions/core/libs/logger_functions.cpp b/src/lua/functions/core/libs/logger_functions.cpp
index dc039a9ed..1b2605f64 100644
--- a/src/lua/functions/core/libs/logger_functions.cpp
+++ b/src/lua/functions/core/libs/logger_functions.cpp
@@ -24,6 +24,7 @@ void LoggerFunctions::init(lua_State* L) {
registerMethod(L, "logger", "warn", LoggerFunctions::luaLoggerWarn);
registerMethod(L, "logger", "error", LoggerFunctions::luaLoggerError);
registerMethod(L, "logger", "debug", LoggerFunctions::luaLoggerDebug);
+ registerMethod(L, "logger", "trace", LoggerFunctions::luaLoggerTrace);
}
int LoggerFunctions::luaSpdlogInfo(lua_State* L) {
@@ -107,3 +108,13 @@ int LoggerFunctions::luaLoggerDebug(lua_State* L) {
}
return 1;
}
+
+int LoggerFunctions::luaLoggerTrace(lua_State* L) {
+ // logger.trace(text)
+ if (isString(L, 1)) {
+ g_logger().trace(getFormatedLoggerMessage(L));
+ } else {
+ reportErrorFunc("First parameter needs to be a string");
+ }
+ return 1;
+}
diff --git a/src/lua/functions/core/libs/logger_functions.hpp b/src/lua/functions/core/libs/logger_functions.hpp
index 5e83f1461..2c4dba46c 100644
--- a/src/lua/functions/core/libs/logger_functions.hpp
+++ b/src/lua/functions/core/libs/logger_functions.hpp
@@ -25,4 +25,5 @@ class LoggerFunctions final : public LuaScriptInterface {
static int luaLoggerError(lua_State* L);
static int luaLoggerInfo(lua_State* L);
static int luaLoggerWarn(lua_State* L);
+ static int luaLoggerTrace(lua_State* L);
};
diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp
index 7d9de9162..41789b883 100644
--- a/src/server/network/connection/connection.cpp
+++ b/src/server/network/connection/connection.cpp
@@ -175,7 +175,9 @@ void Connection::parseHeader(const std::error_code &error) {
readTimer.cancel();
if (error) {
- g_logger().debug("[Connection::parseHeader] - Read error: {}", error.message());
+ if (error != asio::error::operation_aborted && error != asio::error::eof && error != asio::error::connection_reset) {
+ g_logger().debug("[Connection::parseHeader] - Read error: {}", error.message());
+ }
close(FORCE_CLOSE);
return;
} else if (connectionState == CONNECTION_STATE_CLOSED) {
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index 76795ced0..539815506 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -2334,10 +2334,10 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) {
break;
}
- newmsg.add(shouldAddItem == true ? loot.id : 0);
+ newmsg.add(g_configManager().getBoolean(SHOW_LOOTS_IN_BESTIARY, __FUNCTION__) || shouldAddItem == true ? loot.id : 0);
newmsg.addByte(difficult);
newmsg.addByte(0); // 1 if special event - 0 if regular loot (?)
- if (shouldAddItem == true) {
+ if (g_configManager().getBoolean(SHOW_LOOTS_IN_BESTIARY, __FUNCTION__) || shouldAddItem == true) {
newmsg.addString(loot.name, "ProtocolGame::parseBestiarysendMonsterData - loot.name");
newmsg.addByte(loot.countmax > 0 ? 0x1 : 0x0);
}
@@ -3949,6 +3949,10 @@ void ProtocolGame::sendStats() {
}
void ProtocolGame::sendBasicData() {
+ if (!player) {
+ return;
+ }
+
NetworkMessage msg;
msg.addByte(0x9F);
if (player->isPremium() || player->isVip()) {
@@ -5921,7 +5925,12 @@ void ProtocolGame::sendRestingStatus(uint8_t protection) {
msg.addByte(0xA9);
msg.addByte(protection); // 1 / 0
- int32_t dailyStreak = static_cast(player->kv()->scoped("daily-reward")->get("streak")->getNumber());
+ uint8_t dailyStreak = 0;
+ auto dailyRewardKV = player->kv()->scoped("daily-reward")->get("streak");
+ if (dailyRewardKV && dailyRewardKV.has_value()) {
+ dailyStreak = static_cast(dailyRewardKV->getNumber());
+ }
+
msg.addByte(dailyStreak < 2 ? 0 : 1);
if (dailyStreak < 2) {
msg.addString("Resting Area (no active bonus)", "ProtocolGame::sendRestingStatus - Resting Area (no active bonus)");
@@ -6105,6 +6114,10 @@ void ProtocolGame::sendPartyCreatureShowStatus(std::shared_ptr target,
}
void ProtocolGame::sendPartyPlayerVocation(std::shared_ptr target) {
+ if (!target) {
+ return;
+ }
+
uint32_t cid = target->getID();
if (!knownCreatureSet.contains(cid)) {
sendPartyCreatureUpdate(target);
@@ -6124,7 +6137,7 @@ void ProtocolGame::sendPartyPlayerVocation(std::shared_ptr target) {
}
void ProtocolGame::sendPlayerVocation(std::shared_ptr target) {
- if (!player || oldProtocol) {
+ if (!player || !target || oldProtocol) {
return;
}
@@ -8618,7 +8631,7 @@ void ProtocolGame::sendMonsterPodiumWindow(std::shared_ptr
- podium, const P
}
void ProtocolGame::parseSetMonsterPodium(NetworkMessage &msg) const {
- if (oldProtocol) {
+ if (!player || oldProtocol) {
return;
}