From 1fc6c0eeb8c5fbd36a2e5ee32927f21e18d640a0 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Tue, 28 Jan 2025 09:42:33 -0300
Subject: [PATCH 01/18] fix: add missing houses (#3291)
---
data-otservbr-global/world/otservbr-house.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/data-otservbr-global/world/otservbr-house.xml b/data-otservbr-global/world/otservbr-house.xml
index 5786def36f7..b1c4ffd116f 100644
--- a/data-otservbr-global/world/otservbr-house.xml
+++ b/data-otservbr-global/world/otservbr-house.xml
@@ -958,7 +958,7 @@
-
+
@@ -991,4 +991,6 @@
+
+
From e00ec0d1fee1e41f38c2e14d7fcc78344fa1f115 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Tue, 28 Jan 2025 10:07:31 -0300
Subject: [PATCH 02/18] fix: dawnport vocation trial and npc plunderpurse
(#3292)
---
data-otservbr-global/npc/plunderpurse.lua | 6 +--
.../globalevents/dawnport_magic_effect.lua | 14 +++---
.../others/dawnport_vocation_trial.lua | 12 ++---
data-otservbr-global/startup/tables/tile.lua | 48 ++++++++-----------
data-otservbr-global/world/otservbr-npc.xml | 13 ++---
data/items/items.xml | 18 +++++++
6 files changed, 59 insertions(+), 52 deletions(-)
diff --git a/data-otservbr-global/npc/plunderpurse.lua b/data-otservbr-global/npc/plunderpurse.lua
index 4e1d5bd1b10..033c8071836 100644
--- a/data-otservbr-global/npc/plunderpurse.lua
+++ b/data-otservbr-global/npc/plunderpurse.lua
@@ -7,15 +7,15 @@ npcConfig.description = internalNpcName
npcConfig.health = 100
npcConfig.maxHealth = npcConfig.health
-npcConfig.walkInterval = 0
+npcConfig.walkInterval = 2000
npcConfig.walkRadius = 2
npcConfig.outfit = {
lookType = 151,
lookHead = 114,
- lookBody = 131,
+ lookBody = 132,
lookLegs = 0,
- lookFeet = 20,
+ lookFeet = 78,
lookAddons = 1,
}
diff --git a/data-otservbr-global/scripts/globalevents/dawnport_magic_effect.lua b/data-otservbr-global/scripts/globalevents/dawnport_magic_effect.lua
index d2cafef85ff..3f758cef987 100644
--- a/data-otservbr-global/scripts/globalevents/dawnport_magic_effect.lua
+++ b/data-otservbr-global/scripts/globalevents/dawnport_magic_effect.lua
@@ -1,12 +1,12 @@
local positions = {
+ { x = 32054, y = 31889, z = 5 },
{ x = 32056, y = 31889, z = 5 },
- { x = 32056, y = 31892, z = 5 },
- { x = 32063, y = 31900, z = 5 },
- { x = 32066, y = 31900, z = 5 },
- { x = 32074, y = 31892, z = 5 },
- { x = 32074, y = 31889, z = 5 },
- { x = 32063, y = 31881, z = 5 },
- { x = 32066, y = 31881, z = 5 },
+ { x = 32063, y = 31880, z = 5 },
+ { x = 32063, y = 31882, z = 5 },
+ { x = 32073, y = 31889, z = 5 },
+ { x = 32075, y = 31889, z = 5 },
+ { x = 32063, y = 31899, z = 5 },
+ { x = 32063, y = 31901, z = 5 },
}
local dawnportMagicEffect = GlobalEvent("DawnportMagicEffect")
diff --git a/data-otservbr-global/scripts/movements/others/dawnport_vocation_trial.lua b/data-otservbr-global/scripts/movements/others/dawnport_vocation_trial.lua
index faf2c2caee9..9713df78614 100644
--- a/data-otservbr-global/scripts/movements/others/dawnport_vocation_trial.lua
+++ b/data-otservbr-global/scripts/movements/others/dawnport_vocation_trial.lua
@@ -7,7 +7,7 @@ local vocationTrials = {
-- Sorcerer trial
[25005] = {
tutorialId = 5,
- effectPosition = { x = 32050, y = 31891, z = 5 },
+ effectPosition = { x = 32064, y = 31905, z = 5 },
storage = Storage.Dawnport.Sorcerer,
message = "As a sorcerer, you can use the following spells: Magic Patch, Buzz, Scorch.",
vocation = {
@@ -37,7 +37,7 @@ local vocationTrials = {
-- Druid trial
[25006] = {
tutorialId = 6,
- effectPosition = { x = 32064, y = 31905, z = 5 },
+ effectPosition = { x = 32064, y = 31876, z = 5 },
storage = Storage.Dawnport.Druid,
message = "As a druid, you can use these spells: Mud Attack, Chill Out, Magic Patch.",
vocation = {
@@ -67,7 +67,7 @@ local vocationTrials = {
-- Paladin trial
[25007] = {
tutorialId = 4,
- effectPosition = { x = 32078, y = 31891, z = 5 },
+ effectPosition = { x = 32050, y = 31891, z = 5 },
storage = Storage.Dawnport.Paladin,
message = "As a paladin, you can use the following spells: Magic Patch, Arrow Call.",
vocation = {
@@ -98,7 +98,7 @@ local vocationTrials = {
-- Knight trial
[25008] = {
tutorialId = 3,
- effectPosition = { x = 32064, y = 31876, z = 5 },
+ effectPosition = { x = 32078, y = 31891, z = 5 },
storage = Storage.Dawnport.Knight,
message = "As a knight, you can use the following spells: Bruise Bane.",
vocation = {
@@ -259,8 +259,8 @@ function dawnportVocationTrial.onStepIn(creature, item, position, fromPosition)
local trial = vocationTrials[item.actionid]
if trial then
-- Center room position
- local centerPosition = Position(32065, 31891, 5)
- if centerPosition:getDistance(fromPosition) < centerPosition:getDistance(position) then
+ local centerPosition = Position(32063, 31889, 5)
+ if centerPosition:getDistance(fromPosition) >= centerPosition:getDistance(position) then
-- Blocks the vocation trial if same vocation or after level 20
if player:getVocation():getId() == trial.vocation.id or player:getLevel() >= 20 then
return true
diff --git a/data-otservbr-global/startup/tables/tile.lua b/data-otservbr-global/startup/tables/tile.lua
index 5246b290c37..4a80a8f1335 100644
--- a/data-otservbr-global/startup/tables/tile.lua
+++ b/data-otservbr-global/startup/tables/tile.lua
@@ -13,14 +13,18 @@ TileAction = {
[20001] = {
itemId = 416,
itemPos = {
- { x = 32064, y = 31881, z = 5 },
- { x = 32065, y = 31881, z = 5 },
- { x = 32074, y = 31890, z = 5 },
- { x = 32074, y = 31891, z = 5 },
- { x = 32064, y = 31900, z = 5 },
- { x = 32065, y = 31900, z = 5 },
- { x = 32056, y = 31890, z = 5 },
- { x = 32056, y = 31891, z = 5 },
+ { x = 32064, y = 31880, z = 5 },
+ { x = 32065, y = 31880, z = 5 },
+ { x = 32066, y = 31880, z = 5 },
+ { x = 32075, y = 31890, z = 5 },
+ { x = 32075, y = 31891, z = 5 },
+ { x = 32075, y = 31892, z = 5 },
+ { x = 32064, y = 31901, z = 5 },
+ { x = 32065, y = 31901, z = 5 },
+ { x = 32066, y = 31901, z = 5 },
+ { x = 32054, y = 31890, z = 5 },
+ { x = 32054, y = 31891, z = 5 },
+ { x = 32054, y = 31892, z = 5 },
},
},
-- Dark trails quest tile
@@ -61,42 +65,30 @@ TileAction = {
--Sorcerer vocation tile
--Vocation trial: data\scripts\movements\others\dawnport_vocation_trial.lua
[25005] = {
- itemId = 416,
+ itemId = 44640,
itemPos = {
- { x = 32055, y = 31889, z = 5 },
- { x = 32055, y = 31890, z = 5 },
- { x = 32055, y = 31891, z = 5 },
- { x = 32055, y = 31892, z = 5 },
+ { x = 32063, y = 31900, z = 5 },
},
},
--Druid vocation tile
[25006] = {
- itemId = 416,
+ itemId = 44641,
itemPos = {
- { x = 32063, y = 31901, z = 5 },
- { x = 32064, y = 31901, z = 5 },
- { x = 32065, y = 31901, z = 5 },
- { x = 32066, y = 31901, z = 5 },
+ { x = 32063, y = 31881, z = 5 },
},
},
--Paladin vocation tile
[25007] = {
- itemId = 416,
+ itemId = 44639,
itemPos = {
- { x = 32075, y = 31889, z = 5 },
- { x = 32075, y = 31890, z = 5 },
- { x = 32075, y = 31891, z = 5 },
- { x = 32075, y = 31892, z = 5 },
+ { x = 32055, y = 31889, z = 5 },
},
},
--Knight vocation tile
[25008] = {
- itemId = 416,
+ itemId = 44638,
itemPos = {
- { x = 32063, y = 31880, z = 5 },
- { x = 32064, y = 31880, z = 5 },
- { x = 32065, y = 31880, z = 5 },
- { x = 32066, y = 31880, z = 5 },
+ { x = 32074, y = 31889, z = 5 },
},
},
-- Tiles data\scripts\movements\others\dawnport_tiles.lua
diff --git a/data-otservbr-global/world/otservbr-npc.xml b/data-otservbr-global/world/otservbr-npc.xml
index 6d9ed07557e..2166b890643 100644
--- a/data-otservbr-global/world/otservbr-npc.xml
+++ b/data-otservbr-global/world/otservbr-npc.xml
@@ -171,9 +171,6 @@
-
-
-
@@ -282,8 +279,8 @@
-
-
+
+
@@ -1897,7 +1894,7 @@
-
+
@@ -2458,7 +2455,7 @@
-
+
@@ -2879,7 +2876,7 @@
-
+
diff --git a/data/items/items.xml b/data/items/items.xml
index 552f0cb85c8..44476042d8b 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -76639,6 +76639,18 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
-
@@ -76854,6 +76866,12 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+ -
+
+
-
From 7a51fdb71deeec2b50fb6595e0ccce8ad6c55312 Mon Sep 17 00:00:00 2001
From: Felipe <87909998+FelipePaluco@users.noreply.github.com>
Date: Tue, 28 Jan 2025 14:33:54 -0300
Subject: [PATCH 03/18] feat: soul pit arena/animus mastery/soul core (#3230)
SoulPit fighting arena along with Animus Mastery Logic.
---------
Co-authored-by: Pedro Henrique Alves Cruz
---
config.lua.dist | 11 ++
data-otservbr-global/lib/others/load.lua | 1 +
data-otservbr-global/lib/others/soulpit.lua | 174 ++++++++++++++++++
data-otservbr-global/migrations/49.lua | 5 +
.../actions/soulpit/soulpit_arena_exit.lua | 17 ++
.../actions/soulpit/soulpit_entrance.lua | 57 ++++++
.../scripts/quests/soulpit/exalted_core.lua | 92 +++++++++
.../quests/soulpit/ondroploot_soul_core.lua | 58 ++++++
.../scripts/quests/soulpit/soul_prism.lua | 106 +++++++++++
.../quests/soulpit/soulpit_creatureevents.lua | 17 ++
.../scripts/quests/soulpit/soulpit_fight.lua | 163 ++++++++++++++++
.../quests/soulpit/soulpit_intensehex.lua | 23 +++
.../quests/soulpit/soulpit_opressor.lua | 42 +++++
.../quests/soulpit/soulpit_powerless.lua | 21 +++
data/items/items.xml | 10 +
data/libs/functions/lever.lua | 2 +-
data/scripts/lib/register_spells.lua | 26 +++
schema.sql | 1 +
src/config/config_enums.hpp | 4 +
src/config/configmanager.cpp | 4 +
src/creatures/CMakeLists.txt | 1 +
src/creatures/combat/combat.cpp | 7 +-
src/creatures/combat/condition.cpp | 26 +++
src/creatures/combat/spells.cpp | 10 +-
src/creatures/combat/spells.hpp | 2 +-
src/creatures/creatures_definitions.hpp | 3 +
src/creatures/monsters/monster.cpp | 76 ++++++--
src/creatures/monsters/monster.hpp | 21 ++-
src/creatures/monsters/monsters.cpp | 26 +++
src/creatures/monsters/monsters.hpp | 20 +-
.../players/animus_mastery/animus_mastery.cpp | 72 ++++++++
.../players/animus_mastery/animus_mastery.hpp | 43 +++++
src/creatures/players/player.cpp | 43 ++++-
src/creatures/players/player.hpp | 9 +-
src/enums/player_icons.hpp | 1 +
src/game/game.cpp | 11 +-
src/io/functions/iologindata_load_player.cpp | 15 ++
src/io/functions/iologindata_load_player.hpp | 1 +
src/io/functions/iologindata_save_player.cpp | 9 +
src/io/iologindata.cpp | 3 +
src/items/functions/item/item_parse.hpp | 2 +-
src/items/items.hpp | 4 +
.../functions/core/game/game_functions.cpp | 93 ++++++++++
.../functions/core/game/game_functions.hpp | 6 +
.../creatures/monster/monster_functions.cpp | 103 +++++++++++
.../creatures/monster/monster_functions.hpp | 7 +
.../monster/monster_type_functions.cpp | 48 +++++
.../monster/monster_type_functions.hpp | 4 +
.../creatures/player/player_functions.cpp | 44 +++++
.../creatures/player/player_functions.hpp | 4 +
src/server/network/protocol/protocolgame.cpp | 20 +-
51 files changed, 1510 insertions(+), 58 deletions(-)
create mode 100644 data-otservbr-global/lib/others/soulpit.lua
create mode 100644 data-otservbr-global/migrations/49.lua
create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua
create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/exalted_core.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soul_prism.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua
create mode 100644 data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua
create mode 100644 src/creatures/players/animus_mastery/animus_mastery.cpp
create mode 100644 src/creatures/players/animus_mastery/animus_mastery.hpp
diff --git a/config.lua.dist b/config.lua.dist
index 8f7422da26a..10105287115 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -88,6 +88,17 @@ maxItem = 5000
maxContainer = 500
maxContainerDepth = 200
+-- Animus Mastery - SoulPit (Get more info in: https://github.com/opentibiabr/canary/pull/3230)
+-- NOTE: animusMasteryMaxMonsterXpMultiplier is the maximum experience the multiplier can be.
+-- NOTE: animusMasteryMonsterXpMultiplier is the monster experience multiplier that has the animus mastery unlocked.
+-- NOTE: animusMasteryMonstersXpMultiplier is the multiplier for each 'animusMasteryMonstersToIncreaseXpMultiplier' monsters that
+-- the player has the animus mastery unlocked.
+-- NOTE: animusMasteryMonstersToIncreaseXpMultiplier is the amount of monster to increase the experience multiplier by 'animusMasteryMonstersXpMultiplier'.
+animusMasteryMaxMonsterXpMultiplier = 4.0
+animusMasteryMonsterXpMultiplier = 2.0
+animusMasteryMonstersXpMultiplier = 0.1
+animusMasteryMonstersToIncreaseXpMultiplier = 10
+
-- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602)
-- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact".
-- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above.
diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua
index 031c8fb2026..3422819b9f8 100644
--- a/data-otservbr-global/lib/others/load.lua
+++ b/data-otservbr-global/lib/others/load.lua
@@ -1 +1,2 @@
dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua")
+dofile(DATA_DIRECTORY .. "/lib/others/soulpit.lua")
diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua
new file mode 100644
index 00000000000..b677b10fd9c
--- /dev/null
+++ b/data-otservbr-global/lib/others/soulpit.lua
@@ -0,0 +1,174 @@
+SoulPit = {
+ SoulCoresConfiguration = {
+ chanceToGetSameMonsterSoulCore = 15, -- 15%
+ chanceToDropSoulCore = 5, -- 5%
+ chanceToGetOminousSoulCore = 2, -- 2%
+ chanceToDropSoulPrism = 4, -- 4%
+ monsterVariationsSoulCore = {
+ ["Horse"] = "horse soul core (taupe)",
+ ["Brown Horse"] = "horse soul core (brown)",
+ ["Grey Horse"] = "horse soul core (gray)",
+ ["Nomad"] = "nomad soul core (basic)",
+ ["Nomad Blue"] = "nomad soul core (blue)",
+ ["Nomad Female"] = "nomad soul core (female)",
+ ["Purple Butterfly"] = "butterfly soul core (purple)",
+ ["Butterfly"] = "butterfly soul core (blue)",
+ ["Blue Butterfly"] = "butterfly soul core (blue)",
+ ["Red Butterfly"] = "butterfly soul core (red)",
+ },
+ monstersDifficulties = {
+ ["Harmless"] = 1,
+ ["Trivial"] = 2,
+ ["Easy"] = 3,
+ ["Medium"] = 4,
+ ["Hard"] = 5,
+ ["Challenge"] = 6,
+ },
+ },
+ encounter = nil,
+ kickEvent = nil,
+ soulCores = Game.getSoulCoreItems(),
+ requiredLevel = 8,
+ playerPositions = {
+ {
+ pos = Position(32375, 31158, 8),
+ teleport = Position(32373, 31151, 8),
+ effect = CONST_ME_TELEPORT,
+ },
+ {
+ pos = Position(32375, 31159, 8),
+ teleport = Position(32374, 31151, 8),
+ effect = CONST_ME_TELEPORT,
+ },
+ {
+ pos = Position(32375, 31160, 8),
+ teleport = Position(32375, 31151, 8),
+ effect = CONST_ME_TELEPORT,
+ },
+ {
+ pos = Position(32375, 31161, 8),
+ teleport = Position(32376, 31151, 8),
+ effect = CONST_ME_TELEPORT,
+ },
+ {
+ pos = Position(32375, 31162, 8),
+ teleport = Position(32377, 31151, 8),
+ effect = CONST_ME_TELEPORT,
+ },
+ },
+ waves = {
+ [1] = {
+ stacks = {
+ [1] = 7,
+ },
+ },
+ [2] = {
+ stacks = {
+ [1] = 4,
+ [5] = 3,
+ },
+ },
+ [3] = {
+ stacks = {
+ [1] = 5,
+ [15] = 2,
+ },
+ },
+ [4] = {
+ stacks = {
+ [1] = 3,
+ [5] = 3,
+ [40] = 1,
+ },
+ },
+ },
+ effects = {
+ [1] = CONST_ME_TELEPORT,
+ [5] = CONST_ME_ORANGETELEPORT,
+ [15] = CONST_ME_REDTELEPORT,
+ [40] = CONST_ME_PURPLETELEPORT,
+ },
+ possibleAbilities = {
+ "overpowerSoulPit",
+ "enrageSoulPit",
+ "opressorSoulPit",
+ },
+ bossAbilities = {
+ overpowerSoulPit = {
+ criticalChance = 50, -- 50%
+ criticalDamage = 25, -- 25%
+ apply = function(monster)
+ monster:criticalChance(SoulPit.bossAbilities.overpowerSoulPit.criticalChance)
+ monster:criticalDamage(SoulPit.bossAbilities.overpowerSoulPit.criticalDamage)
+ end,
+ },
+ enrageSoulPit = {
+ bounds = {
+ [{ 0.8, 0.6 }] = 0.9, -- 10% damage reduction
+ [{ 0.6, 0.4 }] = 0.75, -- 25% damage reduction
+ [{ 0.4, 0.2 }] = 0.6, -- 40% damage reduction
+ [{ 0.0, 0.2 }] = 0.4, -- 60% damage reduction
+ },
+ apply = function(monster)
+ monster:registerEvent("enrageSoulPit")
+ end,
+ },
+ opressorSoulPit = {
+ spells = {
+ { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 },
+ { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 },
+ { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 },
+ },
+ apply = function(monster)
+ -- Applying spells
+ for _, spell in pairs(SoulPit.bossAbilities.opressorSoulPit.spells) do
+ monster:addAttackSpell(readSpell(spell, monster:getType()))
+ end
+
+ return true
+ end,
+ },
+ },
+ timeToKick = 10 * 60 * 1000, -- 10 minutes
+ checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters.
+ timeToSpawnMonsters = 4 * 1000, -- 4 seconds
+ totalMonsters = 7,
+ obeliskActive = 47379,
+ obeliskInactive = 47367,
+ obeliskPosition = Position(32375, 31157, 8),
+ bossPosition = Position(32376, 31144, 8),
+ exit = Position(32373, 31158, 8),
+ zone = Zone("soulpit"),
+
+ getMonsterVariationNameBySoulCore = function(searchName)
+ for mTypeName, soulCoreName in pairs(SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore) do
+ if soulCoreName == searchName then
+ return mTypeName
+ end
+ end
+
+ return nil
+ end,
+ getSoulCoreMonster = function(name)
+ return name:match("^(.-) soul core")
+ end,
+ onFuseSoulCores = function(player, item, target)
+ local itemName = item:getName()
+ local targetItemName = target:getName()
+
+ if SoulPit.getSoulCoreMonster(itemName) and SoulPit.getSoulCoreMonster(targetItemName) then
+ local randomSoulCore = SoulPit.soulCores[math.random(#SoulPit.soulCores)]
+ player:addItem(randomSoulCore:getId(), 1)
+ player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", randomSoulCore:getName()))
+ item:remove(1)
+ target:remove(1)
+ return true
+ end
+
+ return false
+ end,
+}
+
+SoulPit.zone:addArea(Position(32362, 31132, 8), Position(32390, 31153, 8))
+SoulPit.zone:setRemoveDestination(SoulPit.exit)
diff --git a/data-otservbr-global/migrations/49.lua b/data-otservbr-global/migrations/49.lua
new file mode 100644
index 00000000000..2f2f2460d10
--- /dev/null
+++ b/data-otservbr-global/migrations/49.lua
@@ -0,0 +1,5 @@
+function onUpdateDatabase()
+ logger.info("Updating database to version 49 (feat: animus mastery (soulpit))")
+
+ db.query("ALTER TABLE `players` ADD `animus_mastery` mediumblob DEFAULT NULL;")
+end
diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua
new file mode 100644
index 00000000000..21304d0cfb5
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua
@@ -0,0 +1,17 @@
+local soulpitArenaExitConfig = {
+ arenaExit = Position(32375, 31153, 8),
+ destination = Position(32373, 31158, 8),
+}
+
+local soulpitArenaExit = Action()
+
+function soulpitArenaExit.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if not player then
+ return false
+ end
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ player:teleportTo(soulpitArenaExitConfig.destination)
+end
+
+soulpitArenaExit:position(soulpitArenaExitConfig.arenaExit)
+soulpitArenaExit:register()
diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua
new file mode 100644
index 00000000000..d31f63fe386
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua
@@ -0,0 +1,57 @@
+local config = {
+ entrance = {
+ positions = {
+ Position(32350, 31030, 3),
+ Position(32349, 31030, 3),
+ },
+ destination = Position(32374, 31171, 8),
+ },
+ exit = {
+ position = Position(32374, 31173, 8),
+ destination = Position(32349, 31032, 3),
+ },
+}
+
+local soulpitEntrance = MoveEvent()
+
+function soulpitEntrance.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return true
+ end
+
+ if not config.entrance.destination then
+ return true
+ end
+
+ player:teleportTo(config.entrance.destination)
+ position:sendMagicEffect(CONST_ME_TELEPORT)
+ return true
+end
+
+soulpitEntrance:type("stepin")
+for value in pairs(config.entrance.positions) do
+ soulpitEntrance:position(config.entrance.positions[value])
+end
+soulpitEntrance:register()
+
+local soulpitExit = MoveEvent()
+
+function soulpitExit.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return true
+ end
+
+ if not config.exit then
+ return true
+ end
+
+ player:teleportTo(config.exit.destination)
+ position:sendMagicEffect(CONST_ME_TELEPORT)
+ return true
+end
+
+soulpitExit:type("stepin")
+soulpitExit:position(config.exit.position)
+soulpitExit:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua
new file mode 100644
index 00000000000..dadb7b7fd0d
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua
@@ -0,0 +1,92 @@
+local exaltedCore = Action()
+
+local function getPreviousDifficultyLevel(currentLevel)
+ for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do
+ if value == currentLevel - 1 then
+ return level
+ end
+ end
+ return nil
+end
+
+local function getSoulCoreItemForMonster(monsterName)
+ local lowerMonsterName = monsterName:lower()
+ local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName]
+
+ if soulCoreName then
+ local newSoulCoreId = getItemIdByName(soulCoreName)
+ if newSoulCoreId then
+ return newSoulCoreId
+ end
+ else
+ local newMonsterSoulCore = string.format("%s soul core", monsterName)
+ local newSoulCoreId = getItemIdByName(newMonsterSoulCore)
+ if newSoulCoreId then
+ return newSoulCoreId
+ end
+ end
+
+ return false
+end
+
+function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ local itemName = target:getName()
+ local monsterName = SoulPit.getSoulCoreMonster(itemName)
+
+ if not monsterName then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Exalted Core with a Soul Core.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local monsterType = MonsterType(monsterName)
+ if not monsterType then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local currentDifficulty = monsterType:BestiaryStars()
+ local previousDifficultyLevel = getPreviousDifficultyLevel(currentDifficulty)
+ local previousDifficultyMonsters = nil
+
+ if previousDifficultyLevel then
+ previousDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[previousDifficultyLevel])
+ else
+ previousDifficultyLevel = currentDifficulty
+ previousDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty])
+ end
+
+ if #previousDifficultyMonsters == 0 then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the previous difficulty level.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local newMonsterType = previousDifficultyMonsters[math.random(#previousDifficultyMonsters)]
+ local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName())
+ if not newSoulCoreItem then -- Retry a second time.
+ newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName())
+ if not newSoulCoreItem then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+ end
+
+ if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ player:addItem(newSoulCoreItem, 1)
+ target:remove(1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName()))
+ item:remove(1)
+ player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ return true
+end
+
+exaltedCore:id(37110)
+exaltedCore:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
new file mode 100644
index 00000000000..9ddb479c117
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
@@ -0,0 +1,58 @@
+local callback = EventCallback("MonsterOnDropLootSoulCore")
+
+function callback.monsterOnDropLoot(monster, corpse)
+ if not monster or not corpse then
+ return
+ end
+ local player = Player(corpse:getCorpseOwner())
+ if not player or not player:canReceiveLoot() then
+ return
+ end
+ if monster:getMonsterForgeClassification() ~= FORGE_FIENDISH_MONSTER then
+ return
+ end
+
+ local soulCoreId = nil
+ local trySameMonsterSoulCore = math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetSameMonsterSoulCore
+ local mType = monster:getType()
+ local lootTable = {}
+
+ if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulCore then
+ if trySameMonsterSoulCore then
+ local itemName = monster:getName():lower() .. " soul core"
+ soulCoreId = getItemIdByName(itemName)
+ end
+
+ if not soulCoreId and not trySameMonsterSoulCore then
+ local race = mType:Bestiaryrace()
+ local monstersInCategory = Game.getMonstersByRace(race)
+
+ if monstersInCategory and #monstersInCategory > 0 then
+ local randomMonster = monstersInCategory[math.random(#monstersInCategory)]
+ local itemName = randomMonster:name():lower() .. " soul core"
+ soulCoreId = getItemIdByName(itemName)
+ logger.info("soulcoreId: " .. soulCoreId)
+ end
+ end
+
+ if soulCoreId then
+ lootTable[soulCoreId] = {
+ count = 1,
+ }
+ else
+ return {}
+ end
+ end
+
+ if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulPrism then
+ local soulPrismId = getItemIdByName("soul prism")
+ if soulPrismId then
+ lootTable[soulPrismId] = {
+ count = 1,
+ }
+ end
+ end
+ corpse:addLoot(mType:generateLootRoll({}, lootTable, player))
+end
+
+callback:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua
new file mode 100644
index 00000000000..326bba11e4d
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua
@@ -0,0 +1,106 @@
+local soulPrism = Action()
+
+local function getNextDifficultyLevel(currentLevel)
+ for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do
+ if value == currentLevel + 1 then
+ return level
+ end
+ end
+ return nil
+end
+
+local function getPreviousDifficultyLevel(currentLevel)
+ for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do
+ if value == currentLevel - 1 then
+ return level
+ end
+ end
+ return nil
+end
+
+local function getSoulCoreItemForMonster(monsterName)
+ local lowerMonsterName = monsterName:lower()
+ local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName]
+
+ if soulCoreName then
+ local newSoulCoreId = getItemIdByName(soulCoreName)
+ if newSoulCoreId then
+ return newSoulCoreId
+ end
+ else
+ local newMonsterSoulCore = string.format("%s soul core", monsterName)
+ local newSoulCoreId = getItemIdByName(newMonsterSoulCore)
+ if newSoulCoreId then
+ return newSoulCoreId
+ end
+ end
+
+ return false
+end
+
+function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ local itemName = target:getName()
+ local monsterName = SoulPit.getSoulCoreMonster(itemName)
+
+ if not monsterName then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local monsterType = MonsterType(monsterName)
+ if not monsterType then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local currentDifficulty = monsterType:BestiaryStars()
+ local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty)
+ local nextDifficultyMonsters = nil
+
+ if nextDifficultyLevel then
+ nextDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel])
+ else
+ nextDifficultyLevel = currentDifficulty
+ nextDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty])
+ end
+
+ if #nextDifficultyMonsters == 0 then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)]
+ local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName())
+ if not newSoulCoreItem then -- Retry a second time.
+ newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName())
+ if not newSoulCoreItem then
+ player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+ end
+
+ if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.")
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
+ if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then
+ player:addItem(49163, 1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.")
+ else
+ player:addItem(newSoulCoreItem, 1)
+ target:remove(1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName()))
+ end
+ item:remove(1)
+ player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ return true
+end
+
+soulPrism:id(49164)
+soulPrism:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua
new file mode 100644
index 00000000000..118a190bf51
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua
@@ -0,0 +1,17 @@
+local enrage = CreatureEvent("enrageSoulPit")
+function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
+ if not creature or not creature:isMonster() and creature:getMaster() then
+ return true
+ end
+
+ local healthPercentage = creature:getHealth() / creature:getMaxHealth()
+
+ for bounds, reduction in pairs(SoulPit.bossAbilities.enrageSoulPit.bounds) do
+ if healthPercentage > bounds[2] and healthPercentage <= bounds[1] then
+ return primaryDamage * reduction, primaryType, secondaryDamage * reduction, secondaryType
+ end
+ end
+
+ return primaryDamage, primaryType, secondaryDamage, secondaryType
+end
+enrage:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua
new file mode 100644
index 00000000000..c607a0de9a9
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua
@@ -0,0 +1,163 @@
+SoulPit.zone:blockFamiliars()
+
+local zoneEvent = ZoneEvent(SoulPit.zone)
+function zoneEvent.afterLeave(zone, creature)
+ local player = creature:getPlayer()
+ if not player then
+ return false
+ end
+
+ if table.empty(zone:getPlayers()) then
+ if SoulPit.encounter then
+ SoulPit.encounter:reset()
+ SoulPit.encounter = nil
+ end
+ if SoulPit.kickEvent then
+ stopEvent(SoulPit.kickEvent)
+ SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive)
+ end
+ end
+end
+zoneEvent:register()
+
+local soulPitAction = Action()
+function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if SoulPit.onFuseSoulCores(player, item, target) then
+ return true
+ end
+
+ if target and target:getId() == SoulPit.obeliskActive then
+ creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!")
+ return false
+ end
+ if not target or target:getId() ~= SoulPit.obeliskInactive then
+ return false
+ end
+
+ local isParticipant = false
+ for _, v in ipairs(SoulPit.playerPositions) do
+ if Position(v.pos) == player:getPosition() then
+ isParticipant = true
+ end
+ end
+
+ if not isParticipant then
+ return false
+ end
+
+ local lever = Lever()
+ lever:setPositions(SoulPit.playerPositions)
+ lever:setCondition(function(creature)
+ if not creature or not creature:isPlayer() then
+ return true
+ end
+
+ local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER
+ if isAccountNormal and creature:getLevel() < SoulPit.requiredLevel then
+ local message = string.format("All players need to be level %s or higher.", SoulPit.requiredLevel)
+ creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
+ return false
+ end
+
+ return true
+ end)
+
+ lever:checkPositions()
+ if not lever:checkConditions() then
+ return true
+ end
+
+ item:remove(1)
+
+ if SoulPit.kickEvent then
+ stopEvent(SoulPit.kickEvent)
+ end
+
+ lever:teleportPlayers()
+ SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive)
+ SoulPit.kickEvent = addEvent(function()
+ SoulPit.kickEvent = nil
+ SoulPit.encounter = nil
+ SoulPit.zone:removePlayers()
+ SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive)
+ end, SoulPit.timeToKick)
+
+ local monsterName = string.gsub(item:getName(), " soul core", "")
+ local monsterVariationName = SoulPit.getMonsterVariationNameBySoulCore(item:getName())
+ monsterName = monsterVariationName and monsterVariationName or monsterName
+
+ SoulPit.zone:removeMonsters()
+
+ if SoulPit.encounter ~= nil then
+ SoulPit.encounter:reset()
+ end
+
+ local encounter = Encounter("Soulpit", {
+ zone = SoulPit.zone,
+ })
+
+ function encounter:onReset(position)
+ SoulPit.zone:removeMonsters()
+
+ for _, player in pairs(SoulPit.zone:getPlayers()) do
+ player:addAnimusMastery(monsterName)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName))
+ end
+
+ SoulPit.zone:removePlayers()
+ end
+
+ SoulPit.encounter = encounter
+
+ local function waveStart()
+ for stack, amount in pairs(SoulPit.waves[encounter.currentStage].stacks) do
+ for i = 1, amount do
+ local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition
+ for i = 1, SoulPit.timeToSpawnMonsters / 1000 do
+ encounter:addEvent(function(position)
+ local effect = SoulPit.effects[stack]
+ if effect then
+ position:sendMagicEffect(effect)
+ end
+ end, i * 1000, position)
+ end
+
+ local randomAbility = SoulPit.possibleAbilities[math.random(1, #SoulPit.possibleAbilities)]
+ local chosenBossAbility = SoulPit.bossAbilities[randomAbility]
+
+ encounter:addEvent(function(name, stack, position, bossAbilityName, bossAbility)
+ local monster = Game.createSoulPitMonster(name, position, stack)
+ if not monster then
+ return false
+ end
+
+ if stack ~= 40 then
+ return false
+ end
+
+ bossAbility.apply(monster)
+ end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility)
+ end
+ end
+ end
+
+ for i = 1, #SoulPit.waves do
+ encounter
+ :addStage({
+ start = waveStart,
+ })
+ :autoAdvance({ delay = SoulPit.checkMonstersDelay, monstersKilled = true })
+ end
+
+ encounter:start()
+ encounter:register()
+
+ return true
+end
+
+for _, itemType in pairs(SoulPit.soulCores) do
+ if itemType:getId() ~= 49164 then -- Exclude soul prism
+ soulPitAction:id(itemType:getId())
+ end
+end
+soulPitAction:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua
new file mode 100644
index 00000000000..9dc9f54dfbe
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua
@@ -0,0 +1,23 @@
+local combat = Combat()
+combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_STUN)
+combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE)
+
+local condition = Condition(CONDITION_INTENSEHEX)
+condition:setParameter(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50)
+condition:setParameter(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50)
+condition:setParameter(CONDITION_PARAM_TICKS, 3000)
+combat:addCondition(condition)
+
+local spell = Spell("instant")
+
+function spell.onCastSpell(creature, var)
+ return combat:execute(creature, var)
+end
+
+spell:name("soulpit intensehex")
+spell:words("###940")
+spell:blockWalls(true)
+spell:needTarget(true)
+spell:needLearn(true)
+spell:isAggressive(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua
new file mode 100644
index 00000000000..0b29b4944b5
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua
@@ -0,0 +1,42 @@
+-- ROOT
+local combatRoot = Combat()
+combatRoot:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ROOTS)
+
+local area = createCombatArea(AREA_ROOT_OPRESSOR)
+combatRoot:setArea(area)
+
+local condition = Condition(CONDITION_ROOTED)
+condition:setParameter(CONDITION_PARAM_TICKS, 3000)
+combatRoot:addCondition(condition)
+
+-- FEAR
+
+local combatFear = Combat()
+combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST)
+
+local area = createCombatArea(AREA_FEAR_OPRESSOR)
+combatFear:setArea(area)
+
+local condition = Condition(CONDITION_FEARED)
+condition:setParameter(CONDITION_PARAM_TICKS, 3000)
+combatFear:addCondition(condition)
+
+local spell = Spell("instant")
+
+local combats = { combatRoot, combatFear }
+
+function spell.onCastSpell(creature, var)
+ for _, combat in pairs(combats) do
+ combat:execute(creature, var)
+ end
+
+ return true
+end
+
+spell:name("soulpit opressor")
+spell:words("###938")
+spell:blockWalls(true)
+spell:needTarget(false)
+spell:needLearn(true)
+spell:isAggressive(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua
new file mode 100644
index 00000000000..36af89a9860
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua
@@ -0,0 +1,21 @@
+local combat = Combat()
+combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONHIT)
+combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE)
+
+local condition = Condition(CONDITION_POWERLESS)
+condition:setParameter(CONDITION_PARAM_TICKS, 3000)
+combat:addCondition(condition)
+
+local spell = Spell("instant")
+
+function spell.onCastSpell(creature, var)
+ return combat:execute(creature, var)
+end
+
+spell:name("soulpit powerless")
+spell:words("###939")
+spell:blockWalls(true)
+spell:needTarget(true)
+spell:needLearn(true)
+spell:isAggressive(true)
+spell:register()
diff --git a/data/items/items.xml b/data/items/items.xml
index 44476042d8b..9b228d3882a 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -80328,5 +80328,15 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+ -
+
+
+ -
+
+
diff --git a/data/libs/functions/lever.lua b/data/libs/functions/lever.lua
index 39802af49d2..651b1e9e908 100644
--- a/data/libs/functions/lever.lua
+++ b/data/libs/functions/lever.lua
@@ -172,7 +172,7 @@ function Lever.teleportPlayers(self) -- It will teleport all players to the posi
local player = v.creature
if player then
player:teleportTo(v.teleport)
- player:getPosition():sendMagicEffect(v.effect)
+ player:getPosition():sendMagicEffect(v.effect or CONST_ME_TELEPORT)
self:getTeleportPlayerFunc(player)
end
end
diff --git a/data/scripts/lib/register_spells.lua b/data/scripts/lib/register_spells.lua
index f2e7cacee3d..3d945661ae4 100644
--- a/data/scripts/lib/register_spells.lua
+++ b/data/scripts/lib/register_spells.lua
@@ -399,6 +399,32 @@ CrossBeamArea3X2 = {
{ 0, 3, 0 },
}
+AREA_FEAR_OPRESSOR = {
+ { 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 1 },
+ { 1, 1, 3, 1, 1 },
+ { 1, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0 },
+}
+
+AREA_ROOT_OPRESSOR = {
+ { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
+ { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+ { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
+}
+
-- The numbered-keys represents the damage values, and their table
-- contains the minimum and maximum number of rounds of those damage values.
RANGE = {
diff --git a/schema.sql b/schema.sql
index 6fe1f21cbb8..9d8f3522f69 100644
--- a/schema.sql
+++ b/schema.sql
@@ -149,6 +149,7 @@ CREATE TABLE IF NOT EXISTS `players` (
`forge_dust_level` bigint(21) NOT NULL DEFAULT '100',
`randomize_mount` tinyint(1) NOT NULL DEFAULT '0',
`boss_points` int NOT NULL DEFAULT '0',
+ `animus_mastery` mediumblob DEFAULT NULL,
INDEX `account_id` (`account_id`),
INDEX `vocation` (`vocation`),
CONSTRAINT `players_pk` PRIMARY KEY (`id`),
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 16b91289480..85a685cef3e 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -16,6 +16,10 @@ enum ConfigKey_t : uint16_t {
AIMBOT_HOTKEY_ENABLED,
ALLOW_CHANGEOUTFIT,
ALLOW_RELOAD,
+ ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER,
+ ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER,
+ ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER,
+ ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER,
AUGMENT_INCREASED_DAMAGE_PERCENT,
AUGMENT_POWERFUL_IMPACT_PERCENT,
AUGMENT_STRONG_IMPACT_PERCENT,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index e0eefc98298..af7c72c24bc 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -199,6 +199,9 @@ bool ConfigManager::load() {
loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_A, "transcendanceChanceFormulaA", 0.0127);
loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_B, "transcendanceChanceFormulaB", 0.1070);
loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_C, "transcendanceChanceFormulaC", 0.0073);
+ loadFloatConfig(L, ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER, "animusMasteryMaxMonsterXpMultiplier", 4.0);
+ loadFloatConfig(L, ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER, "animusMasteryMonsterXpMultiplier", 2.0);
+ loadFloatConfig(L, ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER, "animusMasteryMonstersXpMultiplier", 0.1);
loadIntConfig(L, ACTIONS_DELAY_INTERVAL, "timeBetweenActions", 200);
loadIntConfig(L, ADVENTURERSBLESSING_LEVEL, "adventurersBlessingLevel", 21);
@@ -345,6 +348,7 @@ bool ConfigManager::load() {
loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5);
loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10);
loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7);
+ loadIntConfig(L, ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER, "animusMasteryMonstersToIncreaseXpMultiplier", 10);
loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data");
loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global");
diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt
index c87a45a0aa0..b48b9c42844 100644
--- a/src/creatures/CMakeLists.txt
+++ b/src/creatures/CMakeLists.txt
@@ -12,6 +12,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
npcs/npc.cpp
npcs/npcs.cpp
npcs/spawns/spawn_npc.cpp
+ players/animus_mastery/animus_mastery.cpp
players/grouping/familiars.cpp
players/grouping/groups.cpp
players/grouping/guild.cpp
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 6b7667a71a5..273eb3d382d 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -641,6 +641,10 @@ void Combat::CombatHealthFunc(const std::shared_ptr &caster, const std
}
}
+ if (targetPlayer && damage.primary.type == COMBAT_HEALING) {
+ damage.primary.value *= targetPlayer->getBuff(BUFF_HEALINGRECEIVED) / 100.;
+ }
+
damage.damageMultiplier += attackerPlayer->wheel()->getMajorStatConditional("Divine Empowerment", WheelMajor_t::DAMAGE);
g_logger().trace("Wheel Divine Empowerment damage multiplier {}", damage.damageMultiplier);
}
@@ -2250,7 +2254,8 @@ void Combat::applyExtensions(const std::shared_ptr &caster, const std:
}
}
} else if (monster) {
- chance = monster->critChance() * 100;
+ chance = monster->getCriticalChance() * 100;
+ bonus = monster->getCriticalDamage() * 100;
}
bonus += damage.criticalDamage;
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index 4d4796ec2d5..0b769ce6d91 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -237,6 +237,9 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_SOUL:
return ObjectPool::allocateShared(id, type, ticks, buff, subId);
+ case CONDITION_LESSERHEX:
+ case CONDITION_INTENSEHEX:
+ case CONDITION_GREATERHEX:
case CONDITION_ATTRIBUTES:
return ObjectPool::allocateShared(id, type, ticks, buff, subId);
@@ -261,6 +264,7 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_MUTED:
case CONDITION_CHANNELMUTEDTICKS:
case CONDITION_YELLTICKS:
+ case CONDITION_POWERLESS:
case CONDITION_PACIFIED:
return ObjectPool::allocateShared(id, type, ticks, buff, subId);
@@ -464,6 +468,23 @@ std::unordered_set ConditionGeneric::getIcons() const {
case CONDITION_ROOTED:
icons.insert(PlayerIcon::Rooted);
break;
+
+ case CONDITION_LESSERHEX:
+ icons.insert(PlayerIcon::LesserHex);
+ break;
+
+ case CONDITION_INTENSEHEX:
+ icons.insert(PlayerIcon::IntenseHex);
+ break;
+
+ case CONDITION_GREATERHEX:
+ icons.insert(PlayerIcon::GreaterHex);
+ break;
+
+ case CONDITION_POWERLESS:
+ icons.insert(PlayerIcon::Powerless);
+ break;
+
case CONDITION_GOSHNARTAINT:
switch (subId) {
case 1:
@@ -1004,6 +1025,11 @@ bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) {
return true;
}
+ case CONDITION_PARAM_BUFF_HEALINGRECEIVED: {
+ buffsPercent[BUFF_HEALINGRECEIVED] = std::max(0, value);
+ return true;
+ }
+
case CONDITION_PARAM_BUFF_DAMAGEDEALT: {
buffsPercent[BUFF_DAMAGEDEALT] = std::max(0, value);
return true;
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index f522eac86a6..2d611f3bfa6 100644
--- a/src/creatures/combat/spells.cpp
+++ b/src/creatures/combat/spells.cpp
@@ -931,7 +931,7 @@ void Spell::addVocMap(uint16_t vocationId, bool b) {
vocSpellMap[vocationId] = b;
}
-SpellGroup_t Spell::getGroup() {
+SpellGroup_t Spell::getGroup() const {
return group;
}
@@ -1058,6 +1058,10 @@ bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std:
return false;
}
+ if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) {
+ return false;
+ }
+
LuaVariant var;
var.instantName = getName();
std::shared_ptr playerTarget = nullptr;
@@ -1379,6 +1383,10 @@ bool RuneSpell::executeUse(const std::shared_ptr &player, const std::sha
return false;
}
+ if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) {
+ return false;
+ }
+
LuaVariant var;
var.runeName = getName();
diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp
index 56ff1a330de..e7b8a2a6183 100644
--- a/src/creatures/combat/spells.hpp
+++ b/src/creatures/combat/spells.hpp
@@ -150,7 +150,7 @@ class Spell : public BaseSpell {
[[nodiscard]] const VocSpellMap &getVocMap() const;
void addVocMap(uint16_t vocationId, bool b);
- SpellGroup_t getGroup();
+ SpellGroup_t getGroup() const;
void setGroup(SpellGroup_t g);
SpellGroup_t getSecondaryGroup();
void setSecondaryGroup(SpellGroup_t g);
diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp
index dd554c086b3..017a8db7c62 100644
--- a/src/creatures/creatures_definitions.hpp
+++ b/src/creatures/creatures_definitions.hpp
@@ -116,6 +116,7 @@ enum ConditionType_t : uint8_t {
CONDITION_GREATERHEX = 33,
CONDITION_BAKRAGORE = 34,
CONDITION_GOSHNARTAINT = 35,
+ CONDITION_POWERLESS = 36,
// Need the last ever
CONDITION_COUNT
@@ -222,6 +223,7 @@ enum ConditionParam_t {
CONDITION_PARAM_INCREASE_MANADRAINPERCENT = 80,
CONDITION_PARAM_INCREASE_DROWNPERCENT = 81,
CONDITION_PARAM_CHARM_CHANCE_MODIFIER = 82,
+ CONDITION_PARAM_BUFF_HEALINGRECEIVED = 83,
};
enum stats_t {
@@ -1332,6 +1334,7 @@ enum class CreatureIconModifications_t {
Influenced,
Fiendish,
ReducedHealth,
+ ReducedHealthExclamation,
};
enum class CreatureIconQuests_t {
diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp
index 3535cd0897c..c3c5b4975fd 100644
--- a/src/creatures/monsters/monster.cpp
+++ b/src/creatures/monsters/monster.cpp
@@ -48,6 +48,8 @@ Monster::Monster(const std::shared_ptr &mType) :
internalLight = mType->info.light;
hiddenHealth = mType->info.hiddenHealth;
targetDistance = mType->info.targetDistance;
+ attackSpells = mType->info.attackSpells;
+ defenseSpells = mType->info.defenseSpells;
// Register creature events
for (const std::string &scriptName : mType->info.scripts) {
@@ -249,8 +251,20 @@ bool Monster::canSeeInvisibility() const {
return isImmune(CONDITION_INVISIBLE);
}
-uint16_t Monster::critChance() const {
- return mType->info.critChance;
+void Monster::setCriticalDamage(uint16_t damage) {
+ criticalDamage = damage;
+}
+
+uint16_t Monster::getCriticalDamage() const {
+ return criticalDamage;
+}
+
+void Monster::setCriticalChance(uint16_t chance) {
+ criticalChance = chance;
+}
+
+uint16_t Monster::getCriticalChance() const {
+ return mType->info.critChance + criticalChance;
}
uint32_t Monster::getManaCost() const {
@@ -692,7 +706,8 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const {
}
uint64_t Monster::getLostExperience() const {
- return skillLoss ? mType->info.experience : 0;
+ float extraExperience = forgeStack <= 15 ? (forgeStack + 10) / 10 : 28;
+ return skillLoss ? static_cast(std::round(mType->info.experience * extraExperience)) : 0;
}
uint16_t Monster::getLookCorpse() const {
@@ -1132,7 +1147,7 @@ void Monster::doAttacking(uint32_t interval) {
const Position &myPos = getPosition();
const Position &targetPos = attackedCreature->getPosition();
- for (const spellBlock_t &spellBlock : mType->info.attackSpells) {
+ for (const spellBlock_t &spellBlock : attackSpells) {
bool inRange = false;
if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) {
@@ -1184,7 +1199,7 @@ bool Monster::canUseAttack(const Position &pos, const std::shared_ptr
if (isHostile()) {
const Position &targetPos = target->getPosition();
uint32_t distance = std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos));
- for (const spellBlock_t &spellBlock : mType->info.attackSpells) {
+ for (const spellBlock_t &spellBlock : attackSpells) {
if (spellBlock.range != 0 && distance <= spellBlock.range) {
return g_game().isSightClear(pos, targetPos, true);
}
@@ -1279,7 +1294,7 @@ void Monster::onThinkDefense(uint32_t interval) {
bool resetTicks = true;
defenseTicks += interval;
- for (const spellBlock_t &spellBlock : mType->info.defenseSpells) {
+ for (const spellBlock_t &spellBlock : defenseSpells) {
if (spellBlock.speed > defenseTicks) {
resetTicks = false;
continue;
@@ -1329,13 +1344,15 @@ void Monster::onThinkDefense(uint32_t interval) {
}
const auto &summon = Monster::createMonster(summonName);
- if (summon) {
- if (g_game().placeCreature(summon, getPosition(), false, summonForce)) {
- summon->setMaster(static_self_cast(), true);
- g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
- g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
- g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster());
+ if (summon && g_game().placeCreature(summon, getPosition(), false, summonForce)) {
+ if (getSoulPit()) {
+ const auto stack = getForgeStack();
+ summon->setSoulPitStack(stack, true);
}
+ summon->setMaster(static_self_cast(), true);
+ g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
+ g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
+ g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster());
}
}
}
@@ -2199,6 +2216,24 @@ void Monster::setHazardSystemDefenseBoost(bool value) {
hazardDefenseBoost = value;
}
+bool Monster::getSoulPit() const {
+ return soulPit;
+}
+
+void Monster::setSoulPit(bool value) {
+ soulPit = value;
+}
+
+void Monster::setSoulPitStack(uint8_t stack, bool isSummon /* = false */) {
+ const bool isBoss = stack == 40;
+ const CreatureIconModifications_t icon = isBoss ? CreatureIconModifications_t::ReducedHealthExclamation : CreatureIconModifications_t::ReducedHealth;
+ setForgeStack(stack);
+ setIcon("soulpit", CreatureIcon(icon, isBoss ? 0 : stack));
+ setSoulPit(true);
+ setDropLoot(false);
+ setSkillLoss(isBoss && !isSummon);
+}
+
bool Monster::canWalkTo(Position pos, Direction moveDirection) {
pos = getNextPosition(moveDirection, pos);
if (isInSpawnRange(pos)) {
@@ -2523,6 +2558,15 @@ void Monster::getPathSearchParams(const std::shared_ptr &creature, Fin
}
}
+void Monster::applyStacks() {
+ // Change health based in stacks
+ const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f;
+ auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement));
+
+ healthMax = newHealth;
+ health = newHealth;
+}
+
void Monster::configureForgeSystem() {
if (!canBeForgeMonster()) {
return;
@@ -2539,13 +2583,6 @@ void Monster::configureForgeSystem() {
g_game().updateCreatureIcon(static_self_cast());
}
- // Change health based in stacks
- const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f;
- auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement));
-
- healthMax = newHealth;
- health = newHealth;
-
// Event to give Dusts
const std::string &Eventname = "ForgeSystemMonster";
registerCreatureEvent(Eventname);
@@ -2571,6 +2608,7 @@ uint16_t Monster::getForgeStack() const {
void Monster::setForgeStack(uint16_t stack) {
forgeStack = stack;
+ applyStacks();
}
ForgeClassifications_t Monster::getMonsterForgeClassification() const {
diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp
index 3661eea9bef..89305e7f5f7 100644
--- a/src/creatures/monsters/monster.hpp
+++ b/src/creatures/monsters/monster.hpp
@@ -77,7 +77,6 @@ class Monster final : public Creature {
bool isHostile() const;
bool isFamiliar() const;
bool canSeeInvisibility() const override;
- uint16_t critChance() const;
uint32_t getManaCost() const;
RespawnType getRespawnType() const;
void setSpawnMonster(const std::shared_ptr &newSpawnMonster);
@@ -179,6 +178,10 @@ class Monster final : public Creature {
void setHazardSystemDefenseBoost(bool value);
// Hazard end
+ bool getSoulPit() const;
+ void setSoulPit(bool value);
+ void setSoulPitStack(uint8_t stack, bool isSummon = false);
+
void updateTargetList();
void clearTargetList();
void clearFriendList();
@@ -187,6 +190,8 @@ class Monster final : public Creature {
static uint32_t monsterAutoID;
+ void applyStacks();
+
void configureForgeSystem();
bool canBeForgeMonster() const;
@@ -225,6 +230,12 @@ class Monster final : public Creature {
void setDead(bool isDead);
+ void setCriticalChance(uint16_t chance);
+ uint16_t getCriticalChance() const;
+
+ void setCriticalDamage(uint16_t damage);
+ uint16_t getCriticalDamage() const;
+
protected:
void onExecuteAsyncTasks() override;
@@ -258,6 +269,9 @@ class Monster final : public Creature {
uint16_t totalPlayersOnScreen = 0;
+ uint16_t criticalChance = 0;
+ uint16_t criticalDamage = 0;
+
uint32_t attackTicks = 0;
uint32_t targetChangeTicks = 0;
uint32_t defenseTicks = 0;
@@ -276,6 +290,9 @@ class Monster final : public Creature {
std::unordered_map m_reflectElementMap;
+ std::vector attackSpells;
+ std::vector defenseSpells;
+
Position masterPos;
bool isWalkingBack = false;
@@ -290,6 +307,8 @@ class Monster final : public Creature {
bool hazardDamageBoost = false;
bool hazardDefenseBoost = false;
+ bool soulPit = false;
+
bool m_isDead = false;
bool m_isImmune = false;
diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp
index cd33e12e248..fd269da1709 100644
--- a/src/creatures/monsters/monsters.cpp
+++ b/src/creatures/monsters/monsters.cpp
@@ -382,3 +382,29 @@ bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr<
monsters[lowerName] = mType;
return true;
}
+
+std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const {
+ std::vector> monstersByRace;
+ const auto &bestiaryList = g_game().getBestiaryList();
+
+ for (const auto &[raceId, name] : bestiaryList) {
+ const auto &monsterType = g_monsters().getMonsterType(name);
+ if (monsterType && monsterType->info.bestiaryRace == race) {
+ monstersByRace.emplace_back(monsterType);
+ }
+ }
+ return monstersByRace;
+}
+
+std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const {
+ std::vector> monstersByStars;
+ const auto &bestiaryList = g_game().getBestiaryList();
+
+ for (const auto &[raceId, name] : bestiaryList) {
+ const auto &monsterType = g_monsters().getMonsterType(name);
+ if (monsterType && monsterType->info.bestiaryStars == stars) {
+ monstersByStars.emplace_back(monsterType);
+ }
+ }
+ return monstersByStars;
+}
diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp
index 35c4d73050d..146f53e604c 100644
--- a/src/creatures/monsters/monsters.hpp
+++ b/src/creatures/monsters/monsters.hpp
@@ -30,22 +30,6 @@ class Loot {
class BaseSpell;
struct spellBlock_t {
- constexpr spellBlock_t() = default;
- ~spellBlock_t() = default;
- spellBlock_t(const spellBlock_t &other) = delete;
- spellBlock_t &operator=(const spellBlock_t &other) = delete;
- spellBlock_t(spellBlock_t &&other) noexcept :
- spell(std::move(other.spell)),
- chance(other.chance),
- speed(other.speed),
- range(other.range),
- minCombatValue(other.minCombatValue),
- maxCombatValue(other.maxCombatValue),
- combatSpell(other.combatSpell),
- isMelee(other.isMelee) {
- other.spell = nullptr;
- }
-
std::shared_ptr spell = nullptr;
uint32_t chance = 100;
uint32_t speed = 2000;
@@ -94,6 +78,8 @@ class MonsterType {
uint32_t maxSummons = 0;
uint32_t changeTargetSpeed = 0;
+ uint32_t soulCore = 0;
+
std::bitset m_conditionImmunities;
std::bitset m_damageImmunities;
@@ -265,6 +251,8 @@ class Monsters {
std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const;
bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType);
bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = "") const;
+ std::vector> getMonstersByRace(BestiaryType_t race) const;
+ std::vector> getMonstersByBestiaryStars(uint8_t stars) const;
std::unique_ptr scriptInterface;
std::map> monsters;
diff --git a/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp
new file mode 100644
index 00000000000..595c98ac81e
--- /dev/null
+++ b/src/creatures/players/animus_mastery/animus_mastery.cpp
@@ -0,0 +1,72 @@
+/**
+ * Canary - A free and open-source MMORPG server emulator
+ * Copyright (©) 2019-2024 OpenTibiaBR
+ * Repository: https://github.com/opentibiabr/canary
+ * License: https://github.com/opentibiabr/canary/blob/main/LICENSE
+ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors
+ * Website: https://docs.opentibiabr.com/
+ */
+
+// Player.hpp already includes the animus mastery
+#include "creatures/players/player.hpp"
+
+#include "config/configmanager.hpp"
+#include "io/fileloader.hpp"
+#include "utils/tools.hpp"
+
+AnimusMastery::AnimusMastery(Player &player) :
+ m_player(player) {
+ maxMonsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER);
+ monsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER);
+ monstersXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER);
+ monstersAmountToMultiply = std::clamp(g_configManager().getNumber(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER), 1, std::numeric_limits::max());
+}
+
+void AnimusMastery::add(const std::string &addMonsterType) {
+ if (!has(addMonsterType)) {
+ const std::string &lowerMonsterName = asLowerCaseString(addMonsterType);
+ animusMasteries.emplace_back(lowerMonsterName);
+ }
+}
+
+void AnimusMastery::remove(const std::string &removeMonsterType) {
+ const std::string &lowerMonsterName = asLowerCaseString(removeMonsterType);
+ std::erase_if(animusMasteries, [lowerMonsterName](const std::string &monsterType) {
+ return asLowerCaseString(monsterType) == lowerMonsterName;
+ });
+}
+
+bool AnimusMastery::has(const std::string &searchMonsterType) const {
+ const std::string &lowerMonsterName = asLowerCaseString(searchMonsterType);
+ auto it = std::ranges::find(animusMasteries, lowerMonsterName);
+
+ return it != animusMasteries.end();
+}
+
+float AnimusMastery::getExperienceMultiplier() const {
+ uint16_t monsterAmountMultiplier = animusMasteries.size() / monstersAmountToMultiply;
+
+ return std::min(maxMonsterXpMultiplier, 1 + (monsterXpMultiplier + (monsterAmountMultiplier * monstersXpMultiplier)) / 100);
+}
+
+uint16_t AnimusMastery::getPoints() const {
+ return animusMasteries.size();
+}
+
+const std::vector &AnimusMastery::getAnimusMasteries() const {
+ return animusMasteries;
+}
+
+void AnimusMastery::serialize(PropWriteStream &propWriteStream) const {
+ std::ranges::for_each(animusMasteries, [&propWriteStream](const std::string &monsterName) {
+ propWriteStream.writeString(monsterName);
+ });
+}
+
+bool AnimusMastery::unserialize(PropStream &propStream) {
+ std::string monsterName;
+ while (propStream.readString(monsterName)) {
+ animusMasteries.emplace_back(monsterName);
+ }
+ return true;
+}
diff --git a/src/creatures/players/animus_mastery/animus_mastery.hpp b/src/creatures/players/animus_mastery/animus_mastery.hpp
new file mode 100644
index 00000000000..e0b9625b4f2
--- /dev/null
+++ b/src/creatures/players/animus_mastery/animus_mastery.hpp
@@ -0,0 +1,43 @@
+/**
+ * Canary - A free and open-source MMORPG server emulator
+ * Copyright (©) 2019-2024 OpenTibiaBR
+ * Repository: https://github.com/opentibiabr/canary
+ * License: https://github.com/opentibiabr/canary/blob/main/LICENSE
+ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors
+ * Website: https://docs.opentibiabr.com/
+ */
+
+#pragma once
+
+class Player;
+class PropStream;
+class PropWriteStream;
+
+class AnimusMastery {
+public:
+ explicit AnimusMastery(Player &player);
+
+ void add(const std::string &addMonsterType);
+ void remove(const std::string &removeMonsterType);
+
+ bool has(const std::string &searchMonsterType) const;
+
+ float getExperienceMultiplier() const;
+
+ uint16_t getPoints() const;
+
+ const std::vector &getAnimusMasteries() const;
+
+ void serialize(PropWriteStream &propWriteStream) const;
+ bool unserialize(PropStream &propStream);
+
+private:
+ Player &m_player;
+
+ float maxMonsterXpMultiplier = 4.0;
+ float monsterXpMultiplier = 2.0;
+ float monstersXpMultiplier = 0.1;
+ uint16_t monstersAmountToMultiply = 10;
+
+ std::vector animusMasteries;
+};
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index a6fb54646e6..d55cb8f5022 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -19,6 +19,7 @@
#include "creatures/monsters/monster.hpp"
#include "creatures/monsters/monsters.hpp"
#include "creatures/npcs/npc.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
#include "creatures/players/wheel/player_wheel.hpp"
#include "creatures/players/wheel/wheel_gems.hpp"
#include "creatures/players/achievement/player_achievement.hpp"
@@ -71,7 +72,8 @@ Player::Player(std::shared_ptr p) :
lastPing(OTSYS_TIME()),
lastPong(lastPing),
inbox(std::make_shared(ITEM_INBOX)),
- client(std::move(p)) {
+ client(std::move(p)),
+ m_animusMastery(*this) {
m_playerVIP = std::make_unique(*this);
m_wheelPlayer = std::make_unique(*this);
m_playerAchievement = std::make_unique(*this);
@@ -3134,6 +3136,14 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp
exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getFloat(HAZARD_EXP_BONUS_MULTIPLIER))) / 100.;
}
+ const bool handleAnimusMastery = monster && animusMastery().has(monster->getMonsterType()->name);
+ float animusMasteryMultiplier = 0;
+
+ if (handleAnimusMastery) {
+ animusMasteryMultiplier = animusMastery().getExperienceMultiplier();
+ exp *= animusMasteryMultiplier;
+ }
+
experience += exp;
if (sendText) {
@@ -3145,6 +3155,10 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp
}
}
+ if (handleAnimusMastery) {
+ expString = fmt::format("{} (animus mastery bonus {:.1f}%)", expString, (animusMasteryMultiplier - 1) * 100);
+ }
+
TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString + (handleHazardExperience ? " (Hazard)" : ""));
message.position = position;
message.primary.value = exp;
@@ -5854,9 +5868,11 @@ bool Player::onKilledMonster(const std::shared_ptr &monster) {
g_logger().error("[{}] Monster type is null.", __FUNCTION__);
return false;
}
- addHuntingTaskKill(mType);
- addBestiaryKill(mType);
- addBosstiaryKill(mType);
+ if (!monster->getSoulPit()) {
+ addHuntingTaskKill(mType);
+ addBestiaryKill(mType);
+ addBosstiaryKill(mType);
+ }
return false;
}
@@ -10367,6 +10383,15 @@ const std::unique_ptr &Player::title() const {
return m_playerTitle;
}
+// Cyclopedia interface
+std::unique_ptr &Player::cyclopedia() {
+ return m_playerCyclopedia;
+}
+
+const std::unique_ptr &Player::cyclopedia() const {
+ return m_playerCyclopedia;
+}
+
// VIP interface
std::unique_ptr &Player::vip() {
return m_playerVIP;
@@ -10376,13 +10401,13 @@ const std::unique_ptr &Player::vip() const {
return m_playerVIP;
}
-// Cyclopedia
-std::unique_ptr &Player::cyclopedia() {
- return m_playerCyclopedia;
+// Animus Mastery interface
+AnimusMastery &Player::animusMastery() {
+ return m_animusMastery;
}
-const std::unique_ptr &Player::cyclopedia() const {
- return m_playerCyclopedia;
+const AnimusMastery &Player::animusMastery() const {
+ return m_animusMastery;
}
void Player::sendLootMessage(const std::string &message) const {
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 8f99f9d1821..28ef63ec8ef 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -16,7 +16,9 @@
#include "items/cylinder.hpp"
#include "game/movement/position.hpp"
#include "creatures/creatures_definitions.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
+class AnimusMastery;
class House;
class NetworkMessage;
class Weapon;
@@ -1270,7 +1272,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::unique_ptr &title();
const std::unique_ptr &title() const;
- // Player summary interface
+ // Player cyclopedia interface
std::unique_ptr &cyclopedia();
const std::unique_ptr &cyclopedia() const;
@@ -1278,6 +1280,10 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::unique_ptr &vip();
const std::unique_ptr &vip() const;
+ // Player animusMastery interface
+ AnimusMastery &animusMastery();
+ const AnimusMastery &animusMastery() const;
+
void sendLootMessage(const std::string &message) const;
std::shared_ptr getLootPouch();
@@ -1654,6 +1660,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::unique_ptr m_playerCyclopedia;
std::unique_ptr m_playerTitle;
std::unique_ptr m_playerVIP;
+ AnimusMastery m_animusMastery;
std::mutex quickLootMutex;
diff --git a/src/enums/player_icons.hpp b/src/enums/player_icons.hpp
index 7878d9e5037..ecab8a5d8ce 100644
--- a/src/enums/player_icons.hpp
+++ b/src/enums/player_icons.hpp
@@ -42,6 +42,7 @@ enum class PlayerIcon : uint8_t {
GoshnarTaint5 = 25,
NewManaShield = 26,
Agony = 27,
+ Powerless = 28,
// Must always be the last
Count
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 09fa4346773..d755c05bb9e 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -7649,8 +7649,12 @@ void Game::buildMessageAsTarget(
const std::string &damageString
) const {
ss.str({});
- auto attackMsg = damage.critical ? "critical " : "";
- auto article = damage.critical ? "a" : "an";
+ const auto &monster = attacker ? attacker->getMonster() : nullptr;
+ bool handleSoulPit = monster ? monster->getSoulPit() && monster->getForgeStack() == 40 : false;
+
+ std::string attackMsg = damage.critical && !handleSoulPit ? "critical " : "";
+ std::string article = damage.critical && !handleSoulPit ? "a" : "an";
+
ss << "You lose " << damageString;
if (!attacker) {
ss << '.';
@@ -7662,6 +7666,9 @@ void Game::buildMessageAsTarget(
if (damage.extension) {
ss << " " << damage.exString;
}
+ if (handleSoulPit && damage.critical) {
+ ss << " (Soulpit Crit)";
+ }
message.type = MESSAGE_DAMAGE_RECEIVED;
message.text = ss.str();
}
diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp
index 54d7135edf0..9410a5535cc 100644
--- a/src/io/functions/iologindata_load_player.cpp
+++ b/src/io/functions/iologindata_load_player.cpp
@@ -14,6 +14,7 @@
#include "creatures/combat/condition.hpp"
#include "database/database.hpp"
#include "creatures/monsters/monsters.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
#include "creatures/players/achievement/player_achievement.hpp"
#include "creatures/players/cyclopedia/player_badge.hpp"
#include "creatures/players/cyclopedia/player_cyclopedia.hpp"
@@ -275,6 +276,20 @@ void IOLoginDataLoad::loadPlayerConditions(const std::shared_ptr &player
}
}
+void IOLoginDataLoad::loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result) {
+ if (!result || !player) {
+ g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__);
+ return;
+ }
+
+ unsigned long attrSize;
+ const char* attr = result->getStream("animus_mastery", attrSize);
+ PropStream propStream;
+ propStream.init(attr, attrSize);
+
+ player->animusMastery().unserialize(propStream);
+}
+
void IOLoginDataLoad::loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result) {
if (!result || !player) {
g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__);
diff --git a/src/io/functions/iologindata_load_player.hpp b/src/io/functions/iologindata_load_player.hpp
index ca05bdcb1b8..08d31e93072 100644
--- a/src/io/functions/iologindata_load_player.hpp
+++ b/src/io/functions/iologindata_load_player.hpp
@@ -22,6 +22,7 @@ class IOLoginDataLoad : public IOLoginData {
static void loadPlayerExperience(const std::shared_ptr &player, const DBResult_ptr &result);
static void loadPlayerBlessings(const std::shared_ptr &player, const DBResult_ptr &result);
static void loadPlayerConditions(const std::shared_ptr &player, const DBResult_ptr &result);
+ static void loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result);
static void loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result);
static void loadPlayerSkullSystem(const std::shared_ptr &player, const DBResult_ptr &result);
static void loadPlayerSkill(const std::shared_ptr &player, const DBResult_ptr &result);
diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp
index 613fdac1cb0..11888ee6660 100644
--- a/src/io/functions/iologindata_save_player.cpp
+++ b/src/io/functions/iologindata_save_player.cpp
@@ -10,6 +10,7 @@
#include "io/functions/iologindata_save_player.hpp"
#include "config/configmanager.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
#include "creatures/combat/condition.hpp"
#include "creatures/monsters/monsters.hpp"
#include "game/game.hpp"
@@ -246,6 +247,14 @@ bool IOLoginDataSave::savePlayerFirst(const std::shared_ptr &player) {
query << "`conditions` = " << db.escapeBlob(attributes, static_cast(attributesSize)) << ",";
+ // serialize animus mastery
+ PropWriteStream propAnimusMasteryStream;
+ player->animusMastery().serialize(propAnimusMasteryStream);
+ size_t animusMasterySize;
+ const char* animusMastery = propAnimusMasteryStream.getStream(animusMasterySize);
+
+ query << "`animus_mastery` = " << db.escapeBlob(animusMastery, static_cast(animusMasterySize)) << ",";
+
if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
int64_t skullTime = 0;
diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp
index 90c6a99c62e..0df4de64e2b 100644
--- a/src/io/iologindata.cpp
+++ b/src/io/iologindata.cpp
@@ -113,6 +113,9 @@ bool IOLoginData::loadPlayer(const std::shared_ptr &player, const DBResu
// load conditions
IOLoginDataLoad::loadPlayerConditions(player, result);
+ // load animus mastery
+ IOLoginDataLoad::loadPlayerAnimusMastery(player, result);
+
// load default outfit
IOLoginDataLoad::loadPlayerDefaultOutfit(player, result);
diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp
index 6fe6fbcccd3..43a4f79433c 100644
--- a/src/items/functions/item/item_parse.hpp
+++ b/src/items/functions/item/item_parse.hpp
@@ -179,7 +179,7 @@ const phmap::flat_hash_map ItemTypesMap = {
{ "food", ITEM_TYPE_FOOD },
{ "valuable", ITEM_TYPE_VALUABLE },
{ "potion", ITEM_TYPE_POTION },
-
+ { "soulcore", ITEM_TYPE_SOULCORES },
{ "ladder", ITEM_TYPE_LADDER },
{ "dummy", ITEM_TYPE_DUMMY },
};
diff --git a/src/items/items.hpp b/src/items/items.hpp
index b977f0bf7a9..f4eae14df27 100644
--- a/src/items/items.hpp
+++ b/src/items/items.hpp
@@ -415,6 +415,10 @@ class Items {
return items.size();
}
+ std::vector &getItems() {
+ return items;
+ }
+
NameMap nameToItems;
void addLadderId(uint16_t newId) {
diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp
index eec876eee7c..eff01af0313 100644
--- a/src/lua/functions/core/game/game_functions.cpp
+++ b/src/lua/functions/core/game/game_functions.cpp
@@ -66,6 +66,7 @@ void GameFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Game", "createItem", GameFunctions::luaGameCreateItem);
Lua::registerMethod(L, "Game", "createContainer", GameFunctions::luaGameCreateContainer);
Lua::registerMethod(L, "Game", "createMonster", GameFunctions::luaGameCreateMonster);
+ Lua::registerMethod(L, "Game", "createSoulPitMonster", GameFunctions::luaGameCreateSoulPitMonster);
Lua::registerMethod(L, "Game", "createNpc", GameFunctions::luaGameCreateNpc);
Lua::registerMethod(L, "Game", "generateNpc", GameFunctions::luaGameGenerateNpc);
Lua::registerMethod(L, "Game", "createTile", GameFunctions::luaGameCreateTile);
@@ -107,6 +108,11 @@ void GameFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Game", "getSecretAchievements", GameFunctions::luaGameGetSecretAchievements);
Lua::registerMethod(L, "Game", "getPublicAchievements", GameFunctions::luaGameGetPublicAchievements);
Lua::registerMethod(L, "Game", "getAchievements", GameFunctions::luaGameGetAchievements);
+
+ Lua::registerMethod(L, "Game", "getSoulCoreItems", GameFunctions::luaGameGetSoulCoreItems);
+
+ Lua::registerMethod(L, "Game", "getMonstersByRace", GameFunctions::luaGameGetMonstersByRace);
+ Lua::registerMethod(L, "Game", "getMonstersByBestiaryStars", GameFunctions::luaGameGetMonstersByBestiaryStars);
}
// Game
@@ -546,6 +552,41 @@ int GameFunctions::luaGameCreateMonster(lua_State* L) {
return 1;
}
+int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) {
+ // Game.createSoulPitMonster(monsterName, position, [stack = 1, [, extended = false[, force = false[, master = nil]]]])
+ const auto &monster = Monster::createMonster(Lua::getString(L, 1));
+ if (!monster) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ bool isSummon = false;
+ if (lua_gettop(L) >= 6) {
+ if (const auto &master = Lua::getCreature(L, 6)) {
+ monster->setMaster(master, true);
+ isSummon = true;
+ }
+ }
+
+ const Position &position = Lua::getPosition(L, 2);
+ const uint8_t stack = Lua::getNumber(L, 3, 1);
+ const bool extended = Lua::getBoolean(L, 4, false);
+ const bool force = Lua::getBoolean(L, 5, false);
+ if (g_game().placeCreature(monster, position, extended, force)) {
+ monster->setSoulPitStack(stack);
+ monster->onSpawn(position);
+
+ Lua::pushUserdata(L, monster);
+ Lua::setMetatable(L, -1, "Monster");
+ } else {
+ if (isSummon) {
+ monster->setMaster(nullptr);
+ }
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
int GameFunctions::luaGameGenerateNpc(lua_State* L) {
// Game.generateNpc(npcName)
const auto &npc = Npc::createNpc(Lua::getString(L, 1));
@@ -994,3 +1035,55 @@ int GameFunctions::luaGameGetAchievements(lua_State* L) {
}
return 1;
}
+
+int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) {
+ // Game.getSoulCoreItems()
+ std::vector soulCoreItems;
+
+ for (const auto &itemType : Item::items.getItems()) {
+ if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) {
+ soulCoreItems.emplace_back(&itemType);
+ }
+ }
+
+ lua_createtable(L, soulCoreItems.size(), 0);
+
+ int index = 0;
+ for (const auto* itemType : soulCoreItems) {
+ Lua::pushUserdata(L, itemType);
+ Lua::setMetatable(L, -1, "ItemType");
+ lua_rawseti(L, -2, ++index);
+ }
+
+ return 1;
+}
+
+int GameFunctions::luaGameGetMonstersByRace(lua_State* L) {
+ // Game.getMonstersByRace(race)
+ const BestiaryType_t race = Lua::getNumber(L, 1);
+ const auto monstersByRace = g_monsters().getMonstersByRace(race);
+
+ lua_createtable(L, monstersByRace.size(), 0);
+ int index = 0;
+ for (const auto &monsterType : monstersByRace) {
+ Lua::pushUserdata(L, monsterType);
+ Lua::setMetatable(L, -1, "MonsterType");
+ lua_rawseti(L, -2, ++index);
+ }
+ return 1;
+}
+
+int GameFunctions::luaGameGetMonstersByBestiaryStars(lua_State* L) {
+ // Game.getMonstersByBestiaryStars(stars)
+ const uint8_t stars = Lua::getNumber(L, 1);
+ const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars);
+
+ lua_createtable(L, monstersByStars.size(), 0);
+ int index = 0;
+ for (const auto &monsterType : monstersByStars) {
+ Lua::pushUserdata(L, monsterType);
+ Lua::setMetatable(L, -1, "MonsterType");
+ lua_rawseti(L, -2, ++index);
+ }
+ return 1;
+}
diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp
index 6d332face9f..b24b960368c 100644
--- a/src/lua/functions/core/game/game_functions.hpp
+++ b/src/lua/functions/core/game/game_functions.hpp
@@ -46,6 +46,7 @@ class GameFunctions {
static int luaGameCreateItem(lua_State* L);
static int luaGameCreateContainer(lua_State* L);
static int luaGameCreateMonster(lua_State* L);
+ static int luaGameCreateSoulPitMonster(lua_State* L);
static int luaGameGenerateNpc(lua_State* L);
static int luaGameCreateNpc(lua_State* L);
static int luaGameCreateTile(lua_State* L);
@@ -88,4 +89,9 @@ class GameFunctions {
static int luaGameGetSecretAchievements(lua_State* L);
static int luaGameGetPublicAchievements(lua_State* L);
static int luaGameGetAchievements(lua_State* L);
+
+ static int luaGameGetSoulCoreItems(lua_State* L);
+
+ static int luaGameGetMonstersByRace(lua_State* L);
+ static int luaGameGetMonstersByBestiaryStars(lua_State* L);
};
diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp
index 3f5d0c25ea8..7d73d7bbea9 100644
--- a/src/lua/functions/creatures/monster/monster_functions.cpp
+++ b/src/lua/functions/creatures/monster/monster_functions.cpp
@@ -66,6 +66,8 @@ void MonsterFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Monster", "hazardDamageBoost", MonsterFunctions::luaMonsterHazardDamageBoost);
Lua::registerMethod(L, "Monster", "hazardDefenseBoost", MonsterFunctions::luaMonsterHazardDefenseBoost);
+ Lua::registerMethod(L, "Monster", "soulPit", MonsterFunctions::luaMonsterSoulPit);
+
Lua::registerMethod(L, "Monster", "addReflectElement", MonsterFunctions::luaMonsterAddReflectElement);
Lua::registerMethod(L, "Monster", "addDefense", MonsterFunctions::luaMonsterAddDefense);
Lua::registerMethod(L, "Monster", "getDefense", MonsterFunctions::luaMonsterGetDefense);
@@ -73,6 +75,12 @@ void MonsterFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Monster", "isDead", MonsterFunctions::luaMonsterIsDead);
Lua::registerMethod(L, "Monster", "immune", MonsterFunctions::luaMonsterImmune);
+ Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance);
+ Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage);
+
+ Lua::registerMethod(L, "Monster", "addAttackSpell", MonsterFunctions::luaMonsterAddAttackSpell);
+ Lua::registerMethod(L, "Monster", "addDefenseSpell", MonsterFunctions::luaMonsterAddDefenseSpell);
+
CharmFunctions::init(L);
LootFunctions::init(L);
MonsterSpellFunctions::init(L);
@@ -700,6 +708,23 @@ int MonsterFunctions::luaMonsterHazardDefenseBoost(lua_State* L) {
return 1;
}
+int MonsterFunctions::luaMonsterSoulPit(lua_State* L) {
+ // get: monster:soulPit() ; set: monster:soulPit(hazard)
+ const auto &monster = Lua::getUserdataShared(L, 1);
+ const bool soulPit = Lua::getBoolean(L, 2, false);
+ if (monster) {
+ if (lua_gettop(L) == 1) {
+ Lua::pushBoolean(L, monster->getSoulPit());
+ } else {
+ monster->setSoulPit(soulPit);
+ Lua::pushBoolean(L, monster->getSoulPit());
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
int MonsterFunctions::luaMonsterAddReflectElement(lua_State* L) {
// monster:addReflectElement(type, percent)
const auto &monster = Lua::getUserdataShared(L, 1);
@@ -772,3 +797,81 @@ int MonsterFunctions::luaMonsterImmune(lua_State* L) {
Lua::pushBoolean(L, monster->isImmune());
return 1;
}
+
+int MonsterFunctions::luaMonsterCriticalChance(lua_State* L) {
+ // get: monster:criticalChance(); set: monster:criticalChance(critical)
+ const auto &monster = Lua::getUserdataShared(L, 1);
+ const auto critical = Lua::getNumber(L, 2, 0);
+ if (monster) {
+ if (lua_gettop(L) == 1) {
+ Lua::pushBoolean(L, monster->getCriticalChance());
+ } else {
+ monster->setCriticalChance(critical);
+ Lua::pushBoolean(L, monster->getCriticalChance());
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+int MonsterFunctions::luaMonsterCriticalDamage(lua_State* L) {
+ // get: monster:criticalDamage(); set: monster:criticalDamage(damage)
+ const auto &monster = Lua::getUserdataShared(L, 1);
+ const auto damage = Lua::getNumber(L, 2, 0);
+ if (monster) {
+ if (lua_gettop(L) == 1) {
+ Lua::pushBoolean(L, monster->getCriticalDamage());
+ } else {
+ monster->setCriticalDamage(damage);
+ Lua::pushBoolean(L, monster->getCriticalDamage());
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+int MonsterFunctions::luaMonsterAddAttackSpell(lua_State* L) {
+ // monster:addAttackSpell(monsterspell)
+ const auto &monster = Lua::getUserdataShared(L, 1);
+ if (monster) {
+ const auto &spell = Lua::getUserdataShared(L, 2);
+ if (spell) {
+ spellBlock_t sb;
+ const auto &monsterName = monster->getName();
+ if (g_monsters().deserializeSpell(spell, sb, monsterName)) {
+ monster->attackSpells.push_back(std::move(sb));
+ } else {
+ g_logger().warn("Monster: {}, cant load spell: {}", monsterName, spell->name);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+int MonsterFunctions::luaMonsterAddDefenseSpell(lua_State* L) {
+ // monster:addDefenseSpell(monsterspell)
+ const auto &monster = Lua::getUserdataShared(L, 1);
+ if (monster) {
+ const auto &spell = Lua::getUserdataShared(L, 2);
+ if (spell) {
+ spellBlock_t sb;
+ const auto &monsterName = monster->getName();
+ if (g_monsters().deserializeSpell(spell, sb, monsterName)) {
+ monster->defenseSpells.push_back(std::move(sb));
+ } else {
+ g_logger().warn("Monster: {}, Cant load spell: {}", monsterName, spell->name);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp
index 4ba696e941b..a7c6a5269e1 100644
--- a/src/lua/functions/creatures/monster/monster_functions.hpp
+++ b/src/lua/functions/creatures/monster/monster_functions.hpp
@@ -76,8 +76,15 @@ class MonsterFunctions {
static int luaMonsterAddDefense(lua_State* L);
static int luaMonsterGetDefense(lua_State* L);
+ static int luaMonsterSoulPit(lua_State* L);
+
static int luaMonsterIsDead(lua_State* L);
static int luaMonsterImmune(lua_State* L);
+ static int luaMonsterCriticalChance(lua_State* L);
+ static int luaMonsterCriticalDamage(lua_State* L);
+
+ static int luaMonsterAddAttackSpell(lua_State* L);
+ static int luaMonsterAddDefenseSpell(lua_State* L);
friend class CreatureFunctions;
};
diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp
index 8b2f4a5f314..03ade91f273 100644
--- a/src/lua/functions/creatures/monster/monster_type_functions.cpp
+++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp
@@ -151,6 +151,8 @@ void MonsterTypeFunctions::init(lua_State* L) {
Lua::registerMethod(L, "MonsterType", "deathSound", MonsterTypeFunctions::luaMonsterTypedeathSound);
Lua::registerMethod(L, "MonsterType", "variant", MonsterTypeFunctions::luaMonsterTypeVariant);
+ Lua::registerMethod(L, "MonsterType", "getMonstersByRace", MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace);
+ Lua::registerMethod(L, "MonsterType", "getMonstersByBestiaryStars", MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars);
}
void MonsterTypeFunctions::createMonsterTypeLootLuaTable(lua_State* L, const std::vector &lootList) {
@@ -658,6 +660,22 @@ int MonsterTypeFunctions::luaMonsterTypeRaceid(lua_State* L) {
return 1;
}
+int MonsterTypeFunctions::luaMonsterTypeSoulCore(lua_State* L) {
+ // get: monsterType:luaMonsterTypeSoulCore() set: monsterType:luaMonsterTypeSoulCore(id)
+ const auto &monsterType = Lua::getUserdataShared(L, 1);
+ if (monsterType) {
+ if (lua_gettop(L) == 1) {
+ lua_pushnumber(L, monsterType->info.soulCore);
+ } else {
+ monsterType->info.soulCore = Lua::getNumber(L, 2);
+ Lua::pushBoolean(L, true);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
int MonsterTypeFunctions::luaMonsterTypeBestiarytoKill(lua_State* L) {
// get: monsterType:BestiarytoKill() set: monsterType:BestiarytoKill(value)
const auto &monsterType = Lua::getUserdataShared(L, 1);
@@ -1857,3 +1875,33 @@ int MonsterTypeFunctions::luaMonsterTypeVariant(lua_State* L) {
return 1;
}
+
+int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) {
+ // monsterType:getMonstersByRace(race)
+ const BestiaryType_t race = Lua::getNumber(L, 1);
+ const auto monstersByRace = g_monsters().getMonstersByRace(race);
+
+ lua_createtable(L, monstersByRace.size(), 0);
+ int index = 0;
+ for (const auto &monsterType : monstersByRace) {
+ Lua::pushUserdata(L, monsterType);
+ Lua::setMetatable(L, -1, "MonsterType");
+ lua_rawseti(L, -2, ++index);
+ }
+ return 1;
+}
+
+int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) {
+ // monsterType:getMonstersByBestiaryStars(stars)
+ const uint8_t stars = Lua::getNumber(L, 1);
+ const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars);
+
+ lua_createtable(L, monstersByStars.size(), 0);
+ int index = 0;
+ for (const auto &monsterType : monstersByStars) {
+ Lua::pushUserdata(L, monsterType);
+ Lua::setMetatable(L, -1, "MonsterType");
+ lua_rawseti(L, -2, ++index);
+ }
+ return 1;
+}
diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp
index e272ea45cd7..0994edaed23 100644
--- a/src/lua/functions/creatures/monster/monster_type_functions.hpp
+++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp
@@ -131,6 +131,8 @@ class MonsterTypeFunctions {
static int luaMonsterTypeBossRace(lua_State* L);
static int luaMonsterTypeBossRaceId(lua_State* L);
+ static int luaMonsterTypeSoulCore(lua_State* L);
+
static int luaMonsterTypeSoundChance(lua_State* L);
static int luaMonsterTypeSoundSpeedTicks(lua_State* L);
static int luaMonsterTypeAddSound(lua_State* L);
@@ -139,4 +141,6 @@ class MonsterTypeFunctions {
static int luaMonsterTypeCritChance(lua_State* L);
static int luaMonsterTypeVariant(lua_State* L);
+ static int luaMonsterTypeGetMonstersByRace(lua_State* L);
+ static int luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L);
};
diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp
index 46fdebd016e..97d3a07f876 100644
--- a/src/lua/functions/creatures/player/player_functions.cpp
+++ b/src/lua/functions/creatures/player/player_functions.cpp
@@ -15,6 +15,7 @@
#include "creatures/creature.hpp"
#include "creatures/interactions/chat.hpp"
#include "creatures/monsters/monsters.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
#include "creatures/players/achievement/player_achievement.hpp"
#include "creatures/players/cyclopedia/player_cyclopedia.hpp"
#include "creatures/players/cyclopedia/player_title.hpp"
@@ -404,6 +405,10 @@ void PlayerFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Player", "removeIconBakragore", PlayerFunctions::luaPlayerRemoveIconBakragore);
Lua::registerMethod(L, "Player", "sendCreatureAppear", PlayerFunctions::luaPlayerSendCreatureAppear);
+ Lua::registerMethod(L, "Player", "addAnimusMastery", PlayerFunctions::luaPlayerAddAnimusMastery);
+ Lua::registerMethod(L, "Player", "removeAnimusMastery", PlayerFunctions::luaPlayerRemoveAnimusMastery);
+ Lua::registerMethod(L, "Player", "hasAnimusMastery", PlayerFunctions::luaPlayerHasAnimusMastery);
+
GroupFunctions::init(L);
GuildFunctions::init(L);
MountFunctions::init(L);
@@ -4870,3 +4875,42 @@ int PlayerFunctions::luaPlayerSendCreatureAppear(lua_State* L) {
Lua::pushBoolean(L, true);
return 1;
}
+
+int PlayerFunctions::luaPlayerAddAnimusMastery(lua_State* L) {
+ auto player = Lua::getUserdataShared(L, 1);
+ if (!player) {
+ Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
+ return 1;
+ }
+
+ const std::string &monsterType = Lua::getString(L, 2);
+ player->animusMastery().add(monsterType);
+
+ return 1;
+}
+int PlayerFunctions::luaPlayerRemoveAnimusMastery(lua_State* L) {
+ auto player = Lua::getUserdataShared(L, 1);
+ if (!player) {
+ Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
+ return 1;
+ }
+
+ const std::string &monsterType = Lua::getString(L, 2);
+ player->animusMastery().remove(monsterType);
+
+ return 1;
+}
+int PlayerFunctions::luaPlayerHasAnimusMastery(lua_State* L) {
+ auto player = Lua::getUserdataShared(L, 1);
+ if (!player) {
+ Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
+ return 1;
+ }
+
+ const std::string &monsterType = Lua::getString(L, 2);
+
+ bool has = player->animusMastery().has(monsterType);
+ Lua::pushBoolean(L, has);
+
+ return 1;
+}
diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp
index 8e4f1381b7b..54e57d56155 100644
--- a/src/lua/functions/creatures/player/player_functions.hpp
+++ b/src/lua/functions/creatures/player/player_functions.hpp
@@ -384,5 +384,9 @@ class PlayerFunctions {
static int luaPlayerSendCreatureAppear(lua_State* L);
+ static int luaPlayerAddAnimusMastery(lua_State* L);
+ static int luaPlayerRemoveAnimusMastery(lua_State* L);
+ static int luaPlayerHasAnimusMastery(lua_State* L);
+
friend class CreatureFunctions;
};
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index 9d385be58c7..4cdc5ecbdf1 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -19,6 +19,7 @@
#include "creatures/monsters/monster.hpp"
#include "creatures/monsters/monsters.hpp"
#include "creatures/npcs/npc.hpp"
+#include "creatures/players/animus_mastery/animus_mastery.hpp"
#include "creatures/players/achievement/player_achievement.hpp"
#include "creatures/players/cyclopedia/player_badge.hpp"
#include "creatures/players/cyclopedia/player_cyclopedia.hpp"
@@ -2390,9 +2391,13 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) {
newmsg.addByte(currentLevel);
- newmsg.add(0); // Animus Mastery Bonus
- newmsg.add(0); // Animus Mastery Points
-
+ if (player->animusMastery().has(mtype->name)) {
+ newmsg.add(static_cast(std::round((player->animusMastery().getExperienceMultiplier() - 1) * 1000))); // Animus Mastery Bonus
+ newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points
+ } else {
+ newmsg.add(0);
+ newmsg.add(0);
+ }
newmsg.add(killCounter);
newmsg.add(mtype->info.bestiaryFirstUnlock);
@@ -3026,10 +3031,15 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) {
newmsg.addByte(0);
}
- newmsg.add(0); // Creature Animous Bonus
+ const auto monsterType = g_monsters().getMonsterType(it_.second);
+ if (monsterType && player->animusMastery().has(it_.second)) {
+ newmsg.add(static_cast(std::round((player->animusMastery().getExperienceMultiplier() - 1) * 1000))); // Animus Mastery Bonus
+ } else {
+ newmsg.add(0);
+ }
}
- newmsg.add(0); // Animus Mastery Points
+ newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points
writeToOutputBuffer(newmsg);
}
From 51fb9703a5305ace45d4bc6fba6b1df8402ae004 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Tue, 28 Jan 2025 18:48:09 -0300
Subject: [PATCH 04/18] fix: add soulpit includes on visual studio solution
(#3296)
---
vcproj/canary.vcxproj | 2 ++
1 file changed, 2 insertions(+)
diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj
index 6436dd704a9..38342c2b885 100644
--- a/vcproj/canary.vcxproj
+++ b/vcproj/canary.vcxproj
@@ -45,6 +45,7 @@
+
@@ -260,6 +261,7 @@
+
From 018f3b79e881cd86661713c1393ca3360f52294d Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Wed, 29 Jan 2025 11:29:52 -0300
Subject: [PATCH 05/18] fix: warning on transformItem (#3302)
---
data/libs/functions/position.lua | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/data/libs/functions/position.lua b/data/libs/functions/position.lua
index 27923eeb268..ee0dad49967 100644
--- a/data/libs/functions/position.lua
+++ b/data/libs/functions/position.lua
@@ -302,7 +302,9 @@ function Position:transformItem(itemId, itemTransform, effect)
local thing = Tile(self):getItemById(itemId)
if thing then
thing:transform(itemTransform)
- Position(self):sendMagicEffect(effect)
+ if effect then
+ Position(self):sendMagicEffect(effect)
+ end
end
end
From ede9b6ac1be514f871c9a6361db93354de5290f9 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 14:17:03 -0300
Subject: [PATCH 06/18] fix: ferumbras ascension quest bosses (#3297)
---
.../ferumbras_ascension/actions_mazoran.lua | 71 +++++-----------
.../ferumbras_ascension/actions_plagirath.lua | 71 +++++-----------
.../ferumbras_ascension/actions_ragiaz.lua | 81 ++++++------------
.../ferumbras_ascension/actions_razzagorn.lua | 68 +++++----------
.../ferumbras_ascension/actions_shulgrax.lua | 70 +++++-----------
.../ferumbras_ascension/actions_tarbaz.lua | 67 +++++----------
.../ferumbras_ascension/actions_zamulosh.lua | 83 +++++++------------
7 files changed, 153 insertions(+), 358 deletions(-)
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua
index aa564b94fd6..83ab13b7613 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua
@@ -1,57 +1,24 @@
local config = {
- centerRoom = Position(33584, 32689, 14),
- BossPosition = Position(33584, 32689, 14),
+ boss = {
+ name = "Mazoran",
+ position = Position(33584, 32689, 14),
+ },
+
+ timeToDefeat = 30 * 60,
playerPositions = {
- Position(33593, 32644, 14),
- Position(33593, 32645, 14),
- Position(33593, 32646, 14),
- Position(33593, 32647, 14),
- Position(33593, 32648, 14),
+ { pos = Position(33593, 32644, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33593, 32645, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33593, 32646, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33593, 32647, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33593, 32648, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT },
},
- newPosition = Position(33585, 32693, 14),
+ specPos = {
+ from = Position(33570, 32677, 14),
+ to = Position(33597, 32700, 14),
+ },
+ exit = Position(33319, 32318, 13),
}
-local leverMazoran = Action()
-
-function leverMazoran.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33593, 32644, 14) then
- item:transform(8912)
- return true
- end
- end
-
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Mazoran", Position(33572, 32679, 14), Position(33599, 32701, 14)) then
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Mazoran.")
- return true
- end
- end
- Game.createMonster("Mazoran", config.BossPosition, true, true)
- for y = 32644, 32648 do
- local playerTile = Tile(Position(33593, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.MazoranTimer, os.time() + os.time() + 60 * 60 * 2 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33572, 32679, 14), Position(33599, 32701, 14), Position(33319, 32318, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverMazoran:uid(1025)
-leverMazoran:register()
+local lever = BossLever(config)
+lever:position(Position(33593, 32643, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua
index 82d5535f8e6..a29930503a8 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua
@@ -1,57 +1,24 @@
local config = {
- centerRoom = Position(33172, 31501, 13),
- BossPosition = Position(33172, 31501, 13),
+ boss = {
+ name = "Plagirath",
+ position = Position(33172, 31501, 13),
+ },
+
+ timeToDefeat = 30 * 60,
playerPositions = {
- Position(33229, 31500, 13),
- Position(33229, 31501, 13),
- Position(33229, 31502, 13),
- Position(33229, 31503, 13),
- Position(33229, 31504, 13),
+ { pos = Position(33229, 31500, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33229, 31501, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33229, 31502, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33229, 31503, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33229, 31504, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33159, 31488, 13),
+ to = Position(33190, 31515, 13),
},
- newPosition = Position(33173, 31504, 13),
+ exit = Position(33319, 32318, 13),
}
-local leverPlagirath = Action()
-
-function leverPlagirath.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33229, 31500, 13) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer) >= 1 then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Plagirath.")
- return true
- end
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Plagirath.")
- return true
- end
- end
- Game.createMonster("Plagirath", config.BossPosition, true, true)
- for y = 31500, 31504 do
- local playerTile = Tile(Position(33229, y, 13)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer, os.time() + 60 * 60 * 24 * 2)
- end
- end
- Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer, 1)
- addEvent(clearForgotten, 30 * 60 * 1000, Position(33159, 31491, 13), Position(33185, 31513, 13), Position(33319, 32318, 13), Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer)
- item:transform(8912)
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverPlagirath:uid(1022)
-leverPlagirath:register()
+local lever = BossLever(config)
+lever:position(Position(33229, 31499, 13))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua
index e63df82d82d..36533dbdb33 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua
@@ -1,59 +1,30 @@
local config = {
- centerRoom = Position(33481, 32334, 13),
- BossPosition = Position(33481, 32334, 13),
- newPosition = Position(33482, 32339, 13),
- deathDragons = {
- Position(33476, 32331, 13),
- Position(33476, 32340, 13),
- Position(33487, 32340, 13),
- Position(33488, 32331, 13),
+ boss = {
+ name = "Ragiaz",
+ position = Position(33481, 32334, 13),
},
-}
-
-local leverRagiaz = Action()
-
-function leverRagiaz.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33456, 32356, 13) then
- item:transform(8912)
- return true
- end
- end
-
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Ragiaz", Position(33472, 32323, 13), Position(33493, 32347, 13)) then
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Ragiaz.")
- return true
- end
- end
- Game.createMonster("Ragiaz", config.BossPosition, true, true)
- for d = 1, #config.deathDragons do
- Game.createMonster("Death Dragon", config.deathDragons[d], true, true)
- end
- for x = 33456, 33460 do
- local playerTile = Tile(Position(x, 32356, 13)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RagiazTimer, os.time() + 60 * 60 * 2 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33472, 32323, 13), Position(33493, 32347, 13), Position(33319, 32318, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
- return true
-end
+ timeToDefeat = 30 * 60,
+ playerPositions = {
+ { pos = Position(33456, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33457, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33458, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33459, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33460, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT },
+ },
+ monsters = {
+ { name = "Death Dragon", pos = Position(33476, 32331, 13) },
+ { name = "Death Dragon", pos = Position(33476, 32340, 13) },
+ { name = "Death Dragon", pos = Position(33487, 32340, 13) },
+ { name = "Death Dragon", pos = Position(33488, 32331, 13) },
+ },
+ specPos = {
+ from = Position(33468, 32319, 13),
+ to = Position(33495, 32347, 13),
+ },
+ exit = Position(33319, 32318, 13),
+}
-leverRagiaz:uid(1023)
-leverRagiaz:register()
+local lever = BossLever(config)
+lever:position(Position(33455, 32356, 13))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua
index fe5b4e06ed9..ca4f464a1b1 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua
@@ -1,50 +1,24 @@
local config = {
- centerRoom = Position(33422, 32467, 14),
- BossPosition = Position(33422, 32467, 14),
- newPosition = Position(33419, 32467, 14),
-}
-
-local leverRazzagorn = Action()
+ boss = {
+ name = "Razzagorn",
+ position = Position(33422, 32467, 14),
+ },
-function leverRazzagorn.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33386, 32455, 14) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer) >= 1 then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Razzagorn.")
- return true
- end
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Razzagorn.")
- return true
- end
- end
- Game.createMonster("Razzagorn", config.BossPosition, true, true)
- for x = 33386, 33390 do
- local playerTile = Tile(Position(x, 32455, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer, os.time() + 60 * 60 * 2 * 24)
- end
- end
- Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer, 1)
- addEvent(clearForgotten, 30 * 60 * 1000, Position(33408, 32454, 14), Position(33440, 32480, 14), Position(33319, 32318, 13), Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer)
- item:transform(8912)
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
+ timeToDefeat = 30 * 60,
+ playerPositions = {
+ { pos = Position(33386, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33387, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33388, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33389, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(33390, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33407, 32453, 14),
+ to = Position(33439, 32481, 14),
+ },
+ exit = Position(33319, 32318, 13),
+}
-leverRazzagorn:uid(1024)
-leverRazzagorn:register()
+local lever = BossLever(config)
+lever:position(Position(33385, 32455, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua
index 2b1792a9261..a9106ce224d 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua
@@ -1,56 +1,24 @@
local config = {
- centerRoom = Position(33485, 32786, 13),
- BossPosition = Position(33485, 32786, 13),
+ boss = {
+ name = "Shulgrax",
+ position = Position(33485, 32786, 13),
+ },
+
+ timeToDefeat = 30 * 60,
playerPositions = {
- Position(33434, 32785, 13),
- Position(33434, 32786, 13),
- Position(33434, 32787, 13),
- Position(33434, 32788, 13),
- Position(33434, 32789, 13),
+ { pos = Position(33434, 32785, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33434, 32786, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33434, 32787, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33434, 32788, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33434, 32789, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33474, 32775, 13),
+ to = Position(33496, 32798, 13),
},
- newPosition = Position(33485, 32790, 13),
+ exit = Position(33319, 32318, 13),
}
-local leverShulgrax = Action()
-
-function leverShulgrax.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33434, 32785, 13) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Shulgrax", Position(33473, 32776, 13), Position(33496, 32798, 13)) then
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Shulgrax.")
- return true
- end
- end
- Game.createMonster("Shulgrax", config.BossPosition, true, true)
- for y = 32785, 32789 do
- local playerTile = Tile(Position(33434, y, 13)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.ShulgraxTimer, os.time() + 60 * 60 * 2 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33473, 32776, 13), Position(33496, 32798, 13), Position(33319, 32318, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverShulgrax:uid(1028)
-leverShulgrax:register()
+local lever = BossLever(config)
+lever:position(Position(33434, 32784, 13))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua
index 7423b8f3820..135b5f4940c 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua
@@ -1,49 +1,24 @@
local config = {
- centerRoom = Position(33459, 32844, 11),
- BossPosition = Position(33459, 32844, 11),
- newPosition = Position(33459, 32848, 11),
-}
-
-local leverTarbaz = Action()
+ boss = {
+ name = "Tarbaz",
+ position = Position(33459, 32844, 11),
+ },
-function leverTarbaz.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33418, 32849, 11) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Tarbaz", Position(33446, 32833, 11), Position(33515, 32875, 12)) then
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Tarbaz.")
- return true
- end
- end
- Game.createMonster("Tarbaz", config.BossPosition, true, true)
- for y = 32849, 32853 do
- local playerTile = Tile(Position(33418, y, 11)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TarbazTimer, os.time() + 60 * 60 * 2 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33446, 32833, 11), Position(33515, 32875, 12), Position(33319, 32318, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
+ timeToDefeat = 30 * 60,
+ playerPositions = {
+ { pos = Position(33418, 32849, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33418, 32850, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33418, 32851, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33418, 32852, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33418, 32853, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33447, 32832, 11),
+ to = Position(33473, 32856, 11),
+ },
+ exit = Position(33319, 32318, 13),
+}
-leverTarbaz:uid(1027)
-leverTarbaz:register()
+local lever = BossLever(config)
+lever:position(Position(33418, 32848, 11))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua
index fc3e34a8219..9c62e19f19c 100644
--- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua
+++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua
@@ -1,60 +1,33 @@
local config = {
- centerRoom = Position(33643, 32756, 11),
- BossPosition = Position(33643, 32756, 11),
- newPosition = Position(33644, 32760, 11),
- zamuloshSummons = {
- Position(33642, 32756, 11),
- Position(33642, 32756, 11),
- Position(33642, 32756, 11),
- Position(33644, 32756, 11),
- Position(33644, 32756, 11),
- Position(33644, 32756, 11),
+ boss = {
+ name = "Zamulosh",
+ position = Position(33643, 32756, 11),
},
-}
-
-local leverZamulosh = Action()
-function leverZamulosh.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33680, 32741, 11) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Zamulosh", Position(33634, 32749, 11), Position(33654, 32765, 11)) then
- local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isPlayer() then
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Zamulosh.")
- return true
- end
- end
- Game.createMonster("Zamulosh", config.BossPosition, true, true)
- for d = 1, #config.zamuloshSummons do
- Game.createMonster("Zamulosh3", config.zamuloshSummons[d], true, true)
- end
- for y = 32741, 32745 do
- local playerTile = Tile(Position(33680, y, 11)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.ZamuloshTimer, os.time() + 60 * 60 * 2 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33634, 32749, 11), Position(33654, 32765, 11), Position(33319, 32318, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
+ timeToDefeat = 30 * 60,
+ playerPositions = {
+ { pos = Position(33680, 32741, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33680, 32742, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33680, 32743, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33680, 32744, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT },
+ { pos = Position(33680, 32745, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33632, 32747, 11),
+ to = Position(33654, 32765, 11),
+ },
+ exit = Position(33319, 32318, 13),
+}
- return true
-end
+local zamuloshSummons = {
+ Position(33642, 32756, 11),
+ Position(33642, 32756, 11),
+ Position(33642, 32756, 11),
+ Position(33644, 32756, 11),
+ Position(33644, 32756, 11),
+ Position(33644, 32756, 11),
+}
-leverZamulosh:uid(1026)
-leverZamulosh:register()
+local lever = BossLever(config)
+lever:position(Position(33680, 32740, 11))
+lever:register()
From 69e3b155010eed640b3b821a89f9772e7592223d Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 14:36:18 -0300
Subject: [PATCH 07/18] fix: forgotten knowledge quest bosses (#3298)
---
.../actions_dragonking_zyrtarch.lua | 76 ++++++----------
.../actions_frozen_horror.lua | 8 +-
.../actions_lady_tenebris.lua | 65 ++++++--------
.../forgotten_knowledge/actions_lloyd.lua | 74 ++++++---------
.../actions_the_last_lore_keeper.lua | 89 +++++++++----------
.../actions_the_thorn_knight.lua | 67 ++++++--------
.../actions_the_time_guardian.lua | 68 +++++---------
7 files changed, 174 insertions(+), 273 deletions(-)
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua
index 426177864b2..99585188b91 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua
@@ -1,52 +1,30 @@
local config = {
- bossPosition = Position(33357, 31182, 10),
- newPosition = Position(33359, 31186, 10),
- soulPosition = Position(33359, 31182, 12),
+ boss = {
+ name = "soul of dragonking zyrtarch",
+ position = Position(33359, 31182, 12),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33391, 31178, 10), teleport = Position(33359, 31186, 10) },
+ { pos = Position(33391, 31179, 10), teleport = Position(33359, 31186, 10) },
+ { pos = Position(33391, 31180, 10), teleport = Position(33359, 31186, 10) },
+ { pos = Position(33391, 31181, 10), teleport = Position(33359, 31186, 10) },
+ { pos = Position(33391, 31182, 10), teleport = Position(33359, 31186, 10) },
+ },
+ monsters = {
+ { name = "soulcatcher", pos = Position(33352, 31187, 10) },
+ { name = "soulcatcher", pos = Position(33363, 31187, 10) },
+ { name = "soulcatcher", pos = Position(33353, 31176, 10) },
+ { name = "soulcatcher", pos = Position(33363, 31176, 10) },
+ { name = "dragonking zyrtarch", pos = Position(33357, 31182, 10) },
+ },
+ specPos = {
+ from = Position(33348, 31172, 10),
+ to = Position(33368, 31190, 12),
+ },
+ exit = Position(33407, 31172, 10),
}
-local monsters = {
- { position = Position(33352, 31187, 10) },
- { position = Position(33363, 31187, 10) },
- { position = Position(33353, 31176, 10) },
- { position = Position(33363, 31176, 10) },
-}
-
-local leverZyrtarch = Action()
-
-function leverZyrtarch.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33391, 31178, 10) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Dragonking Zyrtarch", Position(33348, 31172, 10), Position(33368, 31190, 12)) then
- for d = 1, #monsters do
- Game.createMonster("soulcatcher", monsters[d].position, true, true)
- end
- Game.createMonster("dragonking zyrtarch", config.bossPosition, true, true)
- Game.createMonster("soul of dragonking zyrtarch", config.soulPosition, true, true)
- for y = 31178, 31182 do
- local playerTile = Tile(Position(33391, y, 10)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DragonkingTimer, os.time() + 20 * 60 * 60)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33348, 31172, 10), Position(33368, 31190, 12), Position(33407, 31172, 10))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverZyrtarch:position(Position(33391, 31177, 10))
-leverZyrtarch:register()
+local lever = BossLever(config)
+lever:position(Position(33391, 31177, 10))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua
index fc7e5049b6a..c21a5e60f0d 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua
@@ -21,7 +21,7 @@ function leverMeltingFrozenHorror.onUse(player, item, fromPosition, target, toPo
end
end
if item.itemid == 8911 then
- if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer) >= 1 then
+ if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled) >= 1 then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Frozen Horror.")
return true
end
@@ -43,11 +43,11 @@ function leverMeltingFrozenHorror.onUse(player, item, fromPosition, target, toPo
playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
playerTile:teleportTo(config.newPosition)
playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer, os.stime() + 20 * 60 * 60)
+ playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, os.time() + 20 * 60 * 60)
end
end
- Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer, 1)
- addEvent(clearForgotten, 30 * 60 * 1000, Position(32264, 31070, 14), Position(32284, 31104, 14), Position(32319, 31091, 14), Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer)
+ Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, 1)
+ addEvent(clearForgotten, 30 * 60 * 1000, Position(32264, 31070, 14), Position(32284, 31104, 14), Position(32319, 31091, 14), Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled)
item:transform(8912)
elseif item.itemid == 8912 then
item:transform(8911)
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua
index 7f784c4516f..795b3cf2234 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua
@@ -1,43 +1,28 @@
local config = {
- centerRoom = Position(32912, 31599, 14),
- bossPosition = Position(32912, 31599, 14),
- newPosition = Position(32911, 31603, 14),
+ boss = {
+ name = "Lady Tenebris",
+ position = Position(32912, 31599, 14),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32902, 31623, 14), teleport = Position(32911, 31603, 14) },
+ { pos = Position(32902, 31624, 14), teleport = Position(32911, 31603, 14) },
+ { pos = Position(32902, 31625, 14), teleport = Position(32911, 31603, 14) },
+ { pos = Position(32902, 31626, 14), teleport = Position(32911, 31603, 14) },
+ { pos = Position(32902, 31627, 14), teleport = Position(32911, 31603, 14) },
+ },
+ monsters = {
+ { name = "shadow tentacle", pos = Position(32910, 31599, 14) },
+ { name = "shadow tentacle", pos = Position(32912, 31597, 14) },
+ { name = "shadow tentacle", pos = Position(32914, 31599, 14) },
+ },
+ specPos = {
+ from = Position(32899, 31587, 14),
+ to = Position(32923, 31612, 14),
+ },
+ exit = Position(32902, 31629, 14),
}
-local leverLadyTenebris = Action()
-
-function leverLadyTenebris.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(32902, 31623, 14) then
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Lady Tenebris", Position(32902, 31589, 14), Position(32924, 31610, 14)) then
- for d = 1, 6 do
- Game.createMonster("shadow tentacle", Position(math.random(32909, 32914), math.random(31596, 31601), 14), true, true)
- end
- Game.createMonster("lady tenebris", config.bossPosition, true, true)
- for y = 31623, 31627 do
- local playerTile = Tile(Position(32902, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisTimer, os.time() + 20 * 60 * 60)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32902, 31589, 14), Position(32924, 31610, 14), Position(32919, 31639, 14))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverLadyTenebris:position(Position(32902, 31622, 14))
-leverLadyTenebris:register()
+local lever = BossLever(config)
+lever:position(Position(32902, 31622, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua
index 2a6a48b84c6..7a2fa1d0a86 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua
@@ -1,51 +1,29 @@
local config = {
- centerRoom = Position(32799, 32832, 14),
- bossPosition = Position(32799, 32827, 14),
- newPosition = Position(32800, 32831, 14),
+ boss = {
+ name = "Lloyd",
+ position = Position(32799, 32827, 14),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32759, 32868, 14), teleport = Position(32800, 32831, 14) },
+ { pos = Position(32759, 32869, 14), teleport = Position(32800, 32831, 14) },
+ { pos = Position(32759, 32870, 14), teleport = Position(32800, 32831, 14) },
+ { pos = Position(32759, 32871, 14), teleport = Position(32800, 32831, 14) },
+ { pos = Position(32759, 32872, 14), teleport = Position(32800, 32831, 14) },
+ },
+ monsters = {
+ { name = "cosmic energy prism a invu", pos = Position(32801, 32827, 14) },
+ { name = "cosmic energy prism b invu", pos = Position(32798, 32827, 14) },
+ { name = "cosmic energy prism c invu", pos = Position(32803, 32826, 14) },
+ { name = "cosmic energy prism d invu", pos = Position(32796, 32826, 14) },
+ },
+ specPos = {
+ from = Position(32785, 32813, 14),
+ to = Position(32812, 32838, 14),
+ },
+ exit = Position(32815, 32873, 13),
}
-local monsters = {
- { cosmic = "cosmic energy prism a", pos = Position(32801, 32827, 14) },
- { cosmic = "cosmic energy prism b", pos = Position(32798, 32827, 14) },
- { cosmic = "cosmic energy prism c", pos = Position(32803, 32826, 14) },
- { cosmic = "cosmic energy prism d", pos = Position(32796, 32826, 14) },
-}
-
-local leverLloyd = Action()
-
-function leverLloyd.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(32759, 32868, 14) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom(player:getId(), "Lloyd", Position(32785, 32814, 14), Position(32812, 32838, 14)) then
- for n = 1, #monsters do
- Game.createMonster(monsters[n].cosmic, monsters[n].pos, true, true)
- end
- Game.createMonster("lloyd", config.bossPosition, true, true)
- for y = 32868, 32872 do
- local playerTile = Tile(Position(32759, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LloydTimer, os.time() + 20 * 60 * 60)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32785, 32814, 14), Position(32812, 32838, 14), Position(32815, 32873, 13))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverLloyd:position(Position(32759, 32867, 14))
-leverLloyd:register()
+local lever = BossLever(config)
+lever:position(Position(32759, 32867, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua
index aa704ba6435..d14d9b88d73 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua
@@ -1,51 +1,44 @@
local config = {
- { newPosition = Position(31985, 32851, 14) },
- { pos = Position(31986, 32840, 14), monster = "a shielded astral glyph" },
- { pos = Position(31975, 32856, 15), monster = "bound astral power" },
- { pos = Position(31987, 32839, 14), monster = "the astral source" },
- { pos = Position(31986, 32823, 15), monster = "the distorted astral source" },
- { pos = Position(31989, 32823, 15), monster = "an astral glyph" },
+ boss = {
+ name = "The Last Lore Keeper",
+ position = Position(31987, 32839, 14),
+ },
+ timeToFightAgain = ParseDuration("14d") / 1000,
+ timeToDefeat = ParseDuration("17m") / 1000,
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32018, 32844, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32019, 32844, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32020, 32844, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32018, 32845, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32019, 32845, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32020, 32845, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32018, 32846, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32019, 32846, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32020, 32846, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32018, 32847, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32019, 32847, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32020, 32847, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32018, 32848, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32019, 32848, 14), teleport = Position(31984, 32851, 14) },
+ { pos = Position(32020, 32848, 14), teleport = Position(31984, 32851, 14) },
+ },
+ monsters = {
+ { name = "bound astral power", pos = Position(31973, 32840, 15) },
+ { name = "bound astral power", pos = Position(31973, 32856, 15) },
+ { name = "bound astral power", pos = Position(31989, 32856, 15) },
+ { name = "bound astral power", pos = Position(31989, 32840, 15) },
+ { name = "a shielded astral glyph", pos = Position(31986, 32840, 14) },
+ { name = "the distorted astral source", pos = Position(31986, 32823, 15) },
+ { name = "an astral glyph", pos = Position(31989, 32823, 15) },
+ },
+ specPos = {
+ from = Position(31968, 32821, 14),
+ to = Position(32004, 32865, 15),
+ },
+ exit = Position(32035, 32859, 14),
}
-local leverLoreKeeper = Action()
-
-function leverLoreKeeper.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(32019, 32844, 14) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("The Last Lorekeeper", Position(31968, 32821, 14), Position(32004, 32865, 15)) then
- for x = 32018, 32020 do
- for y = 32844, 32848 do
- local playerTile = Tile(Position(x, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config[1].newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LastLoreTimer, os.time() + 60 * 60 * 14 * 24)
- table.insert(playersTable, playerTile:getId())
- end
- end
- end
- for b = 2, #config do
- Game.createMonster(config[b].monster, config[b].pos, true, true)
- end
- Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter, 1)
- Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph, 0)
- player:say("The Astral Glyph begins to draw upon bound astral power to expel you from the room!", TALKTYPE_MONSTER_SAY)
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(31968, 32821, 14), Position(32004, 32865, 15), Position(32035, 32859, 14))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
-
- return true
-end
-
-leverLoreKeeper:position(Position(32019, 32843, 14))
-leverLoreKeeper:register()
+local lever = BossLever(config)
+lever:position(Position(32019, 32843, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua
index 16b22300ea1..b48a07e44e1 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua
@@ -1,43 +1,30 @@
local config = {
- centerRoom = Position(32624, 32880, 14),
- bossPosition = Position(32624, 32880, 14),
- newPosition = Position(32624, 32886, 14),
-}
-
-local leverThornKnight = Action()
-
-function leverThornKnight.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(32657, 32877, 14) then
- item:transform(8912)
- return true
+ boss = {
+ name = "The Enraged Thorn Knight",
+ createFunction = function()
+ return Game.createMonster("Mounted Thorn Knight", Position(32624, 32880, 14), true, true)
+ end,
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32657, 32877, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(32657, 32878, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(32657, 32879, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(32657, 32880, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT },
+ { pos = Position(32657, 32881, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT },
+ },
+ onUseExtra = function(player)
+ for d = 1, 6 do
+ Game.createMonster("possessed tree", Position(math.random(32619, 32629), math.random(32877, 32884), 14), true, true)
end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("Thorn Knight", Position(32613, 32869, 14), Position(32636, 32892, 14)) then
- for d = 1, 6 do
- Game.createMonster("possessed tree", Position(math.random(32619, 32629), math.random(32877, 32884), 14), true, true)
- end
- Game.createMonster("mounted thorn knight", config.bossPosition, true, true)
- for y = 32877, 32881 do
- local playerTile = Tile(Position(32657, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightTimer, os.stime() + 20 * 60 * 60)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32613, 32869, 14), Position(32636, 32892, 14), Position(32678, 32888, 14))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
- return true
-end
+ end,
+ specPos = {
+ from = Position(32613, 32869, 14),
+ to = Position(32636, 32892, 14),
+ },
+ exit = Position(32678, 32888, 14),
+}
-leverThornKnight:position(Position(32657, 32876, 14))
-leverThornKnight:register()
+local lever = BossLever(config)
+lever:position(Position(32657, 32876, 14))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua
index 75dcd0da028..386bce7d94d 100644
--- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua
+++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua
@@ -1,47 +1,27 @@
local config = {
- centerRoom = Position(32977, 31662, 14),
- newPosition = Position(32977, 31667, 14),
+ boss = {
+ name = "The Time Guardian",
+ position = Position(32977, 31662, 14),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33010, 31660, 14), teleport = Position(32977, 31667, 14) },
+ { pos = Position(33010, 31661, 14), teleport = Position(32977, 31667, 14) },
+ { pos = Position(33010, 31662, 14), teleport = Position(32977, 31667, 14) },
+ { pos = Position(33010, 31663, 14), teleport = Position(32977, 31667, 14) },
+ { pos = Position(33010, 31664, 14), teleport = Position(32977, 31667, 14) },
+ },
+ monsters = {
+ { name = "The Freezing Time Guardian", pos = Position(32975, 31664, 13) },
+ { name = "The Blazing Time Guardian", pos = Position(32980, 31664, 13) },
+ },
+ specPos = {
+ from = Position(32967, 31654, 14),
+ to = Position(32989, 31677, 14),
+ },
+ exit = Position(32870, 32724, 14),
}
-local bosses = {
- { bossPosition = Position(32977, 31662, 14), bossName = "The Time Guardian" },
- { bossPosition = Position(32975, 31664, 13), bossName = "The Freezing Time Guardian" },
- { bossPosition = Position(32980, 31664, 13), bossName = "The Blazing Time Guardian" },
-}
-
-local leverTimeGuardian = Action()
-
-function leverTimeGuardian.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if item.itemid == 8911 then
- if player:getPosition() ~= Position(33010, 31660, 14) then
- item:transform(8912)
- return true
- end
- end
- if item.itemid == 8911 then
- local playersTable = {}
- if player:doCheckBossRoom("The Time Guardian", Position(32967, 31654, 13), Position(32989, 31677, 14)) then
- for q = 1, #bosses do
- Game.createMonster(bosses[q].bossName, bosses[q].bossPosition, true, true)
- end
- for y = 31660, 31664 do
- local playerTile = Tile(Position(33010, y, 14)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianTimer, os.time() + 20 * 60 * 60)
- table.insert(playersTable, playerTile:getId())
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32967, 31654, 13), Position(32989, 31677, 14), Position(32870, 32724, 14))
- item:transform(8912)
- end
- elseif item.itemid == 8912 then
- item:transform(8911)
- end
- return true
-end
-
-leverTimeGuardian:position(Position(33010, 31659, 14))
-leverTimeGuardian:register()
+local lever = BossLever(config)
+lever:position(Position(33010, 31659, 14))
+lever:register()
From 6e5a1c34484ffd933fc1a144cecf5e5ab9eac089 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 14:43:46 -0300
Subject: [PATCH 08/18] fix: grave danger quest bosses (#3299)
---
.../actions/bosses_levers/king_zelos.lua | 28 -----
.../actions_azaram_fight.lua | 28 -----
...c_fight.lua => actions_baeloc_nictros.lua} | 0
.../actions_count_vlarkorth_.lua} | 2 +-
.../grave_danger_quest/actions_duke_fight.lua | 107 ------------------
.../grave_danger_quest/actions_duke_krule.lua | 23 ++++
.../grave_danger_quest/actions_earl_fight.lua | 24 ----
.../grave_danger_quest/actions_earl_osam.lua} | 2 +-
.../grave_danger_quest/actions_king_zelos.lua | 99 ++++------------
.../actions_lord_azaram.lua} | 2 +-
.../actions_vlarkorth_fight.lua | 24 ----
...lua => creaturescripts_baeloc_nictros.lua} | 0
...ua => creaturescripts_count_vlarkorth.lua} | 0
...ight.lua => creaturescripts_earl_osam.lua} | 0
...ght.lua => creaturescripts_king_zelos.lua} | 0
...ht.lua => creaturescripts_lord_azaram.lua} | 0
data-otservbr-global/startup/tables/lever.lua | 25 ----
17 files changed, 50 insertions(+), 314 deletions(-)
delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/king_zelos.lua
delete mode 100644 data-otservbr-global/scripts/quests/grave_danger_quest/actions_azaram_fight.lua
rename data-otservbr-global/scripts/quests/grave_danger_quest/{actions_baeloc_fight.lua => actions_baeloc_nictros.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/count_vlarkorth.lua => quests/grave_danger_quest/actions_count_vlarkorth_.lua} (94%)
delete mode 100644 data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_fight.lua
create mode 100644 data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_krule.lua
delete mode 100644 data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_fight.lua
rename data-otservbr-global/scripts/{actions/bosses_levers/earl_osam.lua => quests/grave_danger_quest/actions_earl_osam.lua} (94%)
rename data-otservbr-global/scripts/{actions/bosses_levers/lord_azaram.lua => quests/grave_danger_quest/actions_lord_azaram.lua} (94%)
delete mode 100644 data-otservbr-global/scripts/quests/grave_danger_quest/actions_vlarkorth_fight.lua
rename data-otservbr-global/scripts/quests/grave_danger_quest/{creaturescripts_baeloc_nictros_fight.lua => creaturescripts_baeloc_nictros.lua} (100%)
rename data-otservbr-global/scripts/quests/grave_danger_quest/{creaturescripts_count_vlarkorth_fight.lua => creaturescripts_count_vlarkorth.lua} (100%)
rename data-otservbr-global/scripts/quests/grave_danger_quest/{creaturescripts_earl_osam_fight.lua => creaturescripts_earl_osam.lua} (100%)
rename data-otservbr-global/scripts/quests/grave_danger_quest/{creaturescripts_king_zelos_fight.lua => creaturescripts_king_zelos.lua} (100%)
rename data-otservbr-global/scripts/quests/grave_danger_quest/{creaturescripts_lord_azaram_fight.lua => creaturescripts_lord_azaram.lua} (100%)
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/king_zelos.lua b/data-otservbr-global/scripts/actions/bosses_levers/king_zelos.lua
deleted file mode 100644
index aa14e575107..00000000000
--- a/data-otservbr-global/scripts/actions/bosses_levers/king_zelos.lua
+++ /dev/null
@@ -1,28 +0,0 @@
-local config = {
- boss = {
- name = "King Zelos",
- position = Position(33443, 31545, 13),
- },
- requiredLevel = 250,
- playerPositions = {
- { pos = Position(33485, 31546, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33485, 31547, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33485, 31548, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33485, 31545, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33485, 31544, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33486, 31546, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33486, 31547, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33486, 31548, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33486, 31545, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33486, 31544, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
- },
- specPos = {
- from = Position(33433, 31535, 13),
- to = Position(33453, 31555, 13),
- },
- exit = Position(32172, 31918, 8),
-}
-
-local lever = BossLever(config)
-lever:position({ x = 33484, y = 31546, z = 13 })
-lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_azaram_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_azaram_fight.lua
deleted file mode 100644
index 14e479f6fe3..00000000000
--- a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_azaram_fight.lua
+++ /dev/null
@@ -1,28 +0,0 @@
-local config = {
- boss = {
- name = "Lord Azaram",
- position = Position(33424, 31472, 13),
- },
- timeAfterKill = 30 * 60,
- playerPositions = {
- { pos = Position(33422, 31493, 13), teleport = Position(33424, 31478, 13) },
- { pos = Position(33423, 31493, 13), teleport = Position(33424, 31478, 13) },
- { pos = Position(33424, 31493, 13), teleport = Position(33424, 31478, 13) },
- { pos = Position(33425, 31493, 13), teleport = Position(33424, 31478, 13) },
- { pos = Position(33426, 31493, 13), teleport = Position(33424, 31478, 13) },
- },
- specPos = {
- from = Position(33414, 31463, 13),
- to = Position(33433, 31481, 13),
- },
- monsters = {
- { name = "Condensed Sin", pos = Position(33426, 31471, 13) },
- { name = "Condensed Sin", pos = Position(33422, 31471, 13) },
- },
- exit = Position(32190, 31819, 8),
- exitTeleporter = Position(32192, 31819, 8),
-}
-
-local lever = BossLever(config)
-lever:aid(14561)
-lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_baeloc_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_baeloc_nictros.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/actions_baeloc_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/actions_baeloc_nictros.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/count_vlarkorth.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_count_vlarkorth_.lua
similarity index 94%
rename from data-otservbr-global/scripts/actions/bosses_levers/count_vlarkorth.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/actions_count_vlarkorth_.lua
index 184d5fa6eca..ddb769917d5 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/count_vlarkorth.lua
+++ b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_count_vlarkorth_.lua
@@ -19,5 +19,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 33454, y = 31413, z = 13 })
+lever:position(Position(33454, 31413, 13))
lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_fight.lua
deleted file mode 100644
index 283547db05e..00000000000
--- a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_fight.lua
+++ /dev/null
@@ -1,107 +0,0 @@
-local config = {
- boss = {
- name = "Duke Krule",
- createFunction = function()
- local boss = Game.createMonster("Duke Krule", Position(33456, 31473, 13), true, true)
- boss:setStorageValue(1, os.time())
- return boss
- end,
- },
- requiredLevel = 250,
- playerPositions = {
- { pos = Position(33455, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33456, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33457, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33458, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
- { pos = Position(33459, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
- },
- specPos = {
- from = Position(33447, 31464, 13),
- to = Position(33464, 31481, 13),
- },
- exit = Position(32347, 32167, 12),
-}
-
-local duke_water = Combat()
-duke_water:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ICETORNADO)
-duke_water:setArea(createCombatArea(AREA_CIRCLE3X3))
-
-function onTargetTile(cid, pos)
- local tile = Tile(pos)
- local target = tile:getTopCreature()
- if tile then
- if target and target:isPlayer() and target:getOutfit().lookType == 49 then
- doTargetCombatHealth(0, target, COMBAT_ICEDAMAGE, -1500, -2000)
- end
- end
-end
-
-duke_water:setCallback(CALLBACK_PARAM_TARGETTILE, "onTargetTile")
-
-local duke_fire = Combat()
-duke_fire:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_HITBYFIRE)
-duke_fire:setArea(createCombatArea(AREA_CIRCLE3X3))
-
-function onTargetTile(cid, pos)
- local tile = Tile(pos)
- local target = tile:getTopCreature()
- if tile then
- if target and target:isPlayer() and target:getOutfit().lookType == 286 then
- doTargetCombatHealth(0, target, COMBAT_FIREDAMAGE, -1500, -2000)
- end
- end
-end
-
-duke_fire:setCallback(CALLBACK_PARAM_TARGETTILE, "onTargetTile")
-
-config.onUseExtra = function()
- local config = {
- centerRoom = Position(33456, 31472, 13),
- x = 10,
- y = 10,
- transformCD = Storage.Quest.U12_20.GraveDanger.Bosses.DukeKrule.TransformCD,
- }
- local function hitArea(creature)
- local player = Player(creature)
-
- if player then
- if player:getStorageValue(config.transformCD) <= os.time() then
- if player:getOutfit().lookType == 49 then
- local var = { type = 1, number = creature }
- duke_fire:execute(player, var)
- player:setStorageValue(config.transformCD, os.time() + 3)
- addEvent(hitArea, 3 * 1000, creature)
- elseif player:getOutfit().lookType == 286 then
- local var = { type = 1, number = creature }
- duke_water:execute(player, var)
- player:setStorageValue(config.transformCD, os.time() + 3)
- addEvent(hitArea, 3 * 1000, creature)
- end
- end
- end
-
- return true
- end
-
- local function transformPlayers(id)
- local spectators = Game.getSpectators(config.centerRoom, false, true, config.x, config.x, config.y, config.y)
- local form = { 49, 286 }
- local boss = Creature("Duke Krule")
-
- if boss and boss:getStorageValue(1) == id then
- if #spectators > 0 then
- for _, player in pairs(spectators) do
- doSetCreatureOutfit(player, { lookType = form[math.random(#form)] }, 30 * 1000)
- addEvent(hitArea, 3 * 1000, player:getId())
- end
- addEvent(transformPlayers, 36 * 1000, id)
- end
- end
- return true
- end
- addEvent(transformPlayers, 30 * 1000, os.time())
-end
-
-local lever = BossLever(config)
-lever:position({ x = 33454, y = 31493, z = 13 })
-lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_krule.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_krule.lua
new file mode 100644
index 00000000000..5828099813b
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_duke_krule.lua
@@ -0,0 +1,23 @@
+local config = {
+ boss = {
+ name = "Duke Krule",
+ position = Position(33456, 31473, 13),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33455, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33456, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33457, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33458, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33459, 31493, 13), teleport = Position(33455, 31464, 13), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33447, 31464, 13),
+ to = Position(33464, 31481, 13),
+ },
+ exit = Position(32347, 32167, 12),
+}
+
+local lever = BossLever(config)
+lever:position(Position(33454, 31493, 13))
+lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_fight.lua
deleted file mode 100644
index 0d40c7d8593..00000000000
--- a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_fight.lua
+++ /dev/null
@@ -1,24 +0,0 @@
-local config = {
- boss = {
- name = "Earl Osam",
- position = Position(33488, 31438, 13),
- },
- timeAfterKill = 30 * 60,
- playerPositions = {
- { pos = Position(33516, 31444, 13), teleport = Position(33489, 31441, 13) },
- { pos = Position(33517, 31444, 13), teleport = Position(33489, 31441, 13) },
- { pos = Position(33518, 31444, 13), teleport = Position(33489, 31441, 13) },
- { pos = Position(33519, 31444, 13), teleport = Position(33489, 31441, 13) },
- { pos = Position(33520, 31444, 13), teleport = Position(33489, 31441, 13) },
- },
- specPos = {
- from = Position(33479, 31429, 13),
- to = Position(33497, 31447, 13),
- },
- exit = Position(33261, 31985, 8),
- exitTeleporter = Position(33263, 31985, 8),
-}
-
-local lever = BossLever(config)
-lever:aid(14558)
-lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/earl_osam.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_osam.lua
similarity index 94%
rename from data-otservbr-global/scripts/actions/bosses_levers/earl_osam.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_osam.lua
index 363e3b2ba55..86ee236cf21 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/earl_osam.lua
+++ b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_earl_osam.lua
@@ -19,5 +19,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 33515, y = 31444, z = 13 })
+lever:position(Position(33515, 31444, 13))
lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_king_zelos.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_king_zelos.lua
index f1c571de7f8..c3ce9126309 100644
--- a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_king_zelos.lua
+++ b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_king_zelos.lua
@@ -1,79 +1,28 @@
local config = {
- centerRoom = Position(33443, 31545, 13),
- newPosition = Position(33436, 31572, 13),
- exitPos = Position(32172, 31917, 8),
- x = 30,
- y = 30,
- summons = {
- {
- name = "Rewar The Bloody",
- pos = Position(33463, 31562, 13),
- },
- {
- name = "The Red Knight",
- pos = Position(33423, 31562, 13),
- },
- {
- name = "Magnor Mournbringer",
- pos = Position(33463, 31529, 13),
- },
- {
- name = "Nargol the Impaler",
- pos = Position(33423, 31529, 13),
- },
- {
- name = "King Zelos",
- pos = Position(33443, 31545, 13),
- },
+ boss = {
+ name = "King Zelos",
+ position = Position(33443, 31545, 13),
},
- timer = Storage.Quest.U12_20.GraveDanger.Bosses.KingZelos.Timer,
- room = Storage.Quest.U12_20.GraveDanger.Bosses.KingZelos.Room,
- fromPos = Position(33414, 31520, 13),
- toPos = Position(33474, 31574, 13),
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33485, 31546, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33485, 31547, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33485, 31548, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33485, 31545, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33485, 31544, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33486, 31546, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33486, 31547, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33486, 31548, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33486, 31545, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ { pos = Position(33486, 31544, 13), teleport = Position(33443, 31554, 13), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33433, 31535, 13),
+ to = Position(33453, 31555, 13),
+ },
+ exit = Position(32172, 31918, 8),
}
-local king_zelos = Action()
-
-function king_zelos.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if not player:doCheckBossRoom("King Zelos", config.fromPos, config.toPos) then
- player:sendCancelMessage("The room is already in use. Please wait.")
- return true
- end
-
- local spectators = Game.getSpectators(config.centerRoom, false, true, config.x, config.x, config.y, config.y)
-
- if player:getPosition() ~= Position(33485, 31546, 13) then
- player:sendCancelMessage("Sorry, not possible.")
- return true
- end
-
- if #spectators > 0 then
- player:say("The room is occupied by another team, please wait.", TALKTYPE_MONSTER_SAY, false, player)
- return true
- end
-
- for _, boss in pairs(config.summons) do
- Game.createMonster(boss.name, boss.pos, false, true)
- end
-
- for x = 33485, 33486 do
- for y = 31544, 31548 do
- local playerTile = Tile(Position(x, y, 13)):getTopCreature()
- if playerTile and playerTile:isPlayer() then
- playerTile:getPosition():sendMagicEffect(CONST_ME_POFF)
- playerTile:teleportTo(config.newPosition)
- playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
- playerTile:setStorageValue(config.timer, os.time() + 20 * 3600)
- playerTile:setStorageValue(config.room, os.time() + 24 * 60)
- playerTile:say("You have 24 minutes to kill and loot this boss. Otherwise you will lose that chance and will be kicked out.", TALKTYPE_MONSTER_SAY, false, playerTile)
- end
- end
- end
-
- addEvent(clearForgotten, 24 * 60 * 1000, config.centerRoom, config.x, config.y, config.exitPos, config.room)
-
- return true
-end
-
-king_zelos:aid(14568)
-king_zelos:register()
+local lever = BossLever(config)
+lever:position(Position(33484, 31546, 13))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/lord_azaram.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_lord_azaram.lua
similarity index 94%
rename from data-otservbr-global/scripts/actions/bosses_levers/lord_azaram.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/actions_lord_azaram.lua
index 28a9369bc55..0613441b47c 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/lord_azaram.lua
+++ b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_lord_azaram.lua
@@ -19,5 +19,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 33421, y = 31493, z = 13 })
+lever:position(Position(33421, 31493, 13))
lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_vlarkorth_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/actions_vlarkorth_fight.lua
deleted file mode 100644
index 86711ac8382..00000000000
--- a/data-otservbr-global/scripts/quests/grave_danger_quest/actions_vlarkorth_fight.lua
+++ /dev/null
@@ -1,24 +0,0 @@
-local config = {
- boss = {
- name = "Count Vlarkorth",
- position = Position(33456, 31437, 13),
- },
- timeAfterKill = 30 * 60,
- playerPositions = {
- { pos = Position(33455, 31413, 13), teleport = Position(33457, 31442, 13) },
- { pos = Position(33456, 31413, 13), teleport = Position(33457, 31442, 13) },
- { pos = Position(33457, 31413, 13), teleport = Position(33457, 31442, 13) },
- { pos = Position(33458, 31413, 13), teleport = Position(33457, 31442, 13) },
- { pos = Position(33459, 31413, 13), teleport = Position(33457, 31442, 13) },
- },
- specPos = {
- from = Position(33451, 31432, 13),
- to = Position(33461, 31442, 13),
- },
- exit = Position(33195, 31696, 8),
- exitTeleporter = Position(33456, 31446, 13),
-}
-
-local lever = BossLever(config)
-lever:aid(14557)
-lever:register()
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_baeloc_nictros_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_baeloc_nictros.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_baeloc_nictros_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_baeloc_nictros.lua
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_count_vlarkorth_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_count_vlarkorth.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_count_vlarkorth_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_count_vlarkorth.lua
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_earl_osam_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_earl_osam.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_earl_osam_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_earl_osam.lua
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_king_zelos_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_king_zelos.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_king_zelos_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_king_zelos.lua
diff --git a/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_lord_azaram_fight.lua b/data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_lord_azaram.lua
similarity index 100%
rename from data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_lord_azaram_fight.lua
rename to data-otservbr-global/scripts/quests/grave_danger_quest/creaturescripts_lord_azaram.lua
diff --git a/data-otservbr-global/startup/tables/lever.lua b/data-otservbr-global/startup/tables/lever.lua
index bcf98109646..ab887209866 100644
--- a/data-otservbr-global/startup/tables/lever.lua
+++ b/data-otservbr-global/startup/tables/lever.lua
@@ -129,31 +129,6 @@ LeverAction = {
{ x = 32576, y = 31862, z = 14 },
},
},
- -- Grave Danger Quest
- [14557] = {
- itemId = 8911,
- itemPos = {
- { x = 33454, y = 31413, z = 13 },
- },
- },
- [14558] = {
- itemId = 8911,
- itemPos = {
- { x = 33515, y = 31444, z = 13 },
- },
- },
- [14561] = {
- itemId = 8911,
- itemPos = {
- { x = 33421, y = 31493, z = 13 },
- },
- },
- [14568] = {
- itemId = 8911,
- itemPos = {
- { x = 33484, y = 31546, z = 13 },
- },
- },
-- Forgotten Knowledge Quest
[26663] = {
itemId = 9125,
From 08a35ac68c3052877e8eca193dd6f317cefda8f3 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 14:52:37 -0300
Subject: [PATCH 09/18] fix: heart of destruction quest bosses (#3300)
---
.../heart_of_destruction/actions_anomaly.lua} | 2 +-
.../heart_of_destruction/actions_eradicator.lua} | 2 +-
.../heart_of_destruction/actions_foreshock.lua} | 2 +-
.../heart_of_destruction/actions_outburst.lua} | 2 +-
.../heart_of_destruction/actions_rupture.lua} | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
rename data-otservbr-global/scripts/{actions/bosses_levers/anomaly.lua => quests/heart_of_destruction/actions_anomaly.lua} (97%)
rename data-otservbr-global/scripts/{actions/bosses_levers/eradicator.lua => quests/heart_of_destruction/actions_eradicator.lua} (97%)
rename data-otservbr-global/scripts/{actions/bosses_levers/foreshock.lua => quests/heart_of_destruction/actions_foreshock.lua} (97%)
rename data-otservbr-global/scripts/{actions/bosses_levers/outburst.lua => quests/heart_of_destruction/actions_outburst.lua} (97%)
rename data-otservbr-global/scripts/{actions/bosses_levers/rupture.lua => quests/heart_of_destruction/actions_rupture.lua} (97%)
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/anomaly.lua b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_anomaly.lua
similarity index 97%
rename from data-otservbr-global/scripts/actions/bosses_levers/anomaly.lua
rename to data-otservbr-global/scripts/quests/heart_of_destruction/actions_anomaly.lua
index 3cac20d9624..817abefc960 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/anomaly.lua
+++ b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_anomaly.lua
@@ -35,5 +35,5 @@ local config = {
}
local lever = BossLever(config)
-lever:aid(14325)
+lever:position(Position(32245, 31244, 14))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/eradicator.lua b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_eradicator.lua
similarity index 97%
rename from data-otservbr-global/scripts/actions/bosses_levers/eradicator.lua
rename to data-otservbr-global/scripts/quests/heart_of_destruction/actions_eradicator.lua
index a4d88c31c72..6bf1eefbcab 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/eradicator.lua
+++ b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_eradicator.lua
@@ -41,5 +41,5 @@ local config = {
}
local lever = BossLever(config)
-lever:aid(14330)
+lever:position(Position(32334, 31283, 14))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/foreshock.lua b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_foreshock.lua
similarity index 97%
rename from data-otservbr-global/scripts/actions/bosses_levers/foreshock.lua
rename to data-otservbr-global/scripts/quests/heart_of_destruction/actions_foreshock.lua
index 0bf9034d93b..8c62ab48710 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/foreshock.lua
+++ b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_foreshock.lua
@@ -38,5 +38,5 @@ local config = {
}
local lever = BossLever(config)
-lever:aid(14329)
+lever:position(Position(32182, 31243, 14))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/outburst.lua b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_outburst.lua
similarity index 97%
rename from data-otservbr-global/scripts/actions/bosses_levers/outburst.lua
rename to data-otservbr-global/scripts/quests/heart_of_destruction/actions_outburst.lua
index 6e377900bde..3c1ad8bd0c1 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/outburst.lua
+++ b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_outburst.lua
@@ -37,5 +37,5 @@ local config = {
}
local lever = BossLever(config)
-lever:aid(14331)
+lever:position(Position(32207, 31283, 14))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rupture.lua b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_rupture.lua
similarity index 97%
rename from data-otservbr-global/scripts/actions/bosses_levers/rupture.lua
rename to data-otservbr-global/scripts/quests/heart_of_destruction/actions_rupture.lua
index ecb892cef8e..506a5afe93f 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/rupture.lua
+++ b/data-otservbr-global/scripts/quests/heart_of_destruction/actions_rupture.lua
@@ -37,5 +37,5 @@ local config = {
}
local lever = BossLever(config)
-lever:aid(14327)
+lever:position(Position(32309, 31247, 14))
lever:register()
From 0121d79b0b387dfa491c961b83e517858dabb71b Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 15:01:13 -0300
Subject: [PATCH 10/18] fix: the secret library quest bosses (#3301)
---
.../{ => bosses}/lokathmor.lua | 0
.../{ => bosses}/mazzinor.lua | 0
.../actions/bosses_levers/gorzindel.lua | 23 ---
.../library_area/actions_bossesLever.lua | 167 ------------------
.../library_area/actions_ghulosh.lua} | 5 +-
.../library_area/actions_gorzindel.lua | 38 ++++
.../library_area/actions_lokathmor.lua} | 8 +-
.../library_area/actions_mazzinor.lua} | 8 +-
.../actions_the_scourge_of_oblivion.lua} | 2 +-
9 files changed, 57 insertions(+), 194 deletions(-)
rename data-otservbr-global/monster/quests/the_secret_library/{ => bosses}/lokathmor.lua (100%)
rename data-otservbr-global/monster/quests/the_secret_library/{ => bosses}/mazzinor.lua (100%)
delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/gorzindel.lua
delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua
rename data-otservbr-global/scripts/{actions/bosses_levers/ghulosh.lua => quests/the_secret_library_quest/library_area/actions_ghulosh.lua} (86%)
create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_gorzindel.lua
rename data-otservbr-global/scripts/{actions/bosses_levers/lokathmor.lua => quests/the_secret_library_quest/library_area/actions_lokathmor.lua} (72%)
rename data-otservbr-global/scripts/{actions/bosses_levers/mazzinor.lua => quests/the_secret_library_quest/library_area/actions_mazzinor.lua} (72%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_scourge_of_oblivion.lua => quests/the_secret_library_quest/library_area/actions_the_scourge_of_oblivion.lua} (96%)
diff --git a/data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/lokathmor.lua
similarity index 100%
rename from data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua
rename to data-otservbr-global/monster/quests/the_secret_library/bosses/lokathmor.lua
diff --git a/data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/mazzinor.lua
similarity index 100%
rename from data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua
rename to data-otservbr-global/monster/quests/the_secret_library/bosses/mazzinor.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/gorzindel.lua b/data-otservbr-global/scripts/actions/bosses_levers/gorzindel.lua
deleted file mode 100644
index 2cfe4ffb399..00000000000
--- a/data-otservbr-global/scripts/actions/bosses_levers/gorzindel.lua
+++ /dev/null
@@ -1,23 +0,0 @@
-local config = {
- boss = {
- name = "Gorzindel",
- position = Position(32687, 32715, 10),
- },
- requiredLevel = 250,
- playerPositions = {
- { pos = Position(32747, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
- { pos = Position(32748, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
- { pos = Position(32749, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
- { pos = Position(32750, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
- { pos = Position(32751, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
- },
- specPos = {
- from = Position(32680, 32711, 10),
- to = Position(32695, 32726, 10),
- },
- exit = Position(32660, 32734, 12),
-}
-
-local lever = BossLever(config)
-lever:position({ x = 32746, y = 32749, z = 10 })
-lever:register()
diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua
deleted file mode 100644
index 6f579ea8e07..00000000000
--- a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua
+++ /dev/null
@@ -1,167 +0,0 @@
-local mazzinorSummons = {
- name = "Wild Knowledge",
- eventName = "mazzinorDeath",
- middlePosition = Position(32724, 32720, 10),
- timing = 25,
- positions = {
- [1] = Position(32719, 32718, 10),
- [2] = Position(32723, 32719, 10),
- [3] = Position(32728, 32718, 10),
- [4] = Position(32724, 32724, 10),
- },
-}
-
-local ghuloshSummons = {
- name = "Bone Jaw",
- eventName = "",
- middlePosition = Position(32756, 32721, 10),
- timing = 25,
- positions = {
- [1] = Position(32755, 32721, 10),
- },
-}
-
-local gorzindelSummons = {
- name = "Mean Minion",
- name2 = "Malicious Minion",
- eventName = "",
- middlePosition = Position(32687, 32719, 10),
- timing = 25,
- positions = {
- [1] = Position(32687, 32717, 10),
- },
- positions2 = {
- [1] = Position(32687, 32720, 10),
- },
- tomesPosition = {
- [1] = { name = "stolen knowledge of armor", position = Position(32687, 32707, 10) },
- [2] = { name = "stolen knowledge of summoning", position = Position(32698, 32715, 10) },
- [3] = { name = "stolen knowledge of lifesteal", position = Position(32693, 32729, 10) },
- [4] = { name = "stolen knowledge of spells", position = Position(32681, 32729, 10) },
- [5] = { name = "stolen knowledge of healing", position = Position(32676, 32715, 10) },
- },
-}
-
-local lokathmorSummons = {
- name = "Knowledge Raider",
- eventName = "",
- middlePosition = Position(32751, 32689, 10),
- timing = 25,
- positions = {
- [1] = Position(32747, 32684, 10),
- [2] = Position(32755, 32684, 10),
- [3] = Position(32755, 32694, 10),
- [4] = Position(32747, 32694, 10),
- },
-}
-
-local bossNames = { "mazzinor", "supercharged mazzinor", "lokathmor", "ghulosh", "ghuloshz' deathgaze", "gorzindel", "stolen tome of portals" }
-
-local function spawnSummons(k, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel)
- local spectators = Game.getSpectators(middlePosition, false, false, 12, 12, 12, 12)
- local hasPlayer = false
-
- for _, c in pairs(spectators) do
- if c and c:isPlayer() then
- hasPlayer = true
- end
- end
-
- if isGorzindel then
- local hasTome = false
- for _, c in pairs(spectators) do
- for i = 1, #gorzindelSummons.tomesPosition do
- if c and (c:getName():lower() == gorzindelSummons.tomesPosition[i].name) then
- hasTome = true
- end
- end
- end
- if not hasTome then
- return false
- end
- end
- if hasPlayer then
- if k <= 4 then
- for i = 1, #positionTable do
- local sqm = positionTable[i]
- if sqm then
- sqm:sendMagicEffect(CONST_ME_TELEPORT)
- end
- end
- k = k + 1
- addEvent(spawnSummons, 2 * 1000, k, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel)
- else
- for i = 1, #positionTable do
- local monster = Game.createMonster(monsterName, positionTable[i])
- if monster then
- monster:registerEvent(eventName)
- end
- end
- addEvent(function()
- spawnSummons(1, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel)
- end, timing * 1000)
- end
- end
-end
-
-local leverInfo = {
- [1] = { bossName = "Mazzinor", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.MazzinorTimer, exit = Position(32616, 32532, 13), position = Position(32720, 32773, 10), type = "x", bossPosition = Position(32724, 32720, 10), teleportTo = Position(32724, 32726, 10), fromPosition = Position(32715, 32712, 10), toPosition = Position(32733, 32729, 10) },
- [2] = { bossName = "Lokathmor", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.LokathmorTimer, exit = Position(32467, 32654, 12), position = Position(32720, 32749, 10), type = "x", bossPosition = Position(32751, 32689, 10), teleportTo = Position(32750, 32694, 10), fromPosition = Position(32741, 32680, 10), toPosition = Position(32759, 32697, 10) },
- [3] = { bossName = "Ghulosh", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.GhuloshTimer, exit = Position(32659, 32713, 13), position = Position(32746, 32773, 10), type = "x", bossPosition = Position(32756, 32721, 10), teleportTo = Position(32755, 32727, 10), fromPosition = Position(32745, 32711, 10), toPosition = Position(32768, 32730, 10) },
- [4] = { bossName = "Gorzindel", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.GorzindelTimer, exit = Position(32660, 32734, 12), position = Position(32746, 32749, 10), type = "x", bossPosition = Position(32685, 32717, 10), teleportTo = Position(32687, 32724, 10), fromPosition = Position(32671, 32703, 10), toPosition = Position(32702, 32734, 10) },
-}
-
-local actions_library_bossesLever = Action()
-
-function actions_library_bossesLever.onUse(player, item, fromPosition, itemEx, toPosition)
- local playersTable = {}
-
- for _, lever in pairs(leverInfo) do
- if toPosition == lever.position then
- if player:doCheckBossRoom(lever.bossName, lever.fromPosition, lever.toPosition) then
- if lever.type == "x" then
- local startPos = lever.position.x + 1
- for x = startPos, startPos + 4 do
- local sqm = Tile(Position(x, lever.position.y, lever.position.z))
- if sqm then
- local c = sqm:getTopCreature()
- if c and c:isPlayer() then
- table.insert(playersTable, c:getId())
- c:teleportTo(lever.teleportTo)
- c:setStorageValue(lever.storage, os.time() + 20 * 60 * 60)
- end
- end
- end
- end
-
- local monster = Game.createMonster(lever.bossName, lever.bossPosition)
-
- if monster then
- if lever.bossName:lower() == "mazzinor" then
- addEvent(spawnSummons, 4 * 1000, 1, mazzinorSummons.name, mazzinorSummons.eventName, mazzinorSummons.timing, mazzinorSummons.positions, mazzinorSummons.middlePosition, false)
- elseif lever.bossName:lower() == "lokathmor" then
- addEvent(spawnSummons, 4 * 1000, 1, lokathmorSummons.name, lokathmorSummons.eventName, lokathmorSummons.timing, lokathmorSummons.positions, lokathmorSummons.middlePosition, false)
- elseif lever.bossName:lower() == "ghulosh" then
- addEvent(spawnSummons, 4 * 1000, 1, ghuloshSummons.name, ghuloshSummons.eventName, ghuloshSummons.timing, ghuloshSummons.positions, ghuloshSummons.middlePosition, false)
- local book = Game.createMonster("The Book of Death", Position(32755, 32716, 10))
- Game.setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Library.Ghulosh, 1)
- elseif lever.bossName:lower() == "gorzindel" then
- addEvent(spawnSummons, 4 * 1000, 1, gorzindelSummons.name, gorzindelSummons.eventName, gorzindelSummons.timing, gorzindelSummons.positions, gorzindelSummons.middlePosition, true)
- addEvent(spawnSummons, 4 * 1000, 1, gorzindelSummons.name2, gorzindelSummons.eventName, gorzindelSummons.timing, gorzindelSummons.positions2, gorzindelSummons.middlePosition, true)
- local tome = Game.createMonster("Stolen Tome of Portals", Position(32688, 32715, 10))
- for _, k in pairs(gorzindelSummons.tomesPosition) do
- local monster = Game.createMonster(k.name, k.position)
- local minion = Game.createMonster("Malicious Minion", k.position)
- end
- end
- end
- addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, lever.fromPosition, lever.toPosition, lever.exit)
- end
- end
- end
-
- return true
-end
-
-actions_library_bossesLever:aid(4950)
-actions_library_bossesLever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/ghulosh.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_ghulosh.lua
similarity index 86%
rename from data-otservbr-global/scripts/actions/bosses_levers/ghulosh.lua
rename to data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_ghulosh.lua
index 48fb4c17818..4f618c76d7b 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/ghulosh.lua
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_ghulosh.lua
@@ -11,6 +11,9 @@ local config = {
{ pos = Position(32750, 32773, 10), teleport = Position(32757, 32727, 10), effect = CONST_ME_TELEPORT },
{ pos = Position(32751, 32773, 10), teleport = Position(32757, 32727, 10), effect = CONST_ME_TELEPORT },
},
+ monsters = {
+ { name = "the book of death", pos = Position(32756, 32718, 10) },
+ },
specPos = {
from = Position(32748, 32713, 10),
to = Position(32763, 32729, 10),
@@ -19,5 +22,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 32746, y = 32773, z = 10 })
+lever:position(Position(32746, 32773, 10))
lever:register()
diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_gorzindel.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_gorzindel.lua
new file mode 100644
index 00000000000..8ab1e568feb
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_gorzindel.lua
@@ -0,0 +1,38 @@
+local config = {
+ boss = {
+ name = "Gorzindel",
+ position = Position(32687, 32715, 10),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32747, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
+ { pos = Position(32748, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
+ { pos = Position(32749, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
+ { pos = Position(32750, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
+ { pos = Position(32751, 32749, 10), teleport = Position(32686, 32721, 10), effect = CONST_ME_TELEPORT },
+ },
+ monsters = {
+ { name = "mean minion", pos = Position(32687, 32717, 10) },
+ { name = "malicious minion", pos = Position(32687, 32720, 10) },
+ { name = "malicious minion", pos = Position(32687, 32708, 10) },
+ { name = "malicious minion", pos = Position(32698, 32716, 10) },
+ { name = "malicious minion", pos = Position(32693, 32730, 10) },
+ { name = "malicious minion", pos = Position(32681, 32730, 10) },
+ { name = "malicious minion", pos = Position(32676, 32716, 10) },
+ { name = "stolen knowledge of armor", pos = Position(32687, 32707, 10) },
+ { name = "stolen knowledge of summoning", pos = Position(32698, 32715, 10) },
+ { name = "stolen knowledge of lifesteal", pos = Position(32693, 32729, 10) },
+ { name = "stolen knowledge of spells", pos = Position(32681, 32729, 10) },
+ { name = "stolen knowledge of healing", pos = Position(32676, 32715, 10) },
+ { name = "stolen tome of portals", pos = Position(32688, 32715, 10) },
+ },
+ specPos = {
+ from = Position(32680, 32711, 10),
+ to = Position(32695, 32726, 10),
+ },
+ exit = Position(32660, 32734, 12),
+}
+
+local lever = BossLever(config)
+lever:position(Position(32746, 32749, 10))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/lokathmor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_lokathmor.lua
similarity index 72%
rename from data-otservbr-global/scripts/actions/bosses_levers/lokathmor.lua
rename to data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_lokathmor.lua
index 5a56c186ee9..3855eb515ee 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/lokathmor.lua
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_lokathmor.lua
@@ -11,6 +11,12 @@ local config = {
{ pos = Position(32724, 32749, 10), teleport = Position(32751, 32685, 10), effect = CONST_ME_TELEPORT },
{ pos = Position(32725, 32749, 10), teleport = Position(32751, 32685, 10), effect = CONST_ME_TELEPORT },
},
+ monsters = {
+ { name = "knowledge raider", pos = Position(32747, 32684, 10) },
+ { name = "knowledge raider", pos = Position(32755, 32684, 10) },
+ { name = "knowledge raider", pos = Position(32755, 32694, 10) },
+ { name = "knowledge raider", pos = Position(32747, 32694, 10) },
+ },
specPos = {
from = Position(32742, 32681, 10),
to = Position(32758, 32696, 10),
@@ -19,5 +25,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 32720, y = 32749, z = 10 })
+lever:position(Position(32720, 32749, 10))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/mazzinor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_mazzinor.lua
similarity index 72%
rename from data-otservbr-global/scripts/actions/bosses_levers/mazzinor.lua
rename to data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_mazzinor.lua
index 2472b5634ac..765c04ea08a 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/mazzinor.lua
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_mazzinor.lua
@@ -11,6 +11,12 @@ local config = {
{ pos = Position(32724, 32773, 10), teleport = Position(32726, 32726, 10), effect = CONST_ME_TELEPORT },
{ pos = Position(32725, 32773, 10), teleport = Position(32726, 32726, 10), effect = CONST_ME_TELEPORT },
},
+ monsters = {
+ { name = "wild knowledge", pos = Position(32719, 32718, 10) },
+ { name = "wild knowledge", pos = Position(32723, 32719, 10) },
+ { name = "wild knowledge", pos = Position(32728, 32718, 10) },
+ { name = "wild knowledge", pos = Position(32724, 32724, 10) },
+ },
specPos = {
from = Position(32716, 32713, 10),
to = Position(32732, 32728, 10),
@@ -19,5 +25,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 32720, y = 32773, z = 10 })
+lever:position(Position(32720, 32773, 10))
lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_scourge_of_oblivion.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_the_scourge_of_oblivion.lua
similarity index 96%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_scourge_of_oblivion.lua
rename to data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_the_scourge_of_oblivion.lua
index f7373fbe49b..2e4a9b19df6 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/the_scourge_of_oblivion.lua
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_the_scourge_of_oblivion.lua
@@ -24,5 +24,5 @@ local config = {
}
local lever = BossLever(config)
-lever:position({ x = 32675, y = 32743, z = 11 })
+lever:position(Position(32675, 32743, 11))
lever:register()
From 7f0bfccc9b5706e89169cde51d349cb3d4ace919 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 15:03:32 -0300
Subject: [PATCH 11/18] fix: move rotten blood quest files to quest folder
(#3303)
---
.../rotten_blood_quest/actions_bakragore.lua} | 0
.../rotten_blood_quest/actions_chagorz.lua} | 0
.../rotten_blood_quest/actions_ichgahal.lua} | 0
.../rotten_blood_quest/actions_murcion.lua} | 0
.../rotten_blood_quest/actions_sacrifice.lua} | 0
.../rotten_blood_quest/actions_vemiath.lua} | 0
.../rotten_blood_quest/creaturescripts_bosses_killed.lua} | 0
.../rotten_blood_quest/movements_blood_entrance.lua} | 0
.../rotten_blood_quest/movements_entrances.lua} | 0
9 files changed, 0 insertions(+), 0 deletions(-)
rename data-otservbr-global/scripts/{actions/bosses_levers/rotten_bakragore.lua => quests/rotten_blood_quest/actions_bakragore.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/rotten_chagorz.lua => quests/rotten_blood_quest/actions_chagorz.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/rotten_ichgahal.lua => quests/rotten_blood_quest/actions_ichgahal.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/rotten_murcion.lua => quests/rotten_blood_quest/actions_murcion.lua} (100%)
rename data-otservbr-global/scripts/{actions/quests/rotten_blood/sacrifice.lua => quests/rotten_blood_quest/actions_sacrifice.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/rotten_vemiath.lua => quests/rotten_blood_quest/actions_vemiath.lua} (100%)
rename data-otservbr-global/scripts/{actions/quests/rotten_blood/bosses_killed.lua => quests/rotten_blood_quest/creaturescripts_bosses_killed.lua} (100%)
rename data-otservbr-global/scripts/{actions/quests/rotten_blood/blood_entrance.lua => quests/rotten_blood_quest/movements_blood_entrance.lua} (100%)
rename data-otservbr-global/scripts/{actions/quests/rotten_blood/entrances.lua => quests/rotten_blood_quest/movements_entrances.lua} (100%)
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_bakragore.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_bakragore.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_chagorz.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_chagorz.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_ichgahal.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_ichgahal.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_murcion.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_murcion.lua
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_sacrifice.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_sacrifice.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/actions_vemiath.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/actions_vemiath.lua
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/creaturescripts_bosses_killed.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/creaturescripts_bosses_killed.lua
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/movements_blood_entrance.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/movements_blood_entrance.lua
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua b/data-otservbr-global/scripts/quests/rotten_blood_quest/movements_entrances.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua
rename to data-otservbr-global/scripts/quests/rotten_blood_quest/movements_entrances.lua
From 2ffb075d96e731b1607a51ca1629c98a5fb02474 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Wed, 29 Jan 2025 15:31:14 -0300
Subject: [PATCH 12/18] fix: move bosses levers to quests folders (#3305)
---
.../a_pirates_tail/actions_ratmiral_blackwhiskers.lua} | 0
.../a_pirates_tail/actions_tentuglys_head.lua} | 0
.../ahau.lua => quests/adventures_of_galthen/actions_ahau.lua} | 0
.../adventures_of_galthen/actions_megasylvan_yselda.lua} | 0
.../bosses_levers => quests/cradle_of_monsters}/the_monster.lua | 0
.../feaster_of_souls/actions_the_dread_maiden.lua} | 0
.../feaster_of_souls/actions_the_fear_feaster.lua} | 0
.../feaster_of_souls/actions_the_pale_worm.lua} | 0
.../feaster_of_souls/actions_the_unwelcome.lua} | 0
.../bosses_levers => quests/marapur}/timira_the_many-headed.lua | 0
.../bosses_levers => quests/primal_ordeal_quest}/magma_bubble.lua | 0
.../primal_ordeal_quest/the_primal_menace.lua} | 0
.../the_dream_courts_quest/actions_the_nightmare_beast.lua} | 0
.../too_hot_to_handle_quest}/the_brainstealer.lua | 0
14 files changed, 0 insertions(+), 0 deletions(-)
rename data-otservbr-global/scripts/{actions/bosses_levers/ratmiral_blackwhiskers.lua => quests/a_pirates_tail/actions_ratmiral_blackwhiskers.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/tentuglys_head.lua => quests/a_pirates_tail/actions_tentuglys_head.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/ahau.lua => quests/adventures_of_galthen/actions_ahau.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/megasylvan_yselda.lua => quests/adventures_of_galthen/actions_megasylvan_yselda.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers => quests/cradle_of_monsters}/the_monster.lua (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_dread_maiden.lua => quests/feaster_of_souls/actions_the_dread_maiden.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_fear_feaster.lua => quests/feaster_of_souls/actions_the_fear_feaster.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_pale_worm.lua => quests/feaster_of_souls/actions_the_pale_worm.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_unwelcome.lua => quests/feaster_of_souls/actions_the_unwelcome.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers => quests/marapur}/timira_the_many-headed.lua (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers => quests/primal_ordeal_quest}/magma_bubble.lua (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_primal_manace.lua => quests/primal_ordeal_quest/the_primal_menace.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers/the_nightmare_beast.lua => quests/the_dream_courts_quest/actions_the_nightmare_beast.lua} (100%)
rename data-otservbr-global/scripts/{actions/bosses_levers => quests/too_hot_to_handle_quest}/the_brainstealer.lua (100%)
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/ratmiral_blackwhiskers.lua b/data-otservbr-global/scripts/quests/a_pirates_tail/actions_ratmiral_blackwhiskers.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/ratmiral_blackwhiskers.lua
rename to data-otservbr-global/scripts/quests/a_pirates_tail/actions_ratmiral_blackwhiskers.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/tentuglys_head.lua b/data-otservbr-global/scripts/quests/a_pirates_tail/actions_tentuglys_head.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/tentuglys_head.lua
rename to data-otservbr-global/scripts/quests/a_pirates_tail/actions_tentuglys_head.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/ahau.lua b/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_ahau.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/ahau.lua
rename to data-otservbr-global/scripts/quests/adventures_of_galthen/actions_ahau.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/megasylvan_yselda.lua b/data-otservbr-global/scripts/quests/adventures_of_galthen/actions_megasylvan_yselda.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/megasylvan_yselda.lua
rename to data-otservbr-global/scripts/quests/adventures_of_galthen/actions_megasylvan_yselda.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_monster.lua b/data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_monster.lua
rename to data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_dread_maiden.lua b/data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_dread_maiden.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_dread_maiden.lua
rename to data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_dread_maiden.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua b/data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_fear_feaster.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua
rename to data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_fear_feaster.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_pale_worm.lua b/data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_pale_worm.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_pale_worm.lua
rename to data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_pale_worm.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_unwelcome.lua b/data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_unwelcome.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_unwelcome.lua
rename to data-otservbr-global/scripts/quests/feaster_of_souls/actions_the_unwelcome.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/timira_the_many-headed.lua b/data-otservbr-global/scripts/quests/marapur/timira_the_many-headed.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/timira_the_many-headed.lua
rename to data-otservbr-global/scripts/quests/marapur/timira_the_many-headed.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/magma_bubble.lua b/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/magma_bubble.lua
rename to data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_primal_manace.lua b/data-otservbr-global/scripts/quests/primal_ordeal_quest/the_primal_menace.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_primal_manace.lua
rename to data-otservbr-global/scripts/quests/primal_ordeal_quest/the_primal_menace.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_nightmare_beast.lua b/data-otservbr-global/scripts/quests/the_dream_courts_quest/actions_the_nightmare_beast.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_nightmare_beast.lua
rename to data-otservbr-global/scripts/quests/the_dream_courts_quest/actions_the_nightmare_beast.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_brainstealer.lua b/data-otservbr-global/scripts/quests/too_hot_to_handle_quest/the_brainstealer.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/the_brainstealer.lua
rename to data-otservbr-global/scripts/quests/too_hot_to_handle_quest/the_brainstealer.lua
From 6e7f1a9125d9e24e410e195bc6b826c0045be981 Mon Sep 17 00:00:00 2001
From: aphirotx
Date: Wed, 29 Jan 2025 22:37:59 +0100
Subject: [PATCH 13/18] fix: werecrocodile raceid (#3308)
---
data-otservbr-global/monster/lycanthropes/werecrocodile.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data-otservbr-global/monster/lycanthropes/werecrocodile.lua b/data-otservbr-global/monster/lycanthropes/werecrocodile.lua
index 125c25799c0..0d598a21413 100644
--- a/data-otservbr-global/monster/lycanthropes/werecrocodile.lua
+++ b/data-otservbr-global/monster/lycanthropes/werecrocodile.lua
@@ -13,7 +13,7 @@ monster.outfit = {
lookMount = 0,
}
-monster.raceId = 2403
+monster.raceId = 2388
monster.Bestiary = {
class = "Lycanthrope",
race = BESTY_RACE_LYCANTHROPE,
From 304656677dac994ce2784b9b5042f8c763806e93 Mon Sep 17 00:00:00 2001
From: Jonyrewind
Date: Sat, 1 Feb 2025 18:28:14 +0100
Subject: [PATCH 14/18] fix: grand master oberon immunity and butterfly's
raceid (#3307)
---
.../quests/the_explorer_society/blue_butterfly.lua | 2 +-
.../quests/the_explorer_society/yellow_butterfly.lua | 2 +-
.../the_secret_library/bosses/grand_master_oberon.lua | 1 -
data-otservbr-global/monster/vermins/butterfly.lua | 4 ++--
.../the_secret_library_quest/creaturescripts_kill.lua | 10 ----------
5 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/data-otservbr-global/monster/quests/the_explorer_society/blue_butterfly.lua b/data-otservbr-global/monster/quests/the_explorer_society/blue_butterfly.lua
index 3893adff8c4..bf2e1f56136 100644
--- a/data-otservbr-global/monster/quests/the_explorer_society/blue_butterfly.lua
+++ b/data-otservbr-global/monster/quests/the_explorer_society/blue_butterfly.lua
@@ -14,7 +14,7 @@ monster.outfit = {
lookMount = 0,
}
-monster.raceId = 213
+monster.raceId = 227
monster.Bestiary = {
class = "Vermin",
race = BESTY_RACE_VERMIN,
diff --git a/data-otservbr-global/monster/quests/the_explorer_society/yellow_butterfly.lua b/data-otservbr-global/monster/quests/the_explorer_society/yellow_butterfly.lua
index b5e88d7f102..834130574f3 100644
--- a/data-otservbr-global/monster/quests/the_explorer_society/yellow_butterfly.lua
+++ b/data-otservbr-global/monster/quests/the_explorer_society/yellow_butterfly.lua
@@ -14,7 +14,7 @@ monster.outfit = {
lookMount = 0,
}
-monster.raceId = 227
+monster.raceId = 235
monster.Bestiary = {
class = "Vermin",
race = BESTY_RACE_VERMIN,
diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua
index 116555f7c4a..ce86aa47861 100644
--- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua
+++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua
@@ -27,7 +27,6 @@ monster.manaCost = 0
monster.events = {
"killingLibrary",
- "oberonImmune",
}
monster.changeTarget = {
diff --git a/data-otservbr-global/monster/vermins/butterfly.lua b/data-otservbr-global/monster/vermins/butterfly.lua
index 9ea18cb070f..89bcbca5815 100644
--- a/data-otservbr-global/monster/vermins/butterfly.lua
+++ b/data-otservbr-global/monster/vermins/butterfly.lua
@@ -13,7 +13,7 @@ monster.outfit = {
lookMount = 0,
}
-monster.raceId = 213
+monster.raceId = 227
monster.Bestiary = {
class = "Vermin",
race = BESTY_RACE_VERMIN,
@@ -31,7 +31,7 @@ monster.Bestiary = {
monster.health = 2
monster.maxHealth = 2
monster.race = "venom"
-monster.corpse = 4378
+monster.corpse = 4993
monster.speed = 160
monster.manaCost = 0
diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua
index 5a3bde3b049..c7e88237022 100644
--- a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua
+++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua
@@ -45,13 +45,3 @@ function creaturescripts_library_bosses.onDeath(creature, corpse, killer, mostDa
end
creaturescripts_library_bosses:register()
-
-local creaturescripts_library_bosses_oberon = CreatureEvent("oberonImmune")
-
-function creaturescripts_library_bosses_oberon.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
- primaryDamage = 0
- secondaryDamage = 0
- return primaryDamage, primaryType, secondaryDamage, secondaryType
-end
-
-creaturescripts_library_bosses_oberon:register()
From 2a5a0e74d49338254af9cb79f01e01930dbfb719 Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Sun, 2 Feb 2025 21:49:47 -0300
Subject: [PATCH 15/18] fix: imbuement slot validation to prevent duplicate
applications (#3316)
---
src/creatures/players/player.cpp | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index d55cb8f5022..399dbeb28e3 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -2294,6 +2294,13 @@ void Player::onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr<
return;
}
+ auto itemSlots = item->getImbuementSlot();
+ if (slot >= itemSlots) {
+ g_logger().error("[Player::onApplyImbuement] - Player {} attempted to apply imbuement in an invalid slot ({})", this->getName(), slot);
+ this->sendImbuementResult("Invalid slot selection.");
+ return;
+ }
+
ImbuementInfo imbuementInfo;
if (item->getImbuementInfo(slot, &imbuementInfo)) {
g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement", this->getName());
@@ -2301,6 +2308,21 @@ void Player::onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr<
return;
}
+ for (uint8_t i = 0; i < item->getImbuementSlot(); i++) {
+ if (i == slot) {
+ continue;
+ }
+
+ ImbuementInfo existingImbuement;
+ if (item->getImbuementInfo(i, &existingImbuement) && existingImbuement.imbuement) {
+ if (existingImbuement.imbuement->getName() == imbuement->getName()) {
+ g_logger().error("[Player::onApplyImbuement] - Player {} attempted to apply the same imbuement in multiple slots", this->getName());
+ this->sendImbuementResult("You cannot apply the same imbuement in multiple slots.");
+ return;
+ }
+ }
+ }
+
const auto &items = imbuement->getItems();
for (auto &[key, value] : items) {
const ItemType &itemType = Item::items[key];
From 8157e52e1cabdcca5f03ce61280631c83fc6055d Mon Sep 17 00:00:00 2001
From: Marco
Date: Thu, 6 Feb 2025 15:57:13 -0300
Subject: [PATCH 16/18] fix: set db version to last migration version (#3319)
---
schema.sql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/schema.sql b/schema.sql
index 9d8f3522f69..10efc5dce00 100644
--- a/schema.sql
+++ b/schema.sql
@@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` (
CONSTRAINT `server_config_pk` PRIMARY KEY (`config`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '48'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
+INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '49'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
-- Table structure `accounts`
CREATE TABLE IF NOT EXISTS `accounts` (
From 95cd92cdb5f43329f282715af115a9428283a358 Mon Sep 17 00:00:00 2001
From: "Leilani A." <168607226+kaleohanopahala@users.noreply.github.com>
Date: Thu, 6 Feb 2025 16:03:30 -0300
Subject: [PATCH 17/18] fix: add missing exit action on morguthis tomb (#3327)
---
.../quests/the_ancient_tombs/action_morguthis_wall.lua | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua b/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua
index 420a2f3fbc2..811efdd4b33 100644
--- a/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua
+++ b/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua
@@ -14,7 +14,7 @@ function morguthisWall.onUse(player, item, fromPosition, target, toPosition)
wall:remove()
else
local creatures = tile:getCreatures()
- if creatures then
+ if #creatures > 0 then
for _, creature in ipairs(creatures) do
local newPosition = Position(wallPosition.x, wallPosition.y + 1, wallPosition.z)
creature:teleportTo(newPosition)
@@ -22,7 +22,7 @@ function morguthisWall.onUse(player, item, fromPosition, target, toPosition)
end
local items = tile:getItems()
- if items then
+ if #items > 0 then
for _, tileItem in ipairs(items) do
local newPosition = Position(wallPosition.x, wallPosition.y + 1, wallPosition.z)
tileItem:moveTo(newPosition)
@@ -36,4 +36,5 @@ function morguthisWall.onUse(player, item, fromPosition, target, toPosition)
end
morguthisWall:position(Position(33212, 32693, 13))
+morguthisWall:position(Position(33209, 32701, 13))
morguthisWall:register()
From a17306668feaea5562213fa2ea39cbd4897a4503 Mon Sep 17 00:00:00 2001
From: Marco
Date: Thu, 6 Feb 2025 16:25:19 -0300
Subject: [PATCH 18/18] refactor: soulcore removal logic (#3326)
---
data-otservbr-global/lib/others/soulpit.lua | 27 +++++++++++--------
.../quests/soulpit/ondroploot_soul_core.lua | 22 +++++++--------
2 files changed, 26 insertions(+), 23 deletions(-)
diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua
index b677b10fd9c..33d615ce92e 100644
--- a/data-otservbr-global/lib/others/soulpit.lua
+++ b/data-otservbr-global/lib/others/soulpit.lua
@@ -153,20 +153,25 @@ SoulPit = {
return name:match("^(.-) soul core")
end,
onFuseSoulCores = function(player, item, target)
- local itemName = item:getName()
- local targetItemName = target:getName()
+ local itemCount = item:getCount(item:getId())
+ if item:getId() == target:getId() and itemCount <= 1 then
+ return false
+ end
- if SoulPit.getSoulCoreMonster(itemName) and SoulPit.getSoulCoreMonster(targetItemName) then
- local randomSoulCore = SoulPit.soulCores[math.random(#SoulPit.soulCores)]
- player:addItem(randomSoulCore:getId(), 1)
- player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
- player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", randomSoulCore:getName()))
- item:remove(1)
- target:remove(1)
- return true
+ local itemSoulCore = SoulPit.getSoulCoreMonster(item:getName())
+ local targetSoulCore = SoulPit.getSoulCoreMonster(target:getName())
+ if not itemSoulCore or not targetSoulCore then
+ return false
end
- return false
+ local randomSoulCore = SoulPit.soulCores[math.random(#SoulPit.soulCores)]
+ player:addItem(randomSoulCore:getId(), 1)
+ player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", randomSoulCore:getName()))
+
+ item:remove(1)
+ target:remove(1)
+ return true
end,
}
diff --git a/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
index 9ddb479c117..beac3b3b9e2 100644
--- a/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
+++ b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua
@@ -4,23 +4,24 @@ function callback.monsterOnDropLoot(monster, corpse)
if not monster or not corpse then
return
end
+
local player = Player(corpse:getCorpseOwner())
if not player or not player:canReceiveLoot() then
return
end
+
if monster:getMonsterForgeClassification() ~= FORGE_FIENDISH_MONSTER then
return
end
- local soulCoreId = nil
local trySameMonsterSoulCore = math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetSameMonsterSoulCore
local mType = monster:getType()
local lootTable = {}
if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulCore then
+ local soulCoreId
if trySameMonsterSoulCore then
- local itemName = monster:getName():lower() .. " soul core"
- soulCoreId = getItemIdByName(itemName)
+ soulCoreId = getItemIdByName(string.format("%s soul core", monster:getName():lower()))
end
if not soulCoreId and not trySameMonsterSoulCore then
@@ -29,16 +30,13 @@ function callback.monsterOnDropLoot(monster, corpse)
if monstersInCategory and #monstersInCategory > 0 then
local randomMonster = monstersInCategory[math.random(#monstersInCategory)]
- local itemName = randomMonster:name():lower() .. " soul core"
- soulCoreId = getItemIdByName(itemName)
- logger.info("soulcoreId: " .. soulCoreId)
+ soulCoreId = getItemIdByName(string.format("%s soul core", randomMonster:name():lower()))
end
end
if soulCoreId then
- lootTable[soulCoreId] = {
- count = 1,
- }
+ lootTable[soulCoreId] = { count = 1 }
+ logger.debug("[monsterOnDropLoot.MonsterOnDropLootSoulCore] {} dropped {} for {}.", monster:getName(), ItemType(soulCoreId):getName(), player:getName())
else
return {}
end
@@ -47,11 +45,11 @@ function callback.monsterOnDropLoot(monster, corpse)
if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulPrism then
local soulPrismId = getItemIdByName("soul prism")
if soulPrismId then
- lootTable[soulPrismId] = {
- count = 1,
- }
+ lootTable[soulPrismId] = { count = 1 }
+ logger.debug("[monsterOnDropLoot.MonsterOnDropLootSoulCore] {} dropped {} for {}.", monster:getName(), ItemType(soulPrismId):getName(), player:getName())
end
end
+
corpse:addLoot(mType:generateLootRoll({}, lootTable, player))
end