diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua
index 741532cda7b..202bb290a38 100644
--- a/data-otservbr-global/lib/core/storages.lua
+++ b/data-otservbr-global/lib/core/storages.lua
@@ -2768,6 +2768,7 @@ Storage = {
GoshnarMegalomaniaAccess = 47220,
GoshnarMegalomaniaKilled = 47222,
QuestReward = 47223,
+ OutfitReward = 47224,
},
},
U12_60 = { -- update 12.60 - Reserved Storages 47501 - 47600
diff --git a/data-otservbr-global/monster/bosses/doctor_marrow.lua b/data-otservbr-global/monster/bosses/doctor_marrow.lua
index 61f19f0b70b..308ab14b27b 100644
--- a/data-otservbr-global/monster/bosses/doctor_marrow.lua
+++ b/data-otservbr-global/monster/bosses/doctor_marrow.lua
@@ -20,10 +20,6 @@ monster.corpse = 18074
monster.speed = 180
monster.manaCost = 0
-monster.events = {
- "DoctorMarrowHealth",
-}
-
monster.changeTarget = {
interval = 4000,
chance = 15,
diff --git a/data-otservbr-global/monster/bosses/the_first_dragon.lua b/data-otservbr-global/monster/bosses/the_first_dragon.lua
index e70114de0a3..34294280dd1 100644
--- a/data-otservbr-global/monster/bosses/the_first_dragon.lua
+++ b/data-otservbr-global/monster/bosses/the_first_dragon.lua
@@ -58,7 +58,7 @@ monster.flags = {
}
monster.events = {
- "First Dragon Death",
+ "FirstDragonDeath",
}
monster.light = {
diff --git a/data-otservbr-global/monster/quests/a_pirates_tail_quest/ratmiral_blackwhiskers.lua b/data-otservbr-global/monster/quests/a_pirates_tail_quest/ratmiral_blackwhiskers.lua
index a1d2b57ae54..d399413ed93 100644
--- a/data-otservbr-global/monster/quests/a_pirates_tail_quest/ratmiral_blackwhiskers.lua
+++ b/data-otservbr-global/monster/quests/a_pirates_tail_quest/ratmiral_blackwhiskers.lua
@@ -20,6 +20,10 @@ monster.corpse = 35846
monster.speed = 115
monster.manaCost = 0
+monster.events = {
+ "RatmiralBlackwhiskersDeath",
+}
+
monster.changeTarget = {
interval = 4000,
chance = 10,
diff --git a/data-otservbr-global/monster/quests/soul_war/goshnars_megalomania.lua b/data-otservbr-global/monster/quests/soul_war/goshnars_megalomania.lua
index 7487f4fda9a..1c871919b87 100644
--- a/data-otservbr-global/monster/quests/soul_war/goshnars_megalomania.lua
+++ b/data-otservbr-global/monster/quests/soul_war/goshnars_megalomania.lua
@@ -18,6 +18,7 @@ monster.maxHealth = 500000
monster.race = "undead"
monster.corpse = 33889
monster.speed = 165
+monster.manaCost = 0
monster.changeTarget = {
interval = 2000,
diff --git a/data-otservbr-global/scripts/custom/monster_training_machine.lua b/data-otservbr-global/monster/trainers/training_machine.lua
similarity index 97%
rename from data-otservbr-global/scripts/custom/monster_training_machine.lua
rename to data-otservbr-global/monster/trainers/training_machine.lua
index 7ef2f78d707..c5a98a9ce28 100644
--- a/data-otservbr-global/scripts/custom/monster_training_machine.lua
+++ b/data-otservbr-global/monster/trainers/training_machine.lua
@@ -1,6 +1,7 @@
local mType = Game.createMonsterType("Training Machine")
local monster = {}
-monster.description = "Training Machine"
+
+monster.description = "a training machine"
monster.experience = 0
monster.outfit = {
lookType = 1142,
diff --git a/data-otservbr-global/npc/imbuement_assistant.lua b/data-otservbr-global/npc/imbuement_assistant.lua
index 35ab1a282ea..a4f1228ecd6 100644
--- a/data-otservbr-global/npc/imbuement_assistant.lua
+++ b/data-otservbr-global/npc/imbuement_assistant.lua
@@ -30,6 +30,8 @@ npcConfig.voices = {
{ text = "Hello adventurer, looking for Imbuement items? Just ask me!" },
}
+local playerImbuementData = {}
+
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
@@ -55,15 +57,309 @@ end
npcType.onCloseChannel = function(npc, creature)
npcHandler:onCloseChannel(npc, creature)
+ playerImbuementData[creature:getId()] = nil
+end
+
+function addItemsToShoppingBag(npc, player)
+ local playerId = player:getId()
+ local playerData = playerImbuementData[playerId]
+
+ if playerData then
+ local moneyRequired = playerData.moneyRequired
+ local itemList = playerData.itemList
+ if player:getMoney() + player:getBankBalance() < moneyRequired then
+ npcHandler:say("Sorry, you don't have enough money", npc, player)
+ npcHandler:setTopic(player:getId(), 0)
+ return false, "You don't have enough money."
+ end
+
+ local totalWeight = 0
+ for _, item in pairs(itemList) do
+ local itemType = ItemType(item.itemId)
+ totalWeight = totalWeight + (itemType:getWeight() * item.count)
+ end
+
+ if player:getFreeCapacity() < totalWeight then
+ return false, "You don't have enough weight."
+ end
+
+ if player:getFreeBackpackSlots() == 0 then
+ return false, "You don't have enough room."
+ end
+
+ local shoppingBag = player:addItem(2856, 1) -- present box
+ for _, item in pairs(itemList) do
+ shoppingBag:addItem(item.itemId, item.count)
+ end
+
+ player:removeMoneyBank(moneyRequired)
+
+ return true
+ end
+
+ return false
end
--- Basic
+local imbuementPackagesData = {
+ -- Skill increase packages
+ ["bash"] = {
+ text = "skill club",
+ moneyRequired = 6250,
+ itemList = {
+ { itemId = 9657, count = 20 },
+ { itemId = 22189, count = 15 },
+ { itemId = 10405, count = 10 },
+ },
+ },
+ ["blockade"] = {
+ text = "skill shield",
+ moneyRequired = 16150,
+ itemList = {
+ { itemId = 9641, count = 20 },
+ { itemId = 11703, count = 25 },
+ { itemId = 20199, count = 25 },
+ },
+ },
+ ["chop"] = {
+ text = "skill axe",
+ moneyRequired = 13050,
+ itemList = {
+ { itemId = 10196, count = 20 }, -- orc tooth
+ { itemId = 11447, count = 25 }, -- battle stone
+ { itemId = 21200, count = 20 }, -- moohtant horn
+ },
+ },
+ ["epiphany"] = {
+ text = "magic level",
+ moneyRequired = 10650,
+ itemList = {
+ { itemId = 9635, count = 25 }, -- elvish talisman
+ { itemId = 11452, count = 15 }, -- broken shamanic staff
+ { itemId = 10309, count = 15 }, -- strand of medusa hair
+ },
+ },
+ ["precision"] = {
+ text = "skill distance",
+ moneyRequired = 6750,
+ itemList = {
+ { itemId = 11464, count = 25 }, -- elven scouting glass
+ { itemId = 18994, count = 20 }, -- elven hoof
+ { itemId = 10298, count = 10 }, -- metal spike
+ },
+ },
+ ["slash"] = {
+ text = "skill sword",
+ moneyRequired = 6550,
+ itemList = {
+ { itemId = 9691, count = 25 }, -- lion's mane
+ { itemId = 21202, count = 25 }, -- mooh'tah shell
+ { itemId = 9654, count = 5 }, -- war crystal
+ },
+ },
+ -- Additional attributes packages
+ ["featherweight"] = {
+ text = "capacity increase",
+ moneyRequired = 12250,
+ itemList = {
+ { itemId = 25694, count = 20 }, -- fairy wings
+ { itemId = 25702, count = 10 }, -- little bowl of myrrh
+ { itemId = 20205, count = 5 }, -- goosebump leather
+ },
+ },
+ ["strike"] = {
+ text = "critical",
+ moneyRequired = 16700,
+ itemList = {
+ { itemId = 11444, count = 20 }, -- protective charm
+ { itemId = 10311, count = 25 }, -- sabretooth
+ { itemId = 22728, count = 5 }, -- vexclaw talon
+ },
+ },
+ ["swiftness"] = {
+ text = "speed",
+ moneyRequired = 5225,
+ itemList = {
+ { itemId = 17458, count = 15 }, -- damselfly wing
+ { itemId = 10302, count = 25 }, -- compass
+ { itemId = 14081, count = 20 }, -- waspoid wing
+ },
+ },
+ ["vampirism"] = {
+ text = "life leech",
+ moneyRequired = 10475,
+ itemList = {
+ { itemId = 9685, count = 25 }, -- vampire teeth
+ { itemId = 9633, count = 15 }, -- bloody pincers
+ { itemId = 9663, count = 5 }, -- piece of dead brain
+ },
+ },
+ ["vibrancy"] = {
+ text = "paralysis removal",
+ moneyRequired = 15000,
+ itemList = {
+ { itemId = 22053, count = 20 }, -- wereboar hooves
+ { itemId = 23507, count = 15 }, -- crystallized anger
+ { itemId = 28567, count = 5 }, -- quill
+ },
+ },
+ ["void"] = {
+ text = "mana leech",
+ moneyRequired = 17400,
+ itemList = {
+ { itemId = 11492, count = 25 }, -- rope belt
+ { itemId = 20200, count = 25 }, -- silencer claws
+ { itemId = 22730, count = 5 }, -- some grimeleech wings
+ },
+ },
+ -- Elemental damage packages
+ ["electrify"] = {
+ text = "energy damage",
+ moneyRequired = 3770,
+ itemList = {
+ { itemId = 18993, count = 25 }, -- rorc feather
+ { itemId = 21975, count = 5 }, -- peacock feather fan
+ { itemId = 23508, count = 1 }, -- energy vein
+ },
+ },
+ ["frost"] = {
+ text = "ice damage",
+ moneyRequired = 9750,
+ itemList = {
+ { itemId = 9661, count = 25 }, -- frosty heart
+ { itemId = 21801, count = 10 }, -- seacrest hair
+ { itemId = 9650, count = 5 }, -- polar bear paw
+ },
+ },
+ ["reap"] = {
+ text = "death damage",
+ moneyRequired = 3475,
+ itemList = {
+ { itemId = 11484, count = 25 }, -- pile of grave earth
+ { itemId = 9647, count = 20 }, -- demonic skeletal hand
+ { itemId = 10420, count = 5 }, -- petrified scream
+ },
+ },
+ ["scorch"] = {
+ text = "fire damage",
+ moneyRequired = 15875,
+ itemList = {
+ { itemId = 9636, count = 25 }, -- fiery heart
+ { itemId = 5920, count = 5 }, -- green dragon scale
+ { itemId = 5954, count = 5 }, -- demon horn
+ },
+ },
+ ["venom"] = {
+ text = "earth damage",
+ moneyRequired = 1820,
+ itemList = {
+ { itemId = 9686, count = 25 }, -- swamp grass
+ { itemId = 9640, count = 20 }, -- poisonous slime
+ { itemId = 21194, count = 2 }, -- slime heart
+ },
+ },
+ -- Elemental protection packages
+ ["cloud fabric"] = {
+ text = "energy protection",
+ moneyRequired = 13775,
+ itemList = {
+ { itemId = 9644, count = 20 }, -- wyvern talisman
+ { itemId = 14079, count = 15 }, -- crawler head plating
+ { itemId = 9665, count = 10 }, -- wyrm scale
+ },
+ },
+ ["demon presence"] = {
+ text = "holy protection",
+ moneyRequired = 20250,
+ itemList = {
+ { itemId = 9639, count = 25 }, -- cultish robe
+ { itemId = 9638, count = 25 }, -- cultish mask
+ { itemId = 10304, count = 20 }, -- hellspawn tail
+ },
+ },
+ ["dragon hide"] = {
+ text = "fire protection",
+ moneyRequired = 10850,
+ itemList = {
+ { itemId = 5877, count = 20 }, -- green dragon leather
+ { itemId = 16131, count = 10 }, -- blazing bone
+ { itemId = 11658, count = 5 }, -- draken sulphur
+ },
+ },
+ ["lich shroud"] = {
+ text = "death protection",
+ moneyRequired = 5650,
+ itemList = {
+ { itemId = 11466, count = 25 }, -- flask of embalming fluid
+ { itemId = 22007, count = 20 }, -- gloom wolf fur
+ { itemId = 9660, count = 5 }, -- mystical hourglass
+ },
+ },
+ ["quara scale"] = {
+ text = "ice protection",
+ moneyRequired = 3650,
+ itemList = {
+ { itemId = 10295, count = 25 }, -- winter wolf fur
+ { itemId = 10307, count = 15 }, -- thick fur
+ { itemId = 14012, count = 10 }, -- deepling warts
+ },
+ },
+ ["snake skin"] = {
+ text = "earth protection",
+ moneyRequired = 12550,
+ itemList = {
+ { itemId = 17823, count = 25 }, -- piece of swampling wood
+ { itemId = 9694, count = 20 }, -- snake skin
+ { itemId = 11702, count = 10 }, -- brimstone fangs
+ },
+ },
+}
+
+local function purchaseItems(npc, player, message)
+ local packageData = imbuementPackagesData[message]
+ if packageData and npcHandler:getTopic(player:getId()) == 1 then
+ npcHandler:say("Do you want to buy items for " .. packageData.text .. " imbuement for " .. packageData.moneyRequired .. " gold?", npc, player)
+ npcHandler:setTopic(player:getId(), 2)
+ playerImbuementData[player:getId()] = {
+ moneyRequired = packageData.moneyRequired,
+ itemList = packageData.itemList,
+ }
+ end
+end
+
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+ local playerId = player:getId()
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ local imbuementPackages = "Available imbuement packages: {bash}, {blockade}, {chop}, {epiphany}, {precision}, {slash}. additional attributes: {featherweight}, {strike}, {swiftness}, {vampirism}, {vibrancy}, {void}. elemental damage: {electrify}, {frost}, {reap}, {scorch}, {venom}. elemental protection: {cloud fabric}, {demon presence}, {dragon hide}, {lich shroud}, {quara scale}, {snake skin}."
+ if MsgContains(message, "imbuement packages") then
+ npcHandler:setTopic(playerId, 1)
+ npcHandler:say(imbuementPackages, npc, creature)
+ elseif imbuementPackagesData[message] then
+ purchaseItems(npc, player, message)
+ elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then
+ local success, message = addItemsToShoppingBag(npc, player)
+ if not success then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
+ npcHandler:setTopic(playerId, 1)
+ npcHandler:say(imbuementPackages, npc, player)
+ return
+ end
+
+ playerImbuementData[playerId] = nil
+ npcHandler:say("You have successfully completed your purchase of the items.", npc, player)
+ npcHandler:setTopic(playerId, 1)
+ npcHandler:say(imbuementPackages, npc, creature)
+ end
+end
-keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Currently I have been working selling items for imbuement." })
+npcHandler:setMessage(MESSAGE_GREET, "Hello |PLAYERNAME|, say {imbuement packages} or {trade} for buy imbuement items.")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "See you later |PLAYERNAME| come back soon.")
+npcHandler:setMessage(MESSAGE_FAREWELL, "See you later |PLAYERNAME| come back soon.")
-npcHandler:setMessage(MESSAGE_GREET, "Welcome to Imbuement's shop!")
-npcHandler:setMessage(MESSAGE_FAREWELL, "Good bye and come again.")
-npcHandler:setMessage(MESSAGE_WALKAWAY, "Good bye and come again.")
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
diff --git a/data-otservbr-global/npc/the_lootmonger.lua b/data-otservbr-global/npc/the_lootmonger.lua
index bfeea87da97..7374b7645c2 100644
--- a/data-otservbr-global/npc/the_lootmonger.lua
+++ b/data-otservbr-global/npc/the_lootmonger.lua
@@ -7,7 +7,7 @@ npcConfig.description = internalNpcName
npcConfig.health = 100
npcConfig.maxHealth = npcConfig.health
-npcConfig.walkInterval = 0
+npcConfig.walkInterval = 2000
npcConfig.walkRadius = 2
npcConfig.outfit = {
diff --git a/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua b/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua
index 9de94cbe1e3..4218f522bbb 100644
--- a/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua
+++ b/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua
@@ -1,5 +1,5 @@
local config = {
- enableTemplaes = true,
+ enableTemples = true,
enableDepots = false,
Temples = {
@@ -47,7 +47,7 @@ local adventurersStone = Action()
function adventurersStone.onUse(player, item, fromPosition, target, toPosition, isHotkey)
local playerPos, allowed, townId = player:getPosition(), false
- if config.enableTemplaes then
+ if config.enableTemples then
for _, temple in ipairs(config.Temples) do
if isInRangeIgnoreZ(playerPos, temple.fromPos, temple.toPos) then
allowed, townId = true, temple.townId
@@ -67,7 +67,7 @@ function adventurersStone.onUse(player, item, fromPosition, target, toPosition,
if not allowed then
local enabledLocations = {}
- if config.enableTemplaes then
+ if config.enableTemples then
table.insert(enabledLocations, "temple")
end
if config.enableDepots then
diff --git a/data-otservbr-global/scripts/actions/other/potions.lua b/data-otservbr-global/scripts/actions/other/potions.lua
index ab5b97c8ff6..38c323e978e 100644
--- a/data-otservbr-global/scripts/actions/other/potions.lua
+++ b/data-otservbr-global/scripts/actions/other/potions.lua
@@ -262,7 +262,7 @@ function flaskPotion.onUse(player, item, fromPosition, target, toPosition, isHot
potion.combat:execute(target, Variant(target:getId()))
end
- if not potion.effect then
+ if not potion.effect and target:getPosition() ~= nil then
target:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
end
diff --git a/data-otservbr-global/scripts/actions/quests/dangerous_depth/gnomish_pesticide.lua b/data-otservbr-global/scripts/actions/quests/dangerous_depth/gnomish_pesticide.lua
index 68fe03c67e9..bbc6fe66ede 100644
--- a/data-otservbr-global/scripts/actions/quests/dangerous_depth/gnomish_pesticide.lua
+++ b/data-otservbr-global/scripts/actions/quests/dangerous_depth/gnomish_pesticide.lua
@@ -4,7 +4,7 @@ function dangerousDepthPesticide.onUse(player, item, fromPosition, target, toPos
return true
end
- if not target:isItem() then
+ if not target or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/actions/quests/dangerous_depth/using_crystals.lua b/data-otservbr-global/scripts/actions/quests/dangerous_depth/using_crystals.lua
index e3cb7244b7b..ca95fa99bc9 100644
--- a/data-otservbr-global/scripts/actions/quests/dangerous_depth/using_crystals.lua
+++ b/data-otservbr-global/scripts/actions/quests/dangerous_depth/using_crystals.lua
@@ -408,7 +408,7 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi
return true
end
- if not target:isItem() then
+ if not target or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_minis_feaster.lua b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_minis_feaster.lua
index 5e10645ee23..fc957813d3d 100644
--- a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_minis_feaster.lua
+++ b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_minis_feaster.lua
@@ -84,7 +84,7 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition)
creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. value.requiredLevel .. " or higher.")
return true
end
- if creature:canFightBoss(value.bossName) then
+ if not creature:canFightBoss(value.bossName) then
creature:teleportTo(fromPosition, true)
creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. value.timeToFightAgain .. " hours to face " .. value.bossName .. " again!")
diff --git a/data-otservbr-global/scripts/actions/quests/ferumbras_ascendant/ferumbras_lever.lua b/data-otservbr-global/scripts/actions/quests/ferumbras_ascendant/ferumbras_lever.lua
index 847a22b8440..674d6d77e4e 100644
--- a/data-otservbr-global/scripts/actions/quests/ferumbras_ascendant/ferumbras_lever.lua
+++ b/data-otservbr-global/scripts/actions/quests/ferumbras_ascendant/ferumbras_lever.lua
@@ -50,7 +50,7 @@ function ferumbrasAscendantLever.onUse(player, item, fromPosition, target, toPos
for y = 31477, 31481 do
local playerTile = Tile(Position(x, y, 14)):getTopCreature()
if playerTile and playerTile:isPlayer() then
- if playerTile:canFightBoss("Ferumbras Mortal Shell") then
+ if not playerTile:canFightBoss("Ferumbras Mortal Shell") then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait 5 days to face Ferumbras again!")
item:transform(8912)
return true
diff --git a/data-otservbr-global/scripts/actions/quests/kilmaresh/portal_minis_kilmaresh.lua b/data-otservbr-global/scripts/actions/quests/kilmaresh/portal_minis_kilmaresh.lua
index bb334e678d1..bd54e70fa01 100644
--- a/data-otservbr-global/scripts/actions/quests/kilmaresh/portal_minis_kilmaresh.lua
+++ b/data-otservbr-global/scripts/actions/quests/kilmaresh/portal_minis_kilmaresh.lua
@@ -84,7 +84,7 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition)
creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. value.requiredLevel .. " or higher.")
return true
end
- if creature:canFightBoss(value.bossName) then
+ if not creature:canFightBoss(value.bossName) then
creature:teleportTo(fromPosition, true)
creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. value.timeToFightAgain .. " hours to face " .. value.bossName .. " again!")
diff --git a/data-otservbr-global/scripts/actions/quests/soul_war/reward_soul_war.lua b/data-otservbr-global/scripts/actions/quests/soul_war/reward_soul_war.lua
index b069e43fb69..2ab572b9376 100644
--- a/data-otservbr-global/scripts/actions/quests/soul_war/reward_soul_war.lua
+++ b/data-otservbr-global/scripts/actions/quests/soul_war/reward_soul_war.lua
@@ -18,6 +18,16 @@ local rewards = {
{ id = 34098, name = "Pair of Soulstalkers" },
{ id = 34099, name = "Soulbastion" },
}
+local outfits = { 1322, 1323 }
+
+local function addOutfits(player)
+ if player:getStorageValue(Storage.Quest.U12_40.SoulWar.OutfitReward) < 0 then
+ player:addOutfit(outfits[1], 0)
+ player:addOutfit(outfits[2], 0)
+ player:setStorageValue(Storage.Quest.U12_40.SoulWar.OutfitReward, 1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations you received the Revenant Outfit.")
+ end
+end
local rewardSoulWar = Action()
function rewardSoulWar.onUse(creature, item, fromPosition, target, toPosition, isHotkey)
@@ -31,8 +41,10 @@ function rewardSoulWar.onUse(creature, item, fromPosition, target, toPosition, i
player:addItem(rewardItem.id, 1)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a " .. rewardItem.name .. ".")
player:setStorageValue(Storage.Quest.U12_40.SoulWar.QuestReward, 1)
+ addOutfits(player)
return true
else
+ addOutfits(player)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have already collected your reward")
return false
end
diff --git a/data-otservbr-global/scripts/actions/quests/the_dream_courts/dream_courts_lever.lua b/data-otservbr-global/scripts/actions/quests/the_dream_courts/dream_courts_lever.lua
index b336b0a380c..eef26894389 100644
--- a/data-otservbr-global/scripts/actions/quests/the_dream_courts/dream_courts_lever.lua
+++ b/data-otservbr-global/scripts/actions/quests/the_dream_courts/dream_courts_lever.lua
@@ -25,6 +25,7 @@ local config = {
},
exit = Position(32210, 32035, 13),
}
+local bossToday = config.bossName[os.date("%A")]
local dreamCourtsLever = Action()
function dreamCourtsLever.onUse(player, item, fromPosition, target, toPosition, isHotkey)
@@ -37,7 +38,7 @@ function dreamCourtsLever.onUse(player, item, fromPosition, target, toPosition,
spec:setCheckPosition(config.specPos)
spec:check()
if spec:getPlayers() > 0 then
- player:say("There's someone fighting with " .. config.bossName[os.date("%A")] .. ".", TALKTYPE_MONSTER_SAY)
+ player:say("There's someone fighting with " .. bossToday .. ".", TALKTYPE_MONSTER_SAY)
return true
end
local lever = Lever()
@@ -55,7 +56,7 @@ function dreamCourtsLever.onUse(player, item, fromPosition, target, toPosition,
for _, v in pairs(info) do
local newPlayer = v.creature
if newPlayer then
- newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. config.timeToFightAgain .. " hours to face " .. config.bossName[os.date("%A")] .. " again!")
+ newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. config.timeToFightAgain .. " hours to face " .. bossToday .. " again!")
if newPlayer:getStorageValue(config.storage) > os.time() then
newPlayer:getPosition():sendMagicEffect(CONST_ME_POFF)
end
@@ -68,12 +69,12 @@ function dreamCourtsLever.onUse(player, item, fromPosition, target, toPosition,
lever:checkPositions()
if lever:checkConditions() then
spec:removeMonsters()
- local monster = Game.createMonster(config.bossName[os.date("%A")], config.bossPosition, true, true)
+ local monster = Game.createMonster(bossToday, config.bossPosition, true, true)
if not monster then
return true
end
lever:teleportPlayers()
- lever:setCooldownAllPlayers(config.bossName, os.time() + config.timeToFightAgain * 3600)
+ lever:setCooldownAllPlayers(bossToday, os.time() + config.timeToFightAgain * 3600)
addEvent(function()
local old_players = lever:getInfoPositions()
spec:clearCreaturesCache()
diff --git a/data-otservbr-global/scripts/creaturescripts/quests/a_pirates_tail/ratmiral_death.lua b/data-otservbr-global/scripts/creaturescripts/quests/a_pirates_tail/ratmiral_death.lua
index ea00d6ead07..8d659edbf10 100644
--- a/data-otservbr-global/scripts/creaturescripts/quests/a_pirates_tail/ratmiral_death.lua
+++ b/data-otservbr-global/scripts/creaturescripts/quests/a_pirates_tail/ratmiral_death.lua
@@ -1,7 +1,7 @@
-local event = CreatureEvent("RatmiralBlackwhiskersDeath")
+local ratmiralBlackwhiskersDeath = CreatureEvent("RatmiralBlackwhiskersDeath")
local outfits = { 1371, 1372 }
-function event.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified)
+function ratmiralBlackwhiskersDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified)
if not creature or not creature:getMonster() then
return
end
@@ -17,4 +17,4 @@ function event.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDa
end
end
-event:register()
+ratmiralBlackwhiskersDeath:register()
diff --git a/data-otservbr-global/scripts/custom/movement_trainer_entrance.lua b/data-otservbr-global/scripts/custom/movement_trainer_entrance.lua
index 161c0ffb220..c7d805e8f42 100644
--- a/data-otservbr-global/scripts/custom/movement_trainer_entrance.lua
+++ b/data-otservbr-global/scripts/custom/movement_trainer_entrance.lua
@@ -67,8 +67,8 @@ function trainerEntrance.onStepIn(creature, item, position, fromPosition)
end
calculatingRoom(creature.uid, config.firstRoomPosition, 0, 0)
- Game.createMonster("training machine", creature:getPosition(), true, false)
- Game.createMonster("training machine", creature:getPosition(), true, false)
+ Game.createMonster("Training Machine", creature:getPosition(), true, false)
+ Game.createMonster("Training Machine", creature:getPosition(), true, false)
return true
end
diff --git a/data-otservbr-global/scripts/movements/quests/cults_of_tibia/boss_timer.lua b/data-otservbr-global/scripts/movements/quests/cults_of_tibia/boss_timer.lua
index 9b03b83d74e..1c2f1834270 100644
--- a/data-otservbr-global/scripts/movements/quests/cults_of_tibia/boss_timer.lua
+++ b/data-otservbr-global/scripts/movements/quests/cults_of_tibia/boss_timer.lua
@@ -40,7 +40,7 @@ function bossTimer.onStepIn(creature, item, position, fromPosition)
end
for b = 1, #setting do
if player:getPosition() == Position(setting[b].tpPos) then
- if not player:canFightBoss(setting[b].storage) > os.time() then
+ if not player:canFightBoss(setting[b].boss) then
player:sendCancelMessage("You need to wait for 20 hours to face this boss again.")
player:teleportTo(fromPosition)
return false
diff --git a/data-otservbr-global/scripts/movements/quests/first_dragon/entrance_teleport.lua b/data-otservbr-global/scripts/movements/quests/first_dragon/entrance_teleport.lua
index 642c02f57e4..f0d30494c8c 100644
--- a/data-otservbr-global/scripts/movements/quests/first_dragon/entrance_teleport.lua
+++ b/data-otservbr-global/scripts/movements/quests/first_dragon/entrance_teleport.lua
@@ -57,7 +57,7 @@ function entranceTeleport.onStepIn(creature, item, position, fromPosition)
return true
end
- if player:canFightBoss(setting.bossName) >= os.time() then
+ if not player:canFightBoss(setting.bossName) then
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:teleportTo(fromPosition)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
diff --git a/data-otservbr-global/scripts/movements/quests/forgotten_knowledge/servant_teleport.lua b/data-otservbr-global/scripts/movements/quests/forgotten_knowledge/servant_teleport.lua
index 534373c47fd..42921e1712f 100644
--- a/data-otservbr-global/scripts/movements/quests/forgotten_knowledge/servant_teleport.lua
+++ b/data-otservbr-global/scripts/movements/quests/forgotten_knowledge/servant_teleport.lua
@@ -6,7 +6,7 @@ function servantTeleport.onStepIn(creature, item, position, fromPosition)
return
end
- if player:canFightBoss("LLoyd") then
+ if not player:canFightBoss("LLoyd") then
player:teleportTo(Position(32815, 32872, 13))
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
position:sendMagicEffect(CONST_ME_TELEPORT)
diff --git a/data-otservbr-global/world/custom/otservbr-custom-npc.xml b/data-otservbr-global/world/custom/otservbr-custom-npc.xml
index 6978f4a6bc1..3597ef592c3 100644
--- a/data-otservbr-global/world/custom/otservbr-custom-npc.xml
+++ b/data-otservbr-global/world/custom/otservbr-custom-npc.xml
@@ -48,4 +48,10 @@
+
+
+
+
+
+
diff --git a/data/XML/imbuements.xml b/data/XML/imbuements.xml
index 4fca37e46b9..867bab66f52 100644
--- a/data/XML/imbuements.xml
+++ b/data/XML/imbuements.xml
@@ -242,23 +242,23 @@
-
+
-
+
-
+
-
+
diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua
index 6ff45d87d65..0a78d96c40d 100644
--- a/data/events/scripts/player.lua
+++ b/data/events/scripts/player.lua
@@ -247,8 +247,8 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder,
-- SSA exhaust
local exhaust = {}
if toPosition.x == CONTAINER_POSITION and toPosition.y == CONST_SLOT_NECKLACE and item:getId() == ITEM_STONE_SKIN_AMULET then
- local pid = self:getId()
- if exhaust[pid] then
+ local playerId = self:getId()
+ if exhaust[playerId] then
self:sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED)
return false
end
diff --git a/data/items/items.xml b/data/items/items.xml
index d23468cabf3..ec4ab6f9bbb 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -67826,6 +67826,10 @@
+ -
+
+
+
-
@@ -67964,6 +67968,20 @@
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
-
diff --git a/data/libs/encounters_lib.lua b/data/libs/encounters_lib.lua
index 1707799b8ac..dee7a38c0c1 100644
--- a/data/libs/encounters_lib.lua
+++ b/data/libs/encounters_lib.lua
@@ -184,7 +184,12 @@ function Encounter:spawnMonsters(config)
spawn(monster)
end
if event then
- monster:registerEvent(event)
+ if type(event) == "string" then
+ event = { event }
+ end
+ for _, event in ipairs(event) do
+ monster:registerEvent(event)
+ end
end
if timeLimit then
self:addEvent(function(monsterId)
diff --git a/data/libs/functions/bosslever.lua b/data/libs/functions/bosslever.lua
index a1441d6cb86..7e099ec9183 100644
--- a/data/libs/functions/bosslever.lua
+++ b/data/libs/functions/bosslever.lua
@@ -14,9 +14,11 @@
---@field private playerPositions {pos: Position, teleport: Position}[]
---@field private area {from: Position, to: Position}
---@field private monsters {name: string, pos: Position}[]
+---@field private exitTeleporter Position
---@field private exit Position
---@field private encounter Encounter
---@field private timeoutEvent Event
+---@field private testMode boolean
BossLever = {}
--[[
@@ -65,9 +67,11 @@ setmetatable(BossLever, {
disabled = config.disabled,
playerPositions = config.playerPositions,
onUseExtra = config.onUseExtra or function() end,
+ exitTeleporter = config.exitTeleporter,
exit = config.exit,
area = config.specPos,
monsters = config.monsters or {},
+ testMode = config.testMode,
_position = nil,
_uid = nil,
_aid = nil,
@@ -111,7 +115,7 @@ end
---@param player Player
---@return number
function BossLever:lastEncounterTime(player)
- if not player then
+ if not player or self.testMode then
return 0
end
return player:getBossCooldown(self.name)
@@ -142,7 +146,7 @@ end
function BossLever:onUse(player)
local isParticipant = false
for _, v in ipairs(self.playerPositions) do
- if v.pos == player:getPosition() then
+ if Position(v.pos) == player:getPosition() then
isParticipant = true
end
end
@@ -212,6 +216,7 @@ function BossLever:onUse(player)
lever:teleportPlayers()
if self.encounter then
local encounter = Encounter(self.encounter)
+ encounter:reset()
encounter:start()
end
self:setLastEncounterTime(os.time() + self.timeToFightAgain)
@@ -278,5 +283,9 @@ function BossLever:register()
end
action:register()
BossLever[self.name] = self
+
+ if self.exitTeleporter then
+ SimpleTeleport(self.exitTeleporter, self.exit)
+ end
return true
end
diff --git a/data/libs/functions/player.lua b/data/libs/functions/player.lua
index 3cda2a65b80..ac1706a364f 100644
--- a/data/libs/functions/player.lua
+++ b/data/libs/functions/player.lua
@@ -677,8 +677,5 @@ end
function Player:canFightBoss(bossNameOrId)
local cooldown = self:getBossCooldown(bossNameOrId)
- if cooldown > os.time() then
- return false
- end
- return true
+ return cooldown > os.time() and false or true
end
diff --git a/data/libs/zones_lib.lua b/data/libs/zones_lib.lua
index 833c4d2cd4b..ff37af5b16a 100644
--- a/data/libs/zones_lib.lua
+++ b/data/libs/zones_lib.lua
@@ -41,6 +41,16 @@ function Zone:countMonsters(name)
return count
end
+function Zone:getMonstersByName(name)
+ local monsters = {}
+ for _, monster in ipairs(self:getMonsters()) do
+ if monster:getName():lower() == name:lower() then
+ table.insert(monsters, monster)
+ end
+ end
+ return monsters
+end
+
function Zone:countPlayers(notFlag)
local players = self:getPlayers()
local count = 0
diff --git a/data/scripts/reward_chest/boss_death.lua b/data/scripts/reward_chest/boss_death.lua
index 7b3f992b384..f3a80909c8f 100644
--- a/data/scripts/reward_chest/boss_death.lua
+++ b/data/scripts/reward_chest/boss_death.lua
@@ -1,7 +1,7 @@
local bossDeath = CreatureEvent("BossDeath")
function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
- if not corpse then
+ if not corpse or corpse == 0 then
return true
end
-- Deny summons and players
@@ -14,9 +14,14 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn
-- Make sure it is a boss
if monsterType and monsterType:isRewardBoss() then
if not corpse.isContainer or not corpse:isContainer() then
- logger.warn("[bossDeath.onDeath] Corpse (id: {}) for reward boss {} is not a container.", corpse:getId(), creature:getName())
+ if corpse.getId() then
+ logger.warn("[bossDeath.onDeath] Corpse (id: {}, name: {}) for reward boss {} is not a container.", corpse:getId(), corpse:getName(), creature:getName())
+ else
+ logger.warn("[bossDeath.onDeath] Error to get corpseId from boss: {}", creature:getName())
+ end
+ else
+ corpse:registerReward()
end
- corpse:registerReward()
local bossId = creature:getId()
local rewardId = corpse:getAttribute(ITEM_ATTRIBUTE_DATE)
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 75cec14c30f..c398f07a2b2 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -2016,7 +2016,7 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptrgetName(), target ? target->getName() : "null", damage.primary.value);
+ g_logger().trace("[Combat::applyExtensions] - Applying extensions for {} on {}. Initial damage: {}", caster->getName(), target ? target->getName() : "null", damage.primary.value);
// Critical hit
uint16_t chance = 0;
diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp
index e0df6f8b80f..2f855c6ac15 100644
--- a/src/creatures/creature.cpp
+++ b/src/creatures/creature.cpp
@@ -841,7 +841,7 @@ void Creature::mitigateDamage(const CombatType_t &combatType, BlockType_t &block
if (combatType != COMBAT_MANADRAIN && combatType != COMBAT_LIFEDRAIN && combatType != COMBAT_AGONYDAMAGE) { // Increase mitigate damage
auto originalDamage = damage;
damage -= (damage * getMitigation()) / 100.;
- g_logger().debug("[mitigation] creature: {}, original damage: {}, mitigation damage: {}", getName(), originalDamage, damage);
+ g_logger().trace("[mitigation] creature: {}, original damage: {}, mitigation damage: {}", getName(), originalDamage, damage);
if (damage <= 0) {
damage = 0;
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index bd11cc3394e..1f7f9f088b0 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -592,7 +592,7 @@ void Player::updateInventoryImbuement() {
continue;
}
- g_logger().debug("Decaying imbuement {} from item {} of player {}", imbuement->getName(), item->getName(), getName());
+ g_logger().trace("Decaying imbuement {} from item {} of player {}", imbuement->getName(), item->getName(), getName());
// Calculate the new duration of the imbuement, making sure it doesn't go below 0
uint32_t duration = std::max(0, imbuementInfo.duration - EVENT_IMBUEMENT_INTERVAL / 1000);
// Update the imbuement's duration in the item
@@ -1658,7 +1658,7 @@ void Player::onCreatureAppear(std::shared_ptr creature, bool isLogin)
g_game().checkPlayersRecord();
IOLoginData::updateOnlineStatus(guid, true);
- if (getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL)) {
+ if (getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL) && getVocationId() > VOCATION_NONE) {
for (uint8_t i = 2; i <= 6; i++) {
if (!hasBlessing(i)) {
addBlessing(i, 1);
diff --git a/src/items/functions/item/custom_attribute.cpp b/src/items/functions/item/custom_attribute.cpp
index 596ac55d127..f7cfa925eac 100644
--- a/src/items/functions/item/custom_attribute.cpp
+++ b/src/items/functions/item/custom_attribute.cpp
@@ -18,23 +18,19 @@ CustomAttribute::~CustomAttribute() = default;
// Constructor for int64_t
CustomAttribute::CustomAttribute(const std::string &initStringKey, const int64_t initInt64) :
- stringKey(initStringKey) {
- setValue(initInt64);
+ stringKey(initStringKey), value(initInt64) {
}
// Constructor for string
CustomAttribute::CustomAttribute(const std::string &initStringKey, const std::string &initStringValue) :
- stringKey(initStringKey) {
- setValue(initStringValue);
+ stringKey(initStringKey), value(initStringValue) {
}
// Constructor for double
CustomAttribute::CustomAttribute(const std::string &initStringKey, const double initDoubleValue) :
- stringKey(initStringKey) {
- setValue(initDoubleValue);
+ stringKey(initStringKey), value(initDoubleValue) {
}
// Constructor for boolean
CustomAttribute::CustomAttribute(const std::string &initStringKey, const bool initBoolValue) :
- stringKey(initStringKey) {
- setValue(initBoolValue);
+ stringKey(initStringKey), value(initBoolValue) {
}
const std::string &CustomAttribute::getStringKey() const {
@@ -122,7 +118,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun
g_logger().error("[{}] failed to read string, call function: {}", __FUNCTION__, function);
return false;
}
- setValue(readString);
+ value = readString;
break;
}
case 2: {
@@ -131,7 +127,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun
g_logger().error("[{}] failed to read int64, call function: {}", __FUNCTION__, function);
return false;
}
- setValue(readInt);
+ value = readInt;
break;
}
case 3: {
@@ -140,7 +136,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun
g_logger().error("[{}] failed to read double, call function: {}", __FUNCTION__, function);
return false;
}
- setValue(readDouble);
+ value = readDouble;
break;
}
case 4: {
@@ -149,7 +145,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun
g_logger().error("[{}] failed to read boolean, call function: {}", __FUNCTION__, function);
return false;
}
- setValue(readBoolean);
+ value = readBoolean;
break;
}
default:
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index a2466038c99..6da9e94180e 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -919,7 +919,7 @@ void ProtocolGame::addBless() {
std::ostringstream lostBlesses;
(bless.length() == 0) ? lostBlesses << "You lost all your blessings." : lostBlesses << "You are still blessed with " << bless;
player->sendTextMessage(MESSAGE_EVENT_ADVANCE, lostBlesses.str());
- if (player->getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL)) {
+ if (player->getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL) && player->getVocationId() > VOCATION_NONE) {
for (uint8_t i = 2; i <= 6; i++) {
if (!player->hasBlessing(i)) {
player->addBlessing(i, 1);
@@ -3972,7 +3972,7 @@ void ProtocolGame::sendBlessStatus() {
if (oldProtocol) {
msg.add(blessCount >= 5 ? 0x01 : 0x00);
} else {
- bool glow = (g_configManager().getBoolean(INVENTORY_GLOW) && blessCount >= 5) || player->getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL);
+ bool glow = player->getVocationId() > VOCATION_NONE && ((g_configManager().getBoolean(INVENTORY_GLOW) && blessCount >= 5) || player->getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL));
msg.add(glow ? 1 : 0); // Show up the glowing effect in items if you have all blesses or adventurer's blessing
msg.addByte((blessCount >= 7) ? 3 : ((blessCount >= 5) ? 2 : 1)); // 1 = Disabled | 2 = normal | 3 = green
}
diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp
index 386e804a7a9..69d05070aab 100644
--- a/src/server/network/webhook/webhook.cpp
+++ b/src/server/network/webhook/webhook.cpp
@@ -74,7 +74,7 @@ int Webhook::sendRequest(const char* url, const char* payload, std::string* resp
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Webhook::writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast(response_body));
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_USERAGENT, "canary (https://github.com/Hydractify/canary)");
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "canary (https://github.com/opentibiabr/canary)");
CURLcode res = curl_easy_perform(curl);